diff options
Diffstat (limited to 'src/compiler')
13 files changed, 84 insertions, 58 deletions
diff --git a/src/compiler/scala/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/reflect/quasiquotes/Reifiers.scala index e753c9787a..8462debe21 100644 --- a/src/compiler/scala/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/reflect/quasiquotes/Reifiers.scala @@ -322,7 +322,7 @@ trait Reifiers { self: Quasiquotes => * in the domain of the fill function; * * 2. fold the groups into a sequence of lists added together with ++ using - * fill reification for holeMapĀ and fallback reification for non-holeMap. + * fill reification for holeMap and fallback reification for non-holeMap. * * Example: * diff --git a/src/compiler/scala/tools/nsc/Properties.scala b/src/compiler/scala/tools/nsc/Properties.scala index 9f160e2485..ca7d8776d4 100644 --- a/src/compiler/scala/tools/nsc/Properties.scala +++ b/src/compiler/scala/tools/nsc/Properties.scala @@ -13,7 +13,7 @@ object Properties extends scala.util.PropertiesTrait { // settings based on jar properties, falling back to System prefixed by "scala." def residentPromptString = scalaPropOrElse("resident.prompt", "\nnsc> ") - def shellPromptString = scalaPropOrElse("shell.prompt", "\nscala> ") + def shellPromptString = scalaPropOrElse("shell.prompt", "%nscala> ") // message to display at EOF (which by default ends with // a newline so as not to break the user's terminal) def shellInterruptedString = scalaPropOrElse("shell.interrupted", f":quit$lineSeparator") diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala index eadc404bee..dec5adc9aa 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala @@ -9,6 +9,7 @@ package backend.jvm import scala.tools.nsc.Global import scala.tools.nsc.backend.jvm.BTypes.{InternalName, MethodInlineInfo, InlineInfo} import BackendReporting.ClassSymbolInfoFailureSI9111 +import scala.tools.asm /** * This trait contains code shared between GenBCode and GenASM that depends on types defined in @@ -229,6 +230,44 @@ final class BCodeAsmCommon[G <: Global](val global: G) { } /** + * Reconstruct the classfile flags from a Java defined class symbol. + * + * The implementation of this method is slightly different that `javaFlags` in BTypesFromSymbols. + * The javaFlags method is primarily used to map Scala symbol flags to sensible classfile flags + * that are used in the generated classfiles. For example, all classes emitted by the Scala + * compiler have ACC_PUBLIC. + * + * When building a [[ClassBType]] from a Java class symbol, the flags in the type's `info` have + * to correspond exactly to the flags in the classfile. For example, if the class is package + * protected (i.e., it doesn't have the ACC_PUBLIC flag), this needs to be reflected in the + * ClassBType. For example, the inliner needs the correct flags for access checks. + * + * Class flags are listed here: + * https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1-200-E.1 + */ + def javaClassfileFlags(classSym: Symbol): Int = { + assert(classSym.isJava, s"Expected Java class symbol, got ${classSym.fullName}") + import asm.Opcodes._ + def enumFlags = ACC_ENUM | { + // Java enums have the `ACC_ABSTRACT` flag if they have a deferred method. + // We cannot trust `hasAbstractFlag`: the ClassfileParser adds `ABSTRACT` and `SEALED` to all + // Java enums for exhaustiveness checking. + val hasAbstractMethod = classSym.info.decls.exists(s => s.isMethod && s.isDeferred) + if (hasAbstractMethod) ACC_ABSTRACT else 0 + } + GenBCode.mkFlags( + if (classSym.isPublic) ACC_PUBLIC else 0, + if (classSym.isFinal) ACC_FINAL else 0, + // see the link above. javac does the same: ACC_SUPER for all classes, but not interfaces. + if (classSym.isInterface) ACC_INTERFACE else ACC_SUPER, + // for Java enums, we cannot trust `hasAbstractFlag` (see comment in enumFlags) + if (!classSym.hasEnumFlag && classSym.hasAbstractFlag) ACC_ABSTRACT else 0, + if (classSym.isArtifact) ACC_SYNTHETIC else 0, + if (classSym.hasEnumFlag) enumFlags else 0 + ) + } + + /** * The member classes of a class symbol. Note that the result of this method depends on the * current phase, for example, after lambdalift, all local classes become member of the enclosing * class. @@ -399,3 +438,16 @@ final class BCodeAsmCommon[G <: Global](val global: G) { InlineInfo(traitSelfType, isEffectivelyFinal, methodInlineInfos, warning) } } + +object BCodeAsmCommon { + /** + * Valid flags for InnerClass attribute entry. + * See http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.6 + */ + val INNER_CLASSES_FLAGS = { + asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_PRIVATE | asm.Opcodes.ACC_PROTECTED | + asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_FINAL | asm.Opcodes.ACC_INTERFACE | + asm.Opcodes.ACC_ABSTRACT | asm.Opcodes.ACC_SYNTHETIC | asm.Opcodes.ACC_ANNOTATION | + asm.Opcodes.ACC_ENUM + } +} diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala index ec0017270e..9c6889668d 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala @@ -900,7 +900,7 @@ abstract class BTypes { // the static flag in the InnerClass table has a special meaning, see InnerClass comment i.flags & ~Opcodes.ACC_STATIC, if (isStaticNestedClass) Opcodes.ACC_STATIC else 0 - ) & ClassBType.INNER_CLASSES_FLAGS + ) & BCodeAsmCommon.INNER_CLASSES_FLAGS ) }) @@ -989,17 +989,6 @@ abstract class BTypes { } object ClassBType { - /** - * Valid flags for InnerClass attribute entry. - * See http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.6 - */ - private val INNER_CLASSES_FLAGS = { - asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_PRIVATE | asm.Opcodes.ACC_PROTECTED | - asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_FINAL | asm.Opcodes.ACC_INTERFACE | - asm.Opcodes.ACC_ABSTRACT | asm.Opcodes.ACC_SYNTHETIC | asm.Opcodes.ACC_ANNOTATION | - asm.Opcodes.ACC_ENUM - } - // Primitive classes have no super class. A ClassBType for those is only created when // they are actually being compiled (e.g., when compiling scala/Boolean.scala). private val hasNoSuper = Set( diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala index 8740193b58..5f8f0e167c 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala @@ -215,35 +215,6 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { assert(!primitiveTypeMap.contains(sym) || isCompilingPrimitive, sym) } - /** - * Reconstruct the classfile flags from a Java defined class symbol. - * - * The implementation of this method is slightly different that [[javaFlags]]. The javaFlags - * method is primarily used to map Scala symbol flags to sensible classfile flags that are used - * in the generated classfiles. For example, all classes emitted by the Scala compiler have - * ACC_PUBLIC. - * - * When building a [[ClassBType]] from a Java class symbol, the flags in the type's `info` have - * to correspond exactly to the flags in the classfile. For example, if the class is package - * protected (i.e., it doesn't have the ACC_PUBLIC flag), this needs to be reflected in the - * ClassBType. For example, the inliner needs the correct flags for access checks. - * - * Class flags are listed here: - * https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1-200-E.1 - */ - private def javaClassfileFlags(classSym: Symbol): Int = { - assert(classSym.isJava, s"Expected Java class symbol, got ${classSym.fullName}") - import asm.Opcodes._ - GenBCode.mkFlags( - if (classSym.isPublic) ACC_PUBLIC else 0, - if (classSym.isFinal) ACC_FINAL else 0, - if (classSym.isInterface) ACC_INTERFACE else ACC_SUPER, // see the link above. javac does the same: ACC_SUPER for all classes, but not interfaces. - if (classSym.hasAbstractFlag) ACC_ABSTRACT else 0, - if (classSym.isArtifact) ACC_SYNTHETIC else 0, - if (classSym.hasEnumFlag) ACC_ENUM else 0 - ) - } - private def setClassInfo(classSym: Symbol, classBType: ClassBType): ClassBType = { val superClassSym = if (classSym.isImplClass) ObjectClass else classSym.superClass assert( diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 76af40b330..71686fd9d7 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -479,10 +479,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self => val CLASS_CONSTRUCTOR_NAME = "<clinit>" val INSTANCE_CONSTRUCTOR_NAME = "<init>" - val INNER_CLASSES_FLAGS = - (asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_PRIVATE | asm.Opcodes.ACC_PROTECTED | - asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_INTERFACE | asm.Opcodes.ACC_ABSTRACT | asm.Opcodes.ACC_FINAL) - // ----------------------------------------------------------------------------------------- // factory methods // ----------------------------------------------------------------------------------------- @@ -756,9 +752,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self => val flagsWithFinal: Int = mkFlags( // See comment in BTypes, when is a class marked static in the InnerClass table. if (isOriginallyStaticOwner(innerSym.originalOwner)) asm.Opcodes.ACC_STATIC else 0, - javaFlags(innerSym), + (if (innerSym.isJava) javaClassfileFlags(innerSym) else javaFlags(innerSym)) & ~asm.Opcodes.ACC_STATIC, if(isDeprecated(innerSym)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo-access flag - ) & (INNER_CLASSES_FLAGS | asm.Opcodes.ACC_DEPRECATED) + ) & (BCodeAsmCommon.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 = javaName(innerSym) // never null val oname = outerName(innerSym) // null when method-enclosed diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 1b6631e7a4..8911a3a28c 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -378,7 +378,7 @@ abstract class DeadCodeElimination extends SubComponent { } else { i match { case NEW(REFERENCE(sym)) => - log(s"Eliminated instantation of $sym inside $m") + log(s"Eliminated instantiation of $sym inside $m") case STORE_LOCAL(l) if clobbers contains ((bb, idx)) => // if an unused instruction was a clobber of a used store to a reference or array type // then we'll replace it with the store of a null to make sure the reference is diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index d34c14be0f..9708cba281 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -761,9 +761,13 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { val interfaces = interfacesOpt() accept(LBRACE) val buf = new ListBuffer[Tree] + var enumIsFinal = true def parseEnumConsts() { if (in.token != RBRACE && in.token != SEMI && in.token != EOF) { - buf += enumConst(enumType) + val (const, hasClassBody) = enumConst(enumType) + buf += const + // if any of the enum constants has a class body, the enum class is not final (JLS 8.9.) + enumIsFinal &&= !hasClassBody if (in.token == COMMA) { in.nextToken() parseEnumConsts() @@ -793,15 +797,25 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { accept(RBRACE) val superclazz = AppliedTypeTree(javaLangDot(tpnme.Enum), List(enumType)) + val finalFlag = if (enumIsFinal) Flags.FINAL else 0l + val abstractFlag = { + // javac adds `ACC_ABSTRACT` to enum classes with deferred members + val hasAbstractMember = body exists { + case d: DefDef => d.mods.isDeferred + case _ => false + } + if (hasAbstractMember) Flags.ABSTRACT else 0l + } addCompanionObject(consts ::: statics ::: predefs, atPos(pos) { - ClassDef(mods | Flags.ENUM, name, List(), + ClassDef(mods | Flags.ENUM | finalFlag | abstractFlag, name, List(), makeTemplate(superclazz :: interfaces, body)) }) } - def enumConst(enumType: Tree) = { + def enumConst(enumType: Tree): (ValDef, Boolean) = { annotations() - atPos(in.currentPos) { + var hasClassBody = false + val res = atPos(in.currentPos) { val name = ident() if (in.token == LPAREN) { // skip arguments @@ -809,12 +823,14 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { accept(RPAREN) } if (in.token == LBRACE) { + hasClassBody = true // skip classbody skipAhead() accept(RBRACE) } ValDef(Modifiers(Flags.ENUM | Flags.STABLE | Flags.JAVA | Flags.STATIC), name.toTermName, enumType, blankExpr) } + (res, hasClassBody) } def typeDecl(mods: Modifiers): List[Tree] = in.token match { diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 518a402230..660028eab8 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -539,6 +539,8 @@ abstract class ClassfileParser { devWarning(s"no linked class for java enum $sym in ${sym.owner}. A referencing class file might be missing an InnerClasses entry.") case linked => if (!linked.isSealed) + // Marking the enum class SEALED | ABSTRACT enables exhaustiveness checking. + // This is a bit of a hack and requires excluding the ABSTRACT flag in the backend, see method javaClassfileFlags. linked setFlag (SEALED | ABSTRACT) linked addChild sym } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index ea46116976..438a71061e 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -599,7 +599,7 @@ abstract class ICodeReader extends ClassfileParser { } case JVM.invokedynamic => // TODO, this is just a place holder. A real implementation must parse the class constant entry - debuglog("Found JVM invokedynamic instructionm, inserting place holder ICode INVOKE_DYNAMIC.") + debuglog("Found JVM invokedynamic instruction, inserting place holder ICode INVOKE_DYNAMIC.") containsInvokeDynamic = true val poolEntry = in.nextChar.toInt in.skip(2) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 5ecca5abce..80e06eb8fa 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -985,7 +985,7 @@ trait Implicits { if (implicitInfoss.forall(_.isEmpty)) SearchFailure else new ImplicitComputation(implicitInfoss, isLocalToCallsite) findBest() - /** Produce an implicict info map, i.e. a map from the class symbols C of all parts of this type to + /** Produce an implicit info map, i.e. a map from the class symbols C of all parts of this type to * the implicit infos in the companion objects of these class symbols C. * The parts of a type is the smallest set of types that contains * - the type itself diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index f9582a54ff..ea0a9bb243 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -934,7 +934,7 @@ trait Infer extends Checkable { def infer_s = map3(tparams, tvars, targs)((tparam, tvar, targ) => s"$tparam=$tvar/$targ") mkString "," printTyping(tree, s"infer expr instance from pt=$pt, $infer_s") - // SI-7899 infering by-name types is unsound. The correct behaviour is conditional because the hole is + // SI-7899 inferring by-name types is unsound. The correct behaviour is conditional because the hole is // exploited in Scalaz (Free.scala), as seen in: run/t7899-regression. def dropByNameIfStrict(tp: Type): Type = if (settings.inferByName) tp else dropByName(tp) def targsStrict = if (targs eq null) null else targs mapConserve dropByNameIfStrict diff --git a/src/compiler/scala/tools/nsc/util/DocStrings.scala b/src/compiler/scala/tools/nsc/util/DocStrings.scala index 352816803f..4ff7067a21 100755 --- a/src/compiler/scala/tools/nsc/util/DocStrings.scala +++ b/src/compiler/scala/tools/nsc/util/DocStrings.scala @@ -184,7 +184,7 @@ object DocStrings { extractSectionTag(str, section) -> section } - /** Extract the section tag, treating the section tag as an indentifier */ + /** Extract the section tag, treating the section tag as an identifier */ def extractSectionTag(str: String, section: (Int, Int)): String = str.substring(section._1, skipTag(str, section._1)) |