diff options
Diffstat (limited to 'src/compiler')
33 files changed, 767 insertions, 421 deletions
diff --git a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala index 90fb41f80b..3a97089d51 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala @@ -131,9 +131,9 @@ trait GenSymbols { if (sym.isCapturedVariable) { assert(binding.isInstanceOf[Ident], showRaw(binding)) val capturedBinding = referenceCapturedVariable(sym) - Reification(name, capturedBinding, mirrorBuildCall(nme.newFreeTerm, reify(sym.name.toString), capturedBinding, mirrorBuildCall(nme.flagsFromBits, reify(sym.flags)), reify(origin(sym)))) + Reification(name, capturedBinding, mirrorBuildCall(nme.newFreeTerm, reify(sym.name.toString), capturedBinding, mirrorBuildCall(nme.FlagsRepr, reify(sym.flags)), reify(origin(sym)))) } else { - Reification(name, binding, mirrorBuildCall(nme.newFreeTerm, reify(sym.name.toString), binding, mirrorBuildCall(nme.flagsFromBits, reify(sym.flags)), reify(origin(sym)))) + Reification(name, binding, mirrorBuildCall(nme.newFreeTerm, reify(sym.name.toString), binding, mirrorBuildCall(nme.FlagsRepr, reify(sym.flags)), reify(origin(sym)))) } } @@ -142,7 +142,7 @@ trait GenSymbols { if (reifyDebug) println("Free type: %s (%s)".format(sym, sym.accurateKindString)) state.reificationIsConcrete = false val name: TermName = nme.REIFY_FREE_PREFIX append sym.name - Reification(name, binding, mirrorBuildCall(nme.newFreeType, reify(sym.name.toString), mirrorBuildCall(nme.flagsFromBits, reify(sym.flags)), reify(origin(sym)))) + Reification(name, binding, mirrorBuildCall(nme.newFreeType, reify(sym.name.toString), mirrorBuildCall(nme.FlagsRepr, reify(sym.flags)), reify(origin(sym)))) } def reifySymDef(sym: Symbol): Tree = @@ -150,7 +150,7 @@ trait GenSymbols { if (reifyDebug) println("Sym def: %s (%s)".format(sym, sym.accurateKindString)) val name: TermName = nme.REIFY_SYMDEF_PREFIX append sym.name def reifiedOwner = if (sym.owner.isLocatable) reify(sym.owner) else reifySymDef(sym.owner) - Reification(name, Ident(sym), mirrorBuildCall(nme.newNestedSymbol, reifiedOwner, reify(sym.name), reify(sym.pos), mirrorBuildCall(nme.flagsFromBits, reify(sym.flags)), reify(sym.isClass))) + Reification(name, Ident(sym), mirrorBuildCall(nme.newNestedSymbol, reifiedOwner, reify(sym.name), reify(sym.pos), mirrorBuildCall(nme.FlagsRepr, reify(sym.flags)), reify(sym.isClass))) } case class Reification(name: Name, binding: Tree, tree: Tree) diff --git a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala index 3507c2a173..9de8451873 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala @@ -83,8 +83,12 @@ trait GenTrees { reifyProduct(tree) } + def reifyFlags(flags: FlagSet) = + if (flags != 0) reifyBuildCall(nme.FlagsRepr, flags) else mirrorSelect(nme.NoFlags) + def reifyModifiers(m: global.Modifiers) = - mirrorFactoryCall(nme.Modifiers, mirrorBuildCall(nme.flagsFromBits, reify(m.flags)), reify(m.privateWithin), reify(m.annotations)) + if (m == NoMods) mirrorSelect(nme.NoMods) + else mirrorFactoryCall(nme.Modifiers, reifyFlags(m.flags), reify(m.privateWithin), reify(m.annotations)) private def spliceTree(tree: Tree): Tree = { tree match { diff --git a/src/compiler/scala/reflect/reify/utils/Extractors.scala b/src/compiler/scala/reflect/reify/utils/Extractors.scala index d5f27dc119..9af8f2de2a 100644 --- a/src/compiler/scala/reflect/reify/utils/Extractors.scala +++ b/src/compiler/scala/reflect/reify/utils/Extractors.scala @@ -164,6 +164,16 @@ trait Extractors { } } + // abstract over possible additional .apply select + // which is sometimes inserted after desugaring of calls + object ApplyCall { + def unapply(tree: Tree): Option[(Tree, List[Tree])] = tree match { + case Apply(Select(id, nme.apply), args) => Some((id, args)) + case Apply(id, args) => Some((id, args)) + case _ => None + } + } + sealed abstract class FreeDefExtractor(acceptTerms: Boolean, acceptTypes: Boolean) { def unapply(tree: Tree): Option[(Tree, TermName, Tree, Long, String)] = { def acceptFreeTermFactory(name: Name) = { @@ -175,10 +185,10 @@ trait Extractors { ValDef(_, name, _, Apply( Select(Select(uref1 @ Ident(_), build1), freeTermFactory), _ :+ - Apply(Select(Select(uref2 @ Ident(_), build2), flagsFromBits), List(Literal(Constant(flags: Long)))) :+ + ApplyCall(Select(Select(uref2 @ Ident(_), build2), flagsRepr), List(Literal(Constant(flags: Long)))) :+ Literal(Constant(origin: String)))) if uref1.name == nme.UNIVERSE_SHORT && build1 == nme.build && acceptFreeTermFactory(freeTermFactory) && - uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsFromBits == nme.flagsFromBits => + uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsRepr == nme.FlagsRepr => Some((uref1, name, reifyBinding(tree), flags, origin)) case _ => None @@ -208,10 +218,10 @@ trait Extractors { _, _, _, - Apply(Select(Select(uref2 @ Ident(_), build2), flagsFromBits), List(Literal(Constant(flags: Long)))), + ApplyCall(Select(Select(uref2 @ Ident(_), build2), flagsRepr), List(Literal(Constant(flags: Long)))), Literal(Constant(isClass: Boolean))))) if uref1.name == nme.UNIVERSE_SHORT && build1 == nme.build && newNestedSymbol == nme.newNestedSymbol && - uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsFromBits == nme.flagsFromBits => + uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsRepr == nme.FlagsRepr => Some((uref1, name, flags, isClass)) case _ => None diff --git a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala index 0903bc481c..e37b861461 100644 --- a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala +++ b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala @@ -32,7 +32,7 @@ trait NodePrinters { s = "List\\[List\\[.*?\\].*?\\]".r.replaceAllIn(s, "List") s = "List\\[.*?\\]".r.replaceAllIn(s, "List") s = s.replace("immutable.this.Nil", "List()") - s = """build\.flagsFromBits\((\d+)[lL]\)""".r.replaceAllIn(s, m => { + s = """build\.FlagsRepr\((\d+)[lL]\)""".r.replaceAllIn(s, m => { flagsAreUsed = true show(m.group(1).toLong) }) diff --git a/src/compiler/scala/tools/ant/templates/tool-windows.tmpl b/src/compiler/scala/tools/ant/templates/tool-windows.tmpl index a3a95ffd37..1288eb0b7c 100644 --- a/src/compiler/scala/tools/ant/templates/tool-windows.tmpl +++ b/src/compiler/scala/tools/ant/templates/tool-windows.tmpl @@ -22,13 +22,74 @@ if "%~1"=="-toolcp" ( goto another_param ) -set _LINE_PARAMS=%1 +rem We keep in _JAVA_PARAMS all -J-prefixed and -D-prefixed arguments +set _JAVA_PARAMS= + +if [%1]==[] goto param_afterloop +set _TEST_PARAM=%~1 +if not "%_TEST_PARAM:~0,1%"=="-" goto param_afterloop + +rem ignore -e "scala code" +if "%_TEST_PARAM:~0,2%"=="-e" ( + shift + shift + if [%1]==[] goto param_afterloop +) + +set _TEST_PARAM=%~1 +if "%_TEST_PARAM:~0,2%"=="-J" ( + set _JAVA_PARAMS=%_TEST_PARAM:~2% +) + +if "%_TEST_PARAM:~0,2%"=="-D" ( + rem test if this was double-quoted property "-Dprop=42" + for /F "delims== tokens=1-2" %%G in ("%_TEST_PARAM%") DO ( + if not "%%G" == "%_TEST_PARAM%" ( + rem double quoted: "-Dprop=42" -> -Dprop="42" + set _JAVA_PARAMS=%%G="%%H" + ) else if [%2] neq [] ( + rem it was a normal property: -Dprop=42 or -Drop="42" + set _JAVA_PARAMS=%_TEST_PARAM%=%2 + shift + ) + ) +) + :param_loop shift + if [%1]==[] goto param_afterloop -set _LINE_PARAMS=%_LINE_PARAMS% %1 +set _TEST_PARAM=%~1 +if not "%_TEST_PARAM:~0,1%"=="-" goto param_afterloop + +rem ignore -e "scala code" +if "%_TEST_PARAM:~0,2%"=="-e" ( + shift + shift + if [%1]==[] goto param_afterloop +) + +set _TEST_PARAM=%~1 +if "%_TEST_PARAM:~0,2%"=="-J" ( + set _JAVA_PARAMS=%_JAVA_PARAMS% %_TEST_PARAM:~2% +) + +if "%_TEST_PARAM:~0,2%"=="-D" ( + rem test if this was double-quoted property "-Dprop=42" + for /F "delims== tokens=1-2" %%G in ("%_TEST_PARAM%") DO ( + if not "%%G" == "%_TEST_PARAM%" ( + rem double quoted: "-Dprop=42" -> -Dprop="42" + set _JAVA_PARAMS=%_JAVA_PARAMS% %%G="%%H" + ) else if [%2] neq [] ( + rem it was a normal property: -Dprop=42 or -Drop="42" + set _JAVA_PARAMS=%_JAVA_PARAMS% %_TEST_PARAM%=%2 + shift + ) + ) +) goto param_loop :param_afterloop + if "%OS%" NEQ "Windows_NT" ( echo "Warning, your version of Windows is not supported. Attempting to start scala anyway." ) @@ -51,6 +112,9 @@ rem We use the value of the JAVA_OPTS environment variable if defined set _JAVA_OPTS=%JAVA_OPTS% if not defined _JAVA_OPTS set _JAVA_OPTS=@javaflags@ +rem We append _JAVA_PARAMS java arguments to JAVA_OPTS if necessary +if defined _JAVA_PARAMS set _JAVA_OPTS=%_JAVA_OPTS% %_JAVA_PARAMS% + set _TOOL_CLASSPATH=@classpath@ if "%_TOOL_CLASSPATH%"=="" ( for %%f in ("!_SCALA_HOME!\lib\*") do call :add_cpath "%%f" diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 3f2d759a6d..0e3b2993c7 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -352,9 +352,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // Here comes another one... override protected val enableTypeVarExperimentals = settings.Xexperimental.value - def getSourceFile(f: AbstractFile): BatchSourceFile = - if (settings.script.isSetByUser) ScriptSourceFile(f, reader read f) - else new BatchSourceFile(f, reader read f) + def getSourceFile(f: AbstractFile): BatchSourceFile = new BatchSourceFile(f, reader read f) def getSourceFile(name: String): SourceFile = { val f = AbstractFile.getFile(name) @@ -1490,20 +1488,23 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } } - /** Compile list of source files */ - def compileSources(sources: List[SourceFile]) { - // there is a problem already, e.g. a plugin was passed a bad option - if (reporter.hasErrors) - return + /** Compile list of source files, + * unless there is a problem already, + * such as a plugin was passed a bad option. + */ + def compileSources(sources: List[SourceFile]) = if (!reporter.hasErrors) { - // nothing to compile, but we should still report use of deprecated options - if (sources.isEmpty) { + def checkDeprecations() = { checkDeprecatedSettings(newCompilationUnit("")) reportCompileErrors() - return } - compileUnits(sources map (new CompilationUnit(_)), firstPhase) + val units = sources map scripted map (new CompilationUnit(_)) + + units match { + case Nil => checkDeprecations() // nothing to compile, report deprecated options + case _ => compileUnits(units, firstPhase) + } } def compileUnits(units: List[CompilationUnit], fromPhase: Phase): Unit = @@ -1605,12 +1606,18 @@ class Global(var currentSettings: Settings, var reporter: Reporter) catch { case ex: IOException => globalError(ex.getMessage()) } } + /** If this compilation is scripted, convert the source to a script source. */ + private def scripted(s: SourceFile) = s match { + case b: BatchSourceFile if settings.script.isSetByUser => ScriptSourceFile(b) + case _ => s + } + /** Compile abstract file until `globalPhase`, but at least * to phase "namer". */ def compileLate(file: AbstractFile) { if (!compiledFiles(file.path)) - compileLate(new CompilationUnit(getSourceFile(file))) + compileLate(new CompilationUnit(scripted(getSourceFile(file)))) } /** Compile abstract file until `globalPhase`, but at least to phase "namer". diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index ad1977b9aa..7122e864a4 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -264,42 +264,6 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { mkNew(Nil, emptyValDef, stats1, NoPosition, NoPosition) } - /** Create positioned tree representing an object creation <new parents { stats } - * @param npos the position of the new - * @param cpos the position of the anonymous class starting with parents - */ - def mkNew(parents: List[Tree], self: ValDef, stats: List[Tree], - npos: Position, cpos: Position): Tree = - if (parents.isEmpty) - mkNew(List(scalaAnyRefConstr), self, stats, npos, cpos) - else if (parents.tail.isEmpty && stats.isEmpty) { - // `Parsers.template` no longer differentiates tpts and their argss - // e.g. `C()` will be represented as a single tree Apply(Ident(C), Nil) - // instead of parents = Ident(C), argss = Nil as before - // this change works great for things that are actually templates - // but in this degenerate case we need to perform postprocessing - val app = treeInfo.dissectApplied(parents.head) - atPos(npos union cpos) { New(app.callee, app.argss) } - } else { - val x = tpnme.ANON_CLASS_NAME - atPos(npos union cpos) { - Block( - List( - atPos(cpos) { - ClassDef( - Modifiers(FINAL), x, Nil, - mkTemplate(parents, self, NoMods, ListOfNil, stats, cpos.focus)) - }), - atPos(npos) { - New( - Ident(x) setPos npos.focus, - Nil) - } - ) - } - } - def mkSyntheticParam(pname: TermName) = ValDef(Modifiers(PARAM | SYNTHETIC), pname, TypeTree(), EmptyTree) - } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 94270e4cf3..52aa11cb40 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1445,7 +1445,7 @@ self => // The case still missed is unparenthesized single argument, like "x: Int => x + 1", which // may be impossible to distinguish from a self-type and so remains an error. (See #1564) def lhsIsTypedParamList() = t match { - case Parens(xs) if xs forall (_.isInstanceOf[Typed]) => true + case Parens(xs) if xs.forall(isTypedParam) => true case _ => false } if (in.token == ARROW && (location != InTemplate || lhsIsTypedParamList)) { @@ -1458,6 +1458,8 @@ self => parseOther } + def isTypedParam(t: Tree) = t.isInstanceOf[Typed] + /** {{{ * Expr ::= implicit Id => Expr * }}} @@ -2704,8 +2706,7 @@ self => syntaxError("classes are not allowed to be virtual", skipIt = false) } val template = templateOpt(mods1, name, constrMods withAnnotations constrAnnots, vparamss, tstart) - if (isInterface(mods1, template.body)) mods1 |= Flags.INTERFACE - val result = ClassDef(mods1, name, tparams, template) + val result = gen.mkClassDef(mods1, name, tparams, template) // Context bounds generate implicit parameters (part of the template) with types // from tparams: we need to ensure these don't overlap if (!classContextBounds.isEmpty) @@ -2796,16 +2797,7 @@ self => // @S: pre template body cannot stub like post body can! val (self, body) = templateBody(isPre = true) if (in.token == WITH && (self eq emptyValDef)) { - val earlyDefs: List[Tree] = body flatMap { - case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred => - List(copyValDef(vdef)(mods = mods | Flags.PRESUPER)) - case tdef @ TypeDef(mods, name, tparams, rhs) => - List(treeCopy.TypeDef(tdef, mods | Flags.PRESUPER, name, tparams, rhs)) - case stat if !stat.isEmpty => - syntaxError(stat.pos, "only type definitions and concrete field definitions allowed in early object initialization section", skipIt = false) - List() - case _ => List() - } + val earlyDefs: List[Tree] = body.map(ensureEarlyDef).filter(_.nonEmpty) in.nextToken() val parents = templateParents() val (self1, body1) = templateBodyOpt(parenMeansSyntaxError = false) @@ -2820,8 +2812,18 @@ self => } } - def isInterface(mods: Modifiers, body: List[Tree]): Boolean = - mods.isTrait && (body forall treeInfo.isInterfaceMember) + def ensureEarlyDef(tree: Tree): Tree = tree match { + case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred => + copyValDef(vdef)(mods = mods | Flags.PRESUPER) + case tdef @ TypeDef(mods, name, tparams, rhs) => + deprecationWarning(tdef.pos.point, "early type members are deprecated. Move them to the regular body: the semantics are the same.") + treeCopy.TypeDef(tdef, mods | Flags.PRESUPER, name, tparams, rhs) + case stat if !stat.isEmpty => + syntaxError(stat.pos, "only concrete field definitions allowed in early object initialization section", skipIt = false) + EmptyTree + case _ => + EmptyTree + } /** {{{ * ClassTemplateOpt ::= `extends' ClassTemplate | [[`extends'] TemplateBody] @@ -2830,7 +2832,7 @@ self => * }}} */ def templateOpt(mods: Modifiers, name: Name, constrMods: Modifiers, vparamss: List[List[ValDef]], tstart: Int): Template = { - val (parents0, self, body) = ( + val (parents, self, body) = ( if (in.token == EXTENDS || in.token == SUBTYPE && mods.isTrait) { in.nextToken() template() @@ -2841,26 +2843,21 @@ self => (List(), self, body) } ) - def anyrefParents() = { - val caseParents = if (mods.isCase) List(productConstr, serializableConstr) else Nil - parents0 ::: caseParents match { - case Nil => atInPos(scalaAnyRefConstr) :: Nil - case ps => ps - } - } def anyvalConstructor() = ( // Not a well-formed constructor, has to be finished later - see note // regarding AnyVal constructor in AddInterfaces. DefDef(NoMods, nme.CONSTRUCTOR, Nil, ListOfNil, TypeTree(), Block(Nil, literalUnit)) ) - val tstart0 = if (body.isEmpty && in.lastOffset < tstart) in.lastOffset else tstart + val parentPos = o2p(in.offset) + val tstart1 = if (body.isEmpty && in.lastOffset < tstart) in.lastOffset else tstart - atPos(tstart0) { + atPos(tstart1) { // Exclude only the 9 primitives plus AnyVal. if (inScalaRootPackage && ScalaValueClassNames.contains(name)) - Template(parents0, self, anyvalConstructor :: body) + Template(parents, self, anyvalConstructor :: body) else - gen.mkTemplate(anyrefParents(), self, constrMods, vparamss, body, o2p(tstart)) + gen.mkTemplate(gen.mkParents(mods, parents, parentPos), + self, constrMods, vparamss, body, o2p(tstart)) } } @@ -3011,19 +3008,23 @@ self => def refineStatSeq(): List[Tree] = checkNoEscapingPlaceholders { val stats = new ListBuffer[Tree] while (!isStatSeqEnd) { - if (isDclIntro) { // don't IDE hook - stats ++= joinComment(defOrDcl(in.offset, NoMods)) - } else if (!isStatSep) { - syntaxErrorOrIncomplete( - "illegal start of declaration"+ - (if (inFunReturnType) " (possible cause: missing `=' in front of current method body)" - else ""), skipIt = true) - } + stats ++= refineStat() if (in.token != RBRACE) acceptStatSep() } stats.toList } + def refineStat(): List[Tree] = + if (isDclIntro) { // don't IDE hook + joinComment(defOrDcl(in.offset, NoMods)) + } else if (!isStatSep) { + syntaxErrorOrIncomplete( + "illegal start of declaration"+ + (if (inFunReturnType) " (possible cause: missing `=' in front of current method body)" + else ""), skipIt = true) + Nil + } else Nil + /** overridable IDE hook for local definitions of blockStatSeq * Here's an idea how to fill in start and end positions. def localDef : List[Tree] = { @@ -3066,7 +3067,7 @@ self => while (!isStatSeqEnd && !isCaseDefStart) { if (in.token == IMPORT) { stats ++= importClause() - acceptStatSep() + acceptStatSepOpt() } else if (isExprIntro) { stats += statement(InBlock) diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index ed694023d7..28e3217449 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -229,11 +229,7 @@ abstract class TreeBuilder { } /** Create block of statements `stats` */ - def makeBlock(stats: List[Tree]): Tree = - if (stats.isEmpty) Literal(Constant(())) - else if (!stats.last.isTerm) Block(stats, Literal(Constant(()))) - else if (stats.length == 1) stats.head - else Block(stats.init, stats.last) + def makeBlock(stats: List[Tree]): Tree = gen.mkBlock(stats) def makeFilter(tree: Tree, condition: Tree, scrutineeName: String): Tree = { val cases = List( @@ -520,8 +516,7 @@ abstract class TreeBuilder { } /** Create a tree representing the function type (argtpes) => restpe */ - def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = - AppliedTypeTree(rootScalaDot(newTypeName("Function" + argtpes.length)), argtpes ::: List(restpe)) + def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = gen.mkFunctionTypeTree(argtpes, restpe) /** Append implicit parameter section if `contextBounds` nonempty */ def addEvidenceParams(owner: Name, vparamss: List[List[ValDef]], contextBounds: List[Tree]): List[List[ValDef]] = { diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 3947db2dd4..edb1c55224 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -2316,7 +2316,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { lastLineNr = currentLineNr val lineLab = new asm.Label jmethod.visitLabel(lineLab) - lnEntries ::= LineNumberEntry(currentLineNr, lineLab) + val actual = iPos inUltimateSource iPos.source + lnEntries ::= LineNumberEntry(actual.line, lineLab) } } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 14e3f5b642..2b96961291 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -874,9 +874,14 @@ abstract class ClassfileParser { case ENUM_TAG => val t = pool.getType(index) val n = readName() - val s = t.typeSymbol.companionModule.info.decls.lookup(n) - assert(s != NoSymbol, t) - Some(LiteralAnnotArg(Constant(s))) + val module = t.typeSymbol.companionModule + val s = module.info.decls.lookup(n) + if (s != NoSymbol) Some(LiteralAnnotArg(Constant(s))) + else { + warning(s"""While parsing annotations in ${in.file}, could not find $n in enum $module.\nThis is likely due to an implementation restriction: an annotation argument cannot refer to a member of the annotated class (SI-7014).""") + None + } + case ARRAY_TAG => val arr = new ArrayBuffer[ClassfileAnnotArg]() var hasError = false diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index cbe4f69d25..2ec7e97ac5 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -174,7 +174,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { omittables ++= outerCandidatesForElision val bodyOfOuterAccessor: Map[Symbol, DefDef] = - defBuf collect { case dd: DefDef if outerCandidatesForElision(dd.symbol) => dd.symbol -> dd } toMap + defBuf.collect { case dd: DefDef if outerCandidatesForElision(dd.symbol) => dd.symbol -> dd }.toMap // no point traversing further once omittables is empty, all candidates ruled out already. object detectUsages extends Traverser { diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 48263a496e..d6a6e027cb 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -373,16 +373,10 @@ abstract class ExplicitOuter extends InfoTransform /** The definition tree of the outer accessor of current class */ - def outerAccessorDef: Tree = { - val outerAcc = outerAccessor(currentClass) - val rhs: Tree = - if (outerAcc.isDeferred) EmptyTree - else This(currentClass) DOT outerField(currentClass) - - /* If we don't re-type the tree, we see self-type related crashes like #266. - */ - localTyper typed { - (DEF(outerAcc) withPos currentClass.pos withType null) === rhs + def outerAccessorDef: Tree = localTyper typed { + outerAccessor(currentClass) match { + case acc if acc.isDeferred => DefDef(acc, EmptyTree) + case acc => DefDef(acc, Select(This(currentClass), outerField(currentClass))) } } @@ -404,12 +398,8 @@ abstract class ExplicitOuter extends InfoTransform else if (mixinPrefix.typeArgs.nonEmpty) gen.mkAttributedThis(mixinPrefix.typeSymbol) else gen.mkAttributedQualifier(mixinPrefix) ) - localTyper typed { - (DEF(outerAcc) withPos currentClass.pos) === { - // Need to cast for nested outer refs in presence of self-types. See ticket #3274. - gen.mkCast(transformer.transform(path), outerAcc.info.resultType) - } - } + // Need to cast for nested outer refs in presence of self-types. See ticket #3274. + localTyper typed DefDef(outerAcc, gen.mkCast(transformer.transform(path), outerAcc.info.resultType)) } /** The main transformation method */ diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala b/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala index 3c7dc79636..114bcba5df 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala @@ -202,15 +202,16 @@ trait Solving extends Logic { withLit(findModelFor(dropUnit(f, unitLit)), unitLit) case _ => // partition symbols according to whether they appear in positive and/or negative literals - val pos = new mutable.HashSet[Sym]() - val neg = new mutable.HashSet[Sym]() + // SI-7020 Linked- for deterministic counter examples. + val pos = new mutable.LinkedHashSet[Sym]() + val neg = new mutable.LinkedHashSet[Sym]() f.foreach{_.foreach{ lit => if (lit.pos) pos += lit.sym else neg += lit.sym }} // appearing in both positive and negative - val impures = pos intersect neg + val impures: mutable.LinkedHashSet[Sym] = pos intersect neg // appearing only in either positive/negative positions - val pures = (pos ++ neg) -- impures + val pures: mutable.LinkedHashSet[Sym] = (pos ++ neg) -- impures if (pures nonEmpty) { val pureSym = pures.head diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 8d42bf94f3..cd2b9b3a97 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -19,6 +19,8 @@ trait Contexts { self: Analyzer => import definitions.{ JavaLangPackage, ScalaPackage, PredefModule, ScalaXmlTopScope, ScalaXmlPackage } import ContextMode._ + protected def onTreeCheckerError(pos: Position, msg: String): Unit = () + object NoContext extends Context(EmptyTree, NoSymbol, EmptyScope, NoCompilationUnit, null) { // We can't pass the uninitialized `this`. Instead, we treat null specially in `Context#outer` @@ -531,8 +533,8 @@ trait Contexts { self: Analyzer => if (msg endsWith ds) msg else msg + ds } - private def unitError(pos: Position, msg: String) = - unit.error(pos, if (checking) "\n**** ERROR DURING INTERNAL CHECKING ****\n" + msg else msg) + private def unitError(pos: Position, msg: String): Unit = + if (checking) onTreeCheckerError(pos, msg) else unit.error(pos, msg) @inline private def issueCommon(err: AbsTypeError)(pf: PartialFunction[AbsTypeError, Unit]) { if (settings.Yissuedebug) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 3a6b25f1cd..fbe8cd77fb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -316,7 +316,7 @@ trait Implicits { */ class ImplicitSearch(tree: Tree, pt: Type, isView: Boolean, context0: Context, pos0: Position = NoPosition) extends Typer(context0) with ImplicitsContextErrors { val searchId = implicitSearchId() - private def typingLog(what: String, msg: String) = + private def typingLog(what: String, msg: => String) = typingStack.printTyping(tree, f"[search #$searchId] $what $msg") import infer._ diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 3a5845c8ca..263b5ad784 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -397,6 +397,12 @@ trait MethodSynthesis { if (mods.isDeferred) basisSym else basisSym.getter(enclClass) ) + // Range position errors ensue if we don't duplicate this in some + // circumstances (at least: concrete vals with existential types.) + private def tptOriginal = ( + if (mods.isDeferred) tree.tpt // keep type tree of original abstract field + else tree.tpt.duplicate setPos tree.tpt.pos.focus // focused position of original tpt + ) override def derivedTree: DefDef = { // For existentials, don't specify a type for the getter, even one derived @@ -407,16 +413,11 @@ trait MethodSynthesis { // starts compiling (instead of failing like it's supposed to) because the typer // expects to be able to identify escaping locals in typedDefDef, and fails to // spot that brand of them. In other words it's an artifact of the implementation. - val tpt = derivedSym.tpe.finalResultType match { + val tpt = atPos(derivedSym.pos.focus)(derivedSym.tpe.finalResultType match { case ExistentialType(_, _) => TypeTree() case _ if mods.isDeferred => TypeTree() case tp => TypeTree(tp) - } - tpt setPos derivedSym.pos.focus - // keep type tree of original abstract field - if (mods.isDeferred) - tpt setOriginal tree.tpt - + }) // TODO - reconcile this with the DefDef creator in Trees (which // at this writing presented no way to pass a tree in for tpt.) atPos(derivedSym.pos) { @@ -425,7 +426,7 @@ trait MethodSynthesis { derivedSym.name.toTermName, Nil, Nil, - tpt, + tpt setOriginal tptOriginal, if (mods.isDeferred) EmptyTree else fieldSelection ) setSymbol derivedSym } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 95d6ca52ec..454f913412 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1418,14 +1418,6 @@ trait Namers extends MethodSynthesis { annCtx.setReportErrors() // need to be lazy, #1782. beforeTyper to allow inferView in annotation args, SI-5892. AnnotationInfo lazily { - if (typer.context ne ctx) - log(sm"""|The var `typer.context` in ${Namer.this} was mutated before the annotation ${ann} was forced. - | - |current value = ${typer.context} - |original value = $ctx - | - |This confirms the hypothesis for the cause of SI-7603. If you see this message, please comment on that ticket.""") - enteringTyper(newTyper(annCtx) typedAnnotation ann) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 8e9933f734..dea4c46e79 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -162,7 +162,7 @@ trait NamesDefaults { self: Analyzer => // never used for constructor calls, they always have a stable qualifier def blockWithQualifier(qual: Tree, selected: Name) = { - val sym = blockTyper.context.owner.newValue(unit.freshTermName("qual$"), qual.pos, newFlags = ARTIFACT) setInfo qual.tpe + val sym = blockTyper.context.owner.newValue(unit.freshTermName("qual$"), qual.pos, newFlags = ARTIFACT) setInfo uncheckedBounds(qual.tpe) blockTyper.context.scope enter sym val vd = atPos(sym.pos)(ValDef(sym, qual) setType NoType) // it stays in Vegas: SI-5720, SI-5727 @@ -289,9 +289,10 @@ trait NamesDefaults { self: Analyzer => 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, newFlags = ARTIFACT) setInfo ( - if (byName) functionType(Nil, argTpe) else argTpe - ) + val s = context.owner.newValue(unit.freshTermName("x$"), arg.pos, newFlags = ARTIFACT) setInfo { + val tp = if (byName) functionType(Nil, argTpe) else argTpe + uncheckedBounds(tp) + } Some((context.scope.enter(s), byName, repeated)) }) map2(symPs, args) { diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala index 8bf9ce49be..38a3f18bf8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala @@ -45,12 +45,6 @@ trait PatternTypers { } } - // when true: - // - we may virtualize matches (if -Xexperimental and there's a suitable __match in scope) - // - we synthesize PartialFunction implementations for `x => x match {...}` and `match {...}` when the expected type is PartialFunction - // this is disabled by: interactive compilation (we run it for scaladoc due to SI-5933) - protected def newPatternMatching = true // presently overridden in the presentation compiler - trait PatternTyper { self: Typer => diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 5929cab1d1..32e908e03b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1414,17 +1414,35 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans false } - private def checkTypeRef(tp: Type, tree: Tree) = tp match { + private def checkTypeRef(tp: Type, tree: Tree, skipBounds: Boolean) = tp match { case TypeRef(pre, sym, args) => checkDeprecated(sym, tree.pos) if(sym.isJavaDefined) sym.typeParams foreach (_.cookJavaRawInfo()) - if (!tp.isHigherKinded) + if (!tp.isHigherKinded && !skipBounds) checkBounds(tree, pre, sym.owner, sym.typeParams, args) case _ => } - private def checkAnnotations(tpes: List[Type], tree: Tree) = tpes foreach (tp => checkTypeRef(tp, tree)) + private def checkTypeRefBounds(tp: Type, tree: Tree) = { + var skipBounds = false + tp match { + case AnnotatedType(ann :: Nil, underlying, selfSym) if ann.symbol == UncheckedBoundsClass => + skipBounds = true + underlying + case TypeRef(pre, sym, args) => + if (!tp.isHigherKinded && !skipBounds) + checkBounds(tree, pre, sym.owner, sym.typeParams, args) + tp + case _ => + tp + } + } + + private def checkAnnotations(tpes: List[Type], tree: Tree) = tpes foreach { tp => + checkTypeRef(tp, tree, skipBounds = false) + checkTypeRefBounds(tp, tree) + } private def doTypeTraversal(tree: Tree)(f: Type => Unit) = if (!inPattern) tree.tpe foreach f private def applyRefchecksToAnnotations(tree: Tree): Unit = { @@ -1453,8 +1471,9 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } doTypeTraversal(tree) { - case AnnotatedType(annots, _, _) => applyChecks(annots) - case _ => + case tp @ AnnotatedType(annots, _, _) => + applyChecks(annots) + case tp => } case _ => } @@ -1639,13 +1658,27 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } val existentialParams = new ListBuffer[Symbol] - doTypeTraversal(tree) { // check all bounds, except those that are existential type parameters - case ExistentialType(tparams, tpe) => + var skipBounds = false + // check all bounds, except those that are existential type parameters + // or those within typed annotated with @uncheckedBounds + doTypeTraversal(tree) { + case tp @ ExistentialType(tparams, tpe) => existentialParams ++= tparams - case t: TypeRef => - checkTypeRef(deriveTypeWithWildcards(existentialParams.toList)(t), tree) + case ann: AnnotatedType if ann.hasAnnotation(UncheckedBoundsClass) => + // SI-7694 Allow code synthetizers to disable checking of bounds for TypeTrees based on inferred LUBs + // which might not conform to the constraints. + skipBounds = true + case tp: TypeRef => + val tpWithWildcards = deriveTypeWithWildcards(existentialParams.toList)(tp) + checkTypeRef(tpWithWildcards, tree, skipBounds) case _ => } + if (skipBounds) { + tree.tpe = tree.tpe.map { + _.filterAnnotations(_.symbol != UncheckedBoundsClass) + } + } + tree case TypeApply(fn, args) => @@ -1715,6 +1748,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans inPattern = false super.transform(result) } + case ValDef(_, _, _, _) if treeInfo.hasSynthCaseSymbol(result) => + deriveValDef(result)(transform) // SI-7716 Don't refcheck the tpt of the synthetic val that holds the selector. case _ => super.transform(result) } diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index 1c8d37ef39..3a188c0044 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -9,15 +9,69 @@ package typechecker import scala.collection.mutable import mutable.ListBuffer import util.returning +import scala.reflect.internal.util.shortClassOfInstance +import scala.reflect.internal.util.StringOps._ abstract class TreeCheckers extends Analyzer { import global._ - private def classstr(x: AnyRef) = (x.getClass.getName split """\\.|\\$""").last + override protected def onTreeCheckerError(pos: Position, msg: String) { + if (settings.fatalWarnings) + currentUnit.warning(pos, "\n** Error during internal checking:\n" + msg) + } + + case class DiffResult[T](lost: List[T], gained: List[T]) { + def isEmpty = lost.isEmpty && gained.isEmpty + def lost_s = if (lost.isEmpty) "" else lost.mkString("lost: ", ", ", "") + def gained_s = if (gained.isEmpty) "" else gained.mkString("gained: ", ", ", "") + override def toString = ojoin(lost_s, gained_s) + } + + def diffList[T](xs: List[T], ys: List[T]): DiffResult[T] = + DiffResult(xs filterNot ys.contains, ys filterNot xs.contains) + + def diffTrees(t1: Tree, t2: Tree): DiffResult[Tree] = + diffList(t1 filter (_ ne t1), t2 filter (_ ne t2)) + + def diffTemplates(t1: Template, t2: Template): String = { + val parents = diffList(t1.parents, t2.parents).toString match { case "" => "" case s => "parents " + s } + val stats = diffList(t1.body, t2.body).toString match { case "" => "" case s => "stats " + s } + oempty(parents, stats) mkString ", " + } + + def diff(t1: Tree, t2: Tree): String = (t1, t2) match { + case (_: Literal, _: Literal) => "" + case (t1: ImplDef, t2: ImplDef) => diff(t1.impl, t2.impl) + case (t1: Template, t2: Template) => diffTemplates(t1, t2) + case _ => diffTrees(t1, t2).toString // "<error: different tree classes>" + } + + private def clean_s(s: String) = s.replaceAllLiterally("scala.collection.", "s.c.") private def typestr(x: Type) = " (tpe = " + x + ")" - private def treestr(t: Tree) = t + " [" + classstr(t) + "]" + typestr(t.tpe) + private def treestr(t: Tree) = t + " [" + classString(t) + "]" + typestr(t.tpe) private def ownerstr(s: Symbol) = "'" + s + "'" + s.locationString private def wholetreestr(t: Tree) = nodeToString(t) + "\n" + private def truncate(str: String, len: Int): String = ( + if (str.length <= len) str + else (str takeWhile (_ != '\n') take len - 3) + "..." + ) + private def signature(sym: Symbol) = clean_s(sym match { + case null => "null" + case _: ClassSymbol => sym.name + ": " + sym.tpe_* + case _ => sym.defString + }) + private def classString(x: Any) = x match { + case null => "" + case t: Tree => t.shortClass + case s: Symbol => s.shortSymbolClass + case x: AnyRef => shortClassOfInstance(x) + } + private def nonPackageOwners(s: Symbol) = s.ownerChain drop 1 takeWhile (!_.hasPackageFlag) + private def nonPackageOwnersPlusOne(s: Symbol) = nonPackageOwners(s) ::: (s.ownerChain dropWhile (!_.hasPackageFlag) take 1) + private def ownersString(s: Symbol) = nonPackageOwnersPlusOne(s) match { + case Nil => "NoSymbol" + case xs => xs mkString " -> " + } private def beststr(t: Tree) = "<" + { if (t.symbol != null && t.symbol != NoSymbol) "sym=" + ownerstr(t.symbol) @@ -25,46 +79,50 @@ abstract class TreeCheckers extends Analyzer { else t match { case x: DefTree => "name=" + x.name case x: RefTree => "reference=" + x.name - case _ => "clazz=" + classstr(t) + case _ => "clazz=" + classString(t) } } + ">" /** This is a work in progress, don't take it too seriously. */ object SymbolTracker extends Traverser { - type PhaseMap = mutable.HashMap[Symbol, List[Tree]] + type PhaseMap = mutable.Map[Symbol, List[Tree]] + def symbolTreeMap[T <: Tree]() = mutable.Map[Symbol, List[T]]() withDefaultValue Nil - val maps = ListBuffer[(Phase, PhaseMap)]() - def prev = maps.init.last._2 - def latest = maps.last._2 - val defSyms = mutable.HashMap[Symbol, List[DefTree]]() + var maps: List[(Phase, PhaseMap)] = ((NoPhase, null)) :: Nil + def prev = maps.tail.head._2 + def latest = maps.head._2 + val defSyms = symbolTreeMap[DefTree]() val newSyms = mutable.HashSet[Symbol]() val movedMsgs = new ListBuffer[String] def sortedNewSyms = newSyms.toList.distinct sortBy (_.name.toString) - def inPrev(sym: Symbol) = { - (maps.size >= 2) && (prev contains sym) - } - def record(sym: Symbol, tree: Tree) = { - if (latest contains sym) latest(sym) = latest(sym) :+ tree - else latest(sym) = List(tree) + def record(tree: Tree) { + val sym = tree.symbol + if ((sym eq null) || (sym eq NoSymbol)) return - if (inPrev(sym)) { - val prevTrees = prev(sym) + val prevMap = maps.tail.head._2 + val prevTrees = if (prevMap eq null) Nil else prevMap(sym) - if (prevTrees exists (t => (t eq tree) || (t.symbol == sym))) () - else if (prevTrees exists (_.symbol.owner == sym.owner.implClass)) { - errorFn("Noticed " + ownerstr(sym) + " moving to implementation class.") - } - else { - val s1 = (prevTrees map wholetreestr).sorted.distinct - val s2 = wholetreestr(tree) - if (s1 contains s2) () - else movedMsgs += ("\n** %s moved:\n** Previously:\n%s\n** Currently:\n%s".format(ownerstr(sym), s1 mkString ", ", s2)) - } + tree match { + case t: DefTree => defSyms(sym) ::= t + case _ => + } + + if (prevTrees.isEmpty) + newSyms += sym + else if (prevTrees exists (t => (t eq tree) || (t.symbol == sym))) + () + else if (prevTrees exists (_.symbol.owner == sym.owner.implClass)) + errorFn("Noticed " + ownerstr(sym) + " moving to implementation class.") + else { + val s1 = (prevTrees map wholetreestr).sorted.distinct + val s2 = wholetreestr(tree) + if (s1 contains s2) () + else movedMsgs += ("\n** %s moved:\n** Previously:\n%s\n** Currently:\n%s".format(ownerstr(sym), s1 mkString ", ", s2)) } - else newSyms += sym } + def reportChanges(): Unit = { // new symbols if (newSyms.nonEmpty) { @@ -88,37 +146,34 @@ abstract class TreeCheckers extends Analyzer { } def check(ph: Phase, unit: CompilationUnit): Unit = { - if (maps.isEmpty || maps.last._1 != ph) - maps += ((ph, new PhaseMap)) - + maps match { + case ((`ph`, _)) :: _ => + case _ => maps ::= ((ph, symbolTreeMap[Tree]())) + } traverse(unit.body) reportChanges() } - override def traverse(tree: Tree): Unit = { - val sym = tree.symbol - if (sym != null && sym != NoSymbol) { - record(sym, tree) - tree match { - case x: DefTree => - if (defSyms contains sym) defSyms(sym) = defSyms(sym) :+ x - else defSyms(sym) = List(x) - case _ => () - } - } - + override def traverse(tree: Tree) { + record(tree) super.traverse(tree) } } lazy val tpeOfTree = mutable.HashMap[Tree, Type]() + private lazy val reportedAlready = mutable.HashSet[(Tree, Symbol)]() + + def posstr(t: Tree): String = if (t eq null) "" else posstr(t.pos) + def posstr(p: Position): String = ( + if (p eq null) "" else { + try p.source.path + ":" + p.line + catch { case _: UnsupportedOperationException => p.toString } + } + ) - def posstr(p: Position) = - try p.source.path + ":" + p.line - catch { case _: UnsupportedOperationException => p.toString } - private var hasError: Boolean = false - def errorFn(msg: Any): Unit = {hasError = true; println("[check: %s] %s".format(phase.prev, msg))} + def errorFn(msg: Any): Unit = Console.err 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 || settings.debug) println("[check: %s] %s".format(phase.prev, msg)) @@ -127,12 +182,13 @@ abstract class TreeCheckers extends Analyzer { def assertFn(cond: Boolean, msg: => Any) = if (!cond) errorFn(msg) - private def wrap[T](msg: => Any)(body: => Unit) { + private def wrap[T](msg: => Any)(body: => T): T = { try body catch { case x: Throwable => Console.println("Caught " + x) Console.println(msg) x.printStackTrace + null.asInstanceOf[T] } } @@ -144,7 +200,6 @@ abstract class TreeCheckers extends Analyzer { } def runWithUnit[T](unit: CompilationUnit)(body: => Unit): Unit = { - hasError = false val unit0 = currentUnit currentRun.currentUnit = unit body @@ -163,22 +218,28 @@ 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) } } override def newTyper(context: Context): Typer = new TreeChecker(context) class TreeChecker(context0: Context) extends Typer(context0) { - override protected def finishMethodSynthesis(templ: Template, clazz: Symbol, context: Context): Template = { - // If we don't intercept this all the synthetics get added at every phase, - // with predictably unfortunate results. - templ - } + // If we don't intercept this all the synthetics get added at every phase, + // with predictably unfortunate results. + override protected def finishMethodSynthesis(templ: Template, clazz: Symbol, context: Context): Template = templ // XXX check for tree.original on TypeTrees. - private def treesDiffer(t1: Tree, t2: Tree) = - errorFn(t1.pos, "trees differ\n old: " + treestr(t1) + "\n new: " + treestr(t2)) + private def treesDiffer(t1: Tree, t2: Tree): Unit = { + def len1 = t1.toString.length + def len2 = t2.toString.length + def name = t1 match { + case t: NameTree => t.name + case _ => t1.summaryString + } + def summary = s"${t1.shortClass} $name differs, bytes $len1 -> $len2, " + errorFn(t1.pos, summary + diff(t1, t2)) + } + private def typesDiffer(tree: Tree, tp1: Type, tp2: Type) = errorFn(tree.pos, "types differ\n old: " + tp1 + "\n new: " + tp2 + "\n tree: " + tree) @@ -192,27 +253,45 @@ abstract class TreeCheckers extends Analyzer { if (t.symbol == NoSymbol) errorFn(t.pos, "no symbol: " + treestr(t)) - override def typed(tree: Tree, mode: Mode, pt: Type): Tree = returning(tree) { - case EmptyTree | TypeTree() => () - case _ if tree.tpe != null => - tpeOfTree.getOrElseUpdate(tree, try tree.tpe finally tree.clearType()) - - wrap(tree)(super.typed(tree, mode, pt) match { - case _: Literal => () - case x if x ne tree => treesDiffer(tree, x) - case _ => () - }) - case _ => () + private def passThrough(tree: Tree) = tree match { + case EmptyTree | TypeTree() => true + case _ => tree.tpe eq null + } + override def typed(tree: Tree, mode: Mode, pt: Type): Tree = ( + if (passThrough(tree)) + super.typed(tree, mode, pt) + else + checkedTyped(tree, mode, pt) + ) + private def checkedTyped(tree: Tree, mode: Mode, pt: Type): Tree = { + def tpe = try tree.tpe finally tree.clearType() + val recorded = tpeOfTree.getOrElseUpdate(tree, tpe) + val typed = wrap(tree)(super.typed(tree, mode, pt)) + + if (tree ne typed) + treesDiffer(tree, typed) + + tree } object precheck extends TreeStackTraverser { - override def traverse(tree: Tree) { - checkSymbolRefsRespectScope(tree) + private var enclosingMemberDefs: List[MemberDef] = Nil + private def pushMemberDef[T](md: MemberDef)(body: => T): T = { + enclosingMemberDefs ::= md + try body finally enclosingMemberDefs = enclosingMemberDefs.tail + } + override def traverse(tree: Tree): Unit = tree match { + case md: MemberDef => pushMemberDef(md)(traverseInternal(tree)) + case _ => traverseInternal(tree) + } + + private def traverseInternal(tree: Tree) { + checkSymbolRefsRespectScope(enclosingMemberDefs takeWhile (md => !md.symbol.hasPackageFlag), tree) checkReturnReferencesDirectlyEnclosingDef(tree) val sym = tree.symbol def accessed = sym.accessed - def fail(msg: String) = errorFn(tree.pos, msg + classstr(tree) + " / " + tree) + def fail(msg: String) = errorFn(tree.pos, msg + tree.shortClass + " / " + tree) tree match { case DefDef(_, _, _, _, _, _) => @@ -254,7 +333,7 @@ abstract class TreeCheckers extends Analyzer { case _ => } - if (tree.pos == NoPosition && tree != EmptyTree) + if (tree.canHaveAttrs && tree.pos == NoPosition) noPos(tree) else if (tree.tpe == null && phase.id > currentRun.typerPhase.id) noType(tree) @@ -281,57 +360,99 @@ abstract class TreeCheckers extends Analyzer { super.traverse(tree) } - private def checkSymbolRefsRespectScope(tree: Tree) { - def symbolOf(t: Tree): Symbol = Option(tree.symbol).getOrElse(NoSymbol) - val info = Option(symbolOf(tree).info).getOrElse(NoType) - val referencedSymbols: List[Symbol] = { - val directRef = tree match { - case _: RefTree => symbolOf(tree).toOption - case _ => None + private def checkSymbolRefsRespectScope(enclosingMemberDefs: List[MemberDef], tree: Tree) { + def symbolOf(t: Tree): Symbol = if (t.symbol eq null) NoSymbol else t.symbol + def typeOf(t: Tree): Type = if (t.tpe eq null) NoType else t.tpe + def infoOf(t: Tree): Type = symbolOf(t).info + def referencesInType(tp: Type) = tp collect { case TypeRef(_, sym, _) => sym } + def referencesInTree(t: Tree) = referencesInType(typeOf(t)) ++ referencesInType(infoOf(t)) + def symbolRefsInTree(t: Tree) = t collect { case t: RefTree => symbolOf(t) } + // Accessors are known to steal the type of the underlying field without cloning existential symbols at the new owner. + // This happens in Namer#accessorTypeCompleter. We just look the other way here. + if (symbolOf(tree).isAccessor) + return + + val treeSym = symbolOf(tree) + val treeInfo = infoOf(tree) + val treeTpe = typeOf(tree) + + def isOk(sym: Symbol) = treeSym hasTransOwner sym.enclosingSuchThat(x => !x.isTypeParameterOrSkolem) // account for higher order type params + def isEligible(sym: Symbol) = (sym ne NoSymbol) && ( + sym.isTypeParameter + || sym.isLocal + ) + val direct = tree match { + case _: RefTree => treeSym + case _ => NoSymbol + } + val referencedSymbols = (treeSym :: referencesInType(treeInfo)).distinct filter (sym => isEligible(sym) && !isOk(sym)) + def mk[T](what: String, x: T, str: T => String = (x: T) => "" + x): ((Any, String)) = + x -> s"%10s %-20s %s".format(what, classString(x), truncate(str(x), 80).trim) + + def encls = enclosingMemberDefs.filterNot(_.symbol == treeSym).zipWithIndex map { case (md, i) => mk(s"encl(${i+1})", md.symbol, signature) } + + def mkErrorMsg(outOfScope: Symbol): String = { + + def front = List( + mk[Tree]("tree", tree), + mk[Position]("position", tree.pos, posstr), + mk("with sym", treeSym, signature) + ) + def tpes = treeTpe match { + case NoType => Nil + case _ => mk[Type]("and tpe", treeTpe) :: Nil } - def referencedSyms(tp: Type) = (tp collect { - case TypeRef(_, sym, _) => sym - }).toList - val indirectRefs = referencedSyms(info) - (indirectRefs ++ directRef).distinct + def ref = mk[Symbol]("ref to", outOfScope, (s: Symbol) => s.nameString + " (" + s.debugFlagString + ")") + + val pairs = front ++ tpes ++ encls ++ (ref :: Nil) + val width = pairs.map(_._2.length).max + val fmt = "%-" + width + "s" + val lines = pairs map { + case (s: Symbol, msg) => fmt.format(msg) + " in " + ownersString(s) + case (x, msg) => fmt.format(msg) + } + lines.mkString("Out of scope symbol reference {\n", "\n", "\n}") } - for { - sym <- referencedSymbols - // Accessors are known to steal the type of the underlying field without cloning existential symbols at the new owner. - // This happens in Namer#accessorTypeCompleter. We just look the other way here. - if !tree.symbol.isAccessor - 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})") + referencedSymbols foreach (sym => + if (reportedAlready((tree, sym))) { + def what = tree match { + case tt: TypeTree => s"TypeTree(${tt.tpe})" + case _ => tree.shortClass + "(" + tree.symbol.nameString + ")" } - case _ => - } + } + else { + errorFn("\n" + mkErrorMsg(sym)) + reportedAlready += ((tree, sym)) + } + ) + } + + private def checkReturnReferencesDirectlyEnclosingDef(tree: Tree): Unit = 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 _ => + } + case _ => } } object postcheck extends Traverser { - override def traverse(tree: Tree) { - tree match { - case EmptyTree | TypeTree() => () - case _ => - tpeOfTree get tree foreach { oldtpe => - if (oldtpe =:= tree.tpe) () - else typesDiffer(tree, oldtpe, tree.tpe) - - tree setType oldtpe - super.traverse(tree) - } - } + override def traverse(tree: Tree): Unit = tree match { + case EmptyTree | TypeTree() => () + case _ => + tpeOfTree get tree foreach { oldtpe => + if (tree.tpe eq null) + errorFn(s"tree.tpe=null for " + tree.shortClass + " (symbol: " + classString(tree.symbol) + " " + signature(tree.symbol) + "), last seen tpe was " + oldtpe) + else if (oldtpe =:= tree.tpe) + () + else + typesDiffer(tree, oldtpe, tree.tpe) + + super.traverse(tree setType oldtpe) + } } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 13fa2a947d..695a1e2e24 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -350,11 +350,14 @@ trait TypeDiagnostics { val strings = mutable.Map[String, Set[TypeDiag]]() withDefaultValue Set() val names = mutable.Map[Name, Set[TypeDiag]]() withDefaultValue Set() - def record(t: Type, sym: Symbol) = { - val diag = TypeDiag(t, sym) + val localsSet = locals.toSet - strings("" + t) += diag - names(sym.name) += diag + def record(t: Type, sym: Symbol) = { + if (!localsSet(sym)) { + val diag = TypeDiag(t, sym) + strings("" + t) += diag + names(sym.name) += diag + } } for (tpe <- types ; t <- tpe) { t match { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index dd16b5be85..629513ada3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2436,7 +2436,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // TODO: add fallback __match sentinel to predef val matchStrategy: Tree = - if (!(newPatternMatching && settings.Xexperimental && context.isNameInScope(vpmName._match))) null // fast path, avoiding the next line if there's no __match to be seen + if (!(settings.Xexperimental && context.isNameInScope(vpmName._match))) null // fast path, avoiding the next line if there's no __match to be seen else newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match)), reportAmbiguousErrors = false) orElse (_ => null) if (matchStrategy ne null) // virtualize @@ -2713,7 +2713,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper fun.body match { // translate `x => x match { <cases> }` : PartialFunction to // `new PartialFunction { def applyOrElse(x, default) = x match { <cases> } def isDefinedAt(x) = ... }` - case Match(sel, cases) if (sel ne EmptyTree) && newPatternMatching && (pt.typeSymbol == PartialFunctionClass) => + case Match(sel, cases) if (sel ne EmptyTree) && (pt.typeSymbol == PartialFunctionClass) => // go to outer context -- must discard the context that was created for the Function since we're discarding the function // thus, its symbol, which serves as the current context.owner, is not the right owner // you won't know you're using the wrong owner until lambda lift crashes (unless you know better than to use the wrong owner) @@ -3789,7 +3789,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // we need symbol-ful originals for reification // hence we go the extra mile to hand-craft tis guy val original = arg1 match { - case tt @ TypeTree() => Annotated(ann, tt.original) + case tt @ TypeTree() if tt.original != null => Annotated(ann, tt.original) // this clause is needed to correctly compile stuff like "new C @D" or "@(inline @getter)" case _ => Annotated(ann, arg1) } @@ -3997,7 +3997,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val selector = tree.selector val cases = tree.cases if (selector == EmptyTree) { - if (newPatternMatching && (pt.typeSymbol == PartialFunctionClass)) + if (pt.typeSymbol == PartialFunctionClass) synthesizePartialFunction(newTermName(context.unit.fresh.newName("x")), tree.pos, tree, mode, pt) else { val arity = if (isFunctionType(pt)) pt.dealiasWiden.typeArgs.length - 1 else 1 diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala index 7f9b81e1ec..906a575d90 100644 --- a/src/compiler/scala/tools/nsc/util/ClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala @@ -16,6 +16,7 @@ import File.pathSeparator import scala.collection.convert.WrapAsScala.enumerationAsScalaIterator import java.net.MalformedURLException import java.util.regex.PatternSyntaxException +import scala.reflect.runtime.ReflectionUtils /** <p> * This module provides star expansion of '-classpath' option arguments, behaves the same as @@ -80,7 +81,7 @@ object ClassPath { } /** A useful name filter. */ - def isTraitImplementation(name: String) = name endsWith "$class.class" + def isTraitImplementation(name: String) = ReflectionUtils.isTraitImplementation(name) def specToURL(spec: String): Option[URL] = try Some(new URL(spec)) @@ -139,7 +140,7 @@ object ClassPath { } object DefaultJavaContext extends JavaContext { - override def isValidName(name: String) = !isTraitImplementation(name) + override def isValidName(name: String) = !ReflectionUtils.scalacShouldntLoadClassfile(name) } private def endsClass(s: String) = s.length > 6 && s.substring(s.length - 6) == ".class" diff --git a/src/compiler/scala/tools/nsc/util/StackTracing.scala b/src/compiler/scala/tools/nsc/util/StackTracing.scala new file mode 100644 index 0000000000..fa4fe29f28 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/StackTracing.scala @@ -0,0 +1,76 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + */ + +package scala.tools.nsc.util + +private[util] trait StackTracing extends Any { + + /** Format a stack trace, returning the prefix consisting of frames that satisfy + * a given predicate. + * The format is similar to the typical case described in the JavaDoc + * for [[java.lang.Throwable#printStackTrace]]. + * If a stack trace is truncated, it will be followed by a line of the form + * `... 3 elided`, by analogy to the lines `... 3 more` which indicate + * shared stack trace segments. + * @param e the exception + * @param p the predicate to select the prefix + */ + def stackTracePrefixString(e: Throwable)(p: StackTraceElement => Boolean): String = { + import collection.mutable.{ ArrayBuffer, ListBuffer } + import compat.Platform.EOL + import util.Properties.isJavaAtLeast + + val sb = ListBuffer.empty[String] + + type TraceRelation = String + val Self = new TraceRelation("") + val CausedBy = new TraceRelation("Caused by: ") + val Suppressed = new TraceRelation("Suppressed: ") + + val suppressable = isJavaAtLeast("1.7") + + def clazz(e: Throwable) = e.getClass.getName + def because(e: Throwable): String = e.getCause match { case null => null ; case c => header(c) } + def msg(e: Throwable): String = e.getMessage match { case null => because(e) ; case s => s } + def txt(e: Throwable): String = msg(e) match { case null => "" ; case s => s": $s" } + def header(e: Throwable): String = s"${clazz(e)}${txt(e)}" + + val indent = "\u0020\u0020" + + val seen = new ArrayBuffer[Throwable](16) + def unseen(t: Throwable) = { + def inSeen = seen exists (_ eq t) + val interesting = (t != null) && !inSeen + if (interesting) seen += t + interesting + } + + def print(e: Throwable, r: TraceRelation, share: Array[StackTraceElement], indents: Int): Unit = if (unseen(e)) { + val trace = e.getStackTrace + val frames = ( + if (share.nonEmpty) { + val spare = share.reverseIterator + val trimmed = trace.reverse dropWhile (spare.hasNext && spare.next == _) + trimmed.reverse + } else trace + ) + val prefix = frames takeWhile p + val margin = indent * indents + val indented = margin + indent + sb append s"${margin}${r}${header(e)}" + prefix foreach (f => sb append s"${indented}at $f") + if (frames.size < trace.size) sb append s"$indented... ${trace.size - frames.size} more" + if (r == Self && prefix.size < frames.size) sb append s"$indented... ${frames.size - prefix.size} elided" + print(e.getCause, CausedBy, trace, indents) + if (suppressable) { + import scala.language.reflectiveCalls + type Suppressing = { def getSuppressed(): Array[Throwable] } + for (s <- e.asInstanceOf[Suppressing].getSuppressed) print(s, Suppressed, frames, indents + 1) + } + } + print(e, Self, share = Array.empty, indents = 0) + + sb mkString EOL + } +} diff --git a/src/compiler/scala/tools/nsc/util/package.scala b/src/compiler/scala/tools/nsc/util/package.scala index ea3c9d8dde..72a4bbf5c0 100644 --- a/src/compiler/scala/tools/nsc/util/package.scala +++ b/src/compiler/scala/tools/nsc/util/package.scala @@ -8,7 +8,6 @@ package tools package nsc import java.io.{ OutputStream, PrintStream, ByteArrayOutputStream, PrintWriter, StringWriter } -import scala.compat.Platform.EOL package object util { @@ -79,12 +78,17 @@ package object util { s"$clazz$msg @ $frame" } - def stackTracePrefixString(ex: Throwable)(p: StackTraceElement => Boolean): String = { - val frames = ex.getStackTrace takeWhile p map (" at " + _) - val msg = ex.getMessage match { case null => "" ; case s => s": $s" } - val clazz = ex.getClass.getName - - s"$clazz$msg" +: frames mkString EOL + implicit class StackTraceOps(val e: Throwable) extends AnyVal with StackTracing { + /** Format the stack trace, returning the prefix consisting of frames that satisfy + * a given predicate. + * The format is similar to the typical case described in the JavaDoc + * for [[java.lang.Throwable#printStackTrace]]. + * If a stack trace is truncated, it will be followed by a line of the form + * `... 3 elided`, by analogy to the lines `... 3 more` which indicate + * shared stack trace segments. + * @param p the predicate to select the prefix + */ + def stackTracePrefixString(p: StackTraceElement => Boolean): String = stackTracePrefixString(e)(p) } lazy val trace = new SimpleTracer(System.out) diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 8d2f200e99..57ebe1b30d 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -12,6 +12,7 @@ import java.lang.{Class => jClass} import scala.compat.Platform.EOL import scala.reflect.NameTransformer import scala.reflect.api.JavaUniverse +import scala.reflect.io.NoAbstractFile abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => @@ -136,7 +137,9 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => val wrapper2 = if (!withMacrosDisabled) (currentTyper.context.withMacrosEnabled[Tree] _) else (currentTyper.context.withMacrosDisabled[Tree] _) def wrapper (tree: => Tree) = wrapper1(wrapper2(tree)) - phase = (new Run).typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled + val run = new Run + run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works + phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions reporter.reset() diff --git a/src/compiler/scala/tools/reflect/WrappedProperties.scala b/src/compiler/scala/tools/reflect/WrappedProperties.scala index 20567719be..523287fc66 100644 --- a/src/compiler/scala/tools/reflect/WrappedProperties.scala +++ b/src/compiler/scala/tools/reflect/WrappedProperties.scala @@ -27,9 +27,13 @@ trait WrappedProperties extends PropertiesTrait { override def envOrNone(name: String) = wrap(super.envOrNone(name)).flatten override def envOrSome(name: String, alt: Option[String]) = wrap(super.envOrNone(name)).flatten orElse alt - def systemProperties: Iterator[(String, String)] = { + def systemProperties: List[(String, String)] = { import scala.collection.JavaConverters._ - wrap(System.getProperties.asScala.iterator) getOrElse Iterator.empty + wrap { + val props = System.getProperties + // SI-7269 Be careful to avoid `ConcurrentModificationException` if another thread modifies the properties map + props.stringPropertyNames().asScala.toList.map(k => (k, props.get(k).asInstanceOf[String])) + } getOrElse Nil } } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index 18a806e5ff..19888fa8d2 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -50,6 +50,10 @@ trait Parsers { self: Quasiquotes => def entryPoint: QuasiquoteParser => Tree class QuasiquoteParser(source0: SourceFile) extends SourceFileParser(source0) { + def isHole: Boolean = isIdent && isHole(in.name) + + def isHole(name: Name): Boolean = holeMap.contains(name) + override val treeBuilder = new ParserTreeBuilder { // q"(..$xs)" override def makeTupleTerm(trees: List[Tree], flattenUnary: Boolean): Tree = @@ -61,9 +65,13 @@ trait Parsers { self: Quasiquotes => // q"{ $x }" override def makeBlock(stats: List[Tree]): Tree = stats match { - case (head @ Ident(name)) :: Nil if holeMap.contains(name) => Block(Nil, head) + case (head @ Ident(name)) :: Nil if isHole(name) => Block(Nil, head) case _ => super.makeBlock(stats) } + + // tq"$a => $b" + override def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = + AppliedTypeTree(Ident(tpnme.QUASIQUOTE_FUNCTION), argtpes :+ restpe) } import treeBuilder.{global => _, _} @@ -79,7 +87,10 @@ trait Parsers { self: Quasiquotes => } else super.caseClause() - def isHole: Boolean = isIdent && holeMap.contains(in.name) + override def caseBlock(): Tree = super.caseBlock() match { + case Block(Nil, expr) => expr + case other => other + } override def isAnnotation: Boolean = super.isAnnotation || (isHole && lookingAhead { isAnnotation }) @@ -105,13 +116,30 @@ trait Parsers { self: Quasiquotes => case AT => in.nextToken() annot :: readAnnots(annot) - case _ if isHole && lookingAhead { in.token == AT || isModifier || isDefIntro || isIdent} => + case _ if isHole && lookingAhead { isAnnotation || isModifier || isDefIntro || isIdent || isStatSep || in.token == LPAREN } => val ann = Apply(Select(New(Ident(tpnme.QUASIQUOTE_MODS)), nme.CONSTRUCTOR), List(Literal(Constant(in.name.toString)))) in.nextToken() ann :: readAnnots(annot) case _ => Nil } + + override def refineStat(): List[Tree] = + if (isHole && !isDclIntro) { + val result = ValDef(NoMods, in.name, Ident(tpnme.QUASIQUOTE_REFINE_STAT), EmptyTree) :: Nil + in.nextToken() + result + } else super.refineStat() + + override def ensureEarlyDef(tree: Tree) = tree match { + case Ident(name: TermName) if isHole(name) => ValDef(NoMods | Flag.PRESUPER, name, Ident(tpnme.QUASIQUOTE_EARLY_DEF), EmptyTree) + case _ => super.ensureEarlyDef(tree) + } + + override def isTypedParam(tree: Tree) = super.isTypedParam(tree) || (tree match { + case Ident(name) if isHole(name) => true + case _ => false + }) } } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala index b3ac1e293a..e20d98c0f1 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala @@ -92,10 +92,9 @@ trait Placeholders { self: Quasiquotes => } } - object AnnotPlaceholder { - def unapply(tree: Tree): Option[(Tree, Location, Cardinality, List[Tree])] = tree match { - case Apply(Select(New(Placeholder(tree, loc, card)), nme.CONSTRUCTOR), args) => Some(tree, loc, card, args) - case _ => None + object AnnotPlaceholder extends HolePlaceholder { + def matching = { + case Apply(Select(New(Ident(name)), nme.CONSTRUCTOR), Nil) => name } } @@ -113,6 +112,13 @@ trait Placeholders { self: Quasiquotes => } } + object FunctionTypePlaceholder { + def unapply(tree: Tree): Option[(List[Tree], Tree)] = tree match { + case AppliedTypeTree(Ident(tpnme.QUASIQUOTE_FUNCTION), args :+ res) => Some((args, res)) + case _ => None + } + } + object SymbolPlaceholder { def unapply(scrutinee: Any): Option[Tree] = scrutinee match { case Placeholder(tree, SymbolLocation, _) => Some(tree) @@ -127,9 +133,16 @@ trait Placeholders { self: Quasiquotes => } } - object ClassPlaceholder { - def unapply(tree: Tree): Option[Tree] = tree match { - case ClassDef(_, _, _, _) => Some(tree) + object RefineStatPlaceholder { + def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match { + case ValDef(_, Placeholder(tree, location, card), Ident(tpnme.QUASIQUOTE_REFINE_STAT), _) => Some((tree, location, card)) + case _ => None + } + } + + object EarlyDefPlaceholder { + def unapply(tree: Tree): Option[(Tree, Location, Cardinality)] = tree match { + case ValDef(_, Placeholder(tree, location, card), Ident(tpnme.QUASIQUOTE_EARLY_DEF), _) => Some((tree, location, card)) case _ => None } } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala index ee99a5e280..1305e25240 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala @@ -41,11 +41,11 @@ abstract class Quasiquotes extends Parsers lazy val universeTypes = new definitions.UniverseDependentTypes(universe) def expandQuasiquote = { - debug(s"\ncode to parse=\n$code\n") + debug(s"\ncode to parse:\n$code\n") val tree = parse(code) - debug(s"parsed tree\n=${tree}\n=${showRaw(tree)}\n") + debug(s"parsed:\n${showRaw(tree)}\n$tree\n") val reified = reify(tree) - debug(s"reified tree\n=${reified}\n=${showRaw(reified)}\n") + debug(s"reified tree:\n$reified\n") reified } } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala index 900237b00d..af4e34536c 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala @@ -7,7 +7,10 @@ import scala.reflect.internal.Flags._ trait Reifiers { self: Quasiquotes => import global._ - import global.build.SyntacticClassDef + import global.build.{SyntacticClassDef, SyntacticTraitDef, SyntacticModuleDef, + SyntacticDefDef, SyntacticValDef, SyntacticVarDef, + SyntacticBlock, SyntacticApplied, SyntacticTypeApplied, + SyntacticFunction, SyntacticNew} import global.treeInfo._ import global.definitions._ import Cardinality._ @@ -35,13 +38,9 @@ trait Reifiers { self: Quasiquotes => reified } - override def reifyTree(tree: Tree): Tree = { - val reified = - reifyTreePlaceholder(tree) orElse - reifyTreeSyntactically(tree) - //println(s"reified ${showRaw(tree)} as $reified") - reified - } + override def reifyTree(tree: Tree): Tree = + reifyTreePlaceholder(tree) orElse + reifyTreeSyntactically(tree) def reifyTreePlaceholder(tree: Tree): Tree = tree match { case Placeholder(tree, TreeLocation(_), _) if isReifyingExpressions => tree @@ -49,11 +48,49 @@ trait Reifiers { self: Quasiquotes => case Placeholder(tree, _, card @ Dot()) => c.abort(tree.pos, s"Can't $action with $card here") case TuplePlaceholder(args) => reifyTuple(args) case TupleTypePlaceholder(args) => reifyTupleType(args) + case FunctionTypePlaceholder(argtpes, restpe) => reifyFunctionType(argtpes, restpe) case CasePlaceholder(tree, location, _) => reifyCase(tree, location) - case ClassPlaceholder(tree) => reifyClass(tree) + case RefineStatPlaceholder(tree, _, _) => reifyRefineStat(tree) + case EarlyDefPlaceholder(tree, _, _) => reifyEarlyDef(tree) case _ => EmptyTree } + override def reifyTreeSyntactically(tree: Tree) = tree match { + case SyntacticTraitDef(mods, name, tparams, earlyDefs, parents, selfdef, body) => + reifyBuildCall(nme.SyntacticTraitDef, mods, name, tparams, earlyDefs, parents, selfdef, body) + case SyntacticClassDef(mods, name, tparams, constrmods, vparamss, earlyDefs, parents, selfdef, body) => + reifyBuildCall(nme.SyntacticClassDef, mods, name, tparams, constrmods, vparamss, + earlyDefs, parents, selfdef, body) + case SyntacticModuleDef(mods, name, earlyDefs, parents, selfdef, body) => + reifyBuildCall(nme.SyntacticModuleDef, mods, name, earlyDefs, parents, selfdef, body) + case SyntacticNew(earlyDefs, parents, selfdef, body) => + reifyBuildCall(nme.SyntacticNew, earlyDefs, parents, selfdef, body) + case SyntacticDefDef(mods, name, tparams, vparamss, tpt, rhs) => + reifyBuildCall(nme.SyntacticDefDef, mods, name, tparams, vparamss, tpt, rhs) + case SyntacticValDef(mods, name, tpt, rhs) => + reifyBuildCall(nme.SyntacticValDef, mods, name, tpt, rhs) + case SyntacticVarDef(mods, name, tpt, rhs) => + reifyBuildCall(nme.SyntacticVarDef, mods, name, tpt, rhs) + case SyntacticApplied(fun, argss) if argss.length > 1 => + reifyBuildCall(nme.SyntacticApplied, fun, argss) + case SyntacticApplied(fun, argss @ (_ :+ (_ :+ Placeholder(_, _, DotDotDot)))) => + reifyBuildCall(nme.SyntacticApplied, fun, argss) + case SyntacticTypeApplied(fun, targs) if targs.nonEmpty => + reifyBuildCall(nme.SyntacticTypeApplied, fun, targs) + case SyntacticFunction(args, body) => + reifyBuildCall(nme.SyntacticFunction, args, body) + case Block(stats, last) => + reifyBuildCall(nme.SyntacticBlock, stats :+ last) + // parser emits trees with scala package symbol to ensure + // that some names hygienically point to various scala package + // members; we need to preserve this symbol to preserve + // correctness of the trees produced by quasiquotes + case Select(id @ Ident(nme.scala_), name) if id.symbol == ScalaPackage => + reifyBuildCall(nme.ScalaDot, name) + case _ => + super.reifyTreeSyntactically(tree) + } + override def reifyName(name: Name): Tree = name match { case Placeholder(tree, location, _) => if (holesHaveTypes && !(location.tpe <:< nameType)) c.abort(tree.pos, s"$nameType expected but ${location.tpe} found") @@ -70,26 +107,30 @@ trait Reifiers { self: Quasiquotes => def reifyTuple(args: List[Tree]) = args match { case Nil => reify(Literal(Constant(()))) case List(hole @ Placeholder(_, _, NoDot)) => reify(hole) - case List(Placeholder(_, _, _)) => reifyBuildCall(nme.TupleN, args) + case List(Placeholder(_, _, _)) => reifyBuildCall(nme.SyntacticTuple, args) // in a case we only have one element tuple without // any cardinality annotations this means that this is // just an expression wrapped in parentheses case List(other) => reify(other) - case _ => reifyBuildCall(nme.TupleN, args) + case _ => reifyBuildCall(nme.SyntacticTuple, args) } def reifyTupleType(args: List[Tree]) = args match { case Nil => reify(Select(Ident(nme.scala_), tpnme.Unit)) case List(hole @ Placeholder(_, _, NoDot)) => reify(hole) - case List(Placeholder(_, _, _)) => reifyBuildCall(nme.TupleTypeN, args) + case List(Placeholder(_, _, _)) => reifyBuildCall(nme.SyntacticTupleType, args) case List(other) => reify(other) - case _ => reifyBuildCall(nme.TupleTypeN, args) + case _ => reifyBuildCall(nme.SyntacticTupleType, args) } - def reifyClass(tree: Tree) = { - val SyntacticClassDef(mods, name, tparams, constrmods, argss, parents, selfval, body) = tree - reifyBuildCall(nme.SyntacticClassDef, mods, name, tparams, constrmods, argss, parents, selfval, body) - } + def reifyFunctionType(argtpes: List[Tree], restpe: Tree) = + reifyBuildCall(nme.SyntacticFunctionType, argtpes, restpe) + + def reifyRefineStat(tree: Tree) = tree + + def reifyEarlyDef(tree: Tree) = tree + + def reifyAnnotation(tree: Tree) = tree /** Splits list into a list of groups where subsequent elements are considered * similar by the corresponding function. @@ -143,15 +184,33 @@ trait Reifiers { self: Quasiquotes => override def reifyList(xs: List[Any]): Tree = reifyMultiCardinalityList(xs) { case Placeholder(tree, _, DotDot) => tree case CasePlaceholder(tree, _, DotDot) => tree + case RefineStatPlaceholder(tree, _, DotDot) => reifyRefineStat(tree) + case EarlyDefPlaceholder(tree, _, DotDot) => reifyEarlyDef(tree) case List(Placeholder(tree, _, DotDotDot)) => tree } { reify(_) } - def reifyAnnotList(annots: List[Tree]): Tree + def reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) { + case AnnotPlaceholder(tree, _, DotDot) => reifyAnnotation(tree) + } { + case AnnotPlaceholder(tree, UnknownLocation | TreeLocation(_), NoDot) => reifyAnnotation(tree) + case other => reify(other) + } - def ensureNoExplicitFlags(m: Modifiers, pos: Position) = - if ((m.flags & ExplicitFlags) != 0L) c.abort(pos, s"Can't $action modifiers together with flags, consider merging flags into modifiers") + // These are explicit flags except those that are used + // to overload the same tree for two different concepts: + // - MUTABLE that is used to override ValDef for vars + // - TRAIT that is used to override ClassDef for traits + val nonoverloadedExplicitFlags = ExplicitFlags & ~MUTABLE & ~TRAIT + + def ensureNoExplicitFlags(m: Modifiers, pos: Position) = { + // Traits automatically have ABSTRACT flag assigned to + // them so in that case it's not an explicit flag + val flags = if (m.isTrait) m.flags & ~ABSTRACT else m.flags + if ((flags & nonoverloadedExplicitFlags) != 0L) + c.abort(pos, s"Can't $action modifiers together with flags, consider merging flags into modifiers") + } override def mirrorSelect(name: String): Tree = Select(universe, TermName(name)) @@ -167,19 +226,10 @@ trait Reifiers { self: Quasiquotes => def isReifyingExpressions = true override def reifyTreeSyntactically(tree: Tree): Tree = tree match { - case Block(stats, p @ Placeholder(_, _, _)) => reifyBuildCall(nme.Block, stats :+ p) - case Apply(f, List(Placeholder(argss, _, DotDotDot))) => reifyCallWithArgss(f, argss) - case RefTree(qual, SymbolPlaceholder(tree)) => mirrorBuildCall(nme.RefTree, reify(qual), tree) - case _ => super.reifyTreeSyntactically(tree) - } - - def reifyCallWithArgss(f: Tree, argss: Tree) = { - val f1 = reifyTree(f) - val foldLeftF1 = Apply(TypeApply(Select(argss, nme.foldLeft), List(Select(u, tpnme.Tree))), List(f1)) - val uDotApply = Function( - List(gen.mkSyntheticParam(nme.x_1), gen.mkSyntheticParam(nme.x_2)), - Apply(Select(u, nme.Apply), List(Ident(nme.x_1), Ident(nme.x_2)))) - Apply(foldLeftF1, List(uDotApply)) + case RefTree(qual, SymbolPlaceholder(tree)) => + mirrorBuildCall(nme.RefTree, reify(qual), tree) + case _ => + super.reifyTreeSyntactically(tree) } override def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree): Tree = xs match { @@ -193,61 +243,47 @@ trait Reifiers { self: Quasiquotes => tail.foldLeft[Tree](reifyGroup(head)) { (tree, lst) => Apply(Select(tree, nme.PLUSPLUS), List(reifyGroup(lst))) } } - override def reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) { - case AnnotPlaceholder(tree, _, DotDot, args) => - val x: TermName = c.freshName() - val xToAnnotationCtor = Function( - List(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree)), - mirrorBuildCall(nme.mkAnnotationCtor, Ident(x), reify(args))) - Apply(Select(tree, nme.map), List(xToAnnotationCtor)) - } { - case AnnotPlaceholder(tree, _: TreeLocation, _, args) => - mirrorBuildCall(nme.mkAnnotationCtor, tree, reify(args)) - case other => reify(other) - } - - override def reifyModifiers(m: Modifiers) = { - val (modsPlaceholders, annots) = m.annotations.partition { - case ModsPlaceholder(_, _, _) => true - case _ => false - } - val (mods, flags) = modsPlaceholders.map { - case ModsPlaceholder(tree, location, card) => (tree, location) - }.partition { case (tree, location) => - location match { - case ModsLocation => true - case FlagsLocation => false - case _ => c.abort(tree.pos, s"$flagsType or $modsType expected but ${tree.tpe} found") + override def reifyModifiers(m: Modifiers) = + if (m == NoMods) super.reifyModifiers(m) + else { + val (modsPlaceholders, annots) = m.annotations.partition { + case ModsPlaceholder(_, _, _) => true + case _ => false + } + val (mods, flags) = modsPlaceholders.map { + case ModsPlaceholder(tree, location, card) => (tree, location) + }.partition { case (tree, location) => + location match { + case ModsLocation => true + case FlagsLocation => false + case _ => c.abort(tree.pos, s"$flagsType or $modsType expected but ${tree.tpe} found") + } + } + mods match { + case (tree, _) :: Nil => + if (flags.nonEmpty) c.abort(flags(0)._1.pos, "Can't splice flags together with modifiers, consider merging flags into modifiers") + if (annots.nonEmpty) c.abort(tree.pos, "Can't splice modifiers together with annotations, consider merging annotations into modifiers") + ensureNoExplicitFlags(m, tree.pos) + tree + case _ :: (second, _) :: Nil => + c.abort(second.pos, "Can't splice multiple modifiers, consider merging them into a single modifiers instance") + case _ => + val baseFlags = reifyFlags(m.flags) + val reifiedFlags = flags.foldLeft[Tree](baseFlags) { case (flag, (tree, _)) => Apply(Select(flag, nme.OR), List(tree)) } + mirrorFactoryCall(nme.Modifiers, reifiedFlags, reify(m.privateWithin), reifyAnnotList(annots)) } } - mods match { - case (tree, _) :: Nil => - if (flags.nonEmpty) c.abort(flags(0)._1.pos, "Can't splice flags together with modifiers, consider merging flags into modifiers") - if (annots.nonEmpty) c.abort(tree.pos, "Can't splice modifiers together with annotations, consider merging annotations into modifiers") - ensureNoExplicitFlags(m, tree.pos) - tree - case _ :: (second, _) :: Nil => - c.abort(second.pos, "Can't splice multiple modifiers, consider merging them into a single modifiers instance") - case _ => - val baseFlags = reifyBuildCall(nme.flagsFromBits, m.flags) - val reifiedFlags = flags.foldLeft[Tree](baseFlags) { case (flag, (tree, _)) => Apply(Select(flag, nme.OR), List(tree)) } - mirrorFactoryCall(nme.Modifiers, reifiedFlags, reify(m.privateWithin), reifyAnnotList(annots)) - } - } + + override def reifyRefineStat(tree: Tree) = mirrorBuildCall(nme.mkRefineStat, tree) + + override def reifyEarlyDef(tree: Tree) = mirrorBuildCall(nme.mkEarlyDef, tree) + + override def reifyAnnotation(tree: Tree) = mirrorBuildCall(nme.mkAnnotation, tree) } class UnapplyReifier extends Reifier { def isReifyingExpressions = false - override def reifyTreeSyntactically(tree: Tree): Tree = tree match { - case treeInfo.Applied(fun, Nil, argss) if fun != tree && !tree.isInstanceOf[AppliedTypeTree] => - reifyBuildCall(nme.Applied, fun, argss) - case treeInfo.Applied(fun, targs, argss) if fun != tree & !tree.isInstanceOf[AppliedTypeTree] => - mirrorBuildCall(nme.Applied, reifyBuildCall(nme.TypeApplied, fun, targs), reifyList(argss)) - case _ => - super.reifyTreeSyntactically(tree) - } - override def scalaFactoryCall(name: String, args: Tree*): Tree = call("scala." + name, args: _*) @@ -261,30 +297,20 @@ trait Reifiers { self: Quasiquotes => mkList(xs.map(fallback)) } - override def reifyAnnotList(annots: List[Tree]): Tree = reifyMultiCardinalityList(annots) { - case AnnotPlaceholder(tree, _, DotDot, Nil) => tree - } { - case AnnotPlaceholder(tree, _, NoDot, Nil) => tree - case AnnotPlaceholder(tree, _, NoDot, args) => - val selectCONSTRUCTOR = Apply(Select(u, nme.Select), List(Apply(Select(u, nme.New), List(tree)), Select(Select(u, nme.nmeNme), nme.nmeCONSTRUCTOR))) - Apply(Select(u, nme.Apply), List(selectCONSTRUCTOR, reify(args))) - case other => - reify(other) - } - - override def reifyModifiers(m: Modifiers) = { - val mods = m.annotations.collect { case ModsPlaceholder(tree, _, _) => tree } - mods match { - case tree :: Nil => - if (m.annotations.length != 1) c.abort(tree.pos, "Can't extract modifiers together with annotations, consider extracting just modifiers") - ensureNoExplicitFlags(m, tree.pos) - tree - case _ :: second :: rest => - c.abort(second.pos, "Can't extract multiple modifiers together, consider extracting a single modifiers instance") - case Nil => - mirrorFactoryCall(nme.Modifiers, reifyBuildCall(nme.FlagsAsBits, m.flags), - reify(m.privateWithin), reifyAnnotList(m.annotations)) + override def reifyModifiers(m: Modifiers) = + if (m == NoMods) super.reifyModifiers(m) + else { + val mods = m.annotations.collect { case ModsPlaceholder(tree, _, _) => tree } + mods match { + case tree :: Nil => + if (m.annotations.length != 1) c.abort(tree.pos, "Can't extract modifiers together with annotations, consider extracting just modifiers") + ensureNoExplicitFlags(m, tree.pos) + tree + case _ :: second :: rest => + c.abort(second.pos, "Can't extract multiple modifiers together, consider extracting a single modifiers instance") + case Nil => + mirrorFactoryCall(nme.Modifiers, reifyFlags(m.flags), reify(m.privateWithin), reifyAnnotList(m.annotations)) + } } - } } -}
\ No newline at end of file +} |