Skip to content

Commit acb2ceb

Browse files
Merge pull request #6122 from dotty-staging/add-inlined-invariant-assertions
Add inlined invariant assertions and fixes
2 parents f575444 + 8f49bb0 commit acb2ceb

File tree

11 files changed

+121
-27
lines changed

11 files changed

+121
-27
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class Compiler {
3737
/** Phases dealing with the frontend up to trees ready for TASTY pickling */
3838
protected def frontendPhases: List[List[Phase]] =
3939
List(new FrontEnd) :: // Compiler frontend: scanner, parser, namer, typer
40+
List(new YCheckPositions) :: // YCheck positions
4041
List(new Staging) :: // Check PCP, heal quoted types and expand macros
4142
List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks
4243
List(new PostTyper) :: // Additional checks and cleanups after type checking

compiler/src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@ object Contexts {
481481
}
482482

483483
def typerPhase: Phase = base.typerPhase
484+
def postTyperPhase: Phase = base.postTyperPhase
484485
def sbtExtractDependenciesPhase: Phase = base.sbtExtractDependenciesPhase
485486
def picklerPhase: Phase = base.picklerPhase
486487
def reifyQuotesPhase: Phase = base.reifyQuotesPhase

compiler/src/dotty/tools/dotc/core/Phases.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ object Phases {
217217
}
218218

219219
private[this] var myTyperPhase: Phase = _
220+
private[this] var myPostTyperPhase: Phase = _
220221
private[this] var mySbtExtractDependenciesPhase: Phase = _
221222
private[this] var myPicklerPhase: Phase = _
222223
private[this] var myReifyQuotesPhase: Phase = _
@@ -234,6 +235,7 @@ object Phases {
234235
private[this] var myGenBCodePhase: Phase = _
235236

236237
final def typerPhase: Phase = myTyperPhase
238+
final def postTyperPhase: Phase = myPostTyperPhase
237239
final def sbtExtractDependenciesPhase: Phase = mySbtExtractDependenciesPhase
238240
final def picklerPhase: Phase = myPicklerPhase
239241
final def reifyQuotesPhase: Phase = myReifyQuotesPhase
@@ -254,6 +256,7 @@ object Phases {
254256
def phaseOfClass(pclass: Class[_]) = phases.find(pclass.isInstance).getOrElse(NoPhase)
255257

256258
myTyperPhase = phaseOfClass(classOf[FrontEnd])
259+
myPostTyperPhase = phaseOfClass(classOf[PostTyper])
257260
mySbtExtractDependenciesPhase = phaseOfClass(classOf[sbt.ExtractDependencies])
258261
myPicklerPhase = phaseOfClass(classOf[Pickler])
259262
myReifyQuotesPhase = phaseOfClass(classOf[ReifyQuotes])

compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,13 @@ import scala.annotation.constructorOnly
3333
class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(ictx) {
3434
import tpd._
3535

36-
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
37-
case tree: DefDef if tree.symbol.is(Inline) && level > 0 => EmptyTree
38-
case _ => checkLevel(super.transform(tree))
36+
override def transform(tree: Tree)(implicit ctx: Context): Tree = {
37+
if (tree.source != ctx.source && tree.source.exists)
38+
transform(tree)(ctx.withSource(tree.source))
39+
else tree match {
40+
case tree: DefDef if tree.symbol.is(Inline) && level > 0 => EmptyTree
41+
case _ => checkLevel(super.transform(tree))
42+
}
3943
}
4044

4145
/** Transform quoted trees while maintaining phase correctness */

compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,14 @@ class ReifyQuotes extends MacroTransform {
306306

307307
val tpe = MethodType(defn.SeqType.appliedTo(defn.AnyType) :: Nil, tree.tpe.widen)
308308
val meth = ctx.newSymbol(lambdaOwner, UniqueName.fresh(nme.ANON_FUN), Synthetic | Method, tpe)
309-
Closure(meth, tss => body(tss.head.head)(ctx.withOwner(meth)).changeOwner(ctx.owner, meth))
309+
val closure = Closure(meth, tss => body(tss.head.head)(ctx.withOwner(meth)).changeOwner(ctx.owner, meth)).withSpan(tree.span)
310+
311+
enclosingInlineds match {
312+
case enclosingInline :: _ =>
313+
// In case a tree was inlined inside of the quote and we this closure corresponds to code within it we need to keep the inlined node.
314+
Inlined(enclosingInline, Nil, closure)(ctx.withSource(lambdaOwner.topLevelClass.source))
315+
case Nil => closure
316+
}
310317
}
311318

312319
private def transformWithCapturer(tree: Tree)(capturer: mutable.Map[Symbol, Tree] => Tree => Tree)(implicit ctx: Context): Tree = {
@@ -346,8 +353,10 @@ class ReifyQuotes extends MacroTransform {
346353
Hole(idx, splices).withType(tpe).asInstanceOf[Hole]
347354
}
348355

349-
override def transform(tree: Tree)(implicit ctx: Context): Tree =
350-
reporting.trace(i"Reifier.transform $tree at $level", show = true) {
356+
override def transform(tree: Tree)(implicit ctx: Context): Tree = {
357+
if (tree.source != ctx.source && tree.source.exists)
358+
transform(tree)(ctx.withSource(tree.source))
359+
else reporting.trace(i"Reifier.transform $tree at $level", show = true) {
351360
tree match {
352361
case tree: RefTree if isCaptured(tree.symbol, level) =>
353362
val body = capturers(tree.symbol).apply(tree)
@@ -366,6 +375,7 @@ class ReifyQuotes extends MacroTransform {
366375
super.transform(tree)
367376
}
368377
}
378+
}
369379

370380
private def liftList(list: List[Tree], tpe: Type)(implicit ctx: Context): Tree = {
371381
list.foldRight[Tree](ref(defn.NilModule)) { (x, acc) =>

compiler/src/dotty/tools/dotc/transform/Staging.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ import dotty.tools.dotc.core.StdNames._
1414
import dotty.tools.dotc.core.Symbols._
1515
import dotty.tools.dotc.core.tasty.TreePickler.Hole
1616
import dotty.tools.dotc.core.Types._
17-
import dotty.tools.dotc.util.SourcePosition
18-
import dotty.tools.dotc.util.Spans._
17+
import dotty.tools.dotc.util.{SourceFile, SourcePosition}
1918
import dotty.tools.dotc.transform.SymUtils._
2019
import dotty.tools.dotc.transform.TreeMapWithStages._
2120
import dotty.tools.dotc.typer.Implicits.SearchFailureType

compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import scala.annotation.constructorOnly
3030
* @param levels a stacked map from symbols to the levels in which they were defined
3131
*/
3232
abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMapWithImplicits {
33+
3334
import tpd._
3435
import TreeMapWithStages._
3536

@@ -68,7 +69,9 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
6869
protected def transformSplice(body: Tree, splice: Tree)(implicit ctx: Context): Tree
6970

7071
override def transform(tree: Tree)(implicit ctx: Context): Tree = {
71-
reporting.trace(i"StagingTransformer.transform $tree at $level", show = true) {
72+
if (tree.source != ctx.source && tree.source.exists)
73+
transform(tree)(ctx.withSource(tree.source))
74+
else reporting.trace(i"StagingTransformer.transform $tree at $level", show = true) {
7275
def mapOverTree(lastEntered: List[Symbol]) =
7376
try super.transform(tree)
7477
finally
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import dotty.tools.dotc.ast.Trees._
5+
import dotty.tools.dotc.ast.{tpd, untpd}
6+
import dotty.tools.dotc.core.Contexts._
7+
import dotty.tools.dotc.core.Decorators._
8+
import dotty.tools.dotc.core.Flags._
9+
import dotty.tools.dotc.core.Phases
10+
import dotty.tools.dotc.core.Symbols._
11+
import dotty.tools.dotc.util.{SourceFile, SourcePosition}
12+
13+
/** Ycheck inlined positions */
14+
class YCheckPositions extends Phases.Phase {
15+
import tpd._
16+
17+
def phaseName: String = "inlinedPositions"
18+
19+
override def run(implicit ctx: Context): Unit = () // YCheck only
20+
21+
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
22+
tree match {
23+
case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass =>
24+
new TreeTraverser {
25+
private[this] var sources: List[SourceFile] = ctx.source :: Nil
26+
def traverse(tree: tpd.Tree)(implicit ctx: Context): Unit = {
27+
28+
// Check current context is correct
29+
assert(ctx.source == sources.head)
30+
if (!tree.isEmpty && !tree.isInstanceOf[untpd.TypedSplice] && ctx.typerState.isGlobalCommittable) {
31+
if (!tree.isType) { // TODO also check types, currently we do not add Inlined(EmptyTree, _, _) for types. We should.
32+
val currentSource = sources.head
33+
assert(tree.source == currentSource, i"wrong source set for $tree # ${tree.uniqueId} of ${tree.getClass}, set to ${tree.source} but context had $currentSource")
34+
}
35+
}
36+
37+
// Recursivlely check children while keeping track of current source
38+
tree match {
39+
case Inlined(EmptyTree, bindings, expansion) =>
40+
assert(bindings.isEmpty)
41+
val old = sources
42+
sources = old.tail
43+
traverse(expansion)(inlineContext(EmptyTree))
44+
sources = old
45+
case Inlined(call, bindings, expansion) =>
46+
bindings.foreach(traverse(_))
47+
sources = call.symbol.topLevelClass.source :: sources
48+
if (!isMacro(call)) // FIXME macro implementations can drop Inlined nodes. We should reinsert them after macro expansion based on the positions of the trees
49+
traverse(expansion)(inlineContext(call))
50+
sources = sources.tail
51+
case _ => traverseChildren(tree)
52+
}
53+
}
54+
}.traverse(tree)
55+
case _ =>
56+
}
57+
}
58+
59+
private def isMacro(call: Tree)(implicit ctx: Context) = {
60+
if (ctx.phase <= ctx.typerPhase.next) call.symbol.is(Macro)
61+
else (call.symbol.unforcedDecls.exists(_.is(Macro)) || call.symbol.unforcedDecls.toList.exists(_.is(Macro)))
62+
}
63+
64+
}

compiler/src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1587,7 +1587,7 @@ final class SearchRoot extends SearchHistory {
15871587
// Substitute dictionary references into dictionary entry RHSs
15881588
val rhsMap = new TreeTypeMap(treeMap = {
15891589
case id: Ident if vsymMap.contains(id.symbol) =>
1590-
tpd.ref(vsymMap(id.symbol))
1590+
tpd.ref(vsymMap(id.symbol)).withSpan(id.span)
15911591
case tree => tree
15921592
})
15931593
val nrhss = rhss.map(rhsMap(_))

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -372,12 +372,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
372372
* from its `originalOwner`, and, if it comes from outside the inlined method
373373
* itself, it has to be marked as an inlined argument.
374374
*/
375-
def integrate(tree: Tree, originalOwner: Symbol)(implicit ctx: Context): Tree = {
376-
val result = tree.changeOwner(originalOwner, ctx.owner)
377-
if (!originalOwner.isContainedIn(inlinedMethod))
378-
Inlined(EmptyTree, Nil, result).withSpan(tree.span)
379-
else result
380-
}
375+
def integrate(tree: Tree, originalOwner: Symbol)(implicit ctx: Context): Tree =
376+
tree.changeOwner(originalOwner, ctx.owner)
381377

382378
def tryConstValue: Tree =
383379
ctx.typeComparer.constValue(callTypeArgs.head.tpe) match {
@@ -418,6 +414,9 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
418414

419415
val inlineCtx = inlineContext(call).fresh.setTyper(inlineTyper).setNewScope
420416

417+
def inlinedFromOutside(tree: Tree)(span: Span): Tree =
418+
Inlined(EmptyTree, Nil, tree)(ctx.withSource(inlinedMethod.topLevelClass.source)).withSpan(span)
419+
421420
// A tree type map to prepare the inlined body for typechecked.
422421
// The translation maps references to `this` and parameters to
423422
// corresponding arguments or proxies on the type and term level. It also changes
@@ -438,7 +437,9 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
438437
tree.tpe match {
439438
case thistpe: ThisType =>
440439
thisProxy.get(thistpe.cls) match {
441-
case Some(t) => ref(t).withSpan(tree.span)
440+
case Some(t) =>
441+
val thisRef = ref(t).withSpan(call.span)
442+
inlinedFromOutside(thisRef)(tree.span)
442443
case None => tree
443444
}
444445
case _ => tree
@@ -449,12 +450,11 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
449450
if (tree.name == nme.WILDCARD) tree.span // From type match
450451
else if (tree.symbol.isTypeParam && tree.symbol.owner.isClass) tree.span // TODO is this the correct span?
451452
else paramSpan(tree.name)
453+
val inlinedCtx = ctx.withSource(inlinedMethod.topLevelClass.source)
452454
paramProxy.get(tree.tpe) match {
453455
case Some(t) if tree.isTerm && t.isSingleton =>
454-
t.dealias match {
455-
case tp: ConstantType => Inlined(EmptyTree, Nil, singleton(tp).withSpan(argSpan)).withSpan(tree.span)
456-
case tp => singleton(tp).withSpan(argSpan)
457-
}
456+
val inlinedSingleton = singleton(t).withSpan(argSpan)
457+
inlinedFromOutside(inlinedSingleton)(tree.span)
458458
case Some(t) if tree.isType =>
459459
TypeTree(t).withSpan(argSpan)
460460
case _ => tree
@@ -467,7 +467,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
467467

468468
// Apply inliner to `rhsToInline`, split off any implicit bindings from result, and
469469
// make them part of `bindingsBuf`. The expansion is then the tree that remains.
470-
val expansion = inliner.transform(rhsToInline).withSpan(call.span)
470+
val expansion = inliner.transform(rhsToInline)
471471

472472
def issueError() = callValueArgss match {
473473
case (msgArg :: rest) :: Nil =>
@@ -1159,8 +1159,17 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
11591159
assert(level == 0)
11601160
val inlinedFrom = enclosingInlineds.last
11611161
val evaluatedSplice = Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)(ctx.withSource(inlinedFrom.source))
1162+
1163+
val inlinedNormailizer = new TreeMap {
1164+
override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
1165+
case Inlined(EmptyTree, Nil, expr) if enclosingInlineds.isEmpty => transform(expr)
1166+
case _ => super.transform(tree)
1167+
}
1168+
}
1169+
val normalizedSplice = inlinedNormailizer.transform(evaluatedSplice)
1170+
11621171
if (ctx.reporter.hasErrors) EmptyTree
1163-
else evaluatedSplice.withSpan(span)
1172+
else normalizedSplice.withSpan(span)
11641173
}
11651174

11661175
}

tests/run/i4803/App_2.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ object Test {
1212
println(n.power(5))
1313

1414
val n2 = new Num2(1.5)
15-
println(n.power(0))
16-
println(n.power(1))
17-
println(n.power(2))
18-
println(n.power(5))
15+
println(n2.power(0))
16+
println(n2.power(1))
17+
println(n2.power(2))
18+
println(n2.power(5))
1919
}
2020
}

0 commit comments

Comments
 (0)