Skip to content

Commit 36c2e34

Browse files
authored
Merge pull request scala#4616 from dotty-staging/add-transparent
Add transparent methods - untyped trees version
2 parents 6240daa + 88e7d18 commit 36c2e34

File tree

283 files changed

+4404
-1586
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

283 files changed

+4404
-1586
lines changed

bench/tests/power-macro/PowerMacro.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import scala.quoted.Expr
22

33
object PowerMacro {
44

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

77
def powerCode(n: Long, x: Expr[Double]): Expr[Double] =
88
if (n == 0) '(1.0)

compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
433433
val Flag_METHOD: Flags = Flags.Method.bits
434434
val ExcludedForwarderFlags: Flags = {
435435
Flags.Specialized | Flags.Lifted | Flags.Protected | Flags.JavaStatic |
436-
Flags.Bridge | Flags.VBridge | Flags.Private | Flags.Macro
436+
Flags.Bridge | Flags.Private | Flags.Macro
437437
}.bits
438438

439439
def isQualifierSafeToElide(qual: Tree): Boolean = tpd.isIdempotentExpr(qual)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import core.Types.Type // Do not remove me #3383
55
import util.SourceFile
66
import ast.{tpd, untpd}
77
import tpd.{ Tree, TreeTraverser }
8-
import typer.Inliner.InlineAccessors
8+
import typer.PrepareTransparent.InlineAccessors
99
import dotty.tools.dotc.core.Contexts.Context
1010
import dotty.tools.dotc.core.SymDenotations.ClassDenotation
1111
import dotty.tools.dotc.core.Symbols._

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,7 @@ class Compiler {
8080
new StringInterpolatorOpt, // Optimizes raw and s string interpolators by rewriting them to string concatentations
8181
new CrossCastAnd, // Normalize selections involving intersection types.
8282
new Splitter) :: // Expand selections involving union types into conditionals
83-
List(new ErasedDecls, // Removes all erased defs and vals decls (except for parameters)
84-
new IsInstanceOfChecker, // check runtime realisability for `isInstanceOf`
83+
List(new PruneErasedDefs, // Drop erased definitions from scopes and simplify erased expressions
8584
new VCInlineMethods, // Inlines calls to value class methods
8685
new SeqLiterals, // Express vararg arguments as arrays
8786
new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods
@@ -119,7 +118,6 @@ class Compiler {
119118
new SelectStatic, // get rid of selects that would be compiled into GetStatic
120119
new CollectEntryPoints, // Find classes with main methods
121120
new CollectSuperCalls, // Find classes that are called with super
122-
new DropInlined, // Drop Inlined nodes, since backend has no use for them
123121
new LabelDefs) :: // Converts calls to labels to jumps
124122
Nil
125123

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -391,12 +391,6 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
391391
SimplyPure
392392
case TypeApply(fn, _) =>
393393
exprPurity(fn)
394-
/*
395-
* Not sure we'll need that. Comment out until we find out
396-
case Apply(Select(free @ Ident(_), nme.apply), _) if free.symbol.name endsWith nme.REIFY_FREE_VALUE_SUFFIX =>
397-
// see a detailed explanation of this trick in `GenSymbols.reifyFreeTerm`
398-
free.symbol.hasStableFlag && isIdempotentExpr(free)
399-
*/
400394
case Apply(fn, args) =>
401395
def isKnownPureOp(sym: Symbol) =
402396
sym.owner.isPrimitiveValueClass || sym.owner == defn.StringClass
@@ -424,6 +418,8 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
424418
def isPureExpr(tree: Tree)(implicit ctx: Context) = exprPurity(tree) >= Pure
425419
def isIdempotentExpr(tree: Tree)(implicit ctx: Context) = exprPurity(tree) >= Idempotent
426420

421+
def isPureBinding(tree: Tree)(implicit ctx: Context) = statPurity(tree) >= Pure
422+
427423
/** The purity level of this reference.
428424
* @return
429425
* SimplyPure if reference is (nonlazy and stable) or to a parameterized function
@@ -460,11 +456,11 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
460456
* Strictly speaking we can't replace `O.x` with `42`. But this would make
461457
* most expressions non-constant. Maybe we can change the spec to accept this
462458
* kind of eliding behavior. Or else enforce true purity in the compiler.
463-
* The choice will be affected by what we will do with `inline` and with
459+
* The choice will be affected by what we will do with `transparent` and with
464460
* Singleton type bounds (see SIP 23). Presumably
465461
*
466462
* object O1 { val x: Singleton = 42; println("43") }
467-
* object O2 { inline val x = 42; println("43") }
463+
* object O2 { transparent val x = 42; println("43") }
468464
*
469465
* should behave differently.
470466
*
@@ -474,8 +470,8 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
474470
*
475471
* O2.x = 42
476472
*
477-
* Revisit this issue once we have implemented `inline`. Then we can demand
478-
* purity of the prefix unless the selection goes to an inline val.
473+
* Revisit this issue once we have standardized on `transparent`. Then we can demand
474+
* purity of the prefix unless the selection goes to a transparent val.
479475
*
480476
* Note: This method should be applied to all term tree nodes that are not literals,
481477
* that can be idempotent, and that can have constant types. So far, only nodes
@@ -585,11 +581,15 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
585581

586582
/** An extractor for def of a closure contained the block of the closure. */
587583
object closureDef {
588-
def unapply(tree: Tree): Option[DefDef] = tree match {
589-
case Block(Nil, expr) => unapply(expr)
584+
def unapply(tree: Tree)(implicit ctx: Context): Option[DefDef] = tree match {
590585
case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, closure: Closure) =>
591586
Some(meth)
592-
case _ => None
587+
case Block(Nil, expr) =>
588+
unapply(expr)
589+
case Inlined(_, bindings, expr) if bindings.forall(isPureBinding) =>
590+
unapply(expr)
591+
case _ =>
592+
None
593593
}
594594
}
595595

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

713+
/** The qualifier part of a Select or Ident.
714+
* For an Ident, this is the `This` of the current class.
715+
*/
716+
def qualifier(tree: Tree)(implicit ctx: Context) = tree match {
717+
case Select(qual, _) => qual
718+
case tree: Ident => desugarIdentPrefix(tree)
719+
case _ => This(ctx.owner.enclosingClass.asClass)
720+
}
721+
713722
/** Is this a selection of a member of a structural type that is not a member
714723
* of an underlying class or trait?
715724
*/

compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import core.tasty.TreePickler.Hole
1111

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

129-
private def transformDefs[TT <: tpd.Tree](trees: List[TT])(implicit ctx: Context): (TreeTypeMap, List[TT]) = {
129+
def transformDefs[TT <: tpd.Tree](trees: List[TT])(implicit ctx: Context): (TreeTypeMap, List[TT]) = {
130130
val tmap = withMappedSyms(tpd.localSyms(trees))
131131
(tmap, tmap.transformSub(trees))
132132
}

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,7 @@ object Trees {
589589
case class Inlined[-T >: Untyped] private[ast] (call: tpd.Tree, bindings: List[MemberDef[T]], expansion: Tree[T])
590590
extends Tree[T] {
591591
type ThisTree[-T >: Untyped] = Inlined[T]
592+
override def initialPos = call.pos
592593
}
593594

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

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

927933
val cpy: TreeCopier
@@ -1109,6 +1115,10 @@ object Trees {
11091115
case tree: Annotated if (arg eq tree.arg) && (annot eq tree.annot) => tree
11101116
case _ => finalize(tree, untpd.Annotated(arg, annot))
11111117
}
1118+
def UntypedSplice(tree: Tree)(splice: untpd.Tree) = tree match {
1119+
case tree: tpd.UntypedSplice if tree.splice `eq` splice => tree
1120+
case _ => finalize(tree, tpd.UntypedSplice(splice))
1121+
}
11121122
def Thicket(tree: Tree)(trees: List[Tree]): Thicket = tree match {
11131123
case tree: Thicket if trees eq tree.trees => tree
11141124
case _ => finalize(tree, untpd.Thicket(trees))
@@ -1146,7 +1156,7 @@ object Trees {
11461156
*/
11471157
protected def inlineContext(call: Tree)(implicit ctx: Context): Context = ctx
11481158

1149-
abstract class TreeMap(val cpy: TreeCopier = inst.cpy) {
1159+
abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { self =>
11501160

11511161
def transform(tree: Tree)(implicit ctx: Context): Tree = {
11521162
Stats.record(s"TreeMap.transform $getClass")
@@ -1245,8 +1255,8 @@ object Trees {
12451255
case Thicket(trees) =>
12461256
val trees1 = transform(trees)
12471257
if (trees1 eq trees) tree else Thicket(trees1)
1248-
case _ if ctx.reporter.errorsReported =>
1249-
tree
1258+
case _ =>
1259+
transformMoreCases(tree)
12501260
}
12511261
}
12521262

@@ -1258,9 +1268,26 @@ object Trees {
12581268
transform(tree).asInstanceOf[Tr]
12591269
def transformSub[Tr <: Tree](trees: List[Tr])(implicit ctx: Context): List[Tr] =
12601270
transform(trees).asInstanceOf[List[Tr]]
1271+
1272+
protected def transformMoreCases(tree: Tree)(implicit ctx: Context): Tree = tree match {
1273+
case tpd.UntypedSplice(usplice) =>
1274+
// For a typed tree map: homomorphism on the untyped part with
1275+
// recursive mapping of typed splices.
1276+
// The case is overridden in UntypedTreeMap.##
1277+
val untpdMap = new untpd.UntypedTreeMap {
1278+
override def transform(tree: untpd.Tree)(implicit ctx: Context): untpd.Tree = tree match {
1279+
case untpd.TypedSplice(tsplice) =>
1280+
untpd.cpy.TypedSplice(tree)(self.transform(tsplice).asInstanceOf[tpd.Tree])
1281+
// the cast is safe, since the UntypedSplice case is overridden in UntypedTreeMap.
1282+
case _ => super.transform(tree)
1283+
}
1284+
}
1285+
cpy.UntypedSplice(tree)(untpdMap.transform(usplice))
1286+
case _ if ctx.reporter.errorsReported => tree
1287+
}
12611288
}
12621289

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

@@ -1355,14 +1382,29 @@ object Trees {
13551382
this(this(x, arg), annot)
13561383
case Thicket(ts) =>
13571384
this(x, ts)
1358-
case _ if ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive) =>
1359-
// In interactive mode, errors might come from previous runs.
1360-
// In case of errors it may be that typed trees point to untyped ones.
1361-
// The IDE can still traverse inside such trees, either in the run where errors
1362-
// are reported, or in subsequent ones.
1363-
x
1385+
case _ =>
1386+
foldMoreCases(x, tree)
13641387
}
13651388
}
1389+
1390+
def foldMoreCases(x: X, tree: Tree)(implicit ctx: Context): X = tree match {
1391+
case tpd.UntypedSplice(usplice) =>
1392+
// For a typed tree accumulator: skip the untyped part and fold all typed splices.
1393+
// The case is overridden in UntypedTreeAccumulator.
1394+
val untpdAcc = new untpd.UntypedTreeAccumulator[X] {
1395+
override def apply(x: X, tree: untpd.Tree)(implicit ctx: Context): X = tree match {
1396+
case untpd.TypedSplice(tsplice) => self(x, tsplice)
1397+
case _ => foldOver(x, tree)
1398+
}
1399+
}
1400+
untpdAcc(x, usplice)
1401+
case _ if ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive) =>
1402+
// In interactive mode, errors might come from previous runs.
1403+
// In case of errors it may be that typed trees point to untyped ones.
1404+
// The IDE can still traverse inside such trees, either in the run where errors
1405+
// are reported, or in subsequent ones.
1406+
x
1407+
}
13661408
}
13671409

13681410
abstract class TreeTraverser extends TreeAccumulator[Unit] {

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import scala.io.Codec
2121
/** Some creators for typed trees */
2222
object tpd extends Trees.Instance[Type] with TypedTreeInfo {
2323

24+
case class UntypedSplice(splice: untpd.Tree) extends Tree
25+
2426
private def ta(implicit ctx: Context) = ctx.typeAssigner
2527

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

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

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

662-
implicit class TreeOps[ThisTree <: tpd.Tree](val tree: ThisTree) extends AnyVal {
666+
implicit class TreeOps[ThisTree <: tpd.Tree](private val tree: ThisTree) extends AnyVal {
663667

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

1044-
def evalOnce(tree: Tree)(within: Tree => Tree)(implicit ctx: Context) = {
1045-
if (isIdempotentExpr(tree)) within(tree)
1048+
/** If `tree`'s purity level is less than `level`, let-bind it so that it gets evaluated
1049+
* only once. I.e. produce a
1050+
*
1051+
* { val x = 'tree ; ~within('x) }
1052+
*
1053+
* instead of otherwise
1054+
*
1055+
* ~within('tree)
1056+
*/
1057+
def letBindUnless(level: TreeInfo.PurityLevel, tree: Tree)(within: Tree => Tree)(implicit ctx: Context) = {
1058+
if (exprPurity(tree) >= level) within(tree)
10461059
else {
10471060
val vdef = SyntheticValDef(TempResultName.fresh(), tree)
10481061
Block(vdef :: Nil, within(Ident(vdef.namedType)))
10491062
}
10501063
}
10511064

1065+
/** Let bind `tree` unless `tree` is at least idempotent */
1066+
def evalOnce(tree: Tree)(within: Tree => Tree)(implicit ctx: Context) =
1067+
letBindUnless(TreeInfo.Idempotent, tree)(within)
1068+
10521069
def runtimeCall(name: TermName, args: List[Tree])(implicit ctx: Context): Tree = {
10531070
Ident(defn.ScalaRuntimeModule.requiredMethod(name).termRef).appliedToArgs(args)
10541071
}
@@ -1067,9 +1084,17 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
10671084
override def inlineContext(call: Tree)(implicit ctx: Context): Context =
10681085
ctx.fresh.setProperty(InlinedCalls, call :: enclosingInlineds)
10691086

1070-
/** All enclosing calls that are currently inlined, from innermost to outermost */
1071-
def enclosingInlineds(implicit ctx: Context): List[Tree] =
1072-
ctx.property(InlinedCalls).getOrElse(Nil)
1087+
/** All enclosing calls that are currently inlined, from innermost to outermost.
1088+
* EmptyTree calls cancel the next-enclosing non-empty call in the list
1089+
*/
1090+
def enclosingInlineds(implicit ctx: Context): List[Tree] = {
1091+
def normalize(ts: List[Tree]): List[Tree] = ts match {
1092+
case t :: (ts1 @ (t1 :: ts2)) if t.isEmpty => normalize(if (t1.isEmpty) ts1 else ts2)
1093+
case t :: ts1 => t :: normalize(ts1)
1094+
case Nil => Nil
1095+
}
1096+
normalize(ctx.property(InlinedCalls).getOrElse(Nil))
1097+
}
10731098

10741099
/** The source file where the symbol of the `inline` method referred to by `call`
10751100
* is defined

0 commit comments

Comments
 (0)