Skip to content

Add Inlined.inlinedFromOuterScope and refactor inline context #18236

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 1 commit into from
Jul 19, 2023
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
13 changes: 8 additions & 5 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,9 @@ object Trees {
*/
case class Inlined[+T <: Untyped] private[ast] (call: tpd.Tree, bindings: List[MemberDef[T]], expansion: Tree[T])(implicit @constructorOnly src: SourceFile)
extends Tree[T] {

def inlinedFromOuterScope: Boolean = call.isEmpty

type ThisTree[+T <: Untyped] = Inlined[T]
override def isTerm = expansion.isTerm
override def isType = expansion.isType
Expand Down Expand Up @@ -1479,7 +1482,7 @@ object Trees {
* innermost enclosing call for which the inlined version is currently
* processed.
*/
protected def inlineContext(call: tpd.Tree)(using Context): Context = ctx
protected def inlineContext(tree: Inlined)(using Context): Context = ctx

/** The context to use when mapping or accumulating over a tree */
def localCtx(tree: Tree)(using Context): Context
Expand Down Expand Up @@ -1549,8 +1552,8 @@ object Trees {
cpy.Try(tree)(transform(block), transformSub(cases), transform(finalizer))
case SeqLiteral(elems, elemtpt) =>
cpy.SeqLiteral(tree)(transform(elems), transform(elemtpt))
case Inlined(call, bindings, expansion) =>
cpy.Inlined(tree)(call, transformSub(bindings), transform(expansion)(using inlineContext(call)))
case tree @ Inlined(call, bindings, expansion) =>
cpy.Inlined(tree)(call, transformSub(bindings), transform(expansion)(using inlineContext(tree)))
case TypeTree() =>
tree
case SingletonTypeTree(ref) =>
Expand Down Expand Up @@ -1693,8 +1696,8 @@ object Trees {
this(this(this(x, block), handler), finalizer)
case SeqLiteral(elems, elemtpt) =>
this(this(x, elems), elemtpt)
case Inlined(call, bindings, expansion) =>
this(this(x, bindings), expansion)(using inlineContext(call))
case tree @ Inlined(call, bindings, expansion) =>
this(this(x, bindings), expansion)(using inlineContext(tree))
case TypeTree() =>
x
case SingletonTypeTree(ref) =>
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1392,17 +1392,17 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
* EmptyTree calls (for parameters) cancel the next-enclosing call in the list instead of being added to it.
* We assume parameters are never nested inside parameters.
*/
override def inlineContext(call: Tree)(using Context): Context = {
override def inlineContext(tree: Inlined)(using Context): Context = {
// We assume enclosingInlineds is already normalized, and only process the new call with the head.
val oldIC = enclosingInlineds

val newIC =
if call.isEmpty then
if tree.inlinedFromOuterScope then
oldIC match
case t1 :: ts2 => ts2
case _ => oldIC
else
call :: oldIC
tree.call :: oldIC

val ctx1 = ctx.fresh.setProperty(InlinedCalls, newIC)
if oldIC.isEmpty then ctx1.setProperty(InlinedTrees, new Counter) else ctx1
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -528,12 +528,12 @@ class TreePickler(pickler: TastyPickler) {
case SeqLiteral(elems, elemtpt) =>
writeByte(REPEATED)
withLength { pickleTree(elemtpt); elems.foreach(pickleTree) }
case Inlined(call, bindings, expansion) =>
case tree @ Inlined(call, bindings, expansion) =>
writeByte(INLINED)
bindings.foreach(preRegister)
withLength {
pickleTree(expansion)
if (!call.isEmpty) pickleTree(call)
if (!tree.inlinedFromOuterScope) pickleTree(call)
bindings.foreach { b =>
assert(b.isInstanceOf[DefDef] || b.isInstanceOf[ValDef])
pickleTree(b)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,8 @@ class InlineReducer(inliner: Inliner)(using Context):
}
case Alternative(pats) =>
pats.exists(reducePattern(caseBindingMap, scrut, _))
case Inlined(EmptyTree, Nil, ipat) =>
reducePattern(caseBindingMap, scrut, ipat)
case tree: Inlined if tree.inlinedFromOuterScope =>
reducePattern(caseBindingMap, scrut, tree.expansion)
case _ => false
}
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/inlines/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ object Inliner:
new InlinerMap(typeMap, treeMap, oldOwners, newOwners, substFrom, substTo)

override def transformInlined(tree: Inlined)(using Context) =
if tree.call.isEmpty then
if tree.inlinedFromOuterScope then
tree.expansion match
case expansion: TypeTree => expansion
case _ => tree
Expand Down Expand Up @@ -549,7 +549,7 @@ class Inliner(val call: tpd.Tree)(using Context):

val inlineTyper = new InlineTyper(ctx.reporter.errorCount)

val inlineCtx = inlineContext(call).fresh.setTyper(inlineTyper).setNewScope
val inlineCtx = inlineContext(Inlined(call, Nil, ref(defn.Predef_undefined))).fresh.setTyper(inlineTyper).setNewScope

def inlinedFromOutside(tree: Tree)(span: Span): Tree =
Inlined(EmptyTree, Nil, tree)(using ctx.withSource(inlinedMethod.topLevelClass.source)).withSpan(span)
Expand Down Expand Up @@ -1049,7 +1049,7 @@ class Inliner(val call: tpd.Tree)(using Context):
}
val inlinedNormalizer = new TreeMap {
override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match {
case Inlined(EmptyTree, Nil, expr) if enclosingInlineds.isEmpty => transform(expr)
case tree @ Inlined(_, Nil, expr) if tree.inlinedFromOuterScope && enclosingInlineds.isEmpty => transform(expr)
case _ => super.transform(tree)
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/inlines/Inlines.scala
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ object Inlines:
override def transform(t: Tree)(using Context) =
if call.span.exists then
t match
case Inlined(t, Nil, expr) if t.isEmpty => expr
case t @ Inlined(_, Nil, expr) if t.inlinedFromOuterScope => expr
case _ if t.isEmpty => t
case _ => super.transform(t.withSpan(call.span))
else t
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case tree @ Inlined(call, bindings, body) =>
val bodyText = if bindings.isEmpty then toText(body) else blockText(bindings :+ body)
if homogenizedView || !ctx.settings.XprintInline.value then bodyText
else if call.isEmpty then stringText("{{") ~ stringText("/* inlined from outside */") ~ bodyText ~ stringText("}}")
else if tree.inlinedFromOuterScope then stringText("{{") ~ stringText("/* inlined from outside */") ~ bodyText ~ stringText("}}")
else keywordText("{{") ~ keywordText("/* inlined from ") ~ toText(call) ~ keywordText(" */") ~ bodyText ~ keywordText("}}")
case tpt: untpd.DerivedTypeTree =>
"<derived typetree watching " ~ tpt.watched.showSummary() ~ ">"
Expand Down
12 changes: 6 additions & 6 deletions compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@ object PickledQuotes {

/** Unpickle the tree contained in the TastyExpr */
def unpickleTerm(pickled: String | List[String], typeHole: TypeHole, termHole: ExprHole)(using Context): Tree = {
val unpickled = withMode(Mode.ReadPositions)(unpickle(pickled, isType = false))
val Inlined(call, Nil, expansion) = unpickled: @unchecked
val inlineCtx = inlineContext(call)
val expansion1 = spliceTypes(expansion, typeHole)(using inlineCtx)
val expansion2 = spliceTerms(expansion1, typeHole, termHole)(using inlineCtx)
cpy.Inlined(unpickled)(call, Nil, expansion2)
withMode(Mode.ReadPositions)(unpickle(pickled, isType = false)) match
case tree @ Inlined(call, Nil, expansion) =>
val inlineCtx = inlineContext(tree)
val expansion1 = spliceTypes(expansion, typeHole)(using inlineCtx)
val expansion2 = spliceTerms(expansion1, typeHole, termHole)(using inlineCtx)
cpy.Inlined(tree)(call, Nil, expansion2)
}


Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -388,10 +388,10 @@ private class ExtractDependenciesCollector extends tpd.TreeTraverser { thisTreeT
}

tree match {
case Inlined(call, _, _) if !call.isEmpty =>
case tree: Inlined if !tree.inlinedFromOuterScope =>
// The inlined call is normally ignored by TreeTraverser but we need to
// record it as a dependency
traverse(call)
traverse(tree.call)
case vd: ValDef if vd.symbol.is(ModuleVal) =>
// Don't visit module val
case t: Template if t.symbol.owner.is(ModuleClass) =>
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/MegaPhase.scala
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
case tree: Inlined =>
inContext(prepInlined(tree, start)(using outerCtx)) {
val bindings = transformSpecificTrees(tree.bindings, start)
val expansion = transformTree(tree.expansion, start)(using inlineContext(tree.call))
val expansion = transformTree(tree.expansion, start)(using inlineContext(tree))
goInlined(cpy.Inlined(tree)(tree.call, bindings, expansion), start)
}
case tree: Quote =>
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -363,12 +363,12 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
case _ =>
super.transform(tree1)
}
case Inlined(call, bindings, expansion) if !call.isEmpty =>
case tree @ Inlined(call, bindings, expansion) if !tree.inlinedFromOuterScope =>
val pos = call.sourcePos
CrossVersionChecks.checkExperimentalRef(call.symbol, pos)
withMode(Mode.InlinedCall)(transform(call))
val callTrace = Inlines.inlineCallTrace(call.symbol, pos)(using ctx.withSource(pos.source))
cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)(using inlineContext(call)))
cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)(using inlineContext(tree)))
case templ: Template =>
withNoCheckNews(templ.parents.flatMap(newPart)) {
forwardParamAccessors(templ)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/Recheck.scala
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ abstract class Recheck extends Phase, SymTransformer:
recheckBlock(tree.stats, tree.expr, pt)

def recheckInlined(tree: Inlined, pt: Type)(using Context): Type =
recheckBlock(tree.bindings, tree.expansion, pt)(using inlineContext(tree.call))
recheckBlock(tree.bindings, tree.expansion, pt)(using inlineContext(tree))

def recheckIf(tree: If, pt: Type)(using Context): Type =
recheck(tree.cond, defn.BooleanType)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/Splicer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ object Splicer {
case expr: Ident if expr.symbol.isAllOf(InlineByNameProxy) =>
// inline proxy for by-name parameter
expr.symbol.defTree.asInstanceOf[DefDef].rhs
case Inlined(EmptyTree, _, body1) => body1
case tree: Inlined if tree.inlinedFromOuterScope => tree.expansion
case _ => body
}
new ExprImpl(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(body1, ctx.owner)).withSpan(body1.span), SpliceScope.getCurrent)
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,17 @@ class YCheckPositions extends Phase {
// Recursivlely check children while keeping track of current source
reporting.trace(i"check pos ${tree.getClass} ${tree.source} ${sources.head} $tree") {
tree match {
case Inlined(EmptyTree, bindings, expansion) =>
case tree @ Inlined(_, bindings, expansion) if tree.inlinedFromOuterScope =>
assert(bindings.isEmpty)
val old = sources
sources = old.tail
traverse(expansion)(using inlineContext(EmptyTree).withSource(sources.head))
traverse(expansion)(using inlineContext(tree).withSource(sources.head))
sources = old
case Inlined(call, bindings, expansion) =>
case tree @ Inlined(call, bindings, expansion) =>
// bindings.foreach(traverse(_)) // TODO check inline proxies (see tests/tun/lst)
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)(using inlineContext(call).withSource(sources.head))
traverse(expansion)(using inlineContext(tree).withSource(sources.head))
sources = sources.tail
case _ => traverseChildren(tree)
}
Expand Down
7 changes: 7 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/ReTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking
override def typedUnApply(tree: untpd.Apply, selType: Type)(using Context): Tree =
typedApply(tree, selType)

override def typedInlined(tree: untpd.Inlined, pt: Type)(using Context): Tree = {
val (bindings1, exprCtx) = typedBlockStats(tree.bindings)
val expansion1 = typed(tree.expansion, pt)(using inlineContext(promote(tree))(using exprCtx))
untpd.cpy.Inlined(tree)(tree.call, bindings1.asInstanceOf[List[MemberDef]], expansion1)
.withType(avoidingType(expansion1, bindings1))
}

override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree =
assertTyped(tree)
val body1 = typed(tree.body, promote(tree).bodyType)(using quoteContext)
Expand Down
8 changes: 2 additions & 6 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2092,12 +2092,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
}
}

def typedInlined(tree: untpd.Inlined, pt: Type)(using Context): Tree = {
val (bindings1, exprCtx) = typedBlockStats(tree.bindings)
val expansion1 = typed(tree.expansion, pt)(using inlineContext(tree.call)(using exprCtx))
assignType(cpy.Inlined(tree)(tree.call, bindings1.asInstanceOf[List[MemberDef]], expansion1),
bindings1, expansion1)
}
def typedInlined(tree: untpd.Inlined, pt: Type)(using Context): Tree =
throw new UnsupportedOperationException("cannot type check a Inlined node")

def completeTypeTree(tree: untpd.TypeTree, pt: Type, original: untpd.Tree)(using Context): TypeTree =
tree.withSpan(original.span).withAttachmentsFrom(original)
Expand Down