Skip to content

Add inlined invariant assertions and fixes #6122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class Compiler {
/** Phases dealing with the frontend up to trees ready for TASTY pickling */
protected def frontendPhases: List[List[Phase]] =
List(new FrontEnd) :: // Compiler frontend: scanner, parser, namer, typer
List(new YCheckPositions) :: // YCheck positions
List(new Staging) :: // Check PCP, heal quoted types and expand macros
List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks
List(new PostTyper) :: // Additional checks and cleanups after type checking
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ object Contexts {
}

def typerPhase: Phase = base.typerPhase
def postTyperPhase: Phase = base.postTyperPhase
def sbtExtractDependenciesPhase: Phase = base.sbtExtractDependenciesPhase
def picklerPhase: Phase = base.picklerPhase
def reifyQuotesPhase: Phase = base.reifyQuotesPhase
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Phases.scala
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ object Phases {
}

private[this] var myTyperPhase: Phase = _
private[this] var myPostTyperPhase: Phase = _
private[this] var mySbtExtractDependenciesPhase: Phase = _
private[this] var myPicklerPhase: Phase = _
private[this] var myReifyQuotesPhase: Phase = _
Expand All @@ -234,6 +235,7 @@ object Phases {
private[this] var myGenBCodePhase: Phase = _

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

myTyperPhase = phaseOfClass(classOf[FrontEnd])
myPostTyperPhase = phaseOfClass(classOf[PostTyper])
mySbtExtractDependenciesPhase = phaseOfClass(classOf[sbt.ExtractDependencies])
myPicklerPhase = phaseOfClass(classOf[Pickler])
myReifyQuotesPhase = phaseOfClass(classOf[ReifyQuotes])
Expand Down
10 changes: 7 additions & 3 deletions compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ import scala.annotation.constructorOnly
class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(ictx) {
import tpd._

override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
case tree: DefDef if tree.symbol.is(Inline) && level > 0 => EmptyTree
case _ => checkLevel(super.transform(tree))
override def transform(tree: Tree)(implicit ctx: Context): Tree = {
if (tree.source != ctx.source && tree.source.exists)
transform(tree)(ctx.withSource(tree.source))
else tree match {
case tree: DefDef if tree.symbol.is(Inline) && level > 0 => EmptyTree
case _ => checkLevel(super.transform(tree))
}
}

/** Transform quoted trees while maintaining phase correctness */
Expand Down
16 changes: 13 additions & 3 deletions compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,14 @@ class ReifyQuotes extends MacroTransform {

val tpe = MethodType(defn.SeqType.appliedTo(defn.AnyType) :: Nil, tree.tpe.widen)
val meth = ctx.newSymbol(lambdaOwner, UniqueName.fresh(nme.ANON_FUN), Synthetic | Method, tpe)
Closure(meth, tss => body(tss.head.head)(ctx.withOwner(meth)).changeOwner(ctx.owner, meth))
val closure = Closure(meth, tss => body(tss.head.head)(ctx.withOwner(meth)).changeOwner(ctx.owner, meth)).withSpan(tree.span)

enclosingInlineds match {
case enclosingInline :: _ =>
// 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.
Inlined(enclosingInline, Nil, closure)(ctx.withSource(lambdaOwner.topLevelClass.source))
case Nil => closure
}
}

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

override def transform(tree: Tree)(implicit ctx: Context): Tree =
reporting.trace(i"Reifier.transform $tree at $level", show = true) {
override def transform(tree: Tree)(implicit ctx: Context): Tree = {
if (tree.source != ctx.source && tree.source.exists)
transform(tree)(ctx.withSource(tree.source))
else reporting.trace(i"Reifier.transform $tree at $level", show = true) {
tree match {
case tree: RefTree if isCaptured(tree.symbol, level) =>
val body = capturers(tree.symbol).apply(tree)
Expand All @@ -366,6 +375,7 @@ class ReifyQuotes extends MacroTransform {
super.transform(tree)
}
}
}

private def liftList(list: List[Tree], tpe: Type)(implicit ctx: Context): Tree = {
list.foldRight[Tree](ref(defn.NilModule)) { (x, acc) =>
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/transform/Staging.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.core.tasty.TreePickler.Hole
import dotty.tools.dotc.core.Types._
import dotty.tools.dotc.util.SourcePosition
import dotty.tools.dotc.util.Spans._
import dotty.tools.dotc.util.{SourceFile, SourcePosition}
import dotty.tools.dotc.transform.SymUtils._
import dotty.tools.dotc.transform.TreeMapWithStages._
import dotty.tools.dotc.typer.Implicits.SearchFailureType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import scala.annotation.constructorOnly
* @param levels a stacked map from symbols to the levels in which they were defined
*/
abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMapWithImplicits {

import tpd._
import TreeMapWithStages._

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

override def transform(tree: Tree)(implicit ctx: Context): Tree = {
reporting.trace(i"StagingTransformer.transform $tree at $level", show = true) {
if (tree.source != ctx.source && tree.source.exists)
transform(tree)(ctx.withSource(tree.source))
else reporting.trace(i"StagingTransformer.transform $tree at $level", show = true) {
def mapOverTree(lastEntered: List[Symbol]) =
try super.transform(tree)
finally
Expand Down
64 changes: 64 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package dotty.tools.dotc
package transform

import dotty.tools.dotc.ast.Trees._
import dotty.tools.dotc.ast.{tpd, untpd}
import dotty.tools.dotc.core.Contexts._
import dotty.tools.dotc.core.Decorators._
import dotty.tools.dotc.core.Flags._
import dotty.tools.dotc.core.Phases
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.util.{SourceFile, SourcePosition}

/** Ycheck inlined positions */
class YCheckPositions extends Phases.Phase {
import tpd._

def phaseName: String = "inlinedPositions"

override def run(implicit ctx: Context): Unit = () // YCheck only

override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
tree match {
case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass =>
new TreeTraverser {
private[this] var sources: List[SourceFile] = ctx.source :: Nil
def traverse(tree: tpd.Tree)(implicit ctx: Context): Unit = {

// Check current context is correct
assert(ctx.source == sources.head)
if (!tree.isEmpty && !tree.isInstanceOf[untpd.TypedSplice] && ctx.typerState.isGlobalCommittable) {
if (!tree.isType) { // TODO also check types, currently we do not add Inlined(EmptyTree, _, _) for types. We should.
val currentSource = sources.head
assert(tree.source == currentSource, i"wrong source set for $tree # ${tree.uniqueId} of ${tree.getClass}, set to ${tree.source} but context had $currentSource")
}
}

// Recursivlely check children while keeping track of current source
tree match {
case Inlined(EmptyTree, bindings, expansion) =>
assert(bindings.isEmpty)
val old = sources
sources = old.tail
traverse(expansion)(inlineContext(EmptyTree))
sources = old
case Inlined(call, bindings, expansion) =>
bindings.foreach(traverse(_))
sources = call.symbol.topLevelClass.source :: sources
if (!isMacro(call)) // FIXME macro implementations can drop Inlined nodes. We should reinsert them after macro expansion based on the positions of the trees
traverse(expansion)(inlineContext(call))
sources = sources.tail
case _ => traverseChildren(tree)
}
}
}.traverse(tree)
case _ =>
}
}

private def isMacro(call: Tree)(implicit ctx: Context) = {
if (ctx.phase <= ctx.typerPhase.next) call.symbol.is(Macro)
else (call.symbol.unforcedDecls.exists(_.is(Macro)) || call.symbol.unforcedDecls.toList.exists(_.is(Macro)))
}

}
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1587,7 +1587,7 @@ final class SearchRoot extends SearchHistory {
// Substitute dictionary references into dictionary entry RHSs
val rhsMap = new TreeTypeMap(treeMap = {
case id: Ident if vsymMap.contains(id.symbol) =>
tpd.ref(vsymMap(id.symbol))
tpd.ref(vsymMap(id.symbol)).withSpan(id.span)
case tree => tree
})
val nrhss = rhss.map(rhsMap(_))
Expand Down
35 changes: 22 additions & 13 deletions compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -372,12 +372,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
* from its `originalOwner`, and, if it comes from outside the inlined method
* itself, it has to be marked as an inlined argument.
*/
def integrate(tree: Tree, originalOwner: Symbol)(implicit ctx: Context): Tree = {
val result = tree.changeOwner(originalOwner, ctx.owner)
if (!originalOwner.isContainedIn(inlinedMethod))
Inlined(EmptyTree, Nil, result).withSpan(tree.span)
else result
}
def integrate(tree: Tree, originalOwner: Symbol)(implicit ctx: Context): Tree =
tree.changeOwner(originalOwner, ctx.owner)

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

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

def inlinedFromOutside(tree: Tree)(span: Span): Tree =
Inlined(EmptyTree, Nil, tree)(ctx.withSource(inlinedMethod.topLevelClass.source)).withSpan(span)

// A tree type map to prepare the inlined body for typechecked.
// The translation maps references to `this` and parameters to
// corresponding arguments or proxies on the type and term level. It also changes
Expand All @@ -438,7 +437,9 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
tree.tpe match {
case thistpe: ThisType =>
thisProxy.get(thistpe.cls) match {
case Some(t) => ref(t).withSpan(tree.span)
case Some(t) =>
val thisRef = ref(t).withSpan(call.span)
inlinedFromOutside(thisRef)(tree.span)
case None => tree
}
case _ => tree
Expand All @@ -449,12 +450,11 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
if (tree.name == nme.WILDCARD) tree.span // From type match
else if (tree.symbol.isTypeParam && tree.symbol.owner.isClass) tree.span // TODO is this the correct span?
else paramSpan(tree.name)
val inlinedCtx = ctx.withSource(inlinedMethod.topLevelClass.source)
paramProxy.get(tree.tpe) match {
case Some(t) if tree.isTerm && t.isSingleton =>
t.dealias match {
case tp: ConstantType => Inlined(EmptyTree, Nil, singleton(tp).withSpan(argSpan)).withSpan(tree.span)
case tp => singleton(tp).withSpan(argSpan)
}
val inlinedSingleton = singleton(t).withSpan(argSpan)
inlinedFromOutside(inlinedSingleton)(tree.span)
case Some(t) if tree.isType =>
TypeTree(t).withSpan(argSpan)
case _ => tree
Expand All @@ -467,7 +467,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {

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

def issueError() = callValueArgss match {
case (msgArg :: rest) :: Nil =>
Expand Down Expand Up @@ -1159,8 +1159,17 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
assert(level == 0)
val inlinedFrom = enclosingInlineds.last
val evaluatedSplice = Splicer.splice(body, inlinedFrom.sourcePos, MacroClassLoader.fromContext)(ctx.withSource(inlinedFrom.source))

val inlinedNormailizer = new TreeMap {
override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
case Inlined(EmptyTree, Nil, expr) if enclosingInlineds.isEmpty => transform(expr)
case _ => super.transform(tree)
}
}
val normalizedSplice = inlinedNormailizer.transform(evaluatedSplice)

if (ctx.reporter.hasErrors) EmptyTree
else evaluatedSplice.withSpan(span)
else normalizedSplice.withSpan(span)
}

}
8 changes: 4 additions & 4 deletions tests/run/i4803/App_2.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ object Test {
println(n.power(5))

val n2 = new Num2(1.5)
println(n.power(0))
println(n.power(1))
println(n.power(2))
println(n.power(5))
println(n2.power(0))
println(n2.power(1))
println(n2.power(2))
println(n2.power(5))
}
}