Skip to content

Commit b12326c

Browse files
authored
Merge pull request #2870 from dotty-staging/fix-param-captures
Fix #2869: Have constructors run after group of Memoize
2 parents 5ba8ea3 + daef8ec commit b12326c

File tree

6 files changed

+31
-13
lines changed

6 files changed

+31
-13
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ class Compiler {
8787
new LazyVals, // Expand lazy vals
8888
new Memoize, // Add private fields to getters and setters
8989
new NonLocalReturns, // Expand non-local returns
90-
new CapturedVars, // Represent vars captured by closures as heap objects
91-
new Constructors, // Collect initialization code in primary constructors
90+
new CapturedVars), // Represent vars captured by closures as heap objects
91+
List(new Constructors, // Collect initialization code in primary constructors
9292
// Note: constructors changes decls in transformTemplate, no InfoTransformers should be added after it
9393
new FunctionalInterfaces, // Rewrites closures to implement @specialized types of Functions.
9494
new GetClass, // Rewrites getClass calls on primitive types.

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

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,20 @@ class Constructors extends MiniPhaseTransform with IdentityDenotTransformer { th
3030
import tpd._
3131

3232
override def phaseName: String = "constructors"
33-
override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Memoize], classOf[HoistSuperArgs])
33+
override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[HoistSuperArgs])
34+
override def runsAfterGroupsOf: Set[Class[_ <: Phase]] = Set(classOf[Memoize])
35+
// Memoized needs to be finished because we depend on the ownerchain after Memoize
36+
// when checking whether an ident is an access in a constructor or outside it.
37+
// This test is done in the right-hand side of a value definition. If Memoize
38+
// was in the same group as Constructors, the test on the rhs ident would be
39+
// performed before the rhs undergoes the owner change. This would lead
40+
// to more symbols being retained as parameters. Test case in run/capturing.scala.
41+
42+
/** The private vals that are known to be retained as class fields */
43+
private val retainedPrivateVals = mutable.Set[Symbol]()
44+
45+
/** The private vals whose definition comes before the current focus */
46+
private val seenPrivateVals = mutable.Set[Symbol]()
3447

3548
// Collect all private parameter accessors and value definitions that need
3649
// to be retained. There are several reasons why a parameter accessor or
@@ -40,14 +53,10 @@ class Constructors extends MiniPhaseTransform with IdentityDenotTransformer { th
4053
// 3. It is accessed on an object other than `this`
4154
// 4. It is a mutable parameter accessor
4255
// 5. It is has a wildcard initializer `_`
43-
private val retainedPrivateVals = mutable.Set[Symbol]()
44-
private val seenPrivateVals = mutable.Set[Symbol]()
45-
4656
private def markUsedPrivateSymbols(tree: RefTree)(implicit ctx: Context): Unit = {
4757

4858
val sym = tree.symbol
49-
def retain() =
50-
retainedPrivateVals.add(sym)
59+
def retain() = retainedPrivateVals.add(sym)
5160

5261
if (sym.exists && sym.owner.isClass && mightBeDropped(sym)) {
5362
val owner = sym.owner.asClass
@@ -58,7 +67,8 @@ class Constructors extends MiniPhaseTransform with IdentityDenotTransformer { th
5867
val method = ctx.owner.enclosingMethod
5968
method.isPrimaryConstructor && ctx.owner.enclosingClass == owner
6069
}
61-
if (inConstructor && (sym.is(ParamAccessor) || seenPrivateVals.contains(sym))) {
70+
if (inConstructor &&
71+
(sym.is(ParamAccessor) || seenPrivateVals.contains(sym))) {
6272
// used inside constructor, accessed on this,
6373
// could use constructor argument instead, no need to retain field
6474
}
@@ -79,8 +89,7 @@ class Constructors extends MiniPhaseTransform with IdentityDenotTransformer { th
7989
}
8090

8191
override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
82-
if (mightBeDropped(tree.symbol))
83-
(if (isWildcardStarArg(tree.rhs)) retainedPrivateVals else seenPrivateVals) += tree.symbol
92+
if (mightBeDropped(tree.symbol)) seenPrivateVals += tree.symbol
8493
tree
8594
}
8695

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
6666

6767
override def relaxedTyping = true
6868

69-
override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Constructors], classOf[HoistSuperArgs])
69+
override def runsAfterGroupsOf: Set[Class[_ <: Phase]] = Set(classOf[Constructors], classOf[HoistSuperArgs])
7070
// Constructors has to happen before LambdaLift because the lambda lift logic
7171
// becomes simpler if it can assume that parameter accessors have already been
7272
// converted to parameters in super calls. Without this it is very hard to get

tests/run/capturing.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
private final java.lang.String MT.s

tests/run/capturing.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class MT(sf: MT => String) {
2+
// `s` is retained as a field, but `sf` should not be.
3+
val s = sf(this)
4+
}
5+
object Test extends App {
6+
def printFields(obj: Any) =
7+
println(obj.getClass.getDeclaredFields.map(_.toString).sorted.deep.mkString("\n"))
8+
printFields(new MT(_ => ""))
9+
}
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# Fields of A:
22
private final int A.a
33
private final int A.b
4-
private final scala.Tuple2 A.$1$
54
# Methods of A:
65
public int A.a()
76
public int A.b()

0 commit comments

Comments
 (0)