Skip to content

Commit 80987e3

Browse files
committed
SI-2897, SI-6231 method-local traits capturing variables
Instead of passing the ObjectRef to the impl class's ctor, which would store it in a field in the impl class, which is then mixed in to the subclass, reworking the scheme. The new scheme doesn't introduce fields/ctor args for captured variables in traits, only a getter for it. The getter is implemented in subclasses that mix in the trait, which have the captured variable in scope.
1 parent c4694d9 commit 80987e3

File tree

2 files changed

+88
-14
lines changed

2 files changed

+88
-14
lines changed

src/compiler/scala/tools/nsc/transform/LambdaLift.scala

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -309,17 +309,22 @@ abstract class LambdaLift extends InfoTransform {
309309
afterOwnPhase {
310310
for ((owner, freeValues) <- free.toList) {
311311
val newFlags = SYNTHETIC | (
312-
if (owner.isClass) PARAMACCESSOR | PrivateLocal
312+
if (owner.isTrait) PARAMACCESSOR | TRANS_FLAG // will need an impl in subclass that mixes in the trait
313+
else if (owner.isClass) PARAMACCESSOR | PrivateLocal
313314
else PARAM)
314315

315316
proxies(owner) =
316317
for (fv <- freeValues.toList) yield {
317318
val proxyName = proxyNames.getOrElse(fv, fv.name)
318-
debuglog(s"new proxy ${proxyName} in ${owner.fullLocationString}")
319-
val proxy = owner.newValue(proxyName.toTermName, owner.pos, newFlags.toLong) setInfo fv.info
319+
println(s"new proxy ${proxyName} in ${owner.fullLocationString}")
320+
val proxy =
321+
if (owner.isTrait) owner.newMethod(proxyName.toTermName, owner.pos, newFlags.toLong) setInfo MethodType(Nil, fv.info)
322+
else owner.newValue(proxyName.toTermName, owner.pos, newFlags.toLong) setInfo fv.info
320323
if (owner.isClass) owner.info.decls enter proxy
321324
proxy
322325
}
326+
327+
proxies(owner.toInterface) = proxies(owner)
323328
}
324329
}
325330
}
@@ -368,7 +373,11 @@ abstract class LambdaLift extends InfoTransform {
368373

369374
qual match {
370375
case EmptyTree => EmptyTree
371-
case qual => Select(qual, sym) setType sym.tpe
376+
case qual =>
377+
val sel = Select(qual, sym) setType sym.tpe
378+
379+
if (sym.isParamAccessor && (sym hasFlag TRANS_FLAG)) Apply(sel, Nil) setType sym.tpe.finalResultType
380+
else sel
372381
}
373382
}
374383

