diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc')
29 files changed, 580 insertions, 592 deletions
diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index df5952a4cf..c2caed70a0 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -98,6 +98,11 @@ trait CompilationUnits { global: Global => override def toString = map.toString } + // namer calls typer.computeType(rhs) on DefDef / ValDef when tpt is empty. the result + // is cached here and re-used in typedDefDef / typedValDef + // Also used to cache imports type-checked by namer. + val transformed = new mutable.AnyRefMap[Tree, Tree] + /** things to check at end of compilation unit */ val toCheck = new ListBuffer[() => Unit] diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 5492e563dd..81e96b76ac 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -81,6 +81,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def picklerPhase: Phase = if (currentRun.isDefined) currentRun.picklerPhase else NoPhase + def erasurePhase: Phase = if (currentRun.isDefined) currentRun.erasurePhase else NoPhase + // platform specific elements protected class GlobalPlatform extends { @@ -527,7 +529,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } with Erasure // phaseName = "posterasure" - object postErasure extends { + override object postErasure extends { val global: Global.this.type = Global.this val runsAfter = List("erasure") val runsRightAfter = Some("erasure") @@ -1212,7 +1214,26 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** Have we already supplemented the error message of a compiler crash? */ private[nsc] final var supplementedError = false - private val unitbuf = new mutable.ListBuffer[CompilationUnit] + private class SyncedCompilationBuffer { self => + private val underlying = new mutable.ArrayBuffer[CompilationUnit] + def size = synchronized { underlying.size } + def +=(cu: CompilationUnit): this.type = { synchronized { underlying += cu }; this } + def head: CompilationUnit = synchronized{ underlying.head } + def apply(i: Int): CompilationUnit = synchronized { underlying(i) } + def iterator: Iterator[CompilationUnit] = new collection.AbstractIterator[CompilationUnit] { + private var used = 0 + def hasNext = self.synchronized{ used < underlying.size } + def next = self.synchronized { + if (!hasNext) throw new NoSuchElementException("next on empty Iterator") + used += 1 + underlying(used-1) + } + } + def toList: List[CompilationUnit] = synchronized{ underlying.toList } + } + + private val unitbuf = new SyncedCompilationBuffer + val compiledFiles = new mutable.HashSet[String] /** A map from compiled top-level symbols to their source files */ @@ -1223,9 +1244,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) private var phasec: Int = 0 // phases completed private var unitc: Int = 0 // units completed this phase - private var _unitbufSize = 0 - def size = _unitbufSize + def size = unitbuf.size override def toString = "scalac Run for:\n " + compiledFiles.toList.sorted.mkString("\n ") // Calculate where to stop based on settings -Ystop-before or -Ystop-after. @@ -1450,7 +1470,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** add unit to be compiled in this run */ private def addUnit(unit: CompilationUnit) { unitbuf += unit - _unitbufSize += 1 // counting as they're added so size is cheap compiledFiles += unit.source.file.path } private def checkDeprecatedSettings(unit: CompilationUnit) { @@ -1466,11 +1485,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /* !!! Note: changing this to unitbuf.toList.iterator breaks a bunch of tests in tests/res. This is bad, it means the resident compiler relies on an iterator of a mutable data structure reflecting changes - made to the underlying structure (in whatever accidental way it is - currently depending upon.) + made to the underlying structure. */ def units: Iterator[CompilationUnit] = unitbuf.iterator - + def registerPickle(sym: Symbol): Unit = () /** does this run compile given class, module, or case factory? */ diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala index d553d71bf5..c2d62db558 100644 --- a/src/compiler/scala/tools/nsc/ScriptRunner.scala +++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala @@ -19,7 +19,7 @@ import util.Exceptional.unwrap * exec scala "$0" "$@" * !# * Console.println("Hello, world!") - * argv.toList foreach Console.println + * args.toList foreach Console.println * </pre> * <p>And here is a batch file example on Windows XP:</p> * <pre> @@ -29,7 +29,7 @@ import util.Exceptional.unwrap * goto :eof * ::!# * Console.println("Hello, world!") - * argv.toList foreach Console.println + * args.toList foreach Console.println * </pre> * * @author Lex Spoon diff --git a/src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala b/src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala new file mode 100644 index 0000000000..5fcb02814b --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala @@ -0,0 +1,112 @@ +package scala.tools.nsc +package ast.parser + +/** Common code between Scala's Tokens and JavaTokens. */ +abstract class CommonTokens { + + def isIdentifier(code: Int): Boolean + def isLiteral(code: Int): Boolean + + /** special tokens */ + final val EMPTY = -3 + final val UNDEF = -2 + final val ERROR = -1 + final val EOF = 0 + + /** literals */ + final val CHARLIT = 1 + final val INTLIT = 2 + final val LONGLIT = 3 + final val FLOATLIT = 4 + final val DOUBLELIT = 5 + final val STRINGLIT = 6 + + /** keywords */ + final val NEW = 20 + final val THIS = 21 + final val SUPER = 23 + + final val NULL = 24 + final val TRUE = 25 + final val FALSE = 26 + + // J: INSTANCEOF = 27 + // J: CONST = 28 + + /** modifiers */ + // S: IMPLICIT = 40 + // S: OVERRIDE = 41 + // J: PUBLIC = 42 + final val PROTECTED = 43 + final val PRIVATE = 44 + // S: SEALED = 45 + final val ABSTRACT = 46 + // J: DEFAULT = 47 + // J: STATIC = 48 + final val FINAL = 49 + // J: TRANSIENT = 50 + // J: VOLATILE = 51 + // J: SYNCHRONIZED = 52 + // J: NATIVE = 53 + // J: STRICTFP = 54 + // S: LAZY = 55 + // J: THROWS = 56 + // S: MACRO = 57 + + /** templates */ + final val PACKAGE = 60 + final val IMPORT = 61 + final val CLASS = 62 + // S: CASECLASS = 63 + // S: OBJECT = 64 + // S: CASEOBJECT = 65 + // S: TRAIT, J: INTERFACE = 66 + // J: ENUM = 67 + final val EXTENDS = 68 + // S: WITH, J: IMPLEMENTS = 69 + // S: TYPE = 70 + // S: FORSOME = 71 + // S: DEF = 72 + // S: VAL = 73 + // S: VAR = 74 + + /** control structures */ + final val IF = 80 + // S: THEN = 81 + final val ELSE = 82 + final val WHILE = 83 + final val DO = 84 + final val FOR = 85 + // S: YIELD = 86 + // J: BREAK = 87 + // J: CONTINUE = 88 + // J: GOTO = 89 + final val THROW = 90 + final val TRY = 91 + final val CATCH = 92 + final val FINALLY = 93 + // J: SWITCH = 94 + // S: MATCH = 95 + final val CASE = 96 + final val RETURN = 97 + // J: ASSERT = 98 + + /** parenthesis */ + final val LPAREN = 100 + final val RPAREN = 101 + final val LBRACKET = 102 + final val RBRACKET = 103 + final val LBRACE = 104 + final val RBRACE = 105 + + /** special symbols */ + final val COMMA = 120 + final val SEMI = 121 + final val DOT = 122 + final val COLON = 123 + final val EQUALS = 124 + final val AT = 125 + // S: <special symbols> = 130 - 139 + // J: <special symbols> = 140 - 179 + // J: <primitive types> = 180 - 189 +} diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index e3d2bf14a0..8271363527 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -413,12 +413,10 @@ self => * * {{{ * object moduleName { - * def main(argv: Array[String]): Unit = { - * val args = argv + * def main(args: Array[String]): Unit = * new AnyRef { * stmts * } - * } * } * }}} */ @@ -433,9 +431,8 @@ self => // def main def mainParamType = AppliedTypeTree(Ident(tpnme.Array), List(Ident(tpnme.String))) - def mainParameter = List(ValDef(Modifiers(Flags.PARAM), nme.argv, mainParamType, EmptyTree)) - def mainSetArgv = List(ValDef(NoMods, nme.args, TypeTree(), Ident(nme.argv))) - def mainDef = DefDef(NoMods, nme.main, Nil, List(mainParameter), scalaDot(tpnme.Unit), Block(mainSetArgv, gen.mkAnonymousNew(stmts))) + def mainParameter = List(ValDef(Modifiers(Flags.PARAM), nme.args, mainParamType, EmptyTree)) + def mainDef = DefDef(NoMods, nme.main, Nil, List(mainParameter), scalaDot(tpnme.Unit), gen.mkAnonymousNew(stmts)) // object Main def moduleName = newTermName(ScriptRunner scriptMain settings) @@ -2495,7 +2492,7 @@ self => def mkDefs(p: Tree, tp: Tree, rhs: Tree): List[Tree] = { val trees = { val pat = if (tp.isEmpty) p else Typed(p, tp) setPos (p.pos union tp.pos) - gen.mkPatDef(newmods, pat, rhs) + makePatDef(newmods, pat, rhs) } if (newmods.isDeferred) { trees match { diff --git a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala index 5a7dc4950d..e624aec88c 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala @@ -6,123 +6,57 @@ package scala.tools.nsc package ast.parser -/** Common code between JavaTokens and Tokens. Not as much (and not as concrete) - * as one might like because JavaTokens for no clear reason chose new numbers for - * identical token sets. - */ -abstract class Tokens { - /** special tokens */ - final val EMPTY = -3 - final val UNDEF = -2 - final val ERROR = -1 - final val EOF = 0 - - /** literals */ - final val CHARLIT = 1 - final val INTLIT = 2 - final val LONGLIT = 3 - final val FLOATLIT = 4 - final val DOUBLELIT = 5 - final val STRINGLIT = 6 - - def LPAREN: Int - def RBRACE: Int - - def isIdentifier(code: Int): Boolean - def isLiteral(code: Int): Boolean -} - -object Tokens extends Tokens { - final val STRINGPART = 7 // a part of an interpolated string +object Tokens extends CommonTokens { + final val STRINGPART = 7 // a part of an interpolated string final val SYMBOLLIT = 8 final val INTERPOLATIONID = 9 // the lead identifier of an interpolated string - def isLiteral(code: Int) = - code >= CHARLIT && code <= INTERPOLATIONID + def isLiteral(code: Int) = code >= CHARLIT && code <= INTERPOLATIONID /** identifiers */ final val IDENTIFIER = 10 final val BACKQUOTED_IDENT = 11 - def isIdentifier(code: Int) = code >= IDENTIFIER && code <= BACKQUOTED_IDENT // used by ide - - /** keywords */ - final val IF = 20 - final val FOR = 21 - final val ELSE = 22 - final val THIS = 23 - final val NULL = 24 - final val NEW = 25 - final val WITH = 26 - final val SUPER = 27 - final val CASE = 28 - final val CASECLASS = 29 - final val CASEOBJECT = 30 - final val VAL = 31 - final val ABSTRACT = 32 - final val FINAL = 33 - final val PRIVATE = 34 - final val PROTECTED = 35 - final val OVERRIDE = 36 - final val IMPLICIT = 37 - final val VAR = 38 - final val DEF = 39 - final val TYPE = 40 - final val EXTENDS = 41 - final val TRUE = 42 - final val FALSE = 43 - final val OBJECT = 44 - final val CLASS = 45 - - final val IMPORT = 46 - final val PACKAGE = 47 - final val YIELD = 48 - final val DO = 49 - final val TRAIT = 50 - final val SEALED = 51 - final val THROW = 52 - final val TRY = 53 - final val CATCH = 54 - final val FINALLY = 55 - final val WHILE = 56 - final val RETURN = 57 - final val MATCH = 58 - final val FORSOME = 59 - final val LAZY = 61 - final val MACRO = 62 // not yet used in 2.10 - final val THEN = 63 // not yet used in 2.10 + def isIdentifier(code: Int) = code == IDENTIFIER || code == BACKQUOTED_IDENT // used by ide + + /** modifiers */ + final val IMPLICIT = 40 + final val OVERRIDE = 41 + final val SEALED = 45 + final val LAZY = 55 + final val MACRO = 57 + + /** templates */ + final val CASECLASS = 63 + final val OBJECT = 64 + final val CASEOBJECT = 65 + final val TRAIT = 66 + final val WITH = 69 + final val TYPE = 70 + final val FORSOME = 71 + final val DEF = 72 + final val VAL = 73 + final val VAR = 74 + + /** control structures */ + final val THEN = 81 + final val YIELD = 86 + final val MATCH = 95 /** special symbols */ - final val COMMA = 70 - final val SEMI = 71 - final val DOT = 72 - final val USCORE = 73 - final val COLON = 74 - final val EQUALS = 75 - final val LARROW = 76 - final val ARROW = 77 - final val NEWLINE = 78 - final val NEWLINES = 79 - final val SUBTYPE = 80 - final val SUPERTYPE = 81 - final val HASH = 82 - final val AT = 83 - final val VIEWBOUND = 84 - - /** parenthesis */ - final val LPAREN = 90 - final val RPAREN = 91 - final val LBRACKET = 92 - final val RBRACKET = 93 - final val LBRACE = 94 - final val RBRACE = 95 - - /** XML mode */ - final val XMLSTART = 96 + final val HASH = 130 + final val USCORE = 131 + final val ARROW = 132 + final val LARROW = 133 + final val SUBTYPE = 134 + final val SUPERTYPE = 135 + final val VIEWBOUND = 136 + final val NEWLINE = 137 + final val NEWLINES = 138 + final val XMLSTART = 139 /** for IDE only */ - final val COMMENT = 97 - - final val WHITESPACE = 105 - final val IGNORE = 106 - final val ESCAPE = 109 + final val COMMENT = 200 + final val WHITESPACE = 201 + final val IGNORE = 202 + final val ESCAPE = 203 } diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 525dcffb0c..6e5a3f6ef7 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -168,4 +168,6 @@ abstract class TreeBuilder { vparamss ::: List(evidenceParams) } } + + def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree) = gen.mkPatDef(mods, pat, rhs) } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index 18ccced75e..359e5d6c29 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -583,53 +583,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { trait BCAnnotGen extends BCInnerClassGen { - /* - * can-multi-thread - */ - def ubytesToCharArray(bytes: Array[Byte]): Array[Char] = { - val ca = new Array[Char](bytes.length) - var idx = 0 - while (idx < bytes.length) { - val b: Byte = bytes(idx) - assert((b & ~0x7f) == 0) - ca(idx) = b.asInstanceOf[Char] - idx += 1 - } - - ca - } - - /* - * can-multi-thread - */ - private def arrEncode(sb: ScalaSigBytes): Array[String] = { - var strs: List[String] = Nil - val bSeven: Array[Byte] = sb.sevenBitsMayBeZero - // chop into slices of at most 65535 bytes, counting 0x00 as taking two bytes (as per JVMS 4.4.7 The CONSTANT_Utf8_info Structure) - var prevOffset = 0 - var offset = 0 - var encLength = 0 - while (offset < bSeven.size) { - val deltaEncLength = (if (bSeven(offset) == 0) 2 else 1) - val newEncLength = encLength.toLong + deltaEncLength - if (newEncLength >= 65535) { - val ba = bSeven.slice(prevOffset, offset) - strs ::= new java.lang.String(ubytesToCharArray(ba)) - encLength = 0 - prevOffset = offset - } else { - encLength += deltaEncLength - offset += 1 - } - } - if (prevOffset < offset) { - assert(offset == bSeven.length) - val ba = bSeven.slice(prevOffset, offset) - strs ::= new java.lang.String(ubytesToCharArray(ba)) - } - assert(strs.size > 1, "encode instead as one String via strEncode()") // TODO too strict? - mkArrayReverse(strs) - } + import genASM.{ubytesToCharArray, arrEncode} /* * can-multi-thread @@ -676,7 +630,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { av.visit(name, strEncode(sb)) } else { val arrAnnotV: asm.AnnotationVisitor = av.visitArray(name) - for(arg <- arrEncode(sb)) { arrAnnotV.visit(name, arg) } + for(arg <- genASM.arrEncode(sb)) { arrAnnotV.visit(name, arg) } arrAnnotV.visitEnd() } // for the lazy val in ScalaSigBytes to be GC'ed, the invoker of emitAnnotations() should hold the ScalaSigBytes in a method-local var that doesn't escape. @@ -772,25 +726,6 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { trait BCJGenSigGen { - // @M don't generate java generics sigs for (members of) implementation - // classes, as they are monomorphic (TODO: ok?) - /* - * must-single-thread - */ - private def needsGenericSignature(sym: Symbol) = !( - // PP: This condition used to include sym.hasExpandedName, but this leads - // to the total loss of generic information if a private member is - // accessed from a closure: both the field and the accessor were generated - // without it. This is particularly bad because the availability of - // generic information could disappear as a consequence of a seemingly - // unrelated change. - settings.Ynogenericsig - || sym.isArtifact - || sym.isLiftedMethod - || sym.isBridge - || (sym.ownerChain exists (_.isImplClass)) - ) - def getCurrentCUnit(): CompilationUnit /* @return @@ -799,61 +734,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { * * must-single-thread */ - def getGenericSignature(sym: Symbol, owner: Symbol): String = { - - if (!needsGenericSignature(sym)) { return null } - - val memberTpe = enteringErasure(owner.thisType.memberInfo(sym)) - - val jsOpt: Option[String] = erasure.javaSig(sym, memberTpe) - if (jsOpt.isEmpty) { return null } - - val sig = jsOpt.get - log(sig) // This seems useful enough in the general case. - - def wrap(op: => Unit) = { - try { op; true } - catch { case _: Throwable => false } - } - - if (settings.Xverify) { - // Run the signature parser to catch bogus signatures. - val isValidSignature = wrap { - // Alternative: scala.tools.reflect.SigParser (frontend to sun.reflect.generics.parser.SignatureParser) - import scala.tools.asm.util.CheckClassAdapter - if (sym.isMethod) { CheckClassAdapter checkMethodSignature sig } - else if (sym.isTerm) { CheckClassAdapter checkFieldSignature sig } - else { CheckClassAdapter checkClassSignature sig } - } - - if (!isValidSignature) { - getCurrentCUnit().warning(sym.pos, - """|compiler bug: created invalid generic signature for %s in %s - |signature: %s - |if this is reproducible, please report bug at https://issues.scala-lang.org/ - """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig)) - return null - } - } - - if ((settings.check containsName phaseName)) { - val normalizedTpe = enteringErasure(erasure.prepareSigMap(memberTpe)) - val bytecodeTpe = owner.thisType.memberInfo(sym) - if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym)(normalizedTpe) =:= bytecodeTpe)) { - getCurrentCUnit().warning(sym.pos, - """|compiler bug: created generic signature for %s in %s that does not conform to its erasure - |signature: %s - |original type: %s - |normalized type: %s - |erasure type: %s - |if this is reproducible, please report bug at http://issues.scala-lang.org/ - """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig, memberTpe, normalizedTpe, bytecodeTpe)) - return null - } - } - - sig - } + def getGenericSignature(sym: Symbol, owner: Symbol): String = genASM.getGenericSignature(sym, owner, getCurrentCUnit()) } // end of trait BCJGenSigGen @@ -906,7 +787,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { ) // TODO needed? for(ann <- m.annotations) { ann.symbol.initialize } - val jgensig = if (m.isDeferred) null else getGenericSignature(m, module); // only add generic signature if method concrete; bug #1745 + val jgensig = genASM.staticForwarderGenericSignature(m, module, getCurrentCUnit()) addRemoteExceptionAnnot(isRemoteClass, hasPublicBitSet(flags), m) val (throws, others) = m.annotations partition (_.symbol == definitions.ThrowsClass) val thrownExceptions: List[String] = getExceptions(throws) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index eb40e1dbde..a389816caf 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -20,7 +20,7 @@ import scala.annotation.tailrec * * Documentation at http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/2012Q2/GenASM.pdf */ -abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { +abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { self => import global._ import icodes._ import icodes.opcodes._ @@ -171,10 +171,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { var pickledBytes = 0 // statistics - val javaNameCache = perRunCaches.newMap[Symbol, Name]() + val javaNameCache = perRunCaches.newAnyRefMap[Symbol, Name]() // unlike javaNameCache, reverseJavaName contains entries only for class symbols and their internal names. - val reverseJavaName = perRunCaches.newMap[String, Symbol]() + val reverseJavaName = perRunCaches.newAnyRefMap[String, Symbol]() private def mkFlags(args: Int*) = args.foldLeft(0)(_ | _) private def hasPublicBitSet(flags: Int) = (flags & asm.Opcodes.ACC_PUBLIC) != 0 @@ -803,134 +803,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { annot.args.isEmpty && !annot.matches(DeprecatedAttr) - // @M don't generate java generics sigs for (members of) implementation - // classes, as they are monomorphic (TODO: ok?) - private def needsGenericSignature(sym: Symbol) = !( - // PP: This condition used to include sym.hasExpandedName, but this leads - // to the total loss of generic information if a private member is - // accessed from a closure: both the field and the accessor were generated - // without it. This is particularly bad because the availability of - // generic information could disappear as a consequence of a seemingly - // unrelated change. - settings.Ynogenericsig - || sym.isArtifact - || sym.isLiftedMethod - || sym.isBridge - || (sym.ownerChain exists (_.isImplClass)) - ) - def getCurrentCUnit(): CompilationUnit - /** @return - * - `null` if no Java signature is to be added (`null` is what ASM expects in these cases). - * - otherwise the signature in question - */ - def getGenericSignature(sym: Symbol, owner: Symbol): String = { - - if (!needsGenericSignature(sym)) { return null } - - val memberTpe = enteringErasure(owner.thisType.memberInfo(sym)) - - val jsOpt: Option[String] = erasure.javaSig(sym, memberTpe) - if (jsOpt.isEmpty) { return null } - - val sig = jsOpt.get - log(sig) // This seems useful enough in the general case. - - def wrap(op: => Unit) = { - try { op; true } - catch { case _: Throwable => false } - } - - if (settings.Xverify) { - // Run the signature parser to catch bogus signatures. - val isValidSignature = wrap { - // Alternative: scala.tools.reflect.SigParser (frontend to sun.reflect.generics.parser.SignatureParser) - import scala.tools.asm.util.CheckClassAdapter - if (sym.isMethod) { CheckClassAdapter checkMethodSignature sig } // requires asm-util.jar - else if (sym.isTerm) { CheckClassAdapter checkFieldSignature sig } - else { CheckClassAdapter checkClassSignature sig } - } - - if(!isValidSignature) { - getCurrentCUnit().warning(sym.pos, - """|compiler bug: created invalid generic signature for %s in %s - |signature: %s - |if this is reproducible, please report bug at https://issues.scala-lang.org/ - """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig)) - return null - } - } - - if ((settings.check containsName phaseName)) { - val normalizedTpe = enteringErasure(erasure.prepareSigMap(memberTpe)) - val bytecodeTpe = owner.thisType.memberInfo(sym) - if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym)(normalizedTpe) =:= bytecodeTpe)) { - getCurrentCUnit().warning(sym.pos, - """|compiler bug: created generic signature for %s in %s that does not conform to its erasure - |signature: %s - |original type: %s - |normalized type: %s - |erasure type: %s - |if this is reproducible, please report bug at http://issues.scala-lang.org/ - """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig, memberTpe, normalizedTpe, bytecodeTpe)) - return null - } - } - - sig - } - - def ubytesToCharArray(bytes: Array[Byte]): Array[Char] = { - val ca = new Array[Char](bytes.length) - var idx = 0 - while(idx < bytes.length) { - val b: Byte = bytes(idx) - assert((b & ~0x7f) == 0) - ca(idx) = b.asInstanceOf[Char] - idx += 1 - } - - ca - } - - private def arrEncode(sb: ScalaSigBytes): Array[String] = { - var strs: List[String] = Nil - val bSeven: Array[Byte] = sb.sevenBitsMayBeZero - // chop into slices of at most 65535 bytes, counting 0x00 as taking two bytes (as per JVMS 4.4.7 The CONSTANT_Utf8_info Structure) - var prevOffset = 0 - var offset = 0 - var encLength = 0 - while(offset < bSeven.length) { - val deltaEncLength = (if(bSeven(offset) == 0) 2 else 1) - val newEncLength = encLength.toLong + deltaEncLength - if(newEncLength >= 65535) { - val ba = bSeven.slice(prevOffset, offset) - strs ::= new java.lang.String(ubytesToCharArray(ba)) - encLength = 0 - prevOffset = offset - } else { - encLength += deltaEncLength - offset += 1 - } - } - if(prevOffset < offset) { - assert(offset == bSeven.length) - val ba = bSeven.slice(prevOffset, offset) - strs ::= new java.lang.String(ubytesToCharArray(ba)) - } - assert(strs.size > 1, "encode instead as one String via strEncode()") // TODO too strict? - strs.reverse.toArray - } - - private def strEncode(sb: ScalaSigBytes): String = { - val ca = ubytesToCharArray(sb.sevenBitsMayBeZero) - new java.lang.String(ca) - // debug val bvA = new asm.ByteVector; bvA.putUTF8(s) - // debug val enc: Array[Byte] = scala.reflect.internal.pickling.ByteCodecs.encode(bytes) - // debug assert(enc(idx) == bvA.getByte(idx + 2)) - // debug assert(bvA.getLength == enc.size + 2) - } + def getGenericSignature(sym: Symbol, owner: Symbol) = self.getGenericSignature(sym, owner, getCurrentCUnit()) def emitArgument(av: asm.AnnotationVisitor, name: String, @@ -1064,7 +939,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { ) // TODO needed? for(ann <- m.annotations) { ann.symbol.initialize } - val jgensig = if (m.isDeferred) null else getGenericSignature(m, module); // only add generic signature if method concrete; bug #1745 + val jgensig = staticForwarderGenericSignature(m, module, getCurrentCUnit()) addRemoteExceptionAnnot(isRemoteClass, hasPublicBitSet(flags), m) val (throws, others) = m.annotations partition (_.symbol == ThrowsClass) val thrownExceptions: List[String] = getExceptions(throws) @@ -3303,4 +3178,147 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } + // @M don't generate java generics sigs for (members of) implementation + // classes, as they are monomorphic (TODO: ok?) + private def needsGenericSignature(sym: Symbol) = !( + // PP: This condition used to include sym.hasExpandedName, but this leads + // to the total loss of generic information if a private member is + // accessed from a closure: both the field and the accessor were generated + // without it. This is particularly bad because the availability of + // generic information could disappear as a consequence of a seemingly + // unrelated change. + settings.Ynogenericsig + || sym.isArtifact + || sym.isLiftedMethod + || sym.isBridge + || (sym.ownerChain exists (_.isImplClass)) + ) + + final def staticForwarderGenericSignature(sym: Symbol, moduleClass: Symbol, unit: CompilationUnit): String = { + if (sym.isDeferred) null // only add generic signature if method concrete; bug #1745 + else { + // SI-3452 Static forwarder generation uses the same erased signature as the method if forwards to. + // By rights, it should use the signature as-seen-from the module class, and add suitable + // primitive and value-class boxing/unboxing. + // But for now, just like we did in mixin, we just avoid writing a wrong generic signature + // (one that doesn't erase to the actual signature). See run/t3452b for a test case. + val memberTpe = enteringErasure(moduleClass.thisType.memberInfo(sym)) + val erasedMemberType = erasure.erasure(sym)(memberTpe) + if (erasedMemberType =:= sym.info) + getGenericSignature(sym, moduleClass, memberTpe, unit) + else null + } + } + + /** @return + * - `null` if no Java signature is to be added (`null` is what ASM expects in these cases). + * - otherwise the signature in question + */ + def getGenericSignature(sym: Symbol, owner: Symbol, unit: CompilationUnit): String = { + val memberTpe = enteringErasure(owner.thisType.memberInfo(sym)) + getGenericSignature(sym, owner, memberTpe, unit) + } + def getGenericSignature(sym: Symbol, owner: Symbol, memberTpe: Type, unit: CompilationUnit): String = { + if (!needsGenericSignature(sym)) { return null } + + val jsOpt: Option[String] = erasure.javaSig(sym, memberTpe) + if (jsOpt.isEmpty) { return null } + + val sig = jsOpt.get + log(sig) // This seems useful enough in the general case. + + def wrap(op: => Unit) = { + try { op; true } + catch { case _: Throwable => false } + } + + if (settings.Xverify) { + // Run the signature parser to catch bogus signatures. + val isValidSignature = wrap { + // Alternative: scala.tools.reflect.SigParser (frontend to sun.reflect.generics.parser.SignatureParser) + import scala.tools.asm.util.CheckClassAdapter + if (sym.isMethod) { CheckClassAdapter checkMethodSignature sig } // requires asm-util.jar + else if (sym.isTerm) { CheckClassAdapter checkFieldSignature sig } + else { CheckClassAdapter checkClassSignature sig } + } + + if(!isValidSignature) { + unit.warning(sym.pos, + """|compiler bug: created invalid generic signature for %s in %s + |signature: %s + |if this is reproducible, please report bug at https://issues.scala-lang.org/ + """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig)) + return null + } + } + + if ((settings.check containsName phaseName)) { + val normalizedTpe = enteringErasure(erasure.prepareSigMap(memberTpe)) + val bytecodeTpe = owner.thisType.memberInfo(sym) + if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym)(normalizedTpe) =:= bytecodeTpe)) { + unit.warning(sym.pos, + """|compiler bug: created generic signature for %s in %s that does not conform to its erasure + |signature: %s + |original type: %s + |normalized type: %s + |erasure type: %s + |if this is reproducible, please report bug at http://issues.scala-lang.org/ + """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig, memberTpe, normalizedTpe, bytecodeTpe)) + return null + } + } + + sig + } + + def ubytesToCharArray(bytes: Array[Byte]): Array[Char] = { + val ca = new Array[Char](bytes.length) + var idx = 0 + while(idx < bytes.length) { + val b: Byte = bytes(idx) + assert((b & ~0x7f) == 0) + ca(idx) = b.asInstanceOf[Char] + idx += 1 + } + + ca + } + + final def arrEncode(sb: ScalaSigBytes): Array[String] = { + var strs: List[String] = Nil + val bSeven: Array[Byte] = sb.sevenBitsMayBeZero + // chop into slices of at most 65535 bytes, counting 0x00 as taking two bytes (as per JVMS 4.4.7 The CONSTANT_Utf8_info Structure) + var prevOffset = 0 + var offset = 0 + var encLength = 0 + while(offset < bSeven.length) { + val deltaEncLength = (if(bSeven(offset) == 0) 2 else 1) + val newEncLength = encLength.toLong + deltaEncLength + if(newEncLength >= 65535) { + val ba = bSeven.slice(prevOffset, offset) + strs ::= new java.lang.String(ubytesToCharArray(ba)) + encLength = 0 + prevOffset = offset + } else { + encLength += deltaEncLength + offset += 1 + } + } + if(prevOffset < offset) { + assert(offset == bSeven.length) + val ba = bSeven.slice(prevOffset, offset) + strs ::= new java.lang.String(ubytesToCharArray(ba)) + } + assert(strs.size > 1, "encode instead as one String via strEncode()") // TODO too strict? + strs.reverse.toArray + } + + private def strEncode(sb: ScalaSigBytes): String = { + val ca = ubytesToCharArray(sb.sevenBitsMayBeZero) + new java.lang.String(ca) + // debug val bvA = new asm.ByteVector; bvA.putUTF8(s) + // debug val enc: Array[Byte] = scala.reflect.internal.pickling.ByteCodecs.encode(bytes) + // debug assert(enc(idx) == bvA.getByte(idx + 2)) + // debug assert(bvA.getLength == enc.size + 2) + } } diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index 9875d27047..a61ad392ee 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -190,7 +190,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { case GTGTGT => GTGT case GTGTEQ => GTEQ case GTGT => GT - case GTEQ => ASSIGN + case GTEQ => EQUALS } if (closers isDefinedAt in.token) in.token = closers(in.token) else accept(GT) @@ -538,7 +538,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { in.nextToken() if (in.token == IDENTIFIER) { // if there's an ident after the comma ... val name = ident() - if (in.token == ASSIGN || in.token == SEMI) { // ... followed by a `=` or `;`, we know it's a real variable definition + if (in.token == EQUALS || in.token == SEMI) { // ... followed by a `=` or `;`, we know it's a real variable definition buf ++= maybe buf += varDecl(in.currentPos, mods, tpt.duplicate, name.toTermName) maybe.clear() @@ -563,7 +563,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { def varDecl(pos: Position, mods: Modifiers, tpt: Tree, name: TermName): ValDef = { val tpt1 = optArrayBrackets(tpt) - if (in.token == ASSIGN && !mods.isParameter) skipTo(COMMA, SEMI) + if (in.token == EQUALS && !mods.isParameter) skipTo(COMMA, SEMI) val mods1 = if (mods.isFinal) mods &~ Flags.FINAL else mods | Flags.MUTABLE atPos(pos) { ValDef(mods1, name, tpt1, blankExpr) diff --git a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala index b7ea70e2c7..c5401219dd 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala @@ -155,7 +155,6 @@ trait JavaScanners extends ast.parser.ScannersCommon { case AMP => "`&'" case AMPAMP => "`&&'" case AMPEQ => "`&='" - case ASSIGN => "`='" case ASTERISK => "`*'" case ASTERISKEQ => "`*='" case AT => "`@'" @@ -169,6 +168,7 @@ trait JavaScanners extends ast.parser.ScannersCommon { case DOT => "`.'" case DOTDOTDOT => "`...'" case EQEQ => "`=='" + case EQUALS => "`='" case GT => "`>'" case GTEQ => "`>='" case GTGT => "`>>'" @@ -337,7 +337,7 @@ trait JavaScanners extends ast.parser.ScannersCommon { return case '=' => - token = ASSIGN + token = EQUALS in.next() if (in.ch == '=') { token = EQEQ diff --git a/src/compiler/scala/tools/nsc/javac/JavaTokens.scala b/src/compiler/scala/tools/nsc/javac/JavaTokens.scala index 953a3c6d82..9b31e6e8a2 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaTokens.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaTokens.scala @@ -6,117 +6,89 @@ package scala.tools.nsc package javac -object JavaTokens extends ast.parser.Tokens { +object JavaTokens extends ast.parser.CommonTokens { - def isLiteral(code : Int) = + def isLiteral(code: Int) = code >= CHARLIT && code <= STRINGLIT /** identifiers */ final val IDENTIFIER = 10 - def isIdentifier(code : Int) = + def isIdentifier(code: Int) = code == IDENTIFIER /** keywords */ - final val ABSTRACT = 20 - final val ASSERT = 21 - final val BOOLEAN = 22 - final val BREAK = 23 - final val BYTE = 24 - final val CASE = 25 - final val CATCH = 26 - final val CHAR = 27 - final val CLASS = 28 - final val CONST = 29 - final val CONTINUE = 30 - final val DEFAULT = 31 - final val DO = 32 - final val DOUBLE = 33 - final val ELSE = 34 - final val ENUM = 35 - final val EXTENDS = 36 - final val FINAL = 37 - final val FINALLY = 38 - final val FLOAT = 39 - final val FOR = 40 - final val IF = 41 - final val GOTO = 42 - final val IMPLEMENTS = 43 - final val IMPORT = 44 - final val INSTANCEOF = 45 - final val INT = 46 - final val INTERFACE = 47 - final val LONG = 48 - final val NATIVE = 49 - final val NEW = 50 - final val PACKAGE = 51 - final val PRIVATE = 52 - final val PROTECTED = 53 - final val PUBLIC = 54 - final val RETURN = 55 - final val SHORT = 56 - final val STATIC = 57 - final val STRICTFP = 58 - final val SUPER = 59 - final val SWITCH = 60 - final val SYNCHRONIZED = 61 - final val THIS = 62 - final val THROW = 63 - final val THROWS = 64 - final val TRANSIENT = 65 - final val TRY = 66 - final val VOID = 67 - final val VOLATILE = 68 - final val WHILE = 69 + final val INSTANCEOF = 27 + final val CONST = 28 + + /** modifiers */ + final val PUBLIC = 42 + final val DEFAULT = 47 + final val STATIC = 48 + final val TRANSIENT = 50 + final val VOLATILE = 51 + final val SYNCHRONIZED = 52 + final val NATIVE = 53 + final val STRICTFP = 54 + final val THROWS = 56 + + /** templates */ + final val INTERFACE = 66 + final val ENUM = 67 + final val IMPLEMENTS = 69 + + /** control structures */ + final val BREAK = 87 + final val CONTINUE = 88 + final val GOTO = 89 + final val SWITCH = 94 + final val ASSERT = 98 /** special symbols */ - final val COMMA = 70 - final val SEMI = 71 - final val DOT = 72 - final val AT = 73 - final val COLON = 74 - final val ASSIGN = 75 - final val EQEQ = 76 - final val BANGEQ = 77 - final val LT = 78 - final val GT = 79 - final val LTEQ = 80 - final val GTEQ = 81 - final val BANG = 82 - final val QMARK = 83 - final val AMP = 84 - final val BAR = 85 - final val PLUS = 86 - final val MINUS = 87 - final val ASTERISK = 88 - final val SLASH = 89 - final val PERCENT = 90 - final val HAT = 91 - final val LTLT = 92 - final val GTGT = 93 - final val GTGTGT = 94 - final val AMPAMP = 95 - final val BARBAR = 96 - final val PLUSPLUS = 97 - final val MINUSMINUS = 98 - final val TILDE = 99 - final val DOTDOTDOT = 100 - final val AMPEQ = 104 - final val BAREQ = 105 - final val PLUSEQ = 106 - final val MINUSEQ = 107 - final val ASTERISKEQ = 1010 - final val SLASHEQ = 109 - final val PERCENTEQ = 110 - final val HATEQ = 111 - final val LTLTEQ = 112 - final val GTGTEQ = 113 - final val GTGTGTEQ = 114 + final val EQEQ = 140 + final val BANGEQ = 141 + final val LT = 142 + final val GT = 143 + final val LTEQ = 144 + final val GTEQ = 145 + final val BANG = 146 + final val QMARK = 147 + final val AMP = 148 + final val BAR = 149 + final val PLUS = 150 + final val MINUS = 151 + final val ASTERISK = 152 + final val SLASH = 153 + final val PERCENT = 154 + final val HAT = 155 + final val LTLT = 156 + final val GTGT = 157 + final val GTGTGT = 158 + final val AMPAMP = 159 + final val BARBAR = 160 + final val PLUSPLUS = 161 + final val MINUSMINUS = 162 + final val TILDE = 163 + final val DOTDOTDOT = 164 + final val AMPEQ = 165 + final val BAREQ = 166 + final val PLUSEQ = 167 + final val MINUSEQ = 168 + final val ASTERISKEQ = 169 + final val SLASHEQ = 170 + final val PERCENTEQ = 171 + final val HATEQ = 172 + final val LTLTEQ = 173 + final val GTGTEQ = 174 + final val GTGTGTEQ = 175 - /** parenthesis */ - final val LPAREN = 115 - final val RPAREN = 116 - final val LBRACKET = 117 - final val RBRACKET = 118 - final val LBRACE = 119 - final val RBRACE = 120 + /** primitive types */ + final val VOID = 180 + final val BOOLEAN = 181 + final val BYTE = 182 + final val SHORT = 183 + final val CHAR = 184 + final val INT = 185 + final val LONG = 186 + final val FLOAT = 187 + final val DOUBLE = 188 } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 664645e53e..2f9cc01c0b 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -665,8 +665,11 @@ abstract class ClassfileParser { // so have to check unsafeTypeParams.isEmpty before worrying about raw type case below, // or we'll create a boatload of needless existentials. else if (classSym.isMonomorphicType || classSym.unsafeTypeParams.isEmpty) tp - // raw type - existentially quantify all type parameters - else debuglogResult(s"raw type from $classSym")(unsafeClassExistentialType(classSym)) + else debuglogResult(s"raw type from $classSym"){ + // raw type - existentially quantify all type parameters + val eparams = typeParamsToExistentials(classSym, classSym.unsafeTypeParams) + newExistentialType(eparams, typeRef(pre, classSym, eparams.map(_.tpeHK))) + } case tp => assert(sig.charAt(index) != '<', s"sig=$sig, index=$index, tp=$tp") tp diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala index 933a2f70a1..d81a5d5755 100644 --- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala +++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala @@ -232,6 +232,10 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre val parents = addSerializable(abstractFunctionErasedType) val funOwner = originalFunction.symbol.owner + // TODO harmonize the naming of delamdafy anon-fun classes with those spun up by Uncurry + // - make `anonClass.isAnonymousClass` true. + // - use `newAnonymousClassSymbol` or push the required variations into a similar factory method + // - reinstate the assertion in `Erasure.resolveAnonymousBridgeClash` val suffix = "$lambda$" + ( if (funOwner.isPrimaryConstructor) "" else "$" + funOwner.name @@ -282,18 +286,11 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre if (sym == NoSymbol) sym.toString else s"$sym: ${sym.tpe} in ${sym.owner}" - def clashError(bm: Symbol) = { - unit.error( - applyMethodDef.symbol.pos, - sm"""bridge generated for member ${fulldef(applyMethodDef.symbol)} - |which overrides ${fulldef(getMember(abstractFunctionErasedType.typeSymbol, nme.apply))} - |clashes with definition of the member itself; - |both have erased type ${exitingPostErasure(bm.tpe)}""") - } - bridgeMethod foreach (bm => + // TODO SI-6260 maybe just create the apply method with the signature (Object => Object) in all cases + // rather than the method+bridge pair. if (bm.symbol.tpe =:= applyMethodDef.symbol.tpe) - clashError(bm.symbol) + erasure.resolveAnonymousBridgeClash(applyMethodDef.symbol, bm.symbol) ) val body = members ++ List(constr, applyMethodDef) ++ bridgeMethod diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index ccfddab94a..60c1553ef3 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -403,19 +403,19 @@ abstract class Erasure extends AddInterfaces * @param other The overidden symbol for which the bridge was generated * @param bridge The bridge */ - def checkBridgeOverrides(member: Symbol, other: Symbol, bridge: Symbol): Boolean = { + def checkBridgeOverrides(member: Symbol, other: Symbol, bridge: Symbol): Seq[(Position, String)] = { def fulldef(sym: Symbol) = if (sym == NoSymbol) sym.toString else s"$sym: ${sym.tpe} in ${sym.owner}" var noclash = true + val clashErrors = mutable.Buffer[(Position, String)]() def clashError(what: String) = { - noclash = false - unit.error( - if (member.owner == root) member.pos else root.pos, - sm"""bridge generated for member ${fulldef(member)} - |which overrides ${fulldef(other)} - |clashes with definition of $what; - |both have erased type ${exitingPostErasure(bridge.tpe)}""") + val pos = if (member.owner == root) member.pos else root.pos + val msg = sm"""bridge generated for member ${fulldef(member)} + |which overrides ${fulldef(other)} + |clashes with definition of $what; + |both have erased type ${exitingPostErasure(bridge.tpe)}""" + clashErrors += Tuple2(pos, msg) } for (bc <- root.baseClasses) { if (settings.debug) @@ -440,7 +440,7 @@ abstract class Erasure extends AddInterfaces } } } - noclash + clashErrors } /** TODO - work through this logic with a fine-toothed comb, incorporating @@ -478,8 +478,18 @@ abstract class Erasure extends AddInterfaces bridge setInfo (otpe cloneInfo bridge) bridgeTarget(bridge) = member - if (!(member.tpe exists (_.typeSymbol.isDerivedValueClass)) || - checkBridgeOverrides(member, other, bridge)) { + def sigContainsValueClass = (member.tpe exists (_.typeSymbol.isDerivedValueClass)) + + val shouldAdd = ( + !sigContainsValueClass + || (checkBridgeOverrides(member, other, bridge) match { + case Nil => true + case es if member.owner.isAnonymousClass => resolveAnonymousBridgeClash(member, bridge); true + case es => for ((pos, msg) <- es) unit.error(pos, msg); false + }) + ) + + if (shouldAdd) { exitingErasure(root.info.decls enter bridge) if (other.owner == root) { exitingErasure(root.info.decls.unlink(other)) @@ -1127,5 +1137,13 @@ abstract class Erasure extends AddInterfaces } } + final def resolveAnonymousBridgeClash(sym: Symbol, bridge: Symbol) { + // TODO reinstate this after Delambdafy generates anonymous classes that meet this requirement. + // require(sym.owner.isAnonymousClass, sym.owner) + log(s"Expanding name of ${sym.debugLocationString} as it clashes with bridge. Renaming deemed safe because the owner is anonymous.") + sym.expandName(sym.owner) + bridge.resetFlag(BRIDGE) + } + private class TypeRefAttachment(val tpe: TypeRef) } diff --git a/src/compiler/scala/tools/nsc/transform/Flatten.scala b/src/compiler/scala/tools/nsc/transform/Flatten.scala index b4329965fc..c3fbfae322 100644 --- a/src/compiler/scala/tools/nsc/transform/Flatten.scala +++ b/src/compiler/scala/tools/nsc/transform/Flatten.scala @@ -20,12 +20,16 @@ abstract class Flatten extends InfoTransform { /** Updates the owning scope with the given symbol, unlinking any others. */ private def replaceSymbolInCurrentScope(sym: Symbol): Unit = exitingFlatten { + removeSymbolInCurrentScope(sym) + sym.owner.info.decls enter sym + } + + private def removeSymbolInCurrentScope(sym: Symbol): Unit = exitingFlatten { val scope = sym.owner.info.decls val old = (scope lookupUnshadowedEntries sym.name).toList old foreach (scope unlink _) - scope enter sym def old_s = old map (_.sym) mkString ", " - debuglog(s"In scope of ${sym.owner}, unlinked $old_s and entered $sym") + if (old.nonEmpty) debuglog(s"In scope of ${sym.owner}, unlinked $old_s") } private def liftClass(sym: Symbol) { @@ -121,6 +125,8 @@ abstract class Flatten extends InfoTransform { val liftedBuffer = liftedDefs(tree.symbol.enclosingTopLevelClass.owner) val index = liftedBuffer.length liftedBuffer.insert(index, super.transform(tree)) + if (tree.symbol.sourceModule.isStaticModule) + removeSymbolInCurrentScope(tree.symbol.sourceModule) EmptyTree case _ => super.transform(tree) diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 89f9cb4b06..673bc04bd9 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -172,18 +172,23 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // info) as they are seen from the class. We can't use the member that we get from the // implementation class, as it's a clone that was made after erasure, and thus it does not // know its info at the beginning of erasure anymore. - // Optimize: no need if mixinClass has no typeparams. - mixinMember cloneSymbol clazz modifyInfo (info => - if (mixinClass.typeParams.isEmpty) info - else (clazz.thisType baseType mixinClass) memberInfo mixinMember - ) + val sym = mixinMember cloneSymbol clazz + + val erasureMap = erasure.erasure(mixinMember) + val erasedInterfaceInfo: Type = erasureMap(mixinMember.info) + val specificForwardInfo = (clazz.thisType baseType mixinClass) memberInfo mixinMember + val forwarderInfo = + if (erasureMap(specificForwardInfo) =:= erasedInterfaceInfo) + specificForwardInfo + else { + erasedInterfaceInfo + } + // Optimize: no need if mixinClass has no typeparams. + // !!! JZ Really? What about the effect of abstract types, prefix? + if (mixinClass.typeParams.isEmpty) sym + else sym modifyInfo (_ => forwarderInfo) } - // clone before erasure got rid of type info we'll need to generate a javaSig - // now we'll have the type info at (the beginning of) erasure in our history, - // and now newSym has the info that's been transformed to fit this period - // (no need for asSeenFrom as phase.erasedTypes) - // TODO: verify we need the updateInfo and document why - newSym updateInfo (mixinMember.info cloneInfo newSym) + newSym } /** Add getters and setters for all non-module fields of an implementation diff --git a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala index 870eafbf20..bbd11efa7e 100644 --- a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala +++ b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala @@ -24,7 +24,12 @@ abstract class OverridingPairs extends SymbolPairs { /** Symbols to exclude: Here these are constructors and private/artifact symbols, * including bridges. But it may be refined in subclasses. */ - override protected def exclude(sym: Symbol) = sym.isPrivateLocal || sym.isArtifact || sym.isConstructor + override protected def exclude(sym: Symbol) = ( + sym.isPrivateLocal + || sym.isArtifact + || sym.isConstructor + || (sym.isPrivate && sym.owner != base) // Privates aren't inherited. Needed for pos/t7475a.scala + ) /** Types always match. Term symbols match if their member types * relative to `self` match. diff --git a/src/compiler/scala/tools/nsc/transform/PostErasure.scala b/src/compiler/scala/tools/nsc/transform/PostErasure.scala index cc78e27282..32987fed8c 100644 --- a/src/compiler/scala/tools/nsc/transform/PostErasure.scala +++ b/src/compiler/scala/tools/nsc/transform/PostErasure.scala @@ -8,7 +8,7 @@ package transform /** This phase maps ErasedValueTypes to the underlying unboxed representation and * performs peephole optimizations. */ -trait PostErasure extends InfoTransform with TypingTransformers { +trait PostErasure extends InfoTransform with TypingTransformers with scala.reflect.internal.transform.PostErasure { val global: Global import global._ @@ -19,16 +19,6 @@ trait PostErasure extends InfoTransform with TypingTransformers { def newTransformer(unit: CompilationUnit): Transformer = new PostErasureTransformer(unit) override def changesBaseClasses = false - object elimErasedValueType extends TypeMap { - def apply(tp: Type) = tp match { - case ConstantType(Constant(tp: Type)) => ConstantType(Constant(apply(tp))) - case ErasedValueType(_, underlying) => underlying - case _ => mapOver(tp) - } - } - - def transformInfo(sym: Symbol, tp: Type) = elimErasedValueType(tp) - class PostErasureTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { override def transform(tree: Tree) = { def finish(res: Tree) = logResult(s"Posterasure reduction\n Old: $tree\n New")(res) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index e5907e1a0f..c065fb54b7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -89,6 +89,8 @@ trait Contexts { self: Analyzer => if (settings.noimports) Nil else if (unit.isJava) RootImports.javaList else if (settings.nopredef || treeInfo.noPredefImportForUnit(unit.body)) { + // SI-8258 Needed for the presentation compiler using -sourcepath, otherwise cycles can occur. See the commit + // message for this ticket for an example. debuglog("Omitted import of Predef._ for " + unit) RootImports.javaAndScalaList } @@ -457,7 +459,9 @@ trait Contexts { self: Analyzer => c.prefix = prefixInChild c.enclClass = if (isTemplateOrPackage) c else enclClass c(ConstructorSuffix) = !isTemplateOrPackage && c(ConstructorSuffix) - c.enclMethod = if (isDefDef) c else enclMethod + + // SI-8245 `isLazy` need to skip lazy getters to ensure `return` binds to the right place + c.enclMethod = if (isDefDef && !owner.isLazy) c else enclMethod registerContext(c.asInstanceOf[analyzer.Context]) debuglog("[context] ++ " + c.unit + " / " + tree.summaryString) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 776920ed42..8f5778862d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -306,7 +306,10 @@ trait Implicits { */ object Function1 { val Sym = FunctionClass(1) - def unapply(tp: Type) = tp baseType Sym match { + // It is tempting to think that this should be inspecting "tp baseType Sym" + // rather than tp. See test case run/t8280 and the commit message which + // accompanies it for explanation why that isn't done. + def unapply(tp: Type) = tp match { case TypeRef(_, Sym, arg1 :: arg2 :: _) => Some((arg1, arg2)) case _ => None } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 997fd6fc65..2d6c94349b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -174,7 +174,8 @@ trait Infer extends Checkable { private lazy val stdErrorValue = stdErrorClass.newErrorValue(nme.ERROR) /** The context-dependent inferencer part */ - class Inferencer(context: Context) extends InferencerContextErrors with InferCheckable { + abstract class Inferencer extends InferencerContextErrors with InferCheckable { + def context: Context import InferErrorGen._ /* -- Error Messages --------------------------------------------------- */ diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 29b3ec7f3e..1f90dd4939 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -661,9 +661,11 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { val expanded1 = atPos(enclosingMacroPosition.makeTransparent)(Typed(expanded0, TypeTree(innerPt))) typecheck("blackbox typecheck", expanded1, outerPt) } else { - val expanded1 = expanded0 - val expanded2 = typecheck("whitebox typecheck #1", expanded1, outerPt) - typecheck("whitebox typecheck #2", expanded2, innerPt) + // whitebox expansions need to be typechecked against WildcardType first in order to avoid SI-6992 and SI-8048 + // then we typecheck against innerPt, not against outerPt in order to prevent SI-8209 + val expanded1 = typecheck("whitebox typecheck #0", expanded0, WildcardType) + val expanded2 = typecheck("whitebox typecheck #1", expanded1, innerPt) + typecheck("whitebox typecheck #2", expanded2, outerPt) } } override def onDelayed(delayed: Tree) = { diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index ec2b7d49f5..ba183fe3e6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -427,7 +427,7 @@ trait MethodSynthesis { override def derivedSym = basisSym.lazyAccessor override def derivedTree: DefDef = { val ValDef(_, _, tpt0, rhs0) = tree - val rhs1 = transformed.getOrElse(rhs0, rhs0) + val rhs1 = context.unit.transformed.getOrElse(rhs0, rhs0) val body = ( if (tree.symbol.owner.isTrait || hasUnitType(basisSym)) rhs1 else gen.mkAssignAndReturn(basisSym, rhs1) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 632e25aa2e..9b5b0e1f37 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -169,6 +169,13 @@ trait Namers extends MethodSynthesis { def updatePosFlags(sym: Symbol, pos: Position, flags: Long): Symbol = { debuglog("[overwrite] " + sym) val newFlags = (sym.flags & LOCKED) | flags + sym.rawInfo match { + case tr: TypeRef => + // !!! needed for: pos/t5954d; the uniques type cache will happilly serve up the same TypeRef + // over this mutated symbol, and we witness a stale cache for `parents`. + tr.invalidateCaches() + case _ => + } sym reset NoType setFlag newFlags setPos pos sym.moduleClass andAlso (updatePosFlags(_, pos, moduleClassFlags(flags))) @@ -457,6 +464,17 @@ trait Namers extends MethodSynthesis { var m: Symbol = context.scope lookupModule tree.name val moduleFlags = tree.mods.flags | MODULE if (m.isModule && !m.isPackage && inCurrentScope(m) && (currentRun.canRedefine(m) || m.isSynthetic)) { + // This code accounts for the way the package objects found in the classpath are opened up + // early by the completer of the package itself. If the `packageobjects` phase then finds + // the same package object in sources, we have to clean the slate and remove package object + // members from the package class. + // + // TODO SI-4695 Pursue the approach in https://github.com/scala/scala/pull/2789 that avoids + // opening up the package object on the classpath at all if one exists in source. + if (m.isPackageObject) { + val packageScope = m.enclosingPackageClass.rawInfo.decls + packageScope.filter(_.owner != m.enclosingPackageClass).toList.foreach(packageScope unlink _) + } updatePosFlags(m, tree.pos, moduleFlags) setPrivateWithin(tree, m) m.moduleClass andAlso (setPrivateWithin(tree, _)) @@ -1408,12 +1426,18 @@ trait Namers extends MethodSynthesis { if (expr1.isErrorTyped) ErrorType else { - if (!treeInfo.isStableIdentifierPattern(expr1)) - typer.TyperErrorGen.UnstableTreeError(expr1) + expr1 match { + case This(_) => + // SI-8207 okay, typedIdent expands Ident(self) to C.this which doesn't satisfy the next case + // TODO should we change `typedIdent` not to expand to the `Ident` to a `This`? + case _ if treeInfo.isStableIdentifierPattern(expr1) => + case _ => + typer.TyperErrorGen.UnstableTreeError(expr1) + } val newImport = treeCopy.Import(imp, expr1, selectors).asInstanceOf[Import] checkSelectors(newImport) - transformed(imp) = newImport + context.unit.transformed(imp) = newImport // copy symbol and type attributes back into old expression // so that the structure builder will find it. expr setSymbol expr1.symbol setType expr1.tpe diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala index 41c656f8ce..cf3f265f0c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala @@ -221,10 +221,12 @@ trait PatternTypers { * see test/files/../t5189*.scala */ private def convertToCaseConstructor(tree: Tree, caseClass: Symbol, ptIn: Type): Tree = { - def untrustworthyPt = ( + // TODO SI-7886 / SI-5900 This is well intentioned but doesn't quite hit the nail on the head. + // For now, I've put it completely behind -Xstrict-inference. + val untrustworthyPt = settings.strictInference && ( ptIn =:= AnyTpe || ptIn =:= NothingTpe - || settings.strictInference && ptIn.typeSymbol != caseClass + || ptIn.typeSymbol != caseClass ) val variantToSkolem = new VariantToSkolemMap val caseClassType = tree.tpe.prefix memberType caseClass @@ -371,4 +373,4 @@ trait PatternTypers { } } } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 2125e281f0..916b8a3e0c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -467,6 +467,11 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // overrideError("may not override parameterized type"); // @M: substSym def checkOverrideAlias() { + // Important: first check the pair has the same kind, since the substitution + // carries high's type parameter's bounds over to low, so that + // type equality doesn't consider potentially different bounds on low/high's type params. + // In b781e25afe this went from using memberInfo to memberType (now lowType/highType), tested by neg/override.scala. + // TODO: was that the right fix? it seems type alias's RHS should be checked by looking at the symbol's info if (pair.sameKind && lowType.substSym(low.typeParams, high.typeParams) =:= highType) () else overrideTypeError() // (1.6) } @@ -853,7 +858,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans val baseClass = clazz.info.baseTypeSeq(i).typeSymbol seenTypes(i) match { case Nil => - println("??? base "+baseClass+" not found in basetypes of "+clazz) + devWarning(s"base $baseClass not found in basetypes of $clazz. This might indicate incorrect caching of TypeRef#parents.") case _ :: Nil => ;// OK case tp1 :: tp2 :: _ => diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 020d26c712..aadc8f5e3b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -13,7 +13,7 @@ package scala package tools.nsc package typechecker -import scala.collection.{ mutable, immutable } +import scala.collection.{mutable, immutable} import scala.reflect.internal.util.{ BatchSourceFile, Statistics, shortClassOfInstance } import mutable.ListBuffer import symtab.Flags._ @@ -39,7 +39,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // namer calls typer.computeType(rhs) on DefDef / ValDef when tpt is empty. the result // is cached here and re-used in typedDefDef / typedValDef // Also used to cache imports type-checked by namer. - val transformed = new mutable.HashMap[Tree, Tree] + val transformed = new mutable.AnyRefMap[Tree, Tree] final val shortenImports = false @@ -52,11 +52,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper //println("resetTyper called") resetContexts() resetImplicits() - transformed.clear() resetDocComments() } sealed abstract class SilentResult[+T] { + def isEmpty: Boolean + def nonEmpty = !isEmpty + @inline final def fold[U](none: => U)(f: T => U): U = this match { case SilentResultValue(value) => f(value) case _ => none @@ -75,6 +77,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } class SilentTypeError private(val errors: List[AbsTypeError]) extends SilentResult[Nothing] { + override def isEmpty = true def err: AbsTypeError = errors.head def reportableErrors = errors match { case (e1: AmbiguousImplicitTypeError) +: _ => @@ -88,7 +91,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def unapply(error: SilentTypeError): Option[AbsTypeError] = error.errors.headOption } - case class SilentResultValue[+T](value: T) extends SilentResult[T] { } + case class SilentResultValue[+T](value: T) extends SilentResult[T] { override def isEmpty = false } def newTyper(context: Context): Typer = new NormalTyper(context) @@ -108,7 +111,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val runDefinitions = currentRun.runDefinitions import runDefinitions._ - val infer = new Inferencer(context0) { + private val transformed: mutable.Map[Tree, Tree] = unit.transformed + + val infer = new Inferencer { + def context = Typer.this.context // See SI-3281 re undoLog override def isCoercible(tp: Type, pt: Type) = undoLog undo viewExists(tp, pt) } @@ -1797,32 +1803,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (settings.isScala211 && mdef.symbol == PredefModule) ensurePredefParentsAreInSameSourceFile(impl2) - // 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 will warn on companion objects in package - // objects. But this code also tries to be friendly by distinguishing between case classes and - // user written companion pairs - def warnPackageObjectMembers(mdef : ModuleDef) = for (m <- mdef.symbol.info.members) { - // ignore synthetic objects, because the "companion" object to a case class is synthetic and - // we only want one error per case class - if (!m.isSynthetic) { - // can't handle case classes in package objects - if (m.isCaseClass) pkgObjectWarning(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)) - pkgObjectWarning(m, mdef, "companion") - } - - def pkgObjectWarning(m : Symbol, mdef : ModuleDef, restricted : String) = { - val pkgName = mdef.symbol.ownerChain find (_.isPackage) map (_.decodedName) getOrElse mdef.symbol.toString - context.warning(if (m.pos.isDefined) m.pos else mdef.pos, s"${m} should be placed directly in package ${pkgName} instead of package object ${pkgName}. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954.") - } - } - - if (mdef.symbol.isPackageObject) - warnPackageObjectMembers(mdef) - treeCopy.ModuleDef(mdef, typedMods, mdef.name, impl2) setType NoType } @@ -4880,20 +4860,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper asym setInfo logResult(s"Updating bounds of ${asym.fullLocationString} in $tree from '$abounds' to")(TypeBounds(lo, hi)) } if (asym != null && asym.isAbstractType) { - // See pos/t1786 to follow what's happening here. - def canEnhanceIdent = ( - asym.hasCompleteInfo - && tparam.exists /* sometimes it is NoSymbol */ - && tparam.hasCompleteInfo /* SI-2940 */ - && !tparam.isFBounded /* SI-2251 */ - && !tparam.isHigherOrderTypeParameter - && !(abounds.hi <:< tbounds.hi) - && asym.isSynthetic /* this limits us to placeholder tparams, excluding named ones */ - ) arg match { - case Bind(_, _) => enhanceBounds() - case Ident(name) if canEnhanceIdent => enhanceBounds() - case _ => + // I removed the Ident() case that partially fixed SI-1786, + // because the stricter bounds being inferred broke e.g., slick + // worse, the fix was compilation order-dependent + // sharpenQuantifierBounds (used in skolemizeExistential) has an alternative fix (SI-6169) that's less invasive + case Bind(_, _) => enhanceBounds() + case _ => } } } diff --git a/src/compiler/scala/tools/nsc/util/package.scala b/src/compiler/scala/tools/nsc/util/package.scala index 4237f36ade..bd95fdbb50 100644 --- a/src/compiler/scala/tools/nsc/util/package.scala +++ b/src/compiler/scala/tools/nsc/util/package.scala @@ -7,7 +7,7 @@ package scala package tools package nsc -import java.io.{ OutputStream, PrintStream, ByteArrayOutputStream, PrintWriter, StringWriter } +import java.io.{ OutputStream, PrintStream, ByteArrayOutputStream, PrintWriter, StringWriter, Reader } package object util { // forwarder for old code that builds against 2.9 and 2.10 @@ -46,6 +46,17 @@ package object util { (result, ts2 filterNot (ts1 contains _)) } + def stringFromReader(reader: Reader) = { + val writer = new StringWriter() + var c = reader.read() + while(c != -1) { + writer.write(c) + c = reader.read() + } + reader.close() + writer.toString() + } + /** Generate a string using a routine that wants to write on a stream. */ def stringFromWriter(writer: PrintWriter => Unit): String = { val stringWriter = new StringWriter() |