diff options
Diffstat (limited to 'src')
83 files changed, 622 insertions, 406 deletions
diff --git a/src/compiler/scala/reflect/quasiquotes/Holes.scala b/src/compiler/scala/reflect/quasiquotes/Holes.scala index 38b05f9d4b..6fa6b9b37a 100644 --- a/src/compiler/scala/reflect/quasiquotes/Holes.scala +++ b/src/compiler/scala/reflect/quasiquotes/Holes.scala @@ -132,7 +132,7 @@ trait Holes { self: Quasiquotes => private def mapF(tree: Tree, f: Tree => Tree): Tree = if (f(Ident(TermName("x"))) equalsStructure Ident(TermName("x"))) tree else { - val x: TermName = c.freshName() + val x = TermName(c.freshName()) // q"$tree.map { $x => ${f(Ident(x))} }" Apply(Select(tree, nme.map), Function(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree) :: Nil, @@ -187,7 +187,7 @@ trait Holes { self: Quasiquotes => lazy val tree = tptopt.map { tpt => val TypeDef(_, _, _, typedTpt) = - try c.typeCheck(TypeDef(NoMods, TypeName("T"), Nil, tpt)) + try c.typecheck(TypeDef(NoMods, TypeName("T"), Nil, tpt)) catch { case TypecheckException(pos, msg) => c.abort(pos.asInstanceOf[c.Position], msg) } val tpe = typedTpt.tpe val (iterableRank, _) = stripIterable(tpe) diff --git a/src/compiler/scala/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/reflect/quasiquotes/Reifiers.scala index cc98717c4e..7c0e7dfbb8 100644 --- a/src/compiler/scala/reflect/quasiquotes/Reifiers.scala +++ b/src/compiler/scala/reflect/quasiquotes/Reifiers.scala @@ -247,7 +247,7 @@ trait Reifiers { self: Quasiquotes => hole.tree case Placeholder(hole: UnapplyHole) => hole.treeNoUnlift case FreshName(prefix) if prefix != nme.QUASIQUOTE_NAME_PREFIX => - def fresh() = c.freshName[TermName](nme.QUASIQUOTE_NAME_PREFIX) + def fresh() = c.freshName(TermName(nme.QUASIQUOTE_NAME_PREFIX)) def introduceName() = { val n = fresh(); nameMap(name) += n; n} def result(n: Name) = if (isReifyingExpressions) Ident(n) else Bind(n, Ident(nme.WILDCARD)) if (isReifyingPatterns) result(introduceName()) diff --git a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala index 52ddcb154b..e41fbf042a 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala @@ -39,7 +39,7 @@ trait GenSymbols { else if (sym.isModuleClass) if (sym.sourceModule.isLocatable) Select(Select(reify(sym.sourceModule), nme.asModule), nme.moduleClass) else reifySymDef(sym) - else if (sym.isPackage) + else if (sym.hasPackageFlag) mirrorMirrorCall(nme.staticPackage, reify(sym.fullName)) else if (sym.isLocatable) { /* This is a fancy conundrum that stems from the fact that Scala allows diff --git a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala index 743fe131c4..f34d75140b 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala @@ -153,7 +153,7 @@ trait GenTrees { else mirrorCall(nme.Ident, reify(name)) case Select(qual, name) => - if (qual.symbol != null && qual.symbol.isPackage) { + if (qual.symbol != null && qual.symbol.hasPackageFlag) { mirrorBuildCall(nme.mkIdent, reify(sym)) } else { val effectiveName = if (sym != null && sym != NoSymbol) sym.name else name @@ -199,7 +199,7 @@ trait GenTrees { } } else tree match { - case Select(qual, name) if !qual.symbol.isPackage => + case Select(qual, name) if !qual.symbol.hasPackageFlag => if (reifyDebug) println(s"reifying Select($qual, $name)") mirrorCall(nme.Select, reify(qual), reify(name)) case SelectFromTypeTree(qual, name) => diff --git a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala index de9fec0df5..b5b0f93750 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala @@ -15,7 +15,7 @@ trait GenUtils { def reifyProduct(prefix: String, elements: List[Any]): Tree = { // reflection would be more robust, but, hey, this is a hot path if (prefix.startsWith("Tuple")) scalaFactoryCall(prefix, (elements map reify).toList: _*) - else mirrorCall(prefix, (elements map reify): _*) + else mirrorCall(TermName(prefix), (elements map reify): _*) } // helper functions @@ -49,16 +49,16 @@ trait GenUtils { call("" + nme.MIRROR_PREFIX + name, args: _*) def mirrorFactoryCall(value: Product, args: Tree*): Tree = - mirrorFactoryCall(value.productPrefix, args: _*) + mirrorFactoryCall(TermName(value.productPrefix), args: _*) def mirrorFactoryCall(prefix: TermName, args: Tree*): Tree = - mirrorCall("" + prefix, args: _*) + mirrorCall(TermName("" + prefix), args: _*) def scalaFactoryCall(name: TermName, args: Tree*): Tree = call(s"scala.$name.apply", args: _*) def scalaFactoryCall(name: String, args: Tree*): Tree = - scalaFactoryCall(name: TermName, args: _*) + scalaFactoryCall(TermName(name), args: _*) def mkList(args: List[Tree]): Tree = scalaFactoryCall("collection.immutable.List", args: _*) diff --git a/src/compiler/scala/tools/ant/FastScalac.scala b/src/compiler/scala/tools/ant/FastScalac.scala index c3eb9eef9c..6f0a30aa9d 100644 --- a/src/compiler/scala/tools/ant/FastScalac.scala +++ b/src/compiler/scala/tools/ant/FastScalac.scala @@ -15,7 +15,7 @@ import org.apache.tools.ant.types.Path import scala.tools.nsc.Settings import scala.tools.nsc.io.File import scala.tools.nsc.settings.FscSettings -import scala.tools.nsc.util.ScalaClassLoader +import scala.reflect.internal.util.ScalaClassLoader /** An Ant task to compile with the fast Scala compiler (`fsc`). * diff --git a/src/compiler/scala/tools/ant/sabbus/Compiler.scala b/src/compiler/scala/tools/ant/sabbus/Compiler.scala index 65cd9f41c2..81cd1f3196 100644 --- a/src/compiler/scala/tools/ant/sabbus/Compiler.scala +++ b/src/compiler/scala/tools/ant/sabbus/Compiler.scala @@ -12,7 +12,7 @@ package scala.tools.ant.sabbus import java.io.File import java.net.URL import java.lang.reflect.InvocationTargetException -import scala.tools.nsc.util.ScalaClassLoader +import scala.reflect.internal.util.ScalaClassLoader class Compiler(classpath: Array[URL], val settings: Settings) { diff --git a/src/compiler/scala/tools/ant/sabbus/ScalacFork.scala b/src/compiler/scala/tools/ant/sabbus/ScalacFork.scala index 595b45ae51..cde827ba54 100644 --- a/src/compiler/scala/tools/ant/sabbus/ScalacFork.scala +++ b/src/compiler/scala/tools/ant/sabbus/ScalacFork.scala @@ -16,7 +16,7 @@ import org.apache.tools.ant.taskdefs.Java import org.apache.tools.ant.util.{ GlobPatternMapper, SourceFileScanner } import org.apache.tools.ant.BuildException import scala.tools.nsc.io -import scala.tools.nsc.util.ScalaClassLoader +import scala.reflect.internal.util.ScalaClassLoader /** An Ant task to compile with the new Scala compiler (NSC). * diff --git a/src/compiler/scala/tools/nsc/EvalLoop.scala b/src/compiler/scala/tools/nsc/EvalLoop.scala index 15a296c836..73f4b9a119 100644 --- a/src/compiler/scala/tools/nsc/EvalLoop.scala +++ b/src/compiler/scala/tools/nsc/EvalLoop.scala @@ -6,6 +6,7 @@ package scala.tools.nsc import scala.annotation.tailrec +import scala.io.StdIn import java.io.EOFException trait EvalLoop { @@ -14,7 +15,7 @@ trait EvalLoop { def loop(action: (String) => Unit) { @tailrec def inner() { Console.print(prompt) - val line = try Console.readLine() catch { case _: EOFException => null } + val line = try StdIn.readLine() catch { case _: EOFException => null } if (line != null && line != "") { action(line) inner() diff --git a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala index dbdeec809f..2584054686 100644 --- a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala +++ b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala @@ -6,6 +6,7 @@ package scala.tools.nsc import GenericRunnerCommand._ +import scala.reflect.internal.util.ScalaClassLoader /** A command for ScriptRunner */ class GenericRunnerCommand( @@ -32,7 +33,7 @@ extends CompilerCommand(args, settings) { private def guessHowToRun(target: String): GenericRunnerCommand.HowToRun = { if (!ok) Error else if (io.Jar.isJarOrZip(target)) AsJar - else if (util.ScalaClassLoader.classExists(settings.classpathURLs, target)) AsObject + else if (ScalaClassLoader.classExists(settings.classpathURLs, target)) AsObject else { val f = io.File(target) if (!f.hasExtension("class", "jar", "zip") && f.canRead) AsScript diff --git a/src/compiler/scala/tools/nsc/ObjectRunner.scala b/src/compiler/scala/tools/nsc/ObjectRunner.scala index 7c14f4943f..8e01418e8b 100644 --- a/src/compiler/scala/tools/nsc/ObjectRunner.scala +++ b/src/compiler/scala/tools/nsc/ObjectRunner.scala @@ -7,8 +7,8 @@ package scala.tools.nsc import java.net.URL -import util.ScalaClassLoader import util.Exceptional.unwrap +import scala.reflect.internal.util.ScalaClassLoader trait CommonRunner { /** Run a given object, specified by name, using a diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala index 72a4b69536..4e7a527a5a 100644 --- a/src/compiler/scala/tools/nsc/Reporting.scala +++ b/src/compiler/scala/tools/nsc/Reporting.scala @@ -46,7 +46,7 @@ trait Reporting extends scala.reflect.internal.Reporting { self: ast.Positions w private val _deprecationWarnings = new ConditionalWarning("deprecation", settings.deprecation)() private val _uncheckedWarnings = new ConditionalWarning("unchecked", settings.unchecked)() private val _featureWarnings = new ConditionalWarning("feature", settings.feature)() - private val _inlinerWarnings = new ConditionalWarning("inliner", settings.YinlinerWarnings)(if (settings.isBCodeAskedFor) settings.YoptWarnings.name else settings.YinlinerWarnings.name) + private val _inlinerWarnings = new ConditionalWarning("inliner", settings.YinlinerWarnings)(if (settings.isBCodeActive) settings.YoptWarnings.name else settings.YinlinerWarnings.name) private val _allConditionalWarnings = List(_deprecationWarnings, _uncheckedWarnings, _featureWarnings, _inlinerWarnings) // TODO: remove in favor of the overload that takes a Symbol, give that argument a default (NoSymbol) diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala index 9c8e13a1a9..6fe85cde7a 100644 --- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala @@ -9,6 +9,7 @@ package ast import scala.compat.Platform.EOL import symtab.Flags._ import scala.language.postfixOps +import scala.reflect.internal.util.ListOfNil /** The object `nodePrinter` converts the internal tree * representation to a string. diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 3a502bdb7a..934257092f 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -308,7 +308,7 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => // Erasing locally-defined symbols is useful to prevent tree corruption, but erasing external bindings is not, // therefore we want to retain those bindings, especially given that restoring them can be impossible // if we move these trees into lexical contexts different from their original locations. - if (dupl.hasSymbol) { + if (dupl.hasSymbolField) { val sym = dupl.symbol val vetoScope = !brutally && !(locals contains sym) && !(locals contains sym.deSkolemize) val vetoThis = dupl.isInstanceOf[This] && sym.isPackageClass diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 4663810003..67e91ae857 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -13,7 +13,7 @@ import scala.collection.{ mutable, immutable } import mutable.{ ListBuffer, StringBuilder } import scala.reflect.internal.{ Precedence, ModifierFlags => Flags } import scala.reflect.internal.Chars.{ isScalaLetter } -import scala.reflect.internal.util.{ SourceFile, Position, FreshNameCreator } +import scala.reflect.internal.util.{ SourceFile, Position, FreshNameCreator, ListOfNil } import Tokens._ /** Historical note: JavaParsers started life as a direct copy of Parsers @@ -2788,7 +2788,7 @@ self => */ def packageObjectDef(start: Offset): PackageDef = { val defn = objectDef(in.offset, NoMods) - val pidPos = o2p(defn.pos.startOrPoint) + val pidPos = o2p(defn.pos.start) val pkgPos = r2p(start, pidPos.point) gen.mkPackageObject(defn, pidPos, pkgPos) } diff --git a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala index 8cd915bf22..d2a999cdec 100755 --- a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala @@ -8,6 +8,7 @@ package ast.parser import scala.collection.{ mutable, immutable } import symtab.Flags.MUTABLE +import scala.reflect.internal.util.ListOfNil import scala.reflect.internal.util.StringOps.splitWhere /** This class builds instance of `Tree` that represent XML. diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index cf52ad6636..137954b52d 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -876,13 +876,20 @@ abstract class GenICode extends SubComponent { genLoadModule(ctx, tree) generatedType = toTypeKind(sym.info) } else { - try { - val Some(l) = ctx.method.lookupLocal(sym) - ctx.bb.emit(LOAD_LOCAL(l), tree.pos) - generatedType = l.kind - } catch { - case ex: MatchError => - abort("symbol " + sym + " does not exist in " + ctx.method) + ctx.method.lookupLocal(sym) match { + case Some(l) => + ctx.bb.emit(LOAD_LOCAL(l), tree.pos) + generatedType = l.kind + case None => + val saved = settings.uniqid + settings.uniqid.value = true + try { + val methodCode = unit.body.collect { case dd: DefDef + if dd.symbol == ctx.method.symbol => showCode(dd); + }.headOption.getOrElse("<unknown>") + abort(s"symbol $sym does not exist in ${ctx.method}, which contains locals ${ctx.method.locals.mkString(",")}. \nMethod code: $methodCode") + } + finally settings.uniqid.value = saved } } } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index 3b7dbc18da..18468f5ae3 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -780,8 +780,8 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { var fieldList = List[String]() for (f <- fieldSymbols if f.hasGetter; - g = f.getter(cls); - s = f.setter(cls); + g = f.getterIn(cls); + s = f.setterIn(cls); if g.isPublic && !(f.name startsWith "$") ) { // inserting $outer breaks the bean diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala index d8a17e975e..d690542f0e 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala @@ -11,11 +11,12 @@ import scala.collection.concurrent.TrieMap import scala.reflect.internal.util.Position import scala.tools.asm import asm.Opcodes -import scala.tools.asm.tree.{MethodInsnNode, InnerClassNode, ClassNode} +import scala.tools.asm.tree.{MethodNode, MethodInsnNode, InnerClassNode, ClassNode} import scala.tools.nsc.backend.jvm.BTypes.{InlineInfo, MethodInlineInfo} import scala.tools.nsc.backend.jvm.BackendReporting._ import scala.tools.nsc.backend.jvm.opt._ import scala.collection.convert.decorateAsScala._ +import scala.tools.nsc.settings.ScalaSettings /** * The BTypes component defines The BType class hierarchy. A BType stores all type information @@ -39,6 +40,8 @@ abstract class BTypes { */ val byteCodeRepository: ByteCodeRepository + val localOpt: LocalOpt[this.type] + val inliner: Inliner[this.type] val callGraph: CallGraph[this.type] @@ -48,14 +51,9 @@ abstract class BTypes { // Allows to define per-run caches here and in the CallGraph component, which don't have a global def recordPerRunCache[T <: collection.generic.Clearable](cache: T): T - // When building the call graph, we need to know if global inlining is allowed (the component doesn't have a global) - def inlineGlobalEnabled: Boolean - - // When the inliner is not enabled, there's no point in adding InlineInfos to all ClassBTypes - def inlinerEnabled: Boolean + // Allows access to the compiler settings for backend components that don't have a global in scope + def compilerSettings: ScalaSettings - // Settings that define what kind of optimizer warnings are emitted. - def warnSettings: WarnSettings /** * A map from internal names to ClassBTypes. Every ClassBType is added to this map on its @@ -83,6 +81,18 @@ abstract class BTypes { val javaDefinedClasses: collection.mutable.Set[InternalName] = recordPerRunCache(collection.mutable.Set.empty) /** + * Cache, contains methods whose unreachable instructions are eliminated. + * + * The ASM Analyzer class does not compute any frame information for unreachable instructions. + * Transformations that use an analyzer (including inlining) therefore require unreachable code + * to be eliminated. + * + * This cache allows running dead code elimination whenever an analyzer is used. If the method + * is already optimized, DCE can return early. + */ + val unreachableCodeEliminated: collection.mutable.Set[MethodNode] = recordPerRunCache(collection.mutable.Set.empty) + + /** * Obtain the BType for a type descriptor or internal name. For class descriptors, the ClassBType * is constructed by parsing the corresponding classfile. * @@ -229,7 +239,7 @@ abstract class BTypes { // The InlineInfo is built from the classfile (not from the symbol) for all classes that are NOT // being compiled. For those classes, the info is only needed if the inliner is enabled, othewise // we can save the memory. - if (!inlinerEnabled) BTypes.EmptyInlineInfo + if (!compilerSettings.YoptInlinerEnabled) BTypes.EmptyInlineInfo else fromClassfileAttribute getOrElse fromClassfileWithoutAttribute } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala index eeb6ed24a2..1b9fd5e298 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala @@ -7,9 +7,10 @@ package scala.tools.nsc package backend.jvm import scala.tools.asm -import scala.tools.nsc.backend.jvm.opt.{CallGraph, Inliner, ByteCodeRepository} +import scala.tools.nsc.backend.jvm.opt.{LocalOpt, CallGraph, Inliner, ByteCodeRepository} import scala.tools.nsc.backend.jvm.BTypes.{InlineInfo, MethodInlineInfo, InternalName} import BackendReporting._ +import scala.tools.nsc.settings.ScalaSettings /** * This class mainly contains the method classBTypeFromSymbol, which extracts the necessary @@ -37,6 +38,8 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { val byteCodeRepository = new ByteCodeRepository(global.classPath, javaDefinedClasses, recordPerRunCache(collection.concurrent.TrieMap.empty)) + val localOpt: LocalOpt[this.type] = new LocalOpt(this) + val inliner: Inliner[this.type] = new Inliner(this) val callGraph: CallGraph[this.type] = new CallGraph(this) @@ -49,19 +52,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { def recordPerRunCache[T <: collection.generic.Clearable](cache: T): T = perRunCaches.recordCache(cache) - def inlineGlobalEnabled: Boolean = settings.YoptInlineGlobal - - def inlinerEnabled: Boolean = settings.YoptInlinerEnabled - - def warnSettings: WarnSettings = { - val c = settings.YoptWarningsChoices - // cannot extract settings.YoptWarnings into a local val due to some dependent typing issue. - WarnSettings( - !settings.YoptWarnings.isSetByUser || settings.YoptWarnings.contains(c.atInlineFailedSummary.name) || settings.YoptWarnings.contains(c.atInlineFailed.name), - settings.YoptWarnings.contains(c.noInlineMixed.name), - settings.YoptWarnings.contains(c.noInlineMissingBytecode.name), - settings.YoptWarnings.contains(c.noInlineMissingScalaInlineInfoAttr.name)) - } + def compilerSettings: ScalaSettings = settings // helpers that need access to global. // TODO @lry create a separate component, they don't belong to BTypesFromSymbols @@ -418,8 +409,8 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { // phase travel required, see implementation of `compiles`. for nested classes, it checks if the // enclosingTopLevelClass is being compiled. after flatten, all classes are considered top-level, // so `compiles` would return `false`. - if (exitingPickler(currentRun.compiles(classSym))) buildFromSymbol // InlineInfo required for classes being compiled, we have to create the classfile attribute - else if (!inlinerEnabled) BTypes.EmptyInlineInfo // For other classes, we need the InlineInfo only inf the inliner is enabled. + if (exitingPickler(currentRun.compiles(classSym))) buildFromSymbol // InlineInfo required for classes being compiled, we have to create the classfile attribute + else if (!compilerSettings.YoptInlinerEnabled) BTypes.EmptyInlineInfo // For other classes, we need the InlineInfo only inf the inliner is enabled. else { // For classes not being compiled, the InlineInfo is read from the classfile attribute. This // fixes an issue with mixed-in methods: the mixin phase enters mixin methods only to class diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala index a06fb4bab8..d641f708d2 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala @@ -4,6 +4,8 @@ package backend.jvm import scala.tools.asm.tree.{AbstractInsnNode, MethodNode} import scala.tools.nsc.backend.jvm.BTypes.InternalName import scala.reflect.internal.util.Position +import scala.tools.nsc.settings.ScalaSettings +import scala.util.control.ControlThrowable /** * Interface for emitting inline warnings. The interface is required because the implementation @@ -73,24 +75,22 @@ object BackendReporting { } } - case class Invalid[A](e: A) extends Exception + case class Invalid[A](e: A) extends ControlThrowable /** * See documentation of orThrow above. */ def tryEither[A, B](op: => Either[A, B]): Either[A, B] = try { op } catch { case Invalid(e) => Left(e.asInstanceOf[A]) } - final case class WarnSettings(atInlineFailed: Boolean, noInlineMixed: Boolean, noInlineMissingBytecode: Boolean, noInlineMissingScalaInlineInfoAttr: Boolean) - sealed trait OptimizerWarning { - def emitWarning(settings: WarnSettings): Boolean + def emitWarning(settings: ScalaSettings): Boolean } // Method filter in RightBiasedEither requires an implicit empty value. Taking the value here // in scope allows for-comprehensions that desugar into filter calls (for example when using a // tuple de-constructor). implicit object emptyOptimizerWarning extends OptimizerWarning { - def emitWarning(settings: WarnSettings): Boolean = false + def emitWarning(settings: ScalaSettings): Boolean = false } sealed trait MissingBytecodeWarning extends OptimizerWarning { @@ -112,17 +112,17 @@ object BackendReporting { missingClass.map(c => s" Reason:\n$c").getOrElse("") } - def emitWarning(settings: WarnSettings): Boolean = this match { + def emitWarning(settings: ScalaSettings): Boolean = this match { case ClassNotFound(_, javaDefined) => - if (javaDefined) settings.noInlineMixed - else settings.noInlineMissingBytecode + if (javaDefined) settings.YoptWarningNoInlineMixed + else settings.YoptWarningNoInlineMissingBytecode case m @ MethodNotFound(_, _, _, missing) => if (m.isArrayMethod) false - else settings.noInlineMissingBytecode || missing.exists(_.emitWarning(settings)) + else settings.YoptWarningNoInlineMissingBytecode || missing.exists(_.emitWarning(settings)) case FieldNotFound(_, _, _, missing) => - settings.noInlineMissingBytecode || missing.exists(_.emitWarning(settings)) + settings.YoptWarningNoInlineMissingBytecode || missing.exists(_.emitWarning(settings)) } } @@ -141,9 +141,9 @@ object BackendReporting { s"Failed to get the type of class symbol $classFullName due to SI-9111." } - def emitWarning(settings: WarnSettings): Boolean = this match { + def emitWarning(settings: ScalaSettings): Boolean = this match { case NoClassBTypeInfoMissingBytecode(cause) => cause.emitWarning(settings) - case NoClassBTypeInfoClassSymbolInfoFailedSI9111(_) => settings.noInlineMissingBytecode + case NoClassBTypeInfoClassSymbolInfoFailedSI9111(_) => settings.YoptWarningNoInlineMissingBytecode } } @@ -175,11 +175,11 @@ object BackendReporting { cause.toString } - def emitWarning(settings: WarnSettings): Boolean = this match { + def emitWarning(settings: ScalaSettings): Boolean = this match { case MethodInlineInfoIncomplete(_, _, _, cause) => cause.emitWarning(settings) case MethodInlineInfoMissing(_, _, _, Some(cause)) => cause.emitWarning(settings) - case MethodInlineInfoMissing(_, _, _, None) => settings.noInlineMissingBytecode + case MethodInlineInfoMissing(_, _, _, None) => settings.YoptWarningNoInlineMissingBytecode case MethodInlineInfoError(_, _, _, cause) => cause.emitWarning(settings) @@ -214,11 +214,21 @@ object BackendReporting { case SynchronizedMethod(_, _, _) => s"Method $calleeMethodSig cannot be inlined because it is synchronized." + + case StrictfpMismatch(_, _, _, callsiteClass, callsiteName, callsiteDesc) => + s"""The callsite method ${BackendReporting.methodSignature(callsiteClass, callsiteName, callsiteDesc)} + |does not have the same strictfp mode as the callee $calleeMethodSig. + """.stripMargin + + case ResultingMethodTooLarge(_, _, _, callsiteClass, callsiteName, callsiteDesc) => + s"""The size of the callsite method ${BackendReporting.methodSignature(callsiteClass, callsiteName, callsiteDesc)} + |would exceed the JVM method size limit after inlining $calleeMethodSig. + """.stripMargin } - def emitWarning(settings: WarnSettings): Boolean = this match { - case _: IllegalAccessInstruction | _: MethodWithHandlerCalledOnNonEmptyStack | _: SynchronizedMethod => - settings.atInlineFailed + def emitWarning(settings: ScalaSettings): Boolean = this match { + case _: IllegalAccessInstruction | _: MethodWithHandlerCalledOnNonEmptyStack | _: SynchronizedMethod | _: StrictfpMismatch | _: ResultingMethodTooLarge => + settings.YoptWarningEmitAtInlineFailed case IllegalAccessCheckFailed(_, _, _, _, _, cause) => cause.emitWarning(settings) @@ -231,6 +241,10 @@ object BackendReporting { case class MethodWithHandlerCalledOnNonEmptyStack(calleeDeclarationClass: InternalName, name: String, descriptor: String, callsiteClass: InternalName, callsiteName: String, callsiteDesc: String) extends CannotInlineWarning case class SynchronizedMethod(calleeDeclarationClass: InternalName, name: String, descriptor: String) extends CannotInlineWarning + case class StrictfpMismatch(calleeDeclarationClass: InternalName, name: String, descriptor: String, + callsiteClass: InternalName, callsiteName: String, callsiteDesc: String) extends CannotInlineWarning + case class ResultingMethodTooLarge(calleeDeclarationClass: InternalName, name: String, descriptor: String, + callsiteClass: InternalName, callsiteName: String, callsiteDesc: String) extends CannotInlineWarning /** * Used in the InlineInfo of a ClassBType, when some issue occurred obtaining the inline information. @@ -250,11 +264,11 @@ object BackendReporting { s"Cannot read ScalaInlineInfo version $version in classfile $internalName. Use a more recent compiler." } - def emitWarning(settings: WarnSettings): Boolean = this match { - case NoInlineInfoAttribute(_) => settings.noInlineMissingScalaInlineInfoAttr + def emitWarning(settings: ScalaSettings): Boolean = this match { + case NoInlineInfoAttribute(_) => settings.YoptWarningNoInlineMissingScalaInlineInfoAttr case ClassNotFoundWhenBuildingInlineInfoFromSymbol(cause) => cause.emitWarning(settings) - case ClassSymbolInfoFailureSI9111(_) => settings.noInlineMissingBytecode - case UnknownScalaInlineInfoVersion(_, _) => settings.noInlineMissingScalaInlineInfoAttr + case ClassSymbolInfoFailureSI9111(_) => settings.YoptWarningNoInlineMissingBytecode + case UnknownScalaInlineInfoVersion(_, _) => settings.YoptWarningNoInlineMissingScalaInlineInfoAttr } } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 86a69b92ea..f866c0d038 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -689,7 +689,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self => null else { val outerName = javaName(innerSym.rawowner) - if (isTopLevelModule(innerSym.rawowner)) "" + nme.stripModuleSuffix(newTermName(outerName)) + if (isTopLevelModule(innerSym.rawowner)) "" + TermName(outerName).dropModule else outerName } } @@ -2054,7 +2054,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self => seen ::= LocVarEntry(lv, start, end) case _ => // TODO SI-6049 track down the cause for these. - debugwarn(s"$iPos: Visited SCOPE_EXIT before visiting corresponding SCOPE_ENTER. SI-6191") + devWarning(s"$iPos: Visited SCOPE_EXIT before visiting corresponding SCOPE_ENTER. SI-6191") } } @@ -2424,7 +2424,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self => // SI-6102: Determine whether eliding this JUMP results in an empty range being covered by some EH. // If so, emit a NOP in place of the elided JUMP, to avoid "java.lang.ClassFormatError: Illegal exception table range" else if (newNormal.isJumpOnly(b) && m.exh.exists(eh => eh.covers(b))) { - debugwarn("Had a jump only block that wasn't collapsed") + devWarning("Had a jump only block that wasn't collapsed") emit(asm.Opcodes.NOP) } @@ -2883,8 +2883,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self => var fieldList = List[String]() for (f <- clasz.fields if f.symbol.hasGetter; - g = f.symbol.getter(clasz.symbol); - s = f.symbol.setter(clasz.symbol) + g = f.symbol.getterIn(clasz.symbol); + s = f.symbol.setterIn(clasz.symbol) if g.isPublic && !(f.symbol.name startsWith "$") ) { // inserting $outer breaks the bean @@ -3136,13 +3136,13 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self => val (remappings, cycles) = detour partition {case (source, target) => source != target} for ((source, target) <- remappings) { debuglog(s"Will elide jump only block $source because it can be jumped around to get to $target.") - if (m.startBlock == source) debugwarn("startBlock should have been re-wired by now") + if (m.startBlock == source) devWarning("startBlock should have been re-wired by now") } val sources = remappings.keySet val targets = remappings.values.toSet val intersection = sources intersect targets - if (intersection.nonEmpty) debugwarn(s"contradiction: we seem to have some source and target overlap in blocks ${intersection.mkString}. Map was ${detour.mkString}") + if (intersection.nonEmpty) devWarning(s"contradiction: we seem to have some source and target overlap in blocks ${intersection.mkString}. Map was ${detour.mkString}") for ((source, _) <- cycles) { debuglog(s"Block $source is in a do-nothing infinite loop. Did the user write 'while(true){}'?") diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala index be1595dc29..c6ee36d7b2 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala @@ -14,7 +14,6 @@ import scala.reflect.internal.util.Statistics import scala.tools.asm import scala.tools.asm.tree.ClassNode -import scala.tools.nsc.backend.jvm.opt.LocalOpt /* * Prepare in-memory representations of classfiles using the ASM Tree API, and serialize them to disk. @@ -215,17 +214,12 @@ abstract class GenBCode extends BCodeSyncAndTry { * - converting the plain ClassNode to byte array and placing it on queue-3 */ class Worker2 { - lazy val localOpt = new LocalOpt(settings) - def runGlobalOptimizations(): Unit = { import scala.collection.convert.decorateAsScala._ q2.asScala foreach { case Item2(_, _, plain, _, _) => // skip mirror / bean: wd don't inline into tem, and they are not used in the plain class - if (plain != null) { - localOpt.minimalRemoveUnreachableCode(plain) - callGraph.addClass(plain) - } + if (plain != null) callGraph.addClass(plain) } bTypes.inliner.runInliner() } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala index 14e8cccc60..201ab15177 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala @@ -10,6 +10,7 @@ package opt import scala.annotation.{tailrec, switch} import scala.collection.mutable import scala.reflect.internal.util.Collections._ +import scala.tools.asm.commons.CodeSizeEvaluator import scala.tools.asm.tree.analysis._ import scala.tools.asm.{MethodWriter, ClassWriter, Label, Opcodes} import scala.tools.asm.tree._ @@ -21,6 +22,12 @@ import scala.tools.nsc.backend.jvm.BTypes._ object BytecodeUtils { + // http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.9.1 + final val maxJVMMethodSize = 65535 + + // 5% margin, more than enough for the instructions added by the inliner (store / load args, null check for instance methods) + final val maxMethodSizeAfterInline = maxJVMMethodSize - (maxJVMMethodSize / 20) + object Goto { def unapply(instruction: AbstractInsnNode): Option[JumpInsnNode] = { if (instruction.getOpcode == Opcodes.GOTO) Some(instruction.asInstanceOf[JumpInsnNode]) @@ -83,10 +90,14 @@ object BytecodeUtils { def isSynchronizedMethod(methodNode: MethodNode): Boolean = (methodNode.access & Opcodes.ACC_SYNCHRONIZED) != 0 + def isNativeMethod(methodNode: MethodNode): Boolean = (methodNode.access & Opcodes.ACC_NATIVE) != 0 + def isFinalClass(classNode: ClassNode): Boolean = (classNode.access & Opcodes.ACC_FINAL) != 0 def isFinalMethod(methodNode: MethodNode): Boolean = (methodNode.access & (Opcodes.ACC_FINAL | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC)) != 0 + def isStrictfpMethod(methodNode: MethodNode): Boolean = (methodNode.access & Opcodes.ACC_STRICT) != 0 + def nextExecutableInstruction(instruction: AbstractInsnNode, alsoKeep: AbstractInsnNode => Boolean = Set()): Option[AbstractInsnNode] = { var result = instruction do { result = result.getNext } @@ -215,7 +226,7 @@ object BytecodeUtils { * to create a separate visitor for computing those values, duplicating the functionality from the * MethodWriter. */ - def computeMaxLocalsMaxStack(method: MethodNode) { + def computeMaxLocalsMaxStack(method: MethodNode): Unit = { val cw = new ClassWriter(ClassWriter.COMPUTE_MAXS) val excs = method.exceptions.asScala.toArray val mw = cw.visitMethod(method.access, method.name, method.desc, method.signature, excs).asInstanceOf[MethodWriter] @@ -224,6 +235,21 @@ object BytecodeUtils { method.maxStack = mw.getMaxStack } + def codeSizeOKForInlining(caller: MethodNode, callee: MethodNode): Boolean = { + // Looking at the implementation of CodeSizeEvaluator, all instructions except tableswitch and + // lookupswitch are <= 8 bytes. These should be rare enough for 8 to be an OK rough upper bound. + def roughUpperBound(methodNode: MethodNode): Int = methodNode.instructions.size * 8 + + def maxSize(methodNode: MethodNode): Int = { + val eval = new CodeSizeEvaluator(null) + methodNode.accept(eval) + eval.getMaxSize + } + + (roughUpperBound(caller) + roughUpperBound(callee) > maxMethodSizeAfterInline) && + (maxSize(caller) + maxSize(callee) > maxMethodSizeAfterInline) + } + def removeLineNumberNodes(classNode: ClassNode): Unit = { for (m <- classNode.methods.asScala) removeLineNumberNodes(m.instructions) } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala index 47d32c94cb..028f0f8fa6 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala @@ -43,14 +43,14 @@ class CallGraph[BT <: BTypes](val btypes: BT) { // callee, we only check there for the methodInlineInfo, we should find it there. calleeDeclarationClassBType.info.orThrow.inlineInfo.methodInfos.get(methodSignature) match { case Some(methodInlineInfo) => - val canInlineFromSource = inlineGlobalEnabled || calleeSource == CompilationUnit + val canInlineFromSource = compilerSettings.YoptInlineGlobal || calleeSource == CompilationUnit val isAbstract = BytecodeUtils.isAbstractMethod(calleeMethodNode) // (1) A non-final method can be safe to inline if the receiver type is a final subclass. Example: // class A { @inline def f = 1 }; object B extends A; B.f // can be inlined // - // TODO: type analysis can render more calls statically resolved. Example˜∫ + // TODO: type analysis can render more calls statically resolved. Example: // new A.f // can be inlined, the receiver type is known to be exactly A. val isStaticallyResolved: Boolean = { methodInlineInfo.effectivelyFinal || @@ -68,8 +68,13 @@ class CallGraph[BT <: BTypes](val btypes: BT) { // (2) Final trait methods can be rewritten from the interface to the static implementation // method to enable inlining. CallsiteInfo( - safeToInline = canInlineFromSource && isStaticallyResolved && !isAbstract, // (1) - safeToRewrite = canInlineFromSource && isRewritableTraitCall, // (2) + safeToInline = + canInlineFromSource && + isStaticallyResolved && // (1) + !isAbstract && + !BytecodeUtils.isConstructor(calleeMethodNode) && + !BytecodeUtils.isNativeMethod(calleeMethodNode), + safeToRewrite = canInlineFromSource && isRewritableTraitCall, // (2) annotatedInline = methodInlineInfo.annotatedInline, annotatedNoInline = methodInlineInfo.annotatedNoInline, warning = warning) @@ -92,6 +97,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) { // TODO: for now we run a basic analyzer to get the stack height at the call site. // once we run a more elaborate analyzer (types, nullness), we can get the stack height out of there. + localOpt.minimalRemoveUnreachableCode(methodNode, definingClass.internalName) val analyzer = new AsmAnalyzer(methodNode, definingClass.internalName) methodNode.instructions.iterator.asScala.collect({ diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala index e14e57d3ab..ac5c9ce2e6 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala @@ -16,7 +16,7 @@ import scala.collection.convert.decorateAsJava._ import AsmUtils._ import BytecodeUtils._ import collection.mutable -import scala.tools.asm.tree.analysis.{SourceInterpreter, Analyzer} +import scala.tools.asm.tree.analysis.SourceInterpreter import BackendReporting._ import scala.tools.nsc.backend.jvm.BTypes.InternalName @@ -24,21 +24,39 @@ class Inliner[BT <: BTypes](val btypes: BT) { import btypes._ import callGraph._ + def eliminateUnreachableCodeAndUpdateCallGraph(methodNode: MethodNode, definingClass: InternalName): Unit = { + localOpt.minimalRemoveUnreachableCode(methodNode, definingClass) foreach { + case invocation: MethodInsnNode => callGraph.callsites.remove(invocation) + case _ => + } + } + def runInliner(): Unit = { rewriteFinalTraitMethodInvocations() for (request <- collectAndOrderInlineRequests) { val Right(callee) = request.callee // collectAndOrderInlineRequests returns callsites with a known callee - val r = inline(request.callsiteInstruction, request.callsiteStackHeight, request.callsiteMethod, request.callsiteClass, - callee.callee, callee.calleeDeclarationClass, - receiverKnownNotNull = false, keepLineNumbers = false) - - for (warning <- r) { - if ((callee.annotatedInline && btypes.warnSettings.atInlineFailed) || warning.emitWarning(warnSettings)) { - val annotWarn = if (callee.annotatedInline) " is annotated @inline but" else "" - val msg = s"${BackendReporting.methodSignature(callee.calleeDeclarationClass.internalName, callee.callee)}$annotWarn could not be inlined:\n$warning" - backendReporting.inlinerWarning(request.callsitePosition, msg) + // Inlining a method can create unreachable code. Example: + // def f = throw e + // def g = f; println() // println is unreachable after inlining f + // If we have an inline request for a call to g, and f has been already inlined into g, we + // need to run DCE before inlining g. + eliminateUnreachableCodeAndUpdateCallGraph(callee.callee, callee.calleeDeclarationClass.internalName) + + // DCE above removes unreachable callsites from the call graph. If the inlining request denotes + // such an eliminated callsite, do nothing. + if (callGraph.callsites contains request.callsiteInstruction) { + val r = inline(request.callsiteInstruction, request.callsiteStackHeight, request.callsiteMethod, request.callsiteClass, + callee.callee, callee.calleeDeclarationClass, + receiverKnownNotNull = false, keepLineNumbers = false) + + for (warning <- r) { + if ((callee.annotatedInline && btypes.compilerSettings.YoptWarningEmitAtInlineFailed) || warning.emitWarning(compilerSettings)) { + val annotWarn = if (callee.annotatedInline) " is annotated @inline but" else "" + val msg = s"${BackendReporting.methodSignature(callee.calleeDeclarationClass.internalName, callee.callee)}$annotWarn could not be inlined:\n$warning" + backendReporting.inlinerWarning(request.callsitePosition, msg) + } } } } @@ -75,7 +93,7 @@ class Inliner[BT <: BTypes](val btypes: BT) { val res = doInlineCallsite(callsite) if (!res) { - if (annotatedInline && btypes.warnSettings.atInlineFailed) { + if (annotatedInline && btypes.compilerSettings.YoptWarningEmitAtInlineFailed) { // if the callsite is annotated @inline, we report an inline warning even if the underlying // reason is, for example, mixed compilation (which has a separate -Yopt-warning flag). def initMsg = s"${BackendReporting.methodSignature(calleeDeclClass.internalName, callee)} is annotated @inline but cannot be inlined" @@ -86,7 +104,7 @@ class Inliner[BT <: BTypes](val btypes: BT) { backendReporting.inlinerWarning(pos, s"$initMsg: the method is not final and may be overridden." + warnMsg) else backendReporting.inlinerWarning(pos, s"$initMsg." + warnMsg) - } else if (warning.isDefined && warning.get.emitWarning(warnSettings)) { + } else if (warning.isDefined && warning.get.emitWarning(compilerSettings)) { // when annotatedInline is false, and there is some warning, the callsite metadata is possibly incomplete. backendReporting.inlinerWarning(pos, s"there was a problem determining if method ${callee.name} can be inlined: \n"+ warning.get) } @@ -95,7 +113,7 @@ class Inliner[BT <: BTypes](val btypes: BT) { res case Callsite(ins, _, _, Left(warning), _, _, pos) => - if (warning.emitWarning(warnSettings)) + if (warning.emitWarning(compilerSettings)) backendReporting.inlinerWarning(pos, s"failed to determine if ${ins.name} should be inlined:\n$warning") false }).toList @@ -106,7 +124,8 @@ class Inliner[BT <: BTypes](val btypes: BT) { */ def doInlineCallsite(callsite: Callsite): Boolean = callsite match { case Callsite(_, _, _, Right(Callee(callee, calleeDeclClass, safeToInline, _, annotatedInline, _, warning)), _, _, pos) => - annotatedInline && safeToInline + if (compilerSettings.YoptInlineHeuristics.value == "everything") safeToInline + else annotatedInline && safeToInline case _ => false } @@ -167,6 +186,8 @@ class Inliner[BT <: BTypes](val btypes: BT) { // VerifyError. We run a `SourceInterpreter` to find all producer instructions of the // receiver value and add a cast to the self type after each. if (!selfTypeOk) { + // there's no need to run eliminateUnreachableCode here. building the call graph does that + // already, no code can become unreachable in the meantime. val analyzer = new AsmAnalyzer(callsite.callsiteMethod, callsite.callsiteClass.internalName, new SourceInterpreter) val receiverValue = analyzer.frameAt(callsite.callsiteInstruction).peekDown(traitMethodArgumentTypes.length) for (i <- receiverValue.insns.asScala) { @@ -311,6 +332,7 @@ class Inliner[BT <: BTypes](val btypes: BT) { val localVarShift = callsiteMethod.maxLocals clonedInstructions.iterator.asScala foreach { case varInstruction: VarInsnNode => varInstruction.`var` += localVarShift + case iinc: IincInsnNode => iinc.`var` += localVarShift case _ => () } @@ -433,6 +455,9 @@ class Inliner[BT <: BTypes](val btypes: BT) { // Remove the elided invocation from the call graph callGraph.callsites.remove(callsiteInstruction) + // Inlining a method body can render some code unreachable, see example above (in runInliner). + unreachableCodeEliminated -= callsiteMethod + callsiteMethod.maxLocals += returnType.getSize + callee.maxLocals callsiteMethod.maxStack = math.max(callsiteMethod.maxStack, callee.maxStack + callsiteStackHeight) @@ -472,10 +497,18 @@ class Inliner[BT <: BTypes](val btypes: BT) { callsiteStackHeight > expectedArgs } - if (isSynchronizedMethod(callee)) { + if (codeSizeOKForInlining(callsiteMethod, callee)) { + Some(ResultingMethodTooLarge( + calleeDeclarationClass.internalName, callee.name, callee.desc, + callsiteClass.internalName, callsiteMethod.name, callsiteMethod.desc)) + } else if (isSynchronizedMethod(callee)) { // Could be done by locking on the receiver, wrapping the inlined code in a try and unlocking // in finally. But it's probably not worth the effort, scala never emits synchronized methods. Some(SynchronizedMethod(calleeDeclarationClass.internalName, callee.name, callee.desc)) + } else if (isStrictfpMethod(callsiteMethod) != isStrictfpMethod(callee)) { + Some(StrictfpMismatch( + calleeDeclarationClass.internalName, callee.name, callee.desc, + callsiteClass.internalName, callsiteMethod.name, callsiteMethod.desc)) } else if (!callee.tryCatchBlocks.isEmpty && stackHasNonParameters) { Some(MethodWithHandlerCalledOnNonEmptyStack( calleeDeclarationClass.internalName, callee.name, callee.desc, diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala index f6cfc5598b..5f51a94673 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala @@ -14,7 +14,6 @@ import scala.tools.asm.tree._ import scala.collection.convert.decorateAsScala._ import scala.tools.nsc.backend.jvm.BTypes.InternalName import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._ -import scala.tools.nsc.settings.ScalaSettings /** * Optimizations within a single method. @@ -47,18 +46,9 @@ import scala.tools.nsc.settings.ScalaSettings * stale labels * - eliminate labels that are not referenced, merge sequences of label definitions. */ -class LocalOpt(settings: ScalaSettings) { - /** - * Remove unreachable code from all methods of `classNode`. See of its overload. - * - * @param classNode The class to optimize - * @return `true` if unreachable code was removed from any method - */ - def minimalRemoveUnreachableCode(classNode: ClassNode): Boolean = { - classNode.methods.asScala.foldLeft(false) { - case (changed, method) => minimalRemoveUnreachableCode(method, classNode.name) || changed - } - } +class LocalOpt[BT <: BTypes](val btypes: BT) { + import LocalOptImpls._ + import btypes._ /** * Remove unreachable code from a method. @@ -66,25 +56,30 @@ class LocalOpt(settings: ScalaSettings) { * This implementation only removes instructions that are unreachable for an ASM analyzer / * interpreter. This ensures that future analyses will not produce `null` frames. The inliner * and call graph builder depend on this property. + * + * @return A set containing the eliminated instructions */ - def minimalRemoveUnreachableCode(method: MethodNode, ownerClassName: InternalName): Boolean = { - if (method.instructions.size == 0) return false // fast path for abstract methods + def minimalRemoveUnreachableCode(method: MethodNode, ownerClassName: InternalName): Set[AbstractInsnNode] = { + if (method.instructions.size == 0) return Set.empty // fast path for abstract methods + if (unreachableCodeEliminated(method)) return Set.empty // we know there is no unreachable code // For correctness, after removing unreachable code, we have to eliminate empty exception // handlers, see scaladoc of def methodOptimizations. Removing an live handler may render more // code unreachable and therefore requires running another round. - def removalRound(): Boolean = { - val (codeRemoved, liveLabels) = removeUnreachableCodeImpl(method, ownerClassName) - if (codeRemoved) { + def removalRound(): Set[AbstractInsnNode] = { + val (removedInstructions, liveLabels) = removeUnreachableCodeImpl(method, ownerClassName) + val removedRecursively = if (removedInstructions.nonEmpty) { val liveHandlerRemoved = removeEmptyExceptionHandlers(method).exists(h => liveLabels(h.start)) if (liveHandlerRemoved) removalRound() - } - codeRemoved + else Set.empty + } else Set.empty + removedInstructions ++ removedRecursively } - val codeRemoved = removalRound() - if (codeRemoved) removeUnusedLocalVariableNodes(method)() - codeRemoved + val removedInstructions = removalRound() + if (removedInstructions.nonEmpty) removeUnusedLocalVariableNodes(method)() + unreachableCodeEliminated += method + removedInstructions } /** @@ -95,7 +90,7 @@ class LocalOpt(settings: ScalaSettings) { * @return `true` if unreachable code was eliminated in some method, `false` otherwise. */ def methodOptimizations(clazz: ClassNode): Boolean = { - !settings.YoptNone && clazz.methods.asScala.foldLeft(false) { + !compilerSettings.YoptNone && clazz.methods.asScala.foldLeft(false) { case (changed, method) => methodOptimizations(method, clazz.name) || changed } } @@ -144,15 +139,15 @@ class LocalOpt(settings: ScalaSettings) { def removalRound(): Boolean = { // unreachable-code, empty-handlers and simplify-jumps run until reaching a fixpoint (see doc on class LocalOpt) - val (codeRemoved, handlersRemoved, liveHandlerRemoved) = if (settings.YoptUnreachableCode) { - val (codeRemoved, liveLabels) = removeUnreachableCodeImpl(method, ownerClassName) + val (codeRemoved, handlersRemoved, liveHandlerRemoved) = if (compilerSettings.YoptUnreachableCode) { + val (removedInstructions, liveLabels) = removeUnreachableCodeImpl(method, ownerClassName) val removedHandlers = removeEmptyExceptionHandlers(method) - (codeRemoved, removedHandlers.nonEmpty, removedHandlers.exists(h => liveLabels(h.start))) + (removedInstructions.nonEmpty, removedHandlers.nonEmpty, removedHandlers.exists(h => liveLabels(h.start))) } else { (false, false, false) } - val jumpsChanged = if (settings.YoptSimplifyJumps) simplifyJumps(method) else false + val jumpsChanged = if (compilerSettings.YoptSimplifyJumps) simplifyJumps(method) else false // Eliminating live handlers and simplifying jump instructions may render more code // unreachable, so we need to run another round. @@ -165,13 +160,13 @@ class LocalOpt(settings: ScalaSettings) { // (*) Removing stale local variable descriptors is required for correctness of unreachable-code val localsRemoved = - if (settings.YoptCompactLocals) compactLocalVariables(method) // also removes unused - else if (settings.YoptUnreachableCode) removeUnusedLocalVariableNodes(method)() // (*) + if (compilerSettings.YoptCompactLocals) compactLocalVariables(method) // also removes unused + else if (compilerSettings.YoptUnreachableCode) removeUnusedLocalVariableNodes(method)() // (*) else false - val lineNumbersRemoved = if (settings.YoptEmptyLineNumbers) removeEmptyLineNumbers(method) else false + val lineNumbersRemoved = if (compilerSettings.YoptEmptyLineNumbers) removeEmptyLineNumbers(method) else false - val labelsRemoved = if (settings.YoptEmptyLabels) removeEmptyLabelNodes(method) else false + val labelsRemoved = if (compilerSettings.YoptEmptyLabels) removeEmptyLabelNodes(method) else false // assert that local variable annotations are empty (we don't emit them) - otherwise we'd have // to eliminate those covering an empty range, similar to removeUnusedLocalVariableNodes. @@ -179,15 +174,22 @@ class LocalOpt(settings: ScalaSettings) { assert(nullOrEmpty(method.visibleLocalVariableAnnotations), method.visibleLocalVariableAnnotations) assert(nullOrEmpty(method.invisibleLocalVariableAnnotations), method.invisibleLocalVariableAnnotations) + unreachableCodeEliminated += method + codeHandlersOrJumpsChanged || localsRemoved || lineNumbersRemoved || labelsRemoved } +} + +object LocalOptImpls { /** * Removes unreachable basic blocks. * * TODO: rewrite, don't use computeMaxLocalsMaxStack (runs a ClassWriter) / Analyzer. Too slow. + * + * @return A set containing eliminated instructions, and a set containing all live label nodes. */ - def removeUnreachableCodeImpl(method: MethodNode, ownerClassName: InternalName): (Boolean, Set[LabelNode]) = { + def removeUnreachableCodeImpl(method: MethodNode, ownerClassName: InternalName): (Set[AbstractInsnNode], Set[LabelNode]) = { // The data flow analysis requires the maxLocals / maxStack fields of the method to be computed. computeMaxLocalsMaxStack(method) val a = new Analyzer(new BasicInterpreter) @@ -197,6 +199,7 @@ class LocalOpt(settings: ScalaSettings) { val initialSize = method.instructions.size var i = 0 var liveLabels = Set.empty[LabelNode] + var removedInstructions = Set.empty[AbstractInsnNode] val itr = method.instructions.iterator() while (itr.hasNext) { itr.next() match { @@ -209,11 +212,12 @@ class LocalOpt(settings: ScalaSettings) { // Instruction iterators allow removing during iteration. // Removing is O(1): instructions are doubly linked list elements. itr.remove() + removedInstructions += ins } } i += 1 } - (method.instructions.size != initialSize, liveLabels) + (removedInstructions, liveLabels) } /** diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index 9433ddcf31..d34c14be0f 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -13,6 +13,7 @@ import symtab.Flags import JavaTokens._ import scala.language.implicitConversions import scala.reflect.internal.util.Position +import scala.reflect.internal.util.ListOfNil trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { val global : Global @@ -125,7 +126,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { def makeSyntheticParam(count: Int, tpt: Tree): ValDef = makeParam(nme.syntheticParamName(count), tpt) def makeParam(name: String, tpt: Tree): ValDef = - makeParam(name: TermName, tpt) + makeParam(TermName(name), tpt) def makeParam(name: TermName, tpt: Tree): ValDef = ValDef(Modifiers(Flags.JAVA | Flags.PARAM), name, tpt, EmptyTree) diff --git a/src/compiler/scala/tools/nsc/plugins/Plugin.scala b/src/compiler/scala/tools/nsc/plugins/Plugin.scala index 7837f9a11a..1a5529140c 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugin.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugin.scala @@ -7,7 +7,7 @@ package scala.tools.nsc package plugins import scala.tools.nsc.io.{ Jar } -import scala.tools.nsc.util.ScalaClassLoader +import scala.reflect.internal.util.ScalaClassLoader import scala.reflect.io.{ Directory, File, Path } import java.io.InputStream import java.util.zip.ZipException @@ -60,6 +60,8 @@ abstract class Plugin { * @return true to continue, or false to opt out */ def init(options: List[String], error: String => Unit): Boolean = { + // call to deprecated method required here, we must continue to support + // code that subclasses that override `processOptions`. processOptions(options, error) true } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index d273995e6e..03fd0976e5 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -256,6 +256,13 @@ trait ScalaSettings extends AbsScalaSettings def YoptInlineGlobal = Yopt.contains(YoptChoices.inlineGlobal) def YoptInlinerEnabled = YoptInlineProject || YoptInlineGlobal + val YoptInlineHeuristics = ChoiceSetting( + name = "-Yopt-inline-heuristics", + helpArg = "strategy", + descr = "Set the heuristics for inlining decisions.", + choices = List("at-inline-annotated", "everything"), + default = "at-inline-annotated") + object YoptWarningsChoices extends MultiChoiceEnumeration { val none = Choice("none" , "No optimizer warnings.") val atInlineFailedSummary = Choice("at-inline-failed-summary" , "One-line summary if there were @inline method calls that could not be inlined.") @@ -267,7 +274,7 @@ trait ScalaSettings extends AbsScalaSettings val YoptWarnings = MultiChoiceSetting( name = "-Yopt-warnings", - helpArg = "warnings", + helpArg = "warning", descr = "Enable optimizer warnings", domain = YoptWarningsChoices, default = Some(List(YoptWarningsChoices.atInlineFailed.name))) withPostSetHook (self => { @@ -275,6 +282,15 @@ trait ScalaSettings extends AbsScalaSettings else YinlinerWarnings.value = true }) + def YoptWarningEmitAtInlineFailed = + !YoptWarnings.isSetByUser || + YoptWarnings.contains(YoptWarningsChoices.atInlineFailedSummary) || + YoptWarnings.contains(YoptWarningsChoices.atInlineFailed) + + def YoptWarningNoInlineMixed = YoptWarnings.contains(YoptWarningsChoices.noInlineMixed) + def YoptWarningNoInlineMissingBytecode = YoptWarnings.contains(YoptWarningsChoices.noInlineMissingBytecode) + def YoptWarningNoInlineMissingScalaInlineInfoAttr = YoptWarnings.contains(YoptWarningsChoices.noInlineMissingScalaInlineInfoAttr) + private def removalIn212 = "This flag is scheduled for removal in 2.12. If you have a case where you need this flag then please report a bug." object YstatisticsPhases extends MultiChoiceEnumeration { val parser, typer, patmat, erasure, cleanup, jvm = Value } @@ -345,12 +361,7 @@ trait ScalaSettings extends AbsScalaSettings /** Test whether this is scaladoc we're looking at */ def isScaladoc = false - /** - * Helper utilities for use by checkConflictingSettings() - */ - def isBCodeActive = !isICodeAskedFor - def isBCodeAskedFor = (Ybackend.value != "GenASM") - def isICodeAskedFor = ((Ybackend.value == "GenASM") || optimiseSettings.exists(_.value) || writeICode.isSetByUser) + def isBCodeActive = Ybackend.value == "GenBCode" object MacroExpand { val None = "none" diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 4d08be3c24..994bcd8359 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -15,6 +15,7 @@ import scala.collection.mutable.{ ListBuffer, ArrayBuffer } import scala.annotation.switch import scala.reflect.internal.{ JavaAccFlags } import scala.reflect.internal.pickling.{PickleBuffer, ByteCodecs} +import scala.reflect.io.NoAbstractFile import scala.tools.nsc.io.AbstractFile import scala.tools.nsc.util.ClassFileLookup @@ -807,10 +808,10 @@ abstract class ClassfileParser { val c = pool.getConstant(u2) val c1 = convertTo(c, symtype) if (c1 ne null) sym.setInfo(ConstantType(c1)) - else debugwarn(s"failure to convert $c to $symtype") + else devWarning(s"failure to convert $c to $symtype") case tpnme.ScalaSignatureATTR => if (!isScalaAnnot) { - debugwarn(s"symbol ${sym.fullName} has pickled signature in attribute") + devWarning(s"symbol ${sym.fullName} has pickled signature in attribute") unpickler.unpickle(in.buf, in.bp, clazz, staticModule, in.file.name) } in.skip(attrLen) @@ -1022,11 +1023,18 @@ abstract class ClassfileParser { val sflags = jflags.toScalaFlags val owner = ownerForFlags(jflags) val scope = getScope(jflags) - val innerClass = owner.newClass(name.toTypeName, NoPosition, sflags) setInfo completer - val innerModule = owner.newModule(name.toTermName, NoPosition, sflags) setInfo completer + def newStub(name: Name) = + owner.newStubSymbol(name, s"Class file for ${entry.externalName} not found").setFlag(JAVA) - innerModule.moduleClass setInfo loaders.moduleClassLoader - List(innerClass, innerModule.moduleClass) foreach (_.associatedFile = file) + val (innerClass, innerModule) = if (file == NoAbstractFile) { + (newStub(name.toTypeName), newStub(name.toTermName)) + } else { + val cls = owner.newClass(name.toTypeName, NoPosition, sflags) setInfo completer + val mod = owner.newModule(name.toTermName, NoPosition, sflags) setInfo completer + mod.moduleClass setInfo loaders.moduleClassLoader + List(cls, mod.moduleClass) foreach (_.associatedFile = file) + (cls, mod) + } scope enter innerClass scope enter innerModule @@ -1046,10 +1054,8 @@ abstract class ClassfileParser { for (entry <- innerClasses.entries) { // create a new class member for immediate inner classes if (entry.outerName == currentClass) { - val file = classFileLookup.findClassFile(entry.externalName.toString) getOrElse { - throw new AssertionError(s"Class file for ${entry.externalName} not found") - } - enterClassAndModule(entry, file) + val file = classFileLookup.findClassFile(entry.externalName.toString) + enterClassAndModule(entry, file.getOrElse(NoAbstractFile)) } } } @@ -1103,7 +1109,7 @@ abstract class ClassfileParser { def enclosing = if (jflags.isStatic) enclModule else enclClass // The name of the outer class, without its trailing $ if it has one. - private def strippedOuter = nme stripModuleSuffix outerName + private def strippedOuter = outerName.dropModule private def isInner = innerClasses contains strippedOuter private def enclClass = if (isInner) innerClasses innerSymbol strippedOuter else classNameToSymbol(strippedOuter) private def enclModule = enclClass.companionModule @@ -1123,7 +1129,7 @@ abstract class ClassfileParser { def add(entry: InnerClassEntry): Unit = { inners get entry.externalName foreach (existing => - debugwarn(s"Overwriting inner class entry! Was $existing, now $entry") + devWarning(s"Overwriting inner class entry! Was $existing, now $entry") ) inners(entry.externalName) = entry } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index bd1fa4e707..ea46116976 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -74,7 +74,7 @@ abstract class ICodeReader extends ClassfileParser { 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) + debuglog("getMemberSymbol(static: " + static + "): owner type: " + ownerTpe + " " + ownerTpe.typeSymbol.unexpandedName) val (name0, tpe0) = getNameAndType(in.getChar(start + 3).toInt, ownerTpe) debuglog("getMemberSymbol: name and tpe: " + name0 + ": " + tpe0) diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala index 3591372bbe..79776485de 100644 --- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala +++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala @@ -207,7 +207,7 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure => } def transformMixinInfo(tp: Type): Type = tp match { - case ClassInfoType(parents, decls, clazz) => + case ClassInfoType(parents, decls, clazz) if clazz.isPackageClass || !clazz.isJavaDefined => if (clazz.needsImplClass) implClass(clazz setFlag lateINTERFACE) // generate an impl class diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index 362cbde04f..d0fca12e6a 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -8,6 +8,7 @@ package transform import scala.collection.{ mutable, immutable } import scala.collection.mutable.ListBuffer +import scala.reflect.internal.util.ListOfNil import symtab.Flags._ /** This phase converts classes with parameters into Java-like classes with diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 438d39bc86..9fdc3a9d72 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -1038,7 +1038,7 @@ abstract class Erasure extends AddInterfaces // See SI-5568. tree setSymbol Object_getClass } else { - debugwarn(s"The symbol '${fn.symbol}' was interecepted but didn't match any cases, that means the intercepted methods set doesn't match the code") + devWarning(s"The symbol '${fn.symbol}' was interecepted but didn't match any cases, that means the intercepted methods set doesn't match the code") tree } } else qual match { diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 6225b486c2..540de2cfe1 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -207,7 +207,7 @@ abstract class ExplicitOuter extends InfoTransform // class needs to have a common naming scheme, independently of whether // the field was accessed from an inner class or not. See #2946 if (sym.owner.isTrait && sym.isLocalToThis && - (sym.getter(sym.owner.toInterface) == NoSymbol)) + (sym.getterIn(sym.owner.toInterface) == NoSymbol)) sym.makeNotPrivate(sym.owner) tp } diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 7927875583..408f4466e1 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -232,13 +232,13 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { for (member <- impl.info.decls) { if (!member.isMethod && !member.isModule && !member.isModuleVar) { assert(member.isTerm && !member.isDeferred, member) - if (member.getter(impl).isPrivate) { + if (member.getterIn(impl).isPrivate) { member.makeNotPrivate(clazz) // this will also make getter&setter not private } - val getter = member.getter(clazz) + val getter = member.getterIn(clazz) if (getter == NoSymbol) addMember(clazz, newGetter(member)) if (!member.tpe.isInstanceOf[ConstantType] && !member.isLazy) { - val setter = member.setter(clazz) + val setter = member.setterIn(clazz) if (setter == NoSymbol) addMember(clazz, newSetter(member)) } } @@ -267,7 +267,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { /* Mix in members of implementation class mixinClass into class clazz */ def mixinImplClassMembers(mixinClass: Symbol, mixinInterface: Symbol) { - if (!mixinClass.isImplClass) debugwarn ("Impl class flag is not set " + + if (!mixinClass.isImplClass) devWarning ("Impl class flag is not set " + ((mixinClass.debugLocationString, mixinInterface.debugLocationString))) for (member <- mixinClass.info.decls ; if isForwarded(member)) { @@ -872,7 +872,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } def mkCheckedAccessor(clazz: Symbol, retVal: Tree, offset: Int, pos: Position, fieldSym: Symbol): Tree = { - val sym = fieldSym.getter(fieldSym.owner) + val sym = fieldSym.getterIn(fieldSym.owner) val bitmapSym = bitmapFor(clazz, offset, sym) val kind = bitmapKind(sym) val mask = maskForOffset(offset, sym, kind) @@ -921,7 +921,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { deriveDefDef(stat)(addInitBits(clazz, _)) } else if (settings.checkInit && !clazz.isTrait && sym.isSetter) { - val getter = sym.getter(clazz) + val getter = sym.getterIn(clazz) if (needsInitFlag(getter) && fieldOffset.isDefinedAt(getter)) deriveDefDef(stat)(rhs => Block(List(rhs, localTyper.typed(mkSetFlag(clazz, fieldOffset(getter), getter, bitmapKind(getter)))), UNIT)) else stat @@ -1057,7 +1057,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { def isOverriddenSetter(sym: Symbol) = nme.isTraitSetterName(sym.name) && { val other = sym.nextOverriddenSymbol - isOverriddenAccessor(other.getter(other.owner), clazz.info.baseClasses) + isOverriddenAccessor(other.getterIn(other.owner), clazz.info.baseClasses) } // for all symbols `sym` in the class definition, which are mixed in: @@ -1232,7 +1232,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // refer to fields in some implementation class via an abstract // getter in the interface. val iface = toInterface(sym.owner.tpe).typeSymbol - val ifaceGetter = sym getter iface + val ifaceGetter = sym getterIn iface if (ifaceGetter == NoSymbol) abort("No getter for " + sym + " in " + iface) else typedPos(tree.pos)((qual DOT ifaceGetter)()) @@ -1240,7 +1240,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { case Assign(Apply(lhs @ Select(qual, _), List()), rhs) => // assign to fields in some implementation class via an abstract // setter in the interface. - def setter = lhs.symbol.setter(toInterface(lhs.symbol.owner.tpe).typeSymbol) setPos lhs.pos + def setter = lhs.symbol.setterIn(toInterface(lhs.symbol.owner.tpe).typeSymbol) setPos lhs.pos typedPos(tree.pos)((qual DOT setter)(rhs)) diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 1691b01e3e..086512677e 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -699,7 +699,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } else if (m.isValue && !m.isMethod && !m.hasFlag(LAZY)) { // concrete value definition def mkAccessor(field: Symbol, name: Name) = { - val newFlags = (SPECIALIZED | m.getter(clazz).flags) & ~(LOCAL | CASEACCESSOR | PARAMACCESSOR) + val newFlags = (SPECIALIZED | m.getterIn(clazz).flags) & ~(LOCAL | CASEACCESSOR | PARAMACCESSOR) // we rely on the super class to initialize param accessors val sym = sClass.newMethod(name.toTermName, field.pos, newFlags) info(sym) = SpecializedAccessor(field) @@ -720,7 +720,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (nme.isLocalName(m.name)) { val specGetter = mkAccessor(specVal, specVal.getterName) setInfo MethodType(Nil, specVal.info) - val origGetter = overrideIn(sClass, m.getter(clazz)) + val origGetter = overrideIn(sClass, m.getterIn(clazz)) info(origGetter) = Forward(specGetter) enterMember(specGetter) enterMember(origGetter) @@ -733,12 +733,12 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { debuglog("override case field accessor %s -> %s".format(m.name.decode, cfaGetter.name.decode)) } - if (specVal.isVariable && m.setter(clazz) != NoSymbol) { + if (specVal.isVariable && m.setterIn(clazz) != NoSymbol) { val specSetter = mkAccessor(specVal, specGetter.setterName) .resetFlag(STABLE) specSetter.setInfo(MethodType(specSetter.newSyntheticValueParams(List(specVal.info)), UnitTpe)) - val origSetter = overrideIn(sClass, m.setter(clazz)) + val origSetter = overrideIn(sClass, m.setterIn(clazz)) info(origSetter) = Forward(specSetter) enterMember(specSetter) enterMember(origSetter) diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index e1cf53059a..3330fbcae2 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -10,6 +10,7 @@ package transform import symtab.Flags._ import scala.collection.{ mutable, immutable } import scala.language.postfixOps +import scala.reflect.internal.util.ListOfNil /*<export> */ /** - uncurry all symbol and tree types (@see UnCurryPhase) -- this includes normalizing all proper types. @@ -206,7 +207,7 @@ abstract class UnCurry extends InfoTransform // (() => Int) { def apply(): Int @typeConstraint } case RefinedType(List(funTp), decls) => debuglog(s"eliminate refinement from function type ${fun.tpe}") - fun.tpe = funTp + fun.setType(funTp) case _ => () } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index 0b53dc37de..4ea569c8e6 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala @@ -9,8 +9,7 @@ package tools.nsc.transform.patmat import scala.language.postfixOps import scala.collection.mutable -import scala.reflect.internal.util.Statistics -import scala.reflect.internal.util.HashSet +import scala.reflect.internal.util.{NoPosition, Position, Statistics, HashSet} trait Logic extends Debugging { import PatternMatchingStats._ @@ -71,6 +70,8 @@ trait Logic extends Debugging { def unapply(v: Var): Some[Tree] } + def uncheckedWarning(pos: Position, msg: String): Unit + def reportWarning(message: String): Unit // resets hash consing -- only supposed to be called by TreeMakersToProps @@ -283,6 +284,23 @@ trait Logic extends Debugging { } } + // to govern how much time we spend analyzing matches for unreachability/exhaustivity + object AnalysisBudget { + val maxDPLLdepth = global.settings.YpatmatExhaustdepth.value + val maxFormulaSize = 100 * math.min(Int.MaxValue / 100, maxDPLLdepth) + + private def advice = + s"Please try with scalac -Ypatmat-exhaust-depth ${maxDPLLdepth * 2} or -Ypatmat-exhaust-depth off." + + def recursionDepthReached = + s"Exhaustivity analysis reached max recursion depth, not all missing cases are reported.\n($advice)" + + abstract class Exception(val advice: String) extends RuntimeException("CNF budget exceeded") + + object formulaSizeExceeded extends Exception(s"The analysis required more space than allowed.\n$advice") + + } + // TODO: remove since deprecated val budgetProp = scala.sys.Prop[String]("scalac.patmat.analysisBudget") if (budgetProp.isSet) { @@ -385,7 +403,7 @@ trait Logic extends Debugging { def findModelFor(solvable: Solvable): Model - def findAllModelsFor(solvable: Solvable): List[Solution] + def findAllModelsFor(solvable: Solvable, pos: Position = NoPosition): List[Solution] } } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala index 34ebbc7463..cecb5c37be 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala @@ -399,6 +399,7 @@ trait MatchAnalysis extends MatchApproximation { trait MatchAnalyzer extends MatchApproximator { def uncheckedWarning(pos: Position, msg: String) = currentRun.reporting.uncheckedWarning(pos, msg) + def warn(pos: Position, ex: AnalysisBudget.Exception, kind: String) = uncheckedWarning(pos, s"Cannot check match for $kind.\n${ex.advice}") def reportWarning(message: String) = global.reporter.warning(typer.context.tree.pos, message) // TODO: model dependencies between variables: if V1 corresponds to (x: List[_]) and V2 is (x.hd), V2 cannot be assigned when V1 = null or V1 = Nil @@ -429,44 +430,50 @@ trait MatchAnalysis extends MatchApproximation { val propsCasesOk = approximate(True) map caseWithoutBodyToProp val propsCasesFail = approximate(False) map (t => Not(caseWithoutBodyToProp(t))) - val (eqAxiomsFail, symbolicCasesFail) = removeVarEq(propsCasesFail, modelNull = true) - val (eqAxiomsOk, symbolicCasesOk) = removeVarEq(propsCasesOk, modelNull = true) - val eqAxioms = simplify(And(eqAxiomsOk, eqAxiomsFail)) // I'm pretty sure eqAxiomsOk == eqAxiomsFail, but not 100% sure. - - val prefix = mutable.ArrayBuffer[Prop]() - prefix += eqAxioms - - var prefixRest = symbolicCasesFail - var current = symbolicCasesOk - var reachable = true - var caseIndex = 0 - - debug.patmat("reachability, vars:\n" + ((propsCasesFail flatMap gatherVariables).distinct map (_.describe) mkString ("\n"))) - debug.patmat(s"equality axioms:\n$eqAxiomsOk") - - // invariant (prefixRest.length == current.length) && (prefix.reverse ++ prefixRest == symbolicCasesFail) - // termination: prefixRest.length decreases by 1 - while (prefixRest.nonEmpty && reachable) { - val prefHead = prefixRest.head - caseIndex += 1 - prefixRest = prefixRest.tail - if (prefixRest.isEmpty) reachable = true - else { - prefix += prefHead - current = current.tail + try { + val (eqAxiomsFail, symbolicCasesFail) = removeVarEq(propsCasesFail, modelNull = true) + val (eqAxiomsOk, symbolicCasesOk) = removeVarEq(propsCasesOk, modelNull = true) + val eqAxioms = simplify(And(eqAxiomsOk, eqAxiomsFail)) // I'm pretty sure eqAxiomsOk == eqAxiomsFail, but not 100% sure. + + val prefix = mutable.ArrayBuffer[Prop]() + prefix += eqAxioms + + var prefixRest = symbolicCasesFail + var current = symbolicCasesOk + var reachable = true + var caseIndex = 0 + + debug.patmat("reachability, vars:\n" + ((propsCasesFail flatMap gatherVariables).distinct map (_.describe) mkString ("\n"))) + debug.patmat(s"equality axioms:\n$eqAxiomsOk") + + // invariant (prefixRest.length == current.length) && (prefix.reverse ++ prefixRest == symbolicCasesFail) + // termination: prefixRest.length decreases by 1 + while (prefixRest.nonEmpty && reachable) { + val prefHead = prefixRest.head + caseIndex += 1 + prefixRest = prefixRest.tail + if (prefixRest.isEmpty) reachable = true + else { + prefix += prefHead + current = current.tail val and = And((current.head +: prefix): _*) val model = findModelFor(eqFreePropToSolvable(and)) - // debug.patmat("trying to reach:\n"+ cnfString(current.head) +"\nunder prefix:\n"+ cnfString(prefix)) - // if (NoModel ne model) debug.patmat("reached: "+ modelString(model)) + // debug.patmat("trying to reach:\n"+ cnfString(current.head) +"\nunder prefix:\n"+ cnfString(prefix)) + // if (NoModel ne model) debug.patmat("reached: "+ modelString(model)) - reachable = NoModel ne model + reachable = NoModel ne model + } } - } - if (Statistics.canEnable) Statistics.stopTimer(patmatAnaReach, start) + if (Statistics.canEnable) Statistics.stopTimer(patmatAnaReach, start) - if (reachable) None else Some(caseIndex) + if (reachable) None else Some(caseIndex) + } catch { + case ex: AnalysisBudget.Exception => + warn(prevBinder.pos, ex, "unreachability") + None // CNF budget exceeded + } } // exhaustivity @@ -507,32 +514,38 @@ trait MatchAnalysis extends MatchApproximation { // when does the match fail? val matchFails = Not(\/(symbolicCases)) - // debug output: + // debug output: debug.patmat("analysing:") showTreeMakers(cases) // debug.patmat("\nvars:\n"+ (vars map (_.describe) mkString ("\n"))) // debug.patmat("\nmatchFails as CNF:\n"+ cnfString(propToSolvable(matchFails))) - // find the models (under which the match fails) - val matchFailModels = findAllModelsFor(propToSolvable(matchFails)) + try { + // find the models (under which the match fails) + val matchFailModels = findAllModelsFor(propToSolvable(matchFails), prevBinder.pos) - val scrutVar = Var(prevBinderTree) - val counterExamples = { - matchFailModels.flatMap { - model => - val varAssignments = expandModel(model) - varAssignments.flatMap(modelToCounterExample(scrutVar) _) + val scrutVar = Var(prevBinderTree) + val counterExamples = { + matchFailModels.flatMap { + model => + val varAssignments = expandModel(model) + varAssignments.flatMap(modelToCounterExample(scrutVar) _) + } } - } - - // sorting before pruning is important here in order to - // keep neg/t7020.scala stable - // since e.g. List(_, _) would cover List(1, _) - val pruned = CounterExample.prune(counterExamples.sortBy(_.toString)).map(_.toString) - if (Statistics.canEnable) Statistics.stopTimer(patmatAnaExhaust, start) - pruned + // sorting before pruning is important here in order to + // keep neg/t7020.scala stable + // since e.g. List(_, _) would cover List(1, _) + val pruned = CounterExample.prune(counterExamples.sortBy(_.toString)).map(_.toString) + + if (Statistics.canEnable) Statistics.stopTimer(patmatAnaExhaust, start) + pruned + } catch { + case ex: AnalysisBudget.Exception => + warn(prevBinder.pos, ex, "exhaustivity") + Nil // CNF budget exceeded + } } } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala index b703b5bc6d..e1fe220556 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala @@ -577,8 +577,6 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { lengthMax3(casesNoSubstOnly) > 2 } val requireSwitch = hasSwitchAnnotation && exceedsTwoCasesOrAlts - if (hasSwitchAnnotation && !requireSwitch) - reporter.warning(scrut.pos, "matches with two cases or fewer are emitted using if-then-else instead of switch") (suppression, requireSwitch) case _ => (Suppression.NoSuppression, false) diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala b/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala index 27217f0dc2..c43f1b6209 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala @@ -11,6 +11,7 @@ import scala.reflect.internal.util.Statistics import scala.language.postfixOps import scala.collection.mutable import scala.reflect.internal.util.Collections._ +import scala.reflect.internal.util.Position // a literal is a (possibly negated) variable class Lit(val v: Int) extends AnyVal { @@ -64,7 +65,12 @@ trait Solving extends Logic { def size = symbols.size } - case class Solvable(cnf: Cnf, symbolMapping: SymbolMapping) + final case class Solvable(cnf: Cnf, symbolMapping: SymbolMapping) { + def ++(other: Solvable) = { + require(this.symbolMapping eq other.symbolMapping) + Solvable(cnf ++ other.cnf, symbolMapping) + } + } trait CnfBuilder { private[this] val buff = ArrayBuffer[Clause]() @@ -95,7 +101,11 @@ trait Solving extends Logic { } } - def buildCnf: Array[Clause] = buff.toArray + def buildCnf: Array[Clause] = { + val cnf = buff.toArray + buff.clear() + cnf + } } @@ -244,19 +254,54 @@ trait Solving extends Logic { def eqFreePropToSolvable(p: Prop): Solvable = { + def doesFormulaExceedSize(p: Prop): Boolean = { + p match { + case And(ops) => + if (ops.size > AnalysisBudget.maxFormulaSize) { + true + } else { + ops.exists(doesFormulaExceedSize) + } + case Or(ops) => + if (ops.size > AnalysisBudget.maxFormulaSize) { + true + } else { + ops.exists(doesFormulaExceedSize) + } + case Not(a) => doesFormulaExceedSize(a) + case _ => false + } + } + + val simplified = simplify(p) + if (doesFormulaExceedSize(simplified)) { + throw AnalysisBudget.formulaSizeExceeded + } + // collect all variables since after simplification / CNF conversion // they could have been removed from the formula val symbolMapping = new SymbolMapping(gatherSymbols(p)) - - val simplified = simplify(p) val cnfExtractor = new AlreadyInCNF(symbolMapping) + val cnfTransformer = new TransformToCnf(symbolMapping) + + def cnfFor(prop: Prop): Solvable = { + prop match { + case cnfExtractor.ToCnf(solvable) => + // this is needed because t6942 would generate too many clauses with Tseitin + // already in CNF, just add clauses + solvable + case p => + cnfTransformer.apply(p) + } + } + simplified match { - case cnfExtractor.ToCnf(solvable) => - // this is needed because t6942 would generate too many clauses with Tseitin - // already in CNF, just add clauses - solvable - case p => - new TransformToCnf(symbolMapping).apply(p) + case And(props) => + // SI-6942: + // CNF(P1 /\ ... /\ PN) == CNF(P1) ++ CNF(...) ++ CNF(PN) + props.map(cnfFor).reduce(_ ++ _) + case p => + cnfFor(p) } } } @@ -288,7 +333,7 @@ trait Solving extends Logic { val NoTseitinModel: TseitinModel = null // returns all solutions, if any (TODO: better infinite recursion backstop -- detect fixpoint??) - def findAllModelsFor(solvable: Solvable): List[Solution] = { + def findAllModelsFor(solvable: Solvable, pos: Position): List[Solution] = { debug.patmat("find all models for\n"+ cnfString(solvable.cnf)) // we must take all vars from non simplified formula @@ -308,13 +353,12 @@ trait Solving extends Logic { final case class TseitinSolution(model: TseitinModel, unassigned: List[Int]) { def projectToSolution(symForVar: Map[Int, Sym]) = Solution(projectToModel(model, symForVar), unassigned map symForVar) } + def findAllModels(clauses: Array[Clause], models: List[TseitinSolution], - recursionDepthAllowed: Int = global.settings.YpatmatExhaustdepth.value): List[TseitinSolution]= + recursionDepthAllowed: Int = AnalysisBudget.maxDPLLdepth): List[TseitinSolution]= if (recursionDepthAllowed == 0) { - val maxDPLLdepth = global.settings.YpatmatExhaustdepth.value - reportWarning("(Exhaustivity analysis reached max recursion depth, not all missing cases are reported. " + - s"Please try with scalac -Ypatmat-exhaust-depth ${maxDPLLdepth * 2} or -Ypatmat-exhaust-depth off.)") + uncheckedWarning(pos, AnalysisBudget.recursionDepthReached) models } else { debug.patmat("find all models for\n" + cnfString(clauses)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index d3cd26f256..5ecca5abce 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -276,7 +276,7 @@ trait Implicits { /** An extractor for types of the form ? { name: (? >: argtpe <: Any*)restp } */ object HasMethodMatching { - val dummyMethod = NoSymbol.newTermSymbol("typer$dummy") setInfo NullaryMethodType(AnyTpe) + val dummyMethod = NoSymbol.newTermSymbol(TermName("typer$dummy")) setInfo NullaryMethodType(AnyTpe) def templateArgType(argtpe: Type) = new BoundedWildcardType(TypeBounds.lower(argtpe)) @@ -893,7 +893,7 @@ trait Implicits { try improves(firstPending, alt) catch { case e: CyclicReference => - debugwarn(s"Discarding $firstPending during implicit search due to cyclic reference.") + devWarning(s"Discarding $firstPending during implicit search due to cyclic reference.") true } ) diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index cf97474d9a..27e17fc65f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -553,9 +553,8 @@ trait Infer extends Checkable { // ...or lower bound of a type param, since they're asking for it. def canWarnAboutAny = { val loBounds = tparams map (_.info.bounds.lo) - val hasAny = pt :: restpe :: formals ::: argtpes ::: loBounds exists (t => - (t contains AnyClass) || (t contains AnyValClass) - ) + def containsAny(t: Type) = (t contains AnyClass) || (t contains AnyValClass) + val hasAny = pt :: restpe :: formals ::: argtpes ::: loBounds exists (_.dealiasWidenChain exists containsAny) !hasAny } def argumentPosition(idx: Int): Position = context.tree match { diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index da7b8b09aa..10aefae20b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -3,13 +3,14 @@ package typechecker import java.lang.Math.min import symtab.Flags._ -import scala.tools.nsc.util._ +import scala.reflect.internal.util.ScalaClassLoader import scala.reflect.runtime.ReflectionUtils import scala.collection.mutable.ListBuffer import scala.reflect.ClassTag import scala.reflect.internal.util.Statistics import scala.reflect.macros.util._ import scala.util.control.ControlThrowable +import scala.reflect.internal.util.ListOfNil import scala.reflect.macros.runtime.{AbortMacroException, MacroRuntimes} import scala.reflect.runtime.{universe => ru} import scala.reflect.macros.compiler.DefaultMacroCompiler diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 0aa62d771e..f90e32ce8a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -8,6 +8,7 @@ package typechecker import symtab.Flags._ import scala.reflect.internal.util.StringOps.{ ojoin } import scala.reflect.ClassTag +import scala.reflect.internal.util.ListOfNil import scala.reflect.runtime.{ universe => ru } import scala.language.higherKinds @@ -384,7 +385,7 @@ trait MethodSynthesis { } } case class Getter(tree: ValDef) extends BaseGetter(tree) { - override def derivedSym = if (mods.isDeferred) basisSym else basisSym.getter(enclClass) + override def derivedSym = if (mods.isDeferred) basisSym else basisSym.getterIn(enclClass) private def derivedRhs = if (mods.isDeferred) EmptyTree else fieldSelection private def derivedTpt = { // For existentials, don't specify a type for the getter, even one derived @@ -451,7 +452,7 @@ trait MethodSynthesis { def flagsMask = SetterFlags def flagsExtra = ACCESSOR - override def derivedSym = basisSym.setter(enclClass) + override def derivedSym = basisSym.setterIn(enclClass) } case class Field(tree: ValDef) extends DerivedFromValDef { def name = tree.localName diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 711cfba24d..24238b8e41 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -10,6 +10,7 @@ import scala.collection.mutable import scala.annotation.tailrec import symtab.Flags._ import scala.language.postfixOps +import scala.reflect.internal.util.ListOfNil /** This trait declares methods to create symbols and to enter them into scopes. * @@ -464,7 +465,7 @@ trait Namers extends MethodSynthesis { def enterModuleSymbol(tree : ModuleDef): Symbol = { var m: Symbol = context.scope lookupModule tree.name val moduleFlags = tree.mods.flags | MODULE - if (m.isModule && !m.isPackage && inCurrentScope(m) && (currentRun.canRedefine(m) || m.isSynthetic)) { + if (m.isModule && !m.hasPackageFlag && inCurrentScope(m) && (currentRun.canRedefine(m) || m.isSynthetic)) { // This code accounts for the way the package objects found in the classpath are opened up // early by the completer of the package itself. If the `packageobjects` phase then finds // the same package object in sources, we have to clean the slate and remove package object @@ -487,7 +488,7 @@ trait Namers extends MethodSynthesis { m.moduleClass setFlag moduleClassFlags(moduleFlags) setPrivateWithin(tree, m.moduleClass) } - if (m.isTopLevel && !m.isPackage) { + if (m.isTopLevel && !m.hasPackageFlag) { m.moduleClass.associatedFile = contextFile currentRun.symSource(m) = m.moduleClass.sourceFile registerTopLevelSym(m) @@ -844,7 +845,7 @@ trait Namers extends MethodSynthesis { private def widenIfNecessary(sym: Symbol, tpe: Type, pt: Type): Type = { val getter = if (sym.isValue && sym.owner.isClass && sym.isPrivate) - sym.getter(sym.owner) + sym.getterIn(sym.owner) else sym def isHidden(tp: Type): Boolean = tp match { case SingleType(pre, sym) => @@ -1305,7 +1306,7 @@ trait Namers extends MethodSynthesis { // by martin: the null case can happen in IDE; this is really an ugly hack on top of an ugly hack but it seems to work case Some(cda) => if (cda.companionModuleClassNamer == null) { - debugwarn(s"SI-6576 The companion module namer for $meth was unexpectedly null") + devWarning(s"SI-6576 The companion module namer for $meth was unexpectedly null") return } val p = (cda.classWithDefault, cda.companionModuleClassNamer) diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala index fa4a764f1b..8a66c7d274 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala @@ -268,7 +268,7 @@ trait PatternTypers { def freshArgType(tp: Type): Type = tp match { case MethodType(param :: _, _) => param.tpe - case PolyType(tparams, restpe) => createFromClonedSymbols(tparams, freshArgType(restpe))(polyType) + case PolyType(tparams, restpe) => createFromClonedSymbols(tparams, freshArgType(restpe))(genPolyType) case OverloadedType(_, _) => OverloadedUnapplyError(fun) ; ErrorType case _ => UnapplyWithSingleArgError(fun) ; ErrorType } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index d2931ff9e1..5abfbe850f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -718,7 +718,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // Check the remainder for invalid absoverride. for (member <- rest ; if (member.isAbstractOverride && member.isIncompleteIn(clazz))) { - val other = member.superSymbol(clazz) + val other = member.superSymbolIn(clazz) val explanation = if (other != NoSymbol) " and overrides incomplete superclass member " + infoString(other) else ", but no concrete implementation could be found in a base class" @@ -1684,9 +1684,9 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case _ => } if (skipBounds) { - tree.tpe = tree.tpe.map { + tree.setType(tree.tpe.map { _.filterAnnotations(_.symbol != UncheckedBoundsClass) - } + }) } tree diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index db81eecdf5..e0d96df062 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -322,7 +322,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT case Super(_, mix) => if (sym.isValue && !sym.isMethod || sym.hasAccessorFlag) { if (!settings.overrideVars) - reporter.error(tree.pos, "super may be not be used on " + sym.accessedOrSelf) + reporter.error(tree.pos, "super may not be used on " + sym.accessedOrSelf) } else if (isDisallowed(sym)) { reporter.error(tree.pos, "super not allowed here: use this." + name.decode + " instead") } diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index d2046a158c..966e8f1abe 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -344,7 +344,7 @@ trait SyntheticMethods extends ast.TreeDSL { // Without a means to suppress this warning, I've thought better of it. if (settings.warnValueOverrides) { (clazz.info nonPrivateMember m.name) filter (m => (m.owner != AnyClass) && (m.owner != clazz) && !m.isDeferred) andAlso { m => - currentUnit.warning(clazz.pos, s"Implementation of ${m.name} inherited from ${m.owner} overridden in $clazz to enforce value class semantics") + typer.context.warning(clazz.pos, s"Implementation of ${m.name} inherited from ${m.owner} overridden in $clazz to enforce value class semantics") } } true diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index 02356580cc..a7d48ceb89 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -300,8 +300,8 @@ abstract class TreeCheckers extends Analyzer { checkSym(tree) /* XXX: lots of syms show up here with accessed == NoSymbol. */ if (accessed != NoSymbol) { - val agetter = accessed.getter(sym.owner) - val asetter = accessed.setter(sym.owner) + val agetter = accessed.getterIn(sym.owner) + val asetter = accessed.setterIn(sym.owner) assertFn(agetter == sym || asetter == sym, sym + " is getter or setter, but accessed sym " + accessed + " shows " + agetter + " and " + asetter @@ -311,7 +311,7 @@ abstract class TreeCheckers extends Analyzer { } case ValDef(_, _, _, _) => if (sym.hasGetter && !sym.isOuterField && !sym.isOuterAccessor) { - assertFn(sym.getter(sym.owner) != NoSymbol, ownerstr(sym) + " has getter but cannot be found. " + sym.ownerChain) + assertFn(sym.getterIn(sym.owner) != NoSymbol, ownerstr(sym) + " has getter but cannot be found. " + sym.ownerChain) } case Apply(fn, args) => if (args exists (_ == EmptyTree)) diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 0f90c6a478..059981aa37 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -141,8 +141,8 @@ trait TypeDiagnostics { if (!member.hasAccessorFlag) member else if (!member.isDeferred) member.accessed else { - val getter = if (member.isSetter) member.getter(member.owner) else member - val flags = if (getter.setter(member.owner) != NoSymbol) DEFERRED.toLong | MUTABLE else DEFERRED + val getter = if (member.isSetter) member.getterIn(member.owner) else member + val flags = if (getter.setterIn(member.owner) != NoSymbol) DEFERRED.toLong | MUTABLE else DEFERRED getter.owner.newValue(getter.name.toTermName, getter.pos, flags) setInfo getter.tpe.resultType } @@ -439,7 +439,7 @@ trait TypeDiagnostics { context.warning(pos, "imported `%s' is permanently hidden by definition of %s".format(hidden, defn.fullLocationString)) object checkUnused { - val ignoreNames = Set[TermName]("readResolve", "readObject", "writeObject", "writeReplace") + val ignoreNames: Set[TermName] = Set(TermName("readResolve"), TermName("readObject"), TermName("writeObject"), TermName("writeReplace")) class UnusedPrivates extends Traverser { val defnTrees = ListBuffer[MemberDef]() diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index d01e4f84ba..391ef9e337 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -14,7 +14,7 @@ package tools.nsc package typechecker import scala.collection.{mutable, immutable} -import scala.reflect.internal.util.{ BatchSourceFile, Statistics, shortClassOfInstance } +import scala.reflect.internal.util.{ BatchSourceFile, Statistics, shortClassOfInstance, ListOfNil } import mutable.ListBuffer import symtab.Flags._ import Mode._ @@ -245,7 +245,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case TypeRef(_, sym, _) if sym.isAliasType => val tp0 = tp.dealias if (tp eq tp0) { - debugwarn(s"dropExistential did not progress dealiasing $tp, see SI-7126") + devWarning(s"dropExistential did not progress dealiasing $tp, see SI-7126") tp } else { val tp1 = dropExistential(tp0) @@ -2044,7 +2044,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (mexists(vparamss)(_.symbol == superArg.symbol)) { val alias = ( superAcc.initialize.alias - orElse (superAcc getter superAcc.owner) + orElse (superAcc getterIn superAcc.owner) filter (alias => superClazz.info.nonPrivateMember(alias.name) == alias) ) if (alias.exists && !alias.accessed.isVariable && !isRepeatedParamType(alias.accessed.info)) { @@ -2565,7 +2565,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val default = methodSym newValueParameter (newTermName("default"), tree.pos.focus, SYNTHETIC) setInfo functionType(List(A1.tpe), B1.tpe) val paramSyms = List(x, default) - methodSym setInfo polyType(List(A1, B1), MethodType(paramSyms, B1.tpe)) + methodSym setInfo genPolyType(List(A1, B1), MethodType(paramSyms, B1.tpe)) val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) if (!paramSynthetic) methodBodyTyper.context.scope enter x @@ -2865,7 +2865,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper ClassDef(Modifiers(FINAL), tpnme.ANON_FUN_NAME, tparams = Nil, gen.mkTemplate( parents = TypeTree(samClassTpFullyDefined) :: serializableParentAddendum, - self = emptyValDef, + self = noSelfType, constrMods = NoMods, vparamss = ListOfNil, body = List(samDef), @@ -2934,7 +2934,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper var issuedMissingParameterTypeError = false foreach2(fun.vparams, argpts) { (vparam, argpt) => if (vparam.tpt.isEmpty) { - vparam.tpt.tpe = + val vparamType = if (isFullyDefined(argpt)) argpt else { fun match { @@ -2953,6 +2953,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper issuedMissingParameterTypeError = true ErrorType } + vparam.tpt.setType(vparamType) if (!vparam.tpt.pos.isDefined) vparam.tpt setPos vparam.pos.focus } } @@ -3387,7 +3388,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // defaults are needed. they are added to the argument list in named style as // calls to the default getters. Example: // foo[Int](a)() ==> foo[Int](a)(b = foo$qual.foo$default$2[Int](a)) - checkNotMacro() // SI-8111 transformNamedApplication eagerly shuffles around the application to preserve // evaluation order. During this process, it calls `changeOwner` on symbols that @@ -3434,6 +3434,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper duplErrTree } else if (lencmp2 == 0) { // useful when a default doesn't match parameter type, e.g. def f[T](x:T="a"); f[Int]() + checkNotMacro() context.diagUsedDefaults = true doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt) } else { @@ -4365,7 +4366,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def narrowRhs(tp: Type) = { val sym = context.tree.symbol context.tree match { case ValDef(mods, _, _, Apply(Select(`tree`, _), _)) if !mods.isMutable && sym != null && sym != NoSymbol => - val sym1 = if (sym.owner.isClass && sym.getter(sym.owner) != NoSymbol) sym.getter(sym.owner) + val sym1 = if (sym.owner.isClass && sym.getterIn(sym.owner) != NoSymbol) sym.getterIn(sym.owner) else sym.lazyAccessorOrSelf val pre = if (sym1.owner.isClass) sym1.owner.thisType else NoPrefix intersectionType(List(tp, singleType(pre, sym1))) @@ -5198,15 +5199,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def warn(message: String) = context.warning(lit.pos, s"possible missing interpolator: $message") def suspiciousSym(name: TermName) = context.lookupSymbol(name, _ => true).symbol def suspiciousExpr = InterpolatorCodeRegex findFirstIn s - def suspiciousIdents = InterpolatorIdentRegex findAllIn s map (s => suspiciousSym(s drop 1)) + def suspiciousIdents = InterpolatorIdentRegex findAllIn s map (s => suspiciousSym(TermName(s drop 1))) - // heuristics - no warning on e.g. a string with only "$asInstanceOf" - if (s contains ' ') ( - if (suspiciousExpr.nonEmpty) - warn("detected an interpolated expression") // "${...}" - else - suspiciousIdents find isPlausible foreach (sym => warn(s"detected interpolated identifier `$$${sym.name}`")) // "$id" - ) + if (suspiciousExpr.nonEmpty) + warn("detected an interpolated expression") // "${...}" + else + suspiciousIdents find isPlausible foreach (sym => warn(s"detected interpolated identifier `$$${sym.name}`")) // "$id" } lit match { case Literal(Constant(s: String)) if !isRecognizablyNotForInterpolation => maybeWarn(s) @@ -5384,8 +5382,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper ) def runTyper(): Tree = { if (retypingOk) { - tree.tpe = null - if (tree.hasSymbol) tree.symbol = NoSymbol + tree.setType(null) + if (tree.hasSymbolField) tree.symbol = NoSymbol } val alreadyTyped = tree.tpe ne null val shouldPrint = !alreadyTyped && !phase.erasedTypes diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index fc1f45e358..22fb0728e6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -7,6 +7,7 @@ package scala.tools.nsc package typechecker import symtab.Flags._ +import scala.reflect.internal.util.ListOfNil /* * @author Martin Odersky diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 1643e0061f..47c88f2c00 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -7,7 +7,7 @@ import scala.tools.nsc.Global import scala.tools.nsc.reporters._ import scala.tools.nsc.CompilerCommand import scala.tools.nsc.io.{AbstractFile, VirtualDirectory} -import scala.tools.nsc.util.AbstractFileClassLoader +import scala.reflect.internal.util.AbstractFileClassLoader import scala.reflect.internal.Flags._ import scala.reflect.internal.util.{BatchSourceFile, NoSourceFile, NoFile} import java.lang.{Class => jClass} diff --git a/src/library/scala/annotation/switch.scala b/src/library/scala/annotation/switch.scala index 23e3923407..00124cf88b 100644 --- a/src/library/scala/annotation/switch.scala +++ b/src/library/scala/annotation/switch.scala @@ -22,6 +22,9 @@ package scala.annotation } }}} * + * Note: for pattern matches with one or two cases, the compiler generates jump instructions. + * Annotating such a match with `@switch` does not issue any warning. + * * @author Paul Phillips * @since 2.8 */ diff --git a/src/library/scala/collection/GenTraversableOnce.scala b/src/library/scala/collection/GenTraversableOnce.scala index 8c7c754af8..f77462ce88 100644 --- a/src/library/scala/collection/GenTraversableOnce.scala +++ b/src/library/scala/collection/GenTraversableOnce.scala @@ -278,7 +278,7 @@ trait GenTraversableOnce[+A] extends Any { * * @param op the binary operator. * @tparam B the result type of the binary operator. - * @return an option value containing the result of `reduceLeft(op)` is this $coll is nonempty, + * @return an option value containing the result of `reduceLeft(op)` if this $coll is nonempty, * `None` otherwise. */ def reduceLeftOption[B >: A](op: (B, A) => B): Option[B] @@ -290,7 +290,7 @@ trait GenTraversableOnce[+A] extends Any { * * @param op the binary operator. * @tparam B the result type of the binary operator. - * @return an option value containing the result of `reduceRight(op)` is this $coll is nonempty, + * @return an option value containing the result of `reduceRight(op)` if this $coll is nonempty, * `None` otherwise. */ def reduceRightOption[B >: A](op: (A, B) => B): Option[B] diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index 32d31f0be8..96374ef653 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -54,7 +54,7 @@ import scala.language.higherKinds * `HashMap` of objects. The traversal order for hash maps will * depend on the hash codes of its elements, and these hash codes might * differ from one run to the next. By contrast, a `LinkedHashMap` - * is ordered because it's `foreach` method visits elements in the + * is ordered because its `foreach` method visits elements in the * order they were inserted into the `HashMap`. * * @author Martin Odersky diff --git a/src/library/scala/collection/TraversableOnce.scala b/src/library/scala/collection/TraversableOnce.scala index 2eab58009c..c5b0d0f085 100644 --- a/src/library/scala/collection/TraversableOnce.scala +++ b/src/library/scala/collection/TraversableOnce.scala @@ -128,8 +128,21 @@ trait TraversableOnce[+A] extends Any with GenTraversableOnce[A] { * @example `Seq("a", 1, 5L).collectFirst({ case x: Int => x*10 }) = Some(10)` */ def collectFirst[B](pf: PartialFunction[A, B]): Option[B] = { - // make sure to use an iterator or `seq` - self.toIterator.foreach(pf.runWith(b => return Some(b))) + // TODO 2.12 -- move out alternate implementations into child classes + val i: Iterator[A] = self match { + case it: Iterator[A] => it + case _: GenIterable[_] => self.toIterator // If it might be parallel, be sure to .seq or use iterator! + case _ => // Not parallel, not iterable--just traverse + self.foreach(pf.runWith(b => return Some(b))) + return None + } + // Presumably the fastest way to get in and out of a partial function is for a sentinel function to return itself + // (Tested to be lower-overhead than runWith. Would be better yet to not need to (formally) allocate it--change in 2.12.) + val sentinel: Function1[A, Any] = new scala.runtime.AbstractFunction1[A, Any]{ def apply(a: A) = this } + while (i.hasNext) { + val x = pf.applyOrElse(i.next, sentinel) + if (x.asInstanceOf[AnyRef] ne sentinel) return Some(x.asInstanceOf[B]) + } None } diff --git a/src/library/scala/runtime/BoxesRunTime.java b/src/library/scala/runtime/BoxesRunTime.java index 82a3b00ac4..9cb1dee41c 100644 --- a/src/library/scala/runtime/BoxesRunTime.java +++ b/src/library/scala/runtime/BoxesRunTime.java @@ -28,7 +28,7 @@ import scala.math.ScalaNumber; * @version 2.0 */ public final class BoxesRunTime { - private static final int CHAR = 0, BYTE = 1, SHORT = 2, INT = 3, LONG = 4, FLOAT = 5, DOUBLE = 6, OTHER = 7; + private static final int CHAR = 0, /* BYTE = 1, SHORT = 2, */ INT = 3, LONG = 4, FLOAT = 5, DOUBLE = 6, OTHER = 7; /** We don't need to return BYTE and SHORT, as everything which might * care widens to INT. @@ -43,10 +43,6 @@ public final class BoxesRunTime return OTHER; } - private static String boxDescription(Object a) { - return "" + a.getClass().getSimpleName() + "(" + a + ")"; - } - /* BOXING ... BOXING ... BOXING ... BOXING ... BOXING ... BOXING ... BOXING ... BOXING */ public static java.lang.Boolean boxToBoolean(boolean b) { diff --git a/src/reflect/scala/reflect/api/Names.scala b/src/reflect/scala/reflect/api/Names.scala index 472da60338..cc01225287 100644 --- a/src/reflect/scala/reflect/api/Names.scala +++ b/src/reflect/scala/reflect/api/Names.scala @@ -30,15 +30,15 @@ import scala.language.implicitConversions */ trait Names { /** An implicit conversion from String to TermName. - * Enables an alternative notation `"map": TermName` as opposed to `TermName("map")`. - * @group Names + * Enables an alternative notation `"map": TermName` as opposed to `TermName("map")`. + * @group Names */ @deprecated("Use explicit `TermName(s)` instead", "2.11.0") implicit def stringToTermName(s: String): TermName = TermName(s) /** An implicit conversion from String to TypeName. - * Enables an alternative notation `"List": TypeName` as opposed to `TypeName("List")`. - * @group Names + * Enables an alternative notation `"List": TypeName` as opposed to `TypeName("List")`. + * @group Names */ @deprecated("Use explicit `TypeName(s)` instead", "2.11.0") implicit def stringToTypeName(s: String): TypeName = TypeName(s) diff --git a/src/reflect/scala/reflect/api/StandardLiftables.scala b/src/reflect/scala/reflect/api/StandardLiftables.scala index 66ac62cc9e..ebf15e4f57 100644 --- a/src/reflect/scala/reflect/api/StandardLiftables.scala +++ b/src/reflect/scala/reflect/api/StandardLiftables.scala @@ -230,6 +230,6 @@ trait StandardLiftables { self: Universe => val Symbol = TermName("Symbol") val util = TermName("util") val Vector = TermName("Vector") - val WILDCARD = self.nme.WILDCARD + val WILDCARD = self.termNames.WILDCARD } } diff --git a/src/reflect/scala/reflect/api/Trees.scala b/src/reflect/scala/reflect/api/Trees.scala index 9ecd87c17e..2bf407ee19 100644 --- a/src/reflect/scala/reflect/api/Trees.scala +++ b/src/reflect/scala/reflect/api/Trees.scala @@ -2661,7 +2661,7 @@ trait Trees { self: Universe => * @group Traversal */ abstract class ModifiersExtractor { - def apply(): Modifiers = Modifiers(NoFlags, tpnme.EMPTY, List()) + def apply(): Modifiers = Modifiers(NoFlags, typeNames.EMPTY, List()) def apply(flags: FlagSet, privateWithin: Name, annotations: List[Tree]): Modifiers def unapply(mods: Modifiers): Option[(FlagSet, Name, List[Tree])] } @@ -2674,7 +2674,7 @@ trait Trees { self: Universe => /** The factory for `Modifiers` instances. * @group Traversal */ - def Modifiers(flags: FlagSet): Modifiers = Modifiers(flags, tpnme.EMPTY) + def Modifiers(flags: FlagSet): Modifiers = Modifiers(flags, typeNames.EMPTY) /** An empty `Modifiers` object: no flags, empty visibility annotation and no Scala annotations. * @group Traversal diff --git a/src/reflect/scala/reflect/api/Types.scala b/src/reflect/scala/reflect/api/Types.scala index f6995dd5de..cd7648a44a 100644 --- a/src/reflect/scala/reflect/api/Types.scala +++ b/src/reflect/scala/reflect/api/Types.scala @@ -469,7 +469,7 @@ trait Types { def unapply(tpe: SingleType): Option[(Type, Symbol)] /** @see [[InternalApi.singleType]] */ - @deprecated("Use `ClassSymbol.thisPrefix` or `internal.singleType` instead") + @deprecated("Use `ClassSymbol.thisPrefix` or `internal.singleType` instead", "2.11.0") def apply(pre: Type, sym: Symbol)(implicit token: CompatToken): Type = internal.singleType(pre, sym) } diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 9f4ec3e6d1..756ed870ca 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -156,11 +156,11 @@ trait Definitions extends api.StandardDefinitions { // It becomes tricky to create dedicated objects for other symbols because // of initialization order issues. - lazy val JavaLangPackage = getPackage("java.lang") + lazy val JavaLangPackage = getPackage(TermName("java.lang")) lazy val JavaLangPackageClass = JavaLangPackage.moduleClass.asClass - lazy val ScalaPackage = getPackage("scala") + lazy val ScalaPackage = getPackage(TermName("scala")) lazy val ScalaPackageClass = ScalaPackage.moduleClass.asClass - lazy val RuntimePackage = getPackage("scala.runtime") + lazy val RuntimePackage = getPackage(TermName("scala.runtime")) lazy val RuntimePackageClass = RuntimePackage.moduleClass.asClass def javaTypeToValueClass(jtype: Class[_]): Symbol = jtype match { @@ -453,7 +453,7 @@ trait Definitions extends api.StandardDefinitions { // XML lazy val ScalaXmlTopScope = getModuleIfDefined("scala.xml.TopScope") - lazy val ScalaXmlPackage = getPackageIfDefined("scala.xml") + lazy val ScalaXmlPackage = getPackageIfDefined(TermName("scala.xml")) // scala.reflect lazy val ReflectPackage = requiredModule[scala.reflect.`package`.type] @@ -1148,7 +1148,7 @@ trait Definitions extends api.StandardDefinitions { // Trying to allow for deprecated locations sym.isAliasType && isMetaAnnotation(sym.info.typeSymbol) ) - lazy val metaAnnotations: Set[Symbol] = getPackage("scala.annotation.meta").info.members filter (_ isSubClass StaticAnnotationClass) toSet + lazy val metaAnnotations: Set[Symbol] = getPackage(TermName("scala.annotation.meta")).info.members filter (_ isSubClass StaticAnnotationClass) toSet // According to the scala.annotation.meta package object: // * By default, annotations on (`val`-, `var`- or plain) constructor parameters @@ -1462,7 +1462,7 @@ trait Definitions extends api.StandardDefinitions { ) lazy val TagSymbols = TagMaterializers.keySet lazy val Predef_conforms = (getMemberIfDefined(PredefModule, nme.conforms) - orElse getMemberMethod(PredefModule, "conforms": TermName)) // TODO: predicate on -Xsource:2.10 (for now, needed for transition from M8 -> RC1) + orElse getMemberMethod(PredefModule, TermName("conforms"))) // TODO: predicate on -Xsource:2.10 (for now, needed for transition from M8 -> RC1) lazy val Predef_classOf = getMemberMethod(PredefModule, nme.classOf) lazy val Predef_implicitly = getMemberMethod(PredefModule, nme.implicitly) lazy val Predef_wrapRefArray = getMemberMethod(PredefModule, nme.wrapRefArray) diff --git a/src/reflect/scala/reflect/internal/Importers.scala b/src/reflect/scala/reflect/internal/Importers.scala index dc4ad25ef2..494f62af06 100644 --- a/src/reflect/scala/reflect/internal/Importers.scala +++ b/src/reflect/scala/reflect/internal/Importers.scala @@ -301,7 +301,7 @@ trait Importers { to: SymbolTable => case (their: from.TypeTree, my: to.TypeTree) => if (their.wasEmpty) my.defineType(importType(their.tpe)) else my.setType(importType(their.tpe)) case (_, _) => - my.tpe = importType(their.tpe) + my.setType(importType(their.tpe)) } } } diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index 98b2c48379..b44c4022f6 100644 --- a/src/reflect/scala/reflect/internal/Printers.scala +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -1006,7 +1006,7 @@ trait Printers extends api.Printers { self: SymbolTable => printSuper(st, printedName(qual), checkSymbol = false) case th @ This(qual) => - if (tree.hasExistingSymbol && tree.symbol.isPackage) print(tree.symbol.fullName) + if (tree.hasExistingSymbol && tree.symbol.hasPackageFlag) print(tree.symbol.fullName) else printThis(th, printedName(qual)) // remove this prefix from constructor invocation in typechecked trees: this.this -> this @@ -1023,7 +1023,7 @@ trait Printers extends api.Printers { self: SymbolTable => }) && (tr match { // check that Select contains package case Select(q, _) => checkRootPackage(q) case _: Ident | _: This => val sym = tr.symbol - tr.hasExistingSymbol && sym.isPackage && sym.name != nme.ROOTPKG + tr.hasExistingSymbol && sym.hasPackageFlag && sym.name != nme.ROOTPKG case _ => false }) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index d23a102b28..d85ec22a84 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -155,11 +155,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => def toTypeConstructor: Type = typeConstructor def setAnnotations(annots: AnnotationInfo*): this.type = { setAnnotations(annots.toList); this } - def getter: Symbol = getter(owner) - def setter: Symbol = setter(owner) + def getter: Symbol = getterIn(owner) + def setter: Symbol = setterIn(owner) def companion: Symbol = { - if (isModule && !isPackage) companionSymbol + if (isModule && !hasPackageFlag) companionSymbol else if (isModuleClass && !isPackageClass) sourceModule.companionSymbol else if (isClass && !isModuleClass && !isPackageClass) companionSymbol else NoSymbol @@ -1047,7 +1047,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isIncompleteIn(base: Symbol): Boolean = this.isDeferred || (this hasFlag ABSOVERRIDE) && { - val supersym = superSymbol(base) + val supersym = superSymbolIn(base) supersym == NoSymbol || supersym.isIncompleteIn(base) } @@ -2375,13 +2375,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => Nil ) + @deprecated("Use `superSymbolIn` instead", "2.11.0") + final def superSymbol(base: Symbol): Symbol = superSymbolIn(base) + /** The symbol accessed by a super in the definition of this symbol when * seen from class `base`. This symbol is always concrete. * pre: `this.owner` is in the base class sequence of `base`. */ - @deprecated("Use `superSymbolIn` instead", "2.11.0") - final def superSymbol(base: Symbol): Symbol = superSymbolIn(base) - final def superSymbolIn(base: Symbol): Symbol = { var bcs = base.info.baseClasses dropWhile (owner != _) drop 1 var sym: Symbol = NoSymbol @@ -2393,12 +2393,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => sym } - /** The getter of this value or setter definition in class `base`, or NoSymbol if - * none exists. - */ @deprecated("Use `getterIn` instead", "2.11.0") final def getter(base: Symbol): Symbol = getterIn(base) + /** The getter of this value or setter definition in class `base`, or NoSymbol if none exists. */ final def getterIn(base: Symbol): Symbol = base.info decl getterName filter (_.hasAccessorFlag) @@ -2406,11 +2404,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => def setterName: TermName = name.setterName def localName: TermName = name.localName - /** The setter of this value or getter definition, or NoSymbol if none exists */ @deprecated("Use `setterIn` instead", "2.11.0") final def setter(base: Symbol, hasExpandedName: Boolean = needsExpandedSetterName): Symbol = setterIn(base, hasExpandedName) + /** The setter of this value or getter definition, or NoSymbol if none exists. */ final def setterIn(base: Symbol, hasExpandedName: Boolean = needsExpandedSetterName): Symbol = base.info decl setterNameInBase(base, hasExpandedName) filter (_.hasAccessorFlag) @@ -2538,7 +2536,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => else if (isInstanceOf[FreeTermSymbol]) ("free term", "free term", "FTE") else if (isInstanceOf[FreeTypeSymbol]) ("free type", "free type", "FTY") else if (isPackageClass) ("package class", "package", "PKC") - else if (isPackage) ("package", "package", "PK") + else if (hasPackageFlag) ("package", "package", "PK") else if (isPackageObject) ("package object", "package", "PKO") else if (isPackageObjectClass) ("package object class", "package", "PKOC") else if (isAnonymousClass) ("anonymous class", "anonymous class", "AC") @@ -2822,7 +2820,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def outerSource: Symbol = // SI-6888 Approximate the name to workaround the deficiencies in `nme.originalName` // in the face of classes named '$'. SI-2806 remains open to address the deeper problem. - if (originalName endsWith (nme.OUTER)) initialize.referenced + if (unexpandedName endsWith (nme.OUTER)) initialize.referenced else NoSymbol def setModuleClass(clazz: Symbol): TermSymbol = { @@ -2852,8 +2850,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => accessed.expandName(base) } else if (hasGetter) { - getter(owner).expandName(base) - setter(owner).expandName(base) + getterIn(owner).expandName(base) + setterIn(owner).expandName(base) } name = nme.expandedName(name.toTermName, base) } diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index b3e11a826e..75a1969d22 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -362,7 +362,7 @@ abstract class TreeGen { if (body forall treeInfo.isInterfaceMember) None else Some( atPos(wrappingPos(superPos, lvdefs)) ( - DefDef(NoMods, nme.MIXIN_CONSTRUCTOR, Nil, ListOfNil, TypeTree(), Block(lvdefs, Literal(Constant()))))) + DefDef(NoMods, nme.MIXIN_CONSTRUCTOR, Nil, ListOfNil, TypeTree(), Block(lvdefs, Literal(Constant(())))))) } else { // convert (implicit ... ) to ()(implicit ... ) if its the only parameter section @@ -376,7 +376,7 @@ abstract class TreeGen { // therefore here we emit a dummy which gets populated when the template is named and typechecked Some( atPos(wrappingPos(superPos, lvdefs ::: vparamss1.flatten).makeTransparent) ( - DefDef(constrMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), Block(lvdefs ::: List(superCall), Literal(Constant()))))) + DefDef(constrMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), Block(lvdefs ::: List(superCall), Literal(Constant(())))))) } } constr foreach (ensureNonOverlapping(_, parents ::: gvdefs, focus = false)) diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index fd918b8595..e3f95f9fd8 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -87,7 +87,7 @@ trait Trees extends api.Trees { private[scala] def copyAttrs(tree: Tree): this.type = { rawatt = tree.rawatt - tpe = tree.tpe + setType(tree.tpe) if (hasSymbolField) symbol = tree.symbol this } diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 8f114caac0..86a53a1b02 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -209,7 +209,7 @@ trait Types case object UnmappableTree extends TermTree { override def toString = "<unmappable>" - super.tpe_=(NoType) + super.setType(NoType) override def tpe_=(t: Type) = if (t != NoType) { throw new UnsupportedOperationException("tpe_=("+t+") inapplicable for <empty>") } @@ -247,7 +247,7 @@ trait Types def companion = { val sym = typeSymbolDirect - if (sym.isModule && !sym.isPackage) sym.companionSymbol.tpe + if (sym.isModule && !sym.hasPackageFlag) sym.companionSymbol.tpe else if (sym.isModuleClass && !sym.isPackageClass) sym.sourceModule.companionSymbol.tpe else if (sym.isClass && !sym.isModuleClass && !sym.isPackageClass) sym.companionSymbol.info else NoType diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 3b497227e7..237efd004f 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -142,7 +142,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive object ConstantArg { def enumToSymbol(enum: Enum[_]): Symbol = { val staticPartOfEnum = classToScala(enum.getClass).companionSymbol - staticPartOfEnum.info.declaration(enum.name: TermName) + staticPartOfEnum.info.declaration(TermName(enum.name)) } def unapply(schemaAndValue: (jClass[_], Any)): Option[Any] = schemaAndValue match { @@ -172,7 +172,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive // currently I'm simply sorting the methods to guarantee stability of the output override lazy val assocs: List[(Name, ClassfileAnnotArg)] = ( jann.annotationType.getDeclaredMethods.sortBy(_.getName).toList map (m => - (m.getName: TermName) -> toAnnotArg(m.getReturnType -> m.invoke(jann)) + TermName(m.getName) -> toAnnotArg(m.getReturnType -> m.invoke(jann)) ) ) } @@ -940,7 +940,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive val ownerModule: ModuleSymbol = if (split > 0) packageNameToScala(fullname take split) else this.RootPackage val owner = ownerModule.moduleClass - val name = (fullname: TermName) drop split + 1 + val name = TermName(fullname) drop split + 1 val opkg = owner.info decl name if (opkg.hasPackageFlag) opkg.asModule @@ -991,7 +991,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive if (name.startsWith(nme.NAME_JOIN_STRING)) coreLookup(name drop 1) else NoSymbol } if (nme.isModuleName(simpleName)) - coreLookup(nme.stripModuleSuffix(simpleName).toTermName) map (_.moduleClass) + coreLookup(simpleName.dropModule.toTermName) map (_.moduleClass) else coreLookup(simpleName) } @@ -1285,16 +1285,12 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive jclazz getDeclaredConstructor (effectiveParamClasses: _*) } - private def jArrayClass(elemClazz: jClass[_]): jClass[_] = { - jArray.newInstance(elemClazz, 0).getClass - } - /** The Java class that corresponds to given Scala type. * Pre: Scala type is already transformed to Java level. */ def typeToJavaClass(tpe: Type): jClass[_] = tpe match { case ExistentialType(_, rtpe) => typeToJavaClass(rtpe) - case TypeRef(_, ArrayClass, List(elemtpe)) => jArrayClass(typeToJavaClass(elemtpe)) + case TypeRef(_, ArrayClass, List(elemtpe)) => ScalaRunTime.arrayClass(typeToJavaClass(elemtpe)) case TypeRef(_, sym: ClassSymbol, _) => classToJava(sym.asClass) case tpe @ TypeRef(_, sym: AliasTypeSymbol, _) => typeToJavaClass(tpe.dealias) case SingleType(_, sym: ModuleSymbol) => classToJava(sym.moduleClass.asClass) diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala index 50ea8d9868..9ce6331e33 100644 --- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala +++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala @@ -107,7 +107,8 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => if (isCompilerUniverse) super.enter(sym) else { val existing = super.lookupEntry(sym.name) - assert(existing == null || existing.sym.isMethod, s"pkgClass = $pkgClass, sym = $sym, existing = $existing") + def eitherIsMethod(sym1: Symbol, sym2: Symbol) = sym1.isMethod || sym2.isMethod + assert(existing == null || eitherIsMethod(existing.sym, sym), s"pkgClass = $pkgClass, sym = $sym, existing = $existing") super.enter(sym) } } diff --git a/src/reflect/scala/reflect/runtime/package.scala b/src/reflect/scala/reflect/runtime/package.scala index e240bed0a7..77eb610a84 100644 --- a/src/reflect/scala/reflect/runtime/package.scala +++ b/src/reflect/scala/reflect/runtime/package.scala @@ -30,9 +30,9 @@ package runtime { import c.universe._ val runtimeClass = c.reifyEnclosingRuntimeClass if (runtimeClass.isEmpty) c.abort(c.enclosingPosition, "call site does not have an enclosing class") - val scalaPackage = Select(Ident(newTermName("_root_")), newTermName("scala")) - val runtimeUniverse = Select(Select(Select(scalaPackage, newTermName("reflect")), newTermName("runtime")), newTermName("universe")) - val currentMirror = Apply(Select(runtimeUniverse, newTermName("runtimeMirror")), List(Select(runtimeClass, newTermName("getClassLoader")))) + val scalaPackage = Select(Ident(TermName("_root_")), TermName("scala")) + val runtimeUniverse = Select(Select(Select(scalaPackage, TermName("reflect")), TermName("runtime")), TermName("universe")) + val currentMirror = Apply(Select(runtimeUniverse, TermName("runtimeMirror")), List(Select(runtimeClass, TermName("getClassLoader")))) c.Expr[Nothing](currentMirror)(c.WeakTypeTag.Nothing) } } diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index 0347622cf4..c281126d5f 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -309,7 +309,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set def shift[T](op: => T): T = exitingFlatten(op) } - def originalPath(name: String): String = originalPath(name: TermName) + def originalPath(name: String): String = originalPath(TermName(name)) def originalPath(name: Name): String = typerOp path name def originalPath(sym: Symbol): String = typerOp path sym def flatPath(sym: Symbol): String = flatOp shift sym.javaClassName @@ -1106,8 +1106,8 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set def tryTwice(op: => Symbol): Symbol = exitingTyper(op) orElse exitingFlatten(op) def symbolOfIdent(id: String): Symbol = symbolOfType(id) orElse symbolOfTerm(id) - def symbolOfType(id: String): Symbol = tryTwice(replScope lookup (id: TypeName)) - def symbolOfTerm(id: String): Symbol = tryTwice(replScope lookup (id: TermName)) + def symbolOfType(id: String): Symbol = tryTwice(replScope lookup TypeName(id)) + def symbolOfTerm(id: String): Symbol = tryTwice(replScope lookup TermName(id)) def symbolOfName(id: Name): Symbol = replScope lookup id def runtimeClassAndTypeOfTerm(id: String): Option[(JClass, Type)] = { diff --git a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala index d31b877262..fb4ed34571 100755 --- a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala +++ b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala @@ -281,13 +281,16 @@ trait CommentFactoryBase { this: MemberLookupBase => parse0(docBody, tags + (key -> value), Some(key), ls, inCodeBlock) case line :: ls if (lastTagKey.isDefined) => - val key = lastTagKey.get - val value = - ((tags get key): @unchecked) match { - case Some(b :: bs) => (b + endOfLine + line) :: bs - case None => oops("lastTagKey set when no tag exists for key") - } - parse0(docBody, tags + (key -> value), lastTagKey, ls, inCodeBlock) + val newtags = if (!line.isEmpty) { + val key = lastTagKey.get + val value = + ((tags get key): @unchecked) match { + case Some(b :: bs) => (b + endOfLine + line) :: bs + case None => oops("lastTagKey set when no tag exists for key") + } + tags + (key -> value) + } else tags + parse0(docBody, newtags, lastTagKey, ls, inCodeBlock) case line :: ls => if (docBody.length > 0) docBody append endOfLine @@ -315,18 +318,18 @@ trait CommentFactoryBase { this: MemberLookupBase => val bodyTags: mutable.Map[TagKey, List[Body]] = mutable.Map(tagsWithoutDiagram mapValues {tag => tag map (parseWikiAtSymbol(_, pos, site))} toSeq: _*) - def oneTag(key: SimpleTagKey): Option[Body] = + def oneTag(key: SimpleTagKey, filterEmpty: Boolean = true): Option[Body] = ((bodyTags remove key): @unchecked) match { - case Some(r :: rs) => + case Some(r :: rs) if !(filterEmpty && r.blocks.isEmpty) => if (!rs.isEmpty) reporter.warning(pos, "Only one '@" + key.name + "' tag is allowed") Some(r) - case None => None + case _ => None } def allTags(key: SimpleTagKey): List[Body] = - (bodyTags remove key) getOrElse Nil + (bodyTags remove key).getOrElse(Nil).filterNot(_.blocks.isEmpty) - def allSymsOneTag(key: TagKey): Map[String, Body] = { + def allSymsOneTag(key: TagKey, filterEmpty: Boolean = true): Map[String, Body] = { val keys: Seq[SymbolTagKey] = bodyTags.keys.toSeq flatMap { case stk: SymbolTagKey if (stk.name == key.name) => Some(stk) @@ -342,11 +345,11 @@ trait CommentFactoryBase { this: MemberLookupBase => reporter.warning(pos, "Only one '@" + key.name + "' tag for symbol " + key.symbol + " is allowed") (key.symbol, bs.head) } - Map.empty[String, Body] ++ pairs + Map.empty[String, Body] ++ (if (filterEmpty) pairs.filterNot(_._2.blocks.isEmpty) else pairs) } def linkedExceptions: Map[String, Body] = { - val m = allSymsOneTag(SimpleTagKey("throws")) + val m = allSymsOneTag(SimpleTagKey("throws"), filterEmpty = false) m.map { case (name,body) => val link = memberLookup(pos, name, site) @@ -372,7 +375,7 @@ trait CommentFactoryBase { this: MemberLookupBase => version0 = oneTag(SimpleTagKey("version")), since0 = oneTag(SimpleTagKey("since")), todo0 = allTags(SimpleTagKey("todo")), - deprecated0 = oneTag(SimpleTagKey("deprecated")), + deprecated0 = oneTag(SimpleTagKey("deprecated"), filterEmpty = false), note0 = allTags(SimpleTagKey("note")), example0 = allTags(SimpleTagKey("example")), constructor0 = oneTag(SimpleTagKey("constructor")), diff --git a/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala b/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala index ce75749859..86155845b0 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala @@ -206,25 +206,42 @@ abstract class HtmlPage extends Page { thisPage => case tpl :: tpls => templateToHtml(tpl) ++ sep ++ templatesToHtml(tpls, sep) } - /** Returns the _big image name corresponding to the DocTemplate Entity (upper left icon) */ - def docEntityKindToBigImage(ety: DocTemplateEntity) = - if (ety.isTrait && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None) "trait_to_object_big.png" - else if (ety.isTrait) "trait_big.png" - else if (ety.isClass && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None) "class_to_object_big.png" - else if (ety.isClass) "class_big.png" - else if ((ety.isAbstractType || ety.isAliasType) && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None) "type_to_object_big.png" - else if ((ety.isAbstractType || ety.isAliasType)) "type_big.png" - else if (ety.isObject && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None && ety.companion.get.isClass) "object_to_class_big.png" - else if (ety.isObject && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None && ety.companion.get.isTrait) "object_to_trait_big.png" - else if (ety.isObject && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None && (ety.companion.get.isAbstractType || ety.companion.get.isAliasType)) "object_to_trait_big.png" - else if (ety.isObject) "object_big.png" - else if (ety.isPackage) "package_big.png" - else "class_big.png" // FIXME: an entity *should* fall into one of the above categories, but AnyRef is somehow not + object Image extends Enumeration { + val Trait, Class, Type, Object, Package = Value + } + + /** Returns the _big image name and the alt attribute + * corresponding to the DocTemplate Entity (upper left icon) */ + def docEntityKindToBigImage(ety: DocTemplateEntity) = { + def entityToImage(e: DocTemplateEntity) = + if (e.isTrait) Image.Trait + else if (e.isClass) Image.Class + else if (e.isAbstractType || e.isAliasType) Image.Type + else if (e.isObject) Image.Object + else if (e.isPackage) Image.Package + else { + // FIXME: an entity *should* fall into one of the above categories, + // but AnyRef is somehow not + Image.Class + } + + val image = entityToImage(ety) + val companionImage = ety.companion filter { + e => e.visibility.isPublic && ! e.inSource.isEmpty + } map { entityToImage } + + (image, companionImage) match { + case (from, Some(to)) => + ((from + "_to_" + to + "_big.png").toLowerCase, from + "/" + to) + case (from, None) => + ((from + "_big.png").toLowerCase, from.toString) + } + } def permalink(template: Entity, isSelf: Boolean = true): Elem = <span class="permalink"> <a href={ memberToUrl(template, isSelf) } title="Permalink" target="_top"> - <img src={ relativeLinkTo(List("permalink.png", "lib")) } /> + <img src={ relativeLinkTo(List("permalink.png", "lib")) } alt="Permalink" /> </a> </span> diff --git a/src/scaladoc/scala/tools/nsc/doc/html/SyntaxHigh.scala b/src/scaladoc/scala/tools/nsc/doc/html/SyntaxHigh.scala index 910148532d..9ab3999447 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/SyntaxHigh.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/SyntaxHigh.scala @@ -52,7 +52,7 @@ private[html] object SyntaxHigh { "Triple", "TypeTag", "Unit") def apply(data: String): NodeSeq = { - val buf = data.getBytes + val buf = data.toCharArray val out = new StringBuilder def compare(offset: Int, key: String): Int = { @@ -60,7 +60,7 @@ private[html] object SyntaxHigh { var j = 0 val l = key.length while (i < buf.length && j < l) { - val bch = buf(i).toChar + val bch = buf(i) val kch = key charAt j if (bch < kch) return -1 else if (bch > kch) return 1 @@ -94,13 +94,13 @@ private[html] object SyntaxHigh { def line(i: Int): Int = if (i == buf.length || buf(i) == '\n') i else { - out append buf(i).toChar + out append buf(i) line(i+1) } var level = 0 def multiline(i: Int, star: Boolean): Int = { if (i == buf.length) return i - val ch = buf(i).toChar + val ch = buf(i) out append ch ch match { case '*' => @@ -127,7 +127,7 @@ private[html] object SyntaxHigh { if (i == buf.length) i else if (i > j+6) { out setLength 0; j } else { - val ch = buf(i).toChar + val ch = buf(i) out append ch ch match { case '\\' => @@ -148,7 +148,7 @@ private[html] object SyntaxHigh { val out = new StringBuilder("\"") def strlit0(i: Int, bslash: Boolean): Int = { if (i == buf.length) return i - val ch = buf(i).toChar + val ch = buf(i) out append ch ch match { case '\\' => @@ -167,7 +167,7 @@ private[html] object SyntaxHigh { val out = new StringBuilder def intg(i: Int): Int = { if (i == buf.length) return i - val ch = buf(i).toChar + val ch = buf(i) ch match { case '.' => out append ch @@ -181,7 +181,7 @@ private[html] object SyntaxHigh { } def frac(i: Int): Int = { if (i == buf.length) return i - val ch = buf(i).toChar + val ch = buf(i) ch match { case 'e' | 'E' => out append ch @@ -195,7 +195,7 @@ private[html] object SyntaxHigh { } def expo(i: Int, signed: Boolean): Int = { if (i == buf.length) return i - val ch = buf(i).toChar + val ch = buf(i) ch match { case '+' | '-' if !signed => out append ch @@ -222,7 +222,7 @@ private[html] object SyntaxHigh { case '&' => parse("&", i+1) case '<' if i+1 < buf.length => - val ch = buf(i+1).toChar + val ch = buf(i+1) if (ch == '-' || ch == ':' || ch == '%') parse("<span class=\"kw\"><"+ch+"</span>", i+2) else @@ -236,19 +236,19 @@ private[html] object SyntaxHigh { if (i+1 < buf.length && buf(i+1) == '>') parse("<span class=\"kw\">=></span>", i+2) else - parse(buf(i).toChar.toString, i+1) + parse(buf(i).toString, i+1) case '/' => if (i+1 < buf.length && (buf(i+1) == '/' || buf(i+1) == '*')) { val c = comment(i+1) parse("<span class=\"cmt\">"+c+"</span>", i+c.length) } else - parse(buf(i).toChar.toString, i+1) + parse(buf(i).toString, i+1) case '\'' => val s = charlit(i+1) if (s.length > 0) parse("<span class=\"lit\">"+s+"</span>", i+s.length) else - parse(buf(i).toChar.toString, i+1) + parse(buf(i).toString, i+1) case '"' => val s = strlit(i+1) parse("<span class=\"lit\">"+s+"</span>", i+s.length) @@ -257,9 +257,9 @@ private[html] object SyntaxHigh { if (k >= 0) parse("<span class=\"ano\">@"+annotations(k)+"</span>", i+annotations(k).length+1) else - parse(buf(i).toChar.toString, i+1) + parse(buf(i).toString, i+1) case _ => - if (i == 0 || (i >= 1 && !Character.isJavaIdentifierPart(buf(i-1).toChar))) { + if (i == 0 || (i >= 1 && !Character.isJavaIdentifierPart(buf(i-1)))) { if (Character.isDigit(buf(i).toInt) || (buf(i) == '.' && i + 1 < buf.length && Character.isDigit(buf(i+1).toInt))) { val s = numlit(i) @@ -273,11 +273,11 @@ private[html] object SyntaxHigh { if (k >= 0) parse("<span class=\"std\">"+standards(k)+"</span>", i+standards(k).length) else - parse(buf(i).toChar.toString, i+1) + parse(buf(i).toString, i+1) } } } else - parse(buf(i).toChar.toString, i+1) + parse(buf(i).toString, i+1) } } diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala index e10c54a414..c384ed7034 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala @@ -103,11 +103,13 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp <body class={ if (tpl.isType) "type" else "value" }> <div id="definition"> { + val (src, alt) = docEntityKindToBigImage(tpl) + tpl.companion match { case Some(companion) if (companion.visibility.isPublic && companion.inSource != None) => - <a href={relativeLinkTo(companion)} title={docEntityKindToCompanionTitle(tpl)}><img src={ relativeLinkTo(List(docEntityKindToBigImage(tpl), "lib")) }/></a> + <a href={relativeLinkTo(companion)} title={docEntityKindToCompanionTitle(tpl)}><img alt={alt} src={ relativeLinkTo(List(src, "lib")) }/></a> case _ => - <img src={ relativeLinkTo(List(docEntityKindToBigImage(tpl), "lib")) }/> + <img alt={alt} src={ relativeLinkTo(List(src, "lib")) }/> }} { owner } <h1>{ displayName }</h1>{ diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.css b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.css index e129e6cf6a..e84d7c1ca6 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.css +++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.css @@ -210,6 +210,7 @@ dl.attributes > dd { display: block; padding-left: 10em; margin-bottom: 5px; + min-height: 15px; } #template .values > h3 { @@ -669,6 +670,7 @@ div.fullcomment dl.paramcmts > dd { padding-left: 10px; margin-bottom: 5px; margin-left: 70px; + min-height: 15px; } /* Members filter tool */ diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.js b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.js index 1ebcb67f04..798a2d430b 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.js +++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.js @@ -179,7 +179,7 @@ $(document).ready(function(){ filter(); }); - $("#visbl > ol > li.public").click(function() { + $("#order > ol > li.alpha").click(function() { if ($(this).hasClass("out")) { orderAlpha(); } |