summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/transform
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2016-09-26 22:11:40 +0200
committerLukas Rytz <lukas.rytz@gmail.com>2016-09-28 10:17:53 +0200
commit19f6209e5b1db295320bfbd3ef00eeaa729c1eec (patch)
tree2d29641720f5eafacd9b020eab1a92c103282080 /src/compiler/scala/tools/nsc/transform
parent6df14e57a4980897d5517f002c04584b82e05b15 (diff)
downloadscala-19f6209e5b1db295320bfbd3ef00eeaa729c1eec.tar.gz
scala-19f6209e5b1db295320bfbd3ef00eeaa729c1eec.tar.bz2
scala-19f6209e5b1db295320bfbd3ef00eeaa729c1eec.zip
SI-9697 / SD-229 Fix DelayedInit subclass capturing local value
When a class captures an outer value, a field for that value is created in the class. The class also gets a constructor parameter for the captured value, the constructor will assign the field. LambdaLift re-writes accesses to the local value (Ident trees) to the field. However, if the statement accessing the local value will end up inside the constructor, the access is re-written to the constructor parameter instead. This is the case for constructor statements: class C { { println(capturedLocal) } } If C extends DelayedInit, the statement does not end up in C's constructor, but into a new synthetic method. The access to `capturedLocal` needs to be re-written to the field instead of the constructor parameter. LambdaLift takes the decision (field or constructor parameter) based on the owner chain of `currentOwner`. For the constructor statement block, the owner is a local dummy, for which `logicallyEnclosingMember` returns the constructor symbol. This commit introduces a special case in LambdaLift for local dummies of DelayedInit subclasses: instead of the constructor, we use a temporary symbol representing the synthetic method holding the initializer statements.
Diffstat (limited to 'src/compiler/scala/tools/nsc/transform')
-rw-r--r--src/compiler/scala/tools/nsc/transform/LambdaLift.scala44
1 files changed, 34 insertions, 10 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
index 74e6c58388..1ec3d4d4cb 100644
--- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
+++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
@@ -104,8 +104,31 @@ abstract class LambdaLift extends InfoTransform {
/** Buffers for lifted out classes and methods */
private val liftedDefs = new LinkedHashMap[Symbol, List[Tree]]
+ val delayedInitDummies = new mutable.HashMap[Symbol, Symbol]
+
+ /**
+ * For classes capturing locals, LambdaLift uses `local.logicallyEnclosingMember` to decide
+ * whether an access to the local is re-written to the field or constructor parameter. If the
+ * access is in a constructor statement, the constructor parameter is used.
+ *
+ * For DelayedInit subclasses, constructor statements end up in the synthetic init method
+ * instead of the constructor itself, so the access should go to the field. This method changes
+ * `logicallyEnclosingMember` in this case to return a temprorary symbol corresponding to that
+ * method.
+ */
+ private def logicallyEnclosingMember(sym: Symbol): Symbol = {
+ if (sym.isLocalDummy) {
+ val enclClass = sym.enclClass
+ if (enclClass.isSubClass(DelayedInitClass))
+ delayedInitDummies.getOrElseUpdate(enclClass, enclClass.newMethod(nme.delayedInit))
+ else
+ enclClass.primaryConstructor
+ } else if (sym.isMethod || sym.isClass || sym == NoSymbol) sym
+ else logicallyEnclosingMember(sym.owner)
+ }
+
private def isSameOwnerEnclosure(sym: Symbol) =
- sym.owner.logicallyEnclosingMember == currentOwner.logicallyEnclosingMember
+ logicallyEnclosingMember(sym.owner) == logicallyEnclosingMember(currentOwner)
/** Mark symbol `sym` as being free in `enclosure`, unless `sym`
* is defined in `enclosure` or there is a class between `enclosure`s owner
@@ -139,9 +162,9 @@ abstract class LambdaLift extends InfoTransform {
*/
private def markFree(sym: Symbol, enclosure: Symbol): Boolean = {
// println(s"mark free: ${sym.fullLocationString} marked free in $enclosure")
- (enclosure == sym.owner.logicallyEnclosingMember) || {
- debuglog("%s != %s".format(enclosure, sym.owner.logicallyEnclosingMember))
- if (enclosure.isPackageClass || !markFree(sym, enclosure.skipConstructor.owner.logicallyEnclosingMember)) false
+ (enclosure == logicallyEnclosingMember(sym.owner)) || {
+ debuglog("%s != %s".format(enclosure, logicallyEnclosingMember(sym.owner)))
+ if (enclosure.isPackageClass || !markFree(sym, logicallyEnclosingMember(enclosure.skipConstructor.owner))) false
else {
val ss = symSet(free, enclosure)
if (!ss(sym)) {
@@ -184,14 +207,14 @@ abstract class LambdaLift extends InfoTransform {
if (sym == NoSymbol) {
assert(name == nme.WILDCARD)
} else if (sym.isLocalToBlock) {
- val owner = currentOwner.logicallyEnclosingMember
+ val owner = logicallyEnclosingMember(currentOwner)
if (sym.isTerm && !sym.isMethod) markFree(sym, owner)
else if (sym.isMethod) markCalled(sym, owner)
//symSet(called, owner) += sym
}
case Select(_, _) =>
if (sym.isConstructor && sym.owner.isLocalToBlock)
- markCalled(sym, currentOwner.logicallyEnclosingMember)
+ markCalled(sym, logicallyEnclosingMember(currentOwner))
case _ =>
}
super.traverse(tree)
@@ -283,17 +306,18 @@ abstract class LambdaLift extends InfoTransform {
private def proxy(sym: Symbol) = {
def searchIn(enclosure: Symbol): Symbol = {
- if (enclosure eq NoSymbol) throw new IllegalArgumentException("Could not find proxy for "+ sym.defString +" in "+ sym.ownerChain +" (currentOwner= "+ currentOwner +" )")
- debuglog("searching for " + sym + "(" + sym.owner + ") in " + enclosure + " " + enclosure.logicallyEnclosingMember)
+ if (enclosure eq NoSymbol)
+ throw new IllegalArgumentException("Could not find proxy for "+ sym.defString +" in "+ sym.ownerChain +" (currentOwner= "+ currentOwner +" )")
+ debuglog("searching for " + sym + "(" + sym.owner + ") in " + enclosure + " " + logicallyEnclosingMember(enclosure))
val proxyName = proxyNames.getOrElse(sym, sym.name)
- val ps = (proxies get enclosure.logicallyEnclosingMember).toList.flatten find (_.name == proxyName)
+ val ps = (proxies get logicallyEnclosingMember(enclosure)).toList.flatten find (_.name == proxyName)
ps getOrElse searchIn(enclosure.skipConstructor.owner)
}
debuglog("proxy %s from %s has logical enclosure %s".format(
sym.debugLocationString,
currentOwner.debugLocationString,
- sym.owner.logicallyEnclosingMember.debugLocationString)
+ logicallyEnclosingMember(sym.owner).debugLocationString)
)
if (isSameOwnerEnclosure(sym)) sym