Skip to content

Commit 7363503

Browse files
committed
Add inlined invariant assertions and fixes
1 parent 4e1d3d4 commit 7363503

File tree

9 files changed

+96
-27
lines changed

9 files changed

+96
-27
lines changed

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
@@ -303,7 +303,14 @@ class ReifyQuotes extends MacroTransform {
303303

304304
val tpe = MethodType(defn.SeqType.appliedTo(defn.AnyType) :: Nil, tree.tpe.widen)
305305
val meth = ctx.newSymbol(lambdaOwner, UniqueName.fresh(nme.ANON_FUN), Synthetic | Method, tpe)
306-
Closure(meth, tss => body(tss.head.head)(ctx.withOwner(meth)).changeOwner(ctx.owner, meth))
306+
val closure = Closure(meth, tss => body(tss.head.head)(ctx.withOwner(meth)).changeOwner(ctx.owner, meth)).withSpan(tree.span)
307+
308+
enclosingInlineds match {
309+
case enclosingInline :: _ =>
310+
// 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.
311+
Inlined(enclosingInline, Nil, closure)(ctx.withSource(lambdaOwner.topLevelClass.source))
312+
case Nil => closure
313+
}
307314
}
308315

309316
private def transformWithCapturer(tree: Tree)(capturer: mutable.Map[Symbol, Tree] => Tree => Tree)(implicit ctx: Context): Tree = {
@@ -343,8 +350,10 @@ class ReifyQuotes extends MacroTransform {
343350
Hole(idx, splices).withType(tpe).asInstanceOf[Hole]
344351
}
345352

346-
override def transform(tree: Tree)(implicit ctx: Context): Tree =
347-
reporting.trace(i"Reifier.transform $tree at $level", show = true) {
353+
override def transform(tree: Tree)(implicit ctx: Context): Tree = {
354+
if (tree.source != ctx.source && tree.source.exists)
355+
transform(tree)(ctx.withSource(tree.source))
356+
else reporting.trace(i"Reifier.transform $tree at $level", show = true) {
348357
tree match {
349358
case TypeApply(Select(spliceTree @ Spliced(_), _), tp) if tree.symbol.isTypeCast =>
350359
// Splice term which should be in the form `${x}.asInstanceOf[T]` where T is an artifact of
@@ -364,6 +373,7 @@ class ReifyQuotes extends MacroTransform {
364373
super.transform(tree)
365374
}
366375
}
376+
}
367377

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

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

Lines changed: 38 additions & 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
@@ -65,6 +64,43 @@ class Staging extends MacroTransform {
6564
case _ =>
6665
}
6766
}
67+
if (ctx.phase <= ctx.erasurePhase) {
68+
tree match {
69+
case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass =>
70+
new TreeTraverser {
71+
private[this] var sources: List[SourceFile] = ctx.source :: Nil
72+
def traverse(tree: tpd.Tree)(implicit ctx: Context): Unit = {
73+
if (!tree.isEmpty && !tree.isInstanceOf[untpd.TypedSplice] && ctx.typerState.isGlobalCommittable) {
74+
if (!tree.isType) { // TODO also check types, currently we do not add Inlined(EmptyTree, _, _) for types. We should.
75+
val currentSource = sources.head
76+
assert(tree.source == currentSource, i"wrong source set for $tree # ${tree.uniqueId} of ${tree.getClass}, set to ${tree.source} but context had $currentSource")
77+
}
78+
}
79+
tree match {
80+
case Inlined(EmptyTree, bindings, expansion) =>
81+
assert(bindings.isEmpty)
82+
val old = sources
83+
sources = old.tail
84+
traverse(expansion)(inlineContext(EmptyTree))
85+
sources = old
86+
case Inlined(call, bindings, expansion) =>
87+
bindings.foreach(traverse(_))
88+
sources = call.symbol.topLevelClass.source :: sources
89+
if (
90+
!( // FIXME macro implementations can drop Inlined nodes. We should reinsert them after macro expansion based on the positions of the trees
91+
((ctx.phase <= ctx.typerPhase.next) && call.symbol.is(Macro)) ||
92+
(!(ctx.phase <= ctx.typerPhase.next) && call.symbol.unforcedDecls.exists(_.is(Macro)) || call.symbol.unforcedDecls.toList.exists(_.is(Macro)))
93+
)
94+
) traverse(expansion)(inlineContext(call))
95+
sources = sources.tail
96+
case _ => traverseChildren(tree)
97+
}
98+
}
99+
}.traverse(tree)
100+
case _ =>
101+
}
102+
103+
}
68104
}
69105

70106
override def run(implicit ctx: Context): Unit =

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(splice: Select)(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

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

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

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

Lines changed: 25 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)(inlinedSourceCtx).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 =>
@@ -979,8 +979,17 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
979979
assert(level == 0)
980980
val inlinedFrom = enclosingInlineds.last
981981
val evaluatedSplice = Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)(ctx.withSource(inlinedFrom.source))
982+
983+
val inlinedNormailizer = new TreeMap {
984+
override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
985+
case Inlined(EmptyTree, Nil, expr) if enclosingInlineds.isEmpty => transform(expr)
986+
case _ => super.transform(tree)
987+
}
988+
}
989+
val normalizedSplice = inlinedNormailizer.transform(evaluatedSplice)
990+
982991
if (ctx.reporter.hasErrors) EmptyTree
983-
else evaluatedSplice.withSpan(span)
992+
else normalizedSplice.withSpan(span)
984993
}
985994

986995
override def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context): Tree =
@@ -1159,4 +1168,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
11591168
inlineTermBindings(termBindings1.asInstanceOf[List[ValOrDefDef]], tree1)
11601169
}
11611170
}
1171+
1172+
private def inlinedSourceCtx(implicit ctx: Context): Context = ctx.withSource(inlinedMethod.topLevelClass.source)
1173+
11621174
}

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)