Skip to content

Commit d360ac0

Browse files
authored
Merge pull request #3205 from dotty-staging/fix-#3130
Fix #3130: Various fixes to inline
2 parents 54b170e + 03511d1 commit d360ac0

File tree

13 files changed

+160
-21
lines changed

13 files changed

+160
-21
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ class Compiler {
7575
new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods
7676
new Getters, // Replace non-private vals and vars with getter defs (fields are added later)
7777
new ElimByName, // Expand by-name parameter references
78+
new ElimOuterSelect, // Expand outer selections
7879
new AugmentScala2Traits, // Expand traits defined in Scala 2.x to simulate old-style rewritings
7980
new ResolveSuper, // Implement super accessors and add forwarders to trait methods
8081
new Simplify, // Perform local optimizations, simplified versions of what linker does.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ final class TreeTypeMap(
8080
val tmap = withMappedSyms(localSyms(impl :: self :: Nil))
8181
cpy.Template(impl)(
8282
constr = tmap.transformSub(constr),
83-
parents = parents mapconserve transform,
83+
parents = parents.mapconserve(transform),
8484
self = tmap.transformSub(self),
8585
body = impl.body mapconserve
8686
(tmap.transform(_)(ctx.withOwner(mapOwner(impl.symbol.owner))))

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -826,7 +826,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
826826
* @param tp The type of the destination of the outer path.
827827
*/
828828
def outerSelect(levels: Int, tp: Type)(implicit ctx: Context): Tree =
829-
untpd.Select(tree, OuterSelectName(EmptyTermName, levels)).withType(tp)
829+
untpd.Select(tree, OuterSelectName(EmptyTermName, levels)).withType(SkolemType(tp))
830830

831831
// --- Higher order traversal methods -------------------------------
832832

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3948,6 +3948,17 @@ object Types {
39483948
protected def mapClassInfo(tp: ClassInfo): Type =
39493949
derivedClassInfo(tp, this(tp.prefix))
39503950

3951+
/** A version of mapClassInfo which also maps parents and self type */
3952+
protected def mapFullClassInfo(tp: ClassInfo) =
3953+
tp.derivedClassInfo(
3954+
prefix = this(tp.prefix),
3955+
classParents = tp.classParents.mapConserve(this),
3956+
selfInfo = tp.selfInfo match {
3957+
case tp: Type => this(tp)
3958+
case sym => sym
3959+
}
3960+
)
3961+
39513962
def andThen(f: Type => Type): TypeMap = new TypeMap {
39523963
override def stopAtStatic = thisMap.stopAtStatic
39533964
def apply(tp: Type) = f(thisMap(tp))
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import core._
5+
import TreeTransforms.{MiniPhaseTransform, TransformerInfo}
6+
import Contexts.Context
7+
import Types._
8+
import Decorators._
9+
import NameKinds.OuterSelectName
10+
import ast.Trees._
11+
12+
/** This phase rewrites outer selects `E.n_<outer>` which were introduced by
13+
* inlining to outer paths.
14+
*/
15+
class ElimOuterSelect extends MiniPhaseTransform { thisTransform =>
16+
import ast.tpd._
17+
18+
override def phaseName: String = "elimOuterSelect"
19+
20+
override def runsAfterGroupsOf = Set(classOf[ExplicitOuter])
21+
// ExplicitOuter needs to have run to completion before so that all classes
22+
// that need an outer accessor have one.
23+
24+
/** Convert a selection of the form `qual.n_<outer>` to an outer path from `qual` of
25+
* length `n`.
26+
*/
27+
override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) =
28+
tree.name match {
29+
case OuterSelectName(_, nhops) =>
30+
val SkolemType(tp) = tree.tpe
31+
ExplicitOuter.outer.path(start = tree.qualifier, count = nhops).ensureConforms(tp)
32+
case _ => tree
33+
}
34+
}

compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import core.Decorators._
1111
import core.StdNames.nme
1212
import core.Names._
1313
import core.NameOps._
14-
import core.NameKinds.OuterSelectName
1514
import ast.Trees._
1615
import SymUtils._
1716
import dotty.tools.dotc.ast.tpd
@@ -62,14 +61,6 @@ class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransf
6261

6362
override def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym.isClass
6463

65-
/** Convert a selection of the form `qual.C_<OUTER>` to an outer path from `qual` to `C` */
66-
override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) =
67-
tree.name match {
68-
case OuterSelectName(_, nhops) =>
69-
outer.path(start = tree.qualifier, count = nhops).ensureConforms(tree.tpe)
70-
case _ => tree
71-
}
72-
7364
/** First, add outer accessors if a class does not have them yet and it references an outer this.
7465
* If the class has outer accessors, implement them.
7566
* Furthermore, if a parent trait might have an outer accessor,

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,21 @@ object Inliner {
3939
*/
4040
private def makeInlineable(tree: Tree)(implicit ctx: Context) = {
4141

42+
val inlineMethod = ctx.owner
43+
4244
/** A tree map which inserts accessors for all non-public term members accessed
43-
* from inlined code. Accesors are collected in the `accessors` buffer.
45+
* from inlined code. Accessors are collected in the `accessors` buffer.
4446
*/
4547
object addAccessors extends TreeMap {
46-
val inlineMethod = ctx.owner
4748
val accessors = new mutable.ListBuffer[MemberDef]
4849

49-
/** A definition needs an accessor if it is private, protected, or qualified private */
50+
/** A definition needs an accessor if it is private, protected, or qualified private
51+
* and it is not part of the tree that gets inlined. The latter test is implemented
52+
* by excluding all symbols properly contained in the inlined method.
53+
*/
5054
def needsAccessor(sym: Symbol)(implicit ctx: Context) =
51-
sym.is(AccessFlags) || sym.privateWithin.exists
55+
(sym.is(AccessFlags) || sym.privateWithin.exists) &&
56+
!sym.owner.isContainedIn(inlineMethod)
5257

5358
/** The name of the next accessor to be generated */
5459
def accessorName(implicit ctx: Context) = InlineAccessorName.fresh(inlineMethod.name.asTermName)
@@ -116,7 +121,8 @@ object Inliner {
116121

117122
// The types that are local to the inlined method, and that therefore have
118123
// to be abstracted out in the accessor, which is external to the inlined method
119-
val localRefs = qualType.namedPartsWith(_.symbol.isContainedIn(inlineMethod)).toList
124+
val localRefs = qualType.namedPartsWith(ref =>
125+
ref.isType && ref.symbol.isContainedIn(inlineMethod)).toList
120126

121127
// Abstract accessed type over local refs
122128
def abstractQualType(mtpe: Type): Type =
@@ -175,8 +181,14 @@ object Inliner {
175181
}
176182
}
177183

178-
val tree1 = addAccessors.transform(tree)
179-
flatTree(tree1 :: addAccessors.accessors.toList)
184+
if (inlineMethod.owner.isTerm)
185+
// Inline methods in local scopes can only be called in the scope they are defined,
186+
// so no accessors are needed for them.
187+
tree
188+
else {
189+
val tree1 = addAccessors.transform(tree)
190+
flatTree(tree1 :: addAccessors.accessors.toList)
191+
}
180192
}
181193

182194
/** Register inline info for given inline method `sym`.
@@ -359,13 +371,15 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
359371

360372
private def canElideThis(tpe: ThisType): Boolean =
361373
prefix.tpe == tpe && ctx.owner.isContainedIn(tpe.cls) ||
374+
tpe.cls.isContainedIn(meth) ||
362375
tpe.cls.is(Package)
363376

364377
/** Populate `thisProxy` and `paramProxy` as follows:
365378
*
366379
* 1a. If given type refers to a static this, thisProxy binds it to corresponding global reference,
367-
* 1b. If given type refers to an instance this, create a proxy symbol and bind the thistype to
368-
* refer to the proxy. The proxy is not yet entered in `bindingsBuf` that will come later.
380+
* 1b. If given type refers to an instance this to a class that is not contained in the
381+
* inlined method, create a proxy symbol and bind the thistype to refer to the proxy.
382+
* The proxy is not yet entered in `bindingsBuf`; that will come later.
369383
* 2. If given type refers to a parameter, make `paramProxy` refer to the entry stored
370384
* in `paramNames` under the parameter's name. This roundabout way to bind parameter
371385
* references to proxies is done because we not known a priori what the parameter
@@ -421,11 +435,12 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
421435
val rhs =
422436
if (lastSelf.exists)
423437
ref(lastSelf).outerSelect(lastLevel - level, selfSym.info)
424-
else if (rhsClsSym.is(Module))
438+
else if (rhsClsSym.is(Module) && rhsClsSym.isStatic)
425439
ref(rhsClsSym.sourceModule)
426440
else
427441
prefix
428442
bindingsBuf += ValDef(selfSym.asTerm, rhs)
443+
inlining.println(i"proxy at $level: $selfSym = ${bindingsBuf.last}")
429444
lastSelf = selfSym
430445
lastLevel = level
431446
}
@@ -439,6 +454,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
439454
case t: SingletonType => paramProxy.getOrElse(t, mapOver(t))
440455
case t => mapOver(t)
441456
}
457+
override def mapClassInfo(tp: ClassInfo) = mapFullClassInfo(tp)
442458
}
443459

444460
// The tree map to apply to the inlined tree. This maps references to this-types

tests/pos/i3082.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
object Test {
2+
private def foo(arg1: Int): Int = {
3+
inline def bar: Int = foo(0)
4+
if (arg1 == 0) 0 else bar
5+
}
6+
assert(foo(11) == 0)
7+
}

tests/pos/i3129.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
object companions2 {
2+
inline def foo() = {
3+
class C {
4+
println(C.p)
5+
}
6+
7+
object C {
8+
private val p = 1
9+
}
10+
}
11+
}
12+
13+
class A {
14+
val b = new B
15+
16+
class B {
17+
private def getAncestor2(p: A): A = p
18+
private inline def getAncestor(p: A): A = {
19+
p.b.getAncestor(p)
20+
}
21+
}
22+
}

tests/pos/i3130a.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
object O {
2+
new D(2).DD.apply()
3+
}
4+
5+
class D(val x: Int) {
6+
class DD()
7+
object DD {
8+
inline def apply() = x // new DD()
9+
}
10+
}

tests/pos/i3130b.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class Outer {
2+
trait F { def f(): Int }
3+
inline def inner: F = {
4+
class InnerClass(x: Int) extends F {
5+
def this() = this(3)
6+
def f() = x
7+
}
8+
new InnerClass(3)
9+
}
10+
}
11+
12+
object Test extends App {
13+
val o = new Outer
14+
assert(o.inner.f() == 3)
15+
}

tests/pos/i3130c.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
trait Test {
2+
trait Global {
3+
type Tree
4+
def get: Tree
5+
}
6+
7+
trait TreeBuilder {
8+
val global: Global
9+
inline def set(tree: global.Tree) = {}
10+
}
11+
12+
val nsc: Global
13+
14+
trait FileImpl {
15+
object treeBuilder extends TreeBuilder {
16+
val global: nsc.type = nsc
17+
}
18+
treeBuilder.set(nsc.get)
19+
}
20+
def file: FileImpl
21+
22+
file.treeBuilder.set(nsc.get)
23+
}

tests/pos/i3130d.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class D(x: Int) {
2+
class DD {
3+
inline def apply() = new DD()
4+
}
5+
val inner = new DD
6+
}
7+
object Test {
8+
new D(2).inner.apply()
9+
}

0 commit comments

Comments
 (0)