summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/CompilationUnits.scala6
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala82
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala11
-rw-r--r--src/compiler/scala/tools/nsc/transform/LambdaLift.scala21
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala17
-rw-r--r--test/files/jvm/innerClassAttribute.check30
-rw-r--r--test/files/jvm/innerClassAttribute/Classes_1.scala38
-rw-r--r--test/files/jvm/innerClassAttribute/Test.scala78
-rw-r--r--test/files/jvm/t9105.check18
-rw-r--r--test/files/jvm/t9105.scala22
12 files changed, 286 insertions, 43 deletions
diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala
index 1a6843a249..6be1fda1b5 100644
--- a/src/compiler/scala/tools/nsc/CompilationUnits.scala
+++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala
@@ -25,9 +25,9 @@ trait CompilationUnits { global: Global =>
class CompilationUnit(val source: SourceFile) extends CompilationUnitContextApi { self =>
/** the fresh name creator */
- implicit val fresh: FreshNameCreator = new FreshNameCreator
- def freshTermName(prefix: String = "x$") = global.freshTermName(prefix)
- def freshTypeName(prefix: String) = global.freshTypeName(prefix)
+ implicit val fresh: FreshNameCreator = new FreshNameCreator
+ def freshTermName(prefix: String = nme.FRESH_TERM_NAME_PREFIX) = global.freshTermName(prefix)
+ def freshTypeName(prefix: String) = global.freshTypeName(prefix)
/** the content of the compilation unit in tree form */
var body: Tree = EmptyTree
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
index a5f33aa786..90d2fcf4dc 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
@@ -22,6 +22,23 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
}
/**
+ * Cache the value of delambdafy == "inline" for each run. We need to query this value many
+ * times, so caching makes sense.
+ */
+ object delambdafyInline {
+ private var runId = -1
+ private var value = false
+
+ def apply(): Boolean = {
+ if (runId != global.currentRunId) {
+ runId = global.currentRunId
+ value = settings.Ydelambdafy.value == "inline"
+ }
+ value
+ }
+ }
+
+ /**
* True if `classSym` is an anonymous class or a local class. I.e., false if `classSym` is a
* member class. This method is used to decide if we should emit an EnclosingMethod attribute.
* It is also used to decide whether the "owner" field in the InnerClass attribute should be
@@ -29,10 +46,59 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
*/
def isAnonymousOrLocalClass(classSym: Symbol): Boolean = {
assert(classSym.isClass, s"not a class: $classSym")
- // Here used to be an `assert(!classSym.isDelambdafyFunction)`: delambdafy lambda classes are
- // always top-level. However, SI-8900 shows an example where the weak name-based implementation
- // of isDelambdafyFunction failed (for a function declared in a package named "lambda").
- classSym.isAnonymousClass || !classSym.originalOwner.isClass
+ val r = exitingPickler(classSym.isAnonymousClass) || !classSym.originalOwner.isClass
+ if (r && settings.Ybackend.value == "GenBCode") {
+ // this assertion only holds in GenBCode. lambda lift renames symbols and may accidentally
+ // introduce `$lambda` into a class name, making `isDelambdafyFunction` true. under GenBCode
+ // we prevent this, see `nonAnon` in LambdaLift.
+ // phase travel necessary: after flatten, the name includes the name of outer classes.
+ // if some outer name contains $lambda, a non-lambda class is considered lambda.
+ assert(exitingPickler(!classSym.isDelambdafyFunction), classSym.name)
+ }
+ r
+ }
+
+ /**
+ * The next enclosing definition in the source structure. Includes anonymous function classes
+ * under delambdafy:inline, even though they are only generated during UnCurry.
+ */
+ def nextEnclosing(sym: Symbol): Symbol = {
+ val origOwner = sym.originalOwner
+ // phase travel necessary: after flatten, the name includes the name of outer classes.
+ // if some outer name contains $anon, a non-anon class is considered anon.
+ if (delambdafyInline() && sym.rawowner.isAnonymousFunction) {
+ // SI-9105: special handling for anonymous functions under delambdafy:inline.
+ //
+ // class C { def t = () => { def f { class Z } } }
+ //
+ // class C { def t = byNameMethod { def f { class Z } } }
+ //
+ // In both examples, the method f lambda-lifted into the anonfun class.
+ //
+ // In both examples, the enclosing method of Z is f, the enclosing class is the anonfun.
+ // So nextEnclosing needs to return the following chain: Z - f - anonFunClassSym - ...
+ //
+ // In the first example, the initial owner of f is a TermSymbol named "$anonfun" (note: not the anonFunClassSym!)
+ // In the second, the initial owner of f is t (no anon fun term symbol for by-name args!).
+ //
+ // In both cases, the rawowner of class Z is the anonFunClassSym. So the check in the `if`
+ // above makes sure we don't jump over the anonymous function in the by-name argument case.
+ //
+ // However, we cannot directly return the rawowner: if `sym` is Z, we need to include method f
+ // in the result. This is done by comparing the rawowners (read: lambdalift-targets) of `sym`
+ // and `sym.originalOwner`: if they are the same, then the originalOwner is "in between", and
+ // we need to return it.
+ // If the rawowners are different, the symbol was not in between. In the first example, the
+ // originalOwner of `f` is the anonfun-term-symbol, whose rawowner is C. So the nextEnclosing
+ // of `f` is its rawowner, the anonFunClassSym.
+ //
+ // In delambdafy:method we don't have that problem. The f method is lambda-lifted into C,
+ // not into the anonymous function class. The originalOwner chain is Z - f - C.
+ if (sym.originalOwner.rawowner == sym.rawowner) sym.originalOwner
+ else sym.rawowner
+ } else {
+ origOwner
+ }
}
/**
@@ -63,9 +129,9 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
def enclosingMethod(sym: Symbol): Option[Symbol] = {
if (sym.isClass || sym == NoSymbol) None
else if (sym.isMethod) Some(sym)
- else enclosingMethod(sym.originalOwner)
+ else enclosingMethod(nextEnclosing(sym))
}
- enclosingMethod(classSym.originalOwner)
+ enclosingMethod(nextEnclosing(classSym))
}
/**
@@ -76,9 +142,9 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
assert(classSym.isClass, classSym)
def enclosingClass(sym: Symbol): Symbol = {
if (sym.isClass) sym
- else enclosingClass(sym.originalOwner)
+ else enclosingClass(nextEnclosing(sym))
}
- enclosingClass(classSym.originalOwner)
+ enclosingClass(nextEnclosing(classSym))
}
final case class EnclosingMethodEntry(owner: String, name: String, methodDescriptor: String)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
index a9bce82acd..7814ed858b 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -527,7 +527,7 @@ abstract class BTypes {
* local and anonymous classes, no matter if there is an enclosing method or not. Accordingly, the
* "class" field (see below) must be always defined, while the "method" field may be null.
*
- * NOTE: When a EnclosingMethod attribute is requried (local and anonymous classes), the "outer"
+ * NOTE: When an EnclosingMethod attribute is requried (local and anonymous classes), the "outer"
* field in the InnerClass table must be null.
*
* Fields:
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
index 94f9b585d9..bc880e002e 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
@@ -227,7 +227,9 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
}
val innerName: Option[String] = {
- if (innerClassSym.isAnonymousClass || innerClassSym.isAnonymousFunction) None
+ // phase travel necessary: after flatten, the name includes the name of outer classes.
+ // if some outer name contains $anon, a non-anon class is considered anon.
+ if (exitingPickler(innerClassSym.isAnonymousClass || innerClassSym.isAnonymousFunction)) None
else Some(innerClassSym.rawname + innerClassSym.moduleSuffix) // moduleSuffix for module classes
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index abe3bc512c..c36afd018b 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -692,11 +692,12 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
}
}
- def innerName(innerSym: Symbol): String =
- if (innerSym.isAnonymousClass || innerSym.isAnonymousFunction)
- null
- else
- innerSym.rawname + innerSym.moduleSuffix
+ def innerName(innerSym: Symbol): String = {
+ // phase travel necessary: after flatten, the name includes the name of outer classes.
+ // if some outer name contains $anon, a non-anon class is considered anon.
+ if (exitingPickler(innerSym.isAnonymousClass || innerSym.isAnonymousFunction)) null
+ else innerSym.rawname + innerSym.moduleSuffix
+ }
innerClassBuffer ++= {
val members = exitingPickler(memberClassesOf(csym))
diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
index fa0c1f797b..e9fd6e14a9 100644
--- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
+++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
@@ -250,21 +250,30 @@ abstract class LambdaLift extends InfoTransform {
debuglog("renaming in %s: %s => %s".format(sym.owner.fullLocationString, originalName, sym.name))
}
+ // make sure that the name doesn't make the symbol accidentally `isAnonymousClass` (et.al) by
+ // introducing `$anon` in its name. to be cautious, we don't make this change in the default
+ // backend under 2.11.x, so only in GenBCode.
+ def nonAnon(s: String) = if (settings.Ybackend.value == "GenBCode") nme.ensureNonAnon(s) else s
+
def newName(sym: Symbol): Name = {
val originalName = sym.name
def freshen(prefix: String): Name =
if (originalName.isTypeName) unit.freshTypeName(prefix)
else unit.freshTermName(prefix)
+ val join = nme.NAME_JOIN_STRING
if (sym.isAnonymousFunction && sym.owner.isMethod) {
- freshen(sym.name + nme.NAME_JOIN_STRING + sym.owner.name + nme.NAME_JOIN_STRING)
+ freshen(sym.name + join + nonAnon(sym.owner.name.toString) + join)
} else {
+ val name = freshen(sym.name + join)
// SI-5652 If the lifted symbol is accessed from an inner class, it will be made public. (where?)
- // Generating a unique name, mangled with the enclosing class name, avoids a VerifyError
- // in the case that a sub-class happens to lifts out a method with the *same* name.
- val name = freshen("" + sym.name + nme.NAME_JOIN_STRING)
- if (originalName.isTermName && !sym.enclClass.isImplClass && calledFromInner(sym)) nme.expandedName(name.toTermName, sym.enclClass)
- else name
+ // Generating a unique name, mangled with the enclosing full class name (including
+ // package - subclass might have the same name), avoids a VerifyError in the case
+ // that a sub-class happens to lifts out a method with the *same* name.
+ if (originalName.isTermName && !sym.enclClass.isImplClass && calledFromInner(sym))
+ newTermNameCached(nonAnon(sym.enclClass.fullName('$')) + nme.EXPAND_SEPARATOR_STRING + name)
+ else
+ name
}
}
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index c94ee996e4..f32b7326fe 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -112,6 +112,23 @@ trait StdNames {
val ROOT: NameType = "<root>"
val SPECIALIZED_SUFFIX: NameType = "$sp"
+ val NESTED_IN: String = "$nestedIn"
+ val NESTED_IN_ANON_CLASS: String = NESTED_IN + ANON_CLASS_NAME.toString.replace("$", "")
+ val NESTED_IN_ANON_FUN: String = NESTED_IN + ANON_FUN_NAME.toString.replace("$", "")
+ val NESTED_IN_LAMBDA: String = NESTED_IN + DELAMBDAFY_LAMBDA_CLASS_NAME.toString.replace("$", "")
+
+ /**
+ * Ensures that name mangling does not accidentally make a class respond `true` to any of
+ * isAnonymousClass, isAnonymousFunction, isDelambdafyFunction, e.g. by introducing "$anon".
+ */
+ def ensureNonAnon(name: String) = {
+ name
+ .replace(nme.ANON_CLASS_NAME.toString, NESTED_IN_ANON_CLASS)
+ .replace(nme.ANON_FUN_NAME.toString, NESTED_IN_ANON_FUN)
+ .replace(nme.DELAMBDAFY_LAMBDA_CLASS_NAME.toString, NESTED_IN_LAMBDA)
+ }
+
+
// value types (and AnyRef) are all used as terms as well
// as (at least) arguments to the @specialize annotation.
final val Boolean: NameType = "Boolean"
diff --git a/test/files/jvm/innerClassAttribute.check b/test/files/jvm/innerClassAttribute.check
index 20518aa49e..8395ff0b09 100644
--- a/test/files/jvm/innerClassAttribute.check
+++ b/test/files/jvm/innerClassAttribute.check
@@ -14,27 +14,27 @@ A19 / null / null
A19 / null / null
A19 / null / null
-- A20 --
-A20$$anonfun$4 / null / null / 17
+A20$$anonfun$6 / null / null / 17
fun1: attribute for itself and the two child closures `() => ()` and `() => () => 1`
-A20$$anonfun$4 / null / null / 17
-A20$$anonfun$4$$anonfun$apply$1 / null / null / 17
-A20$$anonfun$4$$anonfun$apply$3 / null / null / 17
+A20$$anonfun$6 / null / null / 17
+A20$$anonfun$6$$anonfun$apply$1 / null / null / 17
+A20$$anonfun$6$$anonfun$apply$3 / null / null / 17
fun2 () => (): itself and the outer closure
-A20$$anonfun$4 / null / null / 17
-A20$$anonfun$4$$anonfun$apply$1 / null / null / 17
+A20$$anonfun$6 / null / null / 17
+A20$$anonfun$6$$anonfun$apply$1 / null / null / 17
fun3 () => () => (): itself, the outer closure and its child closure
-A20$$anonfun$4 / null / null / 17
-A20$$anonfun$4$$anonfun$apply$3 / null / null / 17
-A20$$anonfun$4$$anonfun$apply$3$$anonfun$apply$2 / null / null / 17
+A20$$anonfun$6 / null / null / 17
+A20$$anonfun$6$$anonfun$apply$3 / null / null / 17
+A20$$anonfun$6$$anonfun$apply$3$$anonfun$apply$2 / null / null / 17
fun4: () => 1: itself and the two outer closures
-A20$$anonfun$4 / null / null / 17
-A20$$anonfun$4$$anonfun$apply$3 / null / null / 17
-A20$$anonfun$4$$anonfun$apply$3$$anonfun$apply$2 / null / null / 17
+A20$$anonfun$6 / null / null / 17
+A20$$anonfun$6$$anonfun$apply$3 / null / null / 17
+A20$$anonfun$6$$anonfun$apply$3$$anonfun$apply$2 / null / null / 17
enclosing: nested closures have the apply method of the outer closure
A20 / null / null
-A20$$anonfun$4 / apply / ()Lscala/Function0;
-A20$$anonfun$4 / apply / ()Lscala/Function0;
-A20$$anonfun$4$$anonfun$apply$3 / apply / ()Lscala/Function0;
+A20$$anonfun$6 / apply / ()Lscala/Function0;
+A20$$anonfun$6 / apply / ()Lscala/Function0;
+A20$$anonfun$6$$anonfun$apply$3 / apply / ()Lscala/Function0;
#partest -Ydelambdafy:method
-- A4 --
null / null / null
diff --git a/test/files/jvm/innerClassAttribute/Classes_1.scala b/test/files/jvm/innerClassAttribute/Classes_1.scala
index 9c3ea7f013..a2ade70c18 100644
--- a/test/files/jvm/innerClassAttribute/Classes_1.scala
+++ b/test/files/jvm/innerClassAttribute/Classes_1.scala
@@ -185,3 +185,41 @@ trait A24 extends A24Base {
override object Conc extends A24Sym
}
}
+
+class SI_9105 {
+ // the EnclosingMethod attributes depend on the delambdafy strategy (inline vs method)
+
+ // outerClass-inline enclMeth-inline outerClass-method enclMeth-method
+ val fun = () => {
+ class A // closure null (*) SI_9105 null
+ def m: Object = { class B; new B } // closure m$1 SI_9105 m$1
+ val f: Object = { class C; new C } // closure null (*) SI_9105 null
+ }
+ def met = () => {
+ class D // closure null (*) SI_9105 met
+ def m: Object = { class E; new E } // closure m$1 SI_9105 m$1
+ val f: Object = { class F; new F } // closure null (*) SI_9105 met
+ }
+
+ // (*) the originalOwner chain of A (similar for D) is: SI_9105.fun.$anonfun-value.A
+ // we can get to the anonfun-class (created by uncurry), but not to the apply method.
+ //
+ // for C and F, the originalOwner chain is fun.$anonfun-value.f.C. at later phases, the rawowner of f is
+ // an apply$sp method of the closure class. we could use that as enclosing method, but it would be unsystematic
+ // (A / D don't have an encl meth either), and also strange to use the $sp, which is a compilation artifact.
+ // So using `null` looks more like the situation in the source code: C / F are nested classes of the anon-fun, and
+ // there's no method in between.
+
+ def byName[T](op: => T) = 0
+
+ val bnV = byName {
+ class G // closure null (*) SI_9105 null
+ def m: Object = { class H; new H } // closure m$1 SI_9105 m$1
+ val f: Object = { class I; new I } // closure null (*) SI_9105 null
+ }
+ def bnM = byName {
+ class J // closure null (*) SI_9105 bnM
+ def m: Object = { class K; new K } // closure m$1 SI_9105 m$1
+ val f: Object = { class L; new L } // closure null (*) SI_9105 bnM
+ }
+}
diff --git a/test/files/jvm/innerClassAttribute/Test.scala b/test/files/jvm/innerClassAttribute/Test.scala
index 3820048cb4..30dc412ff7 100644
--- a/test/files/jvm/innerClassAttribute/Test.scala
+++ b/test/files/jvm/innerClassAttribute/Test.scala
@@ -16,6 +16,8 @@ object Test extends BytecodeTest {
loadClassNode(className).innerClasses.asScala.toList.sortBy(_.name)
}
+ def ownInnerClassNode(n: String) = innerClassNodes(n).filter(_.name == n).head
+
final case class EnclosingMethod(name: String, descriptor: String, outerClass: String)
def enclosingMethod(className: String) = {
val n = loadClassNode(className)
@@ -256,10 +258,10 @@ object Test extends BytecodeTest {
printInnerClassNodes("A20")
- val fun1 = lambdaClass("A20$$anonfun$4", "A20$lambda$1")
- val fun2 = lambdaClass("A20$$anonfun$4$$anonfun$apply$1", "A20$lambda$$$nestedInAnonfun$5$1")
- val fun3 = lambdaClass("A20$$anonfun$4$$anonfun$apply$3", "A20$lambda$$$nestedInAnonfun$5$2")
- val fun4 = lambdaClass("A20$$anonfun$4$$anonfun$apply$3$$anonfun$apply$2", "A20$lambda$$$nestedInAnonfun$7$1")
+ val fun1 = lambdaClass("A20$$anonfun$6", "A20$lambda$1")
+ val fun2 = lambdaClass("A20$$anonfun$6$$anonfun$apply$1", "A20$lambda$$$nestedInAnonfun$5$1")
+ val fun3 = lambdaClass("A20$$anonfun$6$$anonfun$apply$3", "A20$lambda$$$nestedInAnonfun$5$2")
+ val fun4 = lambdaClass("A20$$anonfun$6$$anonfun$apply$3$$anonfun$apply$2", "A20$lambda$$$nestedInAnonfun$7$1")
println("fun1: attribute for itself and the two child closures `() => ()` and `() => () => 1`")
printInnerClassNodes(fun1)
@@ -324,6 +326,73 @@ object Test extends BytecodeTest {
assertMember(defsApiImpl, "A24Base", "DefinitionsApi$class", flags = Flags.ACC_PUBLIC | Flags.ACC_ABSTRACT)
}
+ def testSI_9105() {
+ val isDelambdafyMethod = classpath.findClass("SI_9105$lambda$1").isDefined
+ if (isDelambdafyMethod) {
+ assertEnclosingMethod ("SI_9105$A$2" , "SI_9105", null , null)
+ assertEnclosingMethod ("SI_9105$B$5" , "SI_9105", "m$1", "()Ljava/lang/Object;")
+ assertEnclosingMethod ("SI_9105$C$1" , "SI_9105", null , null)
+ assertEnclosingMethod ("SI_9105$D$1" , "SI_9105", "met", "()Lscala/Function0;")
+ assertEnclosingMethod ("SI_9105$E$1" , "SI_9105", "m$3", "()Ljava/lang/Object;")
+ assertEnclosingMethod ("SI_9105$F$1" , "SI_9105", "met", "()Lscala/Function0;")
+ assertNoEnclosingMethod("SI_9105$lambda$$met$1")
+ assertNoEnclosingMethod("SI_9105$lambda$1")
+ assertNoEnclosingMethod("SI_9105")
+
+ assertLocal(innerClassNodes("SI_9105$A$2").head, "SI_9105$A$2", "A$2")
+ assertLocal(innerClassNodes("SI_9105$B$5").head, "SI_9105$B$5", "B$5")
+ assertLocal(innerClassNodes("SI_9105$C$1").head, "SI_9105$C$1", "C$1")
+ assertLocal(innerClassNodes("SI_9105$D$1").head, "SI_9105$D$1", "D$1")
+ assertLocal(innerClassNodes("SI_9105$E$1").head, "SI_9105$E$1", "E$1")
+ assertLocal(innerClassNodes("SI_9105$F$1").head, "SI_9105$F$1", "F$1")
+
+ // by-name
+ assertEnclosingMethod("SI_9105$G$1", "SI_9105", null , null)
+ assertEnclosingMethod("SI_9105$H$1", "SI_9105", "m$2", "()Ljava/lang/Object;")
+ assertEnclosingMethod("SI_9105$I$1", "SI_9105", null , null)
+ assertEnclosingMethod("SI_9105$J$1", "SI_9105", "bnM", "()I")
+ assertEnclosingMethod("SI_9105$K$2", "SI_9105", "m$4", "()Ljava/lang/Object;")
+ assertEnclosingMethod("SI_9105$L$1", "SI_9105", "bnM", "()I")
+
+ assert(innerClassNodes("SI_9105$lambda$$met$1").isEmpty)
+ assert(innerClassNodes("SI_9105$lambda$1").isEmpty)
+ assert(innerClassNodes("SI_9105").length == 12) // the 12 local classes
+ } else {
+ // comment in innerClassAttribute/Classes_1.scala explains the difference between A / C and D / F.
+ assertEnclosingMethod ("SI_9105$$anonfun$4$A$2" , "SI_9105$$anonfun$4" , null , null)
+ assertEnclosingMethod ("SI_9105$$anonfun$4$B$5" , "SI_9105$$anonfun$4" , "m$1" , "()Ljava/lang/Object;")
+ assertEnclosingMethod ("SI_9105$$anonfun$4$C$1" , "SI_9105$$anonfun$4" , null , null)
+ assertEnclosingMethod ("SI_9105$$anonfun$met$1$D$1", "SI_9105$$anonfun$met$1", null , null)
+ assertEnclosingMethod ("SI_9105$$anonfun$met$1$E$1", "SI_9105$$anonfun$met$1", "m$3" , "()Ljava/lang/Object;")
+ assertEnclosingMethod ("SI_9105$$anonfun$met$1$F$1", "SI_9105$$anonfun$met$1", null , null)
+ assertEnclosingMethod ("SI_9105$$anonfun$4" , "SI_9105" , null , null)
+ assertEnclosingMethod ("SI_9105$$anonfun$met$1" , "SI_9105" , "met" , "()Lscala/Function0;")
+ assertNoEnclosingMethod("SI_9105")
+
+ assertLocal(ownInnerClassNode("SI_9105$$anonfun$4$A$2"), "SI_9105$$anonfun$4$A$2" , "A$2")
+ assertLocal(ownInnerClassNode("SI_9105$$anonfun$4$B$5"), "SI_9105$$anonfun$4$B$5" , "B$5")
+ assertLocal(ownInnerClassNode("SI_9105$$anonfun$4$C$1"), "SI_9105$$anonfun$4$C$1" , "C$1")
+ assertLocal(ownInnerClassNode("SI_9105$$anonfun$met$1$D$1"), "SI_9105$$anonfun$met$1$D$1", "D$1")
+ assertLocal(ownInnerClassNode("SI_9105$$anonfun$met$1$E$1"), "SI_9105$$anonfun$met$1$E$1", "E$1")
+ assertLocal(ownInnerClassNode("SI_9105$$anonfun$met$1$F$1"), "SI_9105$$anonfun$met$1$F$1", "F$1")
+
+ // by-name
+ assertEnclosingMethod("SI_9105$$anonfun$5$G$1", "SI_9105$$anonfun$5", null, null)
+ assertEnclosingMethod("SI_9105$$anonfun$5$H$1", "SI_9105$$anonfun$5", "m$2", "()Ljava/lang/Object;")
+ assertEnclosingMethod("SI_9105$$anonfun$5$I$1", "SI_9105$$anonfun$5", null, null)
+ assertEnclosingMethod("SI_9105$$anonfun$bnM$1$J$1", "SI_9105$$anonfun$bnM$1", null, null)
+ assertEnclosingMethod("SI_9105$$anonfun$bnM$1$K$2", "SI_9105$$anonfun$bnM$1", "m$4", "()Ljava/lang/Object;")
+ assertEnclosingMethod("SI_9105$$anonfun$bnM$1$L$1", "SI_9105$$anonfun$bnM$1", null, null)
+
+ assertAnonymous(ownInnerClassNode("SI_9105$$anonfun$4"), "SI_9105$$anonfun$4")
+ assertAnonymous(ownInnerClassNode("SI_9105$$anonfun$met$1"), "SI_9105$$anonfun$met$1")
+
+ assert(innerClassNodes("SI_9105$$anonfun$4").length == 4) // itself and three of the local classes
+ assert(innerClassNodes("SI_9105$$anonfun$met$1").length == 4) // itself and three of the local classes
+ assert(innerClassNodes("SI_9105").length == 4) // the four anon funs
+ }
+ }
+
def show(): Unit = {
testA1()
testA2()
@@ -347,5 +416,6 @@ object Test extends BytecodeTest {
testA22()
testA23()
testA24()
+ testSI_9105()
}
}
diff --git a/test/files/jvm/t9105.check b/test/files/jvm/t9105.check
new file mode 100644
index 0000000000..34750833f1
--- /dev/null
+++ b/test/files/jvm/t9105.check
@@ -0,0 +1,18 @@
+#partest !-Ydelambdafy:method
+(class C$$anonfun$1$A$1,class C$$anonfun$1,null)
+(class C$$anonfun$1$B$1,class C$$anonfun$1,private final java.lang.Object C$$anonfun$1.m$1())
+(class C$$anonfun$1$C$1,class C$$anonfun$1,null)
+(class C$$anonfun$1$$anonfun$2$D$1,class C$$anonfun$1$$anonfun$2,null)
+(class C$$anonfun$met$1$E$1,class C$$anonfun$met$1,null)
+(class C$$anonfun$met$1$F$1,class C$$anonfun$met$1,private final java.lang.Object C$$anonfun$met$1.m$2())
+(class C$$anonfun$met$1$G$1,class C$$anonfun$met$1,null)
+(class C$$anonfun$met$1$$anonfun$3$H$1,class C$$anonfun$met$1$$anonfun$3,null)
+#partest -Ydelambdafy:method
+(class C$A$1,class C,null)
+(class C$B$1,class C,private final java.lang.Object C.m$1())
+(class C$C$1,class C,null)
+(class C$D$1,class C,null)
+(class C$E$1,class C,public scala.Function0 C.met())
+(class C$F$1,class C,private final java.lang.Object C.m$2())
+(class C$G$1,class C,public scala.Function0 C.met())
+(class C$H$1,class C,public scala.Function0 C.met())
diff --git a/test/files/jvm/t9105.scala b/test/files/jvm/t9105.scala
new file mode 100644
index 0000000000..636ee8a768
--- /dev/null
+++ b/test/files/jvm/t9105.scala
@@ -0,0 +1,22 @@
+class C {
+ val fun = () => {
+ class A
+ def m: Object = { class B; new B }
+ val f: Object = { class C; new C }
+ val g = () => { class D; new D }
+ List[Object](new A, m, f, g())
+ }
+ def met = () => {
+ class E
+ def m: Object = { class F; new F }
+ val f: Object = { class G; new G }
+ val g = () => { class H; new H }
+ List[Object](new E, m, f, g())
+ }
+}
+
+object Test extends App {
+ val x = new C().fun.apply() ::: new C().met.apply()
+ val results = x.map(_.getClass).map(cls => (cls, cls.getEnclosingClass, cls.getEnclosingMethod))
+ println(results.mkString("\n"))
+}