summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala48
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala37
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala30
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala4
4 files changed, 96 insertions, 23 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
index fb9804be7f..27827015c3 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
@@ -109,6 +109,15 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
}
}
+ def nextEnclosingClass(sym: Symbol): Symbol = {
+ if (sym.isClass) sym
+ else nextEnclosingClass(nextEnclosing(sym))
+ }
+
+ def classOriginallyNestedInClass(nestedClass: Symbol, enclosingClass: Symbol) ={
+ nextEnclosingClass(nextEnclosing(nestedClass)) == enclosingClass
+ }
+
/**
* Returns the enclosing method for non-member classes. In the following example
*
@@ -134,12 +143,23 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
*/
private def enclosingMethodForEnclosingMethodAttribute(classSym: Symbol): Option[Symbol] = {
assert(classSym.isClass, classSym)
+
+ def doesNotExist(method: Symbol) = {
+ // (1) SI-9124, some trait methods don't exist in the generated interface. see comment in BTypes.
+ // (2) Value classes. Member methods of value classes exist in the generated box class. However,
+ // nested methods lifted into a value class are moved to the companion object and don't exist
+ // in the value class itself. We can identify such nested methods: the initial enclosing class
+ // is a value class, but the current owner is some other class (the module class).
+ method.owner.isTrait && method.isImplOnly || { // (1)
+ val enclCls = nextEnclosingClass(method)
+ exitingPickler(enclCls.isDerivedValueClass) && method.owner != enclCls // (2)
+ }
+ }
+
def enclosingMethod(sym: Symbol): Option[Symbol] = {
if (sym.isClass || sym == NoSymbol) None
else if (sym.isMethod) {
- // SI-9124, some trait methods don't exist in the generated interface. see comment in BTypes.
- if (sym.owner.isTrait && sym.isImplOnly) None
- else Some(sym)
+ if (doesNotExist(sym)) None else Some(sym)
}
else enclosingMethod(nextEnclosing(sym))
}
@@ -152,11 +172,7 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
*/
private def enclosingClassForEnclosingMethodAttribute(classSym: Symbol): Symbol = {
assert(classSym.isClass, classSym)
- def enclosingClass(sym: Symbol): Symbol = {
- if (sym.isClass) sym
- else enclosingClass(nextEnclosing(sym))
- }
- val r = enclosingClass(nextEnclosing(classSym))
+ val r = nextEnclosingClass(nextEnclosing(classSym))
// this should be an assertion, but we are more cautious for now as it was introduced before the 2.11.6 minor release
if (considerAsTopLevelImplementationArtifact(r)) devWarning(s"enclosing class of $classSym should not be an implementation artifact class: $r")
r
@@ -175,10 +191,20 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
def enclosingMethodAttribute(classSym: Symbol, classDesc: Symbol => String, methodDesc: Symbol => String): Option[EnclosingMethodEntry] = {
// trait impl classes are always top-level, see comment in BTypes
if (isAnonymousOrLocalClass(classSym) && !considerAsTopLevelImplementationArtifact(classSym)) {
- val methodOpt = enclosingMethodForEnclosingMethodAttribute(classSym)
- debuglog(s"enclosing method for $classSym is $methodOpt (in ${methodOpt.map(_.enclClass)})")
+ val enclosingClass = enclosingClassForEnclosingMethodAttribute(classSym)
+ val methodOpt = enclosingMethodForEnclosingMethodAttribute(classSym) match {
+ case some @ Some(m) =>
+ if (m.owner != enclosingClass) {
+ // This should never happen. In case it does, it prevents emitting an invalid
+ // EnclosingMethod attribute: if the attribute specifies an enclosing method,
+ // it needs to exist in the specified enclosing class.
+ devWarning(s"the owner of the enclosing method ${m.locationString} should be the same as the enclosing class $enclosingClass")
+ None
+ } else some
+ case none => none
+ }
Some(EnclosingMethodEntry(
- classDesc(enclosingClassForEnclosingMethodAttribute(classSym)),
+ classDesc(enclosingClass),
methodOpt.map(_.javaSimpleName.toString).orNull,
methodOpt.map(methodDesc).orNull))
} else {
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
index 52d5cafc9c..2af665d31c 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
@@ -125,11 +125,21 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
* nested classes, but NOT nested in C, that are used within C.
*/
val nestedClassSymbols = {
+ val linkedClass = exitingPickler(classSym.linkedClassOfClass) // linkedCoC does not work properly in late phases
+
// The lambdalift phase lifts all nested classes to the enclosing class, so if we collect
// member classes right after lambdalift, we obtain all nested classes, including local and
// anonymous ones.
val nestedClasses = {
- val nested = exitingPhase(currentRun.lambdaliftPhase)(memberClassesForInnerClassTable(classSym))
+ val allNested = exitingPhase(currentRun.lambdaliftPhase)(memberClassesForInnerClassTable(classSym))
+ val nested = {
+ // Classes nested in value classes are nested in the companion at this point. For InnerClass /
+ // EnclosingMethod, we use the value class as the outer class. So we remove nested classes
+ // from the companion that were originally nested in the value class.
+ if (exitingPickler(linkedClass.isDerivedValueClass)) allNested.filterNot(classOriginallyNestedInClass(_, linkedClass))
+ else allNested
+ }
+
if (isTopLevelModuleClass(classSym)) {
// For Java compatibility, member classes of top-level objects are treated as members of
// the top-level companion class, see comment below.
@@ -158,13 +168,28 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
// a linkedClass symbol is found for which isTopLevelModule is true, so we end up searching
// members of that weird impl-class-module-class-symbol. that search probably cannot return
// any classes, but it's better to exclude it.
- val linkedClass = exitingPickler(classSym.linkedClassOfClass) // linkedCoC does not work properly in late phases
- if (linkedClass != NoSymbol && isTopLevelModuleClass(linkedClass))
+ val javaCompatMembers = {
+ if (linkedClass != NoSymbol && isTopLevelModuleClass(linkedClass))
// phase travel to exitingPickler: this makes sure that memberClassesForInnerClassTable only sees member
// classes, not local classes of the companion module (E in the exmaple) that were lifted by lambdalift.
- exitingPickler(memberClassesForInnerClassTable(linkedClass))
- else
- Nil
+ exitingPickler(memberClassesForInnerClassTable(linkedClass))
+ else
+ Nil
+ }
+
+ // Classes nested in value classes are nested in the companion at this point. For InnerClass /
+ // EnclosingMethod we use the value class as enclosing class. Here we search nested classes
+ // in the companion that were originally nested in the value class, and we add them as nested
+ // in the value class.
+ val valueClassCompanionMembers = {
+ if (linkedClass != NoSymbol && exitingPickler(classSym.isDerivedValueClass)) {
+ val moduleMemberClasses = exitingPhase(currentRun.lambdaliftPhase)(memberClassesForInnerClassTable(linkedClass))
+ moduleMemberClasses.filter(classOriginallyNestedInClass(_, classSym))
+ } else
+ Nil
+ }
+
+ javaCompatMembers ++ valueClassCompanionMembers
}
nestedClasses ++ companionModuleMembers
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index 0543929aef..707336e5de 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -700,30 +700,48 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
else innerSym.rawname + innerSym.moduleSuffix
}
+ val linkedClass = exitingPickler(csym.linkedClassOfClass) // linkedCoC does not work properly in late phases
+
innerClassBuffer ++= {
val members = exitingPickler(memberClassesForInnerClassTable(csym))
// lambdalift makes all classes (also local, anonymous) members of their enclosing class
val allNested = exitingPhase(currentRun.lambdaliftPhase)(memberClassesForInnerClassTable(csym))
+ val nested = {
+ // Classes nested in value classes are nested in the companion at this point. For InnerClass /
+ // EnclosingMethod, we use the value class as the outer class. So we remove nested classes
+ // from the companion that were originally nested in the value class.
+ if (exitingPickler(linkedClass.isDerivedValueClass)) allNested.filterNot(classOriginallyNestedInClass(_, linkedClass))
+ else allNested
+ }
- // for the mirror class, we take the members of the companion module class (Java compat,
- // see doc in BTypes.scala). for module classes, we filter out those members.
+ // for the mirror class, we take the members of the companion module class (Java compat, see doc in BTypes.scala).
+ // for module classes, we filter out those members.
if (isMirror) members
- else if (isTopLevelModule(csym)) allNested diff members
- else allNested
+ else if (isTopLevelModule(csym)) nested diff members
+ else nested
}
if (!considerAsTopLevelImplementationArtifact(csym)) {
- // If this is a top-level non-impl class, add members of the companion object.
+ // If this is a top-level non-impl class, add members of the companion object. These are the
+ // classes for which we change the InnerClass entry to allow using them from Java.
// We exclude impl classes: if the classfile for the impl class exists on the classpath, a
// linkedClass symbol is found for which isTopLevelModule is true, so we end up searching
// members of that weird impl-class-module-class-symbol. that search probably cannot return
// any classes, but it's better to exclude it.
- val linkedClass = exitingPickler(csym.linkedClassOfClass) // linkedCoC does not work properly in late phases
if (linkedClass != NoSymbol && isTopLevelModule(linkedClass)) {
// phase travel to exitingPickler: this makes sure that memberClassesForInnerClassTable only
// sees member classes, not local classes that were lifted by lambdalift.
innerClassBuffer ++= exitingPickler(memberClassesForInnerClassTable(linkedClass))
}
+
+ // Classes nested in value classes are nested in the companion at this point. For InnerClass /
+ // EnclosingMethod we use the value class as enclosing class. Here we search nested classes
+ // in the companion that were originally nested in the value class, and we add them as nested
+ // in the value class.
+ if (linkedClass != NoSymbol && exitingPickler(csym.isDerivedValueClass)) {
+ val moduleMemberClasses = exitingPhase(currentRun.lambdaliftPhase)(memberClassesForInnerClassTable(linkedClass))
+ innerClassBuffer ++= moduleMemberClasses.filter(classOriginallyNestedInClass(_, csym))
+ }
}
val allInners: List[Symbol] = innerClassBuffer.toList filterNot deadCode.elidedClosures
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 3544dc9966..e1cf53059a 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -225,6 +225,10 @@ abstract class UnCurry extends InfoTransform
if (inlineFunctionExpansion || !canUseDelamdafyMethod) {
val parents = addSerializable(abstractFunctionForFunctionType(fun.tpe))
val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation SerialVersionUIDAnnotation
+ // The original owner is used in the backend for the EnclosingMethod attribute. If fun is
+ // nested in a value-class method, its owner was already changed to the extension method.
+ // Saving the original owner allows getting the source structure from the class symbol.
+ defineOriginalOwner(anonClass, fun.symbol.originalOwner)
anonClass setInfo ClassInfoType(parents, newScope, anonClass)
val applyMethodDef = mkMethod(anonClass, nme.apply)