Skip to content

Commit f4a2528

Browse files
committed
Disable -Ydelambdafy:method for specialized FunctionN
The Delambdafy phase generates its `FunctionN` subclasses after the specialization phase. As such, `((x: Int) => x).apply(42)` incurs boxing. This commit falls back to the `-Ydelambdafy:inline` in this case. This is done by running the specialization type map over the type of the function, and seeing if anything changes. To make this work robustly, we first need to ensure that the specialization info transformer has processed all the function types. This is not a fundamental limitation; we could in principle generate the specialized code. A followup change will use `-Ydelambdafy:method` as the basis for invokedymnamic lambdas. As part of that stream of work, we will synthesize specialization-aware lambdas, and remove the fallback to `-Ydelambdafy:inline`. I have updated some tests that intend to test the delambdafy transform to avoid use of specialized function types.
1 parent d14e065 commit f4a2528

11 files changed

+74
-35
lines changed

src/compiler/scala/tools/nsc/transform/UnCurry.scala

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ abstract class UnCurry extends InfoTransform
7070
private val noApply = mutable.HashSet[Tree]()
7171
private val newMembers = mutable.Map[Symbol, mutable.Buffer[Tree]]()
7272

73+
private lazy val forceSpecializationInfoTransformOfFunctionN: Unit = {
74+
if (currentRun.specializePhase != NoPhase) {
75+
exitingSpecialize {
76+
FunctionClass.seq.foreach(cls => cls.info)
77+
}
78+
}
79+
}
80+
7381
/** Add a new synthetic member for `currentOwner` */
7482
private def addNewMember(t: Tree): Unit =
7583
newMembers.getOrElseUpdate(currentOwner, mutable.Buffer()) += t
@@ -220,8 +228,16 @@ abstract class UnCurry extends InfoTransform
220228
def mkMethod(owner: Symbol, name: TermName, additionalFlags: FlagSet = NoFlags): DefDef =
221229
gen.mkMethodFromFunction(localTyper)(fun, owner, name, additionalFlags)
222230

223-
val canUseDelamdafyMethod = (inConstructorFlag == 0) // Avoiding synthesizing code prone to SI-6666, SI-8363 by using old-style lambda translation
231+
def isSpecialized = {
232+
forceSpecializationInfoTransformOfFunctionN
233+
val specialized = specializeTypes.specializedType(fun.tpe)
234+
!(specialized =:= fun.tpe)
235+
}
224236

