diff options
Diffstat (limited to 'src')
85 files changed, 772 insertions, 2053 deletions
diff --git a/src/build/genprod.scala b/src/build/genprod.scala index aec840c262..cd01363cb6 100644 --- a/src/build/genprod.scala +++ b/src/build/genprod.scala @@ -319,6 +319,7 @@ class Tuple(val i: Int) extends Group("Tuple") with Arity { * @constructor Create a new tuple with {i} elements.{idiomatic} {params} */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class {className}{covariantArgs}({fields}) extends {Product.className(i)}{invariantArgs} {{ diff --git a/src/build/pack.xml b/src/build/pack.xml index 79611b55a2..381d3f1931 100644 --- a/src/build/pack.xml +++ b/src/build/pack.xml @@ -179,41 +179,41 @@ MAIN DISTRIBUTION PACKAGING <target name="pack-maven.srcs" depends="pack-maven.libs"> <!-- Add missing src jars. --> - <jar destfile="${dists.dir}/maven/${version.number}/jline/jline-src.jar" + <jar whenmanifestonly="fail" destfile="${dists.dir}/maven/${version.number}/jline/jline-src.jar" basedir="${src.dir}/jline/src/main/java"> <include name="**/*"/> </jar> <!-- Continuations plugin --> - <jar destfile="${dists.dir}/maven/${version.number}/plugins/continuations/continuations-src.jar" + <jar whenmanifestonly="fail" destfile="${dists.dir}/maven/${version.number}/plugins/continuations/continuations-src.jar" basedir="${src.dir}/continuations/plugin"> <include name="**/*"/> </jar> </target> <target name="pack-maven.docs" depends="pack-maven.libs, pack-maven.plugins"> - <jar destfile="${dists.dir}/maven/${version.number}/jline/jline-docs.jar" + <jar whenmanifestonly="fail" destfile="${dists.dir}/maven/${version.number}/jline/jline-docs.jar" basedir="${build-docs.dir}/jline"> <include name="**/*"/> </jar> - <jar destfile="${dists.dir}/maven/${version.number}/scala-library/scala-library-docs.jar" + <jar whenmanifestonly="fail" destfile="${dists.dir}/maven/${version.number}/scala-library/scala-library-docs.jar" basedir="${build-docs.dir}/library"> <include name="**/*"/> </jar> - <jar destfile="${dists.dir}/maven/${version.number}/scala-compiler/scala-compiler-docs.jar" + <jar whenmanifestonly="fail" destfile="${dists.dir}/maven/${version.number}/scala-compiler/scala-compiler-docs.jar" basedir="${build-docs.dir}/compiler"> <include name="**/*"/> </jar> - <jar destfile="${dists.dir}/maven/${version.number}/scalap/scalap-docs.jar" + <jar whenmanifestonly="fail" destfile="${dists.dir}/maven/${version.number}/scalap/scalap-docs.jar" basedir="${build-docs.dir}/scalap"> <include name="**/*"/> </jar> - <jar destfile="${dists.dir}/maven/${version.number}/scala-partest/scala-partest-docs.jar" - basedir="${build-docs.dir}/scala-partest"> + <jar whenmanifestonly="fail" destfile="${dists.dir}/maven/${version.number}/scala-partest/scala-partest-docs.jar" + basedir="${build-docs.dir}/partest"> <include name="**/*"/> </jar> - <jar destfile="${dists.dir}/maven/${version.number}/plugins/continuations/continuations-docs.jar" + <jar whenmanifestonly="fail" destfile="${dists.dir}/maven/${version.number}/plugins/continuations/continuations-docs.jar" basedir="${build-docs.dir}/continuations-plugin"> <include name="**/*"/> </jar> diff --git a/src/compiler/scala/reflect/reify/phases/Reshape.scala b/src/compiler/scala/reflect/reify/phases/Reshape.scala index bb5cb53d7d..bc2dbeed3e 100644 --- a/src/compiler/scala/reflect/reify/phases/Reshape.scala +++ b/src/compiler/scala/reflect/reify/phases/Reshape.scala @@ -254,7 +254,7 @@ trait Reshape { case _ => rhs // unit or trait case } val DefDef(mods0, name0, _, _, tpt0, rhs0) = ddef - val name1 = nme.dropLocalSuffix(name0) + val name1 = name0.dropLocal val Modifiers(flags0, privateWithin0, annotations0) = mods0 val flags1 = (flags0 & GetterFlags) & ~(STABLE | ACCESSOR | METHOD) val mods1 = Modifiers(flags1, privateWithin0, annotations0) setPositions mods0.positions @@ -273,7 +273,9 @@ trait Reshape { if (defdef.name.startsWith(prefix)) { val name = defdef.name.toString.substring(prefix.length) def uncapitalize(s: String) = if (s.length == 0) "" else { val chars = s.toCharArray; chars(0) = chars(0).toLower; new String(chars) } - def findValDef(name: String) = (symdefs.values collect { case vdef: ValDef if nme.dropLocalSuffix(vdef.name).toString == name => vdef }).headOption + def findValDef(name: String) = symdefs.values collectFirst { + case vdef: ValDef if vdef.name.dropLocal string_== name => vdef + } val valdef = findValDef(name).orElse(findValDef(uncapitalize(name))).orNull if (valdef != null) accessors(valdef) = accessors.getOrElse(valdef, Nil) :+ defdef } @@ -297,7 +299,7 @@ trait Reshape { mods } val mods2 = toPreTyperModifiers(mods1, vdef.symbol) - val name1 = nme.dropLocalSuffix(name) + val name1 = name.dropLocal val vdef1 = ValDef(mods2, name1.toTermName, tpt, rhs) if (reifyDebug) println("resetting visibility of field: %s => %s".format(vdef, vdef1)) Some(vdef1) // no copyAttrs here, because new ValDef and old symbols are now out of sync diff --git a/src/compiler/scala/reflect/reify/utils/Extractors.scala b/src/compiler/scala/reflect/reify/utils/Extractors.scala index d57188bf6e..7338df1f72 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/cmd/gen/Codegen.scala b/src/compiler/scala/tools/cmd/gen/Codegen.scala index 4ca9b6cac7..b49322ab4a 100644 --- a/src/compiler/scala/tools/cmd/gen/Codegen.scala +++ b/src/compiler/scala/tools/cmd/gen/Codegen.scala @@ -23,7 +23,7 @@ object Codegen { return println (CodegenSpec.helpMsg) val out = outDir getOrElse { return println("--out is required.") } - val all = genall || (!anyvals && !products) + val all = genall || !anyvals echo("Generating sources into " + out) diff --git a/src/compiler/scala/tools/cmd/gen/CodegenSpec.scala b/src/compiler/scala/tools/cmd/gen/CodegenSpec.scala index ee7e605425..4b4a1e482d 100644 --- a/src/compiler/scala/tools/cmd/gen/CodegenSpec.scala +++ b/src/compiler/scala/tools/cmd/gen/CodegenSpec.scala @@ -14,13 +14,9 @@ trait CodegenSpec extends Spec with Meta.StdOpts with Interpolation { help("Usage: codegen [<options>]") - // val inDir = "in" / "directory containing templates" --^ ExistingDir val outDir = "out" / "directory for generated files" --^ ExistingDir - // val install = "install" / "write source files directly to src/library/scala" val anyvals = "anyvals" / "generate sources for AnyVal types" --? - val products = "products" / "generate sources for ProductN, FunctionN, etc." --? val genall = "all" / "generate sources for everything" --? - val stamp = "stamp" / "add a timestamp to the generated files" --? } object CodegenSpec extends CodegenSpec with Reference { diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala index e471f4256b..0a18adcf4f 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala @@ -172,6 +172,7 @@ trait Members { var returnType: TypeKind = _ var recursive: Boolean = false var bytecodeHasEHs = false // set by ICodeReader only, used by Inliner to prevent inlining (SI-6188) + var bytecodeHasInvokeDynamic = false // set by ICodeReader only, used by Inliner to prevent inlining until we have proper invoke dynamic support /** local variables and method parameters */ var locals: List[Local] = Nil diff --git a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala index d8aac8e9db..ff118be3c4 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala @@ -394,6 +394,25 @@ trait Opcodes { self: ICodes => override def category = mthdsCat } + + /** + * A place holder entry that allows us to parse class files with invoke dynamic + * instructions. Because the compiler doesn't yet really understand the + * behavior of invokeDynamic, this op acts as a poison pill. Any attempt to analyze + * this instruction will cause a failure. The only optimization that + * should ever look at non-Scala generated icode is the inliner, and it + * has been modified to not examine any method with invokeDynamic + * instructions. So if this poison pill ever causes problems then + * there's been a serious misunderstanding + */ + // TODO do the real thing + case class INVOKE_DYNAMIC(poolEntry: Char) extends Instruction { + private def error = sys.error("INVOKE_DYNAMIC is not fully implemented and should not be analyzed") + override def consumed = error + override def produced = error + override def producedTypes = error + override def category = error + } case class BOX(boxType: TypeKind) extends Instruction { assert(boxType.isValueType && (boxType ne UNIT)) // documentation diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 2aa874b567..0c098edf98 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -290,7 +290,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { def inameToSymbol(iname: String): Symbol = { val name = global.newTypeName(iname) val res0 = - if (nme.isModuleName(name)) rootMirror.getModule(nme.stripModuleSuffix(name)) + if (nme.isModuleName(name)) rootMirror.getModule(name.dropModule) else rootMirror.getClassByName(name.replace('/', '.')) // TODO fails for inner classes (but this hasn't been tested). assert(res0 != NoSymbol) val res = jsymbol(res0) @@ -2251,16 +2251,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) @@ -2283,11 +2283,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/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index 38040d921f..557ef925a7 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -382,7 +382,7 @@ abstract class Inliners extends SubComponent { val shouldWarn = hasInline(i.method) def warnNoInline(reason: String): Boolean = { - def msg = "Could not inline required method %s because %s.".format(i.method.originalName.decode, reason) + def msg = "Could not inline required method %s because %s.".format(i.method.unexpandedName.decode, reason) if (settings.debug.value) inlineLog("fail", i.method.fullName, reason) if (shouldWarn) @@ -565,7 +565,7 @@ abstract class Inliners extends SubComponent { while (retry && count < MAX_INLINE_RETRY) for(inlFail <- tfa.warnIfInlineFails) { - warn(inlFail.pos, "At the end of the day, could not inline @inline-marked method " + inlFail.method.originalName.decode) + warn(inlFail.pos, "At the end of the day, could not inline @inline-marked method " + inlFail.method.unexpandedName.decode) } m.normalize() @@ -958,6 +958,7 @@ abstract class Inliners extends SubComponent { if(isInlineForbidden) { rs ::= "is annotated @noinline" } if(inc.isSynchronized) { rs ::= "is synchronized method" } if(inc.m.bytecodeHasEHs) { rs ::= "bytecode contains exception handlers / finally clause" } // SI-6188 + if(inc.m.bytecodeHasInvokeDynamic) { rs ::= "bytecode contains invoke dynamic" } if(rs.isEmpty) null else rs.mkString("", ", and ", "") } diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index cf40fe90fa..f1b1d1a9a7 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -755,13 +755,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 30851f1d46..d75be92f36 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -43,7 +43,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 @@ -136,10 +136,13 @@ abstract class ClassfileParser { (in.nextByte.toInt: @switch) match { case CONSTANT_UTF8 | CONSTANT_UNICODE => in.skip(in.nextChar) - case CONSTANT_CLASS | CONSTANT_STRING => + case CONSTANT_CLASS | CONSTANT_STRING | CONSTANT_METHODTYPE=> in.skip(2) + case CONSTANT_METHODHANDLE => + in.skip(3) case CONSTANT_FIELDREF | CONSTANT_METHODREF | CONSTANT_INTFMETHODREF - | CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT => + | CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT + | CONSTANT_INVOKEDYNAMIC => in.skip(4) case CONSTANT_LONG | CONSTANT_DOUBLE => in.skip(8) @@ -185,7 +188,7 @@ abstract class ClassfileParser { if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) val name = getExternalName(in.getChar(start + 1)) if (nme.isModuleName(name)) - c = rootMirror.getModuleByName(nme.stripModuleSuffix(name)) + c = rootMirror.getModuleByName(name.dropModule) else c = classNameToSymbol(name) @@ -238,7 +241,7 @@ abstract class ClassfileParser { if (f == NoSymbol) f = rootMirror.getModuleByName(name dropRight 1) } else { - val origName = nme.originalName(name) + val origName = nme.unexpandedName(name) val owner = if (static) ownerTpe.typeSymbol.linkedClassOfClass else ownerTpe.typeSymbol // println("\t" + owner.info.member(name).tpe.widen + " =:= " + tpe) f = owner.info.findMember(origName, 0, 0, stableOnly = false).suchThat(_.tpe.widen =:= tpe) @@ -435,63 +438,59 @@ 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.toTermName) 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.toTermName) 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.getClassByName(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 } + } - 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) - } + /** 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) + ) + + /** 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 @@ -647,7 +646,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. @@ -747,7 +746,7 @@ abstract class ClassfileParser { // raw type - existentially quantify all type parameters else logResult(s"raw type from $classSym")(definitions.unsafeClassExistentialType(classSym)) case tp => - assert(sig.charAt(index) != '<', tp) + assert(sig.charAt(index) != '<', s"sig=$sig, index=$index, tp=$tp") tp } @@ -1108,7 +1107,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 { @@ -1146,14 +1145,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) @@ -1167,58 +1161,58 @@ abstract class ClassfileParser { def externalName = pool getClassName external def outerName = pool getClassName outer def originalName = pool getName name - - override def toString = - originalName + " in " + outerName + "(" + externalName +")" + 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 } - object innerClasses extends mutable.HashMap[Name, InnerClassEntry] { - /** 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) - enteringTyper(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) - } + /** 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 + } + 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 enteringTyper(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/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index 86f034223d..80a810703c 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -164,7 +164,7 @@ abstract class ICodeReader extends ClassfileParser { rootMirror.getClassByName(name) } else if (nme.isModuleName(name)) { - val strippedName = nme.stripModuleSuffix(name) + val strippedName = name.dropModule forceMangledName(newTermName(strippedName.decode), module = true) orElse rootMirror.getModuleByName(strippedName) } else { @@ -501,6 +501,13 @@ abstract class ICodeReader extends ClassfileParser { code.emit(UNBOX(toTypeKind(m.info.resultType))) else code.emit(CALL_METHOD(m, Static(onInstance = false))) + 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.") + containsInvokeDynamic = true + val poolEntry = in.nextChar + in.skip(2) + code.emit(INVOKE_DYNAMIC(poolEntry)) case JVM.new_ => code.emit(NEW(REFERENCE(pool.getClassSymbol(in.nextChar)))) @@ -639,6 +646,7 @@ abstract class ICodeReader extends ClassfileParser { var containsDUPX = false var containsNEW = false var containsEHs = false + var containsInvokeDynamic = false def emit(i: Instruction) { instrs += ((pc, i)) @@ -657,6 +665,7 @@ abstract class ICodeReader extends ClassfileParser { val code = new Code(method) method.setCode(code) method.bytecodeHasEHs = containsEHs + method.bytecodeHasInvokeDynamic = containsInvokeDynamic var bb = code.startBlock def makeBasicBlocks: mutable.Map[Int, BasicBlock] = diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index 768c9b6989..06367009b6 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -94,8 +94,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { val paramAccessors = clazz.constrParamAccessors // The constructor parameter corresponding to an accessor - def parameter(acc: Symbol): Symbol = - parameterNamed(nme.getterName(acc.originalName.toTermName)) + def parameter(acc: Symbol): Symbol = parameterNamed(acc.unexpandedName.getterName) // The constructor parameter with given name. This means the parameter // has given name, or starts with given name, and continues with a `$` afterwards. @@ -191,8 +190,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { stat match { case ValDef(mods, name, _, _) if (mods hasFlag PRESUPER) => // stat is the constructor-local definition of the field value - val fields = presupers filter ( - vdef => nme.localToGetter(vdef.name) == name) + val fields = presupers filter (_.getterName == name) assert(fields.length == 1) val to = fields.head.symbol if (!to.tpe.isInstanceOf[ConstantType]) @@ -314,10 +312,8 @@ abstract class Constructors extends Transform with ast.TreeDSL { def specializedAssignFor(sym: Symbol): Option[Tree] = specializedStats find { - case Assign(sel @ Select(This(_), _), rhs) => - ( (sel.symbol hasFlag SPECIALIZED) - && (nme.unspecializedName(nme.localToGetter(sel.symbol.name.toTermName)) == nme.localToGetter(sym.name.toTermName)) - ) + case Assign(sel @ Select(This(_), _), _) => + sel.symbol.isSpecialized && (nme.unspecializedName(sel.symbol.getterName) == sym.getterName) case _ => false } @@ -432,8 +428,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { } def addGetter(sym: Symbol): Symbol = { - val getr = addAccessor( - sym, nme.getterName(sym.name.toTermName), getterFlags(sym.flags)) + val getr = addAccessor(sym, sym.getterName, getterFlags(sym.flags)) getr setInfo MethodType(List(), sym.tpe) defBuf += localTyper.typedPos(sym.pos)(DefDef(getr, Select(This(clazz), sym))) getr @@ -441,8 +436,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { def addSetter(sym: Symbol): Symbol = { sym setFlag MUTABLE - val setr = addAccessor( - sym, nme.getterToSetter(nme.getterName(sym.name.toTermName)), setterFlags(sym.flags)) + val setr = addAccessor(sym, sym.setterName, setterFlags(sym.flags)) setr setInfo MethodType(setr.newSyntheticValueParams(List(sym.tpe)), UnitClass.tpe) defBuf += localTyper.typed { //util.trace("adding setter def for "+setr) { diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 857c01c49a..e6cf5e6346 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -730,7 +730,7 @@ abstract class Erasure extends AddInterfaces else if (tree.symbol == Any_isInstanceOf) adaptMember(atPos(tree.pos)(Select(qual, Object_isInstanceOf))) else if (tree.symbol.owner == AnyClass) - adaptMember(atPos(tree.pos)(Select(qual, getMember(ObjectClass, name)))) + adaptMember(atPos(tree.pos)(Select(qual, getMember(ObjectClass, tree.symbol.name)))) else { var qual1 = typedQualifier(qual) if ((isPrimitiveValueType(qual1.tpe) && !isPrimitiveValueMember(tree.symbol)) || diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 124dd6c995..367825c251 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -41,26 +41,24 @@ abstract class ExplicitOuter extends InfoTransform private def isInner(clazz: Symbol) = !clazz.isPackageClass && !clazz.outerClass.isStaticOwner - private def haveSameOuter(parent: Type, clazz: Symbol) = parent match { - case TypeRef(pre, sym, _) => - val owner = clazz.owner + private def haveSameOuter(parent: Type, clazz: Symbol) = { + val owner = clazz.owner + val parentSym = parent.typeSymbol - //println(s"have same outer $parent $clazz $sym ${sym.owner} $owner $pre") - - sym.isClass && owner.isClass && - (owner isSubClass sym.owner) && - owner.thisType =:= pre - - case _ => false + parentSym.isClass && owner.isClass && + (owner isSubClass parentSym.owner) && + owner.thisType =:= parent.prefix } /** Does given clazz define an outer field? */ def hasOuterField(clazz: Symbol) = { - val parents = clazz.info.parents + val parent = clazz.info.firstParent - isInner(clazz) && !clazz.isTrait && { - parents.isEmpty || !haveSameOuter(parents.head, clazz) - } + // space optimization: inherit the $outer pointer from the parent class if + // we know that it will point to the correct instance. + def canReuseParentOuterField = !parent.typeSymbol.isJavaDefined && haveSameOuter(parent, clazz) + + isInner(clazz) && !clazz.isTrait && !canReuseParentOuterField } private def outerField(clazz: Symbol): Symbol = { @@ -100,6 +98,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> @@ -162,10 +183,14 @@ abstract class ExplicitOuter extends InfoTransform for (mc <- clazz.mixinClasses) { val mixinOuterAcc: Symbol = exitingExplicitOuter(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 + } } } } @@ -370,6 +395,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 = ( @@ -404,7 +430,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/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala index e6c9afb042..69a93b482c 100644 --- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala +++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala @@ -183,7 +183,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD if (bmps.isEmpty) rhs else rhs match { case Block(assign, l @ LabelDef(name, params, _)) - if name.toString == ("_" + methSym.name) && isMatch(params) => + if (name string_== "_" + methSym.name) && isMatch(params) => Block(assign, deriveLabelDef(l)(rhs => typed(prependStats(bmps, rhs)))) case _ => prependStats(bmps, rhs) diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index e0b30ab9f9..8971e27bda 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -185,11 +185,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { newSym updateInfo (mixinMember.info cloneInfo newSym) } - def needsExpandedSetterName(field: Symbol) = !field.isLazy && ( - if (field.isMethod) field.hasStableFlag - else !field.isMutable - ) - /** Add getters and setters for all non-module fields of an implementation * class to its interface unless they are already present. This is done * only once per class. The mixedin flag is used to remember whether late @@ -207,19 +202,19 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // println("creating new getter for "+ field +" : "+ field.info +" at "+ field.locationString+(field hasFlag MUTABLE)) val newFlags = field.flags & ~PrivateLocal | ACCESSOR | lateDEFERRED | ( if (field.isMutable) 0 else STABLE ) // TODO preserve pre-erasure info? - clazz.newMethod(nme.getterName(field.name.toTermName), field.pos, newFlags) setInfo MethodType(Nil, field.info) + clazz.newMethod(field.getterName, field.pos, newFlags) setInfo MethodType(Nil, field.info) } /* Create a new setter. Setters are never private or local. They are * always accessors and deferred. */ def newSetter(field: Symbol): Symbol = { //println("creating new setter for "+field+field.locationString+(field hasFlag MUTABLE)) - val setterName = nme.getterToSetter(nme.getterName(field.name.toTermName)) + val setterName = field.setterName val newFlags = field.flags & ~PrivateLocal | ACCESSOR | lateDEFERRED val setter = clazz.newMethod(setterName, field.pos, newFlags) // TODO preserve pre-erasure info? setter setInfo MethodType(setter.newSyntheticValueParams(List(field.info)), UnitClass.tpe) - if (needsExpandedSetterName(field)) + if (field.needsExpandedSetterName) setter.name = nme.expandedSetterName(setter.name, clazz) setter @@ -237,7 +232,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val getter = member.getter(clazz) if (getter == NoSymbol) addMember(clazz, newGetter(member)) if (!member.tpe.isInstanceOf[ConstantType] && !member.isLazy) { - val setter = member.setter(clazz, needsExpandedSetterName(member)) + val setter = member.setter(clazz) if (setter == NoSymbol) addMember(clazz, newSetter(member)) } } @@ -315,7 +310,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // carries over the current entry in the type history) val sym = enteringErasure { // so we have a type history entry before erasure - clazz.newValue(nme.getterToLocal(mixinMember.name.toTermName), mixinMember.pos).setInfo(mixinMember.tpe.resultType) + clazz.newValue(mixinMember.localName, mixinMember.pos).setInfo(mixinMember.tpe.resultType) } sym updateInfo mixinMember.tpe.resultType // info at current phase @@ -1236,10 +1231,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { case Assign(Apply(lhs @ Select(qual, _), List()), rhs) => // assign to fields in some implementation class via an abstract // setter in the interface. - def setter = lhs.symbol.setter( - toInterface(lhs.symbol.owner.tpe).typeSymbol, - needsExpandedSetterName(lhs.symbol) - ) setPos lhs.pos + def setter = lhs.symbol.setter(toInterface(lhs.symbol.owner.tpe).typeSymbol) setPos lhs.pos typedPos(tree.pos)((qual DOT setter)(rhs)) diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 15fe9f0a24..91a03009bc 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -322,20 +322,20 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { /** Specialize name for the two list of types. The first one denotes * specialization on method type parameters, the second on outer environment. */ - private def specializedName(name: Name, types1: List[Type], types2: List[Type]): TermName = { - if (nme.INITIALIZER == name || (types1.isEmpty && types2.isEmpty)) + private def specializedName(name: Name, types1: List[Type], types2: List[Type]): TermName = ( + if (name == nme.CONSTRUCTOR || (types1.isEmpty && types2.isEmpty)) name.toTermName else if (nme.isSetterName(name)) - nme.getterToSetter(specializedName(nme.setterToGetter(name.toTermName), types1, types2)) + specializedName(name.getterName, types1, types2).setterName else if (nme.isLocalName(name)) - nme.getterToLocal(specializedName(nme.localToGetter(name.toTermName), types1, types2)) + specializedName(name.getterName, types1, types2).localName else { val (base, cs, ms) = nme.splitSpecializedName(name) newTermName(base.toString + "$" + "m" + ms + types1.map(t => definitions.abbrvTag(t.typeSymbol)).mkString("", "", "") + "c" + cs + types2.map(t => definitions.abbrvTag(t.typeSymbol)).mkString("", "", "$sp")) } - } + ) lazy val specializableTypes = ScalaValueClasses map (_.tpe) sorted @@ -714,7 +714,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { // debuglog("m: " + m + " isLocal: " + nme.isLocalName(m.name) + " specVal: " + specVal.name + " isLocal: " + nme.isLocalName(specVal.name)) if (nme.isLocalName(m.name)) { - val specGetter = mkAccessor(specVal, nme.localToGetter(specVal.name.toTermName)) setInfo MethodType(Nil, specVal.info) + val specGetter = mkAccessor(specVal, specVal.getterName) setInfo MethodType(Nil, specVal.info) val origGetter = overrideIn(sClass, m.getter(clazz)) info(origGetter) = Forward(specGetter) enterMember(specGetter) @@ -729,7 +729,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } if (specVal.isVariable && m.setter(clazz) != NoSymbol) { - val specSetter = mkAccessor(specVal, nme.getterToSetter(specGetter.name)) + val specSetter = mkAccessor(specVal, specGetter.setterName) .resetFlag(STABLE) specSetter.setInfo(MethodType(specSetter.newSyntheticValueParams(List(specVal.info)), UnitClass.tpe)) @@ -1805,7 +1805,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { */ def initializesSpecializedField(f: Symbol) = ( (f.name endsWith nme.SPECIALIZED_SUFFIX) - && clazz.info.member(nme.originalName(f.name)).isPublic + && clazz.info.member(f.unexpandedName).isPublic && clazz.info.decl(f.name).suchThat(_.isGetter) != NoSymbol ) diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 8fd1df7cea..11bd0665b1 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -347,10 +347,14 @@ abstract class UnCurry extends InfoTransform } else { log(s"Argument '$arg' at line ${arg.pos.safeLine} is $formal from ${fun.fullName}") + def canUseDirectly(recv: Tree) = ( + recv.tpe.typeSymbol.isSubClass(FunctionClass(0)) + && treeInfo.isExprSafeToInline(recv) + ) arg match { // don't add a thunk for by-name argument if argument already is an application of // a Function0. We can then remove the application and use the existing Function0. - case Apply(Select(recv, nme.apply), Nil) if recv.tpe.typeSymbol isSubClass FunctionClass(0) => + case Apply(Select(recv, nme.apply), Nil) if canUseDirectly(recv) => recv case _ => newFunction0(arg) diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index 0371df3b10..92b7700c04 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala @@ -573,6 +573,7 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { assert(tp.isInstanceOf[SingletonType]) val toString = tp match { case ConstantType(c) => c.escapedStringValue + case _ if tp.typeSymbol.isModuleClass => tp.typeSymbol.name.toString case _ => tp.toString } Const.unique(tp, new ValueConst(tp, tp.widen, toString)) diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala index 125e9a3b65..31b04d0bd6 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala @@ -15,9 +15,9 @@ import scala.reflect.internal.util.Position /** Optimize and analyze matches based on their TreeMaker-representation. * * The patmat translation doesn't rely on this, so it could be disabled in principle. - * - * TODO: split out match analysis + * - well, not quite: the backend crashes if we emit duplicates in switches (e.g. SI-7290) */ +// TODO: split out match analysis trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { import global._ import global.definitions._ @@ -435,7 +435,7 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { case SwitchableTreeMaker(pattern) :: GuardAndBodyTreeMakers(guard, body) => Some(CaseDef(pattern, guard, body)) // alternatives - case AlternativesTreeMaker(_, altss, _) :: GuardAndBodyTreeMakers(guard, body) if alternativesSupported => + case AlternativesTreeMaker(_, altss, pos) :: GuardAndBodyTreeMakers(guard, body) if alternativesSupported => val switchableAlts = altss map { case SwitchableTreeMaker(pattern) :: Nil => Some(pattern) @@ -445,7 +445,17 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { // succeed if they were all switchable sequence(switchableAlts) map { switchableAlts => - CaseDef(Alternative(switchableAlts), guard, body) + def extractConst(t: Tree) = t match { + case Literal(const) => const + case _ => t + } + // SI-7290 Discard duplicate alternatives that would crash the backend + val distinctAlts = distinctBy(switchableAlts)(extractConst) + if (distinctAlts.size < switchableAlts.size) { + val duplicated = switchableAlts.groupBy(extractConst).flatMap(_._2.drop(1).take(1)) // report the first duplicated + global.currentUnit.warning(pos, s"Pattern contains duplicate alternatives: ${duplicated.mkString(", ")}") + } + CaseDef(Alternative(distinctAlts), guard, body) } case _ => // debug.patmat("can't emit switch for "+ makers) diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala index 88bfa6099d..026f5f7bc8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala @@ -195,11 +195,18 @@ trait Checkable { * so I will consult with moors about the optimal time to be doing this. */ def areIrreconcilableAsParents(sym1: Symbol, sym2: Symbol): Boolean = areUnrelatedClasses(sym1, sym2) && ( - sym1.initialize.isEffectivelyFinal // initialization important - || sym2.initialize.isEffectivelyFinal + isEffectivelyFinal(sym1) // initialization important + || isEffectivelyFinal(sym2) || !sym1.isTrait && !sym2.isTrait || sym1.isSealed && sym2.isSealed && allChildrenAreIrreconcilable(sym1, sym2) && !currentRun.compiles(sym1) && !currentRun.compiles(sym2) ) + private def isEffectivelyFinal(sym: Symbol): Boolean = ( + // initialization important + sym.initialize.isEffectivelyFinal || ( + settings.future.value && isTupleSymbol(sym) // SI-7294 step into the future and treat TupleN as final. + ) + ) + def isNeverSubClass(sym1: Symbol, sym2: Symbol) = areIrreconcilableAsParents(sym1, sym2) private def isNeverSubArgs(tps1: List[Type], tps2: List[Type], tparams: List[Symbol]): Boolean = /*logResult(s"isNeverSubArgs($tps1, $tps2, $tparams)")*/ { diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 8723046728..85c44a7ec4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -1058,9 +1058,6 @@ trait ContextErrors { issueSymbolTypeError(currentSym, prevSym.name + " is already defined as " + s2 + s3 + where) } - def MaxParametersCaseClassError(tree: Tree) = - issueNormalTypeError(tree, "Implementation restriction: case classes cannot have more than " + definitions.MaxFunctionArity + " parameters.") - def MissingParameterOrValTypeError(vparam: Tree) = issueNormalTypeError(vparam, "missing parameter type") diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 9d5a9c819c..272e7af48b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -912,7 +912,25 @@ trait Contexts { self: Analyzer => def lookupImport(imp: ImportInfo, requireExplicit: Boolean) = importedAccessibleSymbol(imp, name, requireExplicit) filter qualifies - while (!impSym.exists && imports.nonEmpty && imp1.depth > symbolDepth) { + // Java: A single-type-import declaration d in a compilation unit c of package p + // that imports a type named n shadows, throughout c, the declarations of: + // + // 1) any top level type named n declared in another compilation unit of p + // + // A type-import-on-demand declaration never causes any other declaration to be shadowed. + // + // Scala: Bindings of different kinds have a precedence defined on them: + // + // 1) Definitions and declarations that are local, inherited, or made available by a + // package clause in the same compilation unit where the definition occurs have + // highest precedence. + // 2) Explicit imports have next highest precedence. + def depthOk(imp: ImportInfo) = ( + imp.depth > symbolDepth + || (unit.isJava && imp.isExplicitImport(name) && imp.depth == symbolDepth) + ) + + while (!impSym.exists && imports.nonEmpty && depthOk(imports.head)) { impSym = lookupImport(imp1, requireExplicit = false) if (!impSym.exists) imports = imports.tail diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala index 282dd8a99d..4075aa26f7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -98,6 +98,7 @@ trait EtaExpansion { self: Analyzer => case TypeApply(fn, args) => treeCopy.TypeApply(tree, liftoutPrefix(fn), args).clearType() case Select(qual, name) => + val name = tree.symbol.name // account for renamed imports, SI-7233 treeCopy.Select(tree, liftout(qual, byName = false), name).clearType() setSymbol NoSymbol case Ident(name) => tree diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 5999a64b36..50383a1e78 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -471,7 +471,7 @@ trait MethodSynthesis { } } case class Setter(tree: ValDef) extends DerivedSetter { - def name = nme.getterToSetter(tree.name) + def name = tree.setterName def category = SetterTargetClass def flagsMask = SetterFlags def flagsExtra = ACCESSOR @@ -479,7 +479,7 @@ trait MethodSynthesis { override def derivedSym = basisSym.setter(enclClass) } case class Field(tree: ValDef) extends DerivedFromValDef { - def name = nme.getterToLocal(tree.name) + def name = tree.localName def category = FieldTargetClass def flagsMask = FieldFlags def flagsExtra = PrivateLocal diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index e966cc9060..7d12744cc7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -184,7 +184,7 @@ trait Namers extends MethodSynthesis { (newS.owner.isTypeParameter || newS.owner.isAbstractType) // FIXME: name comparisons not successful, are these underscores // sometimes nme.WILDCARD and sometimes tpnme.WILDCARD? - && (newS.name.toString == nme.WILDCARD.toString) + && (newS.name string_== nme.WILDCARD) ) ) @@ -323,7 +323,7 @@ trait Namers extends MethodSynthesis { } } private def createFieldSymbol(tree: ValDef): TermSymbol = - owner.newValue(nme.getterToLocal(tree.name), tree.pos, tree.mods.flags & FieldFlags | PrivateLocal) + owner.newValue(tree.localName, tree.pos, tree.mods.flags & FieldFlags | PrivateLocal) private def createImportSymbol(tree: Tree) = NoSymbol.newImport(tree.pos) setInfo completerOf(tree) @@ -523,14 +523,20 @@ trait Namers extends MethodSynthesis { if (from != nme.WILDCARD && base != ErrorType) { if (isValid(from)) { // for Java code importing Scala objects - if (!nme.isModuleName(from) || isValid(nme.stripModuleSuffix(from))) { + if (!nme.isModuleName(from) || isValid(from.dropModule)) { typer.TyperErrorGen.NotAMemberError(tree, expr, from) } } // Setting the position at the import means that if there is // more than one hidden name, the second will not be warned. // So it is the position of the actual hidden name. - checkNotRedundant(tree.pos withPoint fromPos, from, to) + // + // Note: java imports have precence over definitions in the same package + // so don't warn for them. There is a corresponding special treatment + // in the shadowing rules in typedIdent to (SI-7232). In any case, + // we shouldn't be emitting warnings for .java source files. + if (!context.unit.isJava) + checkNotRedundant(tree.pos withPoint fromPos, from, to) } } @@ -654,9 +660,6 @@ trait Namers extends MethodSynthesis { tree.symbol setInfo completerOf(tree) if (mods.isCase) { - if (primaryConstructorArity > MaxFunctionArity) - MaxParametersCaseClassError(tree) - val m = ensureCompanionObject(tree, caseModuleDef) m.moduleClass.updateAttachment(new ClassForCaseCompanionAttachment(tree)) } @@ -1373,7 +1376,9 @@ trait Namers extends MethodSynthesis { if (!cdef.symbol.hasAbstractFlag) namer.enterSyntheticSym(caseModuleApplyMeth(cdef)) - namer.enterSyntheticSym(caseModuleUnapplyMeth(cdef)) + val primaryConstructorArity = treeInfo.firstConstructorArgs(cdef.impl.body).size + if (primaryConstructorArity <= MaxTupleArity) + namer.enterSyntheticSym(caseModuleUnapplyMeth(cdef)) } def addCopyMethod(cdef: ClassDef, namer: Namer) { diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index d5ecb687b0..6921f8ce27 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -499,7 +499,7 @@ trait NamesDefaults { self: Analyzer => // disable conforms as a view... val errsBefore = reporter.ERROR.count try typer.silent { tpr => - val res = tpr.typed(arg, subst(paramtpe)) + val res = tpr.typed(arg.duplicate, subst(paramtpe)) // better warning for SI-5044: if `silent` was not actually silent give a hint to the user // [H]: the reason why `silent` is not silent is because the cyclic reference exception is // thrown in a context completely different from `context` here. The exception happens while diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index c967fed0b9..fb692a1954 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -388,7 +388,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT assert(clazz != NoSymbol, sym) debuglog("Decided for host class: " + clazz) - val accName = nme.protName(sym.originalName) + val accName = nme.protName(sym.unexpandedName) val hasArgs = sym.tpe.paramSectionCount > 0 val memberType = refChecks.toScalaRepeatedParam(sym.tpe) // fix for #2413 @@ -406,7 +406,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } val protAcc = clazz.info.decl(accName).suchThat(s => s == NoSymbol || s.tpe =:= accType(s)) orElse { - val newAcc = clazz.newMethod(nme.protName(sym.originalName), tree.pos, newFlags = ARTIFACT) + val newAcc = clazz.newMethod(nme.protName(sym.unexpandedName), tree.pos, newFlags = ARTIFACT) newAcc setInfoAndEnter accType(newAcc) val code = DefDef(newAcc, { @@ -466,7 +466,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT assert(clazz != NoSymbol, field) debuglog("Decided for host class: " + clazz) - val accName = nme.protSetterName(field.originalName) + val accName = nme.protSetterName(field.unexpandedName) val protectedAccessor = clazz.info decl accName orElse { val protAcc = clazz.newMethod(accName, field.pos, newFlags = ARTIFACT) val paramTypes = List(clazz.typeOfThis, field.tpe) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 0e57145343..7436a244bd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -944,6 +944,57 @@ trait Typers extends Adaptations with Tags { // due to wrapClassTagUnapply, but when we support parameterized extractors, it will become // more common place) val extractor = overloadedExtractorOfObject orElse unapplyMember(tree.tpe) + def convertToCaseConstructor(clazz: Symbol): TypeTree = { + // convert synthetic unapply of case class to case class constructor + val prefix = tree.tpe.prefix + val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(prefix, clazz.owner)) + .setOriginal(tree) + + val skolems = new mutable.ListBuffer[TypeSymbol] + object variantToSkolem extends TypeMap(trackVariance = true) { + def apply(tp: Type) = mapOver(tp) match { + // !!! FIXME - skipping this when variance.isInvariant allows unsoundness, see SI-5189 + case TypeRef(NoPrefix, tpSym, Nil) if !variance.isInvariant && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm => + // must initialize or tpSym.tpe might see random type params!! + // without this, we'll get very weird types inferred in test/scaladoc/run/SI-5933.scala + // TODO: why is that?? + tpSym.initialize + val bounds = if (variance.isPositive) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe) + // origin must be the type param so we can deskolemize + val skolem = context.owner.newGADTSkolem(unit.freshTypeName("?"+tpSym.name), tpSym, bounds) + // println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt +" in "+ context.owner +" at "+ context.tree ) + skolems += skolem + skolem.tpe + case tp1 => tp1 + } + } + + // have to open up the existential and put the skolems in scope + // can't simply package up pt in an ExistentialType, because that takes us back to square one (List[_ <: T] == List[T] due to covariance) + val ptSafe = variantToSkolem(pt) // TODO: pt.skolemizeExistential(context.owner, tree) ? + val freeVars = skolems.toList + + // use "tree" for the context, not context.tree: don't make another CaseDef context, + // as instantiateTypeVar's bounds would end up there + val ctorContext = context.makeNewScope(tree, context.owner) + freeVars foreach ctorContext.scope.enter + newTyper(ctorContext).infer.inferConstructorInstance(tree1, clazz.typeParams, ptSafe) + + // simplify types without losing safety, + // so that we get rid of unnecessary type slack, and so that error messages don't unnecessarily refer to skolems + val extrapolate = new ExistentialExtrapolation(freeVars) extrapolate (_: Type) + val extrapolated = tree1.tpe match { + case MethodType(ctorArgs, res) => // ctorArgs are actually in a covariant position, since this is the type of the subpatterns of the pattern represented by this Apply node + ctorArgs foreach (p => p.info = extrapolate(p.info)) // no need to clone, this is OUR method type + copyMethodType(tree1.tpe, ctorArgs, extrapolate(res)) + case tp => tp + } + + // once the containing CaseDef has been type checked (see typedCase), + // tree1's remaining type-slack skolems will be deskolemized (to the method type parameter skolems) + tree1 setType extrapolated + } + if (extractor != NoSymbol) { // if we did some ad-hoc overloading resolution, update the tree's symbol // do not update the symbol if the tree's symbol's type does not define an unapply member @@ -959,59 +1010,16 @@ trait Typers extends Adaptations with Tags { val clazz = unapplyParameterType(unapply) if (unapply.isCase && clazz.isCase) { - // convert synthetic unapply of case class to case class constructor - val prefix = tree.tpe.prefix - val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(prefix, clazz.owner)) - .setOriginal(tree) - - val skolems = new mutable.ListBuffer[TypeSymbol] - object variantToSkolem extends TypeMap(trackVariance = true) { - def apply(tp: Type) = mapOver(tp) match { - // !!! FIXME - skipping this when variance.isInvariant allows unsoundness, see SI-5189 - case TypeRef(NoPrefix, tpSym, Nil) if !variance.isInvariant && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm => - // must initialize or tpSym.tpe might see random type params!! - // without this, we'll get very weird types inferred in test/scaladoc/run/SI-5933.scala - // TODO: why is that?? - tpSym.initialize - val bounds = if (variance.isPositive) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe) - // origin must be the type param so we can deskolemize - val skolem = context.owner.newGADTSkolem(unit.freshTypeName("?"+tpSym.name), tpSym, bounds) - // println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt +" in "+ context.owner +" at "+ context.tree ) - skolems += skolem - skolem.tpe - case tp1 => tp1 - } - } - - // have to open up the existential and put the skolems in scope - // can't simply package up pt in an ExistentialType, because that takes us back to square one (List[_ <: T] == List[T] due to covariance) - val ptSafe = variantToSkolem(pt) // TODO: pt.skolemizeExistential(context.owner, tree) ? - val freeVars = skolems.toList - - // use "tree" for the context, not context.tree: don't make another CaseDef context, - // as instantiateTypeVar's bounds would end up there - val ctorContext = context.makeNewScope(tree, context.owner) - freeVars foreach ctorContext.scope.enter - newTyper(ctorContext).infer.inferConstructorInstance(tree1, clazz.typeParams, ptSafe) - - // simplify types without losing safety, - // so that we get rid of unnecessary type slack, and so that error messages don't unnecessarily refer to skolems - val extrapolate = new ExistentialExtrapolation(freeVars) extrapolate (_: Type) - val extrapolated = tree1.tpe match { - case MethodType(ctorArgs, res) => // ctorArgs are actually in a covariant position, since this is the type of the subpatterns of the pattern represented by this Apply node - ctorArgs foreach (p => p.info = extrapolate(p.info)) // no need to clone, this is OUR method type - copyMethodType(tree1.tpe, ctorArgs, extrapolate(res)) - case tp => tp - } - - // once the containing CaseDef has been type checked (see typedCase), - // tree1's remaining type-slack skolems will be deskolemized (to the method type parameter skolems) - tree1 setType extrapolated + convertToCaseConstructor(clazz) } else { tree } } else { - CaseClassConstructorError(tree) + val clazz = tree.tpe.typeSymbol.linkedClassOfClass + if (clazz.isCase) + convertToCaseConstructor(clazz) + else + CaseClassConstructorError(tree) } } @@ -1324,8 +1332,7 @@ trait Typers extends Adaptations with Tags { def adaptToMemberWithArgs(tree: Tree, qual: Tree, name: Name, mode: Mode, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { def onError(reportError: => Tree): Tree = context.tree match { case Apply(tree1, args) if (tree1 eq tree) && args.nonEmpty => - ( silent (_.typedArgs(args, mode)) - map (_.asInstanceOf[List[Tree]]) + ( silent (_.typedArgs(args.map(_.duplicate), mode)) filter (xs => !(xs exists (_.isErrorTyped))) map (xs => adaptToArguments(qual, name, xs, WildcardType, reportAmbiguous, saveErrors)) orElse ( _ => reportError) @@ -3558,7 +3565,7 @@ trait Typers extends Adaptations with Tags { // If there are dummy type arguments in typeFun part, it suggests we // must type the actual constructor call, not only the select. The value // arguments are how the type arguments will be inferred. - if (targs.isEmpty && typedFun0.exists(t => isDummyAppliedType(t.tpe))) + if (targs.isEmpty && typedFun0.exists(t => t.tpe != null && isDummyAppliedType(t.tpe))) logResult(s"Retyped $typedFun0 to find type args")(typed(argss.foldLeft(fun0)(Apply(_, _)))) else typedFun0 @@ -4211,7 +4218,7 @@ trait Typers extends Adaptations with Tags { if (treeInfo.mayBeVarGetter(varsym)) { lhs1 match { case treeInfo.Applied(Select(qual, name), _, _) => - val sel = Select(qual, nme.getterToSetter(name.toTermName)) setPos lhs.pos + val sel = Select(qual, name.setterName) setPos lhs.pos val app = Apply(sel, List(rhs)) setPos tree.pos return typed(app, mode, pt) @@ -4372,6 +4379,12 @@ trait Typers extends Adaptations with Tags { treeCopy.New(tree, tpt1).setType(tp) } + def functionTypeWildcard(tree: Tree, arity: Int): Type = { + val tp = functionType(List.fill(arity)(WildcardType), WildcardType) + if (tp == NoType) MaxFunctionArityError(tree) + tp + } + def typedEta(expr1: Tree): Tree = expr1.tpe match { case TypeRef(_, ByNameParamClass, _) => val expr2 = Function(List(), expr1) setPos expr1.pos @@ -4383,10 +4396,10 @@ trait Typers extends Adaptations with Tags { typed1(expr2, mode, pt) case PolyType(_, MethodType(formals, _)) => if (isFunctionType(pt)) expr1 - else adapt(expr1, mode, functionType(formals map (t => WildcardType), WildcardType)) + else adapt(expr1, mode, functionTypeWildcard(expr1, formals.length)) case MethodType(formals, _) => if (isFunctionType(pt)) expr1 - else adapt(expr1, mode, functionType(formals map (t => WildcardType), WildcardType)) + else adapt(expr1, mode, functionTypeWildcard(expr1, formals.length)) case ErrorType => expr1 case _ => @@ -4692,11 +4705,13 @@ trait Typers extends Adaptations with Tags { def handleMissing: Tree = { def errorTree = missingSelectErrorTree(tree, qual, name) def asTypeSelection = ( - 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. atPos(tree.pos)(gen.convertToSelectFromType(qual, name)) match { case EmptyTree => None case tree1 => Some(typed1(tree1, mode, pt)) - } + } } else None ) diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 589e5ce6fd..d55dce70c7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -115,11 +115,16 @@ trait Unapplies extends ast.TreeDSL /** The module corresponding to a case class; overrides toString to show the module's name */ def caseModuleDef(cdef: ClassDef): ModuleDef = { - // > MaxFunctionArity is caught in Namers, but for nice error reporting instead of - // an abrupt crash we trim the list here. - def primaries = constrParamss(cdef).head take MaxFunctionArity map (_.tpt) - def inheritFromFun = !cdef.mods.hasAbstractFlag && cdef.tparams.isEmpty && constrParamss(cdef).length == 1 - def createFun = gen.scalaFunctionConstr(primaries, toIdent(cdef), abstractFun = true) + val params = constrParamss(cdef) + def inheritFromFun = !cdef.mods.hasAbstractFlag && cdef.tparams.isEmpty && (params match { + case List(ps) if ps.length <= MaxFunctionArity => true + case _ => false + }) + def createFun = { + def primaries = params.head map (_.tpt) + gen.scalaFunctionConstr(primaries, toIdent(cdef), abstractFun = true) + } + def parents = if (inheritFromFun) List(createFun) else Nil def toString = DefDef( Modifiers(OVERRIDE | FINAL | SYNTHETIC), diff --git a/src/compiler/scala/tools/nsc/util/SimpleTracer.scala b/src/compiler/scala/tools/nsc/util/SimpleTracer.scala index 6997dbd402..4e1cf02a6e 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/compiler/scala/tools/reflect/MacroImplementations.scala b/src/compiler/scala/tools/reflect/MacroImplementations.scala index 002a3fce82..4e8f02084d 100644 --- a/src/compiler/scala/tools/reflect/MacroImplementations.scala +++ b/src/compiler/scala/tools/reflect/MacroImplementations.scala @@ -117,7 +117,8 @@ abstract class MacroImplementations { if (!strIsEmpty) { val len = str.length while (idx < len) { - if (str(idx) == '%') { + def notPercentN = str(idx) != '%' || (idx + 1 < len && str(idx + 1) != 'n') + if (str(idx) == '%' && notPercentN) { bldr append (str substring (start, idx)) append "%%" start = idx + 1 } diff --git a/src/intellij/repl.iml.SAMPLE b/src/intellij/repl.iml.SAMPLE new file mode 100644 index 0000000000..5e11ff1cf6 --- /dev/null +++ b/src/intellij/repl.iml.SAMPLE @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="FacetManager"> + <facet type="scala" name="Scala"> + <configuration> + <option name="compilerLibraryLevel" value="Project" /> + <option name="compilerLibraryName" value="compiler-locker" /> + <option name="maximumHeapSize" value="1536" /> + <option name="vmOptions" value="-Xms1536m -Xss1m -XX:MaxPermSize=512M -XX:ReservedCodeCacheSize=256m -XX:+CMSClassUnloadingEnabled -XX:+UseCompressedOops -XX:+UseParallelGC" /> + </configuration> + </facet> + </component> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$/../repl"> + <sourceFolder url="file://$MODULE_DIR$/../repl" isTestSource="false" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="library" /> + <orderEntry type="module" module-name="reflect" /> + <orderEntry type="module" module-name="compiler" /> + </component> +</module> + diff --git a/src/intellij/scala-lang.ipr.SAMPLE b/src/intellij/scala-lang.ipr.SAMPLE index e470e019c9..61c813df01 100644 --- a/src/intellij/scala-lang.ipr.SAMPLE +++ b/src/intellij/scala-lang.ipr.SAMPLE @@ -206,6 +206,7 @@ <module fileurl="file://$PROJECT_DIR$/manual.iml" filepath="$PROJECT_DIR$/manual.iml" /> <module fileurl="file://$PROJECT_DIR$/partest.iml" filepath="$PROJECT_DIR$/partest.iml" /> <module fileurl="file://$PROJECT_DIR$/reflect.iml" filepath="$PROJECT_DIR$/reflect.iml" /> + <module fileurl="file://$PROJECT_DIR$/repl.iml" filepath="$PROJECT_DIR$/repl.iml" /> <module fileurl="file://$PROJECT_DIR$/scala.iml" filepath="$PROJECT_DIR$/scala.iml" /> <module fileurl="file://$PROJECT_DIR$/scaladoc.iml" filepath="$PROJECT_DIR$/scaladoc.iml" /> <module fileurl="file://$PROJECT_DIR$/scalap.iml" filepath="$PROJECT_DIR$/scalap.iml" /> diff --git a/src/interactive/scala/tools/nsc/interactive/tests/InteractiveTest.scala b/src/interactive/scala/tools/nsc/interactive/tests/InteractiveTest.scala index a4a2de9b51..f30d896fb7 100644 --- a/src/interactive/scala/tools/nsc/interactive/tests/InteractiveTest.scala +++ b/src/interactive/scala/tools/nsc/interactive/tests/InteractiveTest.scala @@ -74,7 +74,7 @@ abstract class InteractiveTest /** Test's entry point */ def main(args: Array[String]) { try execute() - finally shutdown() + finally askShutdown() } protected def execute(): Unit = { @@ -110,14 +110,4 @@ abstract class InteractiveTest tester.run() } ****/ - - /** shutdown the presentation compiler. */ - protected def shutdown() { - askShutdown() - - // this is actually needed to force exit on test completion. - // Note: May be a bug on either the testing framework or (less likely) - // the presentation compiler - sys.exit(0) - } } diff --git a/src/interactive/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala b/src/interactive/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala index 9085eb56e6..c0ad245091 100644 --- a/src/interactive/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala +++ b/src/interactive/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala @@ -17,7 +17,7 @@ private[tests] trait CoreTestDefs with AskCompletionAt { def memberPrinter(member: compiler.Member): String = - "[accessible: %5s] ".format(member.accessible) + "`" + (member.sym.toString() + member.tpe.toString()).trim() + "`" + "[accessible: %5s] ".format(member.accessible) + "`" + (member.sym.toString.trim + member.tpe.toString()).trim + "`" override def runTest() { askAllSources(CompletionMarker) { pos => @@ -29,7 +29,7 @@ private[tests] trait CoreTestDefs // universal check file that we can provide for this to work reporter.println("retrieved %d members".format(members.size)) compiler ask { () => - val filtered = members.filterNot(member => member.sym.name.toString == "getClass" || member.sym.isConstructor) + val filtered = members.filterNot(member => (member.sym.name string_== "getClass") || member.sym.isConstructor) reporter.println(filtered.map(memberPrinter).sortBy(_.toString()).mkString("\n")) } } diff --git a/src/interactive/scala/tools/nsc/interactive/tests/core/SourcesCollector.scala b/src/interactive/scala/tools/nsc/interactive/tests/core/SourcesCollector.scala index 676feeba8a..40cfc111a1 100644 --- a/src/interactive/scala/tools/nsc/interactive/tests/core/SourcesCollector.scala +++ b/src/interactive/scala/tools/nsc/interactive/tests/core/SourcesCollector.scala @@ -11,7 +11,7 @@ private[tests] object SourcesCollector { * With the default `filter` only .scala and .java files are collected. * */ def apply(base: Path, filter: SourceFilter): Array[SourceFile] = { - assert(base.isDirectory) + assert(base.isDirectory, base + " is not a directory") base.walk.filter(filter).map(source).toList.toArray.sortBy(_.file.name) } diff --git a/src/library/scala/Function0.scala b/src/library/scala/Function0.scala index 2223091eb3..54cba021e0 100644 --- a/src/library/scala/Function0.scala +++ b/src/library/scala/Function0.scala @@ -6,7 +6,7 @@ ** |/ ** \* */ // GENERATED CODE: DO NOT EDIT. -// genprod generated these sources at: Tue Aug 07 11:54:44 CEST 2012 +// genprod generated these sources at: Sun Mar 24 14:14:12 CET 2013 package scala diff --git a/src/library/scala/Tuple1.scala b/src/library/scala/Tuple1.scala index 6776e4fbff..5898b63e21 100644 --- a/src/library/scala/Tuple1.scala +++ b/src/library/scala/Tuple1.scala @@ -15,6 +15,7 @@ package scala * @constructor Create a new tuple with 1 elements. * @param _1 Element 1 of this Tuple1 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple1[@specialized(Int, Long, Double) +T1](_1: T1) extends Product1[T1] { diff --git a/src/library/scala/Tuple10.scala b/src/library/scala/Tuple10.scala index e016dea63d..2b0239561d 100644 --- a/src/library/scala/Tuple10.scala +++ b/src/library/scala/Tuple10.scala @@ -24,6 +24,7 @@ package scala * @param _9 Element 9 of this Tuple10 * @param _10 Element 10 of this Tuple10 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple10[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10) extends Product10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] { diff --git a/src/library/scala/Tuple11.scala b/src/library/scala/Tuple11.scala index 87e759fc0a..0d5294d547 100644 --- a/src/library/scala/Tuple11.scala +++ b/src/library/scala/Tuple11.scala @@ -25,6 +25,7 @@ package scala * @param _10 Element 10 of this Tuple11 * @param _11 Element 11 of this Tuple11 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple11[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11) extends Product11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11] { diff --git a/src/library/scala/Tuple12.scala b/src/library/scala/Tuple12.scala index 7c95f8aa5f..d36c8275c1 100644 --- a/src/library/scala/Tuple12.scala +++ b/src/library/scala/Tuple12.scala @@ -26,6 +26,7 @@ package scala * @param _11 Element 11 of this Tuple12 * @param _12 Element 12 of this Tuple12 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple12[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12) extends Product12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12] { diff --git a/src/library/scala/Tuple13.scala b/src/library/scala/Tuple13.scala index 9f2ecd86da..edc37456fe 100644 --- a/src/library/scala/Tuple13.scala +++ b/src/library/scala/Tuple13.scala @@ -27,6 +27,7 @@ package scala * @param _12 Element 12 of this Tuple13 * @param _13 Element 13 of this Tuple13 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple13[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13) extends Product13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13] { diff --git a/src/library/scala/Tuple14.scala b/src/library/scala/Tuple14.scala index f03e279743..9896e736c9 100644 --- a/src/library/scala/Tuple14.scala +++ b/src/library/scala/Tuple14.scala @@ -28,6 +28,7 @@ package scala * @param _13 Element 13 of this Tuple14 * @param _14 Element 14 of this Tuple14 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple14[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14) extends Product14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14] { diff --git a/src/library/scala/Tuple15.scala b/src/library/scala/Tuple15.scala index 6074a40cd0..45cd4f751f 100644 --- a/src/library/scala/Tuple15.scala +++ b/src/library/scala/Tuple15.scala @@ -29,6 +29,7 @@ package scala * @param _14 Element 14 of this Tuple15 * @param _15 Element 15 of this Tuple15 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple15[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15) extends Product15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15] { diff --git a/src/library/scala/Tuple16.scala b/src/library/scala/Tuple16.scala index 0c38bd783f..2e370a5b31 100644 --- a/src/library/scala/Tuple16.scala +++ b/src/library/scala/Tuple16.scala @@ -30,6 +30,7 @@ package scala * @param _15 Element 15 of this Tuple16 * @param _16 Element 16 of this Tuple16 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple16[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16) extends Product16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16] { diff --git a/src/library/scala/Tuple17.scala b/src/library/scala/Tuple17.scala index 7cc7ea8f7e..2242a15fda 100644 --- a/src/library/scala/Tuple17.scala +++ b/src/library/scala/Tuple17.scala @@ -31,6 +31,7 @@ package scala * @param _16 Element 16 of this Tuple17 * @param _17 Element 17 of this Tuple17 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple17[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17) extends Product17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17] { diff --git a/src/library/scala/Tuple18.scala b/src/library/scala/Tuple18.scala index 7404349989..68f245c6ce 100644 --- a/src/library/scala/Tuple18.scala +++ b/src/library/scala/Tuple18.scala @@ -32,6 +32,7 @@ package scala * @param _17 Element 17 of this Tuple18 * @param _18 Element 18 of this Tuple18 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple18[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17, +T18](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17, _18: T18) extends Product18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18] { diff --git a/src/library/scala/Tuple19.scala b/src/library/scala/Tuple19.scala index ca8f2ba401..a8a49549fb 100644 --- a/src/library/scala/Tuple19.scala +++ b/src/library/scala/Tuple19.scala @@ -33,6 +33,7 @@ package scala * @param _18 Element 18 of this Tuple19 * @param _19 Element 19 of this Tuple19 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple19[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17, +T18, +T19](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17, _18: T18, _19: T19) extends Product19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19] { diff --git a/src/library/scala/Tuple2.scala b/src/library/scala/Tuple2.scala index 4337e62a53..9ea1469c5c 100644 --- a/src/library/scala/Tuple2.scala +++ b/src/library/scala/Tuple2.scala @@ -16,6 +16,7 @@ package scala * @param _1 Element 1 of this Tuple2 * @param _2 Element 2 of this Tuple2 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple2[@specialized(Int, Long, Double, Char, Boolean/*, AnyRef*/) +T1, @specialized(Int, Long, Double, Char, Boolean/*, AnyRef*/) +T2](_1: T1, _2: T2) extends Product2[T1, T2] { diff --git a/src/library/scala/Tuple20.scala b/src/library/scala/Tuple20.scala index 9d6e2f71ff..0118d382ab 100644 --- a/src/library/scala/Tuple20.scala +++ b/src/library/scala/Tuple20.scala @@ -34,6 +34,7 @@ package scala * @param _19 Element 19 of this Tuple20 * @param _20 Element 20 of this Tuple20 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple20[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17, +T18, +T19, +T20](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17, _18: T18, _19: T19, _20: T20) extends Product20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20] { diff --git a/src/library/scala/Tuple21.scala b/src/library/scala/Tuple21.scala index 6173ddb118..ceae94af41 100644 --- a/src/library/scala/Tuple21.scala +++ b/src/library/scala/Tuple21.scala @@ -35,6 +35,7 @@ package scala * @param _20 Element 20 of this Tuple21 * @param _21 Element 21 of this Tuple21 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple21[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17, +T18, +T19, +T20, +T21](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17, _18: T18, _19: T19, _20: T20, _21: T21) extends Product21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21] { diff --git a/src/library/scala/Tuple22.scala b/src/library/scala/Tuple22.scala index d426a548e5..ecd567a710 100644 --- a/src/library/scala/Tuple22.scala +++ b/src/library/scala/Tuple22.scala @@ -36,6 +36,7 @@ package scala * @param _21 Element 21 of this Tuple22 * @param _22 Element 22 of this Tuple22 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple22[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, +T13, +T14, +T15, +T16, +T17, +T18, +T19, +T20, +T21, +T22](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9, _10: T10, _11: T11, _12: T12, _13: T13, _14: T14, _15: T15, _16: T16, _17: T17, _18: T18, _19: T19, _20: T20, _21: T21, _22: T22) extends Product22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22] { diff --git a/src/library/scala/Tuple3.scala b/src/library/scala/Tuple3.scala index 3c7e2af0d1..6e71d3ae8c 100644 --- a/src/library/scala/Tuple3.scala +++ b/src/library/scala/Tuple3.scala @@ -17,6 +17,7 @@ package scala * @param _2 Element 2 of this Tuple3 * @param _3 Element 3 of this Tuple3 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple3[+T1, +T2, +T3](_1: T1, _2: T2, _3: T3) extends Product3[T1, T2, T3] { diff --git a/src/library/scala/Tuple4.scala b/src/library/scala/Tuple4.scala index b6913dbf48..4c84cfc674 100644 --- a/src/library/scala/Tuple4.scala +++ b/src/library/scala/Tuple4.scala @@ -18,6 +18,7 @@ package scala * @param _3 Element 3 of this Tuple4 * @param _4 Element 4 of this Tuple4 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple4[+T1, +T2, +T3, +T4](_1: T1, _2: T2, _3: T3, _4: T4) extends Product4[T1, T2, T3, T4] { diff --git a/src/library/scala/Tuple5.scala b/src/library/scala/Tuple5.scala index 4f83f44cb9..fe8e853f12 100644 --- a/src/library/scala/Tuple5.scala +++ b/src/library/scala/Tuple5.scala @@ -19,6 +19,7 @@ package scala * @param _4 Element 4 of this Tuple5 * @param _5 Element 5 of this Tuple5 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple5[+T1, +T2, +T3, +T4, +T5](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5) extends Product5[T1, T2, T3, T4, T5] { diff --git a/src/library/scala/Tuple6.scala b/src/library/scala/Tuple6.scala index ac2ec43bd6..6bf1c73d4b 100644 --- a/src/library/scala/Tuple6.scala +++ b/src/library/scala/Tuple6.scala @@ -20,6 +20,7 @@ package scala * @param _5 Element 5 of this Tuple6 * @param _6 Element 6 of this Tuple6 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple6[+T1, +T2, +T3, +T4, +T5, +T6](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6) extends Product6[T1, T2, T3, T4, T5, T6] { diff --git a/src/library/scala/Tuple7.scala b/src/library/scala/Tuple7.scala index 62407b1d9b..ea42709cb7 100644 --- a/src/library/scala/Tuple7.scala +++ b/src/library/scala/Tuple7.scala @@ -21,6 +21,7 @@ package scala * @param _6 Element 6 of this Tuple7 * @param _7 Element 7 of this Tuple7 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple7[+T1, +T2, +T3, +T4, +T5, +T6, +T7](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7) extends Product7[T1, T2, T3, T4, T5, T6, T7] { diff --git a/src/library/scala/Tuple8.scala b/src/library/scala/Tuple8.scala index 0611fefd16..c24f9454e0 100644 --- a/src/library/scala/Tuple8.scala +++ b/src/library/scala/Tuple8.scala @@ -22,6 +22,7 @@ package scala * @param _7 Element 7 of this Tuple8 * @param _8 Element 8 of this Tuple8 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple8[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8) extends Product8[T1, T2, T3, T4, T5, T6, T7, T8] { diff --git a/src/library/scala/Tuple9.scala b/src/library/scala/Tuple9.scala index 52f27f7c46..ed02b30df2 100644 --- a/src/library/scala/Tuple9.scala +++ b/src/library/scala/Tuple9.scala @@ -23,6 +23,7 @@ package scala * @param _8 Element 8 of this Tuple9 * @param _9 Element 9 of this Tuple9 */ +@deprecatedInheritance("Tuples will be made final in a future version.", "2.11.0") case class Tuple9[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5, _6: T6, _7: T7, _8: T8, _9: T9) extends Product9[T1, T2, T3, T4, T5, T6, T7, T8, T9] { diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index c85a4fb6e7..72a23a0dd0 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -161,6 +161,41 @@ object Iterator { def hasNext = true def next = elem } + + /** Avoid stack overflows when applying ++ to lots of iterators by + * flattening the unevaluated iterators out into a vector of closures. + */ + private[scala] final class ConcatIterator[+A](initial: Vector[() => Iterator[A]]) extends Iterator[A] { + // current set to null when all iterators are exhausted + private[this] var current: Iterator[A] = Iterator.empty + private[this] var queue: Vector[() => Iterator[A]] = initial + // Advance current to the next non-empty iterator + private[this] def advance(): Boolean = { + if (queue.isEmpty) { + current = null + false + } + else { + current = queue.head() + queue = queue.tail + current.hasNext || advance() + } + } + def hasNext = (current ne null) && (current.hasNext || advance()) + def next() = if (hasNext) current.next else Iterator.empty.next + + override def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] = + new ConcatIterator(queue :+ (() => that.toIterator)) + } + + private[scala] final class JoinIterator[+A](lhs: Iterator[A], that: => GenTraversableOnce[A]) extends Iterator[A] { + private[this] lazy val rhs: Iterator[A] = that.toIterator + def hasNext = lhs.hasNext || rhs.hasNext + def next = if (lhs.hasNext) lhs.next else rhs.next + + override def ++[B >: A](that: => GenTraversableOnce[B]) = + new ConcatIterator(Vector(() => this, () => that.toIterator)) + } } import Iterator.empty @@ -338,24 +373,7 @@ trait Iterator[+A] extends TraversableOnce[A] { * @usecase def ++(that: => Iterator[A]): Iterator[A] * @inheritdoc */ - def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] = new AbstractIterator[B] { - // optimize a little bit to prevent n log n behavior. - private var cur : Iterator[B] = self - private var selfExhausted : Boolean = false - // since that is by-name, make sure it's only referenced once - - // if "val it = that" is inside the block, then hasNext on an empty - // iterator will continually reevaluate it. (ticket #3269) - lazy val it = that.toIterator - // the eq check is to avoid an infinite loop on "x ++ x" - def hasNext = cur.hasNext || (!selfExhausted && { - it.hasNext && { - cur = it - selfExhausted = true - true - } - }) - def next() = { hasNext; cur.next() } - } + def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] = new Iterator.JoinIterator(self, that) /** Creates a new iterator by applying a function to all values produced by this iterator * and concatenating the results. diff --git a/src/library/scala/collection/parallel/package.scala b/src/library/scala/collection/parallel/package.scala index d91f70da75..85c620239a 100644 --- a/src/library/scala/collection/parallel/package.scala +++ b/src/library/scala/collection/parallel/package.scala @@ -42,11 +42,8 @@ package object parallel { private[parallel] def outofbounds(idx: Int) = throw new IndexOutOfBoundsException(idx.toString) private[parallel] def getTaskSupport: TaskSupport = - if (scala.util.Properties.isJavaAtLeast("1.6")) { - val vendor = scala.util.Properties.javaVmVendor - if ((vendor contains "Oracle") || (vendor contains "Sun") || (vendor contains "Apple")) new ForkJoinTaskSupport - else new ThreadPoolTaskSupport - } else new ThreadPoolTaskSupport + if (scala.util.Properties.isJavaAtLeast("1.6")) new ForkJoinTaskSupport + else new ThreadPoolTaskSupport val defaultTaskSupport: TaskSupport = getTaskSupport diff --git a/src/library/scala/reflect/NameTransformer.scala b/src/library/scala/reflect/NameTransformer.scala index 8a1cce6b02..6192971c74 100755 --- a/src/library/scala/reflect/NameTransformer.scala +++ b/src/library/scala/reflect/NameTransformer.scala @@ -15,9 +15,12 @@ package reflect object NameTransformer { // XXX Short term: providing a way to alter these without having to recompile // the compiler before recompiling the compiler. - val MODULE_SUFFIX_STRING = sys.props.getOrElse("SCALA_MODULE_SUFFIX_STRING", "$") - val NAME_JOIN_STRING = sys.props.getOrElse("SCALA_NAME_JOIN_STRING", "$") - val MODULE_INSTANCE_NAME = "MODULE$" + val MODULE_SUFFIX_STRING = sys.props.getOrElse("SCALA_MODULE_SUFFIX_STRING", "$") + val NAME_JOIN_STRING = sys.props.getOrElse("SCALA_NAME_JOIN_STRING", "$") + val MODULE_INSTANCE_NAME = "MODULE$" + val LOCAL_SUFFIX_STRING = " " + val SETTER_SUFFIX_STRING = "_$eq" + val TRAIT_SETTER_SEPARATOR_STRING = "$_setter_$" private val nops = 128 private val ncodes = 26 * 26 diff --git a/src/partest/scala/tools/partest/nest/Diff.java b/src/partest/scala/tools/partest/nest/Diff.java deleted file mode 100644 index f69fc6858b..0000000000 --- a/src/partest/scala/tools/partest/nest/Diff.java +++ /dev/null @@ -1,873 +0,0 @@ - -package scala.tools.partest.nest; - -import java.util.Hashtable; - -/** A class to compare IndexedSeqs of objects. The result of comparison - is a list of <code>change</code> objects which form an - edit script. The objects compared are traditionally lines - of text from two files. Comparison options such as "ignore - whitespace" are implemented by modifying the <code>equals</code> - and <code>hashcode</code> methods for the objects compared. -<p> - The basic algorithm is described in: </br> - "An O(ND) Difference Algorithm and its Variations", Eugene Myers, - Algorithmica Vol. 1 No. 2, 1986, p 251. -<p> - This class outputs different results from GNU diff 1.15 on some - inputs. Our results are actually better (smaller change list, smaller - total size of changes), but it would be nice to know why. Perhaps - there is a memory overwrite bug in GNU diff 1.15. - - @author Stuart D. Gathman, translated from GNU diff 1.15 - Copyright (C) 2000 Business Management Systems, Inc. -<p> - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. -<p> - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -<p> - You should have received a copy of the <a href=COPYING.txt> - GNU General Public License</a> - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - */ - -public class Diff { - - /** Prepare to find differences between two arrays. Each element of - the arrays is translated to an "equivalence number" based on - the result of <code>equals</code>. The original Object arrays - are no longer needed for computing the differences. They will - be needed again later to print the results of the comparison as - an edit script, if desired. - */ - public Diff(Object[] a,Object[] b) { - Hashtable<Object, Integer> h = new Hashtable<Object, Integer>(a.length + b.length); - filevec[0] = new file_data(a,h); - filevec[1] = new file_data(b,h); - } - - /** 1 more than the maximum equivalence value used for this or its - sibling file. */ - private int equiv_max = 1; - - /** When set to true, the comparison uses a heuristic to speed it up. - With this heuristic, for files with a constant small density - of changes, the algorithm is linear in the file size. */ - public boolean heuristic = false; - - /** When set to true, the algorithm returns a guarranteed minimal - set of changes. This makes things slower, sometimes much slower. */ - public boolean no_discards = false; - - private int[] xvec, yvec; /* IndexedSeqs being compared. */ - private int[] fdiag; /* IndexedSeq, indexed by diagonal, containing - the X coordinate of the point furthest - along the given diagonal in the forward - search of the edit matrix. */ - private int[] bdiag; /* IndexedSeq, indexed by diagonal, containing - the X coordinate of the point furthest - along the given diagonal in the backward - search of the edit matrix. */ - private int fdiagoff, bdiagoff; - private final file_data[] filevec = new file_data[2]; - private int cost; - - /** Find the midpoint of the shortest edit script for a specified - portion of the two files. - - We scan from the beginnings of the files, and simultaneously from the ends, - doing a breadth-first search through the space of edit-sequence. - When the two searches meet, we have found the midpoint of the shortest - edit sequence. - - The value returned is the number of the diagonal on which the midpoint lies. - The diagonal number equals the number of inserted lines minus the number - of deleted lines (counting only lines before the midpoint). - The edit cost is stored into COST; this is the total number of - lines inserted or deleted (counting only lines before the midpoint). - - This function assumes that the first lines of the specified portions - of the two files do not match, and likewise that the last lines do not - match. The caller must trim matching lines from the beginning and end - of the portions it is going to specify. - - Note that if we return the "wrong" diagonal value, or if - the value of bdiag at that diagonal is "wrong", - the worst this can do is cause suboptimal diff output. - It cannot cause incorrect diff output. */ - - private int diag (int xoff, int xlim, int yoff, int ylim) { - final int[] fd = fdiag; // Give the compiler a chance. - final int[] bd = bdiag; // Additional help for the compiler. - final int[] xv = xvec; // Still more help for the compiler. - final int[] yv = yvec; // And more and more . . . - final int dmin = xoff - ylim; // Minimum valid diagonal. - final int dmax = xlim - yoff; // Maximum valid diagonal. - final int fmid = xoff - yoff; // Center diagonal of top-down search. - final int bmid = xlim - ylim; // Center diagonal of bottom-up search. - int fmin = fmid, fmax = fmid; // Limits of top-down search. - int bmin = bmid, bmax = bmid; // Limits of bottom-up search. - /* True if southeast corner is on an odd - diagonal with respect to the northwest. */ - final boolean odd = (fmid - bmid & 1) != 0; - - fd[fdiagoff + fmid] = xoff; - bd[bdiagoff + bmid] = xlim; - - for (int c = 1;; ++c) - { - int d; /* Active diagonal. */ - boolean big_snake = false; - - /* Extend the top-down search by an edit step in each diagonal. */ - if (fmin > dmin) - fd[fdiagoff + --fmin - 1] = -1; - else - ++fmin; - if (fmax < dmax) - fd[fdiagoff + ++fmax + 1] = -1; - else - --fmax; - for (d = fmax; d >= fmin; d -= 2) - { - int x, y, oldx, tlo = fd[fdiagoff + d - 1], thi = fd[fdiagoff + d + 1]; - - if (tlo >= thi) - x = tlo + 1; - else - x = thi; - oldx = x; - y = x - d; - while (x < xlim && y < ylim && xv[x] == yv[y]) { - ++x; ++y; - } - if (x - oldx > 20) - big_snake = true; - fd[fdiagoff + d] = x; - if (odd && bmin <= d && d <= bmax && bd[bdiagoff + d] <= fd[fdiagoff + d]) - { - cost = 2 * c - 1; - return d; - } - } - - /* Similar extend the bottom-up search. */ - if (bmin > dmin) - bd[bdiagoff + --bmin - 1] = Integer.MAX_VALUE; - else - ++bmin; - if (bmax < dmax) - bd[bdiagoff + ++bmax + 1] = Integer.MAX_VALUE; - else - --bmax; - for (d = bmax; d >= bmin; d -= 2) - { - int x, y, oldx, tlo = bd[bdiagoff + d - 1], thi = bd[bdiagoff + d + 1]; - - if (tlo < thi) - x = tlo; - else - x = thi - 1; - oldx = x; - y = x - d; - while (x > xoff && y > yoff && xv[x - 1] == yv[y - 1]) { - --x; --y; - } - if (oldx - x > 20) - big_snake = true; - bd[bdiagoff + d] = x; - if (!odd && fmin <= d && d <= fmax && bd[bdiagoff + d] <= fd[fdiagoff + d]) - { - cost = 2 * c; - return d; - } - } - - /* Heuristic: check occasionally for a diagonal that has made - lots of progress compared with the edit distance. - If we have any such, find the one that has made the most - progress and return it as if it had succeeded. - - With this heuristic, for files with a constant small density - of changes, the algorithm is linear in the file size. */ - - if (c > 200 && big_snake && heuristic) - { - int best = 0; - int bestpos = -1; - - for (d = fmax; d >= fmin; d -= 2) - { - int dd = d - fmid; - if ((fd[fdiagoff + d] - xoff)*2 - dd > 12 * (c + (dd > 0 ? dd : -dd))) - { - if (fd[fdiagoff + d] * 2 - dd > best - && fd[fdiagoff + d] - xoff > 20 - && fd[fdiagoff + d] - d - yoff > 20) - { - int k; - int x = fd[fdiagoff + d]; - - /* We have a good enough best diagonal; - now insist that it end with a significant snake. */ - for (k = 1; k <= 20; k++) - if (xvec[x - k] != yvec[x - d - k]) - break; - - if (k == 21) - { - best = fd[fdiagoff + d] * 2 - dd; - bestpos = d; - } - } - } - } - if (best > 0) - { - cost = 2 * c - 1; - return bestpos; - } - - best = 0; - for (d = bmax; d >= bmin; d -= 2) - { - int dd = d - bmid; - if ((xlim - bd[bdiagoff + d])*2 + dd > 12 * (c + (dd > 0 ? dd : -dd))) - { - if ((xlim - bd[bdiagoff + d]) * 2 + dd > best - && xlim - bd[bdiagoff + d] > 20 - && ylim - (bd[bdiagoff + d] - d) > 20) - { - /* We have a good enough best diagonal; - now insist that it end with a significant snake. */ - int k; - int x = bd[bdiagoff + d]; - - for (k = 0; k < 20; k++) - if (xvec[x + k] != yvec[x - d + k]) - break; - if (k == 20) - { - best = (xlim - bd[bdiagoff + d]) * 2 + dd; - bestpos = d; - } - } - } - } - if (best > 0) - { - cost = 2 * c - 1; - return bestpos; - } - } - } - } - - /** Compare in detail contiguous subsequences of the two files - which are known, as a whole, to match each other. - - The results are recorded in the IndexedSeqs filevec[N].changed_flag, by - storing a 1 in the element for each line that is an insertion or deletion. - - The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1. - - Note that XLIM, YLIM are exclusive bounds. - All line numbers are origin-0 and discarded lines are not counted. */ - - private void compareseq (int xoff, int xlim, int yoff, int ylim) { - /* Slide down the bottom initial diagonal. */ - while (xoff < xlim && yoff < ylim && xvec[xoff] == yvec[yoff]) { - ++xoff; ++yoff; - } - /* Slide up the top initial diagonal. */ - while (xlim > xoff && ylim > yoff && xvec[xlim - 1] == yvec[ylim - 1]) { - --xlim; --ylim; - } - - /* Handle simple cases. */ - if (xoff == xlim) - while (yoff < ylim) - filevec[1].changed_flag[1+filevec[1].realindexes[yoff++]] = true; - else if (yoff == ylim) - while (xoff < xlim) - filevec[0].changed_flag[1+filevec[0].realindexes[xoff++]] = true; - else - { - /* Find a point of correspondence in the middle of the files. */ - - int d = diag (xoff, xlim, yoff, ylim); - int c = cost; - int f = fdiag[fdiagoff + d]; - int b = bdiag[bdiagoff + d]; - - if (c == 1) - { - /* This should be impossible, because it implies that - one of the two subsequences is empty, - and that case was handled above without calling `diag'. - Let's verify that this is true. */ - throw new IllegalArgumentException("Empty subsequence"); - } - else - { - /* Use that point to split this problem into two subproblems. */ - compareseq (xoff, b, yoff, b - d); - /* This used to use f instead of b, - but that is incorrect! - It is not necessarily the case that diagonal d - has a snake from b to f. */ - compareseq (b, xlim, b - d, ylim); - } - } - } - - /** Discard lines from one file that have no matches in the other file. - */ - - private void discard_confusing_lines() { - filevec[0].discard_confusing_lines(filevec[1]); - filevec[1].discard_confusing_lines(filevec[0]); - } - - private boolean inhibit = false; - - /** Adjust inserts/deletes of blank lines to join changes - as much as possible. - */ - - private void shift_boundaries() { - if (inhibit) - return; - filevec[0].shift_boundaries(filevec[1]); - filevec[1].shift_boundaries(filevec[0]); - } - - public interface ScriptBuilder { - /** Scan the tables of which lines are inserted and deleted, - producing an edit script. - @param changed0 true for lines in first file which do not match 2nd - @param len0 number of lines in first file - @param changed1 true for lines in 2nd file which do not match 1st - @param len1 number of lines in 2nd file - @return a linked list of changes - or null - */ - public change build_script( - boolean[] changed0,int len0, - boolean[] changed1,int len1 - ); - } - - /** Scan the tables of which lines are inserted and deleted, - producing an edit script in reverse order. */ - - static class ReverseScript implements ScriptBuilder { - public change build_script( - final boolean[] changed0,int len0, - final boolean[] changed1,int len1) - { - change script = null; - int i0 = 0, i1 = 0; - while (i0 < len0 || i1 < len1) { - if (changed0[1+i0] || changed1[1+i1]) { - int line0 = i0, line1 = i1; - - /* Find # lines changed here in each file. */ - while (changed0[1+i0]) ++i0; - while (changed1[1+i1]) ++i1; - - /* Record this change. */ - script = new change(line0, line1, i0 - line0, i1 - line1, script); - } - - /* We have reached lines in the two files that match each other. */ - i0++; i1++; - } - - return script; - } - } - - static class ForwardScript implements ScriptBuilder { - /** Scan the tables of which lines are inserted and deleted, - producing an edit script in forward order. */ - public change build_script( - final boolean[] changed0,int len0, - final boolean[] changed1,int len1) - { - change script = null; - int i0 = len0, i1 = len1; - - while (i0 >= 0 || i1 >= 0) - { - if (changed0[i0] || changed1[i1]) - { - int line0 = i0, line1 = i1; - - /* Find # lines changed here in each file. */ - while (changed0[i0]) --i0; - while (changed1[i1]) --i1; - - /* Record this change. */ - script = new change(i0, i1, line0 - i0, line1 - i1, script); - } - - /* We have reached lines in the two files that match each other. */ - i0--; i1--; - } - - return script; - } - } - - /** Standard ScriptBuilders. */ - public final static ScriptBuilder - forwardScript = new ForwardScript(), - reverseScript = new ReverseScript(); - - /* Report the differences of two files. DEPTH is the current directory - depth. */ - public final change diff_2(final boolean reverse) { - return diff(reverse ? reverseScript : forwardScript); - } - - /** Get the results of comparison as an edit script. The script - is described by a list of changes. The standard ScriptBuilder - implementations provide for forward and reverse edit scripts. - Alternate implementations could, for instance, list common elements - instead of differences. - @param bld an object to build the script from change flags - @return the head of a list of changes - */ - public change diff(final ScriptBuilder bld) { - - /* Some lines are obviously insertions or deletions - because they don't match anything. Detect them now, - and avoid even thinking about them in the main comparison algorithm. */ - - discard_confusing_lines (); - - /* Now do the main comparison algorithm, considering just the - undiscarded lines. */ - - xvec = filevec[0].undiscarded; - yvec = filevec[1].undiscarded; - - int diags = - filevec[0].nondiscarded_lines + filevec[1].nondiscarded_lines + 3; - fdiag = new int[diags]; - fdiagoff = filevec[1].nondiscarded_lines + 1; - bdiag = new int[diags]; - bdiagoff = filevec[1].nondiscarded_lines + 1; - - compareseq (0, filevec[0].nondiscarded_lines, - 0, filevec[1].nondiscarded_lines); - fdiag = null; - bdiag = null; - - /* Modify the results slightly to make them prettier - in cases where that can validly be done. */ - - shift_boundaries (); - - /* Get the results of comparison in the form of a chain - of `struct change's -- an edit script. */ - return bld.build_script( - filevec[0].changed_flag, - filevec[0].buffered_lines, - filevec[1].changed_flag, - filevec[1].buffered_lines - ); - - } - - /** The result of comparison is an "edit script": a chain of change objects. - Each change represents one place where some lines are deleted - and some are inserted. - - LINE0 and LINE1 are the first affected lines in the two files (origin 0). - DELETED is the number of lines deleted here from file 0. - INSERTED is the number of lines inserted here in file 1. - - If DELETED is 0 then LINE0 is the number of the line before - which the insertion was done; vice versa for INSERTED and LINE1. */ - - public static class change { - /** Previous or next edit command. */ - public change link; - /** # lines of file 1 changed here. */ - public final int inserted; - /** # lines of file 0 changed here. */ - public final int deleted; - /** Line number of 1st deleted line. */ - public final int line0; - /** Line number of 1st inserted line. */ - public final int line1; - - /** Cons an additional entry onto the front of an edit script OLD. - LINE0 and LINE1 are the first affected lines in the two files (origin 0). - DELETED is the number of lines deleted here from file 0. - INSERTED is the number of lines inserted here in file 1. - - If DELETED is 0 then LINE0 is the number of the line before - which the insertion was done; vice versa for INSERTED and LINE1. */ - public change(int line0, int line1, int deleted, int inserted, change old) { - this.line0 = line0; - this.line1 = line1; - this.inserted = inserted; - this.deleted = deleted; - this.link = old; - //System.err.println(line0+","+line1+","+inserted+","+deleted); - } - } - - /** Data on one input file being compared. - */ - - class file_data { - - /** Allocate changed array for the results of comparison. */ - void clear() { - /* Allocate a flag for each line of each file, saying whether that line - is an insertion or deletion. - Allocate an extra element, always zero, at each end of each IndexedSeq. - */ - changed_flag = new boolean[buffered_lines + 2]; - } - - /** Return equiv_count[I] as the number of lines in this file - that fall in equivalence class I. - @return the array of equivalence class counts. - */ - int[] equivCount() { - int[] equiv_count = new int[equiv_max]; - for (int i = 0; i < buffered_lines; ++i) - ++equiv_count[equivs[i]]; - return equiv_count; - } - - /** Discard lines that have no matches in another file. - - A line which is discarded will not be considered by the actual - comparison algorithm; it will be as if that line were not in the file. - The file's `realindexes' table maps virtual line numbers - (which don't count the discarded lines) into real line numbers; - this is how the actual comparison algorithm produces results - that are comprehensible when the discarded lines are counted. -<p> - When we discard a line, we also mark it as a deletion or insertion - so that it will be printed in the output. - @param f the other file - */ - void discard_confusing_lines(file_data f) { - clear(); - /* Set up table of which lines are going to be discarded. */ - final byte[] discarded = discardable(f.equivCount()); - - /* Don't really discard the provisional lines except when they occur - in a run of discardables, with nonprovisionals at the beginning - and end. */ - filterDiscards(discarded); - - /* Actually discard the lines. */ - discard(discarded); - } - - /** Mark to be discarded each line that matches no line of another file. - If a line matches many lines, mark it as provisionally discardable. - @see equivCount() - @param counts The count of each equivalence number for the other file. - @return 0=nondiscardable, 1=discardable or 2=provisionally discardable - for each line - */ - - private byte[] discardable(final int[] counts) { - final int end = buffered_lines; - final byte[] discards = new byte[end]; - final int[] equivs = this.equivs; - int many = 5; - int tem = end / 64; - - /* Multiply MANY by approximate square root of number of lines. - That is the threshold for provisionally discardable lines. */ - while ((tem = tem >> 2) > 0) - many *= 2; - - for (int i = 0; i < end; i++) - { - int nmatch; - if (equivs[i] == 0) - continue; - nmatch = counts[equivs[i]]; - if (nmatch == 0) - discards[i] = 1; - else if (nmatch > many) - discards[i] = 2; - } - return discards; - } - - /** Don't really discard the provisional lines except when they occur - in a run of discardables, with nonprovisionals at the beginning - and end. */ - - private void filterDiscards(final byte[] discards) { - final int end = buffered_lines; - - for (int i = 0; i < end; i++) - { - /* Cancel provisional discards not in middle of run of discards. */ - if (discards[i] == 2) - discards[i] = 0; - else if (discards[i] != 0) - { - /* We have found a nonprovisional discard. */ - int j; - int length; - int provisional = 0; - - /* Find end of this run of discardable lines. - Count how many are provisionally discardable. */ - for (j = i; j < end; j++) - { - if (discards[j] == 0) - break; - if (discards[j] == 2) - ++provisional; - } - - /* Cancel provisional discards at end, and shrink the run. */ - while (j > i && discards[j - 1] == 2) { - discards[--j] = 0; --provisional; - } - - /* Now we have the length of a run of discardable lines - whose first and last are not provisional. */ - length = j - i; - - /* If 1/4 of the lines in the run are provisional, - cancel discarding of all provisional lines in the run. */ - if (provisional * 4 > length) - { - while (j > i) - if (discards[--j] == 2) - discards[j] = 0; - } - else - { - int consec; - int minimum = 1; - int tem = length / 4; - - /* MINIMUM is approximate square root of LENGTH/4. - A subrun of two or more provisionals can stand - when LENGTH is at least 16. - A subrun of 4 or more can stand when LENGTH >= 64. */ - while ((tem = tem >> 2) > 0) - minimum *= 2; - minimum++; - - /* Cancel any subrun of MINIMUM or more provisionals - within the larger run. */ - for (j = 0, consec = 0; j < length; j++) - if (discards[i + j] != 2) - consec = 0; - else if (minimum == ++consec) - /* Back up to start of subrun, to cancel it all. */ - j -= consec; - else if (minimum < consec) - discards[i + j] = 0; - - /* Scan from beginning of run - until we find 3 or more nonprovisionals in a row - or until the first nonprovisional at least 8 lines in. - Until that point, cancel any provisionals. */ - for (j = 0, consec = 0; j < length; j++) - { - if (j >= 8 && discards[i + j] == 1) - break; - if (discards[i + j] == 2) { - consec = 0; discards[i + j] = 0; - } - else if (discards[i + j] == 0) - consec = 0; - else - consec++; - if (consec == 3) - break; - } - - /* I advances to the last line of the run. */ - i += length - 1; - - /* Same thing, from end. */ - for (j = 0, consec = 0; j < length; j++) - { - if (j >= 8 && discards[i - j] == 1) - break; - if (discards[i - j] == 2) { - consec = 0; discards[i - j] = 0; - } - else if (discards[i - j] == 0) - consec = 0; - else - consec++; - if (consec == 3) - break; - } - } - } - } - } - - /** Actually discard the lines. - @param discards flags lines to be discarded - */ - private void discard(final byte[] discards) { - final int end = buffered_lines; - int j = 0; - for (int i = 0; i < end; ++i) - if (no_discards || discards[i] == 0) - { - undiscarded[j] = equivs[i]; - realindexes[j++] = i; - } - else - changed_flag[1+i] = true; - nondiscarded_lines = j; - } - - file_data(Object[] data, Hashtable<Object, Integer> h) { - buffered_lines = data.length; - - equivs = new int[buffered_lines]; - undiscarded = new int[buffered_lines]; - realindexes = new int[buffered_lines]; - - for (int i = 0; i < data.length; ++i) { - Integer ir = h.get(data[i]); - if (ir == null) - h.put(data[i], new Integer(equivs[i] = equiv_max++)); - else - equivs[i] = ir.intValue(); - } - } - - /** Adjust inserts/deletes of blank lines to join changes - as much as possible. - - We do something when a run of changed lines include a blank - line at one end and have an excluded blank line at the other. - We are free to choose which blank line is included. - `compareseq' always chooses the one at the beginning, - but usually it is cleaner to consider the following blank line - to be the "change". The only exception is if the preceding blank line - would join this change to other changes. - @param f the file being compared against - */ - - void shift_boundaries(file_data f) { - final boolean[] changed = changed_flag; - final boolean[] other_changed = f.changed_flag; - int i = 0; - int j = 0; - int i_end = buffered_lines; - int preceding = -1; - int other_preceding = -1; - - for (;;) - { - int start, end, other_start; - - /* Scan forwards to find beginning of another run of changes. - Also keep track of the corresponding point in the other file. */ - - while (i < i_end && !changed[1+i]) - { - while (other_changed[1+j++]) - /* Non-corresponding lines in the other file - will count as the preceding batch of changes. */ - other_preceding = j; - i++; - } - - if (i == i_end) - break; - - start = i; - other_start = j; - - for (;;) - { - /* Now find the end of this run of changes. */ - - while (i < i_end && changed[1+i]) i++; - end = i; - - /* If the first changed line matches the following unchanged one, - and this run does not follow right after a previous run, - and there are no lines deleted from the other file here, - then classify the first changed line as unchanged - and the following line as changed in its place. */ - - /* You might ask, how could this run follow right after another? - Only because the previous run was shifted here. */ - - if (end != i_end - && equivs[start] == equivs[end] - && !other_changed[1+j] - && end != i_end - && !((preceding >= 0 && start == preceding) - || (other_preceding >= 0 - && other_start == other_preceding))) - { - changed[1+end++] = true; - changed[1+start++] = false; - ++i; - /* Since one line-that-matches is now before this run - instead of after, we must advance in the other file - to keep in synch. */ - ++j; - } - else - break; - } - - preceding = i; - other_preceding = j; - } - } - - /** Number of elements (lines) in this file. */ - final int buffered_lines; - - /** IndexedSeq, indexed by line number, containing an equivalence code for - each line. It is this IndexedSeq that is actually compared with that - of another file to generate differences. */ - private final int[] equivs; - - /** IndexedSeq, like the previous one except that - the elements for discarded lines have been squeezed out. */ - final int[] undiscarded; - - /** IndexedSeq mapping virtual line numbers (not counting discarded lines) - to real ones (counting those lines). Both are origin-0. */ - final int[] realindexes; - - /** Total number of nondiscarded lines. */ - int nondiscarded_lines; - - /** Array, indexed by real origin-1 line number, - containing true for a line that is an insertion or a deletion. - The results of comparison are stored here. */ - boolean[] changed_flag; - - } -} diff --git a/src/partest/scala/tools/partest/nest/DiffPrint.java b/src/partest/scala/tools/partest/nest/DiffPrint.java deleted file mode 100644 index 31f9a1bc79..0000000000 --- a/src/partest/scala/tools/partest/nest/DiffPrint.java +++ /dev/null @@ -1,606 +0,0 @@ - -package scala.tools.partest.nest; - -import java.io.*; -import java.util.Vector; -import java.util.Date; -//import com.objectspace.jgl.predicates.UnaryPredicate; - -interface UnaryPredicate { - boolean execute(Object obj); -} - -/** A simple framework for printing change lists produced by <code>Diff</code>. - @see bmsi.util.Diff - @author Stuart D. Gathman - Copyright (C) 2000 Business Management Systems, Inc. -<p> - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. -<p> - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -<p> - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -public class DiffPrint { - /** A Base class for printing edit scripts produced by Diff. - This class divides the change list into "hunks", and calls - <code>print_hunk</code> for each hunk. Various utility methods - are provided as well. - */ - public static abstract class Base { - protected Base(Object[] a,Object[] b, Writer w) { - outfile = new PrintWriter(w); - file0 = a; - file1 = b; - } - /** Set to ignore certain kinds of lines when printing - an edit script. For example, ignoring blank lines or comments. - */ - protected UnaryPredicate ignore = null; - - /** Set to the lines of the files being compared. - */ - protected Object[] file0, file1; - - /** Divide SCRIPT into pieces by calling HUNKFUN and - print each piece with PRINTFUN. - Both functions take one arg, an edit script. - - PRINTFUN takes a subscript which belongs together (with a null - link at the end) and prints it. */ - public void print_script(Diff.change script) { - Diff.change next = script; - - while (next != null) - { - Diff.change t, end; - - /* Find a set of changes that belong together. */ - t = next; - end = hunkfun(next); - - /* Disconnect them from the rest of the changes, - making them a hunk, and remember the rest for next iteration. */ - next = end.link; - end.link = null; - //if (DEBUG) - // debug_script(t); - - /* Print this hunk. */ - print_hunk(t); - - /* Reconnect the script so it will all be freed properly. */ - end.link = next; - } - outfile.flush(); - } - - /** Called with the tail of the script - and returns the last link that belongs together with the start - of the tail. */ - - protected Diff.change hunkfun(Diff.change hunk) { - return hunk; - } - - protected int first0, last0, first1, last1, deletes, inserts; - protected PrintWriter outfile; - - /** Look at a hunk of edit script and report the range of lines in each file - that it applies to. HUNK is the start of the hunk, which is a chain - of `struct change'. The first and last line numbers of file 0 are stored - in *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1. - Note that these are internal line numbers that count from 0. - - If no lines from file 0 are deleted, then FIRST0 is LAST0+1. - - Also set *DELETES nonzero if any lines of file 0 are deleted - and set *INSERTS nonzero if any lines of file 1 are inserted. - If only ignorable lines are inserted or deleted, both are - set to 0. */ - - protected void analyze_hunk(Diff.change hunk) { - int f0, l0 = 0, f1, l1 = 0, show_from = 0, show_to = 0; - int i; - Diff.change next; - boolean nontrivial = (ignore == null); - - show_from = show_to = 0; - - f0 = hunk.line0; - f1 = hunk.line1; - - for (next = hunk; next != null; next = next.link) - { - l0 = next.line0 + next.deleted - 1; - l1 = next.line1 + next.inserted - 1; - show_from += next.deleted; - show_to += next.inserted; - for (i = next.line0; i <= l0 && ! nontrivial; i++) - if (!ignore.execute(file0[i])) - nontrivial = true; - for (i = next.line1; i <= l1 && ! nontrivial; i++) - if (!ignore.execute(file1[i])) - nontrivial = true; - } - - first0 = f0; - last0 = l0; - first1 = f1; - last1 = l1; - - /* If all inserted or deleted lines are ignorable, - tell the caller to ignore this hunk. */ - - if (!nontrivial) - show_from = show_to = 0; - - deletes = show_from; - inserts = show_to; - } - - /** Print the script header which identifies the files compared. */ - protected void print_header(String filea, String fileb) { } - - protected abstract void print_hunk(Diff.change hunk); - - protected void print_1_line(String pre,Object linbuf) { - outfile.println(pre + linbuf.toString()); - } - - /** Print a pair of line numbers with SEPCHAR, translated for file FILE. - If the two numbers are identical, print just one number. - - Args A and B are internal line numbers. - We print the translated (real) line numbers. */ - - protected void print_number_range (char sepchar, int a, int b) { - /* Note: we can have B < A in the case of a range of no lines. - In this case, we should print the line number before the range, - which is B. */ - if (++b > ++a) - outfile.print("" + a + sepchar + b); - else - outfile.print(b); - } - - public static char change_letter(int inserts, int deletes) { - if (inserts == 0) - return 'd'; - else if (deletes == 0) - return 'a'; - else - return 'c'; - } - } - - /** Print a change list in the standard diff format. - */ - public static class NormalPrint extends Base { - - public NormalPrint(Object[] a,Object[] b, Writer w) { - super(a,b,w); - } - - /** Print a hunk of a normal diff. - This is a contiguous portion of a complete edit script, - describing changes in consecutive lines. */ - - protected void print_hunk (Diff.change hunk) { - - /* Determine range of line numbers involved in each file. */ - analyze_hunk(hunk); - if (deletes == 0 && inserts == 0) - return; - - /* Print out the line number header for this hunk */ - print_number_range (',', first0, last0); - outfile.print(change_letter(inserts, deletes)); - print_number_range (',', first1, last1); - outfile.println(); - - /* Print the lines that the first file has. */ - if (deletes != 0) - for (int i = first0; i <= last0; i++) - print_1_line ("< ", file0[i]); - - if (inserts != 0 && deletes != 0) - outfile.println("---"); - - /* Print the lines that the second file has. */ - if (inserts != 0) - for (int i = first1; i <= last1; i++) - print_1_line ("> ", file1[i]); - } - } - - /** Prints an edit script in a format suitable for input to <code>ed</code>. - The edit script must be generated with the reverse option to - be useful as actual <code>ed</code> input. - */ - public static class EdPrint extends Base { - - public EdPrint(Object[] a,Object[] b, Writer w) { - super(a,b,w); - } - - /** Print a hunk of an ed diff */ - protected void print_hunk(Diff.change hunk) { - - /* Determine range of line numbers involved in each file. */ - analyze_hunk (hunk); - if (deletes == 0 && inserts == 0) - return; - - /* Print out the line number header for this hunk */ - print_number_range (',', first0, last0); - outfile.println(change_letter(inserts, deletes)); - - /* Print new/changed lines from second file, if needed */ - if (inserts != 0) - { - boolean inserting = true; - for (int i = first1; i <= last1; i++) - { - /* Resume the insert, if we stopped. */ - if (! inserting) - outfile.println(i - first1 + first0 + "a"); - inserting = true; - - /* If the file's line is just a dot, it would confuse `ed'. - So output it with a double dot, and set the flag LEADING_DOT - so that we will output another ed-command later - to change the double dot into a single dot. */ - - if (".".equals(file1[i])) - { - outfile.println(".."); - outfile.println("."); - /* Now change that double dot to the desired single dot. */ - outfile.println(i - first1 + first0 + 1 + "s/^\\.\\././"); - inserting = false; - } - else - /* Line is not `.', so output it unmodified. */ - print_1_line ("", file1[i]); - } - - /* End insert mode, if we are still in it. */ - if (inserting) - outfile.println("."); - } - } - } - - /** Prints an edit script in context diff format. This and its - 'unified' variation is used for source code patches. - */ - public static class ContextPrint extends Base { - - protected int context = 3; - - public ContextPrint(Object[] a,Object[] b, Writer w) { - super(a,b,w); - } - - protected void print_context_label (String mark, File inf, String label) { - if (label != null) - outfile.println(mark + ' ' + label); - else if (inf.lastModified() > 0) - // FIXME: use DateFormat to get precise format needed. - outfile.println( - mark + ' ' + inf.getPath() + '\t' + new Date(inf.lastModified()) - ); - else - /* Don't pretend that standard input is ancient. */ - outfile.println(mark + ' ' + inf.getPath()); - } - - public void print_header(String filea,String fileb) { - print_context_label ("***", new File(filea), filea); - print_context_label ("---", new File(fileb), fileb); - } - - /** If function_regexp defined, search for start of function. */ - private String find_function(Object[] lines, int start) { - return null; - } - - protected void print_function(Object[] file,int start) { - String function = find_function (file0, first0); - if (function != null) { - outfile.print(" "); - outfile.print( - (function.length() < 40) ? function : function.substring(0,40) - ); - } - } - - protected void print_hunk(Diff.change hunk) { - - /* Determine range of line numbers involved in each file. */ - - analyze_hunk (hunk); - - if (deletes == 0 && inserts == 0) - return; - - /* Include a context's width before and after. */ - - first0 = Math.max(first0 - context, 0); - first1 = Math.max(first1 - context, 0); - last0 = Math.min(last0 + context, file0.length - 1); - last1 = Math.min(last1 + context, file1.length - 1); - - - outfile.print("***************"); - - /* If we looked for and found a function this is part of, - include its name in the header of the diff section. */ - print_function (file0, first0); - - outfile.println(); - outfile.print("*** "); - print_number_range (',', first0, last0); - outfile.println(" ****"); - - if (deletes != 0) { - Diff.change next = hunk; - - for (int i = first0; i <= last0; i++) { - /* Skip past changes that apply (in file 0) - only to lines before line I. */ - - while (next != null && next.line0 + next.deleted <= i) - next = next.link; - - /* Compute the marking for line I. */ - - String prefix = " "; - if (next != null && next.line0 <= i) - /* The change NEXT covers this line. - If lines were inserted here in file 1, this is "changed". - Otherwise it is "deleted". */ - prefix = (next.inserted > 0) ? "!" : "-"; - - print_1_line (prefix, file0[i]); - } - } - - outfile.print("--- "); - print_number_range (',', first1, last1); - outfile.println(" ----"); - - if (inserts != 0) { - Diff.change next = hunk; - - for (int i = first1; i <= last1; i++) { - /* Skip past changes that apply (in file 1) - only to lines before line I. */ - - while (next != null && next.line1 + next.inserted <= i) - next = next.link; - - /* Compute the marking for line I. */ - - String prefix = " "; - if (next != null && next.line1 <= i) - /* The change NEXT covers this line. - If lines were deleted here in file 0, this is "changed". - Otherwise it is "inserted". */ - prefix = (next.deleted > 0) ? "!" : "+"; - - print_1_line (prefix, file1[i]); - } - } - } - } - - /** Prints an edit script in context diff format. This and its - 'unified' variation is used for source code patches. - */ - public static class UnifiedPrint extends ContextPrint { - - public UnifiedPrint(Object[] a,Object[] b, Writer w) { - super(a,b,w); - } - - public void print_header(String filea,String fileb) { - print_context_label ("---", new File(filea), filea); - print_context_label ("+++", new File(fileb), fileb); - } - - private void print_number_range (int a, int b) { - //translate_range (file, a, b, &trans_a, &trans_b); - - /* Note: we can have B < A in the case of a range of no lines. - In this case, we should print the line number before the range, - which is B. */ - if (b < a) - outfile.print(b + ",0"); - else - super.print_number_range(',',a,b); - } - - protected void print_hunk(Diff.change hunk) { - /* Determine range of line numbers involved in each file. */ - analyze_hunk (hunk); - - if (deletes == 0 && inserts == 0) - return; - - /* Include a context's width before and after. */ - - first0 = Math.max(first0 - context, 0); - first1 = Math.max(first1 - context, 0); - last0 = Math.min(last0 + context, file0.length - 1); - last1 = Math.min(last1 + context, file1.length - 1); - - - - outfile.print("@@ -"); - print_number_range (first0, last0); - outfile.print(" +"); - print_number_range (first1, last1); - outfile.print(" @@"); - - /* If we looked for and found a function this is part of, - include its name in the header of the diff section. */ - print_function(file0,first0); - - outfile.println(); - - Diff.change next = hunk; - int i = first0; - int j = first1; - - while (i <= last0 || j <= last1) { - - /* If the line isn't a difference, output the context from file 0. */ - - if (next == null || i < next.line0) { - outfile.print(' '); - print_1_line ("", file0[i++]); - j++; - } - else { - /* For each difference, first output the deleted part. */ - - int k = next.deleted; - while (k-- > 0) { - outfile.print('-'); - print_1_line ("", file0[i++]); - } - - /* Then output the inserted part. */ - - k = next.inserted; - while (k-- > 0) { - outfile.print('+'); - print_1_line ("", file1[j++]); - } - - /* We're done with this hunk, so on to the next! */ - - next = next.link; - } - } - } - } - - - /** Read a text file into an array of String. This provides basic diff - functionality. A more advanced diff utility will use specialized - objects to represent the text lines, with options to, for example, - convert sequences of whitespace to a single space for comparison - purposes. - */ - static String[] slurp(String file) throws IOException { - BufferedReader rdr = new BufferedReader(new FileReader(file)); - Vector<String> s = new Vector<String>(); - for (;;) { - String line = rdr.readLine(); - if (line == null) break; - s.addElement(line); - } - String[] a = new String[s.size()]; - s.copyInto(a); - return a; - } - - public static void main(String[] argv) throws IOException { - String filea = argv[argv.length - 2]; - String fileb = argv[argv.length - 1]; - String[] a = slurp(filea); - String[] b = slurp(fileb); - Diff d = new Diff(a,b); - char style = 'n'; - for (int i = 0; i < argv.length - 2; ++i) { - String f = argv[i]; - if (f.startsWith("-")) { - for (int j = 1; j < f.length(); ++j) { - switch (f.charAt(j)) { - case 'e': // Ed style - style = 'e'; break; - case 'c': // Context diff - style = 'c'; break; - case 'u': - style = 'u'; break; - } - } - } - } - boolean reverse = style == 'e'; - Diff.change script = d.diff_2(reverse); - if (script == null) - System.err.println("No differences"); - else { - Base p; - Writer w = new OutputStreamWriter(System.out); - switch (style) { - case 'e': - p = new EdPrint(a,b,w); break; - case 'c': - p = new ContextPrint(a,b,w); break; - case 'u': - p = new UnifiedPrint(a,b,w); break; - default: - p = new NormalPrint(a,b,w); - } - p.print_header(filea,fileb); - p.print_script(script); - } - } - - public static void doDiff(String[] argv, Writer w) throws IOException { - String filea = argv[argv.length - 2]; - String fileb = argv[argv.length - 1]; - String[] a = slurp(filea); - String[] b = slurp(fileb); - Diff d = new Diff(a,b); - char style = 'n'; - for (int i = 0; i < argv.length - 2; ++i) { - String f = argv[i]; - if (f.startsWith("-")) { - for (int j = 1; j < f.length(); ++j) { - switch (f.charAt(j)) { - case 'e': // Ed style - style = 'e'; break; - case 'c': // Context diff - style = 'c'; break; - case 'u': - style = 'u'; break; - } - } - } - } - boolean reverse = style == 'e'; - Diff.change script = d.diff_2(reverse); - if (script == null) - w.write("No differences\n"); - else { - Base p; - switch (style) { - case 'e': - p = new EdPrint(a,b,w); break; - case 'c': - p = new ContextPrint(a,b,w); break; - case 'u': - p = new UnifiedPrint(a,b,w); break; - default: - p = new NormalPrint(a,b,w); - } - p.print_header(filea,fileb); - p.print_script(script); - } - } - -} diff --git a/src/partest/scala/tools/partest/nest/FileManager.scala b/src/partest/scala/tools/partest/nest/FileManager.scala index a32c56e973..23546c6511 100644 --- a/src/partest/scala/tools/partest/nest/FileManager.scala +++ b/src/partest/scala/tools/partest/nest/FileManager.scala @@ -17,34 +17,31 @@ import scala.collection.mutable trait FileUtil { /** - * Compares two files using a Java implementation of the GNU diff - * available at http://www.bmsi.com/java/#diff. + * Compares two files using difflib to produce a unified diff. * * @param f1 the first file to be compared * @param f2 the second file to be compared - * @return the text difference between the compared files + * @return the unified diff of the compared files or the empty string if they're equal */ def compareFiles(f1: File, f2: File): String = { - val diffWriter = new StringWriter - val args = Array(f1.getAbsolutePath(), f2.getAbsolutePath()) - - DiffPrint.doDiff(args, diffWriter) - val res = diffWriter.toString - if (res startsWith "No") "" else res + compareContents(io.Source.fromFile(f1).getLines.toSeq, io.Source.fromFile(f2).getLines.toSeq, f1.getName, f2.getName) } - def compareContents(lines1: Seq[String], lines2: Seq[String]): String = { - val xs1 = lines1.toArray[AnyRef] - val xs2 = lines2.toArray[AnyRef] - - val diff = new Diff(xs1, xs2) - val change = diff.diff_2(false) - val writer = new StringWriter - val p = new DiffPrint.NormalPrint(xs1, xs2, writer) - - p.print_script(change) - val res = writer.toString - if (res startsWith "No ") "" - else res + + /** + * Compares two lists of lines using difflib to produce a unified diff. + * + * @param origLines the first seq of lines to be compared + * @param newLines the second seq of lines to be compared + * @param origName file name to be used in unified diff for `origLines` + * @param newName file name to be used in unified diff for `newLines` + * @return the unified diff of the `origLines` and `newLines` or the empty string if they're equal + */ + def compareContents(origLines: Seq[String], newLines: Seq[String], origName: String = "a", newName: String = "b"): String = { + import collection.JavaConverters._ + + val diff = difflib.DiffUtils.diff(origLines.asJava, newLines.asJava) + if (diff.getDeltas.isEmpty) "" + else difflib.DiffUtils.generateUnifiedDiff(origName, newName, origLines.asJava, diff, 1).asScala.mkString("\n") } } object FileUtil extends FileUtil { } diff --git a/src/partest/scala/tools/partest/nest/PathSettings.scala b/src/partest/scala/tools/partest/nest/PathSettings.scala index 02651c527b..7c005b4f61 100644 --- a/src/partest/scala/tools/partest/nest/PathSettings.scala +++ b/src/partest/scala/tools/partest/nest/PathSettings.scala @@ -71,6 +71,9 @@ object PathSettings { findJar(buildPackLibDir.files ++ srcLibDir.files, "scalacheck") getOrElse { sys.error("No scalacheck jar found in '%s' or '%s'".format(buildPackLibDir, srcLibDir)) } + + lazy val diffUtils: File = + findJar(buildPackLibDir.files, "diffutils") getOrElse sys.error(s"No diffutils.jar found in '$buildPackLibDir'.") } class PathSettings() { diff --git a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala index 05cae7b238..9780e82cd9 100644 --- a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala +++ b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala @@ -51,10 +51,13 @@ class ReflectiveRunner { new ConsoleFileManager // this is a workaround for https://issues.scala-lang.org/browse/SI-5433 - // when that bug is fixed, the addition of PathSettings.srcCodeLib can be removed + // when that bug is fixed, the addition of PathSettings.srcCodeLib can be removed // we hack into the classloader that will become parent classloader for scalac // this way we ensure that reflective macro lookup will pick correct Code.lift - val sepUrls = PathSettings.srcCodeLib.toURI.toURL :: fileManager.latestUrls + // it's also used to inject diffutils into the classpath when running partest from the test/partest script + val srcCodeLibAndDiff = List(PathSettings.srcCodeLib, PathSettings.diffUtils) + val sepUrls = srcCodeLibAndDiff.map(_.toURI.toURL) ::: fileManager.latestUrls + // this seems to be the core classloader that determines which classes can be found when running partest from the test/partest script val sepLoader = new URLClassLoader(sepUrls.toArray, null) if (isPartestDebug) diff --git a/src/partest/scala/tools/partest/nest/RunnerManager.scala b/src/partest/scala/tools/partest/nest/RunnerManager.scala index 8f28277a6c..a63a81c47b 100644 --- a/src/partest/scala/tools/partest/nest/RunnerManager.scala +++ b/src/partest/scala/tools/partest/nest/RunnerManager.scala @@ -78,6 +78,7 @@ class RunnerManager(kind: String, val fileManager: FileManager, params: TestRunP val compileMgr = new CompileManager(fileManager) fileManager.CLASSPATH += File.pathSeparator + PathSettings.scalaCheck + fileManager.CLASSPATH += File.pathSeparator + PathSettings.diffUtils // needed to put diffutils on test/partest's classpath private def compareFiles(f1: File, f2: File): String = try fileManager.compareFiles(f1, f2) diff --git a/src/reflect/scala/reflect/internal/ClassfileConstants.scala b/src/reflect/scala/reflect/internal/ClassfileConstants.scala index 78f7438429..faf61e5205 100644 --- a/src/reflect/scala/reflect/internal/ClassfileConstants.scala +++ b/src/reflect/scala/reflect/internal/ClassfileConstants.scala @@ -72,6 +72,9 @@ object ClassfileConstants { final val CONSTANT_METHODREF = 10 final val CONSTANT_INTFMETHODREF = 11 final val CONSTANT_NAMEANDTYPE = 12 + final val CONSTANT_METHODHANDLE = 15 + final val CONSTANT_METHODTYPE = 16 + final val CONSTANT_INVOKEDYNAMIC = 18 // tags describing the type of a literal in attribute values final val BYTE_TAG = 'B' @@ -306,7 +309,7 @@ object ClassfileConstants { final val invokespecial = 0xb7 final val invokestatic = 0xb8 final val invokeinterface = 0xb9 - final val xxxunusedxxxx = 0xba + final val invokedynamic = 0xba final val new_ = 0xbb final val newarray = 0xbc diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 55954196f6..5392daf674 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -244,8 +244,8 @@ trait Definitions extends api.StandardDefinitions { (sym eq NoSymbol) || sym.isConstructor || sym.isPrivateLocal - || isUniversalMember(sym) ) + def isUnimportableUnlessRenamed(sym: Symbol) = isUnimportable(sym) || isUniversalMember(sym) def isImportable(sym: Symbol) = !isUnimportable(sym) /** Is this type equivalent to Any, AnyVal, or AnyRef? */ @@ -973,7 +973,7 @@ trait Definitions extends api.StandardDefinitions { getMemberIfDefined(owner, name) orElse { if (phase.flatClasses && name.isTypeName && !owner.isPackageObjectOrClass) { val pkg = owner.owner - val flatname = nme.flattenedName(owner.name, name) + val flatname = tpnme.flattenedName(owner.name, name) getMember(pkg, flatname) } else fatalMissingSymbol(owner, name) diff --git a/src/reflect/scala/reflect/internal/Names.scala b/src/reflect/scala/reflect/internal/Names.scala index f8598dca7a..b8141d25f5 100644 --- a/src/reflect/scala/reflect/internal/Names.scala +++ b/src/reflect/scala/reflect/internal/Names.scala @@ -177,6 +177,12 @@ trait Names extends api.Names { /** @return the hash value of this name */ final override def hashCode(): Int = index + /** @return true if the string value of this name is equal + * to the string value of the given name or String. + */ + def string_==(that: Name): Boolean = (that ne null) && (toString == that.toString) + def string_==(that: String): Boolean = (that ne null) && (toString == that) + /**** * This has been quite useful to find places where people are comparing * a TermName and a TypeName, or a Name and a String. @@ -210,7 +216,7 @@ trait Names extends api.Names { /** @return the index of first occurrence of char c in this name, length if not found */ final def pos(c: Char): Int = pos(c, 0) - /** @return the index of first occurrence of char c in this name, length if not found */ + /** @return the index of first occurrence of s in this name, length if not found */ final def pos(s: String): Int = pos(s, 0) /** Returns the index of the first occurrence of character c in @@ -319,15 +325,18 @@ trait Names extends api.Names { final def endsWith(char: Char): Boolean = len > 0 && endChar == char final def endsWith(name: String): Boolean = endsWith(newTermName(name)) - def indexOf(ch: Char) = { - val idx = pos(ch) - if (idx == length) -1 else idx - } - def indexOf(ch: Char, fromIndex: Int) = { - val idx = pos(ch, fromIndex) - if (idx == length) -1 else idx - } - def lastIndexOf(ch: Char) = lastPos(ch) + /** Rewrite the confusing failure indication via result == length to + * the normal failure indication via result == -1. + */ + private def fixIndexOf(idx: Int): Int = if (idx == length) -1 else idx + + def indexOf(ch: Char) = fixIndexOf(pos(ch)) + def indexOf(ch: Char, fromIndex: Int) = fixIndexOf(pos(ch, fromIndex)) + def indexOf(s: String) = fixIndexOf(pos(s)) + + /** The lastPos methods already return -1 on failure. */ + def lastIndexOf(ch: Char): Int = lastPos(ch) + def lastIndexOf(s: String): Int = toString lastIndexOf s /** Replace all occurrences of `from` by `to` in * name; result is always a term name. @@ -392,9 +401,24 @@ trait Names extends api.Names { * reap the benefits because an (unused) $outer pointer so it is not single-field. */ final class NameOps[T <: Name](name: T) { - def stripSuffix(suffix: Name): T = if (name endsWith suffix) dropRight(suffix.length) else name - def dropRight(n: Int): T = name.subName(0, name.length - n).asInstanceOf[T] - def drop(n: Int): T = name.subName(n, name.length).asInstanceOf[T] + import NameTransformer._ + def stripSuffix(suffix: String): T = stripSuffix(suffix: TermName) + def stripSuffix(suffix: Name): T = if (name endsWith suffix) dropRight(suffix.length) else name + def take(n: Int): T = name.subName(0, n).asInstanceOf[T] + def drop(n: Int): T = name.subName(n, name.length).asInstanceOf[T] + def dropRight(n: Int): T = name.subName(0, name.length - n).asInstanceOf[T] + def dropLocal: TermName = name.toTermName stripSuffix LOCAL_SUFFIX_STRING + def dropSetter: TermName = name.toTermName stripSuffix SETTER_SUFFIX_STRING + def dropModule: T = this stripSuffix MODULE_SUFFIX_STRING + def localName: TermName = getterName append LOCAL_SUFFIX_STRING + def setterName: TermName = getterName append SETTER_SUFFIX_STRING + def getterName: TermName = dropTraitSetterSeparator.dropSetter.dropLocal + + private def dropTraitSetterSeparator: TermName = + name indexOf TRAIT_SETTER_SEPARATOR_STRING match { + case -1 => name.toTermName + case idx => name.toTermName drop idx drop TRAIT_SETTER_SEPARATOR_STRING.length + } } implicit val NameTag = ClassTag[Name](classOf[Name]) diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index 28837c4ae8..e1ef6d6365 100644 --- a/src/reflect/scala/reflect/internal/Printers.scala +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -29,18 +29,19 @@ trait Printers extends api.Printers { self: SymbolTable => def quotedName(name: String): String = quotedName(newTermName(name), decode = false) private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = { - val sym = tree.symbol - if (sym.name.toString == nme.ERROR.toString) { - "<" + quotedName(name, decoded) + ": error>" - } else if (sym != null && sym != NoSymbol) { - val prefix = if (sym.isMixinConstructor) "/*%s*/".format(quotedName(sym.owner.name, decoded)) else "" - var suffix = "" - if (settings.uniqid.value) suffix += ("#" + sym.id) - if (settings.Yshowsymkinds.value) suffix += ("#" + sym.abbreviatedKindString) - prefix + quotedName(tree.symbol.decodedName) + suffix - } else { - quotedName(name, decoded) - } + val sym = tree.symbol + def qname = quotedName(name.dropLocal, decoded) + def qowner = quotedName(sym.owner.name.dropLocal, decoded) + def qsymbol = quotedName(sym.nameString) + + if (sym.name.toTermName == nme.ERROR) + s"<$qname: error>" + else if (sym == null || sym == NoSymbol) + qname + else if (sym.isMixinConstructor) + s"/*$qowner*/$qsymbol" + else + qsymbol } def decodedSymName(tree: Tree, name: Name) = symNameInternal(tree, name, decoded = true) @@ -546,7 +547,7 @@ trait Printers extends api.Printers { self: SymbolTable => print("pendingSuperCall") case tree: Tree => val hasSymbolField = tree.hasSymbolField && tree.symbol != NoSymbol - val isError = hasSymbolField && tree.symbol.name.toString == nme.ERROR.toString + val isError = hasSymbolField && (tree.symbol.name string_== nme.ERROR) printProduct( tree, preamble = _ => { diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index a894bd649c..4fd86aa8b1 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -86,8 +86,12 @@ trait StdNames { def flattenedName(segments: Name*): NameType = compactify(segments mkString NAME_JOIN_STRING) - val MODULE_SUFFIX_STRING: String = NameTransformer.MODULE_SUFFIX_STRING - val NAME_JOIN_STRING: String = NameTransformer.NAME_JOIN_STRING + val NAME_JOIN_STRING: String = NameTransformer.NAME_JOIN_STRING + val MODULE_SUFFIX_STRING: String = NameTransformer.MODULE_SUFFIX_STRING + val SETTER_SUFFIX_STRING: String = NameTransformer.SETTER_SUFFIX_STRING + val LOCAL_SUFFIX_STRING: String = NameTransformer.LOCAL_SUFFIX_STRING + val TRAIT_SETTER_SEPARATOR_STRING: String = NameTransformer.TRAIT_SETTER_SEPARATOR_STRING + val SINGLETON_SUFFIX: String = ".type" val ANON_CLASS_NAME: NameType = "$anon" @@ -265,7 +269,7 @@ trait StdNames { val BITMAP_PREFIX = "bitmap$" val CHECK_IF_REFUTABLE_STRING = "check$ifrefutable$" val DEFAULT_GETTER_STRING = "$default$" - val DEFAULT_GETTER_INIT_STRING = "$lessinit$greater" // CONSTRUCTOR.encoded, less is more + val DEFAULT_GETTER_INIT_STRING = NameTransformer.encode("<init>") + DEFAULT_GETTER_STRING val DO_WHILE_PREFIX = "doWhile$" val EVIDENCE_PARAM_PREFIX = "evidence$" val EXCEPTION_RESULT_PREFIX = "exceptionResult" @@ -275,7 +279,6 @@ trait StdNames { val PROTECTED_PREFIX = "protected$" val PROTECTED_SET_PREFIX = PROTECTED_PREFIX + "set" val SUPER_PREFIX_STRING = "super$" - val TRAIT_SETTER_SEPARATOR_STRING = "$_setter_$" val WHILE_PREFIX = "while$" // Compiler internal names @@ -284,10 +287,8 @@ trait StdNames { val DEFAULT_CASE: NameType = "defaultCase$" val EQEQ_LOCAL_VAR: NameType = "eqEqTemp$" val FAKE_LOCAL_THIS: NameType = "this$" - val INITIALIZER: NameType = CONSTRUCTOR // Is this buying us something? val LAZY_LOCAL: NameType = "$lzy" val LAZY_SLOW_SUFFIX: NameType = "$lzycompute" - val LOCAL_SUFFIX_STRING = " " val UNIVERSE_BUILD_PREFIX: NameType = "$u.build." val UNIVERSE_PREFIX: NameType = "$u." val UNIVERSE_SHORT: NameType = "$u" @@ -301,21 +302,16 @@ trait StdNames { val MIXIN_CONSTRUCTOR: NameType = "$init$" val MODULE_INSTANCE_FIELD: NameType = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$" val OUTER: NameType = "$outer" - val OUTER_LOCAL: NameType = OUTER + LOCAL_SUFFIX_STRING // "$outer ", note the space + val OUTER_LOCAL: NameType = OUTER.localName val OUTER_SYNTH: NameType = "<outer>" // emitted by virtual pattern matcher, replaced by outer accessor in explicitouter val ROOTPKG: NameType = "_root_" val SELECTOR_DUMMY: NameType = "<unapply-selector>" val SELF: NameType = "$this" - val SETTER_SUFFIX: NameType = encode("_=") + val SETTER_SUFFIX: NameType = NameTransformer.SETTER_SUFFIX_STRING val SPECIALIZED_INSTANCE: NameType = "specInstance$" val STAR: NameType = "*" val THIS: NameType = "_$this" - @deprecated("Use SPECIALIZED_SUFFIX", "2.10.0") - def SPECIALIZED_SUFFIX_STRING = SPECIALIZED_SUFFIX.toString - @deprecated("Use SPECIALIZED_SUFFIX", "2.10.0") - def SPECIALIZED_SUFFIX_NAME: TermName = SPECIALIZED_SUFFIX.toTermName - def isConstructorName(name: Name) = name == CONSTRUCTOR || name == MIXIN_CONSTRUCTOR def isExceptionResultName(name: Name) = name startsWith EXCEPTION_RESULT_PREFIX def isImplClassName(name: Name) = name endsWith IMPL_CLASS_SUFFIX @@ -345,31 +341,52 @@ trait StdNames { name.endChar == '=' && name.startChar != '=' && isOperatorPart(name.startChar) } - /** The expanded name of `name` relative to this class `base` with given `separator` - */ - def expandedName(name: TermName, base: Symbol, separator: String = EXPAND_SEPARATOR_STRING): TermName = + private def expandedNameInternal(name: TermName, base: Symbol, separator: String): TermName = newTermNameCached(base.fullName('$') + separator + name) + /** The expanded name of `name` relative to this class `base` + */ + def expandedName(name: TermName, base: Symbol) = expandedNameInternal(name, base, EXPAND_SEPARATOR_STRING) + /** The expanded setter name of `name` relative to this class `base` */ - def expandedSetterName(name: TermName, base: Symbol): TermName = - expandedName(name, base, separator = TRAIT_SETTER_SEPARATOR_STRING) + def expandedSetterName(name: TermName, base: Symbol) = expandedNameInternal(name, base, TRAIT_SETTER_SEPARATOR_STRING) - /** If `name` is an expandedName name, the original name. - * Otherwise `name` itself. - */ - def originalName(name: Name): Name = { - var i = name.length - while (i >= 2 && !(name.charAt(i - 1) == '$' && name.charAt(i - 2) == '$')) i -= 1 - if (i >= 2) { - while (i >= 3 && name.charAt(i - 3) == '$') i -= 1 - name.subName(i, name.length) - } else name + /** If `name` is an expandedName name, the original (unexpanded) name. + * Otherwise `name` itself. + * Look backward from the end of the string for "$$", and take the + * part of the string after that; but if the string is "$$$" or longer, + * be sure to retain the extra dollars. + */ + def unexpandedName(name: Name): Name = name lastIndexOf "$$" match { + case -1 => name + case idx0 => + // Sketchville - We've found $$ but if it's part of $$$ or $$$$ + // or something we need to keep the bonus dollars, so e.g. foo$$$outer + // has an original name of $outer. + var idx = idx0 + while (idx > 0 && name.charAt(idx - 1) == '$') + idx -= 1 + name drop idx + 2 } + @deprecated("Use SPECIALIZED_SUFFIX", "2.10.0") + def SPECIALIZED_SUFFIX_STRING = SPECIALIZED_SUFFIX.toString + @deprecated("Use SPECIALIZED_SUFFIX", "2.10.0") + def SPECIALIZED_SUFFIX_NAME: TermName = SPECIALIZED_SUFFIX.toTermName + + @deprecated("Use unexpandedName", "2.11.0") def originalName(name: Name): Name = unexpandedName(name) + @deprecated("Use Name#dropModule", "2.11.0") def stripModuleSuffix(name: Name): Name = name.dropModule + @deprecated("Use Name#dropLocal", "2.11.0") def localToGetter(name: TermName): TermName = name.dropLocal + @deprecated("Use Name#dropLocal", "2.11.0") def dropLocalSuffix(name: Name): TermName = name.dropLocal + @deprecated("Use Name#localName", "2.11.0") def getterToLocal(name: TermName): TermName = name.localName + @deprecated("Use Name#setterName", "2.11.0") def getterToSetter(name: TermName): TermName = name.setterName + @deprecated("Use Name#getterName", "2.11.0") def getterName(name: TermName): TermName = name.getterName + @deprecated("Use Name#getterName", "2.11.0") def setterToGetter(name: TermName): TermName = name.getterName + def unspecializedName(name: Name): Name = ( if (name endsWith SPECIALIZED_SUFFIX) - name.subName(0, name.lastIndexOf('m') - 1) + name.subName(0, name.lastIndexOf('m') - 1) else name ) @@ -394,39 +411,23 @@ trait StdNames { } else (name, "", "") - def getterName(name: TermName): TermName = if (isLocalName(name)) localToGetter(name) else name - def getterToLocal(name: TermName): TermName = name append LOCAL_SUFFIX_STRING - def getterToSetter(name: TermName): TermName = name append SETTER_SUFFIX - def localToGetter(name: TermName): TermName = name dropRight LOCAL_SUFFIX_STRING.length - - def dropLocalSuffix(name: Name): Name = if (name endsWith ' ') name dropRight 1 else name - - def setterToGetter(name: TermName): TermName = { - val p = name.pos(TRAIT_SETTER_SEPARATOR_STRING) - if (p < name.length) - setterToGetter(name drop (p + TRAIT_SETTER_SEPARATOR_STRING.length)) - else - name.subName(0, name.length - SETTER_SUFFIX.length) - } - // Nominally, name$default$N, encoded for <init> - def defaultGetterName(name: Name, pos: Int): TermName = { - val prefix = if (isConstructorName(name)) DEFAULT_GETTER_INIT_STRING else name - newTermName(prefix + DEFAULT_GETTER_STRING + pos) - } + def defaultGetterName(name: Name, pos: Int): TermName = ( + if (isConstructorName(name)) + DEFAULT_GETTER_INIT_STRING + pos + else + name + DEFAULT_GETTER_STRING + pos + ) // Nominally, name from name$default$N, CONSTRUCTOR for <init> - def defaultGetterToMethod(name: Name): TermName = { - val p = name.pos(DEFAULT_GETTER_STRING) - if (p < name.length) { - val q = name.toTermName.subName(0, p) - // i.e., if (q.decoded == CONSTRUCTOR.toString) CONSTRUCTOR else q - if (q.toString == DEFAULT_GETTER_INIT_STRING) CONSTRUCTOR else q - } else name.toTermName - } - - def stripModuleSuffix(name: Name): Name = ( - if (isModuleName(name)) name dropRight MODULE_SUFFIX_STRING.length else name + def defaultGetterToMethod(name: Name): TermName = ( + if (name startsWith DEFAULT_GETTER_INIT_STRING) + nme.CONSTRUCTOR + else name indexOf DEFAULT_GETTER_STRING match { + case -1 => name.toTermName + case idx => name.toTermName take idx + } ) + def localDummyName(clazz: Symbol): TermName = newTermName(LOCALDUMMY_PREFIX + clazz.name + ">") def superName(name: Name): TermName = newTermName(SUPER_PREFIX_STRING + name) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index c881de7830..ad7a0d45eb 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -761,16 +761,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => def compileTimeOnlyMessage = getAnnotation(CompileTimeOnlyAttr) flatMap (_ stringArg 0) /** Is this symbol an accessor method for outer? */ - final def isOuterAccessor = { - hasFlag(STABLE | ARTIFACT) && - originalName == nme.OUTER - } + final def isOuterAccessor = hasFlag(STABLE | ARTIFACT) && (unexpandedName == nme.OUTER) /** Is this symbol an accessor method for outer? */ - final def isOuterField = { - hasFlag(ARTIFACT) && - originalName == nme.OUTER_LOCAL - } + final def isOuterField = isArtifact && (unexpandedName == nme.OUTER_LOCAL) /** Does this symbol denote a stable value? */ def isStable = false @@ -995,10 +989,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => // ------ name attribute -------------------------------------------------------------- - /** If this symbol has an expanded name, its original name, otherwise its name itself. - * @see expandName + @deprecated("Use unexpandedName", "2.11.0") def originalName: Name = unexpandedName + + /** If this symbol has an expanded name, its original (unexpanded) name, + * otherwise the name itself. */ - def originalName: Name = nme.originalName(nme.dropLocalSuffix(name)) + def unexpandedName: Name = nme.unexpandedName(name) /** The name of the symbol before decoding, e.g. `\$eq\$eq` instead of `==`. */ @@ -1006,7 +1002,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** The decoded name of the symbol, e.g. `==` instead of `\$eq\$eq`. */ - def decodedName: String = nme.dropLocalSuffix(name).decode + def decodedName: String = name.decode private def addModuleSuffix(n: Name): Name = if (needsModuleSuffix) n append nme.MODULE_SUFFIX_STRING else n @@ -1025,7 +1021,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => ) /** These should be moved somewhere like JavaPlatform. */ - def javaSimpleName: Name = addModuleSuffix(nme.dropLocalSuffix(simpleName)) + def javaSimpleName: Name = addModuleSuffix(simpleName.dropLocal) def javaBinaryName: Name = addModuleSuffix(fullNameInternal('/')) def javaClassName: String = addModuleSuffix(fullNameInternal('.')).toString @@ -1046,7 +1042,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => else ((effectiveOwner.enclClass.fullNameAsName(separator) append separator): Name) append name ) - def fullNameAsName(separator: Char): Name = nme.dropLocalSuffix(fullNameInternal(separator)) + def fullNameAsName(separator: Char): Name = fullNameInternal(separator).dropLocal /** The encoded full path name of this symbol, where outer names and inner names * are separated by periods. @@ -1823,7 +1819,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => // // The slightly more principled approach of using the paramss of the // primary constructor leads to cycles in, for example, pos/t5084.scala. - val primaryNames = constrParamAccessors.map(acc => nme.dropLocalSuffix(acc.name)) + val primaryNames = constrParamAccessors map (_.name.dropLocal) caseFieldAccessorsUnsorted.sortBy { acc => primaryNames indexWhere { orig => (acc.name == orig) || (acc.name startsWith (orig append "$")) @@ -1842,7 +1838,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** The symbol accessed by this accessor function, but with given owner type. */ final def accessed(ownerTp: Type): Symbol = { assert(hasAccessorFlag, this) - ownerTp decl nme.getterToLocal(getterName.toTermName) + ownerTp decl localName } /** The module corresponding to this module class (note that this @@ -2199,22 +2195,23 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** The getter of this value or setter definition in class `base`, or NoSymbol if * none exists. */ - final def getter(base: Symbol): Symbol = base.info.decl(getterName) filter (_.hasAccessorFlag) + final def getter(base: Symbol): Symbol = + base.info decl getterName filter (_.hasAccessorFlag) - def getterName: TermName = ( - if (isSetter) nme.setterToGetter(name.toTermName) - else if (nme.isLocalName(name)) nme.localToGetter(name.toTermName) - else name.toTermName - ) + def getterName: TermName = name.getterName + def setterName: TermName = name.setterName + def localName: TermName = name.localName /** The setter of this value or getter definition, or NoSymbol if none exists */ - final def setter(base: Symbol): Symbol = setter(base, hasExpandedName = false) + final def setter(base: Symbol, hasExpandedName: Boolean = needsExpandedSetterName): Symbol = + base.info decl setterNameInBase(base, hasExpandedName) filter (_.hasAccessorFlag) - final def setter(base: Symbol, hasExpandedName: Boolean): Symbol = { - var sname = nme.getterToSetter(nme.getterName(name.toTermName)) - if (hasExpandedName) sname = nme.expandedSetterName(sname, base) - base.info.decl(sname) filter (_.hasAccessorFlag) - } + def needsExpandedSetterName = ( + if (isMethod) hasStableFlag && !isLazy + else hasNoFlags(LAZY | MUTABLE) + ) + def setterNameInBase(base: Symbol, expanded: Boolean): TermName = + if (expanded) nme.expandedSetterName(setterName, base) else setterName /** If this is a derived value class, return its unbox method * or NoSymbol if it does not exist. @@ -2391,12 +2388,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => * If settings.uniqid, adds id. * If settings.Yshowsymkinds, adds abbreviated symbol kind. */ - def nameString: String = ( - if (!settings.uniqid.value && !settings.Yshowsymkinds.value) "" + originalName.decode - else if (settings.uniqid.value && !settings.Yshowsymkinds.value) originalName.decode + "#" + id - else if (!settings.uniqid.value && settings.Yshowsymkinds.value) originalName.decode + "#" + abbreviatedKindString - else originalName.decode + "#" + id + "#" + abbreviatedKindString - ) + def nameString: String = { + val name_s = if (settings.debug.value) "" + unexpandedName else unexpandedName.dropLocal.decode + val id_s = if (settings.uniqid.value) "#" + id else "" + val kind_s = if (settings.Yshowsymkinds.value) "#" + abbreviatedKindString else "" + + name_s + id_s + kind_s + } def fullNameString: String = { def recur(sym: Symbol): String = { @@ -2623,20 +2621,32 @@ trait Symbols extends api.Symbols { self: SymbolTable => } /** change name by appending $$<fully-qualified-name-of-class `base`> - * Do the same for any accessed symbols or setters/getters + * Do the same for any accessed symbols or setters/getters. + * If the accessor to be renamed is overriding a base symbol, enter + * a cloned symbol with the original name but without ACCESSOR flag. */ override def expandName(base: Symbol) { - if (!hasFlag(EXPANDEDNAME)) { - setFlag(EXPANDEDNAME) - if (hasAccessorFlag && !isDeferred) { - accessed.expandName(base) - } - else if (hasGetter) { - getter(owner).expandName(base) - setter(owner).expandName(base) - } - name = nme.expandedName(name.toTermName, base) + def expand(sym: Symbol) { + if ((sym eq NoSymbol) || (sym hasFlag EXPANDEDNAME)) () // skip + else sym setFlag EXPANDEDNAME setName nme.expandedName(sym.name.toTermName, base) + } + def cloneAndExpand(accessor: Symbol) { + val clone = accessor.cloneSymbol(accessor.owner, (accessor.flags | ARTIFACT) & ~ACCESSOR) + expand(accessor) + log(s"Expanded overriding accessor to $accessor, but cloned $clone to preserve override") + accessor.owner.info.decls enter clone + } + def expandAccessor(accessor: Symbol) { + if (accessor.isOverridingSymbol) cloneAndExpand(accessor) else expand(accessor) + } + if (hasAccessorFlag && !isDeferred) { + expand(accessed) + } + else if (hasGetter) { + expandAccessor(getter(owner)) + expandAccessor(setter(owner)) } + expand(this) } } implicit val TermSymbolTag = ClassTag[TermSymbol](classOf[TermSymbol]) @@ -3065,7 +3075,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (Statistics.canEnable) Statistics.incCounter(nameCount) if (needsFlatClasses) { if (flatname eq null) - flatname = nme.flattenedName(rawowner.name, rawname).toTypeName + flatname = tpnme.flattenedName(rawowner.name, rawname) flatname } diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index e96fcc90df..b1f58814c7 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -188,7 +188,7 @@ abstract class TreeInfo { def isVariableOrGetter(tree: Tree) = { def sym = tree.symbol def isVar = sym.isVariable - def isGetter = mayBeVarGetter(sym) && sym.owner.info.member(nme.getterToSetter(sym.name.toTermName)) != NoSymbol + def isGetter = mayBeVarGetter(sym) && sym.owner.info.member(sym.setterName) != NoSymbol tree match { case Ident(_) => isVar diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index c00337e578..410bc738e2 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -242,6 +242,9 @@ trait Trees extends api.Trees { self: SymbolTable => trait NameTree extends Tree with NameTreeApi { def name: Name + def getterName: TermName = name.getterName + def setterName: TermName = name.setterName + def localName: TermName = name.localName } trait RefTree extends SymTree with NameTree with RefTreeApi { diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index d7ff4faa5d..611873efc0 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -2422,20 +2422,28 @@ trait Types override def kind = "TypeRef" } + // No longer defined as anonymous classes in `object TypeRef` to avoid an unnecessary outer pointer. + private final class AliasArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args) with AliasTypeRef + private final class AbstractArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args) with AbstractTypeRef + private final class ClassArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args) with ClassTypeRef + private final class AliasNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym) with AliasTypeRef + private final class AbstractNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym) with AbstractTypeRef + private final class ClassNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym) with ClassTypeRef + object TypeRef extends TypeRefExtractor { def apply(pre: Type, sym: Symbol, args: List[Type]): Type = unique({ if (args.nonEmpty) { - if (sym.isAliasType) new ArgsTypeRef(pre, sym, args) with AliasTypeRef - else if (sym.isAbstractType) new ArgsTypeRef(pre, sym, args) with AbstractTypeRef - else new ArgsTypeRef(pre, sym, args) with ClassTypeRef + if (sym.isAliasType) new AliasArgsTypeRef(pre, sym, args) + else if (sym.isAbstractType) new AbstractArgsTypeRef(pre, sym, args) + else new ClassArgsTypeRef(pre, sym, args) } else { - if (sym.isAliasType) new NoArgsTypeRef(pre, sym) with AliasTypeRef - else if (sym.isAbstractType) new NoArgsTypeRef(pre, sym) with AbstractTypeRef + if (sym.isAliasType) new AliasNoArgsTypeRef(pre, sym) + else if (sym.isAbstractType) new AbstractNoArgsTypeRef(pre, sym) else if (sym.isRefinementClass) new RefinementTypeRef(pre, sym) else if (sym.isPackageClass) new PackageTypeRef(pre, sym) else if (sym.isModuleClass) new ModuleTypeRef(pre, sym) - else new NoArgsTypeRef(pre, sym) with ClassTypeRef + else new ClassNoArgsTypeRef(pre, sym) } }) } @@ -3752,10 +3760,11 @@ trait Types if (tp.isTrivial) tp else if (tp.prefix.typeSymbol isNonBottomSubClass owner) { val widened = tp match { - case _: ConstantType => tp // Java enum constants: don't widen to the enum type! - case _ => tp.widen // C.X.type widens to C.this.X.type, otherwise `tp asSeenFrom (pre, C)` has no effect. + case _: ConstantType => tp // Java enum constants: don't widen to the enum type! + case _ => tp.widen // C.X.type widens to C.this.X.type, otherwise `tp asSeenFrom (pre, C)` has no effect. } - widened asSeenFrom (pre, tp.typeSymbol.owner) + val memType = widened asSeenFrom (pre, tp.typeSymbol.owner) + if (tp eq widened) memType else memType.narrow } else loop(tp.prefix) memberType tp.typeSymbol diff --git a/src/reflect/scala/reflect/internal/util/Collections.scala b/src/reflect/scala/reflect/internal/util/Collections.scala index 63b7f73386..d6fca9d186 100644 --- a/src/reflect/scala/reflect/internal/util/Collections.scala +++ b/src/reflect/scala/reflect/internal/util/Collections.scala @@ -76,6 +76,19 @@ trait Collections { lb.toList } + final def distinctBy[A, B](xs: List[A])(f: A => B): List[A] = { + val buf = new ListBuffer[A] + val seen = mutable.Set[B]() + xs foreach { x => + val y = f(x) + if (!seen(y)) { + buf += x + seen += y + } + } + buf.toList + } + @tailrec final def flattensToEmpty(xss: Seq[Seq[_]]): Boolean = { xss.isEmpty || xss.head.isEmpty && flattensToEmpty(xss.tail) } diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 2e38caaf5d..e58e89a4b1 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -242,10 +242,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni def reflectField(field: TermSymbol): FieldMirror = { checkMemberOf(field, symbol) if ((field.isMethod && !field.isAccessor) || field.isModule) ErrorNotField(field) - val name = - if (field.isGetter) nme.getterToLocal(field.name) - else if (field.isSetter) nme.getterToLocal(nme.setterToGetter(field.name)) - else field.name + val name = if (field.isAccessor) field.localName else field.name val field1 = (field.owner.info decl name).asTerm try fieldToJava(field1) catch { @@ -313,7 +310,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni // the "symbol == Any_getClass || symbol == Object_getClass" test doesn't cut it // because both AnyVal and its primitive descendants define their own getClass methods - private def isGetClass(meth: MethodSymbol) = meth.name.toString == "getClass" && meth.paramss.flatten.isEmpty + private def isGetClass(meth: MethodSymbol) = (meth.name string_== "getClass") && meth.paramss.flatten.isEmpty private def isStringConcat(meth: MethodSymbol) = meth == String_+ || (meth.owner.isPrimitiveValueClass && meth.returnType =:= StringClass.toType) lazy val bytecodelessMethodOwners = Set[Symbol](AnyClass, AnyValClass, AnyRefClass, ObjectClass, ArrayClass) ++ ScalaPrimitiveValueClasses lazy val bytecodefulObjectMethods = Set[Symbol](Object_clone, Object_equals, Object_finalize, Object_hashCode, Object_toString, @@ -844,9 +841,10 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * that start with the given name are searched instead. */ private def lookup(clazz: Symbol, jname: String): Symbol = { - def approximateMatch(sym: Symbol, jstr: String): Boolean = - (sym.name.toString == jstr) || - sym.isPrivate && nme.expandedName(sym.name.toTermName, sym.owner).toString == jstr + def approximateMatch(sym: Symbol, jstr: String): Boolean = ( + (sym.name string_== jstr) + || sym.isPrivate && (nme.expandedName(sym.name.toTermName, sym.owner) string_== jstr) + ) clazz.info.decl(newTermName(jname)) orElse { (clazz.info.decls.iterator filter (approximateMatch(_, jname))).toList match { @@ -1008,7 +1006,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def typeParamToScala1(jparam: jTypeVariable[_ <: GenericDeclaration]): TypeSymbol = { val owner = genericDeclarationToScala(jparam.getGenericDeclaration) owner.info match { - case PolyType(tparams, _) => tparams.find(_.name.toString == jparam.getName).get.asType + case PolyType(tparams, _) => tparams.find(_.name string_== jparam.getName).get.asType } } @@ -1202,7 +1200,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni */ def fieldToJava(fld: TermSymbol): jField = fieldCache.toJava(fld) { val jclazz = classToJava(fld.owner.asClass) - val jname = nme.dropLocalSuffix(fld.name).toString + val jname = fld.name.dropLocal.toString try jclazz getDeclaredField jname catch { case ex: NoSuchFieldException => jclazz getDeclaredField expandedName(fld) @@ -1215,7 +1213,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni def methodToJava(meth: MethodSymbol): jMethod = methodCache.toJava(meth) { val jclazz = classToJava(meth.owner.asClass) val paramClasses = transformedType(meth).paramTypes map typeToJavaClass - val jname = nme.dropLocalSuffix(meth.name).toString + val jname = meth.name.dropLocal.toString try jclazz getDeclaredMethod (jname, paramClasses: _*) catch { case ex: NoSuchMethodException => diff --git a/src/reflect/scala/reflect/runtime/JavaUniverse.scala b/src/reflect/scala/reflect/runtime/JavaUniverse.scala index a130013398..4d90afcdc3 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverse.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverse.scala @@ -14,7 +14,7 @@ class JavaUniverse extends internal.SymbolTable with ReflectSetup with runtime.S lazy val settings = new Settings private val isLogging = sys.props contains "scala.debug.reflect" - def log(msg: => AnyRef): Unit = if (isLogging) Console.err.println("[reflect] " + msg) + def log(msg: => AnyRef): Unit = if (isLogging) Console.err.println("[reflect] " + msg) type TreeCopier = InternalTreeCopierOps def newStrictTreeCopier: TreeCopier = new StrictTreeCopier diff --git a/src/scaladoc/scala/tools/nsc/doc/base/comment/Body.scala b/src/scaladoc/scala/tools/nsc/doc/base/comment/Body.scala index 2a07547de2..ac5fec80b3 100755 --- a/src/scaladoc/scala/tools/nsc/doc/base/comment/Body.scala +++ b/src/scaladoc/scala/tools/nsc/doc/base/comment/Body.scala @@ -73,16 +73,20 @@ object EntityLink { def unapply(el: EntityLink): Option[(Inline, LinkTo)] = Some((el.title, el.link)) } final case class HtmlTag(data: String) extends Inline { - def canClose(open: HtmlTag) = { - open.data.stripPrefix("<") == data.stripPrefix("</") + private val Pattern = """(?ms)\A<(/?)(.*?)[\s>].*\z""".r + private val (isEnd, tagName) = data match { + case Pattern(s1, s2) => + (! s1.isEmpty, Some(s2.toLowerCase)) + case _ => + (false, None) } - def close = { - if (data.indexOf("</") == -1) - Some(HtmlTag("</" + data.stripPrefix("<"))) - else - None + def canClose(open: HtmlTag) = { + isEnd && tagName == open.tagName } + + private val TagsNotToClose = Set("br", "img") + def close = tagName collect { case name if !TagsNotToClose(name) => HtmlTag(s"</$name>") } } /** The summary of a comment, usually its first sentence. There must be exactly one summary per body. */ diff --git a/src/scaladoc/scala/tools/nsc/doc/html/SyntaxHigh.scala b/src/scaladoc/scala/tools/nsc/doc/html/SyntaxHigh.scala index 5781e680dd..348ea97c5b 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/SyntaxHigh.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/SyntaxHigh.scala @@ -6,6 +6,7 @@ package scala.tools.nsc.doc.html import scala.xml.NodeSeq +import scala.annotation.tailrec /** Highlight the syntax of Scala code appearing in a `{{{` wiki block * (see method `HtmlPage.blockToHtml`). @@ -209,9 +210,9 @@ private[html] object SyntaxHigh { out.toString } - def parse(pre: String, i: Int): Int = { + @tailrec def parse(pre: String, i: Int): Unit = { out append pre - if (i == buf.length) return i + if (i == buf.length) return buf(i) match { case '\n' => parse("\n", i+1) @@ -277,7 +278,6 @@ private[html] object SyntaxHigh { } else parse(buf(i).toChar.toString, i+1) } - i } parse("", 0) |