summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGrzegorz Kossakowski <grzegorz.kossakowski@gmail.com>2014-07-25 17:47:56 +0200
committerGrzegorz Kossakowski <grzegorz.kossakowski@gmail.com>2014-07-25 17:47:56 +0200
commite89fe92ac34b111ba887a79ac788552135c7cce0 (patch)
tree6928d27ce37c81a925a05b6862730d7125357fe3 /src
parent33b847b7f56cbc5add74692385a9e0f5ad41c7a6 (diff)
parenta8c88b194eefd5d4d55361b934faa0ebd954ef08 (diff)
downloadscala-e89fe92ac34b111ba887a79ac788552135c7cce0.tar.gz
scala-e89fe92ac34b111ba887a79ac788552135c7cce0.tar.bz2
scala-e89fe92ac34b111ba887a79ac788552135c7cce0.zip
Merge pull request #3826 from lrytz/opt/refactorTracked
Assortiment of cleanups and comments around the backend
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala3
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala1
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala111
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala270
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala4
-rw-r--r--src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala41
-rw-r--r--src/compiler/scala/tools/nsc/transform/Flatten.scala11
-rw-r--r--src/compiler/scala/tools/nsc/transform/LazyVals.scala4
-rw-r--r--src/reflect/scala/reflect/api/Symbols.scala3
-rw-r--r--src/reflect/scala/reflect/internal/Symbols.scala101
-rw-r--r--src/reflect/scala/reflect/runtime/SymbolLoaders.scala11
15 files changed, 481 insertions, 89 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
index 4583462b71..2d1030121e 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
@@ -977,7 +977,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
if (!isModuleInitialized &&
jMethodName == INSTANCE_CONSTRUCTOR_NAME &&
jname == INSTANCE_CONSTRUCTOR_NAME &&
- isStaticModule(siteSymbol)) {
+ isStaticModuleClass(siteSymbol)) {
isModuleInitialized = true
mnode.visitVarInsn(asm.Opcodes.ALOAD, 0)
mnode.visitFieldInsn(
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
index 31a392ed55..de1587c7c3 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
@@ -460,6 +460,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
// Can't call .toInterface (at this phase) or we trip an assertion.
// See PackratParser#grow for a method which fails with an apparent mismatch
// between "object PackratParsers$class" and "trait PackratParsers"
+ // TODO @lry do we have a test for that?
if (sym.isImplClass) {
// pos/spec-List.scala is the sole failure if we don't check for NoSymbol
val traitSym = sym.owner.info.decl(tpnme.interfaceName(sym.name))
@@ -469,6 +470,8 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
}
}
+ // TODO @lry: code duplication between here and method asmClassType.
+
assert(hasInternalName(sym), s"Invoked for a symbol lacking JVM internal name: ${sym.fullName}")
assert(!phantomTypeMap.contains(sym), "phantom types not supposed to reach here.")
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
index 9b7c975960..2343d378db 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
@@ -34,6 +34,7 @@ abstract class BCodeIdiomatic extends SubComponent {
case "jvm-1.5" => asm.Opcodes.V1_5
case "jvm-1.6" => asm.Opcodes.V1_6
case "jvm-1.7" => asm.Opcodes.V1_7
+ case "jvm-1.8" => asm.Opcodes.V1_8
}
val majorVersion: Int = (classfileVersion & 0xFF)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
index 8845ffa0cd..0d67a07e0f 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
@@ -95,7 +95,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
claszSymbol = cd.symbol
isCZParcelable = isAndroidParcelableClass(claszSymbol)
- isCZStaticModule = isStaticModule(claszSymbol)
+ isCZStaticModule = isStaticModuleClass(claszSymbol)
isCZRemote = isRemote(claszSymbol)
thisName = internalName(claszSymbol)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala
index 62dfb4917d..b373f8d74d 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala
@@ -21,8 +21,9 @@ abstract class BCodeTypes extends BCodeIdiomatic {
import global._
import bTypes._
- // when compiling the Scala library, some assertions don't hold (e.g., scala.Boolean has null superClass although it's not an interface)
- val isCompilingStdLib = !(settings.sourcepath.isDefault)
+ // Used only for assertions. When compiling the Scala library, some assertions don't hold
+ // (e.g., scala.Boolean has null superClass although it's not an interface)
+ private val isCompilingStdLib = !(settings.sourcepath.isDefault)
// special names
var StringReference : ClassBType = null
@@ -175,12 +176,21 @@ abstract class BCodeTypes extends BCodeIdiomatic {
// ------------------------------------------------
/**
- * TODO @lry should probably be a map form ClassBType to Tracked
+ * Type information for classBTypes.
+ *
+ * TODO rename Tracked
*/
- val exemplars = new java.util.concurrent.ConcurrentHashMap[BType, Tracked]
+ val exemplars = new java.util.concurrent.ConcurrentHashMap[ClassBType, Tracked]
/**
* Maps class symbols to their corresponding `Tracked` instance.
+ *
+ * This map is only used during the first backend phase (Worker1) where ClassDef trees are
+ * transformed into ClassNode asm trees. In this phase, ClassBTypes and their Tracked are created
+ * and added to the `exemplars` map. The `symExemplars` map is only used to know if a symbol has
+ * already been visited.
+ *
+ * TODO move this map to the builder class. it's only used during building. can be gc'd with the builder.
*/
val symExemplars = new java.util.concurrent.ConcurrentHashMap[Symbol, Tracked]
@@ -313,7 +323,7 @@ abstract class BCodeTypes extends BCodeIdiomatic {
final def isDeprecated(sym: Symbol): Boolean = { sym.annotations exists (_ matches definitions.DeprecatedAttr) }
/* must-single-thread */
- final def hasInternalName(sym: Symbol) = { sym.isClass || (sym.isModule && !sym.isMethod) }
+ final def hasInternalName(sym: Symbol) = sym.isClass || sym.isModuleNotMethod
/* must-single-thread */
def getSuperInterfaces(csym: Symbol): List[Symbol] = {
@@ -617,20 +627,52 @@ abstract class BCodeTypes extends BCodeIdiomatic {
false
}
- /*
+ /**
* must-single-thread
+ *
+ * True for module classes of package level objects. The backend will generate a mirror class for
+ * such objects.
*/
- def isTopLevelModule(sym: Symbol): Boolean = {
- exitingPickler { sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass }
+ def isTopLevelModuleClass(sym: Symbol): Boolean = exitingPickler {
+ // phase travel to pickler required for isNestedClass (looks at owner)
+ val r = sym.isModuleClass && !sym.isNestedClass
+ // The mixin phase adds the `lateMODULE` flag to trait implementation classes. Since the flag
+ // is late, it should not be visible here inside the time travel. We check this.
+ if (r) assert(!sym.isImplClass, s"isModuleClass should be false for impl class $sym")
+ r
}
- /*
+ /**
* must-single-thread
+ *
+ * True for module classes of modules that are top-level or owned only by objects. Module classes
+ * for such objects will get a MODULE$ flag and a corresponding static initializer.
*/
- def isStaticModule(sym: Symbol): Boolean = {
- sym.isModuleClass && !sym.isImplClass && !sym.isLifted
+ def isStaticModuleClass(sym: Symbol): Boolean = {
+ /* The implementation of this method is tricky because it is a source-level property. Various
+ * phases changed the symbol's properties in the meantime.
+ *
+ * (1) Phase travel to to pickler is required to exclude implementation classes; they have the
+ * lateMODULEs after mixin, so isModuleClass would be true.
+ *
+ * (2) We cannot use `sym.isStatic` because lambdalift modified (destructively) the owner. For
+ * example, in
+ * object T { def f { object U } }
+ * the owner of U is T, so UModuleClass.isStatic is true. Phase travel does not help here.
+ * So we basically re-implement `sym.isStaticOwner`, but using the original owner chain.
+ */
+
+ def isOriginallyStaticOwner(sym: Symbol): Boolean = {
+ sym.isPackageClass || sym.isModuleClass && isOriginallyStaticOwner(sym.originalOwner)
+ }
+
+ exitingPickler { // (1)
+ sym.isModuleClass &&
+ isOriginallyStaticOwner(sym.originalOwner) // (2)
+ }
}
+
// ---------------------------------------------------------------------
// ---------------- InnerClasses attribute (JVMS 4.7.6) ----------------
// ---------------------------------------------------------------------
@@ -702,6 +744,10 @@ abstract class BCodeTypes extends BCodeIdiomatic {
var x = ics
while (x ne NoSymbol) {
assert(x.isClass, s"not a class symbol: ${x.fullName}")
+ // Uses `rawowner` because `owner` reflects changes in the owner chain due to flattening.
+ // The owner chain of a class only contains classes. This is because the lambdalift phase
+ // changes the `rawowner` destructively to point to the enclosing class. Before, the owner
+ // might be for example a method.
val isInner = !x.rawowner.isPackageClass
if (isInner) {
chain ::= x
@@ -729,7 +775,7 @@ abstract class BCodeTypes extends BCodeIdiomatic {
null
else {
val outerName = innerSym.rawowner.javaBinaryName
- if (isTopLevelModule(innerSym.rawowner)) nme.stripModuleSuffix(outerName)
+ if (isTopLevelModuleClass(innerSym.rawowner)) nme.stripModuleSuffix(outerName)
else outerName
}
}
@@ -741,12 +787,23 @@ abstract class BCodeTypes extends BCodeIdiomatic {
innerSym.rawname + innerSym.moduleSuffix
}
- val flagsWithFinal: Int = mkFlags(
+ // TODO @lry compare with table in spec: for example, deprecated should not be there it seems.
+ // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.6-300-D.1-D.1
+ // including "deprecated" was added in the initial commit of GenASM, but it was never in GenJVM.
+ val flags: Int = mkFlags(
+ // TODO @lry adding "static" whenever the class is owned by a module seems wrong.
+ // class C { object O { class I } }
+ // here, I is marked static in the InnerClass attribute. But the I constructor takes an outer instance.
+ // was added in 0469d41
+ // what should it be? check what would make sense for java reflection.
+ // member of top-level object should be static? how about anonymous / local class that has
+ // been lifted to a top-level object?
+ // member that is only nested in objects should be static?
+ // verify: will ICodeReader still work after that? the code was introduced because of icode reader.
if (innerSym.rawowner.hasModuleFlag) asm.Opcodes.ACC_STATIC else 0,
javaFlags(innerSym),
if (isDeprecated(innerSym)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo-access flag
) & (INNER_CLASSES_FLAGS | asm.Opcodes.ACC_DEPRECATED)
- val flags = if (innerSym.isModuleClass) flagsWithFinal & ~asm.Opcodes.ACC_FINAL else flagsWithFinal // For SI-5676, object overriding.
val jname = innerSym.javaBinaryName.toString // never null
val oname = { // null when method-enclosed
@@ -794,14 +851,23 @@ abstract class BCodeTypes extends BCodeIdiomatic {
* must-single-thread
*/
def javaFlags(sym: Symbol): Int = {
- // constructors of module classes should be private
- // PP: why are they only being marked private at this stage and not earlier?
+ // constructors of module classes should be private. introduced in b06edbc, probably to prevent
+ // creating module instances from java. for nested modules, the constructor needs to be public
+ // since they are created by the outer class and stored in a field. a java client can create
+ // new instances via outerClassInstance.new InnerModuleClass$().
+ // TODO: do this early, mark the symbol private.
val privateFlag =
- sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModule(sym.owner))
+ sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModuleClass(sym.owner))
- // Final: the only fields which can receive ACC_FINAL are eager vals.
- // Neither vars nor lazy vals can, because:
+ // Symbols marked in source as `final` have the FINAL flag. (In the past, the flag was also
+ // added to modules and module classes, not anymore since 296b706).
+ // Note that the presence of the `FINAL` flag on a symbol does not correspond 1:1 to emitting
+ // ACC_FINAL in bytecode.
+ //
+ // Top-level modules are marked ACC_FINAL in bytecode (even without the FINAL flag). Nested
+ // objects don't get the flag to allow overriding (under -Yoverride-objects, SI-5676).
//
+ // For fields, only eager val fields can receive ACC_FINAL. vars or lazy vals can't:
// Source: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3
// "Another problem is that the specification allows aggressive
// optimization of final fields. Within a thread, it is permissible to
@@ -818,10 +884,9 @@ abstract class BCodeTypes extends BCodeIdiomatic {
// we can exclude lateFINAL. Such symbols are eligible for inlining, but to
// avoid breaking proxy software which depends on subclassing, we do not
// emit ACC_FINAL.
- // Nested objects won't receive ACC_FINAL in order to allow for their overriding.
val finalFlag = (
- (((sym.rawflags & symtab.Flags.FINAL) != 0) || isTopLevelModule(sym))
+ (((sym.rawflags & symtab.Flags.FINAL) != 0) || isTopLevelModuleClass(sym))
&& !sym.enclClass.isInterface
&& !sym.isClassConstructor
&& !sym.isMutable // lazy vals and vars both
@@ -845,6 +910,10 @@ abstract class BCodeTypes extends BCodeIdiomatic {
if (sym.isVarargsMethod) ACC_VARARGS else 0,
if (sym.hasFlag(symtab.Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0
)
+ // TODO @lry should probably also check / add "deprectated"
+ // all call sites of "javaFlags" seem to check for deprecation rigth after.
+ // Exception: the call below in javaFieldFlags. However, the caller of javaFieldFlags then
+ // does the check.
}
/*
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
index 5b0fa6f7a8..15bc068533 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -43,9 +43,9 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
case FLOAT => "F"
case LONG => "J"
case DOUBLE => "D"
- case c @ ClassBType(_, _) => "L" + c.internalName + ";"
- case ArrayBType(component) => "[" + component
- case MethodBType(args, res) => "(" + args.mkString + ")" + res
+ case ClassBType(internalName) => "L" + internalName + ";"
+ case ArrayBType(component) => "[" + component
+ case MethodBType(args, res) => "(" + args.mkString + ")" + res
}
/**
@@ -160,9 +160,9 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
case FLOAT => asm.Type.FLOAT_TYPE
case LONG => asm.Type.LONG_TYPE
case DOUBLE => asm.Type.DOUBLE_TYPE
- case c @ ClassBType(_, _) => asm.Type.getObjectType(c.internalName) // (*)
- case a @ ArrayBType(_) => asm.Type.getObjectType(a.descriptor)
- case m @ MethodBType(_, _) => asm.Type.getMethodType(m.descriptor)
+ case ClassBType(internalName) => asm.Type.getObjectType(internalName) // see (*) above
+ case a: ArrayBType => asm.Type.getObjectType(a.descriptor)
+ case m: MethodBType => asm.Type.getMethodType(m.descriptor)
}
def asRefBType : RefBType = this.asInstanceOf[RefBType]
@@ -227,39 +227,254 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
* This can be verified for example using javap or ASMifier.
*/
def classOrArrayType: String = this match {
- case c: ClassBType => c.internalName
- case a: ArrayBType => a.descriptor
+ case ClassBType(internalName) => internalName
+ case a: ArrayBType => a.descriptor
}
}
/**
+ * InnerClass and EnclosingMethod attributes (EnclosingMethod is displayed as OUTERCLASS in asm).
+ *
+ * In this summary, "class" means "class or interface".
+ *
+ * JLS: http://docs.oracle.com/javase/specs/jls/se8/html/index.html
+ * JVMS: http://docs.oracle.com/javase/specs/jvms/se8/html/index.html
+ *
+ * Terminology
+ * -----------
+ *
+ * - Nested class (JLS 8): class whose declaration occurs within the body of another class
+ *
+ * - Top-level class (JLS 8): non-nested class
+ *
+ * - Inner class (JLS 8.1.3): nested class that is not (explicitly or implicitly) static
+ *
+ * - Member class (JLS 8.5): class directly enclosed in the body of a class (and not, for
+ * example, defined in a method). Member classes cannot be anonymous. May be static.
+ *
+ * - Local class (JLS 14.3): nested, non-anonymous class that is not a member of a class
+ * - cannot be static (therefore they are "inner" classes)
+ * - can be defined in a method, a constructor or in an initializer block
+ *
+ * - Initializer block (JLS 8.6 / 8.7): block of statements in a java class
+ * - static initializer: executed before constructor body
+ * - instance initializer: exectued when class is initialized (instance creation, static
+ * field access, ...)
+ *
+ *
+ * InnerClass
+ * ----------
+ *
+ * The JVMS 4.7.6 requires an entry for every class mentioned in a CONSTANT_Class_info in the
+ * constant pool (CP) that is not a member of a package (JLS 7.1).
+ *
+ * The JLS 13.1, points 9. / 10. requires: a class must reference (in the CP)
+ * - its immediately enclosing class
+ * - all of its member classes
+ * - all local and anonymous classes that appear elsewhere (method, constructor, initializer
+ * block, field initializer)
+ *
+ * In a comment, the 4.7.6 spec says: this implies an entry in the InnerClass attribute for
+ * - All enclosing classes (except the outermost, which is top-level)
+ * - My comment: not sure how this is implied, below (*) a Java counter-example.
+ * In any case, the Java compiler seems to add all enclosing classes, even if they are not
+ * otherwise mentioned in the CP. So we should do the same.
+ * - All nested classes (including anonymous and local, but not transitively)
+ *
+ * Fields in the InnerClass entries:
+ * - inner class: the (nested) class C we are talking about
+ * - outer class: the class of which C is a member. Has to be null for non-members, i.e. for
+ * local and anonymous classes.
+ * - inner name: A string with the simple name of the inner class. Null for anonymous classes.
+ * - flags: access property flags, details in JVMS, table in 4.7.6.
+ *
+ *
+ * Note 1: when a nested class is present in the InnerClass attribute, all of its enclosing
+ * classes have to be present as well (by the rules above). Example:
+ *
+ * class Outer { class I1 { class I2 { } } }
+ * class User { Outer.I1.I2 foo() { } }
+ *
+ * The return type "Outer.I1.I2" puts "Outer$I1$I2" in the CP, therefore the class is added to the
+ * InnerClass attribute. For this entry, the "outer class" field will be "Outer$I1". This in turn
+ * adds "Outer$I1" to the CP, which requires adding that class to the InnerClass attribute.
+ * (For local / anonymous classes this would not be the case, since the "outer class" attribute
+ * would be empty. However, no class (other than the enclosing class) can refer to them, as they
+ * have no name.)
+ *
+ * In the current implementation of the Scala compiler, when adding a class to the InnerClass
+ * attribute, all of its enclosing classes will be added as well. Javac seems to do the same,
+ * see (*).
+ *
+ *
+ * Note 2: If a class name is mentioned only in a CONSTANT_Utf8_info, but not in a
+ * CONSTANT_Class_info, the JVMS does not require an entry in the InnerClass attribute. However,
+ * the Java compiler seems to add such classes anyway. For example, when using an annotation, the
+ * annotation class is stored as a CONSTANT_Utf8_info in the CP:
+ *
+ * @O.Ann void foo() { }
+ *
+ * adds "const #13 = Asciz LO$Ann;;" in the constant pool. The "RuntimeInvisibleAnnotations"
+ * attribute refers to that constant pool entry. Even though there is no other reference to
+ * `O.Ann`, the java compiler adds an entry for that class to the InnerClass attribute (which
+ * entails adding a CONSTANT_Class_info for the class).
+ *
+ *
+ *
+ * EnclosingMethod
+ * ---------------
+ *
+ * JVMS 4.7.7: the attribute must be present "if and only if it represents a local class
+ * or an anonymous class" (i.e. not for member classes).
+ *
+ * Fields:
+ * - class: the enclosing class
+ * - method: the enclosing method (or constructor). Null if the class is not enclosed by a
+ * method, i.e. for
+ * - local or anonymous classes defined in (static or non-static) initializer blocks
+ * - anonymous classes defined in initializer blocks or field initializers
+ *
+ * Note: the field is required for anonymous classes defined within local variable
+ * initializers (within a method), Java example below (**).
+ *
+ * Currently, the Scala compiler sets "method" to the class constructor for classes
+ * defined in initializer blocks or field initializers. This is probably OK, since the
+ * Scala compiler desugars these statements into to the primary constructor.
+ *
+ *
+ * (*)
+ * public class Test {
+ * void foo() {
+ * class Foo1 {
+ * // constructor statement block
+ * {
+ * class Foo2 {
+ * class Foo3 { }
+ * }
+ * }
+ * }
+ * }
+ * }
+ *
+ * The class file Test$1Foo1$1Foo2$Foo3 has no reference to the class Test$1Foo1, however it
+ * still contains an InnerClass attribute for Test$1Foo1.
+ * Maybe this is just because the Java compiler follows the JVMS comment ("InnerClasses
+ * information for each enclosing class").
+ *
+ *
+ * (**)
+ * void foo() {
+ * // anonymous class defined in local variable initializer expression.
+ * Runnable x = true ? (new Runnable() {
+ * public void run() { return; }
+ * }) : null;
+ * }
+ *
+ * The EnclosingMethod attribute of the anonymous class mentions "foo" in the "method" field.
+ *
+ *
+ * Java Compatibility
+ * ------------------
+ *
+ * In the InnerClass entry for classes in top-level modules, the "outer class" is emitted as the
+ * mirror class (or the existing companion class), i.e. C1 is nested in T (not T$).
+ * For classes nested in a nested object, the "outer class" is the module class: C2 is nested in T$N$
+ * object T {
+ * class C1
+ * object N { class C2 }
+ * }
+ *
+ * Reason: java compat. It's a "best effort" "solution". If you want to use "C1" from Java, you
+ * can write "T.C1", and the Java compiler will translate that to the classfile T$C1.
+ *
+ * If we would emit the "outer class" of C1 as "T$", then in Java you'd need to write "T$.C1"
+ * because the java compiler looks at the InnerClass attribute to find if an inner class exists.
+ * However, the Java compiler would then translate the '.' to '$' and you'd get the class name
+ * "T$$C1". This class file obviously does not exist.
+ *
+ * Directly using the encoded class name "T$C1" in Java does not work: since the classfile
+ * describes a nested class, the Java compiler hides it from the classpath and will report
+ * "cannot find symbol T$C1". This means that the class T.N.C2 cannot be referenced from a
+ * Java source file in any way.
+ *
+ *
+ * STATIC flag
+ * -----------
+ *
+ * Java: static nested classes have the "static" flag in the InnerClass attribute. This is not the
+ * case for local classes defined within a static method, even though such classes, as they are
+ * defined in a static context, don't take an "outer" instance.
+ * Non-static nested classes (inner classes, including local classes defined in a non-static
+ * method) take an "outer" instance on construction.
+ *
+ * Scala: Explicitouter adds an "outer" parameter to nested classes, except for classes defined
+ * in a static context, i.e. when all outer classes are module classes.
+ * package p
+ * object O1 {
+ * class C1 // static
+ * object O2 {
+ * def f = {
+ * class C2 { // static
+ * class C3 // non-static, needs outer
+ * }
+ * }
+ * }
+ * }
+ *
+ * Int the InnerClass attribute, the `static` flag is added for all classes defined in a static
+ * context, i.e. also for C2. This is different than in Java.
+ *
+ *
+ * Mirror Classes
+ * --------------
+ *
+ * TODO: innerclass attributes on mirror class, bean info class
+ */
+
+ /**
* Class or Interface type.
*
- * Classes are represented using their name as a slice of the `chrs` array. This representation is
- * efficient because the JVM class name is initially created using `classSymbol.javaBinaryName`.
- * This already adds the necessary string to the `chrs` array, so it makes sense to reuse the same
- * name table in the backend.
+ * The information for creating a ClassBType (superClass, interfaces, etc) is obtained
+ * - either from a ClassSymbol, for classes being compiled or referenced from source (see
+ * BCodeTypes)
+ * - or, during inlining, from ASM ClassNodes that are parsed from class files.
+ *
+ * The class name is represented as a slice of the `chrs` array. This representation is efficient
+ * because the JVM class name is obtained through `classSymbol.javaBinaryName`. This already adds
+ * the necessary string to the `chrs` array, so it makes sense to reuse the same name table in the
+ * backend.
+ *
+ * Not a case class because that would expose the constructor that takes (offset, length)
+ * parameters (I didn't find a way to make it private, also the factory in the companion).
+ *
+ * @param offset See below
+ * @param length The class name is represented as offset and length in the `chrs` array.
+ * The (public) constructors of ClassBType take a BTypeName, which are
+ * hash-consed. This ensures that two ClassBType instances for the same name
+ * have the same offset and length.
*
* Not a case class because that would expose the (Int, Int) constructor (didn't find a way to
* make it private, also the factory in the companion).
*/
class ClassBType private(val offset: Int, val length: Int) extends RefBType {
/**
- * Construct a ClassBType for a given (intenred) class name.
+ * Construct a ClassBType from the (intenred) internal name of a class.
*
- * @param n The class name as a slice of the `chrs` array, without the surrounding 'L' and ';'.
- * Note that `classSymbol.javaBinaryName` returns exactly such a name.
+ * @param internalName The internal name as a slice of the `chrs` array. The internal name does
+ * not have the surrounding 'L' and ';'. Note that
+ * `classSymbol.javaBinaryName` returns exactly such a name.
*/
- def this(n: BTypeName) = this(n.start, n.length)
+ def this(internalName: BTypeName) = this(internalName.start, internalName.length)
/**
- * Construct a ClassBType for a given java class name.
+ * Construct a ClassBType from the internal name of a class.
*
- * @param s A class name of the form "java/lang/String", without the surrounding 'L' and ';'.
+ * @param internalName The internal name of a class has the form "java/lang/String", without the
+ * surrounding 'L' and ';'.
*/
- def this(s: String) = this({
- assert(!(s.head == 'L' && s.last == ';'), s"Descriptor instead of internal name: $s")
- createNewName(s)
+ def this(internalName: String) = this({
+ assert(!(internalName.head == 'L' && internalName.last == ';'), s"Descriptor instead of internal name: $internalName")
+ createNewName(internalName)
})
/**
@@ -277,7 +492,7 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
* Custom equals / hashCode are needed because this is not a case class.
*/
override def equals(o: Any): Boolean = (this eq o.asInstanceOf[Object]) || (o match {
- case ClassBType(`offset`, `length`) => true
+ case c: ClassBType => c.offset == this.offset && c.length == this.length
case _ => false
})
@@ -291,12 +506,15 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
}
object ClassBType {
- def apply(n: BTypeName): ClassBType = new ClassBType(n)
- def apply(s: String): ClassBType = new ClassBType(s)
+ def apply(internalName: BTypeName): ClassBType = new ClassBType(internalName)
+ def apply(internalName: String): ClassBType = new ClassBType(internalName)
- def unapply(c: ClassBType): Option[(Int, Int)] =
+ /**
+ * Pattern matching on a ClassBType extracts the `internalName` of the class.
+ */
+ def unapply(c: ClassBType): Option[String] =
if (c == null) None
- else Some((c.offset, c.length))
+ else Some(c.internalName)
}
case class ArrayBType(componentType: BType) extends RefBType {
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index b0fb3069c1..2392033760 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -381,6 +381,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
case "jvm-1.5" => asm.Opcodes.V1_5
case "jvm-1.6" => asm.Opcodes.V1_6
case "jvm-1.7" => asm.Opcodes.V1_7
+ case "jvm-1.8" => asm.Opcodes.V1_8
}
private val majorVersion: Int = (classfileVersion & 0xFF)
@@ -636,6 +637,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
innerSym.rawname + innerSym.moduleSuffix
// add inner classes which might not have been referenced yet
+ // TODO @lry according to the spec, all nested classes should be added, also local and
+ // anonymous. This seems to add only member classes - or not? it's exitingErasure, so maybe
+ // local / anonymous classes have been lifted by lambdalift. are they in the "decls" though?
exitingErasure {
for (sym <- List(csym, csym.linkedClassOfClass); m <- sym.info.decls.map(innerClassSymbolFor) if m.isClass)
innerClassBuffer += m
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
index a401de05e5..89866b7ce9 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
@@ -165,7 +165,7 @@ abstract class GenBCode extends BCodeSyncAndTry {
// -------------- mirror class, if needed --------------
val mirrorC =
- if (isStaticModule(claszSymbol) && isTopLevelModule(claszSymbol)) {
+ if (isTopLevelModuleClass(claszSymbol)) {
if (claszSymbol.companionClass == NoSymbol) {
mirrorCodeGen.genMirrorClass(claszSymbol, cunit)
} else {
@@ -271,7 +271,7 @@ abstract class GenBCode extends BCodeSyncAndTry {
override def run() {
arrivalPos = 0 // just in case
- scalaPrimitives.init
+ scalaPrimitives.init()
initBCodeTypes()
// initBytecodeWriter invokes fullName, thus we have to run it before the typer-dependent thread is activated.
diff --git a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala
index 37dfafb01c..d42c0dd730 100644
--- a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala
@@ -39,7 +39,7 @@ trait StandardScalaSettings {
val optimise: BooleanSetting // depends on post hook which mutates other settings
val print = BooleanSetting ("-print", "Print program with Scala-specific features removed.")
val target = ChoiceSetting ("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.",
- List("jvm-1.5", "jvm-1.6", "jvm-1.7"), "jvm-1.6")
+ List("jvm-1.5", "jvm-1.6", "jvm-1.7", "jvm-1.8"), "jvm-1.6")
val unchecked = BooleanSetting ("-unchecked", "Enable additional warnings where generated code depends on assumptions.")
val uniqid = BooleanSetting ("-uniqid", "Uniquely tag all identifiers in debugging output.")
val usejavacp = BooleanSetting ("-usejavacp", "Utilize the java.class.path in classpath resolution.")
diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala
index 447fa66ae4..82c2a4d6ed 100644
--- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala
+++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala
@@ -240,6 +240,12 @@ abstract class SymbolLoaders {
}
}
+ private def phaseBeforeRefchecks: Phase = {
+ var resPhase = phase
+ while (resPhase.refChecked) resPhase = resPhase.prev
+ resPhase
+ }
+
/**
* Load contents of a package
*/
@@ -248,19 +254,24 @@ abstract class SymbolLoaders {
protected def doComplete(root: Symbol) {
assert(root.isPackageClass, root)
- root.setInfo(new PackageClassInfoType(newScope, root))
-
- if (!root.isRoot) {
- for (classRep <- classpath.classes) {
- initializeFromClassPath(root, classRep)
- }
- }
- if (!root.isEmptyPackageClass) {
- for (pkg <- classpath.packages) {
- enterPackage(root, pkg.name, new PackageLoader(pkg))
+ // Time travel to a phase before refchecks avoids an initialization issue. `openPackageModule`
+ // creates a module symbol and invokes invokes `companionModule` while the `infos` field is
+ // still null. This calls `isModuleNotMethod`, which forces the `info` if run after refchecks.
+ enteringPhase(phaseBeforeRefchecks) {
+ root.setInfo(new PackageClassInfoType(newScope, root))
+
+ if (!root.isRoot) {
+ for (classRep <- classpath.classes) {
+ initializeFromClassPath(root, classRep)
+ }
}
+ if (!root.isEmptyPackageClass) {
+ for (pkg <- classpath.packages) {
+ enterPackage(root, pkg.name, new PackageLoader(pkg))
+ }
- openPackageModule(root)
+ openPackageModule(root)
+ }
}
}
}
@@ -290,7 +301,13 @@ abstract class SymbolLoaders {
protected def doComplete(root: Symbol) {
val start = if (Statistics.canEnable) Statistics.startTimer(classReadNanos) else null
- classfileParser.parse(classfile, root)
+
+ // Running the classfile parser after refchecks can lead to "illegal class file dependency"
+ // errors. More concretely, the classfile parser calls "sym.companionModule", which calls
+ // "isModuleNotMethod" on the companion. After refchecks, this method forces the info, which
+ // may run the classfile parser. This produces the error.
+ enteringPhase(phaseBeforeRefchecks)(classfileParser.parse(classfile, root))
+
if (root.associatedFile eq NoAbstractFile) {
root match {
// In fact, the ModuleSymbol forwards its setter to the module class
diff --git a/src/compiler/scala/tools/nsc/transform/Flatten.scala b/src/compiler/scala/tools/nsc/transform/Flatten.scala
index c3fbfae322..fa53ef48b5 100644
--- a/src/compiler/scala/tools/nsc/transform/Flatten.scala
+++ b/src/compiler/scala/tools/nsc/transform/Flatten.scala
@@ -76,8 +76,17 @@ abstract class Flatten extends InfoTransform {
for (sym <- decls) {
if (sym.isTerm && !sym.isStaticModule) {
decls1 enter sym
- if (sym.isModule)
+ if (sym.isModule) {
+ // Nested, non-static moduls are transformed to methods.
+ assert(sym.isMethod, s"Non-static $sym should have the lateMETHOD flag from RefChecks")
+ // Note that module classes are not entered into the 'decls' of the ClassInfoType
+ // of the outer class, only the module symbols are. So the current loop does
+ // not visit module classes. Therefore we set the LIFTED flag here for module
+ // classes.
+ // TODO: should we also set the LIFTED flag for static, nested module classes?
+ // currently they don't get the flag, even though they are lifted to the package
sym.moduleClass setFlag LIFTED
+ }
} else if (sym.isClass)
liftSymbol(sym)
}
diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
index b71d14a04f..38671ebaae 100644
--- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala
+++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
@@ -192,13 +192,15 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
def mkSlowPathDef(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree],
stats: List[Tree], retVal: Tree): Tree = {
+ // Q: is there a reason to first set owner to `clazz` (by using clazz.newMethod), and then
+ // changing it to lzyVal.owner very soon after? Could we just do lzyVal.owner.newMethod?
val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), lzyVal.pos, STABLE | PRIVATE)
defSym setInfo MethodType(List(), lzyVal.tpe.resultType)
defSym.owner = lzyVal.owner
debuglog(s"crete slow compute path $defSym with owner ${defSym.owner} for lazy val $lzyVal")
if (bitmaps.contains(lzyVal))
bitmaps(lzyVal).map(_.owner = defSym)
- val rhs: Tree = (gen.mkSynchronizedCheck(clazz, cond, syncBody, stats)).changeOwner(currentOwner -> defSym)
+ val rhs: Tree = gen.mkSynchronizedCheck(clazz, cond, syncBody, stats).changeOwner(currentOwner -> defSym)
DefDef(defSym, addBitmapDefs(lzyVal, BLOCK(rhs, retVal)))
}
diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala
index dddd3c0e61..42cf600c85 100644
--- a/src/reflect/scala/reflect/api/Symbols.scala
+++ b/src/reflect/scala/reflect/api/Symbols.scala
@@ -260,6 +260,9 @@ trait Symbols { self: Universe =>
* with an object definition (module class in scala compiler parlance).
* If yes, `isType` is also guaranteed to be true.
*
+ * Note to compiler developers: During the "mixin" phase, trait implementation class symbols
+ * receive the `lateMODULE` flag, hence `isImplClass && isModuleClass` becomes true.
+ *
* @group Tests
*/
def isModuleClass: Boolean = false
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index 2ce54d2259..b6cce4524b 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -55,23 +55,29 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
def newFreeTypeSymbol(name: TypeName, flags: Long = 0L, origin: String): FreeTypeSymbol =
new FreeTypeSymbol(name, origin) initFlags flags
- /** The original owner of a class. Used by the backend to generate
- * EnclosingMethod attributes.
+ /**
+ * This map stores the original owner the the first time the owner of a symbol is re-assigned.
+ * The original owner of a symbol is needed in some places in the backend. Ideally, owners should
+ * be versioned like the type history.
*/
- val originalOwner = perRunCaches.newMap[Symbol, Symbol]()
+ private val originalOwnerMap = perRunCaches.newMap[Symbol, Symbol]()
// TODO - don't allow the owner to be changed without checking invariants, at least
// when under some flag. Define per-phase invariants for owner/owned relationships,
// e.g. after flatten all classes are owned by package classes, there are lots and
// lots of these to be declared (or more realistically, discovered.)
- protected def saveOriginalOwner(sym: Symbol) {
- if (originalOwner contains sym) ()
- else originalOwner(sym) = sym.rawowner
+ protected def saveOriginalOwner(sym: Symbol): Unit = {
+ // some synthetic symbols have NoSymbol as owner initially
+ if (sym.owner != NoSymbol) {
+ if (originalOwnerMap contains sym) ()
+ else originalOwnerMap(sym) = sym.rawowner
+ }
}
+
protected def originalEnclosingMethod(sym: Symbol): Symbol = {
if (sym.isMethod || sym == NoSymbol) sym
else {
- val owner = originalOwner.getOrElse(sym, sym.rawowner)
+ val owner = sym.originalOwner
if (sym.isLocalDummy) owner.enclClass.primaryConstructor
else originalEnclosingMethod(owner)
}
@@ -757,8 +763,22 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
* So "isModuleNotMethod" exists not for its achievement in
* brevity, but to encapsulate the relevant condition.
*/
- def isModuleNotMethod = isModule && !isMethod
- def isStaticModule = isModuleNotMethod && isStatic
+ def isModuleNotMethod = {
+ if (isModule) {
+ if (phase.refChecked) this.info // force completion to make sure lateMETHOD is there.
+ !isMethod
+ } else false
+ }
+
+ // After RefChecks, the `isStatic` check is mostly redundant: all non-static modules should
+ // be methods (and vice versa). There's a corner case on the vice-versa with mixed-in module
+ // symbols:
+ // trait T { object A }
+ // object O extends T
+ // The module symbol A is cloned into T$impl (addInterfaces), and then cloned into O (mixin).
+ // Since the original A is not static, it's turned into a method. The clone in O however is
+ // static (owned by a module), but it's also a method.
+ def isStaticModule = isModuleNotMethod && isStatic
final def isInitializedToDefault = !isType && hasAllFlags(DEFAULTINIT | ACCESSOR)
final def isThisSym = isTerm && owner.thisSym == this
@@ -909,10 +929,31 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
)
final def isModuleVar = hasFlag(MODULEVAR)
- /** Is this symbol static (i.e. with no outer instance)?
- * Q: When exactly is a sym marked as STATIC?
- * A: If it's a member of a toplevel object, or of an object contained in a toplevel object, or any number of levels deep.
- * http://groups.google.com/group/scala-internals/browse_thread/thread/d385bcd60b08faf6
+ /**
+ * Is this symbol static (i.e. with no outer instance)?
+ * Q: When exactly is a sym marked as STATIC?
+ * A: If it's a member of a toplevel object, or of an object contained in a toplevel object, or
+ * any number of levels deep.
+ * http://groups.google.com/group/scala-internals/browse_thread/thread/d385bcd60b08faf6
+ *
+ * TODO: should this only be invoked on class / module symbols? because there's also `isStaticMember`.
+ *
+ * Note: the result of `isStatic` changes over time.
+ * - Lambdalift local definitions to the class level, the `owner` field is modified.
+ * object T { def foo { object O } }
+ * After lambdalift, the OModule.isStatic is true.
+ *
+ * - After flatten, nested classes are moved to the package level. Invoking `owner` on a
+ * class returns a package class, for which `isStaticOwner` is true. For example,
+ * class C { object O }
+ * OModuleClass.isStatic is true after flatten. Using phase travel to get before flatten,
+ * method `owner` returns the class C.
+ *
+ * Why not make a stable version of `isStatic`? Maybe some parts of the compiler depend on the
+ * current implementation. For example
+ * trait T { def foo = 1 }
+ * The method `foo` in the implementation class T$impl will be `isStatic`, because trait
+ * impl classes get the `lateMODULE` flag (T$impl.isStaticOwner is true).
*/
def isStatic = (this hasFlag STATIC) || owner.isStaticOwner
@@ -1099,13 +1140,28 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
// ------ owner attribute --------------------------------------------------------------
- /** In general when seeking the owner of a symbol, one should call `owner`.
- * The other possibilities include:
- * - call `safeOwner` if it is expected that the target may be NoSymbol
- * - call `assertOwner` if it is an unrecoverable error if the target is NoSymbol
+ /**
+ * The owner of a symbol. Changes over time to adapt to the structure of the trees:
+ * - Up to lambdalift, the owner is the lexically enclosing definition. For definitions
+ * in a local block, the owner is also the next enclosing definition.
+ * - After lambdalift, all local method and class definitions (those not owned by a class
+ * or package class) change their owner to the enclosing class. This is done through
+ * a destructive "sym.owner = sym.owner.enclClass". The old owner is saved by
+ * saveOriginalOwner.
+ * - After flatten, all classes are owned by a PackageClass. This is done through a
+ * phase check (if after flatten) in the (overridden) method "def owner" in
+ * ModuleSymbol / ClassSymbol. The `rawowner` field is not modified.
+ * - Owners are also changed in other situations, for example when moving trees into a new
+ * lexical context, e.g. in the named/default arguments tranformation, or when translating
+ * extension method definitions.
+ *
+ * In general when seeking the owner of a symbol, one should call `owner`.
+ * The other possibilities include:
+ * - call `safeOwner` if it is expected that the target may be NoSymbol
+ * - call `assertOwner` if it is an unrecoverable error if the target is NoSymbol
*
- * `owner` behaves like `safeOwner`, but logs NoSymbol.owner calls under -Xdev.
- * `assertOwner` aborts compilation immediately if called on NoSymbol.
+ * `owner` behaves like `safeOwner`, but logs NoSymbol.owner calls under -Xdev.
+ * `assertOwner` aborts compilation immediately if called on NoSymbol.
*/
def owner: Symbol = {
if (Statistics.hotEnabled) Statistics.incCounter(ownerCount)
@@ -1114,6 +1170,11 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
final def safeOwner: Symbol = if (this eq NoSymbol) NoSymbol else owner
final def assertOwner: Symbol = if (this eq NoSymbol) abort("no-symbol does not have an owner") else owner
+ /**
+ * The initial owner of this symbol.
+ */
+ def originalOwner: Symbol = originalOwnerMap.getOrElse(this, rawowner)
+
// TODO - don't allow the owner to be changed without checking invariants, at least
// when under some flag. Define per-phase invariants for owner/owned relationships,
// e.g. after flatten all classes are owned by package classes, there are lots and
@@ -1127,7 +1188,6 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
}
def ownerChain: List[Symbol] = this :: owner.ownerChain
- def originalOwnerChain: List[Symbol] = this :: originalOwner.getOrElse(this, rawowner).originalOwnerChain
// Non-classes skip self and return rest of owner chain; overridden in ClassSymbol.
def enclClassChain: List[Symbol] = owner.enclClassChain
@@ -2811,6 +2871,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
override def owner = {
if (Statistics.hotEnabled) Statistics.incCounter(ownerCount)
+ // a module symbol may have the lateMETHOD flag after refchecks, see isModuleNotMethod
if (!isMethod && needsFlatClasses) rawowner.owner
else rawowner
}
diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala
index 7ba68b8733..50ea8d9868 100644
--- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala
+++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala
@@ -65,10 +65,15 @@ private[reflect] trait SymbolLoaders { self: SymbolTable =>
class LazyPackageType extends LazyType with FlagAgnosticCompleter {
override def complete(sym: Symbol) {
assert(sym.isPackageClass)
- sym setInfo new ClassInfoType(List(), new PackageScope(sym), sym)
+ // Time travel to a phase before refchecks avoids an initialization issue. `openPackageModule`
+ // creates a module symbol and invokes invokes `companionModule` while the `infos` field is
+ // still null. This calls `isModuleNotMethod`, which forces the `info` if run after refchecks.
+ slowButSafeEnteringPhaseNotLaterThan(picklerPhase) {
+ sym setInfo new ClassInfoType(List(), new PackageScope(sym), sym)
// override def safeToString = pkgClass.toString
- openPackageModule(sym)
- markAllCompleted(sym)
+ openPackageModule(sym)
+ markAllCompleted(sym)
+ }
}
}