impasse in "Poor Man's Method Handles" due to lambdalift behavior

86 views
Skip to first unread message

Miguel Garcia

unread,
Nov 14, 2012, 8:56:57 AM11/14/12
to scala-i...@googlegroups.com

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/


Jason Zaugg

unread,
Nov 14, 2012, 9:17:07 AM11/14/12
to scala-i...@googlegroups.com
On Wed, Nov 14, 2012 at 2:56 PM, Miguel Garcia <miguel...@tuhh.de> 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

unread,
Nov 14, 2012, 9:37:47 AM11/14/12
to scala-i...@googlegroups.com

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/

Paul Phillips

unread,
Nov 14, 2012, 10:47:04 AM11/14/12
to scala-i...@googlegroups.com


On Wed, Nov 14, 2012 at 6:17 AM, Jason Zaugg <jza...@gmail.com> wrote:
Here's a smaller version of the same crash:

And here's an older version:

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
})


Reply all
Reply to author
Forward
0 new messages