Skip to content
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

Add transparent methods - untyped trees version #4616

Merged
merged 62 commits into from
Jul 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
bba562a
Functional Typelevel Programming in Scala
odersky Jul 18, 2018
8b70e37
Update section on transparent values
odersky Jul 18, 2018
44a5de1
Introduce UntypedSplice
odersky Jul 18, 2018
9d7fff9
Untasty trees
odersky Jul 18, 2018
f8f82e1
Dual purpose inliner
odersky Jul 18, 2018
34bc5c4
Pretype nested transparent applications
odersky Jul 18, 2018
d7a3430
Better error position when inline call limit exceeded
odersky Jul 18, 2018
097043f
Some refactorings to Inliner and Typer
odersky Jul 18, 2018
4aeeed6
Reduce projections of data type fields in inliner
odersky Jul 18, 2018
5896f28
Replace inlined pure constant expressions by literals
odersky Jul 18, 2018
dc5c3a0
Refine handling of result types of transparent methods
odersky Jul 18, 2018
de8f2fd
Don't drop impure arguments when reducing projections
odersky Jul 18, 2018
632abc1
Exclude members of Predef from implicit inline environment
odersky Jul 18, 2018
cbb7573
Inline -> Transparent
odersky Jul 18, 2018
b7f8302
Consolidate InlineTyper
odersky Jul 18, 2018
67e9fef
Split PrepareTransparent off Inliner
odersky Jul 18, 2018
8e5eaca
Don't add extra bindings for own implicit parameters in inlineable code
odersky Jul 18, 2018
8fe1a0e
Traverse all of bindings to compute reference counts of bindings
odersky Jul 18, 2018
812d22d
Two fixes to pickling and unpickling of untyped trees
odersky Jul 18, 2018
bdf2b54
Adapt transparent value checking to new restrictions
odersky Jul 18, 2018
b666323
Fix types of implicit definitions in transparent closure
odersky Jul 18, 2018
a125c67
Refactor Inliner
odersky Jul 18, 2018
e96c20f
Support transparent matches with simple typed patterns
odersky Jul 18, 2018
a019a4e
Enforce restrictions on typelevel methods
odersky Jul 18, 2018
31f6db7
Match bindings should go with inlined body
odersky Jul 18, 2018
7f48ed1
Refinements to erased
odersky Jul 18, 2018
19aa394
Compute gadt context when reducing matches
odersky Jul 18, 2018
dd29dc6
Wait until PostTyper before eliminating erased arguments
odersky Jul 18, 2018
25a2cc9
Fix return type of defaultValue in typelevel.md
odersky Jul 18, 2018
1f12ce2
Reorganize labeling of toplevel matches
odersky Jul 18, 2018
33e60bc
Implement implicit match expressions
odersky Jul 18, 2018
8d03e44
Fix test
odersky Jul 18, 2018
9bee4c0
Keep track of positions for inlined arguments
odersky Jul 18, 2018
dea6772
Reset Config option.
odersky Jul 18, 2018
6f65a20
Implement restrections where an implicit match can appear
odersky Jul 18, 2018
e78fc79
Bump Tasty format major version
odersky Jul 18, 2018
cd87085
Address reviewers comments
odersky Jul 18, 2018
3b98431
Refine implicit match checking
odersky Jul 18, 2018
4b7a240
Doc fix
odersky Jul 19, 2018
e4a7e60
Explain rewrite rules and fix implementation to conform
odersky Jul 19, 2018
22d6f66
Drop redundant test
odersky Jul 20, 2018
59ede6d
Handle nested transparent methods
odersky Jul 20, 2018
2336ae1
Handle constant literals in toplevel matches
odersky Jul 20, 2018
ed7f10c
Refined rules for overriding of typelevel methods
odersky Jul 20, 2018
dbaf124
Address review comments
odersky Jul 22, 2018
44feda7
Fix test
odersky Jul 22, 2018
57853b0
Merge TypeLevel and Erased Flags
odersky Jul 23, 2018
0b7710a
Merge IsInstanceOfChecker and TypeTestsCasts
odersky Jul 23, 2018
beca150
Fix inlining of right-associative transparent methods
odersky Jul 24, 2018
39cd368
Make Transparent method imply Erased
odersky Jul 24, 2018
5d84486
Use ??? instead of defaultValue for erased code
odersky Jul 24, 2018
1112b5e
Enter implicit scrutinee
odersky Jul 24, 2018
1e9e474
Renaming
odersky Jul 24, 2018
e486384
Treat @forceInline specially.
odersky Jul 24, 2018
33523d1
Redo transparent overloading resolution also for Idents as refs
odersky Jul 24, 2018
43f4b55
New test and drop println
odersky Jul 24, 2018
bf5e9c8
Reclassify test
odersky Jul 24, 2018
101daf5
Improvements to Inliner
odersky Jul 25, 2018
52e0560
Fix #4557 rework: Keep original type untpd.New
odersky Jul 26, 2018
d827f65
Fix FromTasty tests
odersky Jul 26, 2018
3bee284
Fix FromSymbol.classDef
odersky Jul 26, 2018
88e7d18
Update check file
odersky Jul 26, 2018
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
2 changes: 1 addition & 1 deletion bench/tests/power-macro/PowerMacro.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import scala.quoted.Expr