@@ -379,19 +388,43 @@ abstract class LambdaLift extends InfoTransform {
379388

380389
def freeArgsOrNil(sym: Symbol) = free.getOrElse(sym, Nil).toList
381390

382-
private def freeArgs(sym: Symbol): List[Symbol] =
383-
freeArgsOrNil(sym)
391+
private def transformApply(tree: Apply) = {
392+
val Apply(fn, args) = tree
393+
val sym = tree.symbol
394+
val pos = tree.pos
384395

385-
private def addFreeArgs(pos: Position, sym: Symbol, args: List[Tree]) =
386-
freeArgs(sym) match {
387-
case Nil => args
388-
case fvs => addFree(sym, free = fvs map (fv => atPos(pos)(proxyRef(fv))), original = args)
396+
if (sym.isMethod && sym.isConstructor && sym.owner.isTrait) {
397+
// TODO: add types / symbols
398+
val inits = freeParams(currentOwner).map { ctorParam =>
399+
ugh, since we just mutate trees without first transforming infos, we can't get to the symbol
400+
val proxyFieldSym = currentClass.info.decl(ctorParam.name)
401+
val proxyField = gen.mkAttributedSelect(gen.mkAttributedThis(currentClass), proxyFieldSym)
402+
403+
atPos(currentOwner.pos)(Assign(proxyField, Ident(ctorParam))) setType BoxedUnitTpe
404+
}
405+
Block(inits, tree) setType tree.tpe setPos tree.pos
406+
} else {
407+
val newArgs =
408+
freeArgsOrNil(sym) match {
409+
case Nil => args
410+
case fvs => addFree (sym, free = fvs map (fv => atPos (pos) (proxyRef (fv) ) ), original = args)
411+
}
412+
413+
treeCopy.Apply(tree, fn, newArgs)
389414
}
415+
}
390416

391417
def proxiesOrNil(sym: Symbol) = proxies.getOrElse(sym, Nil)
392418

419+
private def mixinProxies(mixin: Symbol): List[Symbol] = {
420+
proxiesOrNil(mixin) map (mixedin => mixedin.cloneSymbol setFlag TRANS_FLAG)
421+
}
422+
393423
private def freeParams(sym: Symbol): List[Symbol] =
394-
proxiesOrNil(sym)
424+
if (sym.isMethod && sym.isConstructor && sym.owner.isTrait) Nil
425+
else if (sym.isClass && !sym.isTrait)
426+
proxiesOrNil(sym) ++ (sym.mixinClasses flatMap mixinProxies)
427+
else proxiesOrNil(sym)
395428

396429
private def addFreeParams(tree: Tree, sym: Symbol): Tree =
397430
tree match {
@@ -407,12 +440,31 @@ abstract class LambdaLift extends InfoTransform {
407440
copyDefDef(tree)(vparamss = List(addFree(sym, free = paramDefs, original = vparams)))
408441
}
409442

410-
case ClassDef(_, _, _, _) =>
443+
case ClassDef(_, _, _, _) if !sym.isTrait =>
411444
val freeParamDefs = freeParams(sym) map (p => ValDef(p) setPos tree.pos setType NoType)
412445

413446
if (freeParamDefs isEmpty) tree
414447
else deriveClassDef(tree)(impl => deriveTemplate(impl)(_ ::: freeParamDefs))
415448

449+
case ClassDef(_, _, _, _) =>
450+
// SI-2897, SI-6231
451+
// Disabled attempt to to add getters to freeParams
452+
// this does not work yet. Problem is that local symbols need local names
453+
// and references to local symbols need to be transformed into
454+
// method calls to setters.
455+
// def paramGetter(param: Symbol): Tree = {
456+
// val getter = param.newGetter setFlag TRANS_FLAG resetFlag PARAMACCESSOR // mark because we have to add them to interface
457+
// sym.info.decls.enter(getter)
458+
// val rhs = Select(gen.mkAttributedThis(sym), param) setType param.tpe
459+
// DefDef(getter, rhs) setPos tree.pos setType NoType
460+
// }
461+
val ps = freeParams(sym)
462+
if (ps isEmpty) tree
463+
else {
464+
val freeParams = ps map (p => DefDef(p, EmptyTree) setPos tree.pos setType NoType)
465+
deriveClassDef(tree)(impl => deriveTemplate(impl)(_ ::: freeParams))
466+
}
467+
416468
case _ => tree
417469
}
418470

@@ -504,8 +556,7 @@ abstract class LambdaLift extends InfoTransform {
504556
case Return(expr) =>
505557
assert(sym == currentMethod, sym)
506558
tree
507-
case Apply(fn, args) =>
508-
treeCopy.Apply(tree, fn, addFreeArgs(tree.pos, sym, args))
559+
case tree: Apply => transformApply(tree)
509560
case Assign(Apply(TypeApply(sel @ Select(qual, _), _), List()), rhs) =>
510561
// eliminate casts introduced by selecting a captured variable field
511562
// on the lhs of an assignment.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
class Lift {
2+
def foo = {
3+
// this will be captured by the MouseHandler trait,
4+
// which gives rise to a new trait field during LambdaLift
5+
var Clicked = "Clicked"
6+
7+
def bar = Clicked
8+
9+
trait MouseHandler {
10+
def mouseClicked = {println(Clicked) ; println(bar)}
11+
}
12+
13+
trait C extends MouseHandler
14+
class CC extends C
15+
16+
new C {}
17+
new CC
18+
}
19+
}
20+
21+
object O extends Lift with App {
22+
println(foo)
23+
}

0 commit comments

Comments
 (0)