237+
def canUseDelamdafyMethod = (
238+
(inConstructorFlag == 0) // Avoiding synthesizing code prone to SI-6666, SI-8363 by using old-style lambda translation
239+
&& !isSpecialized // DelambdafyTransformer currently only emits generic FunctionN-s, use the old style in the meantime
240+
)
225241
if (inlineFunctionExpansion || !canUseDelamdafyMethod) {
226242
val parents = addSerializable(abstractFunctionForFunctionType(fun.tpe))
227243
val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation SerialVersionUIDAnnotation
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
scala.runtime.AbstractFunction1$mcII$sp
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Ydelambdafy:method
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
object Test {
2+
def main(args: Array[String]): Unit = {
3+
val f = (x: Int) => -x
4+
println(f.getClass.getSuperclass.getName)
5+
}
6+
}

test/files/run/delambdafy_t6028.check

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
11
[[syntax trees at end of lambdalift]] // newSource1.scala
22
package <empty> {
33
class T extends Object {
4-
<paramaccessor> private[this] val classParam: Int = _;
5-
def <init>(classParam: Int): T = {
4+
<paramaccessor> private[this] val classParam: String = _;
5+
def <init>(classParam: String): T = {
66
T.super.<init>();
77
()
88
};
9-
private[this] val field: Int = 0;
10-
<stable> <accessor> def field(): Int = T.this.field;
11-
def foo(methodParam: Int): Function0 = {
12-
val methodLocal: Int = 0;
9+
private[this] val field: String = "";
10+
<stable> <accessor> def field(): String = T.this.field;
11+
def foo(methodParam: String): Function0 = {
12+
val methodLocal: String = "";
1313
{
1414
(() => T.this.$anonfun$1(methodParam, methodLocal)).$asInstanceOf[Function0]()
1515
}
1616
};
17-
def bar(barParam: Int): Object = {
17+
def bar(barParam: String): Object = {
1818
@volatile var MethodLocalObject$module: runtime.VolatileObjectRef = scala.runtime.VolatileObjectRef.zero();
1919
T.this.MethodLocalObject$1(barParam, MethodLocalObject$module)
2020
};
21-
def tryy(tryyParam: Int): Function0 = {
22-
var tryyLocal: runtime.IntRef = scala.runtime.IntRef.create(0);
21+
def tryy(tryyParam: String): Function0 = {
22+
var tryyLocal: runtime.ObjectRef = scala.runtime.ObjectRef.create("");
2323
{
24-
(() => T.this.$anonfun$2(tryyParam, tryyLocal)).$asInstanceOf[Function0]()
24+
(new <$anon: Function0>(T.this, tryyParam, tryyLocal): Function0)
2525
}
2626
};
27-
final <artifact> private[this] def $anonfun$1(methodParam$1: Int, methodLocal$1: Int): Int = T.this.classParam.+(T.this.field()).+(methodParam$1).+(methodLocal$1);
27+
final <artifact> private[this] def $anonfun$1(methodParam$1: String, methodLocal$1: String): String = T.this.classParam.+(T.this.field()).+(methodParam$1).+(methodLocal$1);
2828
abstract trait MethodLocalTrait$1 extends Object {
2929
<synthetic> <stable> <artifact> def $outer(): T
3030
};
3131
object MethodLocalObject$2 extends Object with T#MethodLocalTrait$1 {
32-
def <init>($outer: T, barParam$1: Int): T#MethodLocalObject$2.type = {
32+
def <init>($outer: T, barParam$1: String): T#MethodLocalObject$2.type = {
3333
MethodLocalObject$2.super.<init>();
3434
MethodLocalObject$2.this.$asInstanceOf[T#MethodLocalTrait$1$class]()./*MethodLocalTrait$1$class*/$init$(barParam$1);
3535
()
@@ -38,19 +38,34 @@ package <empty> {
3838
<synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer;
3939
<synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer
4040
};
41-
final <stable> private[this] def MethodLocalObject$1(barParam$1: Int, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = {
41+
final <stable> private[this] def MethodLocalObject$1(barParam$1: String, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = {
4242
MethodLocalObject$module$1.elem = new T#MethodLocalObject$2.type(T.this, barParam$1);
4343
MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]()
4444
};
4545
abstract trait MethodLocalTrait$1$class extends Object with T#MethodLocalTrait$1 {
46-
def /*MethodLocalTrait$1$class*/$init$(barParam$1: Int): Unit = {
46+
def /*MethodLocalTrait$1$class*/$init$(barParam$1: String): Unit = {
4747
()
4848
};
49-
scala.this.Predef.print(scala.Int.box(barParam$1))
49+
scala.this.Predef.print(barParam$1)
5050
};
51-
final <artifact> private[this] def $anonfun$2(tryyParam$1: Int, tryyLocal$1: runtime.IntRef): Unit = try {
52-
tryyLocal$1.elem = tryyParam$1
53-
} finally ()
51+
@SerialVersionUID(value = 0) final <synthetic> class $anonfun$tryy$1 extends scala.runtime.AbstractFunction0$mcV$sp with Serializable {
52+
def <init>($outer: T, tryyParam$1: String, tryyLocal$1: runtime.ObjectRef): <$anon: Function0> = {
53+
$anonfun$tryy$1.super.<init>();
54+
()
55+
};
56+
final def apply(): Unit = $anonfun$tryy$1.this.apply$mcV$sp();
57+
<specialized> def apply$mcV$sp(): Unit = try {
58+
$anonfun$tryy$1.this.tryyLocal$1.elem = $anonfun$tryy$1.this.tryyParam$1
59+
} finally ();
60+
<synthetic> <paramaccessor> <artifact> private[this] val $outer: T = _;
61+
<synthetic> <stable> <artifact> def $outer(): T = $anonfun$tryy$1.this.$outer;
62+
final <bridge> <artifact> def apply(): Object = {
63+
$anonfun$tryy$1.this.apply();
64+
scala.runtime.BoxedUnit.UNIT
65+
};
66+
<synthetic> <paramaccessor> private[this] val tryyParam$1: String = _;
67+
<synthetic> <paramaccessor> private[this] val tryyLocal$1: runtime.ObjectRef = _
68+
}
5469
}
5570
}
5671

test/files/run/delambdafy_t6028.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ object Test extends DirectTest {
55

66
override def extraSettings: String = "-usejavacp -Ydelambdafy:method -Xprint:lambdalift -d " + testOutput.path
77

8-
override def code = """class T(classParam: Int) {
9-
| val field: Int = 0
10-
| def foo(methodParam: Int) = {val methodLocal = 0 ; () => classParam + field + methodParam + methodLocal }
11-
| def bar(barParam: Int) = { trait MethodLocalTrait { print(barParam) }; object MethodLocalObject extends MethodLocalTrait; MethodLocalObject }
12-
| def tryy(tryyParam: Int) = { var tryyLocal = 0; () => try { tryyLocal = tryyParam } finally () }
8+
override def code = """class T(classParam: String) {
9+
| val field: String = ""
10+
| def foo(methodParam: String) = {val methodLocal = "" ; () => classParam + field + methodParam + methodLocal }
11+
| def bar(barParam: String) = { trait MethodLocalTrait { print(barParam) }; object MethodLocalObject extends MethodLocalTrait; MethodLocalObject }
12+
| def tryy(tryyParam: String) = { var tryyLocal = ""; () => try { tryyLocal = tryyParam } finally () }
1313
|}
1414
|""".stripMargin.trim
1515

test/files/run/delambdafy_t6555.check

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ package <empty> {
55
Foo.super.<init>();
66
()
77
};
8-
private[this] val f: Int => Int = {
9-
final <artifact> def $anonfun(param: Int): Int = param;
10-
((param: Int) => $anonfun(param))
8+
private[this] val f: String => String = {
9+
final <artifact> def $anonfun(param: String): String = param;
10+
((param: String) => $anonfun(param))
1111
};
12-
<stable> <accessor> def f(): Int => Int = Foo.this.f
12+
<stable> <accessor> def f(): String => String = Foo.this.f
1313
}
1414
}
1515

test/files/run/delambdafy_t6555.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ object Test extends DirectTest {
55

66
override def extraSettings: String = "-usejavacp -Xprint:specialize -Ydelambdafy:method -d " + testOutput.path
77

8-
override def code = "class Foo { val f = (param: Int) => param } "
8+
override def code = "class Foo { val f = (param: String) => param } "
99

1010
override def show(): Unit = {
1111
Console.withErr(System.out) {

test/files/run/delambdafy_uncurry_byname_method.check

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ package <empty> {
55
Foo.super.<init>();
66
()
77
};
8-
def bar(x: () => Int): Int = x.apply();
9-
def foo(): Int = Foo.this.bar({
10-
final <artifact> def $anonfun(): Int = 1;
8+
def bar(x: () => String): String = x.apply();
9+
def foo(): String = Foo.this.bar({
10+
final <artifact> def $anonfun(): String = "";
1111
(() => $anonfun())
1212
})
1313
}

test/files/run/delambdafy_uncurry_byname_method.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ object Test extends DirectTest {
66
override def extraSettings: String = "-usejavacp -Xprint:uncurry -Ydelambdafy:method -Ystop-after:uncurry -d " + testOutput.path
77

88
override def code = """class Foo {
9-
| def bar(x: => Int) = x
9+
| def bar(x: => String) = x
1010
|
11-
| def foo = bar(1)
11+
| def foo = bar("")
1212
|}
1313
|""".stripMargin.trim
1414

test/files/run/t9097.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ object Test extends StoreReporterDirectTest {
1515
override def code = """package o
1616
|package a {
1717
| class C {
18-
| def hihi = List(1,2).map(_ * 2)
18+
| def hihi = List(1,2).map(_ => "")
1919
| }
2020
|}
2121
|package object a {

0 commit comments

Comments
 (0)