object PowerMacro {

inline def power(inline n: Long, x: Double) = ~powerCode(n, '(x))
transparent def power(transparent n: Long, x: Double) = ~powerCode(n, '(x))

def powerCode(n: Long, x: Expr[Double]): Expr[Double] =
if (n == 0) '(1.0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
val Flag_METHOD: Flags = Flags.Method.bits
val ExcludedForwarderFlags: Flags = {
Flags.Specialized | Flags.Lifted | Flags.Protected | Flags.JavaStatic |
Flags.Bridge | Flags.VBridge | Flags.Private | Flags.Macro
Flags.Bridge | Flags.Private | Flags.Macro
}.bits

def isQualifierSafeToElide(qual: Tree): Boolean = tpd.isIdempotentExpr(qual)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/CompilationUnit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import core.Types.Type // Do not remove me #3383
import util.SourceFile
import ast.{tpd, untpd}
import tpd.{ Tree, TreeTraverser }
import typer.Inliner.InlineAccessors
import typer.PrepareTransparent.InlineAccessors
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.SymDenotations.ClassDenotation
import dotty.tools.dotc.core.Symbols._
Expand Down
4 changes: 1 addition & 3 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,7 @@ class Compiler {
new StringInterpolatorOpt, // Optimizes raw and s string interpolators by rewriting them to string concatentations
new CrossCastAnd, // Normalize selections involving intersection types.
new Splitter) :: // Expand selections involving union types into conditionals
List(new ErasedDecls, // Removes all erased defs and vals decls (except for parameters)
new IsInstanceOfChecker, // check runtime realisability for `isInstanceOf`
List(new PruneErasedDefs, // Drop erased definitions from scopes and simplify erased expressions
new VCInlineMethods, // Inlines calls to value class methods
new SeqLiterals, // Express vararg arguments as arrays
new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods
Expand Down Expand Up @@ -119,7 +118,6 @@ class Compiler {
new SelectStatic, // get rid of selects that would be compiled into GetStatic
new CollectEntryPoints, // Find classes with main methods
new CollectSuperCalls, // Find classes that are called with super
new DropInlined, // Drop Inlined nodes, since backend has no use for them
new LabelDefs) :: // Converts calls to labels to jumps
Nil

Expand Down
35 changes: 22 additions & 13 deletions compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -391,12 +391,6 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
SimplyPure
case TypeApply(fn, _) =>
exprPurity(fn)
/*
* Not sure we'll need that. Comment out until we find out
case Apply(Select(free @ Ident(_), nme.apply), _) if free.symbol.name endsWith nme.REIFY_FREE_VALUE_SUFFIX =>
// see a detailed explanation of this trick in `GenSymbols.reifyFreeTerm`
free.symbol.hasStableFlag && isIdempotentExpr(free)
*/
case Apply(fn, args) =>
def isKnownPureOp(sym: Symbol) =
sym.owner.isPrimitiveValueClass || sym.owner == defn.StringClass
Expand Down Expand Up @@ -424,6 +418,8 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
def isPureExpr(tree: Tree)(implicit ctx: Context) = exprPurity(tree) >= Pure
def isIdempotentExpr(tree: Tree)(implicit ctx: Context) = exprPurity(tree) >= Idempotent

def isPureBinding(tree: Tree)(implicit ctx: Context) = statPurity(tree) >= Pure

/** The purity level of this reference.
* @return
* SimplyPure if reference is (nonlazy and stable) or to a parameterized function
Expand Down Expand Up @@ -460,11 +456,11 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
* Strictly speaking we can't replace `O.x` with `42`. But this would make
* most expressions non-constant. Maybe we can change the spec to accept this
* kind of eliding behavior. Or else enforce true purity in the compiler.
* The choice will be affected by what we will do with `inline` and with
* The choice will be affected by what we will do with `transparent` and with
* Singleton type bounds (see SIP 23). Presumably
*
* object O1 { val x: Singleton = 42; println("43") }
* object O2 { inline val x = 42; println("43") }
* object O2 { transparent val x = 42; println("43") }
*
* should behave differently.
*
Expand All @@ -474,8 +470,8 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
*
* O2.x = 42
*
* Revisit this issue once we have implemented `inline`. Then we can demand
* purity of the prefix unless the selection goes to an inline val.
* Revisit this issue once we have standardized on `transparent`. Then we can demand
* purity of the prefix unless the selection goes to a transparent val.
*
* Note: This method should be applied to all term tree nodes that are not literals,
* that can be idempotent, and that can have constant types. So far, only nodes
Expand Down Expand Up @@ -585,11 +581,15 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>

/** An extractor for def of a closure contained the block of the closure. */
object closureDef {
def unapply(tree: Tree): Option[DefDef] = tree match {
case Block(Nil, expr) => unapply(expr)
def unapply(tree: Tree)(implicit ctx: Context): Option[DefDef] = tree match {
case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, closure: Closure) =>
Some(meth)
case _ => None
case Block(Nil, expr) =>
unapply(expr)
case Inlined(_, bindings, expr) if bindings.forall(isPureBinding) =>
unapply(expr)
case _ =>
None
}
}

Expand Down Expand Up @@ -710,6 +710,15 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
Nil
}

/** The qualifier part of a Select or Ident.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a less complete version of what tpd.desugarIdentPrefix does

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.

* For an Ident, this is the `This` of the current class.
*/
def qualifier(tree: Tree)(implicit ctx: Context) = tree match {
case Select(qual, _) => qual
case tree: Ident => desugarIdentPrefix(tree)
case _ => This(ctx.owner.enclosingClass.asClass)
}

/** Is this a selection of a member of a structural type that is not a member
* of an underlying class or trait?
*/
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import core.tasty.TreePickler.Hole

/** A map that applies three functions and a substitution together to a tree and
* makes sure they are coordinated so that the result is well-typed. The functions are
* @param typeMap A function from Type to Type that gets applied to the
* @param typeMap A function from Type to Type that gets applied to the
* type of every tree node and to all locally defined symbols,
* followed by the substitution [substFrom := substTo].
* @param treeMap A transformer that translates all encountered subtrees in
Expand Down Expand Up @@ -95,7 +95,7 @@ class TreeTypeMap(
val (tmap2, vparamss1) = tmap1.transformVParamss(vparamss)
val res = cpy.DefDef(ddef)(name, tparams1, vparamss1, tmap2.transform(tpt), tmap2.transform(ddef.rhs))
res.symbol.transformAnnotations {
case ann: BodyAnnotation => ann.derivedAnnotation(res.rhs)
case ann: BodyAnnotation => ann.derivedAnnotation(transform(ann.tree))
case ann => ann
}
res
Expand Down Expand Up @@ -126,7 +126,7 @@ class TreeTypeMap(
override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context) =
transformDefs(trees)._2

private def transformDefs[TT <: tpd.Tree](trees: List[TT])(implicit ctx: Context): (TreeTypeMap, List[TT]) = {
def transformDefs[TT <: tpd.Tree](trees: List[TT])(implicit ctx: Context): (TreeTypeMap, List[TT]) = {
val tmap = withMappedSyms(tpd.localSyms(trees))
(tmap, tmap.transformSub(trees))
}
Expand Down
62 changes: 52 additions & 10 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,7 @@ object Trees {
case class Inlined[-T >: Untyped] private[ast] (call: tpd.Tree, bindings: List[MemberDef[T]], expansion: Tree[T])
extends Tree[T] {
type ThisTree[-T >: Untyped] = Inlined[T]
override def initialPos = call.pos
}

/** A type tree that represents an existing or inferred type */
Expand Down Expand Up @@ -922,6 +923,11 @@ object Trees {
case ys => Thicket(ys)
}

/** Extractor for the synthetic scrutinee tree of an implicit match */
object ImplicitScrutinee {
def apply() = Ident(nme.IMPLICITkw)
def unapply(id: Ident): Boolean = id.name == nme.IMPLICITkw && !id.isInstanceOf[BackquotedIdent]
}
// ----- Helper classes for copying, transforming, accumulating -----------------

val cpy: TreeCopier
Expand Down Expand Up @@ -1109,6 +1115,10 @@ object Trees {
case tree: Annotated if (arg eq tree.arg) && (annot eq tree.annot) => tree
case _ => finalize(tree, untpd.Annotated(arg, annot))
}
def UntypedSplice(tree: Tree)(splice: untpd.Tree) = tree match {
case tree: tpd.UntypedSplice if tree.splice `eq` splice => tree
case _ => finalize(tree, tpd.UntypedSplice(splice))
}
def Thicket(tree: Tree)(trees: List[Tree]): Thicket = tree match {
case tree: Thicket if trees eq tree.trees => tree
case _ => finalize(tree, untpd.Thicket(trees))
Expand Down Expand Up @@ -1146,7 +1156,7 @@ object Trees {
*/
protected def inlineContext(call: Tree)(implicit ctx: Context): Context = ctx

abstract class TreeMap(val cpy: TreeCopier = inst.cpy) {
abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { self =>

def transform(tree: Tree)(implicit ctx: Context): Tree = {
Stats.record(s"TreeMap.transform $getClass")
Expand Down Expand Up @@ -1245,8 +1255,8 @@ object Trees {
case Thicket(trees) =>
val trees1 = transform(trees)
if (trees1 eq trees) tree else Thicket(trees1)
case _ if ctx.reporter.errorsReported =>
tree
case _ =>
transformMoreCases(tree)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there a specific reason to add it as an extra method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance. If we add a rare case, we only override transformMoreCases. Before the rare case was in the overridden transform. So every match had to first look at the rare cases before it would forward to super.transform.

}
}

Expand All @@ -1258,9 +1268,26 @@ object Trees {
transform(tree).asInstanceOf[Tr]
def transformSub[Tr <: Tree](trees: List[Tr])(implicit ctx: Context): List[Tr] =
transform(trees).asInstanceOf[List[Tr]]

protected def transformMoreCases(tree: Tree)(implicit ctx: Context): Tree = tree match {
case tpd.UntypedSplice(usplice) =>
// For a typed tree map: homomorphism on the untyped part with
// recursive mapping of typed splices.
// The case is overridden in UntypedTreeMap.##
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, why define this case here instead of in TypedTreeMap?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no TypedTreeMap and this addition did not seem sufficient reason to me to create one. Mostly because I was worried that creating more indirections in tree maps would hurt performance.

val untpdMap = new untpd.UntypedTreeMap {
override def transform(tree: untpd.Tree)(implicit ctx: Context): untpd.Tree = tree match {
case untpd.TypedSplice(tsplice) =>
untpd.cpy.TypedSplice(tree)(self.transform(tsplice).asInstanceOf[tpd.Tree])
// the cast is safe, since the UntypedSplice case is overridden in UntypedTreeMap.
case _ => super.transform(tree)
}
}
cpy.UntypedSplice(tree)(untpdMap.transform(usplice))
case _ if ctx.reporter.errorsReported => tree
}
}

abstract class TreeAccumulator[X] {
abstract class TreeAccumulator[X] { self =>
// Ties the knot of the traversal: call `foldOver(x, tree))` to dive in the `tree` node.
def apply(x: X, tree: Tree)(implicit ctx: Context): X

Expand Down Expand Up @@ -1355,14 +1382,29 @@ object Trees {
this(this(x, arg), annot)
case Thicket(ts) =>
this(x, ts)
case _ if ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive) =>
// In interactive mode, errors might come from previous runs.
// In case of errors it may be that typed trees point to untyped ones.
// The IDE can still traverse inside such trees, either in the run where errors
// are reported, or in subsequent ones.
x
case _ =>
foldMoreCases(x, tree)
}
}

def foldMoreCases(x: X, tree: Tree)(implicit ctx: Context): X = tree match {
case tpd.UntypedSplice(usplice) =>
// For a typed tree accumulator: skip the untyped part and fold all typed splices.
// The case is overridden in UntypedTreeAccumulator.
val untpdAcc = new untpd.UntypedTreeAccumulator[X] {
override def apply(x: X, tree: untpd.Tree)(implicit ctx: Context): X = tree match {
case untpd.TypedSplice(tsplice) => self(x, tsplice)
case _ => foldOver(x, tree)
}
}
untpdAcc(x, usplice)
case _ if ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive) =>
// In interactive mode, errors might come from previous runs.
// In case of errors it may be that typed trees point to untyped ones.
// The IDE can still traverse inside such trees, either in the run where errors
// are reported, or in subsequent ones.
x
}
}

abstract class TreeTraverser extends TreeAccumulator[Unit] {
Expand Down
41 changes: 33 additions & 8 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import scala.io.Codec
/** Some creators for typed trees */
object tpd extends Trees.Instance[Type] with TypedTreeInfo {

case class UntypedSplice(splice: untpd.Tree) extends Tree

private def ta(implicit ctx: Context) = ctx.typeAssigner

def Ident(tp: NamedType)(implicit ctx: Context): Ident =
Expand Down Expand Up @@ -249,8 +251,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
val constr = firstParent.decl(nme.CONSTRUCTOR).suchThat(constr => isApplicable(constr.info))
New(firstParent, constr.symbol.asTerm, superArgs)
}
val parents = superRef :: otherParents.map(TypeTree(_))
ClassDefWithParents(cls, constr, superRef :: otherParents.map(TypeTree(_)), body)
}

def ClassDefWithParents(cls: ClassSymbol, constr: DefDef, parents: List[Tree], body: List[Tree])(implicit ctx: Context): TypeDef = {
val selfType =
if (cls.classInfo.selfInfo ne NoType) ValDef(ctx.newSelfSym(cls))
else EmptyValDef
Expand Down Expand Up @@ -325,7 +329,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
case pre: ThisType =>
tp.isType ||
pre.cls.isStaticOwner ||
tp.symbol.isParamOrAccessor && !pre.cls.is(Trait) && ctx.owner.enclosingClass == pre.cls
tp.symbol.isParamOrAccessor && !pre.cls.is(Trait) && ctx.owner.enclosingClass == pre.cls
// was ctx.owner.enclosingClass.derivesFrom(pre.cls) which was not tight enough
// and was spuriously triggered in case inner class would inherit from outer one
// eg anonymous TypeMap inside TypeMap.andThen
Expand Down Expand Up @@ -659,7 +663,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {

override def skipTransform(tree: Tree)(implicit ctx: Context) = tree.tpe.isError

implicit class TreeOps[ThisTree <: tpd.Tree](val tree: ThisTree) extends AnyVal {
implicit class TreeOps[ThisTree <: tpd.Tree](private val tree: ThisTree) extends AnyVal {

def isValue(implicit ctx: Context): Boolean =
tree.isTerm && tree.tpe.widen.isValueType
Expand Down Expand Up @@ -1041,14 +1045,27 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
else (trees.head.tpe eq trees1.head.tpe) && sameTypes(trees.tail, trees1.tail)
}

def evalOnce(tree: Tree)(within: Tree => Tree)(implicit ctx: Context) = {
if (isIdempotentExpr(tree)) within(tree)
/** If `tree`'s purity level is less than `level`, let-bind it so that it gets evaluated
* only once. I.e. produce a
*
* { val x = 'tree ; ~within('x) }
*
* instead of otherwise
*
* ~within('tree)
*/
def letBindUnless(level: TreeInfo.PurityLevel, tree: Tree)(within: Tree => Tree)(implicit ctx: Context) = {
if (exprPurity(tree) >= level) within(tree)
else {
val vdef = SyntheticValDef(TempResultName.fresh(), tree)
Block(vdef :: Nil, within(Ident(vdef.namedType)))
}
}

/** Let bind `tree` unless `tree` is at least idempotent */
def evalOnce(tree: Tree)(within: Tree => Tree)(implicit ctx: Context) =
letBindUnless(TreeInfo.Idempotent, tree)(within)

def runtimeCall(name: TermName, args: List[Tree])(implicit ctx: Context): Tree = {
Ident(defn.ScalaRuntimeModule.requiredMethod(name).termRef).appliedToArgs(args)
}
Expand All @@ -1067,9 +1084,17 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
override def inlineContext(call: Tree)(implicit ctx: Context): Context =
ctx.fresh.setProperty(InlinedCalls, call :: enclosingInlineds)

/** All enclosing calls that are currently inlined, from innermost to outermost */
def enclosingInlineds(implicit ctx: Context): List[Tree] =
ctx.property(InlinedCalls).getOrElse(Nil)
/** All enclosing calls that are currently inlined, from innermost to outermost.
* EmptyTree calls cancel the next-enclosing non-empty call in the list
*/
def enclosingInlineds(implicit ctx: Context): List[Tree] = {
def normalize(ts: List[Tree]): List[Tree] = ts match {
case t :: (ts1 @ (t1 :: ts2)) if t.isEmpty => normalize(if (t1.isEmpty) ts1 else ts2)
case t :: ts1 => t :: normalize(ts1)
case Nil => Nil
}
normalize(ctx.property(InlinedCalls).getOrElse(Nil))
}

/** The source file where the symbol of the `inline` method referred to by `call`
* is defined
Expand Down
Loading