summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2014-09-10 13:59:03 +0200
committerLukas Rytz <lukas.rytz@gmail.com>2014-09-12 12:07:42 +0200
commit63207e115a46634d47446a87a7f4bc3c2651b0e7 (patch)
treeb5b79a9faa7b2e9e9f1cbe56723374fe1508e0b5
parent2ac6dc03088ecec871838d23aeb688d0e3d4eefb (diff)
downloadscala-63207e115a46634d47446a87a7f4bc3c2651b0e7.tar.gz
scala-63207e115a46634d47446a87a7f4bc3c2651b0e7.tar.bz2
scala-63207e115a46634d47446a87a7f4bc3c2651b0e7.zip
isAnonymousClass/Function for delambdafy classes is not true
Ydelambdafy:method lambda classes are not anonymous classes, and not anonymous function classes either. They are somethig new, so there's a new predicate isDelambdafyFunction. They are not anonymous classes (or functions) because anonymous classes in Java speak are nested. Delambdafy classes are always top-level, they are just synthetic. Before this patch, isAnonymous was sometimes accidentailly true: if the lambda is nested in an anonymous class. Now it's always false.
-rwxr-xr-xbuild.xml9
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala13
-rw-r--r--src/compiler/scala/tools/nsc/transform/Delambdafy.scala28
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala23
-rw-r--r--src/reflect/scala/reflect/internal/Symbols.scala1
-rw-r--r--test/files/run/delambdafyLambdaClassNames.check1
-rw-r--r--test/files/run/delambdafyLambdaClassNames.flags1
-rw-r--r--test/files/run/delambdafyLambdaClassNames/A_1.scala5
-rw-r--r--test/files/run/delambdafyLambdaClassNames/Test.scala4
9 files changed, 52 insertions, 33 deletions
diff --git a/build.xml b/build.xml
index 6c750e530d..944feb2c10 100755
--- a/build.xml
+++ b/build.xml
@@ -1512,7 +1512,12 @@ TODO:
<!-- ===========================================================================
BINARY COMPATIBILITY TESTING
============================================================================ -->
- <target name="bc.init" depends="init" unless="maven-deps-done-mima">
+ <target name="bc.init" depends="init" if="test.bc.skip">
+ <!-- if test.bc.skip is set, make sure that pc.prepare is not executed either -->
+ <property name="maven-deps-done-mima" value="true"/>
+ </target>
+
+ <target name="bc.prepare" depends="bc.init" unless="maven-deps-done-mima">
<property name="bc-reference-version" value="2.11.0"/>
<property name="bc-build.dir" value="${build.dir}/bc"/>
@@ -1530,7 +1535,7 @@ TODO:
</target>
<target name="test.bc-opt" description="Optimized version of test.bc."> <optimized name="test.bc"/></target>
- <target name="test.bc" depends="bc.init, pack.lib, pack.reflect">
+ <target name="test.bc" depends="bc.prepare, pack.lib, pack.reflect" unless="test.bc.skip">
<bc.check project="library"/>
<bc.check project="reflect"/>
</target>
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
index e5b4c4a6c2..0c0d726630 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
@@ -22,14 +22,11 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
* null.
*/
def isAnonymousOrLocalClass(classSym: Symbol): Boolean = {
- def isDelambdafyLambdaClass(classSym: Symbol): Boolean = {
- classSym.isAnonymousFunction && (settings.Ydelambdafy.value == "method")
- }
-
assert(classSym.isClass, s"not a class: $classSym")
-
- !isDelambdafyLambdaClass(classSym) &&
- (classSym.isAnonymousClass || !classSym.originalOwner.isClass)
+ val res = (classSym.isAnonymousClass || !classSym.originalOwner.isClass)
+ // lambda classes are always top-level classes.
+ if (res) assert(!classSym.isDelambdafyFunction)
+ res
}
/**
@@ -81,7 +78,7 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
final case class EnclosingMethodEntry(owner: String, name: String, methodDescriptor: String)
/**
- * If data for emitting an EnclosingMethod attribute. None if `classSym` is a member class (not
+ * Data for emitting an EnclosingMethod attribute. None if `classSym` is a member class (not
* an anonymous or local class). See doc in BTypes.
*
* The class is parametrized by two functions to obtain a bytecode class descriptor for a class
diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
index 9cc8d779a0..12e7b23f48 100644
--- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
+++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
@@ -245,18 +245,22 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
// - make `anonClass.isAnonymousClass` true.
// - use `newAnonymousClassSymbol` or push the required variations into a similar factory method
// - reinstate the assertion in `Erasure.resolveAnonymousBridgeClash`
- val suffix = "$lambda$" + (
+ val suffix = nme.DELAMBDAFY_LAMBDA_CLASS_NAME + "$" + (
if (funOwner.isPrimaryConstructor) ""
else "$" + funOwner.name + "$"
)
- val name = unit.freshTypeName(s"${oldClass.name.decode}$suffix")
+ val oldClassPart = oldClass.name.decode
+ // make sure the class name doesn't contain $anon, otherwsie isAnonymousClass/Function may be true
+ val name = unit.freshTypeName(s"$oldClassPart$suffix".replace("$anon", "$nestedInAnon"))
- val anonClass = pkg newClassSymbol(name, originalFunction.pos, FINAL | SYNTHETIC) addAnnotation SerialVersionUIDAnnotation
- anonClass setInfo ClassInfoType(parents, newScope, anonClass)
+ val lambdaClass = pkg newClassSymbol(name, originalFunction.pos, FINAL | SYNTHETIC) addAnnotation SerialVersionUIDAnnotation
+ lambdaClass setInfo ClassInfoType(parents, newScope, lambdaClass)
+ assert(!lambdaClass.isAnonymousClass && !lambdaClass.isAnonymousFunction, "anonymous class name: "+ lambdaClass.name)
+ assert(lambdaClass.isDelambdafyFunction, "not lambda class name: " + lambdaClass.name)
val captureProxies2 = new LinkedHashMap[Symbol, TermSymbol]
captures foreach {capture =>
- val sym = anonClass.newVariable(capture.name.toTermName, capture.pos, SYNTHETIC)
+ val sym = lambdaClass.newVariable(capture.name.toTermName, capture.pos, SYNTHETIC)
sym setInfo capture.info
captureProxies2 += ((capture, sym))
}
@@ -266,30 +270,30 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
val thisProxy = {
val target = targetMethod(originalFunction)
if (thisReferringMethods contains target) {
- val sym = anonClass.newVariable(nme.FAKE_LOCAL_THIS, originalFunction.pos, SYNTHETIC)
+ val sym = lambdaClass.newVariable(nme.FAKE_LOCAL_THIS, originalFunction.pos, SYNTHETIC)
sym.info = oldClass.tpe
sym
} else NoSymbol
}
- val decapturify = new DeCapturifyTransformer(captureProxies2, unit, oldClass, anonClass, originalFunction.symbol.pos, thisProxy)
+ val decapturify = new DeCapturifyTransformer(captureProxies2, unit, oldClass, lambdaClass, originalFunction.symbol.pos, thisProxy)
val accessorMethod = createAccessorMethod(thisProxy, originalFunction)
val decapturedFunction = decapturify.transform(originalFunction).asInstanceOf[Function]
val members = (optionSymbol(thisProxy).toList ++ (captureProxies2 map (_._2))) map {member =>
- anonClass.info.decls enter member
+ lambdaClass.info.decls enter member
ValDef(member, gen.mkZero(member.tpe)) setPos decapturedFunction.pos
}
// constructor
- val constr = createConstructor(anonClass, members)
+ val constr = createConstructor(lambdaClass, members)
// apply method with same arguments and return type as original lambda.
- val applyMethodDef = createApplyMethod(anonClass, decapturedFunction, accessorMethod, thisProxy)
+ val applyMethodDef = createApplyMethod(lambdaClass, decapturedFunction, accessorMethod, thisProxy)
- val bridgeMethod = createBridgeMethod(anonClass, originalFunction, applyMethodDef)
+ val bridgeMethod = createBridgeMethod(lambdaClass, originalFunction, applyMethodDef)
def fulldef(sym: Symbol) =
if (sym == NoSymbol) sym.toString
@@ -305,7 +309,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
val body = members ++ List(constr, applyMethodDef) ++ bridgeMethod
// TODO if member fields are private this complains that they're not accessible
- (localTyper.typedPos(decapturedFunction.pos)(ClassDef(anonClass, body)).asInstanceOf[ClassDef], thisProxy, accessorMethod)
+ (localTyper.typedPos(decapturedFunction.pos)(ClassDef(lambdaClass, body)).asInstanceOf[ClassDef], thisProxy, accessorMethod)
}
val (anonymousClassDef, thisProxy, accessorMethod) = makeAnonymousClass
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index d203218c09..99ff6a10b4 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -99,17 +99,18 @@ trait StdNames {
val SINGLETON_SUFFIX: String = ".type"
- val ANON_CLASS_NAME: NameType = "$anon"
- val ANON_FUN_NAME: NameType = "$anonfun"
- val EMPTY: NameType = ""
- val EMPTY_PACKAGE_NAME: NameType = "<empty>"
- val IMPL_CLASS_SUFFIX = "$class"
- val IMPORT: NameType = "<import>"
- val MODULE_SUFFIX_NAME: NameType = MODULE_SUFFIX_STRING
- val MODULE_VAR_SUFFIX: NameType = "$module"
- val PACKAGE: NameType = "package"
- val ROOT: NameType = "<root>"
- val SPECIALIZED_SUFFIX: NameType = "$sp"
+ val ANON_CLASS_NAME: NameType = "$anon"
+ val DELAMBDAFY_LAMBDA_CLASS_NAME: NameType = "$lambda"
+ val ANON_FUN_NAME: NameType = "$anonfun"
+ val EMPTY: NameType = ""
+ val EMPTY_PACKAGE_NAME: NameType = "<empty>"
+ val IMPL_CLASS_SUFFIX = "$class"
+ val IMPORT: NameType = "<import>"
+ val MODULE_SUFFIX_NAME: NameType = MODULE_SUFFIX_STRING
+ val MODULE_VAR_SUFFIX: NameType = "$module"
+ val PACKAGE: NameType = "package"
+ val ROOT: NameType = "<root>"
+ val SPECIALIZED_SUFFIX: NameType = "$sp"
// value types (and AnyRef) are all used as terms as well
// as (at least) arguments to the @specialize annotation.
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index 2670faa22d..44fce2c9ab 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -789,6 +789,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
isMethod && owner.isDerivedValueClass && !isParamAccessor && !isConstructor && !hasFlag(SUPERACCESSOR) && !isMacro && !isSpecialized
final def isAnonymousFunction = isSynthetic && (name containsName tpnme.ANON_FUN_NAME)
+ final def isDelambdafyFunction = isSynthetic && (name containsName tpnme.DELAMBDAFY_LAMBDA_CLASS_NAME)
final def isDefinedInPackage = effectiveOwner.isPackageClass
final def needsFlatClasses = phase.flatClasses && rawowner != NoSymbol && !rawowner.isPackageClass
diff --git a/test/files/run/delambdafyLambdaClassNames.check b/test/files/run/delambdafyLambdaClassNames.check
new file mode 100644
index 0000000000..d425d15dd0
--- /dev/null
+++ b/test/files/run/delambdafyLambdaClassNames.check
@@ -0,0 +1 @@
+A$$nestedInAnon$1$lambda$$run$1
diff --git a/test/files/run/delambdafyLambdaClassNames.flags b/test/files/run/delambdafyLambdaClassNames.flags
new file mode 100644
index 0000000000..b10233d322
--- /dev/null
+++ b/test/files/run/delambdafyLambdaClassNames.flags
@@ -0,0 +1 @@
+-Ybackend:GenBCode -Ydelambdafy:method \ No newline at end of file
diff --git a/test/files/run/delambdafyLambdaClassNames/A_1.scala b/test/files/run/delambdafyLambdaClassNames/A_1.scala
new file mode 100644
index 0000000000..10489414b7
--- /dev/null
+++ b/test/files/run/delambdafyLambdaClassNames/A_1.scala
@@ -0,0 +1,5 @@
+class A {
+ def f = new Runnable {
+ def run(): Unit = List(1,2).foreach(println)
+ }
+}
diff --git a/test/files/run/delambdafyLambdaClassNames/Test.scala b/test/files/run/delambdafyLambdaClassNames/Test.scala
new file mode 100644
index 0000000000..49a397d1d2
--- /dev/null
+++ b/test/files/run/delambdafyLambdaClassNames/Test.scala
@@ -0,0 +1,4 @@
+object Test extends App {
+ val c = Class.forName("A$$nestedInAnon$1$lambda$$run$1")
+ println(c.getName)
+}