Skip to content

Commit

Permalink
SI-6231 Report unsupported free var capture by a trait.
Browse files Browse the repository at this point in the history
If a class nested in a trait captures a free variable from
the enclosing scope of the trait, the transformation to
add that variable to the `init` method of the trait
implementation class happens *after* the abstract trait
interface has been extracted. This would lead to a crash
when trying to find the corresponding interface method.

This commit detects this situation and reports an
implementation restriction. The enclosed test case
shows a workaround.

To lift this restriction, LambdaLifter should add the getters
and make sure they end up in the trait interface. Looks like
Martin tried this once:

    // LambdaLift.scala
    //
    // Disabled attempt to to add getters to freeParams
    // this does not work yet. Problem is that local symbols need local names
    // and references to local symbols need to be transformed into
    // method calls to setters.
    // def paramGetter(param: Symbol): Tree = {
    //   val getter = param.newGetter setFlag TRANS_FLAG resetFlag PARAMACCESSOR // mark because we have to add them to interface
    //   sym.info.decls.enter(getter)
    //   val rhs = Select(gen.mkAttributedThis(sym), param) setType param.tpe
    //   DefDef(getter, rhs) setPos tree.pos setType NoType
    // }
    // val newDefs = if (sym.isTrait) freeParams ::: (ps map paramGetter) else freeParams
  • Loading branch information
retronym committed Jan 21, 2013
1 parent 6f72ed8 commit f6168b8
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/compiler/scala/tools/nsc/transform/LambdaLift.scala
Expand Up @@ -352,6 +352,7 @@ abstract class LambdaLift extends InfoTransform {

copyDefDef(tree)(vparamss = List(vparams ++ freeParams))
case ClassDef(_, _, _, _) =>
// SI-6231
// Disabled attempt to to add getters to freeParams
// this does not work yet. Problem is that local symbols need local names
// and references to local symbols need to be transformed into
Expand All @@ -369,7 +370,7 @@ abstract class LambdaLift extends InfoTransform {
tree
}

/* Something like this will be necessary to eliminate the implementation
/* SI-6231: Something like this will be necessary to eliminate the implementation
* restiction from paramGetter above:
* We need to pass getters to the interface of an implementation class.
private def fixTraitGetters(lifted: List[Tree]): List[Tree] =
Expand Down
19 changes: 17 additions & 2 deletions src/compiler/scala/tools/nsc/transform/Mixin.scala
Expand Up @@ -1215,9 +1215,24 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
// refer to fields in some implementation class via an abstract
// getter in the interface.
val iface = toInterface(sym.owner.tpe).typeSymbol
val getter = sym getter iface orElse abort("No getter for " + sym + " in " + iface)
val ifaceGetter = sym getter iface

def si6231Restriction() {
// See SI-6231 comments in LamdaLift for ideas on how to lift the restriction.
val msg = sm"""Implementation restriction: local ${iface.fullLocationString} is unable to automatically capture the
|free variable ${sym} on behalf of ${currentClass}. You can manually assign it to a val inside the trait,
|and refer that that val in ${currentClass}. For more details, see SI-6231."""
reporter.error(tree.pos, msg)
}

typedPos(tree.pos)((qual DOT getter)())
if (ifaceGetter == NoSymbol) {
if (sym.isParamAccessor) {
si6231Restriction()
EmptyTree
}
else abort("No getter for " + sym + " in " + iface)
}
else typedPos(tree.pos)((qual DOT ifaceGetter)())

case Assign(Apply(lhs @ Select(qual, _), List()), rhs) =>
// assign to fields in some implementation class via an abstract
Expand Down
6 changes: 6 additions & 0 deletions test/files/neg/t6231.check
@@ -0,0 +1,6 @@
t6231.scala:4: error: Implementation restriction: local trait Bug$X$1 is unable to automatically capture the
free variable value ev$1 on behalf of anonymous class anonfun$qux$1. You can manually assign it to a val inside the trait,
and refer that that val in anonymous class anonfun$qux$1. For more details, see SI-6231.
def qux = { () => ev }
^
one error found
15 changes: 15 additions & 0 deletions test/files/neg/t6231.scala
@@ -0,0 +1,15 @@
object Bug {
def bar(ev: Any) = {
trait X {
def qux = { () => ev }
}
new X {}.qux()

// workaround
trait Y {
val ev2 = ev // manually capture `ev` so that `ev2` is added to the trait interface.
def qux = { () => ev2 }
}
}
}

0 comments on commit f6168b8

Please sign in to comment.