diff options
30 files changed, 469 insertions, 297 deletions
@@ -2665,100 +2665,53 @@ Binary compatibility testing </artifact:dependencies> </target> + <macrodef name="bc.run-mima"> + <attribute name="jar-name"/> + <attribute name="prev"/> + <attribute name="curr"/> + <attribute name="direction"/> + <sequential> + <echo message="Checking @{direction} binary compatibility for @{jar-name}"/> + <java + fork="true" + failonerror="true" + classname="com.typesafe.tools.mima.cli.Main"> + <arg value="--prev"/> + <arg value="@{prev}"/> + <arg value="--curr"/> + <arg value="@{curr}"/> + <arg value="--filters"/> + <arg value="${basedir}/bincompat-@{direction}.whitelist.conf"/> + <arg value="--generate-filters"/> + <classpath> + <path refid="mima.classpath"/> + </classpath> + </java> + </sequential> + </macrodef> + + <macrodef name="bc.check"> + <attribute name="jar-name"/> + <sequential> + <bc.run-mima + jar-name="@{jar-name}" + prev="${org.scala-lang:@{jar-name}:jar}" + curr="${build-pack.dir}/lib/@{jar-name}.jar" + direction="backward"/> + <bc.run-mima + jar-name="@{jar-name}" + prev="${build-pack.dir}/lib/@{jar-name}.jar" + curr="${org.scala-lang:@{jar-name}:jar}" + direction="forward"/> + </sequential> + </macrodef> + <target name="bc.run" depends="bc.init, pack.done"> - <java - fork="true" - failonerror="true" - classname="com.typesafe.tools.mima.cli.Main"> - <arg value="--prev"/> - <arg value="${org.scala-lang:scala-library:jar}"/> - <arg value="--curr"/> - <arg value="${build-pack.dir}/lib/scala-library.jar"/> - <arg value="--filters"/> - <arg value="${basedir}/bincompat-backward.whitelist.conf"/> - <arg value="--generate-filters"/> - <classpath> - <path refid="mima.classpath"/> - </classpath> - </java> - <java - fork="true" - failonerror="true" - classname="com.typesafe.tools.mima.cli.Main"> - <arg value="--prev"/> - <arg value="${org.scala-lang:scala-reflect:jar}"/> - <arg value="--curr"/> - <arg value="${build-pack.dir}/lib/scala-reflect.jar"/> - <arg value="--filters"/> - <arg value="${basedir}/bincompat-backward.whitelist.conf"/> - <arg value="--generate-filters"/> - <classpath> - <path refid="mima.classpath"/> - </classpath> - </java> - <java - fork="true" - failonerror="true" - classname="com.typesafe.tools.mima.cli.Main"> - <arg value="--prev"/> - <arg value="${org.scala-lang:scala-swing:jar}"/> - <arg value="--curr"/> - <arg value="${build-pack.dir}/lib/scala-swing.jar"/> - <arg value="--filters"/> - <arg value="${basedir}/bincompat-backward.whitelist.conf"/> - <arg value="--generate-filters"/> - <classpath> - <path refid="mima.classpath"/> - </classpath> - </java> - <java - fork="true" - failonerror="true" - classname="com.typesafe.tools.mima.cli.Main"> - <arg value="--curr"/> - <arg value="${org.scala-lang:scala-library:jar}"/> - <arg value="--prev"/> - <arg value="${build-pack.dir}/lib/scala-library.jar"/> - <arg value="--filters"/> - <arg value="${basedir}/bincompat-forward.whitelist.conf"/> - <arg value="--generate-filters"/> - <classpath> - <path refid="mima.classpath"/> - </classpath> - </java> - <java - fork="true" - failonerror="true" - classname="com.typesafe.tools.mima.cli.Main"> - <arg value="--curr"/> - <arg value="${org.scala-lang:scala-reflect:jar}"/> - <arg value="--prev"/> - <arg value="${build-pack.dir}/lib/scala-reflect.jar"/> - <arg value="--filters"/> - <arg value="${basedir}/bincompat-forward.whitelist.conf"/> - <arg value="--generate-filters"/> - <classpath> - <path refid="mima.classpath"/> - </classpath> - </java> - <java - fork="true" - failonerror="true" - classname="com.typesafe.tools.mima.cli.Main"> - <arg value="--curr"/> - <arg value="${org.scala-lang:scala-swing:jar}"/> - <arg value="--prev"/> - <arg value="${build-pack.dir}/lib/scala-swing.jar"/> - <arg value="--filters"/> - <arg value="${basedir}/bincompat-forward.whitelist.conf"/> - <arg value="--generate-filters"/> - <classpath> - <path refid="mima.classpath"/> - </classpath> - </java> + <bc.check jar-name="scala-library"/> + <bc.check jar-name="scala-reflect"/> + <bc.check jar-name="scala-swing"/> </target> - <!-- =========================================================================== DISTRIBUTION ============================================================================ --> diff --git a/src/compiler/scala/reflect/reify/utils/Extractors.scala b/src/compiler/scala/reflect/reify/utils/Extractors.scala index 134ae13890..59cd4e5047 100644 --- a/src/compiler/scala/reflect/reify/utils/Extractors.scala +++ b/src/compiler/scala/reflect/reify/utils/Extractors.scala @@ -164,51 +164,30 @@ trait Extractors { } } - object FreeDef { - def unapply(tree: Tree): Option[(Tree, TermName, Tree, Long, String)] = tree match { - case FreeTermDef(uref, name, binding, flags, origin) => - Some((uref, name, binding, flags, origin)) - case FreeTypeDef(uref, name, binding, flags, origin) => - Some((uref, name, binding, flags, origin)) - case _ => - None - } - } - - object FreeTermDef { - def unapply(tree: Tree): Option[(Tree, TermName, Tree, Long, String)] = tree match { - case - ValDef(_, name, _, Apply( - Select(Select(uref1 @ Ident(_), build1), newFreeTerm), - List( - _, - _, - Apply(Select(Select(uref2 @ Ident(_), build2), flagsFromBits), List(Literal(Constant(flags: Long)))), - Literal(Constant(origin: String))))) - if uref1.name == nme.UNIVERSE_SHORT && build1 == nme.build && newFreeTerm == nme.newFreeTerm && - uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsFromBits == nme.flagsFromBits => - Some(uref1, name, reifyBinding(tree), flags, origin) - case _ => - None - } - } - - object FreeTypeDef { - def unapply(tree: Tree): Option[(Tree, TermName, Tree, Long, String)] = tree match { - case - ValDef(_, name, _, Apply( - Select(Select(uref1 @ Ident(_), build1), newFreeType), - List( - _, - Apply(Select(Select(uref2 @ Ident(_), build2), flagsFromBits), List(Literal(Constant(flags: Long)))), - Literal(Constant(origin: String))))) - if uref1.name == nme.UNIVERSE_SHORT && build1 == nme.build && newFreeType == nme.newFreeType && - uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsFromBits == nme.flagsFromBits => - Some(uref1, name, reifyBinding(tree), flags, origin) - case _ => - None + sealed abstract class FreeDefExtractor(acceptTerms: Boolean, acceptTypes: Boolean) { + def unapply(tree: Tree): Option[(Tree, TermName, Tree, Long, String)] = { + def acceptFreeTermFactory(name: Name) = { + (acceptTerms && name == nme.newFreeTerm) || + (acceptTypes && name == nme.newFreeType) + } + tree match { + case + ValDef(_, name, _, Apply( + Select(Select(uref1 @ Ident(_), build1), freeTermFactory), + _ :+ + Apply(Select(Select(uref2 @ Ident(_), build2), flagsFromBits), List(Literal(Constant(flags: Long)))) :+ + Literal(Constant(origin: String)))) + if uref1.name == nme.UNIVERSE_SHORT && build1 == nme.build && acceptFreeTermFactory(freeTermFactory) && + uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsFromBits == nme.flagsFromBits => + Some(uref1, name, reifyBinding(tree), flags, origin) + case _ => + None + } } } + object FreeDef extends FreeDefExtractor(acceptTerms = true, acceptTypes = true) + object FreeTermDef extends FreeDefExtractor(acceptTerms = true, acceptTypes = false) + object FreeTypeDef extends FreeDefExtractor(acceptTerms = false, acceptTypes = true) object FreeRef { def unapply(tree: Tree): Option[(Tree, TermName)] = tree match { diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 9b16327ffc..7c46d648fe 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -2259,16 +2259,16 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { // info calls so that types are up to date; erasure may add lateINTERFACE to traits hostSymbol.info ; methodOwner.info - def isInterfaceCall(sym: Symbol) = ( - sym.isInterface && methodOwner != ObjectClass + def needsInterfaceCall(sym: Symbol) = ( + sym.isInterface || sym.isJavaDefined && sym.isNonBottomSubClass(ClassfileAnnotationClass) ) // whether to reference the type of the receiver or - // the type of the method owner (if not an interface!) + // the type of the method owner val useMethodOwner = ( style != Dynamic - || !isInterfaceCall(hostSymbol) && isAccessibleFrom(methodOwner, siteSymbol) || hostSymbol.isBottomClass + || methodOwner == ObjectClass ) val receiver = if (useMethodOwner) methodOwner else hostSymbol val jowner = javaName(receiver) @@ -2291,11 +2291,11 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } style match { - case Static(true) => dbg("invokespecial"); jcode.invokespecial (jowner, jname, jtype) - case Static(false) => dbg("invokestatic"); jcode.invokestatic (jowner, jname, jtype) - case Dynamic if isInterfaceCall(receiver) => dbg("invokinterface"); jcode.invokeinterface(jowner, jname, jtype) - case Dynamic => dbg("invokevirtual"); jcode.invokevirtual (jowner, jname, jtype) - case SuperCall(_) => + case Static(true) => dbg("invokespecial"); jcode.invokespecial (jowner, jname, jtype) + case Static(false) => dbg("invokestatic"); jcode.invokestatic (jowner, jname, jtype) + case Dynamic if needsInterfaceCall(receiver) => dbg("invokinterface"); jcode.invokeinterface(jowner, jname, jtype) + case Dynamic => dbg("invokevirtual"); jcode.invokevirtual (jowner, jname, jtype) + case SuperCall(_) => dbg("invokespecial") jcode.invokespecial(jowner, jname, jtype) initModule() diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index 598965b982..36b294b289 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -1196,16 +1196,16 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with // info calls so that types are up to date; erasure may add lateINTERFACE to traits hostSymbol.info ; methodOwner.info - def isInterfaceCall(sym: Symbol) = ( - sym.isInterface && methodOwner != ObjectClass + def needsInterfaceCall(sym: Symbol) = ( + sym.isInterface || sym.isJavaDefined && sym.isNonBottomSubClass(ClassfileAnnotationClass) ) // whether to reference the type of the receiver or - // the type of the method owner (if not an interface!) + // the type of the method owner val useMethodOwner = ( style != Dynamic - || !isInterfaceCall(hostSymbol) && isAccessibleFrom(methodOwner, siteSymbol) || hostSymbol.isBottomClass + || methodOwner == ObjectClass ) val receiver = if (useMethodOwner) methodOwner else hostSymbol val jowner = javaName(receiver) @@ -1230,11 +1230,11 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with } style match { - case Static(true) => dbg("invokespecial"); jcode.emitINVOKESPECIAL(jowner, jname, jtype) - case Static(false) => dbg("invokestatic"); jcode.emitINVOKESTATIC(jowner, jname, jtype) - case Dynamic if isInterfaceCall(receiver) => dbg("invokinterface"); jcode.emitINVOKEINTERFACE(jowner, jname, jtype) - case Dynamic => dbg("invokevirtual"); jcode.emitINVOKEVIRTUAL(jowner, jname, jtype) - case SuperCall(_) => + case Static(true) => dbg("invokespecial"); jcode.emitINVOKESPECIAL(jowner, jname, jtype) + case Static(false) => dbg("invokestatic"); jcode.emitINVOKESTATIC(jowner, jname, jtype) + case Dynamic if needsInterfaceCall(receiver) => dbg("invokinterface"); jcode.emitINVOKEINTERFACE(jowner, jname, jtype) + case Dynamic => dbg("invokevirtual"); jcode.emitINVOKEVIRTUAL(jowner, jname, jtype) + case SuperCall(_) => dbg("invokespecial") jcode.emitINVOKESPECIAL(jowner, jname, jtype) initModule() diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index 43a8402fc7..8aa9b81a72 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -800,13 +800,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { val pos = in.currentPos val name = identForType() val (statics, body) = typeBody(AT, name) - def getValueMethodType(tree: Tree) = tree match { - case DefDef(_, nme.value, _, _, tpt, _) => Some(tpt.duplicate) - case _ => None - } - var templ = makeTemplate(annotationParents, body) - for (stat <- templ.body; tpt <- getValueMethodType(stat)) - templ = makeTemplate(annotationParents, makeConstructor(List(tpt)) :: templ.body) + val templ = makeTemplate(annotationParents, body) addCompanionObject(statics, atPos(pos) { ClassDef(mods, name, List(), templ) }) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 1f42fa8aab..7f822e1c5d 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -44,7 +44,7 @@ abstract class ClassfileParser { def srcfile = srcfile0 - private def currentIsTopLevel = currentClass.toString.indexOf('$') < 0 + private def currentIsTopLevel = !(currentClass.decodedName containsChar '$') private object unpickler extends scala.reflect.internal.pickling.UnPickler { val global: ClassfileParser.this.global.type = ClassfileParser.this.global @@ -436,63 +436,58 @@ abstract class ClassfileParser { sym } - /** Return the class symbol of the given name. */ - def classNameToSymbol(name: Name): Symbol = { - def loadClassSymbol(name: Name): Symbol = { - val file = global.classPath findSourceFile ("" +name) getOrElse { - // SI-5593 Scaladoc's current strategy is to visit all packages in search of user code that can be documented - // therefore, it will rummage through the classpath triggering errors whenever it encounters package objects - // that are not in their correct place (see bug for details) - if (!settings.isScaladoc) - warning("Class " + name + " not found - continuing with a stub.") - return NoSymbol.newClass(name.toTypeName) - } - val completer = new global.loaders.ClassfileLoader(file) - var owner: Symbol = rootMirror.RootClass - var sym: Symbol = NoSymbol - var ss: Name = null - var start = 0 - var end = name indexOf '.' - - while (end > 0) { - ss = name.subName(start, end) - sym = owner.info.decls lookup ss - if (sym == NoSymbol) { - sym = owner.newPackage(ss) setInfo completer - sym.moduleClass setInfo completer - owner.info.decls enter sym - } - owner = sym.moduleClass - start = end + 1 - end = name.indexOf('.', start) - } - ss = name.subName(0, start) - owner.info.decls lookup ss orElse { - sym = owner.newClass(ss.toTypeName) setInfoAndEnter completer - debuglog("loaded "+sym+" from file "+file) - sym + private def loadClassSymbol(name: Name): Symbol = { + val file = global.classPath findSourceFile ("" +name) getOrElse { + // SI-5593 Scaladoc's current strategy is to visit all packages in search of user code that can be documented + // therefore, it will rummage through the classpath triggering errors whenever it encounters package objects + // that are not in their correct place (see bug for details) + if (!settings.isScaladoc) + warning(s"Class $name not found - continuing with a stub.") + return NoSymbol.newClass(name.toTypeName) + } + val completer = new global.loaders.ClassfileLoader(file) + var owner: Symbol = rootMirror.RootClass + var sym: Symbol = NoSymbol + var ss: Name = null + var start = 0 + var end = name indexOf '.' + + while (end > 0) { + ss = name.subName(start, end) + sym = owner.info.decls lookup ss + if (sym == NoSymbol) { + sym = owner.newPackage(ss) setInfo completer + sym.moduleClass setInfo completer + owner.info.decls enter sym } + owner = sym.moduleClass + start = end + 1 + end = name.indexOf('.', start) } - - def lookupClass(name: Name) = try { - if (name.pos('.') == name.length) - definitions.getMember(rootMirror.EmptyPackageClass, name.toTypeName) - else - rootMirror.getClass(name) // see tickets #2464, #3756 - } catch { - case _: FatalError => loadClassSymbol(name) + ss = name.subName(0, start) + owner.info.decls lookup ss orElse { + sym = owner.newClass(ss.toTypeName) setInfoAndEnter completer + debuglog("loaded "+sym+" from file "+file) + sym } + } + /** FIXME - we shouldn't be doing ad hoc lookups in the empty package. + * The method called "getClassByName" should either return the class or not. + */ + private def lookupClass(name: Name) = ( + if (name containsChar '.') + rootMirror getClassByName name // see tickets #2464, #3756 + else + definitions.getMember(rootMirror.EmptyPackageClass, name.toTypeName) + ) - innerClasses.get(name) match { - case Some(entry) => - //println("found inner class " + name) - val res = innerClasses.classSymbol(entry.externalName) - //println("\trouted to: " + res) - res - case None => - //if (name.toString.contains("$")) println("No inner class: " + name + innerClasses + " while parsing " + in.file.name) - lookupClass(name) - } + /** Return the class symbol of the given name. */ + def classNameToSymbol(name: Name): Symbol = { + if (innerClasses contains name) + innerClasses innerSymbol name + else + try lookupClass(name) + catch { case _: FatalError => loadClassSymbol(name) } } var sawPrivateConstructor = false @@ -646,7 +641,7 @@ abstract class ClassfileParser { info match { case MethodType(params, restpe) => // if this is a non-static inner class, remove the explicit outer parameter - val newParams = innerClasses.get(currentClass) match { + val newParams = innerClasses getEntry currentClass match { case Some(entry) if !isScalaRaw && !isStatic(entry.jflags) => /* About `clazz.owner.isPackage` below: SI-5957 * For every nested java class A$B, there are two symbols in the scala compiler. @@ -748,7 +743,7 @@ abstract class ClassfileParser { res } case tp => - assert(sig.charAt(index) != '<', tp) + assert(sig.charAt(index) != '<', s"sig=$sig, index=$index, tp=$tp") tp } @@ -1107,7 +1102,7 @@ abstract class ClassfileParser { unlinkIfPresent(cName.toTypeName) } - for (entry <- innerClasses.values) { + for (entry <- innerClasses.entries) { // create a new class member for immediate inner classes if (entry.outerName == currentClass) { val file = global.classPath.findSourceFile(entry.externalName.toString) getOrElse { @@ -1145,14 +1140,9 @@ abstract class ClassfileParser { case tpnme.InnerClassesATTR if !isScala => val entries = in.nextChar.toInt for (i <- 0 until entries) { - val innerIndex = in.nextChar.toInt - val outerIndex = in.nextChar.toInt - val nameIndex = in.nextChar.toInt - val jflags = in.nextChar.toInt - if (innerIndex != 0 && outerIndex != 0 && nameIndex != 0) { - val entry = InnerClassEntry(innerIndex, outerIndex, nameIndex, jflags) - innerClasses += (pool.getClassName(innerIndex) -> entry) - } + val innerIndex, outerIndex, nameIndex, jflags = in.nextChar.toInt + if (innerIndex != 0 && outerIndex != 0 && nameIndex != 0) + innerClasses add InnerClassEntry(innerIndex, outerIndex, nameIndex, jflags) } case _ => in.skip(attrLen) @@ -1166,72 +1156,69 @@ abstract class ClassfileParser { def externalName = pool getClassName external def outerName = pool getClassName outer def originalName = pool getName name + def isStatic = ClassfileParser.this.isStatic(jflags) + def isModule = originalName.isTermName + def scope = if (isStatic) staticScope else instanceScope + def enclosing = if (isStatic) enclModule else enclClass + + // The name of the outer class, without its trailing $ if it has one. + private def strippedOuter = nme stripModuleSuffix outerName + private def isInner = innerClasses contains strippedOuter + private def enclClass = if (isInner) innerClasses innerSymbol strippedOuter else classNameToSymbol(strippedOuter) + private def enclModule = enclClass.companionModule + + private def staticWord = if (isStatic) "static " else "" + override def toString = s"$staticWord$originalName in $outerName ($externalName)" + } - override def toString = - originalName + " in " + outerName + "(" + externalName +")" + /** Return the Symbol of the top level class enclosing `name`, + * or the symbol of `name` itself if no enclosing classes are found. + */ + def topLevelClass(name: Name): Symbol = innerClasses getEntry name match { + case Some(entry) => topLevelClass(entry.outerName) + case _ => classNameToSymbol(name) } - object innerClasses extends scala.collection.mutable.HashMap[Name, InnerClassEntry] { - /** Return the Symbol of the top level class enclosing `name`, - * or 'name's symbol if no entry found for `name`. - */ - def topLevelClass(name: Name): Symbol = { - val tlName = if (isDefinedAt(name)) { - var entry = this(name) - while (isDefinedAt(entry.outerName)) - entry = this(entry.outerName) - entry.outerName - } else - name - classNameToSymbol(tlName) + /** Return the class symbol for the given name. It looks it up in its outer class. + * Forces all outer class symbols to be completed. + * + * If the given name is not an inner class, it returns the symbol found in `definitions`. + */ + object innerClasses { + private val inners = mutable.HashMap[Name, InnerClassEntry]() + + def contains(name: Name) = inners contains name + def getEntry(name: Name) = inners get name + def entries = inners.values + + def add(entry: InnerClassEntry): Unit = { + inners get entry.externalName foreach (existing => + debugwarn(s"Overwriting inner class entry! Was $existing, now $entry") + ) + inners(entry.externalName) = entry } - - /** Return the class symbol for `externalName`. It looks it up in its outer class. - * Forces all outer class symbols to be completed. - * - * If the given name is not an inner class, it returns the symbol found in `definitions`. - */ - def classSymbol(externalName: Name): Symbol = { - /** Return the symbol of `innerName`, having the given `externalName`. */ - def innerSymbol(externalName: Name, innerName: Name, static: Boolean): Symbol = { - def getMember(sym: Symbol, name: Name): Symbol = - if (static) - if (sym == clazz) staticScope.lookup(name) - else sym.companionModule.info.member(name) - else - if (sym == clazz) instanceScope.lookup(name) - else sym.info.member(name) - - innerClasses.get(externalName) match { - case Some(entry) => - val outerName = nme.stripModuleSuffix(entry.outerName) - val sym = classSymbol(outerName) - val s = - // if loading during initialization of `definitions` typerPhase is not yet set. - // in that case we simply load the member at the current phase - if (currentRun.typerPhase != null) - beforeTyper(getMember(sym, innerName.toTypeName)) - else - getMember(sym, innerName.toTypeName) - - assert(s ne NoSymbol, - "" + ((externalName, outerName, innerName, sym.fullLocationString)) + " / " + - " while parsing " + ((in.file, busy)) + - sym + "." + innerName + " linkedModule: " + sym.companionModule + sym.companionModule.info.members - ) - s - - case None => - classNameToSymbol(externalName) - } - } - - get(externalName) match { - case Some(entry) => - innerSymbol(entry.externalName, entry.originalName, isStatic(entry.jflags)) - case None => - classNameToSymbol(externalName) - } + def innerSymbol(externalName: Name): Symbol = this getEntry externalName match { + case Some(entry) => innerSymbol(entry) + case _ => NoSymbol + } + // if loading during initialization of `definitions` typerPhase is not yet set. + // in that case we simply load the member at the current phase + @inline private def enteringTyperIfPossible(body: => Symbol): Symbol = + if (currentRun.typerPhase eq null) body else beforeTyper(body) + + private def innerSymbol(entry: InnerClassEntry): Symbol = { + val name = entry.originalName.toTypeName + val enclosing = entry.enclosing + def getMember = ( + if (enclosing == clazz) entry.scope lookup name + else enclosing.info member name + ) + enteringTyperIfPossible(getMember) + /** There used to be an assertion that this result is not NoSymbol; changing it to an error + * revealed it had been going off all the time, but has been swallowed by a catch t: Throwable + * in Repository.scala. Since it has been accomplishing nothing except misleading anyone who + * thought it wasn't triggering, I removed it entirely. + */ } } diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 2f28a16416..f6ee7be511 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -112,6 +112,29 @@ abstract class ExplicitOuter extends InfoTransform sym setInfo clazz.outerClass.thisType } + /** + * Will the outer accessor of the `clazz` subsume the outer accessor of + * `mixin`? + * + * This arises when an inner object mixes in its companion trait. + * + * {{{ + * class C { + * trait T { C.this } // C$T$$$outer$ : C + * object T extends T { C.this } // C$T$$$outer$ : C.this.type + * } + * }}} + * + * See SI-7242. + }} + */ + private def skipMixinOuterAccessor(clazz: Symbol, mixin: Symbol) = { + // Reliant on the current scheme for name expansion, the expanded name + // of the outer accessors in a trait and its companion object are the same. + // If the assumption is one day falsified, run/t7424.scala will let us know. + clazz.fullName == mixin.fullName + } + /** <p> * The type transformation method: * </p> @@ -177,10 +200,14 @@ abstract class ExplicitOuter extends InfoTransform for (mc <- clazz.mixinClasses) { val mixinOuterAcc: Symbol = afterExplicitOuter(outerAccessor(mc)) if (mixinOuterAcc != NoSymbol) { - if (decls1 eq decls) decls1 = decls.cloneScope - val newAcc = mixinOuterAcc.cloneSymbol(clazz, mixinOuterAcc.flags & ~DEFERRED) - newAcc setInfo (clazz.thisType memberType mixinOuterAcc) - decls1 enter newAcc + if (skipMixinOuterAccessor(clazz, mc)) + debuglog(s"Reusing outer accessor symbol of $clazz for the mixin outer accessor of $mc") + else { + if (decls1 eq decls) decls1 = decls.cloneScope + val newAcc = mixinOuterAcc.cloneSymbol(clazz, mixinOuterAcc.flags & ~DEFERRED) + newAcc setInfo (clazz.thisType memberType mixinOuterAcc) + decls1 enter newAcc + } } } } @@ -390,6 +417,7 @@ abstract class ExplicitOuter extends InfoTransform val outerAcc = outerAccessor(mixinClass) overridingSymbol currentClass def mixinPrefix = (currentClass.thisType baseType mixinClass).prefix assert(outerAcc != NoSymbol, "No outer accessor for inner mixin " + mixinClass + " in " + currentClass) + assert(outerAcc.alternatives.size == 1, s"Multiple outer accessors match inner mixin $mixinClass in $currentClass : ${outerAcc.alternatives.map(_.defString)}") // I added the mixinPrefix.typeArgs.nonEmpty condition to address the // crash in SI-4970. I feel quite sure this can be improved. val path = ( @@ -492,7 +520,7 @@ abstract class ExplicitOuter extends InfoTransform } if (!currentClass.isTrait) for (mc <- currentClass.mixinClasses) - if (outerAccessor(mc) != NoSymbol) + if (outerAccessor(mc) != NoSymbol && !skipMixinOuterAccessor(currentClass, mc)) newDefs += mixinOuterAccessorDef(mc) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ef28837c51..2458fc54e1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4812,7 +4812,9 @@ trait Typers extends Modes with Adaptations with Tags { if (!reallyExists(sym)) { def handleMissing: Tree = { - if (context.owner.enclosingTopLevelClass.isJavaDefined && name.isTypeName) { + if (context.unit.isJava && name.isTypeName) { + // SI-3120 Java uses the same syntax, A.B, to express selection from the + // value A and from the type A. We have to try both. val tree1 = atPos(tree.pos) { gen.convertToSelectFromType(qual, name) } if (tree1 != EmptyTree) return typed1(tree1, mode, pt) } diff --git a/src/compiler/scala/tools/nsc/util/SimpleTracer.scala b/src/compiler/scala/tools/nsc/util/SimpleTracer.scala index b103ae9cb0..2601798b96 100644 --- a/src/compiler/scala/tools/nsc/util/SimpleTracer.scala +++ b/src/compiler/scala/tools/nsc/util/SimpleTracer.scala @@ -10,7 +10,7 @@ import java.io.PrintStream * @param enabled: A condition that must be true for trace info to be produced. */ class SimpleTracer(out: PrintStream, enabled: Boolean = true) { - def apply[T](msg: String)(value: T): T = { + def apply[T](msg: => String)(value: T): T = { if (enabled) out.println(msg+value) value } diff --git a/src/reflect/scala/reflect/runtime/JavaUniverse.scala b/src/reflect/scala/reflect/runtime/JavaUniverse.scala index e18435d5b0..1b69ca4e89 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverse.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverse.scala @@ -17,7 +17,7 @@ class JavaUniverse extends internal.SymbolTable with ReflectSetup with runtime.S def forInteractive = false def forScaladoc = false - def log(msg: => AnyRef): Unit = println(" [] "+msg) + def log(msg: => AnyRef): Unit = if (settings.debug.value) println(" [] "+msg) type TreeCopier = InternalTreeCopierOps def newStrictTreeCopier: TreeCopier = new StrictTreeCopier diff --git a/test/files/jvm/t7253.check b/test/files/jvm/t7253.check new file mode 100644 index 0000000000..43f53aba12 --- /dev/null +++ b/test/files/jvm/t7253.check @@ -0,0 +1 @@ +bytecode identical diff --git a/test/files/jvm/t7253/Base_1.scala b/test/files/jvm/t7253/Base_1.scala new file mode 100644 index 0000000000..a531ebb69d --- /dev/null +++ b/test/files/jvm/t7253/Base_1.scala @@ -0,0 +1,5 @@ +trait A { def f(): Int } +trait B1 extends A +abstract class B2 extends A +class B3 extends A { def f(): Int = 1 } +class B4 extends B3 diff --git a/test/files/jvm/t7253/JavaClient_1.java b/test/files/jvm/t7253/JavaClient_1.java new file mode 100644 index 0000000000..43475de2f5 --- /dev/null +++ b/test/files/jvm/t7253/JavaClient_1.java @@ -0,0 +1,9 @@ +public class JavaClient_1 { + int foo() { + ((A) null).f(); + ((B1) null).f(); + ((B2) null).f(); + ((B3) null).f(); + return ((B4) null).f(); + } +} diff --git a/test/files/jvm/t7253/ScalaClient_1.scala b/test/files/jvm/t7253/ScalaClient_1.scala new file mode 100644 index 0000000000..d244b326a8 --- /dev/null +++ b/test/files/jvm/t7253/ScalaClient_1.scala @@ -0,0 +1,9 @@ +class ScalaClient_1 { + def foo() = { + (null: A).f() + (null: B1).f() + (null: B2).f() + (null: B3).f() + (null: B4).f() + } +} diff --git a/test/files/jvm/t7253/test.scala b/test/files/jvm/t7253/test.scala new file mode 100644 index 0000000000..7fe08e8813 --- /dev/null +++ b/test/files/jvm/t7253/test.scala @@ -0,0 +1,28 @@ +import scala.tools.partest.BytecodeTest + +import scala.tools.nsc.util.JavaClassPath +import java.io.InputStream +import scala.tools.asm +import asm.ClassReader +import asm.tree.{ClassNode, InsnList} +import scala.collection.JavaConverters._ + +object Test extends BytecodeTest { + import instructions._ + + def show: Unit = { + val instrBaseSeqs = Seq("ScalaClient_1", "JavaClient_1") map (name => instructions.fromMethod(getMethod(loadClassNode(name), "foo"))) + val instrSeqs = instrBaseSeqs map (_ filter isInvoke) + cmpInstructions(instrSeqs(0), instrSeqs(1)) + } + + def cmpInstructions(isa: List[Instruction], isb: List[Instruction]) = { + if (isa == isb) println("bytecode identical") + else diffInstructions(isa, isb) + } + + def isInvoke(node: Instruction): Boolean = { + val opcode = node.opcode + (opcode == "INVOKEVIRTUAL") || (opcode == "INVOKEINTERFACE") + } +} diff --git a/test/files/neg/t7251.check b/test/files/neg/t7251.check new file mode 100644 index 0000000000..8df8984d63 --- /dev/null +++ b/test/files/neg/t7251.check @@ -0,0 +1,4 @@ +B_2.scala:5: error: object s.Outer$Triple$ is not a value + println( s.Outer$Triple$ ) + ^ +one error found diff --git a/test/files/neg/t7251/A_1.scala b/test/files/neg/t7251/A_1.scala new file mode 100644 index 0000000000..d05373ed28 --- /dev/null +++ b/test/files/neg/t7251/A_1.scala @@ -0,0 +1,10 @@ +package s + +object Outer { + type Triple[+A, +B, +C] = Tuple3[A, B, C] + object Triple { + def apply[A, B, C](x: A, y: B, z: C) = Tuple3(x, y, z) + def unapply[A, B, C](x: Tuple3[A, B, C]): Option[Tuple3[A, B, C]] = Some(x) + } +} + diff --git a/test/files/neg/t7251/B_2.scala b/test/files/neg/t7251/B_2.scala new file mode 100644 index 0000000000..eb59b30902 --- /dev/null +++ b/test/files/neg/t7251/B_2.scala @@ -0,0 +1,7 @@ +package s + +object Test { + def main(args: Array[String]): Unit = { + println( s.Outer$Triple$ ) + } +} diff --git a/test/files/neg/t7259.check b/test/files/neg/t7259.check new file mode 100644 index 0000000000..0ad627fc3b --- /dev/null +++ b/test/files/neg/t7259.check @@ -0,0 +1,7 @@ +t7259.scala:1: error: not found: type xxxxx +@xxxxx // error: not found: type xxxx + ^ +t7259.scala:8: error: type xxxxx is not a member of package annotation +@annotation.xxxxx // error: not found: type scala + ^ +two errors found diff --git a/test/files/neg/t7259.scala b/test/files/neg/t7259.scala new file mode 100644 index 0000000000..0fdfe18822 --- /dev/null +++ b/test/files/neg/t7259.scala @@ -0,0 +1,9 @@ +@xxxxx // error: not found: type xxxx +class Ok + +// +// This had the wrong error message in 2.9 and 2.10. +// + +@annotation.xxxxx // error: not found: type scala +class WrongErrorMessage diff --git a/test/files/pos/t3120/J1.java b/test/files/pos/t3120/J1.java new file mode 100644 index 0000000000..12b23c1c98 --- /dev/null +++ b/test/files/pos/t3120/J1.java @@ -0,0 +1,4 @@ +class J1 { + public class Inner1 { } + public static class Inner2 { } +} diff --git a/test/files/pos/t3120/J2.java b/test/files/pos/t3120/J2.java new file mode 100644 index 0000000000..db6e859020 --- /dev/null +++ b/test/files/pos/t3120/J2.java @@ -0,0 +1,4 @@ +public class J2 { + public void f1(J1.Inner1 p) { } + public void f2(J1.Inner2 p) { } +} diff --git a/test/files/pos/t3120/Q.java b/test/files/pos/t3120/Q.java new file mode 100644 index 0000000000..fe2269308a --- /dev/null +++ b/test/files/pos/t3120/Q.java @@ -0,0 +1,3 @@ +public class Q { + public static void passInner(J1.Inner1 myInner) {} +} diff --git a/test/files/pos/t3120/Test.scala b/test/files/pos/t3120/Test.scala new file mode 100644 index 0000000000..c02146fba1 --- /dev/null +++ b/test/files/pos/t3120/Test.scala @@ -0,0 +1,3 @@ +object Test { + Q.passInner(null) +} diff --git a/test/files/run/t3994.scala b/test/files/run/t3994.scala new file mode 100644 index 0000000000..0ee1d9d966 --- /dev/null +++ b/test/files/run/t3994.scala @@ -0,0 +1,20 @@ +trait T { + trait Default { def foo = this } + object Default extends Default +} + +class Crash { // if you change this to a `trait` it keeps failing, though if it is an `object` it compiles just fine! + class Element + + /* declare this as a class, and the crash goes away */ + trait ElementOrdering extends Ordering[Element] { + def compare(a: Element, b: Element): Int = 0 + } + + implicit object ElementOrdering extends ElementOrdering +} + +object Test extends App { + (new T {}).Default + (new Crash).ElementOrdering +} diff --git a/test/files/run/t5699.check b/test/files/run/t5699.check new file mode 100755 index 0000000000..df19644ae6 --- /dev/null +++ b/test/files/run/t5699.check @@ -0,0 +1,11 @@ +[[syntax trees at end of parser]] // annodef.java +package <empty> { + object MyAnnotation extends { + def <init>() = _ + }; + class MyAnnotation extends scala.annotation.Annotation with _root_.java.lang.annotation.Annotation with scala.annotation.ClassfileAnnotation { + def <init>() = _; + def value(): String + } +} + diff --git a/test/files/run/t5699.scala b/test/files/run/t5699.scala new file mode 100755 index 0000000000..5cef67e3b1 --- /dev/null +++ b/test/files/run/t5699.scala @@ -0,0 +1,24 @@ +import scala.tools.partest.DirectTest +import scala.tools.nsc.util.BatchSourceFile + +object Test extends DirectTest { + // Java code + override def code = """ + |public @interface MyAnnotation { String value(); } + """.stripMargin + + override def extraSettings: String = "-usejavacp -Ystop-after:typer -Xprint:parser" + + override def show(): Unit = { + // redirect err to out, for logging + val prevErr = System.err + System.setErr(System.out) + compile() + System.setErr(prevErr) + } + + override def newSources(sourceCodes: String*) = { + assert(sourceCodes.size == 1) + List(new BatchSourceFile("annodef.java", sourceCodes(0))) + } +} diff --git a/test/files/run/t6223.check b/test/files/run/t6223.check index 90ec019407..f83799bab1 100644 --- a/test/files/run/t6223.check +++ b/test/files/run/t6223.check @@ -1,4 +1,4 @@ bar -bar$mcI$sp bar$mIc$sp bar$mIcI$sp +bar$mcI$sp
\ No newline at end of file diff --git a/test/files/run/t6223.scala b/test/files/run/t6223.scala index 4ab7c832e6..0996ea1c45 100644 --- a/test/files/run/t6223.scala +++ b/test/files/run/t6223.scala @@ -5,7 +5,7 @@ class Foo[@specialized(Int) A](a:A) { object Test { def main(args:Array[String]) { val f = new Foo(333) - val ms = f.getClass().getDeclaredMethods() - ms.foreach(m => println(m.getName)) + val ms = f.getClass().getDeclaredMethods().map(_.getName).sorted + ms.foreach(println) } } diff --git a/test/files/run/t7242.scala b/test/files/run/t7242.scala new file mode 100644 index 0000000000..c995336144 --- /dev/null +++ b/test/files/run/t7242.scala @@ -0,0 +1,71 @@ +class CrashTest { + def foo = () + trait CrashTestTable { + def cols = foo + } + // This was leading to a class between the mixed in + // outer accessor and the outer accessor of this object. + object CrashTestTable extends CrashTestTable { + foo + cols + } +} + +class CrashTest1 { + def foo = () + class CrashTestTable { + def cols = foo + } + object CrashTestTable extends CrashTestTable { + foo + cols + } +} + +class CrashTest2 { + def foo = () + trait CrashTestTable { + def cols = foo + } + object Obj extends CrashTestTable { + foo + cols + } +} + +class CrashTest3 { + def foo = () + + def meth() { + trait CrashTestTable { + def cols = foo + } + object Obj extends CrashTestTable { + foo + cols + } + Obj + } +} + +object Test extends App { + { + val c = new CrashTest + c.CrashTestTable + } + + { + val c = new CrashTest1 + c.CrashTestTable + } + + { + val c = new CrashTest2 + c.Obj + } + + { + val c = new CrashTest3 + c.meth() + } +} |