@@ -15,7 +15,7 @@ import StdNames.nme
15
15
import Contexts .Context
16
16
import Names .{Name , TermName , EmptyTermName }
17
17
import NameOps ._
18
- import NameKinds .{ClassifiedNameKind , InlineAccessorName }
18
+ import NameKinds .{ClassifiedNameKind , InlineAccessorName , UniqueName }
19
19
import ProtoTypes .selectionProto
20
20
import SymDenotations .SymDenotation
21
21
import Annotations ._
@@ -63,23 +63,13 @@ object Inliner {
63
63
// This is quite tricky, as such types can appear anywhere, including as parts
64
64
// of types of other things. For the moment we do nothing and complain
65
65
// at the implicit expansion site if there's a reference to an inaccessible type.
66
- override def transform (tree : Tree )(implicit ctx : Context ): Tree = tree match {
67
- case tree : Assign =>
68
- transform(tree.lhs) match {
69
- case lhs1 : RefTree =>
70
- lhs1.name match {
71
- case InlineAccessorName (name) =>
72
- cpy.Apply (tree)(useSetter(lhs1), transform(tree.rhs) :: Nil )
73
- case _ =>
74
- super .transform(tree)
75
- }
76
- case _ =>
77
- super .transform(tree)
78
- }
79
- case _ =>
80
- super .transform(accessorIfNeeded(tree))
81
- }
82
-
66
+ override def transform (tree : Tree )(implicit ctx : Context ): Tree =
67
+ super .transform(accessorIfNeeded(tree)) match {
68
+ case tree1 @ Assign (lhs : RefTree , rhs) if lhs.symbol.name.is(InlineAccessorName ) =>
69
+ cpy.Apply (tree1)(useSetter(lhs), rhs :: Nil )
70
+ case tree1 =>
71
+ tree1
72
+ }
83
73
}
84
74
85
75
/** Adds accessors for all non-public term members accessed
@@ -114,22 +104,134 @@ object Inliner {
114
104
* to have the inlined method as owner.
115
105
*/
116
106
def registerInlineInfo (
117
- sym : SymDenotation , originalBody : untpd.Tree , treeExpr : Context => Tree )(implicit ctx : Context ): Unit = {
118
- sym .unforcedAnnotation(defn.BodyAnnot ) match {
107
+ inlined : Symbol , originalBody : untpd.Tree , treeExpr : Context => Tree )(implicit ctx : Context ): Unit = {
108
+ inlined .unforcedAnnotation(defn.BodyAnnot ) match {
119
109
case Some (ann : ConcreteBodyAnnotation ) =>
120
110
case Some (ann : LazyBodyAnnotation ) if ann.isEvaluated =>
121
111
case _ =>
122
112
if (! ctx.isAfterTyper) {
123
113
val inlineCtx = ctx
124
- sym .updateAnnotation(LazyBodyAnnotation { _ =>
114
+ inlined .updateAnnotation(LazyBodyAnnotation { _ =>
125
115
implicit val ctx = inlineCtx
126
- val body = treeExpr(ctx)
127
- if (ctx.reporter.hasErrors) body else ctx.compilationUnit.inlineAccessors.makeInlineable(body)
116
+ val rawBody = treeExpr(ctx)
117
+ val typedBody =
118
+ if (ctx.reporter.hasErrors) rawBody
119
+ else ctx.compilationUnit.inlineAccessors.makeInlineable(rawBody)
120
+ val inlineableBody =
121
+ if (inlined.isInlinedMethod) typedBody
122
+ else addReferences(inlined, originalBody, typedBody)
123
+ inlining.println(i " Body to inline for $inlined: $inlineableBody" )
124
+ inlineableBody
128
125
})
129
126
}
130
127
}
131
128
}
132
129
130
+ /** Tweak untyped tree `original` so that all external references are typed
131
+ * and it reflects the changes in the corresponding typed tree `typed` that
132
+ * make `typed` inlineable. Concretely:
133
+ *
134
+ * - all external references via identifiers or this-references are converted
135
+ * to typed splices,
136
+ * - if X gets an inline accessor in `typed`, references to X in `original`
137
+ * are converted to the inline accessor name.
138
+ */
139
+ def addReferences (inlineMethod : Symbol ,
140
+ original : untpd.Tree , typed : tpd.Tree )(implicit ctx : Context ): tpd.Tree = {
141
+
142
+ def isExternal (sym : Symbol ) = sym.exists && ! isLocal(sym, inlineMethod)
143
+
144
+ // Maps from positions to external reference types and inline selector names.
145
+ object referenced extends TreeTraverser {
146
+ val typeAtPos = mutable.Map [Position , Type ]()
147
+ val nameAtPos = mutable.Map [Position , Name ]()
148
+ val implicitSyms = mutable.Set [Symbol ]()
149
+ val implicitRefs = new mutable.ListBuffer [Tree ]
150
+ def registerIfContextualImplicit (tree : Tree ) = tree match {
151
+ case tree : RefTree
152
+ if tree.removeAttachment(ContextualImplicit ).isDefined &&
153
+ isExternal(tree.symbol) &&
154
+ ! implicitSyms.contains(tree.symbol) =>
155
+ if (tree.existsSubTree(t => isLocal(tree.symbol, inlineMethod)))
156
+ ctx.warning(" implicit reference $tree is dropped at inline site because it refers to local symbol(s)" , tree.pos)
157
+ else {
158
+ implicitSyms += tree.symbol
159
+ implicitRefs += tree
160
+ }
161
+ case _ =>
162
+ }
163
+ def traverse (tree : Tree )(implicit ctx : Context ): Unit = {
164
+ tree match {
165
+ case _ : Ident | _ : This =>
166
+ // println(i"leaf: $tree at ${tree.pos}")
167
+ if (isExternal(tree.symbol)) {
168
+ inlining.println(i " type at pos ${tree.pos.toSynthetic} = ${tree.tpe}" )
169
+ typeAtPos(tree.pos.toSynthetic) = tree.tpe
170
+ }
171
+ case _ : Select if tree.symbol.name.is(InlineAccessorName ) =>
172
+ inlining.println(i " accessor: $tree at ${tree.pos}" )
173
+ nameAtPos(tree.pos.toSynthetic) = tree.symbol.name
174
+ case _ =>
175
+ }
176
+ registerIfContextualImplicit(tree)
177
+ traverseChildren(tree)
178
+ }
179
+ }
180
+ referenced.traverse(typed)
181
+
182
+ // The untyped tree transform that applies the tweaks
183
+ object addRefs extends untpd.UntypedTreeMap {
184
+ override def transform (tree : untpd.Tree )(implicit ctx : Context ): untpd.Tree = {
185
+ def adjustLeaf (tree : untpd.Tree ): untpd.Tree = referenced.typeAtPos.get(tree.pos.toSynthetic) match {
186
+ case Some (tpe) => untpd.TypedSplice (tree.withType(tpe))
187
+ case none => tree
188
+ }
189
+ def adjustName (name : Name ) = referenced.nameAtPos.get(tree.pos.toSynthetic) match {
190
+ case Some (n) => n
191
+ case none => name
192
+ }
193
+ def adjustQualifier (tree : untpd.Tree ): untpd.Tree = tree match {
194
+ case tree @ Ident (name1) =>
195
+ referenced.typeAtPos.get(tree.pos.startPos) match {
196
+ case Some (tp : ThisType ) =>
197
+ val qual = untpd.TypedSplice (This (tp.cls).withPos(tree.pos.startPos))
198
+ cpy.Select (tree)(qual, name1)
199
+ case none =>
200
+ tree
201
+ }
202
+ case tree => tree
203
+ }
204
+ val tree1 = super .transform(tree)
205
+ tree1 match {
206
+ case This (_) =>
207
+ adjustLeaf(tree1)
208
+ case Ident (name) =>
209
+ adjustQualifier(adjustLeaf(cpy.Ident (tree1)(adjustName(name))))
210
+ case Select (pre, name) =>
211
+ cpy.Select (tree1)(pre, adjustName(name))
212
+ case tree : untpd.DerivedTypeTree =>
213
+ inlining.println(i " inlining derived $tree --> ${ctx.typer.typed(tree)}" )
214
+ untpd.TypedSplice (ctx.typer.typed(tree))
215
+ case _ =>
216
+ tree1
217
+ }
218
+ }
219
+ }
220
+ val implicitBindings =
221
+ for (iref <- referenced.implicitRefs.toList) yield {
222
+ val localImplicit = iref.symbol.asTerm.copy(
223
+ owner = inlineMethod,
224
+ name = UniqueName .fresh(iref.symbol.name.asTermName),
225
+ flags = Implicit | Method | Stable ,
226
+ info = iref.symbol.info.ensureMethodic,
227
+ coord = inlineMethod.pos).asTerm
228
+ polyDefDef(localImplicit, tps => vrefss =>
229
+ iref.appliedToTypes(tps).appliedToArgss(vrefss))
230
+ }
231
+ val untpdSplice = tpd.UntypedSplice (addRefs.transform(original)).withType(typed.tpe)
232
+ seq(implicitBindings, untpdSplice)
233
+ }
234
+
133
235
/** `sym` has an inline method with a known body to inline (note: definitions coming
134
236
* from Scala2x class files might be `@inline`, but still lack that body.
135
237
*/
@@ -391,17 +493,38 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
391
493
case _ => tree
392
494
}}
393
495
394
- val inlineCtx = inlineContext(call)
496
+ val inlineTyper = if (meth.isTransparentMethod) new InlineTyper else new InlineReTyper
497
+ val inlineCtx = inlineContext(call).fresh.setTyper(inlineTyper).setNewScope
498
+
395
499
// The complete translation maps references to `this` and parameters to
396
500
// corresponding arguments or proxies on the type and term level. It also changes
397
501
// the owner from the inlined method to the current owner.
398
502
val inliner = new TreeTypeMap (typeMap, treeMap, meth :: Nil , ctx.owner :: Nil )(inlineCtx)
399
503
400
- val expansion = inliner(rhsToInline.withPos(call.pos))
504
+ val expansion = inliner.transform(rhsToInline.withPos(call.pos)) match {
505
+ case Block (implicits, tpd.UntypedSplice (expansion)) =>
506
+ val prevOwners = implicits.map(_.symbol.owner).distinct
507
+ val localizer = new TreeTypeMap (oldOwners = prevOwners, newOwners = prevOwners.map(_ => ctx.owner))
508
+ val (_, implicits1) = localizer.transformDefs(implicits)
509
+ for (idef <- implicits1) {
510
+ bindingsBuf += idef.withType(idef.symbol.typeRef).asInstanceOf [ValOrDefDef ]
511
+ // Note: Substituting new symbols does not automatically lead to good prefixes
512
+ // if the previous symbol was owned by a class. That's why we need to set the type
513
+ // of `idef` explicitly. It would be nice if substituters were smarter, but
514
+ // it seems non-trivial to come up with rules that work in
515
+ inlineCtx.enter(idef.symbol)
516
+ }
517
+ expansion
518
+ case tpd.UntypedSplice (expansion) =>
519
+ expansion
520
+ case expansion =>
521
+ expansion
522
+ }
523
+
401
524
trace(i " inlining $call\n , BINDINGS = \n ${bindingsBuf.toList}% \n % \n EXPANSION = \n $expansion" , inlining, show = true ) {
402
525
403
526
// The final expansion runs a typing pass over the inlined tree. See InlineTyper for details.
404
- val expansion1 = InlineTyper .typed(expansion, pt)(inlineCtx)
527
+ val expansion1 = inlineTyper .typed(expansion, pt)(inlineCtx)
405
528
406
529
/** All bindings in `bindingsBuf` except bindings of inlineable closures */
407
530
val bindings = bindingsBuf.toList.map(_.withPos(call.pos))
@@ -421,8 +544,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
421
544
*/
422
545
private object InlineableArg {
423
546
lazy val paramProxies = paramProxy.values.toSet
424
- def unapply (tree : Ident )(implicit ctx : Context ): Option [Tree ] =
425
- if (paramProxies.contains(tree.tpe ))
547
+ def unapply (tree : Trees . Ident [_] )(implicit ctx : Context ): Option [Tree ] =
548
+ if (paramProxies.contains(tree.typeOpt ))
426
549
bindingsBuf.find(_.name == tree.name) match {
427
550
case Some (vdef : ValDef ) if vdef.symbol.is(Inline ) =>
428
551
Some (vdef.rhs.changeOwner(vdef.symbol, ctx.owner))
@@ -433,46 +556,24 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
433
556
else None
434
557
}
435
558
436
- /** A typer for inlined code. Its purpose is:
559
+ /** A base trait of InlineTyper and InlineReTyper containing operations that
560
+ * work the same way for both. Beyond typing or retyping, an inline typer performs
561
+ * the following functions:
562
+ *
437
563
* 1. Implement constant folding over inlined code
438
564
* 2. Selectively expand ifs with constant conditions
439
- * 3. Inline arguments that are inlineable closures
565
+ * 3. Inline arguments that are by-name closures
440
566
* 4. Make sure inlined code is type-correct.
441
567
* 5. Make sure that the tree's typing is idempotent (so that future -Ycheck passes succeed)
442
568
*/
443
- private object InlineTyper extends ReTyper {
444
-
445
- override def ensureAccessible (tpe : Type , superAccess : Boolean , pos : Position )(implicit ctx : Context ): Type = {
446
- tpe match {
447
- case tpe @ TypeRef (pre, _) if ! tpe.symbol.isAccessibleFrom(pre, superAccess) =>
448
- tpe.info match {
449
- case TypeAlias (alias) => return ensureAccessible(alias, superAccess, pos)
450
- case _ =>
451
- }
452
- case _ =>
453
- }
454
- super .ensureAccessible(tpe, superAccess, pos)
455
- }
456
-
457
- override def typedIdent (tree : untpd.Ident , pt : Type )(implicit ctx : Context ) =
458
- tree.asInstanceOf [tpd.Tree ] match {
459
- case InlineableArg (rhs) => inlining.println(i " inline arg $tree -> $rhs" ); rhs
460
- case _ => super .typedIdent(tree, pt)
461
- }
462
-
463
- override def typedSelect (tree : untpd.Select , pt : Type )(implicit ctx : Context ): Tree = {
464
- assert(tree.hasType, tree)
465
- val qual1 = typed(tree.qualifier, selectionProto(tree.name, pt, this ))
466
- val res = untpd.cpy.Select (tree)(qual1, tree.name).withType(tree.typeOpt)
467
- ensureAccessible(res.tpe, tree.qualifier.isInstanceOf [untpd.Super ], tree.pos)
468
- res
469
- }
569
+ trait InlineTyping extends Typer {
470
570
471
571
override def typedIf (tree : untpd.If , pt : Type )(implicit ctx : Context ) = {
472
572
val cond1 = typed(tree.cond, defn.BooleanType )
473
573
cond1.tpe.widenTermRefExpr match {
474
574
case ConstantType (Constant (condVal : Boolean )) =>
475
- val selected = typed(if (condVal) tree.thenp else tree.elsep, pt)
575
+ var selected = typed(if (condVal) tree.thenp else tree.elsep, pt)
576
+ if (selected.isEmpty) selected = tpd.Literal (Constant (()))
476
577
if (isIdempotentExpr(cond1)) selected
477
578
else Block (cond1 :: Nil , selected)
478
579
case _ =>
@@ -509,6 +610,48 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
509
610
}
510
611
}
511
612
613
+ /** A full typer used for transparent methods */
614
+ private class InlineTyper extends Typer with InlineTyping {
615
+
616
+ override def typedTypedSplice (tree : untpd.TypedSplice )(implicit ctx : Context ): Tree =
617
+ tree.splice match {
618
+ case InlineableArg (rhs) => inlining.println(i " inline arg $tree -> $rhs" ); rhs
619
+ case _ => super .typedTypedSplice(tree)
620
+ }
621
+ }
622
+
623
+ /** A re-typer used for inlined methods */
624
+ private class InlineReTyper extends ReTyper with InlineTyping {
625
+
626
+ override def ensureAccessible (tpe : Type , superAccess : Boolean , pos : Position )(implicit ctx : Context ): Type = {
627
+ tpe match {
628
+ case tpe @ TypeRef (pre, _) if ! tpe.symbol.isAccessibleFrom(pre, superAccess) =>
629
+ tpe.info match {
630
+ case TypeAlias (alias) => return ensureAccessible(alias, superAccess, pos)
631
+ case _ =>
632
+ }
633
+ case _ =>
634
+ }
635
+ super .ensureAccessible(tpe, superAccess, pos)
636
+ }
637
+
638
+ override def typedIdent (tree : untpd.Ident , pt : Type )(implicit ctx : Context ) =
639
+ tree.asInstanceOf [tpd.Tree ] match {
640
+ case InlineableArg (rhs) => inlining.println(i " inline arg $tree -> $rhs" ); rhs
641
+ case _ => super .typedIdent(tree, pt)
642
+ }
643
+
644
+ override def typedSelect (tree : untpd.Select , pt : Type )(implicit ctx : Context ): Tree = {
645
+ assert(tree.hasType, tree)
646
+ val qual1 = typed(tree.qualifier, selectionProto(tree.name, pt, this ))
647
+ val res = untpd.cpy.Select (tree)(qual1, tree.name).withType(tree.typeOpt)
648
+ ensureAccessible(res.tpe, tree.qualifier.isInstanceOf [untpd.Super ], tree.pos)
649
+ res
650
+ }
651
+
652
+ override def newLikeThis : Typer = new InlineReTyper
653
+ }
654
+
512
655
/** Drop any side-effect-free bindings that are unused in expansion or other reachable bindings.
513
656
* Inline def bindings that are used only once.
514
657
*/
0 commit comments