FAQ

[scala-internals] impasse in "Poor Man's Method Handles" due to lambdalift behavior

Miguel Garcia
Nov 14, 2012 at 1:57 pm
To recap, it's been conjectured that we can get the performance benefit of
MHs without MHs, relying instead on two small changes:

- in the way uncurry lowers closures, and
- in the way closures are inlined, in the experimental optimizer

I built a prototype for the first step above, and stumbled upon a test not
passing. After reformulating it, I noticed the compiler crashes also
without my patch, thus this question.

The test that passes, run/t0911

class Foo(val bar : () => String);

class IP extends {
val baz = "bar";
} with Foo(() => baz);

object Test extends App{
(new IP).bar();
}


Manually expanding the "() => baz", we get the following which crashes the
compiler. As can be seen, the only change involves the additional "def
poorMansMH1(): String = baz;" and its invocation in the closure's apply():


class Foo(val bar : () => String);

class IP extends {
val baz = "bar";
} with Foo( {
def poorMansMH1(): String = baz;

@SerialVersionUID(0) final class anonfun extends
scala.runtime.AbstractFunction0[String] with Serializable {
def apply(): String = poorMansMH1()
};

new anonfun()
}
);

object Test extends App{
(new IP).bar();
}


Is this an implementation restriction, or am I doing something wrong?


Miguel
http://lampwww.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/
reply

Search Discussions

3 responses

  • Jason Zaugg at Nov 14, 2012 at 2:17 pm

    On Wed, Nov 14, 2012 at 2:56 PM, Miguel Garcia wrote:
    To recap, it's been conjectured that we can get the performance benefit of
    MHs without MHs, relying instead on two small changes:

    - in the way uncurry lowers closures, and
    - in the way closures are inlined, in the experimental optimizer

    I built a prototype for the first step above, and stumbled upon a test not
    passing. After reformulating it, I noticed the compiler crashes also
    without my patch, thus this question.
    Here's a smaller version of the same crash:

    class Foo(a: Any);

    class IP extends Foo({
    def poorMansMH1 = ""
    object X {
    poorMansMH1
    }
    }
    )

    uncaught exception during compilation: scala.reflect.internal.FatalError
    error: scala.reflect.internal.FatalError:
    while compiling: sandbox/test.scala
    during phase: global=lambdalift, atPhase=constructors



    no-symbol does not have an owner
    at scala.reflect.internal.SymbolTable.abort(SymbolTable.scala:49)
    at scala.tools.nsc.Global.abort(Global.scala:253)
    at scala.reflect.internal.Symbols$NoSymbol.owner(Symbols.scala:3195)
    at
    scala.tools.nsc.transform.ExplicitOuter$OuterPathTransformer.outerSelect(ExplicitOuter.scala:229)
    at
    scala.tools.nsc.transform.ExplicitOuter$OuterPathTransformer.outerValue(ExplicitOuter.scala:215)
    at
    scala.tools.nsc.transform.LambdaLift$LambdaLifter.memberRef(LambdaLift.scala:327)
    at
    scala.tools.nsc.transform.LambdaLift$LambdaLifter.postTransform(LambdaLift.scala:472)

    This variation works:

    object Test {
    new Foo({
    def poorMansMH1 = ""
    object X {
    poorMansMH1
    }
    })
    }

    Perhaps to keep yourself moving forward, you could disable the new
    optimization in trees which are arguments to constructor invocations, and
    raise a ticket for the crash.

    -jason
  • Miguel Garcia at Nov 14, 2012 at 2:37 pm
    That's a great minimization. The ticket
    https://issues.scala-lang.org/browse/SI-6666 now gives a few more details,
    including a summary of the proposed lowering of closures (during uncurry)
    and why it's needed irrespective of whether "the real" JDK8 MethodHandle or
    the "poor man's" version are targeted.

    Miguel
    http://lampwww.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/
    On Wednesday, November 14, 2012 3:17:31 PM UTC+1, Jason Zaugg wrote:

    On Wed, Nov 14, 2012 at 2:56 PM, Miguel Garcia <miguel...@tuhh.de<javascript:>
    wrote:
    To recap, it's been conjectured that we can get the performance benefit
    of MHs without MHs, relying instead on two small changes:

    - in the way uncurry lowers closures, and
    - in the way closures are inlined, in the experimental optimizer

    I built a prototype for the first step above, and stumbled upon a test
    not passing. After reformulating it, I noticed the compiler crashes also
    without my patch, thus this question.
    Here's a smaller version of the same crash:

    class Foo(a: Any);

    class IP extends Foo({
    def poorMansMH1 = ""
    object X {
    poorMansMH1
    }
    }
    )

    uncaught exception during compilation: scala.reflect.internal.FatalError
    error: scala.reflect.internal.FatalError:
    while compiling: sandbox/test.scala
    during phase: global=lambdalift, atPhase=constructors



    no-symbol does not have an owner
    at scala.reflect.internal.SymbolTable.abort(SymbolTable.scala:49)
    at scala.tools.nsc.Global.abort(Global.scala:253)
    at scala.reflect.internal.Symbols$NoSymbol.owner(Symbols.scala:3195)
    at
    scala.tools.nsc.transform.ExplicitOuter$OuterPathTransformer.outerSelect(ExplicitOuter.scala:229)
    at
    scala.tools.nsc.transform.ExplicitOuter$OuterPathTransformer.outerValue(ExplicitOuter.scala:215)
    at
    scala.tools.nsc.transform.LambdaLift$LambdaLifter.memberRef(LambdaLift.scala:327)
    at
    scala.tools.nsc.transform.LambdaLift$LambdaLifter.postTransform(LambdaLift.scala:472)

    This variation works:

    object Test {
    new Foo({
    def poorMansMH1 = ""
    object X {
    poorMansMH1
    }
    })
    }

    Perhaps to keep yourself moving forward, you could disable the new
    optimization in trees which are arguments to constructor invocations, and
    raise a ticket for the crash.

    -jason
  • Paul Phillips at Nov 14, 2012 at 3:47 pm

    On Wed, Nov 14, 2012 at 6:17 AM, Jason Zaugg wrote:

    Here's a smaller version of the same crash:
    And here's an older version:

    https://issues.scala-lang.org/browse/SI-2487

    class C1(l: List[Int])

    class C2 extends C1({
    def intsFrom(n: Int): Stream[Int] = Stream.cons(n, intsFrom(n+1))
    intsFrom(0).take(10).toList
    })

Related Discussions

Discussion Navigation
viewthread | post