diff options
Diffstat (limited to 'src')
39 files changed, 620 insertions, 350 deletions
diff --git a/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala b/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala index 8a3c531ff0..caf6ad14cf 100644 --- a/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala @@ -33,7 +33,7 @@ class OfflineCompilerCommand(arguments: List[String], settings: FscSettings) ext } else { // Otherwise we're on the server and will use it to absolutize the paths. - settings.absolutize(currentDir.value) + settings.absolutize() } } diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 54402f0903..def1198dae 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -111,7 +111,7 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => rhs = EmptyTree ) } - val lvdefs = evdefs collect { case vdef: ValDef => copyValDef(vdef)(mods = Modifiers(PRESUPER)) } + val lvdefs = evdefs collect { case vdef: ValDef => copyValDef(vdef)(mods = vdef.mods | PRESUPER) } val constrs = { if (constrMods hasFlag TRAIT) { diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 33db4ee2d5..c508e14343 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -980,11 +980,8 @@ self => /** Assumed (provisionally) to be TermNames. */ def ident(skipIt: Boolean): Name = - if (isIdent) { - val name = in.name.encode - in.nextToken() - name - } else { + if (isIdent) rawIdent().encode + else { syntaxErrorOrIncomplete(expectedMsg(IDENTIFIER), skipIt) nme.ERROR } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 4f564c5d0b..046122d83b 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -113,6 +113,11 @@ trait Scanners extends ScannersCommon { cbuf.append(c) } + /** Determines whether this scanner should emit identifier deprecation warnings, + * e.g. when seeing `macro` or `then`, which are planned to become keywords in future versions of Scala. + */ + protected def emitIdentifierDeprecationWarnings = true + /** Clear buffer and set name and token */ private def finishNamed(idtoken: Int = IDENTIFIER) { name = newTermName(cbuf.toString) @@ -122,7 +127,7 @@ trait Scanners extends ScannersCommon { val idx = name.start - kwOffset if (idx >= 0 && idx < kwArray.length) { token = kwArray(idx) - if (token == IDENTIFIER && allowIdent != name) + if (token == IDENTIFIER && allowIdent != name && emitIdentifierDeprecationWarnings) deprecationWarning(name+" is now a reserved word; usage as an identifier is deprecated") } } @@ -607,7 +612,10 @@ trait Scanners extends ScannersCommon { if (ch == '`') { nextChar() finishNamed(BACKQUOTED_IDENT) - if (name.length == 0) syntaxError("empty quoted identifier") + if (name.length == 0) + syntaxError("empty quoted identifier") + else if (name == nme.WILDCARD) + syntaxError("wildcard invalid as backquoted identifier") } else syntaxError("unclosed quoted identifier") } @@ -1488,6 +1496,10 @@ trait Scanners extends ScannersCommon { def improves(patches1: List[BracePatch]): Boolean = imbalanceMeasure > new ParensAnalyzer(unit, patches1).imbalanceMeasure + // don't emit deprecation warnings about identifiers like `macro` or `then` + // when skimming through the source file trying to heal braces + override def emitIdentifierDeprecationWarnings = false + override def error(offset: Int, msg: String) {} } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index f94055f666..ac8ab493e0 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -258,7 +258,7 @@ abstract class TreeBuilder { /** Create tree representing a while loop */ def makeWhile(lname: TermName, cond: Tree, body: Tree): Tree = { - val continu = atPos(o2p(body.pos.endOrPoint)) { Apply(Ident(lname), Nil) } + val continu = atPos(o2p(body.pos pointOrElse wrappingPos(List(cond, body)).pos.endOrPoint)) { Apply(Ident(lname), Nil) } val rhs = If(cond, Block(List(body), continu), Literal(Constant())) LabelDef(lname, Nil, rhs) } diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index fd2b11898c..44d7a1929b 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -1164,34 +1164,28 @@ abstract class GenICode extends SubComponent { resCtx } - private def adapt(from: TypeKind, to: TypeKind, ctx: Context, pos: Position): Unit = { - if (!(from <:< to) && !(from == NullReference && to == NothingReference)) { - to match { - case UNIT => - ctx.bb.emit(DROP(from), pos) - debuglog("Dropped an " + from); - - case _ => - debugassert(from != UNIT, "Can't convert from UNIT to " + to + " at: " + pos) - assert(!from.isReferenceType && !to.isReferenceType, - "type error: can't convert from " + from + " to " + to +" in unit " + unit.source + " at " + pos) - - ctx.bb.emit(CALL_PRIMITIVE(Conversion(from, to)), pos) - } - } else if (from == NothingReference) { - ctx.bb.emit(THROW(ThrowableClass)) - ctx.bb.enterIgnoreMode - } else if (from == NullReference) { - ctx.bb.emit(DROP(from)) - ctx.bb.emit(CONSTANT(Constant(null))) + private def adapt(from: TypeKind, to: TypeKind, ctx: Context, pos: Position) { + // An awful lot of bugs explode here - let's leave ourselves more clues. + // A typical example is an overloaded type assigned after typer. + log(s"GenICode#adapt($from, $to, $ctx, $pos)") + + val conforms = (from <:< to) || (from == NullReference && to == NothingReference) + def coerce(from: TypeKind, to: TypeKind) = ctx.bb.emit(CALL_PRIMITIVE(Conversion(from, to)), pos) + def checkAssertions() { + def msg = s"Can't convert from $from to $to in unit ${unit.source} at $pos" + debugassert(from != UNIT, msg) + assert(!from.isReferenceType && !to.isReferenceType, msg) } - else if (from == ThrowableReference && !(ThrowableClass.tpe <:< to.toType)) { - log("Inserted check-cast on throwable to " + to + " at " + pos) - ctx.bb.emit(CHECK_CAST(to)) + if (conforms) from match { + case NothingReference => ctx.bb.emit(THROW(ThrowableClass)) ; ctx.bb.enterIgnoreMode + case NullReference => ctx.bb.emit(Seq(DROP(from), CONSTANT(Constant(null)))) + case ThrowableReference if !(ThrowableClass.tpe <:< to.toType) => ctx.bb.emit(CHECK_CAST(to)) // downcast throwables + case BYTE | SHORT | CHAR | INT if to == LONG => coerce(INT, LONG) // widen subrange types + case _ => () } - else (from, to) match { - case (BYTE, LONG) | (SHORT, LONG) | (CHAR, LONG) | (INT, LONG) => ctx.bb.emit(CALL_PRIMITIVE(Conversion(INT, LONG))) - case _ => () + else to match { + case UNIT => ctx.bb.emit(DROP(from), pos) // value discarding + case _ => checkAssertions() ; coerce(from, to) // other primitive coercions } } @@ -1907,18 +1901,8 @@ abstract class GenICode extends SubComponent { var handlerCount = 0 - override def toString(): String = { - val buf = new StringBuilder() - buf.append("\tpackage: ").append(packg).append('\n') - buf.append("\tclazz: ").append(clazz).append('\n') - buf.append("\tmethod: ").append(method).append('\n') - buf.append("\tbb: ").append(bb).append('\n') - buf.append("\tlabels: ").append(labels).append('\n') - buf.append("\texception handlers: ").append(handlers).append('\n') - buf.append("\tcleanups: ").append(cleanups).append('\n') - buf.append("\tscope: ").append(scope).append('\n') - buf.toString() - } + override def toString = + s"package $packg { class $clazz { def $method { bb=$bb } } }" def loadException(ctx: Context, exh: ExceptionHandler, pos: Position) = { debuglog("Emitting LOAD_EXCEPTION for class: " + exh.loadExceptionClass) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index d185ed0c34..0abbe44b02 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -1018,7 +1018,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { if (needsAnnotation) { val c = Constant(RemoteExceptionClass.tpe) val arg = Literal(c) setType c.tpe - meth.addAnnotation(ThrowsClass, arg) + meth.addAnnotation(appliedType(ThrowsClass, c.tpe), arg) } } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index fe0020e074..598965b982 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -888,7 +888,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with if (needsAnnotation) { val c = Constant(RemoteExceptionClass.tpe) val arg = Literal(c) setType c.tpe - meth.addAnnotation(ThrowsClass, arg) + meth.addAnnotation(appliedType(ThrowsClass, c.tpe), arg) } } diff --git a/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala index 10e2f23142..4ee6daf73e 100755 --- a/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala @@ -20,7 +20,7 @@ object IndexModelFactory { /* Owner template ordering */ implicit def orderingSet = math.Ordering.String.on { x: MemberEntity => x.name.toLowerCase } /* symbol name ordering */ - implicit def orderingMap = math.Ordering.String.on { x: String => x.toLowerCase } + implicit def orderingMap = math.Ordering.String def addMember(d: MemberEntity) = { val firstLetter = { diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala index 10f972452f..5fd5b41625 100644 --- a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala @@ -37,6 +37,9 @@ class JLineReader(_completion: => Completion) extends InteractiveReader { } class JLineConsoleReader extends ConsoleReader with ConsoleReaderHelper { + if ((history: History) ne NoHistory) + this setHistory history + // working around protected/trait/java insufficiencies. def goBack(num: Int): Unit = back(num) def readOneKey(prompt: String) = { @@ -51,8 +54,6 @@ class JLineReader(_completion: => Completion) extends InteractiveReader { // A hook for running code after the repl is done initializing. lazy val postInit: Unit = { this setBellEnabled false - if ((history: History) ne NoHistory) - this setHistory history if (completion ne NoCompletion) { val argCompletor: ArgumentCompleter = diff --git a/src/compiler/scala/tools/nsc/settings/FscSettings.scala b/src/compiler/scala/tools/nsc/settings/FscSettings.scala index 06ebc20d3e..5c852ae07c 100644 --- a/src/compiler/scala/tools/nsc/settings/FscSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/FscSettings.scala @@ -38,14 +38,25 @@ class FscSettings(error: String => Unit) extends Settings(error) { private def holdsPath = Set[Settings#Setting]( d, dependencyfile, pluginsDir, Ygenjavap ) + + override def processArguments(arguments: List[String], processAll: Boolean): (Boolean, List[String]) = { + val (r, args) = super.processArguments(arguments, processAll) + // we need to ensure the files specified with relative locations are absolutized based on the currentDir + (r, args map {a => absolutizePath(a)}) + } + + /** + * Take an individual path and if it's not absolute turns it into an absolute path based on currentDir. + * If it's already absolute then it's left alone. + */ + private[this] def absolutizePath(p: String) = (Path(currentDir.value) resolve Path(p)).normalize.path - /** All user set settings rewritten with absolute paths. */ - def absolutize(root: Path) { - def rewrite(p: String) = (root resolve Path(p)).normalize.path + /** All user set settings rewritten with absolute paths based on currentDir */ + def absolutize() { userSetSettings foreach { - case p: OutputSetting => p.outputDirs setSingleOutput AbstractFile.getDirectory(rewrite(p.value)) - case p: PathSetting => p.value = ClassPath.map(p.value, rewrite) - case p: StringSetting => if (holdsPath(p)) p.value = rewrite(p.value) + case p: OutputSetting => p.outputDirs setSingleOutput AbstractFile.getDirectory(absolutizePath(p.value)) + case p: PathSetting => p.value = ClassPath.map(p.value, absolutizePath) + case p: StringSetting => if (holdsPath(p)) p.value = absolutizePath(p.value) case _ => () } } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index a708a262e7..4b1d3c34f3 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -1043,7 +1043,13 @@ abstract class ClassfileParser { val nClasses = in.nextChar for (n <- 0 until nClasses) { val cls = pool.getClassSymbol(in.nextChar.toInt) - sym.addAnnotation(definitions.ThrowsClass, Literal(Constant(cls.tpe))) + val tp = if (cls.isMonomorphicType) cls.tpe else { + debuglog(s"Encountered polymorphic exception `${cls.fullName}` while parsing class file.") + // in case we encounter polymorphic exception the best we can do is to convert that type to + // monomorphic one by introducing existientals, see SI-7009 for details + typer.packSymbols(cls.typeParams, cls.tpe) + } + sym.addAnnotation(appliedType(definitions.ThrowsClass, tp), Literal(Constant(tp))) } } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 25b7813646..e8b0cd2696 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -71,8 +71,8 @@ abstract class Pickler extends SubComponent { if (!t.isDef && t.hasSymbol && t.symbol.isTermMacro) { unit.error(t.pos, t.symbol.typeParams.length match { case 0 => "macro has not been expanded" - case 1 => "type parameter not specified" - case _ => "type parameters not specified" + case 1 => "this type parameter must be specified" + case _ => "these type parameters must be specified" }) return } diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index 44510ab0c2..7a0b034fd0 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -450,19 +450,31 @@ abstract class CleanUp extends Transform with ast.TreeDSL { * is a value type (int et al.) in which case it must cast to the boxed version * because invoke only returns object and erasure made sure the result is * expected to be an AnyRef. */ - val t: Tree = ad.symbol.tpe match { - case MethodType(mparams, resType) => - assert(params.length == mparams.length, mparams) - - typedPos { - val sym = currentOwner.newValue(mkTerm("qual"), ad.pos) setInfo qual0.tpe - qual = REF(sym) + val t: Tree = { + val (mparams, resType) = ad.symbol.tpe match { + case MethodType(mparams, resType) => + assert(params.length == mparams.length, ((params, mparams))) + (mparams, resType) + case tpe @ OverloadedType(pre, alts) => + unit.warning(ad.pos, s"Overloaded type reached the backend! This is a bug in scalac.\n Symbol: ${ad.symbol}\n Overloads: $tpe\n Arguments: " + ad.args.map(_.tpe)) + alts filter (_.paramss.flatten.size == params.length) map (_.tpe) match { + case mt @ MethodType(mparams, resType) :: Nil => + unit.warning(NoPosition, "Only one overload has the right arity, proceeding with overload " + mt) + (mparams, resType) + case _ => + unit.error(ad.pos, "Cannot resolve overload.") + (Nil, NoType) + } + } + typedPos { + val sym = currentOwner.newValue(mkTerm("qual"), ad.pos) setInfo qual0.tpe + qual = REF(sym) - BLOCK( - VAL(sym) === qual0, - callAsReflective(mparams map (_.tpe), resType) - ) - } + BLOCK( + VAL(sym) === qual0, + callAsReflective(mparams map (_.tpe), resType) + ) + } } /* For testing purposes, the dynamic application's condition diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index 39e16c3f58..589aa43ac2 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -75,7 +75,19 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { val candidates = extensionNames(imeth) map (companionInfo.decl(_)) filter (_.exists) val matching = candidates filter (alt => normalize(alt.tpe, imeth.owner) matches imeth.tpe) assert(matching.nonEmpty, - s"no extension method found for $imeth:${imeth.tpe} among ${candidates.map(c => c.name+":"+c.tpe).toList} / ${extensionNames(imeth).toList}") + sm"""|no extension method found for: + | + | $imeth:${imeth.tpe} + | + | Candidates: + | + | ${candidates.map(c => c.name+":"+c.tpe).mkString("\n")} + | + | Candidates (signatures normalized): + | + | ${candidates.map(c => c.name+":"+normalize(c.tpe, imeth.owner)).mkString("\n")} + | + | Eligible Names: ${extensionNames(imeth).mkString(",")}"""") matching.head } @@ -94,11 +106,18 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { */ private def normalize(stpe: Type, clazz: Symbol): Type = stpe match { case PolyType(tparams, restpe) => - GenPolyType(tparams dropRight clazz.typeParams.length, normalize(restpe.substSym(tparams takeRight clazz.typeParams.length, clazz.typeParams), clazz)) + // Split the type parameters of the extension method into two groups, + // corresponding the to class and method type parameters. + val numClassParams = clazz.typeParams.length + val methTParams = tparams dropRight numClassParams + val classTParams = tparams takeRight numClassParams + + GenPolyType(methTParams, + normalize(restpe.substSym(classTParams, clazz.typeParams), clazz)) case MethodType(List(thiz), restpe) if thiz.name == nme.SELF => - restpe - case MethodType(tparams, restpe) => - MethodType(tparams.drop(1), restpe) + restpe.substituteTypes(thiz :: Nil, clazz.thisType :: Nil) + case MethodType(thiz :: params, restpe) => + MethodType(params, restpe) case _ => stpe } @@ -128,7 +147,11 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { MethodType(List(thisParam), restpe) } val GenPolyType(tparams, restpe) = origInfo cloneInfo extensionMeth - GenPolyType(tparams ::: newTypeParams, transform(restpe) substSym (clazz.typeParams, newTypeParams)) + val selfParamSingletonType = singleType(currentOwner.companionModule.thisType, thisParam) + GenPolyType( + tparams ::: newTypeParams, + transform(restpe) substThisAndSym (clazz, selfParamSingletonType, clazz.typeParams, newTypeParams) + ) } private def allParams(tpe: Type): List[Symbol] = tpe match { @@ -162,7 +185,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { origMeth.defString, extensionMeth.defString)) // extensionMeth.defStringSeenAs(origInfo - def thisParamRef = gen.mkAttributedIdent(extensionMeth.info.params.head setPos extensionMeth.pos) + def thisParamRef = gen.mkAttributedStableRef(extensionMeth.info.params.head setPos extensionMeth.pos) val GenPolyType(extensionTpeParams, extensionMono) = extensionMeth.info val origTpeParams = (tparams map (_.symbol)) ::: currentOwner.typeParams val extensionBody = rhs diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 3cd943aa74..c9c68d080d 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -867,7 +867,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { rhs match { case Block(List(assign), returnTree) => val Assign(moduleVarRef, _) = assign - val cond = Apply(Select(moduleVarRef, nme.eq), List(NULL)) + val cond = Apply(Select(moduleVarRef, Object_eq), List(NULL)) mkFastPathBody(clazz, moduleSym, cond, List(assign), List(NULL), returnTree, attrThis, args) case _ => abort("Invalid getter " + rhs + " for module in class " + clazz) diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 64051b56ec..232148676c 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -178,6 +178,14 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case class Overload(sym: Symbol, env: TypeEnv) { override def toString = "specialized overload " + sym + " in " + env + def matchesSym(other: Symbol) = sym.tpe =:= other.tpe + def matchesEnv(env1: TypeEnv) = TypeEnv.includes(env, env1) + } + private def newOverload(method: Symbol, specializedMethod: Symbol, env: TypeEnv) = { + assert(!specializedMethod.isOverloaded, specializedMethod.defString) + val om = Overload(specializedMethod, env) + overloads(method) ::= om + om } /** Just to mark uncheckable */ @@ -289,10 +297,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } - /** Return the specialized overload of sym in the given env, if any. */ - def overload(sym: Symbol, env: TypeEnv) = - overloads(sym).find(ov => TypeEnv.includes(ov.env, env)) - /** Return the specialized name of 'sym' in the given environment. It * guarantees the same result regardless of the map order by sorting * type variables alphabetically. @@ -628,7 +632,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { info(om) = if (original.isDeferred) Forward(original) else Implementation(original) typeEnv(om) = env ++ typeEnv(m) // add the environment for any method tparams - overloads(specMember) ::= Overload(om, typeEnv(om)) + newOverload(specMember, om, typeEnv(om)) enterMember(om) } @@ -835,7 +839,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { debuglog("%s expands to %s in %s".format(sym, specMember.name.decode, pp(env))) info(specMember) = NormalizedMember(sym) - overloads(sym) ::= Overload(specMember, env) + newOverload(sym, specMember, env) owner.info.decls.enter(specMember) specMember } @@ -877,9 +881,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (wasSpec.nonEmpty) debuglog("specialized overload for %s in %s".format(specMember, pp(typeEnv(specMember)))) - overloads(sym) ::= Overload(specMember, spec) + newOverload(sym, specMember, spec) info(specMember) = SpecialOverload(sym, typeEnv(specMember)) - specMember } @@ -994,7 +997,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { SpecialOverride(impl) } ) - overloads(overriding) ::= Overload(om, env) + newOverload(overriding, om, env) ifDebug(afterSpecialize(assert( overridden.owner.info.decl(om.name) != NoSymbol, "Could not find " + om.name + " in " + overridden.owner.info.decls)) @@ -1476,54 +1479,41 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } transformTypeApply - case Select(qual, name) => - def transformSelect = { - qual match { - case _: Super if illegalSpecializedInheritance(currentClass) => - val pos = tree.pos - debuglog(pos.source.file.name+":"+pos.line+": not specializing call to super inside illegal specialized inheritance class.") - debuglog(pos.lineContent) - tree - case _ => + case Select(Super(_, _), _) if illegalSpecializedInheritance(currentClass) => + val pos = tree.pos + debuglog(pos.source.file.name+":"+pos.line+": not specializing call to super inside illegal specialized inheritance class.\n" + pos.lineContent) + tree + case Select(qual, name) if name != nme.CONSTRUCTOR && specializedTypeVars(symbol.info).nonEmpty => debuglog("specializing Select %s [tree.tpe: %s]".format(symbol.defString, tree.tpe)) - - //log("!!! select " + tree + " -> " + symbol.info + " specTypeVars: " + specializedTypeVars(symbol.info)) - if (specializedTypeVars(symbol.info).nonEmpty && name != nme.CONSTRUCTOR) { - // log("!!! unifying " + (symbol, symbol.tpe) + " and " + (tree, tree.tpe)) - val env = unify(symbol.tpe, tree.tpe, emptyEnv, false) - // log("!!! found env: " + env + "; overloads: " + overloads(symbol)) - if (!env.isEmpty) { - // debuglog("checking for rerouting: " + tree + " with sym.tpe: " + symbol.tpe + " tree.tpe: " + tree.tpe + " env: " + env) - val specMember = overload(symbol, env) - if (specMember.isDefined) { - localTyper.typedOperator(atPos(tree.pos)(Select(transform(qual), specMember.get.sym.name))) - } - else { - val qual1 = transform(qual) + val env = unify(symbol.tpe, tree.tpe, emptyEnv, false) + if (env.isEmpty) super.transform(tree) + else { + val qual1 = transform(qual) + def reselect(member: Symbol) = { + val newSelect = atPos(tree.pos)(Select(qual1, member)) + if (member.isMethod) localTyper typedOperator newSelect + else localTyper typed newSelect + } + overloads(symbol) find (_ matchesEnv env) match { + case Some(Overload(member, _)) => reselect(member) + case _ => val specMember = qual1.tpe.member(specializedName(symbol, env)).suchThat(_.tpe matches subst(env, symbol.tpe)) - if (specMember ne NoSymbol) { - val tree1 = atPos(tree.pos)(Select(qual1, specMember)) - if (specMember.isMethod) - localTyper.typedOperator(tree1) - else - localTyper.typed(tree1) - } else + if (specMember ne NoSymbol) + reselect(specMember) + else treeCopy.Select(tree, qual1, name) - } - } else - super.transform(tree) - } else overloads(symbol).find(_.sym.info =:= symbol.info) match { - case Some(specMember) => - val qual1 = transform(qual) - debuglog("** routing " + tree + " to " + specMember.sym.fullName + " tree: " + Select(qual1, specMember.sym)) - localTyper.typedOperator(atPos(tree.pos)(Select(qual1, specMember.sym))) - case None => - super.transform(tree) - } + } } + case Select(qual, _) => + overloads(symbol) find (_ matchesSym symbol) match { + case Some(Overload(member, _)) => + val newTree = Select(transform(qual), member) + debuglog(s"** routing $tree to ${member.fullName} tree: $newTree") + localTyper.typedOperator(atPos(tree.pos)(newTree)) + case None => + super.transform(tree) } - transformSelect case PackageDef(pid, stats) => tree.symbol.info // make sure specializations have been performed diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index c07177ec10..f338e390bb 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -627,7 +627,7 @@ abstract class UnCurry extends InfoTransform } } - case Assign(Select(_, _), _) => + case Assign(_: RefTree, _) => withNeedLift(true) { super.transform(tree) } case Assign(lhs, _) if lhs.symbol.owner != currentMethod || lhs.symbol.hasFlag(LAZY | ACCESSOR) => diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index dc367b11fd..2e5d61cc6b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -273,9 +273,6 @@ trait ContextErrors { def VolatileValueError(vdef: Tree) = issueNormalTypeError(vdef, "values cannot be volatile") - def FinalVolatileVarError(vdef: Tree) = - issueNormalTypeError(vdef, "final vars cannot be volatile") - def LocalVarUninitializedError(vdef: Tree) = issueNormalTypeError(vdef, "local variables must be initialized") diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index df753ba53c..0b46582cbf 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -317,15 +317,33 @@ abstract class Duplicators extends Analyzer { super.typed(tree, mode, pt) case Select(th @ This(_), sel) if (oldClassOwner ne null) && (th.symbol == oldClassOwner) => - // log("selection on this, no type ascription required") - // we use the symbol name instead of the tree name because the symbol may have been - // name mangled, rendering the tree name obsolete - // log(tree) - val t = super.typedPos(tree.pos, mode, pt) { - Select(This(newClassOwner), tree.symbol.name) - } - // log("typed to: " + t + "; tpe = " + t.tpe + "; " + inspectTpe(t.tpe)) - t + // We use the symbol name instead of the tree name because the symbol + // may have been name mangled, rendering the tree name obsolete. + // ...but you can't just do a Select on a name because if the symbol is + // overloaded, you will crash in the backend. + val memberByName = newClassOwner.thisType.member(tree.symbol.name) + def nameSelection = Select(This(newClassOwner), tree.symbol.name) + val newTree = ( + if (memberByName.isOverloaded) { + // Find the types of the overload alternatives as seen in the new class, + // and filter the list down to those which match the old type (after + // fixing the old type so it is seen as if from the new class.) + val typeInNewClass = fixType(oldClassOwner.info memberType tree.symbol) + val alts = memberByName.alternatives + val memberTypes = alts map (newClassOwner.info memberType _) + val memberString = memberByName.defString + alts zip memberTypes filter (_._2 =:= typeInNewClass) match { + case ((alt, tpe)) :: Nil => + log(s"Arrested overloaded type in Duplicators, narrowing to ${alt.defStringSeenAs(tpe)}\n Overload was: $memberString") + Select(This(newClassOwner), alt) + case _ => + log(s"Could not disambiguate $memberString in Duplicators. Attempting name-based selection, but this may not end well...") + nameSelection + } + } + else nameSelection + ) + super.typed(atPos(tree.pos)(newTree), mode, pt) case This(_) if (oldClassOwner ne null) && (tree.symbol == oldClassOwner) => // val tree1 = Typed(This(newClassOwner), TypeTree(fixType(tree.tpe.widen))) diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 2c2aa03d24..581f9f3bfa 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -1578,10 +1578,10 @@ trait Infer extends Checkable { } // Drop those that use a default; keep those that use vararg/tupling conversion. mtypes exists (t => - !t.typeSymbol.hasDefaultFlag && { - compareLengths(t.params, argtpes) < 0 || // tupling (*) - hasExactlyNumParams(t, argtpes.length) // same nb or vararg - } + !t.typeSymbol.hasDefaultFlag && ( + compareLengths(t.params, argtpes) < 0 // tupling (*) + || hasExactlyNumParams(t, argtpes.length) // same nb or vararg + ) ) // (*) more arguments than parameters, but still applicable: tupling conversion works. // todo: should not return "false" when paramTypes = (Unit) no argument is given @@ -1608,15 +1608,18 @@ trait Infer extends Checkable { case OverloadedType(pre, alts) => val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0 tryTwice { isSecondTry => - debuglog("infer method alt "+ tree.symbol +" with alternatives "+ - (alts map pre.memberType) +", argtpes = "+ argtpes +", pt = "+ pt) + debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt") - val applicable = resolveOverloadedMethod(argtpes, { - alts filter { alt => - inSilentMode(context)(isApplicable(undetparams, followApply(pre.memberType(alt)), argtpes, pt)) && - (!varArgsOnly || isVarArgsList(alt.tpe.params)) - } - }) + def varargsApplicableCheck(alt: Symbol) = !varArgsOnly || ( + isVarArgsList(alt.tpe.params) + && (argtpes.size >= alt.tpe.params.size) // must be checked now due to SI-5859 + ) + val applicable = resolveOverloadedMethod(argtpes, + alts filter (alt => + varargsApplicableCheck(alt) + && inSilentMode(context)(isApplicable(undetparams, followApply(pre memberType alt), argtpes, pt)) + ) + ) def improves(sym1: Symbol, sym2: Symbol) = { // util.trace("improve "+sym1+sym1.locationString+" on "+sym2+sym2.locationString) diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index b20a9ea626..0ba76643ca 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -452,7 +452,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { if (aparam.name != rparam.name && !rparam.isSynthetic) MacroImplParamNameMismatchError(aparam, rparam) if (isRepeated(aparam) ^ isRepeated(rparam)) MacroImplVarargMismatchError(aparam, rparam) val aparamtpe = aparam.tpe.dealias match { - case RefinedType(List(tpe), Scope(sym)) if tpe == MacroContextClass.tpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe + case RefinedType(List(tpe), Scope(sym)) if tpe =:= MacroContextClass.tpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe case tpe => tpe } checkMacroImplParamTypeMismatch(atpeToRtpe(aparamtpe), rparam) diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index be218fcb02..2340c78f8c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -268,26 +268,32 @@ trait NamesDefaults { self: Analyzer => * * For by-name parameters, create a value * x$n: () => T = () => arg + * + * For Ident(<unapply-selector>) arguments, no ValDef is created (SI-3353). */ - def argValDefs(args: List[Tree], paramTypes: List[Type], blockTyper: Typer): List[ValDef] = { + def argValDefs(args: List[Tree], paramTypes: List[Type], blockTyper: Typer): List[Option[ValDef]] = { val context = blockTyper.context - val symPs = map2(args, paramTypes)((arg, tpe) => { - val byName = isByNameParamType(tpe) - val repeated = isScalaRepeatedParamType(tpe) - val argTpe = ( - if (repeated) arg match { - case Typed(expr, Ident(tpnme.WILDCARD_STAR)) => expr.tpe - case _ => seqType(arg.tpe) - } - else arg.tpe - ).widen // have to widen or types inferred from literal defaults will be singletons - val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos) setInfo ( - if (byName) functionType(Nil, argTpe) else argTpe - ) - (context.scope.enter(s), byName, repeated) + val symPs = map2(args, paramTypes)((arg, tpe) => arg match { + case Ident(nme.SELECTOR_DUMMY) => + None // don't create a local ValDef if the argument is <unapply-selector> + case _ => + val byName = isByNameParamType(tpe) + val repeated = isScalaRepeatedParamType(tpe) + val argTpe = ( + if (repeated) arg match { + case Typed(expr, Ident(tpnme.WILDCARD_STAR)) => expr.tpe + case _ => seqType(arg.tpe) + } + else arg.tpe + ).widen // have to widen or types inferred from literal defaults will be singletons + val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos) setInfo ( + if (byName) functionType(Nil, argTpe) else argTpe + ) + Some((context.scope.enter(s), byName, repeated)) }) map2(symPs, args) { - case ((sym, byName, repeated), arg) => + case (None, _) => None + case (Some((sym, byName, repeated)), arg) => val body = if (byName) { val res = blockTyper.typed(Function(List(), arg)) @@ -303,7 +309,7 @@ trait NamesDefaults { self: Analyzer => blockTyper.typed(Apply(factory, List(resetLocalAttrs(arg)))) } else arg } - atPos(body.pos)(ValDef(sym, body).setType(NoType)) + Some(atPos(body.pos)(ValDef(sym, body).setType(NoType))) } } @@ -329,27 +335,29 @@ trait NamesDefaults { self: Analyzer => // ValDef's in the block), change the arguments to these local values. case Apply(expr, typedArgs) => // typedArgs: definition-site order - val formals = formalTypes(expr.tpe.paramTypes, typedArgs.length, false, false) + val formals = formalTypes(expr.tpe.paramTypes, typedArgs.length, removeByName = false, removeRepeated = false) // valDefs: call-site order val valDefs = argValDefs(reorderArgsInv(typedArgs, argPos), reorderArgsInv(formals, argPos), blockTyper) // refArgs: definition-site order again - val refArgs = map2(reorderArgs(valDefs, argPos), formals)((vDef, tpe) => { - val ref = gen.mkAttributedRef(vDef.symbol) - atPos(vDef.pos.focus) { - // for by-name parameters, the local value is a nullary function returning the argument - tpe.typeSymbol match { - case ByNameParamClass => Apply(ref, Nil) - case RepeatedParamClass => Typed(ref, Ident(tpnme.WILDCARD_STAR)) - case _ => ref + val refArgs = map3(reorderArgs(valDefs, argPos), formals, typedArgs)((vDefOpt, tpe, origArg) => vDefOpt match { + case None => origArg + case Some(vDef) => + val ref = gen.mkAttributedRef(vDef.symbol) + atPos(vDef.pos.focus) { + // for by-name parameters, the local value is a nullary function returning the argument + tpe.typeSymbol match { + case ByNameParamClass => Apply(ref, Nil) + case RepeatedParamClass => Typed(ref, Ident(tpnme.WILDCARD_STAR)) + case _ => ref + } } - } }) // cannot call blockTyper.typedBlock here, because the method expr might be partially applied only val res = blockTyper.doTypedApply(tree, expr, refArgs, mode, pt) res.setPos(res.pos.makeTransparent) - val block = Block(stats ::: valDefs, res).setType(res.tpe).setPos(tree.pos.makeTransparent) + val block = Block(stats ::: valDefs.flatten, res).setType(res.tpe).setPos(tree.pos.makeTransparent) context.namedApplyBlockInfo = Some((block, NamedApplyInfo(qual, targs, vargss :+ refArgs, blockTyper))) block diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 69bbab6e42..b0745b4c09 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -746,26 +746,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // reference the (i-1)th case accessor if it exists, otherwise the (i-1)th tuple component override protected def tupleSel(binder: Symbol)(i: Int): Tree = { import CODE._ - // caseFieldAccessors is messed up after typers (reversed, names mangled for non-public fields) - // TODO: figure out why... val accessors = binder.caseFieldAccessors - // luckily, the constrParamAccessors are still sorted properly, so sort the field-accessors using them - // (need to undo name-mangling, including the sneaky trailing whitespace) - val constrParamAccessors = binder.constrParamAccessors - - def indexInCPA(acc: Symbol) = - constrParamAccessors indexWhere { orig => - // patmatDebug("compare: "+ (orig, acc, orig.name, acc.name, (acc.name == orig.name), (acc.name startsWith (orig.name append "$")))) - val origName = orig.name.toString.trim - val accName = acc.name.toString.trim - (accName == origName) || (accName startsWith (origName + "$")) - } - - // patmatDebug("caseFieldAccessors: "+ (accessors, binder.caseFieldAccessors map indexInCPA)) - // patmatDebug("constrParamAccessors: "+ constrParamAccessors) - - val accessorsSorted = accessors sortBy indexInCPA - if (accessorsSorted isDefinedAt (i-1)) REF(binder) DOT accessorsSorted(i-1) + if (accessors isDefinedAt (i-1)) REF(binder) DOT accessors(i-1) else codegen.tupleSel(binder)(i) // this won't type check for case classes, as they do not inherit ProductN } @@ -1897,17 +1879,24 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL case object False extends Prop // symbols are propositions - case class Sym(val variable: Var, val const: Const) extends Prop { - private[this] val id = nextSymId + abstract case class Sym(val variable: Var, val const: Const) extends Prop { + private[this] val id = Sym.nextSymId + override def toString = variable +"="+ const +"#"+ id } - private def nextSymId = {_symId += 1; _symId}; private var _symId = 0 - + class UniqueSym(variable: Var, const: Const) extends Sym(variable, const) + object Sym { + private val uniques: util.HashSet[Sym] = new util.HashSet("uniques", 512) + def apply(variable: Var, const: Const): Sym = { + val newSym = new UniqueSym(variable, const) + (uniques findEntryOrUpdate newSym) + } + private def nextSymId = {_symId += 1; _symId}; private var _symId = 0 + } def /\(props: Iterable[Prop]) = if (props.isEmpty) True else props.reduceLeft(And(_, _)) def \/(props: Iterable[Prop]) = if (props.isEmpty) False else props.reduceLeft(Or(_, _)) - trait PropTraverser { def apply(x: Prop): Unit = x match { case And(a, b) => apply(a); apply(b) @@ -2063,6 +2052,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL import scala.collection.mutable.ArrayBuffer type FormulaBuilder = ArrayBuffer[Clause] def formulaBuilder = ArrayBuffer[Clause]() + def formulaBuilderSized(init: Int) = new ArrayBuffer[Clause](init) def addFormula(buff: FormulaBuilder, f: Formula): Unit = buff ++= f def toFormula(buff: FormulaBuilder): Formula = buff @@ -2167,7 +2157,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL class Lit(val sym: Sym, val pos: Boolean) { override def toString = if (!pos) "-"+ sym.toString else sym.toString override def equals(o: Any) = o match { - case o: Lit => (o.sym == sym) && (o.pos == pos) + case o: Lit => (o.sym eq sym) && (o.pos == pos) case _ => false } override def hashCode = sym.hashCode + pos.hashCode @@ -2216,13 +2206,18 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } private def withLit(res: Model, l: Lit): Model = if (res eq NoModel) NoModel else res + (l.sym -> l.pos) - private def dropUnit(f: Formula, unitLit: Lit) = { + private def dropUnit(f: Formula, unitLit: Lit): Formula = { val negated = -unitLit // drop entire clauses that are trivially true // (i.e., disjunctions that contain the literal we're making true in the returned model), // and simplify clauses by dropping the negation of the literal we're making true // (since False \/ X == X) - f.filterNot(_.contains(unitLit)).map(_ - negated) + val dropped = formulaBuilderSized(f.size) + for { + clause <- f + if !(clause contains unitLit) + } dropped += (clause - negated) + dropped } def findModelFor(f: Formula): Model = { diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index d1d70370d2..65ba8b1303 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -63,23 +63,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans super.transformInfo(sym, tp) } - val toJavaRepeatedParam = new TypeMap { - def apply(tp: Type) = tp match { - case TypeRef(pre, RepeatedParamClass, args) => - typeRef(pre, JavaRepeatedParamClass, args) - case _ => - mapOver(tp) - } - } - - val toScalaRepeatedParam = new TypeMap { - def apply(tp: Type): Type = tp match { - case TypeRef(pre, JavaRepeatedParamClass, args) => - typeRef(pre, RepeatedParamClass, args) - case _ => - mapOver(tp) - } - } + val toJavaRepeatedParam = new SubstSymMap(RepeatedParamClass -> JavaRepeatedParamClass) + val toScalaRepeatedParam = new SubstSymMap(JavaRepeatedParamClass -> RepeatedParamClass) def accessFlagsToString(sym: Symbol) = flagsToString( sym getFlag (PRIVATE | PROTECTED), @@ -159,27 +144,22 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // Override checking ------------------------------------------------------------ - def isJavaVarargsAncestor(clazz: Symbol) = ( - clazz.isClass - && clazz.isJavaDefined - && (clazz.info.nonPrivateDecls exists isJavaVarArgsMethod) - ) - /** Add bridges for vararg methods that extend Java vararg methods */ def addVarargBridges(clazz: Symbol): List[Tree] = { // This is quite expensive, so attempt to skip it completely. // Insist there at least be a java-defined ancestor which // defines a varargs method. TODO: Find a cheaper way to exclude. - if (clazz.thisType.baseClasses exists isJavaVarargsAncestor) { + if (inheritsJavaVarArgsMethod(clazz)) { log("Found java varargs ancestor in " + clazz.fullLocationString + ".") val self = clazz.thisType val bridges = new ListBuffer[Tree] def varargBridge(member: Symbol, bridgetpe: Type): Tree = { - log("Generating varargs bridge for " + member.fullLocationString + " of type " + bridgetpe) + log(s"Generating varargs bridge for ${member.fullLocationString} of type $bridgetpe") - val bridge = member.cloneSymbolImpl(clazz, member.flags | VBRIDGE) setPos clazz.pos + val newFlags = (member.flags | VBRIDGE | ARTIFACT) & ~PRIVATE + val bridge = member.cloneSymbolImpl(clazz, newFlags) setPos clazz.pos bridge.setInfo(bridgetpe.cloneInfo(bridge)) clazz.info.decls enter bridge @@ -192,26 +172,35 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans localTyper typed DefDef(bridge, body) } - // For all concrete non-private members that have a (Scala) repeated parameter: - // compute the corresponding method type `jtpe` with a Java repeated parameter + // For all concrete non-private members (but: see below) that have a (Scala) repeated + // parameter: compute the corresponding method type `jtpe` with a Java repeated parameter // if a method with type `jtpe` exists and that method is not a varargs bridge // then create a varargs bridge of type `jtpe` that forwards to the // member method with the Scala vararg type. - for (member <- clazz.info.nonPrivateMembers) { + // + // @PP: Can't call nonPrivateMembers because we will miss refinement members, + // which have been marked private. See SI-4729. + for (member <- nonTrivialMembers(clazz)) { + log(s"Considering $member for java varargs bridge in $clazz") if (!member.isDeferred && member.isMethod && hasRepeatedParam(member.info)) { val inherited = clazz.info.nonPrivateMemberAdmitting(member.name, VBRIDGE) + // Delaying calling memberType as long as possible if (inherited ne NoSymbol) { - val jtpe = toJavaRepeatedParam(self.memberType(member)) + val jtpe = toJavaRepeatedParam(self memberType member) // this is a bit tortuous: we look for non-private members or bridges // if we find a bridge everything is OK. If we find another member, // we need to create a bridge - if (inherited filter (sym => (self.memberType(sym) matches jtpe) && !(sym hasFlag VBRIDGE)) exists) + val inherited1 = inherited filter (sym => !(sym hasFlag VBRIDGE) && (self memberType sym matches jtpe)) + if (inherited1.exists) bridges += varargBridge(member, jtpe) } } } + if (bridges.size > 0) + log(s"Adding ${bridges.size} bridges for methods extending java varargs.") + bridges.toList } else Nil @@ -908,13 +897,15 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans * the type occurs itself at variance position given by `variance` */ def validateVariance(tp: Type, variance: Int): Unit = tp match { - case ErrorType => ; - case WildcardType => ; - case NoType => ; - case NoPrefix => ; - case ThisType(_) => ; - case ConstantType(_) => ; - // case DeBruijnIndex(_, _) => ; + case ErrorType => + case WildcardType => + case BoundedWildcardType(bounds) => + validateVariance(bounds, variance) + case NoType => + case NoPrefix => + case ThisType(_) => + case ConstantType(_) => + // case DeBruijnIndex(_, _) => case SingleType(pre, sym) => validateVariance(pre, variance) case TypeRef(pre, sym, args) => @@ -1484,8 +1475,11 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } private def isRepeatedParamArg(tree: Tree) = currentApplication match { case Apply(fn, args) => - !args.isEmpty && (args.last eq tree) && - fn.tpe.params.length == args.length && isRepeatedParamType(fn.tpe.params.last.tpe) + ( args.nonEmpty + && (args.last eq tree) + && (fn.tpe.params.length == args.length) + && isRepeatedParamType(fn.tpe.params.last.tpe) + ) case _ => false } diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index a907ab6c66..39f6f764e7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -78,14 +78,7 @@ trait SyntheticMethods extends ast.TreeDSL { else templ } - val originalAccessors = clazz.caseFieldAccessors - // private ones will have been renamed -- make sure they are entered - // in the original order. - def accessors = clazz.caseFieldAccessors sortBy { acc => - originalAccessors indexWhere { orig => - (acc.name == orig.name) || (acc.name startsWith (orig.name append "$")) - } - } + def accessors = clazz.caseFieldAccessors val arity = accessors.size // If this is ProductN[T1, T2, ...], accessorLub is the lub of T1, T2, ..., . // !!! Hidden behind -Xexperimental due to bummer type inference bugs. diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index 48a5a36b00..c5c3c560ea 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -117,7 +117,8 @@ abstract class TreeCheckers extends Analyzer { try p.source.path + ":" + p.line catch { case _: UnsupportedOperationException => p.toString } - def errorFn(msg: Any): Unit = println("[check: %s] %s".format(phase.prev, msg)) + private var hasError: Boolean = false + def errorFn(msg: Any): Unit = {hasError = true; println("[check: %s] %s".format(phase.prev, msg))} def errorFn(pos: Position, msg: Any): Unit = errorFn(posstr(pos) + ": " + msg) def informFn(msg: Any) { if (settings.verbose.value || settings.debug.value) @@ -151,6 +152,7 @@ abstract class TreeCheckers extends Analyzer { result } def runWithUnit[T](unit: CompilationUnit)(body: => Unit): Unit = { + hasError = false val unit0 = currentUnit currentRun.currentUnit = unit body @@ -169,6 +171,7 @@ abstract class TreeCheckers extends Analyzer { checker.precheck.traverse(unit.body) checker.typed(unit.body) checker.postcheck.traverse(unit.body) + if (hasError) unit.warning(NoPosition, "TreeCheckers detected non-compliant trees in " + unit) } } @@ -217,8 +220,11 @@ abstract class TreeCheckers extends Analyzer { case _ => () } - object precheck extends Traverser { + object precheck extends TreeStackTraverser { override def traverse(tree: Tree) { + checkSymbolRefsRespectScope(tree) + checkReturnReferencesDirectlyEnclosingDef(tree) + val sym = tree.symbol def accessed = sym.accessed def fail(msg: String) = errorFn(tree.pos, msg + classstr(tree) + " / " + tree) @@ -289,6 +295,41 @@ abstract class TreeCheckers extends Analyzer { } super.traverse(tree) } + + private def checkSymbolRefsRespectScope(tree: Tree) { + def symbolOf(t: Tree): Symbol = Option(tree.symbol).getOrElse(NoSymbol) + def definedSymbolOf(t: Tree): Symbol = if (t.isDef) symbolOf(t) else NoSymbol + val info = Option(symbolOf(tree).info).getOrElse(NoType) + val referencedSymbols: List[Symbol] = { + val directRef = tree match { + case _: RefTree => symbolOf(tree).toOption + case _ => None + } + def referencedSyms(tp: Type) = (tp collect { + case TypeRef(_, sym, _) => sym + }).toList + val indirectRefs = referencedSyms(info) + (indirectRefs ++ directRef).distinct + } + for { + sym <- referencedSymbols + if (sym.isTypeParameter || sym.isLocal) && !(tree.symbol hasTransOwner sym.owner) + } errorFn(s"The symbol, tpe or info of tree `(${tree}) : ${info}` refers to a out-of-scope symbol, ${sym.fullLocationString}. tree.symbol.ownerChain: ${tree.symbol.ownerChain.mkString(", ")}") + } + + private def checkReturnReferencesDirectlyEnclosingDef(tree: Tree) { + tree match { + case _: Return => + path.collectFirst { + case dd: DefDef => dd + } match { + case None => errorFn(s"Return node ($tree) must be enclosed in a DefDef") + case Some(dd) => + if (tree.symbol != dd.symbol) errorFn(s"Return symbol (${tree.symbol}} does not reference directly enclosing DefDef (${dd.symbol})") + } + case _ => + } + } } object postcheck extends Traverser { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 553583e6b7..a68a084d8f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1452,7 +1452,7 @@ trait Typers extends Modes with Adaptations with Tags { case DefDef(_, name, _, _, _, rhs) => if (stat.symbol.isAuxiliaryConstructor) notAllowed("secondary constructor") - else if (isValueClass && (name == nme.equals_ || name == nme.hashCode_)) + else if (isValueClass && (name == nme.equals_ || name == nme.hashCode_) && !stat.symbol.isSynthetic) notAllowed(s"redefinition of $name method. See SIP-15, criterion 4.") else if (stat.symbol != null && stat.symbol.isParamAccessor) notAllowed("additional parameter") @@ -1903,7 +1903,7 @@ trait Typers extends Modes with Adaptations with Tags { }) } val impl2 = finishMethodSynthesis(impl1, clazz, context) - + // SI-5954. On second compile of a companion class contained in a package object we end up // with some confusion of names which leads to having two symbols with the same name in the // same owner. Until that can be straightened out we can't allow companion objects in package @@ -1916,20 +1916,20 @@ trait Typers extends Modes with Adaptations with Tags { // can't handle case classes in package objects if (m.isCaseClass) pkgObjectRestriction(m, mdef, "case") // can't handle companion class/object pairs in package objects - else if ((m.isClass && m.companionModule != NoSymbol && !m.companionModule.isSynthetic) || - (m.isModule && m.companionClass != NoSymbol && !m.companionClass.isSynthetic)) + else if ((m.isClass && m.companionModule != NoSymbol && !m.companionModule.isSynthetic) || + (m.isModule && m.companionClass != NoSymbol && !m.companionClass.isSynthetic)) pkgObjectRestriction(m, mdef, "companion") } def pkgObjectRestriction(m : Symbol, mdef : ModuleDef, restricted : String) = { val pkgName = mdef.symbol.ownerChain find (_.isPackage) map (_.decodedName) getOrElse mdef.symbol.toString context.error(if (m.pos.isDefined) m.pos else mdef.pos, s"implementation restriction: package object ${pkgName} cannot contain ${restricted} ${m}. Instead, ${m} should be placed directly in package ${pkgName}.") - } + } } if (!settings.companionsInPkgObjs.value && mdef.symbol.isPackageObject) restrictPackageObjectMembers(mdef) - + treeCopy.ModuleDef(mdef, typedMods, mdef.name, impl2) setType NoType } /** In order to override this in the TreeCheckers Typer so synthetics aren't re-added @@ -2067,12 +2067,9 @@ trait Typers extends Modes with Adaptations with Tags { var tpt1 = checkNoEscaping.privates(sym, typer1.typedType(vdef.tpt)) checkNonCyclic(vdef, tpt1) - if (sym.hasAnnotation(definitions.VolatileAttr)) { - if (!sym.isMutable) - VolatileValueError(vdef) - else if (sym.isFinal) - FinalVolatileVarError(vdef) - } + if (sym.hasAnnotation(definitions.VolatileAttr) && !sym.isMutable) + VolatileValueError(vdef) + val rhs1 = if (vdef.rhs.isEmpty) { if (sym.isVariable && sym.owner.isTerm && !sym.isLazy && !isPastTyper) @@ -2213,37 +2210,58 @@ trait Typers extends Modes with Adaptations with Tags { */ def checkMethodStructuralCompatible(ddef: DefDef): Unit = { val meth = ddef.symbol - def fail(pos: Position, msg: String) = unit.error(pos, msg) - val tp: Type = meth.tpe match { - case mt @ MethodType(_, _) => mt - case NullaryMethodType(restpe) => restpe // TODO_NMT: drop NullaryMethodType from resultType? - case PolyType(_, restpe) => restpe - case _ => NoType - } - def nthParamPos(n: Int) = ddef.vparamss match { - case xs :: _ if xs.length > n => xs(n).pos - case _ => meth.pos - } - def failStruct(pos: Position, what: String, where: String = "Parameter") = - fail(pos, s"$where type in structural refinement may not refer to $what") - - foreachWithIndex(tp.paramTypes) { (paramType, idx) => - val sym = paramType.typeSymbol - def paramPos = nthParamPos(idx) - - if (sym.isAbstractType) { - if (!sym.hasTransOwner(meth.owner)) - failStruct(paramPos, "an abstract type defined outside that refinement") - else if (!sym.hasTransOwner(meth)) - failStruct(paramPos, "a type member of that refinement") + def parentString = meth.owner.parentSymbols filterNot (_ == ObjectClass) match { + case Nil => "" + case xs => xs.map(_.nameString).mkString(" (of ", " with ", ")") + } + def fail(pos: Position, msg: String): Boolean = { + unit.error(pos, msg) + false + } + /** Have to examine all parameters in all lists. + */ + def paramssTypes(tp: Type): List[List[Type]] = tp match { + case mt @ MethodType(_, restpe) => mt.paramTypes :: paramssTypes(restpe) + case PolyType(_, restpe) => paramssTypes(restpe) + case _ => Nil + } + def resultType = meth.tpe.finalResultType + def nthParamPos(n1: Int, n2: Int) = + try ddef.vparamss(n1)(n2).pos catch { case _: IndexOutOfBoundsException => meth.pos } + + def failStruct(pos: Position, what: String, where: String = "Parameter type") = + fail(pos, s"$where in structural refinement may not refer to $what") + + foreachWithIndex(paramssTypes(meth.tpe)) { (paramList, listIdx) => + foreachWithIndex(paramList) { (paramType, paramIdx) => + val sym = paramType.typeSymbol + def paramPos = nthParamPos(listIdx, paramIdx) + + /** Not enough to look for abstract types; have to recursively check the bounds + * of each abstract type for more abstract types. Almost certainly there are other + * exploitable type soundness bugs which can be seen by bounding a type parameter + * by an abstract type which itself is bounded by an abstract type. + */ + def checkAbstract(tp0: Type, what: String): Boolean = { + def check(sym: Symbol): Boolean = !sym.isAbstractType || { + log(s"""checking $tp0 in refinement$parentString at ${meth.owner.owner.fullLocationString}""") + ( (!sym.hasTransOwner(meth.owner) && failStruct(paramPos, "an abstract type defined outside that refinement", what)) + || (!sym.hasTransOwner(meth) && failStruct(paramPos, "a type member of that refinement", what)) + || checkAbstract(sym.info.bounds.hi, "Type bound") + ) + } + tp0.dealiasWidenChain forall (t => check(t.typeSymbol)) + } + checkAbstract(paramType, "Parameter type") + + if (sym.isDerivedValueClass) + failStruct(paramPos, "a user-defined value class") + if (paramType.isInstanceOf[ThisType] && sym == meth.owner) + failStruct(paramPos, "the type of that refinement (self type)") } - if (sym.isDerivedValueClass) - failStruct(paramPos, "a user-defined value class") - if (paramType.isInstanceOf[ThisType] && sym == meth.owner) - failStruct(paramPos, "the type of that refinement (self type)") } - if (tp.resultType.typeSymbol.isDerivedValueClass) - failStruct(ddef.tpt.pos, "a user-defined value class", where = "Result") + if (resultType.typeSymbol.isDerivedValueClass) + failStruct(ddef.tpt.pos, "a user-defined value class", where = "Result type") } def typedUseCase(useCase: UseCase) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Variances.scala b/src/compiler/scala/tools/nsc/typechecker/Variances.scala index 7d97b0c782..ea436a71fb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Variances.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Variances.scala @@ -67,6 +67,8 @@ trait Variances { def varianceInType(tp: Type)(tparam: Symbol): Int = tp match { case ErrorType | WildcardType | NoType | NoPrefix | ThisType(_) | ConstantType(_) => VARIANCES + case BoundedWildcardType(bounds) => + varianceInType(bounds)(tparam) case SingleType(pre, sym) => varianceInType(pre)(tparam) case TypeRef(pre, sym, args) => diff --git a/src/library/scala/collection/immutable/Stream.scala b/src/library/scala/collection/immutable/Stream.scala index 1c461973e4..5bb4ef5f21 100644 --- a/src/library/scala/collection/immutable/Stream.scala +++ b/src/library/scala/collection/immutable/Stream.scala @@ -841,9 +841,16 @@ self => * // produces: "1, 2, 3, 4, 5, 6" * }}} */ - override def distinct: Stream[A] = - if (isEmpty) this - else cons(head, tail.filter(head != _).distinct) + override def distinct: Stream[A] = { + // This should use max memory proportional to N, whereas + // recursively calling distinct on the tail is N^2. + def loop(seen: Set[A], rest: Stream[A]): Stream[A] = { + if (rest.isEmpty) rest + else if (seen(rest.head)) loop(seen, rest.tail) + else cons(rest.head, loop(seen + rest.head, rest.tail)) + } + loop(Set(), this) + } /** Returns a new sequence of given length containing the elements of this * sequence followed by zero or more occurrences of given elements. diff --git a/src/library/scala/collection/mutable/ListMap.scala b/src/library/scala/collection/mutable/ListMap.scala index 212ee917c5..7f05deffc8 100644 --- a/src/library/scala/collection/mutable/ListMap.scala +++ b/src/library/scala/collection/mutable/ListMap.scala @@ -12,6 +12,7 @@ package scala.collection package mutable import generic._ +import annotation.tailrec /** A simple mutable map backed by a list. * @@ -47,13 +48,17 @@ extends AbstractMap[A, B] def get(key: A): Option[B] = elems find (_._1 == key) map (_._2) def iterator: Iterator[(A, B)] = elems.iterator - def += (kv: (A, B)) = { elems = remove(kv._1, elems); elems = kv :: elems; siz += 1; this } - def -= (key: A) = { elems = remove(key, elems); this } - private def remove(key: A, elems: List[(A, B)]): List[(A, B)] = - if (elems.isEmpty) elems - else if (elems.head._1 == key) { siz -= 1; elems.tail } - else elems.head :: remove(key, elems.tail) + def += (kv: (A, B)) = { elems = remove(kv._1, elems, List()); elems = kv :: elems; siz += 1; this } + def -= (key: A) = { elems = remove(key, elems, List()); this } + + @tailrec + private def remove(key: A, elems: List[(A, B)], acc: List[(A, B)]): List[(A, B)] = { + if (elems.isEmpty) acc + else if (elems.head._1 == key) { siz -= 1; acc ::: elems.tail } + else remove(key, elems.tail, elems.head :: acc) + } + override def clear() = { elems = List(); siz = 0 } override def size: Int = siz diff --git a/src/partest/scala/tools/partest/BytecodeTest.scala b/src/partest/scala/tools/partest/BytecodeTest.scala new file mode 100644 index 0000000000..93183c2095 --- /dev/null +++ b/src/partest/scala/tools/partest/BytecodeTest.scala @@ -0,0 +1,61 @@ +package scala.tools.partest + +import scala.tools.nsc.util.JavaClassPath +import scala.collection.JavaConverters._ +import scala.tools.asm +import asm.ClassReader +import asm.tree.{ClassNode, MethodNode, InsnList} +import java.io.InputStream + +/** + * Providies utilities for inspecting bytecode using ASM library. + * + * HOW TO USE + * 1. Create subdirectory in test/files/jvm for your test. Let's name it $TESTDIR. + * 2. Create $TESTDIR/BytecodeSrc_1.scala that contains Scala source file which you + * want to inspect the bytecode for. The '_1' suffix signals to partest that it + * should compile this file first. + * 3. Create $TESTDIR/Test.scala: + * import scala.tools.partest.BytecodeTest + * object Test extends BytecodeTest { + * def show { + * // your code that inspect ASM trees and prints values + * } + * } + * 4. Create corresponding check file. + * + * EXAMPLE + * See test/files/jvm/bytecode-test-example for an example of bytecode test. + * + */ +abstract class BytecodeTest { + + /** produce the output to be compared against a checkfile */ + protected def show(): Unit + + def main(args: Array[String]): Unit = show + + protected def getMethod(classNode: ClassNode, name: String): MethodNode = + classNode.methods.asScala.find(_.name == name) getOrElse + sys.error(s"Didn't find method '$name' in class '${classNode.name}'") + + protected def loadClassNode(name: String): ClassNode = { + val classBytes: InputStream = (for { + classRep <- classpath.findClass(name) + binary <- classRep.binary + } yield binary.input) getOrElse sys.error(s"failed to load class '$name'; classpath = $classpath") + + val cr = new ClassReader(classBytes) + val cn = new ClassNode() + cr.accept(cn, 0) + cn + } + + protected lazy val classpath: JavaClassPath = { + import scala.tools.nsc.util.ClassPath.DefaultJavaContext + import scala.tools.util.PathResolver.Defaults + // logic inspired by scala.tools.util.PathResolver implementation + val containers = DefaultJavaContext.classesInExpandedPath(Defaults.javaUserClassPath) + new JavaClassPath(containers, DefaultJavaContext) + } +} diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index a4287fb181..1dec11548f 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -1583,8 +1583,21 @@ trait Symbols extends api.Symbols { self: SymbolTable => setAnnotations(annot :: annotations) // Convenience for the overwhelmingly common case - def addAnnotation(sym: Symbol, args: Tree*): this.type = + def addAnnotation(sym: Symbol, args: Tree*): this.type = { + // The assertion below is meant to prevent from issues like SI-7009 but it's disabled + // due to problems with cycles while compiling Scala library. It's rather shocking that + // just checking if sym is monomorphic type introduces nasty cycles. We are definitively + // forcing too much because monomorphism is a local property of a type that can be checked + // syntactically + // assert(sym.initialize.isMonomorphicType, sym) addAnnotation(AnnotationInfo(sym.tpe, args.toList, Nil)) + } + + /** Use that variant if you want to pass (for example) an applied type */ + def addAnnotation(tp: Type, args: Tree*): this.type = { + assert(tp.typeParams.isEmpty, tp) + addAnnotation(AnnotationInfo(tp, args.toList, Nil)) + } // ------ comparisons ---------------------------------------------------------------- @@ -1651,6 +1664,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => @inline final def map(f: Symbol => Symbol): Symbol = if (this eq NoSymbol) this else f(this) + final def toOption: Option[Symbol] = if (exists) Some(this) else None + // ------ cloneing ------------------------------------------------------------------- /** A clone of this symbol. */ @@ -1728,8 +1743,27 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** For a case class, the symbols of the accessor methods, one for each * argument in the first parameter list of the primary constructor. * The empty list for all other classes. - */ - final def caseFieldAccessors: List[Symbol] = + * + * This list will be sorted to correspond to the declaration order + * in the constructor parameter + */ + final def caseFieldAccessors: List[Symbol] = { + // We can't rely on the ordering of the case field accessors within decls -- + // handling of non-public parameters seems to change the order (see SI-7035.) + // + // Luckily, the constrParamAccessors are still sorted properly, so sort the field-accessors using them + // (need to undo name-mangling, including the sneaky trailing whitespace) + // + // 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)) + caseFieldAccessorsUnsorted.sortBy { acc => + primaryNames indexWhere { orig => + (acc.name == orig) || (acc.name startsWith (orig append "$")) + } + } + } + private final def caseFieldAccessorsUnsorted: List[Symbol] = (info.decls filter (_.isCaseAccessorMethod)).toList final def constrParamAccessors: List[Symbol] = diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index ebf0998573..c1753fc5a1 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -172,10 +172,29 @@ abstract class TreeGen extends macros.TreeBuilder { if (qual.symbol != null && (qual.symbol.isEffectiveRoot || qual.symbol.isEmptyPackage)) mkAttributedIdent(sym) else { + // Have to recognize anytime a selection is made on a package + // so it can be rewritten to foo.bar.`package`.name rather than + // foo.bar.name if name is in the package object. + // TODO - factor out the common logic between this and + // the Typers method "isInPackageObject", used in typedIdent. + val qualsym = ( + if (qual.tpe ne null) qual.tpe.typeSymbol + else if (qual.symbol ne null) qual.symbol + else NoSymbol + ) + val needsPackageQualifier = ( + (sym ne null) + && qualsym.isPackage + && !sym.isDefinedInPackage + ) val pkgQualifier = - if (sym != null && sym.owner.isPackageObjectClass && sym.effectiveOwner == qual.tpe.typeSymbol) { - val obj = sym.owner.sourceModule - Select(qual, nme.PACKAGE) setSymbol obj setType singleType(qual.tpe, obj) + if (needsPackageQualifier) { + // The owner of a symbol which requires package qualification may be the + // package object iself, but it also could be any superclass of the package + // object. In the latter case, we must go through the qualifier's info + // to obtain the right symbol. + val packageObject = if (sym.owner.isModuleClass) sym.owner.sourceModule else qual.tpe member nme.PACKAGE + Select(qual, nme.PACKAGE) setSymbol packageObject setType singleType(qual.tpe, packageObject) } else qual diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index e1a18570b2..98c1afb323 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -261,22 +261,24 @@ abstract class TreeInfo { * in the position `for { <tree> <- expr }` based only * on information at the `parser` phase? To qualify, there * may be no subtree that will be interpreted as a - * Stable Identifier Pattern. + * Stable Identifier Pattern, nor any type tests, even + * on TupleN. See SI-6968. * * For instance: * * {{{ - * foo @ (bar, (baz, quux)) + * (foo @ (bar @ _)) = 0 * }}} * - * is a variable pattern; if the structure matches, - * then the remainder is inevitable. + * is a not a variable pattern; if only binds names. * * The following are not variable patterns. * * {{{ - * foo @ (bar, (`baz`, quux)) // back quoted ident, not at top level - * foo @ (bar, Quux) // UpperCase ident, not at top level + * `bar` + * Bar + * (a, b) + * _: T * }}} * * If the pattern is a simple identifier, it is always @@ -305,10 +307,6 @@ abstract class TreeInfo { tree match { case Bind(name, pat) => isVarPatternDeep0(pat) case Ident(name) => isVarPattern(tree) - case Apply(sel, args) => - ( isReferenceToScalaMember(sel, TupleClass(args.size).name.toTermName) - && (args forall isVarPatternDeep0) - ) case _ => false } } diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 431afd286d..a528a9ced8 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -1440,6 +1440,22 @@ trait Trees extends api.Trees { self: SymbolTable => if (tree.hasSymbol) { subst(from, to) tree match { + case _: DefTree => + val newInfo = symSubst(tree.symbol.info) + if (!(newInfo =:= tree.symbol.info)) { + debuglog(sm""" + |TreeSymSubstituter: updated info of symbol ${tree.symbol} + | Old: ${showRaw(tree.symbol.info, printTypes = true, printIds = true)} + | New: ${showRaw(newInfo, printTypes = true, printIds = true)}""") + tree.symbol updateInfo newInfo + } + case _ => + // no special handling is required for Function or Import nodes here. + // as they don't have interesting infos attached to their symbols. + // Subsitution of the referenced symbol of Return nodes is handled + // in .ChangeOwnerTraverser + } + tree match { case Ident(name0) if tree.symbol != NoSymbol => treeCopy.Ident(tree, tree.symbol.name) case Select(qual, name0) if tree.symbol != NoSymbol => @@ -1488,6 +1504,15 @@ trait Trees extends api.Trees { self: SymbolTable => } } + trait TreeStackTraverser extends Traverser { + import collection.mutable + val path: mutable.Stack[Tree] = mutable.Stack() + abstract override def traverse(t: Tree) = { + path push t + try super.traverse(t) finally path.pop() + } + } + private lazy val duplicator = new Transformer { override val treeCopy = newStrictTreeCopier override def transform(t: Tree) = { diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index dbc00edb1a..3136b227db 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -22,6 +22,8 @@ import util.ThreeValues._ // internal: error case WildcardType => // internal: unknown + case BoundedWildcardType(bounds) => + // internal: unknown case NoType => case NoPrefix => case ThisType(sym) => @@ -744,7 +746,7 @@ trait Types extends api.Types { self: SymbolTable => val trivial = ( this.isTrivial || phase.erasedTypes && pre.typeSymbol != ArrayClass - || pre.normalize.isTrivial && !isPossiblePrefix(clazz) + || skipPrefixOf(pre, clazz) ) if (trivial) this else { @@ -3590,12 +3592,6 @@ trait Types extends api.Types { self: SymbolTable => val pre1 = pre match { case x: SuperType if sym1.isEffectivelyFinal || sym1.isDeferred => x.thistpe - case _: CompoundType if sym1.isClass => - // sharpen prefix so that it is maximal and still contains the class. - pre.parents.reverse dropWhile (_.member(sym1.name) != sym1) match { - case Nil => pre - case parent :: _ => parent - } case _ => pre } if (pre eq pre1) TypeRef(pre, sym1, args) @@ -3852,12 +3848,16 @@ trait Types extends api.Types { self: SymbolTable => // This is the specified behavior. protected def etaExpandKeepsStar = false + /** Turn any T* types into Seq[T] except when + * in method parameter position. + */ object dropRepeatedParamType extends TypeMap { def apply(tp: Type): Type = tp match { case MethodType(params, restpe) => - MethodType(params, apply(restpe)) - case PolyType(tparams, restpe) => - PolyType(tparams, apply(restpe)) + // Not mapping over params + val restpe1 = apply(restpe) + if (restpe eq restpe1) tp + else MethodType(params, restpe1) case TypeRef(_, RepeatedParamClass, arg :: Nil) => seqType(arg) case _ => @@ -4469,14 +4469,15 @@ trait Types extends api.Types { self: SymbolTable => */ def isPossiblePrefix(clazz: Symbol) = clazz.isClass && !clazz.isPackageClass + private def skipPrefixOf(pre: Type, clazz: Symbol) = ( + (pre eq NoType) || (pre eq NoPrefix) || !isPossiblePrefix(clazz) + ) + /** A map to compute the asSeenFrom method */ class AsSeenFromMap(pre: Type, clazz: Symbol) extends TypeMap with KeepOnlyTypeConstraints { var capturedSkolems: List[Symbol] = List() var capturedParams: List[Symbol] = List() - private def skipPrefixOf(pre: Type, clazz: Symbol) = ( - (pre eq NoType) || (pre eq NoPrefix) || !isPossiblePrefix(clazz) - ) override def mapOver(tree: Tree, giveup: ()=>Nothing): Tree = { object annotationArgRewriter extends TypeMapTransformer { private def canRewriteThis(sym: Symbol) = ( @@ -4509,8 +4510,7 @@ trait Types extends api.Types { self: SymbolTable => } def apply(tp: Type): Type = - if (skipPrefixOf(pre, clazz)) tp - else tp match { + tp match { case ThisType(sym) => def toPrefix(pre: Type, clazz: Symbol): Type = if (skipPrefixOf(pre, clazz)) tp @@ -4672,6 +4672,8 @@ trait Types extends api.Types { self: SymbolTable => /** A map to implement the `substSym` method. */ class SubstSymMap(from: List[Symbol], to: List[Symbol]) extends SubstMap(from, to) { + def this(pairs: (Symbol, Symbol)*) = this(pairs.toList.map(_._1), pairs.toList.map(_._2)) + protected def toType(fromtp: Type, sym: Symbol) = fromtp match { case TypeRef(pre, _, args) => copyTypeRef(fromtp, pre, sym, args) case SingleType(pre, _) => singleType(pre, sym) @@ -7112,6 +7114,14 @@ trait Types extends api.Types { self: SymbolTable => } } + def isJavaVarargsAncestor(clazz: Symbol) = ( + clazz.isClass + && clazz.isJavaDefined + && (clazz.info.nonPrivateDecls exists isJavaVarArgsMethod) + ) + def inheritsJavaVarArgsMethod(clazz: Symbol) = + clazz.thisType.baseClasses exists isJavaVarargsAncestor + /** All types in list must be polytypes with type parameter lists of * same length as tparams. * Returns list of list of bounds infos, where corresponding type @@ -7224,6 +7234,12 @@ trait Types extends api.Types { self: SymbolTable => else (ps :+ SerializableClass.tpe).toList ) + /** Members of the given class, other than those inherited + * from Any or AnyRef. + */ + def nonTrivialMembers(clazz: Symbol): Iterable[Symbol] = + clazz.info.members filterNot (sym => sym.owner == ObjectClass || sym.owner == AnyClass) + def objToAny(tp: Type): Type = if (!phase.erasedTypes && tp.typeSymbol == ObjectClass) AnyClass.tpe else tp diff --git a/src/reflect/scala/reflect/internal/util/Position.scala b/src/reflect/scala/reflect/internal/util/Position.scala index 3d10d4c9ce..8f287a1640 100644 --- a/src/reflect/scala/reflect/internal/util/Position.scala +++ b/src/reflect/scala/reflect/internal/util/Position.scala @@ -91,7 +91,7 @@ abstract class Position extends scala.reflect.api.Position { self => /** An optional value containing the source file referred to by this position, or * None if not defined. */ - def source: SourceFile = throw new UnsupportedOperationException("Position.source") + def source: SourceFile = throw new UnsupportedOperationException(s"Position.source on ${this.getClass}") /** Is this position neither a NoPosition nor a FakePosition? * If isDefined is true, offset and source are both defined. @@ -111,19 +111,19 @@ abstract class Position extends scala.reflect.api.Position { self => def makeTransparent: Position = this /** The start of the position's range, error if not a range position */ - def start: Int = throw new UnsupportedOperationException("Position.start") + def start: Int = throw new UnsupportedOperationException(s"Position.start on ${this.getClass}") /** The start of the position's range, or point if not a range position */ def startOrPoint: Int = point /** The point (where the ^ is) of the position */ - def point: Int = throw new UnsupportedOperationException("Position.point") + def point: Int = throw new UnsupportedOperationException(s"Position.point on ${this.getClass}") /** The point (where the ^ is) of the position, or else `default` if undefined */ def pointOrElse(default: Int): Int = default /** The end of the position's range, error if not a range position */ - def end: Int = throw new UnsupportedOperationException("Position.end") + def end: Int = throw new UnsupportedOperationException(s"Position.end on ${this.getClass}") /** The end of the position's range, or point if not a range position */ def endOrPoint: Int = point diff --git a/src/scalap/scala/tools/scalap/Main.scala b/src/scalap/scala/tools/scalap/Main.scala index 7c84279699..90f8cb8d71 100644 --- a/src/scalap/scala/tools/scalap/Main.scala +++ b/src/scalap/scala/tools/scalap/Main.scala @@ -184,7 +184,7 @@ object Main extends Main { val cparg = List("-classpath", "-cp") map (arguments getArgument _) reduceLeft (_ orElse _) val path = cparg match { case Some(cp) => new JavaClassPath(DefaultJavaContext.classesInExpandedPath(cp), DefaultJavaContext) - case _ => PathResolver.fromPathString("") + case _ => PathResolver.fromPathString(".") // include '.' in the default classpath SI-6669 } // print the classpath if output is verbose if (verbose) |