diff options
author | Adriaan Moors <adriaan.moors@typesafe.com> | 2016-02-08 18:24:43 -0800 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2016-03-26 22:52:41 -0700 |
commit | 8433b6fa0e86dfdcd3db31b97844b14d65e45359 (patch) | |
tree | 08d2db915b88057ff1b16479e797bbca41a385ce /test/junit | |
parent | 651d67cff7af581751257711ad99d318a5a2879a (diff) | |
download | scala-8433b6fa0e86dfdcd3db31b97844b14d65e45359.tar.gz scala-8433b6fa0e86dfdcd3db31b97844b14d65e45359.tar.bz2 scala-8433b6fa0e86dfdcd3db31b97844b14d65e45359.zip |
Treat `Function` literals uniformly, expecting SAM or FunctionN.
They both compile to INDY/MetaLambdaFactory, except when they
occur in a constructor call. (TODO: can we lift the ctor arg
expression to a method and avoid statically synthesizing
anonymous subclass altogether?)
Typers:
- no longer synthesize SAMs -- *adapt* a Function literal
to the expected (SAM/FunctionN) type
- Deal with polymorphic/existential sams (relevant tests:
pos/t8310, pos/t5099.scala, pos/t4869.scala) We know where
to find the result type, as all Function nodes have a
FunctionN-shaped type during erasure. (Including function
literals targeting a SAM type -- the sam type is tracked as
the *expected* type.)
Lift restriction on sam types being class types. It's enough
that they dealias to one, like regular instance creation
expressions.
Contexts:
- No longer need encl method hack for return in sam.
Erasure:
- erasure preserves SAM type for function nodes
- Normalize sam to erased function type during erasure,
otherwise we may box the function body from `$anonfun(args)`
to `{$anonfun(args); ()}` because the expected type for the
body is now `Object`, and thus `Unit` does not conform.
Delambdafy:
- must set static flag before calling createBoxingBridgeMethod
- Refactored `createBoxingBridgeMethod` to wrap my head around
boxing, reworked it to generalize from FunctionN's boxing
needs to arbitrary LMF targets.
Other refactorings: ThisReferringMethodsTraverser, TreeGen.
Diffstat (limited to 'test/junit')
-rw-r--r-- | test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala | 29 |
1 files changed, 24 insertions, 5 deletions
diff --git a/test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala b/test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala index 758566fe53..d29f6b0a13 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala @@ -31,25 +31,44 @@ class IndyLambdaTest extends ClearAfterClass { case _ => Nil }.head } + + val obj = "Ljava/lang/Object;" + val str = "Ljava/lang/String;" + // unspecialized functions that have a primitive in parameter or return position // give rise to a "boxing bridge" method (which has the suffix `$adapted`). // This is because Scala's unboxing of null values gives zero, whereas Java's throw a NPE. // 1. Here we show that we are calling the boxing bridge (the lambda bodies here are compiled into // methods of `(I)Ljava/lang/Object;` / `(I)Ljava/lang/Object;` respectively.) - assertEquals("(Ljava/lang/Object;)Ljava/lang/Object;", implMethodDescriptorFor("(x: Int) => new Object")) - assertEquals("(Ljava/lang/Object;)Ljava/lang/Object;", implMethodDescriptorFor("(x: Object) => 0")) + assertEquals(s"($obj)$obj", implMethodDescriptorFor("(x: Int) => new Object")) + assertEquals(s"($obj)$obj", implMethodDescriptorFor("(x: Object) => 0")) // 2a. We don't need such adaptations for parameters or return values with types that differ // from Object due to other generic substitution, LambdaMetafactory will downcast the arguments. - assertEquals("(Ljava/lang/String;)Ljava/lang/String;", implMethodDescriptorFor("(x: String) => x")) + assertEquals(s"($str)$str", implMethodDescriptorFor("(x: String) => x")) // 2b. Testing 2a. in combination with 1. - assertEquals("(Ljava/lang/Object;)Ljava/lang/String;", implMethodDescriptorFor("(x: Int) => \"\"")) - assertEquals("(Ljava/lang/String;)Ljava/lang/Object;", implMethodDescriptorFor("(x: String) => 0")) + assertEquals(s"($obj)$str", implMethodDescriptorFor("(x: Int) => \"\"")) + assertEquals(s"($str)$obj", implMethodDescriptorFor("(x: String) => 0")) // 3. Specialized functions, don't need any of this as they implement a method like `apply$mcII$sp`, // and the (un)boxing is handled in the base class in code emitted by scalac. assertEquals("(I)I", implMethodDescriptorFor("(x: Int) => x")) + + // non-builtin sams are like specialized functions + compileClasses(compiler)("class VC(private val i: Int) extends AnyVal; trait FunVC { def apply(a: VC): VC }") + assertEquals("(I)I", implMethodDescriptorFor("((x: VC) => x): FunVC")) + + compileClasses(compiler)("trait Fun1[T, U] { def apply(a: T): U }") + assertEquals(s"($obj)$str", implMethodDescriptorFor("(x => x.toString): Fun1[Int, String]")) + assertEquals(s"($obj)$obj", implMethodDescriptorFor("(x => println(x)): Fun1[Int, Unit]")) + assertEquals(s"($obj)$str", implMethodDescriptorFor("((x: VC) => \"\") : Fun1[VC, String]")) + assertEquals(s"($str)$obj", implMethodDescriptorFor("((x: String) => new VC(0)) : Fun1[String, VC]")) + + compileClasses(compiler)("trait Coll[A, Repr] extends Any") + compileClasses(compiler)("final class ofInt(val repr: Array[Int]) extends AnyVal with Coll[Int, Array[Int]]") + + assertEquals(s"([I)$obj", implMethodDescriptorFor("((xs: Array[Int]) => new ofInt(xs)): Array[Int] => Coll[Int, Array[Int]]")) } } |