Skip to content

Commit 4d60c90

Browse files
committed
Merge pull request #486 from dotty-staging/fix/lambdaLift
Fix #480 in LambdaLift
2 parents 85b48de + 0b4e4cb commit 4d60c90

File tree

4 files changed

+71
-15
lines changed

4 files changed

+71
-15
lines changed

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

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
9090
free.getOrElse(sym, Nil).toList.map(pm)
9191
}
9292

93+
/** Set `liftedOwner(sym)` to `owner` if `owner` is more deeply nested
94+
* than the previous value of `liftedowner(sym)`.
95+
*/
9396
def narrowLiftedOwner(sym: Symbol, owner: Symbol)(implicit ctx: Context) = {
9497
if (sym.owner.isTerm &&
9598
owner.isProperlyContainedIn(liftedOwner(sym)) &&
@@ -100,11 +103,22 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
100103
}
101104
}
102105

103-
/** Mark symbol `sym` as being free in `enclosure`, unless `sym`
104-
* is defined in `enclosure` or there is a class between `enclosure`s owner
105-
* and the owner of `sym`.
106-
* Return `true` if there is no class between `enclosure` and
107-
* the owner of sym.
106+
/** Mark symbol `sym` as being free in `enclosure`, unless `sym` is defined
107+
* in `enclosure` or there is an intermediate class properly containing `enclosure`
108+
* in which `sym` is also free. Also, update `liftedOwner` of `enclosure` so
109+
* that `enclosure` can access `sym`, or its proxy in an intermediate class.
110+
* This means:
111+
*
112+
* 1. If there is an intermediate class in which `sym` is free, `enclosure`
113+
* must be contained in that class (in order to access the `sym proxy stored
114+
* in the class).
115+
*
116+
* 2. If there is no intermediate class, `enclosure` must be contained
117+
* in the class enclosing `sym`.
118+
*
119+
* Return the closest enclosing intermediate class between `enclosure` and
120+
* the owner of sym, or NoSymbol if none exists.
121+
*
108122
* pre: sym.owner.isTerm, (enclosure.isMethod || enclosure.isClass)
109123
*
110124
* The idea of `markFree` is illustrated with an example:
@@ -130,22 +144,30 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
130144
* }
131145
* }
132146
*/
133-
private def markFree(sym: Symbol, enclosure: Symbol)(implicit ctx: Context): Boolean = try {
147+
private def markFree(sym: Symbol, enclosure: Symbol)(implicit ctx: Context): Symbol = try {
134148
if (!enclosure.exists) throw new NoPath
135-
ctx.log(i"mark free: ${sym.showLocated} with owner ${sym.maybeOwner} marked free in $enclosure")
136-
narrowLiftedOwner(enclosure, sym.enclosingClass)
137-
(enclosure == sym.enclosure) || {
149+
if (enclosure == sym.enclosure) NoSymbol
150+
else {
151+
ctx.log(i"mark free: ${sym.showLocated} with owner ${sym.maybeOwner} marked free in $enclosure")
138152
ctx.debuglog(i"$enclosure != ${sym.enclosure}")
139-
if (enclosure.is(PackageClass) ||
140-
!markFree(sym, enclosure.skipConstructor.enclosure)) false
153+
val intermediate =
154+
if (enclosure.is(PackageClass)) enclosure
155+
else markFree(sym, enclosure.skipConstructor.enclosure)
156+
// `enclosure` might be a constructor, in which case we want the enclosure
157+
// of the enclosing class, so skipConstructor is needed here.
158+
if (intermediate.exists) {
159+
narrowLiftedOwner(enclosure, intermediate)
160+
intermediate
161+
}
141162
else {
163+
narrowLiftedOwner(enclosure, sym.enclosingClass)
142164
val ss = symSet(free, enclosure)
143165
if (!ss(sym)) {
144166
ss += sym
145167
changedFreeVars = true
146168
ctx.debuglog(i"$sym is free in $enclosure")
147169
}
148-
!enclosure.isClass
170+
if (enclosure.isClass) enclosure else NoSymbol
149171
}
150172
}
151173
} catch {
@@ -272,7 +294,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
272294
local.copySymDenotation(
273295
owner = newOwner,
274296
name = newName(local),
275-
initFlags = local.flags | Private | maybeStatic | maybeNotJavaPrivate,
297+
initFlags = local.flags &~ InSuperCall | Private | maybeStatic | maybeNotJavaPrivate,
276298
info = liftedInfo(local)).installAfter(thisTransform)
277299
if (local.isClass)
278300
for (member <- local.asClass.info.decls)
@@ -372,8 +394,13 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
372394
val sym = tree.symbol
373395
tree.tpe match {
374396
case tpe @ TermRef(prefix, _) =>
375-
if ((prefix eq NoPrefix) && sym.enclosure != currentEnclosure && !sym.isStatic)
376-
(if (sym is Method) memberRef(sym) else proxyRef(sym)).withPos(tree.pos)
397+
if (prefix eq NoPrefix)
398+
if (sym.enclosure != currentEnclosure && !sym.isStatic)
399+
(if (sym is Method) memberRef(sym) else proxyRef(sym)).withPos(tree.pos)
400+
else if (sym.owner.isClass) // sym was lifted out
401+
ref(sym).withPos(tree.pos)
402+
else
403+
tree
377404
else if (!prefixIsElidable(tpe)) ref(tpe)
378405
else tree
379406
case _ =>

tests/pos/i342.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
object Test {
2+
def test2: Int = {
3+
var ds: String = null
4+
def s = {
5+
ds = "abs"
6+
ds
7+
}
8+
s.length
9+
}
10+
}

tests/pos/i480.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
object O {
2+
val x: Function1[String, String] = a => a
3+
val x2: Function1[String, String] = a => "1"
4+
}

tests/pos/i480a.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package test
2+
3+
/** A class defining symbols and types of standard definitions */
4+
class Definitions {
5+
6+
trait LazyType { def complete(): Unit }
7+
8+
def f(vcs: List[Int]): Unit = {
9+
val completer = new LazyType {
10+
def complete(): Unit =
11+
for (i <- 0 until vcs.length if vcs(i) != 0)
12+
f(vcs.updated(i, 0))
13+
}
14+
}
15+
}

0 commit comments

Comments
 (0)