diff options
Diffstat (limited to 'src/compiler/scala/tools')
27 files changed, 358 insertions, 296 deletions
diff --git a/src/compiler/scala/tools/ant/sabbus/Settings.scala b/src/compiler/scala/tools/ant/sabbus/Settings.scala index 4cbc03d8d4..a86af73fe3 100644 --- a/src/compiler/scala/tools/ant/sabbus/Settings.scala +++ b/src/compiler/scala/tools/ant/sabbus/Settings.scala @@ -93,7 +93,7 @@ class Settings { case _ => false } - override lazy val hashCode: Int = Seq( + override lazy val hashCode: Int = Seq[Any]( gBf, uncheckedBf, classpathBf, diff --git a/src/compiler/scala/tools/ant/templates/tool-windows.tmpl b/src/compiler/scala/tools/ant/templates/tool-windows.tmpl index bd6cf561b9..a3a95ffd37 100644 --- a/src/compiler/scala/tools/ant/templates/tool-windows.tmpl +++ b/src/compiler/scala/tools/ant/templates/tool-windows.tmpl @@ -89,4 +89,4 @@ goto :eof @@endlocal REM exit code fix, see http://stackoverflow.com/questions/4632891/exiting-batch-with-exit-b-x-where-x-1-acts-as-if-command-completed-successfu -@@%COMSPEC% /C exit %errorlevel% >nul +@@"%COMSPEC%" /C exit %errorlevel% >nul diff --git a/src/compiler/scala/tools/cmd/CommandLineParser.scala b/src/compiler/scala/tools/cmd/CommandLineParser.scala index ef55178594..6132eff557 100644 --- a/src/compiler/scala/tools/cmd/CommandLineParser.scala +++ b/src/compiler/scala/tools/cmd/CommandLineParser.scala @@ -40,16 +40,16 @@ object CommandLineParser { // parse `in` for an argument, return it and the remainder of the input (or an error message) // (argument may be in single/double quotes, taking escaping into account, quotes are stripped) private def argument(in: String): Either[String, (String, String)] = in match { - case DoubleQuoted(arg, rest) => Right(arg, rest) - case SingleQuoted(arg, rest) => Right(arg, rest) - case Word(arg, rest) => Right(arg, rest) - case _ => Left("Illegal argument: "+ in) + case DoubleQuoted(arg, rest) => Right((arg, rest)) + case SingleQuoted(arg, rest) => Right((arg, rest)) + case Word(arg, rest) => Right((arg, rest)) + case _ => Left(s"Illegal argument: $in") } // parse a list of whitespace-separated arguments (ignoring whitespace in quoted arguments) @tailrec private def commandLine(in: String, accum: List[String] = Nil): Either[String, (List[String], String)] = { val trimmed = in.trim - if (trimmed.isEmpty) Right(accum.reverse, "") + if (trimmed.isEmpty) Right((accum.reverse, "")) else argument(trimmed) match { case Right((arg, next)) => (next span Character.isWhitespace) match { diff --git a/src/compiler/scala/tools/cmd/gen/AnyVals.scala b/src/compiler/scala/tools/cmd/gen/AnyVals.scala index 7e01afac2b..842851b4f6 100644 --- a/src/compiler/scala/tools/cmd/gen/AnyVals.scala +++ b/src/compiler/scala/tools/cmd/gen/AnyVals.scala @@ -6,24 +6,23 @@ package scala.tools.cmd package gen -/** Code generation of the AnyVal types and their companions. - */ +/** Code generation of the AnyVal types and their companions. */ trait AnyValReps { self: AnyVals => - sealed abstract class AnyValNum(name: String, repr: Option[String], javaEquiv: String) extends AnyValRep(name,repr,javaEquiv) { + sealed abstract class AnyValNum(name: String, repr: Option[String], javaEquiv: String) + extends AnyValRep(name,repr,javaEquiv) { case class Op(op : String, doc : String) private def companionCoercions(tos: AnyValRep*) = { tos.toList map (to => - """implicit def @javaequiv@2%s(x: @name@): %s = x.to%s""".format(to.javaEquiv, to.name, to.name) + s"implicit def @javaequiv@2${to.javaEquiv}(x: @name@): ${to.name} = x.to${to.name}" ) } - def coercionCommentExtra = "" - def coercionComment = """ -/** Language mandated coercions from @name@ to "wider" types.%s - */""".format(coercionCommentExtra) + def coercionComment = +"""/** Language mandated coercions from @name@ to "wider" types. */ +import scala.language.implicitConversions""" def implicitCoercions: List[String] = { val coercions = this match { @@ -41,12 +40,8 @@ trait AnyValReps { def isCardinal: Boolean = isIntegerType(this) def unaryOps = { val ops = List( - Op("+", "/**\n" + - " * Returns this value, unmodified.\n" + - " */"), - Op("-", "/**\n" + - " * Returns the negation of this value.\n" + - " */")) + Op("+", "/** Returns this value, unmodified. */"), + Op("-", "/** Returns the negation of this value. */")) if(isCardinal) Op("~", "/**\n" + @@ -95,7 +90,7 @@ trait AnyValReps { " */")) else Nil - def shiftOps = + def shiftOps = if (isCardinal) List( Op("<<", "/**\n" + @@ -127,20 +122,20 @@ trait AnyValReps { " */")) else Nil - def comparisonOps = List( - Op("==", "/**\n * Returns `true` if this value is equal to x, `false` otherwise.\n */"), - Op("!=", "/**\n * Returns `true` if this value is not equal to x, `false` otherwise.\n */"), - Op("<", "/**\n * Returns `true` if this value is less than x, `false` otherwise.\n */"), - Op("<=", "/**\n * Returns `true` if this value is less than or equal to x, `false` otherwise.\n */"), - Op(">", "/**\n * Returns `true` if this value is greater than x, `false` otherwise.\n */"), - Op(">=", "/**\n * Returns `true` if this value is greater than or equal to x, `false` otherwise.\n */")) + def comparisonOps = List( + Op("==", "/** Returns `true` if this value is equal to x, `false` otherwise. */"), + Op("!=", "/** Returns `true` if this value is not equal to x, `false` otherwise. */"), + Op("<", "/** Returns `true` if this value is less than x, `false` otherwise. */"), + Op("<=", "/** Returns `true` if this value is less than or equal to x, `false` otherwise. */"), + Op(">", "/** Returns `true` if this value is greater than x, `false` otherwise. */"), + Op(">=", "/** Returns `true` if this value is greater than or equal to x, `false` otherwise. */")) def otherOps = List( - Op("+", "/**\n * Returns the sum of this value and `x`.\n */"), - Op("-", "/**\n * Returns the difference of this value and `x`.\n */"), - Op("*", "/**\n * Returns the product of this value and `x`.\n */"), - Op("/", "/**\n * Returns the quotient of this value and `x`.\n */"), - Op("%", "/**\n * Returns the remainder of the division of this value by `x`.\n */")) + Op("+", "/** Returns the sum of this value and `x`. */"), + Op("-", "/** Returns the difference of this value and `x`. */"), + Op("*", "/** Returns the product of this value and `x`. */"), + Op("/", "/** Returns the quotient of this value and `x`. */"), + Op("%", "/** Returns the remainder of the division of this value by `x`. */")) // Given two numeric value types S and T , the operation type of S and T is defined as follows: // If both S and T are subrange types then the operation type of S and T is Int. @@ -278,8 +273,7 @@ trait AnyValReps { } trait AnyValTemplates { - def headerTemplate = (""" -/* __ *\ + def headerTemplate = """/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** @@ -287,12 +281,13 @@ trait AnyValTemplates { ** |/ ** \* */ -%s -package scala +// DO NOT EDIT, CHANGES WILL BE LOST +// This auto-generated code can be modified in scala.tools.cmd.gen. +// Afterwards, running tools/codegen-anyvals regenerates this source file. -import scala.language.implicitConversions +package scala -""".trim.format(timestampString) + "\n\n") +""" def classDocTemplate = (""" /** `@name@`@representation@ (equivalent to Java's `@javaequiv@` primitive type) is a @@ -304,8 +299,6 @@ import scala.language.implicitConversions */ """.trim + "\n") - def timestampString = "// DO NOT EDIT, CHANGES WILL BE LOST.\n" - def allCompanions = """ /** Transform a value type into a boxed reference type. *@boxRunTimeDoc@ @@ -324,20 +317,17 @@ def box(x: @name@): @boxed@ = @boxImpl@ */ def unbox(x: java.lang.Object): @name@ = @unboxImpl@ -/** The String representation of the scala.@name@ companion object. - */ +/** The String representation of the scala.@name@ companion object. */ override def toString = "object scala.@name@" """ def nonUnitCompanions = "" // todo def cardinalCompanion = """ -/** The smallest value representable as a @name@. - */ +/** The smallest value representable as a @name@. */ final val MinValue = @boxed@.MIN_VALUE -/** The largest value representable as a @name@. - */ +/** The largest value representable as a @name@. */ final val MaxValue = @boxed@.MAX_VALUE """ @@ -372,18 +362,16 @@ class AnyVals extends AnyValReps with AnyValTemplates { object D extends AnyValNum("Double", Some("64-bit IEEE-754 floating point number"), "double") object Z extends AnyValRep("Boolean", None, "boolean") { def classLines = """ -/** - * Negates a Boolean expression. - * - * - `!a` results in `false` if and only if `a` evaluates to `true` and - * - `!a` results in `true` if and only if `a` evaluates to `false`. - * - * @return the negated expression - */ +/** Negates a Boolean expression. + * + * - `!a` results in `false` if and only if `a` evaluates to `true` and + * - `!a` results in `true` if and only if `a` evaluates to `false`. + * + * @return the negated expression + */ def unary_! : Boolean -/** - * Compares two Boolean expressions and returns `true` if they evaluate to the same value. +/** Compares two Boolean expressions and returns `true` if they evaluate to the same value. * * `a == b` returns `true` if and only if * - `a` and `b` are `true` or @@ -400,8 +388,7 @@ def ==(x: Boolean): Boolean */ def !=(x: Boolean): Boolean -/** - * Compares two Boolean expressions and returns `true` if one or both of them evaluate to true. +/** Compares two Boolean expressions and returns `true` if one or both of them evaluate to true. * * `a || b` returns `true` if and only if * - `a` is `true` or @@ -414,8 +401,7 @@ def !=(x: Boolean): Boolean */ def ||(x: Boolean): Boolean -/** - * Compares two Boolean expressions and returns `true` if both of them evaluate to true. +/** Compares two Boolean expressions and returns `true` if both of them evaluate to true. * * `a && b` returns `true` if and only if * - `a` and `b` are `true`. @@ -430,8 +416,7 @@ def &&(x: Boolean): Boolean // def ||(x: => Boolean): Boolean // def &&(x: => Boolean): Boolean -/** - * Compares two Boolean expressions and returns `true` if one or both of them evaluate to true. +/** Compares two Boolean expressions and returns `true` if one or both of them evaluate to true. * * `a | b` returns `true` if and only if * - `a` is `true` or @@ -442,8 +427,7 @@ def &&(x: Boolean): Boolean */ def |(x: Boolean): Boolean -/** - * Compares two Boolean expressions and returns `true` if both of them evaluate to true. +/** Compares two Boolean expressions and returns `true` if both of them evaluate to true. * * `a & b` returns `true` if and only if * - `a` and `b` are `true`. @@ -452,8 +436,7 @@ def |(x: Boolean): Boolean */ def &(x: Boolean): Boolean -/** - * Compares two Boolean expressions and returns `true` if they evaluate to a different value. +/** Compares two Boolean expressions and returns `true` if they evaluate to a different value. * * `a ^ b` returns `true` if and only if * - `a` is `true` and `b` is `false` or @@ -499,5 +482,3 @@ override def getClass(): Class[Boolean] = null def make() = values map (x => (x.name, x.make())) } - -object AnyVals extends AnyVals { } diff --git a/src/compiler/scala/tools/cmd/gen/Codegen.scala b/src/compiler/scala/tools/cmd/gen/Codegen.scala index b49322ab4a..c3aa527ef2 100644 --- a/src/compiler/scala/tools/cmd/gen/Codegen.scala +++ b/src/compiler/scala/tools/cmd/gen/Codegen.scala @@ -6,11 +6,9 @@ package scala.tools.cmd package gen -import scala.language.postfixOps - class Codegen(args: List[String]) extends { val parsed = CodegenSpec(args: _*) -} with CodegenSpec with Instance { } +} with CodegenSpec with Instance object Codegen { def echo(msg: String) = Console println msg @@ -31,7 +29,7 @@ object Codegen { val av = new AnyVals { } av.make() foreach { case (name, code ) => - val file = out / (name + ".scala") toFile; + val file = (out / (name + ".scala")).toFile echo("Writing: " + file) file writeAll code } diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 20cb1dab5b..a6c69091c5 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -51,7 +51,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) class GlobalMirror extends Roots(NoSymbol) { val universe: self.type = self - def rootLoader: LazyType = platform.rootLoader + def rootLoader: LazyType = new loaders.PackageLoader(classPath) override def toString = "compiler mirror" } @@ -83,12 +83,15 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // platform specific elements - type ThisPlatform = Platform { val global: Global.this.type } + protected class GlobalPlatform extends { + val global: Global.this.type = Global.this + val settings: Settings = Global.this.settings + } with JavaPlatform - lazy val platform: ThisPlatform = - new { val global: Global.this.type = Global.this } with JavaPlatform + type ThisPlatform = JavaPlatform { val global: Global.this.type } + lazy val platform: ThisPlatform = new GlobalPlatform - type PlatformClassPath = ClassPath[platform.BinaryRepr] + type PlatformClassPath = ClassPath[AbstractFile] type OptClassPath = Option[PlatformClassPath] def classPath: PlatformClassPath = platform.classPath @@ -268,12 +271,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) log("!!! " + msg) // such warnings always at least logged } - private def elapsedMessage(msg: String, start: Long) = - msg + " in " + (currentTime - start) + "ms" - def informComplete(msg: String): Unit = reporter.withoutTruncating(inform(msg)) - def informProgress(msg: String) = if (settings.verbose) inform("[" + msg + "]") - def informTime(msg: String, start: Long) = informProgress(elapsedMessage(msg, start)) def logError(msg: String, t: Throwable): Unit = () @@ -357,9 +355,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) getSourceFile(f) } - lazy val loaders = new SymbolLoaders { + lazy val loaders = new { val global: Global.this.type = Global.this - } + val platform: Global.this.platform.type = Global.this.platform + } with GlobalSymbolLoaders /** Returns the mirror that loaded given symbol */ def mirrorThatLoaded(sym: Symbol): Mirror = rootMirror @@ -914,7 +913,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) invalidated: mutable.ListBuffer[ClassSymbol], failed: mutable.ListBuffer[ClassSymbol]) { ifDebug(informProgress(s"syncing $root, $oldEntries -> $newEntries")) - val getName: ClassPath[platform.BinaryRepr] => String = (_.name) + val getName: ClassPath[AbstractFile] => String = (_.name) def hasClasses(cp: OptClassPath) = cp.isDefined && cp.get.classes.nonEmpty def invalidateOrRemove(root: ClassSymbol) = { allEntries match { diff --git a/src/compiler/scala/tools/nsc/GlobalSymbolLoaders.scala b/src/compiler/scala/tools/nsc/GlobalSymbolLoaders.scala new file mode 100644 index 0000000000..6921548230 --- /dev/null +++ b/src/compiler/scala/tools/nsc/GlobalSymbolLoaders.scala @@ -0,0 +1,30 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Martin Odersky + */ + +package scala +package tools +package nsc + +/** + * Symbol loaders implementation that wires dependencies using Global. + */ +abstract class GlobalSymbolLoaders extends symtab.SymbolLoaders { + val global: Global + val symbolTable: global.type = global + val platform: symbolTable.platform.type + import global._ + def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol = { + def lookup = sym.info.member(name) + // if loading during initialization of `definitions` typerPhase is not yet set. + // in that case we simply load the member at the current phase + if (currentRun.typerPhase eq null) + lookup + else + enteringTyper { lookup } + } + + protected def compileLate(srcfile: io.AbstractFile): Unit = + currentRun.compileLate(srcfile) +} diff --git a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala index b17de9b9d5..66ed0c8fae 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala @@ -187,7 +187,7 @@ trait TreeDSL { def vparamss: List[List[ValDef]] type ResultTreeType = DefDef - def mkTree(rhs: Tree): DefDef = DefDef(mods, name, tparams, vparamss, tpt, rhs) + def mkTree(rhs: Tree): DefDef = DefDef(mods, name.toTermName, tparams, vparamss, tpt, rhs) } class DefSymStart(val sym: Symbol) extends SymVODDStart with DefCreator { diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index eb924a811b..d0b0c09d59 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -2542,7 +2542,7 @@ self => } expr() } - DefDef(newmods, name, tparams, vparamss, restype, rhs) + DefDef(newmods, name.toTermName, tparams, vparamss, restype, rhs) } signalParseProgress(result.pos) result diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala index c5fc12e3ec..32b5a98b98 100644 --- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala +++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala @@ -11,33 +11,22 @@ import util.{ClassPath,MergedClassPath,DeltaClassPath} import scala.tools.util.PathResolver trait JavaPlatform extends Platform { + val global: Global + override val symbolTable: global.type = global import global._ import definitions._ - type BinaryRepr = AbstractFile + private var currentClassPath: Option[MergedClassPath[AbstractFile]] = None - private var currentClassPath: Option[MergedClassPath[BinaryRepr]] = None - - def classPath: ClassPath[BinaryRepr] = { + def classPath: ClassPath[AbstractFile] = { if (currentClassPath.isEmpty) currentClassPath = Some(new PathResolver(settings).result) currentClassPath.get } /** Update classpath with a substituted subentry */ - def updateClassPath(subst: Map[ClassPath[BinaryRepr], ClassPath[BinaryRepr]]) = + def updateClassPath(subst: Map[ClassPath[AbstractFile], ClassPath[AbstractFile]]) = currentClassPath = Some(new DeltaClassPath(currentClassPath.get, subst)) - def rootLoader = new loaders.PackageLoader(classPath.asInstanceOf[ClassPath[platform.BinaryRepr]]) - // [Martin] Why do we need a cast here? - // The problem is that we cannot specify at this point that global.platform should be of type JavaPlatform. - // So we cannot infer that global.platform.BinaryRepr is AbstractFile. - // Ideally, we should be able to write at the top of the JavaPlatform trait: - // val global: Global { val platform: JavaPlatform } - // import global._ - // Right now, this does nothing because the concrete definition of platform in Global - // replaces the tighter abstract definition here. If we had DOT typing rules, the two - // types would be conjoined and everything would work out. Yet another reason to push for DOT. - private def classEmitPhase = if (settings.isBCodeActive) genBCode else genASM @@ -66,10 +55,7 @@ trait JavaPlatform extends Platform { (sym isNonBottomSubClass BoxedBooleanClass) } - def newClassLoader(bin: AbstractFile): loaders.SymbolLoader = - new loaders.ClassfileLoader(bin) - - def doLoad(cls: ClassPath[BinaryRepr]#ClassRep): Boolean = true + def doLoad(cls: ClassPath[AbstractFile]#ClassRep): Boolean = true def needCompile(bin: AbstractFile, src: AbstractFile) = src.lastModified >= bin.lastModified diff --git a/src/compiler/scala/tools/nsc/backend/Platform.scala b/src/compiler/scala/tools/nsc/backend/Platform.scala index e2b22c06d7..3bca16635b 100644 --- a/src/compiler/scala/tools/nsc/backend/Platform.scala +++ b/src/compiler/scala/tools/nsc/backend/Platform.scala @@ -12,20 +12,18 @@ import io.AbstractFile /** The platform dependent pieces of Global. */ trait Platform { - val global: Global - import global._ + val symbolTable: symtab.SymbolTable + import symbolTable._ /** The binary classfile representation type */ - type BinaryRepr + @deprecated("BinaryRepr is not an abstract type anymore. It's an alias that points at AbstractFile. It'll be removed before Scala 2.11 is released.", "2.11.0-M5") + type BinaryRepr = AbstractFile /** The compiler classpath. */ - def classPath: ClassPath[BinaryRepr] - - /** The root symbol loader. */ - def rootLoader: LazyType + def classPath: ClassPath[AbstractFile] /** Update classpath with a substitution that maps entries to entries */ - def updateClassPath(subst: Map[ClassPath[BinaryRepr], ClassPath[BinaryRepr]]) + def updateClassPath(subst: Map[ClassPath[AbstractFile], ClassPath[AbstractFile]]) /** Any platform-specific phases. */ def platformPhases: List[SubComponent] @@ -36,16 +34,13 @@ trait Platform { /** The various ways a boxed primitive might materialize at runtime. */ def isMaybeBoxed(sym: Symbol): Boolean - /** Create a new class loader to load class file `bin` */ - def newClassLoader(bin: BinaryRepr): loaders.SymbolLoader - /** * Tells whether a class should be loaded and entered into the package * scope. On .NET, this method returns `false` for all synthetic classes * (anonymous classes, implementation classes, module classes), their * symtab is encoded in the pickle of another class. */ - def doLoad(cls: ClassPath[BinaryRepr]#ClassRep): Boolean + def doLoad(cls: ClassPath[AbstractFile]#ClassRep): Boolean /** * Tells whether a class with both a binary and a source representation @@ -53,6 +48,6 @@ trait Platform { * on the JVM similar to javac, i.e. if the source file is newer than the classfile, * a re-compile is triggered. On .NET by contrast classfiles always take precedence. */ - def needCompile(bin: BinaryRepr, src: AbstractFile): Boolean + def needCompile(bin: AbstractFile, src: AbstractFile): Boolean } diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index e6f21fc1e3..410d451316 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -1479,26 +1479,18 @@ abstract class GenICode extends SubComponent { if (mustUseAnyComparator) { // when -optimise is on we call the @inline-version of equals, found in ScalaRunTime - val equalsMethod = + val equalsMethod: Symbol = { if (!settings.optimise) { - def default = platform.externalEquals - platform match { - case x: JavaPlatform => - import x._ - if (l.tpe <:< BoxedNumberClass.tpe) { - if (r.tpe <:< BoxedNumberClass.tpe) externalEqualsNumNum - else if (r.tpe <:< BoxedCharacterClass.tpe) externalEqualsNumChar - else externalEqualsNumObject - } - else default - - case _ => default - } - } - else { + if (l.tpe <:< BoxedNumberClass.tpe) { + if (r.tpe <:< BoxedNumberClass.tpe) platform.externalEqualsNumNum + else if (r.tpe <:< BoxedCharacterClass.tpe) platform.externalEqualsNumChar + else platform.externalEqualsNumObject + } else platform.externalEquals + } else { ctx.bb.emit(LOAD_MODULE(ScalaRunTimeModule)) getMember(ScalaRunTimeModule, nme.inlinedEquals) } + } val ctx1 = genLoad(l, ctx, ObjectReference) val ctx2 = genLoad(r, ctx1, ObjectReference) diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala index 3f2141782a..b9eb8f8aac 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala @@ -10,6 +10,7 @@ package icode import java.io.PrintWriter import analysis.{ Liveness, ReachingDefinitions } import scala.tools.nsc.symtab.classfile.ICodeReader +import scala.reflect.io.AbstractFile /** Glue together ICode parts. * @@ -108,6 +109,12 @@ abstract class ICodes extends AnyRef object icodeReader extends ICodeReader { lazy val global: ICodes.this.global.type = ICodes.this.global + import global._ + def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol = + global.loaders.lookupMemberAtTyperPhaseIfPossible(sym, name) + lazy val symbolTable: global.type = global + lazy val loaders: global.loaders.type = global.loaders + def classPath: util.ClassPath[AbstractFile] = ICodes.this.global.platform.classPath } /** A phase which works on icode. */ diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index a7f43eefed..683f35e41f 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -1187,22 +1187,12 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { } if (mustUseAnyComparator) { - val equalsMethod = { - - def default = platform.externalEquals - - platform match { - case x: JavaPlatform => - import x._ - if (l.tpe <:< BoxedNumberClass.tpe) { - if (r.tpe <:< BoxedNumberClass.tpe) externalEqualsNumNum - else if (r.tpe <:< BoxedCharacterClass.tpe) externalEqualsNumChar - else externalEqualsNumObject - } - else default - - case _ => default - } + val equalsMethod: Symbol = { + if (l.tpe <:< BoxedNumberClass.tpe) { + if (r.tpe <:< BoxedNumberClass.tpe) platform.externalEqualsNumNum + else if (r.tpe <:< BoxedCharacterClass.tpe) platform.externalEqualsNumChar + else platform.externalEqualsNumObject + } else platform.externalEquals } genLoad(l, ObjectReference) genLoad(r, ObjectReference) diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index 2a799acbc7..239ecb4f8a 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -510,7 +510,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { if (inInterface) mods1 |= Flags.DEFERRED List { atPos(pos) { - DefDef(mods1, name, tparams, List(vparams), rtpt, body) + DefDef(mods1, name.toTermName, tparams, List(vparams), rtpt, body) } } } else { diff --git a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala index 4e4efef607..4b9e056df3 100644 --- a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala @@ -12,9 +12,10 @@ import scala.tools.nsc.io.AbstractFile * This class should be used whenever file dependencies and recompile sets * are managed automatically. */ -abstract class BrowsingLoaders extends SymbolLoaders { - import global._ +abstract class BrowsingLoaders extends GlobalSymbolLoaders { + val global: Global + import global._ import syntaxAnalyzer.{OutlineParser, MalformedInput} /** In browse mode, it can happen that an encountered symbol is already diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index fd85bbb169..6f27eb8660 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -20,8 +20,23 @@ import scala.reflect.io.{ AbstractFile, NoAbstractFile } * @version 1.0 */ abstract class SymbolLoaders { - val global: Global - import global._ + val symbolTable: symtab.SymbolTable { + def settings: Settings + } + val platform: backend.Platform { + val symbolTable: SymbolLoaders.this.symbolTable.type + } + import symbolTable._ + /** + * Required by ClassfileParser. Check documentation in that class for details. + */ + def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol + /** + * Should forward to `Run.compileLate`. The more principled fix would be to + * determine why this functionality is needed and extract it into a separate + * interface. + */ + protected def compileLate(srcfile: AbstractFile): Unit import SymbolLoadersStats._ protected def enterIfNew(owner: Symbol, member: Symbol, completer: SymbolLoader): Symbol = { @@ -75,14 +90,14 @@ abstract class SymbolLoaders { name+"\none of them needs to be removed from classpath" ) else if (settings.termConflict.value == "package") { - global.warning( + warning( "Resolving package/object name conflict in favor of package " + preExisting.fullName + ". The object will be inaccessible." ) root.info.decls.unlink(preExisting) } else { - global.warning( + warning( "Resolving package/object name conflict in favor of object " + preExisting.fullName + ". The package will be inaccessible." ) @@ -139,17 +154,17 @@ abstract class SymbolLoaders { /** Initialize toplevel class and module symbols in `owner` from class path representation `classRep` */ - def initializeFromClassPath(owner: Symbol, classRep: ClassPath[platform.BinaryRepr]#ClassRep) { + def initializeFromClassPath(owner: Symbol, classRep: ClassPath[AbstractFile]#ClassRep) { ((classRep.binary, classRep.source) : @unchecked) match { case (Some(bin), Some(src)) if platform.needCompile(bin, src) && !binaryOnly(owner, classRep.name) => if (settings.verbose) inform("[symloader] picked up newer source file for " + src.path) - global.loaders.enterToplevelsFromSource(owner, classRep.name, src) + enterToplevelsFromSource(owner, classRep.name, src) case (None, Some(src)) => if (settings.verbose) inform("[symloader] no class, picked up source file for " + src.path) - global.loaders.enterToplevelsFromSource(owner, classRep.name, src) + enterToplevelsFromSource(owner, classRep.name, src) case (Some(bin), _) => - global.loaders.enterClassAndModule(owner, classRep.name, platform.newClassLoader(bin)) + enterClassAndModule(owner, classRep.name, new ClassfileLoader(bin)) } } @@ -221,7 +236,7 @@ abstract class SymbolLoaders { /** * Load contents of a package */ - class PackageLoader(classpath: ClassPath[platform.BinaryRepr]) extends SymbolLoader with FlagAgnosticCompleter { + class PackageLoader(classpath: ClassPath[AbstractFile]) extends SymbolLoader with FlagAgnosticCompleter { protected def description = "package loader "+ classpath.name protected def doComplete(root: Symbol) { @@ -245,8 +260,24 @@ abstract class SymbolLoaders { class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader with FlagAssigningCompleter { private object classfileParser extends { - val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global - } with ClassfileParser + val symbolTable: SymbolLoaders.this.symbolTable.type = SymbolLoaders.this.symbolTable + } with ClassfileParser { + override protected type ThisConstantPool = ConstantPool + override protected def newConstantPool: ThisConstantPool = new ConstantPool + override protected def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol = + SymbolLoaders.this.lookupMemberAtTyperPhaseIfPossible(sym, name) + /* + * The type alias and the cast (where the alias is used) is needed due to problem described + * in SI-7585. In this particular case, the problem is that we need to make sure that symbol + * table used by symbol loaders is exactly the same as they one used by classfileParser. + * If you look at the path-dependent types we have here everything should work out ok but + * due to issue described in SI-7585 type-checker cannot tie the knot here. + * + */ + private type SymbolLoadersRefined = SymbolLoaders { val symbolTable: classfileParser.symbolTable.type } + val loaders = SymbolLoaders.this.asInstanceOf[SymbolLoadersRefined] + val classPath = platform.classPath + } protected def description = "class file "+ classfile.toString @@ -272,7 +303,7 @@ abstract class SymbolLoaders { protected def description = "source file "+ srcfile.toString override def fromSource = true override def sourcefile = Some(srcfile) - protected def doComplete(root: Symbol): Unit = global.currentRun.compileLate(srcfile) + protected def doComplete(root: Symbol): Unit = compileLate(srcfile) } object moduleClassLoader extends SymbolLoader with FlagAssigningCompleter { diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 4c0c16690f..454c9db73c 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -17,6 +17,7 @@ import scala.reflect.internal.{ JavaAccFlags } import scala.reflect.internal.pickling.{PickleBuffer, ByteCodecs} import scala.tools.nsc.io.AbstractFile +import util.ClassPath /** This abstract class implements a class file parser. * @@ -24,18 +25,40 @@ import scala.tools.nsc.io.AbstractFile * @version 1.0 */ abstract class ClassfileParser { - val global: Global - import global._ + val symbolTable: SymbolTable { + def settings: Settings + } + val loaders: SymbolLoaders { + val symbolTable: ClassfileParser.this.symbolTable.type + } + + import symbolTable._ + /** + * If typer phase is defined then perform member lookup of a symbol + * `sym` at typer phase. This method results from refactoring. The + * original author of the logic that uses typer phase didn't explain + * why we need to force infos at that phase specifically. It only mentioned + * that ClassfileParse can be called late (e.g. at flatten phase) and + * we make to make sure we handle such situation properly. + */ + protected def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol + + /** The compiler classpath. */ + def classPath: ClassPath[AbstractFile] + import definitions._ import scala.reflect.internal.ClassfileConstants._ import Flags._ + protected type ThisConstantPool <: ConstantPool + protected def newConstantPool: ThisConstantPool + protected var in: AbstractFileReader = _ // the class file reader protected var clazz: Symbol = _ // the class symbol containing dynamic members protected var staticModule: Symbol = _ // the module symbol containing static members protected var instanceScope: Scope = _ // the scope of all instance definitions protected var staticScope: Scope = _ // the scope of all static definitions - protected var pool: ConstantPool = _ // the classfile's constant pool + protected var pool: ThisConstantPool = _ // the classfile's constant pool protected var isScala: Boolean = _ // does class file describe a scala class? protected var isScalaAnnot: Boolean = _ // does class file describe a scala class with its pickled info in an annotation? protected var isScalaRaw: Boolean = _ // this class file is a scala class with no pickled info @@ -50,8 +73,7 @@ abstract class ClassfileParser { def srcfile = srcfile0 - private def optimized = global.settings.optimise.value - private def currentIsTopLevel = !(currentClass.decodedName containsChar '$') + private def optimized = settings.optimise.value // u1, u2, and u4 are what these data types are called in the JVM spec. // They are an unsigned byte, unsigned char, and unsigned int respectively. @@ -70,7 +92,7 @@ abstract class ClassfileParser { private def readType() = pool getType u2 private object unpickler extends scala.reflect.internal.pickling.UnPickler { - val global: ClassfileParser.this.global.type = ClassfileParser.this.global + val symbolTable: ClassfileParser.this.symbolTable.type = ClassfileParser.this.symbolTable } private def handleMissing(e: MissingRequirementError) = { @@ -119,7 +141,7 @@ abstract class ClassfileParser { this.isScala = false parseHeader() - this.pool = new ConstantPool + this.pool = newConstantPool parseClass() } } @@ -134,11 +156,14 @@ abstract class ClassfileParser { abort(s"class file ${in.file} has unknown version $major.$minor, should be at least $JAVA_MAJOR_VERSION.$JAVA_MINOR_VERSION") } - class ConstantPool { - private val len = u2 - private val starts = new Array[Int](len) - private val values = new Array[AnyRef](len) - private val internalized = new Array[Name](len) + /** + * Constructor of this class should not be called directly, use `newConstantPool` instead. + */ + protected class ConstantPool { + protected val len = u2 + protected val starts = new Array[Int](len) + protected val values = new Array[AnyRef](len) + protected val internalized = new Array[Name](len) { var i = 1 while (i < starts.length) { @@ -212,76 +237,13 @@ abstract class ClassfileParser { getExternalName((in getChar start).toInt) } - /** Return the symbol of the class member at `index`. - * The following special cases exist: - * - If the member refers to special `MODULE$` static field, return - * the symbol of the corresponding module. - * - If the member is a field, and is not found with the given name, - * another try is made by appending `nme.LOCAL_SUFFIX_STRING` - * - If no symbol is found in the right tpe, a new try is made in the - * companion class, in case the owner is an implementation class. - */ - def getMemberSymbol(index: Int, static: Boolean): Symbol = { - if (index <= 0 || len <= index) errorBadIndex(index) - var f = values(index).asInstanceOf[Symbol] - if (f eq null) { - val start = starts(index) - val first = in.buf(start).toInt - if (first != CONSTANT_FIELDREF && - first != CONSTANT_METHODREF && - first != CONSTANT_INTFMETHODREF) errorBadTag(start) - val ownerTpe = getClassOrArrayType(in.getChar(start + 1).toInt) - debuglog("getMemberSymbol(static: " + static + "): owner type: " + ownerTpe + " " + ownerTpe.typeSymbol.originalName) - val (name0, tpe0) = getNameAndType(in.getChar(start + 3).toInt, ownerTpe) - debuglog("getMemberSymbol: name and tpe: " + name0 + ": " + tpe0) - - forceMangledName(tpe0.typeSymbol.name, module = false) - val (name, tpe) = getNameAndType(in.getChar(start + 3).toInt, ownerTpe) - if (name == nme.MODULE_INSTANCE_FIELD) { - val index = in.getChar(start + 1).toInt - val name = getExternalName(in.getChar(starts(index).toInt + 1).toInt) - //assert(name.endsWith("$"), "Not a module class: " + name) - f = forceMangledName(name dropRight 1, module = true) - if (f == NoSymbol) - f = rootMirror.getModuleByName(name dropRight 1) - } else { - val origName = nme.unexpandedName(name) - val owner = if (static) ownerTpe.typeSymbol.linkedClassOfClass else ownerTpe.typeSymbol - f = owner.info.findMember(origName, 0, 0, stableOnly = false).suchThat(_.tpe.widen =:= tpe) - if (f == NoSymbol) - f = owner.info.findMember(newTermName(origName + nme.LOCAL_SUFFIX_STRING), 0, 0, stableOnly = false).suchThat(_.tpe =:= tpe) - if (f == NoSymbol) { - // if it's an impl class, try to find it's static member inside the class - if (ownerTpe.typeSymbol.isImplClass) { - f = ownerTpe.findMember(origName, 0, 0, stableOnly = false).suchThat(_.tpe =:= tpe) - } else { - log("Couldn't find " + name + ": " + tpe + " inside: \n" + ownerTpe) - f = tpe match { - case MethodType(_, _) => owner.newMethod(name.toTermName, owner.pos) - case _ => owner.newVariable(name.toTermName, owner.pos) - } - f setInfo tpe - log("created fake member " + f.fullName) - } - } - } - assert(f != NoSymbol, - s"could not find $name: $tpe in $ownerTpe" + ( - if (settings.debug.value) ownerTpe.members.mkString(", members are:\n ", "\n ", "") else "" - ) - ) - values(index) = f - } - f - } - /** Return a name and a type at the given index. If the type is a method * type, a dummy symbol is created in `ownerTpe`, which is used as the * owner of its value parameters. This might lead to inconsistencies, * if a symbol of the given name already exists, and has a different * type. */ - private def getNameAndType(index: Int, ownerTpe: Type): (Name, Type) = { + protected def getNameAndType(index: Int, ownerTpe: Type): (Name, Type) = { if (index <= 0 || len <= index) errorBadIndex(index) values(index) match { case p: ((Name @unchecked, Type @unchecked)) => p @@ -381,37 +343,16 @@ abstract class ClassfileParser { } /** Throws an exception signaling a bad constant index. */ - private def errorBadIndex(index: Int) = + protected def errorBadIndex(index: Int) = abort(s"bad constant pool index: $index at pos: ${in.bp}") /** Throws an exception signaling a bad tag at given address. */ - private def errorBadTag(start: Int) = - abort("bad constant pool tag ${in.buf(start)} at byte $start") - } - - /** Try to force the chain of enclosing classes for the given name. Otherwise - * flatten would not lift classes that were not referenced in the source code. - */ - def forceMangledName(name: Name, module: Boolean): Symbol = { - val parts = name.decode.toString.split(Array('.', '$')) - var sym: Symbol = rootMirror.RootClass - - // was "at flatten.prev" - enteringFlatten { - for (part0 <- parts; if !(part0 == ""); part = newTermName(part0)) { - val sym1 = enteringIcode { - sym.linkedClassOfClass.info - sym.info.decl(part.encode) - }//.suchThat(module == _.isModule) - - sym = sym1 orElse sym.info.decl(part.encode.toTypeName) - } - } - sym + protected def errorBadTag(start: Int) = + abort(s"bad constant pool tag ${in.buf(start)} at byte $start") } private def loadClassSymbol(name: Name): Symbol = { - val file = global.classPath findSourceFile ("" +name) getOrElse { + val file = classPath findSourceFile ("" +name) getOrElse { // SI-5593 Scaladoc's current strategy is to visit all packages in search of user code that can be documented // therefore, it will rummage through the classpath triggering errors whenever it encounters package objects // that are not in their correct place (see bug for details) @@ -419,7 +360,7 @@ abstract class ClassfileParser { warning(s"Class $name not found - continuing with a stub.") return NoSymbol.newClass(name.toTypeName) } - val completer = new global.loaders.ClassfileLoader(file) + val completer = new loaders.ClassfileLoader(file) var owner: Symbol = rootMirror.RootClass var sym: Symbol = NoSymbol var ss: Name = null @@ -607,9 +548,8 @@ abstract class ClassfileParser { if (name == nme.CONSTRUCTOR) sawPrivateConstructor = true in.skip(2); skipAttributes() - } - else { - if ((sflags & PRIVATE) != 0L && optimized) { + } else { + if ((sflags & PRIVATE) != 0L && optimized) { // TODO this should be !optimized, no? See c4181f656d. in.skip(4); skipAttributes() } else { val name = readName() @@ -619,7 +559,7 @@ abstract class ClassfileParser { info match { case MethodType(params, restpe) => // if this is a non-static inner class, remove the explicit outer parameter - val newParams = innerClasses getEntry currentClass match { + val paramsNoOuter = innerClasses getEntry currentClass match { case Some(entry) if !isScalaRaw && !entry.jflags.isStatic => /* About `clazz.owner.isPackage` below: SI-5957 * For every nested java class A$B, there are two symbols in the scala compiler. @@ -633,6 +573,15 @@ abstract class ClassfileParser { case _ => params } + val newParams = paramsNoOuter match { + case (init :+ tail) if jflags.isSynthetic => + // SI-7455 strip trailing dummy argument ("access constructor tag") from synthetic constructors which + // are added when an inner class needs to access a private constructor. + init + case _ => + paramsNoOuter + } + info = MethodType(newParams, clazz.tpe) } // Note: the info may be overrwritten later with a generic signature @@ -1057,7 +1006,7 @@ abstract class ClassfileParser { def enterClassAndModule(entry: InnerClassEntry, file: AbstractFile) { def jflags = entry.jflags - val completer = new global.loaders.ClassfileLoader(file) + val completer = new loaders.ClassfileLoader(file) val name = entry.originalName val sflags = jflags.toScalaFlags val owner = ownerForFlags(jflags) @@ -1065,7 +1014,7 @@ abstract class ClassfileParser { val innerClass = owner.newClass(name.toTypeName, NoPosition, sflags) setInfo completer val innerModule = owner.newModule(name.toTermName, NoPosition, sflags) setInfo completer - innerModule.moduleClass setInfo global.loaders.moduleClassLoader + innerModule.moduleClass setInfo loaders.moduleClassLoader List(innerClass, innerModule.moduleClass) foreach (_.associatedFile = file) scope enter innerClass @@ -1086,7 +1035,7 @@ abstract class ClassfileParser { for (entry <- innerClasses.entries) { // create a new class member for immediate inner classes if (entry.outerName == currentClass) { - val file = global.classPath.findSourceFile(entry.externalName.toString) getOrElse { + val file = classPath.findSourceFile(entry.externalName.toString) getOrElse { throw new AssertionError(entry.externalName) } enterClassAndModule(entry, file) @@ -1171,19 +1120,15 @@ abstract class ClassfileParser { case Some(entry) => innerSymbol(entry) case _ => NoSymbol } - // if loading during initialization of `definitions` typerPhase is not yet set. - // in that case we simply load the member at the current phase - @inline private def enteringTyperIfPossible(body: => Symbol): Symbol = - if (currentRun.typerPhase eq null) body else enteringTyper(body) private def innerSymbol(entry: InnerClassEntry): Symbol = { val name = entry.originalName.toTypeName val enclosing = entry.enclosing def getMember = ( if (enclosing == clazz) entry.scope lookup name - else enclosing.info member name + else lookupMemberAtTyperPhaseIfPossible(enclosing, name) ) - enteringTyperIfPossible(getMember) + getMember /* There used to be an assertion that this result is not NoSymbol; changing it to an error * revealed it had been going off all the time, but has been swallowed by a catch t: Throwable * in Repository.scala. Since it has been accomplishing nothing except misleading anyone who diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index 01a117895f..f704d8ac89 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -20,6 +20,8 @@ import scala.reflect.internal.JavaAccFlags */ abstract class ICodeReader extends ClassfileParser { val global: Global + val symbolTable: global.type + val loaders: global.loaders.type import global._ import icodes._ @@ -28,6 +30,95 @@ abstract class ICodeReader extends ClassfileParser { var method: IMethod = NoIMethod // the current IMethod var isScalaModule = false + override protected type ThisConstantPool = ICodeConstantPool + override protected def newConstantPool = new ICodeConstantPool + + /** Try to force the chain of enclosing classes for the given name. Otherwise + * flatten would not lift classes that were not referenced in the source code. + */ + def forceMangledName(name: Name, module: Boolean): Symbol = { + val parts = name.decode.toString.split(Array('.', '$')) + var sym: Symbol = rootMirror.RootClass + + // was "at flatten.prev" + enteringFlatten { + for (part0 <- parts; if !(part0 == ""); part = newTermName(part0)) { + val sym1 = enteringIcode { + sym.linkedClassOfClass.info + sym.info.decl(part.encode) + }//.suchThat(module == _.isModule) + + sym = sym1 orElse sym.info.decl(part.encode.toTypeName) + } + } + sym + } + + protected class ICodeConstantPool extends ConstantPool { + /** Return the symbol of the class member at `index`. + * The following special cases exist: + * - If the member refers to special `MODULE$` static field, return + * the symbol of the corresponding module. + * - If the member is a field, and is not found with the given name, + * another try is made by appending `nme.LOCAL_SUFFIX_STRING` + * - If no symbol is found in the right tpe, a new try is made in the + * companion class, in case the owner is an implementation class. + */ + def getMemberSymbol(index: Int, static: Boolean): Symbol = { + if (index <= 0 || len <= index) errorBadIndex(index) + var f = values(index).asInstanceOf[Symbol] + if (f eq null) { + val start = starts(index) + val first = in.buf(start).toInt + if (first != CONSTANT_FIELDREF && + first != CONSTANT_METHODREF && + first != CONSTANT_INTFMETHODREF) errorBadTag(start) + val ownerTpe = getClassOrArrayType(in.getChar(start + 1).toInt) + debuglog("getMemberSymbol(static: " + static + "): owner type: " + ownerTpe + " " + ownerTpe.typeSymbol.originalName) + val (name0, tpe0) = getNameAndType(in.getChar(start + 3).toInt, ownerTpe) + debuglog("getMemberSymbol: name and tpe: " + name0 + ": " + tpe0) + + forceMangledName(tpe0.typeSymbol.name, module = false) + val (name, tpe) = getNameAndType(in.getChar(start + 3).toInt, ownerTpe) + if (name == nme.MODULE_INSTANCE_FIELD) { + val index = in.getChar(start + 1).toInt + val name = getExternalName(in.getChar(starts(index).toInt + 1).toInt) + //assert(name.endsWith("$"), "Not a module class: " + name) + f = forceMangledName(name dropRight 1, module = true) + if (f == NoSymbol) + f = rootMirror.getModuleByName(name dropRight 1) + } else { + val origName = nme.unexpandedName(name) + val owner = if (static) ownerTpe.typeSymbol.linkedClassOfClass else ownerTpe.typeSymbol + f = owner.info.findMember(origName, 0, 0, stableOnly = false).suchThat(_.tpe.widen =:= tpe) + if (f == NoSymbol) + f = owner.info.findMember(newTermName(origName + nme.LOCAL_SUFFIX_STRING), 0, 0, stableOnly = false).suchThat(_.tpe =:= tpe) + if (f == NoSymbol) { + // if it's an impl class, try to find it's static member inside the class + if (ownerTpe.typeSymbol.isImplClass) { + f = ownerTpe.findMember(origName, 0, 0, stableOnly = false).suchThat(_.tpe =:= tpe) + } else { + log("Couldn't find " + name + ": " + tpe + " inside: \n" + ownerTpe) + f = tpe match { + case MethodType(_, _) => owner.newMethod(name.toTermName, owner.pos) + case _ => owner.newVariable(name.toTermName, owner.pos) + } + f setInfo tpe + log("created fake member " + f.fullName) + } + } + } + assert(f != NoSymbol, + s"could not find $name: $tpe in $ownerTpe" + ( + if (settings.debug.value) ownerTpe.members.mkString(", members are:\n ", "\n ", "") else "" + ) + ) + values(index) = f + } + f + } + } + /** Read back bytecode for the given class symbol. It returns * two IClass objects, one for static members and one * for non-static members. diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 0f65b11e9b..ee687e56b0 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -706,7 +706,8 @@ abstract class Erasure extends AddInterfaces // } typed(untyped) } - } else tree + } else qual1 + case Apply(TypeApply(sel @ Select(qual, name), List(targ)), List()) if tree.symbol == Any_isInstanceOf => targ.tpe match { diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 60641d6752..f3a22a2cee 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1194,7 +1194,7 @@ trait Contexts { self: Analyzer => override final def imports = impInfo :: super.imports override final def firstImport = Some(impInfo) override final def isRootImport = !tree.pos.isDefined - override final def toString = s"ImportContext { $impInfo; outer.owner = ${outer.owner} }" + override final def toString = super.toString + " with " + s"ImportContext { $impInfo; outer.owner = ${outer.owner} }" } /** A buffer for warnings and errors that are accumulated during speculative type checking. */ @@ -1340,6 +1340,7 @@ trait Contexts { self: Analyzer => } object ContextMode { + import scala.language.implicitConversions private implicit def liftIntBitsToContextState(bits: Int): ContextMode = apply(bits) def apply(bits: Int): ContextMode = new ContextMode(bits) final val NOmode: ContextMode = 0 diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 100112fec1..4265efc839 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -60,6 +60,8 @@ trait Implicits { * @return A search result */ def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean, pos: Position): SearchResult = { + // Note that the isInvalidConversionTarget seems to make a lot more sense right here, before all the + // work is performed, than at the point where it presently exists. val shouldPrint = printTypings && !context.undetparams.isEmpty val rawTypeStart = if (Statistics.canEnable) Statistics.startCounter(rawTypeImpl) else null val findMemberStart = if (Statistics.canEnable) Statistics.startCounter(findMemberImpl) else null @@ -813,7 +815,7 @@ trait Implicits { if (search.isDivergent && countdown > 0) { countdown -= 1 implicitSym = i.sym - log("discarding divergent implicit ${implicitSym} during implicit search") + log(s"discarding divergent implicit $implicitSym during implicit search") SearchFailure } else search } @@ -1335,12 +1337,18 @@ trait Implicits { } } if (result.isSuccess && isView) { + def maybeInvalidConversionError(msg: String) { + // We have to check context.ambiguousErrors even though we are calling "issueAmbiguousError" + // which ostensibly does exactly that before issuing the error. Why? I have no idea. Test is pos/t7690. + if (context.ambiguousErrors) + context.issueAmbiguousError(AmbiguousImplicitTypeError(tree, msg)) + } if (isInvalidConversionTarget(pt)) { - context.issueAmbiguousError(AmbiguousImplicitTypeError(tree, "the result type of an implicit conversion must be more specific than AnyRef")) + maybeInvalidConversionError("the result type of an implicit conversion must be more specific than AnyRef") result = SearchFailure } else if (isInvalidConversionSource(pt)) { - context.issueAmbiguousError(AmbiguousImplicitTypeError(tree, "an expression of type Null is ineligible for implicit conversion")) + maybeInvalidConversionError("an expression of type Null is ineligible for implicit conversion") result = SearchFailure } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 06892053fa..8ca0d82e93 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -700,7 +700,7 @@ trait Infer extends Checkable { tp nonPrivateMember nme.apply match { case NoSymbol => tp case sym if !sym.isOverloaded && sym.isPublic => OverloadedType(tp, sym.alternatives) - case sym => OverloadedType(tp, sym filter (_.isPublic) alternatives) + case sym => OverloadedType(tp, sym.filter(_.isPublic).alternatives) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index c9849eebb5..1b6963b598 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -249,7 +249,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans * 1.8.1 M's type is a subtype of O's type, or * 1.8.2 M is of type []S, O is of type ()T and S <: T, or * 1.8.3 M is of type ()S, O is of type []T and S <: T, or - * 1.9. If M is a macro def, O cannot be deferred. + * 1.9. If M is a macro def, O cannot be deferred unless there's a concrete method overriding O. * 1.10. If M is not a macro def, O cannot be a macro def. * 2. Check that only abstract classes have deferred members * 3. Check that concrete classes do not have deferred definitions @@ -440,7 +440,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } else if (other.isValue && other.isLazy && !other.isSourceMethod && !other.isDeferred && member.isValue && !member.isLazy) { overrideError("must be declared lazy to override a concrete lazy value") - } else if (other.isDeferred && member.isTermMacro) { // (1.9) + } else if (other.isDeferred && member.isTermMacro && member.extendedOverriddenSymbols.forall(_.isDeferred)) { // (1.9) overrideError("cannot be used here - term macros cannot override abstract methods") } else if (other.isTermMacro && !member.isTermMacro) { // (1.10) overrideError("cannot be used here - only term macros can override term macros") diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index f9e34106ec..d2ff47626d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2103,6 +2103,8 @@ trait Typers extends Adaptations with Tags with TypersTracking { // !!! This method is redundant with other, less buggy ones. def decompose(call: Tree): (Tree, List[Tree]) = call match { + case _ if call.isErrorTyped => // e.g. SI-7636 + (call, Nil) case Apply(fn, args) => // an object cannot be allowed to pass a reference to itself to a superconstructor // because of initialization issues; SI-473, SI-3913, SI-6928. @@ -4012,6 +4014,7 @@ trait Typers extends Adaptations with Tags with TypersTracking { findSelection(cxTree) match { case Some((opName, treeInfo.Applied(_, targs, _))) => val fun = gen.mkTypeApply(Select(qual, opName), targs) + if (opName == nme.updateDynamic) suppressMacroExpansion(fun) // SI-7617 atPos(qual.pos)(Apply(fun, Literal(Constant(name.decode)) :: Nil)) case _ => setError(tree) @@ -4176,7 +4179,9 @@ trait Typers extends Adaptations with Tags with TypersTracking { } def typedAssign(lhs: Tree, rhs: Tree): Tree = { - val lhs1 = typed(lhs, EXPRmode | LHSmode) + // see SI-7617 for an explanation of why macro expansion is suppressed + def typedLhs(lhs: Tree) = typed(lhs, EXPRmode | LHSmode) + val lhs1 = unsuppressMacroExpansion(typedLhs(suppressMacroExpansion(lhs))) val varsym = lhs1.symbol // see #2494 for double error message example diff --git a/src/compiler/scala/tools/reflect/MacroImplementations.scala b/src/compiler/scala/tools/reflect/MacroImplementations.scala index 8e1bcb5f87..4e3761454d 100644 --- a/src/compiler/scala/tools/reflect/MacroImplementations.scala +++ b/src/compiler/scala/tools/reflect/MacroImplementations.scala @@ -94,7 +94,8 @@ abstract class MacroImplementations { def errorAtIndex(idx: Int, msg: String) = c.error(new OffsetPosition(strTree.pos.source, strTree.pos.point + idx), msg) def wrongConversionString(idx: Int) = errorAtIndex(idx, "wrong conversion string") def illegalConversionCharacter(idx: Int) = errorAtIndex(idx, "illegal conversion character") - def nonEscapedPercent(idx: Int) = errorAtIndex(idx, "percent signs not directly following splicees must be escaped") + def nonEscapedPercent(idx: Int) = errorAtIndex(idx, + "conversions must follow a splice; use %% for literal %, %n for newline") // STEP 1: handle argument conversion // 1) "...${smth}" => okay, equivalent to "...${smth}%s" diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala index bdd6a02043..82f2c5dc74 100644 --- a/src/compiler/scala/tools/util/PathResolver.scala +++ b/src/compiler/scala/tools/util/PathResolver.scala @@ -8,8 +8,8 @@ package tools package util import scala.tools.reflect.WrappedProperties.AccessControl -import scala.tools.nsc.{ Settings, GenericRunnerSettings } -import scala.tools.nsc.util.{ ClassPath, JavaClassPath, ScalaClassLoader } +import scala.tools.nsc.{ Settings } +import scala.tools.nsc.util.{ ClassPath, JavaClassPath } import scala.reflect.io.{ File, Directory, Path, AbstractFile } import ClassPath.{ JavaContext, DefaultJavaContext, join, split } import PartialFunction.condOpt |