diff options
Diffstat (limited to 'src')
359 files changed, 6604 insertions, 8719 deletions
diff --git a/src/build/genprod.scala b/src/build/genprod.scala index b470348e8c..f85a151ae5 100644 --- a/src/build/genprod.scala +++ b/src/build/genprod.scala @@ -15,7 +15,6 @@ import scala.language.postfixOps * where the argument is the desired output directory * * @author Burak Emir, Stephane Micheloud, Geoffrey Washburn, Paul Phillips - * @version 1.1 */ object genprod extends App { val MAX_ARITY = 22 @@ -353,7 +352,7 @@ object ProductTwo extends Product(2) class Product(val i: Int) extends Group("Product") with Arity { val productElementComment = """ - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/build/maven/scala-compiler-doc-pom.xml b/src/build/maven/scala-compiler-doc-pom.xml index 8572e55b42..0c33d23d61 100644 --- a/src/build/maven/scala-compiler-doc-pom.xml +++ b/src/build/maven/scala-compiler-doc-pom.xml @@ -51,8 +51,8 @@ <name>EPFL LAMP</name> </developer> <developer> - <id>Typesafe</id> - <name>Typesafe, Inc.</name> + <id>Lightbend</id> + <name>Lightbend, Inc.</name> </developer> </developers> </project> diff --git a/src/build/maven/scala-compiler-interactive-pom.xml b/src/build/maven/scala-compiler-interactive-pom.xml index ad8192b694..d3e5e0b834 100644 --- a/src/build/maven/scala-compiler-interactive-pom.xml +++ b/src/build/maven/scala-compiler-interactive-pom.xml @@ -41,8 +41,8 @@ <name>EPFL LAMP</name> </developer> <developer> - <id>Typesafe</id> - <name>Typesafe, Inc.</name> + <id>Lightbend</id> + <name>Lightbend, Inc.</name> </developer> </developers> </project> diff --git a/src/build/maven/scala-compiler-pom.xml b/src/build/maven/scala-compiler-pom.xml index 8ca18f6f14..15546109c8 100644 --- a/src/build/maven/scala-compiler-pom.xml +++ b/src/build/maven/scala-compiler-pom.xml @@ -63,8 +63,8 @@ <name>EPFL LAMP</name> </developer> <developer> - <id>Typesafe</id> - <name>Typesafe, Inc.</name> + <id>Lightbend</id> + <name>Lightbend, Inc.</name> </developer> </developers> </project> diff --git a/src/build/maven/scala-dist-pom.xml b/src/build/maven/scala-dist-pom.xml index 1f6b6710ac..ce511661b0 100644 --- a/src/build/maven/scala-dist-pom.xml +++ b/src/build/maven/scala-dist-pom.xml @@ -62,8 +62,8 @@ <name>EPFL LAMP</name> </developer> <developer> - <id>Typesafe</id> - <name>Typesafe, Inc.</name> + <id>Lightbend</id> + <name>Lightbend, Inc.</name> </developer> </developers> </project> diff --git a/src/build/maven/scala-library-all-pom.xml b/src/build/maven/scala-library-all-pom.xml index 074c067742..4620c620dc 100644 --- a/src/build/maven/scala-library-all-pom.xml +++ b/src/build/maven/scala-library-all-pom.xml @@ -61,8 +61,8 @@ <name>EPFL LAMP</name> </developer> <developer> - <id>Typesafe</id> - <name>Typesafe, Inc.</name> + <id>Lightbend</id> + <name>Lightbend, Inc.</name> </developer> </developers> </project> diff --git a/src/build/maven/scala-library-pom.xml b/src/build/maven/scala-library-pom.xml index 78fc05a7c3..e27f8fb12f 100644 --- a/src/build/maven/scala-library-pom.xml +++ b/src/build/maven/scala-library-pom.xml @@ -39,8 +39,8 @@ <name>EPFL LAMP</name> </developer> <developer> - <id>Typesafe</id> - <name>Typesafe, Inc.</name> + <id>Lightbend</id> + <name>Lightbend, Inc.</name> </developer> </developers> </project> diff --git a/src/build/maven/scala-reflect-pom.xml b/src/build/maven/scala-reflect-pom.xml index c21caefcf2..f7f3c8bc08 100644 --- a/src/build/maven/scala-reflect-pom.xml +++ b/src/build/maven/scala-reflect-pom.xml @@ -44,8 +44,8 @@ <name>EPFL LAMP</name> </developer> <developer> - <id>Typesafe</id> - <name>Typesafe, Inc.</name> + <id>Lightbend</id> + <name>Lightbend, Inc.</name> </developer> </developers> </project> diff --git a/src/build/maven/scalap-pom.xml b/src/build/maven/scalap-pom.xml index 236ac999fc..acdd44f19b 100644 --- a/src/build/maven/scalap-pom.xml +++ b/src/build/maven/scalap-pom.xml @@ -41,8 +41,8 @@ <name>EPFL LAMP</name> </developer> <developer> - <id>Typesafe</id> - <name>Typesafe, Inc.</name> + <id>Lightbend</id> + <name>Lightbend, Inc.</name> </developer> </developers> </project> diff --git a/src/compiler/scala/reflect/macros/contexts/Reifiers.scala b/src/compiler/scala/reflect/macros/contexts/Reifiers.scala index ecef1c7289..010829f6ab 100644 --- a/src/compiler/scala/reflect/macros/contexts/Reifiers.scala +++ b/src/compiler/scala/reflect/macros/contexts/Reifiers.scala @@ -61,9 +61,9 @@ trait Reifiers { // logging free vars only when they are untyped prevents avalanches of duplicate messages symtab.syms map (sym => symtab.symDef(sym)) foreach { case FreeTermDef(_, _, binding, _, origin) if universe.settings.logFreeTerms && binding.tpe == null => - reporter.echo(position, "free term: %s %s".format(showRaw(binding), origin)) + reporter.echo(position, s"free term: ${showRaw(binding)} $origin") case FreeTypeDef(_, _, binding, _, origin) if universe.settings.logFreeTypes && binding.tpe == null => - reporter.echo(position, "free type: %s %s".format(showRaw(binding), origin)) + reporter.echo(position, s"free type: ${showRaw(binding)} $origin") case _ => // do nothing } diff --git a/src/compiler/scala/reflect/macros/contexts/Typers.scala b/src/compiler/scala/reflect/macros/contexts/Typers.scala index 28c1e3ddb3..a0dfbf5df1 100644 --- a/src/compiler/scala/reflect/macros/contexts/Typers.scala +++ b/src/compiler/scala/reflect/macros/contexts/Typers.scala @@ -18,31 +18,34 @@ trait Typers { * @see [[scala.tools.reflect.ToolBox.typeCheck]] */ def typecheck(tree: Tree, mode: TypecheckMode = TERMmode, pt: Type = universe.WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree = { - macroLogVerbose("typechecking %s with expected type %s, implicit views = %s, macros = %s".format(tree, pt, !withImplicitViewsDisabled, !withMacrosDisabled)) - val context = callsiteTyper.context - val withImplicitFlag = if (!withImplicitViewsDisabled) (context.withImplicitsEnabled[Tree] _) else (context.withImplicitsDisabled[Tree] _) - val withMacroFlag = if (!withMacrosDisabled) (context.withMacrosEnabled[Tree] _) else (context.withMacrosDisabled[Tree] _) - def withContext(tree: => Tree) = withImplicitFlag(withMacroFlag(tree)) - def withWrapping(tree: Tree)(op: Tree => Tree) = if (mode == TERMmode) universe.wrappingIntoTerm(tree)(op) else op(tree) - def typecheckInternal(tree: Tree) = callsiteTyper.silent(_.typed(universe.duplicateAndKeepPositions(tree), mode, pt), reportAmbiguousErrors = false) - withWrapping(tree)(wrappedTree => withContext(typecheckInternal(wrappedTree) match { - case universe.analyzer.SilentResultValue(result) => - macroLogVerbose(result) - result - case error @ universe.analyzer.SilentTypeError(_) => - macroLogVerbose(error.err.errMsg) - if (!silent) throw new TypecheckException(error.err.errPos, error.err.errMsg) - universe.EmptyTree - })) + macroLogVerbose(s"typechecking $tree with expected type $pt, implicit views = ${!withImplicitViewsDisabled}, macros = ${!withMacrosDisabled}") + import callsiteTyper.context + def doTypecheck(wrapped: Tree): Tree = + context.withImplicits(enabled = !withImplicitViewsDisabled) { + context.withMacros(enabled = !withMacrosDisabled) { + callsiteTyper.silent(_.typed(universe.duplicateAndKeepPositions(wrapped), mode, pt), reportAmbiguousErrors = false) match { + case universe.analyzer.SilentResultValue(result) => + macroLogVerbose(result) + result + case error@universe.analyzer.SilentTypeError(_) => + macroLogVerbose(error.err.errMsg) + if (!silent) throw new TypecheckException(error.err.errPos, error.err.errMsg) + universe.EmptyTree + } + } + } + + if (mode == TERMmode) universe.wrappingIntoTerm(tree)(doTypecheck) + else doTypecheck(tree) } def inferImplicitValue(pt: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: Position = enclosingPosition): Tree = { - macroLogVerbose("inferring implicit value of type %s, macros = %s".format(pt, !withMacrosDisabled)) + macroLogVerbose(s"inferring implicit value of type $pt, macros = ${!withMacrosDisabled}") universe.analyzer.inferImplicit(universe.EmptyTree, pt, false, callsiteTyper.context, silent, withMacrosDisabled, pos, (pos, msg) => throw TypecheckException(pos, msg)) } def inferImplicitView(tree: Tree, from: Type, to: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: Position = enclosingPosition): Tree = { - macroLogVerbose("inferring implicit view from %s to %s for %s, macros = %s".format(from, to, tree, !withMacrosDisabled)) + macroLogVerbose(s"inferring implicit view from $from to $to for $tree, macros = ${!withMacrosDisabled}") val viewTpe = universe.appliedType(universe.definitions.FunctionClass(1).toTypeConstructor, List(from, to)) universe.analyzer.inferImplicit(tree, viewTpe, true, callsiteTyper.context, silent, withMacrosDisabled, pos, (pos, msg) => throw TypecheckException(pos, msg)) } diff --git a/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala b/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala index 5fd9c0db34..7e700a524c 100644 --- a/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala +++ b/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala @@ -1,8 +1,11 @@ package scala.reflect.macros package runtime +import java.net.URLClassLoader + import scala.reflect.internal.Flags._ import scala.reflect.runtime.ReflectionUtils +import scala.reflect.internal.util.AbstractFileClassLoader trait MacroRuntimes extends JavaReflectionRuntimes { self: scala.tools.nsc.typechecker.Analyzer => @@ -44,7 +47,15 @@ trait MacroRuntimes extends JavaReflectionRuntimes { * which compiles implementations into a virtual directory (very much like REPL does) and then conjures * a classloader mapped to that virtual directory. */ - lazy val defaultMacroClassloader: ClassLoader = findMacroClassLoader() + private lazy val defaultMacroClassloaderCache = { + def attemptClose(loader: ClassLoader): Unit = loader match { + case u: URLClassLoader => debuglog("Closing macro runtime classloader"); u.close() + case afcl: AbstractFileClassLoader => attemptClose(afcl.getParent) + case _ => ??? + } + perRunCaches.newGeneric(findMacroClassLoader, attemptClose _) + } + def defaultMacroClassloader: ClassLoader = defaultMacroClassloaderCache() /** Abstracts away resolution of macro runtimes. */ @@ -72,4 +83,4 @@ trait MacroRuntimes extends JavaReflectionRuntimes { } } } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala index d007df75e3..b2948f8161 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala @@ -106,14 +106,10 @@ trait GenTypes { private def spliceAsManifest(tpe: Type): Tree = { def isSynthetic(manifest: Tree) = manifest exists (sub => sub.symbol != null && (sub.symbol == FullManifestModule || sub.symbol.owner == FullManifestModule)) def searchForManifest(typer: analyzer.Typer): Tree = - analyzer.inferImplicit( - EmptyTree, + analyzer.inferImplicitByTypeSilent( appliedType(FullManifestClass.toTypeConstructor, List(tpe)), - reportAmbiguous = false, - isView = false, - context = typer.context, - saveAmbiguousDivergent = false, - pos = defaultErrorPosition) match { + typer.context, + defaultErrorPosition) match { case success if !success.tree.isEmpty && !isSynthetic(success.tree) => val manifestInScope = success.tree // todo. write a test for this diff --git a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala index b5b0f93750..242e5d60b3 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala @@ -55,7 +55,7 @@ trait GenUtils { mirrorCall(TermName("" + prefix), args: _*) def scalaFactoryCall(name: TermName, args: Tree*): Tree = - call(s"scala.$name.apply", args: _*) + call(s"_root_.scala.$name.apply", args: _*) def scalaFactoryCall(name: String, args: Tree*): Tree = scalaFactoryCall(TermName(name), args: _*) diff --git a/src/compiler/scala/reflect/reify/phases/Reify.scala b/src/compiler/scala/reflect/reify/phases/Reify.scala index 143424dac5..93f6f99d81 100644 --- a/src/compiler/scala/reflect/reify/phases/Reify.scala +++ b/src/compiler/scala/reflect/reify/phases/Reify.scala @@ -1,7 +1,6 @@ package scala.reflect.reify package phases -import scala.runtime.ScalaRunTime.isAnyVal import scala.reflect.reify.codegen._ trait Reify extends GenSymbols @@ -57,4 +56,9 @@ trait Reify extends GenSymbols case _ => throw new Error("reifee %s of type %s is not supported".format(reifee, reifee.getClass)) }) + + private def isAnyVal(x: Any) = x match { + case _: Byte | _: Short | _: Char | _: Int | _: Long | _: Float | _: Double | _: Boolean | _: Unit => true + case _ => false + } } diff --git a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala index 3b91d28360..a5c4c7e0a3 100644 --- a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala +++ b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala @@ -28,7 +28,7 @@ trait NodePrinters { var s = line substring 2 s = s.replace(nme.UNIVERSE_PREFIX.toString, "") s = s.replace(".apply", "") - s = "([^\"])scala\\.collection\\.immutable\\.".r.replaceAllIn(s, "$1") + s = "([^\"])(_root_\\.)?scala\\.collection\\.immutable\\.".r.replaceAllIn(s, "$1") s = "List\\[List\\[.*?\\].*?\\]".r.replaceAllIn(s, "List") s = "List\\[.*?\\]".r.replaceAllIn(s, "List") s = s.replace("immutable.this.Nil", "List()") diff --git a/src/compiler/scala/tools/ant/FastScalac.scala b/src/compiler/scala/tools/ant/FastScalac.scala index e0b7be8004..3b62c493d3 100644 --- a/src/compiler/scala/tools/ant/FastScalac.scala +++ b/src/compiler/scala/tools/ant/FastScalac.scala @@ -109,7 +109,7 @@ class FastScalac extends Scalac { List( /*scalac*/ s.debuginfo, s.target - ) filter (x => x.value != x.default) map (x => "%s:%s".format(x.name, x.value)) + ) filter (x => x.value != x.default) map (x => s"${x.name}:${x.value}") val booleanSettings = List( @@ -129,7 +129,7 @@ class FastScalac extends Scalac { val phaseSetting = { val s = settings.log if (s.value.isEmpty) Nil - else List("%s:%s".format(s.name, s.value.mkString(","))) + else List(s"${s.name}:${s.value.mkString(",")}") } val fscOptions = @@ -147,8 +147,7 @@ class FastScalac extends Scalac { case cl: AntClassLoader => path add new Path(getProject, cl.getClasspath) case _ => - buildError("Compilation failed because of an internal compiler error;"+ - " see the error output for details.") + buildError("Compilation failed because of an internal compiler error; see the error output for details.") } path } @@ -160,8 +159,7 @@ class FastScalac extends Scalac { File(url.getFile).jfile.getParentFile.getParentFile.getAbsolutePath } catch { case _: Throwable => - buildError("Compilation failed because of an internal compiler error;"+ - " couldn't determine value for -Dscala.home=<value>") + buildError("Compilation failed because of an internal compiler error; couldn't determine value for -Dscala.home=<value>") } java.createJvmarg() setValue "-Dscala.usejavacp=true" java.createJvmarg() setValue ("-Dscala.home="+scalaHome) @@ -186,7 +184,6 @@ class FastScalac extends Scalac { val res = execWithArgFiles(java, paths) if (failonerror && res != 0) - buildError("Compilation failed because of an internal compiler error;"+ - " see the error output for details.") + buildError("Compilation failed because of an internal compiler error; see the error output for details.") } } diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala index fe9815be20..930163af36 100644 --- a/src/compiler/scala/tools/ant/Scalac.scala +++ b/src/compiler/scala/tools/ant/Scalac.scala @@ -551,7 +551,7 @@ class Scalac extends ScalaMatchingTask with ScalacShared { val str = if (javaFiles.isEmpty) "%d source file%s".format(list.length, plural(list)) else "%d scala and %d java source files".format(scalaFiles.length, javaFiles.length) - log("Compiling %s to %s".format(str, getDestination.toString)) + log(s"Compiling $str to $getDestination") } else log("No files selected for compilation", Project.MSG_VERBOSE) diff --git a/src/compiler/scala/tools/cmd/CommandLine.scala b/src/compiler/scala/tools/cmd/CommandLine.scala index 781cc564cb..3a36a7d345 100644 --- a/src/compiler/scala/tools/cmd/CommandLine.scala +++ b/src/compiler/scala/tools/cmd/CommandLine.scala @@ -51,7 +51,7 @@ class CommandLine(val spec: Reference, val originalArgs: List[String]) extends C /* Assumes known options have all been ruled out already. */ def isUnknown(opt: String) = onlyKnownOptions && (opt startsWith "-") && { - errorFn("Option '%s' not recognized.".format(opt)) + errorFn(s"Option '$opt' not recognized.") true } @@ -61,7 +61,7 @@ class CommandLine(val spec: Reference, val originalArgs: List[String]) extends C case x :: Nil => expand(x) foreach (exp => return loop(exp)) if (isBinaryOption(x) && enforceArity) - errorFn("Option '%s' requires argument, found EOF instead.".format(x)) + errorFn(s"Option '$x' requires argument, found EOF instead.") if (isUnaryOption(x)) mapForUnary(x) else if (isUnknown(x)) Map() diff --git a/src/compiler/scala/tools/cmd/Opt.scala b/src/compiler/scala/tools/cmd/Opt.scala index df3d0c4462..70756c5bb2 100644 --- a/src/compiler/scala/tools/cmd/Opt.scala +++ b/src/compiler/scala/tools/cmd/Opt.scala @@ -20,7 +20,7 @@ object Opt { self: Implicit => protected def fail(msg: String) = runAndExit(println(programInfo.runner + ": " + msg)) - protected def failOption(arg: String, why: String) = fail("%s: '%s' is %s".format(opt, arg, why)) + protected def failOption(arg: String, why: String) = fail(s"$opt: '$arg' is $why") } trait Implicit { diff --git a/src/compiler/scala/tools/cmd/Property.scala b/src/compiler/scala/tools/cmd/Property.scala index e6262a7e40..18bedd6f7e 100644 --- a/src/compiler/scala/tools/cmd/Property.scala +++ b/src/compiler/scala/tools/cmd/Property.scala @@ -65,8 +65,8 @@ trait Property extends Reference { propertiesToOptions(loadProperties(file)) def propertiesToOptions(props: java.util.Properties): List[String] = { - import scala.collection.JavaConversions._ - propertiesToOptions(props.toList) + import scala.collection.JavaConverters._ + propertiesToOptions(props.asScala.toList) } def propertiesToOptions(props: List[(String, String)]) = props flatMap propMapper } diff --git a/src/compiler/scala/tools/cmd/Reference.scala b/src/compiler/scala/tools/cmd/Reference.scala index 62b6c893cf..25a16b1e3e 100644 --- a/src/compiler/scala/tools/cmd/Reference.scala +++ b/src/compiler/scala/tools/cmd/Reference.scala @@ -70,18 +70,18 @@ object Reference { def addHelpAlias(f: () => String) = mapHelp { s => val str = "alias for '%s'" format f() def noHelp = (helpFormatStr.format("", "")).length == s.length - val str2 = if (noHelp) str else " (" + str + ")" + val str2 = if (noHelp) str else s" ($str)" s + str2 } def addHelpDefault(f: () => String): Unit = mapHelp { s => val str = "(default: %s)" format f() - if (s.length + str.length < MaxLine) s + " " + str + if (s.length + str.length < MaxLine) s"$s $str" else defaultFormatStr.format(s, str) } def addHelpEnvDefault(name: String): Unit = mapHelp { s => - val line1 = "%s (default: %s)".format(s, name) + val line1 = s"$s (default: $name)" val envNow = envOrNone(name) map ("'" + _ + "'") getOrElse "unset" val line2 = defaultFormatStr.format("Currently " + envNow) diff --git a/src/compiler/scala/tools/cmd/gen/AnyVals.scala b/src/compiler/scala/tools/cmd/gen/AnyVals.scala deleted file mode 100644 index e78589908c..0000000000 --- a/src/compiler/scala/tools/cmd/gen/AnyVals.scala +++ /dev/null @@ -1,484 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2007-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.cmd -package gen - -/** Code generation of the AnyVal types and their companions. */ -trait AnyValReps { - self: AnyVals => - - sealed abstract class AnyValNum(name: String, repr: Option[String], javaEquiv: String) - extends AnyValRep(name,repr,javaEquiv) { - - case class Op(op : String, doc : String) - - private def companionCoercions(tos: AnyValRep*) = { - tos.toList map (to => - s"implicit def @javaequiv@2${to.javaEquiv}(x: @name@): ${to.name} = x.to${to.name}" - ) - } - def coercionComment = -"""/** Language mandated coercions from @name@ to "wider" types. */ -import scala.language.implicitConversions""" - - def implicitCoercions: List[String] = { - val coercions = this match { - case B => companionCoercions(S, I, L, F, D) - case S | C => companionCoercions(I, L, F, D) - case I => companionCoercions(L, F, D) - case L => companionCoercions(F, D) - case F => companionCoercions(D) - case _ => Nil - } - if (coercions.isEmpty) Nil - else coercionComment.lines.toList ++ coercions - } - - def isCardinal: Boolean = isIntegerType(this) - def unaryOps = { - val ops = List( - Op("+", "/** Returns this value, unmodified. */"), - Op("-", "/** Returns the negation of this value. */")) - - if(isCardinal) - Op("~", "/**\n" + - " * Returns the bitwise negation of this value.\n" + - " * @example {{{\n" + - " * ~5 == -6\n" + - " * // in binary: ~00000101 ==\n" + - " * // 11111010\n" + - " * }}}\n" + - " */") :: ops - else ops - } - - def bitwiseOps = - if (isCardinal) - List( - Op("|", "/**\n" + - " * Returns the bitwise OR of this value and `x`.\n" + - " * @example {{{\n" + - " * (0xf0 | 0xaa) == 0xfa\n" + - " * // in binary: 11110000\n" + - " * // | 10101010\n" + - " * // --------\n" + - " * // 11111010\n" + - " * }}}\n" + - " */"), - Op("&", "/**\n" + - " * Returns the bitwise AND of this value and `x`.\n" + - " * @example {{{\n" + - " * (0xf0 & 0xaa) == 0xa0\n" + - " * // in binary: 11110000\n" + - " * // & 10101010\n" + - " * // --------\n" + - " * // 10100000\n" + - " * }}}\n" + - " */"), - Op("^", "/**\n" + - " * Returns the bitwise XOR of this value and `x`.\n" + - " * @example {{{\n" + - " * (0xf0 ^ 0xaa) == 0x5a\n" + - " * // in binary: 11110000\n" + - " * // ^ 10101010\n" + - " * // --------\n" + - " * // 01011010\n" + - " * }}}\n" + - " */")) - else Nil - - def shiftOps = - if (isCardinal) - List( - Op("<<", "/**\n" + - " * Returns this value bit-shifted left by the specified number of bits,\n" + - " * filling in the new right bits with zeroes.\n" + - " * @example {{{ 6 << 3 == 48 // in binary: 0110 << 3 == 0110000 }}}\n" + - " */"), - - Op(">>>", "/**\n" + - " * Returns this value bit-shifted right by the specified number of bits,\n" + - " * filling the new left bits with zeroes.\n" + - " * @example {{{ 21 >>> 3 == 2 // in binary: 010101 >>> 3 == 010 }}}\n" + - " * @example {{{\n" + - " * -21 >>> 3 == 536870909\n" + - " * // in binary: 11111111 11111111 11111111 11101011 >>> 3 ==\n" + - " * // 00011111 11111111 11111111 11111101\n" + - " * }}}\n" + - " */"), - - Op(">>", "/**\n" + - " * Returns this value bit-shifted right by the specified number of bits,\n" + - " * filling in the left bits with the same value as the left-most bit of this.\n" + - " * The effect of this is to retain the sign of the value.\n" + - " * @example {{{\n" + - " * -21 >> 3 == -3\n" + - " * // in binary: 11111111 11111111 11111111 11101011 >> 3 ==\n" + - " * // 11111111 11111111 11111111 11111101\n" + - " * }}}\n" + - " */")) - else Nil - - def comparisonOps = List( - Op("==", "/** Returns `true` if this value is equal to x, `false` otherwise. */"), - Op("!=", "/** Returns `true` if this value is not equal to x, `false` otherwise. */"), - Op("<", "/** Returns `true` if this value is less than x, `false` otherwise. */"), - Op("<=", "/** Returns `true` if this value is less than or equal to x, `false` otherwise. */"), - Op(">", "/** Returns `true` if this value is greater than x, `false` otherwise. */"), - Op(">=", "/** Returns `true` if this value is greater than or equal to x, `false` otherwise. */")) - - def otherOps = List( - Op("+", "/** Returns the sum of this value and `x`. */"), - Op("-", "/** Returns the difference of this value and `x`. */"), - Op("*", "/** Returns the product of this value and `x`. */"), - Op("/", "/** Returns the quotient of this value and `x`. */"), - Op("%", "/** Returns the remainder of the division of this value by `x`. */")) - - // Given two numeric value types S and T , the operation type of S and T is defined as follows: - // If both S and T are subrange types then the operation type of S and T is Int. - // Otherwise the operation type of S and T is the larger of the two types wrt ranking. - // Given two numeric values v and w the operation type of v and w is the operation type - // of their run-time types. - def opType(that: AnyValNum): AnyValNum = { - val rank = IndexedSeq(I, L, F, D) - (rank indexOf this, rank indexOf that) match { - case (-1, -1) => I - case (r1, r2) => rank apply (r1 max r2) - } - } - - def mkCoercions = numeric map (x => "def to%s: %s".format(x, x)) - def mkUnaryOps = unaryOps map (x => "%s\n def unary_%s : %s".format(x.doc, x.op, this opType I)) - def mkStringOps = List("def +(x: String): String") - def mkShiftOps = ( - for (op <- shiftOps ; arg <- List(I, L)) yield - "%s\n def %s(x: %s): %s".format(op.doc, op.op, arg, this opType I) - ) - - def clumps: List[List[String]] = { - val xs1 = List(mkCoercions, mkUnaryOps, mkStringOps, mkShiftOps) map (xs => if (xs.isEmpty) xs else xs :+ "") - val xs2 = List( - mkBinOpsGroup(comparisonOps, numeric, _ => Z), - mkBinOpsGroup(bitwiseOps, cardinal, this opType _), - mkBinOpsGroup(otherOps, numeric, this opType _) - ) - xs1 ++ xs2 - } - def classLines = (clumps :+ commonClassLines).foldLeft(List[String]()) { - case (res, Nil) => res - case (res, lines) => - val xs = lines map { - case "" => "" - case s => interpolate(s) - } - res ++ xs - } - def objectLines = { - val comp = if (isCardinal) cardinalCompanion else floatingCompanion - interpolate(comp + allCompanions + "\n" + nonUnitCompanions).trim.lines.toList ++ (implicitCoercions map interpolate) - } - - /** Makes a set of binary operations based on the given set of ops, args, and resultFn. - * - * @param ops list of function names e.g. List(">>", "%") - * @param args list of types which should appear as arguments - * @param resultFn function which calculates return type based on arg type - * @return list of function definitions - */ - def mkBinOpsGroup(ops: List[Op], args: List[AnyValNum], resultFn: AnyValNum => AnyValRep): List[String] = ( - ops flatMap (op => - args.map(arg => - "%s\n def %s(x: %s): %s".format(op.doc, op.op, arg, resultFn(arg))) :+ "" - ) - ).toList - } - - sealed abstract class AnyValRep(val name: String, val repr: Option[String], val javaEquiv: String) { - def classLines: List[String] - def objectLines: List[String] - def commonClassLines = List( - "override def getClass(): Class[@name@] = null" - ) - - def lcname = name.toLowerCase - def boxedSimpleName = this match { - case C => "Character" - case I => "Integer" - case _ => name - } - def boxedName = this match { - case U => "scala.runtime.BoxedUnit" - case _ => "java.lang." + boxedSimpleName - } - def zeroRep = this match { - case L => "0L" - case F => "0.0f" - case D => "0.0d" - case _ => "0" - } - - def representation = repr.map(", a " + _).getOrElse("") - - def indent(s: String) = if (s == "") "" else " " + s - def indentN(s: String) = s.lines map indent mkString "\n" - - def boxUnboxImpls = Map( - "@boxRunTimeDoc@" -> """ - * Runtime implementation determined by `scala.runtime.BoxesRunTime.boxTo%s`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. - *""".format(boxedSimpleName), - "@boxImpl@" -> "%s.valueOf(x)".format(boxedName), - "@unboxRunTimeDoc@" -> """ - * Runtime implementation determined by `scala.runtime.BoxesRunTime.unboxTo%s`. See [[https://github.com/scala/scala src/library/scala/runtime/BoxesRunTime.java]]. - *""".format(name), - "@unboxImpl@" -> "x.asInstanceOf[%s].%sValue()".format(boxedName, lcname), - "@unboxDoc@" -> "the %s resulting from calling %sValue() on `x`".format(name, lcname) - ) - def interpolations = Map( - "@name@" -> name, - "@representation@" -> representation, - "@javaequiv@" -> javaEquiv, - "@boxed@" -> boxedName, - "@lcname@" -> lcname, - "@zero@" -> zeroRep - ) ++ boxUnboxImpls - - def interpolate(s: String): String = interpolations.foldLeft(s) { - case (str, (key, value)) => str.replaceAll(key, value) - } - def classDoc = interpolate(classDocTemplate) - def objectDoc = "" - def mkImports = "" - - def mkClass = assemble("final abstract class " + name + " private extends AnyVal", classLines) - def mkObject = assemble("object " + name + " extends AnyValCompanion", objectLines) - def make() = List[String]( - headerTemplate, - mkImports, - classDoc, - mkClass, - objectDoc, - mkObject - ) mkString "" - - def assemble(decl: String, lines: List[String]): String = { - val body = if (lines.isEmpty) " { }\n\n" else lines map indent mkString (" {\n", "\n", "\n}\n") - - decl + body + "\n" - } - override def toString = name - } -} - -trait AnyValTemplates { - def headerTemplate = """/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -// DO NOT EDIT, CHANGES WILL BE LOST -// This auto-generated code can be modified in scala.tools.cmd.gen. -// Afterwards, running tools/codegen-anyvals regenerates this source file. - -package scala - -""" - - def classDocTemplate = (""" -/** `@name@`@representation@ (equivalent to Java's `@javaequiv@` primitive type) is a - * subtype of [[scala.AnyVal]]. Instances of `@name@` are not - * represented by an object in the underlying runtime system. - * - * There is an implicit conversion from [[scala.@name@]] => [[scala.runtime.Rich@name@]] - * which provides useful non-primitive operations. - */ -""".trim + "\n") - - def allCompanions = """ -/** Transform a value type into a boxed reference type. - *@boxRunTimeDoc@ - * @param x the @name@ to be boxed - * @return a @boxed@ offering `x` as its underlying value. - */ -def box(x: @name@): @boxed@ = @boxImpl@ - -/** Transform a boxed type into a value type. Note that this - * method is not typesafe: it accepts any Object, but will throw - * an exception if the argument is not a @boxed@. - *@unboxRunTimeDoc@ - * @param x the @boxed@ to be unboxed. - * @throws ClassCastException if the argument is not a @boxed@ - * @return @unboxDoc@ - */ -def unbox(x: java.lang.Object): @name@ = @unboxImpl@ - -/** The String representation of the scala.@name@ companion object. */ -override def toString = "object scala.@name@" -""" - - def nonUnitCompanions = "" // todo - - def cardinalCompanion = """ -/** The smallest value representable as a @name@. */ -final val MinValue = @boxed@.MIN_VALUE - -/** The largest value representable as a @name@. */ -final val MaxValue = @boxed@.MAX_VALUE -""" - - def floatingCompanion = """ -/** The smallest positive value greater than @zero@ which is - * representable as a @name@. - */ -final val MinPositiveValue = @boxed@.MIN_VALUE -final val NaN = @boxed@.NaN -final val PositiveInfinity = @boxed@.POSITIVE_INFINITY -final val NegativeInfinity = @boxed@.NEGATIVE_INFINITY - -/** The negative number with the greatest (finite) absolute value which is representable - * by a @name@. Note that it differs from [[java.lang.@name@.MIN_VALUE]], which - * is the smallest positive value representable by a @name@. In Scala that number - * is called @name@.MinPositiveValue. - */ -final val MinValue = -@boxed@.MAX_VALUE - -/** The largest finite positive number representable as a @name@. */ -final val MaxValue = @boxed@.MAX_VALUE -""" -} - -class AnyVals extends AnyValReps with AnyValTemplates { - object B extends AnyValNum("Byte", Some("8-bit signed integer"), "byte") - object S extends AnyValNum("Short", Some("16-bit signed integer"), "short") - object C extends AnyValNum("Char", Some("16-bit unsigned integer"), "char") - object I extends AnyValNum("Int", Some("32-bit signed integer"), "int") - object L extends AnyValNum("Long", Some("64-bit signed integer"), "long") - object F extends AnyValNum("Float", Some("32-bit IEEE-754 floating point number"), "float") - object D extends AnyValNum("Double", Some("64-bit IEEE-754 floating point number"), "double") - object Z extends AnyValRep("Boolean", None, "boolean") { - def classLines = """ -/** Negates a Boolean expression. - * - * - `!a` results in `false` if and only if `a` evaluates to `true` and - * - `!a` results in `true` if and only if `a` evaluates to `false`. - * - * @return the negated expression - */ -def unary_! : Boolean - -/** Compares two Boolean expressions and returns `true` if they evaluate to the same value. - * - * `a == b` returns `true` if and only if - * - `a` and `b` are `true` or - * - `a` and `b` are `false`. - */ -def ==(x: Boolean): Boolean - -/** - * Compares two Boolean expressions and returns `true` if they evaluate to a different value. - * - * `a != b` returns `true` if and only if - * - `a` is `true` and `b` is `false` or - * - `a` is `false` and `b` is `true`. - */ -def !=(x: Boolean): Boolean - -/** Compares two Boolean expressions and returns `true` if one or both of them evaluate to true. - * - * `a || b` returns `true` if and only if - * - `a` is `true` or - * - `b` is `true` or - * - `a` and `b` are `true`. - * - * @note This method uses 'short-circuit' evaluation and - * behaves as if it was declared as `def ||(x: => Boolean): Boolean`. - * If `a` evaluates to `true`, `true` is returned without evaluating `b`. - */ -def ||(x: Boolean): Boolean - -/** Compares two Boolean expressions and returns `true` if both of them evaluate to true. - * - * `a && b` returns `true` if and only if - * - `a` and `b` are `true`. - * - * @note This method uses 'short-circuit' evaluation and - * behaves as if it was declared as `def &&(x: => Boolean): Boolean`. - * If `a` evaluates to `false`, `false` is returned without evaluating `b`. - */ -def &&(x: Boolean): Boolean - -// Compiler won't build with these seemingly more accurate signatures -// def ||(x: => Boolean): Boolean -// def &&(x: => Boolean): Boolean - -/** Compares two Boolean expressions and returns `true` if one or both of them evaluate to true. - * - * `a | b` returns `true` if and only if - * - `a` is `true` or - * - `b` is `true` or - * - `a` and `b` are `true`. - * - * @note This method evaluates both `a` and `b`, even if the result is already determined after evaluating `a`. - */ -def |(x: Boolean): Boolean - -/** Compares two Boolean expressions and returns `true` if both of them evaluate to true. - * - * `a & b` returns `true` if and only if - * - `a` and `b` are `true`. - * - * @note This method evaluates both `a` and `b`, even if the result is already determined after evaluating `a`. - */ -def &(x: Boolean): Boolean - -/** Compares two Boolean expressions and returns `true` if they evaluate to a different value. - * - * `a ^ b` returns `true` if and only if - * - `a` is `true` and `b` is `false` or - * - `a` is `false` and `b` is `true`. - */ -def ^(x: Boolean): Boolean - -override def getClass(): Class[Boolean] = null - """.trim.lines.toList - - def objectLines = interpolate(allCompanions + "\n" + nonUnitCompanions).lines.toList - } - object U extends AnyValRep("Unit", None, "void") { - override def classDoc = """ -/** `Unit` is a subtype of [[scala.AnyVal]]. There is only one value of type - * `Unit`, `()`, and it is not represented by any object in the underlying - * runtime system. A method with return type `Unit` is analogous to a Java - * method which is declared `void`. - */ -""" - def classLines = List( - """override def getClass(): Class[Unit] = null""" - ) - def objectLines = interpolate(allCompanions).lines.toList - - override def boxUnboxImpls = Map( - "@boxRunTimeDoc@" -> "", - "@boxImpl@" -> "scala.runtime.BoxedUnit.UNIT", - "@unboxRunTimeDoc@" -> "", - "@unboxImpl@" -> "()", - "@unboxDoc@" -> "the Unit value ()" - ) - } - - def isSubrangeType = Set(B, S, C) - def isIntegerType = Set(B, S, C, I, L) - def isFloatingType = Set(F, D) - def isWideType = Set(L, D) - - def cardinal = numeric filter isIntegerType - def numeric = List(B, S, C, I, L, F, D) - def values = List(U, Z) ++ numeric - - def make() = values map (x => (x.name, x.make())) -} diff --git a/src/compiler/scala/tools/cmd/gen/Codegen.scala b/src/compiler/scala/tools/cmd/gen/Codegen.scala deleted file mode 100644 index c3aa527ef2..0000000000 --- a/src/compiler/scala/tools/cmd/gen/Codegen.scala +++ /dev/null @@ -1,39 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.cmd -package gen - -class Codegen(args: List[String]) extends { - val parsed = CodegenSpec(args: _*) -} with CodegenSpec with Instance - -object Codegen { - def echo(msg: String) = Console println msg - - def main(args0: Array[String]): Unit = { - val runner = new Codegen(args0.toList) - import runner._ - - if (args0.isEmpty) - return println (CodegenSpec.helpMsg) - - val out = outDir getOrElse { return println("--out is required.") } - val all = genall || !anyvals - - echo("Generating sources into " + out) - - if (anyvals || all) { - val av = new AnyVals { } - - av.make() foreach { case (name, code ) => - val file = (out / (name + ".scala")).toFile - echo("Writing: " + file) - file writeAll code - } - } - } -} - diff --git a/src/compiler/scala/tools/cmd/gen/CodegenSpec.scala b/src/compiler/scala/tools/cmd/gen/CodegenSpec.scala deleted file mode 100644 index 4b4a1e482d..0000000000 --- a/src/compiler/scala/tools/cmd/gen/CodegenSpec.scala +++ /dev/null @@ -1,25 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.cmd -package gen - -import FromString.ExistingDir - -trait CodegenSpec extends Spec with Meta.StdOpts with Interpolation { - def referenceSpec = CodegenSpec - def programInfo = Spec.Info("codegen", "", "scala.tools.cmd.gen.Codegen") - - help("Usage: codegen [<options>]") - - val outDir = "out" / "directory for generated files" --^ ExistingDir - val anyvals = "anyvals" / "generate sources for AnyVal types" --? - val genall = "all" / "generate sources for everything" --? -} - -object CodegenSpec extends CodegenSpec with Reference { - type ThisCommandLine = CommandLine - def creator(args: List[String]): ThisCommandLine = new CommandLine(CodegenSpec, args) -} diff --git a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala index 2584054686..24496fa013 100644 --- a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala +++ b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala @@ -87,7 +87,11 @@ A file argument will be run as a scala script unless it contains only self-contained compilation units (classes and objects) and exactly one runnable main method. In that case the file will be compiled and the main method invoked. This provides a bridge between scripts and standard -scala source.%n""" +scala source. + +When running a script or using -e, an already running compilation daemon +(fsc) is used, or a new one started on demand. The -nc option can be +used to prevent this.%n""" } object GenericRunnerCommand { diff --git a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala index e99cce9186..c82ed68da8 100644 --- a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala +++ b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala @@ -6,10 +6,10 @@ package scala.tools.nsc import java.net.URL -import scala.tools.util.PathResolverFactory +import scala.tools.util.PathResolver class GenericRunnerSettings(error: String => Unit) extends Settings(error) { - lazy val classpathURLs: Seq[URL] = PathResolverFactory.create(this).resultAsURLs + lazy val classpathURLs: Seq[URL] = new PathResolver(this).resultAsURLs val howtorun = ChoiceSetting( diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index c1d7176d0c..7417d9c09d 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -7,17 +7,17 @@ package scala package tools package nsc -import java.io.{ File, IOException, FileNotFoundException } +import java.io.{File, FileNotFoundException, IOException} import java.net.URL -import java.nio.charset.{ Charset, CharsetDecoder, IllegalCharsetNameException, UnsupportedCharsetException } -import scala.collection.{ mutable, immutable } -import io.{ SourceReader, AbstractFile, Path } +import java.nio.charset.{Charset, CharsetDecoder, IllegalCharsetNameException, UnsupportedCharsetException} +import scala.collection.{immutable, mutable} +import io.{AbstractFile, Path, SourceReader} import reporters.Reporter -import util.{ ClassFileLookup, ClassPath, MergedClassPath, StatisticsInfo, returning } +import util.{ClassPath, StatisticsInfo, returning} import scala.reflect.ClassTag -import scala.reflect.internal.util.{ ScalaClassLoader, SourceFile, NoSourceFile, BatchSourceFile, ScriptSourceFile } +import scala.reflect.internal.util.{BatchSourceFile, NoSourceFile, ScalaClassLoader, ScriptSourceFile, SourceFile} import scala.reflect.internal.pickling.PickleBuffer -import symtab.{ Flags, SymbolTable, SymbolTrackers } +import symtab.{Flags, SymbolTable, SymbolTrackers} import symtab.classfile.Pickler import plugins.Plugins import ast._ @@ -25,12 +25,11 @@ import ast.parser._ import typechecker._ import transform.patmat.PatternMatching import transform._ -import backend.{ ScalaPrimitives, JavaPlatform } +import backend.{JavaPlatform, ScalaPrimitives} import backend.jvm.GenBCode import scala.language.postfixOps import scala.tools.nsc.ast.{TreeGen => AstTreeGen} -import scala.tools.nsc.classpath.FlatClassPath -import scala.tools.nsc.settings.ClassPathRepresentationType +import scala.tools.nsc.classpath._ class Global(var currentSettings: Settings, var reporter: Reporter) extends SymbolTable @@ -54,12 +53,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) class GlobalMirror extends Roots(NoSymbol) { val universe: self.type = self - def rootLoader: LazyType = { - settings.YclasspathImpl.value match { - case ClassPathRepresentationType.Flat => new loaders.PackageLoaderUsingFlatClassPath(FlatClassPath.RootPackage, flatClassPath) - case ClassPathRepresentationType.Recursive => new loaders.PackageLoader(recursiveClassPath) - } - } + def rootLoader: LazyType = new loaders.PackageLoader(ClassPath.RootPackage, classPath) override def toString = "compiler mirror" } implicit val MirrorTag: ClassTag[Mirror] = ClassTag[Mirror](classOf[GlobalMirror]) @@ -102,17 +96,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) type ThisPlatform = JavaPlatform { val global: Global.this.type } lazy val platform: ThisPlatform = new GlobalPlatform - type PlatformClassPath = ClassPath[AbstractFile] - type OptClassPath = Option[PlatformClassPath] - - def classPath: ClassFileLookup[AbstractFile] = settings.YclasspathImpl.value match { - case ClassPathRepresentationType.Flat => flatClassPath - case ClassPathRepresentationType.Recursive => recursiveClassPath - } - - private def recursiveClassPath: ClassPath[AbstractFile] = platform.classPath - - private def flatClassPath: FlatClassPath = platform.flatClassPath + def classPath: ClassPath = platform.classPath // sub-components -------------------------------------------------- @@ -286,7 +270,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // Over 200 closure objects are eliminated by inlining this. @inline final def log(msg: => AnyRef) { if (shouldLogAtThisPhase) - inform("[log %s%s] %s".format(globalPhase, atPhaseStackMessage, msg)) + inform(s"[log $globalPhase$atPhaseStackMessage] $msg") } @inline final override def debuglog(msg: => String) { @@ -308,10 +292,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) try Some(Charset.forName(name)) catch { case _: IllegalCharsetNameException => - globalError("illegal charset name '" + name + "'") + globalError(s"illegal charset name '$name'") None case _: UnsupportedCharsetException => - globalError("unsupported charset '" + name + "'") + globalError(s"unsupported charset '$name'") None } @@ -397,15 +381,18 @@ class Global(var currentSettings: Settings, var reporter: Reporter) if (settings.debug && (settings.verbose || currentRun.size < 5)) inform("[running phase " + name + " on " + unit + "]") + if (!cancelled(unit)) { + currentRun.informUnitStarting(this, unit) + try withCurrentUnitNoLog(unit)(task) + finally currentRun.advanceUnit() + } + } + final def withCurrentUnitNoLog(unit: CompilationUnit)(task: => Unit) { val unit0 = currentUnit try { currentRun.currentUnit = unit - if (!cancelled(unit)) { - currentRun.informUnitStarting(this, unit) - task - } - currentRun.advanceUnit() + task } finally { //assert(currentUnit == unit) currentRun.currentUnit = unit0 @@ -771,12 +758,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** Extend classpath of `platform` and rescan updated packages. */ def extendCompilerClassPath(urls: URL*): Unit = { - if (settings.YclasspathImpl.value == ClassPathRepresentationType.Flat) - throw new UnsupportedOperationException("Flat classpath doesn't support extending the compiler classpath") - - val newClassPath = platform.classPath.mergeUrlsIntoClassPath(urls: _*) + val urlClasspaths = urls.map(u => ClassPathFactory.newClassPath(AbstractFile.getURL(u), settings)) + val newClassPath = AggregateClassPath.createAggregate(platform.classPath +: urlClasspaths : _*) platform.currentClassPath = Some(newClassPath) - // Reload all specified jars into this compiler instance invalidateClassPathEntries(urls.map(_.getPath): _*) } @@ -809,43 +793,54 @@ class Global(var currentSettings: Settings, var reporter: Reporter) * entries on the classpath. */ def invalidateClassPathEntries(paths: String*): Unit = { - if (settings.YclasspathImpl.value == ClassPathRepresentationType.Flat) - throw new UnsupportedOperationException("Flat classpath doesn't support the classpath invalidation") - - implicit object ClassPathOrdering extends Ordering[PlatformClassPath] { - def compare(a:PlatformClassPath, b:PlatformClassPath) = a.asClassPathString compare b.asClassPathString + implicit object ClassPathOrdering extends Ordering[ClassPath] { + def compare(a: ClassPath, b: ClassPath): Int = a.asClassPathString compareTo b.asClassPathString } val invalidated, failed = new mutable.ListBuffer[ClassSymbol] - classPath match { - case cp: MergedClassPath[_] => - def assoc(path: String): List[(PlatformClassPath, PlatformClassPath)] = { - val dir = AbstractFile.getDirectory(path) - val canonical = dir.canonicalPath - def matchesCanonical(e: ClassPath[_]) = e.origin match { - case Some(opath) => - AbstractFile.getDirectory(opath).canonicalPath == canonical - case None => - false - } - cp.entries find matchesCanonical match { - case Some(oldEntry) => - List(oldEntry -> cp.context.newClassPath(dir)) - case None => - error(s"Error adding entry to classpath. During invalidation, no entry named $path in classpath $classPath") - List() - } - } - val subst = immutable.TreeMap(paths flatMap assoc: _*) - if (subst.nonEmpty) { - platform updateClassPath subst - informProgress(s"classpath updated on entries [${subst.keys mkString ","}]") - def mkClassPath(elems: Iterable[PlatformClassPath]): PlatformClassPath = - if (elems.size == 1) elems.head - else new MergedClassPath(elems, recursiveClassPath.context) - val oldEntries = mkClassPath(subst.keys) - val newEntries = mkClassPath(subst.values) - mergeNewEntries(newEntries, RootClass, Some(recursiveClassPath), Some(oldEntries), invalidated, failed) - } + + def assoc(path: String): Option[(ClassPath, ClassPath)] = { + def origin(lookup: ClassPath): Option[String] = lookup match { + case cp: JFileDirectoryLookup[_] => Some(cp.dir.getPath) + case cp: ZipArchiveFileLookup[_] => Some(cp.zipFile.getPath) + case _ => None + } + + def entries(lookup: ClassPath): Seq[ClassPath] = lookup match { + case cp: AggregateClassPath => cp.aggregates + case cp: ClassPath => Seq(cp) + } + + val dir = AbstractFile.getDirectory(path) // if path is a `jar`, this is a FileZipArchive (isDirectory is true) + val canonical = dir.canonicalPath // this is the canonical path of the .jar + def matchesCanonical(e: ClassPath) = origin(e) match { + case Some(opath) => + AbstractFile.getDirectory(opath).canonicalPath == canonical + case None => + false + } + entries(classPath) find matchesCanonical match { + case Some(oldEntry) => + Some(oldEntry -> ClassPathFactory.newClassPath(dir, settings)) + case None => + error(s"Error adding entry to classpath. During invalidation, no entry named $path in classpath $classPath") + None + } + } + val subst = immutable.TreeMap(paths flatMap assoc: _*) + if (subst.nonEmpty) { + platform updateClassPath subst + informProgress(s"classpath updated on entries [${subst.keys mkString ","}]") + def mkClassPath(elems: Iterable[ClassPath]): ClassPath = + if (elems.size == 1) elems.head + else AggregateClassPath.createAggregate(elems.toSeq: _*) + val oldEntries = mkClassPath(subst.keys) + val newEntries = mkClassPath(subst.values) + classPath match { + case cp: ClassPath => mergeNewEntries( + RootClass, "", + oldEntries, newEntries, cp, + invalidated, failed) + } } def show(msg: String, syms: scala.collection.Traversable[Symbol]) = if (syms.nonEmpty) @@ -854,66 +849,61 @@ class Global(var currentSettings: Settings, var reporter: Reporter) show("could not invalidate system packages", failed) } - /** Merges new classpath entries into the symbol table - * - * @param newEntries The new classpath entries - * @param root The root symbol to be resynced (a package class) - * @param allEntries Optionally, the corresponding package in the complete current classpath - * @param oldEntries Optionally, the corresponding package in the old classpath entries - * @param invalidated A listbuffer collecting the invalidated package classes - * @param failed A listbuffer collecting system package classes which could not be invalidated - * - * The merging strategy is determined by the absence or presence of classes and packages. + /** + * Merges new classpath entries into the symbol table * - * If either oldEntries or newEntries contains classes, root is invalidated provided that a corresponding package - * exists in allEntries. Otherwise it is removed. - * Otherwise, the action is determined by the following matrix, with columns: + * @param packageClass The ClassSymbol for the package being updated + * @param fullPackageName The full name of the package being updated + * @param oldEntries The classpath that was removed, it is no longer part of fullClasspath + * @param newEntries The classpath that was added, it is already part of fullClasspath + * @param fullClasspath The full classpath, equivalent to global.classPath + * @param invalidated A ListBuffer collecting the invalidated package classes + * @param failed A ListBuffer collecting system package classes which could not be invalidated * - * old sym action - * + + recurse into all child packages of newEntries - * - + invalidate root - * - - create and enter root + * If either oldEntries or newEntries contains classes in the current package, the package symbol + * is re-initialized to a fresh package loader, provided that a corresponding package exists in + * fullClasspath. Otherwise it is removed. * - * Here, old means classpath, and sym means symboltable. + is presence of an entry in its column, - is absence. + * Otherwise, sub-packages in newEntries are looked up in the symbol table (created if + * non-existent) and the merge function is called recursively. */ - private def mergeNewEntries(newEntries: PlatformClassPath, root: ClassSymbol, - allEntries: OptClassPath, oldEntries: OptClassPath, - invalidated: mutable.ListBuffer[ClassSymbol], failed: mutable.ListBuffer[ClassSymbol]) { - ifDebug(informProgress(s"syncing $root, $oldEntries -> $newEntries")) - - val getName: ClassPath[AbstractFile] => String = (_.name) - def hasClasses(cp: OptClassPath) = cp.isDefined && cp.get.classes.nonEmpty - def invalidateOrRemove(root: ClassSymbol) = { - allEntries match { - case Some(cp) => root setInfo new loaders.PackageLoader(cp) - case None => root.owner.info.decls unlink root.sourceModule - } - invalidated += root + private def mergeNewEntries(packageClass: ClassSymbol, fullPackageName: String, + oldEntries: ClassPath, newEntries: ClassPath, fullClasspath: ClassPath, + invalidated: mutable.ListBuffer[ClassSymbol], failed: mutable.ListBuffer[ClassSymbol]): Unit = { + ifDebug(informProgress(s"syncing $packageClass, $oldEntries -> $newEntries")) + + def packageExists(cp: ClassPath): Boolean = { + val (parent, _) = PackageNameUtils.separatePkgAndClassNames(fullPackageName) + cp.packages(parent).exists(_.name == fullPackageName) } - def subPackage(cp: PlatformClassPath, name: String): OptClassPath = - cp.packages find (cp1 => getName(cp1) == name) - val classesFound = hasClasses(oldEntries) || newEntries.classes.nonEmpty - if (classesFound && !isSystemPackageClass(root)) { - invalidateOrRemove(root) - } else { - if (classesFound) { - if (root.isRoot) invalidateOrRemove(EmptyPackageClass) - else failed += root - } - if (!oldEntries.isDefined) invalidateOrRemove(root) + def invalidateOrRemove(pkg: ClassSymbol) = { + if (packageExists(fullClasspath)) + pkg setInfo new loaders.PackageLoader(fullPackageName, fullClasspath) else - for (pstr <- newEntries.packages.map(getName)) { - val pname = newTermName(pstr) - val pkg = (root.info decl pname) orElse { - // package does not exist in symbol table, create symbol to track it - assert(!subPackage(oldEntries.get, pstr).isDefined) - loaders.enterPackage(root, pstr, new loaders.PackageLoader(allEntries.get)) - } - mergeNewEntries(subPackage(newEntries, pstr).get, pkg.moduleClass.asClass, - subPackage(allEntries.get, pstr), subPackage(oldEntries.get, pstr), - invalidated, failed) + pkg.owner.info.decls unlink pkg.sourceModule + invalidated += pkg + } + + val classesFound = oldEntries.classes(fullPackageName).nonEmpty || newEntries.classes(fullPackageName).nonEmpty + if (classesFound) { + // if the package contains classes either in oldEntries or newEntries, the package is invalidated (or removed if there are no more classes in it) + if (!isSystemPackageClass(packageClass)) invalidateOrRemove(packageClass) + else if (packageClass.isRoot) invalidateOrRemove(EmptyPackageClass) + else failed += packageClass + } else { + // no new or removed classes in the current package + for (p <- newEntries.packages(fullPackageName)) { + val (_, subPackageName) = PackageNameUtils.separatePkgAndClassNames(p.name) + val subPackage = packageClass.info.decl(newTermName(subPackageName)) orElse { + // package does not exist in symbol table, create a new symbol + loaders.enterPackage(packageClass, subPackageName, new loaders.PackageLoader(p.name, fullClasspath)) } + mergeNewEntries( + subPackage.moduleClass.asClass, p.name, + oldEntries, newEntries, fullClasspath, + invalidated, failed) + } } } diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala index e11cba466e..5bdbf4bb6a 100644 --- a/src/compiler/scala/tools/nsc/Reporting.scala +++ b/src/compiler/scala/tools/nsc/Reporting.scala @@ -26,27 +26,30 @@ trait Reporting extends scala.reflect.internal.Reporting { self: ast.Positions w protected def PerRunReporting = new PerRunReporting class PerRunReporting extends PerRunReportingBase { /** Collects for certain classes of warnings during this run. */ - private class ConditionalWarning(what: String, option: Settings#BooleanSetting)(reRunFlag: String = option.name) { + private class ConditionalWarning(what: String, doReport: () => Boolean, setting: Settings#Setting) { + def this(what: String, booleanSetting: Settings#BooleanSetting) { + this(what, () => booleanSetting, booleanSetting) + } val warnings = mutable.LinkedHashMap[Position, String]() def warn(pos: Position, msg: String) = - if (option) reporter.warning(pos, msg) + if (doReport()) reporter.warning(pos, msg) else if (!(warnings contains pos)) warnings += ((pos, msg)) def summarize() = - if (warnings.nonEmpty && (option.isDefault || option)) { + if (warnings.nonEmpty && (setting.isDefault || doReport())) { val numWarnings = warnings.size val warningVerb = if (numWarnings == 1) "was" else "were" val warningCount = countElementsAsString(numWarnings, s"$what warning") - reporter.warning(NoPosition, s"there $warningVerb $warningCount; re-run with $reRunFlag for details") + reporter.warning(NoPosition, s"there $warningVerb $warningCount; re-run with ${setting.name} for details") } } // This change broke sbt; I gave it the thrilling name of uncheckedWarnings0 so // as to recover uncheckedWarnings for its ever-fragile compiler interface. - 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.isBCodeActive) settings.YoptWarnings.name else settings.YinlinerWarnings.name) + 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.YoptWarningsSummaryOnly, settings.YoptWarnings) 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/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala index bf93ad30bc..1f66657d8d 100644 --- a/src/compiler/scala/tools/nsc/ScriptRunner.scala +++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala @@ -8,10 +8,8 @@ package tools.nsc import io.{ AbstractFile, Directory, File, Path } import java.io.IOException -import scala.tools.nsc.classpath.DirectoryFlatClassPath +import scala.tools.nsc.classpath.DirectoryClassPath import scala.tools.nsc.reporters.{Reporter,ConsoleReporter} -import scala.tools.nsc.settings.ClassPathRepresentationType -import scala.tools.nsc.util.ClassPath.DefaultJavaContext import util.Exceptional.unwrap /** An object that runs Scala code in script files. @@ -115,10 +113,7 @@ class ScriptRunner extends HasCompileSocket { } def hasClassToRun(d: Directory): Boolean = { - val cp = settings.YclasspathImpl.value match { - case ClassPathRepresentationType.Recursive => DefaultJavaContext.newClassPath(AbstractFile.getDirectory(d)) - case ClassPathRepresentationType.Flat => DirectoryFlatClassPath(d.jfile) - } + val cp = DirectoryClassPath(d.jfile) cp.findClass(mainClass).isDefined } diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index 3167f87383..0786ceb7c2 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -145,6 +145,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { override def mkCast(tree: Tree, pt: Type): Tree = { debuglog("casting " + tree + ":" + tree.tpe + " to " + pt + " at phase: " + phase) assert(!tree.tpe.isInstanceOf[MethodType], tree) + assert(!pt.isInstanceOf[MethodType], tree) assert(pt eq pt.normalize, tree +" : "+ debugString(pt) +" ~>"+ debugString(pt.normalize)) atPos(tree.pos) { mkAsInstanceOf(tree, pt, any = !phase.next.erasedTypes, wrapInApply = isAtPhaseAfter(currentRun.uncurryPhase)) @@ -260,43 +261,77 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { mkNew(Nil, noSelfType, stats1, NoPosition, NoPosition) } - /** - * Create a method based on a Function - * - * Used both to under `-Ydelambdafy:method` create a lifted function and - * under `-Ydelambdafy:inline` to create the apply method on the anonymous - * class. - * - * It creates a method definition with value params cloned from the - * original lambda. Then it calls a supplied function to create - * the body and types the result. Finally - * everything is wrapped up in a DefDef - * - * @param owner The owner for the new method - * @param name name for the new method - * @param additionalFlags flags to be put on the method in addition to FINAL - */ - def mkMethodFromFunction(localTyper: analyzer.Typer) - (fun: Function, owner: Symbol, name: TermName, additionalFlags: FlagSet = NoFlags) = { - val funParams = fun.vparams map (_.symbol) - val formals :+ restpe = fun.tpe.typeArgs + // Construct a method to implement `fun`'s single abstract method (`apply`, when `fun.tpe` is a built-in function type) + def mkMethodFromFunction(localTyper: analyzer.Typer)(owner: Symbol, fun: Function) = { + // TODO: treat FunctionN like any other SAM -- drop `&& !isFunctionType(fun.tpe)` + val sam = if (!isFunctionType(fun.tpe)) samOf(fun.tpe) else NoSymbol + if (!sam.exists) mkMethodForFunctionBody(localTyper)(owner, fun, nme.apply)() + else { + val samMethType = fun.tpe memberInfo sam + mkMethodForFunctionBody(localTyper)(owner, fun, sam.name.toTermName)(methParamProtos = samMethType.params, resTp = samMethType.resultType) + } + } + + // used to create the lifted method that holds a function's body + def mkLiftedFunctionBodyMethod(localTyper: analyzer.Typer)(owner: Symbol, fun: Function) = + mkMethodForFunctionBody(localTyper)(owner, fun, nme.ANON_FUN_NAME)(additionalFlags = ARTIFACT) + + + /** + * Lift a Function's body to a method. For use during Uncurry, where Function nodes have type FunctionN[T1, ..., Tn, R] + * + * It creates a method definition with value params derived from the original lambda + * or `methParamProtos` (used to create the correct override for sam methods). + * + * Replace the `fun.vparams` symbols by the newly created method params, + * changes owner of `fun.body` from `fun.symbol` to resulting method's symbol. + * + * @param owner The owner for the new method + * @param fun the function to take the body from + * @param name name for the new method + * @param additionalFlags flags to be put on the method in addition to FINAL + */ + private def mkMethodForFunctionBody(localTyper: analyzer.Typer) + (owner: Symbol, fun: Function, name: TermName) + (methParamProtos: List[Symbol] = fun.vparams.map(_.symbol), + resTp: Type = functionResultType(fun.tpe), + additionalFlags: FlagSet = NoFlags): DefDef = { val methSym = owner.newMethod(name, fun.pos, FINAL | additionalFlags) + // for sams, methParamProtos is the parameter symbols for the sam's method, so that we generate the correct override (based on parmeter types) + val methParamSyms = methParamProtos.map { param => methSym.newSyntheticValueParam(param.tpe, param.name.toTermName) } + methSym setInfo MethodType(methParamSyms, resTp) - val paramSyms = map2(formals, fun.vparams) { - (tp, vparam) => methSym.newSyntheticValueParam(tp, vparam.name) - } + // we must rewire reference to the function's param symbols -- and not methParamProtos -- to methParamSyms + val useMethodParams = new TreeSymSubstituter(fun.vparams.map(_.symbol), methParamSyms) + // we're now owned by the method that holds the body, and not the function + val moveToMethod = new ChangeOwnerTraverser(fun.symbol, methSym) - methSym setInfo MethodType(paramSyms, restpe.deconst) + newDefDef(methSym, moveToMethod(useMethodParams(fun.body)))(tpt = TypeTree(resTp)) + } + + // TODO: the rewrite to AbstractFunction is superfluous once we compile FunctionN to a SAM type (aka functional interface) + def functionClassType(fun: Function): Type = + if (isFunctionType(fun.tpe)) abstractFunctionType(fun.vparams.map(_.symbol.tpe), fun.body.tpe.deconst) + else fun.tpe - fun.body.substituteSymbols(funParams, paramSyms) - fun.body changeOwner (fun.symbol -> methSym) + def expandFunction(localTyper: analyzer.Typer)(fun: Function, inConstructorFlag: Long): Tree = { + val parents = addSerializable(functionClassType(fun)) + val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation SerialVersionUIDAnnotation - val methDef = DefDef(methSym, fun.body) + // The original owner is used in the backend for the EnclosingMethod attribute. If fun is + // nested in a value-class method, its owner was already changed to the extension method. + // Saving the original owner allows getting the source structure from the class symbol. + defineOriginalOwner(anonClass, fun.symbol.originalOwner) + anonClass setInfo ClassInfoType(parents, newScope, anonClass) - // Have to repack the type to avoid mismatches when existentials - // appear in the result - see SI-4869. - methDef.tpt setType localTyper.packedType(fun.body, methSym).deconst - methDef + val samDef = mkMethodFromFunction(localTyper)(anonClass, fun) + anonClass.info.decls enter samDef.symbol + + localTyper.typedPos(fun.pos) { + Block( + ClassDef(anonClass, NoMods, ListOfNil, List(samDef), fun.pos), + Typed(New(anonClass.tpe), TypeTree(fun.tpe))) + } } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 53cd2f6d59..0254141bfb 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1713,9 +1713,7 @@ self => } simpleExprRest(app, canApply = true) case USCORE => - atPos(t.pos.start, in.skipToken()) { - Typed(stripParens(t), Function(Nil, EmptyTree)) - } + atPos(t.pos.start, in.skipToken()) { makeMethodValue(stripParens(t)) } case _ => t } @@ -2811,11 +2809,6 @@ self => if (mods.isTrait) (Modifiers(Flags.TRAIT), List()) else (accessModifierOpt(), paramClauses(name, classContextBounds, ofCaseClass = mods.isCase)) var mods1 = mods - if (mods.isTrait) { - if (settings.YvirtClasses && in.token == SUBTYPE) mods1 |= Flags.DEFERRED - } else if (in.token == SUBTYPE) { - syntaxError("classes are not allowed to be virtual", skipIt = false) - } val template = templateOpt(mods1, name, constrMods withAnnotations constrAnnots, vparamss, tstart) val result = gen.mkClassDef(mods1, name, tparams, template) // Context bounds generate implicit parameters (part of the template) with types diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 473a40f42a..1e9a1762eb 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -35,6 +35,9 @@ abstract class TreeBuilder { def repeatedApplication(tpe: Tree): Tree = AppliedTypeTree(rootScalaDot(tpnme.REPEATED_PARAM_CLASS_NAME), List(tpe)) + // represents `expr _`, as specified in Method Values of spec/06-expressions.md + def makeMethodValue(expr: Tree): Tree = Typed(expr, Function(Nil, EmptyTree)) + def makeImportSelector(name: Name, nameOffset: Int): ImportSelector = ImportSelector(name, nameOffset, name, nameOffset) diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala index 16f086e9e7..dc63b335cc 100644 --- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala +++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala @@ -7,11 +7,9 @@ package scala.tools.nsc package backend import io.AbstractFile -import scala.tools.nsc.classpath.FlatClassPath -import scala.tools.nsc.settings.ClassPathRepresentationType -import scala.tools.nsc.util.{ ClassPath, DeltaClassPath, MergedClassPath } -import scala.tools.util.FlatClassPathResolver +import scala.tools.nsc.classpath.AggregateClassPath import scala.tools.util.PathResolver +import scala.tools.nsc.util.ClassPath trait JavaPlatform extends Platform { val global: Global @@ -19,27 +17,22 @@ trait JavaPlatform extends Platform { import global._ import definitions._ - private[nsc] var currentClassPath: Option[MergedClassPath[AbstractFile]] = None - - def classPath: ClassPath[AbstractFile] = { - assert(settings.YclasspathImpl.value == ClassPathRepresentationType.Recursive, - "To use recursive classpath representation you must enable it with -YclasspathImpl:recursive compiler option.") + private[nsc] var currentClassPath: Option[ClassPath] = None + private[nsc] def classPath: ClassPath = { if (currentClassPath.isEmpty) currentClassPath = Some(new PathResolver(settings).result) currentClassPath.get } - private[nsc] lazy val flatClassPath: FlatClassPath = { - assert(settings.YclasspathImpl.value == ClassPathRepresentationType.Flat, - "To use flat classpath representation you must enable it with -YclasspathImpl:flat compiler option.") + /** Update classpath with a substituted subentry */ + def updateClassPath(subst: Map[ClassPath, ClassPath]): Unit = global.classPath match { + case AggregateClassPath(entries) => + currentClassPath = Some(AggregateClassPath(entries map (e => subst.getOrElse(e, e)))) - new FlatClassPathResolver(settings).result + case cp: ClassPath => + currentClassPath = Some(subst.getOrElse(cp, cp)) } - /** Update classpath with a substituted subentry */ - def updateClassPath(subst: Map[ClassPath[AbstractFile], ClassPath[AbstractFile]]) = - currentClassPath = Some(new DeltaClassPath(currentClassPath.get, subst)) - def platformPhases = List( flatten, // get rid of inner classes genBCode // generate .class files diff --git a/src/compiler/scala/tools/nsc/backend/Platform.scala b/src/compiler/scala/tools/nsc/backend/Platform.scala index c3bc213be1..e464768bb3 100644 --- a/src/compiler/scala/tools/nsc/backend/Platform.scala +++ b/src/compiler/scala/tools/nsc/backend/Platform.scala @@ -6,9 +6,8 @@ package scala.tools.nsc package backend -import util.ClassPath import io.AbstractFile -import scala.tools.nsc.classpath.FlatClassPath +import scala.tools.nsc.util.ClassPath /** The platform dependent pieces of Global. */ @@ -16,14 +15,11 @@ trait Platform { val symbolTable: symtab.SymbolTable import symbolTable._ - /** The old, recursive implementation of compiler classpath. */ - def classPath: ClassPath[AbstractFile] - /** The new implementation of compiler classpath. */ - private[nsc] def flatClassPath: FlatClassPath + private[nsc] def classPath: ClassPath /** Update classpath with a substitution that maps entries to entries */ - def updateClassPath(subst: Map[ClassPath[AbstractFile], ClassPath[AbstractFile]]) + def updateClassPath(subst: Map[ClassPath, ClassPath]) /** Any platform-specific phases. */ def platformPhases: List[SubComponent] diff --git a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala index 9a53737554..00771b6b8c 100644 --- a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala +++ b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala @@ -7,7 +7,7 @@ package scala package tools.nsc package backend -import scala.collection.{ mutable, immutable } +import scala.collection.mutable /** Scala primitive operations are represented as methods in `Any` and * `AnyVal` subclasses. Here we demultiplex them by providing a mapping diff --git a/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala index 32f8c7826f..630b2b6c7f 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala @@ -9,8 +9,7 @@ import scala.tools.asm.tree.{InsnList, AbstractInsnNode, ClassNode, MethodNode} import java.io.{StringWriter, PrintWriter} import scala.tools.asm.util.{CheckClassAdapter, TraceClassVisitor, TraceMethodVisitor, Textifier} import scala.tools.asm.{ClassReader, ClassWriter, Attribute} -import scala.collection.convert.decorateAsScala._ -import scala.collection.convert.decorateAsJava._ +import scala.collection.JavaConverters._ import scala.tools.nsc.backend.jvm.analysis.InitialProducer import scala.tools.nsc.backend.jvm.opt.InlineInfoAttributePrototype diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index 8a90eb9780..5d152ef0e8 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -11,7 +11,6 @@ package jvm import scala.annotation.switch import scala.reflect.internal.Flags -import java.lang.invoke.LambdaMetafactory import scala.tools.asm import GenBCode._ @@ -35,14 +34,6 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { * Functionality to build the body of ASM MethodNode, except for `synchronized` and `try` expressions. */ abstract class PlainBodyBuilder(cunit: CompilationUnit) extends PlainSkelBuilder(cunit) { - /* If the selector type has a member with the right name, - * it is the host class; otherwise the symbol's owner. - */ - def findHostClass(selector: Type, sym: Symbol) = selector member sym.name match { - case NoSymbol => debuglog(s"Rejecting $selector as host class for $sym") ; sym.owner - case _ => selector.typeSymbol - } - /* ---------------- helper utils for generating methods and code ---------------- */ def emit(opc: Int) { mnode.visitInsn(opc) } @@ -70,12 +61,14 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { def genStat(tree: Tree) { lineNumber(tree) tree match { - case Assign(lhs @ Select(_, _), rhs) => + case Assign(lhs @ Select(qual, _), rhs) => val isStatic = lhs.symbol.isStaticMember if (!isStatic) { genLoadQualifier(lhs) } genLoad(rhs, symInfoTK(lhs.symbol)) lineNumber(tree) - fieldStore(lhs.symbol) + // receiverClass is used in the bytecode to access the field. using sym.owner may lead to IllegalAccessError, SI-4283 + val receiverClass = qual.tpe.typeSymbol + fieldStore(lhs.symbol, receiverClass) case Assign(lhs, rhs) => val s = lhs.symbol @@ -124,15 +117,16 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { // binary operation case rarg :: Nil => - resKind = tpeTK(larg).maxType(tpeTK(rarg)) - if (scalaPrimitives.isShiftOp(code) || scalaPrimitives.isBitwiseOp(code)) { + val isShiftOp = scalaPrimitives.isShiftOp(code) + resKind = tpeTK(larg).maxType(if (isShiftOp) INT else tpeTK(rarg)) + + if (isShiftOp || scalaPrimitives.isBitwiseOp(code)) { assert(resKind.isIntegralType || (resKind == BOOL), s"$resKind incompatible with arithmetic modulo operation.") } genLoad(larg, resKind) - genLoad(rarg, // check .NET size of shift arguments! - if (scalaPrimitives.isShiftOp(code)) INT else resKind) + genLoad(rarg, if (isShiftOp) INT else resKind) (code: @switch) match { case ADD => bc add resKind @@ -170,21 +164,13 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { genLoad(args.head, INT) generatedType = k.asArrayBType.componentType bc.aload(elementType) - } - else if (scalaPrimitives.isArraySet(code)) { - args match { - case a1 :: a2 :: Nil => - genLoad(a1, INT) - genLoad(a2) - // the following line should really be here, but because of bugs in erasure - // we pretend we generate whatever type is expected from us. - //generatedType = UNIT - bc.astore(elementType) - case _ => - abort(s"Too many arguments for array set operation: $tree") - } - } - else { + } else if (scalaPrimitives.isArraySet(code)) { + val List(a1, a2) = args + genLoad(a1, INT) + genLoad(a2) + generatedType = UNIT + bc.astore(elementType) + } else { generatedType = INT emit(asm.Opcodes.ARRAYLENGTH) } @@ -339,26 +325,22 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { assert(tree.symbol.isModule, s"Selection of non-module from empty package: $tree sym: ${tree.symbol} at: ${tree.pos}") genLoadModule(tree) - case Select(qualifier, selector) => + case Select(qualifier, _) => val sym = tree.symbol generatedType = symInfoTK(sym) - val hostClass = findHostClass(qualifier.tpe, sym) - debuglog(s"Host class of $sym with qual $qualifier (${qualifier.tpe}) is $hostClass") val qualSafeToElide = treeInfo isQualifierSafeToElide qualifier - def genLoadQualUnlessElidable() { if (!qualSafeToElide) { genLoadQualifier(tree) } } - + // receiverClass is used in the bytecode to access the field. using sym.owner may lead to IllegalAccessError, SI-4283 + def receiverClass = qualifier.tpe.typeSymbol if (sym.isModule) { genLoadQualUnlessElidable() genLoadModule(tree) - } - else if (sym.isStaticMember) { + } else if (sym.isStaticMember) { genLoadQualUnlessElidable() - fieldLoad(sym, hostClass) - } - else { + fieldLoad(sym, receiverClass) + } else { genLoadQualifier(tree) - fieldLoad(sym, hostClass) + fieldLoad(sym, receiverClass) } case Ident(name) => @@ -411,24 +393,18 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { /* * must-single-thread */ - def fieldLoad( field: Symbol, hostClass: Symbol = null) { - fieldOp(field, isLoad = true, hostClass) - } + def fieldLoad(field: Symbol, hostClass: Symbol): Unit = fieldOp(field, isLoad = true, hostClass) + /* * must-single-thread */ - def fieldStore(field: Symbol, hostClass: Symbol = null) { - fieldOp(field, isLoad = false, hostClass) - } + def fieldStore(field: Symbol, hostClass: Symbol): Unit = fieldOp(field, isLoad = false, hostClass) /* * must-single-thread */ - private def fieldOp(field: Symbol, isLoad: Boolean, hostClass: Symbol) { - // LOAD_FIELD.hostClass , CALL_METHOD.hostClass , and #4283 - val owner = - if (hostClass == null) internalName(field.owner) - else internalName(hostClass) + private def fieldOp(field: Symbol, isLoad: Boolean, hostClass: Symbol): Unit = { + val owner = internalName(if (hostClass == null) field.owner else hostClass) val fieldJName = field.javaSimpleName.toString val fieldDescr = symInfoTK(field).descriptor val isStatic = field.isStaticMember @@ -436,7 +412,6 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { if (isLoad) { if (isStatic) asm.Opcodes.GETSTATIC else asm.Opcodes.GETFIELD } else { if (isStatic) asm.Opcodes.PUTSTATIC else asm.Opcodes.PUTFIELD } mnode.visitFieldInsn(opc, owner, fieldJName, fieldDescr) - } // ---------------- emitting constant values ---------------- @@ -469,13 +444,10 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { case NullTag => emit(asm.Opcodes.ACONST_NULL) case ClazzTag => - val toPush: BType = { - typeToBType(const.typeValue) match { - case kind: PrimitiveBType => boxedClassOfPrimitive(kind) - case kind => kind - } - } - mnode.visitLdcInsn(toPush.toASMType) + val tp = typeToBType(const.typeValue) + // classOf[Int] is transformed to Integer.TYPE by CleanUp + assert(!tp.isPrimitive, s"expected class type in classOf[T], found primitive type $tp") + mnode.visitLdcInsn(tp.toASMType) case EnumTag => val sym = const.symbolValue @@ -535,6 +507,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { private def genApply(app: Apply, expectedType: BType): BType = { var generatedType = expectedType lineNumber(app) + app match { case Apply(TypeApply(fun, targs), _) => @@ -582,19 +555,33 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { generatedType = genTypeApply() - // 'super' call: Note: since constructors are supposed to - // return an instance of what they construct, we have to take - // special care. On JVM they are 'void', and Scala forbids (syntactically) - // to call super constructors explicitly and/or use their 'returned' value. - // therefore, we can ignore this fact, and generate code that leaves nothing - // on the stack (contrary to what the type in the AST says). case Apply(fun @ Select(Super(_, _), _), args) => - val invokeStyle = InvokeStyle.Super - // if (fun.symbol.isConstructor) Static(true) else SuperCall(mix); + def initModule() { + // we initialize the MODULE$ field immediately after the super ctor + if (!isModuleInitialized && + jMethodName == INSTANCE_CONSTRUCTOR_NAME && + fun.symbol.javaSimpleName.toString == INSTANCE_CONSTRUCTOR_NAME && + isStaticModuleClass(claszSymbol)) { + isModuleInitialized = true + mnode.visitVarInsn(asm.Opcodes.ALOAD, 0) + mnode.visitFieldInsn( + asm.Opcodes.PUTSTATIC, + thisBType.internalName, + strMODULE_INSTANCE_FIELD, + thisBType.descriptor + ) + } + } + // 'super' call: Note: since constructors are supposed to + // return an instance of what they construct, we have to take + // special care. On JVM they are 'void', and Scala forbids (syntactically) + // to call super constructors explicitly and/or use their 'returned' value. + // therefore, we can ignore this fact, and generate code that leaves nothing + // on the stack (contrary to what the type in the AST says). mnode.visitVarInsn(asm.Opcodes.ALOAD, 0) genLoadArguments(args, paramTKs(app)) - genCallMethod(fun.symbol, invokeStyle, app.pos) - generatedType = methodBTypeFromSymbol(fun.symbol).returnType + generatedType = genCallMethod(fun.symbol, InvokeStyle.Super, app.pos) + initModule() // 'new' constructor call: Note: since constructors are // thought to return an instance of what they construct, @@ -625,8 +612,8 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { } argsSize match { case 1 => bc newarray elemKind - case _ => - val descr = ('[' * argsSize) + elemKind.descriptor // denotes the same as: arrayN(elemKind, argsSize).descriptor + case _ => // this is currently dead code is Scalac, unlike in Dotty + val descr = ("[" * argsSize) + elemKind.descriptor // denotes the same as: arrayN(elemKind, argsSize).descriptor mnode.visitMultiANewArrayInsn(descr, argsSize) } @@ -643,19 +630,19 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { case Apply(fun, args) if app.hasAttachment[delambdafy.LambdaMetaFactoryCapable] => val attachment = app.attachments.get[delambdafy.LambdaMetaFactoryCapable].get genLoadArguments(args, paramTKs(app)) - genInvokeDynamicLambda(attachment.target, attachment.arity, attachment.functionalInterface) + genInvokeDynamicLambda(attachment.target, attachment.arity, attachment.functionalInterface, attachment.sam) generatedType = methodBTypeFromSymbol(fun.symbol).returnType - case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isBox(fun.symbol) => + case Apply(fun, List(expr)) if currentRun.runDefinitions.isBox(fun.symbol) => val nativeKind = tpeTK(expr) genLoad(expr, nativeKind) val MethodNameAndType(mname, methodType) = srBoxesRuntimeBoxToMethods(nativeKind) bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, app.pos) - generatedType = boxResultType(fun.symbol) // was typeToBType(fun.symbol.tpe.resultType) + generatedType = boxResultType(fun.symbol) - case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isUnbox(fun.symbol) => + case Apply(fun, List(expr)) if currentRun.runDefinitions.isUnbox(fun.symbol) => genLoad(expr) - val boxType = unboxResultType(fun.symbol) // was typeToBType(fun.symbol.owner.linkedClassOfClass.tpe) + val boxType = unboxResultType(fun.symbol) generatedType = boxType val MethodNameAndType(mname, methodType) = srBoxesRuntimeUnboxToMethods(boxType) bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, app.pos) @@ -663,80 +650,73 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { case app @ Apply(fun, args) => val sym = fun.symbol - if (sym.isLabel) { // jump to a label + if (sym.isLabel) { // jump to a label genLoadLabelArguments(args, labelDef(sym), app.pos) bc goTo programPoint(sym) } else if (isPrimitive(sym)) { // primitive method call generatedType = genPrimitiveOp(app, expectedType) - } else { // normal method call - - def genNormalMethodCall() { - - val invokeStyle = - if (sym.isStaticMember) InvokeStyle.Static - else if (sym.isPrivate || sym.isClassConstructor) InvokeStyle.Special - else InvokeStyle.Virtual - - if (invokeStyle.hasInstance) { - genLoadQualifier(fun) + } else { // normal method call + val invokeStyle = + if (sym.isStaticMember) InvokeStyle.Static + else if (sym.isPrivate || sym.isClassConstructor) InvokeStyle.Special + else InvokeStyle.Virtual + + if (invokeStyle.hasInstance) genLoadQualifier(fun) + genLoadArguments(args, paramTKs(app)) + + val Select(qual, _) = fun // fun is a Select, also checked in genLoadQualifier + if (sym == definitions.Array_clone) { + // Special-case Array.clone, introduced in 36ef60e. The goal is to generate this call + // as "[I.clone" instead of "java/lang/Object.clone". This is consistent with javac. + // Arrays have a public method `clone` (jls 10.7). + // + // The JVMS is not explicit about this, but that receiver type can be an array type + // descriptor (instead of a class internal name): + // invokevirtual #2; //Method "[I".clone:()Ljava/lang/Object + // + // Note that using `Object.clone()` would work as well, but only because the JVM + // relaxes protected access specifically if the receiver is an array: + // http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/vm/interpreter/linkResolver.cpp#l439 + // Example: `class C { override def clone(): Object = "hi" }` + // Emitting `def f(c: C) = c.clone()` as `Object.clone()` gives a VerifyError. + val target: String = tpeTK(qual).asRefBType.classOrArrayType + val methodBType = methodBTypeFromSymbol(sym) + bc.invokevirtual(target, sym.javaSimpleName.toString, methodBType.descriptor, app.pos) + generatedType = methodBType.returnType + } else { + val receiverClass = if (!invokeStyle.isVirtual) null else { + // receiverClass is used in the bytecode to as the method receiver. using sym.owner + // may lead to IllegalAccessErrors, see 9954eaf / aladdin bug 455. + val qualSym = qual.tpe.typeSymbol + if (qualSym == ArrayClass) { + // For invocations like `Array(1).hashCode` or `.wait()`, use Object as receiver + // in the bytecode. Using the array descriptor (like we do for clone above) seems + // to work as well, but it seems safer not to change this. Javac also uses Object. + // Note that array apply/update/length are handled by isPrimitive (above). + assert(sym.owner == ObjectClass, s"unexpected array call: ${show(app)}") + ObjectClass + } else qualSym } - genLoadArguments(args, paramTKs(app)) - - // In "a couple cases", squirrel away a extra information (hostClass, targetTypeKind). TODO Document what "in a couple cases" refers to. - var hostClass: Symbol = null - var targetTypeKind: BType = null - fun match { - case Select(qual, _) => - val qualSym = findHostClass(qual.tpe, sym) - if (qualSym == ArrayClass) { - targetTypeKind = tpeTK(qual) - log(s"Stored target type kind for ${sym.fullName} as $targetTypeKind") - } - else { - hostClass = qualSym - if (qual.tpe.typeSymbol != qualSym) { - log(s"Precisified host class for $sym from ${qual.tpe.typeSymbol.fullName} to ${qualSym.fullName}") - } - } - - case _ => - } - if ((targetTypeKind != null) && (sym == definitions.Array_clone) && invokeStyle.isVirtual) { - // An invokevirtual points to a CONSTANT_Methodref_info which in turn points to a - // CONSTANT_Class_info of the receiver type. - // The JVMS is not explicit about this, but that receiver type may be an array type - // descriptor (instead of a class internal name): - // invokevirtual #2; //Method "[I".clone:()Ljava/lang/Object - val target: String = targetTypeKind.asRefBType.classOrArrayType - bc.invokevirtual(target, "clone", "()Ljava/lang/Object;", app.pos) - } - else { - genCallMethod(sym, invokeStyle, app.pos, hostClass) - // Check if the Apply tree has an InlineAnnotatedAttachment, added by the typer - // for callsites marked `f(): @inline/noinline`. For nullary calls, the attachment - // is on the Select node (not on the Apply node added by UnCurry). - def checkInlineAnnotated(t: Tree): Unit = { - if (t.hasAttachment[InlineAnnotatedAttachment]) lastInsn match { - case m: MethodInsnNode => - if (app.hasAttachment[NoInlineCallsiteAttachment.type]) noInlineAnnotatedCallsites += m - else inlineAnnotatedCallsites += m - case _ => - } else t match { - case Apply(fun, _) => checkInlineAnnotated(fun) - case _ => - } + generatedType = genCallMethod(sym, invokeStyle, app.pos, receiverClass) + + // Check if the Apply tree has an InlineAnnotatedAttachment, added by the typer + // for callsites marked `f(): @inline/noinline`. For nullary calls, the attachment + // is on the Select node (not on the Apply node added by UnCurry). + def recordInlineAnnotated(t: Tree): Unit = { + if (t.hasAttachment[InlineAnnotatedAttachment]) lastInsn match { + case m: MethodInsnNode => + if (app.hasAttachment[NoInlineCallsiteAttachment.type]) noInlineAnnotatedCallsites += m + else inlineAnnotatedCallsites += m + case _ => + } else t match { + case Apply(fun, _) => recordInlineAnnotated(fun) + case _ => } - checkInlineAnnotated(app) } - - } // end of genNormalMethodCall() - - genNormalMethodCall() - - generatedType = methodBTypeFromSymbol(sym).returnType + recordInlineAnnotated(app) + } } - } generatedType @@ -790,7 +770,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { for (caze @ CaseDef(pat, guard, body) <- tree.cases) { assert(guard == EmptyTree, guard) val switchBlockPoint = new asm.Label - switchBlocks ::= (switchBlockPoint, body) + switchBlocks ::= ((switchBlockPoint, body)) pat match { case Literal(value) => flatKeys ::= value.intValue @@ -1014,11 +994,11 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { def genStringConcat(tree: Tree): BType = { lineNumber(tree) liftStringConcat(tree) match { - // Optimization for expressions of the form "" + x. We can avoid the StringBuilder. case List(Literal(Constant("")), arg) => genLoad(arg, ObjectRef) genCallMethod(String_valueOf, InvokeStyle.Static, arg.pos) + case concatenations => bc.genStartConcat(tree.pos) for (elem <- concatenations) { @@ -1035,75 +1015,83 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { bc.genConcat(elemType, loadedElem.pos) } bc.genEndConcat(tree.pos) - } - StringRef } - def genCallMethod(method: Symbol, style: InvokeStyle, pos: Position, hostClass0: Symbol = null) { - - val siteSymbol = claszSymbol - val hostSymbol = if (hostClass0 == null) method.owner else hostClass0 + /** + * Generate a method invocation. If `specificReceiver != null`, it is used as receiver in the + * invocation instruction, otherwise `method.owner`. A specific receiver class is needed to + * prevent an IllegalAccessError, (aladdin bug 455). + */ + def genCallMethod(method: Symbol, style: InvokeStyle, pos: Position, specificReceiver: Symbol = null): BType = { val methodOwner = method.owner - // info calls so that types are up to date; erasure may add lateINTERFACE to traits - hostSymbol.info ; methodOwner.info + // the class used in the invocation's method descriptor in the classfile + val receiverClass = { + if (specificReceiver != null) + assert(style.isVirtual || specificReceiver == methodOwner, s"specificReceiver can only be specified for virtual calls. $method - $specificReceiver") + + val useSpecificReceiver = specificReceiver != null && !specificReceiver.isBottomClass + val receiver = if (useSpecificReceiver) specificReceiver else methodOwner + + // workaround for a JVM bug: https://bugs.openjdk.java.net/browse/JDK-8154587 + // when an interface method overrides a member of Object (note that all interfaces implicitly + // have superclass Object), the receiver needs to be the interface declaring the override (and + // not a sub-interface that inherits it). example: + // trait T { override def clone(): Object = "" } + // trait U extends T + // class C extends U + // class D { def f(u: U) = u.clone() } + // The invocation `u.clone()` needs `T` as a receiver: + // - using Object is illegal, as Object.clone is protected + // - using U results in a `NoSuchMethodError: U.clone. This is the JVM bug. + // Note that a mixin forwarder is generated, so the correct method is executed in the end: + // class C { override def clone(): Object = super[T].clone() } + val isTraitMethodOverridingObjectMember = { + receiver != methodOwner && // fast path - the boolean is used to pick either of these two, if they are the same it does not matter + style.isVirtual && + receiver.isTraitOrInterface && + ObjectTpe.decl(method.name).exists && // fast path - compute overrideChain on the next line only if necessary + method.overrideChain.last.owner == ObjectClass + } + if (isTraitMethodOverridingObjectMember) methodOwner else receiver + } - def needsInterfaceCall(sym: Symbol) = ( - sym.isInterface - || sym.isJavaDefined && sym.isNonBottomSubClass(definitions.ClassfileAnnotationClass) - ) + receiverClass.info // ensure types the type is up to date; erasure may add lateINTERFACE to traits + val receiverName = internalName(receiverClass) + + // super calls are only allowed to direct parents + if (style.isSuper && receiverClass.isTraitOrInterface && !cnode.interfaces.contains(receiverName)) { + thisBType.info.get.inlineInfo.lateInterfaces += receiverName + cnode.interfaces.add(receiverName) + } + + def needsInterfaceCall(sym: Symbol) = { + sym.isTraitOrInterface || + sym.isJavaDefined && sym.isNonBottomSubClass(definitions.ClassfileAnnotationClass) + } - // whether to reference the type of the receiver or - // the type of the method owner - val useMethodOwner = ( - !style.isVirtual - || hostSymbol.isBottomClass - || methodOwner == definitions.ObjectClass - ) - val receiver = if (useMethodOwner) methodOwner else hostSymbol - val jowner = internalName(receiver) val jname = method.javaSimpleName.toString val bmType = methodBTypeFromSymbol(method) val mdescr = bmType.descriptor - def initModule() { - // we initialize the MODULE$ field immediately after the super ctor - if (!isModuleInitialized && - jMethodName == INSTANCE_CONSTRUCTOR_NAME && - jname == INSTANCE_CONSTRUCTOR_NAME && - isStaticModuleClass(siteSymbol)) { - isModuleInitialized = true - mnode.visitVarInsn(asm.Opcodes.ALOAD, 0) - mnode.visitFieldInsn( - asm.Opcodes.PUTSTATIC, - thisName, - strMODULE_INSTANCE_FIELD, - "L" + thisName + ";" - ) - } - } - - if (style.isStatic) { bc.invokestatic (jowner, jname, mdescr, pos) } - else if (style.isSpecial) { bc.invokespecial (jowner, jname, mdescr, pos) } - else if (style.isVirtual) { - if (needsInterfaceCall(receiver)) { bc.invokeinterface(jowner, jname, mdescr, pos) } - else { bc.invokevirtual (jowner, jname, mdescr, pos) } - } - else { - assert(style.isSuper, s"An unknown InvokeStyle: $style") - bc.invokespecial(jowner, jname, mdescr, pos) - initModule() + import InvokeStyle._ + style match { + case Static => bc.invokestatic (receiverName, jname, mdescr, pos) + case Special => bc.invokespecial (receiverName, jname, mdescr, pos) + case Virtual => + if (needsInterfaceCall(receiverClass)) bc.invokeinterface(receiverName, jname, mdescr, pos) + else bc.invokevirtual (receiverName, jname, mdescr, pos) + case Super => bc.invokespecial (receiverName, jname, mdescr, pos) } + bmType.returnType } // end of genCallMethod() /* Generate the scala ## method. */ def genScalaHash(tree: Tree, applyPos: Position): BType = { - genLoadModule(ScalaRunTimeModule) // TODO why load ScalaRunTimeModule if ## has InvokeStyle of Static(false) ? genLoad(tree, ObjectRef) genCallMethod(hashMethodSym, InvokeStyle.Static, applyPos) - INT } /* @@ -1337,12 +1325,12 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { def genSynchronized(tree: Apply, expectedType: BType): BType def genLoadTry(tree: Try): BType - def genInvokeDynamicLambda(lambdaTarget: Symbol, arity: Int, functionalInterface: Symbol) { + def genInvokeDynamicLambda(lambdaTarget: Symbol, arity: Int, functionalInterface: Symbol, sam: Symbol) { val isStaticMethod = lambdaTarget.hasFlag(Flags.STATIC) def asmType(sym: Symbol) = classBTypeFromSymbol(sym).toASMType val implMethodHandle = - new asm.Handle(if (lambdaTarget.hasFlag(Flags.STATIC)) asm.Opcodes.H_INVOKESTATIC else asm.Opcodes.H_INVOKEVIRTUAL, + new asm.Handle(if (lambdaTarget.hasFlag(Flags.STATIC)) asm.Opcodes.H_INVOKESTATIC else if (lambdaTarget.owner.isTrait) asm.Opcodes.H_INVOKEINTERFACE else asm.Opcodes.H_INVOKEVIRTUAL, classBTypeFromSymbol(lambdaTarget.owner).internalName, lambdaTarget.name.toString, methodBTypeFromSymbol(lambdaTarget).descriptor) @@ -1352,7 +1340,6 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { val invokedType = asm.Type.getMethodDescriptor(asmType(functionalInterface), (receiver ::: capturedParams).map(sym => typeToBType(sym.info).toASMType): _*) val constrainedType = new MethodBType(lambdaParams.map(p => typeToBType(p.tpe)), typeToBType(lambdaTarget.tpe.resultType)).toASMType - val sam = functionalInterface.info.decls.find(_.isDeferred).getOrElse(functionalInterface.info.member(nme.apply)) val samName = sam.name.toString val samMethodType = methodBTypeFromSymbol(sam).toASMType diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index f423f3c7fe..a32c21795d 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -8,7 +8,6 @@ package tools.nsc package backend.jvm import scala.tools.asm -import scala.collection.mutable import scala.tools.nsc.io.AbstractFile import GenBCode._ import BackendReporting._ @@ -31,8 +30,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { * True for classes generated by the Scala compiler that are considered top-level in terms of * the InnerClass / EnclosingMethod classfile attributes. See comment in BTypes. */ - def considerAsTopLevelImplementationArtifact(classSym: Symbol) = - classSym.isImplClass || classSym.isSpecialized + def considerAsTopLevelImplementationArtifact(classSym: Symbol) = classSym.isSpecialized /** * Cache the value of delambdafy == "inline" for each run. We need to query this value many @@ -60,9 +58,8 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { def isAnonymousOrLocalClass(classSym: Symbol): Boolean = { assert(classSym.isClass, s"not a class: $classSym") val r = exitingPickler(classSym.isAnonymousClass) || !classSym.originalOwner.isClass - if (r && settings.Ybackend.value == "GenBCode") { - // this assertion only holds in GenBCode. lambda lift renames symbols and may accidentally - // introduce `$lambda` into a class name, making `isDelambdafyFunction` true. under GenBCode + if (r) { + // lambda lift renames symbols and may accidentally introduce `$lambda` into a class name, making `isDelambdafyFunction` true. // we prevent this, see `nonAnon` in LambdaLift. // phase travel necessary: after flatten, the name includes the name of outer classes. // if some outer name contains $lambda, a non-lambda class is considered lambda. @@ -148,15 +145,12 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { assert(classSym.isClass, classSym) def doesNotExist(method: Symbol) = { - // (1) SI-9124, some trait methods don't exist in the generated interface. see comment in BTypes. - // (2) Value classes. Member methods of value classes exist in the generated box class. However, - // nested methods lifted into a value class are moved to the companion object and don't exist - // in the value class itself. We can identify such nested methods: the initial enclosing class - // is a value class, but the current owner is some other class (the module class). - method.owner.isTrait && method.isImplOnly || { // (1) - val enclCls = nextEnclosingClass(method) - exitingPickler(enclCls.isDerivedValueClass) && method.owner != enclCls // (2) - } + // Value classes. Member methods of value classes exist in the generated box class. However, + // nested methods lifted into a value class are moved to the companion object and don't exist + // in the value class itself. We can identify such nested methods: the initial enclosing class + // is a value class, but the current owner is some other class (the module class). + val enclCls = nextEnclosingClass(method) + exitingPickler(enclCls.isDerivedValueClass) && method.owner != enclCls } def enclosingMethod(sym: Symbol): Option[Symbol] = { @@ -176,8 +170,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { private def enclosingClassForEnclosingMethodAttribute(classSym: Symbol): Symbol = { assert(classSym.isClass, classSym) val r = nextEnclosingClass(nextEnclosing(classSym)) - // this should be an assertion, but we are more cautious for now as it was introduced before the 2.11.6 minor release - if (considerAsTopLevelImplementationArtifact(r)) devWarning(s"enclosing class of $classSym should not be an implementation artifact class: $r") r } @@ -192,20 +184,11 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { * on the implementation of GenASM / GenBCode, so they need to be passed in. */ def enclosingMethodAttribute(classSym: Symbol, classDesc: Symbol => String, methodDesc: Symbol => String): Option[EnclosingMethodEntry] = { - // trait impl classes are always top-level, see comment in BTypes + // specialized classes are always top-level, see comment in BTypes if (isAnonymousOrLocalClass(classSym) && !considerAsTopLevelImplementationArtifact(classSym)) { val enclosingClass = enclosingClassForEnclosingMethodAttribute(classSym) - val methodOpt = enclosingMethodForEnclosingMethodAttribute(classSym) match { - case some @ Some(m) => - if (m.owner != enclosingClass) { - // This should never happen. In case it does, it prevents emitting an invalid - // EnclosingMethod attribute: if the attribute specifies an enclosing method, - // it needs to exist in the specified enclosing class. - devWarning(s"the owner of the enclosing method ${m.locationString} should be the same as the enclosing class $enclosingClass") - None - } else some - case none => none - } + val methodOpt = enclosingMethodForEnclosingMethodAttribute(classSym) + for (m <- methodOpt) assert(m.owner == enclosingClass, s"the owner of the enclosing method ${m.locationString} should be the same as the enclosing class $enclosingClass") Some(EnclosingMethodEntry( classDesc(enclosingClass), methodOpt.map(_.javaSimpleName.toString).orNull, @@ -251,22 +234,14 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { * Build the [[InlineInfo]] for a class symbol. */ def buildInlineInfoFromClassSymbol(classSym: Symbol, classSymToInternalName: Symbol => InternalName, methodSymToDescriptor: Symbol => String): InlineInfo = { - val traitSelfType = if (classSym.isTrait && !classSym.isImplClass) { - // The mixin phase uses typeOfThis for the self parameter in implementation class methods. - val selfSym = classSym.typeOfThis.typeSymbol - if (selfSym != classSym) Some(classSymToInternalName(selfSym)) else None - } else { - None - } - val isEffectivelyFinal = classSym.isEffectivelyFinal val sam = { - if (classSym.isImplClass || classSym.isEffectivelyFinal) None + if (classSym.isEffectivelyFinal) None else { // Phase travel necessary. For example, nullary methods (getter of an abstract val) get an // empty parameter list in later phases and would therefore be picked as SAM. - val samSym = exitingPickler(definitions.findSam(classSym.tpe)) + val samSym = exitingPickler(definitions.samOf(classSym.tpe)) if (samSym == NoSymbol) None else Some(samSym.javaSimpleName.toString + methodSymToDescriptor(samSym)) } @@ -287,41 +262,16 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { val name = methodSym.javaSimpleName.toString // same as in genDefDef val signature = name + methodSymToDescriptor(methodSym) - // Some detours are required here because of changing flags (lateDEFERRED, lateMODULE): - // 1. Why the phase travel? Concrete trait methods obtain the lateDEFERRED flag in Mixin. - // This makes isEffectivelyFinalOrNotOverridden false, which would prevent non-final - // but non-overridden methods of sealed traits from being inlined. - // 2. Why the special case for `classSym.isImplClass`? Impl class symbols obtain the - // lateMODULE flag during Mixin. During the phase travel to exitingPickler, the late - // flag is ignored. The members are therefore not isEffectivelyFinal (their owner - // is not a module). Since we know that all impl class members are static, we can - // just take the shortcut. - val effectivelyFinal = classSym.isImplClass || exitingPickler(methodSym.isEffectivelyFinalOrNotOverridden) - - // Identify trait interface methods that have a static implementation in the implementation - // class. Invocations of these methods can be re-wrired directly to the static implementation - // if they are final or the receiver is known. + // In `trait T { object O }`, `oSym.isEffectivelyFinalOrNotOverridden` is true, but the + // method is abstract in bytecode, `defDef.rhs.isEmpty`. Abstract methods are excluded + // so they are not marked final in the InlineInfo attribute. // - // Using `erasure.needsImplMethod` is not enough: it keeps field accessors, module getters - // and super accessors. When AddInterfaces creates the impl class, these methods are - // initially added to it. - // - // The mixin phase later on filters out most of these members from the impl class (see - // Mixin.isImplementedStatically). However, accessors for concrete lazy vals remain in the - // impl class after mixin. So the filter in mixin is not exactly what we need here (we - // want to identify concrete trait methods, not any accessors). So we check some symbol - // properties manually. - val traitMethodWithStaticImplementation = { - import symtab.Flags._ - classSym.isTrait && !classSym.isImplClass && - erasure.needsImplMethod(methodSym) && - !methodSym.isModule && - !(methodSym hasFlag (ACCESSOR | SUPERACCESSOR)) - } + // However, due to https://github.com/scala/scala-dev/issues/126, this currently does not + // work, the abstract accessor for O will be marked effectivelyFinal. + val effectivelyFinal = methodSym.isEffectivelyFinalOrNotOverridden && !methodSym.isDeferred val info = MethodInlineInfo( effectivelyFinal = effectivelyFinal, - traitMethodWithStaticImplementation = traitMethodWithStaticImplementation, annotatedInline = methodSym.hasAnnotation(ScalaInlineClass), annotatedNoInline = methodSym.hasAnnotation(ScalaNoInlineClass) ) @@ -329,7 +279,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { } }).toMap - InlineInfo(traitSelfType, isEffectivelyFinal, sam, methodInlineInfos, warning) + InlineInfo(isEffectivelyFinal, sam, methodInlineInfos, warning) } /* @@ -627,17 +577,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { val classSym = if (sym.isJavaDefined && sym.isModuleClass) exitingPickler(sym.linkedClassOfClass) else sym classBTypeFromSymbol(classSym).internalName } - - /** - * The jvm descriptor of a type. - */ - final def descriptor(t: Type): String = typeToBType(t).descriptor - - /** - * The jvm descriptor for a symbol. - */ - final def descriptor(sym: Symbol): String = classBTypeFromSymbol(sym).descriptor - } // end of trait BCInnerClassGen trait BCAnnotGen extends BCInnerClassGen { @@ -646,6 +585,35 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { private lazy val AnnotationRetentionPolicyClassValue = AnnotationRetentionPolicyModule.tpe.member(TermName("CLASS")) private lazy val AnnotationRetentionPolicyRuntimeValue = AnnotationRetentionPolicyModule.tpe.member(TermName("RUNTIME")) + /** + * Annotations are not processed by the compilation pipeline like ordinary trees. Instead, the + * typer extracts them into [[AnnotationInfo]] objects which are attached to the corresponding + * symbol (sym.annotations) or type (as an AnnotatedType, eliminated by erasure). + * + * For Scala annotations this is OK: they are stored in the pickle and ignored by the backend. + * Java annoations on the other hand are additionally emitted to the classfile in Java's format. + * + * This means that [[Type]] instances within an AnnotaionInfo reach the backend non-erased. Examples: + * - @(javax.annotation.Resource @annotation.meta.getter) val x = 0 + * Here, annotationInfo.atp is an AnnotatedType. + * - @SomeAnnotation[T] val x = 0 + * In principle, the annotationInfo.atp is a non-erased type ref. However, this cannot + * actually happen because Java annotations cannot be generic. + * - @javax.annotation.Resource(`type` = classOf[List[_]]) val x = 0 + * The annotationInfo.assocs contains a LiteralAnnotArg(Constant(tp)) where tp is the + * non-erased existential type. + */ + def erasedType(tp: Type): Type = enteringErasure { + // make sure we don't erase value class references to the type that the value class boxes + // this is basically the same logic as in erasure's preTransform, case Literal(classTag). + tp.dealiasWiden match { + case tr @ TypeRef(_, clazz, _) if clazz.isDerivedValueClass => erasure.scalaErasure.eraseNormalClassRef(tr) + case tpe => erasure.erasure(tpe.typeSymbol)(tpe) + } + } + + def descriptorForErasedType(tp: Type): String = typeToBType(erasedType(tp)).descriptor + /** Whether an annotation should be emitted as a Java annotation * .initialize: if 'annot' is read from pickle, atp might be uninitialized */ @@ -682,7 +650,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { ca(idx) = b.asInstanceOf[Char] idx += 1 } - ca } @@ -745,9 +712,10 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { case StringTag => assert(const.value != null, const) // TODO this invariant isn't documented in `case class Constant` av.visit(name, const.stringValue) // `stringValue` special-cases null, but that execution path isn't exercised for a const with StringTag - case ClazzTag => av.visit(name, typeToBType(const.typeValue).toASMType) + case ClazzTag => + av.visit(name, typeToBType(erasedType(const.typeValue)).toASMType) case EnumTag => - val edesc = descriptor(const.tpe) // the class descriptor of the enumeration class. + val edesc = descriptorForErasedType(const.tpe) // the class descriptor of the enumeration class. val evalue = const.symbolValue.name.toString // value the actual enumeration value. av.visitEnum(name, edesc, evalue) } @@ -772,7 +740,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { case NestedAnnotArg(annInfo) => val AnnotationInfo(typ, args, assocs) = annInfo assert(args.isEmpty, args) - val desc = descriptor(typ) // the class descriptor of the nested annotation class + val desc = descriptorForErasedType(typ) // the class descriptor of the nested annotation class val nestedVisitor = av.visitAnnotation(name, desc) emitAssocs(nestedVisitor, assocs) } @@ -797,7 +765,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { for(annot <- annotations; if shouldEmitAnnotation(annot)) { val AnnotationInfo(typ, args, assocs) = annot assert(args.isEmpty, args) - val av = cw.visitAnnotation(descriptor(typ), isRuntimeVisible(annot)) + val av = cw.visitAnnotation(descriptorForErasedType(typ), isRuntimeVisible(annot)) emitAssocs(av, assocs) } } @@ -809,7 +777,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { for(annot <- annotations; if shouldEmitAnnotation(annot)) { val AnnotationInfo(typ, args, assocs) = annot assert(args.isEmpty, args) - val av = mw.visitAnnotation(descriptor(typ), isRuntimeVisible(annot)) + val av = mw.visitAnnotation(descriptorForErasedType(typ), isRuntimeVisible(annot)) emitAssocs(av, assocs) } } @@ -821,7 +789,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { for(annot <- annotations; if shouldEmitAnnotation(annot)) { val AnnotationInfo(typ, args, assocs) = annot assert(args.isEmpty, args) - val av = fw.visitAnnotation(descriptor(typ), isRuntimeVisible(annot)) + val av = fw.visitAnnotation(descriptorForErasedType(typ), isRuntimeVisible(annot)) emitAssocs(av, assocs) } } @@ -836,7 +804,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { annot <- annots) { val AnnotationInfo(typ, args, assocs) = annot assert(args.isEmpty, args) - val pannVisitor: asm.AnnotationVisitor = jmethod.visitParameterAnnotation(idx, descriptor(typ), isRuntimeVisible(annot)) + val pannVisitor: asm.AnnotationVisitor = jmethod.visitParameterAnnotation(idx, descriptorForErasedType(typ), isRuntimeVisible(annot)) emitAssocs(pannVisitor, assocs) } } @@ -869,7 +837,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { || sym.isArtifact || sym.isLiftedMethod || sym.isBridge - || (sym.ownerChain exists (_.isImplClass)) ) /* @return @@ -909,10 +876,10 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { if(!isValidSignature) { reporter.warning(sym.pos, - """|compiler bug: created invalid generic signature for %s in %s - |signature: %s + sm"""|compiler bug: created invalid generic signature for $sym in ${sym.owner.skipPackageObject.fullName} + |signature: $sig |if this is reproducible, please report bug at https://issues.scala-lang.org/ - """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig)) + """.trim) return null } } @@ -922,13 +889,13 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { val bytecodeTpe = owner.thisType.memberInfo(sym) if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym)(normalizedTpe) =:= bytecodeTpe)) { reporter.warning(sym.pos, - """|compiler bug: created generic signature for %s in %s that does not conform to its erasure - |signature: %s - |original type: %s - |normalized type: %s - |erasure type: %s + sm"""|compiler bug: created generic signature for $sym in ${sym.owner.skipPackageObject.fullName} that does not conform to its erasure + |signature: $sig + |original type: $memberTpe + |normalized type: $normalizedTpe + |erasure type: $bytecodeTpe |if this is reproducible, please report bug at http://issues.scala-lang.org/ - """.trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig, memberTpe, normalizedTpe, bytecodeTpe)) + """.trim) return null } } @@ -1021,7 +988,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { mirrorMethod.visitCode() - mirrorMethod.visitFieldInsn(asm.Opcodes.GETSTATIC, moduleName, strMODULE_INSTANCE_FIELD, descriptor(module)) + mirrorMethod.visitFieldInsn(asm.Opcodes.GETSTATIC, moduleName, strMODULE_INSTANCE_FIELD, classBTypeFromSymbol(module).descriptor) var index = 0 for(jparamType <- paramJavaTypes) { @@ -1082,7 +1049,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { def getExceptions(excs: List[AnnotationInfo]): List[String] = { for (ThrownException(tp) <- excs.distinct) yield { - val erased = enteringErasure(erasure.erasure(tp.typeSymbol)(tp)) + val erased = erasedType(tp) internalName(erased.typeSymbol) } } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala index 96796b3244..bddc41e5c6 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala @@ -59,7 +59,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { // current class var cnode: asm.tree.ClassNode = null - var thisName: String = null // the internal name of the class being emitted + var thisBType: ClassBType = null var claszSymbol: Symbol = null var isCZParcelable = false @@ -91,9 +91,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { isCZParcelable = isAndroidParcelableClass(claszSymbol) isCZStaticModule = isStaticModuleClass(claszSymbol) isCZRemote = isRemote(claszSymbol) - thisName = internalName(claszSymbol) - - val classBType = classBTypeFromSymbol(claszSymbol) + thisBType = classBTypeFromSymbol(claszSymbol) cnode = new asm.tree.ClassNode() @@ -114,7 +112,6 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { gen(cd.impl) - val shouldAddLambdaDeserialize = ( settings.target.value == "jvm-1.8" && settings.Ydelambdafy.value == "method" @@ -123,7 +120,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { if (shouldAddLambdaDeserialize) backendUtils.addLambdaDeserialize(cnode) - cnode.visitAttribute(classBType.inlineInfoAttribute.get) + cnode.visitAttribute(thisBType.inlineInfoAttribute.get) if (AsmUtils.traceClassEnabled && cnode.name.contains(AsmUtils.traceClassPattern)) AsmUtils.traceClass(cnode) @@ -144,7 +141,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { val thisSignature = getGenericSignature(claszSymbol, claszSymbol.owner) cnode.visit(classfileVersion, flags, - thisName, thisSignature, + thisBType.internalName, thisSignature, superClass, interfaceNames.toArray) if (emitSource) { @@ -157,7 +154,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { case _ => () } - val ssa = getAnnotPickle(thisName, claszSymbol) + val ssa = getAnnotPickle(thisBType.internalName, claszSymbol) cnode.visitAttribute(if (ssa.isDefined) pickleMarkerLocal else pickleMarkerForeign) emitAnnotations(cnode, claszSymbol.annotations ++ ssa) @@ -167,18 +164,17 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { } else { - val skipStaticForwarders = (claszSymbol.isInterface || settings.noForwarders) - if (!skipStaticForwarders) { + if (!settings.noForwarders) { val lmoc = claszSymbol.companionModule // add static forwarders if there are no name conflicts; see bugs #363 and #1735 if (lmoc != NoSymbol) { // it must be a top level class (name contains no $s) val isCandidateForForwarders = { - exitingPickler { !(lmoc.name.toString contains '$') && lmoc.hasModuleFlag && !lmoc.isImplClass && !lmoc.isNestedClass } + exitingPickler { !(lmoc.name.toString contains '$') && lmoc.hasModuleFlag && !lmoc.isNestedClass } } if (isCandidateForForwarders) { log(s"Adding static forwarders from '$claszSymbol' to implementations in '$lmoc'") - addForwarders(isRemote(claszSymbol), cnode, thisName, lmoc.moduleClass) + addForwarders(isRemote(claszSymbol), cnode, thisBType.internalName, lmoc.moduleClass) } } } @@ -196,7 +192,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { val fv = cnode.visitField(GenBCode.PublicStaticFinal, // TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED strMODULE_INSTANCE_FIELD, - "L" + thisName + ";", + thisBType.descriptor, null, // no java-generic-signature null // no initial value ) @@ -220,11 +216,11 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { /* "legacy static initialization" */ if (isCZStaticModule) { - clinit.visitTypeInsn(asm.Opcodes.NEW, thisName) + clinit.visitTypeInsn(asm.Opcodes.NEW, thisBType.internalName) clinit.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, - thisName, INSTANCE_CONSTRUCTOR_NAME, "()V", false) + thisBType.internalName, INSTANCE_CONSTRUCTOR_NAME, "()V", false) } - if (isCZParcelable) { legacyAddCreatorCode(clinit, cnode, thisName) } + if (isCZParcelable) { legacyAddCreatorCode(clinit, cnode, thisBType.internalName) } clinit.visitInsn(asm.Opcodes.RETURN) clinit.visitMaxs(0, 0) // just to follow protocol, dummy arguments @@ -232,13 +228,6 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { } def addClassFields() { - /* Non-method term members are fields, except for module members. Module - * members can only happen on .NET (no flatten) for inner traits. There, - * a module symbol is generated (transformInfo in mixin) which is used - * as owner for the members of the implementation class (so that the - * backend emits them as static). - * No code is needed for this module symbol. - */ for (f <- fieldSymbols(claszSymbol)) { val javagensig = getGenericSignature(f, claszSymbol) val flags = javaFieldFlags(f) @@ -563,7 +552,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { } val isNative = methSymbol.hasAnnotation(definitions.NativeAttr) - val isAbstractMethod = (methSymbol.isDeferred || methSymbol.owner.isInterface) && !methSymbol.hasFlag(Flags.JAVA_DEFAULTMETHOD) + val isAbstractMethod = rhs == EmptyTree val flags = GenBCode.mkFlags( javaFlags(methSymbol), if (isAbstractMethod) asm.Opcodes.ACC_ABSTRACT else 0, @@ -611,7 +600,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { if (!hasStaticBitSet) { mnode.visitLocalVariable( "this", - "L" + thisName + ";", + thisBType.descriptor, null, veryFirstProgramPoint, onePastLastProgramPoint, @@ -693,8 +682,8 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { val jname = callee.javaSimpleName.toString val jtype = methodBTypeFromSymbol(callee).descriptor insnParcA = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESTATIC, jowner, jname, jtype, false) - // PUTSTATIC `thisName`.CREATOR; - insnParcB = new asm.tree.FieldInsnNode(asm.Opcodes.PUTSTATIC, thisName, "CREATOR", andrFieldDescr) + // PUTSTATIC `thisBType.internalName`.CREATOR; + insnParcB = new asm.tree.FieldInsnNode(asm.Opcodes.PUTSTATIC, thisBType.internalName, "CREATOR", andrFieldDescr) } // insert a few instructions for initialization before each return instruction diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala index c3f6399901..2637d21050 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala @@ -7,7 +7,7 @@ package scala.tools.nsc package backend.jvm import scala.annotation.switch -import scala.collection.{mutable, concurrent} +import scala.collection.{concurrent, mutable} import scala.collection.concurrent.TrieMap import scala.reflect.internal.util.Position import scala.tools.asm @@ -17,7 +17,8 @@ import scala.tools.nsc.backend.jvm.BTypes.{InlineInfo, MethodInlineInfo} import scala.tools.nsc.backend.jvm.BackendReporting._ import scala.tools.nsc.backend.jvm.analysis.BackendUtils import scala.tools.nsc.backend.jvm.opt._ -import scala.collection.convert.decorateAsScala._ +import scala.collection.JavaConverters._ +import scala.collection.mutable.ListBuffer import scala.tools.nsc.settings.ScalaSettings /** @@ -180,8 +181,6 @@ abstract class BTypes { Some(classBTypeFromParsedClassfile(superName)) } - val interfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut) - val flags = classNode.access /** @@ -226,6 +225,9 @@ abstract class BTypes { val inlineInfo = inlineInfoFromClassfile(classNode) + val classfileInterfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut) + val interfaces = classfileInterfaces.filterNot(i => inlineInfo.lateInterfaces.contains(i.internalName)) + classBType.info = Right(ClassInfo(superClass, interfaces, flags, nestedClasses, nestedInfo, inlineInfo)) classBType } @@ -255,13 +257,11 @@ abstract class BTypes { val methodInfos = classNode.methods.asScala.map(methodNode => { val info = MethodInlineInfo( effectivelyFinal = BytecodeUtils.isFinalMethod(methodNode), - traitMethodWithStaticImplementation = false, annotatedInline = false, annotatedNoInline = false) (methodNode.name + methodNode.desc, info) }).toMap InlineInfo( - traitImplClassSelfType = None, isEffectivelyFinal = BytecodeUtils.isFinalClass(classNode), sam = inlinerHeuristics.javaSam(classNode.name), methodInfos = methodInfos, @@ -792,26 +792,17 @@ abstract class BTypes { * } * * - * Traits Members - * -------------- - * - * Some trait methods don't exist in the generated interface, but only in the implementation class - * (private methods in traits for example). Since EnclosingMethod expresses a source-level property, - * but the source-level enclosing method doesn't exist in the classfile, we the enclosing method - * is null (the enclosing class is still emitted). - * See BCodeAsmCommon.considerAsTopLevelImplementationArtifact - * - * - * Implementation Classes, Specialized Classes, Delambdafy:method closure classes - * ------------------------------------------------------------------------------ + * Specialized Classes, Delambdafy:method closure classes + * ------------------------------------------------------ * - * Trait implementation classes and specialized classes are always considered top-level. Again, - * the InnerClass / EnclosingMethod attributes describe a source-level properties. The impl - * classes are compilation artifacts. + * Specialized classes are always considered top-level, as the InnerClass / EnclosingMethod + * attributes describe a source-level properties. * * The same is true for delambdafy:method closure classes. These classes are generated at * top-level in the delambdafy phase, no special support is required in the backend. * + * See also BCodeHelpers.considerAsTopLevelImplementationArtifact. + * * * Mirror Classes * -------------- @@ -1139,22 +1130,8 @@ object BTypes { * Note that this class should contain information that can only be obtained from the ClassSymbol. * Information that can be computed from the ClassNode should be added to the call graph instead. * - * @param traitImplClassSelfType `Some(tp)` if this InlineInfo describes a trait, and the `self` - * parameter type of the methods in the implementation class is not - * the trait itself. Example: - * trait T { self: U => def f = 1 } - * Generates something like: - * class T$class { static def f(self: U) = 1 } - * - * In order to inline a trat method call, the INVOKEINTERFACE is - * rewritten to an INVOKESTATIC of the impl class, so we need the - * self type (U) to get the right signature. - * - * `None` if the self type is the interface type, or if this - * InlineInfo does not describe a trait. - * * @param isEffectivelyFinal True if the class cannot have subclasses: final classes, module - * classes, trait impl classes. + * classes. * * @param sam If this class is a SAM type, the SAM's "$name$descriptor". * @@ -1166,26 +1143,41 @@ object BTypes { * InlineInfo, for example if some classfile could not be found on * the classpath. This warning can be reported later by the inliner. */ - final case class InlineInfo(traitImplClassSelfType: Option[InternalName], - isEffectivelyFinal: Boolean, + final case class InlineInfo(isEffectivelyFinal: Boolean, sam: Option[String], methodInfos: Map[String, MethodInlineInfo], - warning: Option[ClassInlineInfoWarning]) + warning: Option[ClassInlineInfoWarning]) { + /** + * A super call (invokespecial) to a default method T.m is only allowed if the interface T is + * a direct parent of the class. Super calls are introduced for example in Mixin when generating + * forwarder methods: + * + * trait T { override def clone(): Object = "hi" } + * trait U extends T + * class C extends U + * + * The class C gets a forwarder that invokes T.clone(). During code generation the interface T + * is added as direct parent to class C. Note that T is not a (direct) parent in the frontend + * type of class C. + * + * All interfaces that are added to a class during code generation are added to this buffer and + * stored in the InlineInfo classfile attribute. This ensures that the ClassBTypes for a + * specific class is the same no matter if it's constructed from a Symbol or from a classfile. + * This is tested in BTypesFromClassfileTest. + */ + val lateInterfaces: ListBuffer[InternalName] = ListBuffer.empty + } - val EmptyInlineInfo = InlineInfo(None, false, None, Map.empty, None) + val EmptyInlineInfo = InlineInfo(false, None, Map.empty, None) /** * Metadata about a method, used by the inliner. * * @param effectivelyFinal True if the method cannot be overridden (in Scala) - * @param traitMethodWithStaticImplementation True if the method is an interface method method of - * a trait method and has a static counterpart in the - * implementation class. * @param annotatedInline True if the method is annotated `@inline` * @param annotatedNoInline True if the method is annotated `@noinline` */ final case class MethodInlineInfo(effectivelyFinal: Boolean, - traitMethodWithStaticImplementation: Boolean, annotatedInline: Boolean, annotatedNoInline: Boolean) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala index 3c2ee89b05..d10b6c8dba 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala @@ -96,11 +96,6 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { * scala.Null is mapped to scala.runtime.Null$. This is because there exist no class files * for the Nothing / Null. If used for example as a parameter type, we use the runtime classes * in the classfile method signature. - * - * Note that the referenced class symbol may be an implementation class. For example when - * compiling a mixed-in method that forwards to the static method in the implementation class, - * the class descriptor of the receiver (the implementation class) is obtained by creating the - * ClassBType. */ final def classBTypeFromSymbol(classSym: Symbol): ClassBType = { assert(classSym != NoSymbol, "Cannot create ClassBType from NoSymbol") @@ -159,9 +154,6 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { /** * This method returns the BType for a type reference, for example a parameter type. - * - * If `t` references a class, typeToBType ensures that the class is not an implementation class. - * See also comment on classBTypeFromSymbol, which is invoked for implementation classes. */ final def typeToBType(t: Type): BType = { import definitions.ArrayClass @@ -172,13 +164,12 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { */ def primitiveOrClassToBType(sym: Symbol): BType = { assertClassNotArray(sym) - assert(!sym.isImplClass, sym) primitiveTypeToBType.getOrElse(sym, classBTypeFromSymbol(sym)) } /** * When compiling Array.scala, the type parameter T is not erased and shows up in method - * signatures, e.g. `def apply(i: Int): T`. A TyperRef to T is replaced by ObjectReference. + * signatures, e.g. `def apply(i: Int): T`. A TypeRef for T is replaced by ObjectRef. */ def nonClassTypeRefToBType(sym: Symbol): ClassBType = { assert(sym.isType && isCompilingArray, sym) @@ -191,39 +182,24 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { case TypeRef(_, sym, _) => primitiveOrClassToBType(sym) // Common reference to a type such as scala.Int or java.lang.String case ClassInfoType(_, _, sym) => primitiveOrClassToBType(sym) // We get here, for example, for genLoadModule, which invokes typeToBType(moduleClassSymbol.info) - /* AnnotatedType should (probably) be eliminated by erasure. However we know it happens for - * meta-annotated annotations (@(ann @getter) val x = 0), so we don't emit a warning. - * The type in the AnnotationInfo is an AnnotatedTpe. Tested in jvm/annotations.scala. - */ - case a @ AnnotatedType(_, t) => - debuglog(s"typeKind of annotated type $a") - typeToBType(t) - - /* ExistentialType should (probably) be eliminated by erasure. We know they get here for - * classOf constants: - * class C[T] - * class T { final val k = classOf[C[_]] } - */ - case e @ ExistentialType(_, t) => - debuglog(s"typeKind of existential type $e") - typeToBType(t) - /* The cases below should probably never occur. They are kept for now to avoid introducing * new compiler crashes, but we added a warning. The compiler / library bootstrap and the * test suite don't produce any warning. */ case tp => - currentUnit.warning(tp.typeSymbol.pos, + warning(tp.typeSymbol.pos, s"an unexpected type representation reached the compiler backend while compiling $currentUnit: $tp. " + "If possible, please file a bug on issues.scala-lang.org.") tp match { - case ThisType(ArrayClass) => ObjectRef // was introduced in 9b17332f11 to fix SI-999, but this code is not reached in its test, or any other test - case ThisType(sym) => classBTypeFromSymbol(sym) - case SingleType(_, sym) => primitiveOrClassToBType(sym) - case ConstantType(_) => typeToBType(t.underlying) - case RefinedType(parents, _) => parents.map(typeToBType(_).asClassBType).reduceLeft((a, b) => a.jvmWiseLUB(b).get) + case ThisType(ArrayClass) => ObjectRef // was introduced in 9b17332f11 to fix SI-999, but this code is not reached in its test, or any other test + case ThisType(sym) => classBTypeFromSymbol(sym) + case SingleType(_, sym) => primitiveOrClassToBType(sym) + case ConstantType(_) => typeToBType(t.underlying) + case RefinedType(parents, _) => parents.map(typeToBType(_).asClassBType).reduceLeft((a, b) => a.jvmWiseLUB(b).get) + case AnnotatedType(_, t) => typeToBType(t) + case ExistentialType(_, t) => typeToBType(t) } } } @@ -280,12 +256,12 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { * current phase, for example, after lambdalift, all local classes become member of the enclosing * class. * - * Impl classes are always considered top-level, see comment in BTypes. + * Specialized classes are always considered top-level, see comment in BTypes. */ private def memberClassesForInnerClassTable(classSymbol: Symbol): List[Symbol] = classSymbol.info.decls.collect({ case sym if sym.isClass && !considerAsTopLevelImplementationArtifact(sym) => sym - case sym if sym.isModule && !considerAsTopLevelImplementationArtifact(sym) => // impl classes get the lateMODULE flag in mixin + case sym if sym.isModule && !considerAsTopLevelImplementationArtifact(sym) => val r = exitingPickler(sym.moduleClass) assert(r != NoSymbol, sym.fullLocationString) r @@ -333,11 +309,10 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { ) } - // Check for isImplClass: trait implementation classes have NoSymbol as superClass // Check for hasAnnotationFlag for SI-9393: the classfile / java source parsers add // scala.annotation.Annotation as superclass to java annotations. In reality, java // annotation classfiles have superclass Object (like any interface classfile). - val superClassSym = if (classSym.isImplClass || classSym.hasJavaAnnotationFlag) ObjectClass else { + val superClassSym = if (classSym.hasJavaAnnotationFlag) ObjectClass else { val sc = classSym.superClass // SI-9393: Java annotation classes don't have the ABSTRACT/INTERFACE flag, so they appear // (wrongly) as superclasses. Fix this for BTypes: the java annotation will appear as interface @@ -396,8 +371,8 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { } val companionModuleMembers = if (considerAsTopLevelImplementationArtifact(classSym)) Nil else { - // If this is a top-level non-impl (*) class, the member classes of the companion object are - // added as members of the class. For example: + // If this is a top-level class, the member classes of the companion object are added as + // members of the class. For example: // class C { } // object C { // class D @@ -408,11 +383,6 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { // (done by buildNestedInfo). See comment in BTypes. // For consistency, the InnerClass entry for D needs to be present in C - to Java it looks // like D is a member of C, not C$. - // - // (*) We exclude impl classes: if the classfile for the impl class exists on the classpath, - // a linkedClass symbol is found for which isTopLevelModule is true, so we end up searching - // members of that weird impl-class-module-class-symbol. that search probably cannot return - // any classes, but it's better to exclude it. val javaCompatMembers = { if (linkedClass != NoSymbol && isTopLevelModuleClass(linkedClass)) // phase travel to exitingPickler: this makes sure that memberClassesForInnerClassTable only sees member @@ -470,7 +440,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { assert(innerClassSym.isClass, s"Cannot build NestedInfo for non-class symbol $innerClassSym") val isTopLevel = innerClassSym.rawowner.isPackageClass - // impl classes are considered top-level, see comment in BTypes + // specialized classes are considered top-level, see comment in BTypes if (isTopLevel || considerAsTopLevelImplementationArtifact(innerClassSym)) None else if (innerClassSym.rawowner.isTerm) { // This case should never be reached: the lambdalift phase mutates the rawowner field of all @@ -603,26 +573,16 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { */ final def isTopLevelModuleClass(sym: Symbol): Boolean = exitingPickler { // phase travel to pickler required for isNestedClass (looks at owner) - val r = sym.isModuleClass && !sym.isNestedClass - // The mixin phase adds the `lateMODULE` flag to trait implementation classes. Since the flag - // is late, it should not be visible here inside the time travel. We check this. - if (r) assert(!sym.isImplClass, s"isModuleClass should be false for impl class $sym") - r + sym.isModuleClass && !sym.isNestedClass } /** * True for module classes of modules that are top-level or owned only by objects. Module classes - * for such objects will get a MODULE$ flag and a corresponding static initializer. + * for such objects will get a MODULE$ field and a corresponding static initializer. */ final def isStaticModuleClass(sym: Symbol): Boolean = { - /* (1) Phase travel to to pickler is required to exclude implementation classes; they have the - * lateMODULEs after mixin, so isModuleClass would be true. - * (2) isStaticModuleClass is a source-level property. See comment on isOriginallyStaticOwner. - */ - exitingPickler { // (1) - sym.isModuleClass && - isOriginallyStaticOwner(sym.originalOwner) // (2) - } + sym.isModuleClass && + isOriginallyStaticOwner(sym.originalOwner) // isStaticModuleClass is a source-level property, see comment on isOriginallyStaticOwner } // legacy, to be removed when the @remote annotation gets removed @@ -684,7 +644,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { val finalFlag = ( (((sym.rawflags & symtab.Flags.FINAL) != 0) || isTopLevelModuleClass(sym)) - && !sym.enclClass.isInterface + && !sym.enclClass.isTrait && !sym.isClassConstructor && !sym.isMutable // lazy vals and vars both ) @@ -697,12 +657,12 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { GenBCode.mkFlags( if (privateFlag) ACC_PRIVATE else ACC_PUBLIC, if ((sym.isDeferred && !sym.hasFlag(symtab.Flags.JAVA_DEFAULTMETHOD))|| sym.hasAbstractFlag) ACC_ABSTRACT else 0, - if (sym.isInterface) ACC_INTERFACE else 0, + if (sym.isTraitOrInterface) ACC_INTERFACE else 0, if (finalFlag && !sym.hasAbstractFlag) ACC_FINAL else 0, if (sym.isStaticMember) ACC_STATIC else 0, if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0, if (sym.isArtifact) ACC_SYNTHETIC else 0, - if (sym.isClass && !sym.isInterface) ACC_SUPER else 0, + if (sym.isClass && !sym.isTraitOrInterface) ACC_SUPER else 0, if (sym.hasJavaEnumFlag) ACC_ENUM else 0, if (sym.isVarargsMethod) ACC_VARARGS else 0, if (sym.hasFlag(symtab.Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0, diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala index 13f4738d3d..4287c24dc8 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala @@ -42,15 +42,15 @@ object BackendReporting { def assertionError(message: String): Nothing = throw new AssertionError(message) implicit class RightBiasedEither[A, B](val v: Either[A, B]) extends AnyVal { - def map[U](f: B => U) = v.right.map(f) - def flatMap[BB](f: B => Either[A, BB]) = v.right.flatMap(f) + def map[C](f: B => C): Either[A, C] = v.right.map(f) + def flatMap[C](f: B => Either[A, C]): Either[A, C] = v.right.flatMap(f) def withFilter(f: B => Boolean)(implicit empty: A): Either[A, B] = v match { case Left(_) => v case Right(e) => if (f(e)) v else Left(empty) // scalaz.\/ requires an implicit Monoid m to get m.empty } - def foreach[U](f: B => U) = v.right.foreach(f) + def foreach[U](f: B => U): Unit = v.right.foreach(f) - def getOrElse[BB >: B](alt: => BB): BB = v.right.getOrElse(alt) + def getOrElse[C >: B](alt: => C): C = v.right.getOrElse(alt) /** * Get the value, fail with an assertion if this is an error. @@ -101,11 +101,14 @@ object BackendReporting { else "" } - case MethodNotFound(name, descriptor, ownerInternalName, missingClasses) => - val (javaDef, others) = missingClasses.partition(_.definedInJavaSource) - s"The method $name$descriptor could not be found in the class $ownerInternalName or any of its parents." + - (if (others.isEmpty) "" else others.map(_.internalName).mkString("\nNote that the following parent classes could not be found on the classpath: ", ", ", "")) + - (if (javaDef.isEmpty) "" else javaDef.map(_.internalName).mkString("\nNote that the following parent classes are defined in Java sources (mixed compilation), no bytecode is available: ", ",", "")) + case MethodNotFound(name, descriptor, ownerInternalName, missingClass) => + val missingClassWarning = missingClass match { + case None => "" + case Some(c) => + if (c.definedInJavaSource) s"\nNote that the parent class ${c.internalName} is defined in a Java source (mixed compilation), no bytecode is available." + else s"\nNote that the parent class ${c.internalName} could not be found on the classpath." + } + s"The method $name$descriptor could not be found in the class $ownerInternalName or any of its parents." + missingClassWarning case FieldNotFound(name, descriptor, ownerInternalName, missingClass) => s"The field node $name$descriptor could not be found because the classfile $ownerInternalName cannot be found on the classpath." + @@ -127,7 +130,7 @@ object BackendReporting { } case class ClassNotFound(internalName: InternalName, definedInJavaSource: Boolean) extends MissingBytecodeWarning - case class MethodNotFound(name: String, descriptor: String, ownerInternalNameOrArrayDescriptor: InternalName, missingClasses: List[ClassNotFound]) extends MissingBytecodeWarning { + case class MethodNotFound(name: String, descriptor: String, ownerInternalNameOrArrayDescriptor: InternalName, missingClass: Option[ClassNotFound]) extends MissingBytecodeWarning { def isArrayMethod = ownerInternalNameOrArrayDescriptor.charAt(0) == '[' } case class FieldNotFound(name: String, descriptor: String, ownerInternalName: InternalName, missingClass: Option[ClassNotFound]) extends MissingBytecodeWarning @@ -170,9 +173,6 @@ object BackendReporting { case MethodInlineInfoError(_, _, _, cause) => s"Error while computing the inline information for method $warningMessageSignature:\n" + cause - - case RewriteTraitCallToStaticImplMethodFailed(_, _, _, cause) => - cause.toString } def emitWarning(settings: ScalaSettings): Boolean = this match { @@ -182,15 +182,12 @@ object BackendReporting { case MethodInlineInfoMissing(_, _, _, None) => settings.YoptWarningNoInlineMissingBytecode case MethodInlineInfoError(_, _, _, cause) => cause.emitWarning(settings) - - case RewriteTraitCallToStaticImplMethodFailed(_, _, _, cause) => cause.emitWarning(settings) } } case class MethodInlineInfoIncomplete(declarationClass: InternalName, name: String, descriptor: String, cause: ClassInlineInfoWarning) extends CalleeInfoWarning case class MethodInlineInfoMissing(declarationClass: InternalName, name: String, descriptor: String, cause: Option[ClassInlineInfoWarning]) extends CalleeInfoWarning case class MethodInlineInfoError(declarationClass: InternalName, name: String, descriptor: String, cause: NoClassBTypeInfo) extends CalleeInfoWarning - case class RewriteTraitCallToStaticImplMethodFailed(declarationClass: InternalName, name: String, descriptor: String, cause: OptimizerWarning) extends CalleeInfoWarning sealed trait CannotInlineWarning extends OptimizerWarning { def calleeDeclarationClass: InternalName diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala index 1d29fdee10..84f6d87c5c 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala @@ -6,7 +6,7 @@ package scala.tools.nsc package backend.jvm -import java.io.{ DataOutputStream, FileOutputStream, IOException, OutputStream, File => JFile } +import java.io.{ DataOutputStream, FileOutputStream, IOException, File => JFile } import scala.tools.nsc.io._ import java.util.jar.Attributes.Name import scala.language.postfixOps diff --git a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala index 3617f3d863..1feca56923 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala @@ -1,7 +1,6 @@ package scala.tools.nsc package backend.jvm -import scala.annotation.switch import scala.tools.asm import scala.tools.nsc.backend.jvm.BTypes.InternalName @@ -11,7 +10,7 @@ import scala.tools.nsc.backend.jvm.BTypes.InternalName * * The symbols used to initialize the ClassBTypes may change from one compiler run to the next. To * make sure the definitions are consistent with the symbols in the current run, the - * `intializeCoreBTypes` method in BTypesFromSymbols creates a new instance of CoreBTypes in each + * `initializeCoreBTypes` method in BTypesFromSymbols creates a new instance of CoreBTypes in each * compiler run. * * The class BTypesFromSymbols does not directly reference CoreBTypes, but CoreBTypesProxy. The @@ -31,7 +30,7 @@ import scala.tools.nsc.backend.jvm.BTypes.InternalName class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { import bTypes._ import global._ - import rootMirror.{requiredClass, requiredModule, getRequiredClass, getClassIfDefined} + import rootMirror.{requiredClass, getRequiredClass, getClassIfDefined} import definitions._ /** @@ -108,6 +107,7 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { lazy val juHashMapRef : ClassBType = classBTypeFromSymbol(JavaUtilHashMap) // java/util/HashMap lazy val sbScalaBeanInfoRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.beans.ScalaBeanInfo]) lazy val jliSerializedLambdaRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.SerializedLambda]) + lazy val jliMethodHandleRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodHandle]) lazy val jliMethodHandlesRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodHandles]) lazy val jliMethodHandlesLookupRef : ClassBType = classBTypeFromSymbol(exitingPickler(getRequiredClass("java.lang.invoke.MethodHandles.Lookup"))) // didn't find a reliable non-stringly-typed way that works for inner classes in the backend lazy val jliMethodTypeRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodType]) @@ -219,14 +219,12 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { // enumeration of specialized classes is temporary, while we still use the java-defined JFunctionN. // once we switch to ordinary FunctionN, we can use specializedSubclasses just like for tuples. - private def functionClasses(base: String): Set[Symbol] = { - def primitives = Iterator("B", "S", "I", "J", "C", "F", "D", "Z", "V") + private def specializedJFunctionSymbols(base: String): Seq[Symbol] = { + def primitives = Seq("B", "S", "I", "J", "C", "F", "D", "Z", "V") def ijfd = Iterator("I", "J", "F", "D") def ijfdzv = Iterator("I", "J", "F", "D", "Z", "V") def ijd = Iterator("I", "J", "D") - val classNames = Set.empty[String] ++ { - (0 to 22).map(base + _) - } ++ { + val classNames = { primitives.map(base + "0$mc" + _ + "$sp") // Function0 } ++ { // return type specializations appear first in the name string (alphabetical sorting) @@ -237,7 +235,7 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { classNames map getRequiredClass } - lazy val srJFunctionRefs: Set[InternalName] = functionClasses("scala.runtime.java8.JFunction").map(classBTypeFromSymbol(_).internalName) + lazy val functionRefs: Set[InternalName] = (FunctionClass.seq ++ specializedJFunctionSymbols("scala.runtime.java8.JFunction")).map(classBTypeFromSymbol(_).internalName).toSet lazy val typeOfArrayOp: Map[Int, BType] = { import scalaPrimitives._ @@ -254,7 +252,7 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) { ) } - lazy val hashMethodSym: Symbol = getMember(ScalaRunTimeModule, nme.hash_) + lazy val hashMethodSym: Symbol = getMember(RuntimeStaticsModule, nme.anyHash) // TODO @lry avoiding going through through missingHook for every line in the REPL: https://github.com/scala/scala/commit/8d962ed4ddd310cc784121c426a2e3f56a112540 lazy val AndroidParcelableInterface : Symbol = getClassIfDefined("android.os.Parcelable") @@ -323,6 +321,7 @@ trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] { def jliCallSiteRef : ClassBType def jliMethodTypeRef : ClassBType def jliSerializedLambdaRef : ClassBType + def jliMethodHandleRef : ClassBType def jliMethodHandlesLookupRef : ClassBType def srBoxesRunTimeRef : ClassBType def srBoxedUnitRef : ClassBType @@ -343,7 +342,7 @@ trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] { def srRefConstructors : Map[InternalName, MethodNameAndType] def tupleClassConstructors : Map[InternalName, MethodNameAndType] - def srJFunctionRefs: Set[InternalName] + def functionRefs: Set[InternalName] def lambdaMetaFactoryBootstrapHandle : asm.Handle def lambdaDeserializeBootstrapHandle : asm.Handle @@ -386,6 +385,7 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: def juHashMapRef : ClassBType = _coreBTypes.juHashMapRef def sbScalaBeanInfoRef : ClassBType = _coreBTypes.sbScalaBeanInfoRef def jliSerializedLambdaRef : ClassBType = _coreBTypes.jliSerializedLambdaRef + def jliMethodHandleRef : ClassBType = _coreBTypes.jliMethodHandleRef def jliMethodHandlesRef : ClassBType = _coreBTypes.jliMethodHandlesRef def jliMethodHandlesLookupRef : ClassBType = _coreBTypes.jliMethodHandlesLookupRef def jliMethodTypeRef : ClassBType = _coreBTypes.jliMethodTypeRef @@ -410,7 +410,7 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: def srRefConstructors : Map[InternalName, MethodNameAndType] = _coreBTypes.srRefConstructors def tupleClassConstructors : Map[InternalName, MethodNameAndType] = _coreBTypes.tupleClassConstructors - def srJFunctionRefs: Set[InternalName] = _coreBTypes.srJFunctionRefs + def functionRefs: Set[InternalName] = _coreBTypes.functionRefs def srSymbolLiteral : ClassBType = _coreBTypes.srSymbolLiteral def srStructuralCallSite : ClassBType = _coreBTypes.srStructuralCallSite diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala index b0ec37db97..3520d57599 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala @@ -135,7 +135,7 @@ abstract class GenBCode extends BCodeSyncAndTry { return } else { - try { withCurrentUnit(item.cunit)(visit(item)) } + try { withCurrentUnitNoLog(item.cunit)(visit(item)) } catch { case ex: Throwable => ex.printStackTrace() @@ -187,7 +187,7 @@ abstract class GenBCode extends BCodeSyncAndTry { // -------------- "plain" class -------------- val pcb = new PlainClassBuilder(cunit) pcb.genPlainClass(cd) - val outF = if (needsOutFolder) getOutFolder(claszSymbol, pcb.thisName, cunit) else null + val outF = if (needsOutFolder) getOutFolder(claszSymbol, pcb.thisBType.internalName, cunit) else null val plainC = pcb.cnode // -------------- bean info class, if needed -------------- @@ -221,7 +221,7 @@ abstract class GenBCode extends BCodeSyncAndTry { */ class Worker2 { def runGlobalOptimizations(): Unit = { - import scala.collection.convert.decorateAsScala._ + import scala.collection.JavaConverters._ // add classes to the bytecode repo before building the call graph: the latter needs to // look up classes and methods in the code repo. diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala index 0d6ef93a26..f94642389d 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala @@ -3,18 +3,16 @@ package backend.jvm package analysis import scala.annotation.switch -import scala.tools.asm.{Handle, Type, Label} +import scala.tools.asm.{Handle, Type} import scala.tools.asm.Opcodes._ import scala.tools.asm.tree._ import scala.tools.asm.tree.analysis.{Frame, BasicInterpreter, Analyzer, Value} import GenBCode._ import scala.tools.nsc.backend.jvm.BTypes._ -import scala.tools.nsc.backend.jvm.opt.BytecodeUtils import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._ import java.lang.invoke.LambdaMetafactory import scala.collection.mutable -import scala.collection.convert.decorateAsJava._ -import scala.collection.convert.decorateAsScala._ +import scala.collection.JavaConverters._ /** * This component hosts tools and utilities used in the backend that require access to a `BTypes` @@ -125,7 +123,7 @@ class BackendUtils[BT <: BTypes](val btypes: BT) { private val anonfunAdaptedName = """.*\$anonfun\$\d+\$adapted""".r def hasAdaptedImplMethod(closureInit: ClosureInstantiation): Boolean = { - isrJFunctionType(Type.getReturnType(closureInit.lambdaMetaFactoryCall.indy.desc).getInternalName) && + isBuiltinFunctionType(Type.getReturnType(closureInit.lambdaMetaFactoryCall.indy.desc).getInternalName) && anonfunAdaptedName.pattern.matcher(closureInit.lambdaMetaFactoryCall.implMethod.getName).matches } @@ -250,7 +248,7 @@ class BackendUtils[BT <: BTypes](val btypes: BT) { } } - def isrJFunctionType(internalName: InternalName): Boolean = srJFunctionRefs(internalName) + def isBuiltinFunctionType(internalName: InternalName): Boolean = functionRefs(internalName) /** * Visit the class node and collect all referenced nested classes. @@ -273,7 +271,7 @@ class BackendUtils[BT <: BTypes](val btypes: BT) { } // we are only interested in the class references in the descriptor, so we can skip over - // primitves and the brackets of array descriptors + // primitives and the brackets of array descriptors def visitDescriptor(desc: String): Unit = (desc.charAt(0): @switch) match { case '(' => val internalNames = mutable.ListBuffer.empty[String] @@ -455,7 +453,7 @@ class BackendUtils[BT <: BTypes](val btypes: BT) { insn match { case v: VarInsnNode => val longSize = if (isSize2LoadOrStore(v.getOpcode)) 1 else 0 - maxLocals = math.max(maxLocals, v.`var` + longSize + 1) // + 1 becauase local numbers are 0-based + maxLocals = math.max(maxLocals, v.`var` + longSize + 1) // + 1 because local numbers are 0-based case i: IincInsnNode => maxLocals = math.max(maxLocals, i.`var` + 1) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerImpl.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerImpl.scala index 894799bf36..8af4bd4d5d 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerImpl.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerImpl.scala @@ -15,11 +15,10 @@ import scala.tools.asm.{Type, MethodVisitor} import scala.tools.asm.Opcodes._ import scala.tools.asm.tree._ import scala.tools.asm.tree.analysis._ -import scala.tools.nsc.backend.jvm.BTypes.InternalName import opt.BytecodeUtils._ -import scala.collection.convert.decorateAsScala._ +import scala.collection.JavaConverters._ /** * This class provides additional queries over ASM's built-in `SourceValue` analysis. @@ -441,10 +440,10 @@ trait ProdConsAnalyzerImpl { * return a; * } * - * In the first frame of the method, the SoruceValue for parameter `a` gives an empty set of + * In the first frame of the method, the SourceValue for parameter `a` gives an empty set of * producer instructions. * - * In the frame of the `IRETURN` instruction, the SoruceValue for parameter `a` lists a single + * In the frame of the `IRETURN` instruction, the SourceValue for parameter `a` lists a single * producer instruction: the `ISTORE 1`. This makes it look as if there was a single producer for * `a`, where in fact it might still hold the parameter's initial value. */ diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/package.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/package.scala index f1ac703532..ef961941a0 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/package.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/package.scala @@ -43,7 +43,7 @@ package scala.tools.nsc.backend.jvm * - Stores a frame for each instruction * - `merge` function takes an instruction and a frame, merges the existing frame for that instr * (from the frames array) with the new frame passed as argument. - * if the frame changed, puts the instruction on the work queue (fixpiont). + * if the frame changed, puts the instruction on the work queue (fixpoint). * - initial frame: initialized for first instr by calling interpreter.new[...]Value * for each slot (locals and params), stored in frames[firstInstr] by calling `merge` * - work queue of instructions (`queue` array, `top` index for next instruction to analyze) @@ -191,7 +191,7 @@ package scala.tools.nsc.backend.jvm * I measured nullness analysis (which tracks aliases) and a SimpleValue analysis. Nullness runs * roughly 5x slower (because of alias tracking) at every problem size - this factor doesn't change. * - * The numbers below are for nullness. Note that the the last column is constant, i.e., the running + * The numbers below are for nullness. Note that the last column is constant, i.e., the running * time is proportional to #ins * #loc^2. Therefore we use this factor when limiting the maximal * method size for running an analysis. * diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/BoxUnbox.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/BoxUnbox.scala index 16fe2e5cff..a4cd8fce1e 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BoxUnbox.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BoxUnbox.scala @@ -12,7 +12,7 @@ import scala.tools.asm.Type import scala.tools.asm.Opcodes._ import scala.tools.asm.tree._ import scala.collection.mutable -import scala.collection.convert.decorateAsScala._ +import scala.collection.JavaConverters._ import scala.tools.nsc.backend.jvm.BTypes.InternalName import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._ diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala index 4492d0baf5..16590ec75c 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala @@ -9,12 +9,11 @@ package opt import scala.tools.asm import asm.tree._ -import scala.collection.convert.decorateAsScala._ -import scala.collection.concurrent +import scala.collection.JavaConverters._ +import scala.collection.{concurrent, mutable} import scala.tools.asm.Attribute import scala.tools.nsc.backend.jvm.BackendReporting._ -import scala.tools.nsc.io.AbstractFile -import scala.tools.nsc.util.ClassFileLookup +import scala.tools.nsc.util.ClassPath import BytecodeUtils._ import ByteCodeRepository._ import BTypes.InternalName @@ -26,7 +25,7 @@ import java.util.concurrent.atomic.AtomicLong * * @param classPath The compiler classpath where classfiles are searched and read from. */ -class ByteCodeRepository[BT <: BTypes](val classPath: ClassFileLookup[AbstractFile], val btypes: BT) { +class ByteCodeRepository[BT <: BTypes](val classPath: ClassPath, val btypes: BT) { import btypes._ /** @@ -132,38 +131,135 @@ class ByteCodeRepository[BT <: BTypes](val classPath: ClassFileLookup[AbstractFi * The method node for a method matching `name` and `descriptor`, accessed in class `ownerInternalNameOrArrayDescriptor`. * The declaration of the method may be in one of the parents. * - * TODO: make sure we always return the right method, the one being invoked. write tests. - * - if there's an abstract and a concrete one. could possibly somehow the abstract be returned? - * - with traits and default methods, if there is more than one default method inherited and - * no override: what should be returned? We should not just inline one of the two. + * Note that the JVM spec performs method lookup in two steps: resolution and selection. + * + * Method resolution, defined in jvms-5.4.3.3 and jvms-5.4.3.4, is the first step and is identical + * for all invocation styles (virtual, interface, special, static). If C is the receiver class + * in the invocation instruction: + * 1 find a matching method (name and descriptor) in C + * 2 then in C's superclasses + * 3 then find the maximally-specific matching superinterface methods, succeed if there's a + * single non-abstract one. static and private methods in superinterfaces are not considered. + * 4 then pick a random non-static, non-private superinterface method. + * 5 then fail. + * + * Note that for an `invokestatic` instruction, a method reference `B.m` may resolve to `A.m`, if + * class `B` doesn't specify a matching method `m`, but the parent `A` does. + * + * Selection depends on the invocation style and is defined in jvms-6.5. + * - invokestatic: invokes the resolved method + * - invokevirtual / invokeinterface: searches for an override of the resolved method starting + * at the dynamic receiver type. the search procedure is basically the same as in resolution, + * but it fails at 4 instead of picking a superinterface method at random. + * - invokespecial: if C is the receiver in the invocation instruction, searches for an override + * of the resolved method starting at + * - the superclass of the current class, if C is a superclass of the current class + * - C otherwise + * again, the search procedure is the same. + * + * In the method here we implement method *resolution*. Whether or not the returned method is + * actually invoked at runtime depends on the invocation instruction and the class hierarchy, so + * the users (e.g. the inliner) have to be aware of method selection. + * + * Note that the returned method may be abstract (ACC_ABSTRACT), native (ACC_NATIVE) or signature + * polymorphic (methods `invoke` and `invokeExact` in class `MehtodHandles`). * * @return The [[MethodNode]] of the requested method and the [[InternalName]] of its declaring - * class, or an error message if the method could not be found. + * class, or an error message if the method could not be found. An error message is also + * returned if method resolution results in multiple default methods. */ def methodNode(ownerInternalNameOrArrayDescriptor: String, name: String, descriptor: String): Either[MethodNotFound, (MethodNode, InternalName)] = { - // on failure, returns a list of class names that could not be found on the classpath - def methodNodeImpl(ownerInternalName: InternalName): Either[List[ClassNotFound], (MethodNode, InternalName)] = { - classNode(ownerInternalName) match { - case Left(e) => Left(List(e)) - case Right(c) => - c.methods.asScala.find(m => m.name == name && m.desc == descriptor) match { - case Some(m) => Right((m, ownerInternalName)) - case None => findInParents(Option(c.superName) ++: c.interfaces.asScala.toList, Nil) - } + def findMethod(c: ClassNode): Option[MethodNode] = c.methods.asScala.find(m => m.name == name && m.desc == descriptor) + + // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9: "In Java SE 8, the only + // signature polymorphic methods are the invoke and invokeExact methods of the class MethodHandle. + def isSignaturePolymorphic(owner: InternalName) = owner == coreBTypes.jliMethodHandleRef.internalName && (name == "invoke" || name == "invokeExact") + + // Note: if `owner` is an interface, in the first iteration we search for a matching member in the interface itself. + // If that fails, the recursive invocation checks in the superclass (which is Object) with `publicInstanceOnly == true`. + // This is specified in jvms-5.4.3.4: interface method resolution only returns public, non-static methods of Object. + def findInSuperClasses(owner: ClassNode, publicInstanceOnly: Boolean = false): Either[ClassNotFound, Option[(MethodNode, InternalName)]] = { + findMethod(owner) match { + case Some(m) if !publicInstanceOnly || (isPublicMethod(m) && !isStaticMethod(m)) => Right(Some((m, owner.name))) + case None => + if (isSignaturePolymorphic(owner.name)) Right(Some((owner.methods.asScala.find(_.name == name).get, owner.name))) + else if (owner.superName == null) Right(None) + else classNode(owner.superName).flatMap(findInSuperClasses(_, isInterface(owner))) } } - // find the MethodNode in one of the parent classes - def findInParents(parents: List[InternalName], failedClasses: List[ClassNotFound]): Either[List[ClassNotFound], (MethodNode, InternalName)] = parents match { - case x :: xs => methodNodeImpl(x).left.flatMap(failed => findInParents(xs, failed ::: failedClasses)) - case Nil => Left(failedClasses) + def findInInterfaces(initialOwner: ClassNode): Either[ClassNotFound, Option[(MethodNode, InternalName)]] = { + val visited = mutable.Set.empty[InternalName] + val found = mutable.ListBuffer.empty[(MethodNode, ClassNode)] + + def findIn(owner: ClassNode): Option[ClassNotFound] = { + for (i <- owner.interfaces.asScala if !visited(i)) classNode(i) match { + case Left(e) => return Some(e) + case Right(c) => + visited += i + // abstract and static methods are excluded, see jvms-5.4.3.3 + for (m <- findMethod(c) if !isPrivateMethod(m) && !isStaticMethod(m)) found += ((m, c)) + val recusionResult = findIn(c) + if (recusionResult.isDefined) return recusionResult + } + None + } + + findIn(initialOwner) + + val result = + if (found.size <= 1) found.headOption + else { + val maxSpecific = found.filterNot({ + case (method, owner) => + isAbstractMethod(method) || { + val ownerTp = classBTypeFromClassNode(owner) + found exists { + case (other, otherOwner) => + (other ne method) && { + val otherTp = classBTypeFromClassNode(otherOwner) + otherTp.isSubtypeOf(ownerTp).get + } + } + } + }) + // (*) note that if there's no single, non-abstract, maximally-specific method, the jvm + // method resolution (jvms-5.4.3.3) returns any of the non-private, non-static parent + // methods at random (abstract or concrete). + // we chose not to do this here, to prevent the inliner from potentially inlining the + // wrong method. in other words, we guarantee that a concrete method is only returned if + // it resolves deterministically. + // however, there may be multiple abstract methods inherited. in this case we *do* want + // to return a result to allow performing accessibility checks in the inliner. note that + // for accessibility it does not matter which of these methods is return, as they are all + // non-private (i.e., public, protected is not possible, jvms-4.1). + // the remaining case (when there's no max-specific method, but some non-abstract one) + // does not occur in bytecode generated by scalac or javac. we return no result in this + // case. this may at worst prevent some optimizations from happening. + if (maxSpecific.size == 1) maxSpecific.headOption + else if (found.forall(p => isAbstractMethod(p._1))) found.headOption // (*) + else None + } + Right(result.map(p => (p._1, p._2.name))) } // In a MethodInsnNode, the `owner` field may be an array descriptor, for example when invoking `clone`. We don't have a method node to return in this case. - if (ownerInternalNameOrArrayDescriptor.charAt(0) == '[') - Left(MethodNotFound(name, descriptor, ownerInternalNameOrArrayDescriptor, Nil)) - else - methodNodeImpl(ownerInternalNameOrArrayDescriptor).left.map(MethodNotFound(name, descriptor, ownerInternalNameOrArrayDescriptor, _)) + if (ownerInternalNameOrArrayDescriptor.charAt(0) == '[') { + Left(MethodNotFound(name, descriptor, ownerInternalNameOrArrayDescriptor, None)) + } else { + def notFound(cnf: Option[ClassNotFound]) = Left(MethodNotFound(name, descriptor, ownerInternalNameOrArrayDescriptor, cnf)) + val res: Either[ClassNotFound, Option[(MethodNode, InternalName)]] = classNode(ownerInternalNameOrArrayDescriptor).flatMap(c => + findInSuperClasses(c) flatMap { + case None => findInInterfaces(c) + case res => Right(res) + } + ) + res match { + case Left(e) => notFound(Some(e)) + case Right(None) => notFound(None) + case Right(Some(res)) => Right(res) + } + } } private def parseClass(internalName: InternalName): Either[ClassNotFound, ClassNode] = { 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 9a90c53d3e..63906d80e5 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala @@ -8,6 +8,7 @@ package backend.jvm package opt import scala.annotation.{tailrec, switch} + import scala.collection.mutable import scala.reflect.internal.util.Collections._ import scala.tools.asm.commons.CodeSizeEvaluator @@ -16,8 +17,7 @@ import scala.tools.asm.{Label, Type} import scala.tools.asm.Opcodes._ import scala.tools.asm.tree._ import GenBCode._ -import scala.collection.convert.decorateAsScala._ -import scala.tools.nsc.backend.jvm.BTypes.InternalName +import scala.collection.JavaConverters._ import scala.tools.nsc.backend.jvm.analysis.InstructionStackEffect object BytecodeUtils { @@ -88,12 +88,21 @@ object BytecodeUtils { def isLoadOrStore(instruction: AbstractInsnNode): Boolean = isLoad(instruction) || isStore(instruction) + def isNonVirtualCall(instruction: AbstractInsnNode): Boolean = { + val op = instruction.getOpcode + op == INVOKESPECIAL || op == INVOKESTATIC + } + def isExecutable(instruction: AbstractInsnNode): Boolean = instruction.getOpcode >= 0 def isConstructor(methodNode: MethodNode): Boolean = { methodNode.name == INSTANCE_CONSTRUCTOR_NAME || methodNode.name == CLASS_CONSTRUCTOR_NAME } + def isPublicMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_PUBLIC) != 0 + + def isPrivateMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_PRIVATE) != 0 + def isStaticMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_STATIC) != 0 def isAbstractMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_ABSTRACT) != 0 @@ -102,10 +111,12 @@ object BytecodeUtils { def isNativeMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_NATIVE) != 0 - def hasCallerSensitiveAnnotation(methodNode: MethodNode) = methodNode.visibleAnnotations != null && methodNode.visibleAnnotations.asScala.exists(_.desc == "Lsun/reflect/CallerSensitive;") + def hasCallerSensitiveAnnotation(methodNode: MethodNode): Boolean = methodNode.visibleAnnotations != null && methodNode.visibleAnnotations.asScala.exists(_.desc == "Lsun/reflect/CallerSensitive;") def isFinalClass(classNode: ClassNode): Boolean = (classNode.access & ACC_FINAL) != 0 + def isInterface(classNode: ClassNode): Boolean = (classNode.access & ACC_INTERFACE) != 0 + def isFinalMethod(methodNode: MethodNode): Boolean = (methodNode.access & (ACC_FINAL | ACC_PRIVATE | ACC_STATIC)) != 0 def isStrictfpMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_STRICT) != 0 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 17255cb880..d241acf7b1 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala @@ -12,7 +12,7 @@ import scala.reflect.internal.util.{NoPosition, Position} import scala.tools.asm.{Opcodes, Type, Handle} import scala.tools.asm.tree._ import scala.collection.{concurrent, mutable} -import scala.collection.convert.decorateAsScala._ +import scala.collection.JavaConverters._ import scala.tools.nsc.backend.jvm.BTypes.InternalName import scala.tools.nsc.backend.jvm.BackendReporting._ import scala.tools.nsc.backend.jvm.analysis._ @@ -131,19 +131,19 @@ class CallGraph[BT <: BTypes](val btypes: BT) { (method, declarationClass) <- byteCodeRepository.methodNode(call.owner, call.name, call.desc): Either[OptimizerWarning, (MethodNode, InternalName)] (declarationClassNode, source) <- byteCodeRepository.classNodeAndSource(declarationClass): Either[OptimizerWarning, (ClassNode, Source)] } yield { - val declarationClassBType = classBTypeFromClassNode(declarationClassNode) - val CallsiteInfo(safeToInline, safeToRewrite, canInlineFromSource, annotatedInline, annotatedNoInline, samParamTypes, warning) = analyzeCallsite(method, declarationClassBType, call.owner, source) - Callee( - callee = method, - calleeDeclarationClass = declarationClassBType, - safeToInline = safeToInline, - safeToRewrite = safeToRewrite, - canInlineFromSource = canInlineFromSource, - annotatedInline = annotatedInline, - annotatedNoInline = annotatedNoInline, - samParamTypes = samParamTypes, - calleeInfoWarning = warning) - } + val declarationClassBType = classBTypeFromClassNode(declarationClassNode) + val info = analyzeCallsite(method, declarationClassBType, call, source) + import info._ + Callee( + callee = method, + calleeDeclarationClass = declarationClassBType, + safeToInline = safeToInline, + canInlineFromSource = canInlineFromSource, + annotatedInline = annotatedInline, + annotatedNoInline = annotatedNoInline, + samParamTypes = info.samParamTypes, + calleeInfoWarning = warning) + } val argInfos = computeArgInfos(callee, call, prodCons) @@ -256,7 +256,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) { /** * Just a named tuple used as return type of `analyzeCallsite`. */ - private case class CallsiteInfo(safeToInline: Boolean, safeToRewrite: Boolean, canInlineFromSource: Boolean, + private case class CallsiteInfo(safeToInline: Boolean, canInlineFromSource: Boolean, annotatedInline: Boolean, annotatedNoInline: Boolean, samParamTypes: IntMap[ClassBType], warning: Option[CalleeInfoWarning]) @@ -264,7 +264,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) { /** * Analyze a callsite and gather meta-data that can be used for inlining decisions. */ - private def analyzeCallsite(calleeMethodNode: MethodNode, calleeDeclarationClassBType: ClassBType, receiverTypeInternalName: InternalName, calleeSource: Source): CallsiteInfo = { + private def analyzeCallsite(calleeMethodNode: MethodNode, calleeDeclarationClassBType: ClassBType, call: MethodInsnNode, calleeSource: Source): CallsiteInfo = { val methodSignature = calleeMethodNode.name + calleeMethodNode.desc try { @@ -277,7 +277,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) { val isAbstract = BytecodeUtils.isAbstractMethod(calleeMethodNode) - val receiverType = classBTypeFromParsedClassfile(receiverTypeInternalName) + val receiverType = classBTypeFromParsedClassfile(call.owner) // (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 // @@ -295,20 +295,17 @@ class CallGraph[BT <: BTypes](val btypes: BT) { // 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 = { + isNonVirtualCall(call) || // SD-86: super calls (invokespecial) can be inlined methodInlineInfo.effectivelyFinal || receiverType.info.orThrow.inlineInfo.isEffectivelyFinal // (1) } - val isRewritableTraitCall = isStaticallyResolved && methodInlineInfo.traitMethodWithStaticImplementation - val warning = calleeDeclarationClassBType.info.orThrow.inlineInfo.warning.map( MethodInlineInfoIncomplete(calleeDeclarationClassBType.internalName, calleeMethodNode.name, calleeMethodNode.desc, _)) // (1) For invocations of final trait methods, the callee isStaticallyResolved but also // abstract. Such a callee is not safe to inline - it needs to be re-written to the // static impl method first (safeToRewrite). - // (2) Final trait methods can be rewritten from the interface to the static implementation - // method to enable inlining. CallsiteInfo( safeToInline = canInlineFromSource && @@ -317,7 +314,6 @@ class CallGraph[BT <: BTypes](val btypes: BT) { !BytecodeUtils.isConstructor(calleeMethodNode) && !BytecodeUtils.isNativeMethod(calleeMethodNode) && !BytecodeUtils.hasCallerSensitiveAnnotation(calleeMethodNode), - safeToRewrite = canInlineFromSource && isRewritableTraitCall, // (2) canInlineFromSource = canInlineFromSource, annotatedInline = methodInlineInfo.annotatedInline, annotatedNoInline = methodInlineInfo.annotatedNoInline, @@ -326,12 +322,12 @@ class CallGraph[BT <: BTypes](val btypes: BT) { case None => val warning = MethodInlineInfoMissing(calleeDeclarationClassBType.internalName, calleeMethodNode.name, calleeMethodNode.desc, calleeDeclarationClassBType.info.orThrow.inlineInfo.warning) - CallsiteInfo(false, false, false, false, false, IntMap.empty, Some(warning)) + CallsiteInfo(false, false, false, false, IntMap.empty, Some(warning)) } } catch { case Invalid(noInfo: NoClassBTypeInfo) => val warning = MethodInlineInfoError(calleeDeclarationClassBType.internalName, calleeMethodNode.name, calleeMethodNode.desc, noInfo) - CallsiteInfo(false, false, false, false, false, IntMap.empty, Some(warning)) + CallsiteInfo(false, false, false, false, IntMap.empty, Some(warning)) } } @@ -386,20 +382,17 @@ class CallGraph[BT <: BTypes](val btypes: BT) { * @param calleeDeclarationClass The class in which the callee is declared * @param safeToInline True if the callee can be safely inlined: it cannot be overridden, * and the inliner settings (project / global) allow inlining it. - * @param safeToRewrite True if the callee is the interface method of a concrete trait method - * that can be safely re-written to the static implementation method. * @param annotatedInline True if the callee is annotated @inline * @param annotatedNoInline True if the callee is annotated @noinline * @param samParamTypes A map from parameter positions to SAM parameter types * @param calleeInfoWarning An inliner warning if some information was not available while * gathering the information about this callee. */ - final case class Callee(callee: MethodNode, calleeDeclarationClass: ClassBType, - safeToInline: Boolean, safeToRewrite: Boolean, canInlineFromSource: Boolean, + final case class Callee(callee: MethodNode, calleeDeclarationClass: btypes.ClassBType, + safeToInline: Boolean, canInlineFromSource: Boolean, annotatedInline: Boolean, annotatedNoInline: Boolean, - samParamTypes: IntMap[ClassBType], + samParamTypes: IntMap[btypes.ClassBType], calleeInfoWarning: Option[CalleeInfoWarning]) { - assert(!(safeToInline && safeToRewrite), s"A callee of ${callee.name} can be either safeToInline or safeToRewrite, but not both.") override def toString = s"Callee($calleeDeclarationClass.${callee.name})" } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala index 58054f85ad..93dc40f318 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala @@ -8,17 +8,17 @@ package backend.jvm package opt import scala.annotation.switch -import scala.collection.{mutable, immutable} +import scala.collection.mutable import scala.collection.immutable.IntMap import scala.reflect.internal.util.NoPosition -import scala.tools.asm.{Handle, Type, Opcodes} +import scala.tools.asm.{Type, Opcodes} import scala.tools.asm.tree._ import scala.tools.nsc.backend.jvm.BTypes.InternalName import BytecodeUtils._ import BackendReporting._ import Opcodes._ import scala.tools.nsc.backend.jvm.opt.ByteCodeRepository.CompilationUnit -import scala.collection.convert.decorateAsScala._ +import scala.collection.JavaConverters._ class ClosureOptimizer[BT <: BTypes](val btypes: BT) { import btypes._ @@ -156,7 +156,7 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) { // TODO: This is maybe over-cautious. // We are checking if the closure body method is accessible at the closure callsite. // If the closure allocation has access to the body method, then the callsite (in the same - // method as the alloction) should have access too. + // method as the allocation) should have access too. val bodyAccessible: Either[OptimizerWarning, Boolean] = for { (bodyMethodNode, declClass) <- byteCodeRepository.methodNode(lambdaBodyHandle.getOwner, lambdaBodyHandle.getName, lambdaBodyHandle.getDesc): Either[OptimizerWarning, (MethodNode, InternalName)] isAccessible <- inliner.memberIsAccessible(bodyMethodNode.access, classBTypeFromParsedClassfile(declClass), classBTypeFromParsedClassfile(lambdaBodyHandle.getOwner), ownerClass) @@ -363,7 +363,6 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) { callee = bodyMethodNode, calleeDeclarationClass = bodyDeclClassType, safeToInline = canInlineFromSource, - safeToRewrite = false, // the lambda body method is not a trait interface method canInlineFromSource = canInlineFromSource, annotatedInline = false, annotatedNoInline = false, diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/CopyProp.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/CopyProp.scala index f91530903d..4163d62df7 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CopyProp.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CopyProp.scala @@ -13,7 +13,7 @@ import scala.tools.asm.Type import scala.tools.asm.Opcodes._ import scala.tools.asm.tree._ import scala.collection.mutable -import scala.collection.convert.decorateAsScala._ +import scala.collection.JavaConverters._ import scala.tools.nsc.backend.jvm.BTypes.InternalName import scala.tools.nsc.backend.jvm.analysis._ import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._ @@ -52,7 +52,7 @@ class CopyProp[BT <: BTypes](val btypes: BT) { var r = init while (it.hasNext) { val n = it.next() - // knownUsed.lenght is the number of locals, `n` may be a stack slot + // knownUsed.length is the number of locals, `n` may be a stack slot if (n < knownUsed.length && knownUsed(n)) return n if (n < r) r = n } @@ -171,7 +171,7 @@ class CopyProp[BT <: BTypes](val btypes: BT) { * * A special case eliminates the creation of unused objects with side-effect-free constructors: * NEW scala/Tuple1; DUP; ALOAD 0; INVOKESPECIAL scala/Tuple1.<init>; POP - * The POP has a signle producer (the DUP), it's easy to eliminate these two. A special case + * The POP has a single producer (the DUP), it's easy to eliminate these two. A special case * is needed to eliminate the INVOKESPECIAL and NEW. */ def eliminatePushPop(method: MethodNode, owner: InternalName): Boolean = { @@ -296,11 +296,11 @@ class CopyProp[BT <: BTypes](val btypes: BT) { /** * Eliminate the closure value produced by `indy`. If the SAM type is known to construct - * without side-effects (e.g. scala/runtime/java8/JFunctionN), the `indy` and its inputs + * without side-effects (e.g. scala/FunctionN), the `indy` and its inputs * are eliminated, otherwise a POP is inserted. */ def handleClosureInst(indy: InvokeDynamicInsnNode): Unit = { - if (isrJFunctionType(Type.getReturnType(indy.desc).getInternalName)) { + if (isBuiltinFunctionType(Type.getReturnType(indy.desc).getInternalName)) { toRemove += indy callGraph.removeClosureInstantiation(indy, method) handleInputs(indy, Type.getArgumentTypes(indy.desc).length) @@ -533,7 +533,7 @@ class CopyProp[BT <: BTypes](val btypes: BT) { } /** - * Try to pair `insn` with its correspondant on the stack + * Try to pair `insn` with its correspondent on the stack * - if the stack top is a store and `insn` is a corresponding load, create a pair * - otherwise, check the two top stack values for `null; store`. if it matches, create * a pair and continue pairing `insn` on the remaining stack diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala index c885a29e16..79d26b0b4e 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala @@ -27,7 +27,7 @@ import scala.tools.nsc.backend.jvm.BackendReporting.UnknownScalaInlineInfoVersio * In principle we could encode the InlineInfo into a Java annotation (instead of a classfile attribute). * However, an attribute allows us to save many bits. In particular, note that the strings in an * InlineInfo are serialized as references to constants in the constant pool, and those strings - * (traitImplClassSelfType, method names, method signatures) would exist in there anyway. So the + * (method names, method signatures) would exist in there anyway. So the * ScalaInlineAttribute remains relatively compact. */ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineInfoAttribute.attributeName) { @@ -47,15 +47,12 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI result.putByte(InlineInfoAttribute.VERSION) - var finalSelfSam = 0 - if (inlineInfo.isEffectivelyFinal) finalSelfSam |= 1 - if (inlineInfo.traitImplClassSelfType.isDefined) finalSelfSam |= 2 - if (inlineInfo.sam.isDefined) finalSelfSam |= 4 - result.putByte(finalSelfSam) - - for (selfInternalName <- inlineInfo.traitImplClassSelfType) { - result.putShort(cw.newUTF8(selfInternalName)) - } + var flags = 0 + if (inlineInfo.isEffectivelyFinal) flags |= 1 + // flags |= 2 // no longer written + if (inlineInfo.sam.isDefined) flags |= 4 + if (inlineInfo.lateInterfaces.nonEmpty) flags |= 8 + result.putByte(flags) for (samNameDesc <- inlineInfo.sam) { val (name, desc) = samNameDesc.span(_ != '(') @@ -75,13 +72,16 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI result.putShort(cw.newUTF8(desc)) var inlineInfo = 0 - if (info.effectivelyFinal) inlineInfo |= 1 - if (info.traitMethodWithStaticImplementation) inlineInfo |= 2 - if (info.annotatedInline) inlineInfo |= 4 - if (info.annotatedNoInline) inlineInfo |= 8 + if (info.effectivelyFinal) inlineInfo |= 1 + // inlineInfo |= 2 // no longer written + if (info.annotatedInline) inlineInfo |= 4 + if (info.annotatedNoInline) inlineInfo |= 8 result.putByte(inlineInfo) } + result.putShort(inlineInfo.lateInterfaces.length) + for (i <- inlineInfo.lateInterfaces) result.putShort(cw.newUTF8(i)) + result } @@ -101,15 +101,13 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI val version = nextByte() if (version == 1) { - val finalSelfSam = nextByte() - val isFinal = (finalSelfSam & 1) != 0 - val hasSelf = (finalSelfSam & 2) != 0 - val hasSam = (finalSelfSam & 4) != 0 - - val self = if (!hasSelf) None else { - val selfName = nextUTF8() - Some(selfName) - } + val flags = nextByte() + val isFinal = (flags & 1) != 0 + val hasSelf = (flags & 2) != 0 + val hasSam = (flags & 4) != 0 + val hasLateInterfaces = (flags & 8) != 0 + + if (hasSelf) nextUTF8() // no longer used val sam = if (!hasSam) None else { val name = nextUTF8() @@ -123,14 +121,21 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI val desc = nextUTF8() val inlineInfo = nextByte() - val isFinal = (inlineInfo & 1) != 0 - val traitMethodWithStaticImplementation = (inlineInfo & 2) != 0 - val isInline = (inlineInfo & 4) != 0 - val isNoInline = (inlineInfo & 8) != 0 - (name + desc, MethodInlineInfo(isFinal, traitMethodWithStaticImplementation, isInline, isNoInline)) + val isFinal = (inlineInfo & 1) != 0 + // = (inlineInfo & 2) != 0 // no longer used + val isInline = (inlineInfo & 4) != 0 + val isNoInline = (inlineInfo & 8) != 0 + (name + desc, MethodInlineInfo(isFinal, isInline, isNoInline)) }).toMap - InlineInfoAttribute(InlineInfo(self, isFinal, sam, infos, None)) + val lateInterfaces = if (!hasLateInterfaces) Nil else { + val numLateInterfaces = nextShort() + (0 until numLateInterfaces).map(_ => nextUTF8()) + } + + val info = InlineInfo(isFinal, sam, infos, None) + info.lateInterfaces ++= lateInterfaces + InlineInfoAttribute(info) } else { val msg = UnknownScalaInlineInfoVersion(cr.getClassName, version) InlineInfoAttribute(BTypes.EmptyInlineInfo.copy(warning = Some(msg))) @@ -140,8 +145,15 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI object InlineInfoAttribute { /** + * Notes: + * - `traitImplClassSelfType` is no longer emitted, `hasTraitImplClassSelfType` is always emitted + * as 0. Similarly, `traitMethodWithStaticImplementation` is always emitted 0. + * - When reading an existing attribute where `hasTraitImplClassSelfType` is 1, the + * `traitImplClassSelfType` is ignored. Also the value of `traitMethodWithStaticImplementation` + * is ignored. + * * [u1] version - * [u1] isEffectivelyFinal (<< 0), hasTraitImplClassSelfType (<< 1), hasSam (<< 2) + * [u1] isEffectivelyFinal (<< 0), hasTraitImplClassSelfType (<< 1), hasSam (<< 2), hasLateInterfaces (<< 3) * [u2]? traitImplClassSelfType (reference) * [u2]? samName (reference) * [u2]? samDescriptor (reference) @@ -149,6 +161,8 @@ object InlineInfoAttribute { * [u2] name (reference) * [u2] descriptor (reference) * [u1] isFinal (<< 0), traitMethodWithStaticImplementation (<< 1), hasInlineAnnotation (<< 2), hasNoInlineAnnotation (<< 3) + * [u2]? numLateInterfaces + * [u2] lateInterface (reference) */ final val VERSION: Byte = 1 @@ -159,4 +173,4 @@ object InlineInfoAttribute { * In order to instruct the ASM framework to de-serialize the ScalaInlineInfo attribute, we need * to pass a prototype instance when running the class reader. */ -object InlineInfoAttributePrototype extends InlineInfoAttribute(InlineInfo(null, false, null, null, null)) +object InlineInfoAttributePrototype extends InlineInfoAttribute(InlineInfo(false, null, null, null)) 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 9847c9db58..f35eaa45e9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala @@ -11,8 +11,7 @@ import scala.annotation.tailrec import scala.tools.asm import asm.Opcodes._ import asm.tree._ -import scala.collection.convert.decorateAsScala._ -import scala.collection.convert.decorateAsJava._ +import scala.collection.JavaConverters._ import AsmUtils._ import BytecodeUtils._ import collection.mutable @@ -27,8 +26,6 @@ class Inliner[BT <: BTypes](val btypes: BT) { import backendUtils._ def runInliner(): Unit = { - rewriteFinalTraitMethodInvocations() - for (request <- collectAndOrderInlineRequests) { val Right(callee) = request.callsite.callee // collectAndOrderInlineRequests returns callsites with a known callee @@ -70,111 +67,6 @@ class Inliner[BT <: BTypes](val btypes: BT) { } } - // Rewriting final trait method callsites to the implementation class enables inlining. - def rewriteFinalTraitMethodInvocations(): Unit = { - // Collect callsites to rewrite before actually rewriting anything. This prevents changing the - // `callsties` map while iterating it. - val toRewrite = mutable.ArrayBuffer.empty[Callsite] - for (css <- callsites.valuesIterator; cs <- css.valuesIterator if doRewriteTraitCallsite(cs)) toRewrite += cs - toRewrite foreach rewriteFinalTraitMethodInvocation - } - - /** - * True for statically resolved trait callsites that should be rewritten to the static implementation method. - */ - def doRewriteTraitCallsite(callsite: Callsite) = callsite.callee match { - case Right(callee) => callee.safeToRewrite - case _ => false - } - - /** - * Rewrite the INVOKEINTERFACE callsite of a final trait method invocation to INVOKESTATIC of the - * corresponding method in the implementation class. This enables inlining final trait methods. - * - * In a final trait method callsite, the callee is safeToInline and the callee method is abstract - * (the receiver type is the interface, so the method is abstract). - */ - def rewriteFinalTraitMethodInvocation(callsite: Callsite): Unit = { - // The analyzer used below needs to have a non-null frame for the callsite instruction - localOpt.minimalRemoveUnreachableCode(callsite.callsiteMethod, callsite.callsiteClass.internalName) - - // If the callsite was eliminated by DCE, do nothing. - if (!callGraph.containsCallsite(callsite)) return - - val Right(Callee(callee, calleeDeclarationClass, _, _, canInlineFromSource, annotatedInline, annotatedNoInline, samParamTypes, infoWarning)) = callsite.callee - - val traitMethodArgumentTypes = asm.Type.getArgumentTypes(callee.desc) - - val implClassInternalName = calleeDeclarationClass.internalName + "$class" - - val selfParamTypeV: Either[OptimizerWarning, ClassBType] = calleeDeclarationClass.info.map(_.inlineInfo.traitImplClassSelfType match { - case Some(internalName) => classBTypeFromParsedClassfile(internalName) - case None => calleeDeclarationClass - }) - - def implClassMethodV(implMethodDescriptor: String): Either[OptimizerWarning, MethodNode] = { - byteCodeRepository.methodNode(implClassInternalName, callee.name, implMethodDescriptor).map(_._1) - } - - // The rewrite reading the implementation class and the implementation method from the bytecode - // repository. If either of the two fails, the rewrite is not performed. - val res = for { - selfParamType <- selfParamTypeV - implMethodDescriptor = asm.Type.getMethodDescriptor(asm.Type.getReturnType(callee.desc), selfParamType.toASMType +: traitMethodArgumentTypes: _*) - implClassMethod <- implClassMethodV(implMethodDescriptor) - implClassBType = classBTypeFromParsedClassfile(implClassInternalName) - selfTypeOk <- calleeDeclarationClass.isSubtypeOf(selfParamType) - } yield { - - // The self parameter type may be incompatible with the trait type. - // trait T { self: S => def foo = 1 } - // The $self parameter type of T$class.foo is S, which may be unrelated to T. If we re-write - // a call to T.foo to T$class.foo, we need to cast the receiver to S, otherwise we get a - // 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) { - // We don't need to worry about the method being too large for running an analysis. - // Callsites of large methods are not added to the call graph. - val analyzer = new AsmAnalyzer(callsite.callsiteMethod, callsite.callsiteClass.internalName, new Analyzer(new SourceInterpreter)) - val receiverValue = analyzer.frameAt(callsite.callsiteInstruction).peekStack(traitMethodArgumentTypes.length) - for (i <- receiverValue.insns.asScala) { - val cast = new TypeInsnNode(CHECKCAST, selfParamType.internalName) - callsite.callsiteMethod.instructions.insert(i, cast) - } - } - - val newCallsiteInstruction = new MethodInsnNode(INVOKESTATIC, implClassInternalName, callee.name, implMethodDescriptor, false) - callsite.callsiteMethod.instructions.insert(callsite.callsiteInstruction, newCallsiteInstruction) - callsite.callsiteMethod.instructions.remove(callsite.callsiteInstruction) - - callGraph.removeCallsite(callsite.callsiteInstruction, callsite.callsiteMethod) - val staticCallSamParamTypes = { - if (selfParamType.info.get.inlineInfo.sam.isEmpty) samParamTypes - 0 - else samParamTypes.updated(0, selfParamType) - } - val staticCallsite = callsite.copy( - callsiteInstruction = newCallsiteInstruction, - callee = Right(Callee( - callee = implClassMethod, - calleeDeclarationClass = implClassBType, - safeToInline = true, - safeToRewrite = false, - canInlineFromSource = canInlineFromSource, - annotatedInline = annotatedInline, - annotatedNoInline = annotatedNoInline, - samParamTypes = staticCallSamParamTypes, - calleeInfoWarning = infoWarning)) - ) - callGraph.addCallsite(staticCallsite) - } - - for (warning <- res.left) { - val Right(callee) = callsite.callee - val newCallee = callee.copy(calleeInfoWarning = Some(RewriteTraitCallToStaticImplMethodFailed(calleeDeclarationClass.internalName, callee.callee.name, callee.callee.desc, warning))) - callGraph.addCallsite(callsite.copy(callee = Right(newCallee))) - } - } - /** * Returns the callsites that can be inlined. Ensures that the returned inline request graph does * not contain cycles. diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala index 7e35d0e7f0..6aaf9734d3 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala @@ -7,11 +7,9 @@ package scala.tools.nsc package backend.jvm package opt -import scala.collection.immutable.IntMap -import scala.tools.asm.Type -import scala.tools.asm.tree.{MethodNode, MethodInsnNode} +import scala.tools.asm.tree.MethodNode import scala.tools.nsc.backend.jvm.BTypes.InternalName -import scala.collection.convert.decorateAsScala._ +import scala.collection.JavaConverters._ import scala.tools.nsc.backend.jvm.BackendReporting.OptimizerWarning class InlinerHeuristics[BT <: BTypes](val bTypes: BT) { @@ -41,7 +39,7 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) { compilingMethods.map(methodNode => { var requests = Set.empty[InlineRequest] callGraph.callsites(methodNode).valuesIterator foreach { - case callsite @ Callsite(_, _, _, Right(Callee(callee, calleeDeclClass, safeToInline, _, canInlineFromSource, calleeAnnotatedInline, _, _, callsiteWarning)), _, _, _, pos, _, _) => + case callsite @ Callsite(_, _, _, Right(Callee(callee, calleeDeclClass, safeToInline, canInlineFromSource, calleeAnnotatedInline, _, _, callsiteWarning)), _, _, _, pos, _, _) => inlineRequest(callsite) match { case Some(Right(req)) => requests += req case Some(Left(w)) => @@ -57,9 +55,7 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) { // 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" def warnMsg = callsiteWarning.map(" Possible reason:\n" + _).getOrElse("") - if (doRewriteTraitCallsite(callsite)) - backendReporting.inlinerWarning(pos, s"$initMsg: the trait method call could not be rewritten to the static implementation method." + warnMsg) - else if (!safeToInline) + if (!safeToInline) backendReporting.inlinerWarning(pos, s"$initMsg: the method is not final and may be overridden." + warnMsg) else backendReporting.inlinerWarning(pos, s"$initMsg." + warnMsg) 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 f486bb0cb9..4e1349257e 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala @@ -8,12 +8,13 @@ package backend.jvm package opt import scala.annotation.{tailrec, switch} + import scala.tools.asm.Type import scala.tools.asm.tree.analysis.Frame import scala.tools.asm.Opcodes._ import scala.tools.asm.tree._ -import scala.collection.{mutable, immutable} -import scala.collection.convert.decorateAsScala._ +import scala.collection.mutable +import scala.collection.JavaConverters._ import scala.tools.nsc.backend.jvm.BTypes.InternalName import scala.tools.nsc.backend.jvm.analysis._ import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._ @@ -504,7 +505,7 @@ class LocalOpt[BT <: BTypes](val btypes: BT) { case v: VarInsnNode if isLive => val longSize = if (isSize2LoadOrStore(v.getOpcode)) 1 else 0 - maxLocals = math.max(maxLocals, v.`var` + longSize + 1) // + 1 becauase local numbers are 0-based + maxLocals = math.max(maxLocals, v.`var` + longSize + 1) // + 1 because local numbers are 0-based case i: IincInsnNode if isLive => maxLocals = math.max(maxLocals, i.`var` + 1) diff --git a/src/compiler/scala/tools/nsc/classpath/AggregateFlatClassPath.scala b/src/compiler/scala/tools/nsc/classpath/AggregateClassPath.scala index 3f06264e3c..6b435542a3 100644 --- a/src/compiler/scala/tools/nsc/classpath/AggregateFlatClassPath.scala +++ b/src/compiler/scala/tools/nsc/classpath/AggregateClassPath.scala @@ -12,16 +12,16 @@ import scala.tools.nsc.util.ClassRepresentation /** * A classpath unifying multiple class- and sourcepath entries. - * Flat classpath can obtain entries for classes and sources independently + * The Classpath can obtain entries for classes and sources independently * so it tries to do operations quite optimally - iterating only these collections * which are needed in the given moment and only as far as it's necessary. + * * @param aggregates classpath instances containing entries which this class processes */ -case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatClassPath { - +case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath { override def findClassFile(className: String): Option[AbstractFile] = { @tailrec - def find(aggregates: Seq[FlatClassPath]): Option[AbstractFile] = + def find(aggregates: Seq[ClassPath]): Option[AbstractFile] = if (aggregates.nonEmpty) { val classFile = aggregates.head.findClassFile(className) if (classFile.isDefined) classFile @@ -31,22 +31,27 @@ case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatCl find(aggregates) } - override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = { - val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className) - + override def findClass(className: String): Option[ClassRepresentation] = { @tailrec - def findEntry[T <: ClassRepClassPathEntry](aggregates: Seq[FlatClassPath], getEntries: FlatClassPath => Seq[T]): Option[T] = + def findEntry(aggregates: Seq[ClassPath], isSource: Boolean): Option[ClassRepresentation] = if (aggregates.nonEmpty) { - val entry = getEntries(aggregates.head) - .find(_.name == simpleClassName) + val entry = aggregates.head.findClass(className) match { + case s @ Some(_: SourceFileEntry) if isSource => s + case s @ Some(_: ClassFileEntry) if !isSource => s + case _ => None + } if (entry.isDefined) entry - else findEntry(aggregates.tail, getEntries) + else findEntry(aggregates.tail, isSource) } else None - val classEntry = findEntry(aggregates, classesGetter(pkg)) - val sourceEntry = findEntry(aggregates, sourcesGetter(pkg)) + val classEntry = findEntry(aggregates, isSource = false) + val sourceEntry = findEntry(aggregates, isSource = true) - mergeClassesAndSources(classEntry.toList, sourceEntry.toList).headOption + (classEntry, sourceEntry) match { + case (Some(c: ClassFileEntry), Some(s: SourceFileEntry)) => Some(ClassAndSourceFilesEntry(c.file, s.file)) + case (c @ Some(_), _) => c + case (_, s) => s + } } override def asURLs: Seq[URL] = aggregates.flatMap(_.asURLs) @@ -61,16 +66,16 @@ case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatCl } override private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = - getDistinctEntries(classesGetter(inPackage)) + getDistinctEntries(_.classes(inPackage)) override private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] = - getDistinctEntries(sourcesGetter(inPackage)) + getDistinctEntries(_.sources(inPackage)) - override private[nsc] def list(inPackage: String): FlatClassPathEntries = { + override private[nsc] def list(inPackage: String): ClassPathEntries = { val (packages, classesAndSources) = aggregates.map(_.list(inPackage)).unzip val distinctPackages = packages.flatten.distinct val distinctClassesAndSources = mergeClassesAndSources(classesAndSources: _*) - FlatClassPathEntries(distinctPackages, distinctClassesAndSources) + ClassPathEntries(distinctPackages, distinctClassesAndSources) } /** @@ -78,11 +83,11 @@ case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatCl * creates an entry containing both of them. If there would be more than one class or source * entries for the same class it always would use the first entry of each type found on a classpath. */ - private def mergeClassesAndSources(entries: Seq[ClassRepClassPathEntry]*): Seq[ClassRepClassPathEntry] = { + private def mergeClassesAndSources(entries: Seq[ClassRepresentation]*): Seq[ClassRepresentation] = { // based on the implementation from MergedClassPath var count = 0 val indices = collection.mutable.HashMap[String, Int]() - val mergedEntries = new ArrayBuffer[ClassRepClassPathEntry](1024) + val mergedEntries = new ArrayBuffer[ClassRepresentation](1024) for { partOfEntries <- entries @@ -107,7 +112,7 @@ case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatCl mergedEntries.toIndexedSeq } - private def getDistinctEntries[EntryType <: ClassRepClassPathEntry](getEntries: FlatClassPath => Seq[EntryType]): Seq[EntryType] = { + private def getDistinctEntries[EntryType <: ClassRepresentation](getEntries: ClassPath => Seq[EntryType]): Seq[EntryType] = { val seenNames = collection.mutable.HashSet[String]() val entriesBuffer = new ArrayBuffer[EntryType](1024) for { @@ -119,7 +124,16 @@ case class AggregateFlatClassPath(aggregates: Seq[FlatClassPath]) extends FlatCl } entriesBuffer.toIndexedSeq } +} - private def classesGetter(pkg: String) = (cp: FlatClassPath) => cp.classes(pkg) - private def sourcesGetter(pkg: String) = (cp: FlatClassPath) => cp.sources(pkg) +object AggregateClassPath { + def createAggregate(parts: ClassPath*): ClassPath = { + val elems = new ArrayBuffer[ClassPath]() + parts foreach { + case AggregateClassPath(ps) => elems ++= ps + case p => elems += p + } + if (elems.size == 1) elems.head + else AggregateClassPath(elems.toIndexedSeq) + } } diff --git a/src/compiler/scala/tools/nsc/classpath/ClassPath.scala b/src/compiler/scala/tools/nsc/classpath/ClassPath.scala new file mode 100644 index 0000000000..08bd98b1d8 --- /dev/null +++ b/src/compiler/scala/tools/nsc/classpath/ClassPath.scala @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014 Contributor. All rights reserved. + */ +package scala.tools.nsc.classpath + +import scala.reflect.io.AbstractFile +import scala.tools.nsc.util.ClassRepresentation + +case class ClassPathEntries(packages: Seq[PackageEntry], classesAndSources: Seq[ClassRepresentation]) + +object ClassPathEntries { + import scala.language.implicitConversions + // to have working unzip method + implicit def entry2Tuple(entry: ClassPathEntries): (Seq[PackageEntry], Seq[ClassRepresentation]) = (entry.packages, entry.classesAndSources) +} + +trait ClassFileEntry extends ClassRepresentation { + def file: AbstractFile +} + +trait SourceFileEntry extends ClassRepresentation { + def file: AbstractFile +} + +trait PackageEntry { + def name: String +} + +private[nsc] case class ClassFileEntryImpl(file: AbstractFile) extends ClassFileEntry { + override def name = FileUtils.stripClassExtension(file.name) // class name + + override def binary: Option[AbstractFile] = Some(file) + override def source: Option[AbstractFile] = None +} + +private[nsc] case class SourceFileEntryImpl(file: AbstractFile) extends SourceFileEntry { + override def name = FileUtils.stripSourceExtension(file.name) + + override def binary: Option[AbstractFile] = None + override def source: Option[AbstractFile] = Some(file) +} + +private[nsc] case class ClassAndSourceFilesEntry(classFile: AbstractFile, srcFile: AbstractFile) extends ClassRepresentation { + override def name = FileUtils.stripClassExtension(classFile.name) + + override def binary: Option[AbstractFile] = Some(classFile) + override def source: Option[AbstractFile] = Some(srcFile) +} + +private[nsc] case class PackageEntryImpl(name: String) extends PackageEntry + +private[nsc] trait NoSourcePaths { + def asSourcePathString: String = "" + private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] = Seq.empty +} + +private[nsc] trait NoClassPaths { + def findClassFile(className: String): Option[AbstractFile] = None + private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = Seq.empty +} diff --git a/src/compiler/scala/tools/nsc/classpath/ClassPathFactory.scala b/src/compiler/scala/tools/nsc/classpath/ClassPathFactory.scala index 9bf4e3f779..3a29f1ba11 100644 --- a/src/compiler/scala/tools/nsc/classpath/ClassPathFactory.scala +++ b/src/compiler/scala/tools/nsc/classpath/ClassPathFactory.scala @@ -3,47 +3,49 @@ */ package scala.tools.nsc.classpath -import scala.reflect.io.AbstractFile +import scala.reflect.io.{AbstractFile, VirtualDirectory} +import scala.tools.nsc.Settings +import FileUtils.AbstractFileOps import scala.tools.nsc.util.ClassPath /** - * A trait that contains factory methods for classpath elements of type T. - * - * The logic has been abstracted from ClassPath#ClassPathContext so it's possible - * to have common trait that supports both recursive and flat classpath representations. - * - * Therefore, we expect that T will be either ClassPath[U] or FlatClassPath. + * Provides factory methods for classpath. When creating classpath instances for a given path, + * it uses proper type of classpath depending on a types of particular files containing sources or classes. */ -trait ClassPathFactory[T] { - +class ClassPathFactory(settings: Settings) { /** - * Create a new classpath based on the abstract file. - */ - def newClassPath(file: AbstractFile): T + * Create a new classpath based on the abstract file. + */ + def newClassPath(file: AbstractFile): ClassPath = ClassPathFactory.newClassPath(file, settings) /** - * Creators for sub classpaths which preserve this context. - */ - def sourcesInPath(path: String): List[T] + * Creators for sub classpaths which preserve this context. + */ + def sourcesInPath(path: String): List[ClassPath] = + for { + file <- expandPath(path, expandStar = false) + dir <- Option(AbstractFile getDirectory file) + } yield createSourcePath(dir) + - def expandPath(path: String, expandStar: Boolean = true): List[String] = ClassPath.expandPath(path, expandStar) + def expandPath(path: String, expandStar: Boolean = true): List[String] = scala.tools.nsc.util.ClassPath.expandPath(path, expandStar) - def expandDir(extdir: String): List[String] = ClassPath.expandDir(extdir) + def expandDir(extdir: String): List[String] = scala.tools.nsc.util.ClassPath.expandDir(extdir) - def contentsOfDirsInPath(path: String): List[T] = + def contentsOfDirsInPath(path: String): List[ClassPath] = for { dir <- expandPath(path, expandStar = false) name <- expandDir(dir) entry <- Option(AbstractFile.getDirectory(name)) } yield newClassPath(entry) - def classesInExpandedPath(path: String): IndexedSeq[T] = + def classesInExpandedPath(path: String): IndexedSeq[ClassPath] = classesInPathImpl(path, expand = true).toIndexedSeq def classesInPath(path: String) = classesInPathImpl(path, expand = false) def classesInManifest(useManifestClassPath: Boolean) = - if (useManifestClassPath) ClassPath.manifests.map(url => newClassPath(AbstractFile getResources url)) + if (useManifestClassPath) scala.tools.nsc.util.ClassPath.manifests.map(url => newClassPath(AbstractFile getResources url)) else Nil // Internal @@ -52,4 +54,25 @@ trait ClassPathFactory[T] { file <- expandPath(path, expand) dir <- Option(AbstractFile.getDirectory(file)) } yield newClassPath(dir) + + private def createSourcePath(file: AbstractFile): ClassPath = + if (file.isJarOrZip) + ZipAndJarSourcePathFactory.create(file, settings) + else if (file.isDirectory) + new DirectorySourcePath(file.file) + else + sys.error(s"Unsupported sourcepath element: $file") +} + +object ClassPathFactory { + def newClassPath(file: AbstractFile, settings: Settings): ClassPath = file match { + case vd: VirtualDirectory => VirtualDirectoryClassPath(vd) + case _ => + if (file.isJarOrZip) + ZipAndJarClassPathFactory.create(file, settings) + else if (file.isDirectory) + new DirectoryClassPath(file.file) + else + sys.error(s"Unsupported classpath element: $file") + } } diff --git a/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala b/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala new file mode 100644 index 0000000000..aba941e043 --- /dev/null +++ b/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2014 Contributor. All rights reserved. + */ +package scala.tools.nsc.classpath + +import java.io.File +import java.net.URL +import scala.reflect.io.{AbstractFile, PlainFile} +import scala.tools.nsc.util.{ClassPath, ClassRepresentation} +import FileUtils._ + +/** + * A trait allowing to look for classpath entries in directories. It provides common logic for + * classes handling class and source files. + * It makes use of the fact that in the case of nested directories it's easy to find a file + * when we have a name of a package. + * It abstracts over the file representation to work with both JFile and AbstractFile. + */ +trait DirectoryLookup[FileEntryType <: ClassRepresentation] extends ClassPath { + type F + + val dir: F + + protected def emptyFiles: Array[F] // avoids reifying ClassTag[F] + protected def getSubDir(dirName: String): Option[F] + protected def listChildren(dir: F, filter: Option[F => Boolean] = None): Array[F] + protected def getName(f: F): String + protected def toAbstractFile(f: F): AbstractFile + protected def isPackage(f: F): Boolean + + protected def createFileEntry(file: AbstractFile): FileEntryType + protected def isMatchingFile(f: F): Boolean + + private def getDirectory(forPackage: String): Option[F] = { + if (forPackage == ClassPath.RootPackage) { + Some(dir) + } else { + val packageDirName = FileUtils.dirPath(forPackage) + getSubDir(packageDirName) + } + } + + private[nsc] def packages(inPackage: String): Seq[PackageEntry] = { + val dirForPackage = getDirectory(inPackage) + val nestedDirs: Array[F] = dirForPackage match { + case None => emptyFiles + case Some(directory) => listChildren(directory, Some(isPackage)) + } + val prefix = PackageNameUtils.packagePrefix(inPackage) + nestedDirs.map(f => PackageEntryImpl(prefix + getName(f))) + } + + protected def files(inPackage: String): Seq[FileEntryType] = { + val dirForPackage = getDirectory(inPackage) + val files: Array[F] = dirForPackage match { + case None => emptyFiles + case Some(directory) => listChildren(directory, Some(isMatchingFile)) + } + files.map(f => createFileEntry(toAbstractFile(f))) + } + + private[nsc] def list(inPackage: String): ClassPathEntries = { + val dirForPackage = getDirectory(inPackage) + val files: Array[F] = dirForPackage match { + case None => emptyFiles + case Some(directory) => listChildren(directory) + } + val packagePrefix = PackageNameUtils.packagePrefix(inPackage) + val packageBuf = collection.mutable.ArrayBuffer.empty[PackageEntry] + val fileBuf = collection.mutable.ArrayBuffer.empty[FileEntryType] + for (file <- files) { + if (isPackage(file)) + packageBuf += PackageEntryImpl(packagePrefix + getName(file)) + else if (isMatchingFile(file)) + fileBuf += createFileEntry(toAbstractFile(file)) + } + ClassPathEntries(packageBuf, fileBuf) + } +} + +trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends DirectoryLookup[FileEntryType] { + type F = File + + protected def emptyFiles: Array[File] = Array.empty + protected def getSubDir(packageDirName: String): Option[File] = { + val packageDir = new File(dir, packageDirName) + if (packageDir.exists && packageDir.isDirectory) Some(packageDir) + else None + } + protected def listChildren(dir: File, filter: Option[File => Boolean]): Array[File] = filter match { + case Some(f) => dir.listFiles(mkFileFilter(f)) + case None => dir.listFiles() + } + protected def getName(f: File): String = f.getName + protected def toAbstractFile(f: File): AbstractFile = new PlainFile(new scala.reflect.io.File(f)) + protected def isPackage(f: File): Boolean = f.isPackage + + assert(dir != null, "Directory file in DirectoryFileLookup cannot be null") + + def asURLs: Seq[URL] = Seq(dir.toURI.toURL) + def asClassPathStrings: Seq[String] = Seq(dir.getPath) +} + +case class DirectoryClassPath(dir: File) extends JFileDirectoryLookup[ClassFileEntryImpl] with NoSourcePaths { + override def findClass(className: String): Option[ClassRepresentation] = findClassFile(className) map ClassFileEntryImpl + + def findClassFile(className: String): Option[AbstractFile] = { + val relativePath = FileUtils.dirPath(className) + val classFile = new File(s"$dir/$relativePath.class") + if (classFile.exists) { + val wrappedClassFile = new scala.reflect.io.File(classFile) + val abstractClassFile = new PlainFile(wrappedClassFile) + Some(abstractClassFile) + } else None + } + + protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file) + protected def isMatchingFile(f: File): Boolean = f.isClass + + private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage) +} + +case class DirectorySourcePath(dir: File) extends JFileDirectoryLookup[SourceFileEntryImpl] with NoClassPaths { + def asSourcePathString: String = asClassPathString + + protected def createFileEntry(file: AbstractFile): SourceFileEntryImpl = SourceFileEntryImpl(file) + protected def isMatchingFile(f: File): Boolean = endsScalaOrJava(f.getName) + + override def findClass(className: String): Option[ClassRepresentation] = findSourceFile(className) map SourceFileEntryImpl + + private def findSourceFile(className: String): Option[AbstractFile] = { + val relativePath = FileUtils.dirPath(className) + val sourceFile = Stream("scala", "java") + .map(ext => new File(s"$dir/$relativePath.$ext")) + .collectFirst { case file if file.exists() => file } + + sourceFile.map { file => + val wrappedSourceFile = new scala.reflect.io.File(file) + val abstractSourceFile = new PlainFile(wrappedSourceFile) + abstractSourceFile + } + } + + private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] = files(inPackage) +} diff --git a/src/compiler/scala/tools/nsc/classpath/DirectoryFlatClassPath.scala b/src/compiler/scala/tools/nsc/classpath/DirectoryFlatClassPath.scala deleted file mode 100644 index 81d2f7320f..0000000000 --- a/src/compiler/scala/tools/nsc/classpath/DirectoryFlatClassPath.scala +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) 2014 Contributor. All rights reserved. - */ -package scala.tools.nsc.classpath - -import java.io.File -import java.io.FileFilter -import java.net.URL -import scala.reflect.io.AbstractFile -import scala.reflect.io.PlainFile -import scala.tools.nsc.util.ClassRepresentation -import FileUtils._ - -/** - * A trait allowing to look for classpath entries of given type in directories. - * It provides common logic for classes handling class and source files. - * It makes use of the fact that in the case of nested directories it's easy to find a file - * when we have a name of a package. - */ -trait DirectoryFileLookup[FileEntryType <: ClassRepClassPathEntry] extends FlatClassPath { - val dir: File - assert(dir != null, "Directory file in DirectoryFileLookup cannot be null") - - override def asURLs: Seq[URL] = Seq(dir.toURI.toURL) - override def asClassPathStrings: Seq[String] = Seq(dir.getPath) - - import FlatClassPath.RootPackage - private def getDirectory(forPackage: String): Option[File] = { - if (forPackage == RootPackage) { - Some(dir) - } else { - val packageDirName = FileUtils.dirPath(forPackage) - val packageDir = new File(dir, packageDirName) - if (packageDir.exists && packageDir.isDirectory) { - Some(packageDir) - } else None - } - } - - override private[nsc] def packages(inPackage: String): Seq[PackageEntry] = { - val dirForPackage = getDirectory(inPackage) - val nestedDirs: Array[File] = dirForPackage match { - case None => Array.empty - case Some(directory) => directory.listFiles(DirectoryFileLookup.packageDirectoryFileFilter) - } - val prefix = PackageNameUtils.packagePrefix(inPackage) - val entries = nestedDirs map { file => - PackageEntryImpl(prefix + file.getName) - } - entries - } - - protected def files(inPackage: String): Seq[FileEntryType] = { - val dirForPackage = getDirectory(inPackage) - val files: Array[File] = dirForPackage match { - case None => Array.empty - case Some(directory) => directory.listFiles(fileFilter) - } - val entries = files map { file => - val wrappedFile = new scala.reflect.io.File(file) - createFileEntry(new PlainFile(wrappedFile)) - } - entries - } - - override private[nsc] def list(inPackage: String): FlatClassPathEntries = { - val dirForPackage = getDirectory(inPackage) - val files: Array[File] = dirForPackage match { - case None => Array.empty - case Some(directory) => directory.listFiles() - } - val packagePrefix = PackageNameUtils.packagePrefix(inPackage) - val packageBuf = collection.mutable.ArrayBuffer.empty[PackageEntry] - val fileBuf = collection.mutable.ArrayBuffer.empty[FileEntryType] - for (file <- files) { - if (file.isPackage) { - val pkgEntry = PackageEntryImpl(packagePrefix + file.getName) - packageBuf += pkgEntry - } else if (fileFilter.accept(file)) { - val wrappedFile = new scala.reflect.io.File(file) - val abstractFile = new PlainFile(wrappedFile) - fileBuf += createFileEntry(abstractFile) - } - } - FlatClassPathEntries(packageBuf, fileBuf) - } - - protected def createFileEntry(file: AbstractFile): FileEntryType - protected def fileFilter: FileFilter -} - -object DirectoryFileLookup { - - private[classpath] object packageDirectoryFileFilter extends FileFilter { - override def accept(pathname: File): Boolean = pathname.isPackage - } -} - -case class DirectoryFlatClassPath(dir: File) - extends DirectoryFileLookup[ClassFileEntryImpl] - with NoSourcePaths { - - override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = findClassFile(className) map ClassFileEntryImpl - - override def findClassFile(className: String): Option[AbstractFile] = { - val relativePath = FileUtils.dirPath(className) - val classFile = new File(s"$dir/$relativePath.class") - if (classFile.exists) { - val wrappedClassFile = new scala.reflect.io.File(classFile) - val abstractClassFile = new PlainFile(wrappedClassFile) - Some(abstractClassFile) - } else None - } - - override protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file) - override protected def fileFilter: FileFilter = DirectoryFlatClassPath.classFileFilter - - override private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage) -} - -object DirectoryFlatClassPath { - - private val classFileFilter = new FileFilter { - override def accept(pathname: File): Boolean = pathname.isClass - } -} - -case class DirectoryFlatSourcePath(dir: File) - extends DirectoryFileLookup[SourceFileEntryImpl] - with NoClassPaths { - - override def asSourcePathString: String = asClassPathString - - override protected def createFileEntry(file: AbstractFile): SourceFileEntryImpl = SourceFileEntryImpl(file) - override protected def fileFilter: FileFilter = DirectoryFlatSourcePath.sourceFileFilter - - override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = { - findSourceFile(className) map SourceFileEntryImpl - } - - private def findSourceFile(className: String): Option[AbstractFile] = { - val relativePath = FileUtils.dirPath(className) - val sourceFile = Stream("scala", "java") - .map(ext => new File(s"$dir/$relativePath.$ext")) - .collectFirst { case file if file.exists() => file } - - sourceFile.map { file => - val wrappedSourceFile = new scala.reflect.io.File(file) - val abstractSourceFile = new PlainFile(wrappedSourceFile) - abstractSourceFile - } - } - - override private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] = files(inPackage) -} - -object DirectoryFlatSourcePath { - - private val sourceFileFilter = new FileFilter { - override def accept(pathname: File): Boolean = endsScalaOrJava(pathname.getName) - } -} diff --git a/src/compiler/scala/tools/nsc/classpath/FileUtils.scala b/src/compiler/scala/tools/nsc/classpath/FileUtils.scala index ee2528e15c..bbcfcb24ca 100644 --- a/src/compiler/scala/tools/nsc/classpath/FileUtils.scala +++ b/src/compiler/scala/tools/nsc/classpath/FileUtils.scala @@ -3,7 +3,7 @@ */ package scala.tools.nsc.classpath -import java.io.{ File => JFile } +import java.io.{File => JFile, FileFilter} import java.net.URL import scala.reflect.internal.FatalError import scala.reflect.io.AbstractFile @@ -65,4 +65,8 @@ object FileUtils { // because then some tests in partest don't pass private def mayBeValidPackage(dirName: String): Boolean = (dirName != "META-INF") && (dirName != "") && (dirName.charAt(0) != '.') + + def mkFileFilter(f: JFile => Boolean) = new FileFilter { + def accept(pathname: JFile): Boolean = f(pathname) + } } diff --git a/src/compiler/scala/tools/nsc/classpath/FlatClassPath.scala b/src/compiler/scala/tools/nsc/classpath/FlatClassPath.scala deleted file mode 100644 index cb201617d2..0000000000 --- a/src/compiler/scala/tools/nsc/classpath/FlatClassPath.scala +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2014 Contributor. All rights reserved. - */ -package scala.tools.nsc.classpath - -import scala.reflect.io.AbstractFile -import scala.tools.nsc.util.{ ClassFileLookup, ClassPath, ClassRepresentation } - -/** - * A base trait for the particular flat classpath representation implementations. - * - * We call this variant of a classpath representation flat because it's possible to - * query the whole classpath using just single instance extending this trait. - * - * This is an alternative design compared to scala.tools.nsc.util.ClassPath - */ -trait FlatClassPath extends ClassFileLookup[AbstractFile] { - /** Empty string represents root package */ - private[nsc] def packages(inPackage: String): Seq[PackageEntry] - private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] - private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] - - /** Allows to get entries for packages and classes merged with sources possibly in one pass. */ - private[nsc] def list(inPackage: String): FlatClassPathEntries - - // A default implementation which should be overridden, if we can create the more efficient - // solution for a given type of FlatClassPath - override def findClass(className: String): Option[ClassRepresentation[AbstractFile]] = { - val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className) - - val foundClassFromClassFiles = classes(pkg) - .find(_.name == simpleClassName) - - def findClassInSources = sources(pkg) - .find(_.name == simpleClassName) - - foundClassFromClassFiles orElse findClassInSources - } - - override def asClassPathString: String = ClassPath.join(asClassPathStrings: _*) - def asClassPathStrings: Seq[String] -} - -object FlatClassPath { - val RootPackage = "" -} - -case class FlatClassPathEntries(packages: Seq[PackageEntry], classesAndSources: Seq[ClassRepClassPathEntry]) - -object FlatClassPathEntries { - import scala.language.implicitConversions - // to have working unzip method - implicit def entry2Tuple(entry: FlatClassPathEntries) = (entry.packages, entry.classesAndSources) -} - -sealed trait ClassRepClassPathEntry extends ClassRepresentation[AbstractFile] - -trait ClassFileEntry extends ClassRepClassPathEntry { - def file: AbstractFile -} - -trait SourceFileEntry extends ClassRepClassPathEntry { - def file: AbstractFile -} - -trait PackageEntry { - def name: String -} - -private[nsc] case class ClassFileEntryImpl(file: AbstractFile) extends ClassFileEntry { - override def name = FileUtils.stripClassExtension(file.name) // class name - - override def binary: Option[AbstractFile] = Some(file) - override def source: Option[AbstractFile] = None -} - -private[nsc] case class SourceFileEntryImpl(file: AbstractFile) extends SourceFileEntry { - override def name = FileUtils.stripSourceExtension(file.name) - - override def binary: Option[AbstractFile] = None - override def source: Option[AbstractFile] = Some(file) -} - -private[nsc] case class ClassAndSourceFilesEntry(classFile: AbstractFile, srcFile: AbstractFile) extends ClassRepClassPathEntry { - override def name = FileUtils.stripClassExtension(classFile.name) - - override def binary: Option[AbstractFile] = Some(classFile) - override def source: Option[AbstractFile] = Some(srcFile) -} - -private[nsc] case class PackageEntryImpl(name: String) extends PackageEntry - -private[nsc] trait NoSourcePaths { - def asSourcePathString: String = "" - private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] = Seq.empty -} - -private[nsc] trait NoClassPaths { - def findClassFile(className: String): Option[AbstractFile] = None - private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = Seq.empty -} diff --git a/src/compiler/scala/tools/nsc/classpath/FlatClassPathFactory.scala b/src/compiler/scala/tools/nsc/classpath/FlatClassPathFactory.scala deleted file mode 100644 index d8ca325885..0000000000 --- a/src/compiler/scala/tools/nsc/classpath/FlatClassPathFactory.scala +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2014 Contributor. All rights reserved. - */ -package scala.tools.nsc.classpath - -import scala.tools.nsc.Settings -import scala.tools.nsc.io.AbstractFile -import FileUtils.AbstractFileOps - -/** - * Provides factory methods for flat classpath. When creating classpath instances for a given path, - * it uses proper type of classpath depending on a types of particular files containing sources or classes. - */ -class FlatClassPathFactory(settings: Settings) extends ClassPathFactory[FlatClassPath] { - - override def newClassPath(file: AbstractFile): FlatClassPath = - if (file.isJarOrZip) - ZipAndJarFlatClassPathFactory.create(file, settings) - else if (file.isDirectory) - new DirectoryFlatClassPath(file.file) - else - sys.error(s"Unsupported classpath element: $file") - - override def sourcesInPath(path: String): List[FlatClassPath] = - for { - file <- expandPath(path, expandStar = false) - dir <- Option(AbstractFile getDirectory file) - } yield createSourcePath(dir) - - private def createSourcePath(file: AbstractFile): FlatClassPath = - if (file.isJarOrZip) - ZipAndJarFlatSourcePathFactory.create(file, settings) - else if (file.isDirectory) - new DirectoryFlatSourcePath(file.file) - else - sys.error(s"Unsupported sourcepath element: $file") -} diff --git a/src/compiler/scala/tools/nsc/classpath/PackageNameUtils.scala b/src/compiler/scala/tools/nsc/classpath/PackageNameUtils.scala index c907d565d2..39b0d78135 100644 --- a/src/compiler/scala/tools/nsc/classpath/PackageNameUtils.scala +++ b/src/compiler/scala/tools/nsc/classpath/PackageNameUtils.scala @@ -3,7 +3,7 @@ */ package scala.tools.nsc.classpath -import scala.tools.nsc.classpath.FlatClassPath.RootPackage +import scala.tools.nsc.util.ClassPath.RootPackage /** * Common methods related to package names represented as String diff --git a/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala b/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala new file mode 100644 index 0000000000..8df0c3743d --- /dev/null +++ b/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala @@ -0,0 +1,40 @@ +package scala.tools.nsc.classpath + +import scala.tools.nsc.util.ClassRepresentation +import scala.reflect.io.{Path, PlainFile, VirtualDirectory, AbstractFile} +import FileUtils._ +import java.net.URL +import scala.tools.nsc.util.ClassPath + +case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath with DirectoryLookup[ClassFileEntryImpl] with NoSourcePaths { + type F = AbstractFile + + protected def emptyFiles: Array[AbstractFile] = Array.empty + protected def getSubDir(packageDirName: String): Option[AbstractFile] = + Option(dir.lookupName(packageDirName, directory = true)) + protected def listChildren(dir: AbstractFile, filter: Option[AbstractFile => Boolean] = None): Array[F] = filter match { + case Some(f) => dir.iterator.filter(f).toArray + case _ => dir.toArray + } + def getName(f: AbstractFile): String = f.name + def toAbstractFile(f: AbstractFile): AbstractFile = f + def isPackage(f: AbstractFile): Boolean = f.isPackage + + // mimic the behavior of the old nsc.util.DirectoryClassPath + def asURLs: Seq[URL] = Seq(new URL(dir.name)) + def asClassPathStrings: Seq[String] = Seq(dir.path) + + override def findClass(className: String): Option[ClassRepresentation] = findClassFile(className) map ClassFileEntryImpl + + def findClassFile(className: String): Option[AbstractFile] = { + val relativePath = FileUtils.dirPath(className) + val classFile = new PlainFile(Path(s"$dir/$relativePath.class")) + if (classFile.exists) Some(classFile) + else None + } + + private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage) + + protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file) + protected def isMatchingFile(f: AbstractFile): Boolean = f.isClass +} diff --git a/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala b/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala index 85c7c3c843..fe74e5f874 100644 --- a/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala +++ b/src/compiler/scala/tools/nsc/classpath/ZipAndJarFileLookupFactory.scala @@ -6,7 +6,8 @@ package scala.tools.nsc.classpath import java.io.File import java.net.URL import scala.annotation.tailrec -import scala.reflect.io.{ AbstractFile, FileZipArchive, ManifestResources } +import scala.reflect.io.{AbstractFile, FileZipArchive, ManifestResources} +import scala.tools.nsc.util.ClassPath import scala.tools.nsc.Settings import FileUtils._ @@ -19,17 +20,16 @@ import FileUtils._ * when there are a lot of projects having a lot of common dependencies. */ sealed trait ZipAndJarFileLookupFactory { + private val cache = collection.mutable.Map.empty[AbstractFile, ClassPath] - private val cache = collection.mutable.Map.empty[AbstractFile, FlatClassPath] - - def create(zipFile: AbstractFile, settings: Settings): FlatClassPath = { + def create(zipFile: AbstractFile, settings: Settings): ClassPath = { if (settings.YdisableFlatCpCaching) createForZipFile(zipFile) else createUsingCache(zipFile, settings) } - protected def createForZipFile(zipFile: AbstractFile): FlatClassPath + protected def createForZipFile(zipFile: AbstractFile): ClassPath - private def createUsingCache(zipFile: AbstractFile, settings: Settings): FlatClassPath = cache.synchronized { + private def createUsingCache(zipFile: AbstractFile, settings: Settings): ClassPath = cache.synchronized { def newClassPathInstance = { if (settings.verbose || settings.Ylogcp) println(s"$zipFile is not yet in the classpath cache") @@ -40,12 +40,11 @@ sealed trait ZipAndJarFileLookupFactory { } /** - * Manages creation of flat classpath for class files placed in zip and jar files. + * Manages creation of classpath for class files placed in zip and jar files. * It should be the only way of creating them as it provides caching. */ -object ZipAndJarFlatClassPathFactory extends ZipAndJarFileLookupFactory { - - private case class ZipArchiveFlatClassPath(zipFile: File) +object ZipAndJarClassPathFactory extends ZipAndJarFileLookupFactory { + private case class ZipArchiveClassPath(zipFile: File) extends ZipArchiveFileLookup[ClassFileEntryImpl] with NoSourcePaths { @@ -67,10 +66,7 @@ object ZipAndJarFlatClassPathFactory extends ZipAndJarFileLookupFactory { * with a particularly prepared scala-library.jar. It should have all classes listed in the manifest like e.g. this entry: * Name: scala/Function2$mcFJD$sp.class */ - private case class ManifestResourcesFlatClassPath(file: ManifestResources) - extends FlatClassPath - with NoSourcePaths { - + private case class ManifestResourcesClassPath(file: ManifestResources) extends ClassPath with NoSourcePaths { override def findClassFile(className: String): Option[AbstractFile] = { val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className) classes(pkg).find(_.name == simpleClassName).map(_.file) @@ -80,8 +76,8 @@ object ZipAndJarFlatClassPathFactory extends ZipAndJarFileLookupFactory { override def asURLs: Seq[URL] = file.toURLs() - import ManifestResourcesFlatClassPath.PackageFileInfo - import ManifestResourcesFlatClassPath.PackageInfo + import ManifestResourcesClassPath.PackageFileInfo + import ManifestResourcesClassPath.PackageInfo /** * A cache mapping package name to abstract file for package directory and subpackages of given package. @@ -119,8 +115,8 @@ object ZipAndJarFlatClassPathFactory extends ZipAndJarFileLookupFactory { } val subpackages = getSubpackages(file) - packages.put(FlatClassPath.RootPackage, PackageFileInfo(file, subpackages)) - traverse(FlatClassPath.RootPackage, subpackages, collection.mutable.Queue()) + packages.put(ClassPath.RootPackage, PackageFileInfo(file, subpackages)) + traverse(ClassPath.RootPackage, subpackages, collection.mutable.Queue()) packages } @@ -137,21 +133,21 @@ object ZipAndJarFlatClassPathFactory extends ZipAndJarFileLookupFactory { (for (file <- pkg if file.isClass) yield ClassFileEntryImpl(file))(collection.breakOut) } - override private[nsc] def list(inPackage: String): FlatClassPathEntries = FlatClassPathEntries(packages(inPackage), classes(inPackage)) + override private[nsc] def list(inPackage: String): ClassPathEntries = ClassPathEntries(packages(inPackage), classes(inPackage)) } - private object ManifestResourcesFlatClassPath { + private object ManifestResourcesClassPath { case class PackageFileInfo(packageFile: AbstractFile, subpackages: Seq[AbstractFile]) case class PackageInfo(packageName: String, subpackages: List[AbstractFile]) } - override protected def createForZipFile(zipFile: AbstractFile): FlatClassPath = + override protected def createForZipFile(zipFile: AbstractFile): ClassPath = if (zipFile.file == null) createWithoutUnderlyingFile(zipFile) - else ZipArchiveFlatClassPath(zipFile.file) + else ZipArchiveClassPath(zipFile.file) private def createWithoutUnderlyingFile(zipFile: AbstractFile) = zipFile match { case manifestRes: ManifestResources => - ManifestResourcesFlatClassPath(manifestRes) + ManifestResourcesClassPath(manifestRes) case _ => val errorMsg = s"Abstract files which don't have an underlying file and are not ManifestResources are not supported. There was $zipFile" throw new IllegalArgumentException(errorMsg) @@ -159,12 +155,11 @@ object ZipAndJarFlatClassPathFactory extends ZipAndJarFileLookupFactory { } /** - * Manages creation of flat classpath for source files placed in zip and jar files. + * Manages creation of classpath for source files placed in zip and jar files. * It should be the only way of creating them as it provides caching. */ -object ZipAndJarFlatSourcePathFactory extends ZipAndJarFileLookupFactory { - - private case class ZipArchiveFlatSourcePath(zipFile: File) +object ZipAndJarSourcePathFactory extends ZipAndJarFileLookupFactory { + private case class ZipArchiveSourcePath(zipFile: File) extends ZipArchiveFileLookup[SourceFileEntryImpl] with NoClassPaths { @@ -176,5 +171,5 @@ object ZipAndJarFlatSourcePathFactory extends ZipAndJarFileLookupFactory { override protected def isRequiredFileType(file: AbstractFile): Boolean = file.isScalaOrJavaSource } - override protected def createForZipFile(zipFile: AbstractFile): FlatClassPath = ZipArchiveFlatSourcePath(zipFile.file) + override protected def createForZipFile(zipFile: AbstractFile): ClassPath = ZipArchiveSourcePath(zipFile.file) } diff --git a/src/compiler/scala/tools/nsc/classpath/ZipArchiveFileLookup.scala b/src/compiler/scala/tools/nsc/classpath/ZipArchiveFileLookup.scala index 1d0de57779..9c147cf8cc 100644 --- a/src/compiler/scala/tools/nsc/classpath/ZipArchiveFileLookup.scala +++ b/src/compiler/scala/tools/nsc/classpath/ZipArchiveFileLookup.scala @@ -9,13 +9,14 @@ import scala.collection.Seq import scala.reflect.io.AbstractFile import scala.reflect.io.FileZipArchive import FileUtils.AbstractFileOps +import scala.tools.nsc.util.{ClassPath, ClassRepresentation} /** * A trait allowing to look for classpath entries of given type in zip and jar files. * It provides common logic for classes handling class and source files. * It's aware of things like e.g. META-INF directory which is correctly skipped. */ -trait ZipArchiveFileLookup[FileEntryType <: ClassRepClassPathEntry] extends FlatClassPath { +trait ZipArchiveFileLookup[FileEntryType <: ClassRepresentation] extends ClassPath { val zipFile: File assert(zipFile != null, "Zip file in ZipArchiveFileLookup cannot be null") @@ -39,7 +40,7 @@ trait ZipArchiveFileLookup[FileEntryType <: ClassRepClassPathEntry] extends Flat entry <- dirEntry.iterator if isRequiredFileType(entry) } yield createFileEntry(entry) - override private[nsc] def list(inPackage: String): FlatClassPathEntries = { + override private[nsc] def list(inPackage: String): ClassPathEntries = { val foundDirEntry = findDirEntry(inPackage) foundDirEntry map { dirEntry => @@ -53,11 +54,11 @@ trait ZipArchiveFileLookup[FileEntryType <: ClassRepClassPathEntry] extends Flat else if (isRequiredFileType(entry)) fileBuf += createFileEntry(entry) } - FlatClassPathEntries(pkgBuf, fileBuf) - } getOrElse FlatClassPathEntries(Seq.empty, Seq.empty) + ClassPathEntries(pkgBuf, fileBuf) + } getOrElse ClassPathEntries(Seq.empty, Seq.empty) } - private def findDirEntry(pkg: String) = { + private def findDirEntry(pkg: String): Option[archive.DirEntry] = { val dirName = s"${FileUtils.dirPath(pkg)}/" archive.allDirs.get(dirName) } diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index 7224523a41..cc851b6330 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -264,7 +264,8 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { } } - def typ(): Tree = + def typ(): Tree = { + annotations() optArrayBrackets { if (in.token == FINAL) in.nextToken() if (in.token == IDENTIFIER) { @@ -287,6 +288,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { basicType() } } + } def typeArgs(t: Tree): Tree = { val wildcards = new ListBuffer[TypeDef] @@ -404,6 +406,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { def typeParam(): TypeDef = atPos(in.currentPos) { + annotations() val name = identForType() val hi = if (in.token == EXTENDS) { in.nextToken() ; bound() } else EmptyTree TypeDef(Modifiers(Flags.JAVA | Flags.DEFERRED | Flags.PARAM), name, Nil, TypeBoundsTree(EmptyTree, hi)) @@ -509,7 +512,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { EmptyTree } } - // for abstract methods (of classes), the `DEFERRED` flag is alredy set. + // for abstract methods (of classes), the `DEFERRED` flag is already set. // here we also set it for interface methods that are not static and not default. if (!isConcreteInterfaceMethod) mods1 |= Flags.DEFERRED List { diff --git a/src/compiler/scala/tools/nsc/settings/FscSettings.scala b/src/compiler/scala/tools/nsc/settings/FscSettings.scala index fffbb4333f..d6013e0b00 100644 --- a/src/compiler/scala/tools/nsc/settings/FscSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/FscSettings.scala @@ -37,9 +37,7 @@ class FscSettings(error: String => Unit) extends Settings(error) { /** If a setting (other than a PathSetting) represents a path or paths. * For use in absolutization. */ - private def holdsPath = Set[Settings#Setting]( - d, dependencyfile, pluginsDir, Ygenjavap - ) + private def holdsPath = Set[Settings#Setting](d, dependencyfile, pluginsDir) override def processArguments(arguments: List[String], processAll: Boolean): (Boolean, List[String]) = { val (r, args) = super.processArguments(arguments, processAll) diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 1446d22217..9a0d86a94d 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -9,10 +9,11 @@ package tools package nsc package settings +import scala.language.existentials + import scala.annotation.elidable import scala.tools.util.PathResolver.Defaults import scala.collection.mutable -import scala.language.{implicitConversions, existentials} trait ScalaSettings extends AbsScalaSettings with StandardScalaSettings @@ -34,9 +35,6 @@ trait ScalaSettings extends AbsScalaSettings /** Enabled under -Xfuture. */ protected def futureSettings = List[BooleanSetting]() - /** Enabled under -optimise. */ - def optimiseSettings = List[BooleanSetting](inline, inlineHandlers, Xcloselim, Xdce, YconstOptimization) - /** If any of these settings is enabled, the compiler should print a message and exit. */ def infoSettings = List[Setting](version, help, Xhelp, Yhelp, showPlugins, showPhases, genPhaseGraph) @@ -112,7 +110,7 @@ trait ScalaSettings extends AbsScalaSettings val Xmigration = ScalaVersionSetting ("-Xmigration", "version", "Warn about constructs whose behavior may have changed since version.", initial = NoScalaVersion, default = Some(AnyScalaVersion)) val nouescape = BooleanSetting ("-Xno-uescape", "Disable handling of \\u unicode escapes.") val Xnojline = BooleanSetting ("-Xnojline", "Do not use JLine for editing.") - val Xverify = BooleanSetting ("-Xverify", "Verify generic signatures in generated bytecode (asm backend only.)") + val Xverify = BooleanSetting ("-Xverify", "Verify generic signatures in generated bytecode.") val plugin = MultiStringSetting ("-Xplugin", "paths", "Load a plugin from each classpath.") val disable = MultiStringSetting ("-Xplugin-disable", "plugin", "Disable plugins by name.") val showPlugins = BooleanSetting ("-Xplugin-list", "Print a synopsis of loaded plugins.") @@ -167,18 +165,10 @@ trait ScalaSettings extends AbsScalaSettings val browse = PhasesSetting ("-Ybrowse", "Browse the abstract syntax tree after") val check = PhasesSetting ("-Ycheck", "Check the tree at the end of") val Yshow = PhasesSetting ("-Yshow", "(Requires -Xshow-class or -Xshow-object) Show after") - val Xcloselim = BooleanSetting ("-Yclosure-elim", "Perform closure elimination.") - val YconstOptimization = BooleanSetting ("-Yconst-opt", "Perform optimization with constant values.") val Ycompacttrees = BooleanSetting ("-Ycompact-trees", "Use compact tree printer when displaying trees.") val noCompletion = BooleanSetting ("-Yno-completion", "Disable tab-completion in the REPL.") - val Xdce = BooleanSetting ("-Ydead-code", "Perform dead code elimination.") val debug = BooleanSetting ("-Ydebug", "Increase the quantity of debugging output.") - //val doc = BooleanSetting ("-Ydoc", "Generate documentation") val termConflict = ChoiceSetting ("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error") - val inline = BooleanSetting ("-Yinline", "Perform inlining when possible.") - val inlineHandlers = BooleanSetting ("-Yinline-handlers", "Perform exception handler inlining when possible.") - val YinlinerWarnings= BooleanSetting ("-Yinline-warnings", "Emit inlining warnings. (Normally suppressed due to high volume)") - val Xlinearizer = ChoiceSetting ("-Ylinearizer", "which", "Linearizer to use", List("normal", "dfs", "rpo", "dump"), "rpo") val log = PhasesSetting ("-Ylog", "Log operations during") val Ylogcp = BooleanSetting ("-Ylog-classpath", "Output information about what classpath is being applied.") val Ynogenericsig = BooleanSetting ("-Yno-generic-signatures", "Suppress generation of generic signatures for Java.") @@ -195,7 +185,6 @@ trait ScalaSettings extends AbsScalaSettings val Yshowsymkinds = BooleanSetting ("-Yshow-symkinds", "Print abbreviated symbol kinds next to symbol names.") val Yshowsymowners = BooleanSetting ("-Yshow-symowners", "Print owner identifiers next to symbol names.") val skip = PhasesSetting ("-Yskip", "Skip") - val Ygenjavap = StringSetting ("-Ygen-javap", "dir", "Generate a parallel output directory of .javap files.", "") val Ygenasmp = StringSetting ("-Ygen-asmp", "dir", "Generate a parallel output directory of .asmp files (ie ASM Textifier output).", "") val Ydumpclasses = StringSetting ("-Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "") val stopAfter = PhasesSetting ("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat @@ -211,13 +200,8 @@ trait ScalaSettings extends AbsScalaSettings val YmethodInfer = BooleanSetting ("-Yinfer-argument-types", "Infer types for arguments of overridden methods.") val etaExpandKeepsStar = BooleanSetting ("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.").withDeprecationMessage(removalIn212) val inferByName = BooleanSetting ("-Yinfer-by-name", "Allow inference of by-name types. This is a temporary option to ease transition. See SI-7899.").withDeprecationMessage(removalIn212) - val YclasspathImpl = ChoiceSetting ("-YclasspathImpl", "implementation", "Choose classpath scanning method.", List(ClassPathRepresentationType.Recursive, ClassPathRepresentationType.Flat), ClassPathRepresentationType.Recursive) val YdisableFlatCpCaching = BooleanSetting ("-YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.") - val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes") - val YdisableUnreachablePrevention = BooleanSetting("-Ydisable-unreachable-prevention", "Disable the prevention of unreachable blocks in code generation.") - val YnoLoadImplClass = BooleanSetting ("-Yno-load-impl-class", "Do not load $class.class files.") - val exposeEmptyPackage = BooleanSetting ("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly() val Ydelambdafy = ChoiceSetting ("-Ydelambdafy", "strategy", "Strategy used for translating lambdas into JVM code.", List("inline", "method"), "method") @@ -304,10 +288,9 @@ trait ScalaSettings extends AbsScalaSettings helpArg = "warning", descr = "Enable optimizer warnings", domain = YoptWarningsChoices, - default = Some(List(YoptWarningsChoices.atInlineFailed.name))) withPostSetHook (self => { - if (self.value subsetOf Set(YoptWarningsChoices.none, YoptWarningsChoices.atInlineFailedSummary)) YinlinerWarnings.value = false - else YinlinerWarnings.value = true - }) + default = Some(List(YoptWarningsChoices.atInlineFailed.name))) + + def YoptWarningsSummaryOnly = YoptWarnings.value subsetOf Set(YoptWarningsChoices.none, YoptWarningsChoices.atInlineFailedSummary) def YoptWarningEmitAtInlineFailed = !YoptWarnings.isSetByUser || @@ -352,22 +335,15 @@ trait ScalaSettings extends AbsScalaSettings str => Some(if(str.equalsIgnoreCase("off")) Int.MaxValue else str.toInt)) val Yquasiquotedebug = BooleanSetting("-Yquasiquote-debug", "Trace quasiquote-related activities.") - // TODO 2.12 Remove - val Yinferdebug = BooleanSetting("-Yinfer-debug", "Trace type inference and implicit search.") withDeprecationMessage("Use -Ytyper-debug") enabling(List(Ytyperdebug)) - /** Groups of Settings. */ val future = BooleanSetting("-Xfuture", "Turn on future language features.") enablingIfNotSetByUser futureSettings - val optimise = BooleanSetting("-optimise", "Generates faster bytecode by applying optimisations to the program") withAbbreviation "-optimize" enablingIfNotSetByUser optimiseSettings - val nooptimise = BooleanSetting("-Ynooptimise", "Clears all the flags set by -optimise. Useful for testing optimizations in isolation.") withAbbreviation "-Ynooptimize" disabling optimise::optimiseSettings + val optimise = BooleanSetting("-optimise", "Compiler flag for the optimizer in Scala 2.11") + .withAbbreviation("-optimize") + .withDeprecationMessage("In 2.12, -optimise enables -Yopt:l:classpath. Check -Yopt:help for using the Scala 2.12 optimizer.") + .withPostSetHook(_ => Yopt.tryToSet(List(YoptChoices.lClasspath.name))) val Xexperimental = BooleanSetting("-Xexperimental", "Enable experimental extensions.") enablingIfNotSetByUser experimentalSettings - /** - * Settings motivated by GenBCode - */ - val Ybackend = ChoiceSetting ("-Ybackend", "choice of bytecode emitter", "Choice of bytecode emitter.", - List("GenBCode"), - "GenBCode") // Feature extensions val XmacroSettings = MultiStringSetting("-Xmacro-settings", "option", "Custom settings for macros.") @@ -391,8 +367,6 @@ trait ScalaSettings extends AbsScalaSettings /** Test whether this is scaladoc we're looking at */ def isScaladoc = false - def isBCodeActive = Ybackend.value == "GenBCode" - object MacroExpand { val None = "none" val Normal = "normal" @@ -400,24 +374,17 @@ trait ScalaSettings extends AbsScalaSettings } def conflictWarning: Option[String] = { - def oldOptimiseFlagsInGenBCode: Option[String] = { - val optFlags: List[Setting] = if (optimise.value) List(optimise) else optimiseSettings.filter(_.value) - if (isBCodeActive && optFlags.nonEmpty) { - val msg = s"""Compiler settings for the 2.11 optimizer (${optFlags.map(_.name).mkString(", ")}) are incompatible with -Ybackend:GenBCode (which is the default in 2.12). - |The optimizer settings are ignored. See -Yopt:help for enabling the new optimizer in 2.12.""".stripMargin - Some(msg) - } else - None - } + // See cd878232b5 for an example how to warn about conflicting settings + + /* + def checkSomeConflict: Option[String] = ... - List(oldOptimiseFlagsInGenBCode /*, moreToCome */).flatten match { + List(/* checkSomeConflict, ... */).flatten match { case Nil => None case warnings => Some("Conflicting compiler settings were detected. Some settings will be ignored.\n" + warnings.mkString("\n")) } - } -} + */ -object ClassPathRepresentationType { - val Flat = "flat" - val Recursive = "recursive" + None + } } diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index f570037760..7ef606b6ef 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -7,8 +7,6 @@ package scala.tools package nsc package settings -import language.existentials - /** Settings influencing the printing of warnings. */ trait Warnings { @@ -27,6 +25,8 @@ trait Warnings { // currently considered too noisy for general use val warnUnusedImport = BooleanSetting("-Ywarn-unused-import", "Warn when imports are unused.") + val nowarnDefaultJunitMethods = BooleanSetting("-Ynowarn-default-junit-methods", "Don't warn when a JUnit @Test method is generated as a default method (not supported in JUnit 4).") + // Experimental lint warnings that are turned off, but which could be turned on programmatically. // They are not activated by -Xlint and can't be enabled on the command line because they are not // created using the standard factory methods. @@ -59,6 +59,7 @@ trait Warnings { val PackageObjectClasses = LintWarning("package-object-classes", "Class or object defined in package object.") val UnsoundMatch = LintWarning("unsound-match", "Pattern match may not be typesafe.") val StarsAlign = LintWarning("stars-align", "Pattern sequence wildcard must align with sequence component.") + val Constant = LintWarning("constant", "Evaluation of a constant arithmetic expression results in an error.") def allLintWarnings = values.toSeq.asInstanceOf[Seq[LintWarning]] } @@ -80,6 +81,7 @@ trait Warnings { def warnPackageObjectClasses = lint contains PackageObjectClasses def warnUnsoundMatch = lint contains UnsoundMatch def warnStarsAlign = lint contains StarsAlign + def warnConstant = lint contains Constant // Lint warnings that are currently -Y, but deprecated in that usage @deprecated("Use warnAdaptedArgs", since="2.11.2") diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 4f5589fd7c..b36d5d4ef1 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -10,10 +10,8 @@ import classfile.ClassfileParser import java.io.IOException import scala.reflect.internal.MissingRequirementError import scala.reflect.internal.util.Statistics -import scala.reflect.io.{ AbstractFile, NoAbstractFile } -import scala.tools.nsc.classpath.FlatClassPath -import scala.tools.nsc.settings.ClassPathRepresentationType -import scala.tools.nsc.util.{ ClassPath, ClassRepresentation } +import scala.reflect.io.{AbstractFile, NoAbstractFile} +import scala.tools.nsc.util.{ClassPath, ClassRepresentation} /** This class ... * @@ -154,7 +152,7 @@ abstract class SymbolLoaders { /** Initialize toplevel class and module symbols in `owner` from class path representation `classRep` */ - def initializeFromClassPath(owner: Symbol, classRep: ClassRepresentation[AbstractFile]) { + def initializeFromClassPath(owner: Symbol, classRep: ClassRepresentation) { ((classRep.binary, classRep.source) : @unchecked) match { case (Some(bin), Some(src)) if platform.needCompile(bin, src) && !binaryOnly(owner, classRep.name) => @@ -247,41 +245,11 @@ abstract class SymbolLoaders { } /** - * Load contents of a package - */ - class PackageLoader(classpath: ClassPath[AbstractFile]) extends SymbolLoader with FlagAgnosticCompleter { - protected def description = s"package loader ${classpath.name}" - - protected def doComplete(root: Symbol) { - assert(root.isPackageClass, root) - // Time travel to a phase before refchecks avoids an initialization issue. `openPackageModule` - // creates a module symbol and invokes invokes `companionModule` while the `infos` field is - // still null. This calls `isModuleNotMethod`, which forces the `info` if run after refchecks. - enteringPhase(phaseBeforeRefchecks) { - root.setInfo(new PackageClassInfoType(newScope, root)) - - if (!root.isRoot) { - for (classRep <- classpath.classes) { - initializeFromClassPath(root, classRep) - } - } - if (!root.isEmptyPackageClass) { - for (pkg <- classpath.packages) { - enterPackage(root, pkg.name, new PackageLoader(pkg)) - } - - openPackageModule(root) - } - } - } - } - - /** * Loads contents of a package */ - class PackageLoaderUsingFlatClassPath(packageName: String, classPath: FlatClassPath) extends SymbolLoader with FlagAgnosticCompleter { + class PackageLoader(packageName: String, classPath: ClassPath) extends SymbolLoader with FlagAgnosticCompleter { protected def description = { - val shownPackageName = if (packageName == FlatClassPath.RootPackage) "<root package>" else packageName + val shownPackageName = if (packageName == ClassPath.RootPackage) "<root package>" else packageName s"package loader $shownPackageName" } @@ -298,9 +266,9 @@ abstract class SymbolLoaders { val fullName = pkg.name val name = - if (packageName == FlatClassPath.RootPackage) fullName + if (packageName == ClassPath.RootPackage) fullName else fullName.substring(packageName.length + 1) - val packageLoader = new PackageLoaderUsingFlatClassPath(fullName, classPath) + val packageLoader = new PackageLoader(fullName, classPath) enterPackage(root, name, packageLoader) } @@ -329,10 +297,7 @@ abstract class SymbolLoaders { val loaders = SymbolLoaders.this.asInstanceOf[SymbolLoadersRefined] - override def classFileLookup: util.ClassFileLookup[AbstractFile] = settings.YclasspathImpl.value match { - case ClassPathRepresentationType.Recursive => platform.classPath - case ClassPathRepresentationType.Flat => platform.flatClassPath - } + override def classPath: ClassPath = platform.classPath } protected def description = "class file "+ classfile.toString diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index fffd48d145..0533d420cd 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -8,16 +8,16 @@ package tools.nsc package symtab package classfile -import java.io.{ File, IOException } +import java.io.{File, IOException} import java.lang.Integer.toHexString -import scala.collection.{ mutable, immutable } -import scala.collection.mutable.{ ListBuffer, ArrayBuffer } +import scala.collection.{immutable, mutable} +import scala.collection.mutable.{ArrayBuffer, ListBuffer} import scala.annotation.switch -import scala.reflect.internal.{ JavaAccFlags } -import scala.reflect.internal.pickling.{PickleBuffer, ByteCodecs} +import scala.reflect.internal.JavaAccFlags +import scala.reflect.internal.pickling.{ByteCodecs, PickleBuffer} import scala.reflect.io.NoAbstractFile +import scala.tools.nsc.util.ClassPath import scala.tools.nsc.io.AbstractFile -import scala.tools.nsc.util.ClassFileLookup /** This abstract class implements a class file parser. * @@ -43,8 +43,8 @@ abstract class ClassfileParser { */ protected def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol - /** The way of the class file lookup used by the compiler. */ - def classFileLookup: ClassFileLookup[AbstractFile] + /** The compiler classpath. */ + def classPath: ClassPath import definitions._ import scala.reflect.internal.ClassfileConstants._ @@ -357,7 +357,7 @@ abstract class ClassfileParser { } private def loadClassSymbol(name: Name): Symbol = { - val file = classFileLookup findClassFile name.toString getOrElse { + val file = classPath findClassFile name.toString getOrElse { // SI-5593 Scaladoc's current strategy is to visit all packages in search of user code that can be documented // therefore, it will rummage through the classpath triggering errors whenever it encounters package objects // that are not in their correct place (see bug for details) @@ -1079,7 +1079,7 @@ 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) + val file = classPath.findClassFile(entry.externalName.toString) enterClassAndModule(entry, file.getOrElse(NoAbstractFile)) } } diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala index 085a814c6b..9a8eca152f 100644 --- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala +++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala @@ -8,208 +8,17 @@ package transform import symtab._ import Flags._ -import scala.tools.nsc.util.ClassPath abstract class AddInterfaces extends InfoTransform { self: Erasure => import global._ // the global environment import definitions._ // standard classes and methods - /** The phase sets lateINTERFACE for non-interface traits that now - * become interfaces. It sets lateDEFERRED for formerly concrete - * methods in such traits. + /** lateDEFERRED for formerly concrete methods in such traits. */ - override def phaseNewFlags: Long = lateDEFERRED | lateINTERFACE - - /** A lazily constructed map that associates every non-interface trait with - * its implementation class. - */ - private val implClassMap = perRunCaches.newMap[Symbol, Symbol]() - - /** A lazily constructed map that associates every concrete method in a non-interface - * trait that's currently compiled with its corresponding method in the trait's - * implementation class. - */ - private val implMethodMap = perRunCaches.newMap[Symbol, Symbol]() - - override def newPhase(prev: scala.tools.nsc.Phase): StdPhase = { - implClassMap.clear() - implMethodMap.clear() - super.newPhase(prev) - } - - /** Is given trait member symbol a member of the trait's interface - * after this transform is performed? - */ - private def isInterfaceMember(sym: Symbol) = ( - sym.isType || { - sym.info // initialize to set lateMETHOD flag if necessary - - ( sym.isMethod - && !sym.isLabel - && !sym.isPrivate - && (!(sym hasFlag BRIDGE) || sym.hasBridgeAnnotation) // count @bridge annotated classes as interface members - && !sym.isConstructor - && !sym.isImplOnly - ) - } - ) - - /** Does symbol need an implementation method? */ - def needsImplMethod(sym: Symbol) = ( - sym.isMethod - && isInterfaceMember(sym) - && (!sym.hasFlag(DEFERRED | SUPERACCESSOR) || (sym hasFlag lateDEFERRED)) - ) - - def implClassPhase = currentRun.erasurePhase.next - - private def newImplClass(iface: Symbol): Symbol = { - val inClass = iface.owner.isClass - val implName = tpnme.implClassName(iface.name) - val implFlags = (iface.flags & ~(INTERFACE | lateINTERFACE)) | IMPLCLASS - - val impl0 = { - if (!inClass) NoSymbol - else { - val typeInfo = iface.owner.info - typeInfo.decl(implName) match { - case NoSymbol => NoSymbol - case implSym => - // Unlink a pre-existing symbol only if the implementation class is - // visible on the compilation classpath. In general this is true under - // -optimise and not otherwise, but the classpath can use arbitrary - // logic so the classpath must be queried. - // TODO this is not taken into account by flat classpath yet - classPath match { - case cp: ClassPath[_] if !cp.context.isValidName(implName + ".class") => - log(s"not unlinking $iface's existing implClass ${implSym.name} because it is not on the classpath.") - implSym - case _ => - typeInfo.decls unlink implSym - NoSymbol - } - } - } - } - - val impl = impl0 orElse { - val impl = iface.owner.newImplClass(implName, iface.pos, implFlags) - if (iface.thisSym != iface) { - impl.typeOfThis = iface.typeOfThis - impl.thisSym setName iface.thisSym.name - } - impl.associatedFile = iface.sourceFile - if (inClass) - iface.owner.info.decls enter impl - - impl - } - if (currentRun compiles iface) - currentRun.symSource(impl) = iface.sourceFile - - implClassMap(iface) = impl - impl setInfo new LazyImplClassType(iface) - } - - /** Return the implementation class of a trait; create a new one if one does not yet exist */ - def implClass(iface: Symbol): Symbol = { - iface.info - - implClassMap.getOrElse(iface, enteringPhase(implClassPhase) { - if (iface.implClass eq NoSymbol) - debuglog(s"${iface.fullLocationString} has no implClass yet, creating it now.") - else - log(s"${iface.fullLocationString} impl class is ${iface.implClass.nameString}") - - newImplClass(iface) - }) - } - - /** A lazy type to set the info of an implementation class - * The parents of an implementation class for trait iface are: - * - * - superclass: Object - * - mixin classes: mixin classes of iface where every non-interface - * trait is mapped to its implementation class, followed by iface itself. - * - * The declarations of a mixin class are: - * - for every interface member of iface: its implementation method, if one is needed - * - every former member of iface that is implementation only - */ - private class LazyImplClassType(iface: Symbol) extends LazyType with FlagAgnosticCompleter { - /** Compute the decls of implementation class implClass, - * given the decls ifaceDecls of its interface. - */ - private def implDecls(implClass: Symbol, ifaceDecls: Scope): Scope = { - debuglog("LazyImplClassType calculating decls for " + implClass) - - val decls = newScope - if ((ifaceDecls lookup nme.MIXIN_CONSTRUCTOR) == NoSymbol) { - log("Adding mixin constructor to " + implClass) - - decls enter ( - implClass.newMethod(nme.MIXIN_CONSTRUCTOR, implClass.pos) - setInfo MethodType(Nil, UnitTpe) - ) - } - - for (sym <- ifaceDecls) { - if (isInterfaceMember(sym)) { - if (needsImplMethod(sym)) { - val clone = sym.cloneSymbol(implClass).resetFlag(lateDEFERRED) - if (currentRun.compiles(implClass)) implMethodMap(sym) = clone - decls enter clone - sym setFlag lateDEFERRED - if (!sym.isSpecialized) - log(s"Cloned ${sym.name} from ${sym.owner} into implClass ${implClass.fullName}") - } - } - else { - log(s"Destructively modifying owner of $sym from ${sym.owner} to $implClass") - sym.owner = implClass - // note: OK to destructively modify the owner here, - // because symbol will not be accessible from outside the sourcefile. - // mixin constructors are corrected separately; see TermSymbol.owner - decls enter sym - } - } - - decls - } - - override def complete(implSym: Symbol) { - debuglog("LazyImplClassType completing " + implSym) - - /* If `tp` refers to a non-interface trait, return a - * reference to its implementation class. Otherwise return `tp`. - */ - def mixinToImplClass(tp: Type): Type = AddInterfaces.this.erasure(implSym) { - tp match { //@MATN: no normalize needed (comes after erasure) - case TypeRef(pre, sym, _) if sym.needsImplClass => - typeRef(pre, implClass(sym), Nil) - case _ => - tp - } - } - def implType(tp: Type): Type = tp match { - case ClassInfoType(parents, decls, _) => - assert(phase == implClassPhase, tp) - // Impl class parents: Object first, matching interface last. - val implParents = ObjectTpe +: (parents.tail map mixinToImplClass filter (_.typeSymbol != ObjectClass)) :+ iface.tpe - ClassInfoType(implParents, implDecls(implSym, decls), implSym) - case PolyType(_, restpe) => - implType(restpe) - } - implSym setInfo implType(enteringErasure(iface.info)) - } - - override def load(clazz: Symbol) { complete(clazz) } - } + override def phaseNewFlags: Long = lateDEFERRED def transformMixinInfo(tp: Type): Type = tp match { case ClassInfoType(parents, decls, clazz) if clazz.isPackageClass || !clazz.isJavaDefined => - if (clazz.needsImplClass) - implClass(clazz setFlag lateINTERFACE) // generate an impl class val parents1 = parents match { case Nil => Nil @@ -218,21 +27,20 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure => if (clazz.isTrait) ObjectTpe :: tl else parents } - val decls1 = scopeTransform(clazz)( - decls filter (sym => - if (clazz.isInterface) isInterfaceMember(sym) - else sym.isClass || sym.isTerm - ) - ) - ClassInfoType(parents1, decls1, clazz) + if (clazz.isTrait) { + decls foreach { sym => + if (!sym.isType) sym.info // initialize to set lateMETHOD flag if necessary + } + } + if (parents1 eq parents) tp + else ClassInfoType(parents1, decls, clazz) case _ => tp } // Tree transformation -------------------------------------------------------------- - private class ChangeOwnerAndReturnTraverser(oldowner: Symbol, newowner: Symbol) - extends ChangeOwnerTraverser(oldowner, newowner) { + extends ChangeOwnerTraverser(oldowner, newowner) { override def traverse(tree: Tree) { tree match { case _: Return => change(tree.symbol) @@ -242,61 +50,10 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure => } } - private def isInterfaceTree(tree: Tree) = tree.isDef && isInterfaceMember(tree.symbol) - - private def deriveMemberForImplClass(tree: Tree): Tree = - if (isInterfaceTree(tree)) if (needsImplMethod(tree.symbol)) implMethodDef(tree) else EmptyTree - else tree - - private def deriveMemberForInterface(tree: Tree): Tree = - if (isInterfaceTree(tree)) if (needsImplMethod(tree.symbol)) DefDef(tree.symbol, EmptyTree) else tree - else EmptyTree - - private def ifaceTemplate(templ: Template): Template = - treeCopy.Template(templ, templ.parents, noSelfType, templ.body map deriveMemberForInterface) - - /** Transforms the member tree containing the implementation - * into a member of the impl class. - */ - private def implMethodDef(tree: Tree): Tree = { - val impl = implMethodMap.getOrElse(tree.symbol, abort("implMethod missing for " + tree.symbol)) - - val newTree = if (impl.isErroneous) tree else { // e.g. res/t687 - // SI-5167: Ensure that the tree that we are grafting refers the parameter symbols from the - // new method symbol `impl`, rather than the symbols of the original method signature in - // the trait. `tree setSymbol impl` does *not* suffice! - val DefDef(_, _, _, vparamss, _, _) = tree - val oldSyms = vparamss.flatten.map(_.symbol) - val newSyms = impl.info.paramss.flatten - assert(oldSyms.length == newSyms.length, (oldSyms, impl, impl.info)) - tree.substituteSymbols(oldSyms, newSyms) - } - new ChangeOwnerAndReturnTraverser(newTree.symbol, impl)(newTree setSymbol impl) - } - - /** Add mixin constructor definition - * def $init$(): Unit = () - * to `stats` unless there is already one. - */ - private def addMixinConstructorDef(clazz: Symbol, stats: List[Tree]): List[Tree] = - if (treeInfo.firstConstructor(stats) != EmptyTree) stats - else DefDef(clazz.primaryConstructor, Block(List(), Literal(Constant(())))) :: stats - - private def implTemplate(clazz: Symbol, templ: Template): Template = atPos(templ.pos) { - val templ1 = ( - Template(templ.parents, noSelfType, addMixinConstructorDef(clazz, templ.body map deriveMemberForImplClass)) - setSymbol clazz.newLocalDummy(templ.pos) - ) - templ1.changeOwner(templ.symbol.owner -> clazz, templ.symbol -> templ1.symbol) - templ1 - } - - def implClassDefs(trees: List[Tree]): List[Tree] = { - trees collect { - case cd: ClassDef if cd.symbol.needsImplClass => - val clazz = implClass(cd.symbol).initialize - ClassDef(clazz, implTemplate(clazz, cd.impl)) - } + private def mkAssign(clazz: Symbol, assignSym: Symbol, rhs: Tree): Tree = { + val qual = Select(This(clazz), assignSym) + if (assignSym.isSetter) Apply(qual, List(rhs)) + else Assign(qual, rhs) } /** Add calls to supermixin constructors @@ -304,15 +61,16 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure => * to tree, which is assumed to be the body of a constructor of class clazz. */ private def addMixinConstructorCalls(tree: Tree, clazz: Symbol): Tree = { - def mixinConstructorCall(impl: Symbol): Tree = atPos(tree.pos) { - Apply(Select(This(clazz), impl.primaryConstructor), List()) + def mixinConstructorCall(mc: Symbol): Tree = atPos(tree.pos) { + Apply(SuperSelect(clazz, mc.primaryConstructor), Nil) } val mixinConstructorCalls: List[Tree] = { for (mc <- clazz.mixinClasses.reverse - if mc.hasFlag(lateINTERFACE)) - yield mixinConstructorCall(implClass(mc)) + if mc.isTrait && mc.primaryConstructor != NoSymbol) + yield mixinConstructorCall(mc) } tree match { + case Block(Nil, expr) => // AnyVal constructor - have to provide a real body so the // jvm doesn't throw a VerifyError. But we can't add the @@ -329,42 +87,14 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure => } protected val mixinTransformer = new Transformer { - override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = - (super.transformStats(stats, exprOwner) ::: - super.transformStats(implClassDefs(stats), exprOwner)) override def transform(tree: Tree): Tree = { val sym = tree.symbol val tree1 = tree match { - case ClassDef(mods, _, _, impl) if sym.needsImplClass => - implClass(sym).initialize // to force lateDEFERRED flags - copyClassDef(tree)(mods = mods | INTERFACE, impl = ifaceTemplate(impl)) case DefDef(_,_,_,_,_,_) if sym.isClassConstructor && sym.isPrimaryConstructor && sym.owner != ArrayClass => deriveDefDef(tree)(addMixinConstructorCalls(_, sym.owner)) // (3) case Template(parents, self, body) => val parents1 = sym.owner.info.parents map (t => TypeTree(t) setPos tree.pos) treeCopy.Template(tree, parents1, noSelfType, body) - case This(_) if sym.needsImplClass => - val impl = implClass(sym) - var owner = currentOwner - while (owner != sym && owner != impl) owner = owner.owner; - if (owner == impl) This(impl) setPos tree.pos - else tree - //TODO what about this commented out code? -/* !!! - case Super(qual, mix) => - val mix1 = mix - if (mix == tpnme.EMPTY) mix - else { - val ps = enteringErasure { - sym.info.parents dropWhile (p => p.symbol.name != mix) - } - assert(!ps.isEmpty, tree); - if (ps.head.symbol.needsImplClass) implClass(ps.head.symbol).name - else mix - } - if (sym.needsImplClass) Super(implClass(sym), mix1) setPos tree.pos - else treeCopy.Super(tree, qual, mix1) -*/ case _ => tree } diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index 112dedce81..0fb6213d36 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -21,16 +21,8 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL { val phaseName: String = "cleanup" /* used in GenBCode: collects ClassDef symbols owning a main(Array[String]) method */ - private var entryPoints: List[Symbol] = null - def getEntryPoints: List[Symbol] = { - assert(settings.isBCodeActive, "Candidate Java entry points are collected here only when GenBCode in use.") - entryPoints sortBy ("" + _.fullName) // For predictably ordered error messages. - } - - override def newPhase(prev: scala.tools.nsc.Phase): StdPhase = { - entryPoints = if (settings.isBCodeActive) Nil else null; - super.newPhase(prev) - } + private var entryPoints: List[Symbol] = Nil + def getEntryPoints: List[Symbol] = entryPoints sortBy ("" + _.fullName) // For predictably ordered error messages. protected def newTransformer(unit: CompilationUnit): Transformer = new CleanUpTransformer(unit) @@ -378,11 +370,7 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL { } override def transform(tree: Tree): Tree = tree match { - - case _: ClassDef - if (entryPoints != null) && - genBCode.isJavaEntryPoint(tree.symbol, currentUnit) - => + case _: ClassDef if genBCode.isJavaEntryPoint(tree.symbol, currentUnit) => // collecting symbols for entry points here (as opposed to GenBCode where they are used) // has the advantage of saving an additional pass over all ClassDefs. entryPoints ::= tree.symbol diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index aef2817db7..636fb08b89 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -73,7 +73,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { override def transform(tree: Tree): Tree = { tree match { - case cd @ ClassDef(mods0, name0, tparams0, impl0) if !cd.symbol.isInterface && !isPrimitiveValueClass(cd.symbol) => + case cd @ ClassDef(mods0, name0, tparams0, impl0) if !isPrimitiveValueClass(cd.symbol) && cd.symbol.primaryConstructor != NoSymbol => if(cd.symbol eq AnyValClass) { cd } @@ -305,7 +305,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { val delayedHook = delayedEndpointDef(remainingConstrStats) val delayedHookSym = delayedHook.symbol.asInstanceOf[MethodSymbol] - // transform to make the closure-class' default constructor assign the the outer instance to its param-accessor field. + // transform to make the closure-class' default constructor assign the outer instance to its param-accessor field. val hookCallerClass = (new ConstructorTransformer(unit)) transform delayedInitClosure(delayedHookSym) val delayedInitCall = localTyper.typedPos(impl.pos) { gen.mkMethodCall(This(clazz), delayedInitMethod, Nil, List(New(hookCallerClass.symbol.tpe, This(clazz)))) @@ -456,7 +456,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { // find and dissect primary constructor private val (primaryConstr, _primaryConstrParams, primaryConstrBody) = stats collectFirst { - case dd@DefDef(_, _, _, vps :: Nil, _, rhs: Block) if dd.symbol.isPrimaryConstructor => (dd, vps map (_.symbol), rhs) + case dd@DefDef(_, _, _, vps :: Nil, _, rhs: Block) if dd.symbol.isPrimaryConstructor || dd.symbol.isMixinConstructor => (dd, vps map (_.symbol), rhs) } getOrElse { abort("no constructor in template: impl = " + impl) } @@ -501,8 +501,6 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { !sym.isSetter ) - private def possiblySpecialized(s: Symbol) = specializeTypes.specializedTypeVars(s).nonEmpty - /* * whether `sym` denotes a param-accessor (ie a field) that fulfills all of: * (a) has stationary value, ie the same value provided via the corresponding ctor-arg; and @@ -511,15 +509,17 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { * (b.2) the constructor in the specialized (sub-)class. * (c) isn't part of a DelayedInit subclass. */ - private def canBeSupplanted(sym: Symbol) = !isDelayedInitSubclass && isStationaryParamRef(sym) && !possiblySpecialized(sym) + private def canBeSupplanted(sym: Symbol) = !isDelayedInitSubclass && isStationaryParamRef(sym) && !specializeTypes.possiblySpecialized(sym) override def transform(tree: Tree): Tree = tree match { case Apply(Select(This(_), _), List()) => // references to parameter accessor methods of own class become references to parameters // outer accessors become references to $outer parameter - if (canBeSupplanted(tree.symbol)) + if (clazz.isTrait) + super.transform(tree) + else if (canBeSupplanted(tree.symbol)) gen.mkAttributedIdent(parameter(tree.symbol.accessed)) setPos tree.pos - else if (tree.symbol.outerSource == clazz && !clazz.isImplClass) + else if (tree.symbol.outerSource == clazz) gen.mkAttributedIdent(parameterNamed(nme.OUTER)) setPos tree.pos else super.transform(tree) @@ -529,7 +529,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { gen.mkAttributedIdent(parameter(tree.symbol)) setPos tree.pos case Select(_, _) if guardSpecializedFieldInit => // reasoning behind this guard in the docu of `usesSpecializedField` - if (possiblySpecialized(tree.symbol)) { + if (specializeTypes.possiblySpecialized(tree.symbol)) { usesSpecializedField = true } super.transform(tree) @@ -566,7 +566,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { } // Constant typed vals are not memoized. - def memoizeValue(sym: Symbol) = !sym.info.isInstanceOf[ConstantType] + def memoizeValue(sym: Symbol) = !sym.info.resultType.isInstanceOf[ConstantType] /** Triage definitions and statements in this template into the following categories. * The primary constructor is treated separately, as it is assembled in part from these pieces. @@ -587,7 +587,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { for (stat <- primaryConstrBody.stats) { constrStatBuf += stat stat match { - case ValDef(mods, name, _, _) if mods hasFlag PRESUPER => + case ValDef(mods, name, _, _) if mods.hasFlag(PRESUPER) => // stat is the constructor-local definition of the field value val fields = presupers filter (_.getterName == name) assert(fields.length == 1, s"expected exactly one field by name $name in $presupers of $clazz's early initializers") @@ -624,12 +624,12 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { stat match { // recurse on class definition, store in defBuf - case _: ClassDef => defBuf += new ConstructorTransformer(unit).transform(stat) + case _: ClassDef if !stat.symbol.isInterface => defBuf += new ConstructorTransformer(unit).transform(stat) // Triage methods -- they all end up in the template -- // regular ones go to `defBuf`, secondary contructors go to `auxConstructorBuf`. // The primary constructor is dealt with separately (we're massaging it here). - case _: DefDef if statSym.isPrimaryConstructor => () + case _: DefDef if statSym.isPrimaryConstructor || statSym.isMixinConstructor => () case _: DefDef if statSym.isConstructor => auxConstructorBuf += stat case _: DefDef => defBuf += stat diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala index f15d05f7df..d350ca8e17 100644 --- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala +++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala @@ -4,33 +4,21 @@ package transform import symtab._ import Flags._ import scala.collection._ -import scala.collection.mutable.LinkedHashMap /** - * This transformer is responsible for preparing lambdas for runtime, by either translating to anonymous classes - * or to a tree that will be convereted to invokedynamic by the JVM 1.8+ backend. - * - * The main assumption it makes is that a lambda {args => body} has been turned into - * {args => liftedBody()} where lifted body is a top level method that implements the body of the lambda. - * Currently Uncurry is responsible for that transformation. - * - * From a lambda, Delambdafy will create: - * - * Under GenASM - * - * 1) a new top level class that - a) has fields and a constructor taking the captured environment (including possibly the "this" - * reference) - * b) an apply method that calls the target method - * c) if needed a bridge method for the apply method - * 2) an instantiation of the newly created class which replaces the lambda - * - * Under GenBCode: - * - * 1) An application of the captured arguments to a fictional symbol representing the lambda factory. - * This will be translated by the backed into an invokedynamic using a bootstrap method in JDK8's `LambdaMetaFactory`. - * The captured arguments include `this` if `liftedBody` is unable to be made STATIC. - */ + * This transformer is responsible for preparing Function nodes for runtime, + * by translating to a tree that will be converted to an invokedynamic by the backend. + * + * The main assumption it makes is that a Function {args => body} has been turned into + * {args => liftedBody()} where lifted body is a top level method that implements the body of the function. + * Currently Uncurry is responsible for that transformation. + * + * From this shape of Function, Delambdafy will create: + * + * An application of the captured arguments to a fictional symbol representing the lambda factory. + * This will be translated by the backed into an invokedynamic using a bootstrap method in JDK8's `LambdaMetaFactory`. + * The captured arguments include `this` if `liftedBody` is unable to be made STATIC. + */ abstract class Delambdafy extends Transform with TypingTransformers with ast.TreeDSL with TypeAdaptingTransformer { import global._ import definitions._ @@ -40,6 +28,19 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre /** the following two members override abstract members in Transform */ val phaseName: String = "delambdafy" + final case class LambdaMetaFactoryCapable(target: Symbol, arity: Int, functionalInterface: Symbol, sam: Symbol) + + /** + * Get the symbol of the target lifted lambda body method from a function. I.e. if + * the function is {args => anonfun(args)} then this method returns anonfun's symbol + */ + private def targetMethod(fun: Function): Symbol = fun match { + case Function(_, Apply(target, _)) => target.symbol + case _ => + // any other shape of Function is unexpected at this point + abort(s"could not understand function with tree $fun") + } + override def newPhase(prev: scala.tools.nsc.Phase): StdPhase = { if (settings.Ydelambdafy.value == "method") new Phase(prev) else new SkipPhase(prev) @@ -52,433 +53,217 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre protected def newTransformer(unit: CompilationUnit): Transformer = new DelambdafyTransformer(unit) - class DelambdafyTransformer(unit: CompilationUnit) extends TypingTransformer(unit) with TypeAdapter { - private val lambdaClassDefs = new mutable.LinkedHashMap[Symbol, List[Tree]] withDefaultValue Nil + class DelambdafyTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { + // we need to know which methods refer to the 'this' reference so that we can determine which lambdas need access to it + // TODO: this looks expensive, so I made it a lazy val. Can we make it more pay-as-you-go / optimize for common shapes? + private[this] lazy val methodReferencesThis: Set[Symbol] = + (new ThisReferringMethodsTraverser).methodReferencesThisIn(unit.body) + + private def mkLambdaMetaFactoryCall(fun: Function, target: Symbol, functionalInterface: Symbol, samUserDefined: Symbol, isSpecialized: Boolean): Tree = { + val pos = fun.pos + val allCapturedArgRefs = { + // find which variables are free in the lambda because those are captures that need to be + // passed into the constructor of the anonymous function class + val captureArgs = FreeVarTraverser.freeVarsOf(fun).iterator.map(capture => + gen.mkAttributedRef(capture) setPos pos + ).toList + + if (target hasFlag STATIC) captureArgs // no `this` reference needed + else (gen.mkAttributedThis(fun.symbol.enclClass) setPos pos) :: captureArgs + } + // Create a symbol representing a fictional lambda factory method that accepts the captured + // arguments and returns the SAM type. + val msym = { + val meth = currentOwner.newMethod(nme.ANON_FUN_NAME, pos, ARTIFACT) + val capturedParams = meth.newSyntheticValueParams(allCapturedArgRefs.map(_.tpe)) + meth.setInfo(MethodType(capturedParams, fun.tpe)) + } - val typer = localTyper + // We then apply this symbol to the captures. + val apply = localTyper.typedPos(pos)(Apply(Ident(msym), allCapturedArgRefs)) - // we need to know which methods refer to the 'this' reference so that we can determine - // which lambdas need access to it - val thisReferringMethods: Set[Symbol] = { - val thisReferringMethodsTraverser = new ThisReferringMethodsTraverser() - thisReferringMethodsTraverser traverse unit.body - val methodReferringMap = thisReferringMethodsTraverser.liftedMethodReferences - val referrers = thisReferringMethodsTraverser.thisReferringMethods - // recursively find methods that refer to 'this' directly or indirectly via references to other methods - // for each method found add it to the referrers set - def refersToThis(symbol: Symbol): Boolean = { - if (referrers contains symbol) true - else if (methodReferringMap(symbol) exists refersToThis) { - // add it early to memoize - debuglog(s"$symbol indirectly refers to 'this'") - referrers += symbol - true - } else false + // TODO: this is a bit gross + val sam = samUserDefined orElse { + if (isSpecialized) functionalInterface.info.decls.find(_.isDeferred).get + else functionalInterface.info.member(nme.apply) } - methodReferringMap.keys foreach refersToThis - referrers + + // no need for adaptation when the implemented sam is of a specialized built-in function type + val lambdaTarget = if (isSpecialized) target else createBoxingBridgeMethodIfNeeded(fun, target, functionalInterface, sam) + + // The backend needs to know the target of the lambda and the functional interface in order + // to emit the invokedynamic instruction. We pass this information as tree attachment. + // + // see https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/LambdaMetafactory.html + // instantiatedMethodType is derived from lambdaTarget's signature + // samMethodType is derived from samOf(functionalInterface)'s signature + apply.updateAttachment(LambdaMetaFactoryCapable(lambdaTarget, fun.vparams.length, functionalInterface, sam)) + + apply } - // the result of the transformFunction method. - sealed abstract class TransformedFunction - // A class definition for the lambda, an expression instantiating the lambda class - case class DelambdafyAnonClass(lambdaClassDef: ClassDef, newExpr: Tree) extends TransformedFunction - case class InvokeDynamicLambda(tree: Apply) extends TransformedFunction private val boxingBridgeMethods = mutable.ArrayBuffer[Tree]() - // here's the main entry point of the transform - override def transform(tree: Tree): Tree = tree match { - // the main thing we care about is lambdas - case fun @ Function(_, _) => - transformFunction(fun) match { - case DelambdafyAnonClass(lambdaClassDef, newExpr) => - // a lambda becomes a new class, an instantiation expression - val pkg = lambdaClassDef.symbol.owner - - // we'll add the lambda class to the package later - lambdaClassDefs(pkg) = lambdaClassDef :: lambdaClassDefs(pkg) - - super.transform(newExpr) - case InvokeDynamicLambda(apply) => - // ... or an invokedynamic call - super.transform(apply) - } - case Template(_, _, _) => - try { - // during this call boxingBridgeMethods will be populated from the Function case - val Template(parents, self, body) = super.transform(tree) - Template(parents, self, body ++ boxingBridgeMethods) - } finally boxingBridgeMethods.clear() - case _ => super.transform(tree) + private def reboxValueClass(tp: Type) = tp match { + case ErasedValueType(valueClazz, _) => TypeRef(NoPrefix, valueClazz, Nil) + case _ => tp } - // this entry point is aimed at the statements in the compilation unit. - // after working on the entire compilation until we'll have a set of - // new class definitions to add to the top level - override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { - // Need to remove from the lambdaClassDefs map: there may be multiple PackageDef for the same - // package when defining a package object. We only add the lambda class to one. See SI-9097. - super.transformStats(stats, exprOwner) ++ lambdaClassDefs.remove(exprOwner).getOrElse(Nil) + // exclude primitives and value classes, which need special boxing + private def isReferenceType(tp: Type) = !tp.isInstanceOf[ErasedValueType] && { + val sym = tp.typeSymbol + !(isPrimitiveValueClass(sym) || sym.isDerivedValueClass) } - private def optionSymbol(sym: Symbol): Option[Symbol] = if (sym.exists) Some(sym) else None + // determine which lambda target to use with java's LMF -- create a new one if scala-specific boxing is required + def createBoxingBridgeMethodIfNeeded(fun: Function, target: Symbol, functionalInterface: Symbol, sam: Symbol): Symbol = { + val oldClass = fun.symbol.enclClass + val pos = fun.pos + + // At erasure, there won't be any captured arguments (they are added in constructors) + val functionParamTypes = exitingErasure(target.info.paramTypes) + val functionResultType = exitingErasure(target.info.resultType) + + val samParamTypes = exitingErasure(sam.info.paramTypes) + val samResultType = exitingErasure(sam.info.resultType) + + /** How to satisfy the linking invariants of https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/LambdaMetafactory.html + * + * Given samMethodType: (U1..Un)Ru and function type T1,..., Tn => Rt (the target method created by uncurry) + * + * Do we need a bridge, or can we use the original lambda target for implMethod: (<captured args> A1..An)Ra + * (We can ignore capture here.) + * + * If, for i=1..N: + * Ai =:= Ui || (Ai <:< Ui <:< AnyRef) + * Ru =:= void || (Ra =:= Ru || (Ra <:< AnyRef, Ru <:< AnyRef)) + * + * We can use the target method as-is -- if not, we create a bridging one that uses the types closest + * to the target method that still meet the above requirements. + */ + val resTpOk = ( + samResultType =:= UnitTpe + || functionResultType =:= samResultType + || (isReferenceType(samResultType) && isReferenceType(functionResultType))) // yes, this is what the spec says -- no further correspondance required + if (resTpOk && (samParamTypes corresponds functionParamTypes){ (samParamTp, funParamTp) => + funParamTp =:= samParamTp || (isReferenceType(funParamTp) && isReferenceType(samParamTp) && funParamTp <:< samParamTp) }) target + else { + // We have to construct a new lambda target that bridges to the one created by uncurry. + // The bridge must satisfy the above invariants, while also minimizing adaptation on our end. + // LMF will insert runtime casts according to the spec at the above link. + + // we use the more precise type between samParamTp and funParamTp to minimize boxing in the bridge method + // we are constructing a method whose signature matches the sam's signature (because the original target did not) + // whenever a type in the sam's signature is (erases to) a primitive type, we must pick the sam's version, + // as we don't implement the logic regarding widening that's performed by LMF -- we require =:= for primitives + // + // We use the sam's type for the check whether we're dealin with a reference type, as it could be a generic type, + // which means the function's parameter -- even if it expects a value class -- will need to be + // boxed on the generic call to the sam method. - // turns a lambda into a new class def, a New expression instantiating that class - private def transformFunction(originalFunction: Function): TransformedFunction = { - val functionTpe = originalFunction.tpe - val targs = functionTpe.typeArgs - val formals :+ restpe = targs - val oldClass = originalFunction.symbol.enclClass + val bridgeParamTypes = map2(samParamTypes, functionParamTypes){ (samParamTp, funParamTp) => + if (isReferenceType(samParamTp) && funParamTp <:< samParamTp) funParamTp + else samParamTp + } - // find which variables are free in the lambda because those are captures that need to be - // passed into the constructor of the anonymous function class - val captures = FreeVarTraverser.freeVarsOf(originalFunction) + val bridgeResultType = + if (resTpOk && isReferenceType(samResultType) && functionResultType <:< samResultType) functionResultType + else samResultType - val target = targetMethod(originalFunction) - target.makeNotPrivate(target.owner) - if (!thisReferringMethods.contains(target)) - target setFlag STATIC + val typeAdapter = new TypeAdapter { def typedPos(pos: Position)(tree: Tree): Tree = localTyper.typedPos(pos)(tree) } + import typeAdapter.{adaptToType, unboxValueClass} - val isStatic = target.hasFlag(STATIC) + val targetParams = target.paramss.head + val numCaptures = targetParams.length - functionParamTypes.length + val (targetCapturedParams, targetFunctionParams) = targetParams.splitAt(numCaptures) - def createBoxingBridgeMethod(functionParamTypes: List[Type], functionResultType: Type): Tree = { - // Note: we bail out of this method and return EmptyTree if we find there is no adaptation required. - // If we need to improve performance, we could check the types first before creating the - // method and parameter symbols. val methSym = oldClass.newMethod(target.name.append("$adapted").toTermName, target.pos, target.flags | FINAL | ARTIFACT) - var neededAdaptation = false - def boxedType(tpe: Type): Type = { - if (isPrimitiveValueClass(tpe.typeSymbol)) {neededAdaptation = true; ObjectTpe} - else if (enteringErasure(tpe.typeSymbol.isDerivedValueClass)) {neededAdaptation = true; ObjectTpe} - else tpe - } - val targetParams: List[Symbol] = target.paramss.head - val numCaptures = targetParams.length - functionParamTypes.length - val (targetCaptureParams, targetFunctionParams) = targetParams.splitAt(numCaptures) - val bridgeParams: List[Symbol] = - targetCaptureParams.map(param => methSym.newSyntheticValueParam(param.tpe, param.name.toTermName)) ::: - map2(targetFunctionParams, functionParamTypes)((param, tp) => methSym.newSyntheticValueParam(boxedType(tp), param.name.toTermName)) - - val bridgeResultType: Type = { - if (target.info.resultType == UnitTpe && functionResultType != UnitTpe) { - neededAdaptation = true - ObjectTpe - } else - boxedType(functionResultType) - } - val methodType = MethodType(bridgeParams, bridgeResultType) - methSym setInfo methodType - if (!neededAdaptation) - EmptyTree - else { - val bridgeParamTrees = bridgeParams.map(ValDef(_)) - - oldClass.info.decls enter methSym - - val body = localTyper.typedPos(originalFunction.pos) { - val newTarget = Select(gen.mkAttributedThis(oldClass), target) - val args: List[Tree] = mapWithIndex(bridgeParams) { (param, i) => - if (i < numCaptures) { - gen.mkAttributedRef(param) - } else { - val functionParam = functionParamTypes(i - numCaptures) - val targetParam = targetParams(i) - if (enteringErasure(functionParam.typeSymbol.isDerivedValueClass)) { - val casted = cast(gen.mkAttributedRef(param), functionParam) - val unboxed = unbox(casted, ErasedValueType(functionParam.typeSymbol, targetParam.tpe)).modifyType(postErasure.elimErasedValueType) - unboxed - } else adaptToType(gen.mkAttributedRef(param), targetParam.tpe) - } + val bridgeCapturedParams = targetCapturedParams.map(param => methSym.newSyntheticValueParam(param.tpe, param.name.toTermName)) + val bridgeFunctionParams = + map2(targetFunctionParams, bridgeParamTypes)((param, tp) => methSym.newSyntheticValueParam(tp, param.name.toTermName)) + + val bridgeParams = bridgeCapturedParams ::: bridgeFunctionParams + + methSym setInfo MethodType(bridgeParams, bridgeResultType) + oldClass.info.decls enter methSym + + val forwarderCall = localTyper.typedPos(pos) { + val capturedArgRefs = bridgeCapturedParams map gen.mkAttributedRef + val functionArgRefs = + map3(bridgeFunctionParams, functionParamTypes, targetParams.drop(numCaptures)) { (bridgeParam, functionParamTp, targetParam) => + val bridgeParamRef = gen.mkAttributedRef(bridgeParam) + val targetParamTp = targetParam.tpe + + // TODO: can we simplify this to something like `adaptToType(adaptToType(bridgeParamRef, functionParamTp), targetParamTp)`? + val unboxed = + functionParamTp match { + case ErasedValueType(clazz, underlying) => + // when the original function expected an argument of value class type, + // the original target will expect the unboxed underlying value, + // whereas the bridge will receive the boxed value (since the sam's argument type did not match and we had to adapt) + localTyper.typed(unboxValueClass(bridgeParamRef, clazz, underlying), targetParamTp) + case _ => bridgeParamRef + } + + adaptToType(unboxed, targetParamTp) } - gen.mkMethodCall(newTarget, args) - } - val body1 = if (enteringErasure(functionResultType.typeSymbol.isDerivedValueClass)) - adaptToType(box(body.setType(ErasedValueType(functionResultType.typeSymbol, body.tpe)), "boxing lambda target"), bridgeResultType) - else adaptToType(body, bridgeResultType) - val methDef0 = DefDef(methSym, List(bridgeParamTrees), body1) - postErasure.newTransformer(unit).transform(methDef0).asInstanceOf[DefDef] - } - } - /** - * Creates the apply method for the anonymous subclass of FunctionN - */ - def createApplyMethod(newClass: Symbol, fun: Function, thisProxy: Symbol): DefDef = { - val methSym = newClass.newMethod(nme.apply, fun.pos, FINAL | SYNTHETIC) - val params = fun.vparams map (_.duplicate) - - val paramSyms = map2(formals, params) { - (tp, vparam) => methSym.newSyntheticValueParam(tp, vparam.name) - } - params zip paramSyms foreach { case (valdef, sym) => valdef.symbol = sym } - params foreach (_.symbol.owner = methSym) - - val methodType = MethodType(paramSyms, restpe) - methSym setInfo methodType - - newClass.info.decls enter methSym - val Apply(_, oldParams) = fun.body - val qual = if (thisProxy.exists) - Select(gen.mkAttributedThis(newClass), thisProxy) - else - gen.mkAttributedThis(oldClass) // sort of a lie, EmptyTree.<static method> would be more honest, but the backend chokes on that. - - val body = localTyper typed Apply(Select(qual, target), oldParams) - body.substituteSymbols(fun.vparams map (_.symbol), params map (_.symbol)) - body changeOwner (fun.symbol -> methSym) - - val methDef = DefDef(methSym, List(params), body) + gen.mkMethodCall(Select(gen.mkAttributedThis(oldClass), target), capturedArgRefs ::: functionArgRefs) + } - // Have to repack the type to avoid mismatches when existentials - // appear in the result - see SI-4869. - // TODO probably don't need packedType - methDef.tpt setType localTyper.packedType(body, methSym) - methDef - } + val bridge = postErasure.newTransformer(unit).transform(DefDef(methSym, List(bridgeParams.map(ValDef(_))), + adaptToType(forwarderCall setType functionResultType, bridgeResultType))).asInstanceOf[DefDef] - /** - * Creates the constructor on the newly created class. It will handle - * initialization of members that represent the captured environment - */ - def createConstructor(newClass: Symbol, members: List[ValDef]): DefDef = { - val constrSym = newClass.newConstructor(originalFunction.pos, SYNTHETIC) - - val (paramSymbols, params, assigns) = (members map {member => - val paramSymbol = newClass.newVariable(member.symbol.name.toTermName, newClass.pos, 0) - paramSymbol.setInfo(member.symbol.info) - val paramVal = ValDef(paramSymbol) - val paramIdent = Ident(paramSymbol) - val assign = Assign(Select(gen.mkAttributedThis(newClass), member.symbol), paramIdent) - - (paramSymbol, paramVal, assign) - }).unzip3 - - val constrType = MethodType(paramSymbols, newClass.thisType) - constrSym setInfoAndEnter constrType - - val body = - Block( - List( - Apply(Select(Super(gen.mkAttributedThis(newClass), tpnme.EMPTY) setPos newClass.pos, nme.CONSTRUCTOR) setPos newClass.pos, Nil) setPos newClass.pos - ) ++ assigns, - Literal(Constant(())): Tree - ) setPos newClass.pos - - (localTyper typed DefDef(constrSym, List(params), body) setPos newClass.pos).asInstanceOf[DefDef] + boxingBridgeMethods += bridge + bridge.symbol } + } - val pkg = oldClass.owner - - // Parent for anonymous class def - val abstractFunctionErasedType = AbstractFunctionClass(formals.length).tpe - - // anonymous subclass of FunctionN with an apply method - def makeAnonymousClass: ClassDef = { - val parents = addSerializable(abstractFunctionErasedType) - val funOwner = originalFunction.symbol.owner - - // TODO harmonize the naming of delambdafy anon-fun classes with those spun up by Uncurry - // - make `anonClass.isAnonymousClass` true. - // - use `newAnonymousClassSymbol` or push the required variations into a similar factory method - // - reinstate the assertion in `Erasure.resolveAnonymousBridgeClash` - val suffix = nme.DELAMBDAFY_LAMBDA_CLASS_NAME + "$" + ( - if (funOwner.isPrimaryConstructor) "" - else "$" + funOwner.name + "$" - ) - val oldClassPart = oldClass.name.decode - // make sure the class name doesn't contain $anon, otherwise isAnonymousClass/Function may be true - val name = unit.freshTypeName(s"$oldClassPart$suffix".replace("$anon", "$nestedInAnon")) - - val lambdaClass = pkg newClassSymbol(name, originalFunction.pos, FINAL | SYNTHETIC) addAnnotation SerialVersionUIDAnnotation - lambdaClass.associatedFile = unit.source.file - // make sure currentRun.compiles(lambdaClass) is true (AddInterfaces does the same for trait impl classes) - currentRun.symSource(lambdaClass) = funOwner.sourceFile - lambdaClass setInfo ClassInfoType(parents, newScope, lambdaClass) - assert(!lambdaClass.isAnonymousClass && !lambdaClass.isAnonymousFunction, "anonymous class name: "+ lambdaClass.name) - assert(lambdaClass.isDelambdafyFunction, "not lambda class name: " + lambdaClass.name) - - val captureProxies2 = new LinkedHashMap[Symbol, TermSymbol] - captures foreach {capture => - val sym = lambdaClass.newVariable(unit.freshTermName(capture.name.toString + "$"), capture.pos, SYNTHETIC) - sym setInfo capture.info - captureProxies2 += ((capture, sym)) - } + private def transformFunction(originalFunction: Function): Tree = { + val target = targetMethod(originalFunction) + target.makeNotPrivate(target.owner) - // the Optional proxy that will hold a reference to the 'this' - // object used by the lambda, if any. NoSymbol if there is no this proxy - val thisProxy = { - if (isStatic) - NoSymbol - else { - val sym = lambdaClass.newVariable(nme.FAKE_LOCAL_THIS, originalFunction.pos, SYNTHETIC) - sym.setInfo(oldClass.tpe) - } - } + // must be done before calling createBoxingBridgeMethod and mkLambdaMetaFactoryCall + if (!(target hasFlag STATIC) && !methodReferencesThis(target)) target setFlag STATIC - val decapturify = new DeCapturifyTransformer(captureProxies2, unit, oldClass, lambdaClass, originalFunction.symbol.pos, thisProxy) + val funSym = originalFunction.tpe.typeSymbolDirect + // The functional interface that can be used to adapt the lambda target method `target` to the given function type. + val (functionalInterface, isSpecialized) = + if (!isFunctionSymbol(funSym)) (funSym, false) + else { + val specializedName = + specializeTypes.specializedFunctionName(funSym, + exitingErasure(target.info.paramTypes).map(reboxValueClass) :+ reboxValueClass(exitingErasure(target.info.resultType))).toTypeName - val decapturedFunction = decapturify.transform(originalFunction).asInstanceOf[Function] + val isSpecialized = specializedName != funSym.name + val functionalInterface = // TODO: this is no longer needed, right? we can just use the regular function classes + if (isSpecialized) currentRun.runDefinitions.Scala_Java8_CompatPackage.info.decl(specializedName.prepend("J")) + else FunctionClass(originalFunction.vparams.length) - val members = (optionSymbol(thisProxy).toList ++ (captureProxies2 map (_._2))) map {member => - lambdaClass.info.decls enter member - ValDef(member, gen.mkZero(member.tpe)) setPos decapturedFunction.pos + (functionalInterface, isSpecialized) } - // constructor - val constr = createConstructor(lambdaClass, members) - - // apply method with same arguments and return type as original lambda. - val applyMethodDef = createApplyMethod(lambdaClass, decapturedFunction, thisProxy) - - val bridgeMethod = createBridgeMethod(lambdaClass, originalFunction, applyMethodDef) - - def fulldef(sym: Symbol) = - if (sym == NoSymbol) sym.toString - else s"$sym: ${sym.tpe} in ${sym.owner}" - - bridgeMethod foreach (bm => - // TODO SI-6260 maybe just create the apply method with the signature (Object => Object) in all cases - // rather than the method+bridge pair. - if (bm.symbol.tpe =:= applyMethodDef.symbol.tpe) - erasure.resolveAnonymousBridgeClash(applyMethodDef.symbol, bm.symbol) - ) - - val body = members ++ List(constr, applyMethodDef) ++ bridgeMethod - - // TODO if member fields are private this complains that they're not accessible - localTyper.typedPos(decapturedFunction.pos)(ClassDef(lambdaClass, body)).asInstanceOf[ClassDef] - } - - val allCaptureArgs: List[Tree] = { - val thisArg = if (isStatic) Nil else (gen.mkAttributedThis(oldClass) setPos originalFunction.pos) :: Nil - val captureArgs = captures.iterator.map(capture => gen.mkAttributedRef(capture) setPos originalFunction.pos).toList - thisArg ::: captureArgs - } - - val arity = originalFunction.vparams.length - - // Reconstruct the type of the function entering erasure. - // We do this by taking the type after erasure, and re-boxing `ErasedValueType`. - // - // Unfortunately, the more obvious `enteringErasure(target.info)` doesn't work - // as we would like, value classes in parameter position show up as the unboxed types. - val (functionParamTypes, functionResultType) = exitingErasure { - def boxed(tp: Type) = tp match { - case ErasedValueType(valueClazz, _) => TypeRef(NoPrefix, valueClazz, Nil) - case _ => tp - } - // We don't need to deeply map `boxedValueClassType` over the infos as `ErasedValueType` - // will only appear directly as a parameter type in a method signature, as shown - // https://gist.github.com/retronym/ba81dbd462282c504ff8 - val info = target.info - val boxedParamTypes = info.paramTypes.takeRight(arity).map(boxed) - (boxedParamTypes, boxed(info.resultType)) - } - val functionType = definitions.functionType(functionParamTypes, functionResultType) - - val (functionalInterface, isSpecialized) = java8CompatFunctionalInterface(target, functionType) - if (functionalInterface.exists) { - // Create a symbol representing a fictional lambda factory method that accepts the captured - // arguments and returns a Function. - val msym = currentOwner.newMethod(nme.ANON_FUN_NAME, originalFunction.pos, ARTIFACT) - val argTypes: List[Type] = allCaptureArgs.map(_.tpe) - val params = msym.newSyntheticValueParams(argTypes) - msym.setInfo(MethodType(params, functionType)) - val arity = originalFunction.vparams.length - - val lambdaTarget = - if (isSpecialized) - target - else { - createBoxingBridgeMethod(functionParamTypes, functionResultType) match { - case EmptyTree => - target - case bridge => - boxingBridgeMethods += bridge - bridge.symbol - } - } - - // We then apply this symbol to the captures. - val apply = localTyper.typedPos(originalFunction.pos)(Apply(Ident(msym), allCaptureArgs)).asInstanceOf[Apply] - - // The backend needs to know the target of the lambda and the functional interface in order - // to emit the invokedynamic instruction. We pass this information as tree attachment. - apply.updateAttachment(LambdaMetaFactoryCapable(lambdaTarget, arity, functionalInterface)) - InvokeDynamicLambda(apply) - } else { - val anonymousClassDef = makeAnonymousClass - pkg.info.decls enter anonymousClassDef.symbol - val newStat = Typed(New(anonymousClassDef.symbol, allCaptureArgs: _*), TypeTree(abstractFunctionErasedType)) - val typedNewStat = localTyper.typedPos(originalFunction.pos)(newStat) - DelambdafyAnonClass(anonymousClassDef, typedNewStat) - } + val sam = originalFunction.attachments.get[SAMFunction].map(_.sam).getOrElse(NoSymbol) + mkLambdaMetaFactoryCall(originalFunction, target, functionalInterface, sam, isSpecialized) } - /** - * Creates a bridge method if needed. The bridge method forwards from apply(x1: Object, x2: Object...xn: Object): Object to - * apply(x1: T1, x2: T2...xn: Tn): T0 using type adaptation on each input and output. The only time a bridge isn't needed - * is when the original lambda is already erased to type Object, Object, Object... => Object - */ - def createBridgeMethod(newClass:Symbol, originalFunction: Function, applyMethod: DefDef): Option[DefDef] = { - val bridgeMethSym = newClass.newMethod(nme.apply, applyMethod.pos, FINAL | SYNTHETIC | BRIDGE) - val originalParams = applyMethod.vparamss(0) - val bridgeParams = originalParams map { originalParam => - val bridgeSym = bridgeMethSym.newSyntheticValueParam(ObjectTpe, originalParam.name) - ValDef(bridgeSym) - } - - val bridgeSyms = bridgeParams map (_.symbol) - - val methodType = MethodType(bridgeSyms, ObjectTpe) - bridgeMethSym setInfo methodType - - def adapt(tree: Tree, expectedTpe: Type): (Boolean, Tree) = { - if (tree.tpe =:= expectedTpe) (false, tree) - else (true, adaptToType(tree, expectedTpe)) - } - - def adaptAndPostErase(tree: Tree, pt: Type): (Boolean, Tree) = { - val (needsAdapt, adaptedTree) = adapt(tree, pt) - val trans = postErasure.newTransformer(unit) - val postErasedTree = trans.atOwner(currentOwner)(trans.transform(adaptedTree)) // SI-8017 eliminates ErasedValueTypes - (needsAdapt, postErasedTree) - } - - enteringPhase(currentRun.posterasurePhase) { - // e.g, in: - // class C(val a: Int) extends AnyVal; (x: Int) => new C(x) - // - // This type is: - // (x: Int)ErasedValueType(class C, Int) - val liftedBodyDefTpe: MethodType = { - val liftedBodySymbol = { - val Apply(method, _) = originalFunction.body - method.symbol - } - liftedBodySymbol.info.asInstanceOf[MethodType] - } - val (paramNeedsAdaptation, adaptedParams) = (bridgeSyms zip liftedBodyDefTpe.params map {case (bridgeSym, param) => adapt(Ident(bridgeSym) setType bridgeSym.tpe, param.tpe)}).unzip - // SI-8017 Before, this code used `applyMethod.symbol.info.resultType`. - // But that symbol doesn't have a type history that goes back before `delambdafy`, - // so we just see a plain `Int`, rather than `ErasedValueType(C, Int)`. - // This triggered primitive boxing, rather than value class boxing. - val resTp = liftedBodyDefTpe.finalResultType - val body = Apply(gen.mkAttributedSelect(gen.mkAttributedThis(newClass), applyMethod.symbol), adaptedParams) setType resTp - val (needsReturnAdaptation, adaptedBody) = adaptAndPostErase(body, ObjectTpe) - - val needsBridge = (paramNeedsAdaptation contains true) || needsReturnAdaptation - if (needsBridge) { - val methDef = DefDef(bridgeMethSym, List(bridgeParams), adaptedBody) - newClass.info.decls enter bridgeMethSym - Some((localTyper typed methDef).asInstanceOf[DefDef]) - } else None - } + // here's the main entry point of the transform + override def transform(tree: Tree): Tree = tree match { + // the main thing we care about is lambdas + case fun: Function => super.transform(transformFunction(fun)) + case Template(_, _, _) => + try { + // during this call boxingBridgeMethods will be populated from the Function case + val Template(parents, self, body) = super.transform(tree) + Template(parents, self, body ++ boxingBridgeMethods) + } finally boxingBridgeMethods.clear() + case _ => super.transform(tree) } } // DelambdafyTransformer + // A traverser that finds symbols used but not defined in the given Tree // TODO freeVarTraverser in LambdaLift does a very similar task. With some // analysis this could probably be unified with it @@ -511,40 +296,36 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre } } - // A transformer that converts specified captured symbols into other symbols - // TODO this transform could look more like ThisSubstituter and TreeSymSubstituter. It's not clear that it needs that level of sophistication since the types - // at this point are always very simple flattened/erased types, but it would probably be more robust if it tried to take more complicated types into account - class DeCapturifyTransformer(captureProxies: Map[Symbol, TermSymbol], unit: CompilationUnit, oldClass: Symbol, newClass:Symbol, pos: Position, thisProxy: Symbol) extends TypingTransformer(unit) { - override def transform(tree: Tree) = tree match { - case tree@This(encl) if tree.symbol == oldClass && thisProxy.exists => - gen mkAttributedSelect (gen mkAttributedThis newClass, thisProxy) - case Ident(name) if (captureProxies contains tree.symbol) => - gen mkAttributedSelect (gen mkAttributedThis newClass, captureProxies(tree.symbol)) - case _ => super.transform(tree) + // finds all methods that reference 'this' + class ThisReferringMethodsTraverser extends Traverser { + // the set of methods that refer to this + private val thisReferringMethods = mutable.Set[Symbol]() + + // the set of lifted lambda body methods that each method refers to + private val liftedMethodReferences = mutable.Map[Symbol, Set[Symbol]]().withDefault(_ => mutable.Set()) + + def methodReferencesThisIn(tree: Tree) = { + traverse(tree) + liftedMethodReferences.keys foreach refersToThis + + thisReferringMethods } - } - /** - * Get the symbol of the target lifted lambda body method from a function. I.e. if - * the function is {args => anonfun(args)} then this method returns anonfun's symbol - */ - private def targetMethod(fun: Function): Symbol = fun match { - case Function(_, Apply(target, _)) => - target.symbol - case _ => - // any other shape of Function is unexpected at this point - abort(s"could not understand function with tree $fun") - } + // recursively find methods that refer to 'this' directly or indirectly via references to other methods + // for each method found add it to the referrers set + private def refersToThis(symbol: Symbol): Boolean = + (thisReferringMethods contains symbol) || + (liftedMethodReferences(symbol) exists refersToThis) && { + // add it early to memoize + debuglog(s"$symbol indirectly refers to 'this'") + thisReferringMethods += symbol + true + } - // finds all methods that reference 'this' - class ThisReferringMethodsTraverser() extends Traverser { private var currentMethod: Symbol = NoSymbol - // the set of methods that refer to this - val thisReferringMethods = mutable.Set[Symbol]() - // the set of lifted lambda body methods that each method refers to - val liftedMethodReferences = mutable.Map[Symbol, Set[Symbol]]().withDefault(_ => mutable.Set()) + override def traverse(tree: Tree) = tree match { - case DefDef(_, _, _, _, _, _) => + case DefDef(_, _, _, _, _, _) if tree.symbol.isDelambdafyTarget => // we don't expect defs within defs. At this phase trees should be very flat if (currentMethod.exists) devWarning("Found a def within a def at a phase where defs are expected to be flattened out.") currentMethod = tree.symbol @@ -560,29 +341,10 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre debuglog(s"$currentMethod directly refers to 'this'") thisReferringMethods add currentMethod } + case _: ClassDef if !tree.symbol.isTopLevel => + case _: DefDef => case _ => super.traverse(tree) } } - - final case class LambdaMetaFactoryCapable(target: Symbol, arity: Int, functionalInterface: Symbol) - - // The functional interface that can be used to adapt the lambda target method `target` to the - // given function type. Returns `NoSymbol` if the compiler settings are unsuitable. - private def java8CompatFunctionalInterface(target: Symbol, functionType: Type): (Symbol, Boolean) = { - val canUseLambdaMetafactory = settings.isBCodeActive - - val sym = functionType.typeSymbol - val pack = currentRun.runDefinitions.Scala_Java8_CompatPackage - val name1 = specializeTypes.specializedFunctionName(sym, functionType.typeArgs) - val paramTps :+ restpe = functionType.typeArgs - val arity = paramTps.length - val isSpecialized = name1.toTypeName != sym.name - val functionalInterface = if (!isSpecialized) { - currentRun.runDefinitions.Scala_Java8_CompatPackage_JFunction(arity) - } else { - pack.info.decl(name1.toTypeName.prepend("J")) - } - (if (canUseLambdaMetafactory) functionalInterface else NoSymbol, isSpecialized) - } } diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 0e44751a3f..5e903946c1 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -344,7 +344,7 @@ abstract class Erasure extends AddInterfaces buf.toString case RefinedType(parents, decls) => - boxedSig(intersectionDominator(parents)) + jsig(intersectionDominator(parents), primitiveOK = primitiveOK) case ClassInfoType(parents, _, _) => superSig(parents) case AnnotatedType(_, atp) => @@ -578,8 +578,9 @@ abstract class Erasure extends AddInterfaces } /** The modifier typer which retypes with erased types. */ - class Eraser(_context: Context) extends Typer(_context) with TypeAdapter { - val typer = this.asInstanceOf[analyzer.Typer] + class Eraser(_context: Context) extends Typer(_context) { + val typeAdapter = new TypeAdapter { def typedPos(pos: Position)(tree: Tree): Tree = Eraser.this.typedPos(pos)(tree) } + import typeAdapter._ override protected def stabilize(tree: Tree, pre: Type, mode: Mode, pt: Type): Tree = tree @@ -605,10 +606,8 @@ abstract class Erasure extends AddInterfaces // !!! Make pending/run/t5866b.scala work. The fix might be here and/or in unbox1. if (isPrimitiveValueType(targ.tpe) || isErasedValueType(targ.tpe)) { val noNullCheckNeeded = targ.tpe match { - case ErasedValueType(_, underlying) => - isPrimitiveValueClass(underlying.typeSymbol) - case _ => - true + case ErasedValueType(_, underlying) => isPrimitiveValueType(underlying) + case _ => true } if (noNullCheckNeeded) unbox(qual1, targ.tpe) else { @@ -647,7 +646,7 @@ abstract class Erasure extends AddInterfaces var qual1 = typedQualifier(qual) if ((isPrimitiveValueType(qual1.tpe) && !isPrimitiveValueMember(tree.symbol)) || isErasedValueType(qual1.tpe)) - qual1 = box(qual1, "owner "+tree.symbol.owner) + qual1 = box(qual1) else if (!isPrimitiveValueType(qual1.tpe) && isPrimitiveValueMember(tree.symbol)) qual1 = unbox(qual1, tree.symbol.owner.tpe) @@ -656,13 +655,12 @@ abstract class Erasure extends AddInterfaces if (isPrimitiveValueMember(tree.symbol) && !isPrimitiveValueType(qual1.tpe)) { tree.symbol = NoSymbol selectFrom(qual1) - } else if (isMethodTypeWithEmptyParams(qual1.tpe)) { + } else if (isMethodTypeWithEmptyParams(qual1.tpe)) { // see also adaptToType in TypeAdapter assert(qual1.symbol.isStable, qual1.symbol) - val applied = Apply(qual1, List()) setPos qual1.pos setType qual1.tpe.resultType - adaptMember(selectFrom(applied)) + adaptMember(selectFrom(applyMethodWithEmptyParams(qual1))) } else if (!(qual1.isInstanceOf[Super] || (qual1.tpe.typeSymbol isSubClass tree.symbol.owner))) { assert(tree.symbol.owner != ArrayClass) - selectFrom(cast(qual1, tree.symbol.owner.tpe)) + selectFrom(cast(qual1, tree.symbol.owner.tpe.resultType)) } else { selectFrom(qual1) } @@ -721,6 +719,12 @@ abstract class Erasure extends AddInterfaces if (branch == EmptyTree) branch else adaptToType(branch, tree1.tpe) tree1 match { + case fun: Function => + fun.attachments.get[SAMFunction] match { + case Some(SAMFunction(samTp, _)) => fun setType specialScalaErasure(samTp) + case _ => fun + } + case If(cond, thenp, elsep) => treeCopy.If(tree1, cond, adaptBranch(thenp), adaptBranch(elsep)) case Match(selector, cases) => @@ -1007,24 +1011,20 @@ abstract class Erasure extends AddInterfaces // erasure the ScalaRunTime.hash overload goes from Unit => Int to BoxedUnit => Int. // This must be because some earlier transformation is being skipped on ##, but so // far I don't know what. For null we now define null.## == 0. + def staticsCall(methodName: TermName): Tree = { + val newTree = gen.mkMethodCall(RuntimeStaticsModule, methodName, qual :: Nil) + global.typer.typed(newTree) + } + qual.tpe.typeSymbol match { case UnitClass | NullClass => LIT(0) case IntClass => qual case s @ (ShortClass | ByteClass | CharClass) => numericConversion(qual, s) case BooleanClass => If(qual, LIT(true.##), LIT(false.##)) - case _ => - // Since we are past typer, we need to avoid creating trees carrying - // overloaded types. This logic is custom (and technically incomplete, - // although serviceable) for def hash. What is really needed is for - // the overloading logic presently hidden away in a few different - // places to be properly exposed so we can just call "resolveOverload" - // after typer. Until then: - val alts = ScalaRunTimeModule.info.member(nme.hash_).alternatives - def alt1 = alts find (_.info.paramTypes.head =:= qual.tpe) - def alt2 = ScalaRunTimeModule.info.member(nme.hash_) suchThat (_.info.paramTypes.head.typeSymbol == AnyClass) - val newTree = gen.mkRuntimeCall(nme.hash_, qual :: Nil) setSymbol (alt1 getOrElse alt2) - - global.typer.typed(newTree) + case LongClass => staticsCall(nme.longHash) + case FloatClass => staticsCall(nme.floatHash) + case DoubleClass => staticsCall(nme.doubleHash) + case _ => staticsCall(nme.anyHash) } } else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) { // Rewrite 5.getClass to ScalaRunTime.anyValClass(5) @@ -1103,7 +1103,6 @@ abstract class Erasure extends AddInterfaces } } else tree case Template(parents, self, body) => - assert(!currentOwner.isImplClass) //Console.println("checking no dble defs " + tree)//DEBUG checkNoDoubleDefs(tree.symbol.owner) treeCopy.Template(tree, parents, noSelfType, addBridges(body, currentOwner)) @@ -1113,7 +1112,7 @@ abstract class Erasure extends AddInterfaces case Literal(ct) if ct.tag == ClazzTag && ct.typeValue.typeSymbol != definitions.UnitClass => - val erased = ct.typeValue match { + val erased = ct.typeValue.dealiasWiden match { case tr @ TypeRef(_, clazz, _) if clazz.isDerivedValueClass => scalaErasure.eraseNormalClassRef(tr) case tpe => specialScalaErasure(tpe) } @@ -1142,6 +1141,10 @@ abstract class Erasure extends AddInterfaces else { val tree1 = preErase(tree) tree1 match { + case TypeApply(fun, targs @ List(targ)) if fun.symbol == Any_asInstanceOf && targ.tpe == UnitTpe => + // SI-9066 prevent transforming `o.asInstanceOf[Unit]` to `o.asInstanceOf[BoxedUnit]`. + // adaptMember will then replace the call by a reference to BoxedUnit.UNIT. + treeCopy.TypeApply(tree1, transform(fun), targs).clearType() case EmptyTree | TypeTree() => tree1 setType specialScalaErasure(tree1.tpe) case ArrayValue(elemtpt, trees) => @@ -1182,5 +1185,41 @@ abstract class Erasure extends AddInterfaces bridge.resetFlag(BRIDGE) } + /** Does this symbol compile to the underlying platform's notion of an interface, + * without requiring compiler magic before it can be instantiated? + * + * More specifically, we're interested in whether LambdaMetaFactory can instantiate this type, + * assuming it has a single abstract method. In other words, if we were to mix this + * trait into a class, it should not result in any compiler-generated members having to be + * implemented in ("mixed in to") this class (except for the SAM). + * + * Thus, the type must erase to a java interface, either by virtue of being defined as one, + * or by being a trait that: + * - is static (explicitouter or lambdalift may add disqualifying members) + * - extends only other traits that compile to pure interfaces (except for Any) + * - has no val/var members + * + * TODO: can we speed this up using the INTERFACE flag, or set it correctly by construction? + */ + final def compilesToPureInterface(tpSym: Symbol): Boolean = { + def ok(sym: Symbol) = + sym.isJavaInterface || + sym.isTrait && + // Unless sym.isStatic, even if the constructor is zero-argument now, it may acquire arguments in explicit outer or lambdalift. + // This is an impl restriction to simplify the decision of whether to expand the SAM during uncurry + // (when we don't yet know whether it will receive an outer pointer in explicit outer or whether lambda lift will add proxies for captures). + // When we delay sam expansion until after explicit outer & lambda lift, we could decide there whether + // to expand sam at compile time or use LMF, and this implementation restriction could be lifted. + sym.isStatic && + // HACK: this is to rule out traits with an effectful initializer. + // The constructor only exists if the trait's template has statements. + // Sadly, we can't be more precise without access to the tree that defines the SAM's owner. + !sym.primaryConstructor.exists && + (sym.isInterface || sym.info.decls.forall(mem => mem.isMethod || mem.isType)) // TODO OPT: && {sym setFlag INTERFACE; true}) + + // we still need to check our ancestors even if the INTERFACE flag is set, as it doesn't take inheritance into account + ok(tpSym) && tpSym.ancestors.forall(sym => (sym eq AnyClass) || (sym eq ObjectClass) || ok(sym)) + } + private class TypeRefAttachment(val tpe: TypeRef) } diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 1a0b4cdec0..3d6fad4238 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -210,7 +210,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.getterIn(sym.owner.toInterface) == NoSymbol)) + (sym.getterIn(sym.owner) == NoSymbol)) sym.makeNotPrivate(sym.owner) tp } @@ -241,12 +241,17 @@ abstract class ExplicitOuter extends InfoTransform * Will return `EmptyTree` if there is no outer accessor because of a premature self reference. */ private def outerSelect(base: Tree): Tree = { - val baseSym = base.tpe.typeSymbol.toInterface + val baseSym = base.tpe.typeSymbol val outerAcc = outerAccessor(baseSym) - if (outerAcc == NoSymbol && baseSym.ownersIterator.exists(isUnderConstruction)) { - // e.g neg/t6666.scala - // The caller will report the error with more information. - EmptyTree + if (outerAcc == NoSymbol) { + if (baseSym.ownersIterator.exists(isUnderConstruction)) { + // e.g neg/t6666.scala + // The caller will report the error with more information. + EmptyTree + } else { + globalError(currentOwner.pos, s"Internal error: unable to find the outer accessor symbol of $baseSym") + EmptyTree + } } else { val currentClass = this.currentClass //todo: !!! if this line is removed, we get a build failure that protected$currentClass need an override modifier // outerFld is the $outer field of the current class, if the reference can @@ -254,6 +259,7 @@ abstract class ExplicitOuter extends InfoTransform // otherwise it is NoSymbol val outerFld = if (outerAcc.owner == currentClass && + !outerAcc.owner.isTrait && base.tpe =:= currentClass.thisType && outerAcc.owner.isEffectivelyFinal) outerField(currentClass) suchThat (_.owner == currentClass) @@ -274,8 +280,7 @@ abstract class ExplicitOuter extends InfoTransform */ protected def outerPath(base: Tree, from: Symbol, to: Symbol): Tree = { //Console.println("outerPath from "+from+" to "+to+" at "+base+":"+base.tpe) - //assert(base.tpe.widen.baseType(from.toInterface) != NoType, ""+base.tpe.widen+" "+from.toInterface)//DEBUG - if (from == to || from.isImplClass && from.toInterface == to) base + if (from == to) base else outerPath(outerSelect(base), from.outerClass, to) } @@ -400,7 +405,7 @@ abstract class ExplicitOuter extends InfoTransform case Template(parents, self, decls) => val newDefs = new ListBuffer[Tree] atOwner(tree, currentOwner) { - if (!currentClass.isInterface || (currentClass hasFlag lateINTERFACE)) { + if (!currentClass.isInterface) { if (isInner(currentClass)) { if (hasOuterField(currentClass)) newDefs += outerFieldDef // (1a) @@ -479,14 +484,15 @@ abstract class ExplicitOuter extends InfoTransform // base.<outer>.eq(o) --> base.$outer().eq(o) if there's an accessor, else the whole tree becomes TRUE // TODO remove the synthetic `<outer>` method from outerFor?? case Apply(eqsel@Select(eqapp@Apply(sel@Select(base, nme.OUTER_SYNTH), Nil), eq), args) => - val outerFor = sel.symbol.owner.toInterface // TODO: toInterface necessary? + val outerFor = sel.symbol.owner val acc = outerAccessor(outerFor) if (acc == NoSymbol || // since we can't fix SI-4440 properly (we must drop the outer accessors of final classes when there's no immediate reference to them in sight) // at least don't crash... this duplicates maybeOmittable from constructors (acc.owner.isEffectivelyFinal && !acc.isOverridingSymbol)) { - currentRun.reporting.uncheckedWarning(tree.pos, "The outer reference in this type test cannot be checked at run time.") + if (!base.tpe.hasAnnotation(UncheckedClass)) + currentRun.reporting.uncheckedWarning(tree.pos, "The outer reference in this type test cannot be checked at run time.") transform(TRUE) // urgh... drop condition if there's no accessor (or if it may disappear after constructors) } else { // println("(base, acc)= "+(base, acc)) diff --git a/src/compiler/scala/tools/nsc/transform/Flatten.scala b/src/compiler/scala/tools/nsc/transform/Flatten.scala index fbb0307773..0db9f19597 100644 --- a/src/compiler/scala/tools/nsc/transform/Flatten.scala +++ b/src/compiler/scala/tools/nsc/transform/Flatten.scala @@ -41,8 +41,6 @@ abstract class Flatten extends InfoTransform { } private def liftSymbol(sym: Symbol) { liftClass(sym) - if (sym.needsImplClass) - liftClass(erasure implClass sym) } // This is a short-term measure partially working around objects being // lifted out of parameterized classes, leaving them referencing diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index f6e2dd68f0..074acc1332 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -98,11 +98,6 @@ abstract class LambdaLift extends InfoTransform { */ private val proxyNames = mutable.HashMap[Symbol, Name]() - // (trait, name) -> owner - private val localTraits = mutable.HashMap[(Symbol, Name), Symbol]() - // (owner, name) -> implClass - private val localImplClasses = mutable.HashMap[(Symbol, Name), Symbol]() - /** A flag to indicate whether new free variables have been found */ private var changedFreeVars: Boolean = _ @@ -176,24 +171,7 @@ abstract class LambdaLift extends InfoTransform { case ClassDef(_, _, _, _) => liftedDefs(tree.symbol) = Nil if (sym.isLocalToBlock) { - // Don't rename implementation classes independently of their interfaces. If - // the interface is to be renamed, then we will rename the implementation - // class at that time. You'd think we could call ".implClass" on the trait - // rather than collecting them in another map, but that seems to fail for - // exactly the traits being renamed here (i.e. defined in methods.) - // - // !!! - it makes no sense to have methods like "implClass" and - // "companionClass" which fail for an arbitrary subset of nesting - // arrangements, and then have separate methods which attempt to compensate - // for that failure. There should be exactly one method for any given - // entity which always gives the right answer. - if (sym.isImplClass) - localImplClasses((sym.owner, tpnme.interfaceName(sym.name))) = sym - else { - renamable += sym - if (sym.isTrait) - localTraits((sym, sym.name)) = sym.owner - } + renamable += sym } case DefDef(_, _, _, _, _, _) => if (sym.isLocalToBlock) { @@ -245,11 +223,6 @@ abstract class LambdaLift extends InfoTransform { debuglog("renaming in %s: %s => %s".format(sym.owner.fullLocationString, originalName, sym.name)) } - // make sure that the name doesn't make the symbol accidentally `isAnonymousClass` (et.al) by - // introducing `$anon` in its name. to be cautious, we don't make this change in the default - // backend under 2.11.x, so only in GenBCode. - def nonAnon(s: String) = if (settings.Ybackend.value == "GenBCode") nme.ensureNonAnon(s) else s - def newName(sym: Symbol): Name = { val originalName = sym.name def freshen(prefix: String): Name = @@ -258,47 +231,25 @@ abstract class LambdaLift extends InfoTransform { val join = nme.NAME_JOIN_STRING if (sym.isAnonymousFunction && sym.owner.isMethod) { - freshen(sym.name + join + nonAnon(sym.owner.name.toString) + join) + freshen(sym.name + join + nme.ensureNonAnon(sym.owner.name.toString) + join) } else { val name = freshen(sym.name + join) // SI-5652 If the lifted symbol is accessed from an inner class, it will be made public. (where?) // Generating a unique name, mangled with the enclosing full class name (including // package - subclass might have the same name), avoids a VerifyError in the case // that a sub-class happens to lifts out a method with the *same* name. - if (originalName.isTermName && !sym.enclClass.isImplClass && calledFromInner(sym)) - newTermNameCached(nonAnon(sym.enclClass.fullName('$')) + nme.EXPAND_SEPARATOR_STRING + name) + if (originalName.isTermName && calledFromInner(sym)) + newTermNameCached(nme.ensureNonAnon(sym.enclClass.fullName('$')) + nme.EXPAND_SEPARATOR_STRING + name) else name } } - /* Rename a trait's interface and implementation class in coordinated fashion. */ - def renameTrait(traitSym: Symbol, implSym: Symbol) { - val originalImplName = implSym.name - renameSym(traitSym) - implSym setName tpnme.implClassName(traitSym.name) - - debuglog("renaming impl class in step with %s: %s => %s".format(traitSym, originalImplName, implSym.name)) - } - val allFree: Set[Symbol] = free.values.flatMap(_.iterator).toSet for (sym <- renamable) { - // If we renamed a trait from Foo to Foo$1, we must rename the implementation - // class from Foo$class to Foo$1$class. (Without special consideration it would - // become Foo$class$1 instead.) Since the symbols are being renamed out from - // under us, and there's no reliable link between trait symbol and impl symbol, - // we have maps from ((trait, name)) -> owner and ((owner, name)) -> impl. - localTraits remove ((sym, sym.name)) match { - case None => - if (allFree(sym)) proxyNames(sym) = newName(sym) - else renameSym(sym) - case Some(owner) => - localImplClasses remove ((owner, sym.name)) match { - case Some(implSym) => renameTrait(sym, implSym) - case _ => renameSym(sym) // pure interface, no impl class - } - } + if (allFree(sym)) proxyNames(sym) = newName(sym) + else renameSym(sym) } afterOwnPhase { @@ -457,7 +408,6 @@ abstract class LambdaLift extends InfoTransform { } sym.owner = sym.owner.enclClass - if (sym.isClass) sym.owner = sym.owner.toInterface if (sym.isMethod) sym setFlag LIFTED liftedDefs(sym.owner) ::= tree // TODO: this modifies the ClassInfotype of the enclosing class, which is associated with another phase (explicitouter). diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala index 8a0086dbdd..bc9f70679c 100644 --- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala +++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala @@ -215,14 +215,16 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD def mkSlowPathDef(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree], retVal: Tree): Tree = { - // Q: is there a reason to first set owner to `clazz` (by using clazz.newMethod), and then - // changing it to lzyVal.owner very soon after? Could we just do lzyVal.owner.newMethod? - val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), lzyVal.pos, STABLE | PRIVATE) + val owner = lzyVal.owner + val defSym = owner.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), lzyVal.pos, STABLE | PRIVATE) defSym setInfo MethodType(List(), lzyVal.tpe.resultType) - defSym.owner = lzyVal.owner + if (owner.isClass) owner.info.decls.enter(defSym) debuglog(s"crete slow compute path $defSym with owner ${defSym.owner} for lazy val $lzyVal") - if (bitmaps.contains(lzyVal)) - bitmaps(lzyVal).map(_.owner = defSym) + // this is a hack i don't understand for lazy vals nested in a lazy val, introduced in 3769f4d, + // tested in pos/t3670 (add9be64). class A { val n = { lazy val b = { lazy val dd = 3; dd }; b } } + // bitmaps has an entry bMethodSym -> List(bitmap$0), where bitmap$0.owner == bMethodSym. + // now we set bitmap$0.owner = b$lzycomputeMethodSym. + for (bitmap <- bitmaps(lzyVal)) bitmap.owner = defSym val rhs: Tree = gen.mkSynchronizedCheck(clazz, cond, syncBody, stats).changeOwner(currentOwner -> defSym) DefDef(defSym, addBitmapDefs(lzyVal, BLOCK(rhs, retVal))) diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index d13e11e9ec..19ba9345fa 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -8,6 +8,7 @@ package transform import symtab._ import Flags._ +import scala.annotation.tailrec import scala.collection.mutable abstract class Mixin extends InfoTransform with ast.TreeDSL { @@ -32,8 +33,9 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // --------- helper functions ----------------------------------------------- /** A member of a trait is implemented statically if its implementation after the - * mixin transform is in the static implementation module. To be statically - * implemented, a member must be a method that belonged to the trait's implementation class + * mixin transform is RHS of the method body (destined to be in a interface default method) + * + * To be statically implemented, a member must be a method that belonged to the trait's implementation class * before (i.e. it is not abstract). Not statically implemented are * - non-private modules: these are implemented directly in the mixin composition class * (private modules, on the other hand, are implemented statically, but their @@ -43,33 +45,17 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * methods in the impl class (because they can have arbitrary initializers) */ private def isImplementedStatically(sym: Symbol) = ( - sym.owner.isImplClass - && sym.isMethod + sym.isMethod + && (!sym.hasFlag(DEFERRED | SUPERACCESSOR) || (sym hasFlag lateDEFERRED)) + && sym.owner.isTrait && (!sym.isModule || sym.hasFlag(PRIVATE | LIFTED)) && (!(sym hasFlag (ACCESSOR | SUPERACCESSOR)) || sym.isLazy) + && !sym.isPrivate + && !sym.hasAllFlags(LIFTED | MODULE | METHOD) + && !sym.isConstructor + && (!sym.hasFlag(notPRIVATE | LIFTED) || sym.hasFlag(ACCESSOR | SUPERACCESSOR | MODULE)) ) - /** A member of a trait is static only if it belongs only to the - * implementation class, not the interface, and it is implemented - * statically. - */ - private def isStaticOnly(sym: Symbol) = - isImplementedStatically(sym) && sym.isImplOnly - - /** A member of a trait is forwarded if it is implemented statically and it - * is also visible in the trait's interface. In that case, a forwarder to - * the member's static implementation will be added to the class that - * inherits the trait. - */ - private def isForwarded(sym: Symbol) = - isImplementedStatically(sym) && !sym.isImplOnly - - /** Maps the type of an implementation class to its interface; - * maps all other types to themselves. - */ - private def toInterface(tp: Type): Type = - enteringMixin(tp.typeSymbol.toInterface).tpe - private def isFieldWithBitmap(field: Symbol) = { field.info // ensure that nested objects are transformed // For checkinit consider normal value getters @@ -97,22 +83,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { && !(sym.accessed hasAnnotation TransientAttr) ) - /** Maps all parts of this type that refer to implementation classes to - * their corresponding interfaces. - */ - private val toInterfaceMap = new TypeMap { - def apply(tp: Type): Type = mapOver( tp match { - case TypeRef(pre, sym, args) if sym.isImplClass => - typeRef(pre, enteringMixin(sym.toInterface), args) - case _ => tp - }) - } - - /** The implementation class corresponding to a currently compiled interface. - * todo: try to use Symbol.implClass instead? - */ - private def implClass(iface: Symbol) = iface.implClass orElse (erasure implClass iface) - /** Returns the symbol that is accessed by a super-accessor in a mixin composition. * * @param base The class in which everything is mixed together @@ -159,8 +129,8 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { /** Add given member to given class, and mark member as mixed-in. */ def addMember(clazz: Symbol, member: Symbol): Symbol = { - debuglog("new member of " + clazz + ":" + member.defString) - clazz.info.decls enter member setFlag MIXEDIN + debuglog(s"mixing into $clazz: ${member.defString}") + clazz.info.decls enter member setFlag MIXEDIN resetFlag JAVA_DEFAULTMETHOD } def cloneAndAddMember(mixinClass: Symbol, mixinMember: Symbol, clazz: Symbol): Symbol = addMember(clazz, cloneBeforeErasure(mixinClass, mixinMember, clazz)) @@ -227,12 +197,11 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } clazz.info // make sure info is up to date, so that implClass is set. - val impl = implClass(clazz) orElse abort("No impl class for " + clazz) - for (member <- impl.info.decls) { + for (member <- clazz.info.decls) { if (!member.isMethod && !member.isModule && !member.isModuleVar) { assert(member.isTerm && !member.isDeferred, member) - if (member.getterIn(impl).isPrivate) { + if (member.getterIn(clazz).isPrivate) { member.makeNotPrivate(clazz) // this will also make getter&setter not private } val getter = member.getterIn(clazz) @@ -241,6 +210,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val setter = member.setterIn(clazz) if (setter == NoSymbol) addMember(clazz, newSetter(member)) } + clazz.info.decls.unlink(member) } } debuglog("new defs of " + clazz + " = " + clazz.info.decls) @@ -266,16 +236,51 @@ 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) devWarning ("Impl class flag is not set " + - ((mixinClass.debugLocationString, mixinInterface.debugLocationString))) - - for (member <- mixinClass.info.decls ; if isForwarded(member)) { - val imember = member overriddenSymbol mixinInterface - imember overridingSymbol clazz match { + def mixinTraitForwarders(mixinClass: Symbol) { + for (member <- mixinClass.info.decls ; if isImplementedStatically(member)) { + member overridingSymbol clazz match { case NoSymbol => - if (clazz.info.findMember(member.name, 0, lateDEFERRED, stableOnly = false).alternatives contains imember) - cloneAndAddMixinMember(mixinInterface, imember).asInstanceOf[TermSymbol] setAlias member + val isMemberOfClazz = clazz.info.findMember(member.name, 0, 0L, stableOnly = false).alternatives.contains(member) + if (isMemberOfClazz) { + // `member` is a concrete method defined in `mixinClass`, which is a base class of + // `clazz`, and the method is not overridden in `clazz`. A forwarder is needed if: + // + // - A non-trait base class of `clazz` defines a matching method. Example: + // class C {def f: Int}; trait T extends C {def f = 1}; class D extends T + // Even if C.f is abstract, the forwarder in D is needed, otherwise the JVM would + // resolve `D.f` to `C.f`, see jvms-6.5.invokevirtual. + // + // - There exists another concrete, matching method in a parent interface `p` of + // `clazz`, and the `mixinClass` does not itself extend `p`. In this case the + // forwarder is needed to disambiguate. Example: + // trait T1 {def f = 1}; trait T2 extends T1 {override def f = 2}; class C extends T2 + // In C we don't need a forwarder for f because T2 extends T1, so the JVM resolves + // C.f to T2.f non-ambiguously. See jvms-5.4.3.3, "maximally-specific method". + // trait U1 {def f = 1}; trait U2 {self:U1 => override def f = 2}; class D extends U2 + // In D the forwarder is needed, the interfaces U1 and U2 are unrelated at the JVM + // level. + + @tailrec + def existsCompetingMethod(baseClasses: List[Symbol]): Boolean = baseClasses match { + case baseClass :: rest => + if (baseClass ne mixinClass) { + val m = member.overriddenSymbol(baseClass) + val isCompeting = m.exists && { + !m.owner.isTraitOrInterface || + (!m.isDeferred && !mixinClass.isNonBottomSubClass(m.owner)) + } + isCompeting || existsCompetingMethod(rest) + } else existsCompetingMethod(rest) + + case _ => false + } + + if (existsCompetingMethod(clazz.baseClasses)) + cloneAndAddMixinMember(mixinClass, member).asInstanceOf[TermSymbol] setAlias member + else if (!settings.nowarnDefaultJunitMethods && JUnitTestClass.exists && member.hasAnnotation(JUnitTestClass)) + warning(member.pos, "JUnit tests in traits that are compiled as default methods are not executed by JUnit 4. JUnit 5 will fix this issue.") + } + case _ => } } @@ -296,7 +301,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val mixedInAccessor = cloneAndAddMixinMember(mixinClass, mixinMember) if (mixinMember.isLazy) { initializer(mixedInAccessor) = ( - implClass(mixinClass).info.decl(mixinMember.name) + mixinClass.info.decl(mixinMember.name) orElse abort("Could not find initializer for " + mixinMember.name) ) } @@ -358,67 +363,16 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // first complete the superclass with mixed in members addMixedinMembers(clazz.superClass, unit) - for (mc <- clazz.mixinClasses ; if mc hasFlag lateINTERFACE) { + for (mc <- clazz.mixinClasses ; if mc.isTrait) { // @SEAN: adding trait tracking so we don't have to recompile transitive closures unit.depends += mc addLateInterfaceMembers(mc) mixinTraitMembers(mc) - mixinImplClassMembers(implClass(mc), mc) + mixinTraitForwarders(mc) } } - /** The info transform for this phase does the following: - * - The parents of every class are mapped from implementation class to interface - * - Implementation classes become modules that inherit nothing - * and that define all. - */ - override def transformInfo(sym: Symbol, tp: Type): Type = tp match { - case ClassInfoType(parents, decls, clazz) => - var parents1 = parents - var decls1 = decls - if (!clazz.isPackageClass) { - exitingMixin(clazz.owner.info) - if (clazz.isImplClass) { - clazz setFlag lateMODULE - var sourceModule = clazz.owner.info.decls.lookup(sym.name.toTermName) - if (sourceModule == NoSymbol) { - sourceModule = ( - clazz.owner.newModuleSymbol(sym.name.toTermName, sym.pos, MODULE) - setModuleClass sym.asInstanceOf[ClassSymbol] - ) - clazz.owner.info.decls enter sourceModule - } - else { - sourceModule setPos sym.pos - if (sourceModule.flags != MODULE) { - log(s"!!! Directly setting sourceModule flags for $sourceModule from ${sourceModule.flagString} to MODULE") - sourceModule.flags = MODULE - } - } - sourceModule setInfo sym.tpe - // Companion module isn't visible for anonymous class at this point anyway - assert(clazz.sourceModule != NoSymbol || clazz.isAnonymousClass, s"$clazz has no sourceModule: $sym ${sym.tpe}") - parents1 = List() - decls1 = newScopeWith(decls.toList filter isImplementedStatically: _*) - } else if (!parents.isEmpty) { - parents1 = parents.head :: (parents.tail map toInterface) - } - } - //decls1 = enteringPhase(phase.next)(newScopeWith(decls1.toList: _*))//debug - if ((parents1 eq parents) && (decls1 eq decls)) tp - else ClassInfoType(parents1, decls1, clazz) - - case MethodType(params, restp) => - toInterfaceMap( - if (isImplementedStatically(sym)) { - val ownerParam = sym.newSyntheticValueParam(toInterface(sym.owner.typeOfThis)) - MethodType(ownerParam :: params, restp) - } else - tp) - - case _ => - tp - } + override def transformInfo(sym: Symbol, tp: Type): Type = tp /** Return a map of single-use fields to the lazy value that uses them during initialization. * Each field has to be private and defined in the enclosing class, and there must @@ -466,10 +420,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { new MixinTransformer(unit) class MixinTransformer(unit : CompilationUnit) extends Transformer { - /** Within a static implementation method: the parameter referring to the - * current object. Undefined everywhere else. - */ - private var self: Symbol = _ /** The rootContext used for typing */ private val rootContext = @@ -505,15 +455,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * (that is, every node is processed before its children). * What transform does: * - For every non-trait class, add all mixed in members to the class info. - * - For every trait, add all late interface members to the class info - * - For every static implementation method: - * - remove override flag - * - create a new method definition that also has a `self` parameter - * (which comes first) Iuli: this position is assumed by tail call elimination - * on a different receiver. Storing a new 'this' assumes it is located at - * index 0 in the local variable table. See 'STORE_THIS' and GenASM. - * - Map implementation class types in type-apply's to their interfaces - * - Remove all fields in implementation classes */ private def preTransform(tree: Tree): Tree = { val sym = tree.symbol @@ -524,87 +465,19 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { if (!currentOwner.isTrait && !isPrimitiveValueClass(currentOwner)) addMixedinMembers(currentOwner, unit) - else if (currentOwner hasFlag lateINTERFACE) + else if (currentOwner.isTrait) addLateInterfaceMembers(currentOwner) tree - case DefDef(_, _, _, vparams :: Nil, _, _) => - if (currentOwner.isImplClass) { - if (isImplementedStatically(sym)) { - sym setFlag notOVERRIDE - self = sym.newValueParameter(nme.SELF, sym.pos) setInfo toInterface(currentOwner.typeOfThis) - val selfdef = ValDef(self) setType NoType - copyDefDef(tree)(vparamss = List(selfdef :: vparams)) - } - else EmptyTree - } - else { - if (currentOwner.isTrait && sym.isSetter && !enteringPickler(sym.isDeferred)) { - sym.addAnnotation(TraitSetterAnnotationClass) - } - tree - } - // !!! What is this doing, and why is it only looking for exactly - // one type parameter? It would seem to be - // "Map implementation class types in type-apply's to their interfaces" - // from the comment on preTransform, but is there some way we should know - // that impl class types in type applies can only appear in single - // type parameter type constructors? - case Apply(tapp @ TypeApply(fn, List(arg)), List()) => - if (arg.tpe.typeSymbol.isImplClass) { - val ifacetpe = toInterface(arg.tpe) - arg setType ifacetpe - tapp setType MethodType(Nil, ifacetpe) - tree setType ifacetpe - } - tree - case ValDef(_, _, _, _) if currentOwner.isImplClass => - EmptyTree + case _ => + if (currentOwner.isTrait && sym.isSetter && !enteringPickler(sym.isDeferred)) { + sym.addAnnotation(TraitSetterAnnotationClass) + } tree } } - /** Create an identifier which references self parameter. - */ - private def selfRef(pos: Position) = - gen.mkAttributedIdent(self) setPos pos - - /** Replace a super reference by this or the self parameter, depending - * on whether we are in an implementation class or not. - * Leave all other trees unchanged. - */ - private def transformSuper(tree: Tree) = tree match { - case Super(qual, _) => - transformThis(qual) - case _ => - tree - } - - /** Replace a this reference to the current implementation class by the self - * parameter. Leave all other trees unchanged. - */ - private def transformThis(tree: Tree) = tree match { - case This(_) if tree.symbol.isImplClass => - assert(tree.symbol == currentOwner.enclClass) - selfRef(tree.pos) - case _ => - tree - } - - /** Create a static reference to given symbol `sym` of the - * form `M.sym` where M is the symbol's implementation module. - */ - private def staticRef(sym: Symbol): Tree = { - sym.owner.info //todo: needed? - sym.owner.owner.info //todo: needed? - - if (sym.owner.sourceModule eq NoSymbol) - abort(s"Cannot create static reference to $sym because ${sym.safeOwner} has no source module") - else - REF(sym.owner.sourceModule) DOT sym - } - def needsInitAndHasOffset(sym: Symbol) = needsInitFlag(sym) && (fieldOffset contains sym) @@ -643,6 +516,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * - A super accessor for every super accessor in a mixin class * - Forwarders for all methods that are implemented statically * All superaccessors are completed with right-hand sides (@see completeSuperAccessor) + * * @param clazz The class to which definitions are added */ private def addNewDefs(clazz: Symbol, stats: List[Tree]): List[Tree] = { @@ -700,7 +574,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { */ def completeSuperAccessor(stat: Tree) = stat match { case DefDef(_, _, _, vparams :: Nil, _, EmptyTree) if stat.symbol.isSuperAccessor => - val body = atPos(stat.pos)(Apply(Select(Super(clazz, tpnme.EMPTY), stat.symbol.alias), vparams map (v => Ident(v.symbol)))) + val body = atPos(stat.pos)(Apply(SuperSelect(clazz, stat.symbol.alias), vparams map (v => Ident(v.symbol)))) val pt = stat.symbol.tpe.resultType copyDefDef(stat)(rhs = enteringMixin(transform(localTyper.typed(body, pt)))) @@ -887,7 +761,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { def isUnit = sym.tpe.resultType.typeSymbol == UnitClass def isEmpty = stat.rhs == EmptyTree - if (sym.isLazy && !isEmpty && !clazz.isImplClass) { + if (!clazz.isTrait && sym.isLazy && !isEmpty) { assert(fieldOffset contains sym, sym) deriveDefDef(stat) { case t if isUnit => mkLazyDef(clazz, sym, List(t), UNIT, fieldOffset(sym)) @@ -898,7 +772,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { case t => t // pass specialized lazy vals through } } - else if (needsInitFlag(sym) && !isEmpty && !clazz.hasFlag(IMPLCLASS | TRAIT)) { + else if (needsInitFlag(sym) && !isEmpty && !clazz.hasFlag(TRAIT)) { assert(fieldOffset contains sym, sym) deriveDefDef(stat)(rhs => (mkCheckedAccessor(clazz, _: Tree, fieldOffset(sym), stat.pos, sym))( @@ -992,7 +866,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // if it is a mixed-in lazy value, complete the accessor if (getter.isLazy) { val isUnit = isUnitGetter(getter) - val initCall = Apply(staticRef(initializer(getter)), gen.mkAttributedThis(clazz) :: Nil) + val initCall = Apply(SuperSelect(clazz, initializer(getter)), Nil) val selection = fieldAccess(getter) val init = if (isUnit) initCall else atPos(getter.pos)(Assign(selection, initCall)) val returns = if (isUnit) UNIT else selection @@ -1040,12 +914,11 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // for all symbols `sym` in the class definition, which are mixed in: for (sym <- clazz.info.decls ; if sym hasFlag MIXEDIN) { - // if current class is a trait interface, add an abstract method for accessor `sym` - if (clazz hasFlag lateINTERFACE) { + // if current class is a trait, add an abstract method for accessor `sym` + if (clazz.isTrait) { addDefDef(sym) - } - // if class is not a trait add accessor definitions - else if (!clazz.isTrait) { + } else { + // if class is not a trait add accessor definitions if (isConcreteAccessor(sym)) { // add accessor definitions addDefDef(sym, { @@ -1072,13 +945,20 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } else { // add forwarders - assert(sym.alias != NoSymbol, sym) + assert(sym.alias != NoSymbol, (sym, sym.debugFlagString, clazz)) // debuglog("New forwarder: " + sym.defString + " => " + sym.alias.defString) - if (!sym.isMacro) addDefDef(sym, Apply(staticRef(sym.alias), gen.mkAttributedThis(clazz) :: sym.paramss.head.map(Ident))) + if (!sym.isMacro) addDefDef(sym, Apply(SuperSelect(clazz, sym.alias), sym.paramss.head.map(Ident(_)))) } } } stats1 = add(stats1, newDefs.toList) + if (clazz.isTrait) stats1 = + stats1.filter { + case vd: ValDef => + // TODO do we get here? + false + case _ => true + } if (!clazz.isTrait) stats1 = stats1 map completeSuperAccessor stats1 } @@ -1113,14 +993,8 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * - refer to fields in some implementation class via an abstract method in the interface. */ private def postTransform(tree: Tree): Tree = { - def siteWithinImplClass = currentOwner.enclClass.isImplClass val sym = tree.symbol - // change every node type that refers to an implementation class to its - // corresponding interface, unless the node's symbol is an implementation class. - if (tree.tpe.typeSymbol.isImplClass && ((sym eq null) || !sym.isImplClass)) - tree modifyType toInterface - tree match { case templ @ Template(parents, self, body) => // change parents of templates to conform to parents in the symbol info @@ -1130,87 +1004,21 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // add all new definitions to current class or interface treeCopy.Template(tree, parents1, self, addNewDefs(currentOwner, body)) - // remove widening casts - case Apply(TypeApply(Select(qual, _), targ :: _), _) if isCastSymbol(sym) && (qual.tpe <:< targ.tpe) => - qual - - case Apply(Select(qual, _), args) => - /* Changes `qual.m(args)` where m refers to an implementation - * class method to Q.m(S, args) where Q is the implementation module of - * `m` and S is the self parameter for the call, which - * is determined as follows: - * - if qual != super, qual itself - * - if qual == super, and we are in an implementation class, - * the current self parameter. - * - if qual == super, and we are not in an implementation class, `this` - */ - def staticCall(target: Symbol) = { - def implSym = implClass(sym.owner).info.member(sym.name) - assert(target ne NoSymbol, - List(sym + ":", sym.tpe, sym.owner, implClass(sym.owner), implSym, - enteringPrevPhase(implSym.tpe), phase) mkString " " - ) - typedPos(tree.pos)(Apply(staticRef(target), transformSuper(qual) :: args)) - } - - if (isStaticOnly(sym)) { - // change calls to methods which are defined only in implementation - // classes to static calls of methods in implementation modules - staticCall(sym) - } - else qual match { - case Super(_, mix) => - // change super calls to methods in implementation classes to static calls. - // Transform references super.m(args) as follows: - // - if `m` refers to a trait, insert a static call to the corresponding static - // implementation - // - otherwise return tree unchanged - assert( - !(mix == tpnme.EMPTY && siteWithinImplClass), - "illegal super in trait: " + currentOwner.enclClass + " " + tree - ) - - if (sym.owner hasFlag lateINTERFACE) { - if (sym.hasAccessorFlag) { - assert(args.isEmpty, args) - val sym1 = sym.overridingSymbol(currentOwner.enclClass) - typedPos(tree.pos)((transformSuper(qual) DOT sym1)()) - } - else { - staticCall(enteringPrevPhase(sym.overridingSymbol(implClass(sym.owner)))) - } - } - else { - assert(!siteWithinImplClass, currentOwner.enclClass) - tree - } - case _ => - tree - } - - case This(_) => - transformThis(tree) + case Select(qual, name) if sym.owner.isTrait && !sym.isMethod => + // refer to fields in some trait an abstract getter in the interface. + val ifaceGetter = sym getterIn sym.owner - case Select(Super(_, _), name) => - tree - - case Select(qual, name) if sym.owner.isImplClass && !isStaticOnly(sym) => - assert(!sym.isMethod, "no method allowed here: %s%s %s".format(sym, sym.isImplOnly, sym.flagString)) - // refer to fields in some implementation class via an abstract - // getter in the interface. - val iface = toInterface(sym.owner.tpe).typeSymbol - val ifaceGetter = sym getterIn iface - - if (ifaceGetter == NoSymbol) abort("No getter for " + sym + " in " + iface) + if (ifaceGetter == NoSymbol) abort("No getter for " + sym + " in " + sym.owner) else typedPos(tree.pos)((qual DOT ifaceGetter)()) 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.setterIn(toInterface(lhs.symbol.owner.tpe).typeSymbol) setPos lhs.pos + // assign to fields in some trait via an abstract setter in the interface. + // Note that the case above has added the empty application. + val setter = lhs.symbol.setterIn(lhs.symbol.owner.tpe.typeSymbol) setPos lhs.pos typedPos(tree.pos)((qual DOT setter)(rhs)) + case _ => tree } diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 998f0b22cb..e894c58b1a 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -9,8 +9,6 @@ package transform import scala.tools.nsc.symtab.Flags import scala.collection.{ mutable, immutable } -import scala.language.postfixOps -import scala.language.existentials import scala.annotation.tailrec /** Specialize code on types. @@ -168,7 +166,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { /** Reduce the given environment to contain mappings only for type variables in tps. */ def restrict(env: TypeEnv, tps: immutable.Set[Symbol]): TypeEnv = - env filterKeys tps toMap + env.filterKeys(tps).toMap /** Is the given environment a valid specialization for sym? * It is valid if each binding is from a @specialized type parameter in sym (or its owner) @@ -285,6 +283,19 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { for ((tvar, tpe) <- sym.info.typeParams.zip(args) if !tvar.isSpecialized || !isPrimitiveValueType(tpe)) yield tpe + /** Is `member` potentially affected by specialization? This is a gross overapproximation, + * but it should be okay for use outside of specialization. + */ + def possiblySpecialized(sym: Symbol) = specializedTypeVars(sym).nonEmpty + + /** Refines possiblySpecialized taking into account the instantiation of the specialized type variables at `site` */ + def isSpecializedIn(sym: Symbol, site: Type) = + specializedTypeVars(sym) exists { tvar => + val concretes = concreteTypes(tvar) + (concretes contains AnyRefClass) || (concretes contains site.memberType(tvar)) + } + + val specializedType = new TypeMap { override def apply(tp: Type): Type = tp match { case TypeRef(pre, sym, args) if args.nonEmpty => @@ -354,7 +365,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } ) - lazy val specializableTypes = ScalaValueClasses map (_.tpe) sorted + lazy val specializableTypes = ScalaValueClasses.map(_.tpe).sorted /** If the symbol is the companion of a value class, the value class. * Otherwise, AnyRef. @@ -373,7 +384,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val types = if (!sym.isSpecialized) Nil // no @specialized Annotation else - specializedOn(sym) map (s => specializesClass(s).tpe) sorted + specializedOn(sym).map(s => specializesClass(s).tpe).sorted if (isBoundedGeneric(sym.tpe) && (types contains AnyRefClass)) reporter.warning(sym.pos, sym + " is always a subtype of " + AnyRefTpe + ".") @@ -461,7 +472,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case ExistentialType(_, res) => specializedTypeVars(res) case AnnotatedType(_, tp) => specializedTypeVars(tp) case TypeBounds(lo, hi) => specializedTypeVars(lo :: hi :: Nil) - case RefinedType(parents, _) => parents flatMap specializedTypeVars toSet + case RefinedType(parents, _) => parents.flatMap(specializedTypeVars).toSet case _ => immutable.Set.empty } @@ -848,7 +859,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (unusedStvars.length == 1) "is" else "are") ) unusedStvars foreach (_ removeAnnotation SpecializedClass) - specializingOn = specializingOn filterNot (unusedStvars contains) + specializingOn = specializingOn filterNot (unusedStvars contains _) } for (env0 <- specializations(specializingOn) if needsSpecialization(env0, sym)) yield { // !!! Can't this logic be structured so that the new symbol's name is @@ -1008,7 +1019,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case (NoSymbol, _) => if (overriding.isSuperAccessor) { val alias = overriding.alias - debuglog("checking special overload for super accessor: %s, alias for %s".format(overriding.fullName, alias.fullName)) + debuglog(s"checking special overload for super accessor: ${overriding.fullName}, alias for ${alias.fullName}") needsSpecialOverride(alias) match { case nope @ (NoSymbol, _) => None case (overridden, env) => @@ -1030,7 +1041,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { param.name = overriding.paramss(i)(j).name // SI-6555 Retain the parameter names from the subclass. } } - debuglog("specialized overload %s for %s in %s: %s".format(om, overriding.name.decode, pp(env), om.info)) + debuglog(s"specialized overload $om for ${overriding.name.decode} in ${pp(env)}: ${om.info}") if (overriding.isAbstractOverride) om.setFlag(ABSOVERRIDE) typeEnv(om) = env addConcreteSpecMethod(overriding) @@ -1079,7 +1090,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { */ private def unify(tp1: Type, tp2: Type, env: TypeEnv, strict: Boolean, tparams: Boolean = false): TypeEnv = (tp1, tp2) match { case (TypeRef(_, sym1, _), _) if sym1.isSpecialized => - debuglog("Unify " + tp1 + ", " + tp2) + debuglog(s"Unify $tp1, $tp2") if (isPrimitiveValueClass(tp2.typeSymbol) || isSpecializedAnyRefSubtype(tp2, sym1)) env + ((sym1, tp2)) else if (isSpecializedAnyRefSubtype(tp2, sym1)) @@ -1090,20 +1101,20 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { env case (TypeRef(_, sym1, args1), TypeRef(_, sym2, args2)) => if (args1.nonEmpty || args2.nonEmpty) - debuglog("Unify types " + tp1 + " and " + tp2) + debuglog(s"Unify types $tp1 and $tp2") if (strict && args1.length != args2.length) unifyError(tp1, tp2) val e = unify(args1, args2, env, strict) - if (e.nonEmpty) debuglog("unified to: " + e) + if (e.nonEmpty) debuglog(s"unified to: $e") e case (TypeRef(_, sym1, _), _) if sym1.isTypeParameterOrSkolem => env case (MethodType(params1, res1), MethodType(params2, res2)) => if (strict && params1.length != params2.length) unifyError(tp1, tp2) - debuglog("Unify methods " + tp1 + " and " + tp2) + debuglog(s"Unify methods $tp1 and $tp2") unify(res1 :: (params1 map (_.tpe)), res2 :: (params2 map (_.tpe)), env, strict) case (PolyType(tparams1, res1), PolyType(tparams2, res2)) => - debuglog("Unify polytypes " + tp1 + " and " + tp2) + debuglog(s"Unify polytypes $tp1 and $tp2") if (strict && tparams1.length != tparams2.length) unifyError(tp1, tp2) else if (tparams && tparams1.length == tparams2.length) @@ -1121,7 +1132,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case (ExistentialType(_, res1), _) => unify(tp2, res1, env, strict) case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => unify(List(lo1, hi1), List(lo2, hi2), env, strict) case _ => - debuglog("don't know how to unify %s [%s] with %s [%s]".format(tp1, tp1.getClass, tp2, tp2.getClass)) + debuglog(s"don't know how to unify $tp1 [${tp1.getClass}] with $tp2 [${tp2.getClass}]") env } @@ -1131,9 +1142,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (!strict) unify(args._1, args._2, env, strict) else { val nenv = unify(args._1, args._2, emptyEnv, strict) - if (env.keySet intersect nenv.keySet isEmpty) env ++ nenv + if (env.keySet.intersect(nenv.keySet).isEmpty) env ++ nenv else { - debuglog("could not unify: u(" + args._1 + ", " + args._2 + ") yields " + nenv + ", env: " + env) + debuglog(s"could not unify: u(${args._1}, ${args._2}) yields $nenv, env: $env") unifyError(tp1, tp2) } } @@ -1229,7 +1240,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { env forall { case (tvar, tpe) => matches(tvar.info.bounds.lo, tpe) && matches(tpe, tvar.info.bounds.hi) || { if (warnings) - reporter.warning(tvar.pos, "Bounds prevent specialization of " + tvar) + reporter.warning(tvar.pos, s"Bounds prevent specialization of $tvar") debuglog("specvars: " + tvar.info.bounds.lo + ": " + @@ -1360,7 +1371,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { sym, currentClass, sym.owner.enclClass, isAccessible(sym), nme.isLocalName(sym.name)) ) if (shouldMakePublic(sym) && !isAccessible(sym)) { - debuglog("changing private flag of " + sym) + debuglog(s"changing private flag of $sym") sym.makeNotPrivate(sym.owner) } super.transform(tree) @@ -1415,10 +1426,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { (treeType =:= memberType) || { // anyref specialization memberType match { case PolyType(_, resTpe) => - debuglog("Conformance for anyref - polytype with result type: " + resTpe + " and " + treeType + "\nOrig. sym.: " + origSymbol) + debuglog(s"Conformance for anyref - polytype with result type: $resTpe and $treeType\nOrig. sym.: $origSymbol") try { val e = unify(origSymbol.tpe, memberType, emptyEnv, true) - debuglog("obtained env: " + e) + debuglog(s"obtained env: $e") e.keySet == env.keySet } catch { case _: Throwable => @@ -1518,7 +1529,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { ) val tree1 = gen.mkTypeApply(specTree, residualTargs) - debuglog("rewrote " + tree + " to " + tree1) + debuglog(s"rewrote $tree to $tree1") localTyper.typedOperator(atPos(tree.pos)(tree1)) // being polymorphic, it must be a method } @@ -1526,7 +1537,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { tree match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => def transformNew = { - debuglog("Attempting to specialize new %s(%s)".format(tpt, args.mkString(", "))) + debuglog(s"Attempting to specialize new $tpt(${args.mkString(", ")})") val found = specializedType(tpt.tpe) if (found.typeSymbol ne tpt.tpe.typeSymbol) { // the ctor can be specialized val inst = New(found, transformTrees(args): _*) @@ -1900,8 +1911,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { /** Forward to the generic class constructor. If the current class initializes * specialized fields corresponding to parameters, it passes null to the superclass - * constructor. This saves the boxing cost for initializing generic fields that are - * never used. + * constructor. * * For example: * {{{ @@ -1915,7 +1925,17 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { * super.this(null.asInstanceOf[Int], null.asInstanceOf[Int]) * } * } - * }} + * }}} + * + * Note that erasure first transforms `null.asInstanceOf[Int]` to `unbox(null)`, which is 0. + * Then it adapts the argument `unbox(null)` of type Int to the erased parameter type of Tuple2, + * which is Object, so it inserts a `box` call and we get `box(unbox(null))`, which is + * `new Integer(0)` (not `null`). + * + * However it does not make sense to create an Integer instance to be stored in the generic field + * of the superclass: that field is never used. Therefore we mark the `null` tree with the + * [[SpecializedSuperConstructorCallArgument]] attachment and special-case erasure to replace + * `box(unbox(null))` by `null` in this case. */ private def forwardCtorCall(pos: scala.reflect.internal.util.Position, receiver: Tree, paramss: List[List[ValDef]], clazz: Symbol): Tree = { log(s"forwardCtorCall($pos, $receiver, $paramss, $clazz)") @@ -1934,7 +1954,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val argss = mmap(paramss)(x => if (initializesSpecializedField(x.symbol)) - gen.mkAsInstanceOf(Literal(Constant(null)), x.symbol.tpe) + gen.mkAsInstanceOf(Literal(Constant(null)).updateAttachment(SpecializedSuperConstructorCallArgument), x.symbol.tpe) else Ident(x.symbol) ) @@ -1978,5 +1998,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } resultTree - } } + } + } + object SpecializedSuperConstructorCallArgument } diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index 16ea3ea90f..fa7c503213 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -69,7 +69,7 @@ abstract class TailCalls extends Transform { * are optimized. Since 'this' is not a local variable, a dummy local val * is added and used as a label parameter. The backend knows to load * the corresponding argument in the 'this' (local at index 0). This dummy local - * is never used and should be cleand up by dead code elimination (when enabled). + * is never used and should be cleaned up by dead code elimination (when enabled). * </p> * <p> * This phase has been moved before pattern matching to catch more diff --git a/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala b/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala index 1ed728247b..52d7c0b897 100644 --- a/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala +++ b/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala @@ -1,89 +1,64 @@ package scala.tools.nsc package transform +import scala.annotation.tailrec import scala.tools.nsc.ast.TreeDSL /** * A trait usable by transforms that need to adapt trees of one type to another type */ -trait TypeAdaptingTransformer { - self: TreeDSL => - - val analyzer: typechecker.Analyzer { val global: self.global.type } - - trait TypeAdapter { - val typer: analyzer.Typer +trait TypeAdaptingTransformer { self: TreeDSL => + abstract class TypeAdapter { import global._ import definitions._ - import CODE._ - def isMethodTypeWithEmptyParams(tpe: Type) = tpe match { - case MethodType(Nil, _) => true - case _ => false - } + def typedPos(pos: Position)(tree: Tree): Tree + /** + * SI-4148: can't always replace box(unbox(x)) by x because + * - unboxing x may lead to throwing an exception, e.g. in "aah".asInstanceOf[Int] + * - box(unbox(null)) is not `null` but the box of zero + */ private def isSafelyRemovableUnbox(fn: Tree, arg: Tree): Boolean = { - currentRun.runDefinitions.isUnbox(fn.symbol) && { - val cls = arg.tpe.typeSymbol - (cls == definitions.NullClass) || isBoxedValueClass(cls) - } + currentRun.runDefinitions.isUnbox(fn.symbol) && { + // replace box(unbox(null)) by null when passed to the super constructor in a specialized + // class, see comment in SpecializeTypes.forwardCtorCall. + arg.hasAttachment[specializeTypes.SpecializedSuperConstructorCallArgument.type] || + isBoxedValueClass(arg.tpe.typeSymbol) + } } - private def isPrimitiveValueType(tpe: Type) = isPrimitiveValueClass(tpe.typeSymbol) - - private def isErasedValueType(tpe: Type) = tpe.isInstanceOf[ErasedValueType] - - private def isDifferentErasedValueType(tpe: Type, other: Type) = - isErasedValueType(tpe) && (tpe ne other) + private def isPrimitiveValueType(tpe: Type) = isPrimitiveValueClass(tpe.typeSymbol) + final def isPrimitiveValueMember(sym: Symbol) = isPrimitiveValueClass(sym.owner) + final def isMethodTypeWithEmptyParams(tpe: Type) = tpe.isInstanceOf[MethodType] && tpe.params.isEmpty + final def applyMethodWithEmptyParams(qual: Tree) = Apply(qual, List()) setPos qual.pos setType qual.tpe.resultType - def isPrimitiveValueMember(sym: Symbol) = isPrimitiveValueClass(sym.owner) - - @inline def box(tree: Tree, target: => String): Tree = { - val result = box1(tree) - if (tree.tpe =:= UnitTpe) () - else log(s"boxing ${tree.summaryString}: ${tree.tpe} into $target: ${result.tpe}") - result - } + import CODE._ /** Box `tree` of unboxed type */ - private def box1(tree: Tree): Tree = tree match { + final def box(tree: Tree): Tree = tree match { case LabelDef(_, _, _) => - val ldef = deriveLabelDef(tree)(box1) + val ldef = deriveLabelDef(tree)(box) ldef setType ldef.rhs.tpe case _ => val tree1 = tree.tpe match { - case ErasedValueType(clazz, _) => - New(clazz, cast(tree, underlyingOfValueClass(clazz))) - case _ => - tree.tpe.typeSymbol match { - case UnitClass => - if (treeInfo isExprSafeToInline tree) REF(BoxedUnit_UNIT) - else BLOCK(tree, REF(BoxedUnit_UNIT)) - case NothingClass => tree // a non-terminating expression doesn't need boxing - case x => - assert(x != ArrayClass) - tree match { - /* Can't always remove a Box(Unbox(x)) combination because the process of boxing x - * may lead to throwing an exception. - * - * This is important for specialization: calls to the super constructor should not box/unbox specialized - * fields (see TupleX). (ID) - */ - case Apply(boxFun, List(arg)) if isSafelyRemovableUnbox(tree, arg) => - log(s"boxing an unbox: ${tree.symbol} -> ${arg.tpe}") - arg - case _ => - (REF(currentRun.runDefinitions.boxMethod(x)) APPLY tree) setPos (tree.pos) setType ObjectTpe - } - } + case ErasedValueType(clazz, _) => New(clazz, cast(tree, underlyingOfValueClass(clazz))) + case _ => tree.tpe.typeSymbol match { + case UnitClass => + if (treeInfo isExprSafeToInline tree) REF(BoxedUnit_UNIT) + else BLOCK(tree, REF(BoxedUnit_UNIT)) + case NothingClass => tree // a non-terminating expression doesn't need boxing + case x => + assert(x != ArrayClass) + tree match { + case Apply(boxFun, List(arg)) if isSafelyRemovableUnbox(tree, arg) => + arg + case _ => + (REF(currentRun.runDefinitions.boxMethod(x)) APPLY tree) setPos (tree.pos) setType ObjectTpe + } + } } - typer.typedPos(tree.pos)(tree1) - } - - def unbox(tree: Tree, pt: Type): Tree = { - val result = unbox1(tree, pt) - log(s"unboxing ${tree.shortClass}: ${tree.tpe} as a ${result.tpe}") - result + typedPos(tree.pos)(tree1) } /** Unbox `tree` of boxed type to expected type `pt`. @@ -92,27 +67,13 @@ trait TypeAdaptingTransformer { * @param pt the expected type. * @return the unboxed tree */ - private def unbox1(tree: Tree, pt: Type): Tree = tree match { -/* - case Boxed(unboxed) => - println("unbox shorten: "+tree) // this never seems to kick in during build and test; therefore disabled. - adaptToType(unboxed, pt) - */ + final def unbox(tree: Tree, pt: Type): Tree = tree match { case LabelDef(_, _, _) => val ldef = deriveLabelDef(tree)(unbox(_, pt)) ldef setType ldef.rhs.tpe case _ => val tree1 = pt match { - case ErasedValueType(clazz, underlying) => - val tree0 = - if (tree.tpe.typeSymbol == NullClass && - isPrimitiveValueClass(underlying.typeSymbol)) { - // convert `null` directly to underlying type, as going - // via the unboxed type would yield a NPE (see SI-5866) - unbox1(tree, underlying) - } else - Apply(Select(adaptToType(tree, clazz.tpe), clazz.derivedValueClassUnbox), List()) - cast(tree0, pt) + case ErasedValueType(clazz, underlying) => cast(unboxValueClass(tree, clazz, underlying), pt) case _ => pt.typeSymbol match { case UnitClass => @@ -124,21 +85,28 @@ trait TypeAdaptingTransformer { Apply(currentRun.runDefinitions.unboxMethod(pt.typeSymbol), tree) } } - typer.typedPos(tree.pos)(tree1) + typedPos(tree.pos)(tree1) } + final def unboxValueClass(tree: Tree, clazz: Symbol, underlying: Type): Tree = + if (tree.tpe.typeSymbol == NullClass && isPrimitiveValueClass(underlying.typeSymbol)) { + // convert `null` directly to underlying type, as going via the unboxed type would yield a NPE (see SI-5866) + unbox(tree, underlying) + } else + Apply(Select(adaptToType(tree, clazz.tpe), clazz.derivedValueClassUnbox), List()) + /** Generate a synthetic cast operation from tree.tpe to pt. - * @pre pt eq pt.normalize + * + * @pre pt eq pt.normalize */ - def cast(tree: Tree, pt: Type): Tree = { - if ((tree.tpe ne null) && !(tree.tpe =:= ObjectTpe)) { - def word = ( + final def cast(tree: Tree, pt: Type): Tree = { + if (settings.debug && (tree.tpe ne null) && !(tree.tpe =:= ObjectTpe)) { + def word = if (tree.tpe <:< pt) "upcast" else if (pt <:< tree.tpe) "downcast" else if (pt weak_<:< tree.tpe) "coerce" else if (tree.tpe weak_<:< pt) "widen" else "cast" - ) log(s"erasure ${word}s from ${tree.tpe} to $pt") } if (pt =:= UnitTpe) { @@ -159,27 +127,23 @@ trait TypeAdaptingTransformer { * @param pt the expected type * @return the adapted tree */ - def adaptToType(tree: Tree, pt: Type): Tree = { - if (settings.debug && pt != WildcardType) - log("adapting " + tree + ":" + tree.tpe + " : " + tree.tpe.parents + " to " + pt)//debug - if (tree.tpe <:< pt) - tree - else if (isDifferentErasedValueType(tree.tpe, pt)) - adaptToType(box(tree, pt.toString), pt) - else if (isDifferentErasedValueType(pt, tree.tpe)) - adaptToType(unbox(tree, pt), pt) - else if (isPrimitiveValueType(tree.tpe) && !isPrimitiveValueType(pt)) { - adaptToType(box(tree, pt.toString), pt) - } else if (isMethodTypeWithEmptyParams(tree.tpe)) { - // [H] this assert fails when trying to typecheck tree !(SomeClass.this.bitmap) for single lazy val - //assert(tree.symbol.isStable, "adapt "+tree+":"+tree.tpe+" to "+pt) - adaptToType(Apply(tree, List()) setPos tree.pos setType tree.tpe.resultType, pt) -// } else if (pt <:< tree.tpe) -// cast(tree, pt) - } else if (isPrimitiveValueType(pt) && !isPrimitiveValueType(tree.tpe)) - adaptToType(unbox(tree, pt), pt) - else - cast(tree, pt) + @tailrec final def adaptToType(tree: Tree, pt: Type): Tree = { + val tpe = tree.tpe + + if ((tpe eq pt) || tpe <:< pt) tree + else if (tpe.isInstanceOf[ErasedValueType]) adaptToType(box(tree), pt) // what if pt is an erased value type? + else if (pt.isInstanceOf[ErasedValueType]) adaptToType(unbox(tree, pt), pt) + // See corresponding case in `Eraser`'s `adaptMember` + // [H] this does not hold here, however: `assert(tree.symbol.isStable)` (when typechecking !(SomeClass.this.bitmap) for single lazy val) + else if (isMethodTypeWithEmptyParams(tpe)) adaptToType(applyMethodWithEmptyParams(tree), pt) + else { + val gotPrimitiveVC = isPrimitiveValueType(tpe) + val expectedPrimitiveVC = isPrimitiveValueType(pt) + + if (gotPrimitiveVC && !expectedPrimitiveVC) adaptToType(box(tree), pt) + else if (!gotPrimitiveVC && expectedPrimitiveVC) adaptToType(unbox(tree, pt), pt) + else cast(tree, pt) + } } } } diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 7cd05b56c3..e0b1543f24 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -7,7 +7,7 @@ package scala package tools.nsc package transform -import scala.language.postfixOps +import scala.annotation.tailrec import symtab.Flags._ import scala.collection.mutable @@ -65,19 +65,30 @@ abstract class UnCurry extends InfoTransform // uncurry and uncurryType expand type aliases class UnCurryTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { - private val inlineFunctionExpansion = settings.Ydelambdafy.value == "inline" + private val forceExpandFunction = settings.Ydelambdafy.value == "inline" private var needTryLift = false private var inConstructorFlag = 0L private val byNameArgs = mutable.HashSet[Tree]() private val noApply = mutable.HashSet[Tree]() private val newMembers = mutable.Map[Symbol, mutable.Buffer[Tree]]() - private lazy val forceSpecializationInfoTransformOfFunctionN: Unit = { - if (currentRun.specializePhase != NoPhase) { // be robust in case of -Ystop-after:uncurry - exitingSpecialize { - FunctionClass.seq.foreach(cls => cls.info) - } - } + // Expand `Function`s in constructors to class instance creation (SI-6666, SI-8363) + // We use Java's LambdaMetaFactory (LMF), which requires an interface for the sam's owner + private def mustExpandFunction(fun: Function) = { + // (TODO: Can't use isInterface, yet, as it hasn't been updated for the new trait encoding) + val canUseLambdaMetaFactory = (fun.attachments.get[SAMFunction] match { + case Some(SAMFunction(userDefinedSamTp, sam)) => + // LambdaMetaFactory cannot mix in trait members for us, or instantiate classes -- only pure interfaces need apply + erasure.compilesToPureInterface(erasure.javaErasure(userDefinedSamTp).typeSymbol) && + // impl restriction -- we currently use the boxed apply, so not really useful to allow specialized sam types (https://github.com/scala/scala/pull/4971#issuecomment-198119167) + // specialization and LMF are at odds, since LMF implements the single abstract method, + // but that's the one that specialization leaves generic, whereas we need to implement the specialized one to avoid boxing + !specializeTypes.isSpecializedIn(sam, userDefinedSamTp) + + case _ => true // our built-in FunctionN's are suitable for LambdaMetaFactory by construction + }) + + !canUseLambdaMetaFactory } /** Add a new synthetic member for `currentOwner` */ @@ -88,25 +99,17 @@ abstract class UnCurry extends InfoTransform @inline private def useNewMembers[T](owner: Symbol)(f: List[Tree] => T): T = f(newMembers.remove(owner).getOrElse(Nil).toList) - private def newFunction0(body: Tree): Tree = { - val result = localTyper.typedPos(body.pos)(Function(Nil, body)).asInstanceOf[Function] - log("Change owner from %s to %s in %s".format(currentOwner, result.symbol, result.body)) - result.body changeOwner (currentOwner -> result.symbol) - transformFunction(result) - } - // I don't have a clue why I'm catching TypeErrors here, but it's better // than spewing stack traces at end users for internal errors. Examples // which hit at this point should not be hard to come by, but the immediate // motivation can be seen in continuations-neg/t3718. - override def transform(tree: Tree): Tree = ( + override def transform(tree: Tree): Tree = try postTransform(mainTransform(tree)) catch { case ex: TypeError => reporter.error(ex.pos, ex.msg) debugStack(ex) EmptyTree } - ) /* Is tree a reference `x` to a call by name parameter that needs to be converted to * x.apply()? Note that this is not the case if `x` is used as an argument to another @@ -115,7 +118,7 @@ abstract class UnCurry extends InfoTransform def isByNameRef(tree: Tree) = ( tree.isTerm && (tree.symbol ne null) - && (isByName(tree.symbol)) + && isByName(tree.symbol) && !byNameArgs(tree) ) @@ -192,16 +195,6 @@ abstract class UnCurry extends InfoTransform // ------ Transforming anonymous functions and by-name-arguments ---------------- - /** Undo eta expansion for parameterless and nullary methods */ - def deEta(fun: Function): Tree = fun match { - case Function(List(), expr) if isByNameRef(expr) => - noApply += expr - expr - case _ => - fun - } - - /** Transform a function node (x_1,...,x_n) => body of type FunctionN[T_1, .., T_N, R] to * * class $anon() extends AbstractFunctionN[T_1, .., T_N, R] with Serializable { @@ -210,66 +203,30 @@ abstract class UnCurry extends InfoTransform * new $anon() * */ - def transformFunction(fun: Function): Tree = { - fun.tpe match { - // can happen when analyzer plugins assign refined types to functions, e.g. - // (() => Int) { def apply(): Int @typeConstraint } - case RefinedType(List(funTp), decls) => - debuglog(s"eliminate refinement from function type ${fun.tpe}") - fun.setType(funTp) - case _ => - () - } - - deEta(fun) match { - // nullary or parameterless - case fun1 if fun1 ne fun => fun1 - case _ => - def typedFunPos(t: Tree) = localTyper.typedPos(fun.pos)(t) - val funParams = fun.vparams map (_.symbol) - def mkMethod(owner: Symbol, name: TermName, additionalFlags: FlagSet = NoFlags): DefDef = - gen.mkMethodFromFunction(localTyper)(fun, owner, name, additionalFlags) - - def isSpecialized = { - forceSpecializationInfoTransformOfFunctionN - val specialized = specializeTypes.specializedType(fun.tpe) - !(specialized =:= fun.tpe) - } - - def canUseDelamdafyMethod = ( - (inConstructorFlag == 0) // Avoiding synthesizing code prone to SI-6666, SI-8363 by using old-style lambda translation - && (!isSpecialized || settings.isBCodeActive) // DelambdafyTransformer currently only emits generic FunctionN-s, use the old style in the meantime - ) - if (inlineFunctionExpansion || !canUseDelamdafyMethod) { - val parents = addSerializable(abstractFunctionForFunctionType(fun.tpe)) - val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation SerialVersionUIDAnnotation - // The original owner is used in the backend for the EnclosingMethod attribute. If fun is - // nested in a value-class method, its owner was already changed to the extension method. - // Saving the original owner allows getting the source structure from the class symbol. - defineOriginalOwner(anonClass, fun.symbol.originalOwner) - anonClass setInfo ClassInfoType(parents, newScope, anonClass) - - val applyMethodDef = mkMethod(anonClass, nme.apply) - anonClass.info.decls enter applyMethodDef.symbol - - typedFunPos { - Block( - ClassDef(anonClass, NoMods, ListOfNil, List(applyMethodDef), fun.pos), - Typed(New(anonClass.tpe), TypeTree(fun.tpe))) - } - } else { - // method definition with the same arguments, return type, and body as the original lambda - val liftedMethod = mkMethod(fun.symbol.owner, nme.ANON_FUN_NAME, additionalFlags = ARTIFACT) - - // new function whose body is just a call to the lifted method - val newFun = deriveFunction(fun)(_ => typedFunPos( - gen.mkForwarder(gen.mkAttributedRef(liftedMethod.symbol), funParams :: Nil) - )) - typedFunPos(Block(liftedMethod, super.transform(newFun))) - } - } - } + def transformFunction(fun: Function): Tree = + // Undo eta expansion for parameterless and nullary methods, EXCEPT if `fun` targets a SAM. + // Normally, we can unwrap `() => cbn` to `cbn` where `cbn` refers to a CBN argument (typically `cbn` is an Ident), + // because we know `cbn` will already be a `Function0` thunk. When we're targeting a SAM, + // the types don't align and we must preserve the function wrapper. + if (fun.vparams.isEmpty && isByNameRef(fun.body) && fun.attachments.get[SAMFunction].isEmpty) { noApply += fun.body ; fun.body } + else if (forceExpandFunction || inConstructorFlag != 0) { + // Expand the function body into an anonymous class + gen.expandFunction(localTyper)(fun, inConstructorFlag) + } else { + // method definition with the same arguments, return type, and body as the original lambda + val liftedMethod = gen.mkLiftedFunctionBodyMethod(localTyper)(fun.symbol.owner, fun) + + // new function whose body is just a call to the lifted method + val newFun = deriveFunction(fun)(_ => localTyper.typedPos(fun.pos)( + gen.mkForwarder(gen.mkAttributedRef(liftedMethod.symbol), (fun.vparams map (_.symbol)) :: Nil) + )) + val typedNewFun = localTyper.typedPos(fun.pos)(Block(liftedMethod, super.transform(newFun))) + if (mustExpandFunction(fun)) { + val Block(stats, expr : Function) = typedNewFun + treeCopy.Block(typedNewFun, stats, gen.expandFunction(localTyper)(expr, inConstructorFlag)) + } else typedNewFun + } def transformArgs(pos: Position, fun: Symbol, args: List[Tree], formals: List[Type]) = { val isJava = fun.isJavaDefined @@ -347,25 +304,22 @@ abstract class UnCurry extends InfoTransform val args1 = if (isVarArgTypes(formals)) transformVarargs(formals.last.typeArgs.head) else args map2(formals, args1) { (formal, arg) => - if (!isByNameParamType(formal)) - arg - else if (isByNameRef(arg)) { + if (!isByNameParamType(formal)) arg + else if (isByNameRef(arg)) { // thunk does not need to be forced because it's a reference to a by-name arg passed to a by-name param byNameArgs += arg arg setType functionType(Nil, arg.tpe) - } - else { + } else { log(s"Argument '$arg' at line ${arg.pos.line} is $formal from ${fun.fullName}") - def canUseDirectly(recv: Tree) = ( - recv.tpe.typeSymbol.isSubClass(FunctionClass(0)) - && treeInfo.isExprSafeToInline(recv) - ) + def canUseDirectly(qual: Tree) = qual.tpe.typeSymbol.isSubClass(FunctionClass(0)) && treeInfo.isExprSafeToInline(qual) arg match { // don't add a thunk for by-name argument if argument already is an application of // a Function0. We can then remove the application and use the existing Function0. - case Apply(Select(recv, nme.apply), Nil) if canUseDirectly(recv) => - recv - case _ => - newFunction0(arg) + case Apply(Select(qual, nme.apply), Nil) if canUseDirectly(qual) => qual + case body => + val thunkFun = localTyper.typedPos(body.pos)(Function(Nil, body)).asInstanceOf[Function] + log(s"Change owner from $currentOwner to ${thunkFun.symbol} in ${thunkFun.body}") + thunkFun.body.changeOwner((currentOwner, thunkFun.symbol)) + transformFunction(thunkFun) } } } @@ -377,8 +331,9 @@ abstract class UnCurry extends InfoTransform */ private def replaceElidableTree(tree: Tree): Tree = { tree match { - case DefDef(_,_,_,_,_,_) => - deriveDefDef(tree)(rhs => Block(Nil, gen.mkZero(rhs.tpe)) setType rhs.tpe) setSymbol tree.symbol setType tree.tpe + case DefDef(_,_,_,_,_,rhs) => + val rhs1 = if (rhs == EmptyTree) rhs else Block(Nil, gen.mkZero(rhs.tpe)) setType rhs.tpe + deriveDefDef(tree)(_ => rhs1) setSymbol tree.symbol setType tree.tpe case _ => gen.mkZero(tree.tpe) setType tree.tpe } @@ -435,9 +390,10 @@ abstract class UnCurry extends InfoTransform val sym = tree.symbol // true if the target is a lambda body that's been lifted into a method - def isLiftedLambdaBody(target: Tree) = target.symbol.isLocalToBlock && target.symbol.isArtifact && target.symbol.name.containsName(nme.ANON_FUN_NAME) + def isLiftedLambdaMethod(funSym: Symbol) = + funSym.isArtifact && funSym.name.containsName(nme.ANON_FUN_NAME) && funSym.isLocalToBlock - val result = ( + val result = if ((sym ne null) && sym.elisionLevel.exists(_ < settings.elidebelow.value)) replaceElidableTree(tree) else translateSynchronized(tree) match { @@ -490,7 +446,7 @@ abstract class UnCurry extends InfoTransform case Assign(lhs, _) if lhs.symbol.owner != currentMethod || lhs.symbol.hasFlag(LAZY | ACCESSOR) => withNeedLift(needLift = true) { super.transform(tree) } - case ret @ Return(_) if (isNonLocalReturn(ret)) => + case ret @ Return(_) if isNonLocalReturn(ret) => withNeedLift(needLift = true) { super.transform(ret) } case Try(_, Nil, _) => @@ -509,7 +465,7 @@ abstract class UnCurry extends InfoTransform treeCopy.CaseDef(tree, pat1, transform(guard), transform(body)) // if a lambda is already the right shape we don't need to transform it again - case fun @ Function(_, Apply(target, _)) if (!inlineFunctionExpansion) && isLiftedLambdaBody(target) => + case fun @ Function(_, Apply(target, _)) if !forceExpandFunction && isLiftedLambdaMethod(target.symbol) => super.transform(fun) case fun @ Function(_, _) => @@ -529,9 +485,8 @@ abstract class UnCurry extends InfoTransform } tree1 } - ) - assert(result.tpe != null, result.shortClass + " tpe is null:\n" + result) - result modifyType uncurry + + result.setType(uncurry(result.tpe)) } def postTransform(tree: Tree): Tree = exitingUncurry { @@ -550,7 +505,7 @@ abstract class UnCurry extends InfoTransform tree } - def isThrowable(pat: Tree): Boolean = pat match { + @tailrec def isThrowable(pat: Tree): Boolean = pat match { case Typed(Ident(nme.WILDCARD), tpt) => tpt.tpe =:= ThrowableTpe case Bind(_, pat) => @@ -622,7 +577,7 @@ abstract class UnCurry extends InfoTransform case Select(_, _) | TypeApply(_, _) => applyUnary() case ret @ Return(expr) if isNonLocalReturn(ret) => - log("non-local return from %s to %s".format(currentOwner.enclMethod, ret.symbol)) + log(s"non-local return from ${currentOwner.enclMethod} to ${ret.symbol}") atPos(ret.pos)(nonLocalReturnThrow(expr, ret.symbol)) case TypeTree() => tree @@ -747,7 +702,7 @@ abstract class UnCurry extends InfoTransform case Packed(param, tempVal) => (param, tempVal) }.unzip - val rhs1 = if (tempVals.isEmpty) rhs else { + val rhs1 = if (rhs == EmptyTree || tempVals.isEmpty) rhs else { localTyper.typedPos(rhs.pos) { // Patch the method body to refer to the temp vals val rhsSubstituted = rhs.substituteSymbols(packedParams map (_.symbol), tempVals map (_.symbol)) diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala index ab3b25e76b..ec493b9507 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala @@ -6,8 +6,6 @@ package scala.tools.nsc.transform.patmat -import scala.language.postfixOps - import scala.collection.mutable import scala.reflect.internal.util.Statistics @@ -351,7 +349,7 @@ trait MatchApproximation extends TreeAndTypeAnalysis with ScalaLogic with MatchT object condStrategy extends TypeTestTreeMaker.TypeTestCondStrategy { type Result = Prop def and(a: Result, b: Result) = And(a, b) - def outerTest(testedBinder: Symbol, expectedTp: Type) = True // TODO OuterEqProp(testedBinder, expectedType) + def withOuterTest(testedBinder: Symbol, expectedTp: Type) = True // TODO OuterEqProp(testedBinder, expectedType) def typeTest(b: Symbol, pt: Type) = { // a type test implies the tested path is non-null (null.isInstanceOf[T] is false for all T) val p = binderToUniqueTree(b); And(uniqueNonNullProp(p), uniqueTypeProp(p, uniqueTp(pt))) } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala index c6e7f8fcda..8c59ced28f 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala @@ -316,7 +316,7 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { trait TypeTestCondStrategy { type Result - def outerTest(testedBinder: Symbol, expectedTp: Type): Result + def withOuterTest(orig: Result)(testedBinder: Symbol, expectedTp: Type): Result = orig // TODO: can probably always widen def typeTest(testedBinder: Symbol, expectedTp: Type): Result def nonNullTest(testedBinder: Symbol): Result @@ -336,18 +336,34 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { def equalsTest(pat: Tree, testedBinder: Symbol) = codegen._equals(pat, testedBinder) def eqTest(pat: Tree, testedBinder: Symbol) = REF(testedBinder) OBJ_EQ pat - def outerTest(testedBinder: Symbol, expectedTp: Type): Tree = { - val expectedOuter = expectedTp.prefix match { - case ThisType(clazz) => This(clazz) - case NoType => mkTRUE // fallback for SI-6183 - case pre => REF(pre.prefix, pre.termSymbol) + override def withOuterTest(orig: Tree)(testedBinder: Symbol, expectedTp: Type): Tree = { + val expectedPrefix = expectedTp.prefix + val testedPrefix = testedBinder.info.prefix + + // Check if a type is defined in a static location. Unlike `tp.isStatic` before `flatten`, + // this also includes methods and (possibly nested) objects inside of methods. + def definedInStaticLocation(tp: Type): Boolean = { + def isStatic(tp: Type): Boolean = + if (tp == NoType || tp.typeSymbol.isPackageClass || tp == NoPrefix) true + else if (tp.typeSymbol.isModuleClass) isStatic(tp.prefix) + else false + tp.typeSymbol.owner == tp.prefix.typeSymbol && isStatic(tp.prefix) } - // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` - // if there's an outer accessor, otherwise the condition becomes `true` -- TODO: can we improve needsOuterTest so there's always an outerAccessor? - val outer = expectedTp.typeSymbol.newMethod(vpmName.outer, newFlags = SYNTHETIC | ARTIFACT) setInfo expectedTp.prefix - - (Select(codegen._asInstanceOf(testedBinder, expectedTp), outer)) OBJ_EQ expectedOuter + if ((expectedPrefix eq NoPrefix) + || definedInStaticLocation(expectedTp) + || testedPrefix =:= expectedPrefix) orig + else gen.mkAttributedQualifierIfPossible(expectedPrefix) match { + case None => orig + case Some(expectedOuterRef) => + // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` + // by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` + // if there's an outer accessor, otherwise the condition becomes `true` + // TODO: centralize logic whether there's an outer accessor and use here? + val synthOuterGetter = expectedTp.typeSymbol.newMethod(vpmName.outer, newFlags = SYNTHETIC | ARTIFACT) setInfo expectedPrefix + val outerTest = (Select(codegen._asInstanceOf(testedBinder, expectedTp), synthOuterGetter)) OBJ_EQ expectedOuterRef + and(orig, outerTest) + } } } @@ -356,7 +372,6 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { def typeTest(testedBinder: Symbol, expectedTp: Type): Result = true - def outerTest(testedBinder: Symbol, expectedTp: Type): Result = false def nonNullTest(testedBinder: Symbol): Result = false def equalsTest(pat: Tree, testedBinder: Symbol): Result = false def eqTest(pat: Tree, testedBinder: Symbol): Result = false @@ -368,7 +383,6 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { type Result = Boolean def typeTest(testedBinder: Symbol, expectedTp: Type): Result = testedBinder eq binder - def outerTest(testedBinder: Symbol, expectedTp: Type): Result = false def nonNullTest(testedBinder: Symbol): Result = testedBinder eq binder def equalsTest(pat: Tree, testedBinder: Symbol): Result = false // could in principle analyse pat and see if it's statically known to be non-null def eqTest(pat: Tree, testedBinder: Symbol): Result = false // could in principle analyse pat and see if it's statically known to be non-null @@ -405,12 +419,6 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { import TypeTestTreeMaker._ debug.patmat("TTTM"+((prevBinder, extractorArgTypeTest, testedBinder, expectedTp, nextBinderTp))) - lazy val outerTestNeeded = ( - (expectedTp.prefix ne NoPrefix) - && !expectedTp.prefix.typeSymbol.isPackageClass - && needsOuterTest(expectedTp, testedBinder.info, matchOwner) - ) - // the logic to generate the run-time test that follows from the fact that // a `prevBinder` is expected to have type `expectedTp` // the actual tree-generation logic is factored out, since the analyses generate Cond(ition)s rather than Trees @@ -429,12 +437,11 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { def isExpectedPrimitiveType = isAsExpected && isPrimitiveValueType(expectedTp) def isExpectedReferenceType = isAsExpected && (expectedTp <:< AnyRefTpe) def mkNullTest = nonNullTest(testedBinder) - def mkOuterTest = outerTest(testedBinder, expectedTp) def mkTypeTest = typeTest(testedBinder, expectedWide) def mkEqualsTest(lhs: Tree): cs.Result = equalsTest(lhs, testedBinder) def mkEqTest(lhs: Tree): cs.Result = eqTest(lhs, testedBinder) - def addOuterTest(res: cs.Result): cs.Result = if (outerTestNeeded) and(res, mkOuterTest) else res + def addOuterTest(res: cs.Result): cs.Result = withOuterTest(res)(testedBinder, expectedTp) // If we conform to expected primitive type: // it cannot be null and cannot have an outer pointer. No further checking. diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchWarnings.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchWarnings.scala index 27d674762f..3f27d18e64 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchWarnings.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchWarnings.scala @@ -6,8 +6,6 @@ package scala.tools.nsc.transform.patmat -import scala.language.postfixOps - trait MatchWarnings { self: PatternMatching => diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala index b2f2516b5b..05f2d60be1 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala @@ -222,7 +222,7 @@ trait Interface extends ast.TreeDSL { object substIdentsForTrees extends Transformer { private def typedIfOrigTyped(to: Tree, origTp: Type): Tree = if (origTp == null || origTp == NoType) to - // important: only type when actually substing and when original tree was typed + // important: only type when actually substituting and when original tree was typed // (don't need to use origTp as the expected type, though, and can't always do this anyway due to unknown type params stemming from polymorphic extractors) else typer.typed(to) diff --git a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala index 2f4771e9d4..2cd4785fbf 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala @@ -40,9 +40,10 @@ abstract class ConstantFolder { if ((x ne null) && x.tag != UnitTag) tree setType ConstantType(x) else tree } catch { - case _: ArithmeticException => tree // the code will crash at runtime, - // but that is better than the - // compiler itself crashing + case e: ArithmeticException => + if (settings.warnConstant) + warning(tree.pos, s"Evaluation of a constant expression results in an arithmetic error: ${e.getMessage}") + tree } private def foldUnop(op: Name, x: Constant): Constant = (op, x.tag) match { @@ -102,13 +103,13 @@ abstract class ConstantFolder { case nme.XOR => Constant(x.longValue ^ y.longValue) case nme.AND => Constant(x.longValue & y.longValue) case nme.LSL if x.tag <= IntTag - => Constant(x.intValue << y.longValue) + => Constant(x.intValue << y.longValue.toInt) // TODO: remove .toInt once starr includes the fix for SI-9516 (2.12.0-M5) case nme.LSL => Constant(x.longValue << y.longValue) case nme.LSR if x.tag <= IntTag - => Constant(x.intValue >>> y.longValue) + => Constant(x.intValue >>> y.longValue.toInt) // TODO: remove .toInt once starr includes the fix for SI-9516 (2.12.0-M5) case nme.LSR => Constant(x.longValue >>> y.longValue) case nme.ASR if x.tag <= IntTag - => Constant(x.intValue >> y.longValue) + => Constant(x.intValue >> y.longValue.toInt) // TODO: remove .toInt once starr includes the fix for SI-9516 (2.12.0-M5) case nme.ASR => Constant(x.longValue >> y.longValue) case nme.EQ => Constant(x.longValue == y.longValue) case nme.NE => Constant(x.longValue != y.longValue) @@ -158,7 +159,7 @@ abstract class ConstantFolder { else if (x.isNumeric && y.isNumeric) math.max(x.tag, y.tag) else NoTag - try optag match { + optag match { case BooleanTag => foldBooleanOp(op, x, y) case ByteTag | ShortTag | CharTag | IntTag => foldSubrangeOp(op, x, y) case LongTag => foldLongOp(op, x, y) @@ -167,8 +168,5 @@ abstract class ConstantFolder { case StringTag if op == nme.ADD => Constant(x.stringValue + y.stringValue) case _ => null } - catch { - case _: ArithmeticException => null - } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index ccdff5c9a1..e190b57017 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -469,6 +469,11 @@ trait ContextErrors { setError(tree) } + def ConstructorRecursesError(tree: Tree) = { + issueNormalTypeError(tree, "constructor invokes itself") + setError(tree) + } + def OnlyDeclarationsError(tree: Tree) = { issueNormalTypeError(tree, "only declarations allowed here") setError(tree) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index cb4eab335b..bcc1ed3e64 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -387,8 +387,10 @@ trait Contexts { self: Analyzer => @inline final def withImplicitsEnabled[T](op: => T): T = withMode(enabled = ImplicitsEnabled)(op) @inline final def withImplicitsDisabled[T](op: => T): T = withMode(disabled = ImplicitsEnabled | EnrichmentEnabled)(op) @inline final def withImplicitsDisabledAllowEnrichment[T](op: => T): T = withMode(enabled = EnrichmentEnabled, disabled = ImplicitsEnabled)(op) + @inline final def withImplicits[T](enabled: Boolean)(op: => T): T = if (enabled) withImplicitsEnabled(op) else withImplicitsDisabled(op) @inline final def withMacrosEnabled[T](op: => T): T = withMode(enabled = MacrosEnabled)(op) @inline final def withMacrosDisabled[T](op: => T): T = withMode(disabled = MacrosEnabled)(op) + @inline final def withMacros[T](enabled: Boolean)(op: => T): T = if (enabled) withMacrosEnabled(op) else withMacrosDisabled(op) @inline final def withinStarPatterns[T](op: => T): T = withMode(enabled = StarPatterns)(op) @inline final def withinSuperInit[T](op: => T): T = withMode(enabled = SuperInit)(op) @inline final def withinSecondTry[T](op: => T): T = withMode(enabled = SecondTry)(op) @@ -723,7 +725,6 @@ trait Contexts { self: Analyzer => ( (ab.isTerm || ab == rootMirror.RootClass) || (accessWithin(ab) || accessWithinLinked(ab)) && ( !sym.isLocalToThis - || sym.owner.isImplClass // allow private local accesses to impl classes || sym.isProtected && isSubThisType(pre, sym.owner) || pre =:= sym.owner.thisType ) @@ -978,7 +979,7 @@ trait Contexts { self: Analyzer => */ def isInPackageObject(sym: Symbol, pkg: Symbol): Boolean = { if (sym.isOverloaded) sym.alternatives.exists(alt => isInPackageObject(alt, pkg)) - else pkg.isPackage && sym.owner != pkg && requiresQualifier(sym) + else pkg.hasPackageFlag && sym.owner != pkg && requiresQualifier(sym) } def isNameInScope(name: Name) = lookupSymbol(name, _ => true).isSuccess diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala index 7092f00bff..97de2b6c85 100644 --- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -15,39 +15,29 @@ import symtab.Flags._ * @version 1.0 */ trait EtaExpansion { self: Analyzer => - import global._ - object etaExpansion { - private def isMatch(vparam: ValDef, arg: Tree) = arg match { - case Ident(name) => vparam.name == name - case _ => false - } - - def unapply(tree: Tree): Option[(List[ValDef], Tree, List[Tree])] = tree match { - case Function(vparams, Apply(fn, args)) if (vparams corresponds args)(isMatch) => - Some((vparams, fn, args)) - case _ => - None - } - } - - /** <p> - * Expand partial function applications of type `type`. - * </p><pre> - * p.f(es_1)...(es_n) - * ==> { - * <b>private synthetic val</b> eta$f = p.f // if p is not stable - * ... - * <b>private synthetic val</b> eta$e_i = e_i // if e_i is not stable - * ... - * (ps_1 => ... => ps_m => eta$f([es_1])...([es_m])(ps_1)...(ps_m)) - * }</pre> - * <p> - * tree is already attributed - * </p> - */ - def etaExpand(unit : CompilationUnit, tree: Tree, typer: Typer): Tree = { + /** Expand partial method application `p.f(es_1)...(es_n)`. + * + * We expand this to the following block, which evaluates + * the target of the application and its supplied arguments if needed (they are not stable), + * and then wraps a Function that abstracts over the missing arguments. + * + * ``` + * { + * private synthetic val eta$f = p.f // if p is not stable + * ... + * private synthetic val eta$e_i = e_i // if e_i is not stable + * ... + * (ps_1 => ... => ps_m => eta$f([es_1])...([es_m])(ps_1)...(ps_m)) + * } + * ``` + * + * This is called from instantiateToMethodType after type checking `tree`, + * and we realize we have a method type, where a function type (builtin or SAM) is expected. + * + **/ + def etaExpand(unit: CompilationUnit, tree: Tree, typer: Typer): Tree = { val tpe = tree.tpe var cnt = 0 // for NoPosition def freshName() = { diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index a34e97b6cb..bee2ae8e99 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -34,14 +34,33 @@ trait Implicits { import typingStack.{ printTyping } import typeDebug._ + // standard usage + def inferImplicitFor(pt: Type, tree: Tree, context: Context, reportAmbiguous: Boolean = true): SearchResult = + inferImplicit(tree, pt, reportAmbiguous, isView = false, context, saveAmbiguousDivergent = true, tree.pos) + + // used by typer to find an implicit coercion + def inferImplicitView(from: Type, to: Type, tree: Tree, context: Context, reportAmbiguous: Boolean, saveAmbiguousDivergent: Boolean) = + inferImplicit(tree, Function1(from, to), reportAmbiguous, isView = true, context, saveAmbiguousDivergent, tree.pos) + + // used for manifests, typetags, checking language features, scaladoc + def inferImplicitByType(pt: Type, context: Context, pos: Position = NoPosition): SearchResult = + inferImplicit(EmptyTree, pt, reportAmbiguous = true, isView = false, context, saveAmbiguousDivergent = true, pos) + + def inferImplicitByTypeSilent(pt: Type, context: Context, pos: Position = NoPosition): SearchResult = + inferImplicit(EmptyTree, pt, reportAmbiguous = false, isView = false, context, saveAmbiguousDivergent = false, pos) + + @deprecated("Unused in scalac", "2.12.0-M4") def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context): SearchResult = inferImplicit(tree, pt, reportAmbiguous, isView, context, saveAmbiguousDivergent = true, tree.pos) + @deprecated("Unused in scalac", "2.12.0-M4") def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean): SearchResult = inferImplicit(tree, pt, reportAmbiguous, isView, context, saveAmbiguousDivergent, tree.pos) - /** Search for an implicit value. See the comment on `result` at the end of class `ImplicitSearch` - * for more info how the search is conducted. + /** Search for an implicit value. Consider using one of the convenience methods above. This one has many boolean levers. + * + * See the comment on `result` at the end of class `ImplicitSearch` for more info how the search is conducted. + * * @param tree The tree for which the implicit needs to be inserted. * (the inference might instantiate some of the undetermined * type parameters of that tree. @@ -92,9 +111,10 @@ trait Implicits { /** A friendly wrapper over inferImplicit to be used in macro contexts and toolboxes. */ def inferImplicit(tree: Tree, pt: Type, isView: Boolean, context: Context, silent: Boolean, withMacrosDisabled: Boolean, pos: Position, onError: (Position, String) => Unit): Tree = { - val wrapper1 = if (!withMacrosDisabled) (context.withMacrosEnabled[SearchResult] _) else (context.withMacrosDisabled[SearchResult] _) - def wrapper(inference: => SearchResult) = wrapper1(inference) - val result = wrapper(inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos)) + val result = context.withMacros(enabled = !withMacrosDisabled) { + inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context, saveAmbiguousDivergent = !silent, pos) + } + if (result.isFailure && !silent) { val err = context.reporter.firstError val errPos = err.map(_.errPos).getOrElse(pos) @@ -304,6 +324,10 @@ trait Implicits { */ object Function1 { val Sym = FunctionClass(1) + val Pre = Sym.typeConstructor.prefix + + def apply(from: Type, to: Type) = TypeRef(Pre, Sym, List(from, to)) + // It is tempting to think that this should be inspecting "tp baseType Sym" // rather than tp. See test case run/t8280 and the commit message which // accompanies it for explanation why that isn't done. @@ -1207,7 +1231,7 @@ trait Implicits { /* Re-wraps a type in a manifest before calling inferImplicit on the result */ def findManifest(tp: Type, manifestClass: Symbol = if (full) FullManifestClass else PartialManifestClass) = - inferImplicit(tree, appliedType(manifestClass, tp), reportAmbiguous = true, isView = false, context).tree + inferImplicitFor(appliedType(manifestClass, tp), tree, context).tree def findSubManifest(tp: Type) = findManifest(tp, if (full) FullManifestClass else OptManifestClass) def mot(tp0: Type, from: List[Symbol], to: List[Type]): SearchResult = { diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 684cf788a4..dc91d23011 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -164,7 +164,9 @@ trait Infer extends Checkable { | was: $restpe | now""")(normalize(restpe)) case mt @ MethodType(_, restpe) if mt.isImplicit => normalize(restpe) - case mt @ MethodType(_, restpe) if !mt.isDependentMethodType => functionType(mt.paramTypes, normalize(restpe)) + case mt @ MethodType(_, restpe) if !mt.isDependentMethodType => + if (phase.erasedTypes) FunctionClass(mt.params.length).tpe + else functionType(mt.paramTypes, normalize(restpe)) case NullaryMethodType(restpe) => normalize(restpe) case ExistentialType(tparams, qtpe) => newExistentialType(tparams, normalize(qtpe)) case _ => tp // @MAT aliases already handled by subtyping @@ -295,7 +297,7 @@ trait Infer extends Checkable { && !isByNameParamType(tp) && isCompatible(tp, dropByName(pt)) ) - def isCompatibleSam(tp: Type, pt: Type): Boolean = { + def isCompatibleSam(tp: Type, pt: Type): Boolean = (definitions.isFunctionType(tp) || tp.isInstanceOf[MethodType] || tp.isInstanceOf[PolyType]) && { val samFun = typer.samToFunctionType(pt) (samFun ne NoType) && isCompatible(tp, samFun) } @@ -1218,7 +1220,7 @@ trait Infer extends Checkable { } def inferModulePattern(pat: Tree, pt: Type) = - if (!(pat.tpe <:< pt)) { + if ((pat.symbol ne null) && pat.symbol.isModule && !(pat.tpe <:< pt)) { val ptparams = freeTypeParamsOfTerms(pt) debuglog("free type params (2) = " + ptparams) val ptvars = ptparams map freshVar diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index bcf9e018e2..d7c53ed3c4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -12,7 +12,6 @@ import scala.reflect.internal.util.ListOfNil import scala.reflect.macros.runtime.{AbortMacroException, MacroRuntimes} import scala.reflect.macros.compiler.DefaultMacroCompiler import scala.tools.reflect.FastTrack -import scala.runtime.ScalaRunTime import Fingerprint._ /** @@ -239,7 +238,7 @@ trait Macros extends MacroRuntimes with Traces with Helpers { if (!payload.contains(field)) failField("is supposed to be there") val raw: Any = payload(field) if (raw == null) failField(s"is not supposed to be null") - val expected = ScalaRunTime.box(clazz) + val expected = box(clazz) val actual = raw.getClass if (!expected.isAssignableFrom(actual)) failField(s"has wrong type: expected $expected, actual $actual") raw.asInstanceOf[T] @@ -256,6 +255,19 @@ trait Macros extends MacroRuntimes with Traces with Helpers { val signature = unpickle("signature", classOf[List[List[Fingerprint]]]) MacroImplBinding(isBundle, isBlackbox, className, methodName, signature, targs) } + + private def box[T](clazz: Class[T]): Class[_] = clazz match { + case java.lang.Byte.TYPE => classOf[java.lang.Byte] + case java.lang.Short.TYPE => classOf[java.lang.Short] + case java.lang.Character.TYPE => classOf[java.lang.Character] + case java.lang.Integer.TYPE => classOf[java.lang.Integer] + case java.lang.Long.TYPE => classOf[java.lang.Long] + case java.lang.Float.TYPE => classOf[java.lang.Float] + case java.lang.Double.TYPE => classOf[java.lang.Double] + case java.lang.Void.TYPE => classOf[scala.runtime.BoxedUnit] + case java.lang.Boolean.TYPE => classOf[java.lang.Boolean] + case _ => clazz + } } def bindMacroImpl(macroDef: Symbol, macroImplRef: Tree): Unit = { diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index abc3e91ed2..c03094bc6a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -5,8 +5,6 @@ package scala.tools.nsc package typechecker -import scala.language.higherKinds - import symtab.Flags._ import scala.reflect.internal.util.StringOps.ojoin import scala.reflect.internal.util.ListOfNil diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 1bc5daac65..5062289ed1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -469,8 +469,7 @@ trait NamesDefaults { self: Analyzer => else { // isClass also works for methods in objects, owner is the ModuleClassSymbol if (param.owner.owner.isClass) { - // .toInterface: otherwise we get the method symbol of the impl class - param.owner.owner.toInterface.info.member(defGetterName) + param.owner.owner.info.member(defGetterName) } else { // the owner of the method is another method. find the default // getter in the context. diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 9261d6b851..da269168ec 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1507,9 +1507,8 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } } - private def transformCaseApply(tree: Tree, ifNot: => Unit) = { + private def isSimpleCaseApply(tree: Tree): Boolean = { val sym = tree.symbol - def isClassTypeAccessible(tree: Tree): Boolean = tree match { case TypeApply(fun, targs) => isClassTypeAccessible(fun) @@ -1517,40 +1516,36 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans ( // SI-4859 `CaseClass1().InnerCaseClass2()` must not be rewritten to `new InnerCaseClass2()`; // {expr; Outer}.Inner() must not be rewritten to `new Outer.Inner()`. treeInfo.isQualifierSafeToElide(module) && - // SI-5626 Classes in refinement types cannot be constructed with `new`. In this case, - // the companion class is actually not a ClassSymbol, but a reference to an abstract type. - module.symbol.companionClass.isClass - ) + // SI-5626 Classes in refinement types cannot be constructed with `new`. In this case, + // the companion class is actually not a ClassSymbol, but a reference to an abstract type. + module.symbol.companionClass.isClass + ) } - val doTransform = - sym.isSourceMethod && + sym.isSourceMethod && sym.isCase && sym.name == nme.apply && isClassTypeAccessible(tree) && - !tree.tpe.resultType.typeSymbol.primaryConstructor.isLessAccessibleThan(tree.symbol) - - if (doTransform) { - def loop(t: Tree): Unit = t match { - case Ident(_) => - checkUndesiredProperties(t.symbol, t.pos) - case Select(qual, _) => - checkUndesiredProperties(t.symbol, t.pos) - loop(qual) - case _ => - } - tree foreach { - case i@Ident(_) => - enterReference(i.pos, i.symbol) // SI-5390 need to `enterReference` for `a` in `a.B()` - case _ => - } - loop(tree) - toConstructor(tree.pos, tree.tpe) + !tree.tpe.finalResultType.typeSymbol.primaryConstructor.isLessAccessibleThan(tree.symbol) + } + + private def transformCaseApply(tree: Tree) = { + def loop(t: Tree): Unit = t match { + case Ident(_) => + checkUndesiredProperties(t.symbol, t.pos) + case Select(qual, _) => + checkUndesiredProperties(t.symbol, t.pos) + loop(qual) + case _ => } - else { - ifNot - tree + + tree foreach { + case i@Ident(_) => + enterReference(i.pos, i.symbol) // SI-5390 need to `enterReference` for `a` in `a.B()` + case _ => } + loop(tree) + toConstructor(tree.pos, tree.tpe) } private def transformApply(tree: Apply): Tree = tree match { @@ -1590,12 +1585,24 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // term should have been eliminated by super accessors assert(!(qual.symbol.isTrait && sym.isTerm && mix == tpnme.EMPTY), (qual.symbol, sym, mix)) - transformCaseApply(tree, + // Rewrite eligible calls to monomorphic case companion apply methods to the equivalent constructor call. + // + // Note: for generic case classes the rewrite needs to be handled at the enclosing `TypeApply` to transform + // `TypeApply(Select(C, apply), targs)` to `Select(New(C[targs]), <init>)`. In case such a `TypeApply` + // was deemed ineligible for transformation (e.g. the case constructor was private), the refchecks transform + // will recurse to this point with `Select(C, apply)`, which will have a type `[T](...)C[T]`. + // + // We don't need to perform the check on the Select node, and `!isHigherKinded will guard against this + // redundant (and previously buggy, SI-9546) consideration. + if (!tree.tpe.isHigherKinded && isSimpleCaseApply(tree)) { + transformCaseApply(tree) + } else { qual match { case Super(_, mix) => checkSuper(mix) case _ => } - ) + tree + } } private def transformIf(tree: If): Tree = { val If(cond, thenpart, elsepart) = tree @@ -1720,7 +1727,10 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case TypeApply(fn, args) => checkBounds(tree, NoPrefix, NoSymbol, fn.tpe.typeParams, args map (_.tpe)) - transformCaseApply(tree, ()) + if (isSimpleCaseApply(tree)) + transformCaseApply(tree) + else + tree case x @ Apply(_, _) => transformApply(x) @@ -1739,12 +1749,11 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case Ident(name) => checkUndesiredProperties(sym, tree.pos) - transformCaseApply(tree, - if (name != nme.WILDCARD && name != tpnme.WILDCARD_STAR) { - assert(sym != NoSymbol, "transformCaseApply: name = " + name.debugString + " tree = " + tree + " / " + tree.getClass) //debug - enterReference(tree.pos, sym) - } - ) + if (name != nme.WILDCARD && name != tpnme.WILDCARD_STAR) { + assert(sym != NoSymbol, "transformCaseApply: name = " + name.debugString + " tree = " + tree + " / " + tree.getClass) //debug + enterReference(tree.pos, sym) + } + tree case x @ Select(_, _) => transformSelect(x) diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 243a706745..6d883aee3d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -93,11 +93,14 @@ trait SyntheticMethods extends ast.TreeDSL { def forwardToRuntime(method: Symbol): Tree = forwardMethod(method, getMember(ScalaRunTimeModule, (method.name prepend "_")))(mkThis :: _) - def callStaticsMethod(name: String)(args: Tree*): Tree = { - val method = termMember(RuntimeStaticsModule, name) + def callStaticsMethodName(name: TermName)(args: Tree*): Tree = { + val method = RuntimeStaticsModule.info.member(name) Apply(gen.mkAttributedRef(method), args.toList) } + def callStaticsMethod(name: String)(args: Tree*): Tree = + callStaticsMethodName(newTermName(name))(args: _*) + // Any concrete member, including private def hasConcreteImpl(name: Name) = clazz.info.member(name).alternatives exists (m => !m.isDeferred) @@ -245,10 +248,10 @@ trait SyntheticMethods extends ast.TreeDSL { case BooleanClass => If(Ident(sym), Literal(Constant(1231)), Literal(Constant(1237))) case IntClass => Ident(sym) case ShortClass | ByteClass | CharClass => Select(Ident(sym), nme.toInt) - case LongClass => callStaticsMethod("longHash")(Ident(sym)) - case DoubleClass => callStaticsMethod("doubleHash")(Ident(sym)) - case FloatClass => callStaticsMethod("floatHash")(Ident(sym)) - case _ => callStaticsMethod("anyHash")(Ident(sym)) + case LongClass => callStaticsMethodName(nme.longHash)(Ident(sym)) + case DoubleClass => callStaticsMethodName(nme.doubleHash)(Ident(sym)) + case FloatClass => callStaticsMethodName(nme.floatHash)(Ident(sym)) + case _ => callStaticsMethodName(nme.anyHash)(Ident(sym)) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Tags.scala b/src/compiler/scala/tools/nsc/typechecker/Tags.scala index 56127f4026..e29451f379 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Tags.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Tags.scala @@ -13,16 +13,7 @@ trait Tags { private val runDefinitions = currentRun.runDefinitions private def resolveTag(pos: Position, taggedTp: Type, allowMaterialization: Boolean) = enteringTyper { - def wrapper (tree: => Tree): Tree = if (allowMaterialization) (context.withMacrosEnabled[Tree](tree)) else (context.withMacrosDisabled[Tree](tree)) - wrapper(inferImplicit( - EmptyTree, - taggedTp, - reportAmbiguous = true, - isView = false, - context, - saveAmbiguousDivergent = true, - pos - ).tree) + context.withMacros(enabled = allowMaterialization) { inferImplicitByType(taggedTp, context, pos).tree } } /** Finds in scope or materializes a ClassTag. diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index f6ee0c47a5..990edcd86d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -111,8 +111,6 @@ abstract class TreeCheckers extends Analyzer { newSyms += sym else if (prevTrees exists (t => (t eq tree) || (t.symbol == sym))) () - else if (prevTrees exists (_.symbol.owner == sym.owner.implClass)) - errorFn("Noticed " + ownerstr(sym) + " moving to implementation class.") else { val s1 = (prevTrees map wholetreestr).sorted.distinct val s2 = wholetreestr(tree) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 3a8edafd58..329ce8c23b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -105,7 +105,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // that are turned private by typedBlock private final val SYNTHETIC_PRIVATE = TRANS_FLAG - private final val InterpolatorCodeRegex = """\$\{.*?\}""".r + private final val InterpolatorCodeRegex = """\$\{\s*(.*?)\s*\}""".r private final val InterpolatorIdentRegex = """\$[$\w]+""".r // note that \w doesn't include $ abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tag with PatternTyper with TyperContextErrors { @@ -128,6 +128,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def canTranslateEmptyListToNil = true def missingSelectErrorTree(tree: Tree, qual: Tree, name: Name): Tree = tree + // when type checking during erasure, generate erased types in spots that aren't transformed by erasure + // (it erases in TypeTrees, but not in, e.g., the type a Function node) + def phasedAppliedType(sym: Symbol, args: List[Type]) = { + val tp = appliedType(sym, args) + if (phase.erasedTypes) erasure.specialScalaErasure(tp) else tp + } + def typedDocDef(docDef: DocDef, mode: Mode, pt: Type): Tree = typed(docDef.definition, mode, pt) @@ -151,7 +158,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper for(ar <- argResultsBuff) paramTp = paramTp.subst(ar.subst.from, ar.subst.to) - val res = if (paramFailed || (paramTp.isErroneous && {paramFailed = true; true})) SearchFailure else inferImplicit(fun, paramTp, context.reportErrors, isView = false, context) + val res = + if (paramFailed || (paramTp.isErroneous && {paramFailed = true; true})) SearchFailure + else inferImplicitFor(paramTp, fun, context, reportAmbiguous = context.reportErrors) argResultsBuff += res if (res.isSuccess) { @@ -187,14 +196,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper !from.isError && !to.isError && context.implicitsEnabled - && (inferView(context.tree, from, to, reportAmbiguous = false, saveErrors = true) != EmptyTree) + && (inferView(context.tree, from, to, reportAmbiguous = false) != EmptyTree) // SI-8230 / SI-8463 We'd like to change this to `saveErrors = false`, but can't. // For now, we can at least pass in `context.tree` rather then `EmptyTree` so as // to avoid unpositioned type errors. ) - def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean): Tree = - inferView(tree, from, to, reportAmbiguous, saveErrors = true) /** Infer an implicit conversion (`view`) between two types. * @param tree The tree which needs to be converted. @@ -207,25 +214,23 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * during the inference of a view be put into the original buffer. * False iff we don't care about them. */ - def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { - debuglog("infer view from "+from+" to "+to)//debug - if (isPastTyper) EmptyTree - else from match { - case MethodType(_, _) => EmptyTree - case OverloadedType(_, _) => EmptyTree - case PolyType(_, _) => EmptyTree - case _ => - def wrapImplicit(from: Type): Tree = { - val result = inferImplicit(tree, functionType(from.withoutAnnotations :: Nil, to), reportAmbiguous, isView = true, context, saveAmbiguousDivergent = saveErrors) - if (result.subst != EmptyTreeTypeSubstituter) { - result.subst traverse tree - notifyUndetparamsInferred(result.subst.from, result.subst.to) - } - result.tree - } - wrapImplicit(from) orElse wrapImplicit(byNameType(from)) + def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean = true, saveErrors: Boolean = true): Tree = + if (isPastTyper || from.isInstanceOf[MethodType] || from.isInstanceOf[OverloadedType] || from.isInstanceOf[PolyType]) EmptyTree + else { + debuglog(s"Inferring view from $from to $to for $tree (reportAmbiguous= $reportAmbiguous, saveErrors=$saveErrors)") + + val fromNoAnnot = from.withoutAnnotations + val result = inferImplicitView(fromNoAnnot, to, tree, context, reportAmbiguous, saveErrors) match { + case fail if fail.isFailure => inferImplicitView(byNameType(fromNoAnnot), to, tree, context, reportAmbiguous, saveErrors) + case ok => ok + } + + if (result.subst != EmptyTreeTypeSubstituter) { + result.subst traverse tree + notifyUndetparamsInferred(result.subst.from, result.subst.to) + } + result.tree } - } import infer._ @@ -239,6 +244,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper var context = context0 def context1 = context + // for use with silent type checking to when we can't have results with undetermined type params + // note that this captures the context var + val isMonoContext = (_: Any) => context.undetparams.isEmpty + def dropExistential(tp: Type): Type = tp match { case ExistentialType(tparams, tpe) => new SubstWildcardMap(tparams).apply(tp) @@ -725,7 +734,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper featureTrait.owner.ownerChain.takeWhile(_ != languageFeatureModule.moduleClass).reverse val featureName = (nestedOwners map (_.name + ".")).mkString + featureTrait.name def action(): Boolean = { - def hasImport = inferImplicit(EmptyTree: Tree, featureTrait.tpe, reportAmbiguous = true, isView = false, context).isSuccess + def hasImport = inferImplicitByType(featureTrait.tpe, context).isSuccess def hasOption = settings.language contains featureName val OK = hasImport || hasOption if (!OK) { @@ -802,7 +811,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * (11) Widen numeric literals to their expected type, if necessary * (12) When in mode EXPRmode, convert E to { E; () } if expected type is scala.Unit. * (13) When in mode EXPRmode, apply AnnotationChecker conversion if expected type is annotated. - * (14) When in mode EXPRmode, apply a view + * (14) When in mode EXPRmode, do SAM conversion + * (15) When in mode EXPRmode, apply a view * If all this fails, error */ protected def adapt(tree: Tree, mode: Mode, pt: Type, original: Tree = EmptyTree): Tree = { @@ -1014,72 +1024,70 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } - def fallbackAfterVanillaAdapt(): Tree = { - def isPopulatedPattern = { - if ((tree.symbol ne null) && tree.symbol.isModule) - inferModulePattern(tree, pt) - - isPopulated(tree.tpe, approximateAbstracts(pt)) + def adaptExprNotFunMode(): Tree = { + def lastTry(err: AbsTypeError = null): Tree = { + debuglog("error tree = " + tree) + if (settings.debug && settings.explaintypes) explainTypes(tree.tpe, pt) + if (err ne null) context.issue(err) + if (tree.tpe.isErroneous || pt.isErroneous) setError(tree) + else adaptMismatchedSkolems() } - if (mode.inPatternMode && isPopulatedPattern) - return tree - val tree1 = constfold(tree, pt) // (10) (11) - if (tree1.tpe <:< pt) - return adapt(tree1, mode, pt, original) + // TODO: should we even get to fallbackAfterVanillaAdapt for an ill-typed tree? + if (mode.typingExprNotFun && !tree.tpe.isErroneous) { + @inline def tpdPos(transformed: Tree) = typedPos(tree.pos, mode, pt)(transformed) + @inline def tpd(transformed: Tree) = typed(transformed, mode, pt) - if (mode.typingExprNotFun) { - // The <: Any requirement inhibits attempts to adapt continuation types - // to non-continuation types. - if (tree.tpe <:< AnyTpe) pt.dealias match { - case TypeRef(_, UnitClass, _) => // (12) - if (!isPastTyper && settings.warnValueDiscard) - context.warning(tree.pos, "discarded non-Unit value") - return typedPos(tree.pos, mode, pt)(Block(List(tree), Literal(Constant(())))) - case TypeRef(_, sym, _) if isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt) => - if (!isPastTyper && settings.warnNumericWiden) - context.warning(tree.pos, "implicit numeric widening") - return typedPos(tree.pos, mode, pt)(Select(tree, "to" + sym.name)) - case _ => - } - if (pt.dealias.annotations.nonEmpty && canAdaptAnnotations(tree, this, mode, pt)) // (13) - return typed(adaptAnnotations(tree, this, mode, pt), mode, pt) + @inline def warnValueDiscard(): Unit = + if (!isPastTyper && settings.warnValueDiscard) context.warning(tree.pos, "discarded non-Unit value") + @inline def warnNumericWiden(): Unit = + if (!isPastTyper && settings.warnNumericWiden) context.warning(tree.pos, "implicit numeric widening") - if (hasUndets) - return instantiate(tree, mode, pt) - - if (context.implicitsEnabled && !pt.isError && !tree.isErrorTyped) { - // (14); the condition prevents chains of views - debuglog("inferring view from " + tree.tpe + " to " + pt) - inferView(tree, tree.tpe, pt, reportAmbiguous = true) match { - case EmptyTree => - case coercion => - def msg = "inferred view from " + tree.tpe + " to " + pt + " = " + coercion + ":" + coercion.tpe - if (settings.logImplicitConv) - context.echo(tree.pos, msg) - - debuglog(msg) - val silentContext = context.makeImplicit(context.ambiguousErrors) - val res = newTyper(silentContext).typed( - new ApplyImplicitView(coercion, List(tree)) setPos tree.pos, mode, pt) - silentContext.reporter.firstError match { - case Some(err) => context.issue(err) - case None => return res + // The <: Any requirement inhibits attempts to adapt continuation types to non-continuation types. + val anyTyped = tree.tpe <:< AnyTpe + + pt.dealias match { + case TypeRef(_, UnitClass, _) if anyTyped => // (12) + warnValueDiscard() ; tpdPos(gen.mkUnitBlock(tree)) + case TypeRef(_, numValueCls, _) if anyTyped && isNumericValueClass(numValueCls) && isNumericSubType(tree.tpe, pt) => // (10) (11) + warnNumericWiden() ; tpdPos(Select(tree, s"to${numValueCls.name}")) + case dealiased if dealiased.annotations.nonEmpty && canAdaptAnnotations(tree, this, mode, pt) => // (13) + tpd(adaptAnnotations(tree, this, mode, pt)) + case _ => + if (hasUndets) instantiate(tree, mode, pt) + else { + // (14) sam conversion + // TODO: figure out how to avoid partially duplicating typedFunction (samMatchingFunction) + // Could we infer the SAM type, assign it to the tree and add the attachment, + // all in one fell swoop at the end of typedFunction? + val samAttach = inferSamType(tree, pt, mode) + + if (samAttach.samTp ne NoType) tree.setType(samAttach.samTp).updateAttachment(samAttach) + else { // (15) implicit view application + val coercion = + if (context.implicitsEnabled) inferView(tree, tree.tpe, pt) + else EmptyTree + if (coercion ne EmptyTree) { + def msg = s"inferred view from ${tree.tpe} to $pt via $coercion: ${coercion.tpe}" + if (settings.logImplicitConv) context.echo(tree.pos, msg) + else debuglog(msg) + + val viewApplied = new ApplyImplicitView(coercion, List(tree)) setPos tree.pos + val silentContext = context.makeImplicit(context.ambiguousErrors) + val typedView = newTyper(silentContext).typed(viewApplied, mode, pt) + + silentContext.reporter.firstError match { + case None => typedView + case Some(err) => lastTry(err) + } + } else lastTry() } - } + } } - } - - debuglog("error tree = " + tree) - if (settings.debug && settings.explaintypes) - explainTypes(tree.tpe, pt) - - if (tree.tpe.isErroneous || pt.isErroneous) - setError(tree) - else - adaptMismatchedSkolems() + } else lastTry() } + def vanillaAdapt(tree: Tree) = { def applyPossible = { def applyMeth = member(adaptToName(tree, nme.apply), nme.apply) @@ -1113,8 +1121,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } else if (tree.tpe <:< pt) tree - else - fallbackAfterVanillaAdapt() + else if (mode.inPatternMode && { inferModulePattern(tree, pt); isPopulated(tree.tpe, approximateAbstracts(pt)) }) + tree + else { + val constFolded = constfold(tree, pt) + if (constFolded.tpe <:< pt) adapt(constFolded, mode, pt, original) // set stage for (0) + else adaptExprNotFunMode() // (10) -- (15) + } } // begin adapt @@ -1179,7 +1192,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val savedUndetparams = context.undetparams silent(_.instantiate(tree, mode, UnitTpe)) orElse { _ => context.undetparams = savedUndetparams - val valueDiscard = atPos(tree.pos)(Block(List(instantiate(tree, mode, WildcardType)), Literal(Constant(())))) + val valueDiscard = atPos(tree.pos)(gen.mkUnitBlock(instantiate(tree, mode, WildcardType))) typed(valueDiscard, mode, UnitTpe) } } @@ -1240,7 +1253,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * If no conversion is found, return `qual` unchanged. * */ - def adaptToArguments(qual: Tree, name: Name, args: List[Tree], pt: Type, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { + def adaptToArguments(qual: Tree, name: Name, args: List[Tree], pt: Type, reportAmbiguous: Boolean = true, saveErrors: Boolean = true): Tree = { def doAdapt(restpe: Type) = //util.trace("adaptToArgs "+qual+", name = "+name+", argtpes = "+(args map (_.tpe))+", pt = "+pt+" = ") adaptToMember(qual, HasMethodMatching(name, args map (_.tpe), restpe), reportAmbiguous, saveErrors) @@ -1256,7 +1269,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * a method `name`. If that's ambiguous try taking arguments into * account using `adaptToArguments`. */ - def adaptToMemberWithArgs(tree: Tree, qual: Tree, name: Name, mode: Mode, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { + def adaptToMemberWithArgs(tree: Tree, qual: Tree, name: Name, mode: Mode, reportAmbiguous: Boolean = true, saveErrors: Boolean = true): Tree = { def onError(reportError: => Tree): Tree = context.tree match { case Apply(tree1, args) if (tree1 eq tree) && args.nonEmpty => ( silent (_.typedArgs(args.map(_.duplicate), mode)) @@ -1738,17 +1751,21 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper classinfo.parents map (_.instantiateTypeParams(List(tparam), List(AnyRefTpe))), classinfo.decls, clazz) - clazz.setInfo { - clazz.info match { - case PolyType(tparams, _) => PolyType(tparams, newinfo) - case _ => newinfo - } - } + updatePolyClassInfo(clazz, newinfo) FinitaryError(tparam) } } } + private def updatePolyClassInfo(clazz: Symbol, newinfo: ClassInfoType): clazz.type = { + clazz.setInfo { + clazz.info match { + case PolyType(tparams, _) => PolyType(tparams, newinfo) + case _ => newinfo + } + } + } + def typedClassDef(cdef: ClassDef): Tree = { val clazz = cdef.symbol val typedMods = typedModifiers(cdef.mods) @@ -1857,6 +1874,26 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // please FIXME: uncommenting this line breaks everything // val templ = treeCopy.Template(templ0, templ0.body, templ0.self, templ0.parents) val clazz = context.owner + + val parentTypes = parents1.map(_.tpe) + + // The parents may have been normalized by typedParentTypes. + // We must update the info as well, or we won't find the super constructor for our now-first parent class + // Consider `class C ; trait T extends C ; trait U extends T` + // `U`'s info will start with parent `T`, but `typedParentTypes` will return `List(C, T)` (`== parents1`) + // now, the super call in the primary ctor will fail to find `C`'s ctor, since it bases its search on + // `U`'s info, not the trees. + // + // For correctness and performance, we restrict this rewrite to anonymous classes, + // as others have their parents in order already (it seems!), and we certainly + // don't want to accidentally rewire superclasses for e.g. the primitive value classes. + // + // TODO: Find an example of a named class needing this rewrite, I tried but couldn't find one. + if (clazz.isAnonymousClass && clazz.info.parents != parentTypes) { +// println(s"updating parents of $clazz from ${clazz.info.parents} to $parentTypes") + updatePolyClassInfo(clazz, ClassInfoType(parentTypes, clazz.info.decls, clazz)) + } + clazz.annotations.map(_.completeInfo()) if (templ.symbol == NoSymbol) templ setSymbol clazz.newLocalDummy(templ.pos) @@ -2577,8 +2614,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // the default uses applyOrElse's first parameter since the scrut's type has been widened val match_ = { - val defaultCase = methodBodyTyper.typedCase( - mkDefaultCase(methodBodyTyper.typed1(REF(default) APPLY (REF(x)), mode, B1.tpe).setType(B1.tpe)), argTp, B1.tpe) + val cdef = mkDefaultCase(methodBodyTyper.typed1(REF(default) APPLY (REF(x)), mode, B1.tpe).setType(B1.tpe)) + val List(defaultCase) = methodBodyTyper.typedCases(List(cdef), argTp, B1.tpe) treeCopy.Match(match0, match0.selector, match0.cases :+ defaultCase) } match_ setType B1.tpe @@ -2706,187 +2743,99 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } - /** Synthesize and type check the implementation of a type with a Single Abstract Method - * - * `{ (p1: T1, ..., pN: TN) => body } : S` - * - * expands to (where `S` is the expected type that defines a single abstract method named `apply`) - * - * `{ - * def apply$body(p1: T1, ..., pN: TN): T = body - * new S { - * def apply(p1: T1', ..., pN: TN'): T' = apply$body(p1,..., pN) - * } - * }` - * - * If 'T' is not fully defined, it is inferred by type checking - * `apply$body` without a result type before type checking the block. - * The method's inferred result type is used instead of `T`. [See test/files/pos/sammy_poly.scala] - * - * The `apply` method is identified by the argument `sam`; `S` corresponds to the argument `samClassTp`, - * and `resPt` is derived from `samClassTp` -- it may be fully defined, or not... - * If it is not fully defined, we derive `samClassTpFullyDefined` by inferring any unknown type parameters. - * - * The types T1' ... TN' and T' are derived from the method signature of the sam method, - * as seen from the fully defined `samClassTpFullyDefined`. - * - * The function's body is put in a method outside of the class definition to enforce scoping. - * S's members should not be in scope in `body`. - * - * The restriction on implicit arguments (neither S's constructor, nor sam may take an implicit argument list), - * is largely to keep the implementation of type inference (the computation of `samClassTpFullyDefined`) simple. - * - * NOTE: it would be nicer to not have to type check `apply$body` separately when `T` is not fully defined. - * However T must be fully defined before we type the instantiation, as it'll end up as a parent type, - * which must be fully defined. Would be nice to have some kind of mechanism to insert type vars in a block of code, - * and have the instantiation of the first occurrence propagate to the rest of the block. - * - * TODO: by-name params - * scala> trait LazySink { def accept(a: => Any): Unit } - * defined trait LazySink - * - * scala> val f: LazySink = (a) => (a, a) - * f: LazySink = $anonfun$1@1fb26910 - * - * scala> f(println("!")) - * <console>:10: error: LazySink does not take parameters - * f(println("!")) - * ^ - * - * scala> f.accept(println("!")) - * ! - * ! - */ - def synthesizeSAMFunction(sam: Symbol, fun: Function, resPt: Type, samClassTp: Type, mode: Mode): Tree = { - // assert(fun.vparams forall (vp => isFullyDefined(vp.tpt.tpe))) -- by construction, as we take them from sam's info - val sampos = fun.pos - - // if the expected sam type is fully defined, use it for the method's result type - // otherwise, NoType, so that type inference will determine the method's result type - // resPt is syntactically contained in samClassTp, so if the latter is fully defined, so is the former - // ultimately, we want to fully define samClassTp as it is used as the superclass of our anonymous class - val samDefTp = if (isFullyDefined(resPt)) resPt else NoType - val bodyName = newTermName(sam.name + "$body") - - // `def '${sam.name}\$body'($p1: $T1, ..., $pN: $TN): $resPt = $body` - val samBodyDef = - DefDef(NoMods, - bodyName, - Nil, - List(fun.vparams.map(_.duplicate)), // must duplicate as we're also using them for `samDef` - TypeTree(samDefTp) setPos sampos.focus, - fun.body) - - // If we need to enter the sym for the body def before type checking the block, - // we'll create a nested context, as explained below. - var nestedTyper = this - - // Type check body def before classdef to fully determine samClassTp (if necessary). - // As `samClassTp` determines a parent type for the class, - // we can't type check `block` in one go unless `samClassTp` is fully defined. - val samClassTpFullyDefined = - if (isFullyDefined(samClassTp)) samClassTp + /** Synthesize and type check the implementation of a type with a Single Abstract Method. + * + * Based on a type checked Function node `{ (p1: T1, ..., pN: TN) => body } : S` + * where `S` is the expected type that defines a single abstract method (call it `apply` for the example), + * that has signature `(p1: T1', ..., pN: TN'): T'`, synthesize the instantiation of the following anonymous class + * + * ``` + * new S { + * def apply$body(p1: T1, ..., pN: TN): T = body + * def apply(p1: T1', ..., pN: TN'): T' = apply$body(p1,..., pN) + * } + * ``` + * + * The `apply` method is identified by the argument `sam`; `S` corresponds to the argument `pt`, + * If `pt` is not fully defined, we derive `samClassTpFullyDefined` by inferring any unknown type parameters. + * + * The types T1' ... TN' and T' are derived from the method signature of the sam method, + * as seen from the fully defined `samClassTpFullyDefined`. + * + * The function's body is put in a (static) method in the class definition to enforce scoping. + * S's members should not be in scope in `body`. (Putting it in the block outside the class runs into implementation problems described below) + * + * The restriction on implicit arguments (neither S's constructor, nor sam may take an implicit argument list), + * is to keep the implementation of type inference (the computation of `samClassTpFullyDefined`) simple. + * + * Impl notes: + * - `fun` has a FunctionType, but the expected type `pt` is some SAM type -- let's remedy that + * - `fun` is fully attributed, so we'll have to wrangle some symbols into shape (owner change, vparam syms) + * - after experimentation, it works best to type check function literals fully first and then adapt to a sam type, + * as opposed to a sam-specific code paths earlier on in type checking (in typedFunction). + * For one, we want to emit the same bytecode regardless of whether the expected + * function type is a built-in FunctionN or some SAM type + * + */ + def inferSamType(fun: Tree, pt: Type, mode: Mode): SAMFunction = { + val sam = + if (fun.isInstanceOf[Function] && !isFunctionType(pt)) { + // TODO: can we ensure there's always a SAMFunction attachment, instead of looking up the sam again??? + // seems like overloading complicates things? + val sam = samOf(pt) + if (samMatchesFunctionBasedOnArity(sam, fun.asInstanceOf[Function].vparams)) sam + else NoSymbol + } else NoSymbol + + def fullyDefinedMeetsExpectedFunTp(pt: Type): Boolean = isFullyDefined(pt) && { + val samMethType = pt memberInfo sam + fun.tpe <:< functionType(samMethType.paramTypes, samMethType.resultType) + } + + SAMFunction( + if (!sam.exists) NoType + else if (fullyDefinedMeetsExpectedFunTp(pt)) pt else try { - // This creates a symbol for samBodyDef with a type completer that'll be triggered immediately below. - // The symbol is entered in the same scope used for the block below, and won't thus be reentered later. - // It has to be a new scope, though, or we'll "get ambiguous reference to overloaded definition" [pos/sammy_twice.scala] - // makeSilent: [pos/nonlocal-unchecked.scala -- when translation all functions to sams] - val nestedCtx = enterSym(context.makeNewScope(context.tree, context.owner).makeSilent(), samBodyDef) - nestedTyper = newTyper(nestedCtx) - - // NOTE: this `samBodyDef.symbol.info` runs the type completer set up by the enterSym above - val actualSamType = samBodyDef.symbol.info + val samClassSym = pt.typeSymbol // we're trying to fully define the type arguments for this type constructor - val samTyCon = samClassTp.typeSymbol.typeConstructor + val samTyCon = samClassSym.typeConstructor // the unknowns - val tparams = samClassTp.typeSymbol.typeParams + val tparams = samClassSym.typeParams // ... as typevars - val tvars = tparams map freshVar - - // 1. Recover partial information: - // - derive a type from samClassTp that has the corresponding tparams for type arguments that aren't fully defined - // - constrain typevars to be equal to type args that are fully defined - val samClassTpMoreDefined = appliedType(samTyCon, - (samClassTp.typeArgs, tparams, tvars).zipped map { - case (a, _, tv) if isFullyDefined(a) => tv =:= a; a - case (_, p, _) => p.typeConstructor - }) + val tvars = tparams map freshVar - // the method type we're expecting the synthesized sam to have, based on the expected sam type, - // where fully defined type args to samClassTp have been preserved, - // with the unknown args replaced by their corresponding type param - val expectedSamType = samClassTpMoreDefined.memberInfo(sam) + val ptVars = appliedType(samTyCon, tvars) - // 2. make sure the body def's actual type (formals and result) conforms to - // sam's expected type (in terms of the typevars that represent the sam's class's type params) - actualSamType <:< expectedSamType.substituteTypes(tparams, tvars) + // carry over info from pt + ptVars <:< pt - // solve constraints tracked by tvars - val targs = solvedTypes(tvars, tparams, tparams map varianceInType(sam.info), upper = false, lubDepth(sam.info :: Nil)) - - debuglog(s"sam infer: $samClassTp --> ${appliedType(samTyCon, targs)} by $actualSamType <:< $expectedSamType --> $targs for $tparams") + val samInfoWithTVars = ptVars.memberInfo(sam) - // a fully defined samClassTp - appliedType(samTyCon, targs) - } catch { - case _: NoInstance | _: TypeError => - devWarning(sampos, s"Could not define type $samClassTp using ${samBodyDef.symbol.rawInfo} <:< ${samClassTp memberInfo sam} (for $sam)") - samClassTp - } - - // what's the signature of the method that we should actually be overriding? - val samMethTp = samClassTpFullyDefined memberInfo sam - // Before the mutation, `tp <:< vpar.tpt.tpe` should hold. - // TODO: error message when this is not the case, as the expansion won't type check - // - Ti' <:< Ti and T <: T' must hold for the samDef body to type check - val funArgTps = foreach2(samMethTp.paramTypes, fun.vparams)((tp, vpar) => vpar.tpt setType tp) - - // `final override def ${sam.name}($p1: $T1', ..., $pN: $TN'): ${samMethTp.finalResultType} = ${sam.name}\$body'($p1, ..., $pN)` - val samDef = - DefDef(Modifiers(FINAL | OVERRIDE | SYNTHETIC), - sam.name.toTermName, - Nil, - List(fun.vparams), - TypeTree(samMethTp.finalResultType) setPos sampos.focus, - Apply(Ident(bodyName), fun.vparams map gen.paramToArg) - ) + // use function type subtyping, not method type subtyping (the latter is invariant in argument types) + fun.tpe <:< functionType(samInfoWithTVars.paramTypes, samInfoWithTVars.finalResultType) - val serializableParentAddendum = - if (typeIsSubTypeOfSerializable(samClassTp)) Nil - else List(TypeTree(SerializableTpe)) - - val classDef = - ClassDef(Modifiers(FINAL), tpnme.ANON_FUN_NAME, tparams = Nil, - gen.mkTemplate( - parents = TypeTree(samClassTpFullyDefined) :: serializableParentAddendum, - self = noSelfType, - constrMods = NoMods, - vparamss = ListOfNil, - body = List(samDef), - superPos = sampos.focus - ) - ) + val variances = tparams map varianceInType(sam.info) - // type checking the whole block, so that everything is packaged together nicely - // and we don't have to create any symbols by hand - val block = - nestedTyper.typedPos(sampos, mode, samClassTpFullyDefined) { - Block( - samBodyDef, - classDef, - Apply(Select(New(Ident(tpnme.ANON_FUN_NAME)), nme.CONSTRUCTOR), Nil) - ) - } + // solve constraints tracked by tvars + val targs = solvedTypes(tvars, tparams, variances, upper = false, lubDepth(sam.info :: Nil)) - // TODO: improve error reporting -- when we're in silent mode (from `silent(_.doTypedApply(tree, fun, args, mode, pt)) orElse onError`) - // the errors in the function don't get out... - if (block exists (_.isErroneous)) - context.error(fun.pos, s"Could not derive subclass of $samClassTp\n (with SAM `def $sam$samMethTp`)\n based on: $fun.") + debuglog(s"sam infer: $pt --> ${appliedType(samTyCon, targs)} by ${fun.tpe} <:< $samInfoWithTVars --> $targs for $tparams") - classDef.symbol addAnnotation SerialVersionUIDAnnotation - block + val ptFullyDefined = appliedType(samTyCon, targs) + if (ptFullyDefined <:< pt && fullyDefinedMeetsExpectedFunTp(ptFullyDefined)) { + debuglog(s"sam fully defined expected type: $ptFullyDefined from $pt for ${fun.tpe}") + ptFullyDefined + } else { + debuglog(s"Could not define type $pt using ${fun.tpe} <:< ${pt memberInfo sam} (for $sam)") + NoType + } + } catch { + case e@(_: NoInstance | _: TypeError) => + debuglog(s"Error during SAM synthesis: could not define type $pt using ${fun.tpe} <:< ${pt memberInfo sam} (for $sam)\n$e") + NoType + }, sam) } /** Type check a function literal. @@ -2896,16 +2845,19 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * - a type with a Single Abstract Method (under -Xexperimental for now). */ private def typedFunction(fun: Function, mode: Mode, pt: Type): Tree = { - val numVparams = fun.vparams.length + val vparams = fun.vparams + val numVparams = vparams.length val FunctionSymbol = if (numVparams > definitions.MaxFunctionArity) NoSymbol else FunctionClass(numVparams) + val ptSym = pt.typeSymbol + /* The Single Abstract Member of pt, unless pt is the built-in function type of the expected arity, * as `(a => a): Int => Int` should not (yet) get the sam treatment. */ val sam = - if (pt.typeSymbol == FunctionSymbol) NoSymbol + if (ptSym == NoSymbol || ptSym == FunctionSymbol || ptSym == PartialFunctionClass) NoSymbol else samOf(pt) /* The SAM case comes first so that this works: @@ -2913,79 +2865,101 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * (a => a): MyFun * * Note that the arity of the sam must correspond to the arity of the function. + * TODO: handle vararg sams? */ - val samViable = sam.exists && sameLength(sam.info.params, fun.vparams) - val ptNorm = if (samViable) samToFunctionType(pt, sam) else pt + val ptNorm = + if (samMatchesFunctionBasedOnArity(sam, vparams)) samToFunctionType(pt, sam) + else pt val (argpts, respt) = ptNorm baseType FunctionSymbol match { case TypeRef(_, FunctionSymbol, args :+ res) => (args, res) - case _ => (fun.vparams map (_ => if (pt == ErrorType) ErrorType else NoType), WildcardType) + case _ => (vparams map (if (pt == ErrorType) (_ => ErrorType) else (_ => NoType)), WildcardType) } - if (!FunctionSymbol.exists) - MaxFunctionArityError(fun) - else if (argpts.lengthCompare(numVparams) != 0) - WrongNumberOfParametersError(fun, argpts) + if (!FunctionSymbol.exists) MaxFunctionArityError(fun) + else if (argpts.lengthCompare(numVparams) != 0) WrongNumberOfParametersError(fun, argpts) else { - var issuedMissingParameterTypeError = false - foreach2(fun.vparams, argpts) { (vparam, argpt) => + val paramsMissingType = mutable.ArrayBuffer.empty[ValDef] //.sizeHint(numVparams) probably useless, since initial size is 16 and max fun arity is 22 + // first, try to define param types from expected function's arg types if needed + foreach2(vparams, argpts) { (vparam, argpt) => if (vparam.tpt.isEmpty) { - val vparamType = - if (isFullyDefined(argpt)) argpt - else { - fun match { - case etaExpansion(vparams, fn, args) => - silent(_.typed(fn, mode.forFunMode, pt)) filter (_ => context.undetparams.isEmpty) map { fn1 => - // if context.undetparams is not empty, the function was polymorphic, - // so we need the missing arguments to infer its type. See #871 - //println("typing eta "+fun+":"+fn1.tpe+"/"+context.undetparams) - val ftpe = normalize(fn1.tpe) baseType FunctionClass(numVparams) - if (isFunctionType(ftpe) && isFullyDefined(ftpe)) - return typedFunction(fun, mode, ftpe) - } - case _ => - } - MissingParameterTypeError(fun, vparam, pt, withTupleAddendum = !issuedMissingParameterTypeError) - issuedMissingParameterTypeError = true - ErrorType - } - vparam.tpt.setType(vparamType) + if (isFullyDefined(argpt)) vparam.tpt setType argpt + else paramsMissingType += vparam + if (!vparam.tpt.pos.isDefined) vparam.tpt setPos vparam.pos.focus } } - fun.body match { - // translate `x => x match { <cases> }` : PartialFunction to - // `new PartialFunction { def applyOrElse(x, default) = x match { <cases> } def isDefinedAt(x) = ... }` - case Match(sel, cases) if (sel ne EmptyTree) && (pt.typeSymbol == PartialFunctionClass) => - // go to outer context -- must discard the context that was created for the Function since we're discarding the function - // thus, its symbol, which serves as the current context.owner, is not the right owner - // you won't know you're using the wrong owner until lambda lift crashes (unless you know better than to use the wrong owner) - val outerTyper = newTyper(context.outer) - val p = fun.vparams.head - if (p.tpt.tpe == null) p.tpt setType outerTyper.typedType(p.tpt).tpe + // If we're typing `(a1: T1, ..., aN: TN) => m(a1,..., aN)`, where some Ti are not fully defined, + // type `m` directly (undoing eta-expansion of method m) to determine the argument types. + // This tree is the result from one of: + // - manual eta-expansion with named arguments (x => f(x)); + // - wildcard-style eta expansion (`m(_, _,)`); + // - instantiateToMethodType adapting a tree of method type to a function type using etaExpand. + // + // Note that method values are a separate thing (`m _`): they have the idiosyncratic shape + // of `Typed(expr, Function(Nil, EmptyTree))` + val ptUnrollingEtaExpansion = + if (paramsMissingType.nonEmpty && pt != ErrorType) fun.body match { + // we can compare arguments and parameters by name because there cannot be a binder between + // the function's valdefs and the Apply's arguments + case Apply(meth, args) if (vparams corresponds args) { case (p, Ident(name)) => p.name == name case _ => false } => + // We're looking for a method (as indicated by FUNmode in the silent typed below), + // so let's make sure our expected type is a MethodType + val methArgs = NoSymbol.newSyntheticValueParams(argpts map { case NoType => WildcardType case tp => tp }) + silent(_.typed(meth, mode.forFunMode, MethodType(methArgs, respt))) filter (isMonoContext) map { methTyped => + // if context.undetparams is not empty, the method was polymorphic, + // so we need the missing arguments to infer its type. See #871 + val funPt = normalize(methTyped.tpe) baseType FunctionClass(numVparams) + // println(s"typeUnEtaExpanded $meth : ${methTyped.tpe} --> normalized: $funPt") + + // If we are sure this function type provides all the necesarry info, so that we won't have + // any undetermined argument types, go ahead an recurse below (`typedFunction(fun, mode, ptUnrollingEtaExpansion)`) + // and rest assured we won't end up right back here (and keep recursing) + if (isFunctionType(funPt) && funPt.typeArgs.iterator.take(numVparams).forall(isFullyDefined)) funPt + else null + } orElse { _ => null } + case _ => null + } else null + + + if (ptUnrollingEtaExpansion ne null) typedFunction(fun, mode, ptUnrollingEtaExpansion) + else { + // we ran out of things to try, missing parameter types are an irrevocable error + var issuedMissingParameterTypeError = false + paramsMissingType.foreach { vparam => + vparam.tpt setType ErrorType + MissingParameterTypeError(fun, vparam, pt, withTupleAddendum = !issuedMissingParameterTypeError) + issuedMissingParameterTypeError = true + } - outerTyper.synthesizePartialFunction(p.name, p.pos, paramSynthetic = false, fun.body, mode, pt) + fun.body match { + // translate `x => x match { <cases> }` : PartialFunction to + // `new PartialFunction { def applyOrElse(x, default) = x match { <cases> } def isDefinedAt(x) = ... }` + case Match(sel, cases) if (sel ne EmptyTree) && (pt.typeSymbol == PartialFunctionClass) => + // go to outer context -- must discard the context that was created for the Function since we're discarding the function + // thus, its symbol, which serves as the current context.owner, is not the right owner + // you won't know you're using the wrong owner until lambda lift crashes (unless you know better than to use the wrong owner) + val outerTyper = newTyper(context.outer) + val p = vparams.head + if (p.tpt.tpe == null) p.tpt setType outerTyper.typedType(p.tpt).tpe - // Use synthesizeSAMFunction to expand `(p1: T1, ..., pN: TN) => body` - // to an instance of the corresponding anonymous subclass of `pt`. - case _ if samViable => - newTyper(context.outer).synthesizeSAMFunction(sam, fun, respt, pt, mode) + outerTyper.synthesizePartialFunction(p.name, p.pos, paramSynthetic = false, fun.body, mode, pt) - // regular Function - case _ => - val vparamSyms = fun.vparams map { vparam => - enterSym(context, vparam) - if (context.retyping) context.scope enter vparam.symbol - vparam.symbol - } - val vparams = fun.vparams mapConserve typedValDef - val formals = vparamSyms map (_.tpe) - val body1 = typed(fun.body, respt) - val restpe = packedType(body1, fun.symbol).deconst.resultType - val funtpe = appliedType(FunctionSymbol, formals :+ restpe: _*) + case _ => + val vparamSyms = vparams map { vparam => + enterSym(context, vparam) + if (context.retyping) context.scope enter vparam.symbol + vparam.symbol + } + val vparamsTyped = vparams mapConserve typedValDef + val formals = vparamSyms map (_.tpe) + val body1 = typed(fun.body, respt) + val restpe = packedType(body1, fun.symbol).deconst.resultType + val funtpe = phasedAppliedType(FunctionSymbol, formals :+ restpe) - treeCopy.Function(fun, vparams, body1) setType funtpe + treeCopy.Function(fun, vparamsTyped, body1) setType funtpe + } } } } @@ -3018,43 +2992,36 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def includesTargetPos(tree: Tree) = tree.pos.isRange && context.unit.exists && (tree.pos includes context.unit.targetPos) val localTarget = stats exists includesTargetPos - def typedStat(stat: Tree): Tree = { - if (context.owner.isRefinementClass && !treeInfo.isDeclarationOrTypeDef(stat)) - OnlyDeclarationsError(stat) - else - stat match { - case imp @ Import(_, _) => - imp.symbol.initialize - if (!imp.symbol.isError) { - context = context.make(imp) - typedImport(imp) - } else EmptyTree - case _ => - if (localTarget && !includesTargetPos(stat)) { - // skip typechecking of statements in a sequence where some other statement includes - // the targetposition - stat - } else { - val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) { - this - } else newTyper(context.make(stat, exprOwner)) - // XXX this creates a spurious dead code warning if an exception is thrown - // in a constructor, even if it is the only thing in the constructor. - val result = checkDead(localTyper.typedByValueExpr(stat)) - - if (treeInfo.isSelfOrSuperConstrCall(result)) { - context.inConstructorSuffix = true - if (treeInfo.isSelfConstrCall(result) && result.symbol.pos.pointOrElse(0) >= exprOwner.enclMethod.pos.pointOrElse(0)) - ConstructorsOrderError(stat) - } - - if (!isPastTyper && treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos, - "a pure expression does nothing in statement position; " + - "you may be omitting necessary parentheses" - ) - result - } + def typedStat(stat: Tree): Tree = stat match { + case s if context.owner.isRefinementClass && !treeInfo.isDeclarationOrTypeDef(s) => OnlyDeclarationsError(s) + case imp @ Import(_, _) => + imp.symbol.initialize + if (!imp.symbol.isError) { + context = context.make(imp) + typedImport(imp) + } else EmptyTree + // skip typechecking of statements in a sequence where some other statement includes the targetposition + case s if localTarget && !includesTargetPos(s) => s + case _ => + val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) this + else newTyper(context.make(stat, exprOwner)) + // XXX this creates a spurious dead code warning if an exception is thrown + // in a constructor, even if it is the only thing in the constructor. + val result = checkDead(localTyper.typedByValueExpr(stat)) + + if (treeInfo.isSelfOrSuperConstrCall(result)) { + context.inConstructorSuffix = true + if (treeInfo.isSelfConstrCall(result)) { + if (result.symbol == exprOwner.enclMethod) + ConstructorRecursesError(stat) + else if (result.symbol.pos.pointOrElse(0) >= exprOwner.enclMethod.pos.pointOrElse(0)) + ConstructorsOrderError(stat) + } } + if (!isPastTyper && treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos, + "a pure expression does nothing in statement position; you may be omitting necessary parentheses" + ) + result } /* 'accessor' and 'accessed' are so similar it becomes very difficult to @@ -3148,7 +3115,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (phase.erasedTypes) stats1 else { // As packages are open, it doesn't make sense to check double definitions here. Furthermore, - // it is expensive if the package is large. Instead, such double defininitions are checked in `Namers.enterInScope` + // it is expensive if the package is large. Instead, such double definitions are checked in `Namers.enterInScope` if (!context.owner.isPackageClass) checkNoDoubleDefs addSynthetics(stats1) @@ -3215,6 +3182,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // less expensive than including them in inferMethodAlternative (see below). def shapeType(arg: Tree): Type = arg match { case Function(vparams, body) => + // No need for phasedAppliedType, as we don't get here during erasure -- + // overloading resolution happens during type checking. + // During erasure, the condition above (fun.symbol.isOverloaded) is false. functionType(vparams map (_ => AnyTpe), shapeType(body)) case AssignOrNamedArg(Ident(name), rhs) => NamedType(name, shapeType(rhs)) @@ -4296,7 +4266,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (pt.typeSymbol == PartialFunctionClass) synthesizePartialFunction(newTermName(context.unit.fresh.newName("x")), tree.pos, paramSynthetic = true, tree, mode, pt) else { - val arity = if (isFunctionType(pt)) pt.dealiasWiden.typeArgs.length - 1 else 1 + val arity = functionArityFromType(pt) match { case -1 => 1 case arity => arity } // SI-8429: consider sam and function type equally in determining function arity + val params = for (i <- List.range(0, arity)) yield atPos(tree.pos.focusStart) { ValDef(Modifiers(PARAM | SYNTHETIC), @@ -4396,31 +4367,43 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper treeCopy.New(tree, tpt1).setType(tp) } - def functionTypeWildcard(tree: Tree, arity: Int): Type = { - val tp = functionType(List.fill(arity)(WildcardType), WildcardType) - if (tp == NoType) MaxFunctionArityError(tree) - tp - } - - def typedEta(expr1: Tree): Tree = expr1.tpe match { - case TypeRef(_, ByNameParamClass, _) => - val expr2 = Function(List(), expr1) setPos expr1.pos - new ChangeOwnerTraverser(context.owner, expr2.symbol).traverse(expr2) - typed1(expr2, mode, pt) - case NullaryMethodType(restpe) => - val expr2 = Function(List(), expr1) setPos expr1.pos - new ChangeOwnerTraverser(context.owner, expr2.symbol).traverse(expr2) - typed1(expr2, mode, pt) - case PolyType(_, MethodType(formals, _)) => - if (isFunctionType(pt)) expr1 - else adapt(expr1, mode, functionTypeWildcard(expr1, formals.length)) - case MethodType(formals, _) => - if (isFunctionType(pt)) expr1 - else adapt(expr1, mode, functionTypeWildcard(expr1, formals.length)) + def functionTypeWildcard(arity: Int): Type = + functionType(List.fill(arity)(WildcardType), WildcardType) + + def checkArity(tree: Tree)(tp: Type): tp.type = tp match { + case NoType => MaxFunctionArityError(tree); tp + case _ => tp + } + + + /** Eta expand an expression like `m _`, where `m` denotes a method or a by-name argument + * + * The spec says: + * The expression `$e$ _` is well-formed if $e$ is of method type or if $e$ is a call-by-name parameter. + * (1) If $e$ is a method with parameters, `$e$ _` represents $e$ converted to a function type + * by [eta expansion](#eta-expansion). + * (2) If $e$ is a parameterless method or call-by-name parameter of type `=>$T$`, `$e$ _` represents + * the function of type `() => $T$`, which evaluates $e$ when it is applied to the empty parameterlist `()`. + */ + def typedEta(methodValue: Tree): Tree = methodValue.tpe match { + case tp@(MethodType(_, _) | PolyType(_, MethodType(_, _))) => // (1) + val formals = tp.params + if (isFunctionType(pt) || samMatchesFunctionBasedOnArity(samOf(pt), formals)) methodValue + else adapt(methodValue, mode, checkArity(methodValue)(functionTypeWildcard(formals.length))) + + case TypeRef(_, ByNameParamClass, _) | NullaryMethodType(_) => // (2) + val pos = methodValue.pos + // must create it here to change owner (normally done by typed's typedFunction) + val funSym = context.owner.newAnonymousFunctionValue(pos) + new ChangeOwnerTraverser(context.owner, funSym) traverse methodValue + + typed(Function(List(), methodValue) setSymbol funSym setPos pos, mode, pt) + case ErrorType => - expr1 + methodValue + case _ => - UnderscoreEtaError(expr1) + UnderscoreEtaError(methodValue) } def tryTypedArgs(args: List[Tree], mode: Mode): Option[List[Tree]] = { @@ -4464,7 +4447,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case Annotated(_, r) => treesInResult(r) case If(_, t, e) => treesInResult(t) ++ treesInResult(e) case Try(b, catches, _) => treesInResult(b) ++ catches - case Typed(r, Function(Nil, EmptyTree)) => treesInResult(r) + case Typed(r, Function(Nil, EmptyTree)) => treesInResult(r) // a method value case Select(qual, name) => treesInResult(qual) case Apply(fun, args) => treesInResult(fun) ++ args.flatMap(treesInResult) case TypeApply(fun, args) => treesInResult(fun) ++ args.flatMap(treesInResult) @@ -4483,7 +4466,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper tryTypedArgs(args, forArgMode(fun, mode)) match { case Some(args1) if !args1.exists(arg => arg.exists(_.isErroneous)) => val qual1 = - if (!pt.isError) adaptToArguments(qual, name, args1, pt, reportAmbiguous = true, saveErrors = true) + if (!pt.isError) adaptToArguments(qual, name, args1, pt) else qual if (qual1 ne qual) { val tree1 = Apply(Select(qual1, name) setPos fun.pos, args1) setPos tree.pos @@ -4644,10 +4627,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def findMixinSuper(site: Type): Type = { var ps = site.parents filter (_.typeSymbol.name == mix) if (ps.isEmpty) - ps = site.parents filter (_.typeSymbol.toInterface.name == mix) + ps = site.parents filter (_.typeSymbol.name == mix) if (ps.isEmpty) { debuglog("Fatal: couldn't find site " + site + " in " + site.parents.map(_.typeSymbol.name)) - if (phase.erasedTypes && context.enclClass.owner.isImplClass) { + if (phase.erasedTypes && context.enclClass.owner.isTrait) { // the reference to super class got lost during erasure restrictionError(tree.pos, unit, "traits may not select fields or methods from super[C] where C is a class") ErrorType @@ -4710,7 +4693,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // member. Added `| PATTERNmode` to allow enrichment in patterns (so we can add e.g., an // xml member to StringContext, which in turn has an unapply[Seq] method) if (name != nme.CONSTRUCTOR && mode.inAny(EXPRmode | PATTERNmode)) { - val qual1 = adaptToMemberWithArgs(tree, qual, name, mode, reportAmbiguous = true, saveErrors = true) + val qual1 = adaptToMemberWithArgs(tree, qual, name, mode) if ((qual1 ne qual) && !qual1.isErrorTyped) return typed(treeCopy.Select(tree, qual1, name), mode, pt) } @@ -5104,11 +5087,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // because `expr` might contain nested macro calls (see SI-6673) // // Note: apparently `Function(Nil, EmptyTree)` is the secret parser marker - // which means trailing underscore. + // which means trailing underscore -- denoting a method value. See makeMethodValue in TreeBuilder. case Typed(expr, Function(Nil, EmptyTree)) => typed1(suppressMacroExpansion(expr), mode, pt) match { case macroDef if treeInfo.isMacroApplication(macroDef) => MacroEtaError(macroDef) - case exprTyped => typedEta(checkDead(exprTyped)) + case methodValue => typedEta(checkDead(methodValue)) } case Typed(expr, tpt) => val tpt1 = typedType(tpt, mode) // type the ascribed type first @@ -5199,17 +5182,23 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case MethodType(p :: _, _) => p.isImplicit // implicit method requires no args case _ => true // catches all others including NullaryMethodType } - def isPlausible(m: Symbol) = m.alternatives exists (m => requiresNoArgs(m.info)) + def isPlausible(m: Symbol) = !m.isPackage && m.alternatives.exists(x => requiresNoArgs(x.info)) def maybeWarn(s: String): Unit = { 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 + val suspiciousExprs = InterpolatorCodeRegex findAllMatchIn s def suspiciousIdents = InterpolatorIdentRegex findAllIn s map (s => suspiciousSym(TermName(s drop 1))) - - if (suspiciousExpr.nonEmpty) - warn("detected an interpolated expression") // "${...}" - else + def isCheapIdent(expr: String) = (Character.isJavaIdentifierStart(expr.charAt(0)) && + expr.tail.forall(Character.isJavaIdentifierPart)) + def warnableExpr(expr: String) = !expr.isEmpty && (!isCheapIdent(expr) || isPlausible(suspiciousSym(TermName(expr)))) + + if (suspiciousExprs.nonEmpty) { + val exprs = (suspiciousExprs map (_ group 1)).toList + // short-circuit on leading ${} + if (!exprs.head.isEmpty && exprs.exists(warnableExpr)) + warn("detected an interpolated expression") // "${...}" + } else suspiciousIdents find isPlausible foreach (sym => warn(s"detected interpolated identifier `$$${sym.name}`")) // "$id" } lit match { diff --git a/src/compiler/scala/tools/nsc/util/ClassFileLookup.scala b/src/compiler/scala/tools/nsc/util/ClassFileLookup.scala deleted file mode 100644 index 4451651229..0000000000 --- a/src/compiler/scala/tools/nsc/util/ClassFileLookup.scala +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2014 Contributor. All rights reserved. - */ -package scala.tools.nsc.util - -import scala.tools.nsc.io.AbstractFile -import java.net.URL - -/** - * Simple interface that allows us to abstract over how class file lookup is performed - * in different classpath representations. - */ -// TODO at the end, after the possible removal of the old classpath representation, this class shouldn't be generic -// T should be just changed to AbstractFile -trait ClassFileLookup[T] { - def findClassFile(name: String): Option[AbstractFile] - - /** - * It returns both classes from class file and source files (as our base ClassRepresentation). - * So note that it's not so strictly related to findClassFile. - */ - def findClass(name: String): Option[ClassRepresentation[T]] - - /** - * A sequence of URLs representing this classpath. - */ - def asURLs: Seq[URL] - - /** The whole classpath in the form of one String. - */ - def asClassPathString: String - - // for compatibility purposes - @deprecated("Use asClassPathString instead of this one", "2.11.5") - def asClasspathString: String = asClassPathString - - /** The whole sourcepath in the form of one String. - */ - def asSourcePathString: String -} - -/** - * Represents classes which can be loaded with a ClassfileLoader and/or SourcefileLoader. - */ -// TODO at the end, after the possible removal of the old classpath implementation, this class shouldn't be generic -// T should be just changed to AbstractFile -trait ClassRepresentation[T] { - def binary: Option[T] - def source: Option[AbstractFile] - - def name: String -} - -object ClassRepresentation { - def unapply[T](classRep: ClassRepresentation[T]): Option[(Option[T], Option[AbstractFile])] = - Some((classRep.binary, classRep.source)) -} diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala index 2811520b67..cef2fc4bbf 100644 --- a/src/compiler/scala/tools/nsc/util/ClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala @@ -7,28 +7,61 @@ package scala.tools.nsc package util -import io.{ AbstractFile, Directory, File, Jar } +import io.{AbstractFile, Directory, File, Jar} import java.net.MalformedURLException import java.net.URL import java.util.regex.PatternSyntaxException -import scala.collection.{ mutable, immutable } -import scala.reflect.internal.util.StringOps.splitWhere -import scala.tools.nsc.classpath.FileUtils import File.pathSeparator -import FileUtils.endsClass -import FileUtils.endsScalaOrJava import Jar.isJarOrZip -/** <p> - * This module provides star expansion of '-classpath' option arguments, behaves the same as - * java, see [[http://docs.oracle.com/javase/6/docs/technotes/tools/windows/classpath.html]] - * </p> - * - * @author Stepan Koltsov - */ +/** + * A representation of the compiler's class- or sourcepath. + */ +trait ClassPath { + import scala.tools.nsc.classpath._ + def asURLs: Seq[URL] + + /** Empty string represents root package */ + private[nsc] def packages(inPackage: String): Seq[PackageEntry] + private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] + private[nsc] def sources(inPackage: String): Seq[SourceFileEntry] + + /** Allows to get entries for packages and classes merged with sources possibly in one pass. */ + private[nsc] def list(inPackage: String): ClassPathEntries + + /** + * It returns both classes from class file and source files (as our base ClassRepresentation). + * So note that it's not so strictly related to findClassFile. + */ + def findClass(className: String): Option[ClassRepresentation] = { + // A default implementation which should be overridden, if we can create the more efficient + // solution for a given type of ClassPath + val (pkg, simpleClassName) = PackageNameUtils.separatePkgAndClassNames(className) + + val foundClassFromClassFiles = classes(pkg).find(_.name == simpleClassName) + def findClassInSources = sources(pkg).find(_.name == simpleClassName) + + foundClassFromClassFiles orElse findClassInSources + } + def findClassFile(className: String): Option[AbstractFile] + + def asClassPathStrings: Seq[String] + + /** The whole classpath in the form of one String. + */ + def asClassPathString: String = ClassPath.join(asClassPathStrings: _*) + // for compatibility purposes + @deprecated("Use asClassPathString instead of this one", "2.11.5") + def asClasspathString: String = asClassPathString + + /** The whole sourcepath in the form of one String. + */ + def asSourcePathString: String +} + object ClassPath { - import scala.language.postfixOps + val RootPackage = "" /** Expand single path entry */ private def expandS(pattern: String): List[String] = { @@ -36,14 +69,14 @@ object ClassPath { /* Get all subdirectories, jars, zips out of a directory. */ def lsDir(dir: Directory, filt: String => Boolean = _ => true) = - dir.list filter (x => filt(x.name) && (x.isDirectory || isJarOrZip(x))) map (_.path) toList + dir.list.filter(x => filt(x.name) && (x.isDirectory || isJarOrZip(x))).map(_.path).toList if (pattern == "*") lsDir(Directory(".")) else if (pattern endsWith wildSuffix) lsDir(Directory(pattern dropRight 2)) else if (pattern contains '*') { try { val regexp = ("^" + pattern.replaceAllLiterally("""\*""", """.*""") + "$").r - lsDir(Directory(pattern).parent, regexp findFirstIn _ isDefined) + lsDir(Directory(pattern).parent, regexp.findFirstIn(_).isDefined) } catch { case _: PatternSyntaxException => List(pattern) } } @@ -51,7 +84,7 @@ object ClassPath { } /** Split classpath using platform-dependent path separator */ - def split(path: String): List[String] = (path split pathSeparator).toList filterNot (_ == "") distinct + def split(path: String): List[String] = (path split pathSeparator).toList.filterNot(_ == "").distinct /** Join classpath using platform-dependent path separator */ def join(paths: String*): String = paths filterNot (_ == "") mkString pathSeparator @@ -68,9 +101,10 @@ object ClassPath { def expandDir(extdir: String): List[String] = { AbstractFile getDirectory extdir match { case null => Nil - case dir => dir filter (_.isClassContainer) map (x => new java.io.File(dir.file, x.name) getPath) toList + case dir => dir.filter(_.isClassContainer).map(x => new java.io.File(dir.file, x.name).getPath).toList } } + /** Expand manifest jar classpath entries: these are either urls, or paths * relative to the location of the jar. */ @@ -88,309 +122,30 @@ object ClassPath { try Some(new URL(spec)) catch { case _: MalformedURLException => None } - /** A class modeling aspects of a ClassPath which should be - * propagated to any classpaths it creates. - */ - abstract class ClassPathContext[T] extends classpath.ClassPathFactory[ClassPath[T]] { - /** A filter which can be used to exclude entities from the classpath - * based on their name. - */ - def isValidName(name: String): Boolean = true - - /** Filters for assessing validity of various entities. - */ - def validClassFile(name: String) = endsClass(name) && isValidName(name) - def validPackage(name: String) = (name != "META-INF") && (name != "") && (name.charAt(0) != '.') - def validSourceFile(name: String) = endsScalaOrJava(name) - - /** From the representation to its identifier. - */ - def toBinaryName(rep: T): String - - def sourcesInPath(path: String): List[ClassPath[T]] = - for (file <- expandPath(path, expandStar = false) ; dir <- Option(AbstractFile getDirectory file)) yield - new SourcePath[T](dir, this) - } - def manifests: List[java.net.URL] = { - import scala.collection.convert.WrapAsScala.enumerationAsScalaIterator - Thread.currentThread().getContextClassLoader() - .getResources("META-INF/MANIFEST.MF") - .filter(_.getProtocol == "jar").toList + import scala.collection.JavaConverters._ + val resources = Thread.currentThread().getContextClassLoader().getResources("META-INF/MANIFEST.MF") + resources.asScala.filter(_.getProtocol == "jar").toList } - class JavaContext extends ClassPathContext[AbstractFile] { - def toBinaryName(rep: AbstractFile) = { - val name = rep.name - assert(endsClass(name), name) - FileUtils.stripClassExtension(name) - } + @deprecated("Shim for sbt's compiler interface", since = "2.12") + sealed abstract class ClassPathContext - def newClassPath(dir: AbstractFile) = new DirectoryClassPath(dir, this) - } - - object DefaultJavaContext extends JavaContext - - /** From the source file to its identifier. - */ - def toSourceName(f: AbstractFile): String = FileUtils.stripSourceExtension(f.name) + @deprecated("Shim for sbt's compiler interface", since = "2.12") + sealed abstract class JavaContext } -import ClassPath._ - -/** - * Represents a package which contains classes and other packages - */ -abstract class ClassPath[T] extends ClassFileLookup[T] { - /** - * The short name of the package (without prefix) - */ +trait ClassRepresentation { def name: String - - /** - * A String representing the origin of this classpath element, if known. - * For example, the path of the directory or jar. - */ - def origin: Option[String] = None - - /** Info which should be propagated to any sub-classpaths. - */ - def context: ClassPathContext[T] - - /** Lists of entities. - */ - def classes: IndexedSeq[ClassRepresentation[T]] - def packages: IndexedSeq[ClassPath[T]] - def sourcepaths: IndexedSeq[AbstractFile] - - /** The entries this classpath is composed of. In class `ClassPath` it's just the singleton list containing `this`. - * Subclasses such as `MergedClassPath` typically return lists with more elements. - */ - def entries: IndexedSeq[ClassPath[T]] = IndexedSeq(this) - - /** Merge classpath of `platform` and `urls` into merged classpath */ - def mergeUrlsIntoClassPath(urls: URL*): MergedClassPath[T] = { - // Collect our new jars/directories and add them to the existing set of classpaths - val allEntries = - (entries ++ - urls.map(url => context.newClassPath(io.AbstractFile.getURL(url))) - ).distinct - - // Combine all of our classpaths (old and new) into one merged classpath - new MergedClassPath(allEntries, context) - } - - /** - * Represents classes which can be loaded with a ClassfileLoader and/or SourcefileLoader. - */ - case class ClassRep(binary: Option[T], source: Option[AbstractFile]) extends ClassRepresentation[T] { - def name: String = binary match { - case Some(x) => context.toBinaryName(x) - case _ => - assert(source.isDefined) - toSourceName(source.get) - } - } - - /** Filters for assessing validity of various entities. - */ - def validClassFile(name: String) = context.validClassFile(name) - def validPackage(name: String) = context.validPackage(name) - def validSourceFile(name: String) = context.validSourceFile(name) - - /** - * Find a ClassRep given a class name of the form "package.subpackage.ClassName". - * Does not support nested classes on .NET - */ - override def findClass(name: String): Option[ClassRepresentation[T]] = - splitWhere(name, _ == '.', doDropIndex = true) match { - case Some((pkg, rest)) => - val rep = packages find (_.name == pkg) flatMap (_ findClass rest) - rep map { - case x: ClassRepresentation[T] => x - case x => throw new FatalError("Unexpected ClassRep '%s' found searching for name '%s'".format(x, name)) - } - case _ => - classes find (_.name == name) - } - - override def findClassFile(name: String): Option[AbstractFile] = - findClass(name) match { - case Some(ClassRepresentation(Some(x: AbstractFile), _)) => Some(x) - case _ => None - } - - override def asSourcePathString: String = sourcepaths.mkString(pathSeparator) - - def sortString = join(split(asClassPathString).sorted: _*) - override def equals(that: Any) = that match { - case x: ClassPath[_] => this.sortString == x.sortString - case _ => false - } - override def hashCode = sortString.hashCode() -} - -/** - * A Classpath containing source files - */ -class SourcePath[T](dir: AbstractFile, val context: ClassPathContext[T]) extends ClassPath[T] { - import FileUtils.AbstractFileOps - - def name = dir.name - override def origin = dir.underlyingSource map (_.path) - def asURLs = dir.toURLs() - def asClassPathString = dir.path - val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq(dir) - - private def traverse() = { - val classBuf = immutable.Vector.newBuilder[ClassRep] - val packageBuf = immutable.Vector.newBuilder[SourcePath[T]] - dir foreach { f => - if (!f.isDirectory && validSourceFile(f.name)) - classBuf += ClassRep(None, Some(f)) - else if (f.isDirectory && validPackage(f.name)) - packageBuf += new SourcePath[T](f, context) - } - (packageBuf.result(), classBuf.result()) - } - - lazy val (packages, classes) = traverse() - override def toString() = "sourcepath: "+ dir.toString() -} - -/** - * A directory (or a .jar file) containing classfiles and packages - */ -class DirectoryClassPath(val dir: AbstractFile, val context: ClassPathContext[AbstractFile]) extends ClassPath[AbstractFile] { - import FileUtils.AbstractFileOps - - def name = dir.name - override def origin = dir.underlyingSource map (_.path) - def asURLs = dir.toURLs(default = Seq(new URL(name))) - def asClassPathString = dir.path - val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq() - - // calculates (packages, classes) in one traversal. - private def traverse() = { - val classBuf = immutable.Vector.newBuilder[ClassRep] - val packageBuf = immutable.Vector.newBuilder[DirectoryClassPath] - dir foreach { - f => - // Optimization: We assume the file was not changed since `dir` called - // `Path.apply` and categorized existent files as `Directory` - // or `File`. - val isDirectory = f match { - case pf: io.PlainFile => pf.givenPath match { - case _: io.Directory => true - case _: io.File => false - case _ => f.isDirectory - } - case _ => - f.isDirectory - } - if (!isDirectory && validClassFile(f.name)) - classBuf += ClassRep(Some(f), None) - else if (isDirectory && validPackage(f.name)) - packageBuf += new DirectoryClassPath(f, context) - } - (packageBuf.result(), classBuf.result()) - } - - lazy val (packages, classes) = traverse() - override def toString() = "directory classpath: "+ origin.getOrElse("?") + def binary: Option[AbstractFile] + def source: Option[AbstractFile] } -class DeltaClassPath[T](original: MergedClassPath[T], subst: Map[ClassPath[T], ClassPath[T]]) -extends MergedClassPath[T](original.entries map (e => subst getOrElse (e, e)), original.context) { - // not sure we should require that here. Commented out for now. - // require(subst.keySet subsetOf original.entries.toSet) - // We might add specialized operations for computing classes packages here. Not sure it's worth it. -} - -/** - * A classpath unifying multiple class- and sourcepath entries. - */ -class MergedClassPath[T]( - override val entries: IndexedSeq[ClassPath[T]], - val context: ClassPathContext[T]) -extends ClassPath[T] { - - def this(entries: TraversableOnce[ClassPath[T]], context: ClassPathContext[T]) = - this(entries.toIndexedSeq, context) - - def name = entries.head.name - def asURLs = (entries flatMap (_.asURLs)).toList - lazy val sourcepaths: IndexedSeq[AbstractFile] = entries flatMap (_.sourcepaths) - - override def origin = Some(entries map (x => x.origin getOrElse x.name) mkString ("Merged(", ", ", ")")) - override def asClassPathString: String = join(entries map (_.asClassPathString) : _*) - - lazy val classes: IndexedSeq[ClassRepresentation[T]] = { - var count = 0 - val indices = mutable.HashMap[String, Int]() - val cls = new mutable.ArrayBuffer[ClassRepresentation[T]](1024) - - for (e <- entries; c <- e.classes) { - val name = c.name - if (indices contains name) { - val idx = indices(name) - val existing = cls(idx) - - if (existing.binary.isEmpty && c.binary.isDefined) - cls(idx) = ClassRep(binary = c.binary, source = existing.source) - if (existing.source.isEmpty && c.source.isDefined) - cls(idx) = ClassRep(binary = existing.binary, source = c.source) - } - else { - indices(name) = count - cls += c - count += 1 - } - } - cls.toIndexedSeq - } - - lazy val packages: IndexedSeq[ClassPath[T]] = { - var count = 0 - val indices = mutable.HashMap[String, Int]() - val pkg = new mutable.ArrayBuffer[ClassPath[T]](256) - - for (e <- entries; p <- e.packages) { - val name = p.name - if (indices contains name) { - val idx = indices(name) - pkg(idx) = addPackage(pkg(idx), p) - } - else { - indices(name) = count - pkg += p - count += 1 - } - } - pkg.toIndexedSeq - } +@deprecated("Shim for sbt's compiler interface", since = "2.12") +sealed abstract class DirectoryClassPath - private def addPackage(to: ClassPath[T], pkg: ClassPath[T]) = { - val newEntries: IndexedSeq[ClassPath[T]] = to match { - case cp: MergedClassPath[_] => cp.entries :+ pkg - case _ => IndexedSeq(to, pkg) - } - new MergedClassPath[T](newEntries, context) - } +@deprecated("Shim for sbt's compiler interface", since = "2.12") +sealed abstract class MergedClassPath - def show() { - println("ClassPath %s has %d entries and results in:\n".format(name, entries.size)) - asClassPathString split ':' foreach (x => println(" " + x)) - } - - override def toString() = "merged classpath "+ entries.mkString("(", "\n", ")") -} - -/** - * The classpath when compiling with target:jvm. Binary files (classfiles) are represented - * as AbstractFile. nsc.io.ZipArchive is used to view zip/jar archives as directories. - */ -class JavaClassPath( - containers: IndexedSeq[ClassPath[AbstractFile]], - context: JavaContext) -extends MergedClassPath[AbstractFile](containers, context) { } +@deprecated("Shim for sbt's compiler interface", since = "2.12") +sealed abstract class JavaClassPath diff --git a/src/compiler/scala/tools/reflect/ReflectMain.scala b/src/compiler/scala/tools/reflect/ReflectMain.scala index 8d8418945a..7d82910699 100644 --- a/src/compiler/scala/tools/reflect/ReflectMain.scala +++ b/src/compiler/scala/tools/reflect/ReflectMain.scala @@ -5,12 +5,12 @@ import scala.reflect.internal.util.ScalaClassLoader import scala.tools.nsc.Driver import scala.tools.nsc.Global import scala.tools.nsc.Settings -import scala.tools.util.PathResolverFactory +import scala.tools.util.PathResolver object ReflectMain extends Driver { private def classloaderFromSettings(settings: Settings) = { - val classPathURLs = PathResolverFactory.create(settings).resultAsURLs + val classPathURLs = new PathResolver(settings).resultAsURLs ScalaClassLoader.fromURLs(classPathURLs, getClass.getClassLoader) } diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index ae6a9e22b6..9c4d521336 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -117,13 +117,15 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => def transformDuringTyper(expr: Tree, mode: scala.reflect.internal.Mode, withImplicitViewsDisabled: Boolean, withMacrosDisabled: Boolean)(transform: (analyzer.Typer, Tree) => Tree): Tree = { def withWrapping(tree: Tree)(op: Tree => Tree) = if (mode == TERMmode) wrappingIntoTerm(tree)(op) else op(tree) - withWrapping(verify(expr))(expr1 => { + withWrapping(verify(expr)) { expr => // need to extract free terms, because otherwise you won't be able to typecheck macros against something that contains them - val exprAndFreeTerms = extractFreeTerms(expr1, wrapFreeTermRefs = false) - var expr2 = exprAndFreeTerms._1 - val freeTerms = exprAndFreeTerms._2 - val dummies = freeTerms.map{ case (freeTerm, name) => ValDef(NoMods, name, TypeTree(freeTerm.info), Select(Ident(PredefModule), newTermName("$qmark$qmark$qmark"))) }.toList - expr2 = Block(dummies, expr2) + val (extracted, freeTerms) = extractFreeTerms(expr, wrapFreeTermRefs = false) + val exprBound = { + val binders = freeTerms.toList.map { case (freeTerm, name) => + ValDef(NoMods, name, TypeTree(freeTerm.info), Select(Ident(PredefModule), newTermName("$qmark$qmark$qmark"))) + } + Block(binders, extracted) + } // !!! Why is this is in the empty package? If it's only to make // it inaccessible then please put it somewhere designed for that @@ -131,26 +133,29 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => // [Eugene] how can we implement that? val ownerClass = rootMirror.EmptyPackageClass.newClassSymbol(newTypeName("<expression-owner>")) build.setInfo(ownerClass, ClassInfoType(List(ObjectTpe), newScope, ownerClass)) - val owner = ownerClass.newLocalDummy(expr2.pos) - val currentTyper = analyzer.newTyper(analyzer.rootContext(NoCompilationUnit, EmptyTree).make(expr2, owner)) - val withImplicitFlag = if (!withImplicitViewsDisabled) (currentTyper.context.withImplicitsEnabled[Tree] _) else (currentTyper.context.withImplicitsDisabled[Tree] _) - val withMacroFlag = if (!withMacrosDisabled) (currentTyper.context.withMacrosEnabled[Tree] _) else (currentTyper.context.withMacrosDisabled[Tree] _) - def withContext (tree: => Tree) = withImplicitFlag(withMacroFlag(tree)) + val owner = ownerClass.newLocalDummy(exprBound.pos) + val currentTyper = analyzer.newTyper(analyzer.rootContext(NoCompilationUnit, EmptyTree).make(exprBound, owner)) + import currentTyper.{context => currCtx} val run = new Run run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled globalPhase = run.typerPhase // amazing... looks like phase and globalPhase are different things, so we need to set them separately - currentTyper.context.initRootContext() // need to manually set context mode, otherwise typer.silent will throw exceptions + currCtx.initRootContext() // need to manually set context mode, otherwise typer.silent will throw exceptions reporter.reset() - val expr3 = withContext(transform(currentTyper, expr2)) - var (dummies1, result) = expr3 match { - case Block(dummies, result) => ((dummies, result)) - case result => ((Nil, result)) - } + val (binders, transformed) = + currCtx.withImplicits(enabled = !withImplicitViewsDisabled) { + currCtx.withMacros(enabled = !withMacrosDisabled) { + transform(currentTyper, exprBound) + } + } match { + case Block(binders, transformed) => (binders, transformed) + case transformed => (Nil, transformed) + } + val invertedIndex = freeTerms map (_.swap) - result = new Transformer { + val indexed = new Transformer { override def transform(tree: Tree): Tree = tree match { case Ident(name: TermName) if invertedIndex contains name => @@ -158,10 +163,10 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => case _ => super.transform(tree) } - }.transform(result) - new TreeTypeSubstituter(dummies1 map (_.symbol), dummies1 map (dummy => SingleType(NoPrefix, invertedIndex(dummy.symbol.name.toTermName)))).traverse(result) - result - }) + }.transform(transformed) + new TreeTypeSubstituter(binders map (_.symbol), binders map (b => SingleType(NoPrefix, invertedIndex(b.symbol.name.toTermName)))).traverse(indexed) + indexed + } } def typecheck(expr: Tree, pt: Type, mode: scala.reflect.internal.Mode, silent: Boolean, withImplicitViewsDisabled: Boolean, withMacrosDisabled: Boolean): Tree = diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala index 42b939f18b..c351b6ace1 100644 --- a/src/compiler/scala/tools/util/PathResolver.scala +++ b/src/compiler/scala/tools/util/PathResolver.scala @@ -7,18 +7,13 @@ package scala package tools package util -import scala.language.postfixOps - import java.net.URL import scala.tools.reflect.WrappedProperties.AccessControl import scala.tools.nsc.Settings -import scala.tools.nsc.util.{ ClassFileLookup, ClassPath, JavaClassPath } -import scala.reflect.io.{ File, Directory, Path, AbstractFile } -import scala.reflect.runtime.ReflectionUtils -import ClassPath.{ JavaContext, DefaultJavaContext, split } +import scala.tools.nsc.util.ClassPath +import scala.reflect.io.{Directory, File, Path} import PartialFunction.condOpt -import scala.tools.nsc.classpath.{ AggregateFlatClassPath, ClassPathFactory, FlatClassPath, FlatClassPathFactory } -import scala.tools.nsc.settings.ClassPathRepresentationType +import scala.tools.nsc.classpath._ // Loosely based on the draft specification at: // https://wiki.scala-lang.org/display/SIW/Classpath @@ -43,7 +38,7 @@ object PathResolver { } /** pretty print class path */ - def ppcp(s: String) = split(s) match { + def ppcp(s: String) = ClassPath.split(s) match { case Nil => "" case Seq(x) => x case xs => xs.mkString(EOL, EOL, "") @@ -167,19 +162,6 @@ object PathResolver { |}""".asLines } - // used in PathResolver constructor - private object NoImplClassJavaContext extends JavaContext { - override def isValidName(name: String): Boolean = - !ReflectionUtils.scalacShouldntLoadClassfile(name) - } - - @deprecated("This method is no longer used be scalap and will be deleted", "2.11.5") - def fromPathString(path: String, context: JavaContext = DefaultJavaContext): JavaClassPath = { - val s = new Settings() - s.classpath.value = path - new PathResolver(s, context).result - } - /** With no arguments, show the interesting values in Environment and Defaults. * If there are arguments, show those in Calculated as if those options had been * given to a scala runner. @@ -191,28 +173,19 @@ object PathResolver { } else { val settings = new Settings() val rest = settings.processArguments(args.toList, processAll = false)._2 - val pr = PathResolverFactory.create(settings) + val pr = new PathResolver(settings) println("COMMAND: 'scala %s'".format(args.mkString(" "))) println("RESIDUAL: 'scala %s'\n".format(rest.mkString(" "))) pr.result match { - case cp: JavaClassPath => - cp.show() - case cp: AggregateFlatClassPath => + case cp: AggregateClassPath => println(s"ClassPath has ${cp.aggregates.size} entries and results in:\n${cp.asClassPathStrings}") } } } -trait PathResolverResult { - def result: ClassFileLookup[AbstractFile] - - def resultAsURLs: Seq[URL] = result.asURLs -} - -abstract class PathResolverBase[BaseClassPathType <: ClassFileLookup[AbstractFile], ResultClassPathType <: BaseClassPathType] -(settings: Settings, classPathFactory: ClassPathFactory[BaseClassPathType]) - extends PathResolverResult { +final class PathResolver(settings: Settings) { + private val classPathFactory = new ClassPathFactory(settings) import PathResolver.{ AsLines, Defaults, ppcp } @@ -260,7 +233,7 @@ abstract class PathResolverBase[BaseClassPathType <: ClassFileLookup[AbstractFil import classPathFactory._ // Assemble the elements! - def basis = List[Traversable[BaseClassPathType]]( + def basis = List[Traversable[ClassPath]]( classesInPath(javaBootClassPath), // 1. The Java bootstrap class path. contentsOfDirsInPath(javaExtDirs), // 2. The Java extension class path. classesInExpandedPath(javaUserClassPath), // 3. The Java application class path. @@ -291,7 +264,7 @@ abstract class PathResolverBase[BaseClassPathType <: ClassFileLookup[AbstractFil import PathResolver.MkLines - def result: ResultClassPathType = { + def result: ClassPath = { val cp = computeResult() if (settings.Ylogcp) { Console print f"Classpath built from ${settings.toConciseString} %n" @@ -304,37 +277,11 @@ abstract class PathResolverBase[BaseClassPathType <: ClassFileLookup[AbstractFil cp } + def resultAsURLs: Seq[URL] = result.asURLs + @deprecated("Use resultAsURLs instead of this one", "2.11.5") def asURLs: List[URL] = resultAsURLs.toList - protected def computeResult(): ResultClassPathType + private def computeResult(): ClassPath = AggregateClassPath(containers.toIndexedSeq) } -class PathResolver(settings: Settings, context: JavaContext) - extends PathResolverBase[ClassPath[AbstractFile], JavaClassPath](settings, context) { - - def this(settings: Settings) = - this(settings, - if (settings.YnoLoadImplClass) PathResolver.NoImplClassJavaContext - else DefaultJavaContext) - - override protected def computeResult(): JavaClassPath = - new JavaClassPath(containers.toIndexedSeq, context) -} - -class FlatClassPathResolver(settings: Settings, flatClassPathFactory: ClassPathFactory[FlatClassPath]) - extends PathResolverBase[FlatClassPath, AggregateFlatClassPath](settings, flatClassPathFactory) { - - def this(settings: Settings) = this(settings, new FlatClassPathFactory(settings)) - - override protected def computeResult(): AggregateFlatClassPath = AggregateFlatClassPath(containers.toIndexedSeq) -} - -object PathResolverFactory { - - def create(settings: Settings): PathResolverResult = - settings.YclasspathImpl.value match { - case ClassPathRepresentationType.Flat => new FlatClassPathResolver(settings) - case ClassPathRepresentationType.Recursive => new PathResolver(settings) - } -} diff --git a/src/eclipse/README.md b/src/eclipse/README.md index 3df7fbed7d..f67fa26e5e 100644 --- a/src/eclipse/README.md +++ b/src/eclipse/README.md @@ -7,21 +7,20 @@ The following points describe how to build Scala using Eclipse. 0. Download the [Scala IDE bundle](http://scala-ide.org/download/sdk.html). It comes preconfigured for optimal performance. -0. Run `ant init` to download some necessary jars. - -0. Import the project (in `src/eclipse`) via `File` → `Import Existing Projects` and navigate to `scala/src/eclipse`. Check all projects and click ok. - -0. You need to define a `path variable` inside Eclipse. Define `SCALA_BASEDIR` in -`Preferences/General/Workspace/Linked Resources`. The value should be the absolute -path to your Scala checkout. All paths in the project files are relative to this one, -so nothing will work before you do so. - - The same `SCALA_BASEDIR` variable needs to be defined as a `classpath variable` in +0. Run `ant build` to download some necessary jars and see a successful build. + +0. You need to define a `path variable` and a `classpath variable` inside Eclipse, both pointing to the Scala checkout directory: + - (experimental): run `./update-workspace.sh scala_checkout_dir [workspace_dir]`. This should update your workspace settings + (restart Eclipse if it was running). For example: + ``` + ./update-workspace.sh $HOME/git/scala ~/Documents/workspace-scalac + ``` + - If the above didn't work, you can perform these steps manually: Define `SCALA_BASEDIR` in `Preferences/General/Workspace/Linked Resources`. The value should be the absolute +path to your Scala checkout. All paths in the project files are relative to this one, so nothing will work before you do so. +The same `SCALA_BASEDIR` variable needs to be defined **also** as a `classpath variable` in `Java/Build Path/Classpath Variables`. - Additionally, we start using Maven dependencies (e.g. `JUnit`) so you need to define another -`classpath variable` inside Eclipse. Define `M2_REPO` in `Java/Build Path/Classpath Variables` -to point to your local Maven repository (e.g. `$HOME/.m2/repository`). +0. Import the project (in `src/eclipse`) via `File` → `Import Existing Projects` and navigate to `scala/src/eclipse`. Check all projects and click ok. Lastly, the JRE used by Eclipse needs to know the path to the `JLine` library, which is used by the REPL. To set the JAR file, navigate to `Java/Installed JREs`, select the default JRE, press `Edit/Add External JARs...` diff --git a/src/eclipse/interactive/.classpath b/src/eclipse/interactive/.classpath index 929ce65f2a..721351a207 100644 --- a/src/eclipse/interactive/.classpath +++ b/src/eclipse/interactive/.classpath @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="interactive"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/asm/scala-asm-5.0.4-scala-3.jar"/> <classpathentry combineaccessrules="false" kind="src" path="/scaladoc"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-compiler"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> - <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/asm/scala-asm-5.0.4-scala-3.jar"/> <classpathentry kind="output" path="build-quick-interactive"/> </classpath> diff --git a/src/eclipse/partest/.classpath b/src/eclipse/partest/.classpath index 63f46f46cd..22afd65d43 100644 --- a/src/eclipse/partest/.classpath +++ b/src/eclipse/partest/.classpath @@ -1,14 +1,14 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="partest-extras"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/asm/scala-asm-5.0.4-scala-3.jar"/> <classpathentry combineaccessrules="false" kind="src" path="/repl"/> - <classpathentry kind="var" path="M2_REPO/com/googlecode/java-diff-utils/diffutils/1.3.0/diffutils-1.3.0.jar"/> - <classpathentry kind="var" path="M2_REPO/org/scala-sbt/test-interface/1.0/test-interface-1.0.jar"/> - <classpathentry kind="var" path="M2_REPO/org/scala-lang/modules/scala-partest_2.12.0-M2/1.0.9/scala-partest_2.12.0-M2-1.0.9.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/partest/diffutils-1.3.0.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/partest/test-interface-1.0.jar"/> <classpathentry kind="var" path="SCALA_BASEDIR/lib/ant/ant.jar"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-compiler"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> - <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/asm/scala-asm-5.0.4-scala-3.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/partest/scala-partest_2.12.0-M4-1.0.14.jar"/> <classpathentry kind="output" path="build-quick-partest-extras"/> </classpath> diff --git a/src/eclipse/reflect/.classpath b/src/eclipse/reflect/.classpath index 3f14621da7..ee6bcd47da 100644 --- a/src/eclipse/reflect/.classpath +++ b/src/eclipse/reflect/.classpath @@ -3,5 +3,6 @@ <classpathentry kind="src" path="reflect"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/> <classpathentry kind="output" path="build-quick-reflect"/> </classpath> diff --git a/src/eclipse/repl/.classpath b/src/eclipse/repl/.classpath index 085b58f668..682377adc9 100644 --- a/src/eclipse/repl/.classpath +++ b/src/eclipse/repl/.classpath @@ -1,10 +1,12 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="repl"/> - <classpathentry kind="var" path="M2_REPO/jline/jline/2.12.1/jline-2.12.1.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/asm/scala-asm-5.0.4-scala-3.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/repl/jline-2.12.1.jar"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-compiler"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/asm/scala-asm-5.0.4-scala-3.jar"/> + <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/> + <classpathentry combineaccessrules="false" kind="src" path="/interactive"/> <classpathentry kind="output" path="build-quick-repl"/> </classpath> diff --git a/src/eclipse/scala-compiler/.classpath b/src/eclipse/scala-compiler/.classpath index bbed3324c4..625b9b2e4b 100644 --- a/src/eclipse/scala-compiler/.classpath +++ b/src/eclipse/scala-compiler/.classpath @@ -4,7 +4,8 @@ <classpathentry combineaccessrules="false" exported="true" kind="src" path="/reflect"/> <classpathentry combineaccessrules="false" exported="true" kind="src" path="/scala-library"/> <classpathentry kind="var" path="SCALA_BASEDIR/lib/ant/ant.jar"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/asm/scala-asm-5.0.4-scala-3.jar"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/> <classpathentry kind="output" path="build-quick-compiler"/> </classpath> diff --git a/src/eclipse/scaladoc/.classpath b/src/eclipse/scaladoc/.classpath index 3a3ebf7799..b4450df4ef 100644 --- a/src/eclipse/scaladoc/.classpath +++ b/src/eclipse/scaladoc/.classpath @@ -2,13 +2,12 @@ <classpath> <classpathentry kind="src" path="scaladoc"/> <classpathentry kind="var" path="SCALA_BASEDIR/lib/ant/ant.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/asm/scala-asm-5.0.4-scala-3.jar"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-compiler"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> - <classpathentry combineaccessrules="false" kind="src" path="/partest-extras"/> - <classpathentry kind="var" path="M2_REPO/org/scala-lang/modules/scala-xml_2.12.0-M2/1.0.5/scala-xml_2.12.0-M2-1.0.5.jar"/> - <classpathentry kind="var" path="M2_REPO/org/scala-lang/modules/scala-parser-combinators_2.12.0-M2/1.0.4/scala-parser-combinators_2.12.0-M2-1.0.4.jar"/> - <classpathentry kind="var" path="M2_REPO/org/scala-lang/modules/scala-partest_2.12.0-M2/1.0.9/scala-partest_2.12.0-M2-1.0.9.jar"/> - <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/asm/scala-asm-5.0.4-scala-3.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/scaladoc/scala-xml_2.12.0-M4-1.0.5.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/scaladoc/scala-parser-combinators_2.12.0-M4-1.0.4.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/partest/scala-partest_2.12.0-M4-1.0.14.jar"/> <classpathentry kind="output" path="build-quick-scaladoc"/> </classpath> diff --git a/src/eclipse/test-junit/.classpath b/src/eclipse/test-junit/.classpath index 995d94aa91..3635c85112 100644 --- a/src/eclipse/test-junit/.classpath +++ b/src/eclipse/test-junit/.classpath @@ -1,16 +1,16 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="test-junit"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/asm/scala-asm-5.0.4-scala-3.jar"/> <classpathentry kind="var" path="SCALA_BASEDIR/lib/ant/ant.jar"/> - <classpathentry kind="var" path="M2_REPO/junit/junit/4.11/junit-4.11.jar"/> <classpathentry combineaccessrules="false" kind="src" path="/reflect"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-compiler"/> <classpathentry combineaccessrules="false" kind="src" path="/repl"/> - <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/asm/scala-asm-5.0.4-scala-3.jar"/> <classpathentry combineaccessrules="false" kind="src" path="/partest-extras"/> <classpathentry combineaccessrules="false" kind="src" path="/scaladoc"/> - <classpathentry kind="var" path="M2_REPO/org/scala-lang/modules/scala-xml_2.12.0-M2/1.0.5/scala-xml_2.12.0-M2-1.0.5.jar"/> + <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/scaladoc/scala-xml_2.12.0-M4-1.0.5.jar"/> + <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/> <classpathentry kind="output" path="build-test-junit"/> </classpath> diff --git a/src/eclipse/update-workspace.sh b/src/eclipse/update-workspace.sh new file mode 100755 index 0000000000..24382d1445 --- /dev/null +++ b/src/eclipse/update-workspace.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +function usage() { + echo "$0 scala_checkout_dir [workspace_dir]" + echo "\n Add necessary path variables to Eclipse workspace settings for Scalac to build" +} + +METADATA_DIR=`pwd`/.metadata + +if [ $# -lt 1 ]; then + echo "Need the Scala directory checkout as argument" + exit 1 +fi + +SCALA_DIR=$1 + +if [ ! -z $2 ]; then + METADATA_DIR=$2/.metadata +fi + +if [ ! -d $METADATA_DIR ]; then + echo "$METADATA_DIR is not a directory" + exit 1 +fi + +echo "Using metadata directory $METADATA_DIR and Scala checkout $SCALA_DIR" + +CORE_PREFS=$METADATA_DIR/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs +if [ ! -f $CORE_PREFS ]; then + echo "Couldn't find $CORE_PREFS. Is $METADATA_DIR an Eclipse workspace?" + exit 1 +fi +echo -e "Workspace preferences:\t$CORE_PREFS" + +JDT_PREFS=$METADATA_DIR/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.jdt.core.prefs +if [ ! -f $JDT_PREFS ]; then + echo "Couldn't find $JDT_PREFS. Creating fresh file." + touch $JDT_PREFS +fi +echo -e "JDT preferences:\t$JDT_PREFS" + +# $1 - preference file (will be backed-up before writing) +# $2 - preference key +# $3 - preference value +function updatePref() { + mv $1 ${1}_backup + + awk -v key=$2 -v value=$3 ' + BEGIN { + FS="="; + OFS="="; + prev="" + } + { + if ($1 == key) { + prev=$2 + $2=value + } + print + } + END { + if (prev) { + printf "Updated existing value from %s to %s\n", prev, value > "/dev/stderr" + } else { + print key,value + } + } + ' ${1}_backup >$1 +} + +updatePref $CORE_PREFS "pathvariable.SCALA_BASEDIR" $SCALA_DIR +updatePref $JDT_PREFS "org.eclipse.jdt.core.classpathVariable.SCALA_BASEDIR" $SCALA_DIR diff --git a/src/intellij/README.md b/src/intellij/README.md index 8717003441..41fef04183 100644 --- a/src/intellij/README.md +++ b/src/intellij/README.md @@ -1,20 +1,82 @@ -# Building Scala using IntelliJ IDEA - -## Requirements +# Developing Scala in IntelliJ IDEA Use the latest IntelliJ release and install the Scala plugin from within the IDE. -## Initial setup +## Initial Setup + +To create the IntelliJ project files: + + - Run `sbt intellij` + - Open `src/intellij/scala.ipr` in IntelliJ + - In `File` → `Project Structure` → `Project` → `Project SDK`, create an SDK entry + named "1.8" containing the Java 1.8 SDK (1.6 if you're on the Scala the 2.11.x branch) + +The project files are created as copies of the `.SAMPLE` files, which are under version +control. The actual IntelliJ project files are in `.gitignore` so that local changes +are ignored. + +## Dependencies + +For every module in the IntelliJ project there is a corresponding `-deps` library, for exmaple `compiler-deps` provides `ant.jar` for the compiler codebase. +The `.jar` files in these `-deps` libraries can be easily kept up-to-date by running `sbt intellij` again. +This is necessary whenever the dependencies in the sbt build change, for example when the `starr` version is updated. + +Note that this command only patches the dependency lists, all other settings in the IntelliJ project definition are unchanged. +To overwrite the project definition files by copying the `.SAMPLE` files again run `sbt intellijFromSample`. + +## Switching Branches + +The 2.12.x branch contains IntelliJ module files for `actors` and `forkjoin` even though these modules only exist in 2.11.x. +This allows using the same IntelliJ project files when switching to the 2.11.x branch (without causing any issues while working on 2.12.x). + +When switching between 2.11.x and 2.12.x, make sure to run `sbt intellij`. +Note that the `Project SDK` is not updated in this process. +If you want to use the Java 1.6 SDK while working on 2.11.x you need to change it manually (`File` → `Project Structure` → `Project` → `Project SDK`). + +If you switch between 2.11.x and 2.12.x often, it makes sense to have a separate clone +of the repository for each branch. + +## Incremental Compilation + +Run `Build` → `Make Project` to build all modules of the Scala repository (library, +compiler, etc). Note that compilation IntelliJ is performed in a single pass (no +bootstrap), like the sbt build. + +Note that the output directory when compiling in IntelliJ is the same as for the +sbt and (deprecated) ant builds. This allows building incrementally in IntelliJ +and directly use the changes using the command-line scripts in `build/quick/bin/`. + +## Running JUnit Tests + +JUnit tests can be executed by right-clicking on a test class or test method and +selecting "Run" or "Debug". The debugger will allow you to stop at breakpoints +within the Scala library. + +It is possible to invoke the Scala compiler from a JUnit test (passing the source +code as a string) and inspect the generated bytecode, see for example +`scala.issues.BytecodeTest`. Debugging such a test is an easy way to stop at +breakpoints within the Scala compiler. + +## Running the Compiler and REPL + +You can create run/debug configurations to run the compiler and REPL directly within +IntelliJ, which might accelerate development and debugging of the the compiler. -To create the IntelliJ project definition, +To debug the Scala codebase you can also use "Remote" debug configuration and pass +the corresponding arguments to the jvm running the compiler / program. - - Run `ant init`. This will download some JARs to `./build/deps`, which are included in IntelliJ's classpath. - - Run `./src/intellij/setup.sh`. - - Open `./src/intellij/scala.ipr` in IntelliJ. - - In `File` → `Project Structure` → `Project` → `Project SDK`, create an SDK entry named "1.8" containing the Java 1.8 SDK. +To run the compiler create an "Application" configuration with + - Main class: `scala.tools.nsc.Main` + - Program arguments: `-usejavacp -cp sandbox -d sandbox sandbox/Test.scala` + - Working directory: the path of your checkout + - Use classpath of module: `compiler` -## Usage +To run the REPL create an "Application" configuration with + - Main class: `scala.tools.nsc.MainGenericRunner` + - Program arguments: `-usejavacp` + - Working directory: the path of your checkout + - Use classpath of module: `repl` -Compiling, running, and debugging should all work. You can work on the compiler, the standard library, and other components as well. +## Updating the `.SAMPLE` files -Note that compilation within IntelliJ is performed in `-Dlocker.skip=1` mode. Code is compiled not by bootstrapping the current compiler sources, but simply by using the "STARR" (stable reference) compiler, as specified by `starr.version` in `versions.properties`. +The command `intellijToSample` overwrites the `.SAMPLE` files using the current project definition files. diff --git a/src/intellij/actors.iml.SAMPLE b/src/intellij/actors.iml.SAMPLE new file mode 100644 index 0000000000..f012ee7b0f --- /dev/null +++ b/src/intellij/actors.iml.SAMPLE @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../build/quick/classes/actors" /> + <output-test url="file://$MODULE_DIR$/../../out/test/actors" /> + <exclude-output /> + <content url="file://$MODULE_DIR$/../actors"> + <sourceFolder url="file://$MODULE_DIR$/../actors" isTestSource="false" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="library" /> + <orderEntry type="module" module-name="forkjoin" /> + <orderEntry type="library" name="starr" level="project" /> + </component> +</module>
\ No newline at end of file diff --git a/src/intellij/asm.iml.SAMPLE b/src/intellij/asm.iml.SAMPLE deleted file mode 100644 index 9886154bdf..0000000000 --- a/src/intellij/asm.iml.SAMPLE +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> - <exclude-output /> - <content url="file://$MODULE_DIR$/../asm"> - <sourceFolder url="file://$MODULE_DIR$/../asm" isTestSource="false" /> - </content> - <orderEntry type="inheritedJdk" /> - <orderEntry type="sourceFolder" forTests="false" /> - </component> -</module>
\ No newline at end of file diff --git a/src/intellij/compiler.iml.SAMPLE b/src/intellij/compiler.iml.SAMPLE index 0e121925e6..1ebf409c1b 100644 --- a/src/intellij/compiler.iml.SAMPLE +++ b/src/intellij/compiler.iml.SAMPLE @@ -1,17 +1,17 @@ <?xml version="1.0" encoding="UTF-8"?> <module type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../build/quick/classes/compiler" /> + <output-test url="file://$MODULE_DIR$/../../out/test/compiler" /> <exclude-output /> <content url="file://$MODULE_DIR$/../compiler"> - <sourceFolder url="file://$MODULE_DIR$/../compiler/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/../compiler" isTestSource="false" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="module" module-name="library" /> <orderEntry type="module" module-name="reflect" /> + <orderEntry type="library" name="compiler-deps" level="project" /> <orderEntry type="library" name="starr" level="project" /> - <orderEntry type="library" name="ant" level="project" /> - <orderEntry type="library" name="asm" level="project" /> </component> </module>
\ No newline at end of file diff --git a/src/intellij/diff.sh b/src/intellij/diff.sh deleted file mode 100755 index 54f9248608..0000000000 --- a/src/intellij/diff.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# -# Diffs the SAMPLE files against the working project config. -# -export SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )" -for f in "$SCRIPT_DIR"/*.{iml,ipr}; do - echo $f; diff -u $f.SAMPLE $f; -done diff --git a/src/intellij/forkjoin.iml.SAMPLE b/src/intellij/forkjoin.iml.SAMPLE new file mode 100644 index 0000000000..aa3f83e56e --- /dev/null +++ b/src/intellij/forkjoin.iml.SAMPLE @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../build/quick/classes/forkjoin" /> + <output-test url="file://$MODULE_DIR$/../../out/test/forkjoin" /> + <exclude-output /> + <content url="file://$MODULE_DIR$/../forkjoin"> + <sourceFolder url="file://$MODULE_DIR$/../forkjoin" isTestSource="false" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module>
\ No newline at end of file diff --git a/src/intellij/interactive.iml.SAMPLE b/src/intellij/interactive.iml.SAMPLE index 267bd3f12b..05b4e162db 100644 --- a/src/intellij/interactive.iml.SAMPLE +++ b/src/intellij/interactive.iml.SAMPLE @@ -1,16 +1,18 @@ <?xml version="1.0" encoding="UTF-8"?> <module type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../build/quick/classes/interactive" /> + <output-test url="file://$MODULE_DIR$/../../out/test/interactive" /> <exclude-output /> <content url="file://$MODULE_DIR$/../interactive"> <sourceFolder url="file://$MODULE_DIR$/../interactive" isTestSource="false" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="module" module-name="compiler" /> <orderEntry type="module" module-name="library" /> <orderEntry type="module" module-name="reflect" /> + <orderEntry type="module" module-name="compiler" /> + <orderEntry type="library" name="interactive-deps" level="project" /> <orderEntry type="library" name="starr" level="project" /> - <orderEntry type="library" name="asm" level="project" /> </component> </module>
\ No newline at end of file diff --git a/src/intellij/test-junit.iml.SAMPLE b/src/intellij/junit.iml.SAMPLE index 326c9813aa..e0f52aa42a 100644 --- a/src/intellij/test-junit.iml.SAMPLE +++ b/src/intellij/junit.iml.SAMPLE @@ -1,21 +1,24 @@ <?xml version="1.0" encoding="UTF-8"?> <module type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../build/quick/classes/junit" /> + <output-test url="file://$MODULE_DIR$/../../out/test/junit" /> <exclude-output /> <content url="file://$MODULE_DIR$/../../test/junit"> <sourceFolder url="file://$MODULE_DIR$/../../test/junit" isTestSource="true" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="module" module-name="compiler" /> + <orderEntry type="module" module-name="forkjoin" /> <orderEntry type="module" module-name="library" /> - <orderEntry type="module" module-name="partest-extras" /> + <orderEntry type="module" module-name="actors" /> <orderEntry type="module" module-name="reflect" /> + <orderEntry type="module" module-name="compiler" /> <orderEntry type="module" module-name="repl" /> + <orderEntry type="module" module-name="interactive" /> <orderEntry type="module" module-name="scaladoc" /> - <orderEntry type="library" name="scaladoc-deps" level="project" /> - <orderEntry type="library" name="junit" level="project" /> + <orderEntry type="module" module-name="partest-extras" /> + <orderEntry type="library" name="junit-deps" level="project" /> <orderEntry type="library" name="starr" level="project" /> - <orderEntry type="library" name="asm" level="project" /> </component> </module>
\ No newline at end of file diff --git a/src/intellij/library.iml.SAMPLE b/src/intellij/library.iml.SAMPLE index 8ceb9dd3f1..ce61c097bd 100644 --- a/src/intellij/library.iml.SAMPLE +++ b/src/intellij/library.iml.SAMPLE @@ -1,13 +1,15 @@ <?xml version="1.0" encoding="UTF-8"?> <module type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../build/quick/classes/library" /> + <output-test url="file://$MODULE_DIR$/../../out/test/library" /> <exclude-output /> <content url="file://$MODULE_DIR$/../library"> - <sourceFolder url="file://$MODULE_DIR$/../library/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/../library" isTestSource="false" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="forkjoin" /> <orderEntry type="library" name="starr" level="project" /> </component> </module>
\ No newline at end of file diff --git a/src/intellij/manual.iml.SAMPLE b/src/intellij/manual.iml.SAMPLE index 97bfb5940a..a2ef6e4625 100644 --- a/src/intellij/manual.iml.SAMPLE +++ b/src/intellij/manual.iml.SAMPLE @@ -8,8 +8,7 @@ <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="module" module-name="library" /> - <orderEntry type="library" name="scaladoc-deps" level="project" /> - <orderEntry type="library" name="ant" level="project" /> + <orderEntry type="library" name="manual-deps" level="project" /> <orderEntry type="library" name="starr" level="project" /> </component> </module>
\ No newline at end of file diff --git a/src/intellij/partest-extras.iml.SAMPLE b/src/intellij/partest-extras.iml.SAMPLE index 1cd712184b..79c736f7da 100644 --- a/src/intellij/partest-extras.iml.SAMPLE +++ b/src/intellij/partest-extras.iml.SAMPLE @@ -1,18 +1,19 @@ <?xml version="1.0" encoding="UTF-8"?> <module type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../build/quick/classes/partest-extras" /> + <output-test url="file://$MODULE_DIR$/../../out/test/partest-extras" /> <exclude-output /> <content url="file://$MODULE_DIR$/../partest-extras"> <sourceFolder url="file://$MODULE_DIR$/../partest-extras" isTestSource="false" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="module" module-name="compiler" /> <orderEntry type="module" module-name="library" /> <orderEntry type="module" module-name="reflect" /> + <orderEntry type="module" module-name="compiler" /> <orderEntry type="module" module-name="repl" /> - <orderEntry type="library" name="partest" level="project" /> + <orderEntry type="library" name="partest-extras-deps" level="project" /> <orderEntry type="library" name="starr" level="project" /> - <orderEntry type="library" name="asm" level="project" /> </component> </module>
\ No newline at end of file diff --git a/src/intellij/partest-javaagent.iml.SAMPLE b/src/intellij/partest-javaagent.iml.SAMPLE index ffc540cdb9..22c2cbf1bc 100644 --- a/src/intellij/partest-javaagent.iml.SAMPLE +++ b/src/intellij/partest-javaagent.iml.SAMPLE @@ -1,12 +1,14 @@ <?xml version="1.0" encoding="UTF-8"?> <module type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../build/quick/classes/partest-javaagent" /> + <output-test url="file://$MODULE_DIR$/../../out/test/partest-javaagent" /> <exclude-output /> <content url="file://$MODULE_DIR$/../partest-javaagent"> <sourceFolder url="file://$MODULE_DIR$/../partest-javaagent" isTestSource="false" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" name="asm" level="project" /> + <orderEntry type="library" name="partest-javaagent-deps" level="project" /> </component> </module>
\ No newline at end of file diff --git a/src/intellij/reflect.iml.SAMPLE b/src/intellij/reflect.iml.SAMPLE index c9b7130aef..d0aba81f0b 100644 --- a/src/intellij/reflect.iml.SAMPLE +++ b/src/intellij/reflect.iml.SAMPLE @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <module type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../build/quick/classes/reflect" /> + <output-test url="file://$MODULE_DIR$/../../out/test/reflect" /> <exclude-output /> <content url="file://$MODULE_DIR$/../reflect"> <sourceFolder url="file://$MODULE_DIR$/../reflect" isTestSource="false" /> diff --git a/src/intellij/repl-jline.iml.SAMPLE b/src/intellij/repl-jline.iml.SAMPLE new file mode 100644 index 0000000000..b765a58d96 --- /dev/null +++ b/src/intellij/repl-jline.iml.SAMPLE @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../build/quick/classes/repl-jline" /> + <output-test url="file://$MODULE_DIR$/../../out/test/repl-jline" /> + <exclude-output /> + <content url="file://$MODULE_DIR$/../repl-jline"> + <sourceFolder url="file://$MODULE_DIR$/../repl-jline" isTestSource="false" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="library" /> + <orderEntry type="module" module-name="reflect" /> + <orderEntry type="module" module-name="compiler" /> + <orderEntry type="module" module-name="interactive" /> + <orderEntry type="module" module-name="repl" /> + <orderEntry type="library" name="repl-jline-deps" level="project" /> + <orderEntry type="library" name="starr" level="project" /> + </component> +</module>
\ No newline at end of file diff --git a/src/intellij/repl.iml.SAMPLE b/src/intellij/repl.iml.SAMPLE index e827a2c6d7..07e2923726 100644 --- a/src/intellij/repl.iml.SAMPLE +++ b/src/intellij/repl.iml.SAMPLE @@ -1,18 +1,19 @@ <?xml version="1.0" encoding="UTF-8"?> <module type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../build/quick/classes/repl" /> + <output-test url="file://$MODULE_DIR$/../../out/test/repl" /> <exclude-output /> <content url="file://$MODULE_DIR$/../repl"> <sourceFolder url="file://$MODULE_DIR$/../repl" isTestSource="false" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="module" module-name="compiler" /> <orderEntry type="module" module-name="library" /> <orderEntry type="module" module-name="reflect" /> + <orderEntry type="module" module-name="compiler" /> <orderEntry type="module" module-name="interactive" /> - <orderEntry type="library" name="starr" level="project" /> <orderEntry type="library" name="repl-deps" level="project" /> - <orderEntry type="library" name="asm" level="project" /> + <orderEntry type="library" name="starr" level="project" /> </component> </module>
\ No newline at end of file diff --git a/src/intellij/scala-build.iml.SAMPLE b/src/intellij/scala-build.iml.SAMPLE index bf722e464f..b8f066a2ef 100644 --- a/src/intellij/scala-build.iml.SAMPLE +++ b/src/intellij/scala-build.iml.SAMPLE @@ -1,109 +1,20 @@ <?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.id="scalaz-build" external.linked.project.path="$MODULE_DIR$/../../project" external.root.project.path="$MODULE_DIR$/../.." external.system.id="SBT" sbt.imports="sbt._, Keys._, dsl._, _root_.com.typesafe.sbt.SbtPgp.autoImport._, _root_.sbt.plugins.IvyPlugin, _root_.sbt.plugins.JvmPlugin, _root_.sbt.plugins.CorePlugin, _root_.sbt.plugins.JUnitXmlReportPlugin, _root_.com.typesafe.sbt.SbtPgp" sbt.resolvers="https://repo1.maven.org/maven2/|maven|public, /Users/jason/.ivy2/cache|ivy|Local cache" type="SBT_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="false"> - <output url="file://$MODULE_DIR$/../../project/target/idea-classes" /> - <output-test url="file://$MODULE_DIR$/../../project/target/idea-test-classes" /> +<module external.linked.project.id="scala-build" external.linked.project.path="$MODULE_DIR$/../../project" external.root.project.path="$MODULE_DIR$/../.." external.system.id="SBT" sbt.imports="sbt._, Keys._, dsl._, _root_.sbt.plugins.IvyPlugin, _root_.sbt.plugins.JvmPlugin, _root_.sbt.plugins.CorePlugin, _root_.sbt.plugins.JUnitXmlReportPlugin" sbt.resolvers="https://repo1.maven.org/maven2/|maven|public, https://oss.sonatype.org/content/repositories/snapshots|maven|sonatype-snapshots, https://scala-ci.typesafe.com/artifactory/scala-release-temp/|maven|private-repo, $USER_HOME$/.ivy2/cache|ivy|Local cache" type="SBT_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$/../../project"> <sourceFolder url="file://$MODULE_DIR$/../../project" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/../../project/target/scala-2.10/sbt-0.13/src_managed/main" isTestSource="false" /> + <excludeFolder url="file://$MODULE_DIR$/../../project/project/project/target" /> <excludeFolder url="file://$MODULE_DIR$/../../project/project/target" /> - <excludeFolder url="file://$MODULE_DIR$/../../project/target" /> + <excludeFolder url="file://$MODULE_DIR$/../../project/target/config-classes" /> + <excludeFolder url="file://$MODULE_DIR$/../../project/target/resolution-cache" /> + <excludeFolder url="file://$MODULE_DIR$/../../project/target/scala-2.10/classes" /> + <excludeFolder url="file://$MODULE_DIR$/../../project/target/scala-2.10/sbt-0.13/classes" /> + <excludeFolder url="file://$MODULE_DIR$/../../project/target/streams" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="module-library"> - <library name="SBT: sbt-and-plugins"> - <CLASSES> - <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.commons/commons-lang3/jars/commons-lang3-3.3.2.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.pantsbuild/jarjar/jars/jarjar-1.6.0.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.6.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.6.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.ow2.asm/asm/jars/asm-5.0.4.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.ow2.asm/asm-commons/jars/asm-commons-5.0.4.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.ow2.asm/asm-tree/jars/asm-tree-5.0.4.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.maven/maven-plugin-api/jars/maven-plugin-api-3.3.3.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.maven/maven-model/jars/maven-model-3.3.3.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.codehaus.plexus/plexus-utils/jars/plexus-utils-3.0.20.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.maven/maven-artifact/jars/maven-artifact-3.3.3.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.eclipse.sisu/org.eclipse.sisu.plexus/eclipse-plugins/org.eclipse.sisu.plexus-0.3.0.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/javax.enterprise/cdi-api/jars/cdi-api-1.0.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/javax.annotation/jsr250-api/jars/jsr250-api-1.0.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/javax.inject/javax.inject/jars/javax.inject-1.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.eclipse.sisu/org.eclipse.sisu.inject/eclipse-plugins/org.eclipse.sisu.inject-0.3.0.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.codehaus.plexus/plexus-component-annotations/jars/plexus-component-annotations-1.5.5.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.codehaus.plexus/plexus-classworlds/bundles/plexus-classworlds-2.5.2.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/biz.aQute.bnd/biz.aQute.bnd/jars/biz.aQute.bnd-2.4.1.jar!/" /> - <root url="jar://$USER_HOME$/.sbt/boot/scala-2.10.5/lib/scala-library.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/sbt/jars/sbt-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/main/jars/main-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/actions/jars/actions-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/classpath/jars/classpath-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.sbt/boot/scala-2.10.5/lib/scala-compiler.jar!/" /> - <root url="jar://$USER_HOME$/.sbt/boot/scala-2.10.5/lib/scala-reflect.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/interface/jars/interface-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/io/jars/io-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/control/jars/control-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/launcher-interface/jars/launcher-interface-1.0.0-M1.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/completion/jars/completion-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/collections/jars/collections-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.11.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/api/jars/api-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/compiler-integration/jars/compiler-integration-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/incremental-compiler/jars/incremental-compiler-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/logging/jars/logging-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/process/jars/process-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/relation/jars/relation-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/compile/jars/compile-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/classfile/jars/classfile-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/persist/jars/persist-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-tools.sbinary/sbinary_2.10/jars/sbinary_2.10-0.4.2.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/compiler-ivy-integration/jars/compiler-ivy-integration-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/ivy/jars/ivy-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/cross/jars/cross-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt.ivy/ivy/jars/ivy-2.3.0-sbt-c5d1b95fdcc1e1007740ffbecf4eb07abc51ec93.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/com.jcraft/jsch/jars/jsch-0.1.46.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/serialization_2.10/jars/serialization_2.10-0.1.1.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-pickling_2.10/jars/scala-pickling_2.10-0.10.0.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scalamacros/quasiquotes_2.10/jars/quasiquotes_2.10-2.0.1.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.json4s/json4s-core_2.10/jars/json4s-core_2.10-3.2.10.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.json4s/json4s-ast_2.10/jars/json4s-ast_2.10-3.2.10.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/com.thoughtworks.paranamer/paranamer/jars/paranamer-2.6.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.spire-math/jawn-parser_2.10/jars/jawn-parser_2.10-0.6.0.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.spire-math/json4s-support_2.10/jars/json4s-support_2.10-0.6.0.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/run/jars/run-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/task-system/jars/task-system-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/tasks/jars/tasks-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/tracking/jars/tracking-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/cache/jars/cache-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/testing/jars/testing-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/test-agent/jars/test-agent-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/test-interface/jars/test-interface-1.0.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/main-settings/jars/main-settings-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/apply-macro/jars/apply-macro-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/command/jars/command-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/logic/jars/logic-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/compiler-interface/jars/compiler-interface-bin-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/compiler-interface/jars/compiler-interface-src-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/precompiled-2_8_2/jars/compiler-interface-bin-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/precompiled-2_9_2/jars/compiler-interface-bin-0.13.9.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/precompiled-2_9_3/jars/compiler-interface-bin-0.13.9.jar!/" /> - </CLASSES> - <JAVADOC /> - <SOURCES> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/main/srcs/main-0.13.9-sources.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang/scala-library/srcs/scala-library-2.10.5-sources.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang/scala-reflect/srcs/scala-reflect-2.10.5-sources.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang/scala-compiler/srcs/scala-compiler-2.10.5-sources.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/actions/srcs/actions-0.13.9-sources.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/sbt/srcs/sbt-0.13.9-sources.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/run/srcs/run-0.13.9-sources.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/test-interface/srcs/test-interface-1.0-sources.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/testing/srcs/testing-0.13.9-sources.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/tasks/srcs/tasks-0.13.9-sources.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/ivy/srcs/ivy-0.13.9-sources.jar!/" /> - <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/completion/srcs/completion-0.13.9-sources.jar!/" /> - </SOURCES> - </library> - </orderEntry> + <orderEntry type="library" name="scala-build-deps" level="project" /> </component> -</module>
\ No newline at end of file +</module> diff --git a/src/intellij/scala.iml.SAMPLE b/src/intellij/scala.iml.SAMPLE index 9e8718dd45..f1b2938016 100644 --- a/src/intellij/scala.iml.SAMPLE +++ b/src/intellij/scala.iml.SAMPLE @@ -1,9 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <module type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="true"> - <exclude-output /> <content url="file://$MODULE_DIR$/../.."> <excludeFolder url="file://$MODULE_DIR$/../../build" /> + <excludeFolder url="file://$MODULE_DIR$/../../build-sbt" /> + <excludeFolder url="file://$MODULE_DIR$/../../target" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> diff --git a/src/intellij/scala.ipr.SAMPLE b/src/intellij/scala.ipr.SAMPLE index e88d6ef257..79ad2808f6 100644 --- a/src/intellij/scala.ipr.SAMPLE +++ b/src/intellij/scala.ipr.SAMPLE @@ -20,6 +20,9 @@ </annotationProcessing> </component> <component name="CopyrightManager" default="" /> + <component name="Encoding"> + <file url="PROJECT" charset="UTF-8" /> + </component> <component name="EntryPointsManager"> <entry_points version="2.0" /> </component> @@ -35,23 +38,26 @@ </component> <component name="ProjectModuleManager"> <modules> + <module fileurl="file://$PROJECT_DIR$/actors.iml" filepath="$PROJECT_DIR$/actors.iml" /> <module fileurl="file://$PROJECT_DIR$/compiler.iml" filepath="$PROJECT_DIR$/compiler.iml" /> + <module fileurl="file://$PROJECT_DIR$/forkjoin.iml" filepath="$PROJECT_DIR$/forkjoin.iml" /> <module fileurl="file://$PROJECT_DIR$/interactive.iml" filepath="$PROJECT_DIR$/interactive.iml" /> + <module fileurl="file://$PROJECT_DIR$/junit.iml" filepath="$PROJECT_DIR$/junit.iml" /> <module fileurl="file://$PROJECT_DIR$/library.iml" filepath="$PROJECT_DIR$/library.iml" /> <module fileurl="file://$PROJECT_DIR$/manual.iml" filepath="$PROJECT_DIR$/manual.iml" /> <module fileurl="file://$PROJECT_DIR$/partest-extras.iml" filepath="$PROJECT_DIR$/partest-extras.iml" /> <module fileurl="file://$PROJECT_DIR$/partest-javaagent.iml" filepath="$PROJECT_DIR$/partest-javaagent.iml" /> <module fileurl="file://$PROJECT_DIR$/reflect.iml" filepath="$PROJECT_DIR$/reflect.iml" /> <module fileurl="file://$PROJECT_DIR$/repl.iml" filepath="$PROJECT_DIR$/repl.iml" /> + <module fileurl="file://$PROJECT_DIR$/repl-jline.iml" filepath="$PROJECT_DIR$/repl-jline.iml" /> <module fileurl="file://$PROJECT_DIR$/scala.iml" filepath="$PROJECT_DIR$/scala.iml" /> <module fileurl="file://$PROJECT_DIR$/scala-build.iml" filepath="$PROJECT_DIR$/scala-build.iml" /> <module fileurl="file://$PROJECT_DIR$/scaladoc.iml" filepath="$PROJECT_DIR$/scaladoc.iml" /> <module fileurl="file://$PROJECT_DIR$/scalap.iml" filepath="$PROJECT_DIR$/scalap.iml" /> <module fileurl="file://$PROJECT_DIR$/test.iml" filepath="$PROJECT_DIR$/test.iml" /> - <module fileurl="file://$PROJECT_DIR$/test-junit.iml" filepath="$PROJECT_DIR$/test-junit.iml" /> </modules> </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/../../out" /> </component> <component name="ScalaCompilerConfiguration"> @@ -64,64 +70,246 @@ <mapping directory="$PROJECT_DIR$/../.." vcs="Git" /> </component> <component name="libraryTable"> - <library name="ant"> + <library name="compiler-deps"> + <CLASSES> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-5.0.4-scala-3.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12.0-M3-dc9effe/bundles/scala-xml_2.12.0-M3-dc9effe-1.0.5.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.12.0-M3-dc9effe/bundles/scala-parser-combinators_2.12.0-M3-dc9effe-1.0.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.14.1.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + <library name="interactive-deps"> + <CLASSES> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-5.0.4-scala-3.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12.0-M3-dc9effe/bundles/scala-xml_2.12.0-M3-dc9effe-1.0.5.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.12.0-M3-dc9effe/bundles/scala-parser-combinators_2.12.0-M3-dc9effe-1.0.4.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + <library name="junit-deps"> <CLASSES> - <root url="jar://$PROJECT_DIR$/../../lib/ant/ant.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-5.0.4-scala-3.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12.0-M3-dc9effe/bundles/scala-xml_2.12.0-M3-dc9effe-1.0.5.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.12.0-M3-dc9effe/bundles/scala-parser-combinators_2.12.0-M3-dc9effe-1.0.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.14.1.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-partest_2.12.0-M3-dc9effe/jars/scala-partest_2.12.0-M3-dc9effe-1.0.13.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/com.googlecode.java-diff-utils/diffutils/jars/diffutils-1.3.0.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/test-interface/jars/test-interface-1.0.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/junit/junit/jars/junit-4.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.hamcrest/hamcrest-core/jars/hamcrest-core-1.3.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES /> </library> - <library name="asm"> + <library name="manual-deps"> <CLASSES> - <root url="file://$PROJECT_DIR$/../../build/deps/asm" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12.0-M3-dc9effe/bundles/scala-xml_2.12.0-M3-dc9effe-1.0.5.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.4.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES /> - <jarDirectory url="file://$PROJECT_DIR$/../../build/deps/asm" recursive="false" /> </library> - <library name="junit"> + <library name="partest-extras-deps"> <CLASSES> - <root url="file://$PROJECT_DIR$/../../build/deps/junit" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-5.0.4-scala-3.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12.0-M3-dc9effe/bundles/scala-xml_2.12.0-M3-dc9effe-1.0.5.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.12.0-M3-dc9effe/bundles/scala-parser-combinators_2.12.0-M3-dc9effe-1.0.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.14.1.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-partest_2.12.0-M3-dc9effe/jars/scala-partest_2.12.0-M3-dc9effe-1.0.13.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/com.googlecode.java-diff-utils/diffutils/jars/diffutils-1.3.0.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/test-interface/jars/test-interface-1.0.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES /> - <jarDirectory url="file://$PROJECT_DIR$/../../build/deps/junit" recursive="false" /> </library> - <library name="partest"> + <library name="partest-javaagent-deps"> <CLASSES> - <root url="file://$PROJECT_DIR$/../../build/deps/partest" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-5.0.4-scala-3.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES /> - <jarDirectory url="file://$PROJECT_DIR$/../../build/deps/partest" recursive="false" /> </library> <library name="repl-deps"> <CLASSES> - <root url="file://$PROJECT_DIR$/../../build/deps/repl" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-5.0.4-scala-3.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12.0-M3-dc9effe/bundles/scala-xml_2.12.0-M3-dc9effe-1.0.5.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.12.0-M3-dc9effe/bundles/scala-parser-combinators_2.12.0-M3-dc9effe-1.0.4.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + <library name="repl-jline-deps"> + <CLASSES> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-5.0.4-scala-3.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12.0-M3-dc9effe/bundles/scala-xml_2.12.0-M3-dc9effe-1.0.5.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.12.0-M3-dc9effe/bundles/scala-parser-combinators_2.12.0-M3-dc9effe-1.0.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.14.1.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + <library name="scala-build-deps"> + <CLASSES> + <root url="jar://$USER_HOME$/.ivy2/cache/scala_2.10/sbt_0.13/com.typesafe.sbt/sbt-git/jars/sbt-git-0.8.5.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.eclipse.jgit/org.eclipse.jgit.pgm/jars/org.eclipse.jgit.pgm-3.7.0.201502260915-r.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/args4j/args4j/jars/args4j-2.0.12.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.commons/commons-compress/jars/commons-compress-1.6.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.tukaani/xz/jars/xz-1.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.eclipse.jgit/org.eclipse.jgit.archive/jars/org.eclipse.jgit.archive-3.7.0.201502260915-r.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.eclipse.jgit/org.eclipse.jgit/jars/org.eclipse.jgit-3.7.0.201502260915-r.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/com.jcraft/jsch/jars/jsch-0.1.50.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/com.googlecode.javaewah/JavaEWAH/bundles/JavaEWAH-0.7.9.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.httpcomponents/httpclient/jars/httpclient-4.1.3.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.httpcomponents/httpcore/jars/httpcore-4.1.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/commons-logging/commons-logging/jars/commons-logging-1.1.1.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/commons-codec/commons-codec/jars/commons-codec-1.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.slf4j/slf4j-api/jars/slf4j-api-1.7.2.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.osgi/org.osgi.core/jars/org.osgi.core-4.3.1.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.eclipse.jgit/org.eclipse.jgit.ui/jars/org.eclipse.jgit.ui-3.7.0.201502260915-r.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.commons/commons-lang3/jars/commons-lang3-3.3.2.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.pantsbuild/jarjar/jars/jarjar-1.6.0.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.6.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.6.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.ow2.asm/asm/jars/asm-5.0.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.ow2.asm/asm-commons/jars/asm-commons-5.0.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.ow2.asm/asm-tree/jars/asm-tree-5.0.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.maven/maven-plugin-api/jars/maven-plugin-api-3.3.3.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.maven/maven-model/jars/maven-model-3.3.3.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.codehaus.plexus/plexus-utils/jars/plexus-utils-3.0.20.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.maven/maven-artifact/jars/maven-artifact-3.3.3.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.eclipse.sisu/org.eclipse.sisu.plexus/eclipse-plugins/org.eclipse.sisu.plexus-0.3.0.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/javax.enterprise/cdi-api/jars/cdi-api-1.0.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/javax.annotation/jsr250-api/jars/jsr250-api-1.0.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/javax.inject/javax.inject/jars/javax.inject-1.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.eclipse.sisu/org.eclipse.sisu.inject/eclipse-plugins/org.eclipse.sisu.inject-0.3.0.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.codehaus.plexus/plexus-component-annotations/jars/plexus-component-annotations-1.5.5.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.codehaus.plexus/plexus-classworlds/bundles/plexus-classworlds-2.5.2.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/biz.aQute.bnd/biz.aQute.bnd/jars/biz.aQute.bnd-2.4.1.jar!/" /> + <root url="jar://$USER_HOME$/.sbt/boot/scala-2.10.6/lib/scala-library.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/sbt/jars/sbt-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/main/jars/main-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/actions/jars/actions-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/classpath/jars/classpath-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.sbt/boot/scala-2.10.6/lib/scala-compiler.jar!/" /> + <root url="jar://$USER_HOME$/.sbt/boot/scala-2.10.6/lib/scala-reflect.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/interface/jars/interface-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/io/jars/io-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/control/jars/control-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/launcher-interface/jars/launcher-interface-1.0.0-M1.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/completion/jars/completion-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/collections/jars/collections-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.13.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.fusesource.jansi/jansi/jars/jansi-1.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/api/jars/api-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/classfile/jars/classfile-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/logging/jars/logging-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/process/jars/process-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/compiler-integration/jars/compiler-integration-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/incremental-compiler/jars/incremental-compiler-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/relation/jars/relation-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/compile/jars/compile-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/persist/jars/persist-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-tools.sbinary/sbinary_2.10/jars/sbinary_2.10-0.4.2.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/compiler-ivy-integration/jars/compiler-ivy-integration-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/ivy/jars/ivy-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/cross/jars/cross-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt.ivy/ivy/jars/ivy-2.3.0-sbt-2cc8d2761242b072cedb0a04cb39435c4fa24f9a.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/serialization_2.10/jars/serialization_2.10-0.1.2.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-pickling_2.10/jars/scala-pickling_2.10-0.10.1.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scalamacros/quasiquotes_2.10/jars/quasiquotes_2.10-2.0.1.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.json4s/json4s-core_2.10/jars/json4s-core_2.10-3.2.10.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.json4s/json4s-ast_2.10/jars/json4s-ast_2.10-3.2.10.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/com.thoughtworks.paranamer/paranamer/jars/paranamer-2.6.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.spire-math/jawn-parser_2.10/jars/jawn-parser_2.10-0.6.0.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.spire-math/json4s-support_2.10/jars/json4s-support_2.10-0.6.0.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/run/jars/run-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/task-system/jars/task-system-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/tasks/jars/tasks-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/tracking/jars/tracking-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/cache/jars/cache-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/testing/jars/testing-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/test-agent/jars/test-agent-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/test-interface/jars/test-interface-1.0.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/main-settings/jars/main-settings-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/apply-macro/jars/apply-macro-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/command/jars/command-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/logic/jars/logic-0.13.11.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/compiler-interface/jars/compiler-interface-0.13.11.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES /> - <jarDirectory url="file://$PROJECT_DIR$/../../build/deps/repl" recursive="false" /> </library> <library name="scaladoc-deps"> <CLASSES> - <root url="file://$PROJECT_DIR$/../../build/deps/scaladoc" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-5.0.4-scala-3.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12.0-M3-dc9effe/bundles/scala-xml_2.12.0-M3-dc9effe-1.0.5.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.12.0-M3-dc9effe/bundles/scala-parser-combinators_2.12.0-M3-dc9effe-1.0.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-partest_2.12.0-M3-dc9effe/jars/scala-partest_2.12.0-M3-dc9effe-1.0.13.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/com.googlecode.java-diff-utils/diffutils/jars/diffutils-1.3.0.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/test-interface/jars/test-interface-1.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + <library name="scalap-deps"> + <CLASSES> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-5.0.4-scala-3.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12.0-M3-dc9effe/bundles/scala-xml_2.12.0-M3-dc9effe-1.0.5.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.12.0-M3-dc9effe/bundles/scala-parser-combinators_2.12.0-M3-dc9effe-1.0.4.jar!/" /> </CLASSES> <JAVADOC /> <SOURCES /> - <jarDirectory url="file://$PROJECT_DIR$/../../build/deps/scaladoc" recursive="false" /> </library> <library name="starr" type="Scala"> <properties> + <option name="languageLevel" value="Scala_2_12" /> <compiler-classpath> - <root url="file://$PROJECT_DIR$/../../build/deps/starr/scala-compiler-#starr-version#.jar" /> - <root url="file://$PROJECT_DIR$/../../build/deps/starr/scala-library-#starr-version#.jar" /> - <root url="file://$PROJECT_DIR$/../../build/deps/starr/scala-reflect-#starr-version#.jar" /> + <root url="file://$USER_HOME$/.sbt/boot/scala-2.12.0-M3-dc9effe/lib/scala-library.jar" /> + <root url="file://$USER_HOME$/.sbt/boot/scala-2.12.0-M3-dc9effe/lib/scala-compiler.jar" /> + <root url="file://$USER_HOME$/.sbt/boot/scala-2.12.0-M3-dc9effe/lib/scala-parser-combinators_2.12.0-M3-dc9effe.jar" /> + <root url="file://$USER_HOME$/.sbt/boot/scala-2.12.0-M3-dc9effe/lib/scala-reflect.jar" /> + <root url="file://$USER_HOME$/.sbt/boot/scala-2.12.0-M3-dc9effe/lib/scala-xml_2.12.0-M3-dc9effe.jar" /> + <root url="file://$USER_HOME$/.sbt/boot/scala-2.12.0-M3-dc9effe/lib/jline.jar" /> </compiler-classpath> </properties> <CLASSES /> <JAVADOC /> <SOURCES /> </library> + <library name="test-deps"> + <CLASSES> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-5.0.4-scala-3.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12.0-M3-dc9effe/bundles/scala-xml_2.12.0-M3-dc9effe-1.0.5.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.12.0-M3-dc9effe/bundles/scala-parser-combinators_2.12.0-M3-dc9effe-1.0.4.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.14.1.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-partest_2.12.0-M3-dc9effe/jars/scala-partest_2.12.0-M3-dc9effe-1.0.13.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/com.googlecode.java-diff-utils/diffutils/jars/diffutils-1.3.0.jar!/" /> + <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/test-interface/jars/test-interface-1.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> </component> </project> diff --git a/src/intellij/scaladoc.iml.SAMPLE b/src/intellij/scaladoc.iml.SAMPLE index 6e6d98b396..9ab94c1bbb 100644 --- a/src/intellij/scaladoc.iml.SAMPLE +++ b/src/intellij/scaladoc.iml.SAMPLE @@ -1,19 +1,18 @@ <?xml version="1.0" encoding="UTF-8"?> <module type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../build/quick/classes/scaladoc" /> + <output-test url="file://$MODULE_DIR$/../../out/test/scaladoc" /> <exclude-output /> <content url="file://$MODULE_DIR$/../scaladoc"> <sourceFolder url="file://$MODULE_DIR$/../scaladoc" isTestSource="false" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="module" module-name="compiler" /> <orderEntry type="module" module-name="library" /> <orderEntry type="module" module-name="reflect" /> - <orderEntry type="library" name="starr" level="project" /> + <orderEntry type="module" module-name="compiler" /> <orderEntry type="library" name="scaladoc-deps" level="project" /> - <orderEntry type="library" name="ant" level="project" /> - <orderEntry type="library" name="partest" level="project" /> - <orderEntry type="library" name="asm" level="project" /> + <orderEntry type="library" name="starr" level="project" /> </component> </module>
\ No newline at end of file diff --git a/src/intellij/scalap.iml.SAMPLE b/src/intellij/scalap.iml.SAMPLE index 665aac07f8..dfe6892bd3 100644 --- a/src/intellij/scalap.iml.SAMPLE +++ b/src/intellij/scalap.iml.SAMPLE @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <module type="JAVA_MODULE" version="4"> - <component name="NewModuleRootManager" inherit-compiler-output="true"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../build/quick/classes/scalap" /> + <output-test url="file://$MODULE_DIR$/../../out/test/scalap" /> <exclude-output /> <content url="file://$MODULE_DIR$/../scalap"> <sourceFolder url="file://$MODULE_DIR$/../scalap" isTestSource="false" /> @@ -10,6 +12,7 @@ <orderEntry type="module" module-name="library" /> <orderEntry type="module" module-name="compiler" /> <orderEntry type="module" module-name="reflect" /> + <orderEntry type="library" name="scalap-deps" level="project" /> <orderEntry type="library" name="starr" level="project" /> </component> </module>
\ No newline at end of file diff --git a/src/intellij/setup.sh b/src/intellij/setup.sh deleted file mode 100755 index 251f717829..0000000000 --- a/src/intellij/setup.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -# -# Generates IntelliJ IDEA project files based on the checked-in samples. -# - -set -e -export SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )" -echo "About to delete .ipr and .iml files and replace with the .SAMPLE files. Press enter to continue or CTRL-C to cancel." -read - -for f in "$SCRIPT_DIR"/*.SAMPLE; do - g=${f%.SAMPLE} - cp $f $g -done - -STARR_VERSION="`cat $SCRIPT_DIR/../../versions.properties | grep 'starr.version' | awk '{split($0,a,"="); print a[2]}'`" -sed "s/#starr-version#/$STARR_VERSION/g" $SCRIPT_DIR/scala.ipr.SAMPLE > $SCRIPT_DIR/scala.ipr diff --git a/src/intellij/test.iml.SAMPLE b/src/intellij/test.iml.SAMPLE index d1f2975fbf..0253b539e7 100644 --- a/src/intellij/test.iml.SAMPLE +++ b/src/intellij/test.iml.SAMPLE @@ -7,14 +7,15 @@ </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="module" module-name="compiler" /> - <orderEntry type="module" module-name="interactive" /> + <orderEntry type="module" module-name="forkjoin" /> <orderEntry type="module" module-name="library" /> - <orderEntry type="module" module-name="partest-extras" /> + <orderEntry type="module" module-name="actors" /> <orderEntry type="module" module-name="reflect" /> + <orderEntry type="module" module-name="compiler" /> <orderEntry type="module" module-name="repl" /> - <orderEntry type="library" name="partest" level="project" /> - <orderEntry type="library" name="scaladoc-deps" level="project" /> - <orderEntry type="library" name="asm" level="project" /> + <orderEntry type="module" module-name="interactive" /> + <orderEntry type="module" module-name="scaladoc" /> + <orderEntry type="module" module-name="partest-extras" /> + <orderEntry type="library" name="test-deps" level="project" /> </component> </module>
\ No newline at end of file diff --git a/src/intellij/update.sh b/src/intellij/update.sh deleted file mode 100755 index eb6fea782f..0000000000 --- a/src/intellij/update.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -# -# Updates the .SAMPLE files with the current project files. -# - -set -e -export SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )" - -echo "About to create overwrite the .ipr.SAMPLE and .iml.SAMPLE files with the current project files. Press enter to continue or CTRL-C to cancel." -read - -for f in "$SCRIPT_DIR"/*.{iml,ipr}; do - cp $f $f.SAMPLE -done - -for f in "$SCRIPT_DIR"/*.SAMPLE; do - g=${f%.SAMPLE} - if [[ ! -f $g ]]; then - echo "Stale sample file, deleting $f" - rm $f - fi -done diff --git a/src/interactive/scala/tools/nsc/interactive/Pickler.scala b/src/interactive/scala/tools/nsc/interactive/Pickler.scala index c0446b0402..ffd3b7bc64 100644 --- a/src/interactive/scala/tools/nsc/interactive/Pickler.scala +++ b/src/interactive/scala/tools/nsc/interactive/Pickler.scala @@ -1,7 +1,5 @@ package scala.tools.nsc.interactive -import scala.language.implicitConversions - import Lexer._ import java.io.Writer diff --git a/src/library/rootdoc.txt b/src/library/rootdoc.txt index 95f9836cc9..d78df01046 100644 --- a/src/library/rootdoc.txt +++ b/src/library/rootdoc.txt @@ -37,7 +37,7 @@ Notable packages include: - [[scala.sys `scala.sys`]] - Interaction with other processes and the operating system - [[scala.util.matching `scala.util.matching`]] - [[scala.util.matching.Regex Regular expressions]] -Other packages exist. See the complete list on the left. +Other packages exist. See the complete list on the right. Additional parts of the standard library are shipped as separate libraries. These include: diff --git a/src/library/scala/Boolean.scala b/src/library/scala/Boolean.scala index 53b4fb2af2..017f10a283 100644 --- a/src/library/scala/Boolean.scala +++ b/src/library/scala/Boolean.scala @@ -7,8 +7,8 @@ \* */ // DO NOT EDIT, CHANGES WILL BE LOST -// This auto-generated code can be modified in scala.tools.cmd.gen. -// Afterwards, running tools/codegen-anyvals regenerates this source file. +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. package scala @@ -102,7 +102,8 @@ final abstract class Boolean private extends AnyVal { */ def ^(x: Boolean): Boolean - override def getClass(): Class[Boolean] = null + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Boolean] = ??? } object Boolean extends AnyValCompanion { @@ -114,7 +115,7 @@ object Boolean extends AnyValCompanion { * @param x the Boolean to be boxed * @return a java.lang.Boolean offering `x` as its underlying value. */ - def box(x: Boolean): java.lang.Boolean = java.lang.Boolean.valueOf(x) + def box(x: Boolean): java.lang.Boolean = ??? /** Transform a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw @@ -126,7 +127,7 @@ object Boolean extends AnyValCompanion { * @throws ClassCastException if the argument is not a java.lang.Boolean * @return the Boolean resulting from calling booleanValue() on `x` */ - def unbox(x: java.lang.Object): Boolean = x.asInstanceOf[java.lang.Boolean].booleanValue() + def unbox(x: java.lang.Object): Boolean = ??? /** The String representation of the scala.Boolean companion object. */ override def toString = "object scala.Boolean" diff --git a/src/library/scala/Byte.scala b/src/library/scala/Byte.scala index fb662911b3..3709586f2e 100644 --- a/src/library/scala/Byte.scala +++ b/src/library/scala/Byte.scala @@ -7,8 +7,8 @@ \* */ // DO NOT EDIT, CHANGES WILL BE LOST -// This auto-generated code can be modified in scala.tools.cmd.gen. -// Afterwards, running tools/codegen-anyvals regenerates this source file. +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. package scala @@ -434,7 +434,8 @@ final abstract class Byte private extends AnyVal { /** Returns the remainder of the division of this value by `x`. */ def %(x: Double): Double - override def getClass(): Class[Byte] = null + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Byte] = ??? } object Byte extends AnyValCompanion { @@ -451,7 +452,7 @@ object Byte extends AnyValCompanion { * @param x the Byte to be boxed * @return a java.lang.Byte offering `x` as its underlying value. */ - def box(x: Byte): java.lang.Byte = java.lang.Byte.valueOf(x) + def box(x: Byte): java.lang.Byte = ??? /** Transform a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw @@ -463,7 +464,7 @@ object Byte extends AnyValCompanion { * @throws ClassCastException if the argument is not a java.lang.Byte * @return the Byte resulting from calling byteValue() on `x` */ - def unbox(x: java.lang.Object): Byte = x.asInstanceOf[java.lang.Byte].byteValue() + def unbox(x: java.lang.Object): Byte = ??? /** The String representation of the scala.Byte companion object. */ override def toString = "object scala.Byte" diff --git a/src/library/scala/Char.scala b/src/library/scala/Char.scala index 9f06503569..7dbb0209c3 100644 --- a/src/library/scala/Char.scala +++ b/src/library/scala/Char.scala @@ -7,8 +7,8 @@ \* */ // DO NOT EDIT, CHANGES WILL BE LOST -// This auto-generated code can be modified in scala.tools.cmd.gen. -// Afterwards, running tools/codegen-anyvals regenerates this source file. +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. package scala @@ -434,7 +434,8 @@ final abstract class Char private extends AnyVal { /** Returns the remainder of the division of this value by `x`. */ def %(x: Double): Double - override def getClass(): Class[Char] = null + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Char] = ??? } object Char extends AnyValCompanion { @@ -451,7 +452,7 @@ object Char extends AnyValCompanion { * @param x the Char to be boxed * @return a java.lang.Character offering `x` as its underlying value. */ - def box(x: Char): java.lang.Character = java.lang.Character.valueOf(x) + def box(x: Char): java.lang.Character = ??? /** Transform a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw @@ -463,7 +464,7 @@ object Char extends AnyValCompanion { * @throws ClassCastException if the argument is not a java.lang.Character * @return the Char resulting from calling charValue() on `x` */ - def unbox(x: java.lang.Object): Char = x.asInstanceOf[java.lang.Character].charValue() + def unbox(x: java.lang.Object): Char = ??? /** The String representation of the scala.Char companion object. */ override def toString = "object scala.Char" diff --git a/src/library/scala/Double.scala b/src/library/scala/Double.scala index a58fa3ed25..08bcb9fefc 100644 --- a/src/library/scala/Double.scala +++ b/src/library/scala/Double.scala @@ -7,8 +7,8 @@ \* */ // DO NOT EDIT, CHANGES WILL BE LOST -// This auto-generated code can be modified in scala.tools.cmd.gen. -// Afterwards, running tools/codegen-anyvals regenerates this source file. +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. package scala @@ -200,7 +200,8 @@ final abstract class Double private extends AnyVal { /** Returns the remainder of the division of this value by `x`. */ def %(x: Double): Double - override def getClass(): Class[Double] = null + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Double] = ??? } object Double extends AnyValCompanion { @@ -229,7 +230,7 @@ object Double extends AnyValCompanion { * @param x the Double to be boxed * @return a java.lang.Double offering `x` as its underlying value. */ - def box(x: Double): java.lang.Double = java.lang.Double.valueOf(x) + def box(x: Double): java.lang.Double = ??? /** Transform a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw @@ -241,7 +242,7 @@ object Double extends AnyValCompanion { * @throws ClassCastException if the argument is not a java.lang.Double * @return the Double resulting from calling doubleValue() on `x` */ - def unbox(x: java.lang.Object): Double = x.asInstanceOf[java.lang.Double].doubleValue() + def unbox(x: java.lang.Object): Double = ??? /** The String representation of the scala.Double companion object. */ override def toString = "object scala.Double" diff --git a/src/library/scala/Float.scala b/src/library/scala/Float.scala index 3c59057a8d..01fdbc00e4 100644 --- a/src/library/scala/Float.scala +++ b/src/library/scala/Float.scala @@ -7,8 +7,8 @@ \* */ // DO NOT EDIT, CHANGES WILL BE LOST -// This auto-generated code can be modified in scala.tools.cmd.gen. -// Afterwards, running tools/codegen-anyvals regenerates this source file. +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. package scala @@ -200,7 +200,8 @@ final abstract class Float private extends AnyVal { /** Returns the remainder of the division of this value by `x`. */ def %(x: Double): Double - override def getClass(): Class[Float] = null + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Float] = ??? } object Float extends AnyValCompanion { @@ -229,7 +230,7 @@ object Float extends AnyValCompanion { * @param x the Float to be boxed * @return a java.lang.Float offering `x` as its underlying value. */ - def box(x: Float): java.lang.Float = java.lang.Float.valueOf(x) + def box(x: Float): java.lang.Float = ??? /** Transform a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw @@ -241,7 +242,7 @@ object Float extends AnyValCompanion { * @throws ClassCastException if the argument is not a java.lang.Float * @return the Float resulting from calling floatValue() on `x` */ - def unbox(x: java.lang.Object): Float = x.asInstanceOf[java.lang.Float].floatValue() + def unbox(x: java.lang.Object): Float = ??? /** The String representation of the scala.Float companion object. */ override def toString = "object scala.Float" diff --git a/src/library/scala/Int.scala b/src/library/scala/Int.scala index 3bd3775eba..b605af5e37 100644 --- a/src/library/scala/Int.scala +++ b/src/library/scala/Int.scala @@ -7,8 +7,8 @@ \* */ // DO NOT EDIT, CHANGES WILL BE LOST -// This auto-generated code can be modified in scala.tools.cmd.gen. -// Afterwards, running tools/codegen-anyvals regenerates this source file. +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. package scala @@ -434,7 +434,8 @@ final abstract class Int private extends AnyVal { /** Returns the remainder of the division of this value by `x`. */ def %(x: Double): Double - override def getClass(): Class[Int] = null + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Int] = ??? } object Int extends AnyValCompanion { @@ -451,7 +452,7 @@ object Int extends AnyValCompanion { * @param x the Int to be boxed * @return a java.lang.Integer offering `x` as its underlying value. */ - def box(x: Int): java.lang.Integer = java.lang.Integer.valueOf(x) + def box(x: Int): java.lang.Integer = ??? /** Transform a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw @@ -463,7 +464,7 @@ object Int extends AnyValCompanion { * @throws ClassCastException if the argument is not a java.lang.Integer * @return the Int resulting from calling intValue() on `x` */ - def unbox(x: java.lang.Object): Int = x.asInstanceOf[java.lang.Integer].intValue() + def unbox(x: java.lang.Object): Int = ??? /** The String representation of the scala.Int companion object. */ override def toString = "object scala.Int" diff --git a/src/library/scala/Long.scala b/src/library/scala/Long.scala index b27a66647f..84e6f09da3 100644 --- a/src/library/scala/Long.scala +++ b/src/library/scala/Long.scala @@ -7,8 +7,8 @@ \* */ // DO NOT EDIT, CHANGES WILL BE LOST -// This auto-generated code can be modified in scala.tools.cmd.gen. -// Afterwards, running tools/codegen-anyvals regenerates this source file. +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. package scala @@ -434,7 +434,8 @@ final abstract class Long private extends AnyVal { /** Returns the remainder of the division of this value by `x`. */ def %(x: Double): Double - override def getClass(): Class[Long] = null + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Long] = ??? } object Long extends AnyValCompanion { @@ -451,7 +452,7 @@ object Long extends AnyValCompanion { * @param x the Long to be boxed * @return a java.lang.Long offering `x` as its underlying value. */ - def box(x: Long): java.lang.Long = java.lang.Long.valueOf(x) + def box(x: Long): java.lang.Long = ??? /** Transform a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw @@ -463,7 +464,7 @@ object Long extends AnyValCompanion { * @throws ClassCastException if the argument is not a java.lang.Long * @return the Long resulting from calling longValue() on `x` */ - def unbox(x: java.lang.Object): Long = x.asInstanceOf[java.lang.Long].longValue() + def unbox(x: java.lang.Object): Long = ??? /** The String representation of the scala.Long companion object. */ override def toString = "object scala.Long" diff --git a/src/library/scala/Predef.scala b/src/library/scala/Predef.scala index 50a53732f1..58d43f8666 100644 --- a/src/library/scala/Predef.scala +++ b/src/library/scala/Predef.scala @@ -28,13 +28,11 @@ import scala.io.StdIn * constructors ([[scala.collection.immutable.::]] and * [[scala.collection.immutable.Nil]]). * - * === Console I/O === - * Predef provides a number of simple functions for console I/O, such as - * `print`, `println`, `readLine`, `readInt`, etc. These functions are all - * aliases of the functions provided by [[scala.Console]]. + * === Console Output === + * For basic console output, `Predef` provides convenience methods [[print(x:Any* print]] and [[println(x:Any* println]], + * which are aliases of the methods in the object [[scala.Console]]. * * === Assertions === - * * A set of `assert` functions are provided for use as a way to document * and dynamically check invariants in code. Invocations of `assert` can be elided * at compile time by providing the command line option `-Xdisable-assertions`, @@ -67,6 +65,49 @@ import scala.io.StdIn * are provided for the "widening" of numeric values, for instance, converting a * Short value to a Long value as required, and to add additional higher-order * functions to Array values. These are described in more detail in the documentation of [[scala.Array]]. + * + * @groupname utilities Utility Methods + * @groupprio utilities 10 + * + * @groupname assertions Assertions + * @groupprio assertions 20 + * @groupdesc assertions These methods support program verfication and runtime correctness. + * + * @groupname console-output Console Output + * @groupprio console-output 30 + * @groupdesc console-output These methods provide output via the console. + * + * @groupname type-constraints Type Constraints + * @groupprio type-constraints 40 + * @groupdesc type-constraints These entities allows constraints between types to be stipulated. + * + * @groupname aliases Aliases + * @groupprio aliases 50 + * @groupdesc aliases These aliases bring selected immutable types into scope without any imports. + * + * @groupname conversions-string String Conversions + * @groupprio conversions-string 60 + * @groupdesc conversions-string Conversions to and from String and StringOps. + * + * @groupname implicit-classes-any Implicit Classes + * @groupprio implicit-classes-any 70 + * @groupdesc implicit-classes-any These implicit classes add useful extension methods to every type. + * + * @groupname implicit-classes-char CharSequence Conversions + * @groupprio implicit-classes-char 80 + * @groupdesc implicit-classes-char These implicit classes add CharSequence methods to Array[Char] and IndexedSeq[Char] instances. + * + * @groupname conversions-java-to-anyval Java to Scala + * @groupprio conversions-java-to-anyval 90 + * @groupdesc conversions-java-to-anyval Implicit conversion from Java primitive wrapper types to Scala equivalents. + * + * @groupname conversions-anyval-to-java Scala to Java + * @groupprio conversions-anyval-to-java 100 + * @groupdesc conversions-anyval-to-java Implicit conversion from Scala AnyVals to Java primitive wrapper types equivalents. + * + * @groupname conversions-array-to-wrapped-array Array to WrappedArray + * @groupprio conversions-array-to-wrapped-array 110 + * @groupdesc conversions-array-to-wrapped-array Conversions from Arrays to WrappedArrays. */ object Predef extends LowPriorityImplicits with DeprecatedPredef { /** @@ -80,6 +121,7 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { * val mapIntString = classOf[Map[Int,String]] * // mapIntString is java.lang.Class[Map[Int,String]] = interface scala.collection.immutable.Map * }}} + * @group utilities */ def classOf[T]: Class[T] = null // This is a stub method. The actual implementation is filled in by the compiler. @@ -87,19 +129,26 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { * Java String (see the documentation corresponding to your Java version, for * example [[http://docs.oracle.com/javase/8/docs/api/java/lang/String.html]]) or * are added implicitly through [[scala.collection.immutable.StringOps]]. + * @group aliases */ type String = java.lang.String + /** @group aliases */ type Class[T] = java.lang.Class[T] // miscellaneous ----------------------------------------------------- scala.`package` // to force scala package object to be seen. scala.collection.immutable.List // to force Nil, :: to be seen. + /** @group aliases */ type Function[-A, +B] = Function1[A, B] + /** @group aliases */ type Map[A, +B] = immutable.Map[A, B] + /** @group aliases */ type Set[A] = immutable.Set[A] + /** @group aliases */ val Map = immutable.Map + /** @group aliases */ val Set = immutable.Set // Manifest types, companions, and incantations for summoning @@ -132,24 +181,22 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { def optManifest[T](implicit m: OptManifest[T]) = m // Minor variations on identity functions + /** @group utilities */ @inline def identity[A](x: A): A = x // @see `conforms` for the implicit version + /** @group utilities */ @inline def implicitly[T](implicit e: T) = e // for summoning implicit values from the nether world -- TODO: when dependent method types are on by default, give this result type `e.type`, so that inliner has better chance of knowing which method to inline in calls like `implicitly[MatchingStrategy[Option]].zero` + /** @group utilities */ @inline def locally[T](x: T): T = x // to communicate intent and avoid unmoored statements - // errors and asserts ------------------------------------------------- - - // !!! Remove this when possible - ideally for 2.11. - // We are stuck with it a while longer because sbt's compiler interface - // still calls it as of 0.12.2. - @deprecated("Use `sys.error(message)` instead", "2.9.0") - def error(message: String): Nothing = sys.error(message) + // assertions --------------------------------------------------------- /** Tests an expression, throwing an `AssertionError` if false. * Calls to this method will not be generated if `-Xelide-below` - * is at least `ASSERTION`. + * is greater than `ASSERTION`. * - * @see elidable + * @see [[scala.annotation.elidable elidable]] * @param assertion the expression to test + * @group assertions */ @elidable(ASSERTION) def assert(assertion: Boolean) { @@ -159,11 +206,12 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { /** Tests an expression, throwing an `AssertionError` if false. * Calls to this method will not be generated if `-Xelide-below` - * is at least `ASSERTION`. + * is greater than `ASSERTION`. * - * @see elidable + * @see [[scala.annotation.elidable elidable]] * @param assertion the expression to test * @param message a String to include in the failure message + * @group assertions */ @elidable(ASSERTION) @inline final def assert(assertion: Boolean, message: => Any) { @@ -175,10 +223,11 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { * This method differs from assert only in the intent expressed: * assert contains a predicate which needs to be proven, while * assume contains an axiom for a static checker. Calls to this method - * will not be generated if `-Xelide-below` is at least `ASSERTION`. + * will not be generated if `-Xelide-below` is greater than `ASSERTION`. * - * @see elidable + * @see [[scala.annotation.elidable elidable]] * @param assumption the expression to test + * @group assertions */ @elidable(ASSERTION) def assume(assumption: Boolean) { @@ -190,11 +239,12 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { * This method differs from assert only in the intent expressed: * assert contains a predicate which needs to be proven, while * assume contains an axiom for a static checker. Calls to this method - * will not be generated if `-Xelide-below` is at least `ASSERTION`. + * will not be generated if `-Xelide-below` is greater than `ASSERTION`. * - * @see elidable + * @see [[scala.annotation.elidable elidable]] * @param assumption the expression to test * @param message a String to include in the failure message + * @group assertions */ @elidable(ASSERTION) @inline final def assume(assumption: Boolean, message: => Any) { @@ -207,6 +257,7 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { * for violating the condition. * * @param requirement the expression to test + * @group assertions */ def require(requirement: Boolean) { if (!requirement) @@ -219,6 +270,7 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { * * @param requirement the expression to test * @param message a String to include in the failure message + * @group assertions */ @inline final def require(requirement: Boolean, message: => Any) { if (!requirement) @@ -227,6 +279,7 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { /** `???` can be used for marking methods that remain to be implemented. * @throws NotImplementedError + * @group utilities */ def ??? : Nothing = throw new NotImplementedError @@ -250,11 +303,13 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { // implicit classes ----------------------------------------------------- + /** @group implicit-classes-any */ implicit final class ArrowAssoc[A](private val self: A) extends AnyVal { @inline def -> [B](y: B): Tuple2[A, B] = Tuple2(self, y) def →[B](y: B): Tuple2[A, B] = ->(y) } + /** @group implicit-classes-any */ implicit final class Ensuring[A](private val self: A) extends AnyVal { def ensuring(cond: Boolean): A = { assert(cond); self } def ensuring(cond: Boolean, msg: => Any): A = { assert(cond, msg); self } @@ -262,6 +317,7 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { def ensuring(cond: A => Boolean, msg: => Any): A = { assert(cond(self), msg); self } } + /** @group implicit-classes-any */ implicit final class StringFormat[A](private val self: A) extends AnyVal { /** Returns string formatted according to given `format` string. * Format strings are as for `String.format` @@ -271,6 +327,7 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { } // SI-8229 retaining the pre 2.11 name for source compatibility in shadowing this implicit + /** @group implicit-classes-any */ implicit final class any2stringadd[A](private val self: A) extends AnyVal { def +(other: String): String = String.valueOf(self) + other } @@ -280,6 +337,7 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { @deprecated("Use Throwable#getStackTrace", "2.11.0") def getStackTraceString = self.getStackTrace().mkString("", EOL, EOL) } + /** @group implicit-classes-char */ implicit final class SeqCharSequence(val __sequenceOfChars: scala.collection.IndexedSeq[Char]) extends CharSequence { def length: Int = __sequenceOfChars.length def charAt(index: Int): Char = __sequenceOfChars(index) @@ -287,6 +345,7 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { override def toString = __sequenceOfChars mkString "" } + /** @group implicit-classes-char */ implicit final class ArrayCharSequence(val __arrayOfChars: Array[Char]) extends CharSequence { def length: Int = __arrayOfChars.length def charAt(index: Int): Char = __arrayOfChars(index) @@ -299,14 +358,48 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { def apply() = mutable.StringBuilder.newBuilder } + /** @group conversions-string */ @inline implicit def augmentString(x: String): StringOps = new StringOps(x) + /** @group conversions-string */ @inline implicit def unaugmentString(x: StringOps): String = x.repr // printing ----------------------------------------------------------- + /** Prints an object to `out` using its `toString` method. + * + * @param x the object to print; may be null. + * @group console-output + */ def print(x: Any) = Console.print(x) + + /** Prints a newline character on the default output. + * @group console-output + */ def println() = Console.println() + + /** Prints out an object to the default output, followed by a newline character. + * + * @param x the object to print. + * @group console-output + */ def println(x: Any) = Console.println(x) + + /** Prints its arguments as a formatted string to the default output, + * based on a string pattern (in a fashion similar to printf in C). + * + * The interpretation of the formatting patterns is described in + * <a href="" target="contentFrame" class="java/util/Formatter"> + * `java.util.Formatter`</a>. + * + * Consider using the [[scala.StringContext.f f interpolator]] as more type safe and idiomatic. + * + * @param text the pattern for formatting the arguments. + * @param args the arguments used to instantiating the pattern. + * @throws java.lang.IllegalArgumentException if there was a problem with the format string or arguments + * + * @see [[scala.StringContext.f StringContext.f]] + * @group console-output + */ def printf(text: String, xs: Any*) = Console.print(text.format(xs: _*)) // views -------------------------------------------------------------- @@ -353,22 +446,38 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { // "Autoboxing" and "Autounboxing" --------------------------------------------------- + /** @group conversions-anyval-to-java */ implicit def byte2Byte(x: Byte): java.lang.Byte = x.asInstanceOf[java.lang.Byte] + /** @group conversions-anyval-to-java */ implicit def short2Short(x: Short): java.lang.Short = x.asInstanceOf[java.lang.Short] + /** @group conversions-anyval-to-java */ implicit def char2Character(x: Char): java.lang.Character = x.asInstanceOf[java.lang.Character] + /** @group conversions-anyval-to-java */ implicit def int2Integer(x: Int): java.lang.Integer = x.asInstanceOf[java.lang.Integer] + /** @group conversions-anyval-to-java */ implicit def long2Long(x: Long): java.lang.Long = x.asInstanceOf[java.lang.Long] + /** @group conversions-anyval-to-java */ implicit def float2Float(x: Float): java.lang.Float = x.asInstanceOf[java.lang.Float] + /** @group conversions-anyval-to-java */ implicit def double2Double(x: Double): java.lang.Double = x.asInstanceOf[java.lang.Double] + /** @group conversions-anyval-to-java */ implicit def boolean2Boolean(x: Boolean): java.lang.Boolean = x.asInstanceOf[java.lang.Boolean] + /** @group conversions-java-to-anyval */ implicit def Byte2byte(x: java.lang.Byte): Byte = x.asInstanceOf[Byte] + /** @group conversions-java-to-anyval */ implicit def Short2short(x: java.lang.Short): Short = x.asInstanceOf[Short] + /** @group conversions-java-to-anyval */ implicit def Character2char(x: java.lang.Character): Char = x.asInstanceOf[Char] + /** @group conversions-java-to-anyval */ implicit def Integer2int(x: java.lang.Integer): Int = x.asInstanceOf[Int] + /** @group conversions-java-to-anyval */ implicit def Long2long(x: java.lang.Long): Long = x.asInstanceOf[Long] + /** @group conversions-java-to-anyval */ implicit def Float2float(x: java.lang.Float): Float = x.asInstanceOf[Float] + /** @group conversions-java-to-anyval */ implicit def Double2double(x: java.lang.Double): Double = x.asInstanceOf[Double] + /** @group conversions-java-to-anyval */ implicit def Boolean2boolean(x: java.lang.Boolean): Boolean = x.asInstanceOf[Boolean] // Type Constraints -------------------------------------------------------------- @@ -389,6 +498,7 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { * required lower bound. * * In part contributed by Jason Zaugg. + * @group type-constraints */ @implicitNotFound(msg = "Cannot prove that ${From} <:< ${To}.") sealed abstract class <:<[-From, +To] extends (From => To) with Serializable @@ -396,6 +506,7 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { // The dollar prefix is to dodge accidental shadowing of this method // by a user-defined method of the same name (SI-7788). // The collections rely on this method. + /** @group type-constraints */ implicit def $conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A] @deprecated("Use `implicitly[T <:< U]` or `identity` instead.", "2.11.0") @@ -404,10 +515,12 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef { /** An instance of `A =:= B` witnesses that the types `A` and `B` are equal. * * @see `<:<` for expressing subtyping constraints + * @group type-constraints */ @implicitNotFound(msg = "Cannot prove that ${From} =:= ${To}.") sealed abstract class =:=[From, To] extends (From => To) with Serializable private[this] final val singleton_=:= = new =:=[Any,Any] { def apply(x: Any): Any = x } + /** @group type-constraints */ object =:= { implicit def tpEquals[A]: A =:= A = singleton_=:=.asInstanceOf[A =:= A] } @@ -487,6 +600,7 @@ private[scala] abstract class LowPriorityImplicits { @inline implicit def doubleWrapper(x: Double) = new runtime.RichDouble(x) @inline implicit def booleanWrapper(x: Boolean) = new runtime.RichBoolean(x) + /** @group conversions-array-to-wrapped-array */ implicit def genericWrapArray[T](xs: Array[T]): WrappedArray[T] = if (xs eq null) null else WrappedArray.make(xs) @@ -494,23 +608,35 @@ private[scala] abstract class LowPriorityImplicits { // Since the JVM thinks arrays are covariant, one 0-length Array[AnyRef] // is as good as another for all T <: AnyRef. Instead of creating 100,000,000 // unique ones by way of this implicit, let's share one. + /** @group conversions-array-to-wrapped-array */ implicit def wrapRefArray[T <: AnyRef](xs: Array[T]): WrappedArray[T] = { if (xs eq null) null else if (xs.length == 0) WrappedArray.empty[T] else new WrappedArray.ofRef[T](xs) } + /** @group conversions-array-to-wrapped-array */ implicit def wrapIntArray(xs: Array[Int]): WrappedArray[Int] = if (xs ne null) new WrappedArray.ofInt(xs) else null + /** @group conversions-array-to-wrapped-array */ implicit def wrapDoubleArray(xs: Array[Double]): WrappedArray[Double] = if (xs ne null) new WrappedArray.ofDouble(xs) else null + /** @group conversions-array-to-wrapped-array */ implicit def wrapLongArray(xs: Array[Long]): WrappedArray[Long] = if (xs ne null) new WrappedArray.ofLong(xs) else null + /** @group conversions-array-to-wrapped-array */ implicit def wrapFloatArray(xs: Array[Float]): WrappedArray[Float] = if (xs ne null) new WrappedArray.ofFloat(xs) else null + /** @group conversions-array-to-wrapped-array */ implicit def wrapCharArray(xs: Array[Char]): WrappedArray[Char] = if (xs ne null) new WrappedArray.ofChar(xs) else null + /** @group conversions-array-to-wrapped-array */ implicit def wrapByteArray(xs: Array[Byte]): WrappedArray[Byte] = if (xs ne null) new WrappedArray.ofByte(xs) else null + /** @group conversions-array-to-wrapped-array */ implicit def wrapShortArray(xs: Array[Short]): WrappedArray[Short] = if (xs ne null) new WrappedArray.ofShort(xs) else null + /** @group conversions-array-to-wrapped-array */ implicit def wrapBooleanArray(xs: Array[Boolean]): WrappedArray[Boolean] = if (xs ne null) new WrappedArray.ofBoolean(xs) else null + /** @group conversions-array-to-wrapped-array */ implicit def wrapUnitArray(xs: Array[Unit]): WrappedArray[Unit] = if (xs ne null) new WrappedArray.ofUnit(xs) else null + /** @group conversions-string */ implicit def wrapString(s: String): WrappedString = if (s ne null) new WrappedString(s) else null + /** @group conversions-string */ implicit def unwrapString(ws: WrappedString): String = if (ws ne null) ws.self else null implicit def fallbackStringCanBuildFrom[T]: CanBuildFrom[String, T, immutable.IndexedSeq[T]] = diff --git a/src/library/scala/Product.scala b/src/library/scala/Product.scala index 9cd38ed148..f3a96fb333 100644 --- a/src/library/scala/Product.scala +++ b/src/library/scala/Product.scala @@ -19,7 +19,7 @@ package scala */ trait Product extends Any with Equals { /** The n^th^ element of this product, 0-based. In other words, for a - * product `A(x,,1,,, ..., x,,k,,)`, returns `x,,(n+1),,` where `0 < n < k`. + * product `A(x,,1,,, ..., x,,k,,)`, returns `x,,(n+1),,` where `0 <= n < k`. * * @param n the index of the element to return * @throws IndexOutOfBoundsException diff --git a/src/library/scala/Product1.scala b/src/library/scala/Product1.scala index dbc34ba66a..e82300adf6 100644 --- a/src/library/scala/Product1.scala +++ b/src/library/scala/Product1.scala @@ -24,7 +24,7 @@ trait Product1[@specialized(Int, Long, Double) +T1] extends Any with Product { override def productArity = 1 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product10.scala b/src/library/scala/Product10.scala index 70de79d49a..5fc4874048 100644 --- a/src/library/scala/Product10.scala +++ b/src/library/scala/Product10.scala @@ -24,7 +24,7 @@ trait Product10[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10] extends Any w override def productArity = 10 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product11.scala b/src/library/scala/Product11.scala index 1bb79ac017..dcebc90e3e 100644 --- a/src/library/scala/Product11.scala +++ b/src/library/scala/Product11.scala @@ -24,7 +24,7 @@ trait Product11[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11] extends override def productArity = 11 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product12.scala b/src/library/scala/Product12.scala index d7e1e1b05c..2221170452 100644 --- a/src/library/scala/Product12.scala +++ b/src/library/scala/Product12.scala @@ -24,7 +24,7 @@ trait Product12[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12] e override def productArity = 12 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product13.scala b/src/library/scala/Product13.scala index 8571b45a40..e76f326766 100644 --- a/src/library/scala/Product13.scala +++ b/src/library/scala/Product13.scala @@ -24,7 +24,7 @@ trait Product13[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, + override def productArity = 13 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product14.scala b/src/library/scala/Product14.scala index a2f5140370..a076e2cc7a 100644 --- a/src/library/scala/Product14.scala +++ b/src/library/scala/Product14.scala @@ -24,7 +24,7 @@ trait Product14[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, + override def productArity = 14 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product15.scala b/src/library/scala/Product15.scala index 1c6ad0011c..4568aff1fe 100644 --- a/src/library/scala/Product15.scala +++ b/src/library/scala/Product15.scala @@ -24,7 +24,7 @@ trait Product15[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, + override def productArity = 15 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product16.scala b/src/library/scala/Product16.scala index f03b0b34a2..84dccb0ac8 100644 --- a/src/library/scala/Product16.scala +++ b/src/library/scala/Product16.scala @@ -24,7 +24,7 @@ trait Product16[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, + override def productArity = 16 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product17.scala b/src/library/scala/Product17.scala index 72df1b496a..0d50898bf4 100644 --- a/src/library/scala/Product17.scala +++ b/src/library/scala/Product17.scala @@ -24,7 +24,7 @@ trait Product17[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, + override def productArity = 17 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product18.scala b/src/library/scala/Product18.scala index 0402f90a01..9b32265d71 100644 --- a/src/library/scala/Product18.scala +++ b/src/library/scala/Product18.scala @@ -24,7 +24,7 @@ trait Product18[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, + override def productArity = 18 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product19.scala b/src/library/scala/Product19.scala index b9770db47b..fe6b95669b 100644 --- a/src/library/scala/Product19.scala +++ b/src/library/scala/Product19.scala @@ -24,7 +24,7 @@ trait Product19[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, + override def productArity = 19 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product2.scala b/src/library/scala/Product2.scala index a43a4a285c..091bcc89de 100644 --- a/src/library/scala/Product2.scala +++ b/src/library/scala/Product2.scala @@ -24,7 +24,7 @@ trait Product2[@specialized(Int, Long, Double) +T1, @specialized(Int, Long, Doub override def productArity = 2 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product20.scala b/src/library/scala/Product20.scala index 7b0df201ec..81315e3558 100644 --- a/src/library/scala/Product20.scala +++ b/src/library/scala/Product20.scala @@ -24,7 +24,7 @@ trait Product20[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, + override def productArity = 20 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product21.scala b/src/library/scala/Product21.scala index f81347aac0..b5967c06e1 100644 --- a/src/library/scala/Product21.scala +++ b/src/library/scala/Product21.scala @@ -24,7 +24,7 @@ trait Product21[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, + override def productArity = 21 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product22.scala b/src/library/scala/Product22.scala index 7a25891c6e..c7b9da5ce8 100644 --- a/src/library/scala/Product22.scala +++ b/src/library/scala/Product22.scala @@ -24,7 +24,7 @@ trait Product22[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9, +T10, +T11, +T12, + override def productArity = 22 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product3.scala b/src/library/scala/Product3.scala index 9976240935..7154bf5bdf 100644 --- a/src/library/scala/Product3.scala +++ b/src/library/scala/Product3.scala @@ -24,7 +24,7 @@ trait Product3[+T1, +T2, +T3] extends Any with Product { override def productArity = 3 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product4.scala b/src/library/scala/Product4.scala index d6c1543390..046f8c7a7c 100644 --- a/src/library/scala/Product4.scala +++ b/src/library/scala/Product4.scala @@ -24,7 +24,7 @@ trait Product4[+T1, +T2, +T3, +T4] extends Any with Product { override def productArity = 4 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product5.scala b/src/library/scala/Product5.scala index 5f1b11a30d..3e952c8c55 100644 --- a/src/library/scala/Product5.scala +++ b/src/library/scala/Product5.scala @@ -24,7 +24,7 @@ trait Product5[+T1, +T2, +T3, +T4, +T5] extends Any with Product { override def productArity = 5 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product6.scala b/src/library/scala/Product6.scala index efd9408d73..010c68711a 100644 --- a/src/library/scala/Product6.scala +++ b/src/library/scala/Product6.scala @@ -24,7 +24,7 @@ trait Product6[+T1, +T2, +T3, +T4, +T5, +T6] extends Any with Product { override def productArity = 6 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product7.scala b/src/library/scala/Product7.scala index fab0a997a1..24e5a5c05a 100644 --- a/src/library/scala/Product7.scala +++ b/src/library/scala/Product7.scala @@ -24,7 +24,7 @@ trait Product7[+T1, +T2, +T3, +T4, +T5, +T6, +T7] extends Any with Product { override def productArity = 7 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product8.scala b/src/library/scala/Product8.scala index 41391f7050..4a9f65b00e 100644 --- a/src/library/scala/Product8.scala +++ b/src/library/scala/Product8.scala @@ -24,7 +24,7 @@ trait Product8[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8] extends Any with Product override def productArity = 8 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Product9.scala b/src/library/scala/Product9.scala index e22538e1ee..9af11f709a 100644 --- a/src/library/scala/Product9.scala +++ b/src/library/scala/Product9.scala @@ -24,7 +24,7 @@ trait Product9[+T1, +T2, +T3, +T4, +T5, +T6, +T7, +T8, +T9] extends Any with Pro override def productArity = 9 - /** Returns the n-th projection of this product if 0 < n <= productArity, + /** Returns the n-th projection of this product if 0 <= n < productArity, * otherwise throws an `IndexOutOfBoundsException`. * * @param n number of the projection to be returned diff --git a/src/library/scala/Short.scala b/src/library/scala/Short.scala index 2cbbf3cc59..136d745f16 100644 --- a/src/library/scala/Short.scala +++ b/src/library/scala/Short.scala @@ -7,8 +7,8 @@ \* */ // DO NOT EDIT, CHANGES WILL BE LOST -// This auto-generated code can be modified in scala.tools.cmd.gen. -// Afterwards, running tools/codegen-anyvals regenerates this source file. +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. package scala @@ -434,7 +434,8 @@ final abstract class Short private extends AnyVal { /** Returns the remainder of the division of this value by `x`. */ def %(x: Double): Double - override def getClass(): Class[Short] = null + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Short] = ??? } object Short extends AnyValCompanion { @@ -451,7 +452,7 @@ object Short extends AnyValCompanion { * @param x the Short to be boxed * @return a java.lang.Short offering `x` as its underlying value. */ - def box(x: Short): java.lang.Short = java.lang.Short.valueOf(x) + def box(x: Short): java.lang.Short = ??? /** Transform a boxed type into a value type. Note that this * method is not typesafe: it accepts any Object, but will throw @@ -463,7 +464,7 @@ object Short extends AnyValCompanion { * @throws ClassCastException if the argument is not a java.lang.Short * @return the Short resulting from calling shortValue() on `x` */ - def unbox(x: java.lang.Object): Short = x.asInstanceOf[java.lang.Short].shortValue() + def unbox(x: java.lang.Object): Short = ??? /** The String representation of the scala.Short companion object. */ override def toString = "object scala.Short" diff --git a/src/library/scala/Symbol.scala b/src/library/scala/Symbol.scala index 4fead7a50c..4dcfdd4cba 100644 --- a/src/library/scala/Symbol.scala +++ b/src/library/scala/Symbol.scala @@ -71,8 +71,8 @@ private[scala] abstract class UniquenessCache[K, V >: Null] else { // If we don't remove the old String key from the map, we can // wind up with one String as the key and a different String as - // as the name field in the Symbol, which can lead to surprising - // GC behavior and duplicate Symbols. See SI-6706. + // the name field in the Symbol, which can lead to surprising GC + // behavior and duplicate Symbols. See SI-6706. map remove name val sym = valueFromKey(name) map.put(name, new WeakReference(sym)) diff --git a/src/library/scala/Unit.scala b/src/library/scala/Unit.scala index 018ad24a99..eb6d1d0ddf 100644 --- a/src/library/scala/Unit.scala +++ b/src/library/scala/Unit.scala @@ -7,8 +7,8 @@ \* */ // DO NOT EDIT, CHANGES WILL BE LOST -// This auto-generated code can be modified in scala.tools.cmd.gen. -// Afterwards, running tools/codegen-anyvals regenerates this source file. +// This auto-generated code can be modified in "project/GenerateAnyVals.scala". +// Afterwards, running "sbt generateSources" regenerates this source file. package scala @@ -19,7 +19,8 @@ package scala * method which is declared `void`. */ final abstract class Unit private extends AnyVal { - override def getClass(): Class[Unit] = null + // Provide a more specific return type for Scaladoc + override def getClass(): Class[Unit] = ??? } object Unit extends AnyValCompanion { @@ -39,7 +40,7 @@ object Unit extends AnyValCompanion { * @throws ClassCastException if the argument is not a scala.runtime.BoxedUnit * @return the Unit value () */ - def unbox(x: java.lang.Object): Unit = () + def unbox(x: java.lang.Object): Unit = x.asInstanceOf[scala.runtime.BoxedUnit] /** The String representation of the scala.Unit companion object. */ override def toString = "object scala.Unit" diff --git a/src/library/scala/collection/BitSetLike.scala b/src/library/scala/collection/BitSetLike.scala index 29369447d1..209b00ebf9 100644 --- a/src/library/scala/collection/BitSetLike.scala +++ b/src/library/scala/collection/BitSetLike.scala @@ -204,6 +204,27 @@ trait BitSetLike[+This <: BitSetLike[This] with SortedSet[Int]] extends SortedSe def subsetOf(other: BitSet): Boolean = (0 until nwords) forall (idx => (this.word(idx) & ~ other.word(idx)) == 0L) + override def head: Int = { + val n = nwords + var i = 0 + while (i < n) { + val wi = word(i) + if (wi != 0L) return WordLength*i + java.lang.Long.numberOfTrailingZeros(wi) + i += 1 + } + throw new NoSuchElementException("Empty BitSet") + } + + override def last: Int = { + var i = nwords - 1 + while (i >= 0) { + val wi = word(i) + if (wi != 0L) return WordLength*i + 63 - java.lang.Long.numberOfLeadingZeros(wi) + i += 1 + } + throw new NoSuchElementException("Empty BitSet") + } + override def addString(sb: StringBuilder, start: String, sep: String, end: String) = { sb append start var pre = "" diff --git a/src/library/scala/collection/GenSeqLike.scala b/src/library/scala/collection/GenSeqLike.scala index be1da1660a..405d8d7e57 100644 --- a/src/library/scala/collection/GenSeqLike.scala +++ b/src/library/scala/collection/GenSeqLike.scala @@ -58,6 +58,7 @@ trait GenSeqLike[+A, +Repr] extends Any with GenIterableLike[A, Repr] with Equal * Note: `xs.length` and `xs.size` yield the same result. * * @return the number of elements in this $coll. + * @throws IllegalArgumentException if the length of the sequence cannot be represented in an `Int`, for example, `(-1 to Int.MaxValue).length`. */ def length: Int diff --git a/src/library/scala/collection/GenTraversableOnce.scala b/src/library/scala/collection/GenTraversableOnce.scala index 244ff26397..4af2ca23be 100644 --- a/src/library/scala/collection/GenTraversableOnce.scala +++ b/src/library/scala/collection/GenTraversableOnce.scala @@ -67,6 +67,23 @@ trait GenTraversableOnce[+A] extends Any { */ def foreach[U](f: A => U): Unit + /** Tests whether this $coll is known to have a finite size. + * All strict collections are known to have finite size. For a non-strict + * collection such as `Stream`, the predicate returns `'''true'''` if all + * elements have been computed. It returns `'''false'''` if the stream is + * not yet evaluated to the end. Non-empty Iterators usually return + * `'''false'''` even if they were created from a collection with a known + * finite size. + * + * Note: many collection methods will not work on collections of infinite sizes. + * The typical failure mode is an infinite loop. These methods always attempt a + * traversal without checking first that `hasDefiniteSize` returns `'''true'''`. + * However, checking `hasDefiniteSize` can provide an assurance that size is + * well-defined and non-termination is not a concern. + * + * @return `'''true'''` if this collection is known to have finite size, + * `'''false'''` otherwise. + */ def hasDefiniteSize: Boolean def seq: TraversableOnce[A] @@ -81,6 +98,9 @@ trait GenTraversableOnce[+A] extends Any { /** Tests whether the $coll is empty. * + * Note: Implementations in subclasses that are not repeatedly traversable must take + * care not to consume any elements when `isEmpty` is called. + * * @return `true` if the $coll contains no elements, `false` otherwise. */ def isEmpty: Boolean diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index 518bba6b6d..1426278954 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -11,6 +11,7 @@ package collection import mutable.ArrayBuffer import scala.annotation.{tailrec, migration} +import scala.annotation.unchecked.{uncheckedVariance => uV} import immutable.Stream /** The `Iterator` object provides various functions for creating specialized iterators. @@ -160,30 +161,49 @@ object Iterator { def next = elem } - /** Avoid stack overflows when applying ++ to lots of iterators by - * flattening the unevaluated iterators out into a vector of closures. + /** Creates an iterator to which other iterators can be appended efficiently. + * Nested ConcatIterators are merged to avoid blowing the stack. */ - private[scala] final class ConcatIterator[+A](private[this] var current: Iterator[A], initial: Vector[() => Iterator[A]]) extends Iterator[A] { - @deprecated def this(initial: Vector[() => Iterator[A]]) = this(Iterator.empty, initial) // for binary compatibility - private[this] var queue: Vector[() => Iterator[A]] = initial - private[this] var currentHasNextChecked = false + private final class ConcatIterator[+A](private var current: Iterator[A @uV]) extends Iterator[A] { + private var tail: ConcatIteratorCell[A @uV] = null + private var last: ConcatIteratorCell[A @uV] = null + private var currentHasNextChecked = false + // Advance current to the next non-empty iterator // current is set to null when all iterators are exhausted @tailrec private[this] def advance(): Boolean = { - if (queue.isEmpty) { + if (tail eq null) { current = null + last = null false } else { - current = queue.head() - queue = queue.tail - if (current.hasNext) { + current = tail.headIterator + tail = tail.tail + merge() + if (currentHasNextChecked) true + else if (current.hasNext) { currentHasNextChecked = true true } else advance() } } + + // If the current iterator is a ConcatIterator, merge it into this one + @tailrec + private[this] def merge(): Unit = + if (current.isInstanceOf[ConcatIterator[_]]) { + val c = current.asInstanceOf[ConcatIterator[A]] + current = c.current + currentHasNextChecked = c.currentHasNextChecked + if (c.tail ne null) { + c.last.tail = tail + tail = c.tail + } + merge() + } + def hasNext = if (currentHasNextChecked) true else if (current eq null) false @@ -191,47 +211,29 @@ object Iterator { currentHasNextChecked = true true } else advance() + def next() = if (hasNext) { currentHasNextChecked = false current.next() } else Iterator.empty.next() - override def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] = - new ConcatIterator(current, queue :+ (() => that.toIterator)) - } - - private[scala] final class JoinIterator[+A](lhs: Iterator[A], that: => GenTraversableOnce[A]) extends Iterator[A] { - private[this] var state = 0 // 0: lhs not checked, 1: lhs has next, 2: switched to rhs - private[this] lazy val rhs: Iterator[A] = that.toIterator - def hasNext = state match { - case 0 => - if (lhs.hasNext) { - state = 1 - true - } else { - state = 2 - rhs.hasNext - } - case 1 => true - case _ => rhs.hasNext - } - def next() = state match { - case 0 => - if (lhs.hasNext) lhs.next() - else { - state = 2 - rhs.next() - } - case 1 => - state = 0 - lhs.next() - case _ => - rhs.next() + override def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] = { + val c = new ConcatIteratorCell[B](that, null).asInstanceOf[ConcatIteratorCell[A]] + if(tail eq null) { + tail = c + last = c + } else { + last.tail = c + last = c + } + if(current eq null) current = Iterator.empty + this } + } - override def ++[B >: A](that: => GenTraversableOnce[B]) = - new ConcatIterator(this, Vector(() => that.toIterator)) + private[this] final class ConcatIteratorCell[A](head: => GenTraversableOnce[A], var tail: ConcatIteratorCell[A]) { + def headIterator: Iterator[A] = head.toIterator } /** Creates a delegating iterator capped by a limit count. Negative limit means unbounded. @@ -456,7 +458,7 @@ trait Iterator[+A] extends TraversableOnce[A] { * @usecase def ++(that: => Iterator[A]): Iterator[A] * @inheritdoc */ - def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] = new Iterator.JoinIterator(self, that) + def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] = new Iterator.ConcatIterator(self) ++ that /** Creates a new iterator by applying a function to all values produced by this iterator * and concatenating the results. diff --git a/src/library/scala/collection/JavaConversions.scala b/src/library/scala/collection/JavaConversions.scala index 7bfa60771f..960e452cdf 100644 --- a/src/library/scala/collection/JavaConversions.scala +++ b/src/library/scala/collection/JavaConversions.scala @@ -1,6 +1,6 @@ /* __ *\ ** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** / __/ __// _ | / / / _ | (c) 2006-2016, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://www.scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** @@ -11,21 +11,21 @@ package collection import convert._ -/** A collection of implicit conversions supporting interoperability between - * Scala and Java collections. +/** A variety of implicit conversions supporting interoperability between + * Scala and Java collections. * - * The following conversions are supported: + * The following conversions are supported: *{{{ - * scala.collection.Iterable <=> java.lang.Iterable - * scala.collection.Iterable <=> java.util.Collection - * scala.collection.Iterator <=> java.util.{ Iterator, Enumeration } + * scala.collection.Iterable <=> java.lang.Iterable + * scala.collection.Iterable <=> java.util.Collection + * scala.collection.Iterator <=> java.util.{ Iterator, Enumeration } * scala.collection.mutable.Buffer <=> java.util.List - * scala.collection.mutable.Set <=> java.util.Set - * scala.collection.mutable.Map <=> java.util.{ Map, Dictionary } + * scala.collection.mutable.Set <=> java.util.Set + * scala.collection.mutable.Map <=> java.util.{ Map, Dictionary } * scala.collection.concurrent.Map <=> java.util.concurrent.ConcurrentMap *}}} - * In all cases, converting from a source type to a target type and back - * again will return the original source object, eg. + * In all cases, converting from a source type to a target type and back + * again will return the original source object: * *{{{ * import scala.collection.JavaConversions._ @@ -45,8 +45,16 @@ import convert._ * java.util.Properties => scala.collection.mutable.Map[String, String] *}}} * + * The transparent conversions provided here are considered + * fragile because they can result in unexpected behavior and performance. + * + * Therefore, this API has been deprecated and `JavaConverters` should be + * used instead. `JavaConverters` provides the same conversions, but through + * extension methods. + * * @author Miles Sabin * @author Martin Odersky * @since 2.8 */ +@deprecated("Use JavaConverters", since="2.12") object JavaConversions extends WrapAsScala with WrapAsJava diff --git a/src/library/scala/collection/JavaConverters.scala b/src/library/scala/collection/JavaConverters.scala index 86e86d4584..d48a1764e9 100644 --- a/src/library/scala/collection/JavaConverters.scala +++ b/src/library/scala/collection/JavaConverters.scala @@ -1,6 +1,6 @@ /* __ *\ ** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** / __/ __// _ | / / / _ | (c) 2006-2016, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://www.scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** @@ -11,50 +11,62 @@ package collection import convert._ -// TODO: I cleaned all this documentation up in JavaConversions, but the -// documentation in here is basically the pre-cleaned-up version with minor -// additions. Would be nice to have in one place. - -/** A collection of decorators that allow converting between - * Scala and Java collections using `asScala` and `asJava` methods. - * - * The following conversions are supported via `asJava`, `asScala` +/** A variety of decorators that enable converting between + * Scala and Java collections using extension methods, `asScala` and `asJava`. * - * - `scala.collection.Iterable` <=> `java.lang.Iterable` - * - `scala.collection.Iterator` <=> `java.util.Iterator` - * - `scala.collection.mutable.Buffer` <=> `java.util.List` - * - `scala.collection.mutable.Set` <=> `java.util.Set` - * - `scala.collection.mutable.Map` <=> `java.util.Map` - * - `scala.collection.mutable.concurrent.Map` <=> `java.util.concurrent.ConcurrentMap` + * The extension methods return adapters for the corresponding API. * + * The following conversions are supported via `asScala` and `asJava`: + *{{{ + * scala.collection.Iterable <=> java.lang.Iterable + * scala.collection.Iterator <=> java.util.Iterator + * scala.collection.mutable.Buffer <=> java.util.List + * scala.collection.mutable.Set <=> java.util.Set + * scala.collection.mutable.Map <=> java.util.Map + * scala.collection.mutable.concurrent.Map <=> java.util.concurrent.ConcurrentMap + *}}} + * The following conversions are supported via `asScala` and through + * specially-named extension methods to convert to Java collections, as shown: + *{{{ + * scala.collection.Iterable <=> java.util.Collection (via asJavaCollection) + * scala.collection.Iterator <=> java.util.Enumeration (via asJavaEnumeration) + * scala.collection.mutable.Map <=> java.util.Dictionary (via asJavaDictionary) + *}}} + * In addition, the following one-way conversions are provided via `asJava`: + *{{{ + * scala.collection.Seq => java.util.List + * scala.collection.mutable.Seq => java.util.List + * scala.collection.Set => java.util.Set + * scala.collection.Map => java.util.Map + *}}} + * The following one way conversion is provided via `asScala`: + *{{{ + * java.util.Properties => scala.collection.mutable.Map + *}}} * In all cases, converting from a source type to a target type and back - * again will return the original source object, e.g. + * again will return the original source object. For example: * {{{ * import scala.collection.JavaConverters._ * - * val sl = new scala.collection.mutable.ListBuffer[Int] - * val jl : java.util.List[Int] = sl.asJava - * val sl2 : scala.collection.mutable.Buffer[Int] = jl.asScala - * assert(sl eq sl2) + * val source = new scala.collection.mutable.ListBuffer[Int] + * val target: java.util.List[Int] = source.asJava + * val other: scala.collection.mutable.Buffer[Int] = target.asScala + * assert(source eq other) * }}} - * The following conversions are also supported, but the - * direction from Scala to Java is done by the more specifically named methods: - * `asJavaCollection`, `asJavaEnumeration`, `asJavaDictionary`. - * - * - `scala.collection.Iterable` <=> `java.util.Collection` - * - `scala.collection.Iterator` <=> `java.util.Enumeration` - * - `scala.collection.mutable.Map` <=> `java.util.Dictionary` - * - * In addition, the following one way conversions are provided via `asJava`: + * Alternatively, the conversion methods have descriptive names and can be invoked explicitly. + * {{{ + * scala> val vs = java.util.Arrays.asList("hi", "bye") + * vs: java.util.List[String] = [hi, bye] * - * - `scala.collection.Seq` => `java.util.List` - * - `scala.collection.mutable.Seq` => `java.util.List` - * - `scala.collection.Set` => `java.util.Set` - * - `scala.collection.Map` => `java.util.Map` + * scala> val ss = asScalaIterator(vs.iterator) + * ss: Iterator[String] = non-empty iterator * - * The following one way conversion is provided via `asScala`: + * scala> .toList + * res0: List[String] = List(hi, bye) * - * - `java.util.Properties` => `scala.collection.mutable.Map` + * scala> val ss = asScalaBuffer(vs) + * ss: scala.collection.mutable.Buffer[String] = Buffer(hi, bye) + * }}} * * @since 2.8.1 */ diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index fa9a3a7482..d914f2e0ff 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -138,17 +138,6 @@ trait TraversableLike[+A, +Repr] extends Any result } - /** Tests whether this $coll is known to have a finite size. - * All strict collections are known to have finite size. For a non-strict - * collection such as `Stream`, the predicate returns `'''true'''` if all - * elements have been computed. It returns `'''false'''` if the stream is - * not yet evaluated to the end. - * - * Note: many collection methods will not work on collections of infinite sizes. - * - * @return `'''true'''` if this collection is known to have finite size, - * `'''false'''` otherwise. - */ def hasDefiniteSize = true def ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = { diff --git a/src/library/scala/collection/TraversableOnce.scala b/src/library/scala/collection/TraversableOnce.scala index 41362e8dd7..b87fcd166e 100644 --- a/src/library/scala/collection/TraversableOnce.scala +++ b/src/library/scala/collection/TraversableOnce.scala @@ -38,9 +38,10 @@ import scala.reflect.ClassTag * `Traversables`, such as folds, conversions, and other operations which * traverse some or all of the elements and return a derived value. * Directly subclassing `TraversableOnce` is not recommended - instead, - * consider declaring an `Iterator` with a `next` and `hasNext` method, - * creating an `Iterator` with one of the methods on the `Iterator` object, - * or declaring a subclass of `Traversable`. + * consider declaring an `Iterator` with a `next` and `hasNext` method or + * creating an `Iterator` with one of the methods on the `Iterator` object. + * Consider declaring a subclass of `Traversable` instead if the elements + * can be traversed repeatedly. * * @define coll traversable or iterator * @define orderDependent @@ -61,8 +62,8 @@ import scala.reflect.ClassTag trait TraversableOnce[+A] extends Any with GenTraversableOnce[A] { self => + //TODO 2.12: Remove these methods. They are already defined in GenTraversableOnce /* Self-documenting abstract methods. */ - def foreach[U](f: A => U): Unit def isEmpty: Boolean def hasDefiniteSize: Boolean diff --git a/src/library/scala/collection/concurrent/TrieMap.scala b/src/library/scala/collection/concurrent/TrieMap.scala index d113cbfcdf..5dc01547e6 100644 --- a/src/library/scala/collection/concurrent/TrieMap.scala +++ b/src/library/scala/collection/concurrent/TrieMap.scala @@ -1106,14 +1106,14 @@ private[concurrent] case object TrieMapSerializationEnd private[concurrent] object Debug { - import scala.collection._ + import JavaConverters._ lazy val logbuffer = new java.util.concurrent.ConcurrentLinkedQueue[AnyRef] def log(s: AnyRef) = logbuffer.add(s) def flush() { - for (s <- JavaConversions.asScalaIterator(logbuffer.iterator())) Console.out.println(s.toString) + for (s <- logbuffer.iterator().asScala) Console.out.println(s.toString) logbuffer.clear() } diff --git a/src/library/scala/collection/convert/AsJavaConverters.scala b/src/library/scala/collection/convert/AsJavaConverters.scala new file mode 100644 index 0000000000..c7c1fb9c74 --- /dev/null +++ b/src/library/scala/collection/convert/AsJavaConverters.scala @@ -0,0 +1,262 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2016, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://www.scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package collection +package convert + +import java.{ lang => jl, util => ju }, java.util.{ concurrent => juc } + +/** Defines converter methods from Scala to Java collections. */ +trait AsJavaConverters { + import Wrappers._ + + /** + * Converts a Scala `Iterator` to a Java `Iterator`. + * + * The returned Java `Iterator` is backed by the provided Scala `Iterator` and any side-effects of + * using it via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Iterator` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.asScalaIterator]](java.util.Iterator)` then the original Java `Iterator` will + * be returned. + * + * @param i The Scala `Iterator` to be converted. + * @return A Java `Iterator` view of the argument. + */ + def asJavaIterator[A](i: Iterator[A]): ju.Iterator[A] = i match { + case null => null + case JIteratorWrapper(wrapped) => wrapped.asInstanceOf[ju.Iterator[A]] + case _ => IteratorWrapper(i) + } + + /** + * Converts a Scala `Iterator` to a Java `Enumeration`. + * + * The returned Java `Enumeration` is backed by the provided Scala `Iterator` and any side-effects + * of using it via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Iterator` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.enumerationAsScalaIterator]](java.util.Enumeration)` then the original Java + * `Enumeration` will be returned. + * + * @param i The Scala `Iterator` to be converted. + * @return A Java `Enumeration` view of the argument. + */ + def asJavaEnumeration[A](i: Iterator[A]): ju.Enumeration[A] = i match { + case null => null + case JEnumerationWrapper(wrapped) => wrapped.asInstanceOf[ju.Enumeration[A]] + case _ => IteratorWrapper(i) + } + + /** + * Converts a Scala `Iterable` to a Java `Iterable`. + * + * The returned Java `Iterable` is backed by the provided Scala `Iterable` and any side-effects of + * using it via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Iterable` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.iterableAsScalaIterable]](java.lang.Iterable)` then the original Java + * `Iterable` will be returned. + * + * @param i The Scala `Iterable` to be converted. + * @return A Java `Iterable` view of the argument. + */ + def asJavaIterable[A](i: Iterable[A]): jl.Iterable[A] = i match { + case null => null + case JIterableWrapper(wrapped) => wrapped.asInstanceOf[jl.Iterable[A]] + case _ => IterableWrapper(i) + } + + /** + * Converts a Scala `Iterable` to an immutable Java `Collection`. + * + * If the Scala `Iterable` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.collectionAsScalaIterable]](java.util.Collection)` then the original Java + * `Collection` will be returned. + * + * @param i The Scala `Iterable` to be converted. + * @return A Java `Collection` view of the argument. + */ + def asJavaCollection[A](i: Iterable[A]): ju.Collection[A] = i match { + case null => null + case JCollectionWrapper(wrapped) => wrapped.asInstanceOf[ju.Collection[A]] + case _ => new IterableWrapper(i) + } + + /** + * Converts a Scala mutable `Buffer` to a Java List. + * + * The returned Java List is backed by the provided Scala `Buffer` and any side-effects of using + * it via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Buffer` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.asScalaBuffer]](java.util.List)` then the original Java `List` will be + * returned. + * + * @param b The Scala `Buffer` to be converted. + * @return A Java `List` view of the argument. + */ + def bufferAsJavaList[A](b: mutable.Buffer[A]): ju.List[A] = b match { + case null => null + case JListWrapper(wrapped) => wrapped + case _ => new MutableBufferWrapper(b) + } + + /** + * Converts a Scala mutable `Seq` to a Java `List`. + * + * The returned Java `List` is backed by the provided Scala `Seq` and any side-effects of using it + * via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Seq` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.asScalaBuffer]](java.util.List)` then the original Java `List` will be + * returned. + * + * @param s The Scala `Seq` to be converted. + * @return A Java `List` view of the argument. + */ + def mutableSeqAsJavaList[A](s: mutable.Seq[A]): ju.List[A] = s match { + case null => null + case JListWrapper(wrapped) => wrapped + case _ => new MutableSeqWrapper(s) + } + + /** + * Converts a Scala `Seq` to a Java `List`. + * + * The returned Java `List` is backed by the provided Scala `Seq` and any side-effects of using it + * via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Seq` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.asScalaBuffer]](java.util.List)` then the original Java `List` will be + * returned. + * + * @param s The Scala `Seq` to be converted. + * @return A Java `List` view of the argument. + */ + def seqAsJavaList[A](s: Seq[A]): ju.List[A] = s match { + case null => null + case JListWrapper(wrapped) => wrapped.asInstanceOf[ju.List[A]] + case _ => new SeqWrapper(s) + } + + /** + * Converts a Scala mutable `Set` to a Java `Set`. + * + * The returned Java `Set` is backed by the provided Scala `Set` and any side-effects of using it + * via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Set` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.asScalaSet]](java.util.Set)` then the original Java `Set` will be returned. + * + * @param s The Scala mutable `Set` to be converted. + * @return A Java `Set` view of the argument. + */ + def mutableSetAsJavaSet[A](s: mutable.Set[A]): ju.Set[A] = s match { + case null => null + case JSetWrapper(wrapped) => wrapped + case _ => new MutableSetWrapper(s) + } + + /** + * Converts a Scala `Set` to a Java `Set`. + * + * The returned Java `Set` is backed by the provided Scala `Set` and any side-effects of using it + * via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Set` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.asScalaSet]](java.util.Set)` then the original Java `Set` will be returned. + * + * @param s The Scala `Set` to be converted. + * @return A Java `Set` view of the argument. + */ + def setAsJavaSet[A](s: Set[A]): ju.Set[A] = s match { + case null => null + case JSetWrapper(wrapped) => wrapped + case _ => new SetWrapper(s) + } + + /** + * Converts a Scala mutable `Map` to a Java `Map`. + * + * The returned Java `Map` is backed by the provided Scala `Map` and any side-effects of using it + * via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Map` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.mapAsScalaMap]](java.util.Map)` then the original Java `Map` will be + * returned. + * + * @param m The Scala mutable `Map` to be converted. + * @return A Java `Map` view of the argument. + */ + def mutableMapAsJavaMap[A, B](m: mutable.Map[A, B]): ju.Map[A, B] = m match { + case null => null + case JMapWrapper(wrapped) => wrapped + case _ => new MutableMapWrapper(m) + } + + /** + * Converts a Scala mutable `Map` to a Java `Dictionary`. + * + * The returned Java `Dictionary` is backed by the provided Scala `Dictionary` and any + * side-effects of using it via the Java interface will be visible via the Scala interface and + * vice versa. + * + * If the Scala `Dictionary` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.dictionaryAsScalaMap]](java.util.Dictionary)` then the original Java + * `Dictionary` will be returned. + * + * @param m The Scala `Map` to be converted. + * @return A Java `Dictionary` view of the argument. + */ + def asJavaDictionary[A, B](m: mutable.Map[A, B]): ju.Dictionary[A, B] = m match { + case null => null + case JDictionaryWrapper(wrapped) => wrapped + case _ => new DictionaryWrapper(m) + } + + /** + * Converts a Scala `Map` to a Java `Map`. + * + * The returned Java `Map` is backed by the provided Scala `Map` and any side-effects of using it + * via the Java interface will be visible via the Scala interface and vice versa. + * + * If the Scala `Map` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.mapAsScalaMap]](java.util.Map)` then the original Java `Map` will be + * returned. + * + * @param m The Scala `Map` to be converted. + * @return A Java `Map` view of the argument. + */ + def mapAsJavaMap[A, B](m: Map[A, B]): ju.Map[A, B] = m match { + case null => null + case JMapWrapper(wrapped) => wrapped.asInstanceOf[ju.Map[A, B]] + case _ => new MapWrapper(m) + } + + /** + * Converts a Scala mutable `concurrent.Map` to a Java `ConcurrentMap`. + * + * The returned Java `ConcurrentMap` is backed by the provided Scala `concurrent.Map` and any + * side-effects of using it via the Java interface will be visible via the Scala interface and + * vice versa. + * + * If the Scala `concurrent.Map` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.mapAsScalaConcurrentMap]](java.util.concurrent.ConcurrentMap)` then the + * original Java `ConcurrentMap` will be returned. + * + * @param m The Scala `concurrent.Map` to be converted. + * @return A Java `ConcurrentMap` view of the argument. + */ + def mapAsJavaConcurrentMap[A, B](m: concurrent.Map[A, B]): juc.ConcurrentMap[A, B] = m match { + case null => null + case JConcurrentMapWrapper(wrapped) => wrapped + case _ => new ConcurrentMapWrapper(m) + } +} diff --git a/src/library/scala/collection/convert/AsScalaConverters.scala b/src/library/scala/collection/convert/AsScalaConverters.scala new file mode 100644 index 0000000000..f9e38797e1 --- /dev/null +++ b/src/library/scala/collection/convert/AsScalaConverters.scala @@ -0,0 +1,207 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2016, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://www.scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package collection +package convert + +import java.{ lang => jl, util => ju }, java.util.{ concurrent => juc } + +/** Defines converter methods from Java to Scala collections. */ +trait AsScalaConverters { + import Wrappers._ + + /** + * Converts a Java `Iterator` to a Scala `Iterator`. + * + * The returned Scala `Iterator` is backed by the provided Java `Iterator` and any side-effects of + * using it via the Scala interface will be visible via the Java interface and vice versa. + * + * If the Java `Iterator` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.asJavaIterator]](scala.collection.Iterator)` then the original Scala + * `Iterator` will be returned. + * + * @param i The Java `Iterator` to be converted. + * @return A Scala `Iterator` view of the argument. + */ + def asScalaIterator[A](i: ju.Iterator[A]): Iterator[A] = i match { + case null => null + case IteratorWrapper(wrapped) => wrapped + case _ => JIteratorWrapper(i) + } + + /** + * Converts a Java `Enumeration` to a Scala `Iterator`. + * + * The returned Scala `Iterator` is backed by the provided Java `Enumeration` and any side-effects + * of using it via the Scala interface will be visible via the Java interface and vice versa. + * + * If the Java `Enumeration` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.asJavaEnumeration]](scala.collection.Iterator)` then the original Scala + * `Iterator` will be returned. + * + * @param i The Java `Enumeration` to be converted. + * @return A Scala `Iterator` view of the argument. + */ + def enumerationAsScalaIterator[A](i: ju.Enumeration[A]): Iterator[A] = i match { + case null => null + case IteratorWrapper(wrapped) => wrapped + case _ => JEnumerationWrapper(i) + } + + /** + * Converts a Java `Iterable` to a Scala `Iterable`. + * + * The returned Scala `Iterable` is backed by the provided Java `Iterable` and any side-effects of + * using it via the Scala interface will be visible via the Java interface and vice versa. + * + * If the Java `Iterable` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.asJavaIterable]](scala.collection.Iterable) then the original Scala + * `Iterable` will be returned. + * + * @param i The Java `Iterable` to be converted. + * @return A Scala `Iterable` view of the argument. + */ + def iterableAsScalaIterable[A](i: jl.Iterable[A]): Iterable[A] = i match { + case null => null + case IterableWrapper(wrapped) => wrapped + case _ => JIterableWrapper(i) + } + + /** + * Converts a Java `Collection` to an Scala `Iterable`. + * + * If the Java `Collection` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.asJavaCollection]](scala.collection.Iterable)` then the original Scala + * `Iterable` will be returned. + * + * @param i The Java `Collection` to be converted. + * @return A Scala `Iterable` view of the argument. + */ + def collectionAsScalaIterable[A](i: ju.Collection[A]): Iterable[A] = i match { + case null => null + case IterableWrapper(wrapped) => wrapped + case _ => JCollectionWrapper(i) + } + + /** + * Converts a Java `List` to a Scala mutable `Buffer`. + * + * The returned Scala `Buffer` is backed by the provided Java `List` and any side-effects of using + * it via the Scala interface will be visible via the Java interface and vice versa. + * + * If the Java `List` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.bufferAsJavaList]](scala.collection.mutable.Buffer)` then the original Scala + * `Buffer` will be returned. + * + * @param l The Java `List` to be converted. + * @return A Scala mutable `Buffer` view of the argument. + */ + def asScalaBuffer[A](l: ju.List[A]): mutable.Buffer[A] = l match { + case null => null + case MutableBufferWrapper(wrapped) => wrapped + case _ => new JListWrapper(l) + } + + /** + * Converts a Java `Set` to a Scala mutable `Set`. + * + * The returned Scala `Set` is backed by the provided Java `Set` and any side-effects of using it + * via the Scala interface will be visible via the Java interface and vice versa. + * + * If the Java `Set` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.mutableSetAsJavaSet]](scala.collection.mutable.Set)` then the original Scala + * `Set` will be returned. + * + * @param s The Java `Set` to be converted. + * @return A Scala mutable `Set` view of the argument. + */ + def asScalaSet[A](s: ju.Set[A]): mutable.Set[A] = s match { + case null => null + case MutableSetWrapper(wrapped) => wrapped + case _ => new JSetWrapper(s) + } + + /** + * Converts a Java `Map` to a Scala mutable `Map`. + * + * The returned Scala `Map` is backed by the provided Java `Map` and any side-effects of using it + * via the Scala interface will be visible via the Java interface and vice versa. + * + * If the Java `Map` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.mutableMapAsJavaMap]](scala.collection.mutable.Map)` then the original Scala + * `Map` will be returned. + * + * If the wrapped map is synchronized (e.g. from `java.util.Collections.synchronizedMap`), it is + * your responsibility to wrap all non-atomic operations with `underlying.synchronized`. + * This includes `get`, as `java.util.Map`'s API does not allow for an atomic `get` when `null` + * values may be present. + * + * @param m The Java `Map` to be converted. + * @return A Scala mutable `Map` view of the argument. + */ + def mapAsScalaMap[A, B](m: ju.Map[A, B]): mutable.Map[A, B] = m match { + case null => null + case MutableMapWrapper(wrapped) => wrapped + case _ => new JMapWrapper(m) + } + + /** + * Converts a Java `ConcurrentMap` to a Scala mutable `ConcurrentMap`. + * + * The returned Scala `ConcurrentMap` is backed by the provided Java `ConcurrentMap` and any + * side-effects of using it via the Scala interface will be visible via the Java interface and + * vice versa. + * + * If the Java `ConcurrentMap` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.mapAsJavaConcurrentMap]](scala.collection.mutable.ConcurrentMap)` + * then the original Scala `ConcurrentMap` will be returned. + * + * @param m The Java `ConcurrentMap` to be converted. + * @return A Scala mutable `ConcurrentMap` view of the argument. + */ + def mapAsScalaConcurrentMap[A, B](m: juc.ConcurrentMap[A, B]): concurrent.Map[A, B] = m match { + case null => null + case cmw: ConcurrentMapWrapper[_, _] => cmw.underlying + case _ => new JConcurrentMapWrapper(m) + } + + /** + * Converts a Java `Dictionary` to a Scala mutable `Map`. + * + * The returned Scala `Map` is backed by the provided Java `Dictionary` and any side-effects of + * using it via the Scala interface will be visible via the Java interface and vice versa. + * + * If the Java `Dictionary` was previously obtained from an implicit or explicit call of + * `[[JavaConverters.asJavaDictionary]](scala.collection.mutable.Map)` then the original + * Scala `Map` will be returned. + * + * @param p The Java `Dictionary` to be converted. + * @return A Scala mutable `Map` view of the argument. + */ + def dictionaryAsScalaMap[A, B](p: ju.Dictionary[A, B]): mutable.Map[A, B] = p match { + case null => null + case DictionaryWrapper(wrapped) => wrapped + case _ => new JDictionaryWrapper(p) + } + + /** + * Converts a Java `Properties` to a Scala mutable `Map[String, String]`. + * + * The returned Scala `Map[String, String]` is backed by the provided Java `Properties` and any + * side-effects of using it via the Scala interface will be visible via the Java interface and + * vice versa. + * + * @param p The Java `Properties` to be converted. + * @return A Scala mutable `Map[String, String]` view of the argument. + */ + def propertiesAsScalaMap(p: ju.Properties): mutable.Map[String, String] = p match { + case null => null + case _ => new JPropertiesWrapper(p) + } +} diff --git a/src/library/scala/collection/convert/DecorateAsJava.scala b/src/library/scala/collection/convert/DecorateAsJava.scala index e6aa5da067..8371804b91 100644 --- a/src/library/scala/collection/convert/DecorateAsJava.scala +++ b/src/library/scala/collection/convert/DecorateAsJava.scala @@ -1,6 +1,6 @@ /* __ *\ ** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** / __/ __// _ | / / / _ | (c) 2006-2016, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://www.scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** @@ -12,289 +12,97 @@ package convert import java.{ lang => jl, util => ju }, java.util.{ concurrent => juc } import Decorators._ -import WrapAsJava._ import scala.language.implicitConversions - -/** A collection of decorators that allow converting between - * Scala and Java collections using `asScala` and `asJava` methods. - * - * The following conversions are supported via `asJava`, `asScala` - * - * - `scala.collection.Iterable` <=> `java.lang.Iterable` - * - `scala.collection.Iterator` <=> `java.util.Iterator` - * - `scala.collection.mutable.Buffer` <=> `java.util.List` - * - `scala.collection.mutable.Set` <=> `java.util.Set` - * - `scala.collection.mutable.Map` <=> `java.util.Map` - * - `scala.collection.mutable.concurrent.Map` <=> `java.util.concurrent.ConcurrentMap` - * - * In all cases, converting from a source type to a target type and back - * again will return the original source object, e.g. - * {{{ - * import scala.collection.JavaConverters._ - * - * val sl = new scala.collection.mutable.ListBuffer[Int] - * val jl : java.util.List[Int] = sl.asJava - * val sl2 : scala.collection.mutable.Buffer[Int] = jl.asScala - * assert(sl eq sl2) - * }}} - * The following conversions are also supported, but the - * direction from Scala to Java is done by the more specifically named methods: - * `asJavaCollection`, `asJavaEnumeration`, `asJavaDictionary`. - * - * - `scala.collection.Iterable` <=> `java.util.Collection` - * - `scala.collection.Iterator` <=> `java.util.Enumeration` - * - `scala.collection.mutable.Map` <=> `java.util.Dictionary` - * - * In addition, the following one way conversions are provided via `asJava`: - * - * - `scala.collection.Seq` => `java.util.List` - * - `scala.collection.mutable.Seq` => `java.util.List` - * - `scala.collection.Set` => `java.util.Set` - * - `scala.collection.Map` => `java.util.Map` - * - * The following one way conversion is provided via `asScala`: - * - * - `java.util.Properties` => `scala.collection.mutable.Map` - * - * @since 2.8.1 - */ -trait DecorateAsJava { +/** Defines `asJava` extension methods for [[JavaConverters]]. */ +trait DecorateAsJava extends AsJavaConverters { /** - * Adds an `asJava` method that implicitly converts a Scala `Iterator` to a - * Java `Iterator`. The returned Java `Iterator` is backed by the provided Scala - * `Iterator` and any side-effects of using it via the Java interface will - * be visible via the Scala interface and vice versa. - * - * If the Scala `Iterator` was previously obtained from an implicit or explicit - * call of `asIterator(java.util.Iterator)` then the original Java `Iterator` - * will be returned by the `asJava` method. - * - * @param i The `Iterator` to be converted. - * @return An object with an `asJava` method that returns a Java `Iterator` view of the argument. + * Adds an `asJava` method that implicitly converts a Scala `Iterator` to a Java `Iterator`. + * See [[asJavaIterator]]. */ implicit def asJavaIteratorConverter[A](i : Iterator[A]): AsJava[ju.Iterator[A]] = new AsJava(asJavaIterator(i)) /** - * Adds an `asJavaEnumeration` method that implicitly converts a Scala - * `Iterator` to a Java `Enumeration`. The returned Java `Enumeration` is - * backed by the provided Scala `Iterator` and any side-effects of using - * it via the Java interface will be visible via the Scala interface and - * vice versa. - * - * If the Scala `Iterator` was previously obtained from an implicit or - * explicit call of `asIterator(java.util.Enumeration)` then the - * original Java `Enumeration` will be returned. - * - * @param i The `Iterator` to be converted. - * @return An object with an `asJavaEnumeration` method that returns a Java - * `Enumeration` view of the argument. + * Adds an `asJavaEnumeration` method that implicitly converts a Scala `Iterator` to a Java + * `Enumeration`. See [[asJavaEnumeration]]. */ implicit def asJavaEnumerationConverter[A](i : Iterator[A]): AsJavaEnumeration[A] = new AsJavaEnumeration(i) /** - * Adds an `asJava` method that implicitly converts a Scala `Iterable` to - * a Java `Iterable`. - * - * The returned Java `Iterable` is backed by the provided Scala `Iterable` - * and any side-effects of using it via the Java interface will be visible - * via the Scala interface and vice versa. - * - * If the Scala `Iterable` was previously obtained from an implicit or - * explicit call of `asIterable(java.lang.Iterable)` then the original - * Java `Iterable` will be returned. - * - * @param i The `Iterable` to be converted. - * @return An object with an `asJavaCollection` method that returns a Java - * `Iterable` view of the argument. + * Adds an `asJava` method that implicitly converts a Scala `Iterable` to a Java `Iterable`. + * See [[asJavaIterable]]. */ implicit def asJavaIterableConverter[A](i : Iterable[A]): AsJava[jl.Iterable[A]] = new AsJava(asJavaIterable(i)) /** - * Adds an `asJavaCollection` method that implicitly converts a Scala - * `Iterable` to an immutable Java `Collection`. - * - * If the Scala `Iterable` was previously obtained from an implicit or - * explicit call of `asSizedIterable(java.util.Collection)` then the - * original Java `Collection` will be returned. - * - * @param i The `SizedIterable` to be converted. - * @return An object with an `asJava` method that returns a Java - * `Collection` view of the argument. + * Adds an `asJavaCollection` method that implicitly converts a Scala `Iterable` to an immutable + * Java `Collection`. See [[asJavaCollection]]. */ implicit def asJavaCollectionConverter[A](i : Iterable[A]): AsJavaCollection[A] = new AsJavaCollection(i) /** - * Adds an `asJava` method that implicitly converts a Scala mutable `Buffer` - * to a Java `List`. - * - * The returned Java `List` is backed by the provided Scala `Buffer` and any - * side-effects of using it via the Java interface will be visible via the - * Scala interface and vice versa. - * - * If the Scala `Buffer` was previously obtained from an implicit or explicit - * call of `asBuffer(java.util.List)` then the original Java `List` will be - * returned. - * - * @param b The `Buffer` to be converted. - * @return An object with an `asJava` method that returns a Java `List` view - * of the argument. + * Adds an `asJava` method that implicitly converts a Scala mutable `Buffer` to a Java `List`. + * See [[bufferAsJavaList]]. */ implicit def bufferAsJavaListConverter[A](b : mutable.Buffer[A]): AsJava[ju.List[A]] = new AsJava(bufferAsJavaList(b)) /** - * Adds an `asJava` method that implicitly converts a Scala mutable `Seq` - * to a Java `List`. - * - * The returned Java `List` is backed by the provided Scala `Seq` and any - * side-effects of using it via the Java interface will be visible via the - * Scala interface and vice versa. - * - * If the Scala `Seq` was previously obtained from an implicit or explicit - * call of `asSeq(java.util.List)` then the original Java `List` will be - * returned. - * - * @param b The `Seq` to be converted. - * @return An object with an `asJava` method that returns a Java `List` - * view of the argument. + * Adds an `asJava` method that implicitly converts a Scala mutable `Seq` to a Java `List`. + * See [[mutableSeqAsJavaList]]. */ implicit def mutableSeqAsJavaListConverter[A](b : mutable.Seq[A]): AsJava[ju.List[A]] = new AsJava(mutableSeqAsJavaList(b)) /** - * Adds an `asJava` method that implicitly converts a Scala `Seq` to a - * Java `List`. - * - * The returned Java `List` is backed by the provided Scala `Seq` and any - * side-effects of using it via the Java interface will be visible via the - * Scala interface and vice versa. - * - * If the Scala `Seq` was previously obtained from an implicit or explicit - * call of `asSeq(java.util.List)` then the original Java `List` will be - * returned. - * - * @param b The `Seq` to be converted. - * @return An object with an `asJava` method that returns a Java `List` - * view of the argument. + * Adds an `asJava` method that implicitly converts a Scala `Seq` to a Java `List`. + * See [[seqAsJavaList]]. */ implicit def seqAsJavaListConverter[A](b : Seq[A]): AsJava[ju.List[A]] = new AsJava(seqAsJavaList(b)) /** - * Adds an `asJava` method that implicitly converts a Scala mutable `Set`> - * to a Java `Set`. - * - * The returned Java `Set` is backed by the provided Scala `Set` and any - * side-effects of using it via the Java interface will be visible via - * the Scala interface and vice versa. - * - * If the Scala `Set` was previously obtained from an implicit or explicit - * call of `asSet(java.util.Set)` then the original Java `Set` will be - * returned. - * - * @param s The `Set` to be converted. - * @return An object with an `asJava` method that returns a Java `Set` view - * of the argument. + * Adds an `asJava` method that implicitly converts a Scala mutable `Set` to a Java `Set`. + * See [[mutableSetAsJavaSet]]. */ implicit def mutableSetAsJavaSetConverter[A](s : mutable.Set[A]): AsJava[ju.Set[A]] = new AsJava(mutableSetAsJavaSet(s)) /** - * Adds an `asJava` method that implicitly converts a Scala `Set` to a - * Java `Set`. - * - * The returned Java `Set` is backed by the provided Scala `Set` and any - * side-effects of using it via the Java interface will be visible via - * the Scala interface and vice versa. - * - * If the Scala `Set` was previously obtained from an implicit or explicit - * call of `asSet(java.util.Set)` then the original Java `Set` will be - * returned. - * - * @param s The `Set` to be converted. - * @return An object with an `asJava` method that returns a Java `Set` view - * of the argument. + * Adds an `asJava` method that implicitly converts a Scala `Set` to a Java `Set`. + * See [[setAsJavaSet]]. */ implicit def setAsJavaSetConverter[A](s : Set[A]): AsJava[ju.Set[A]] = new AsJava(setAsJavaSet(s)) /** - * Adds an `asJava` method that implicitly converts a Scala mutable `Map` - * to a Java `Map`. - * - * The returned Java `Map` is backed by the provided Scala `Map` and any - * side-effects of using it via the Java interface will be visible via the - * Scala interface and vice versa. - * - * If the Scala `Map` was previously obtained from an implicit or explicit - * call of `asMap(java.util.Map)` then the original Java `Map` will be - * returned. - * - * @param m The `Map` to be converted. - * @return An object with an `asJava` method that returns a Java `Map` view - * of the argument. + * Adds an `asJava` method that implicitly converts a Scala mutable `Map` to a Java `Map`. + * See [[mutableMapAsJavaMap]]. */ implicit def mutableMapAsJavaMapConverter[A, B](m : mutable.Map[A, B]): AsJava[ju.Map[A, B]] = new AsJava(mutableMapAsJavaMap(m)) /** - * Adds an `asJavaDictionary` method that implicitly converts a Scala - * mutable `Map` to a Java `Dictionary`. - * - * The returned Java `Dictionary` is backed by the provided Scala - * `Dictionary` and any side-effects of using it via the Java interface - * will be visible via the Scala interface and vice versa. - * - * If the Scala `Dictionary` was previously obtained from an implicit or - * explicit call of `asMap(java.util.Dictionary)` then the original - * Java `Dictionary` will be returned. - * - * @param m The `Map` to be converted. - * @return An object with an `asJavaDictionary` method that returns a - * Java `Dictionary` view of the argument. + * Adds an `asJavaDictionary` method that implicitly converts a Scala mutable `Map` to a Java + * `Dictionary`. See [[asJavaDictionary]]. */ implicit def asJavaDictionaryConverter[A, B](m : mutable.Map[A, B]): AsJavaDictionary[A, B] = new AsJavaDictionary(m) /** - * Adds an `asJava` method that implicitly converts a Scala `Map` to - * a Java `Map`. - * - * The returned Java `Map` is backed by the provided Scala `Map` and any - * side-effects of using it via the Java interface will be visible via - * the Scala interface and vice versa. - * - * If the Scala `Map` was previously obtained from an implicit or explicit - * call of `asMap(java.util.Map)` then the original Java `Map` will be - * returned. - * - * @param m The `Map` to be converted. - * @return An object with an `asJava` method that returns a Java `Map` view - * of the argument. + * Adds an `asJava` method that implicitly converts a Scala `Map` to a Java `Map`. + * See [[mapAsJavaMap]]. */ implicit def mapAsJavaMapConverter[A, B](m : Map[A, B]): AsJava[ju.Map[A, B]] = new AsJava(mapAsJavaMap(m)) /** - * Adds an `asJava` method that implicitly converts a Scala mutable - * `concurrent.Map` to a Java `ConcurrentMap`. - * - * The returned Java `ConcurrentMap` is backed by the provided Scala - * `concurrent.Map` and any side-effects of using it via the Java interface - * will be visible via the Scala interface and vice versa. - * - * If the Scala `concurrent.Map` was previously obtained from an implicit or - * explicit call of `asConcurrentMap(java.util.concurrent.ConcurrentMap)` - * then the original Java `ConcurrentMap` will be returned. - * - * @param m The Scala `concurrent.Map` to be converted. - * @return An object with an `asJava` method that returns a Java - * `ConcurrentMap` view of the argument. + * Adds an `asJava` method that implicitly converts a Scala mutable `concurrent.Map` to a Java + * `ConcurrentMap`. See [[mapAsJavaConcurrentMap]]. */ implicit def mapAsJavaConcurrentMapConverter[A, B](m: concurrent.Map[A, B]): AsJava[juc.ConcurrentMap[A, B]] = new AsJava(mapAsJavaConcurrentMap(m)) diff --git a/src/library/scala/collection/convert/DecorateAsScala.scala b/src/library/scala/collection/convert/DecorateAsScala.scala index 5448f5f91c..b74a06ece5 100644 --- a/src/library/scala/collection/convert/DecorateAsScala.scala +++ b/src/library/scala/collection/convert/DecorateAsScala.scala @@ -1,6 +1,6 @@ /* __ *\ ** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** / __/ __// _ | / / / _ | (c) 2006-2016, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://www.scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** @@ -12,185 +12,76 @@ package convert import java.{ lang => jl, util => ju }, java.util.{ concurrent => juc } import Decorators._ -import WrapAsScala._ import scala.language.implicitConversions -trait DecorateAsScala { +/** Defines `asScala` extension methods for [[JavaConverters]]. */ +trait DecorateAsScala extends AsScalaConverters { /** - * Adds an `asScala` method that implicitly converts a Java `Iterator` to - * a Scala `Iterator`. - * - * The returned Scala `Iterator` is backed by the provided Java `Iterator` - * and any side-effects of using it via the Scala interface will be visible - * via the Java interface and vice versa. - * - * If the Java `Iterator` was previously obtained from an implicit or - * explicit call of `asIterator(scala.collection.Iterator)` then the - * original Scala `Iterator` will be returned. - * - * @param i The `Iterator` to be converted. - * @return An object with an `asScala` method that returns a Scala - * `Iterator` view of the argument. + * Adds an `asScala` method that implicitly converts a Java `Iterator` to a Scala `Iterator`. + * See [[asScalaIterator]]. */ implicit def asScalaIteratorConverter[A](i : ju.Iterator[A]): AsScala[Iterator[A]] = new AsScala(asScalaIterator(i)) /** - * Adds an `asScala` method that implicitly converts a Java `Enumeration` - * to a Scala `Iterator`. - * - * The returned Scala `Iterator` is backed by the provided Java - * `Enumeration` and any side-effects of using it via the Scala interface - * will be visible via the Java interface and vice versa. - * - * If the Java `Enumeration` was previously obtained from an implicit or - * explicit call of `asEnumeration(scala.collection.Iterator)` then the - * original Scala `Iterator` will be returned. - * - * @param i The `Enumeration` to be converted. - * @return An object with an `asScala` method that returns a Scala - * `Iterator` view of the argument. + * Adds an `asScala` method that implicitly converts a Java `Enumeration` to a Scala `Iterator`. + * See [[enumerationAsScalaIterator]]. */ implicit def enumerationAsScalaIteratorConverter[A](i : ju.Enumeration[A]): AsScala[Iterator[A]] = new AsScala(enumerationAsScalaIterator(i)) /** - * Adds an `asScala` method that implicitly converts a Java `Iterable` to - * a Scala `Iterable`. - * - * The returned Scala `Iterable` is backed by the provided Java `Iterable` - * and any side-effects of using it via the Scala interface will be visible - * via the Java interface and vice versa. - * - * If the Java `Iterable` was previously obtained from an implicit or - * explicit call of `asIterable(scala.collection.Iterable)` then the original - * Scala `Iterable` will be returned. - * - * @param i The `Iterable` to be converted. - * @return An object with an `asScala` method that returns a Scala `Iterable` - * view of the argument. + * Adds an `asScala` method that implicitly converts a Java `Iterable` to a Scala `Iterable`. + * See [[iterableAsScalaIterable]]. */ implicit def iterableAsScalaIterableConverter[A](i : jl.Iterable[A]): AsScala[Iterable[A]] = new AsScala(iterableAsScalaIterable(i)) /** - * Adds an `asScala` method that implicitly converts a Java `Collection` to - * an Scala `Iterable`. - * - * If the Java `Collection` was previously obtained from an implicit or - * explicit call of `asCollection(scala.collection.SizedIterable)` then - * the original Scala `SizedIterable` will be returned. - * - * @param i The `Collection` to be converted. - * @return An object with an `asScala` method that returns a Scala - * `SizedIterable` view of the argument. + * Adds an `asScala` method that implicitly converts a Java `Collection` to an Scala `Iterable`. + * See [[collectionAsScalaIterable]]. */ implicit def collectionAsScalaIterableConverter[A](i : ju.Collection[A]): AsScala[Iterable[A]] = new AsScala(collectionAsScalaIterable(i)) /** - * Adds an `asScala` method that implicitly converts a Java `List` to a - * Scala mutable `Buffer`. - * - * The returned Scala `Buffer` is backed by the provided Java `List` and - * any side-effects of using it via the Scala interface will be visible via - * the Java interface and vice versa. - * - * If the Java `List` was previously obtained from an implicit or explicit - * call of `asList(scala.collection.mutable.Buffer)` then the original - * Scala `Buffer` will be returned. - * - * @param l The `List` to be converted. - * @return An object with an `asScala` method that returns a Scala mutable - * `Buffer` view of the argument. + * Adds an `asScala` method that implicitly converts a Java `List` to a Scala mutable `Buffer`. + * See [[asScalaBuffer]]. */ implicit def asScalaBufferConverter[A](l : ju.List[A]): AsScala[mutable.Buffer[A]] = new AsScala(asScalaBuffer(l)) /** - * Adds an `asScala` method that implicitly converts a Java `Set` to a - * Scala mutable `Set`. - * - * The returned Scala `Set` is backed by the provided Java `Set` and any - * side-effects of using it via the Scala interface will be visible via - * the Java interface and vice versa. - * - * If the Java `Set` was previously obtained from an implicit or explicit - * call of `asSet(scala.collection.mutable.Set)` then the original - * Scala `Set` will be returned. - * - * @param s The `Set` to be converted. - * @return An object with an `asScala` method that returns a Scala mutable - * `Set` view of the argument. + * Adds an `asScala` method that implicitly converts a Java `Set` to a Scala mutable `Set`. + * See [[asScalaSet]]. */ implicit def asScalaSetConverter[A](s : ju.Set[A]): AsScala[mutable.Set[A]] = new AsScala(asScalaSet(s)) /** - * Adds an `asScala` method that implicitly converts a Java `Map` to a Scala - * mutable `Map`. The returned Scala `Map` is backed by the provided Java - * `Map` and any side-effects of using it via the Scala interface will - * be visible via the Java interface and vice versa. - * - * If the Java `Map` was previously obtained from an implicit or explicit - * call of `asMap(scala.collection.mutable.Map)` then the original - * Scala `Map` will be returned. - * - * If the wrapped map is synchronized (e.g. from `java.util.Collections.synchronizedMap`), - * it is your responsibility to wrap all - * non-atomic operations with `underlying.synchronized`. - * This includes `get`, as `java.util.Map`'s API does not allow for an - * atomic `get` when `null` values may be present. - * - * @param m The `Map` to be converted. - * @return An object with an `asScala` method that returns a Scala mutable - * `Map` view of the argument. + * Adds an `asScala` method that implicitly converts a Java `Map` to a Scala mutable `Map`. + * See [[mapAsScalaMap]]. */ implicit def mapAsScalaMapConverter[A, B](m : ju.Map[A, B]): AsScala[mutable.Map[A, B]] = new AsScala(mapAsScalaMap(m)) /** - * Adds an `asScala` method that implicitly converts a Java `ConcurrentMap` - * to a Scala mutable `concurrent.Map`. The returned Scala `concurrent.Map` is - * backed by the provided Java `ConcurrentMap` and any side-effects of using - * it via the Scala interface will be visible via the Java interface and - * vice versa. - * - * If the Java `ConcurrentMap` was previously obtained from an implicit or - * explicit call of `mapAsScalaConcurrentMap(scala.collection.mutable.ConcurrentMap)` - * then the original Scala `concurrent.Map` will be returned. - * - * @param m The `ConcurrentMap` to be converted. - * @return An object with an `asScala` method that returns a Scala mutable - * `concurrent.Map` view of the argument. + * Adds an `asScala` method that implicitly converts a Java `ConcurrentMap` to a Scala mutable + * `concurrent.Map`. See [[mapAsScalaConcurrentMap]]. */ implicit def mapAsScalaConcurrentMapConverter[A, B](m: juc.ConcurrentMap[A, B]): AsScala[concurrent.Map[A, B]] = new AsScala(mapAsScalaConcurrentMap(m)) /** - * Adds an `asScala` method that implicitly converts a Java `Dictionary` - * to a Scala mutable `Map[String, String]`. The returned Scala - * `Map[String, String]` is backed by the provided Java `Dictionary` and - * any side-effects of using it via the Scala interface will be visible via - * the Java interface and vice versa. - * - * @param p The `Dictionary` to be converted. - * @return An object with an `asScala` method that returns a Scala mutable - * `Map[String, String]` view of the argument. + * Adds an `asScala` method that implicitly converts a Java `Dictionary` to a Scala mutable `Map`. + * See [[dictionaryAsScalaMap]]. */ implicit def dictionaryAsScalaMapConverter[A, B](p: ju.Dictionary[A, B]): AsScala[mutable.Map[A, B]] = new AsScala(dictionaryAsScalaMap(p)) /** - * Adds an `asScala` method that implicitly converts a Java `Properties` - * to a Scala mutable `Map[String, String]`. The returned Scala - * `Map[String, String]` is backed by the provided Java `Properties` and - * any side-effects of using it via the Scala interface will be visible via - * the Java interface and vice versa. - * - * @param p The `Properties` to be converted. - * @return An object with an `asScala` method that returns a Scala mutable - * `Map[String, String]` view of the argument. + * Adds an `asScala` method that implicitly converts a Java `Properties` to a Scala mutable + * `Map[String, String]`. See [[propertiesAsScalaMap]]. */ implicit def propertiesAsScalaMapConverter(p: ju.Properties): AsScala[mutable.Map[String, String]] = new AsScala(propertiesAsScalaMap(p)) diff --git a/src/library/scala/collection/convert/Decorators.scala b/src/library/scala/collection/convert/Decorators.scala index d232fa04e1..3e45a02254 100644 --- a/src/library/scala/collection/convert/Decorators.scala +++ b/src/library/scala/collection/convert/Decorators.scala @@ -12,7 +12,7 @@ package convert import java.{ util => ju } -private[collection] trait Decorators { +private[collection] object Decorators { /** Generic class containing the `asJava` converter method */ class AsJava[A](op: => A) { /** Converts a Scala collection to the corresponding Java collection */ @@ -28,20 +28,18 @@ private[collection] trait Decorators { /** Generic class containing the `asJavaCollection` converter method */ class AsJavaCollection[A](i: Iterable[A]) { /** Converts a Scala `Iterable` to a Java `Collection` */ - def asJavaCollection: ju.Collection[A] = JavaConversions.asJavaCollection(i) + def asJavaCollection: ju.Collection[A] = JavaConverters.asJavaCollection(i) } /** Generic class containing the `asJavaEnumeration` converter method */ class AsJavaEnumeration[A](i: Iterator[A]) { /** Converts a Scala `Iterator` to a Java `Enumeration` */ - def asJavaEnumeration: ju.Enumeration[A] = JavaConversions.asJavaEnumeration(i) + def asJavaEnumeration: ju.Enumeration[A] = JavaConverters.asJavaEnumeration(i) } /** Generic class containing the `asJavaDictionary` converter method */ class AsJavaDictionary[A, B](m : mutable.Map[A, B]) { /** Converts a Scala `Map` to a Java `Dictionary` */ - def asJavaDictionary: ju.Dictionary[A, B] = JavaConversions.asJavaDictionary(m) + def asJavaDictionary: ju.Dictionary[A, B] = JavaConverters.asJavaDictionary(m) } } - -private[collection] object Decorators extends Decorators diff --git a/src/library/scala/collection/convert/ImplicitConversions.scala b/src/library/scala/collection/convert/ImplicitConversions.scala new file mode 100644 index 0000000000..747e0606c8 --- /dev/null +++ b/src/library/scala/collection/convert/ImplicitConversions.scala @@ -0,0 +1,125 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2016, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://www.scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala +package collection +package convert + +import java.{ lang => jl, util => ju }, java.util.{ concurrent => juc } +import scala.language.implicitConversions + +import JavaConverters._ + +/** Defines implicit converter methods from Java to Scala collections. */ +trait ToScalaImplicits { + /** Implicitly converts a Java `Iterator` to a Scala `Iterator`. See [[asScalaIterator]]. */ + implicit def `iterator asScala`[A](it: ju.Iterator[A]): Iterator[A] = asScalaIterator(it) + + /** Implicitly converts a Java `Enumeration` to a Scala `Iterator`. See [[enumerationAsScalaIterator]]. */ + implicit def `enumeration AsScalaIterator`[A](i: ju.Enumeration[A]): Iterator[A] = enumerationAsScalaIterator(i) + + /** Implicitly converts a Java `Iterable` to a Scala `Iterable`. See [[iterableAsScalaIterable]]. */ + implicit def `iterable AsScalaIterable`[A](i: jl.Iterable[A]): Iterable[A] = iterableAsScalaIterable(i) + + /** Implicitly converts a Java `Collection` to an Scala `Iterable`. See [[collectionAsScalaIterable]]. */ + implicit def `collection AsScalaIterable`[A](i: ju.Collection[A]): Iterable[A] = collectionAsScalaIterable(i) + + /** Implicitly converts a Java `List` to a Scala mutable `Buffer`. See [[asScalaBuffer]]. */ + implicit def `list asScalaBuffer`[A](l: ju.List[A]): mutable.Buffer[A] = asScalaBuffer(l) + + /** Implicitly converts a Java `Set` to a Scala mutable `Set`. See [[asScalaSet]]. */ + implicit def `set asScala`[A](s: ju.Set[A]): mutable.Set[A] = asScalaSet(s) + + /** Implicitly converts a Java `Map` to a Scala mutable `Map`. See [[mapAsScalaMap]]. */ + implicit def `map AsScala`[A, B](m: ju.Map[A, B]): mutable.Map[A, B] = mapAsScalaMap(m) + + /** Implicitly converts a Java `ConcurrentMap` to a Scala mutable `ConcurrentMap`. See [[mapAsScalaConcurrentMap]]. */ + implicit def `map AsScalaConcurrentMap`[A, B](m: juc.ConcurrentMap[A, B]): concurrent.Map[A, B] = mapAsScalaConcurrentMap(m) + + /** Implicitly converts a Java `Dictionary` to a Scala mutable `Map`. See [[dictionaryAsScalaMap]]. */ + implicit def `dictionary AsScalaMap`[A, B](p: ju.Dictionary[A, B]): mutable.Map[A, B] = dictionaryAsScalaMap(p) + + /** Implicitly converts a Java `Properties` to a Scala `mutable Map[String, String]`. See [[propertiesAsScalaMap]]. */ + implicit def `properties AsScalaMap`(p: ju.Properties): mutable.Map[String, String] = propertiesAsScalaMap(p) +} + +/** Defines implicit conversions from Scala to Java collections. */ +trait ToJavaImplicits { + /** Implicitly converts a Scala `Iterator` to a Java `Iterator`. See [[asJavaIterator]]. */ + implicit def `iterator asJava`[A](it: Iterator[A]): ju.Iterator[A] = asJavaIterator(it) + + /** Implicitly converts a Scala `Iterator` to a Java `Enumeration`. See [[asJavaEnumeration]]. */ + implicit def `enumeration asJava`[A](it: Iterator[A]): ju.Enumeration[A] = asJavaEnumeration(it) + + /** Implicitly converts a Scala `Iterable` to a Java `Iterable`. See [[asJavaIterable]]. */ + implicit def `iterable asJava`[A](i: Iterable[A]): jl.Iterable[A] = asJavaIterable(i) + + /** Implicitly converts a Scala `Iterable` to an immutable Java `Collection`. See [[asJavaCollection]]. */ + implicit def `collection asJava`[A](it: Iterable[A]): ju.Collection[A] = asJavaCollection(it) + + /** Implicitly converts a Scala mutable `Buffer` to a Java `List`. See [[bufferAsJavaList]]. */ + implicit def `buffer AsJavaList`[A](b: mutable.Buffer[A]): ju.List[A] = bufferAsJavaList(b) + + /** Implicitly converts a Scala mutable `Seq` to a Java `List`. See [[mutableSeqAsJavaList]]. */ + implicit def `mutableSeq AsJavaList`[A](seq: mutable.Seq[A]): ju.List[A] = mutableSeqAsJavaList(seq) + + /** Implicitly converts a Scala `Seq` to a Java `List`. See [[seqAsJavaList]]. */ + implicit def `seq AsJavaList`[A](seq: Seq[A]): ju.List[A] = seqAsJavaList(seq) + + /** Implicitly converts a Scala mutable `Set` to a Java `Set`. See [[mutableSetAsJavaSet]]. */ + implicit def `mutableSet AsJavaSet`[A](s: mutable.Set[A]): ju.Set[A] = mutableSetAsJavaSet(s) + + /** Implicitly converts a Scala `Set` to a Java `Set`. See [[setAsJavaSet]]. */ + implicit def `set AsJavaSet`[A](s: Set[A]): ju.Set[A] = setAsJavaSet(s) + + /** Implicitly converts a Scala mutable `Map` to a Java `Map`. See [[mutableMapAsJavaMap]]. */ + implicit def `mutableMap AsJavaMap`[A, B](m: mutable.Map[A, B]): ju.Map[A, B] = mutableMapAsJavaMap(m) + + /** Implicitly converts a Scala mutable `Map` to a Java `Dictionary`. See [[asJavaDictionary]]. */ + implicit def `dictionary asJava`[A, B](m: mutable.Map[A, B]): ju.Dictionary[A, B] = asJavaDictionary(m) + + /** Implicitly converts a Scala `Map` to a Java `Map`. See [[mapAsJavaMap]]. */ + implicit def `map AsJavaMap`[A, B](m: Map[A, B]): ju.Map[A, B] = mapAsJavaMap(m) + + /** Implicitly converts a Scala mutable `concurrent.Map` to a Java `ConcurrentMap`. See [[mapAsJavaConcurrentMap]]. */ + implicit def `map AsJavaConcurrentMap`[A, B](m: concurrent.Map[A, B]): juc.ConcurrentMap[A, B] = mapAsJavaConcurrentMap(m) +} + +/** + * Convenience for miscellaneous implicit conversions from Scala to Java collections API. + * + * It is recommended to use explicit conversions provided by [[collection.JavaConverters]] instead. + * Implicit conversions may cause unexpected issues, see [[ImplicitConversions]]. + */ +object ImplicitConversionsToJava extends ToJavaImplicits + +/** + * Convenience for miscellaneous implicit conversions from Java to Scala collections API. + * + * It is recommended to use explicit conversions provided by [[collection.JavaConverters]] instead. + * Implicit conversions may cause unexpected issues, see [[ImplicitConversions]]. + */ +object ImplicitConversionsToScala extends ToScalaImplicits + +/** + * Convenience for miscellaneous implicit conversions between Java and Scala collections API. + * + * It is recommended to use explicit conversions provided by [[collection.JavaConverters]] instead. + * Implicit conversions may cause unexpected issues. Example: + * + * {{{ + * import collection.convert.ImplicitConversions._ + * case class StringBox(s: String) + * val m = Map(StringBox("one") -> "uno") + * m.get("one") + * }}} + * + * The above example returns `null` instead of producing a type error at compile-time. The map is + * implicitly converted to a `java.util.Map` which provides a method `get(x: AnyRef)`. + */ +object ImplicitConversions extends ToScalaImplicits with ToJavaImplicits diff --git a/src/library/scala/collection/convert/WrapAsJava.scala b/src/library/scala/collection/convert/WrapAsJava.scala index e97a2ff1fc..e45c1666a5 100644 --- a/src/library/scala/collection/convert/WrapAsJava.scala +++ b/src/library/scala/collection/convert/WrapAsJava.scala @@ -13,7 +13,27 @@ package convert import java.{ lang => jl, util => ju }, java.util.{ concurrent => juc } import scala.language.implicitConversions -trait WrapAsJava { +@deprecated("Use JavaConverters or consider ToJavaImplicits", since="2.12") +trait WrapAsJava extends LowPriorityWrapAsJava { + // provide higher-priority implicits with names that don't exist in JavaConverters for the case + // when importing both JavaConverters._ and JavaConversions._. otherwise implicit conversions + // would not apply, see https://github.com/scala/scala/pull/5109#issuecomment-212417789 + implicit def `deprecated asJavaIterator`[A](it: Iterator[A]): ju.Iterator[A] = asJavaIterator(it) + implicit def `deprecated asJavaEnumeration`[A](it: Iterator[A]): ju.Enumeration[A] = asJavaEnumeration(it) + implicit def `deprecated asJavaIterable`[A](i: Iterable[A]): jl.Iterable[A] = asJavaIterable(i) + implicit def `deprecated asJavaCollection`[A](it: Iterable[A]): ju.Collection[A] = asJavaCollection(it) + implicit def `deprecated bufferAsJavaList`[A](b: mutable.Buffer[A]): ju.List[A] = bufferAsJavaList(b) + implicit def `deprecated mutableSeqAsJavaList`[A](seq: mutable.Seq[A]): ju.List[A] = mutableSeqAsJavaList(seq) + implicit def `deprecated seqAsJavaList`[A](seq: Seq[A]): ju.List[A] = seqAsJavaList(seq) + implicit def `deprecated mutableSetAsJavaSet`[A](s: mutable.Set[A]): ju.Set[A] = mutableSetAsJavaSet(s) + implicit def `deprecated setAsJavaSet`[A](s: Set[A]): ju.Set[A] = setAsJavaSet(s) + implicit def `deprecated mutableMapAsJavaMap`[A, B](m: mutable.Map[A, B]): ju.Map[A, B] = mutableMapAsJavaMap(m) + implicit def `deprecated asJavaDictionary`[A, B](m: mutable.Map[A, B]): ju.Dictionary[A, B] = asJavaDictionary(m) + implicit def `deprecated mapAsJavaMap`[A, B](m: Map[A, B]): ju.Map[A, B] = mapAsJavaMap(m) + implicit def `deprecated mapAsJavaConcurrentMap`[A, B](m: concurrent.Map[A, B]): juc.ConcurrentMap[A, B] = mapAsJavaConcurrentMap(m) +} + +private[convert] trait LowPriorityWrapAsJava { import Wrappers._ /** @@ -266,4 +286,5 @@ trait WrapAsJava { } } -object WrapAsJava extends WrapAsJava { } +@deprecated("Use JavaConverters or consider ImplicitConversionsToJava", since="2.12") +object WrapAsJava extends WrapAsJava diff --git a/src/library/scala/collection/convert/WrapAsScala.scala b/src/library/scala/collection/convert/WrapAsScala.scala index ee161dcc87..514490e348 100644 --- a/src/library/scala/collection/convert/WrapAsScala.scala +++ b/src/library/scala/collection/convert/WrapAsScala.scala @@ -13,8 +13,26 @@ package convert import java.{ lang => jl, util => ju }, java.util.{ concurrent => juc } import scala.language.implicitConversions -trait WrapAsScala { +@deprecated("Use JavaConverters or consider ToScalaImplicits", since="2.12") +trait WrapAsScala extends LowPriorityWrapAsScala { + // provide higher-priority implicits with names that don't exist in JavaConverters for the case + // when importing both JavaConverters._ and JavaConversions._. otherwise implicit conversions + // would not apply, see https://github.com/scala/scala/pull/5109#issuecomment-212417789 + implicit def `deprecated asScalaIterator`[A](it: ju.Iterator[A]): Iterator[A] = asScalaIterator(it) + implicit def `deprecated enumerationAsScalaIterator`[A](i: ju.Enumeration[A]): Iterator[A] = enumerationAsScalaIterator(i) + implicit def `deprecated iterableAsScalaIterable`[A](i: jl.Iterable[A]): Iterable[A] = iterableAsScalaIterable(i) + implicit def `deprecated collectionAsScalaIterable`[A](i: ju.Collection[A]): Iterable[A] = collectionAsScalaIterable(i) + implicit def `deprecated asScalaBuffer`[A](l: ju.List[A]): mutable.Buffer[A] = asScalaBuffer(l) + implicit def `deprecated asScalaSet`[A](s: ju.Set[A]): mutable.Set[A] = asScalaSet(s) + implicit def `deprecated mapAsScalaMap`[A, B](m: ju.Map[A, B]): mutable.Map[A, B] = mapAsScalaMap(m) + implicit def `deprecated mapAsScalaConcurrentMap`[A, B](m: juc.ConcurrentMap[A, B]): concurrent.Map[A, B] = mapAsScalaConcurrentMap(m) + implicit def `deprecated dictionaryAsScalaMap`[A, B](p: ju.Dictionary[A, B]): mutable.Map[A, B] = dictionaryAsScalaMap(p) + implicit def `deprecated propertiesAsScalaMap`(p: ju.Properties): mutable.Map[String, String] = propertiesAsScalaMap(p) +} + +private[convert] trait LowPriorityWrapAsScala { import Wrappers._ + /** * Implicitly converts a Java `Iterator` to a Scala `Iterator`. * @@ -207,4 +225,5 @@ trait WrapAsScala { } } -object WrapAsScala extends WrapAsScala { } +@deprecated("Use JavaConverters or consider ImplicitConversionsToScala", since="2.12") +object WrapAsScala extends WrapAsScala diff --git a/src/library/scala/collection/convert/Wrappers.scala b/src/library/scala/collection/convert/Wrappers.scala index 3edc5ba1b4..6d745bc6b0 100644 --- a/src/library/scala/collection/convert/Wrappers.scala +++ b/src/library/scala/collection/convert/Wrappers.scala @@ -14,10 +14,7 @@ import java.{ lang => jl, util => ju }, java.util.{ concurrent => juc } import WrapAsScala._ import WrapAsJava._ -/** Don't put the implementations in the same scope as the implicits - * which utilize them, or they will stow away into every scope which - * extends one of those implementations. See SI-5580. - */ +/** Adapters for Java/Scala collections API. */ private[collection] trait Wrappers { trait IterableWrapperTrait[A] extends ju.AbstractCollection[A] { val underlying: Iterable[A] diff --git a/src/library/scala/collection/convert/package.scala b/src/library/scala/collection/convert/package.scala index 13970f9a3e..7f48023b58 100644 --- a/src/library/scala/collection/convert/package.scala +++ b/src/library/scala/collection/convert/package.scala @@ -10,10 +10,17 @@ package scala package collection package object convert { + @deprecated("Use JavaConverters", since="2.12") val decorateAsJava = new DecorateAsJava { } + @deprecated("Use JavaConverters", since="2.12") val decorateAsScala = new DecorateAsScala { } - val decorateAll = new DecorateAsJava with DecorateAsScala { } + @deprecated("Use JavaConverters", since="2.12") + val decorateAll = JavaConverters + + @deprecated("Use JavaConverters or consider ImplicitConversionsToJava", since="2.12") val wrapAsJava = new WrapAsJava { } + @deprecated("Use JavaConverters or consider ImplicitConversionsToScala", since="2.12") val wrapAsScala = new WrapAsScala { } + @deprecated("Use JavaConverters or consider ImplicitConversions", since="2.12") val wrapAll = new WrapAsJava with WrapAsScala { } } diff --git a/src/library/scala/collection/generic/BitOperations.scala b/src/library/scala/collection/generic/BitOperations.scala index d430ece2f5..2f460eee1f 100644 --- a/src/library/scala/collection/generic/BitOperations.scala +++ b/src/library/scala/collection/generic/BitOperations.scala @@ -26,16 +26,7 @@ private[collection] object BitOperations { def complement(i: Int) = (-1) ^ i def bits(num: Int) = 31 to 0 by -1 map (i => (num >>> i & 1) != 0) def bitString(num: Int, sep: String = "") = bits(num) map (b => if (b) "1" else "0") mkString sep - - def highestOneBit(j: Int) = { - var i = j - i |= (i >> 1) - i |= (i >> 2) - i |= (i >> 4) - i |= (i >> 8) - i |= (i >> 16) - i - (i >>> 1) - } + def highestOneBit(j: Int) = java.lang.Integer.highestOneBit(j) } object Int extends Int @@ -49,17 +40,7 @@ private[collection] object BitOperations { def complement(i: Long) = (-1L) ^ i def bits(num: Long) = 63L to 0L by -1L map (i => (num >>> i & 1L) != 0L) def bitString(num: Long, sep: String = "") = bits(num) map (b => if (b) "1" else "0") mkString sep - - def highestOneBit(j: Long) = { - var i = j - i |= (i >> 1) - i |= (i >> 2) - i |= (i >> 4) - i |= (i >> 8) - i |= (i >> 16) - i |= (i >> 32) - i - (i >>> 1) - } + def highestOneBit(j: Long) = java.lang.Long.highestOneBit(j) } object Long extends Long } diff --git a/src/library/scala/collection/immutable/BitSet.scala b/src/library/scala/collection/immutable/BitSet.scala index 846bc22182..ecf3326c7f 100644 --- a/src/library/scala/collection/immutable/BitSet.scala +++ b/src/library/scala/collection/immutable/BitSet.scala @@ -68,6 +68,8 @@ object BitSet extends BitSetFactory[BitSet] { /** The empty bitset */ val empty: BitSet = new BitSet1(0L) + private def createSmall(a: Long, b: Long): BitSet = if (b == 0L) new BitSet1(a) else new BitSet2(a, b) + /** A builder that takes advantage of mutable BitSets. */ def newBuilder: Builder[Int, BitSet] = new Builder[Int, BitSet] { private[this] val b = new mutable.BitSet @@ -84,7 +86,7 @@ object BitSet extends BitSetFactory[BitSet] { val len = elems.length if (len == 0) empty else if (len == 1) new BitSet1(elems(0)) - else if (len == 2) new BitSet2(elems(0), elems(1)) + else if (len == 2) createSmall(elems(0), elems(1)) else { val a = new Array[Long](len) Array.copy(elems, 0, a, 0, len) @@ -99,17 +101,24 @@ object BitSet extends BitSetFactory[BitSet] { val len = elems.length if (len == 0) empty else if (len == 1) new BitSet1(elems(0)) - else if (len == 2) new BitSet2(elems(0), elems(1)) + else if (len == 2) createSmall(elems(0), elems(1)) else new BitSetN(elems) } + @SerialVersionUID(2260107458435649300L) class BitSet1(val elems: Long) extends BitSet { protected def nwords = 1 protected def word(idx: Int) = if (idx == 0) elems else 0L protected def updateWord(idx: Int, w: Long): BitSet = if (idx == 0) new BitSet1(w) - else if (idx == 1) new BitSet2(elems, w) + else if (idx == 1) createSmall(elems, w) else fromBitMaskNoCopy(updateArray(Array(elems), idx, w)) + override def head: Int = + if (elems == 0L) throw new NoSuchElementException("Empty BitSet") + else java.lang.Long.numberOfTrailingZeros(elems) + override def tail: BitSet = + if (elems == 0L) throw new NoSuchElementException("Empty BitSet") + else new BitSet1(elems - java.lang.Long.lowestOneBit(elems)) } class BitSet2(val elems0: Long, elems1: Long) extends BitSet { @@ -117,8 +126,20 @@ object BitSet extends BitSetFactory[BitSet] { protected def word(idx: Int) = if (idx == 0) elems0 else if (idx == 1) elems1 else 0L protected def updateWord(idx: Int, w: Long): BitSet = if (idx == 0) new BitSet2(w, elems1) - else if (idx == 1) new BitSet2(elems0, w) + else if (idx == 1) createSmall(elems0, w) else fromBitMaskNoCopy(updateArray(Array(elems0, elems1), idx, w)) + override def head: Int = + if (elems0 == 0L) { + if (elems1 == 0) throw new NoSuchElementException("Empty BitSet") + 64 + java.lang.Long.numberOfTrailingZeros(elems1) + } + else java.lang.Long.numberOfTrailingZeros(elems0) + override def tail: BitSet = + if (elems0 == 0L) { + if (elems1 == 0L) throw new NoSuchElementException("Empty BitSet") + createSmall(elems0, elems1 - java.lang.Long.lowestOneBit(elems1)) + } + else new BitSet2(elems0 - java.lang.Long.lowestOneBit(elems0), elems1) } /** The implementing class for bit sets with elements >= 128 (exceeding @@ -131,5 +152,15 @@ object BitSet extends BitSetFactory[BitSet] { protected def nwords = elems.length protected def word(idx: Int) = if (idx < nwords) elems(idx) else 0L protected def updateWord(idx: Int, w: Long): BitSet = fromBitMaskNoCopy(updateArray(elems, idx, w)) + override def tail: BitSet = { + val n = nwords + var i = 0 + while (i < n) { + val wi = word(i) + if (wi != 0L) return fromBitMaskNoCopy(updateArray(elems, i, wi - java.lang.Long.lowestOneBit(wi))) + i += 1 + } + throw new NoSuchElementException("Empty BitSet") + } } } diff --git a/src/library/scala/collection/immutable/HashMap.scala b/src/library/scala/collection/immutable/HashMap.scala index 92d915fe8b..eac9c14f3f 100644 --- a/src/library/scala/collection/immutable/HashMap.scala +++ b/src/library/scala/collection/immutable/HashMap.scala @@ -33,8 +33,7 @@ import parallel.immutable.ParHashMap * @define willNotTerminateInf */ @SerialVersionUID(2L) -@deprecatedInheritance("The implementation details of immutable hash maps make inheriting from them unwise.", "2.11.0") -class HashMap[A, +B] extends AbstractMap[A, B] +sealed class HashMap[A, +B] extends AbstractMap[A, B] with Map[A, B] with MapLike[A, B, HashMap[A, B]] with Serializable @@ -65,6 +64,8 @@ class HashMap[A, +B] extends AbstractMap[A, B] def - (key: A): HashMap[A, B] = removed0(key, computeHash(key), 0) + override def tail: HashMap[A, B] = this - head._1 + override def filter(p: ((A, B)) => Boolean) = { val buffer = new Array[HashMap[A, B]](bufferSize(size)) nullToEmpty(filter0(p, false, 0, buffer, 0)) @@ -156,7 +157,10 @@ object HashMap extends ImmutableMapFactory[HashMap] with BitOperations.Int { implicit def canBuildFrom[A, B]: CanBuildFrom[Coll, (A, B), HashMap[A, B]] = new MapCanBuildFrom[A, B] def empty[A, B]: HashMap[A, B] = EmptyHashMap.asInstanceOf[HashMap[A, B]] - private object EmptyHashMap extends HashMap[Any, Nothing] { } + private object EmptyHashMap extends HashMap[Any, Nothing] { + override def head: (Any, Nothing) = throw new NoSuchElementException("Empty Map") + override def tail: HashMap[Any, Nothing] = throw new NoSuchElementException("Empty Map") + } // utility method to create a HashTrieMap from two leaf HashMaps (HashMap1 or HashMapCollision1) with non-colliding hash code) private def makeHashTrieMap[A, B](hash0:Int, elem0:HashMap[A, B], hash1:Int, elem1:HashMap[A, B], level:Int, size:Int) : HashTrieMap[A, B] = { diff --git a/src/library/scala/collection/immutable/HashSet.scala b/src/library/scala/collection/immutable/HashSet.scala index 07758bf5a2..fc937e3a22 100644 --- a/src/library/scala/collection/immutable/HashSet.scala +++ b/src/library/scala/collection/immutable/HashSet.scala @@ -31,8 +31,7 @@ import scala.annotation.tailrec * @define coll immutable hash set */ @SerialVersionUID(2L) -@deprecatedInheritance("The implementation details of immutable hash sets make inheriting from them unwise.", "2.11.0") -class HashSet[A] extends AbstractSet[A] +sealed class HashSet[A] extends AbstractSet[A] with Set[A] with GenericSetTemplate[A, HashSet] with SetLike[A, HashSet[A]] @@ -162,6 +161,8 @@ class HashSet[A] extends AbstractSet[A] def - (e: A): HashSet[A] = nullToEmpty(removed0(e, computeHash(e), 0)) + override def tail: HashSet[A] = this - head + override def filter(p: A => Boolean) = { val buffer = new Array[HashSet[A]](bufferSize(size)) nullToEmpty(filter0(p, false, 0, buffer, 0)) @@ -213,7 +214,10 @@ object HashSet extends ImmutableSetFactory[HashSet] { /** $setCanBuildFromInfo */ implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, HashSet[A]] = setCanBuildFrom[A] - private object EmptyHashSet extends HashSet[Any] { } + private object EmptyHashSet extends HashSet[Any] { + override def head: Any = throw new NoSuchElementException("Empty Set") + override def tail: HashSet[Any] = throw new NoSuchElementException("Empty Set") + } private[collection] def emptyInstance: HashSet[Any] = EmptyHashSet // utility method to create a HashTrieSet from two leaf HashSets (HashSet1 or HashSetCollision1) with non-colliding hash code) diff --git a/src/library/scala/collection/immutable/ListMap.scala b/src/library/scala/collection/immutable/ListMap.scala index f30c0cbf8e..589f8bbba9 100644 --- a/src/library/scala/collection/immutable/ListMap.scala +++ b/src/library/scala/collection/immutable/ListMap.scala @@ -6,8 +6,6 @@ ** |/ ** \* */ - - package scala package collection package immutable @@ -15,206 +13,154 @@ package immutable import generic._ import scala.annotation.tailrec -/** $factoryInfo - * @since 1 - * @see [[http://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#list_maps "Scala's Collection Library overview"]] - * section on `List Maps` for more information. - * - * @define Coll immutable.ListMap - * @define coll immutable list map - */ +/** + * $factoryInfo + * + * Note that each element insertion takes O(n) time, which means that creating a list map with + * n elements will take O(n^2^) time. This makes the builder suitable only for a small number of + * elements. + * + * @see [[http://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#list_maps "Scala's Collection Library overview"]] + * section on `List Maps` for more information. + * @since 1 + * @define Coll ListMap + * @define coll list map + */ object ListMap extends ImmutableMapFactory[ListMap] { - /** $mapCanBuildFromInfo */ + + /** + * $mapCanBuildFromInfo + */ implicit def canBuildFrom[A, B]: CanBuildFrom[Coll, (A, B), ListMap[A, B]] = new MapCanBuildFrom[A, B] + def empty[A, B]: ListMap[A, B] = EmptyListMap.asInstanceOf[ListMap[A, B]] @SerialVersionUID(-8256686706655863282L) - private object EmptyListMap extends ListMap[Any, Nothing] { - override def apply(key: Any) = throw new NoSuchElementException("key not found: " + key) - override def contains(key: Any) = false - } + private object EmptyListMap extends ListMap[Any, Nothing] } -/** This class implements immutable maps using a list-based data structure, which preserves insertion order. - * Instances of `ListMap` represent empty maps; they can be either created by - * calling the constructor directly, or by applying the function `ListMap.empty`. - * - * @tparam A the type of the keys in this list map. - * @tparam B the type of the values associated with the keys. - * - * @author Matthias Zenger - * @author Martin Odersky - * @version 2.0, 01/01/2007 - * @since 1 - * @define Coll immutable.ListMap - * @define coll immutable list map - * @define mayNotTerminateInf - * @define willNotTerminateInf - */ +/** + * This class implements immutable maps using a list-based data structure. List map iterators and + * traversal methods visit key-value pairs in the order whey were first inserted. + * + * Entries are stored internally in reversed insertion order, which means the newest key is at the + * head of the list. As such, methods such as `head` and `tail` are O(n), while `last` and `init` + * are O(1). Other operations, such as inserting or removing entries, are also O(n), which makes + * this collection suitable only for a small number of elements. + * + * Instances of `ListMap` represent empty maps; they can be either created by calling the + * constructor directly, or by applying the function `ListMap.empty`. + * + * @tparam A the type of the keys contained in this list map + * @tparam B the type of the values associated with the keys + * + * @author Matthias Zenger + * @author Martin Odersky + * @version 2.0, 01/01/2007 + * @since 1 + * @define Coll ListMap + * @define coll list map + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ @SerialVersionUID(301002838095710379L) -@deprecatedInheritance("The semantics of immutable collections makes inheriting from ListMap error-prone.", "2.11.0") -class ListMap[A, +B] -extends AbstractMap[A, B] - with Map[A, B] - with MapLike[A, B, ListMap[A, B]] - with Serializable { +sealed class ListMap[A, +B] extends AbstractMap[A, B] + with Map[A, B] + with MapLike[A, B, ListMap[A, B]] + with Serializable { override def empty = ListMap.empty - /** Returns the number of mappings in this map. - * - * @return number of mappings in this map. - */ override def size: Int = 0 + override def isEmpty: Boolean = true - /** Checks if this map maps `key` to a value and return the - * value if it exists. - * - * @param key the key of the mapping of interest - * @return the value of the mapping, if it exists - */ def get(key: A): Option[B] = None - /** This method allows one to create a new map with an additional mapping - * from `key` to `value`. If the map contains already a mapping for `key`, - * it will be overridden by this function. - * - * @param key the key element of the updated entry. - * @param value the value element of the updated entry. - */ - override def updated [B1 >: B] (key: A, value: B1): ListMap[A, B1] = - new Node[B1](key, value) - - /** Add a key/value pair to this map. - * @param kv the key/value pair - * @return A new map with the new binding added to this map - */ - def + [B1 >: B] (kv: (A, B1)): ListMap[A, B1] = updated(kv._1, kv._2) - - /** Adds two or more elements to this collection and returns - * either the collection itself (if it is mutable), or a new collection - * with the added elements. - * - * @param elem1 the first element to add. - * @param elem2 the second element to add. - * @param elems the remaining elements to add. - */ - override def + [B1 >: B] (elem1: (A, B1), elem2: (A, B1), elems: (A, B1) *): ListMap[A, B1] = - this + elem1 + elem2 ++ elems - - /** Adds a number of elements provided by a traversable object - * and returns a new collection with the added elements. - * - * @param xs the traversable object. - */ + override def updated[B1 >: B](key: A, value: B1): ListMap[A, B1] = new Node[B1](key, value) + + def +[B1 >: B](kv: (A, B1)): ListMap[A, B1] = new Node[B1](kv._1, kv._2) + def -(key: A): ListMap[A, B] = this + override def ++[B1 >: B](xs: GenTraversableOnce[(A, B1)]): ListMap[A, B1] = - ((repr: ListMap[A, B1]) /: xs.seq) (_ + _) - - /** This creates a new mapping without the given `key`. - * If the map does not contain a mapping for the given key, the - * method returns the same map. - * - * @param key a map without a mapping for the given key. - */ - def - (key: A): ListMap[A, B] = this - - /** Returns an iterator over key-value pairs. - */ - def iterator: Iterator[(A,B)] = - new AbstractIterator[(A,B)] { - var self: ListMap[A,B] = ListMap.this - def hasNext = !self.isEmpty - def next(): (A,B) = - if (!hasNext) throw new NoSuchElementException("next on empty iterator") - else { val res = (self.key, self.value); self = self.next; res } - }.toList.reverseIterator - - protected def key: A = throw new NoSuchElementException("empty map") - protected def value: B = throw new NoSuchElementException("empty map") - protected def next: ListMap[A, B] = throw new NoSuchElementException("empty map") - - /** This class represents an entry in the `ListMap`. - */ + if (xs.isEmpty) this + else ((repr: ListMap[A, B1]) /: xs) (_ + _) + + def iterator: Iterator[(A, B)] = { + def reverseList = { + var curr: ListMap[A, B] = this + var res: List[(A, B)] = Nil + while (!curr.isEmpty) { + res = (curr.key, curr.value) :: res + curr = curr.next + } + res + } + reverseList.iterator + } + + protected def key: A = throw new NoSuchElementException("key of empty map") + protected def value: B = throw new NoSuchElementException("value of empty map") + protected def next: ListMap[A, B] = throw new NoSuchElementException("next of empty map") + + override def stringPrefix = "ListMap" + + /** + * Represents an entry in the `ListMap`. + */ @SerialVersionUID(-6453056603889598734L) protected class Node[B1 >: B](override protected val key: A, override protected val value: B1) extends ListMap[A, B1] with Serializable { - /** Returns the number of mappings in this map. - * - * @return number of mappings. - */ - override def size: Int = size0(this, 0) - - // to allow tail recursion and prevent stack overflows - @tailrec private def size0(cur: ListMap[A, B1], acc: Int): Int = if (cur.isEmpty) acc else size0(cur.next, acc + 1) - - /** Is this an empty map? - * - * @return true, iff the map is empty. - */ - override def isEmpty: Boolean = false - /** Retrieves the value which is associated with the given key. This - * method throws an exception if there is no mapping from the given - * key to a value. - * - * @param k the key - * @return the value associated with the given key. - */ - override def apply(k: A): B1 = apply0(this, k) - - @tailrec private def apply0(cur: ListMap[A, B1], k: A): B1 = - if (cur.isEmpty) throw new NoSuchElementException("key not found: "+k) - else if (k == cur.key) cur.value - else apply0(cur.next, k) + override def size: Int = sizeInternal(this, 0) + + @tailrec private[this] def sizeInternal(cur: ListMap[A, B1], acc: Int): Int = + if (cur.isEmpty) acc + else sizeInternal(cur.next, acc + 1) - /** Checks if this map maps `key` to a value and return the - * value if it exists. - * - * @param k the key of the mapping of interest - * @return the value of the mapping, if it exists - */ - override def get(k: A): Option[B1] = get0(this, k) + override def isEmpty: Boolean = false + + override def apply(k: A): B1 = applyInternal(this, k) - @tailrec private def get0(cur: ListMap[A, B1], k: A): Option[B1] = - if (k == cur.key) Some(cur.value) - else if (cur.next.nonEmpty) get0(cur.next, k) else None + @tailrec private[this] def applyInternal(cur: ListMap[A, B1], k: A): B1 = + if (cur.isEmpty) throw new NoSuchElementException("key not found: " + k) + else if (k == cur.key) cur.value + else applyInternal(cur.next, k) + override def get(k: A): Option[B1] = getInternal(this, k) - override def contains(key: A): Boolean = contains0(this, key) + @tailrec private[this] def getInternal(cur: ListMap[A, B1], k: A): Option[B1] = + if (cur.isEmpty) None + else if (k == cur.key) Some(cur.value) + else getInternal(cur.next, k) - @tailrec private def contains0(cur: ListMap[A, B1], k: A): Boolean = - if (k == cur.key) true - else if (cur.next.nonEmpty) contains0(cur.next, k) - else false + override def contains(k: A): Boolean = containsInternal(this, k) + @tailrec private[this] def containsInternal(cur: ListMap[A, B1], k: A): Boolean = + if(cur.isEmpty) false + else if (k == cur.key) true + else containsInternal(cur.next, k) - /** This method allows one to create a new map with an additional mapping - * from `key` to `value`. If the map contains already a mapping for `key`, - * it will be overridden by this function. - */ - override def updated [B2 >: B1](k: A, v: B2): ListMap[A, B2] = { + override def updated[B2 >: B1](k: A, v: B2): ListMap[A, B2] = { val m = this - k new m.Node[B2](k, v) } + override def +[B2 >: B1](kv: (A, B2)): ListMap[A, B2] = { + val m = this - kv._1 + new m.Node[B2](kv._1, kv._2) + } - /** Creates a new mapping without the given `key`. - * If the map does not contain a mapping for the given key, the - * method returns the same map. - */ - override def - (k: A): ListMap[A, B1] = remove0(k, this, Nil) + override def -(k: A): ListMap[A, B1] = removeInternal(k, this, Nil) - @tailrec private def remove0(k: A, cur: ListMap[A, B1], acc: List[ListMap[A, B1]]): ListMap[A, B1] = - if (cur.isEmpty) - acc.last - else if (k == cur.key) - (cur.next /: acc) { - case (t, h) => val tt = t; new tt.Node(h.key, h.value) // SI-7459 - } - else - remove0(k, cur.next, cur::acc) + @tailrec private[this] def removeInternal(k: A, cur: ListMap[A, B1], acc: List[ListMap[A, B1]]): ListMap[A, B1] = + if (cur.isEmpty) acc.last + else if (k == cur.key) (cur.next /: acc) { case (t, h) => new t.Node(h.key, h.value) } + else removeInternal(k, cur.next, cur :: acc) override protected def next: ListMap[A, B1] = ListMap.this + + override def last: (A, B1) = (key, value) + override def init: ListMap[A, B1] = next } } diff --git a/src/library/scala/collection/immutable/ListSet.scala b/src/library/scala/collection/immutable/ListSet.scala index 98b91f7c84..d9795e9161 100644 --- a/src/library/scala/collection/immutable/ListSet.scala +++ b/src/library/scala/collection/immutable/ListSet.scala @@ -12,175 +12,125 @@ package immutable import generic._ import scala.annotation.tailrec -import mutable.{Builder, ReusableBuilder} -/** $factoryInfo - * @define Coll immutable.ListSet - * @define coll immutable list set - * @since 1 - */ +/** + * $factoryInfo + * + * Note that each element insertion takes O(n) time, which means that creating a list set with + * n elements will take O(n^2^) time. This makes the builder suitable only for a small number of + * elements. + * + * @since 1 + * @define Coll ListSet + * @define coll list set + */ object ListSet extends ImmutableSetFactory[ListSet] { - /** setCanBuildFromInfo */ - implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, ListSet[A]] = setCanBuildFrom[A] - override def newBuilder[A]: Builder[A, ListSet[A]] = new ListSetBuilder[A] + /** + * $setCanBuildFromInfo + */ + implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, ListSet[A]] = + setCanBuildFrom[A] - private object EmptyListSet extends ListSet[Any] { } + @SerialVersionUID(5010379588739277132L) + private object EmptyListSet extends ListSet[Any] private[collection] def emptyInstance: ListSet[Any] = EmptyListSet - - /** A custom builder because forgetfully adding elements one at - * a time to a list backed set puts the "squared" in N^2. There is a - * temporary space cost, but it's improbable a list backed set could - * become large enough for this to matter given its pricy element lookup. - * - * This builder is reusable. - */ - class ListSetBuilder[Elem](initial: ListSet[Elem]) extends ReusableBuilder[Elem, ListSet[Elem]] { - def this() = this(empty[Elem]) - protected val elems = (new mutable.ListBuffer[Elem] ++= initial).reverse - protected val seen = new mutable.HashSet[Elem] ++= initial - - def +=(x: Elem): this.type = { - if (!seen(x)) { - elems += x - seen += x - } - this - } - def clear() = { elems.clear() ; seen.clear() } - def result() = elems.foldLeft(empty[Elem])(_ unchecked_+ _) - } } -/** This class implements immutable sets using a list-based data - * structure. Instances of `ListSet` represent - * empty sets; they can be either created by calling the constructor - * directly, or by applying the function `ListSet.empty`. - * - * @tparam A the type of the elements contained in this list set. - * - * @author Matthias Zenger - * @version 1.0, 09/07/2003 - * @since 1 - * @define Coll immutable.ListSet - * @define coll immutable list set - * @define mayNotTerminateInf - * @define willNotTerminateInf - */ -@deprecatedInheritance("The semantics of immutable collections makes inheriting from ListSet error-prone.", "2.11.0") -class ListSet[A] extends AbstractSet[A] - with Set[A] - with GenericSetTemplate[A, ListSet] - with SetLike[A, ListSet[A]] - with Serializable{ self => +/** + * This class implements immutable sets using a list-based data structure. List set iterators and + * traversal methods visit elements in the order whey were first inserted. + * + * Elements are stored internally in reversed insertion order, which means the newest element is at + * the head of the list. As such, methods such as `head` and `tail` are O(n), while `last` and + * `init` are O(1). Other operations, such as inserting or removing entries, are also O(n), which + * makes this collection suitable only for a small number of elements. + * + * Instances of `ListSet` represent empty sets; they can be either created by calling the + * constructor directly, or by applying the function `ListSet.empty`. + * + * @tparam A the type of the elements contained in this list set + * + * @author Matthias Zenger + * @version 1.0, 09/07/2003 + * @since 1 + * @define Coll ListSet + * @define coll list set + * @define mayNotTerminateInf + * @define willNotTerminateInf + */ +@SerialVersionUID(-8417059026623606218L) +sealed class ListSet[A] extends AbstractSet[A] + with Set[A] + with GenericSetTemplate[A, ListSet] + with SetLike[A, ListSet[A]] + with Serializable { + override def companion: GenericCompanion[ListSet] = ListSet - /** Returns the number of elements in this set. - * - * @return number of set elements. - */ override def size: Int = 0 override def isEmpty: Boolean = true - /** Checks if this set contains element `elem`. - * - * @param elem the element to check for membership. - * @return `'''true'''`, iff `elem` is contained in this set. - */ def contains(elem: A): Boolean = false - /** This method creates a new set with an additional element. - */ - def + (elem: A): ListSet[A] = new Node(elem) - - /** `-` can be used to remove a single element. - */ - def - (elem: A): ListSet[A] = this + def +(elem: A): ListSet[A] = new Node(elem) + def -(elem: A): ListSet[A] = this - /** If we are bulk adding elements and desire a runtime measured in - * sub-interstellar time units, we better find a way to avoid traversing - * the collection on each element. That's what the custom builder does, - * so we take the easy way out and add ourselves and the argument to - * a new builder. - */ override def ++(xs: GenTraversableOnce[A]): ListSet[A] = if (xs.isEmpty) this - else (new ListSet.ListSetBuilder(this) ++= xs.seq).result() - - private[ListSet] def unchecked_+(e: A): ListSet[A] = new Node(e) - private[ListSet] def unchecked_outer: ListSet[A] = - throw new NoSuchElementException("Empty ListSet has no outer pointer") - - /** Creates a new iterator over all elements contained in this set. - * - * @throws java.util.NoSuchElementException - * @return the new iterator - */ - def iterator: Iterator[A] = new AbstractIterator[A] { - var that: ListSet[A] = self - def hasNext = that.nonEmpty - def next: A = - if (hasNext) { - val res = that.head - that = that.tail - res + else (repr /: xs) (_ + _) + + def iterator: Iterator[A] = { + def reverseList = { + var curr: ListSet[A] = this + var res: List[A] = Nil + while (!curr.isEmpty) { + res = curr.elem :: res + curr = curr.next } - else Iterator.empty.next() + res + } + reverseList.iterator } - /** - * @throws java.util.NoSuchElementException - */ - override def head: A = throw new NoSuchElementException("Set has no elements") + protected def elem: A = throw new NoSuchElementException("elem of empty set") + protected def next: ListSet[A] = throw new NoSuchElementException("next of empty set") - /** - * @throws java.util.NoSuchElementException - */ - override def tail: ListSet[A] = throw new NoSuchElementException("Next of an empty set") + override def toSet[B >: A]: Set[B] = this.asInstanceOf[ListSet[B]] override def stringPrefix = "ListSet" - /** Represents an entry in the `ListSet`. - */ - protected class Node(override val head: A) extends ListSet[A] with Serializable { - override private[ListSet] def unchecked_outer = self + /** + * Represents an entry in the `ListSet`. + */ + @SerialVersionUID(-787710309854855049L) + protected class Node(override protected val elem: A) extends ListSet[A] with Serializable { - /** Returns the number of elements in this set. - * - * @return number of set elements. - */ override def size = sizeInternal(this, 0) - @tailrec private def sizeInternal(n: ListSet[A], acc: Int): Int = + + @tailrec private[this] def sizeInternal(n: ListSet[A], acc: Int): Int = if (n.isEmpty) acc - else sizeInternal(n.unchecked_outer, acc + 1) + else sizeInternal(n.next, acc + 1) - /** Checks if this set is empty. - * - * @return true, iff there is no element in the set. - */ override def isEmpty: Boolean = false - /** Checks if this set contains element `elem`. - * - * @param e the element to check for membership. - * @return `'''true'''`, iff `elem` is contained in this set. - */ override def contains(e: A) = containsInternal(this, e) - @tailrec private def containsInternal(n: ListSet[A], e: A): Boolean = - !n.isEmpty && (n.head == e || containsInternal(n.unchecked_outer, e)) - /** This method creates a new set with an additional element. - */ + @tailrec private[this] def containsInternal(n: ListSet[A], e: A): Boolean = + !n.isEmpty && (n.elem == e || containsInternal(n.next, e)) + override def +(e: A): ListSet[A] = if (contains(e)) this else new Node(e) - /** `-` can be used to remove a single element from a set. - */ - override def -(e: A): ListSet[A] = if (e == head) self else { - val tail = self - e; new tail.Node(head) - } + override def -(e: A): ListSet[A] = removeInternal(e, this, Nil) - override def tail: ListSet[A] = self - } + @tailrec private[this] def removeInternal(k: A, cur: ListSet[A], acc: List[ListSet[A]]): ListSet[A] = + if (cur.isEmpty) acc.last + else if (k == cur.elem) (cur.next /: acc) { case (t, h) => new t.Node(h.elem) } + else removeInternal(k, cur.next, cur :: acc) - override def toSet[B >: A]: Set[B] = this.asInstanceOf[ListSet[B]] + override protected def next: ListSet[A] = ListSet.this + + override def last: A = elem + override def init: ListSet[A] = next + } } diff --git a/src/library/scala/collection/immutable/Queue.scala b/src/library/scala/collection/immutable/Queue.scala index 70b51f8251..3ad6656636 100644 --- a/src/library/scala/collection/immutable/Queue.scala +++ b/src/library/scala/collection/immutable/Queue.scala @@ -37,8 +37,7 @@ import mutable.{ Builder, ListBuffer } */ @SerialVersionUID(-7622936493364270175L) -@deprecatedInheritance("The implementation details of immutable queues make inheriting from them unwise.", "2.11.0") -class Queue[+A] protected(protected val in: List[A], protected val out: List[A]) +sealed class Queue[+A] protected(protected val in: List[A], protected val out: List[A]) extends AbstractSeq[A] with LinearSeq[A] with GenericTraversableTemplate[A, Queue] diff --git a/src/library/scala/collection/immutable/Range.scala b/src/library/scala/collection/immutable/Range.scala index fb7dd4cfbf..d3fe367e50 100644 --- a/src/library/scala/collection/immutable/Range.scala +++ b/src/library/scala/collection/immutable/Range.scala @@ -57,8 +57,7 @@ import scala.collection.parallel.immutable.ParRange * and its complexity is O(1). */ @SerialVersionUID(7618862778670199309L) -@deprecatedInheritance("The implementation details of Range makes inheriting from it unwise.", "2.11.0") -class Range(val start: Int, val end: Int, val step: Int) +sealed class Range(val start: Int, val end: Int, val step: Int) extends scala.collection.AbstractSeq[Int] with IndexedSeq[Int] with scala.collection.CustomParallelizable[Int, ParRange] diff --git a/src/library/scala/collection/immutable/Set.scala b/src/library/scala/collection/immutable/Set.scala index 031e5248c1..3a8ee8b0be 100644 --- a/src/library/scala/collection/immutable/Set.scala +++ b/src/library/scala/collection/immutable/Set.scala @@ -103,10 +103,11 @@ object Set extends ImmutableSetFactory[Set] { if (p(elem1)) Some(elem1) else None } + override def head: A = elem1 + override def tail: Set[A] = Set.empty // Why is Set1 non-final? Need to fix that! @deprecatedOverriding("This immutable set should do nothing on toSet but cast itself to a Set with a wider element type.", "2.11.8") override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set1[B]] - } /** An optimized representation for immutable sets of size 2 */ @@ -138,6 +139,8 @@ object Set extends ImmutableSetFactory[Set] { else if (p(elem2)) Some(elem2) else None } + override def head: A = elem1 + override def tail: Set[A] = new Set1(elem2) // Why is Set2 non-final? Need to fix that! @deprecatedOverriding("This immutable set should do nothing on toSet but cast itself to a Set with a wider element type.", "2.11.8") override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set2[B]] @@ -174,6 +177,8 @@ object Set extends ImmutableSetFactory[Set] { else if (p(elem3)) Some(elem3) else None } + override def head: A = elem1 + override def tail: Set[A] = new Set2(elem2, elem3) // Why is Set3 non-final? Need to fix that! @deprecatedOverriding("This immutable set should do nothing on toSet but cast itself to a Set with a wider element type.", "2.11.8") override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set3[B]] @@ -212,6 +217,8 @@ object Set extends ImmutableSetFactory[Set] { else if (p(elem4)) Some(elem4) else None } + override def head: A = elem1 + override def tail: Set[A] = new Set3(elem2, elem3, elem4) // Why is Set4 non-final? Need to fix that! @deprecatedOverriding("This immutable set should do nothing on toSet but cast itself to a Set with a wider element type.", "2.11.8") override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set4[B]] diff --git a/src/library/scala/collection/immutable/Stream.scala b/src/library/scala/collection/immutable/Stream.scala index d92b159f3b..d135bb29a8 100644 --- a/src/library/scala/collection/immutable/Stream.scala +++ b/src/library/scala/collection/immutable/Stream.scala @@ -198,8 +198,7 @@ import scala.language.implicitConversions * @define orderDependentFold * @define willTerminateInf Note: lazily evaluated; will terminate for infinite-sized collections. */ -@deprecatedInheritance("This class will be sealed.", "2.11.0") -abstract class Stream[+A] extends AbstractSeq[A] +sealed abstract class Stream[+A] extends AbstractSeq[A] with LinearSeq[A] with GenericTraversableTemplate[A, Stream] with LinearSeqOptimized[A, Stream[A]] @@ -358,7 +357,7 @@ abstract class Stream[+A] extends AbstractSeq[A] * `List(BigInt(12)) ++ fibs`. * * @tparam B The element type of the returned collection.'''That''' - * @param that The [[scala.collection.GenTraversableOnce]] the be concatenated + * @param that The [[scala.collection.GenTraversableOnce]] to be concatenated * to this `Stream`. * @return A new collection containing the result of concatenating `this` with * `that`. diff --git a/src/library/scala/collection/immutable/StringLike.scala b/src/library/scala/collection/immutable/StringLike.scala index d92db68912..8a9df0e862 100644 --- a/src/library/scala/collection/immutable/StringLike.scala +++ b/src/library/scala/collection/immutable/StringLike.scala @@ -100,11 +100,13 @@ self => /** Return all lines in this string in an iterator, including trailing * line end characters. * - * The number of strings returned is one greater than the number of line - * end characters in this string. For an empty string, a single empty - * line is returned. A line end character is one of - * - `LF` - line feed (`0x0A` hex) - * - `FF` - form feed (`0x0C` hex) + * This method is analogous to `s.split(EOL).toIterator`, + * except that any existing line endings are preserved in the result strings, + * and the empty string yields an empty iterator. + * + * A line end character is one of + * - `LF` - line feed (`0x0A`) + * - `FF` - form feed (`0x0C`) */ def linesWithSeparators: Iterator[String] = new AbstractIterator[String] { val str = self.toString @@ -121,14 +123,14 @@ self => } /** Return all lines in this string in an iterator, excluding trailing line - * end characters, i.e., apply `.stripLineEnd` to all lines + * end characters; i.e., apply `.stripLineEnd` to all lines * returned by `linesWithSeparators`. */ def lines: Iterator[String] = linesWithSeparators map (line => new WrappedString(line).stripLineEnd) /** Return all lines in this string in an iterator, excluding trailing line - * end characters, i.e., apply `.stripLineEnd` to all lines + * end characters; i.e., apply `.stripLineEnd` to all lines * returned by `linesWithSeparators`. */ @deprecated("Use `lines` instead.","2.11.0") diff --git a/src/library/scala/collection/immutable/TreeMap.scala b/src/library/scala/collection/immutable/TreeMap.scala index b845b76026..2d1bf0f6b1 100644 --- a/src/library/scala/collection/immutable/TreeMap.scala +++ b/src/library/scala/collection/immutable/TreeMap.scala @@ -44,8 +44,7 @@ object TreeMap extends ImmutableSortedMapFactory[TreeMap] { * @define mayNotTerminateInf * @define willNotTerminateInf */ -@deprecatedInheritance("The implementation details of immutable tree maps make inheriting from them unwise.", "2.11.0") -class TreeMap[A, +B] private (tree: RB.Tree[A, B])(implicit val ordering: Ordering[A]) +final class TreeMap[A, +B] private (tree: RB.Tree[A, B])(implicit val ordering: Ordering[A]) extends SortedMap[A, B] with SortedMapLike[A, B, TreeMap[A, B]] with MapLike[A, B, TreeMap[A, B]] diff --git a/src/library/scala/collection/immutable/TreeSet.scala b/src/library/scala/collection/immutable/TreeSet.scala index 2800030d67..2cdf3b3521 100644 --- a/src/library/scala/collection/immutable/TreeSet.scala +++ b/src/library/scala/collection/immutable/TreeSet.scala @@ -49,8 +49,7 @@ object TreeSet extends ImmutableSortedSetFactory[TreeSet] { * @define willNotTerminateInf */ @SerialVersionUID(-5685982407650748405L) -@deprecatedInheritance("The implementation details of immutable tree sets make inheriting from them unwise.", "2.11.0") -class TreeSet[A] private (tree: RB.Tree[A, Unit])(implicit val ordering: Ordering[A]) +final class TreeSet[A] private (tree: RB.Tree[A, Unit])(implicit val ordering: Ordering[A]) extends SortedSet[A] with SortedSetLike[A, TreeSet[A]] with Serializable { if (ordering eq null) diff --git a/src/library/scala/collection/immutable/WrappedString.scala b/src/library/scala/collection/immutable/WrappedString.scala index 7592316650..8726bd2ed9 100644 --- a/src/library/scala/collection/immutable/WrappedString.scala +++ b/src/library/scala/collection/immutable/WrappedString.scala @@ -29,8 +29,7 @@ import mutable.{Builder, StringBuilder} * @define Coll `WrappedString` * @define coll wrapped string */ -@deprecatedInheritance("Inherit from StringLike instead of WrappedString.", "2.11.0") -class WrappedString(val self: String) extends AbstractSeq[Char] with IndexedSeq[Char] with StringLike[WrappedString] { +final class WrappedString(val self: String) extends AbstractSeq[Char] with IndexedSeq[Char] with StringLike[WrappedString] { override protected[this] def thisCollection: WrappedString = this override protected[this] def toCollection(repr: WrappedString): WrappedString = repr diff --git a/src/library/scala/collection/mutable/AnyRefMap.scala b/src/library/scala/collection/mutable/AnyRefMap.scala index 2ed5bbea60..6ff79dd1b8 100644 --- a/src/library/scala/collection/mutable/AnyRefMap.scala +++ b/src/library/scala/collection/mutable/AnyRefMap.scala @@ -419,7 +419,11 @@ object AnyRefMap { private final val VacantBit = 0x40000000 private final val MissVacant = 0xC0000000 - private val exceptionDefault = (k: Any) => throw new NoSuchElementException(if (k == null) "(null)" else k.toString) + @SerialVersionUID(1L) + private class ExceptionDefault extends (Any => Nothing) with Serializable { + def apply(k: Any): Nothing = throw new NoSuchElementException(if (k == null) "(null)" else k.toString) + } + private val exceptionDefault = new ExceptionDefault implicit def canBuildFrom[K <: AnyRef, V, J <: AnyRef, U]: CanBuildFrom[AnyRefMap[K,V], (J, U), AnyRefMap[J,U]] = new CanBuildFrom[AnyRefMap[K,V], (J, U), AnyRefMap[J,U]] { diff --git a/src/library/scala/collection/mutable/ArrayBuilder.scala b/src/library/scala/collection/mutable/ArrayBuilder.scala index 549ffef565..d023110c1b 100644 --- a/src/library/scala/collection/mutable/ArrayBuilder.scala +++ b/src/library/scala/collection/mutable/ArrayBuilder.scala @@ -53,8 +53,7 @@ object ArrayBuilder { * * @tparam T type of elements for the array builder, subtype of `AnyRef` with a `ClassTag` context bound. */ - @deprecatedInheritance("ArrayBuilder.ofRef is an internal implementation not intended for subclassing.", "2.11.0") - class ofRef[T <: AnyRef : ClassTag] extends ArrayBuilder[T] { + final class ofRef[T <: AnyRef : ClassTag] extends ArrayBuilder[T] { private var elems: Array[T] = _ private var capacity: Int = 0 @@ -119,8 +118,7 @@ object ArrayBuilder { } /** A class for array builders for arrays of `byte`s. It can be reused. */ - @deprecatedInheritance("ArrayBuilder.ofByte is an internal implementation not intended for subclassing.", "2.11.0") - class ofByte extends ArrayBuilder[Byte] { + final class ofByte extends ArrayBuilder[Byte] { private var elems: Array[Byte] = _ private var capacity: Int = 0 @@ -185,8 +183,7 @@ object ArrayBuilder { } /** A class for array builders for arrays of `short`s. It can be reused. */ - @deprecatedInheritance("ArrayBuilder.ofShort is an internal implementation not intended for subclassing.", "2.11.0") - class ofShort extends ArrayBuilder[Short] { + final class ofShort extends ArrayBuilder[Short] { private var elems: Array[Short] = _ private var capacity: Int = 0 @@ -251,8 +248,7 @@ object ArrayBuilder { } /** A class for array builders for arrays of `char`s. It can be reused. */ - @deprecatedInheritance("ArrayBuilder.ofChar is an internal implementation not intended for subclassing.", "2.11.0") - class ofChar extends ArrayBuilder[Char] { + final class ofChar extends ArrayBuilder[Char] { private var elems: Array[Char] = _ private var capacity: Int = 0 @@ -317,8 +313,7 @@ object ArrayBuilder { } /** A class for array builders for arrays of `int`s. It can be reused. */ - @deprecatedInheritance("ArrayBuilder.ofInt is an internal implementation not intended for subclassing.", "2.11.0") - class ofInt extends ArrayBuilder[Int] { + final class ofInt extends ArrayBuilder[Int] { private var elems: Array[Int] = _ private var capacity: Int = 0 @@ -383,8 +378,7 @@ object ArrayBuilder { } /** A class for array builders for arrays of `long`s. It can be reused. */ - @deprecatedInheritance("ArrayBuilder.ofLong is an internal implementation not intended for subclassing.", "2.11.0") - class ofLong extends ArrayBuilder[Long] { + final class ofLong extends ArrayBuilder[Long] { private var elems: Array[Long] = _ private var capacity: Int = 0 @@ -449,8 +443,7 @@ object ArrayBuilder { } /** A class for array builders for arrays of `float`s. It can be reused. */ - @deprecatedInheritance("ArrayBuilder.ofFloat is an internal implementation not intended for subclassing.", "2.11.0") - class ofFloat extends ArrayBuilder[Float] { + final class ofFloat extends ArrayBuilder[Float] { private var elems: Array[Float] = _ private var capacity: Int = 0 @@ -515,8 +508,7 @@ object ArrayBuilder { } /** A class for array builders for arrays of `double`s. It can be reused. */ - @deprecatedInheritance("ArrayBuilder.ofDouble is an internal implementation not intended for subclassing.", "2.11.0") - class ofDouble extends ArrayBuilder[Double] { + final class ofDouble extends ArrayBuilder[Double] { private var elems: Array[Double] = _ private var capacity: Int = 0 @@ -646,8 +638,7 @@ object ArrayBuilder { } /** A class for array builders for arrays of `Unit` type. It can be reused. */ - @deprecatedInheritance("ArrayBuilder.ofUnit is an internal implementation not intended for subclassing.", "2.11.0") - class ofUnit extends ArrayBuilder[Unit] { + final class ofUnit extends ArrayBuilder[Unit] { private var size: Int = 0 diff --git a/src/library/scala/collection/mutable/ArrayOps.scala b/src/library/scala/collection/mutable/ArrayOps.scala index 5144db7de3..507585b9cf 100644 --- a/src/library/scala/collection/mutable/ArrayOps.scala +++ b/src/library/scala/collection/mutable/ArrayOps.scala @@ -11,7 +11,6 @@ package collection package mutable import scala.reflect.ClassTag -import scala.runtime.ScalaRunTime._ import parallel.mutable.ParArray /** This class serves as a wrapper for `Array`s with all the operations found in @@ -32,11 +31,10 @@ import parallel.mutable.ParArray * @define mayNotTerminateInf * @define willNotTerminateInf */ -@deprecatedInheritance("ArrayOps will be sealed to facilitate greater flexibility with array/collections integration in future releases.", "2.11.0") -trait ArrayOps[T] extends Any with ArrayLike[T, Array[T]] with CustomParallelizable[T, ParArray[T]] { +sealed trait ArrayOps[T] extends Any with ArrayLike[T, Array[T]] with CustomParallelizable[T, ParArray[T]] { private def elementClass: Class[_] = - arrayElementClass(repr.getClass) + repr.getClass.getComponentType override def copyToArray[U >: T](xs: Array[U], start: Int, len: Int) { val l = len min repr.length min (xs.length - start) @@ -44,7 +42,7 @@ trait ArrayOps[T] extends Any with ArrayLike[T, Array[T]] with CustomParalleliza } override def toArray[U >: T : ClassTag]: Array[U] = { - val thatElementClass = arrayElementClass(implicitly[ClassTag[U]]) + val thatElementClass = implicitly[ClassTag[U]].runtimeClass if (elementClass eq thatElementClass) repr.asInstanceOf[Array[U]] else @@ -92,7 +90,7 @@ trait ArrayOps[T] extends Any with ArrayLike[T, Array[T]] with CustomParalleliza val bb: Builder[Array[U], Array[Array[U]]] = Array.newBuilder(ClassTag[Array[U]](elementClass)) if (isEmpty) bb.result() else { - def mkRowBuilder() = Array.newBuilder(ClassTag[U](arrayElementClass(elementClass))) + def mkRowBuilder() = Array.newBuilder(ClassTag[U](elementClass.getComponentType)) val bs = asArray(head) map (_ => mkRowBuilder()) for (xs <- this) { var i = 0 @@ -185,7 +183,7 @@ object ArrayOps { override protected[this] def thisCollection: WrappedArray[T] = new WrappedArray.ofRef[T](repr) override protected[this] def toCollection(repr: Array[T]): WrappedArray[T] = new WrappedArray.ofRef[T](repr) - override protected[this] def newBuilder = new ArrayBuilder.ofRef[T]()(ClassTag[T](arrayElementClass(repr.getClass))) + override protected[this] def newBuilder = new ArrayBuilder.ofRef[T]()(ClassTag[T](repr.getClass.getComponentType)) def length: Int = repr.length def apply(index: Int): T = repr(index) diff --git a/src/library/scala/collection/mutable/IndexedSeqView.scala b/src/library/scala/collection/mutable/IndexedSeqView.scala index 7acdeeff18..b525baaf5f 100644 --- a/src/library/scala/collection/mutable/IndexedSeqView.scala +++ b/src/library/scala/collection/mutable/IndexedSeqView.scala @@ -15,7 +15,6 @@ package mutable import generic._ import TraversableView.NoBuilder -import scala.language.implicitConversions /** A non-strict view of a mutable `IndexedSeq`. * $viewInfo diff --git a/src/library/scala/collection/mutable/ListMap.scala b/src/library/scala/collection/mutable/ListMap.scala index 2ea5b1fa7c..e963af4a8a 100644 --- a/src/library/scala/collection/mutable/ListMap.scala +++ b/src/library/scala/collection/mutable/ListMap.scala @@ -15,7 +15,7 @@ package mutable import generic._ import annotation.tailrec -/** A simple mutable map backed by a list. +/** A simple mutable map backed by a list, so it preserves insertion order. * * @tparam A the type of the keys contained in this list map. * @tparam B the type of the values assigned to keys in this list map. diff --git a/src/library/scala/collection/mutable/PriorityQueue.scala b/src/library/scala/collection/mutable/PriorityQueue.scala index e6889da3b5..d5b7673c37 100644 --- a/src/library/scala/collection/mutable/PriorityQueue.scala +++ b/src/library/scala/collection/mutable/PriorityQueue.scala @@ -46,8 +46,7 @@ import generic._ * @define mayNotTerminateInf * @define willNotTerminateInf */ -@deprecatedInheritance("PriorityQueue is not intended to be subclassed due to extensive private implementation details.", "2.11.0") -class PriorityQueue[A](implicit val ord: Ordering[A]) +sealed class PriorityQueue[A](implicit val ord: Ordering[A]) extends AbstractIterable[A] with Iterable[A] with GenericOrderedTraversableTemplate[A, PriorityQueue] @@ -201,9 +200,7 @@ class PriorityQueue[A](implicit val ord: Ordering[A]) * @return A reversed priority queue. */ def reverse = { - val revq = new PriorityQueue[A]()(new scala.math.Ordering[A] { - def compare(x: A, y: A) = ord.compare(y, x) - }) + val revq = new PriorityQueue[A]()(ord.reverse) for (i <- 1 until resarr.length) revq += resarr(i) revq } @@ -266,3 +263,176 @@ object PriorityQueue extends OrderedTraversableFactory[PriorityQueue] { implicit def canBuildFrom[A](implicit ord: Ordering[A]): CanBuildFrom[Coll, A, PriorityQueue[A]] = new GenericCanBuildFrom[A] } + +/** This class servers as a proxy for priority queues. The + * elements of the queue have to be ordered in terms of the + * `Ordered[T]` class. + * + * @author Matthias Zenger + * @version 1.0, 03/05/2004 + * @since 1 + */ +@deprecated("Proxying is deprecated due to lack of use and compiler-level support.", "2.11.0") +sealed abstract class PriorityQueueProxy[A](implicit ord: Ordering[A]) extends PriorityQueue[A] + with Proxy +{ + def self: PriorityQueue[A] + + /** Creates a new iterator over all elements contained in this + * object. + * + * @return the new iterator + */ + override def iterator: Iterator[A] = self.iterator + + /** Returns the length of this priority queue. + */ + override def length: Int = self.length + + /** Checks if the queue is empty. + * + * @return true, iff there is no element in the queue. + */ + override def isEmpty: Boolean = self.isEmpty + + /** Inserts a single element into the priority queue. + * + * @param elem the element to insert + */ + override def +=(elem: A): this.type = { self += elem; this } + + /** Adds all elements provided by an iterator into the priority queue. + * + * @param it an iterator + */ + override def ++=(it: TraversableOnce[A]): this.type = { + self ++= it + this + } + + /** Adds all elements to the queue. + * + * @param elems the elements to add. + */ + override def enqueue(elems: A*): Unit = self ++= elems + + /** Returns the element with the highest priority in the queue, + * and removes this element from the queue. + * + * @return the element with the highest priority. + */ + override def dequeue(): A = self.dequeue() + + /** Returns the element with the highest priority in the queue, + * or throws an error if there is no element contained in the queue. + * + * @return the element with the highest priority. + */ + override def head: A = self.head + + /** Removes all elements from the queue. After this operation is completed, + * the queue will be empty. + */ + override def clear(): Unit = self.clear() + + /** Returns a regular queue containing the same elements. + */ + override def toQueue: Queue[A] = self.toQueue + + /** This method clones the priority queue. + * + * @return a priority queue with the same elements. + */ + override def clone(): PriorityQueue[A] = new PriorityQueueProxy[A] { + def self = PriorityQueueProxy.this.self.clone() + } +} + + +/** This class implements synchronized priority queues using a binary heap. + * The elements of the queue have to be ordered in terms of the `Ordered[T]` class. + * + * @tparam A type of the elements contained in this synchronized priority queue + * @param ord implicit ordering used to compared elements of type `A` + * + * @author Matthias Zenger + * @version 1.0, 03/05/2004 + * @since 1 + * @define Coll `SynchronizedPriorityQueue` + * @define coll synchronized priority queue + */ +@deprecated("Comprehensive synchronization via selective overriding of methods is inherently unreliable. Consider java.util.concurrent.ConcurrentSkipListSet as an alternative.", "2.11.0") +sealed class SynchronizedPriorityQueue[A](implicit ord: Ordering[A]) extends PriorityQueue[A] { + + /** Checks if the queue is empty. + * + * @return true, iff there is no element in the queue. + */ + override def isEmpty: Boolean = synchronized { super.isEmpty } + + /** Inserts a single element into the priority queue. + * + * @param elem the element to insert + */ + override def +=(elem: A): this.type = { + synchronized { + super.+=(elem) + } + this + } + + /** Adds all elements of a traversable object into the priority queue. + * + * @param xs a traversable object + */ + override def ++=(xs: TraversableOnce[A]): this.type = { + synchronized { + super.++=(xs) + } + this + } + + /** Adds all elements to the queue. + * + * @param elems the elements to add. + */ + override def enqueue(elems: A*): Unit = synchronized { super.++=(elems) } + + /** Returns the element with the highest priority in the queue, + * and removes this element from the queue. + * + * @return the element with the highest priority. + */ + override def dequeue(): A = synchronized { super.dequeue() } + + /** Returns the element with the highest priority in the queue, + * or throws an error if there is no element contained in the queue. + * + * @return the element with the highest priority. + */ + override def head: A = synchronized { super.head } + + /** Removes all elements from the queue. After this operation is completed, + * the queue will be empty. + */ + override def clear(): Unit = synchronized { super.clear() } + + /** Returns an iterator which yield all the elements of the priority + * queue in descending priority order. + * + * @return an iterator over all elements sorted in descending order. + */ + override def iterator: Iterator[A] = synchronized { super.iterator } + + /** Checks if two queues are structurally identical. + * + * @return true, iff both queues contain the same sequence of elements. + */ + override def equals(that: Any): Boolean = synchronized { super.equals(that) } + + /** Returns a textual representation of a queue as a string. + * + * @return the string representation of this queue. + */ + override def toString(): String = synchronized { super.toString() } +} diff --git a/src/library/scala/collection/mutable/PriorityQueueProxy.scala b/src/library/scala/collection/mutable/PriorityQueueProxy.scala deleted file mode 100644 index b24551a6b7..0000000000 --- a/src/library/scala/collection/mutable/PriorityQueueProxy.scala +++ /dev/null @@ -1,96 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - - -package scala -package collection -package mutable - -/** This class servers as a proxy for priority queues. The - * elements of the queue have to be ordered in terms of the - * `Ordered[T]` class. - * - * @author Matthias Zenger - * @version 1.0, 03/05/2004 - * @since 1 - */ -@deprecated("Proxying is deprecated due to lack of use and compiler-level support.", "2.11.0") -abstract class PriorityQueueProxy[A](implicit ord: Ordering[A]) extends PriorityQueue[A] - with Proxy -{ - def self: PriorityQueue[A] - - /** Creates a new iterator over all elements contained in this - * object. - * - * @return the new iterator - */ - override def iterator: Iterator[A] = self.iterator - - /** Returns the length of this priority queue. - */ - override def length: Int = self.length - - /** Checks if the queue is empty. - * - * @return true, iff there is no element in the queue. - */ - override def isEmpty: Boolean = self.isEmpty - - /** Inserts a single element into the priority queue. - * - * @param elem the element to insert - */ - override def +=(elem: A): this.type = { self += elem; this } - - /** Adds all elements provided by an iterator into the priority queue. - * - * @param it an iterator - */ - override def ++=(it: TraversableOnce[A]): this.type = { - self ++= it - this - } - - /** Adds all elements to the queue. - * - * @param elems the elements to add. - */ - override def enqueue(elems: A*): Unit = self ++= elems - - /** Returns the element with the highest priority in the queue, - * and removes this element from the queue. - * - * @return the element with the highest priority. - */ - override def dequeue(): A = self.dequeue() - - /** Returns the element with the highest priority in the queue, - * or throws an error if there is no element contained in the queue. - * - * @return the element with the highest priority. - */ - override def head: A = self.head - - /** Removes all elements from the queue. After this operation is completed, - * the queue will be empty. - */ - override def clear(): Unit = self.clear() - - /** Returns a regular queue containing the same elements. - */ - override def toQueue: Queue[A] = self.toQueue - - /** This method clones the priority queue. - * - * @return a priority queue with the same elements. - */ - override def clone(): PriorityQueue[A] = new PriorityQueueProxy[A] { - def self = PriorityQueueProxy.this.self.clone() - } -} diff --git a/src/library/scala/collection/mutable/ReusableBuilder.scala b/src/library/scala/collection/mutable/ReusableBuilder.scala index caab3071b6..83a4fcfc29 100644 --- a/src/library/scala/collection/mutable/ReusableBuilder.scala +++ b/src/library/scala/collection/mutable/ReusableBuilder.scala @@ -11,8 +11,6 @@ package scala package collection package mutable -import generic._ - /** `ReusableBuilder` is a marker trait that indicates that a `Builder` * can be reused to build more than one instance of a collection. In * particular, calling `result` followed by `clear` will produce a @@ -44,7 +42,7 @@ trait ReusableBuilder[-Elem, +To] extends Builder[Elem, To] { * After a call to `result`, the behavior of all other methods is undefined * save for `clear`. If `clear` is called, then the builder is reset and * may be used to build another instance. - * + * * @return a collection containing the elements added to this builder. */ override def result(): To // Note: overriding for scaladoc only! diff --git a/src/library/scala/collection/mutable/SynchronizedPriorityQueue.scala b/src/library/scala/collection/mutable/SynchronizedPriorityQueue.scala deleted file mode 100644 index d3c0b85f69..0000000000 --- a/src/library/scala/collection/mutable/SynchronizedPriorityQueue.scala +++ /dev/null @@ -1,101 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - - - -package scala -package collection -package mutable - -/** This class implements synchronized priority queues using a binary heap. - * The elements of the queue have to be ordered in terms of the `Ordered[T]` class. - * - * @tparam A type of the elements contained in this synchronized priority queue - * @param ord implicit ordering used to compared elements of type `A` - * - * @author Matthias Zenger - * @version 1.0, 03/05/2004 - * @since 1 - * @define Coll `SynchronizedPriorityQueue` - * @define coll synchronized priority queue - */ -@deprecated("Comprehensive synchronization via selective overriding of methods is inherently unreliable. Consider java.util.concurrent.ConcurrentSkipListSet as an alternative.", "2.11.0") -class SynchronizedPriorityQueue[A](implicit ord: Ordering[A]) extends PriorityQueue[A] { - - /** Checks if the queue is empty. - * - * @return true, iff there is no element in the queue. - */ - override def isEmpty: Boolean = synchronized { super.isEmpty } - - /** Inserts a single element into the priority queue. - * - * @param elem the element to insert - */ - override def +=(elem: A): this.type = { - synchronized { - super.+=(elem) - } - this - } - - /** Adds all elements of a traversable object into the priority queue. - * - * @param xs a traversable object - */ - override def ++=(xs: TraversableOnce[A]): this.type = { - synchronized { - super.++=(xs) - } - this - } - - /** Adds all elements to the queue. - * - * @param elems the elements to add. - */ - override def enqueue(elems: A*): Unit = synchronized { super.++=(elems) } - - /** Returns the element with the highest priority in the queue, - * and removes this element from the queue. - * - * @return the element with the highest priority. - */ - override def dequeue(): A = synchronized { super.dequeue() } - - /** Returns the element with the highest priority in the queue, - * or throws an error if there is no element contained in the queue. - * - * @return the element with the highest priority. - */ - override def head: A = synchronized { super.head } - - /** Removes all elements from the queue. After this operation is completed, - * the queue will be empty. - */ - override def clear(): Unit = synchronized { super.clear() } - - /** Returns an iterator which yield all the elements of the priority - * queue in descending priority order. - * - * @return an iterator over all elements sorted in descending order. - */ - override def iterator: Iterator[A] = synchronized { super.iterator } - - /** Checks if two queues are structurally identical. - * - * @return true, iff both queues contain the same sequence of elements. - */ - override def equals(that: Any): Boolean = synchronized { super.equals(that) } - - /** Returns a textual representation of a queue as a string. - * - * @return the string representation of this queue. - */ - override def toString(): String = synchronized { super.toString() } -} diff --git a/src/library/scala/collection/mutable/UnrolledBuffer.scala b/src/library/scala/collection/mutable/UnrolledBuffer.scala index 2212486bcf..b49d009a17 100644 --- a/src/library/scala/collection/mutable/UnrolledBuffer.scala +++ b/src/library/scala/collection/mutable/UnrolledBuffer.scala @@ -43,8 +43,7 @@ import scala.reflect.ClassTag * */ @SerialVersionUID(1L) -@deprecatedInheritance("UnrolledBuffer is not designed to enable meaningful subclassing.", "2.11.0") -class UnrolledBuffer[T](implicit val tag: ClassTag[T]) +sealed class UnrolledBuffer[T](implicit val tag: ClassTag[T]) extends scala.collection.mutable.AbstractBuffer[T] with scala.collection.mutable.Buffer[T] with scala.collection.mutable.BufferLike[T, UnrolledBuffer[T]] @@ -350,3 +349,11 @@ object UnrolledBuffer extends ClassTagTraversableFactory[UnrolledBuffer] { } } + + +// This is used by scala.collection.parallel.mutable.UnrolledParArrayCombiner: +// Todo -- revisit whether inheritance is the best way to achieve this functionality +private[collection] class DoublingUnrolledBuffer[T](implicit t: ClassTag[T]) extends UnrolledBuffer[T]()(t) { + override def calcNextLength(sz: Int) = if (sz < 10000) sz * 2 else sz + protected override def newUnrolled = new UnrolledBuffer.Unrolled[T](0, new Array[T](4), null, this) +} diff --git a/src/library/scala/collection/mutable/WrappedArray.scala b/src/library/scala/collection/mutable/WrappedArray.scala index 8740bda835..01dcd9bde5 100644 --- a/src/library/scala/collection/mutable/WrappedArray.scala +++ b/src/library/scala/collection/mutable/WrappedArray.scala @@ -13,7 +13,6 @@ package collection package mutable import scala.reflect.ClassTag -import scala.runtime.ScalaRunTime._ import scala.collection.generic._ import scala.collection.parallel.mutable.ParArray @@ -46,7 +45,7 @@ extends AbstractSeq[T] def elemTag: ClassTag[T] @deprecated("use elemTag instead", "2.10.0") - def elemManifest: ClassManifest[T] = ClassManifest.fromClass[T](arrayElementClass(elemTag).asInstanceOf[Class[T]]) + def elemManifest: ClassManifest[T] = ClassManifest.fromClass[T](elemTag.runtimeClass.asInstanceOf[Class[T]]) /** The length of the array */ def length: Int @@ -63,10 +62,10 @@ extends AbstractSeq[T] override def par = ParArray.handoff(array) private def elementClass: Class[_] = - arrayElementClass(array.getClass) + array.getClass.getComponentType override def toArray[U >: T : ClassTag]: Array[U] = { - val thatElementClass = arrayElementClass(implicitly[ClassTag[U]]) + val thatElementClass = implicitly[ClassTag[U]].runtimeClass if (elementClass eq thatElementClass) array.asInstanceOf[Array[U]] else @@ -122,7 +121,7 @@ object WrappedArray { def newBuilder[A]: Builder[A, IndexedSeq[A]] = new ArrayBuffer final class ofRef[T <: AnyRef](val array: Array[T]) extends WrappedArray[T] with Serializable { - lazy val elemTag = ClassTag[T](arrayElementClass(array.getClass)) + lazy val elemTag = ClassTag[T](array.getClass.getComponentType) def length: Int = array.length def apply(index: Int): T = array(index).asInstanceOf[T] def update(index: Int, elem: T) { array(index) = elem } diff --git a/src/library/scala/collection/mutable/WrappedArrayBuilder.scala b/src/library/scala/collection/mutable/WrappedArrayBuilder.scala index c4781321d7..5bc5811450 100644 --- a/src/library/scala/collection/mutable/WrappedArrayBuilder.scala +++ b/src/library/scala/collection/mutable/WrappedArrayBuilder.scala @@ -13,7 +13,6 @@ package collection package mutable import scala.reflect.ClassTag -import scala.runtime.ScalaRunTime._ /** A builder class for arrays. * @@ -34,7 +33,7 @@ class WrappedArrayBuilder[A](tag: ClassTag[A]) extends ReusableBuilder[A, Wrappe private var size: Int = 0 private def mkArray(size: Int): WrappedArray[A] = { - val runtimeClass = arrayElementClass(tag) + val runtimeClass = tag.runtimeClass val newelems = runtimeClass match { case java.lang.Byte.TYPE => new WrappedArray.ofByte(new Array[Byte](size)).asInstanceOf[WrappedArray[A]] case java.lang.Short.TYPE => new WrappedArray.ofShort(new Array[Short](size)).asInstanceOf[WrappedArray[A]] diff --git a/src/library/scala/collection/package.scala b/src/library/scala/collection/package.scala index 856f901b77..6df254c0e0 100644 --- a/src/library/scala/collection/package.scala +++ b/src/library/scala/collection/package.scala @@ -76,13 +76,9 @@ package scala * The concrete parallel collections also have specific performance characteristics which are * described in [[http://docs.scala-lang.org/overviews/parallel-collections/concrete-parallel-collections.html#performance-characteristics the parallel collections guide]] * - * === Converting between Java Collections === + * === Converting to and from Java Collections === * - * The [[scala.collection.JavaConversions]] object provides implicit defs that - * will allow mostly seamless integration between APIs using Java Collections - * and the Scala collections library. - * - * Alternatively the [[scala.collection.JavaConverters]] object provides a collection + * The [[scala.collection.JavaConverters]] object provides a collection * of decorators that allow converting between Scala and Java collections using `asScala` * and `asJava` methods. */ diff --git a/src/library/scala/collection/parallel/Tasks.scala b/src/library/scala/collection/parallel/Tasks.scala index c9a75752df..2a4e40dd16 100644 --- a/src/library/scala/collection/parallel/Tasks.scala +++ b/src/library/scala/collection/parallel/Tasks.scala @@ -66,13 +66,10 @@ trait Task[R, +Tp] { } private[parallel] def mergeThrowables(that: Task[_, _]) { - // TODO: As soon as we target Java >= 7, use Throwable#addSuppressed - // to pass additional Throwables to the caller, e. g. - // if (this.throwable != null && that.throwable != null) - // this.throwable.addSuppressed(that.throwable) - // For now, we just use whatever Throwable comes across “first”. - if (this.throwable == null && that.throwable != null) - this.throwable = that.throwable + if (this.throwable != null && that.throwable != null) + this.throwable.addSuppressed(that.throwable) + else if (this.throwable == null && that.throwable != null) + this.throwable = that.throwable } // override in concrete task implementations to signal abort to other tasks diff --git a/src/library/scala/collection/parallel/mutable/LazyCombiner.scala b/src/library/scala/collection/parallel/mutable/LazyCombiner.scala index 5ab2bb81c6..cc25b5b4b2 100644 --- a/src/library/scala/collection/parallel/mutable/LazyCombiner.scala +++ b/src/library/scala/collection/parallel/mutable/LazyCombiner.scala @@ -30,7 +30,6 @@ trait LazyCombiner[Elem, +To, Buff <: Growable[Elem] with Sizing] extends Combin def result: To = allocateAndCopy def clear() = { chain.clear() } def combine[N <: Elem, NewTo >: To](other: Combiner[N, NewTo]): Combiner[N, NewTo] = if (this ne other) { - import language.existentials // FIXME: See SI-7750 if (other.isInstanceOf[LazyCombiner[_, _, _]]) { val that = other.asInstanceOf[LazyCombiner[Elem, To, Buff]] newLazyCombiner(chain ++= that.chain) diff --git a/src/library/scala/collection/parallel/mutable/ParTrieMap.scala b/src/library/scala/collection/parallel/mutable/ParTrieMap.scala index a1dc37cec9..2faf223b99 100644 --- a/src/library/scala/collection/parallel/mutable/ParTrieMap.scala +++ b/src/library/scala/collection/parallel/mutable/ParTrieMap.scala @@ -152,18 +152,9 @@ extends TrieMapIterator[K, V](lev, ct, mustInit) /** Only used within the `ParTrieMap`. */ private[mutable] trait ParTrieMapCombiner[K, V] extends Combiner[(K, V), ParTrieMap[K, V]] { - def combine[N <: (K, V), NewTo >: ParTrieMap[K, V]](other: Combiner[N, NewTo]): Combiner[N, NewTo] = if (this eq other) this else { - throw new UnsupportedOperationException("This shouldn't have been called in the first place.") - - val thiz = this.asInstanceOf[ParTrieMap[K, V]] - val that = other.asInstanceOf[ParTrieMap[K, V]] - val result = new ParTrieMap[K, V] - - result ++= thiz.iterator - result ++= that.iterator - - result - } + def combine[N <: (K, V), NewTo >: ParTrieMap[K, V]](other: Combiner[N, NewTo]): Combiner[N, NewTo] = + if (this eq other) this + else throw new UnsupportedOperationException("This shouldn't have been called in the first place.") override def canBeShared = true } diff --git a/src/library/scala/collection/parallel/mutable/UnrolledParArrayCombiner.scala b/src/library/scala/collection/parallel/mutable/UnrolledParArrayCombiner.scala index 5e4572da60..e71e61f2f1 100644 --- a/src/library/scala/collection/parallel/mutable/UnrolledParArrayCombiner.scala +++ b/src/library/scala/collection/parallel/mutable/UnrolledParArrayCombiner.scala @@ -10,18 +10,10 @@ package scala package collection.parallel.mutable import scala.collection.mutable.ArraySeq -import scala.collection.mutable.UnrolledBuffer +import scala.collection.mutable.DoublingUnrolledBuffer import scala.collection.mutable.UnrolledBuffer.Unrolled import scala.collection.parallel.Combiner import scala.collection.parallel.Task -import scala.reflect.ClassTag - -// Todo -- revisit whether inheritance is the best way to achieve this functionality -private[mutable] class DoublingUnrolledBuffer[T](implicit t: ClassTag[T]) extends UnrolledBuffer[T]()(t) { - override def calcNextLength(sz: Int) = if (sz < 10000) sz * 2 else sz - protected override def newUnrolled = new Unrolled[T](0, new Array[T](4), null, this) -} - /** An array combiner that uses doubling unrolled buffers to store elements. */ trait UnrolledParArrayCombiner[T] diff --git a/src/library/scala/compat/Platform.scala b/src/library/scala/compat/Platform.scala index 42dfcbfdde..f3745bc189 100644 --- a/src/library/scala/compat/Platform.scala +++ b/src/library/scala/compat/Platform.scala @@ -105,8 +105,7 @@ object Platform { /** The default line separator. * * On the JVM, this is equivalent to calling the method: - * `System.getProperty("line.separator")` - * with a default value of "\n". + * `java.lang.System.lineSeparator` */ val EOL = scala.util.Properties.lineSeparator diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index b10aad0ecc..d9d3d572e8 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -29,8 +29,8 @@ import scala.reflect.ClassTag * val f: Future[String] = Future { * s + " future!" * } - * f onSuccess { - * case msg => println(msg) + * f foreach { + * msg => println(msg) * } * }}} * diff --git a/src/library/scala/concurrent/SyncChannel.scala b/src/library/scala/concurrent/SyncChannel.scala index ec584b3eb0..735598935c 100644 --- a/src/library/scala/concurrent/SyncChannel.scala +++ b/src/library/scala/concurrent/SyncChannel.scala @@ -31,10 +31,10 @@ class SyncChannel[A] { pendingReads = pendingReads.tail // let reader continue - readReq set data + readReq put data // resolve write request - writeReq set true + writeReq put true } else { // enqueue write request @@ -57,10 +57,10 @@ class SyncChannel[A] { pendingWrites = pendingWrites.tail // let writer continue - writeReq set true + writeReq.put(true) // resolve read request - readReq set data + readReq.put (data) } else { // enqueue read request diff --git a/src/library/scala/concurrent/duration/Duration.scala b/src/library/scala/concurrent/duration/Duration.scala index a905ef345c..f69030bd3d 100644 --- a/src/library/scala/concurrent/duration/Duration.scala +++ b/src/library/scala/concurrent/duration/Duration.scala @@ -9,8 +9,6 @@ package scala.concurrent.duration import java.lang.{ Double => JDouble, Long => JLong } -import scala.language.implicitConversions -import scala.language.postfixOps object Duration { @@ -57,7 +55,7 @@ object Duration { case "Inf" | "PlusInf" | "+Inf" => Inf case "MinusInf" | "-Inf" => MinusInf case _ => - val unitName = s1.reverse takeWhile (_.isLetter) reverse; + val unitName = s1.reverse.takeWhile(_.isLetter).reverse; timeUnit get unitName match { case Some(unit) => val valueStr = s1 dropRight unitName.length @@ -87,11 +85,11 @@ object Duration { // TimeUnit => standard label protected[duration] val timeUnitName: Map[TimeUnit, String] = - timeUnitLabels.toMap mapValues (s => words(s).last) toMap + timeUnitLabels.toMap.mapValues(s => words(s).last).toMap // Label => TimeUnit protected[duration] val timeUnit: Map[String, TimeUnit] = - timeUnitLabels flatMap { case (unit, names) => expandLabels(names) map (_ -> unit) } toMap + timeUnitLabels.flatMap{ case (unit, names) => expandLabels(names) map (_ -> unit) }.toMap /** * Extract length and time unit out of a string, where the format must match the description for [[Duration$.apply(s:String)* apply(String)]]. diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index 178cd6d912..626540425f 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -304,7 +304,7 @@ private[concurrent] object Promise { /** Tries to add the callback, if already completed, it dispatches the callback to be executed. * Used by `onComplete()` to add callbacks to a promise and by `link()` to transfer callbacks - * to the root promise when linking two promises togehter. + * to the root promise when linking two promises together. */ @tailrec private def dispatchOrAddCallback(runnable: CallbackRunnable[T]): Unit = { diff --git a/src/library/scala/io/Source.scala b/src/library/scala/io/Source.scala index c0ee5f6a75..7513b423a1 100644 --- a/src/library/scala/io/Source.scala +++ b/src/library/scala/io/Source.scala @@ -180,22 +180,20 @@ object Source { } /** An iterable representation of source data. - * It may be reset with the optional `reset` method. + * It may be reset with the optional [[reset]] method. * - * Subclasses must supply [[scala.io.Source@iter the underlying iterator]]. + * Subclasses must supply [[scala.io.Source.iter the underlying iterator]]. * - * Error handling may be customized by overriding the [[scala.io.Source@report report]] method. + * Error handling may be customized by overriding the [[scala.io.Source.report report]] method. * - * The [[scala.io.Source@ch current input]] and [[scala.io.Source@pos position]], - * as well as the [[scala.io.Source@next next character]] methods delegate to - * [[scala.io.Source$Positioner the positioner]]. + * The [[scala.io.Source.ch current input]] and [[scala.io.Source.pos position]], + * as well as the [[scala.io.Source.next next character]] methods delegate to + * [[scala.io.Source#Positioner the positioner]]. * - * The default positioner encodes line and column numbers in the position passed to `report`. + * The default positioner encodes line and column numbers in the position passed to [[report]]. * This behavior can be changed by supplying a - * [[scala.io.Source@withPositioning(pos:Source.this.Positioner):Source.this.type custom positioner]]. + * [[scala.io.Source.withPositioning(pos:* custom positioner]]. * - * @author Burak Emir - * @version 1.0 */ abstract class Source extends Iterator[Char] with Closeable { /** the actual iterator */ diff --git a/src/library/scala/math/Integral.scala b/src/library/scala/math/Integral.scala index ff1f695f6d..44009fd4a2 100644 --- a/src/library/scala/math/Integral.scala +++ b/src/library/scala/math/Integral.scala @@ -6,8 +6,6 @@ ** |/ ** \* */ - - package scala package math diff --git a/src/library/scala/math/Ordering.scala b/src/library/scala/math/Ordering.scala index 827cccc77e..37096d5ed0 100644 --- a/src/library/scala/math/Ordering.scala +++ b/src/library/scala/math/Ordering.scala @@ -224,42 +224,32 @@ object Ordering extends LowPriorityOrderingImplicits { implicit object Unit extends UnitOrdering trait BooleanOrdering extends Ordering[Boolean] { - def compare(x: Boolean, y: Boolean) = (x, y) match { - case (false, true) => -1 - case (true, false) => 1 - case _ => 0 - } + def compare(x: Boolean, y: Boolean) = java.lang.Boolean.compare(x, y) } implicit object Boolean extends BooleanOrdering trait ByteOrdering extends Ordering[Byte] { - def compare(x: Byte, y: Byte) = x.toInt - y.toInt + def compare(x: Byte, y: Byte) = java.lang.Byte.compare(x, y) } implicit object Byte extends ByteOrdering trait CharOrdering extends Ordering[Char] { - def compare(x: Char, y: Char) = x.toInt - y.toInt + def compare(x: Char, y: Char) = java.lang.Character.compare(x, y) } implicit object Char extends CharOrdering trait ShortOrdering extends Ordering[Short] { - def compare(x: Short, y: Short) = x.toInt - y.toInt + def compare(x: Short, y: Short) = java.lang.Short.compare(x, y) } implicit object Short extends ShortOrdering trait IntOrdering extends Ordering[Int] { - def compare(x: Int, y: Int) = - if (x < y) -1 - else if (x == y) 0 - else 1 + def compare(x: Int, y: Int) = java.lang.Integer.compare(x, y) } implicit object Int extends IntOrdering trait LongOrdering extends Ordering[Long] { - def compare(x: Long, y: Long) = - if (x < y) -1 - else if (x == y) 0 - else 1 + def compare(x: Long, y: Long) = java.lang.Long.compare(x, y) } implicit object Long extends LongOrdering diff --git a/src/library/scala/reflect/ClassTag.scala b/src/library/scala/reflect/ClassTag.scala index 9dd96183da..1811d3a00f 100644 --- a/src/library/scala/reflect/ClassTag.scala +++ b/src/library/scala/reflect/ClassTag.scala @@ -2,7 +2,6 @@ package scala package reflect import java.lang.{ Class => jClass } -import scala.runtime.ScalaRunTime.arrayElementClass /** * @@ -102,10 +101,10 @@ trait ClassTag[T] extends ClassManifestDeprecatedApis[T] with Equals with Serial // case class accessories override def canEqual(x: Any) = x.isInstanceOf[ClassTag[_]] override def equals(x: Any) = x.isInstanceOf[ClassTag[_]] && this.runtimeClass == x.asInstanceOf[ClassTag[_]].runtimeClass - override def hashCode = scala.runtime.ScalaRunTime.hash(runtimeClass) + override def hashCode = runtimeClass.## override def toString = { def prettyprint(clazz: jClass[_]): String = - if (clazz.isArray) s"Array[${prettyprint(arrayElementClass(clazz))}]" else + if (clazz.isArray) s"Array[${prettyprint(clazz.getComponentType)}]" else clazz.getName prettyprint(runtimeClass) } diff --git a/src/library/scala/runtime/ArrayRuntime.java b/src/library/scala/runtime/ArrayRuntime.java deleted file mode 100644 index 1a0f748931..0000000000 --- a/src/library/scala/runtime/ArrayRuntime.java +++ /dev/null @@ -1,26 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - - - -package scala.runtime; - -/** - * Methods on Java arrays - */ -class ArrayRuntime { - static boolean[] cloneArray(boolean[] array) { return array.clone(); } - static byte[] cloneArray(byte[] array) { return array.clone(); } - static short[] cloneArray(short[] array) { return array.clone(); } - static char[] cloneArray(char[] array) { return array.clone(); } - static int[] cloneArray(int[] array) { return array.clone(); } - static long[] cloneArray(long[] array) { return array.clone(); } - static float[] cloneArray(float[] array) { return array.clone(); } - static double[] cloneArray(double[] array) { return array.clone(); } - static Object[] cloneArray(Object[] array) { return array.clone(); } -} diff --git a/src/library/scala/runtime/Boxed.scala b/src/library/scala/runtime/Boxed.scala deleted file mode 100644 index 933444773d..0000000000 --- a/src/library/scala/runtime/Boxed.scala +++ /dev/null @@ -1,12 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala -package runtime - -trait Boxed { } diff --git a/src/library/scala/runtime/BoxesRunTime.java b/src/library/scala/runtime/BoxesRunTime.java index 96e0f23f3d..6b3874fc1f 100644 --- a/src/library/scala/runtime/BoxesRunTime.java +++ b/src/library/scala/runtime/BoxesRunTime.java @@ -198,70 +198,6 @@ public final class BoxesRunTime } } - /** Hashcode algorithm is driven by the requirements imposed - * by primitive equality semantics, namely that equal objects - * have equal hashCodes. The first priority are the integral/char - * types, which already have the same hashCodes for the same - * values except for Long. So Long's hashCode is altered to - * conform to Int's for all values in Int's range. - * - * Float is problematic because it's far too small to hold - * all the Ints, so for instance Int.MaxValue.toFloat claims - * to be == to each of the largest 64 Ints. There is no way - * to preserve equals/hashCode alignment without compromising - * the hashCode distribution, so Floats are only guaranteed - * to have the same hashCode for whole Floats in the range - * Short.MinValue to Short.MaxValue (2^16 total.) - * - * Double has its hashCode altered to match the entire Int range, - * but is not guaranteed beyond that. (But could/should it be? - * The hashCode is only 32 bits so this is a more tractable - * issue than Float's, but it might be better simply to exclude it.) - * - * Note: BigInt and BigDecimal, being arbitrary precision, could - * be made consistent with all other types for the Int range, but - * as yet have not. - * - * Note: Among primitives, Float.NaN != Float.NaN, but the boxed - * versions are equal. This still needs reconciliation. - */ - public static int hashFromLong(java.lang.Long n) { - int iv = n.intValue(); - if (iv == n.longValue()) return iv; - else return n.hashCode(); - } - public static int hashFromDouble(java.lang.Double n) { - int iv = n.intValue(); - double dv = n.doubleValue(); - if (iv == dv) return iv; - - long lv = n.longValue(); - if (lv == dv) return java.lang.Long.valueOf(lv).hashCode(); - - float fv = n.floatValue(); - if (fv == dv) return java.lang.Float.valueOf(fv).hashCode(); - else return n.hashCode(); - } - public static int hashFromFloat(java.lang.Float n) { - int iv = n.intValue(); - float fv = n.floatValue(); - if (iv == fv) return iv; - - long lv = n.longValue(); - if (lv == fv) return java.lang.Long.valueOf(lv).hashCode(); - else return n.hashCode(); - } - public static int hashFromNumber(java.lang.Number n) { - if (n instanceof java.lang.Long) return hashFromLong((java.lang.Long)n); - else if (n instanceof java.lang.Double) return hashFromDouble((java.lang.Double)n); - else if (n instanceof java.lang.Float) return hashFromFloat((java.lang.Float)n); - else return n.hashCode(); - } - public static int hashFromObject(Object a) { - if (a instanceof Number) return hashFromNumber((Number)a); - else return a.hashCode(); - } - private static int unboxCharOrInt(Object arg1, int code) { if (code == CHAR) return ((java.lang.Character) arg1).charValue(); diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala index 1195f95093..b31a94576a 100644 --- a/src/library/scala/runtime/ScalaRunTime.scala +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -29,15 +29,6 @@ object ScalaRunTime { private def isArrayClass(clazz: jClass[_], atLevel: Int): Boolean = clazz.isArray && (atLevel == 1 || isArrayClass(clazz.getComponentType, atLevel - 1)) - def isValueClass(clazz: jClass[_]) = clazz.isPrimitive() - - // includes specialized subclasses and future proofed against hypothetical TupleN (for N > 22) - def isTuple(x: Any) = x != null && x.getClass.getName.startsWith("scala.Tuple") - def isAnyVal(x: Any) = x match { - case _: Byte | _: Short | _: Char | _: Int | _: Long | _: Float | _: Double | _: Boolean | _: Unit => true - case _ => false - } - // A helper method to make my life in the pattern matcher a lot easier. def drop[Repr](coll: Repr, num: Int)(implicit traversable: IsTraversableLike[Repr]): Repr = traversable conversion coll drop num @@ -50,15 +41,6 @@ object ScalaRunTime { else java.lang.reflect.Array.newInstance(clazz, 0).getClass } - /** Return the class object representing elements in arrays described by a given schematic. - */ - def arrayElementClass(schematic: Any): jClass[_] = schematic match { - case cls: jClass[_] => cls.getComponentType - case tag: ClassTag[_] => tag.runtimeClass - case _ => - throw new UnsupportedOperationException(s"unsupported schematic $schematic (${schematic.getClass})") - } - /** Return the class object representing an unboxed value type, * e.g., classOf[int], not classOf[java.lang.Integer]. The compiler * rewrites expressions like 5.getClass to come here. @@ -116,15 +98,15 @@ object ScalaRunTime { } def array_clone(xs: AnyRef): AnyRef = xs match { - case x: Array[AnyRef] => ArrayRuntime.cloneArray(x) - case x: Array[Int] => ArrayRuntime.cloneArray(x) - case x: Array[Double] => ArrayRuntime.cloneArray(x) - case x: Array[Long] => ArrayRuntime.cloneArray(x) - case x: Array[Float] => ArrayRuntime.cloneArray(x) - case x: Array[Char] => ArrayRuntime.cloneArray(x) - case x: Array[Byte] => ArrayRuntime.cloneArray(x) - case x: Array[Short] => ArrayRuntime.cloneArray(x) - case x: Array[Boolean] => ArrayRuntime.cloneArray(x) + case x: Array[AnyRef] => x.clone() + case x: Array[Int] => x.clone() + case x: Array[Double] => x.clone() + case x: Array[Long] => x.clone() + case x: Array[Float] => x.clone() + case x: Array[Char] => x.clone() + case x: Array[Byte] => x.clone() + case x: Array[Short] => x.clone() + case x: Array[Boolean] => x.clone() case x: Array[Unit] => x case null => throw new NullPointerException } @@ -157,9 +139,6 @@ object ScalaRunTime { // More background at ticket #2318. def ensureAccessible(m: JMethod): JMethod = scala.reflect.ensureAccessible(m) - def checkInitialized[T <: AnyRef](x: T): T = - if (x == null) throw new UninitializedError else x - def _toString(x: Product): String = x.productIterator.mkString(x.productPrefix + "(", ",", ")") @@ -179,71 +158,9 @@ object ScalaRunTime { } } - /** Fast path equality method for inlining; used when -optimise is set. - */ - @inline def inlinedEquals(x: Object, y: Object): Boolean = - if (x eq y) true - else if (x eq null) false - else if (x.isInstanceOf[java.lang.Number]) BoxesRunTime.equalsNumObject(x.asInstanceOf[java.lang.Number], y) - else if (x.isInstanceOf[java.lang.Character]) BoxesRunTime.equalsCharObject(x.asInstanceOf[java.lang.Character], y) - else x.equals(y) - - def _equals(x: Product, y: Any): Boolean = y match { - case y: Product if x.productArity == y.productArity => x.productIterator sameElements y.productIterator - case _ => false - } - - // hashcode ----------------------------------------------------------- - // - // Note that these are the implementations called by ##, so they - // must not call ## themselves. - - def hash(x: Any): Int = - if (x == null) 0 - else if (x.isInstanceOf[java.lang.Number]) BoxesRunTime.hashFromNumber(x.asInstanceOf[java.lang.Number]) - else x.hashCode - - def hash(dv: Double): Int = { - val iv = dv.toInt - if (iv == dv) return iv - - val lv = dv.toLong - if (lv == dv) return lv.hashCode - - val fv = dv.toFloat - if (fv == dv) fv.hashCode else dv.hashCode - } - def hash(fv: Float): Int = { - val iv = fv.toInt - if (iv == fv) return iv - - val lv = fv.toLong - if (lv == fv) hash(lv) - else fv.hashCode - } - def hash(lv: Long): Int = { - val low = lv.toInt - val lowSign = low >>> 31 - val high = (lv >>> 32).toInt - low ^ (high + lowSign) - } - def hash(x: Number): Int = runtime.BoxesRunTime.hashFromNumber(x) - - // The remaining overloads are here for completeness, but the compiler - // inlines these definitions directly so they're not generally used. - def hash(x: Int): Int = x - def hash(x: Short): Int = x.toInt - def hash(x: Byte): Int = x.toInt - def hash(x: Char): Int = x.toInt - def hash(x: Boolean): Int = if (x) true.hashCode else false.hashCode - def hash(x: Unit): Int = 0 - - /** A helper method for constructing case class equality methods, - * because existential types get in the way of a clean outcome and - * it's performing a series of Any/Any equals comparisons anyway. - * See ticket #2867 for specifics. - */ - def sameElements(xs1: scala.collection.Seq[Any], xs2: scala.collection.Seq[Any]) = xs1 sameElements xs2 + /** Old implementation of `##`. */ + @deprecated("Use scala.runtime.Statics.anyHash instead.", "2.12.0") + def hash(x: Any): Int = Statics.anyHash(x.asInstanceOf[Object]) /** Given any Scala value, convert it to a String. * @@ -266,6 +183,9 @@ object ScalaRunTime { def isScalaClass(x: AnyRef) = packageOf(x) startsWith "scala." def isScalaCompilerClass(x: AnyRef) = packageOf(x) startsWith "scala.tools.nsc." + // includes specialized subclasses and future proofed against hypothetical TupleN (for N > 22) + def isTuple(x: Any) = x != null && x.getClass.getName.startsWith("scala.Tuple") + // We use reflection because the scala.xml package might not be available def isSubClassOf(potentialSubClass: Class[_], ofClass: String) = try { @@ -345,17 +265,4 @@ object ScalaRunTime { nl + s + "\n" } - - def box[T](clazz: jClass[T]): jClass[_] = clazz match { - case java.lang.Byte.TYPE => classOf[java.lang.Byte] - case java.lang.Short.TYPE => classOf[java.lang.Short] - case java.lang.Character.TYPE => classOf[java.lang.Character] - case java.lang.Integer.TYPE => classOf[java.lang.Integer] - case java.lang.Long.TYPE => classOf[java.lang.Long] - case java.lang.Float.TYPE => classOf[java.lang.Float] - case java.lang.Double.TYPE => classOf[java.lang.Double] - case java.lang.Void.TYPE => classOf[scala.runtime.BoxedUnit] - case java.lang.Boolean.TYPE => classOf[java.lang.Boolean] - case _ => clazz - } } diff --git a/src/library/scala/runtime/Statics.java b/src/library/scala/runtime/Statics.java index 485511ecbb..62390cb9d0 100644 --- a/src/library/scala/runtime/Statics.java +++ b/src/library/scala/runtime/Statics.java @@ -36,10 +36,11 @@ public final class Statics { } public static int longHash(long lv) { - if ((int)lv == lv) - return (int)lv; - else - return (int)(lv ^ (lv >>> 32)); + int iv = (int)lv; + if (iv == lv) + return iv; + + return java.lang.Long.hashCode(lv); } public static int doubleHash(double dv) { @@ -47,16 +48,15 @@ public final class Statics { if (iv == dv) return iv; - float fv = (float)dv; - if (fv == dv) - return java.lang.Float.floatToIntBits(fv); - long lv = (long)dv; if (lv == dv) - return (int)lv; + return java.lang.Long.hashCode(lv); + + float fv = (float)dv; + if (fv == dv) + return java.lang.Float.hashCode(fv); - lv = Double.doubleToLongBits(dv); - return (int)(lv ^ (lv >>> 32)); + return java.lang.Double.hashCode(dv); } public static int floatHash(float fv) { @@ -66,11 +66,39 @@ public final class Statics { long lv = (long)fv; if (lv == fv) - return (int)(lv^(lv>>>32)); + return java.lang.Long.hashCode(lv); - return java.lang.Float.floatToIntBits(fv); + return java.lang.Float.hashCode(fv); } + /** + * Hashcode algorithm is driven by the requirements imposed + * by primitive equality semantics, namely that equal objects + * have equal hashCodes. The first priority are the integral/char + * types, which already have the same hashCodes for the same + * values except for Long. So Long's hashCode is altered to + * conform to Int's for all values in Int's range. + * + * Float is problematic because it's far too small to hold + * all the Ints, so for instance Int.MaxValue.toFloat claims + * to be == to each of the largest 64 Ints. There is no way + * to preserve equals/hashCode alignment without compromising + * the hashCode distribution, so Floats are only guaranteed + * to have the same hashCode for whole Floats in the range + * Short.MinValue to Short.MaxValue (2^16 total.) + * + * Double has its hashCode altered to match the entire Int range, + * but is not guaranteed beyond that. (But could/should it be? + * The hashCode is only 32 bits so this is a more tractable + * issue than Float's, but it might be better simply to exclude it.) + * + * Note: BigInt and BigDecimal, being arbitrary precision, could + * be made consistent with all other types for the Int range, but + * as yet have not. + * + * Note: Among primitives, Float.NaN != Float.NaN, but the boxed + * versions are equal. This still needs reconciliation. + */ public static int anyHash(Object x) { if (x == null) return 0; diff --git a/src/library/scala/runtime/java8/JFunction0.java b/src/library/scala/runtime/java8/JFunction0.java index bdeb7d5f8e..13426cc8af 100644 --- a/src/library/scala/runtime/java8/JFunction0.java +++ b/src/library/scala/runtime/java8/JFunction0.java @@ -6,7 +6,7 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction0<R> extends scala.Function0<R> { +public interface JFunction0<R> extends scala.Function0<R>, java.io.Serializable { default void $init$() { }; default void apply$mcV$sp() { diff --git a/src/library/scala/runtime/java8/JFunction1.java b/src/library/scala/runtime/java8/JFunction1.java index 7c3974e94a..e1f886dad7 100644 --- a/src/library/scala/runtime/java8/JFunction1.java +++ b/src/library/scala/runtime/java8/JFunction1.java @@ -6,19 +6,7 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction1<T1, R> extends scala.Function1<T1, R> { - default void $init$() { - }; - - @Override - default <A> scala.Function1<T1, A> andThen(scala.Function1<R, A> g) { - return scala.Function1$class.andThen(this, g); - } - - @Override - default <A> scala.Function1<A, R> compose(scala.Function1<A, T1> g) { - return scala.Function1$class.compose(this, g); - } +public interface JFunction1<T1, R> extends scala.Function1<T1, R>, java.io.Serializable { default void apply$mcVI$sp(int v1) { apply((T1) scala.runtime.BoxesRunTime.boxToInteger(v1)); } diff --git a/src/library/scala/runtime/java8/JFunction10.java b/src/library/scala/runtime/java8/JFunction10.java index f9af616641..f7a25c0df2 100644 --- a/src/library/scala/runtime/java8/JFunction10.java +++ b/src/library/scala/runtime/java8/JFunction10.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R> extends scala.Function10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, scala.Function1<T7, scala.Function1<T8, scala.Function1<T9, scala.Function1<T10, R>>>>>>>>>> curried() { - return scala.Function10$class.curried(this); - } - - default scala.Function1<scala.Tuple10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>, R> tupled() { - return scala.Function10$class.tupled(this); - } - - +public interface JFunction10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R> extends scala.Function10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction11.java b/src/library/scala/runtime/java8/JFunction11.java index ba1235332b..9a548b8fc9 100644 --- a/src/library/scala/runtime/java8/JFunction11.java +++ b/src/library/scala/runtime/java8/JFunction11.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R> extends scala.Function11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, scala.Function1<T7, scala.Function1<T8, scala.Function1<T9, scala.Function1<T10, scala.Function1<T11, R>>>>>>>>>>> curried() { - return scala.Function11$class.curried(this); - } - - default scala.Function1<scala.Tuple11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>, R> tupled() { - return scala.Function11$class.tupled(this); - } - - +public interface JFunction11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R> extends scala.Function11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction12.java b/src/library/scala/runtime/java8/JFunction12.java index 141388e768..12fb73faaf 100644 --- a/src/library/scala/runtime/java8/JFunction12.java +++ b/src/library/scala/runtime/java8/JFunction12.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R> extends scala.Function12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, scala.Function1<T7, scala.Function1<T8, scala.Function1<T9, scala.Function1<T10, scala.Function1<T11, scala.Function1<T12, R>>>>>>>>>>>> curried() { - return scala.Function12$class.curried(this); - } - - default scala.Function1<scala.Tuple12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>, R> tupled() { - return scala.Function12$class.tupled(this); - } - - +public interface JFunction12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R> extends scala.Function12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction13.java b/src/library/scala/runtime/java8/JFunction13.java index 8d0be96a74..c85c63448a 100644 --- a/src/library/scala/runtime/java8/JFunction13.java +++ b/src/library/scala/runtime/java8/JFunction13.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R> extends scala.Function13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, scala.Function1<T7, scala.Function1<T8, scala.Function1<T9, scala.Function1<T10, scala.Function1<T11, scala.Function1<T12, scala.Function1<T13, R>>>>>>>>>>>>> curried() { - return scala.Function13$class.curried(this); - } - - default scala.Function1<scala.Tuple13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>, R> tupled() { - return scala.Function13$class.tupled(this); - } - - +public interface JFunction13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R> extends scala.Function13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction14.java b/src/library/scala/runtime/java8/JFunction14.java index 58ab028716..9a578833aa 100644 --- a/src/library/scala/runtime/java8/JFunction14.java +++ b/src/library/scala/runtime/java8/JFunction14.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R> extends scala.Function14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, scala.Function1<T7, scala.Function1<T8, scala.Function1<T9, scala.Function1<T10, scala.Function1<T11, scala.Function1<T12, scala.Function1<T13, scala.Function1<T14, R>>>>>>>>>>>>>> curried() { - return scala.Function14$class.curried(this); - } - - default scala.Function1<scala.Tuple14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>, R> tupled() { - return scala.Function14$class.tupled(this); - } - - +public interface JFunction14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R> extends scala.Function14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction15.java b/src/library/scala/runtime/java8/JFunction15.java index 89a4a6cf61..e993643953 100644 --- a/src/library/scala/runtime/java8/JFunction15.java +++ b/src/library/scala/runtime/java8/JFunction15.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R> extends scala.Function15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, scala.Function1<T7, scala.Function1<T8, scala.Function1<T9, scala.Function1<T10, scala.Function1<T11, scala.Function1<T12, scala.Function1<T13, scala.Function1<T14, scala.Function1<T15, R>>>>>>>>>>>>>>> curried() { - return scala.Function15$class.curried(this); - } - - default scala.Function1<scala.Tuple15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>, R> tupled() { - return scala.Function15$class.tupled(this); - } - - +public interface JFunction15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R> extends scala.Function15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction16.java b/src/library/scala/runtime/java8/JFunction16.java index e3287b42ac..a252cb5303 100644 --- a/src/library/scala/runtime/java8/JFunction16.java +++ b/src/library/scala/runtime/java8/JFunction16.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R> extends scala.Function16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, scala.Function1<T7, scala.Function1<T8, scala.Function1<T9, scala.Function1<T10, scala.Function1<T11, scala.Function1<T12, scala.Function1<T13, scala.Function1<T14, scala.Function1<T15, scala.Function1<T16, R>>>>>>>>>>>>>>>> curried() { - return scala.Function16$class.curried(this); - } - - default scala.Function1<scala.Tuple16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>, R> tupled() { - return scala.Function16$class.tupled(this); - } - - +public interface JFunction16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R> extends scala.Function16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction17.java b/src/library/scala/runtime/java8/JFunction17.java index 508614e8b4..045aa7196f 100644 --- a/src/library/scala/runtime/java8/JFunction17.java +++ b/src/library/scala/runtime/java8/JFunction17.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R> extends scala.Function17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, scala.Function1<T7, scala.Function1<T8, scala.Function1<T9, scala.Function1<T10, scala.Function1<T11, scala.Function1<T12, scala.Function1<T13, scala.Function1<T14, scala.Function1<T15, scala.Function1<T16, scala.Function1<T17, R>>>>>>>>>>>>>>>>> curried() { - return scala.Function17$class.curried(this); - } - - default scala.Function1<scala.Tuple17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17>, R> tupled() { - return scala.Function17$class.tupled(this); - } - - +public interface JFunction17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R> extends scala.Function17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction18.java b/src/library/scala/runtime/java8/JFunction18.java index 8aa9c5e2c3..ba2bf31206 100644 --- a/src/library/scala/runtime/java8/JFunction18.java +++ b/src/library/scala/runtime/java8/JFunction18.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R> extends scala.Function18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, scala.Function1<T7, scala.Function1<T8, scala.Function1<T9, scala.Function1<T10, scala.Function1<T11, scala.Function1<T12, scala.Function1<T13, scala.Function1<T14, scala.Function1<T15, scala.Function1<T16, scala.Function1<T17, scala.Function1<T18, R>>>>>>>>>>>>>>>>>> curried() { - return scala.Function18$class.curried(this); - } - - default scala.Function1<scala.Tuple18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18>, R> tupled() { - return scala.Function18$class.tupled(this); - } - - +public interface JFunction18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R> extends scala.Function18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction19.java b/src/library/scala/runtime/java8/JFunction19.java index 89d739366e..dde4824293 100644 --- a/src/library/scala/runtime/java8/JFunction19.java +++ b/src/library/scala/runtime/java8/JFunction19.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R> extends scala.Function19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, scala.Function1<T7, scala.Function1<T8, scala.Function1<T9, scala.Function1<T10, scala.Function1<T11, scala.Function1<T12, scala.Function1<T13, scala.Function1<T14, scala.Function1<T15, scala.Function1<T16, scala.Function1<T17, scala.Function1<T18, scala.Function1<T19, R>>>>>>>>>>>>>>>>>>> curried() { - return scala.Function19$class.curried(this); - } - - default scala.Function1<scala.Tuple19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>, R> tupled() { - return scala.Function19$class.tupled(this); - } - - +public interface JFunction19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R> extends scala.Function19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction2.java b/src/library/scala/runtime/java8/JFunction2.java index 41f2adeae9..548ff60cf6 100644 --- a/src/library/scala/runtime/java8/JFunction2.java +++ b/src/library/scala/runtime/java8/JFunction2.java @@ -6,18 +6,7 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction2<T1, T2, R> extends scala.Function2<T1, T2, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, R>> curried() { - return scala.Function2$class.curried(this); - } - - default scala.Function1<scala.Tuple2<T1, T2>, R> tupled() { - return scala.Function2$class.tupled(this); - } - +public interface JFunction2<T1, T2, R> extends scala.Function2<T1, T2, R>, java.io.Serializable { default void apply$mcVII$sp(int v1, int v2) { apply((T1) scala.runtime.BoxesRunTime.boxToInteger(v1), (T2) scala.runtime.BoxesRunTime.boxToInteger(v2)); } diff --git a/src/library/scala/runtime/java8/JFunction20.java b/src/library/scala/runtime/java8/JFunction20.java index b8b4c11af7..5505743c20 100644 --- a/src/library/scala/runtime/java8/JFunction20.java +++ b/src/library/scala/runtime/java8/JFunction20.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R> extends scala.Function20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, scala.Function1<T7, scala.Function1<T8, scala.Function1<T9, scala.Function1<T10, scala.Function1<T11, scala.Function1<T12, scala.Function1<T13, scala.Function1<T14, scala.Function1<T15, scala.Function1<T16, scala.Function1<T17, scala.Function1<T18, scala.Function1<T19, scala.Function1<T20, R>>>>>>>>>>>>>>>>>>>> curried() { - return scala.Function20$class.curried(this); - } - - default scala.Function1<scala.Tuple20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20>, R> tupled() { - return scala.Function20$class.tupled(this); - } - - +public interface JFunction20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R> extends scala.Function20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction21.java b/src/library/scala/runtime/java8/JFunction21.java index dbae0a0479..80e96d3715 100644 --- a/src/library/scala/runtime/java8/JFunction21.java +++ b/src/library/scala/runtime/java8/JFunction21.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R> extends scala.Function21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, scala.Function1<T7, scala.Function1<T8, scala.Function1<T9, scala.Function1<T10, scala.Function1<T11, scala.Function1<T12, scala.Function1<T13, scala.Function1<T14, scala.Function1<T15, scala.Function1<T16, scala.Function1<T17, scala.Function1<T18, scala.Function1<T19, scala.Function1<T20, scala.Function1<T21, R>>>>>>>>>>>>>>>>>>>>> curried() { - return scala.Function21$class.curried(this); - } - - default scala.Function1<scala.Tuple21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21>, R> tupled() { - return scala.Function21$class.tupled(this); - } - - +public interface JFunction21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R> extends scala.Function21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction22.java b/src/library/scala/runtime/java8/JFunction22.java index 2926ae336d..45e689458b 100644 --- a/src/library/scala/runtime/java8/JFunction22.java +++ b/src/library/scala/runtime/java8/JFunction22.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R> extends scala.Function22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, scala.Function1<T7, scala.Function1<T8, scala.Function1<T9, scala.Function1<T10, scala.Function1<T11, scala.Function1<T12, scala.Function1<T13, scala.Function1<T14, scala.Function1<T15, scala.Function1<T16, scala.Function1<T17, scala.Function1<T18, scala.Function1<T19, scala.Function1<T20, scala.Function1<T21, scala.Function1<T22, R>>>>>>>>>>>>>>>>>>>>>> curried() { - return scala.Function22$class.curried(this); - } - - default scala.Function1<scala.Tuple22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22>, R> tupled() { - return scala.Function22$class.tupled(this); - } - - +public interface JFunction22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R> extends scala.Function22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction3.java b/src/library/scala/runtime/java8/JFunction3.java index b75da0669b..6d81bb3a18 100644 --- a/src/library/scala/runtime/java8/JFunction3.java +++ b/src/library/scala/runtime/java8/JFunction3.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction3<T1, T2, T3, R> extends scala.Function3<T1, T2, T3, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, R>>> curried() { - return scala.Function3$class.curried(this); - } - - default scala.Function1<scala.Tuple3<T1, T2, T3>, R> tupled() { - return scala.Function3$class.tupled(this); - } - - +public interface JFunction3<T1, T2, T3, R> extends scala.Function3<T1, T2, T3, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction4.java b/src/library/scala/runtime/java8/JFunction4.java index 20f89141bd..6c5cd3b61d 100644 --- a/src/library/scala/runtime/java8/JFunction4.java +++ b/src/library/scala/runtime/java8/JFunction4.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction4<T1, T2, T3, T4, R> extends scala.Function4<T1, T2, T3, T4, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, R>>>> curried() { - return scala.Function4$class.curried(this); - } - - default scala.Function1<scala.Tuple4<T1, T2, T3, T4>, R> tupled() { - return scala.Function4$class.tupled(this); - } - - +public interface JFunction4<T1, T2, T3, T4, R> extends scala.Function4<T1, T2, T3, T4, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction5.java b/src/library/scala/runtime/java8/JFunction5.java index ce15f14e22..eca1a406a6 100644 --- a/src/library/scala/runtime/java8/JFunction5.java +++ b/src/library/scala/runtime/java8/JFunction5.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction5<T1, T2, T3, T4, T5, R> extends scala.Function5<T1, T2, T3, T4, T5, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, R>>>>> curried() { - return scala.Function5$class.curried(this); - } - - default scala.Function1<scala.Tuple5<T1, T2, T3, T4, T5>, R> tupled() { - return scala.Function5$class.tupled(this); - } - - +public interface JFunction5<T1, T2, T3, T4, T5, R> extends scala.Function5<T1, T2, T3, T4, T5, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction6.java b/src/library/scala/runtime/java8/JFunction6.java index 07c0ca9665..1c9daed5aa 100644 --- a/src/library/scala/runtime/java8/JFunction6.java +++ b/src/library/scala/runtime/java8/JFunction6.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction6<T1, T2, T3, T4, T5, T6, R> extends scala.Function6<T1, T2, T3, T4, T5, T6, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, R>>>>>> curried() { - return scala.Function6$class.curried(this); - } - - default scala.Function1<scala.Tuple6<T1, T2, T3, T4, T5, T6>, R> tupled() { - return scala.Function6$class.tupled(this); - } - - +public interface JFunction6<T1, T2, T3, T4, T5, T6, R> extends scala.Function6<T1, T2, T3, T4, T5, T6, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction7.java b/src/library/scala/runtime/java8/JFunction7.java index f765ade092..c1aa130ba1 100644 --- a/src/library/scala/runtime/java8/JFunction7.java +++ b/src/library/scala/runtime/java8/JFunction7.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction7<T1, T2, T3, T4, T5, T6, T7, R> extends scala.Function7<T1, T2, T3, T4, T5, T6, T7, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, scala.Function1<T7, R>>>>>>> curried() { - return scala.Function7$class.curried(this); - } - - default scala.Function1<scala.Tuple7<T1, T2, T3, T4, T5, T6, T7>, R> tupled() { - return scala.Function7$class.tupled(this); - } - - +public interface JFunction7<T1, T2, T3, T4, T5, T6, T7, R> extends scala.Function7<T1, T2, T3, T4, T5, T6, T7, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction8.java b/src/library/scala/runtime/java8/JFunction8.java index ffd362b0af..425e694df8 100644 --- a/src/library/scala/runtime/java8/JFunction8.java +++ b/src/library/scala/runtime/java8/JFunction8.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction8<T1, T2, T3, T4, T5, T6, T7, T8, R> extends scala.Function8<T1, T2, T3, T4, T5, T6, T7, T8, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, scala.Function1<T7, scala.Function1<T8, R>>>>>>>> curried() { - return scala.Function8$class.curried(this); - } - - default scala.Function1<scala.Tuple8<T1, T2, T3, T4, T5, T6, T7, T8>, R> tupled() { - return scala.Function8$class.tupled(this); - } - - +public interface JFunction8<T1, T2, T3, T4, T5, T6, T7, T8, R> extends scala.Function8<T1, T2, T3, T4, T5, T6, T7, T8, R>, java.io.Serializable { } diff --git a/src/library/scala/runtime/java8/JFunction9.java b/src/library/scala/runtime/java8/JFunction9.java index e3fca09be0..21c3c8c6e3 100644 --- a/src/library/scala/runtime/java8/JFunction9.java +++ b/src/library/scala/runtime/java8/JFunction9.java @@ -6,17 +6,5 @@ package scala.runtime.java8; @FunctionalInterface -public interface JFunction9<T1, T2, T3, T4, T5, T6, T7, T8, T9, R> extends scala.Function9<T1, T2, T3, T4, T5, T6, T7, T8, T9, R> { - default void $init$() { - }; - - default scala.Function1<T1, scala.Function1<T2, scala.Function1<T3, scala.Function1<T4, scala.Function1<T5, scala.Function1<T6, scala.Function1<T7, scala.Function1<T8, scala.Function1<T9, R>>>>>>>>> curried() { - return scala.Function9$class.curried(this); - } - - default scala.Function1<scala.Tuple9<T1, T2, T3, T4, T5, T6, T7, T8, T9>, R> tupled() { - return scala.Function9$class.tupled(this); - } - - +public interface JFunction9<T1, T2, T3, T4, T5, T6, T7, T8, T9, R> extends scala.Function9<T1, T2, T3, T4, T5, T6, T7, T8, T9, R>, java.io.Serializable { } diff --git a/src/library/scala/sys/process/BasicIO.scala b/src/library/scala/sys/process/BasicIO.scala index 640f7e68c2..b39ae77c62 100644 --- a/src/library/scala/sys/process/BasicIO.scala +++ b/src/library/scala/sys/process/BasicIO.scala @@ -33,7 +33,7 @@ object BasicIO { final val BufferSize = 8192 /** Used to separate lines in the `processFully` function that takes `Appendable`. */ - final val Newline = props("line.separator") + final val Newline = System.lineSeparator private[process] final class Streamed[T]( val process: T => Unit, diff --git a/src/library/scala/sys/process/ProcessBuilderImpl.scala b/src/library/scala/sys/process/ProcessBuilderImpl.scala index 236baaf038..eef140c16a 100644 --- a/src/library/scala/sys/process/ProcessBuilderImpl.scala +++ b/src/library/scala/sys/process/ProcessBuilderImpl.scala @@ -56,7 +56,7 @@ private[process] trait ProcessBuilderImpl { success put false val t = Spawn({ runImpl(io) - success set true + success.put(true) }, io.daemonizeThreads) new ThreadProcess(t, success) diff --git a/src/library/scala/sys/process/ProcessImpl.scala b/src/library/scala/sys/process/ProcessImpl.scala index 559c0ee525..6da0dee056 100644 --- a/src/library/scala/sys/process/ProcessImpl.scala +++ b/src/library/scala/sys/process/ProcessImpl.scala @@ -30,8 +30,8 @@ private[process] trait ProcessImpl { def apply[T](f: => T): (Thread, () => T) = { val result = new SyncVar[Either[Throwable, T]] def run(): Unit = - try result set Right(f) - catch { case e: Exception => result set Left(e) } + try result.put(Right(f)) + catch { case e: Exception => result.put(Left(e)) } val t = Spawn(run()) @@ -91,8 +91,8 @@ private[process] trait ProcessImpl { protected lazy val (processThread, getExitValue, destroyer) = { val code = new SyncVar[Option[Int]]() - code set None - val thread = Spawn(code set runAndExitValue()) + code.put(None) + val thread = Spawn(code.put(runAndExitValue())) ( thread, diff --git a/src/library/scala/sys/process/package.scala b/src/library/scala/sys/process/package.scala index 0e9a1bfc56..ff0fd920c9 100644 --- a/src/library/scala/sys/process/package.scala +++ b/src/library/scala/sys/process/package.scala @@ -203,9 +203,9 @@ package scala.sys { package object process extends ProcessImplicits { /** The arguments passed to `java` when creating this process */ def javaVmArguments: List[String] = { - import scala.collection.JavaConversions._ + import scala.collection.JavaConverters._ - java.lang.management.ManagementFactory.getRuntimeMXBean().getInputArguments().toList + java.lang.management.ManagementFactory.getRuntimeMXBean.getInputArguments.asScala.toList } /** The input stream of this process */ def stdin = java.lang.System.in diff --git a/src/library/scala/util/Either.scala b/src/library/scala/util/Either.scala index 675b3c8c61..01da0c1ef2 100644 --- a/src/library/scala/util/Either.scala +++ b/src/library/scala/util/Either.scala @@ -11,8 +11,6 @@ package scala package util -import scala.language.implicitConversions - /** Represents a value of one of two possible types (a disjoint union.) * Instances of Either are either an instance of [[scala.util.Left]] or [[scala.util.Right]]. * diff --git a/src/library/scala/util/MurmurHash.scala b/src/library/scala/util/MurmurHash.scala index e05fe0875b..1b6db5d6aa 100644 --- a/src/library/scala/util/MurmurHash.scala +++ b/src/library/scala/util/MurmurHash.scala @@ -10,7 +10,7 @@ package scala package util /** An implementation of Austin Appleby's MurmurHash 3.0 algorithm - * (32 bit version); reference: http://code.google.com/p/smhasher + * (32 bit version); reference: https://github.com/aappleby/smhasher * * This is the hash used by collections and case classes (including * tuples). diff --git a/src/library/scala/util/Properties.scala b/src/library/scala/util/Properties.scala index 7ea597eac9..a176748cd6 100644 --- a/src/library/scala/util/Properties.scala +++ b/src/library/scala/util/Properties.scala @@ -120,7 +120,7 @@ private[scala] trait PropertiesTrait { /** The default end of line character. */ - def lineSeparator = propOrElse("line.separator", "\n") + def lineSeparator = System.lineSeparator() /* Various well-known properties. */ def javaClassPath = propOrEmpty("java.class.path") @@ -148,6 +148,8 @@ private[scala] trait PropertiesTrait { // the reason why we don't follow developer.apple.com/library/mac/#technotes/tn2002/tn2110. /** Returns `true` iff the underlying operating system is a version of Apple Mac OSX. */ def isMac = osName startsWith "Mac OS X" + /** Returns `true` iff the underlying operating system is a Linux distribution. */ + def isLinux = osName startsWith "Linux" /* Some runtime values. */ private[scala] def isAvian = javaVmName contains "Avian" diff --git a/src/library/scala/util/Try.scala b/src/library/scala/util/Try.scala index 84d170249d..3c8b21b03c 100644 --- a/src/library/scala/util/Try.scala +++ b/src/library/scala/util/Try.scala @@ -10,7 +10,6 @@ package scala package util import scala.util.control.NonFatal -import scala.language.implicitConversions /** * The `Try` type represents a computation that may either result in an exception, or return a diff --git a/src/library/scala/util/control/Exception.scala b/src/library/scala/util/control/Exception.scala index 702d3b5e5e..64f491d7f0 100644 --- a/src/library/scala/util/control/Exception.scala +++ b/src/library/scala/util/control/Exception.scala @@ -13,21 +13,136 @@ package control import scala.reflect.{ ClassTag, classTag } import scala.language.implicitConversions - /** Classes representing the components of exception handling. - * Each class is independently composable. Some example usages: + * + * Each class is independently composable. + * + * This class differs from [[scala.util.Try]] in that it focuses on composing exception handlers rather than + * composing behavior. All behavior should be composed first and fed to a [[Catch]] object using one of the + * `opt`, `either` or `withTry` methods. Taken together the classes provide a DSL for composing catch and finally + * behaviors. + * + * === Examples === + * + * Create a `Catch` which handles specified exceptions. * {{{ * import scala.util.control.Exception._ * import java.net._ * * val s = "http://www.scala-lang.org/" - * val x1 = catching(classOf[MalformedURLException]) opt new URL(s) - * val x2 = catching(classOf[MalformedURLException], classOf[NullPointerException]) either new URL(s) + * + * // Some(http://www.scala-lang.org/) + * val x1: Option[URL] = catching(classOf[MalformedURLException]) opt new URL(s) + * + * // Right(http://www.scala-lang.org/) + * val x2: Either[Throwable,URL] = + * catching(classOf[MalformedURLException], classOf[NullPointerException]) either new URL(s) + * + * // Success(http://www.scala-lang.org/) + * val x3: Try[URL] = catching(classOf[MalformedURLException], classOf[NullPointerException]) withTry new URL(s) + * + * val defaultUrl = new URL("http://example.com") + * // URL(http://example.com) because htt/xx throws MalformedURLException + * val x4: URL = failAsValue(classOf[MalformedURLException])(defaultUrl)(new URL("htt/xx")) + * }}} + * + * Create a `Catch` which logs exceptions using `handling` and `by`. + * {{{ + * def log(t: Throwable): Unit = t.printStackTrace + * + * val withThrowableLogging: Catch[Unit] = handling(classOf[MalformedURLException]) by (log) + * + * def printUrl(url: String) : Unit = { + * val con = new URL(url) openConnection() + * val source = scala.io.Source.fromInputStream(con.getInputStream()) + * source.getLines.foreach(println) + * } + * + * val badUrl = "htt/xx" + * // Prints stacktrace, + * // java.net.MalformedURLException: no protocol: htt/xx + * // at java.net.URL.<init>(URL.java:586) + * withThrowableLogging { printUrl(badUrl) } + * + * val goodUrl = "http://www.scala-lang.org/" + * // Prints page content, + * // <!DOCTYPE html> + * // <html> + * withThrowableLogging { printUrl(goodUrl) } + * }}} + * + * Use `unwrapping` to create a `Catch` that unwraps exceptions before rethrowing. + * {{{ + * class AppException(cause: Throwable) extends RuntimeException(cause) + * + * val unwrappingCatch: Catch[Nothing] = unwrapping(classOf[AppException]) + * + * def calcResult: Int = throw new AppException(new NullPointerException) + * + * // Throws NPE not AppException, + * // java.lang.NullPointerException + * // at .calcResult(<console>:17) + * val result = unwrappingCatch(calcResult) + * }}} + * + * Use `failAsValue` to provide a default when a specified exception is caught. + * + * {{{ + * val inputDefaulting: Catch[Int] = failAsValue(classOf[NumberFormatException])(0) + * val candidatePick = "seven" // scala.io.StdIn.readLine() + * + * // Int = 0 + * val pick = inputDefaulting(candidatePick.toInt) + * }}} + * + * Compose multiple `Catch`s with `or` to build a `Catch` that provides default values varied by exception. + * {{{ + * val formatDefaulting: Catch[Int] = failAsValue(classOf[NumberFormatException])(0) + * val nullDefaulting: Catch[Int] = failAsValue(classOf[NullPointerException])(-1) + * val otherDefaulting: Catch[Int] = nonFatalCatch withApply(_ => -100) + * + * val combinedDefaulting: Catch[Int] = formatDefaulting or nullDefaulting or otherDefaulting + * + * def p(s: String): Int = s.length * s.toInt + * + * // Int = 0 + * combinedDefaulting(p("tenty-nine")) + * + * // Int = -1 + * combinedDefaulting(p(null: String)) + * + * // Int = -100 + * combinedDefaulting(throw new IllegalStateException) + * + * // Int = 22 + * combinedDefaulting(p("11")) * }}} * - * This class differs from `scala.util.Try` in that it focuses on composing exception handlers rather than - * composing behavior. All behavior should be composed first and fed to a `Catch` object using one of the - * `opt` or `either` methods. + * @groupname composition-catch Catch behavior composition + * @groupprio composition-catch 10 + * @groupdesc composition-catch Build Catch objects from exception lists and catch logic + * + * @groupname composition-finally Finally behavior composition + * @groupprio composition-finally 20 + * @groupdesc composition-finally Build Catch objects from finally logic + * + * @groupname canned-behavior General purpose catch objects + * @groupprio canned-behavior 30 + * @groupdesc canned-behavior Catch objects with predefined behavior. Use combinator methods to compose additional behavior. + * + * @groupname dsl DSL behavior composition + * @groupprio dsl 40 + * @groupdesc dsl Expressive Catch behavior composition + * + * @groupname composition-catch-promiscuously Promiscuous Catch behaviors + * @groupprio composition-catch-promiscuously 50 + * @groupdesc composition-catch-promiscuously Useful if catching `ControlThrowable` or `InterruptedException` is required. + * + * @groupname logic-container Logic Containers + * @groupprio logic-container 60 + * @groupdesc logic-container Containers for catch and finally behavior. + * + * @define protectedExceptions `ControlThrowable` or `InterruptedException` * * @author Paul Phillips */ @@ -51,6 +166,7 @@ object Exception { /** !!! Not at all sure of every factor which goes into this, * and/or whether we need multiple standard variations. + * @return true if `x` is $protectedExceptions otherwise false. */ def shouldRethrow(x: Throwable): Boolean = x match { case _: ControlThrowable => true @@ -70,7 +186,9 @@ object Exception { override def toString() = name + "(" + desc + ")" } - /** A container class for finally code. */ + /** A container class for finally code. + * @group logic-container + */ class Finally private[Exception](body: => Unit) extends Described { protected val name = "Finally" @@ -83,6 +201,11 @@ object Exception { * Pass a different value for rethrow if you want to probably * unwisely allow catching control exceptions and other throwables * which the rest of the world may expect to get through. + * @tparam T result type of bodies used in try and catch blocks + * @param pf Partial function used when applying catch logic to determine result value + * @param fin Finally logic which if defined will be invoked after catch logic + * @param rethrow Predicate on throwables determining when to rethrow a caught [[Throwable]] + * @group logic-container */ class Catch[+T]( val pf: Catcher[T], @@ -105,10 +228,12 @@ object Exception { } finally fin foreach (_.invoke()) - /* Create an empty Try container with this Catch and the supplied `Finally`. */ - def andFinally(body: => Unit): Catch[T] = fin match { - case None => new Catch(pf, Some(new Finally(body)), rethrow) - case Some(f) => new Catch(pf, Some(f and body), rethrow) + /** Create a new Catch container from this object and the supplied finally body. + * @param body The additional logic to apply after all existing finally bodies + */ + def andFinally(body: => Unit): Catch[T] = { + val appendedFin = fin map(_ and body) getOrElse new Finally(body) + new Catch(pf, Some(appendedFin), rethrow) } /** Apply this catch logic to the supplied body, mapping the result @@ -117,13 +242,13 @@ object Exception { def opt[U >: T](body: => U): Option[U] = toOption(Some(body)) /** Apply this catch logic to the supplied body, mapping the result - * into Either[Throwable, T] - Left(exception) if an exception was caught, - * Right(T) otherwise. + * into `Either[Throwable, T]` - `Left(exception)` if an exception was caught, + * `Right(T)` otherwise. */ def either[U >: T](body: => U): Either[Throwable, U] = toEither(Right(body)) /** Apply this catch logic to the supplied body, mapping the result - * into Try[T] - Failure if an exception was caught, Success(T) otherwise. + * into `Try[T]` - `Failure` if an exception was caught, `Success(T)` otherwise. */ def withTry[U >: T](body: => U): scala.util.Try[U] = toTry(Success(body)) @@ -147,23 +272,30 @@ object Exception { final def nonFatalCatcher[T]: Catcher[T] = mkThrowableCatcher({ case NonFatal(_) => true; case _ => false }, throw _) final def allCatcher[T]: Catcher[T] = mkThrowableCatcher(_ => true, throw _) - /** The empty `Catch` object. */ + /** The empty `Catch` object. + * @group canned-behavior + **/ final val noCatch: Catch[Nothing] = new Catch(nothingCatcher) withDesc "<nothing>" - /** A `Catch` object which catches everything. */ + /** A `Catch` object which catches everything. + * @group canned-behavior + **/ final def allCatch[T]: Catch[T] = new Catch(allCatcher[T]) withDesc "<everything>" - /** A `Catch` object which catches non-fatal exceptions. */ + /** A `Catch` object which catches non-fatal exceptions. + * @group canned-behavior + **/ final def nonFatalCatch[T]: Catch[T] = new Catch(nonFatalCatcher[T]) withDesc "<non-fatal>" /** Creates a `Catch` object which will catch any of the supplied exceptions. * Since the returned `Catch` object has no specific logic defined and will simply - * rethrow the exceptions it catches, you will typically want to call `opt` or - * `either` on the return value, or assign custom logic by calling "withApply". + * rethrow the exceptions it catches, you will typically want to call `opt`, + * `either` or `withTry` on the return value, or assign custom logic by calling "withApply". * * Note that `Catch` objects automatically rethrow `ControlExceptions` and others * which should only be caught in exceptional circumstances. If you really want * to catch exactly what you specify, use `catchingPromiscuously` instead. + * @group composition-catch */ def catching[T](exceptions: Class[_]*): Catch[T] = new Catch(pfFromExceptions(exceptions : _*)) withDesc (exceptions map (_.getName) mkString ", ") @@ -172,42 +304,56 @@ object Exception { /** Creates a `Catch` object which will catch any of the supplied exceptions. * Unlike "catching" which filters out those in shouldRethrow, this one will - * catch whatever you ask of it: `ControlThrowable`, `InterruptedException`, - * `OutOfMemoryError`, you name it. + * catch whatever you ask of it including $protectedExceptions. + * @group composition-catch-promiscuously */ def catchingPromiscuously[T](exceptions: Class[_]*): Catch[T] = catchingPromiscuously(pfFromExceptions(exceptions : _*)) def catchingPromiscuously[T](c: Catcher[T]): Catch[T] = new Catch(c, None, _ => false) - /** Creates a `Catch` object which catches and ignores any of the supplied exceptions. */ + /** Creates a `Catch` object which catches and ignores any of the supplied exceptions. + * @group composition-catch + */ def ignoring(exceptions: Class[_]*): Catch[Unit] = catching(exceptions: _*) withApply (_ => ()) - /** Creates a `Catch` object which maps all the supplied exceptions to `None`. */ + /** Creates a `Catch` object which maps all the supplied exceptions to `None`. + * @group composition-catch + */ def failing[T](exceptions: Class[_]*): Catch[Option[T]] = catching(exceptions: _*) withApply (_ => None) - /** Creates a `Catch` object which maps all the supplied exceptions to the given value. */ + /** Creates a `Catch` object which maps all the supplied exceptions to the given value. + * @group composition-catch + */ def failAsValue[T](exceptions: Class[_]*)(value: => T): Catch[T] = catching(exceptions: _*) withApply (_ => value) + class By[T,R](f: T => R) { + def by(x: T): R = f(x) + } + /** Returns a partially constructed `Catch` object, which you must give - * an exception handler function as an argument to `by`. Example: + * an exception handler function as an argument to `by`. + * @example * {{{ - * handling(ex1, ex2) by (_.printStackTrace) + * handling(classOf[MalformedURLException], classOf[NullPointerException]) by (_.printStackTrace) * }}} + * @group dsl */ - class By[T,R](f: T => R) { - def by(x: T): R = f(x) - } + // TODO: Add return type def handling[T](exceptions: Class[_]*) = { def fun(f: Throwable => T) = catching(exceptions: _*) withApply f new By[Throwable => T, Catch[T]](fun _) } - /** Returns a `Catch` object with no catch logic and the argument as `Finally`. */ + /** Returns a `Catch` object with no catch logic and the argument as the finally logic. + * @group composition-finally + */ def ultimately[T](body: => Unit): Catch[T] = noCatch andFinally body - /** Creates a `Catch` object which unwraps any of the supplied exceptions. */ + /** Creates a `Catch` object which unwraps any of the supplied exceptions. + * @group composition-catch + */ def unwrapping[T](exceptions: Class[_]*): Catch[T] = { def unwrap(x: Throwable): Throwable = if (wouldMatch(x, exceptions) && x.getCause != null) unwrap(x.getCause) diff --git a/src/library/scala/util/control/NoStackTrace.scala b/src/library/scala/util/control/NoStackTrace.scala index 3c42944af1..3647af4ac3 100644 --- a/src/library/scala/util/control/NoStackTrace.scala +++ b/src/library/scala/util/control/NoStackTrace.scala @@ -14,6 +14,8 @@ package util.control * on a global basis via a system property wrapper in * [[scala.sys.SystemProperties]]. * + * @note Since JDK 1.7, a similar effect can be achieved with `class Ex extends Throwable(..., writableStackTrace = false)` + * * @author Paul Phillips * @since 2.8 */ diff --git a/src/library/scala/util/hashing/MurmurHash3.scala b/src/library/scala/util/hashing/MurmurHash3.scala index 4e5537954f..6a56910451 100644 --- a/src/library/scala/util/hashing/MurmurHash3.scala +++ b/src/library/scala/util/hashing/MurmurHash3.scala @@ -193,7 +193,7 @@ private[hashing] class MurmurHash3 { * to remedy some weaknesses and improve performance. This represents the * latest and supposedly final version of the algorithm (revision 136). * - * @see [[http://code.google.com/p/smhasher]] + * @see [[https://github.com/aappleby/smhasher]] */ object MurmurHash3 extends MurmurHash3 { final val arraySeed = 0x3c074a61 diff --git a/src/library/scala/util/matching/Regex.scala b/src/library/scala/util/matching/Regex.scala index 6d3d015b1a..bd55fb5d04 100644 --- a/src/library/scala/util/matching/Regex.scala +++ b/src/library/scala/util/matching/Regex.scala @@ -182,6 +182,9 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends * val namedYears = for (m <- namedDate findAllMatchIn dates) yield m group "year" * }}} * + * Group names supplied to the constructor are preferred to inline group names + * when retrieving matched groups by name. Not all platforms support inline names. + * * This constructor does not support options as flags, which must be * supplied as inline flags in the pattern string: `(?idmsux-idmsux)`. * @@ -578,6 +581,9 @@ object Regex { */ trait MatchData { + /** Basically, wraps a platform Matcher. */ + protected def matcher: Matcher + /** The source from which the match originated */ val source: CharSequence @@ -650,16 +656,25 @@ object Regex { private lazy val nameToIndex: Map[String, Int] = Map[String, Int]() ++ ("" :: groupNames.toList).zipWithIndex - /** Returns the group with given name. + /** Returns the group with the given name. + * + * Uses explicit group names when supplied; otherwise, + * queries the underlying implementation for inline named groups. + * Not all platforms support inline group names. * * @param id The group name * @return The requested group - * @throws NoSuchElementException if the requested group name is not defined + * @throws IllegalArgumentException if the requested group name is not defined */ - def group(id: String): String = nameToIndex.get(id) match { - case None => throw new NoSuchElementException("group name "+id+" not defined") - case Some(index) => group(index) - } + def group(id: String): String = ( + if (groupNames.isEmpty) + matcher group id + else + nameToIndex.get(id) match { + case Some(index) => group(index) + case None => matcher group id + } + ) /** The matched string; equivalent to `matched.toString`. */ override def toString = matched @@ -667,7 +682,7 @@ object Regex { /** Provides information about a successful match. */ class Match(val source: CharSequence, - private[matching] val matcher: Matcher, + protected[matching] val matcher: Matcher, val groupNames: Seq[String]) extends MatchData { /** The index of the first matched character. */ diff --git a/src/manual/scala/man1/fsc.scala b/src/manual/scala/man1/fsc.scala index f2f8feb3fa..c7399da635 100644 --- a/src/manual/scala/man1/fsc.scala +++ b/src/manual/scala/man1/fsc.scala @@ -126,11 +126,7 @@ object fsc extends Command { "With Java 1.5 (or newer) one may for example configure the " & "memory usage of the JVM as follows: " & - Mono("JAVA_OPTS=\"-Xmx512M -Xms16M -Xss16M\""), - - "With " & Link("GNU Java", "http://gcc.gnu.org/java/") & " one " & - "may configure the memory usage of the GIJ as follows: " & - Mono("JAVA_OPTS=\"--mx512m --ms16m\"") + Mono("JAVA_OPTS=\"-Xmx512M -Xms16M -Xss16M\"") )))) val exitStatus = Section("EXIT STATUS", diff --git a/src/manual/scala/man1/scala.scala b/src/manual/scala/man1/scala.scala index 92d9c59cca..9f97dd546c 100644 --- a/src/manual/scala/man1/scala.scala +++ b/src/manual/scala/man1/scala.scala @@ -65,6 +65,10 @@ object scala extends Command { "Do not use the " & MBold("fsc") & " offline compiler."), Definition( + CmdOption("nc"), + "Same as " & Mono("-nocompdaemon") & "."), + + Definition( CmdOptionBound("D", "property=value"), "Set a Java system property. If no value is specified, " & "then the property is set to the empty string."), @@ -135,6 +139,11 @@ object scala extends Command { "line. Headers can be used to make stand-alone script files, as shown " & "in the examples below.", + "When running a script or using " & Mono("-e") & ", an already running " & + "compilation daemon (fsc) is used, or a new one started on demand. The " & + Mono("-nocompdaemon") & " or " & Mono("-nc") & " option can be used to " & + "prevent this.", + "If " & Mono("scala") & " is run from an sbaz(1) directory, " & "then it will add to its classpath any jars installed in the " & "lib directory of the sbaz directory. Additionally, if no " & @@ -175,11 +184,7 @@ object scala extends Command { "With Java 1.5 (or newer) one may for example configure the " & "memory usage of the JVM as follows: " & - Mono("JAVA_OPTS=\"-Xmx512M -Xms16M -Xss16M\""), - - "With " & Link("GNU Java", "http://gcc.gnu.org/java/") & " one " & - "may configure the memory usage of the GIJ as follows: " & - Mono("JAVA_OPTS=\"--mx512m --ms16m\"") + Mono("JAVA_OPTS=\"-Xmx512M -Xms16M -Xss16M\"") )))) val examples = Section("EXAMPLES", diff --git a/src/manual/scala/man1/scalac.scala b/src/manual/scala/man1/scalac.scala index 3219b10293..6ffcccea25 100644 --- a/src/manual/scala/man1/scalac.scala +++ b/src/manual/scala/man1/scalac.scala @@ -5,10 +5,6 @@ package scala.man1 -/** - * @author Stephane Micheloud - * @version 1.0 - */ object scalac extends Command { import _root_.scala.tools.docutil.ManPage._ @@ -77,7 +73,7 @@ object scalac extends Command { SeqPara( "Specify where to find user class files (on Unix-based systems " & "a colon-separated list of paths, on Windows-based systems, a " & - "semicolon-separate list of paths). This does not override the " & + "semicolon-separated list of paths). This does not override the " & "built-in (" & Mono("\"boot\"") & ") search path.", "The default class path is the current directory. Setting the " & Mono("CLASSPATH") & " variable or using the " & Mono("-classpath") & " " & @@ -153,7 +149,7 @@ object scalac extends Command { "Specify location(s) of source files."), Definition( CmdOptionBound("target:", "{jvm-1.8}"), - SeqPara( + SeqPara( Mono("\"jvm-1.8\"") & " target JVM 1.8 (default)")), Definition( CmdOption("toolcp", Argument("path")), @@ -193,7 +189,7 @@ object scalac extends Command { Definition( CmdOption("Xcheckinit"), "Wrap field accessors to throw an exception on uninitialized access."), - Definition( + Definition( CmdOption("Xdev"), "Enable warnings for developers working on the Scala compiler"), Definition( @@ -209,7 +205,7 @@ object scalac extends Command { Definition( CmdOption("Xfatal-warnings"), "Fail the compilation if there are any warnings."), - Definition( + Definition( CmdOption("Xfull-lubs"), "Retain pre 2.10 behavior of less aggressive truncation of least upper bounds."), Definition( @@ -221,25 +217,25 @@ object scalac extends Command { Definition( CmdOption("Xlint"), "Enable recommended additional warnings."), - Definition( + Definition( CmdOption("Xlog-free-terms"), "Print a message when reification creates a free term."), - Definition( + Definition( CmdOption("Xlog-free-types"), "Print a message when reification resorts to generating a free type."), - Definition( + Definition( CmdOption("Xlog-implicit-conversions"), "Print a message whenever an implicit conversion is inserted."), Definition( CmdOption("Xlog-implicits"), "Show more detail on why some implicits are not applicable."), - Definition( + Definition( CmdOption("Xlog-reflective-calls"), "Print a message when a reflective method call is generated."), - Definition( + Definition( CmdOptionBound("Xmacro-settings:", Argument("option")), "Custom settings for macros."), - Definition( + Definition( CmdOption("Xmain-class", Argument("path")), "Class for manifest's Main-Class entry (only useful with -d <jar>)."), Definition( @@ -251,7 +247,7 @@ object scalac extends Command { Definition( CmdOption("Xno-forwarders"), "Do not generate static forwarders in mirror classes."), - Definition( + Definition( CmdOption("Xno-patmat-analysis"), "Don't perform exhaustivity/unreachability analysis. Also, ignore " & MItalic("@switch") & " annotation."), Definition( @@ -309,16 +305,21 @@ object scalac extends Command { Definition( CmdOptionBound("Xsource:", Argument("version")), "Treat compiler input as Scala source for the specified version, see SI-8126."), - Definition( + Definition( CmdOption("Xsource-reader", Argument("classname")), "Specify a custom method for reading source files."), - Definition( + Definition( CmdOption("Xstrict-inference"), "Don't infer known-unsound types."), Definition( CmdOption("Xverify"), "Verify generic signatures in generated bytecode (asm backend only)."), Definition( + CmdOptionBound("Xxml:", "{coalescing}"), + SeqPara( + "Configure XML parsing.", + Mono("\"coalescing\"") & " convert PCData to Text and coalesce sibling nodes (default in 2.11).")), + Definition( CmdOption("Y"), "Print a synopsis of private options.") ) @@ -332,34 +333,34 @@ object scalac extends Command { Definition( MItalic("namer"), "resolve names, attach symbols to named trees"), - Definition( + Definition( MItalic("packageobjects"), "load package objects"), - Definition( + Definition( MItalic("typer"), "the meat and potatoes: type the trees"), Definition( MItalic("patmat"), "translate match expressions"), - Definition( + Definition( MItalic("superaccessors"), "add super accessors in traits and nested classes"), - Definition( + Definition( MItalic("extmethods"), "add extension methods for inline classes"), - Definition( + Definition( MItalic("pickler"), "serialize symbol tables"), Definition( MItalic("refchecks"), "reference/override checking, translate nested objects"), - Definition( + Definition( MItalic("selectiveanf"), "ANF pre-transform for " & MItalic("@cps") & " (CPS plugin)"), - Definition( + Definition( MItalic("selectivecps"), MItalic("@cps") & "-driven transform of selectiveanf assignments (CPS plugin)"), - Definition( + Definition( MItalic("uncurry"), "uncurry, translate function values to anonymous classes"), Definition( @@ -447,11 +448,7 @@ object scalac extends Command { "With Java 1.5 (or newer) one may for example configure the " & "memory usage of the JVM as follows: " & - Mono("JAVA_OPTS=\"-Xmx512M -Xms16M -Xss16M\""), - - "With " & Link("GNU Java", "http://gcc.gnu.org/java/") & " one " & - "may configure the memory usage of the GIJ as follows: " & - Mono("JAVA_OPTS=\"--mx512m --ms16m\"") + Mono("JAVA_OPTS=\"-Xmx512M -Xms16M -Xss16M\"") )))) val examples = Section("EXAMPLES", diff --git a/src/partest-extras/scala/tools/partest/BytecodeTest.scala b/src/partest-extras/scala/tools/partest/BytecodeTest.scala index 290b7b434e..532dfd2a73 100644 --- a/src/partest-extras/scala/tools/partest/BytecodeTest.scala +++ b/src/partest-extras/scala/tools/partest/BytecodeTest.scala @@ -1,10 +1,10 @@ package scala.tools.partest -import scala.tools.nsc.util.JavaClassPath import scala.collection.JavaConverters._ -import scala.tools.asm.{ClassWriter, ClassReader} +import scala.tools.asm.{ClassReader, ClassWriter} import scala.tools.asm.tree._ -import java.io.{File => JFile, InputStream} +import java.io.{InputStream, File => JFile} + import AsmNode._ /** @@ -125,12 +125,16 @@ abstract class BytecodeTest { cn } - protected lazy val classpath: JavaClassPath = { - import scala.tools.nsc.util.ClassPath.DefaultJavaContext + protected lazy val classpath: scala.tools.nsc.util.ClassPath = { + import scala.tools.nsc.classpath.AggregateClassPath + import scala.tools.nsc.classpath.ClassPathFactory import scala.tools.util.PathResolver.Defaults + import scala.tools.nsc.Settings // logic inspired by scala.tools.util.PathResolver implementation - val containers = DefaultJavaContext.classesInExpandedPath(Defaults.javaUserClassPath) - new JavaClassPath(containers, DefaultJavaContext) + // `Settings` is used to check YdisableFlatCpCaching in ZipArchiveFlatClassPath + val factory = new ClassPathFactory(new Settings()) + val containers = factory.classesInExpandedPath(Defaults.javaUserClassPath) + new AggregateClassPath(containers) } } diff --git a/src/partest-extras/scala/tools/partest/Util.scala b/src/partest-extras/scala/tools/partest/Util.scala index 60e9dbb0f9..511997ea35 100644 --- a/src/partest-extras/scala/tools/partest/Util.scala +++ b/src/partest-extras/scala/tools/partest/Util.scala @@ -14,7 +14,7 @@ object Util { * An alternative to [[scala.tools.partest.ReplTest]] that avoids the inconvenience of embedding * test code in a string. */ - def trace[A](a: A) = macro traceImpl[A] + def trace[A](a: A): A = macro traceImpl[A] import scala.reflect.macros.blackbox.Context def traceImpl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A]): c.Expr[A] = { diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala index 9e9fe5d67b..b9fb323a4c 100644 --- a/src/reflect/scala/reflect/api/Symbols.scala +++ b/src/reflect/scala/reflect/api/Symbols.scala @@ -260,9 +260,6 @@ trait Symbols { self: Universe => * with an object definition (module class in scala compiler parlance)? * If yes, `isType` is also guaranteed to be true. * - * Note to compiler developers: During the "mixin" phase, trait implementation class symbols - * receive the `lateMODULE` flag, hence `isImplClass && isModuleClass` becomes true. - * * @group Tests */ def isModuleClass: Boolean = false @@ -354,8 +351,7 @@ trait Symbols { self: Universe => /******************* tests *******************/ /** Does this symbol represent a synthetic (i.e. a compiler-generated) entity? - * Examples of synthetic entities are accessors for vals and vars - * or mixin constructors in trait implementation classes. + * Examples of synthetic entities are accessors for vals and vars. * * @group Tests */ diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 44eee5cbfd..db8ac9b0cb 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -675,6 +675,32 @@ trait Definitions extends api.StandardDefinitions { // Note that these call .dealiasWiden and not .normalize, the latter of which // tends to change the course of events by forcing types. def isFunctionType(tp: Type) = isFunctionTypeDirect(tp.dealiasWiden) + // the number of arguments expected by the function described by `tp` (a FunctionN or SAM type), + // or `-1` if `tp` does not represent a function type or SAM + def functionArityFromType(tp: Type) = { + val dealiased = tp.dealiasWiden + if (isFunctionTypeDirect(dealiased)) dealiased.typeArgs.length - 1 + else samOf(tp) match { + case samSym if samSym.exists => samSym.info.params.length + case _ => -1 + } + } + + // the result type of a function or corresponding SAM type + def functionResultType(tp: Type): Type = { + val dealiased = tp.dealiasWiden + if (isFunctionTypeDirect(dealiased)) dealiased.typeArgs.last + else samOf(tp) match { + case samSym if samSym.exists => tp.memberInfo(samSym).resultType.deconst + case _ => NoType + } + } + + // the SAM's parameters and the Function's formals must have the same length + // (varargs etc don't come into play, as we're comparing signatures, not checking an application) + def samMatchesFunctionBasedOnArity(sam: Symbol, formals: List[Any]): Boolean = + sam.exists && sameLength(sam.info.params, formals) + def isTupleType(tp: Type) = isTupleTypeDirect(tp.dealiasWiden) def tupleComponents(tp: Type) = tp.dealiasWiden.typeArgs @@ -793,10 +819,6 @@ trait Definitions extends api.StandardDefinitions { private[this] var volatileRecursions: Int = 0 private[this] val pendingVolatiles = mutable.HashSet[Symbol]() - def abstractFunctionForFunctionType(tp: Type) = { - assert(isFunctionType(tp), tp) - abstractFunctionType(tp.typeArgs.init, tp.typeArgs.last) - } def functionNBaseType(tp: Type): Type = tp.baseClasses find isFunctionSymbol match { case Some(sym) => tp baseType unspecializedSymbol(sym) case _ => tp @@ -807,22 +829,29 @@ trait Definitions extends api.StandardDefinitions { (sym eq PartialFunctionClass) || (sym eq AbstractPartialFunctionClass) } + private[this] val doSam = settings.isScala212 || (settings.isScala211 && settings.Xexperimental) + /** The single abstract method declared by type `tp` (or `NoSymbol` if it cannot be found). * * The method must be monomorphic and have exactly one parameter list. * The class defining the method is a supertype of `tp` that * has a public no-arg primary constructor. */ - def samOf(tp: Type): Symbol = if (!settings.Xexperimental) NoSymbol else findSam(tp) - - def findSam(tp: Type): Symbol = { - // if tp has a constructor, it must be public and must not take any arguments - // (not even an implicit argument list -- to keep it simple for now) - val tpSym = tp.typeSymbol - val ctor = tpSym.primaryConstructor - val ctorOk = !ctor.exists || (!ctor.isOverloaded && ctor.isPublic && ctor.info.params.isEmpty && ctor.info.paramSectionCount <= 1) + def samOf(tp: Type): Symbol = if (!doSam) NoSymbol else { + // look at erased type because we (only) care about what ends up in bytecode + // (e.g., an alias type or intersection type is fine as long as the intersection dominator compiles to an interface) + val tpSym: Symbol = erasure.javaErasure(tp).typeSymbol + + if (tpSym.exists && tpSym.isClass + // if tp has a constructor (its class is not a trait), it must be public and must not take any arguments + // (implementation restriction: implicit argument lists are excluded to simplify type inference in adaptToSAM) + && { val ctor = tpSym.primaryConstructor + !ctor.exists || (!ctor.isOverloaded && ctor.isPublic && ctor.info.params.isEmpty && ctor.info.paramSectionCount <= 1)} + // we won't be able to create an instance of tp if it doesn't correspond to its self type + // (checking conformance gets complicated when tp is not fully defined, so let's just rule out self types entirely) + && !tpSym.hasSelfType + ) { - if (tpSym.exists && ctorOk) { // find the single abstract member, if there is one // don't go out requiring DEFERRED members, as you will get them even if there's a concrete override: // scala> abstract class X { def m: Int } @@ -921,7 +950,6 @@ trait Definitions extends api.StandardDefinitions { def neverHasTypeParameters(sym: Symbol) = sym match { case _: RefinementClassSymbol => true case _: ModuleClassSymbol => true - case _: ImplClassSymbol => true case _ => ( sym.isPrimitiveValueClass @@ -1124,6 +1152,7 @@ trait Definitions extends api.StandardDefinitions { lazy val ElidableMethodClass = requiredClass[scala.annotation.elidable] lazy val ImplicitNotFoundClass = requiredClass[scala.annotation.implicitNotFound] lazy val ImplicitAmbiguousClass = getClassIfDefined("scala.annotation.implicitAmbiguous") + lazy val JUnitTestClass = getClassIfDefined("org.junit.Test") lazy val MigrationAnnotationClass = requiredClass[scala.annotation.migration] lazy val ScalaStrictFPAttr = requiredClass[scala.annotation.strictfp] lazy val SwitchClass = requiredClass[scala.annotation.switch] @@ -1539,7 +1568,6 @@ trait Definitions extends api.StandardDefinitions { private lazy val PolySigMethods: Set[Symbol] = Set[Symbol](MethodHandle.info.decl(sn.Invoke), MethodHandle.info.decl(sn.InvokeExact)).filter(_.exists) lazy val Scala_Java8_CompatPackage = rootMirror.getPackageIfDefined("scala.runtime.java8") - lazy val Scala_Java8_CompatPackage_JFunction = (0 to MaxFunctionArity).toArray map (i => getMemberIfDefined(Scala_Java8_CompatPackage.moduleClass, TypeName("JFunction" + i))) } } } diff --git a/src/reflect/scala/reflect/internal/Flags.scala b/src/reflect/scala/reflect/internal/Flags.scala index b022ba2981..f058acb7c0 100644 --- a/src/reflect/scala/reflect/internal/Flags.scala +++ b/src/reflect/scala/reflect/internal/Flags.scala @@ -50,7 +50,7 @@ package internal // 34: LIFTED // 35: EXISTENTIAL MIXEDIN // 36: EXPANDEDNAME -// 37: IMPLCLASS PRESUPER/M +// 37: PRESUPER/M // 38: TRANS_FLAG // 39: LOCKED // 40: SPECIALIZED @@ -157,7 +157,6 @@ class Flags extends ModifierFlags { final val MIXEDIN = 1L << 35 // term member has been mixed in final val EXISTENTIAL = 1L << 35 // type is an existential parameter or skolem final val EXPANDEDNAME = 1L << 36 // name has been expanded with class suffix - final val IMPLCLASS = 1L << 37 // symbol is an implementation class final val TRANS_FLAG = 1L << 38 // transient flag guaranteed to be reset after each phase. final val LOCKED = 1L << 39 // temporary flag to catch cyclic dependencies @@ -199,8 +198,7 @@ class Flags extends ModifierFlags { // 17: CONTRAVARIANT/M INCONSTRUCTOR LABEL // 25: DEFAULTPARAM/M TRAIT/M // 35: EXISTENTIAL MIXEDIN - // 37: IMPLCLASS PRESUPER/M - val OverloadedFlagsMask = 0L | BYNAMEPARAM | CONTRAVARIANT | DEFAULTPARAM | EXISTENTIAL | IMPLCLASS + val OverloadedFlagsMask = 0L | BYNAMEPARAM | CONTRAVARIANT | DEFAULTPARAM | EXISTENTIAL // ------- late flags (set by a transformer phase) --------------------------------- // @@ -210,7 +208,7 @@ class Flags extends ModifierFlags { // refchecks 7 [START] <latemethod> // specialize 13 [START] <latefinal> <notprivate> // explicitouter 14 [START] <notprotected> - // erasure 15 [START] <latedeferred> <lateinterface> + // erasure 15 [START] <latedeferred> // mixin 20 [START] <latemodule> <notoverride> // // lateMETHOD set in RefChecks#transformInfo. @@ -218,13 +216,11 @@ class Flags extends ModifierFlags { // notPRIVATE set in Symbols#makeNotPrivate, IExplicitOuter#transform, Inliners. // notPROTECTED set in ExplicitOuter#transform. // lateDEFERRED set in AddInterfaces, Mixin, etc. - // lateINTERFACE set in AddInterfaces#transformMixinInfo. // lateMODULE set in Mixin#transformInfo. // notOVERRIDE set in Mixin#preTransform. final val lateDEFERRED = (DEFERRED: Long) << LateShift final val lateFINAL = (FINAL: Long) << LateShift - final val lateINTERFACE = (INTERFACE: Long) << LateShift final val lateMETHOD = (METHOD: Long) << LateShift final val lateMODULE = (MODULE: Long) << LateShift @@ -439,7 +435,7 @@ class Flags extends ModifierFlags { case LIFTED => "<lifted>" // (1L << 34) case EXISTENTIAL => "<existential/mixedin>" // (1L << 35) case EXPANDEDNAME => "<expandedname>" // (1L << 36) - case IMPLCLASS => "<implclass/presuper>" // (1L << 37) + case PRESUPER => "<presuper>" // (1L << 37) case TRANS_FLAG => "<trans_flag>" // (1L << 38) case LOCKED => "<locked>" // (1L << 39) case SPECIALIZED => "<specialized>" // (1L << 40) @@ -456,7 +452,7 @@ class Flags extends ModifierFlags { case `lateDEFERRED` => "<latedeferred>" // (1L << 51) case `lateFINAL` => "<latefinal>" // (1L << 52) case `lateMETHOD` => "<latemethod>" // (1L << 53) - case `lateINTERFACE` => "<lateinterface>" // (1L << 54) + case 0x80000000000000L => "" // (1L << 54) case `lateMODULE` => "<latemodule>" // (1L << 55) case `notPROTECTED` => "<notprotected>" // (1L << 56) case `notOVERRIDE` => "<notoverride>" // (1L << 57) diff --git a/src/reflect/scala/reflect/internal/HasFlags.scala b/src/reflect/scala/reflect/internal/HasFlags.scala index 5162b15206..673ea4fdef 100644 --- a/src/reflect/scala/reflect/internal/HasFlags.scala +++ b/src/reflect/scala/reflect/internal/HasFlags.scala @@ -120,6 +120,7 @@ trait HasFlags { def isSuperAccessor = hasFlag(SUPERACCESSOR) def isSynthetic = hasFlag(SYNTHETIC) def isTrait = hasFlag(TRAIT) && !hasFlag(PARAM) + def isTraitOrInterface = isTrait || isInterface def isDeferredOrJavaDefault = hasFlag(DEFERRED | JAVA_DEFAULTMETHOD) def isDeferredNotJavaDefault = isDeferred && !hasFlag(JAVA_DEFAULTMETHOD) diff --git a/src/reflect/scala/reflect/internal/Internals.scala b/src/reflect/scala/reflect/internal/Internals.scala index cfacad24b4..1a48701ca7 100644 --- a/src/reflect/scala/reflect/internal/Internals.scala +++ b/src/reflect/scala/reflect/internal/Internals.scala @@ -3,7 +3,6 @@ package reflect package internal import scala.language.implicitConversions -import scala.language.higherKinds import scala.reflect.api.Universe import scala.reflect.macros.Attachments diff --git a/src/reflect/scala/reflect/internal/Positions.scala b/src/reflect/scala/reflect/internal/Positions.scala index 15d68bcdfe..95b3b7fb14 100644 --- a/src/reflect/scala/reflect/internal/Positions.scala +++ b/src/reflect/scala/reflect/internal/Positions.scala @@ -254,7 +254,7 @@ trait Positions extends api.Positions { self: SymbolTable => case mdef: MemberDef => val annTrees = mdef.mods.annotations match { case Nil if mdef.symbol != null => - // After typechecking, annotations are mvoed from the modifiers + // After typechecking, annotations are moved from the modifiers // to the annotation on the symbol of the anotatee. mdef.symbol.annotations.map(_.original) case anns => anns diff --git a/src/reflect/scala/reflect/internal/ReificationSupport.scala b/src/reflect/scala/reflect/internal/ReificationSupport.scala index c58cb71e97..30f2efd7e3 100644 --- a/src/reflect/scala/reflect/internal/ReificationSupport.scala +++ b/src/reflect/scala/reflect/internal/ReificationSupport.scala @@ -287,6 +287,8 @@ trait ReificationSupport { self: SymbolTable => val evdefs = gvdefs.zip(lvdefs).map { case (gvdef @ ValDef(_, _, tpt: TypeTree, _), ValDef(_, _, _, rhs)) => copyValDef(gvdef)(tpt = tpt.original, rhs = rhs) + case (tr1, tr2) => + throw new MatchError((tr1, tr2)) } val edefs = evdefs ::: etdefs if (ctorMods.isTrait) diff --git a/src/reflect/scala/reflect/internal/Reporting.scala b/src/reflect/scala/reflect/internal/Reporting.scala index 2534f59c97..afdae81289 100644 --- a/src/reflect/scala/reflect/internal/Reporting.scala +++ b/src/reflect/scala/reflect/internal/Reporting.scala @@ -9,7 +9,7 @@ package internal /** Provides delegates to the reporter doing the actual work. * All forwarding methods should be marked final, - * but some subclasses out of our reach stil override them. + * but some subclasses out of our reach still override them. * * Eventually, this interface should be reduced to one method: `reporter`, * and clients should indirect themselves (reduce duplication of forwarders). diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index 8358c1295c..0243dd48d2 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -38,6 +38,19 @@ trait StdAttachments { */ case class CompoundTypeTreeOriginalAttachment(parents: List[Tree], stats: List[Tree]) + /** Attached to a Function node during type checking when the expected type is a SAM type (and not a built-in FunctionN). + * + * Ideally, we'd move to Dotty's Closure AST, which tracks the environment, + * the lifted method that has the implementation, and the target type. + * For backwards compatibility, an attachment is the best we can do right now. + * + * @param samTp the expected type that triggered sam conversion (may be a subtype of the type corresponding to sam's owner) + * @param sam the single abstract method implemented by the Function we're attaching this to + * + * @since 2.12.0-M4 + */ + case class SAMFunction(samTp: Type, sam: Symbol) extends PlainAttachment + /** When present, indicates that the host `Ident` has been created from a backquoted identifier. */ case object BackquotedIdentifierAttachment extends PlainAttachment diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 48e912d291..0ac72e7d8b 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -104,7 +104,6 @@ trait StdNames { val ANON_FUN_NAME: NameType = "$anonfun" val EMPTY: NameType = "" val EMPTY_PACKAGE_NAME: NameType = "<empty>" - val IMPL_CLASS_SUFFIX = "$class" val IMPORT: NameType = "<import>" val MODULE_SUFFIX_NAME: NameType = MODULE_SUFFIX_STRING val MODULE_VAR_SUFFIX: NameType = "$module" @@ -303,8 +302,6 @@ trait StdNames { def dropSingletonName(name: Name): TypeName = (name dropRight SINGLETON_SUFFIX.length).toTypeName def singletonName(name: Name): TypeName = (name append SINGLETON_SUFFIX).toTypeName - def implClassName(name: Name): TypeName = (name append IMPL_CLASS_SUFFIX).toTypeName - def interfaceName(implname: Name): TypeName = (implname dropRight IMPL_CLASS_SUFFIX.length).toTypeName } abstract class TermNames extends Keywords with TermNamesApi { @@ -378,7 +375,6 @@ trait StdNames { def isConstructorName(name: Name) = name == CONSTRUCTOR || name == MIXIN_CONSTRUCTOR def isExceptionResultName(name: Name) = name startsWith EXCEPTION_RESULT_PREFIX - def isImplClassName(name: Name) = name endsWith IMPL_CLASS_SUFFIX def isLocalDummyName(name: Name) = name startsWith LOCALDUMMY_PREFIX def isLocalName(name: Name) = name endsWith LOCAL_SUFFIX_STRING def isLoopHeaderLabel(name: Name) = (name startsWith WHILE_PREFIX) || (name startsWith DO_WHILE_PREFIX) @@ -645,6 +641,7 @@ trait StdNames { val accessor: NameType = "accessor" val add_ : NameType = "add" val annotation: NameType = "annotation" + val anyHash: NameType = "anyHash" val anyValClass: NameType = "anyValClass" val apply: NameType = "apply" val applyDynamic: NameType = "applyDynamic" @@ -674,6 +671,7 @@ trait StdNames { val delayedInit: NameType = "delayedInit" val delayedInitArg: NameType = "delayedInit$body" val dollarScope: NameType = "$scope" + val doubleHash: NameType = "doubleHash" val drop: NameType = "drop" val elem: NameType = "elem" val noSelfType: NameType = "noSelfType" @@ -692,13 +690,13 @@ trait StdNames { val finalize_ : NameType = "finalize" val find_ : NameType = "find" val flatMap: NameType = "flatMap" + val floatHash: NameType = "floatHash" val foreach: NameType = "foreach" val freshTermName: NameType = "freshTermName" val freshTypeName: NameType = "freshTypeName" val get: NameType = "get" val parameterTypes: NameType = "parameterTypes" val hashCode_ : NameType = "hashCode" - val hash_ : NameType = "hash" val head : NameType = "head" val immutable: NameType = "immutable" val implicitly: NameType = "implicitly" @@ -715,6 +713,7 @@ trait StdNames { val lang: NameType = "lang" val length: NameType = "length" val lengthCompare: NameType = "lengthCompare" + val longHash: NameType = "longHash" val macroContext : NameType = "c" val main: NameType = "main" val manifestToTypeTag: NameType = "manifestToTypeTag" diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index 88a94ab530..6d988479af 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -375,20 +375,30 @@ abstract class SymbolTable extends macros.Universe def newWeakSet[K <: AnyRef]() = recordCache(new WeakHashSet[K]()) def newAnyRefMap[K <: AnyRef, V]() = recordCache(mutable.AnyRefMap[K, V]()) - def newGeneric[T](f: => T): () => T = { + /** + * Register a cache specified by a factory function and (optionally) a cleanup function. + * + * @return A function that will return cached value, or create a fresh value when a new run is started. + */ + def newGeneric[T](f: => T, cleanup: T => Unit = (x: Any) => ()): () => T = { val NoCached: T = null.asInstanceOf[T] var cached: T = NoCached var cachedRunId = NoRunId - recordCache(new Clearable { - def clear(): Unit = cached = NoCached - }) - () => { - if (currentRunId != cachedRunId || cached == NoCached) { - cached = f - cachedRunId = currentRunId + val clearable = new Clearable with (() => T) { + def clear(): Unit = { + if (cached != NoCached) + cleanup(cached) + cached = NoCached + } + def apply(): T = { + if (currentRunId != cachedRunId || cached == NoCached) { + cached = f + cachedRunId = currentRunId + } + cached } - cached } + recordCache(clearable) } } diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 5b613316cc..ed51414382 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -478,10 +478,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def newAnonymousFunctionValue(pos: Position, newFlags: Long = 0L): TermSymbol = newTermSymbol(nme.ANON_FUN_NAME, pos, SYNTHETIC | newFlags) setInfo NoType - def newImplClass(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): ClassSymbol = { - newClassSymbol(name, pos, newFlags | IMPLCLASS) - } - /** Refinement types P { val x: String; type T <: Number } * also have symbols, they are refinementClasses */ @@ -588,7 +584,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isAnonymousClass = false def isCaseClass = false def isConcreteClass = false - def isImplClass = false // the implementation class of a trait + @deprecated("Trait implementation classes have been removed in Scala 2.12", "2.12.0") + def isImplClass = false def isJavaInterface = false def isNumericValueClass = false def isPrimitiveValueClass = false @@ -838,7 +835,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** The package object symbol corresponding to this package or package class symbol, or NoSymbol otherwise */ def packageObject: Symbol = if (isPackageClass) tpe.packageObject - else if (isPackage) moduleClass.packageObject + else if (hasPackageFlag) moduleClass.packageObject else NoSymbol /** If this is a constructor, its owner: otherwise this. @@ -936,21 +933,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isCaseCopy = isMethod && owner.isCase && isSynthetic && name == nme.copy - /** Is this symbol a trait which needs an implementation class? */ - final def needsImplClass = ( - isTrait - && (!isInterface || hasFlag(lateINTERFACE)) - && !isImplClass - ) - - /** Is this a symbol which exists only in the implementation class, not in its trait? */ - final def isImplOnly = isPrivate || ( - (owner.isTrait || owner.isImplClass) && ( - hasAllFlags(LIFTED | MODULE | METHOD) - || isConstructor - || hasFlag(notPRIVATE | LIFTED) && !hasFlag(ACCESSOR | SUPERACCESSOR | MODULE) - ) - ) final def isModuleVar = hasFlag(MODULEVAR) /** @@ -974,10 +956,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * method `owner` returns the class C. * * Why not make a stable version of `isStatic`? Maybe some parts of the compiler depend on the - * current implementation. For example - * trait T { def foo = 1 } - * The method `foo` in the implementation class T$impl will be `isStatic`, because trait - * impl classes get the `lateMODULE` flag (T$impl.isStaticOwner is true). + * current implementation. */ def isStatic = (this hasFlag STATIC) || owner.isStaticOwner @@ -987,7 +966,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Is this symbol a static member of its class? (i.e. needs to be implemented as a Java static?) */ final def isStaticMember: Boolean = - hasFlag(STATIC) || owner.isImplClass + hasFlag(STATIC) /** Does this symbol denote a class that defines static symbols? */ final def isStaticOwner: Boolean = @@ -1261,14 +1240,14 @@ trait Symbols extends api.Symbols { self: SymbolTable => def needsModuleSuffix = ( hasModuleFlag && !isMethod - && !isImplClass && !isJavaDefined ) /** These should be moved somewhere like JavaPlatform. */ def javaSimpleName: Name = addModuleSuffix(simpleName.dropLocal) - def javaBinaryName: Name = addModuleSuffix(fullNameInternal('/')) - def javaClassName: String = addModuleSuffix(fullNameInternal('.')).toString + def javaBinaryName: Name = name.newName(javaBinaryNameString) + def javaBinaryNameString: String = fullName('/', moduleSuffix) + def javaClassName: String = fullName('.', moduleSuffix) /** The encoded full path name of this symbol, where outer names and inner names * are separated by `separator` characters. @@ -1276,18 +1255,29 @@ trait Symbols extends api.Symbols { self: SymbolTable => * Never adds id. * Drops package objects. */ - final def fullName(separator: Char): String = fullNameAsName(separator).toString - - /** Doesn't drop package objects, for those situations (e.g. classloading) - * where the true path is needed. - */ - private def fullNameInternal(separator: Char): Name = ( - if (isRoot || isRootPackage || this == NoSymbol) name - else if (owner.isEffectiveRoot) name - else effectiveOwner.enclClass.fullNameAsName(separator) append (separator, name) - ) + final def fullName(separator: Char): String = fullName(separator, "") + + private def fullName(separator: Char, suffix: CharSequence): String = { + var b: java.lang.StringBuffer = null + def loop(size: Int, sym: Symbol): Unit = { + val symName = sym.name + val nSize = symName.length - (if (symName.endsWith(nme.LOCAL_SUFFIX_STRING)) 1 else 0) + if (sym.isRoot || sym.isRootPackage || sym == NoSymbol || sym.owner.isEffectiveRoot) { + val capacity = size + nSize + b = new java.lang.StringBuffer(capacity) + b.append(chrs, symName.start, nSize) + } else { + loop(size + nSize + 1, sym.effectiveOwner.enclClass) + b.append(separator) + b.append(chrs, symName.start, nSize) + } + } + loop(suffix.length(), this) + b.append(suffix) + b.toString + } - def fullNameAsName(separator: Char): Name = fullNameInternal(separator).dropLocal + def fullNameAsName(separator: Char): Name = name.newName(fullName(separator, "")) /** The encoded full path name of this symbol, where outer names and inner names * are separated by periods. @@ -1322,9 +1312,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => protected def createPackageObjectClassSymbol(pos: Position, newFlags: Long): PackageObjectClassSymbol = new PackageObjectClassSymbol(this, pos) initFlags newFlags - protected def createImplClassSymbol(name: TypeName, pos: Position, newFlags: Long): ClassSymbol = - new ClassSymbol(this, pos, name) with ImplClassSymbol initFlags newFlags - protected def createMethodSymbol(name: TermName, pos: Position, newFlags: Long): MethodSymbol = new MethodSymbol(this, pos, name) initFlags newFlags @@ -1363,8 +1350,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => createPackageObjectClassSymbol(pos, newFlags) else if ((newFlags & MODULE) != 0) createModuleClassSymbol(name, pos, newFlags) - else if ((newFlags & IMPLCLASS) != 0) - createImplClassSymbol(name, pos, newFlags) else createClassSymbol(name, pos, newFlags) } @@ -2012,7 +1997,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ def thisSym: Symbol = this - def hasSelfType = thisSym.tpeHK != this.tpeHK + def hasSelfType = (thisSym ne this) && (typeOfThis.typeConstructor ne typeConstructor) /** The type of `this` in a class, or else the type of the symbol itself. */ def typeOfThis = thisSym.tpe_* @@ -2073,12 +2058,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ def sourceModule: Symbol = NoSymbol - /** The implementation class of a trait. If available it will be the - * symbol with the same owner, and the name of this symbol with $class - * appended to it. - */ - final def implClass: Symbol = owner.info.decl(tpnme.implClassName(name)) - /** The class that is logically an outer class of given `clazz`. * This is the enclosing class, except for classes defined locally to constructors, * where it is the outer class of the enclosing class. @@ -2282,16 +2261,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => owner.rawInfo } - /** If this symbol is an implementation class, its interface, otherwise the symbol itself - * The method follows two strategies to determine the interface. - * - during or after erasure, it takes the last parent of the implementation class - * (which is always the interface, by convention) - * - before erasure, it looks up the interface name in the scope of the owner of the class. - * This only works for implementation classes owned by other classes or traits. - * !!! Why? - */ - def toInterface: Symbol = this - /** The module class corresponding to this module. */ def moduleClass: Symbol = NoSymbol @@ -2411,8 +2380,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => var bcs = base.info.baseClasses dropWhile (owner != _) drop 1 var sym: Symbol = NoSymbol while (!bcs.isEmpty && sym == NoSymbol) { - if (!bcs.head.isImplClass) - sym = matchingSymbol(bcs.head, base.thisType).suchThat(!_.isDeferred) + sym = matchingSymbol(bcs.head, base.thisType).suchThat(!_.isDeferred) bcs = bcs.tail } sym @@ -2546,7 +2514,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** String representation of symbol's definition key word */ final def keyString: String = if (isJavaInterface) "interface" - else if (isTrait && !isImplClass) "trait" + else if (isTrait) "trait" else if (isClass) "class" else if (isType && !isParameter) "type" else if (isVariable) "var" @@ -2573,7 +2541,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => else if (isSetter) ("setter", if (isSourceMethod) "method" else "value", "SET") else if (isTerm && isLazy) ("lazy value", "lazy value", "LAZ") else if (isVariable) ("field", "variable", "VAR") - else if (isImplClass) ("implementation class", "class", "IMPL") else if (isTrait) ("trait", "trait", "TRT") else if (isClass) ("class", "class", "CLS") else if (isType) ("type", "type", "TPE") @@ -2810,7 +2777,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => case DEFAULTPARAM => "<defaultparam>" // TRAIT case MIXEDIN => "<mixedin>" // EXISTENTIAL case LABEL => "<label>" // CONTRAVARIANT / INCONSTRUCTOR - case PRESUPER => "<presuper>" // IMPLCLASS case BYNAMEPARAM => if (this.isValueParameter) "<bynameparam>" else "<captured>" // COVARIANT case _ => super.resolveOverloadedFlag(flag) } @@ -3211,7 +3177,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def resolveOverloadedFlag(flag: Long) = flag match { case INCONSTRUCTOR => "<inconstructor>" // INCONSTRUCTOR / CONTRAVARIANT / LABEL case EXISTENTIAL => "<existential>" // EXISTENTIAL / MIXEDIN - case IMPLCLASS => "<implclass>" // IMPLCLASS / PRESUPER case _ => super.resolveOverloadedFlag(flag) } @@ -3223,7 +3188,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def isAbstractClass = this hasFlag ABSTRACT override def isCaseClass = this hasFlag CASE override def isClassLocalToConstructor = this hasFlag INCONSTRUCTOR - override def isImplClass = this hasFlag IMPLCLASS override def isModuleClass = this hasFlag MODULE override def isPackageClass = this hasFlag PACKAGE override def isTrait = this hasFlag TRAIT @@ -3241,13 +3205,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => // The corresponding interface is the last parent by convention. private def lastParent = if (tpe.parents.isEmpty) NoSymbol else tpe.parents.last.typeSymbol - override def toInterface: Symbol = ( - if (isImplClass) { - if (phase.next.erasedTypes) lastParent - else owner.info.decl(tpnme.interfaceName(name)) - } - else super.toInterface - ) /** Is this class locally defined? * A class is local, if @@ -3278,7 +3235,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def existentialBound = GenPolyType(this.typeParams, TypeBounds.upper(this.classBound)) - def primaryConstructorName = if (this hasFlag TRAIT | IMPLCLASS) nme.MIXIN_CONSTRUCTOR else nme.CONSTRUCTOR + def primaryConstructorName = if (this hasFlag TRAIT) nme.MIXIN_CONSTRUCTOR else nme.CONSTRUCTOR override def primaryConstructor = { val c = info decl primaryConstructorName @@ -3424,12 +3381,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => } } - trait ImplClassSymbol extends ClassSymbol { - override def sourceModule = companionModule - // override def isImplClass = true - override def typeOfThis = thisSym.tpe // don't use the ModuleClassSymbol typeOfThisCache. - } - class PackageClassSymbol protected[Symbols] (owner0: Symbol, pos0: Position, name0: TypeName) extends ModuleClassSymbol(owner0, pos0, name0) { override def sourceModule = companionModule @@ -3687,7 +3638,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => assert((prev eq null) || phaseId(validFrom) > phaseId(prev.validFrom), this) assert(validFrom != NoPeriod, this) - private def phaseString = "%s: %s".format(phaseOf(validFrom), info) + private def phaseString = { + val phase = phaseOf(validFrom) + s"$phase: ${exitingPhase(phase)(info.toString)}" + } override def toString = toList reverseMap (_.phaseString) mkString ", " def toList: List[TypeHistory] = this :: ( if (prev eq null) Nil else prev.toList ) diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index 2933aa3892..c5038fd1bb 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -117,6 +117,30 @@ abstract class TreeGen { case _ => qual } + + + // val selType = testedBinder.info + // + // // See the test for SI-7214 for motivation for dealias. Later `treeCondStrategy#outerTest` + // // generates an outer test based on `patType.prefix` with automatically dealises. + // // Prefixes can have all kinds of shapes SI-9110 + // val patPre = expectedTp.dealiasWiden.prefix + // val selPre = selType.dealiasWiden.prefix + // + // // Optimization: which prefixes can we disqualify from the need for an outer reference check? + // // - classes in static owners do not get outer pointers + // // - if the prefixes are statically known to be equal, the type system ensures an outer test is redundant + // !((patPre eq NoPrefix) || (selPre eq NoPrefix) + // || patPre.typeSymbol.isPackageClass + // || selPre =:= patPre) + + def mkAttributedQualifierIfPossible(prefix: Type): Option[Tree] = prefix match { + case NoType | NoPrefix | ErrorType => None + case TypeRef(_, sym, _) if sym.isModule || sym.isClass || sym.isType => None + case pre => Some(mkAttributedQualifier(prefix)) + } + + /** Builds a reference to given symbol with given stable prefix. */ def mkAttributedRef(pre: Type, sym: Symbol): RefTree = { val qual = mkAttributedQualifier(pre) @@ -286,13 +310,16 @@ abstract class TreeGen { /** Builds a tuple */ def mkTuple(elems: List[Tree], flattenUnary: Boolean = true): Tree = elems match { case Nil => - Literal(Constant(())) + mkLiteralUnit case tree :: Nil if flattenUnary => tree case _ => Apply(scalaDot(TupleClass(elems.length).name.toTermName), elems) } + def mkLiteralUnit: Literal = Literal(Constant(())) + def mkUnitBlock(expr: Tree): Block = Block(List(expr), mkLiteralUnit) + def mkTupleType(elems: List[Tree], flattenUnary: Boolean = true): Tree = elems match { case Nil => scalaDot(tpnme.Unit) @@ -371,7 +398,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, mkLiteralUnit)))) } else { // convert (implicit ... ) to ()(implicit ... ) if it's the only parameter section @@ -385,7 +412,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), mkLiteralUnit)))) } } constr foreach (ensureNonOverlapping(_, parents ::: gvdefs, focus = false)) @@ -457,7 +484,7 @@ abstract class TreeGen { * written by end user. It's important to distinguish the two so that * quasiquotes can strip synthetic ones away. */ - def mkSyntheticUnit() = Literal(Constant(())).updateAttachment(SyntheticUnitAttachment) + def mkSyntheticUnit() = mkLiteralUnit.updateAttachment(SyntheticUnitAttachment) /** Create block of statements `stats` */ def mkBlock(stats: List[Tree], doFlatten: Boolean = true): Tree = diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index b43ea640d9..cba34aa220 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -1161,6 +1161,10 @@ trait Trees extends api.Trees { def Super(sym: Symbol, mix: TypeName): Tree = Super(This(sym), mix) + /** Selection of a method in an arbitrary ancestor */ + def SuperSelect(clazz: Symbol, sym: Symbol): Tree = + Select(Super(clazz, tpnme.EMPTY), sym) + def This(sym: Symbol): Tree = This(sym.name.toTypeName) setSymbol sym diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index e53b47e808..de82a6a0b2 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -462,7 +462,7 @@ trait Types * the empty list for all other types */ def boundSyms: immutable.Set[Symbol] = emptySymbolSet - /** Replace formal type parameter symbols with actual type arguments. + /** Replace formal type parameter symbols with actual type arguments. ErrorType on arity mismatch. * * Amounts to substitution except for higher-kinded types. (See overridden method in TypeRef) -- @M */ @@ -1196,7 +1196,6 @@ trait Types object ThisType extends ThisTypeExtractor { def apply(sym: Symbol): Type = ( if (!phase.erasedTypes) unique(new UniqueThisType(sym)) - else if (sym.isImplClass) sym.typeOfThis else sym.tpe_* ) } @@ -1867,53 +1866,13 @@ trait Types override def isHigherKinded = false override def typeParams = Nil - override def transform(tp: Type): Type = { - // This situation arises when a typevar is encountered for which - // too little information is known to determine its kind, and - // it later turns out not to have kind *. See SI-4070. Only - // logging it for now. - val tparams = sym.typeParams - if (tparams.size != args.size) - devWarning(s"$this.transform($tp), but tparams.isEmpty and args=$args") - def asSeenFromInstantiated(tp: Type) = - asSeenFromOwner(tp).instantiateTypeParams(tparams, args) - // If we're called with a poly type, and we were to run the `asSeenFrom`, over the entire - // type, we can end up with new symbols for the type parameters (clones from TypeMap). - // The subsequent substitution of type arguments would fail. This problem showed up during - // the fix for SI-8046, however the solution taken there wasn't quite right, and led to - // SI-8170. - // - // Now, we detect the PolyType before both the ASF *and* the substitution, and just operate - // on the result type. - // - // TODO: Revisit this and explore the questions raised: - // - // AM: I like this better than the old code, but is there any way the tparams would need the ASF treatment as well? - // JZ: I think its largely irrelevant, as they are no longer referred to in the result type. - // In fact, you can get away with returning a type of kind * here and the sky doesn't fall: - // `case PolyType(`tparams`, result) => asSeenFromInstantiated(result)` - // But I thought it was better to retain the kind. - // AM: I've been experimenting with apply-type-args-then-ASF, but running into cycles. - // In general, it seems iffy the tparams can never occur in the result - // then we might as well represent the type as a no-arg typeref. - // AM: I've also been trying to track down uses of transform (pretty generic name for something that - // does not seem that widely applicable). - // It's kind of a helper for computing baseType (since it tries to propagate our type args to some - // other type, which has to be related to this type for that to make sense). - // - tp match { - case PolyType(`tparams`, result) => PolyType(tparams, asSeenFromInstantiated(result)) - case _ => asSeenFromInstantiated(tp) - } - } - // note: does not go through typeRef. There's no need to because // neither `pre` nor `sym` changes. And there's a performance // advantage to call TypeRef directly. override def typeConstructor = TypeRef(pre, sym, Nil) } - class ModuleTypeRef(pre0: Type, sym0: Symbol) extends NoArgsTypeRef(pre0, sym0) with ClassTypeRef { + class ModuleTypeRef(pre0: Type, sym0: Symbol) extends NoArgsTypeRef(pre0, sym0) { require(sym.isModuleClass, sym) private[this] var narrowedCache: Type = _ override def narrow = { @@ -1922,7 +1881,8 @@ trait Types narrowedCache } - private[Types] def invalidateModuleTypeRefCaches(): Unit = { + override private[Types] def invalidateTypeRefCaches(): Unit = { + super.invalidateTypeRefCaches() narrowedCache = null } override protected def finishPrefix(rest: String) = objectPrefix + rest @@ -1935,12 +1895,12 @@ trait Types require(sym.isPackageClass, sym) override protected def finishPrefix(rest: String) = packagePrefix + rest } - class RefinementTypeRef(pre0: Type, sym0: Symbol) extends NoArgsTypeRef(pre0, sym0) with ClassTypeRef { + class RefinementTypeRef(pre0: Type, sym0: Symbol) extends NoArgsTypeRef(pre0, sym0) { require(sym.isRefinementClass, sym) // I think this is okay, but see #1241 (r12414), #2208, and typedTypeConstructor in Typers override protected def normalizeImpl: Type = sym.info.normalize - override protected def finishPrefix(rest: String) = "" + thisInfo + override protected def finishPrefix(rest: String) = "" + sym.info } class NoArgsTypeRef(pre0: Type, sym0: Symbol) extends TypeRef(pre0, sym0, Nil) { @@ -1951,7 +1911,6 @@ trait Types // represented as existential types. override def isHigherKinded = (typeParams ne Nil) override def typeParams = if (isDefinitionsInitialized) sym.typeParams else sym.unsafeTypeParams - private def isRaw = !phase.erasedTypes && isRawIfWithoutArgs(sym) override def instantiateTypeParams(formals: List[Symbol], actuals: List[Type]): Type = if (isHigherKinded) { @@ -1964,17 +1923,6 @@ trait Types else super.instantiateTypeParams(formals, actuals) - override def transform(tp: Type): Type = { - val res = asSeenFromOwner(tp) - if (isHigherKinded && !isRaw) - res.instantiateTypeParams(typeParams, dummyArgs) - else - res - } - - override def transformInfo(tp: Type): Type = - appliedType(asSeenFromOwner(tp), dummyArgs) - override def narrow = if (sym.isModuleClass) singleType(pre, sym.sourceModule) else super.narrow @@ -1986,69 +1934,75 @@ trait Types if (isHigherKinded) etaExpand else super.normalizeImpl } - trait ClassTypeRef extends TypeRef { - // !!! There are scaladoc-created symbols arriving which violate this require. - // require(sym.isClass, sym) - - override def baseType(clazz: Symbol): Type = - if (sym == clazz) this - else transform(sym.info.baseType(clazz)) - } - trait NonClassTypeRef extends TypeRef { require(sym.isNonClassType, sym) - /* Syncnote: These are pure caches for performance; no problem to evaluate these - * several times. Hence, no need to protected with synchronized in a multi-threaded - * usage scenario. - */ + /** Syncnote: These are pure caches for performance; no problem to evaluate these + * several times. Hence, no need to protected with synchronized in a multi-threaded + * usage scenario. + */ private var relativeInfoCache: Type = _ - private var relativeInfoPeriod: Period = NoPeriod - private[Types] def invalidateNonClassTypeRefCaches(): Unit = { + private var relativeInfoCacheValidForPeriod: Period = NoPeriod + private var relativeInfoCacheValidForSymInfo: Type = _ + + override private[Types] def invalidateTypeRefCaches(): Unit = { + super.invalidateTypeRefCaches() relativeInfoCache = NoType - relativeInfoPeriod = NoPeriod + relativeInfoCacheValidForPeriod = NoPeriod + relativeInfoCacheValidForSymInfo = null } - private[Types] def relativeInfo = /*trace(s"relativeInfo(${safeToString}})")*/{ - if (relativeInfoPeriod != currentPeriod) { - val memberInfo = pre.memberInfo(sym) - relativeInfoCache = transformInfo(memberInfo) - relativeInfoPeriod = currentPeriod + final override protected def relativeInfo = { + val symInfo = sym.info + if ((relativeInfoCache eq null) || (relativeInfoCacheValidForSymInfo ne symInfo) || (relativeInfoCacheValidForPeriod != currentPeriod)) { + relativeInfoCache = super.relativeInfo + + if (this.isInstanceOf[AbstractTypeRef]) validateRelativeInfo() + + relativeInfoCacheValidForSymInfo = symInfo + relativeInfoCacheValidForPeriod = currentPeriod } relativeInfoCache } - override def baseType(clazz: Symbol): Type = - if (sym == clazz) this else baseTypeOfNonClassTypeRef(this, clazz) + private def validateRelativeInfo(): Unit = relativeInfoCache match { + // If a subtyping cycle is not detected here, we'll likely enter an infinite + // loop before a sensible error can be issued. SI-5093 is one example. + case x: SubType if x.supertype eq this => + relativeInfoCache = null + throw new RecoverableCyclicReference(sym) + case _ => + } } - protected def baseTypeOfNonClassTypeRef(tpe: NonClassTypeRef, clazz: Symbol) = try { - basetypeRecursions += 1 - if (basetypeRecursions < LogPendingBaseTypesThreshold) - tpe.relativeInfo.baseType(clazz) - else if (pendingBaseTypes contains tpe) - if (clazz == AnyClass) clazz.tpe else NoType - else - try { - pendingBaseTypes += tpe - tpe.relativeInfo.baseType(clazz) - } finally { - pendingBaseTypes -= tpe - } - } finally { - basetypeRecursions -= 1 - } trait AliasTypeRef extends NonClassTypeRef { require(sym.isAliasType, sym) override def dealias = if (typeParamsMatchArgs) betaReduce.dealias else super.dealias override def narrow = normalize.narrow - override def thisInfo = normalize override def prefix = if (this ne normalize) normalize.prefix else pre override def termSymbol = if (this ne normalize) normalize.termSymbol else super.termSymbol override def typeSymbol = if (this ne normalize) normalize.typeSymbol else sym + override protected[Types] def parentsImpl: List[Type] = normalize.parents map relativize + + // `baseClasses` is sensitive to type args when referencing type members + // consider `type foo[x] = x`, `typeOf[foo[String]].baseClasses` should be the same as `typeOf[String].baseClasses`, + // which would be lost by looking at `sym.info` without propagating args + // since classes cannot be overridden, the prefix can be ignored + // (in fact, taking the prefix into account by replacing `normalize` + // with `relativeInfo` breaks pos/t8177g.scala, which is probably a bug, but a tricky one... + override def baseClasses = normalize.baseClasses + + // similar reasoning holds here as for baseClasses + // as another example, consider the type alias `Foo` in `class O { o => type Foo = X { val bla: o.Bar }; type Bar }` + // o1.Foo and o2.Foo have different decls `val bla: o1.Bar` versus `val bla: o2.Bar` + // In principle, you should only call `sym.info.decls` when you know `sym.isClass`, + // and you should `relativize` the infos of the resulting members. + // The latter is certainly violated in multiple spots in the codebase (the members are usually transformed correctly, though). + override def decls: Scope = normalize.decls + // beta-reduce, but don't do partial application -- cycles have been checked in typeRef override protected def normalizeImpl = if (typeParamsMatchArgs) betaReduce.normalize @@ -2071,7 +2025,7 @@ trait Types // // this crashes pos/depmet_implicit_tpbetareduce.scala // appliedType(sym.info, typeArgs).asSeenFrom(pre, sym.owner) - override def betaReduce = transform(sym.info.resultType) + override def betaReduce = relativize(sym.info.resultType) /** SI-3731, SI-8177: when prefix is changed to `newPre`, maintain consistency of prefix and sym * (where the symbol refers to a declaration "embedded" in the prefix). @@ -2121,31 +2075,13 @@ trait Types trait AbstractTypeRef extends NonClassTypeRef { require(sym.isAbstractType, sym) - /** Syncnote: Pure performance caches; no need to synchronize in multi-threaded environment - */ - private var symInfoCache: Type = _ - private var thisInfoCache: Type = _ + override def baseClasses = relativeInfo.baseClasses + override def decls = relativeInfo.decls + override def bounds = relativeInfo.bounds + + override protected[Types] def baseTypeSeqImpl: BaseTypeSeq = bounds.hi.baseTypeSeq prepend this + override protected[Types] def parentsImpl: List[Type] = relativeInfo.parents - override def thisInfo = { - val symInfo = sym.info - if (thisInfoCache == null || (symInfo ne symInfoCache)) { - symInfoCache = symInfo - thisInfoCache = transformInfo(symInfo) match { - // If a subtyping cycle is not detected here, we'll likely enter an infinite - // loop before a sensible error can be issued. SI-5093 is one example. - case x: SubType if x.supertype eq this => - throw new RecoverableCyclicReference(sym) - case tp => tp - } - } - thisInfoCache - } - private[Types] def invalidateAbstractTypeRefCaches(): Unit = { - symInfoCache = null - thisInfoCache = null - } - override def bounds = thisInfo.bounds - override protected[Types] def baseTypeSeqImpl: BaseTypeSeq = transform(bounds.hi).baseTypeSeq prepend this override def kind = "AbstractTypeRef" } @@ -2189,11 +2125,91 @@ trait Types finalizeHash(h, 2) } + // interpret symbol's info in terms of the type's prefix and type args + protected def relativeInfo: Type = appliedType(sym.info.asSeenFrom(pre, sym.owner), argsOrDummies) + // @M: propagate actual type params (args) to `tp`, by replacing // formal type parameters with actual ones. If tp is higher kinded, // the "actual" type arguments are types that simply reference the // corresponding type parameters (unbound type variables) - def transform(tp: Type): Type + // + // NOTE: for performance, as well as correctness, we do not attempt + // to reframe trivial types in terms of our prefix and args. + // asSeenFrom, by construction, is the identity for trivial types, + // and substitution cannot change them either (abstract types are non-trivial, specifically because they may need to be replaced) + // For correctness, the result for `tp == NoType` must be `NoType`, + // if we don't shield against this, and apply instantiateTypeParams to it, + // this would result in an ErrorType, which behaves differently during subtyping + // (and thus on recursion, subtyping would go from false -- since a NoType is involved -- + // to true, as ErrorType is always a sub/super type....) + final def relativize(tp: Type): Type = + if (tp.isTrivial) tp + else if (args.isEmpty && (phase.erasedTypes || !isHigherKinded || isRawIfWithoutArgs(sym))) tp.asSeenFrom(pre, sym.owner) + else { + // The type params and type args should always match in length, + // though a mismatch can arise when a typevar is encountered for which + // too little information is known to determine its kind, and + // it later turns out not to have kind *. See SI-4070. + val formals = sym.typeParams + + // If we're called with a poly type, and we were to run the `asSeenFrom`, over the entire + // type, we can end up with new symbols for the type parameters (clones from TypeMap). + // The subsequent substitution of type arguments would fail. This problem showed up during + // the fix for SI-8046, however the solution taken there wasn't quite right, and led to + // SI-8170. + // + // Now, we detect the PolyType before both the ASF *and* the substitution, and just operate + // on the result type. + // + // TODO: Revisit this and explore the questions raised: + // + // AM: I like this better than the old code, but is there any way the tparams would need the ASF treatment as well? + // JZ: I think its largely irrelevant, as they are no longer referred to in the result type. + // In fact, you can get away with returning a type of kind * here and the sky doesn't fall: + // `case PolyType(`tparams`, result) => asSeenFromInstantiated(result)` + // But I thought it was better to retain the kind. + // AM: I've been experimenting with apply-type-args-then-ASF, but running into cycles. + // In general, it seems iffy the tparams can never occur in the result + // then we might as well represent the type as a no-arg typeref. + // AM: I've also been trying to track down uses of transform (pretty generic name for something that + // does not seem that widely applicable). + // It's kind of a helper for computing baseType (since it tries to propagate our type args to some + // other type, which has to be related to this type for that to make sense). + // + def seenFromOwnerInstantiated(tp: Type): Type = + tp.asSeenFrom(pre, sym.owner).instantiateTypeParams(formals, argsOrDummies) + + tp match { + case PolyType(`formals`, result) => PolyType(formals, seenFromOwnerInstantiated(result)) + case _ => seenFromOwnerInstantiated(tp) + } + } + + private def argsOrDummies = if (args.isEmpty) dummyArgs else args + + final override def baseType(clazz: Symbol): Type = + if (clazz eq sym) this + // NOTE: this first goes to requested base type, *then* does asSeenFrom prefix & instantiates args + else if (sym.isClass) relativize(sym.info.baseType(clazz)) + else baseTypeOfNonClassTypeRef(clazz) + + // two differences with class type basetype: + // (1) first relativize the type, then go to the requested base type + // (2) cache for cycle robustness + private def baseTypeOfNonClassTypeRef(clazz: Symbol) = + try { + basetypeRecursions += 1 + if (basetypeRecursions >= LogPendingBaseTypesThreshold) baseTypeOfNonClassTypeRefLogged(clazz) + else relativeInfo.baseType(clazz) + } finally basetypeRecursions -= 1 + + private def baseTypeOfNonClassTypeRefLogged(clazz: Symbol) = + if (pendingBaseTypes add this) try relativeInfo.baseType(clazz) finally { pendingBaseTypes remove this } + // TODO: is this optimization for AnyClass worth it? (or is it playing last-ditch cycle defense?) + // NOTE: for correctness, it only applies for non-class types + // (e.g., a package class should not get AnyTpe as its supertype, ever) + else if (clazz eq AnyClass) AnyTpe + else NoType // eta-expand, subtyping relies on eta-expansion of higher-kinded types protected def normalizeImpl: Type = if (isHigherKinded) etaExpand else super.normalize @@ -2226,21 +2242,16 @@ trait Types // (they are allowed to be rebound more liberally) def coevolveSym(pre1: Type): Symbol = sym - //@M! use appliedType on the polytype that represents the bounds (or if aliastype, the rhs) - def transformInfo(tp: Type): Type = appliedType(asSeenFromOwner(tp), args) - - def thisInfo = sym.info def initializedTypeParams = sym.info.typeParams def typeParamsMatchArgs = sameLength(initializedTypeParams, args) - def asSeenFromOwner(tp: Type) = tp.asSeenFrom(pre, sym.owner) - override def baseClasses = thisInfo.baseClasses + override def baseTypeSeqDepth = baseTypeSeq.maxDepth override def prefix = pre override def termSymbol = super.termSymbol override def termSymbolDirect = super.termSymbol override def typeArgs = args - override def typeOfThis = transform(sym.typeOfThis) + override def typeOfThis = relativize(sym.typeOfThis) override def typeSymbol = sym override def typeSymbolDirect = sym @@ -2253,22 +2264,26 @@ trait Types } } - override def decls: Scope = { - sym.info match { - case TypeRef(_, sym1, _) => - assert(sym1 != sym, this) // @MAT was != typeSymbol - case _ => - } - thisInfo.decls - } + protected[Types] def parentsImpl: List[Type] = sym.info.parents map relativize + + // Since type parameters cannot occur in super types, no need to relativize before looking at base *classes*. + // Similarly, our prefix can occur in super class types, but it cannot influence which classes those types resolve to. + // For example, `class Outer { outer => class Inner extends outer.Foo; class Foo }` + // `outer`'s value has no impact on which `Foo` is selected, since classes cannot be overridden. + // besides being faster, we can't use relativeInfo because it causes cycles + override def baseClasses = sym.info.baseClasses + + // in principle, we should use `relativeInfo.decls`, but I believe all uses of `decls` will correctly `relativize` the individual members + override def decls: Scope = sym.info.decls + protected[Types] def baseTypeSeqImpl: BaseTypeSeq = if (sym.info.baseTypeSeq exists (_.typeSymbolDirect.isAbstractType)) // SI-8046 base type sequence might have more elements in a subclass, we can't map it element wise. - transform(sym.info).baseTypeSeq + relativize(sym.info).baseTypeSeq else // Optimization: no abstract types, we can compute the BTS of this TypeRef as an element-wise map // of the BTS of the referenced symbol. - sym.info.baseTypeSeq map transform + sym.info.baseTypeSeq map relativize override def baseTypeSeq: BaseTypeSeq = { val cache = baseTypeSeqCache @@ -2299,9 +2314,10 @@ trait Types ) protected def finishPrefix(rest: String) = ( if (sym.isInitialized && sym.isAnonymousClass && !phase.erasedTypes) - parentsString(thisInfo.parents) + refinementString + parentsString(sym.info.parents) + refinementString else rest - ) + ) + private def noArgsString = finishPrefix(preString + sym.nameString) private def tupleTypeString: String = args match { case Nil => noArgsString @@ -2363,10 +2379,10 @@ trait Types // No longer defined as anonymous classes in `object TypeRef` to avoid an unnecessary outer pointer. private final class AliasArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args) with AliasTypeRef private final class AbstractArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args) with AbstractTypeRef - private final class ClassArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args) with ClassTypeRef + private final class ClassArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args) private final class AliasNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym) with AliasTypeRef private final class AbstractNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym) with AbstractTypeRef - private final class ClassNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym) with ClassTypeRef + private final class ClassNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym) object TypeRef extends TypeRefExtractor { def apply(pre: Type, sym: Symbol, args: List[Type]): Type = unique({ @@ -2391,7 +2407,7 @@ trait Types if (period != currentPeriod) { tpe.parentsPeriod = currentPeriod if (!isValidForBaseClasses(period)) { - tpe.parentsCache = tpe.thisInfo.parents map tpe.transform + tpe.parentsCache = tpe.parentsImpl } else if (tpe.parentsCache == null) { // seems this can happen if things are corrupted enough, see #2641 tpe.parentsCache = List(AnyTpe) } @@ -2446,7 +2462,6 @@ trait Types def isImplicit = (params ne Nil) && params.head.isImplicit def isJava = false // can we do something like for implicits? I.e. do Java methods without parameters need to be recognized? - //assert(paramTypes forall (pt => !pt.typeSymbol.isImplClass))//DEBUG override def paramSectionCount: Int = resultType.paramSectionCount + 1 override def paramss: List[List[Symbol]] = params :: resultType.paramss @@ -3917,6 +3932,8 @@ trait Types et.withTypeVars(isConsistent(_, tp2)) case (_, et: ExistentialType) => et.withTypeVars(isConsistent(tp1, _)) + case (_, _) => + throw new MatchError((tp1, tp2)) } def check(tp1: Type, tp2: Type) = ( @@ -3929,47 +3946,6 @@ trait Types check(tp1, tp2) && check(tp2, tp1) } - /** Does a pattern of type `patType` need an outer test when executed against - * selector type `selType` in context defined by `currentOwner`? - */ - def needsOuterTest(patType: Type, selType: Type, currentOwner: Symbol) = { - def createDummyClone(pre: Type): Type = { - val dummy = currentOwner.enclClass.newValue(nme.ANYname).setInfo(pre.widen) - singleType(ThisType(currentOwner.enclClass), dummy) - } - def maybeCreateDummyClone(pre: Type, sym: Symbol): Type = pre match { - case SingleType(pre1, sym1) => - if (sym1.isModule && sym1.isStatic) { - if (sym.owner == sym1 || sym.isJavaDefined || sym.owner.sourceModule.isStaticModule) NoType - else pre - } else if (sym1.isModule && sym.owner == sym1.moduleClass) { - val pre2 = maybeCreateDummyClone(pre1, sym1) - if (pre2 eq NoType) pre2 - else singleType(pre2, sym1) - } else { - createDummyClone(pre) - } - case ThisType(clazz) => - if (clazz.isModuleClass) - maybeCreateDummyClone(clazz.typeOfThis, sym) - else if (sym.owner == clazz && (sym.hasFlag(PRIVATE) || sym.privateWithin == clazz)) - NoType - else - createDummyClone(pre) - case _ => - NoType - } - // See the test for SI-7214 for motivation for dealias. Later `treeCondStrategy#outerTest` - // generates an outer test based on `patType.prefix` with automatically dealises. - patType.dealias match { - case TypeRef(pre, sym, args) => - val pre1 = maybeCreateDummyClone(pre, sym) - (pre1 ne NoType) && isPopulated(copyTypeRef(patType, pre1, sym, args), selType) - case _ => - false - } - } - def normalizePlus(tp: Type): Type = { if (isRawType(tp)) rawToExistential(tp) else tp.normalize match { @@ -4603,32 +4579,14 @@ trait Types invalidateCaches(tp, updatedSyms) } - def invalidateCaches(t: Type, updatedSyms: List[Symbol]) = { - t match { - case st: SingleType if updatedSyms.contains(st.sym) => st.invalidateSingleTypeCaches() - case _ => - } - t match { - case tr: NonClassTypeRef if updatedSyms.contains(tr.sym) => tr.invalidateNonClassTypeRefCaches() - case _ => - } - t match { - case tr: AbstractTypeRef if updatedSyms.contains(tr.sym) => tr.invalidateAbstractTypeRefCaches() - case _ => - } - t match { - case tr: TypeRef if updatedSyms.contains(tr.sym) => tr.invalidateTypeRefCaches() - case _ => - } - t match { - case tr: ModuleTypeRef if updatedSyms.contains(tr.sym) => tr.invalidateModuleTypeRefCaches() - case _ => - } + def invalidateCaches(t: Type, updatedSyms: List[Symbol]) = t match { + case st: SingleType if updatedSyms.contains(st.sym) => st.invalidateSingleTypeCaches() + case tr: TypeRef if updatedSyms.contains(tr.sym) => tr.invalidateTypeRefCaches() case ct: CompoundType if ct.baseClasses.exists(updatedSyms.contains) => ct.invalidatedCompoundTypeCaches() case _ => } - } + val shorthands = Set( "scala.collection.immutable.List", diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala index 38893d8db3..e75b3dff3d 100644 --- a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala +++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala @@ -58,6 +58,7 @@ abstract class MutableSettings extends AbsSettings { def maxClassfileName: IntSetting def isScala211: Boolean + def isScala212: Boolean } object MutableSettings { diff --git a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala index e6d7b11cad..cf274f24bb 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala @@ -58,7 +58,7 @@ trait TypeComparers { false private def equalSymsAndPrefixes(sym1: Symbol, pre1: Type, sym2: Symbol, pre2: Type): Boolean = ( - if (sym1 == sym2) + if (sym1 eq sym2) sym1.hasPackageFlag || sym1.owner.hasPackageFlag || phase.erasedTypes || pre1 =:= pre2 else (sym1.name == sym2.name) && isUnifiable(pre1, pre2) @@ -79,7 +79,7 @@ trait TypeComparers { def isDifferentTypeConstructor(tp1: Type, tp2: Type) = !isSameTypeConstructor(tp1, tp2) private def isSameTypeConstructor(tr1: TypeRef, tr2: TypeRef): Boolean = ( - (tr1.sym == tr2.sym) + (tr1.sym eq tr2.sym) && !isDifferentType(tr1.pre, tr2.pre) ) private def isSameTypeConstructor(tp1: Type, tp2: Type): Boolean = ( @@ -222,7 +222,7 @@ trait TypeComparers { case SingleType(pre1, sym1) => tp2 match { case SingleType(pre2, sym2) => equalSymsAndPrefixes(sym1, pre1, sym2, pre2) ; case _ => false } case PolyType(ps1, res1) => tp2 match { case PolyType(ps2, res2) => equalTypeParamsAndResult(ps1, res1, ps2, res2) ; case _ => false } case ExistentialType(qs1, res1) => tp2 match { case ExistentialType(qs2, res2) => equalTypeParamsAndResult(qs1, res1, qs2, res2) ; case _ => false } - case ThisType(sym1) => tp2 match { case ThisType(sym2) => sym1 == sym2 ; case _ => false } + case ThisType(sym1) => tp2 match { case ThisType(sym2) => sym1 eq sym2 ; case _ => false } case ConstantType(c1) => tp2 match { case ConstantType(c2) => c1 == c2 ; case _ => false } case NullaryMethodType(res1) => tp2 match { case NullaryMethodType(res2) => res1 =:= res2 ; case _ => false } case TypeBounds(lo1, hi1) => tp2 match { case TypeBounds(lo2, hi2) => lo1 =:= lo2 && hi1 =:= hi2 ; case _ => false } @@ -344,7 +344,7 @@ trait TypeComparers { // in the same class, and the 'x' in the ThisType has in its override chain // the 'x' in the SuperType, then the types conform. private def isThisAndSuperSubtype(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match { - case (SingleType(ThisType(lpre), v1), SingleType(SuperType(ThisType(rpre), _), v2)) => (lpre == rpre) && (v1.overrideChain contains v2) + case (SingleType(ThisType(lpre), v1), SingleType(SuperType(ThisType(rpre), _), v2)) => (lpre eq rpre) && (v1.overrideChain contains v2) case _ => false } @@ -361,8 +361,8 @@ trait TypeComparers { false } - ( tp1.typeSymbol == NothingClass // @M Nothing is subtype of every well-kinded type - || tp2.typeSymbol == AnyClass // @M Any is supertype of every well-kinded type (@PP: is it? What about continuations plugin?) + ( (tp1.typeSymbol eq NothingClass) // @M Nothing is subtype of every well-kinded type + || (tp2.typeSymbol eq AnyClass) // @M Any is supertype of every well-kinded type (@PP: is it? What about continuations plugin?) || isSub(tp1.normalize, tp2.normalize) && annotationsConform(tp1, tp2) // @M! normalize reduces higher-kinded case to PolyType's ) } @@ -394,7 +394,7 @@ trait TypeComparers { val sym2 = tr2.sym val pre1 = tr1.pre val pre2 = tr2.pre - (((if (sym1 == sym2) phase.erasedTypes || sym1.owner.hasPackageFlag || isSubType(pre1, pre2, depth) + (((if (sym1 eq sym2) phase.erasedTypes || sym1.owner.hasPackageFlag || isSubType(pre1, pre2, depth) else (sym1.name == sym2.name && !sym1.isModuleClass && !sym2.isModuleClass && (isUnifiable(pre1, pre2) || isSameSpecializedSkolem(sym1, sym2, pre1, pre2) || @@ -403,7 +403,9 @@ trait TypeComparers { || sym2.isClass && { val base = tr1 baseType sym2 - (base ne tr1) && isSubType(base, tr2, depth) + // During bootstrap, `base eq NoType` occurs about 2.5 times as often as `base ne NoType`. + // The extra check seems like a worthwhile optimization (about 2.5M useless calls to isSubtype saved during that run). + (base ne tr1) && (base ne NoType) && isSubType(base, tr2, depth) } || thirdTryRef(tr1, tr2)) diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala index 32af6529ca..412c49f571 100644 --- a/src/reflect/scala/reflect/internal/transform/Erasure.scala +++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala @@ -112,8 +112,10 @@ trait Erasure { protected def eraseDerivedValueClassRef(tref: TypeRef): Type = erasedValueClassArg(tref) def apply(tp: Type): Type = tp match { - case ConstantType(_) => - tp + case ConstantType(ct) => + // erase classOf[List[_]] to classOf[List]. special case for classOf[Unit], avoid erasing to classOf[BoxedUnit]. + if (ct.tag == ClazzTag && ct.typeValue.typeSymbol != UnitClass) ConstantType(Constant(apply(ct.typeValue))) + else tp case st: ThisType if st.sym.isPackageClass => tp case st: SubType => @@ -164,7 +166,7 @@ trait Erasure { /** The erasure |T| of a type T. This is: * - * - For a constant type, itself. + * - For a constant type classOf[T], classOf[|T|], unless T is Unit. For any other constant type, itself. * - For a type-bounds structure, the erasure of its upper bound. * - For every other singleton type, the erasure of its supertype. * - For a typeref scala.Array+[T] where T is an abstract type, AnyRef. diff --git a/src/reflect/scala/reflect/internal/transform/Transforms.scala b/src/reflect/scala/reflect/internal/transform/Transforms.scala index 296ccde443..0d2f355aa5 100644 --- a/src/reflect/scala/reflect/internal/transform/Transforms.scala +++ b/src/reflect/scala/reflect/internal/transform/Transforms.scala @@ -23,9 +23,9 @@ trait Transforms { self: SymbolTable => } } - private val refChecksLazy = new Lazy(new { val global: Transforms.this.type = self } with RefChecks) - private val uncurryLazy = new Lazy(new { val global: Transforms.this.type = self } with UnCurry) - private val erasureLazy = new Lazy(new { val global: Transforms.this.type = self } with Erasure) + private val refChecksLazy = new Lazy(new { val global: Transforms.this.type = self } with RefChecks) + private val uncurryLazy = new Lazy(new { val global: Transforms.this.type = self } with UnCurry) + private val erasureLazy = new Lazy(new { val global: Transforms.this.type = self } with Erasure) private val postErasureLazy = new Lazy(new { val global: Transforms.this.type = self } with PostErasure) def refChecks = refChecksLazy.force diff --git a/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala b/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala index cc5be92489..f3db2017be 100644 --- a/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala +++ b/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala @@ -139,6 +139,10 @@ object ScalaClassLoader { classloaderURLs :+= url super.addURL(url) } + override def close(): Unit = { + super.close() + classloaderURLs = null + } } def fromURLs(urls: Seq[URL], parent: ClassLoader = null): URLClassLoader = diff --git a/src/reflect/scala/reflect/internal/util/WeakHashSet.scala b/src/reflect/scala/reflect/internal/util/WeakHashSet.scala index f8dfa720d5..412b14d329 100644 --- a/src/reflect/scala/reflect/internal/util/WeakHashSet.scala +++ b/src/reflect/scala/reflect/internal/util/WeakHashSet.scala @@ -56,9 +56,9 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D /** * the limit at which we'll increase the size of the hash table */ - var threshhold = computeThreshHold + private[this] var threshold = computeThreshold - private[this] def computeThreshHold: Int = (table.size * loadFactor).ceil.toInt + private[this] def computeThreshold: Int = (table.size * loadFactor).ceil.toInt /** * find the bucket associated with an element's hash code @@ -121,7 +121,7 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D private[this] def resize() { val oldTable = table table = new Array[Entry[A]](oldTable.size * 2) - threshhold = computeThreshHold + threshold = computeThreshold @tailrec def tableLoop(oldBucket: Int): Unit = if (oldBucket < oldTable.size) { @@ -176,7 +176,7 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D def add() = { table(bucket) = new Entry(elem, hash, oldHead, queue) count += 1 - if (count > threshhold) resize() + if (count > threshold) resize() elem } @@ -206,7 +206,7 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D def add() { table(bucket) = new Entry(elem, hash, oldHead, queue) count += 1 - if (count > threshhold) resize() + if (count > threshold) resize() } @tailrec @@ -223,7 +223,7 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D def +=(elem: A) = this + elem - // from scala.reflect.interanl.Set + // from scala.reflect.internal.Set override def addEntry(x: A) { this += x } // remove an element from this set and return this set @@ -252,7 +252,7 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D // empty this set override def clear(): Unit = { table = new Array[Entry[A]](table.size) - threshhold = computeThreshHold + threshold = computeThreshold count = 0 // drain the queue - doesn't do anything because we're throwing away all the values anyway diff --git a/src/reflect/scala/reflect/io/ZipArchive.scala b/src/reflect/scala/reflect/io/ZipArchive.scala index 42e22fda6f..262ab22ce9 100644 --- a/src/reflect/scala/reflect/io/ZipArchive.scala +++ b/src/reflect/scala/reflect/io/ZipArchive.scala @@ -13,7 +13,7 @@ import java.io.{ File => JFile } import java.util.zip.{ ZipEntry, ZipFile, ZipInputStream } import java.util.jar.Manifest import scala.collection.mutable -import scala.collection.convert.WrapAsScala.asScalaIterator +import scala.collection.JavaConverters._ import scala.annotation.tailrec /** An abstraction for zip files and streams. Everything is written the way @@ -238,7 +238,7 @@ final class ManifestResources(val url: URL) extends ZipArchive(null) { val root = new DirEntry("/") val dirs = mutable.HashMap[String, DirEntry]("/" -> root) val manifest = new Manifest(input) - val iter = manifest.getEntries().keySet().iterator().filter(_.endsWith(".class")).map(new ZipEntry(_)) + val iter = manifest.getEntries().keySet().iterator().asScala.filter(_.endsWith(".class")).map(new ZipEntry(_)) for (zipEntry <- iter) { val dir = getDir(dirs, zipEntry) diff --git a/src/reflect/scala/reflect/macros/Enclosures.scala b/src/reflect/scala/reflect/macros/Enclosures.scala index 1eb6832b5b..798fed2a15 100644 --- a/src/reflect/scala/reflect/macros/Enclosures.scala +++ b/src/reflect/scala/reflect/macros/Enclosures.scala @@ -2,8 +2,6 @@ package scala package reflect package macros -import scala.language.existentials // SI-6541 - /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> * diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 40f2a1ef90..9b0d66f41c 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -154,7 +154,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive } def apply(schemaAndValue: (jClass[_], Any)): ClassfileAnnotArg = schemaAndValue match { case ConstantArg(value) => LiteralAnnotArg(Constant(value)) - case (clazz @ ArrayClass(), value: Array[_]) => ArrayAnnotArg(value map (x => apply(ScalaRunTime.arrayElementClass(clazz) -> x))) + case (clazz @ ArrayClass(), value: Array[_]) => ArrayAnnotArg(value map (x => apply(clazz.getComponentType -> x))) case (AnnotationClass(), value: jAnnotation) => NestedAnnotArg(JavaAnnotationProxy(value)) case _ => UnmappableAnnotArg } @@ -475,9 +475,9 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive } symbol match { - case Any_== | Object_== => ScalaRunTime.inlinedEquals(objReceiver, objArg0) - case Any_!= | Object_!= => !ScalaRunTime.inlinedEquals(objReceiver, objArg0) - case Any_## | Object_## => ScalaRunTime.hash(objReceiver) + case Any_== | Object_== => objReceiver == objArg0 + case Any_!= | Object_!= => objReceiver != objArg0 + case Any_## | Object_## => objReceiver.## case Any_equals => receiver.equals(objArg0) case Any_hashCode => receiver.hashCode case Any_toString => receiver.toString @@ -613,7 +613,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive loadBytes[String]("scala.reflect.ScalaSignature") match { case Some(ssig) => info(s"unpickling Scala $clazz and $module, owner = ${clazz.owner}") - val bytes = ssig.getBytes + val bytes = ssig.getBytes(java.nio.charset.StandardCharsets.UTF_8) val len = ByteCodecs.decode(bytes) assignAssociatedFile(clazz, module, jclazz) unpickler.unpickle(bytes take len, 0, clazz, module, jclazz.getName) @@ -622,7 +622,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive loadBytes[Array[String]]("scala.reflect.ScalaLongSignature") match { case Some(slsig) => info(s"unpickling Scala $clazz and $module with long Scala signature") - val encoded = slsig flatMap (_.getBytes) + val encoded = slsig flatMap (_.getBytes(java.nio.charset.StandardCharsets.UTF_8)) val len = ByteCodecs.decode(encoded) val decoded = encoded.take(len) assignAssociatedFile(clazz, module, jclazz) @@ -999,9 +999,9 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive } val cls = - if (jclazz.isMemberClass && !nme.isImplClassName(jname)) + if (jclazz.isMemberClass) lookupClass - else if (jclazz.isLocalClass0 || scalacShouldntLoadClass(jname)) + else if (jclazz.isLocalClass0) // local classes and implementation classes not preserved by unpickling - treat as Java // // upd. but only if they cannot be loaded as top-level classes @@ -1161,6 +1161,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive propagatePackageBoundary(jmeth.javaFlags, meth) copyAnnotations(meth, jmeth) if (jmeth.javaFlags.isVarargs) meth modifyInfo arrayToRepeated + if (jmeth.getDefaultValue != null) meth.addAnnotation(AnnotationDefaultAttr) markAllCompleted(meth) meth } diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index 13874916cc..d50debd7ee 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -37,6 +37,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.FixedMirrorTreeCreator this.FixedMirrorTypeCreator this.CompoundTypeTreeOriginalAttachment + this.SAMFunction this.BackquotedIdentifierAttachment this.ForAttachment this.SyntheticUnitAttachment @@ -376,6 +377,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.ElidableMethodClass definitions.ImplicitNotFoundClass definitions.ImplicitAmbiguousClass + definitions.JUnitTestClass definitions.MigrationAnnotationClass definitions.ScalaStrictFPAttr definitions.SwitchClass diff --git a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala index d92e37953f..dd15a09b7e 100644 --- a/src/reflect/scala/reflect/runtime/ReflectionUtils.scala +++ b/src/reflect/scala/reflect/runtime/ReflectionUtils.scala @@ -80,12 +80,6 @@ object ReflectionUtils { accessor invoke outer } - def isTraitImplementation(fileName: String) = fileName endsWith "$class.class" - - def scalacShouldntLoadClassfile(fileName: String) = isTraitImplementation(fileName) - - def scalacShouldntLoadClass(name: scala.reflect.internal.SymbolTable#Name) = scalacShouldntLoadClassfile(name + ".class") - object PrimitiveOrArray { def unapply(jclazz: jClass[_]) = jclazz.isPrimitive || jclazz.isArray } diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala index 27d574b1de..b1d7fde1b4 100644 --- a/src/reflect/scala/reflect/runtime/Settings.scala +++ b/src/reflect/scala/reflect/runtime/Settings.scala @@ -51,4 +51,5 @@ private[reflect] class Settings extends MutableSettings { val Yrecursion = new IntSetting(0) val maxClassfileName = new IntSetting(255) def isScala211 = true + def isScala212 = true } diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala index 43ab41f541..768a3d5ce5 100644 --- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala +++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala @@ -3,7 +3,6 @@ package reflect package runtime import scala.collection.mutable -import scala.reflect.runtime.ReflectionUtils.scalacShouldntLoadClass import scala.reflect.internal.Flags._ private[reflect] trait SymbolLoaders { self: SymbolTable => @@ -125,7 +124,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => val e = super.lookupEntry(name) if (e != null) e - else if (scalacShouldntLoadClass(name) || (negatives contains name)) + else if (negatives contains name) null else { val path = diff --git a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala index 4f0c0253e9..313ec89311 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala @@ -176,9 +176,6 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb override protected def createRefinementClassSymbol(pos: Position, newFlags: Long): RefinementClassSymbol = new RefinementClassSymbol(this, pos) with SynchronizedClassSymbol initFlags newFlags - override protected def createImplClassSymbol(name: TypeName, pos: Position, newFlags: Long): ClassSymbol = - new ClassSymbol(this, pos, name) with ImplClassSymbol with SynchronizedClassSymbol initFlags newFlags - override protected def createPackageObjectClassSymbol(pos: Position, newFlags: Long): PackageObjectClassSymbol = new PackageObjectClassSymbol(this, pos) with SynchronizedClassSymbol initFlags newFlags diff --git a/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala b/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala index 1f2b0952e7..95964e18d9 100644 --- a/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala +++ b/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala @@ -11,10 +11,9 @@ import java.util.{Collection => JCollection, List => JList} import _root_.jline.{console => jconsole} import jline.console.ConsoleReader -import jline.console.completer.{CompletionHandler, Completer} +import jline.console.completer.{CandidateListCompletionHandler, Completer, CompletionHandler} import jconsole.history.{History => JHistory} - import scala.tools.nsc.interpreter import scala.tools.nsc.interpreter.{Completion, NoCompletion} import scala.tools.nsc.interpreter.Completion.Candidates @@ -133,32 +132,15 @@ private class JLineConsoleReader extends jconsole.ConsoleReader with interpreter newCursor } } + getCompletionHandler match { + case clch: CandidateListCompletionHandler => clch.setPrintSpaceAfterFullCompletion(false) + } completion match { case NoCompletion => () case _ => this addCompleter completer } - // This is a workaround for https://github.com/jline/jline2/issues/208 - // and should not be necessary once we upgrade to JLine 2.13.1 - /// - // Test by: - // scala> {" ".char}<LEFT><TAB> - // - // And checking we don't get an extra } on the line. - /// - val handler = getCompletionHandler - setCompletionHandler(new CompletionHandler { - override def complete(consoleReader: ConsoleReader, list: JList[CharSequence], i: Int): Boolean = { - try { - handler.complete(consoleReader, list, i) - } finally if (getCursorBuffer.cursor != getCursorBuffer.length()) { - print(" ") - getCursorBuffer.write(' ') - backspace() - } - } - }) setAutoprintThreshold(400) // max completion candidates without warning } } diff --git a/src/repl/scala/tools/nsc/interpreter/Completion.scala b/src/repl/scala/tools/nsc/interpreter/Completion.scala index 3d0b9a975c..6f5194d2f9 100644 --- a/src/repl/scala/tools/nsc/interpreter/Completion.scala +++ b/src/repl/scala/tools/nsc/interpreter/Completion.scala @@ -24,11 +24,11 @@ object Completion { case class Candidates(cursor: Int, candidates: List[String]) { } val NoCandidates = Candidates(-1, Nil) - def looksLikeInvocation(code: String) = ( - (code != null) - && (code startsWith ".") - && !(code == ".") - && !(code startsWith "./") - && !(code startsWith "..") - ) + // a leading dot plus something, but not ".." or "./", ignoring leading whitespace + private val dotlike = """\s*\.[^./].*""".r + def looksLikeInvocation(code: String) = code match { + case null => false // insurance + case dotlike() => true + case _ => false + } } diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala index 459450a94e..56d62f3efc 100644 --- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala @@ -633,10 +633,10 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) } } def alreadyDefined(clsName: String) = intp.classLoader.tryToLoadClass(clsName).isDefined - val exists = entries.filter(_.hasExtension("class")).map(classNameOf).exists(alreadyDefined) + val existingClass = entries.filter(_.hasExtension("class")).map(classNameOf).find(alreadyDefined) if (!f.exists) echo(s"The path '$f' doesn't seem to exist.") - else if (exists) echo(s"The path '$f' cannot be loaded, because existing classpath entries conflict.") // TODO tell me which one + else if (existingClass.nonEmpty) echo(s"The path '$f' cannot be loaded, it contains a classfile that already exists on the classpath: ${existingClass.get}") else { addedClasspath = ClassPath.join(addedClasspath, f.path) intp.addUrlsToClassPath(f.toURI.toURL) diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index 893bde42ab..a42a12a6fc 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -11,18 +11,17 @@ import PartialFunction.cond import scala.language.implicitConversions import scala.beans.BeanProperty import scala.collection.mutable -import scala.concurrent.{ Future, ExecutionContext } -import scala.reflect.runtime.{ universe => ru } -import scala.reflect.{ ClassTag, classTag } -import scala.reflect.internal.util.{ BatchSourceFile, SourceFile } -import scala.tools.util.PathResolverFactory +import scala.concurrent.{ExecutionContext, Future} +import scala.reflect.runtime.{universe => ru} +import scala.reflect.{ClassTag, classTag} +import scala.reflect.internal.util.{BatchSourceFile, SourceFile} import scala.tools.nsc.io.AbstractFile -import scala.tools.nsc.typechecker.{ TypeStrings, StructuredTypeStrings } +import scala.tools.nsc.typechecker.{StructuredTypeStrings, TypeStrings} import scala.tools.nsc.util._ import ScalaClassLoader.URLClassLoader import scala.tools.nsc.util.Exceptional.unwrap -import javax.script.{AbstractScriptEngine, Bindings, ScriptContext, ScriptEngine, ScriptEngineFactory, ScriptException, CompiledScript, Compilable} import java.net.URL +import scala.tools.util.PathResolver /** An interpreter for Scala code. * @@ -56,10 +55,11 @@ import java.net.URL * @author Moez A. Abdel-Gawad * @author Lex Spoon */ -class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Settings, protected val out: JPrintWriter) extends AbstractScriptEngine with Compilable with Imports with PresentationCompilation { +class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends Imports with PresentationCompilation { imain => - setBindings(createBindings, ScriptContext.ENGINE_SCOPE) + def this(initialSettings: Settings) = this(initialSettings, IMain.defaultOut) + object replOutput extends ReplOutput(settings.Yreploutdir) { } @deprecated("Use replOutput.dir instead", "2.11.0") @@ -91,7 +91,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set def compilerClasspath: Seq[java.net.URL] = ( if (isInitializeComplete) global.classPath.asURLs - else PathResolverFactory.create(settings).resultAsURLs // the compiler's classpath + else new PathResolver(settings).resultAsURLs // the compiler's classpath ) def settings = initialSettings // Run the code body with the given boolean settings flipped to true. @@ -104,13 +104,6 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set finally if (!saved) settings.nowarn.value = false } - /** construct an interpreter that reports to Console */ - def this(settings: Settings, out: JPrintWriter) = this(null, settings, out) - def this(factory: ScriptEngineFactory, settings: Settings) = this(factory, settings, new NewLinePrintWriter(new ConsoleWriter, true)) - def this(settings: Settings) = this(settings, new NewLinePrintWriter(new ConsoleWriter, true)) - def this(factory: ScriptEngineFactory) = this(factory, new Settings()) - def this() = this(new Settings()) - // the expanded prompt but without color escapes and without leading newline, for purposes of indenting lazy val formatting = Formatting.forPrompt(replProps.promptText) lazy val reporter: ReplReporter = new ReplReporter(this) @@ -464,7 +457,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set pos } - private[interpreter] def requestFromLine(line: String, synthetic: Boolean): Either[IR.Result, Request] = { + private[interpreter] def requestFromLine(line: String, synthetic: Boolean = false): Either[IR.Result, Request] = { val content = line val trees: List[global.Tree] = parse(content) match { @@ -559,77 +552,8 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set */ def interpret(line: String): IR.Result = interpret(line, synthetic = false) def interpretSynthetic(line: String): IR.Result = interpret(line, synthetic = true) - def interpret(line: String, synthetic: Boolean): IR.Result = compile(line, synthetic) match { - case Left(result) => result - case Right(req) => new WrappedRequest(req).loadAndRunReq - } - - private def compile(line: String, synthetic: Boolean): Either[IR.Result, Request] = { - if (global == null) Left(IR.Error) - else requestFromLine(line, synthetic) match { - case Left(result) => Left(result) - case Right(req) => - // null indicates a disallowed statement type; otherwise compile and - // fail if false (implying e.g. a type error) - if (req == null || !req.compile) Left(IR.Error) else Right(req) - } - } - - var code = "" - var bound = false - def compiled(script: String): CompiledScript = { - if (!bound) { - quietBind("engine" -> this.asInstanceOf[ScriptEngine]) - bound = true - } - val cat = code + script - compile(cat, false) match { - case Left(result) => result match { - case IR.Incomplete => { - code = cat + "\n" - new CompiledScript { - def eval(context: ScriptContext): Object = null - def getEngine: ScriptEngine = IMain.this - } - } - case _ => { - code = "" - throw new ScriptException("compile-time error") - } - } - case Right(req) => { - code = "" - new WrappedRequest(req) - } - } - } - - private class WrappedRequest(val req: Request) extends CompiledScript { - var recorded = false - - /** In Java we would have to wrap any checked exception in the declared - * ScriptException. Runtime exceptions and errors would be ok and would - * not need to be caught. So let us do the same in Scala : catch and - * wrap any checked exception, and let runtime exceptions and errors - * escape. We could have wrapped runtime exceptions just like other - * exceptions in ScriptException, this is a choice. - */ - @throws[ScriptException] - def eval(context: ScriptContext): Object = { - val result = req.lineRep.evalEither match { - case Left(e: RuntimeException) => throw e - case Left(e: Exception) => throw new ScriptException(e) - case Left(e) => throw e - case Right(result) => result.asInstanceOf[Object] - } - if (!recorded) { - recordRequest(req) - recorded = true - } - result - } - - def loadAndRunReq = classLoader.asContext { + def interpret(line: String, synthetic: Boolean): IR.Result = { + def loadAndRunReq(req: Request) = classLoader.asContext { val (result, succeeded) = req.loadAndRun /** To our displeasure, ConsoleReporter offers only printMessage, @@ -654,12 +578,32 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set } } - def getEngine: ScriptEngine = IMain.this + compile(line, synthetic) match { + case Left(result) => result + case Right(req) => loadAndRunReq(req) + } + } + + // create a Request and compile it + private[interpreter] def compile(line: String, synthetic: Boolean): Either[IR.Result, Request] = { + if (global == null) Left(IR.Error) + else requestFromLine(line, synthetic) match { + case Right(null) => Left(IR.Error) // disallowed statement type + case Right(req) if !req.compile => Left(IR.Error) // compile error + case ok @ Right(req) => ok + case err @ Left(result) => err + } } /** Bind a specified name to a specified value. The name may * later be used by expressions passed to interpret. * + * A fresh `ReadEvalPrint`, which defines a `line` package, is used to compile + * a custom `eval` object that wraps the bound value. + * + * If the bound value is successfully installed, then bind the name + * by interpreting `val name = $line42.$eval.value`. + * * @param name the variable name to bind * @param boundType the type of the variable, as a string * @param value the object value to bind to it @@ -667,22 +611,22 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set */ def bind(name: String, boundType: String, value: Any, modifiers: List[String] = Nil): IR.Result = { val bindRep = new ReadEvalPrint() - bindRep.compile(""" - |object %s { - | var value: %s = _ - | def set(x: Any) = value = x.asInstanceOf[%s] + bindRep.compile(s""" + |object ${bindRep.evalName} { + | var value: $boundType = _ + | def set(x: Any) = value = x.asInstanceOf[$boundType] |} - """.stripMargin.format(bindRep.evalName, boundType, boundType) - ) + """.stripMargin + ) bindRep.callEither("set", value) match { case Left(ex) => repldbg("Set failed in bind(%s, %s, %s)".format(name, boundType, value)) repldbg(util.stackTraceString(ex)) IR.Error - case Right(_) => - val line = "%sval %s = %s.value".format(modifiers map (_ + " ") mkString, name, bindRep.evalPath) - repldbg("Interpreting: " + line) + val mods = if (modifiers.isEmpty) "" else modifiers.mkString("", " ", " ") + val line = s"${mods}val $name = ${ bindRep.evalPath }.value" + repldbg(s"Interpreting: $line") interpret(line) } } @@ -1046,31 +990,6 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set override def toString = "Request(line=%s, %s trees)".format(line, trees.size) } - def createBindings: Bindings = new IBindings { - override def put(name: String, value: Object): Object = { - val n = name.indexOf(":") - val p: NamedParam = if (n < 0) (name, value) else { - val nme = name.substring(0, n).trim - val tpe = name.substring(n + 1).trim - NamedParamClass(nme, tpe, value) - } - if (!p.name.startsWith("javax.script")) bind(p) - null - } - } - - @throws[ScriptException] - def compile(script: String): CompiledScript = eval("new javax.script.CompiledScript { def eval(context: javax.script.ScriptContext): Object = { " + script + " }.asInstanceOf[Object]; def getEngine: javax.script.ScriptEngine = engine }").asInstanceOf[CompiledScript] - - @throws[ScriptException] - def compile(reader: java.io.Reader): CompiledScript = compile(stringFromReader(reader)) - - @throws[ScriptException] - def eval(script: String, context: ScriptContext): Object = compiled(script).eval(context) - - @throws[ScriptException] - def eval(reader: java.io.Reader, context: ScriptContext): Object = eval(stringFromReader(reader), context) - override def finalize = close /** Returns the name of the most recent interpreter result. @@ -1267,54 +1186,9 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set /** Utility methods for the Interpreter. */ object IMain { - import java.util.Arrays.{ asList => asJavaList } /** Dummy identifier fragement inserted at the cursor before presentation compilation. Needed to support completion of `global.def<TAB>` */ val DummyCursorFragment = "_CURSOR_" - class Factory extends ScriptEngineFactory { - @BeanProperty - val engineName = "Scala Interpreter" - - @BeanProperty - val engineVersion = "1.0" - - @BeanProperty - val extensions: JList[String] = asJavaList("scala") - - @BeanProperty - val languageName = "Scala" - - @BeanProperty - val languageVersion = scala.util.Properties.versionString - - def getMethodCallSyntax(obj: String, m: String, args: String*): String = null - - @BeanProperty - val mimeTypes: JList[String] = asJavaList("application/x-scala") - - @BeanProperty - val names: JList[String] = asJavaList("scala") - - def getOutputStatement(toDisplay: String): String = null - - def getParameter(key: String): Object = key match { - case ScriptEngine.ENGINE => engineName - case ScriptEngine.ENGINE_VERSION => engineVersion - case ScriptEngine.LANGUAGE => languageName - case ScriptEngine.LANGUAGE_VERSION => languageVersion - case ScriptEngine.NAME => names.get(0) - case _ => null - } - - def getProgram(statements: String*): String = null - - def getScriptEngine: ScriptEngine = { - val settings = new Settings() - settings.usemanifestcp.value = true - new IMain(this, settings) - } - } - // The two name forms this is catching are the two sides of this assignment: // // $line3.$read.$iw.$iw.Bippy = @@ -1366,5 +1240,10 @@ object IMain { def stripImpl(str: String): String = naming.unmangle(str) } + private[interpreter] def defaultSettings = new Settings() + private[scala] def defaultOut = new NewLinePrintWriter(new ConsoleWriter, true) + + /** construct an interpreter that reports to Console */ + def apply(initialSettings: Settings = defaultSettings, out: JPrintWriter = defaultOut) = new IMain(initialSettings, out) } diff --git a/src/repl/scala/tools/nsc/interpreter/Imports.scala b/src/repl/scala/tools/nsc/interpreter/Imports.scala index 6a75432ac6..0cda9c4da3 100644 --- a/src/repl/scala/tools/nsc/interpreter/Imports.scala +++ b/src/repl/scala/tools/nsc/interpreter/Imports.scala @@ -127,7 +127,11 @@ trait Imports { case rh :: rest if !keepHandler(rh.handler) => select(rest, wanted) case rh :: rest => import rh.handler._ - val newWanted = wanted ++ referencedNames -- definedNames -- importedNames + val augment = rh match { + case ReqAndHandler(_, _: ImportHandler) => referencedNames // for "import a.b", add "a" to names to be resolved + case _ => Nil + } + val newWanted = wanted ++ augment -- definedNames -- importedNames rh :: select(rest, newWanted) } } @@ -161,6 +165,8 @@ trait Imports { val tempValLines = mutable.Set[Int]() for (ReqAndHandler(req, handler) <- reqsToUse) { val objName = req.lineRep.readPathInstance + if (isReplTrace) + code.append(ss"// $objName definedNames ${handler.definedNames}, curImps $currentImps\n") handler match { case h: ImportHandler if checkHeader(h) => header.clear() @@ -175,21 +181,20 @@ trait Imports { currentImps ++= x.importedNames case x if isClassBased => - for (imv <- x.definedNames) { - if (!currentImps.contains(imv)) { - x match { - case _: ClassHandler => - code.append("import " + objName + req.accessPath + ".`" + imv + "`\n") - case _ => - val valName = req.lineRep.packageName + req.lineRep.readName - if (!tempValLines.contains(req.lineRep.lineId)) { - code.append(s"val $valName = $objName\n") - tempValLines += req.lineRep.lineId - } - code.append(s"import $valName${req.accessPath}.`$imv`;\n") - } - currentImps += imv + for (sym <- x.definedSymbols) { + maybeWrap(sym.name) + x match { + case _: ClassHandler => + code.append(s"import ${objName}${req.accessPath}.`${sym.name}`\n") + case _ => + val valName = s"${req.lineRep.packageName}${req.lineRep.readName}" + if (!tempValLines.contains(req.lineRep.lineId)) { + code.append(s"val $valName: ${objName}.type = $objName\n") + tempValLines += req.lineRep.lineId + } + code.append(s"import ${valName}${req.accessPath}.`${sym.name}`\n") } + currentImps += sym.name } // For other requests, import each defined name. // import them explicitly instead of with _, so that diff --git a/src/repl/scala/tools/nsc/interpreter/Power.scala b/src/repl/scala/tools/nsc/interpreter/Power.scala index 3c08a03c94..7a24405670 100644 --- a/src/repl/scala/tools/nsc/interpreter/Power.scala +++ b/src/repl/scala/tools/nsc/interpreter/Power.scala @@ -176,12 +176,10 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re */ class InternalInfo[T](value: Option[T] = None)(implicit typeEvidence: ru.TypeTag[T], runtimeClassEvidence: ClassTag[T]) { private def isSpecialized(s: Symbol) = s.name.toString contains "$mc" - private def isImplClass(s: Symbol) = s.name.toString endsWith "$class" /** Standard noise reduction filter. */ def excludeMember(s: Symbol) = ( isSpecialized(s) - || isImplClass(s) || s.isAnonOrRefinementClass || s.isAnonymousFunction ) diff --git a/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala b/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala index 3a2177a4cb..b9a4054ffc 100644 --- a/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala +++ b/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala @@ -5,12 +5,12 @@ package scala.tools.nsc.interpreter import scala.reflect.internal.util.RangePosition +import scala.reflect.io.AbstractFile import scala.tools.nsc.backend.JavaPlatform +import scala.tools.nsc.util.ClassPath import scala.tools.nsc.{interactive, Settings} -import scala.tools.nsc.io._ import scala.tools.nsc.reporters.StoreReporter -import scala.tools.nsc.util.ClassPath.DefaultJavaContext -import scala.tools.nsc.util.{DirectoryClassPath, MergedClassPath} +import scala.tools.nsc.classpath._ trait PresentationCompilation { self: IMain => @@ -55,8 +55,10 @@ trait PresentationCompilation { * You may downcast the `reporter` to `StoreReporter` to access type errors. */ def newPresentationCompiler(): interactive.Global = { - val replOutClasspath: DirectoryClassPath = new DirectoryClassPath(replOutput.dir, DefaultJavaContext) - val mergedClasspath = new MergedClassPath[AbstractFile](replOutClasspath :: global.platform.classPath :: Nil, DefaultJavaContext) + def mergedFlatClasspath = { + val replOutClasspath = ClassPathFactory.newClassPath(replOutput.dir, settings) + AggregateClassPath(replOutClasspath :: global.platform.classPath :: Nil) + } def copySettings: Settings = { val s = new Settings(_ => () /* ignores "bad option -nc" errors, etc */) s.processArguments(global.settings.recreateArgs, processAll = false) @@ -65,10 +67,11 @@ trait PresentationCompilation { } val storeReporter: StoreReporter = new StoreReporter val interactiveGlobal = new interactive.Global(copySettings, storeReporter) { self => - override lazy val platform: ThisPlatform = new JavaPlatform { - val global: self.type = self - - override def classPath: PlatformClassPath = mergedClasspath + override lazy val platform: ThisPlatform = { + new JavaPlatform { + val global: self.type = self + override private[nsc] lazy val classPath: ClassPath = mergedFlatClasspath + } } } new interactiveGlobal.TyperRun() @@ -78,7 +81,7 @@ trait PresentationCompilation { abstract class PresentationCompileResult { val compiler: scala.tools.nsc.interactive.Global def unit: compiler.RichCompilationUnit - /** The length of synthetic code the precedes the user writtn code */ + /** The length of synthetic code the precedes the user written code */ def preambleLength: Int def cleanup(): Unit = { compiler.askShutdown() diff --git a/src/repl/scala/tools/nsc/interpreter/ReplStrings.scala b/src/repl/scala/tools/nsc/interpreter/ReplStrings.scala index 1664546cab..bf7508cb4e 100644 --- a/src/repl/scala/tools/nsc/interpreter/ReplStrings.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplStrings.scala @@ -11,13 +11,21 @@ import scala.reflect.internal.Chars trait ReplStrings { /** Convert a string into code that can recreate the string. * This requires replacing all special characters by escape - * codes. It does not add the surrounding " marks. */ + * codes. It does not add the surrounding " marks. + */ def string2code(str: String): String = { val res = new StringBuilder for (c <- str) c match { - case '"' | '\'' | '\\' => res += '\\' ; res += c - case _ if c.isControl => res ++= Chars.char2uescape(c) - case _ => res += c + case '"' => res ++= """\"""" + case '\'' => res ++= """\'""" + case '\\' => res ++= """\\""" + case '\b' => res ++= """\b""" + case '\t' => res ++= """\t""" + case '\n' => res ++= """\n""" + case '\f' => res ++= """\f""" + case '\r' => res ++= """\r""" + case _ if c.isControl => res ++= Chars.char2uescape(c) + case _ => res += c } res.toString } diff --git a/src/repl/scala/tools/nsc/interpreter/Scripted.scala b/src/repl/scala/tools/nsc/interpreter/Scripted.scala new file mode 100644 index 0000000000..25d359bc0e --- /dev/null +++ b/src/repl/scala/tools/nsc/interpreter/Scripted.scala @@ -0,0 +1,343 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2016 LAMP/EPFL + */ +package scala +package tools.nsc +package interpreter + +import scala.language.dynamics + +import scala.beans.BeanProperty +import scala.collection.JavaConverters._ +import scala.reflect.classTag +import scala.reflect.internal.util.Position +import scala.tools.nsc.util.stringFromReader +import javax.script._, ScriptContext.{ ENGINE_SCOPE, GLOBAL_SCOPE } +import java.io.{ Closeable, Reader } + +/* A REPL adaptor for the javax.script API. */ +class Scripted(@BeanProperty val factory: ScriptEngineFactory, settings: Settings, out: JPrintWriter) + extends AbstractScriptEngine with Compilable { + + def createBindings: Bindings = new SimpleBindings + + // dynamic context bound under this name + final val ctx = "$ctx" + + // the underlying interpreter, tweaked to handle dynamic bindings + val intp = new IMain(settings, out) { + import global.{ Name, TermName } + + /* Modify the template to snag definitions from dynamic context. + * So object $iw { x + 42 } becomes object $iw { def x = $ctx.x ; x + 42 } + */ + override protected def importsCode(wanted: Set[Name], wrapper: Request#Wrapper, definesClass: Boolean, generousImports: Boolean) = { + + // cull references that can be satisfied from the current dynamic context + val contextual = wanted & contextNames + + if (contextual.nonEmpty) { + val neededContext = (wanted &~ contextual) + TermName(ctx) + val ComputedImports(header, preamble, trailer, path) = super.importsCode(neededContext, wrapper, definesClass, generousImports) + val adjusted = contextual.map { n => + val valname = n.decodedName + s"""def `$valname` = $ctx.`$valname` + def `${valname}_=`(x: Object) = $ctx.`$valname` = x""" + }.mkString(preamble, "\n", "\n") + ComputedImports(header, adjusted, trailer, path) + } + else super.importsCode(wanted, wrapper, definesClass, generousImports) + } + + // names available in current dynamic context + def contextNames: Set[Name] = { + val ctx = compileContext + val terms = for { + scope <- ctx.getScopes.asScala + binding <- Option(ctx.getBindings(scope)) map (_.asScala) getOrElse Nil + key = binding._1 + } yield (TermName(key): Name) + terms.to[Set] + } + + // save first error for exception; console display only if debugging + override lazy val reporter: ReplReporter = new ReplReporter(this) { + override def display(pos: Position, msg: String, severity: Severity): Unit = + if (isReplDebug) super.display(pos, msg, severity) + override def error(pos: Position, msg: String): Unit = { + if (firstError.isEmpty) firstError = Some((pos, msg)) + super.error(pos, msg) + } + override def reset() = { super.reset() ; firstError = None } + } + } + intp.initializeSynchronous() + + var compileContext: ScriptContext = getContext + + val scriptContextRep = new intp.ReadEvalPrint + + def dynamicContext_=(ctx: ScriptContext): Unit = scriptContextRep.callEither("set", ctx) + + def dynamicContext: ScriptContext = scriptContextRep.callEither("value") match { + case Right(ctx: ScriptContext) => ctx + case Left(e) => throw e + case Right(other) => throw new ScriptException(s"Unexpected value for context: $other") + } + + if (intp.isInitializeComplete) { + // compile the dynamic ScriptContext object holder + scriptContextRep compile s""" + |import javax.script._ + |object ${scriptContextRep.evalName} { + | var value: ScriptContext = _ + | def set(x: Any) = value = x.asInstanceOf[ScriptContext] + |} + """.stripMargin + dynamicContext = getContext + + // Bridge dynamic references and script context + intp compileString s""" + |package scala.tools.nsc.interpreter + |import language.dynamics + |import javax.script._, ScriptContext.ENGINE_SCOPE + |object dynamicBindings extends Dynamic { + | def context: ScriptContext = ${ scriptContextRep.evalPath }.value + | // $ctx.x retrieves the attribute x + | def selectDynamic(field: String): Object = context.getAttribute(field) + | // $ctx.x = v + | def updateDynamic(field: String)(value: Object) = context.setAttribute(field, value, ENGINE_SCOPE) + |} + |""".stripMargin + intp beQuietDuring { + intp interpret s"val $ctx: scala.tools.nsc.interpreter.dynamicBindings.type = scala.tools.nsc.interpreter.dynamicBindings" + intp bind ("$engine" -> (this: ScriptEngine with Compilable)) + } + } + + // Set the context for dynamic resolution and run the body. + // Defines attributes available for evaluation. + // Avoid reflective access if using default context. + def withScriptContext[A](context: ScriptContext)(body: => A): A = + if (context eq getContext) body else { + val saved = dynamicContext + dynamicContext = context + try body + finally dynamicContext = saved + } + // Defines attributes available for compilation. + def withCompileContext[A](context: ScriptContext)(body: => A): A = { + val saved = compileContext + compileContext = context + try body + finally compileContext = saved + } + + // not obvious that ScriptEngine should accumulate code text + private var code = "" + + private var firstError: Option[(Position, String)] = None + + /* All scripts are compiled. The supplied context defines what references + * not in REPL history are allowed, though a different context may be + * supplied for evaluation of a compiled script. + */ + def compile(script: String, context: ScriptContext): CompiledScript = + withCompileContext(context) { + val cat = code + script + intp.compile(cat, synthetic = false) match { + case Right(req) => + code = "" + new WrappedRequest(req) + case Left(IR.Incomplete) => + code = cat + "\n" + new CompiledScript { + def eval(context: ScriptContext): Object = null + def getEngine: ScriptEngine = Scripted.this + } + case Left(_) => + code = "" + throw firstError map { + case (pos, msg) => new ScriptException(msg, script, pos.line, pos.column) + } getOrElse new ScriptException("compile-time error") + } + } + + // documentation + //protected var context: ScriptContext + //def getContext: ScriptContext = context + + /* Compile with the default context. All references must be resolvable. */ + @throws[ScriptException] + def compile(script: String): CompiledScript = compile(script, context) + + @throws[ScriptException] + def compile(reader: Reader): CompiledScript = compile(stringFromReader(reader), context) + + /* Compile and evaluate with the given context. */ + @throws[ScriptException] + def eval(script: String, context: ScriptContext): Object = compile(script, context).eval(context) + + @throws[ScriptException] + def eval(reader: Reader, context: ScriptContext): Object = compile(stringFromReader(reader), context).eval(context) + + private class WrappedRequest(val req: intp.Request) extends CompiledScript { + var first = true + + private def evalEither(r: intp.Request, ctx: ScriptContext) = { + if (ctx.getWriter == null && ctx.getErrorWriter == null && ctx.getReader == null) r.lineRep.evalEither + else { + val closeables = Array.ofDim[Closeable](2) + val w = if (ctx.getWriter == null) Console.out else { + val v = new WriterOutputStream(ctx.getWriter) + closeables(0) = v + v + } + val e = if (ctx.getErrorWriter == null) Console.err else { + val v = new WriterOutputStream(ctx.getErrorWriter) + closeables(1) = v + v + } + val in = if (ctx.getReader == null) Console.in else ctx.getReader + try { + Console.withOut(w) { + Console.withErr(e) { + Console.withIn(in) { + r.lineRep.evalEither + } + } + } + } finally { + closeables foreach (c => if (c != null) c.close()) + } + } + } + + /* First time, cause lazy evaluation of a memoized result. + * Subsequently, instantiate a new object for evaluation. + * Per the API: Checked exception types thrown by underlying scripting implementations + * must be wrapped in instances of ScriptException. + */ + @throws[ScriptException] + override def eval(context: ScriptContext) = withScriptContext(context) { + if (first) { + val result = evalEither(req, context) match { + case Left(e: RuntimeException) => throw e + case Left(e: Exception) => throw new ScriptException(e) + case Left(e) => throw e + case Right(result) => result.asInstanceOf[Object] + } + intp recordRequest req + first = false + result + } else { + val defines = req.defines + if (defines.isEmpty) { + Scripted.this.eval(s"new ${req.lineRep.readPath}") + intp recordRequest duplicate(req) + null + } else { + val instance = s"val $$INSTANCE = new ${req.lineRep.readPath};" + val newline = (defines map (s => s"val ${s.name} = $$INSTANCE${req.accessPath}.${s.name}")).mkString(instance, ";", ";") + val newreq = intp.requestFromLine(newline).right.get + val ok = newreq.compile + + val result = evalEither(newreq, context) match { + case Left(e: RuntimeException) => throw e + case Left(e: Exception) => throw new ScriptException(e) + case Left(e) => throw e + case Right(result) => intp recordRequest newreq ; result.asInstanceOf[Object] + } + result + } + } + } + + def duplicate(req: intp.Request) = new intp.Request(req.line, req.trees) + + def getEngine: ScriptEngine = Scripted.this + } +} + +object Scripted { + import IMain.{ defaultSettings, defaultOut } + import java.util.Arrays.asList + import scala.util.Properties.versionString + + class Factory extends ScriptEngineFactory { + @BeanProperty val engineName = "Scala REPL" + + @BeanProperty val engineVersion = "2.0" + + @BeanProperty val extensions = asList("scala") + + @BeanProperty val languageName = "Scala" + + @BeanProperty val languageVersion = versionString + + @BeanProperty val mimeTypes = asList("application/x-scala") + + @BeanProperty val names = asList("scala") + + def getMethodCallSyntax(obj: String, m: String, args: String*): String = args.mkString(s"$obj.$m(", ", ", ")") + + def getOutputStatement(toDisplay: String): String = s"Console.println($toDisplay)" + + def getParameter(key: String): Object = key match { + case ScriptEngine.ENGINE => engineName + case ScriptEngine.ENGINE_VERSION => engineVersion + case ScriptEngine.LANGUAGE => languageName + case ScriptEngine.LANGUAGE_VERSION => languageVersion + case ScriptEngine.NAME => names.get(0) + case _ => null + } + + def getProgram(statements: String*): String = statements.mkString("object Main extends App {\n\t", "\n\t", "\n}") + + def getScriptEngine: ScriptEngine = { + val settings = new Settings() + settings.usemanifestcp.value = true + Scripted(this, settings) + } + } + + def apply(factory: ScriptEngineFactory = new Factory, settings: Settings = defaultSettings, out: JPrintWriter = defaultOut) = { + settings.Yreplclassbased.value = true + settings.usejavacp.value = true + val s = new Scripted(factory, settings, out) + s.setBindings(s.createBindings, ScriptContext.ENGINE_SCOPE) + s + } +} + +import java.io.Writer +import java.nio.{ ByteBuffer, CharBuffer } +import java.nio.charset.{ Charset, CodingErrorAction } +import CodingErrorAction.{ REPLACE => Replace } + +/* An OutputStream that decodes bytes and flushes to the writer. */ +class WriterOutputStream(writer: Writer) extends OutputStream { + val decoder = Charset.defaultCharset.newDecoder + decoder onMalformedInput Replace + decoder onUnmappableCharacter Replace + + val byteBuffer = ByteBuffer.allocate(64) + val charBuffer = CharBuffer.allocate(64) + + override def write(b: Int): Unit = { + byteBuffer.put(b.toByte) + byteBuffer.flip() + val result = decoder.decode(byteBuffer, charBuffer, /*eoi=*/ false) + if (byteBuffer.remaining == 0) byteBuffer.clear() + if (charBuffer.position > 0) { + charBuffer.flip() + writer write charBuffer.toString + charBuffer.clear() + } + } + override def close(): Unit = { + decoder.decode(byteBuffer, charBuffer, /*eoi=*/ true) + decoder.flush(charBuffer) + } + override def toString = charBuffer.toString +} diff --git a/src/repl/scala/tools/nsc/interpreter/package.scala b/src/repl/scala/tools/nsc/interpreter/package.scala index 56f1e65376..55949b81a5 100644 --- a/src/repl/scala/tools/nsc/interpreter/package.scala +++ b/src/repl/scala/tools/nsc/interpreter/package.scala @@ -88,9 +88,6 @@ package object interpreter extends ReplConfig with ReplStrings { } } - if (filtered.isEmpty) - return "No implicits have been imported other than those in Predef." - filtered foreach { case (source, syms) => p("/* " + syms.size + " implicit members imported from " + source.fullName + " */") @@ -126,7 +123,14 @@ package object interpreter extends ReplConfig with ReplStrings { } p("") } - "" + + if (filtered.nonEmpty) + "" // side-effects above + else if (global.settings.nopredef || global.settings.noimports) + "No implicits have been imported." + else + "No implicits have been imported other than those in Predef." + } def kindCommandInternal(expr: String, verbose: Boolean): Unit = { @@ -198,13 +202,14 @@ package object interpreter extends ReplConfig with ReplStrings { } } - /* debug assist + /* An s-interpolator that uses `stringOf(arg)` instead of `String.valueOf(arg)`. */ private[nsc] implicit class `smart stringifier`(val sc: StringContext) extends AnyVal { - import StringContext._, runtime.ScalaRunTime.stringOf + import StringContext.treatEscapes, scala.runtime.ScalaRunTime.stringOf def ss(args: Any*): String = sc.standardInterpolator(treatEscapes, args map stringOf) - } debug assist */ + } + /* Try (body) lastly (more) */ private[nsc] implicit class `try lastly`[A](val t: Try[A]) extends AnyVal { - private def effect[X](last: =>Unit)(a: X): Try[A] = { last; t } - def lastly(last: =>Unit): Try[A] = t transform (effect(last) _, effect(last) _) + private def effect[X](last: => Unit)(a: X): Try[A] = { last; t } + def lastly(last: => Unit): Try[A] = t transform (effect(last) _, effect(last) _) } } diff --git a/src/scaladoc/scala/tools/ant/Scaladoc.scala b/src/scaladoc/scala/tools/ant/Scaladoc.scala index b38aadd328..63d3b4ce27 100644 --- a/src/scaladoc/scala/tools/ant/Scaladoc.scala +++ b/src/scaladoc/scala/tools/ant/Scaladoc.scala @@ -14,8 +14,8 @@ import org.apache.tools.ant.Project import org.apache.tools.ant.types.{Path, Reference} import org.apache.tools.ant.util.{FileUtils, GlobPatternMapper} +import scala.tools.nsc.ScalaDocReporter import scala.tools.nsc.doc.Settings -import scala.tools.nsc.reporters.ConsoleReporter /** An Ant task to document Scala code. * @@ -666,7 +666,7 @@ class Scaladoc extends ScalaMatchingTask { /** Performs the compilation. */ override def execute() = { val (docSettings, sourceFiles) = initialize - val reporter = new ConsoleReporter(docSettings) + val reporter = new ScalaDocReporter(docSettings) try { val docProcessor = new scala.tools.nsc.doc.DocFactory(reporter, docSettings) docProcessor.document(sourceFiles.map (_.toString)) diff --git a/src/scaladoc/scala/tools/nsc/ScalaDoc.scala b/src/scaladoc/scala/tools/nsc/ScalaDoc.scala index bd00c27f7b..e266f7beea 100644 --- a/src/scaladoc/scala/tools/nsc/ScalaDoc.scala +++ b/src/scaladoc/scala/tools/nsc/ScalaDoc.scala @@ -8,7 +8,8 @@ package scala.tools.nsc import scala.tools.nsc.doc.DocFactory import scala.tools.nsc.reporters.ConsoleReporter -import scala.reflect.internal.util.FakePos +import scala.reflect.internal.Reporter +import scala.reflect.internal.util.{ FakePos, NoPosition, Position } /** The main class for scaladoc, a front-end for the Scala compiler * that generates documentation from source files. @@ -38,23 +39,43 @@ class ScalaDoc { reporter.echo(command.usageMsg) else try { new DocFactory(reporter, docSettings) document command.files } - catch { - case ex @ FatalError(msg) => - if (docSettings.debug.value) ex.printStackTrace() - reporter.error(null, "fatal error: " + msg) - } - finally reporter.printSummary() + catch { + case ex @ FatalError(msg) => + if (docSettings.debug.value) ex.printStackTrace() + reporter.error(null, "fatal error: " + msg) + } + finally reporter.printSummary() !reporter.reallyHasErrors } } +/** The Scaladoc reporter adds summary messages to the `ConsoleReporter` + * + * Use the `summaryX` methods to add unique summarizing message to the end of + * the run. + */ class ScalaDocReporter(settings: Settings) extends ConsoleReporter(settings) { + import scala.collection.mutable.LinkedHashMap // need to do sometimes lie so that the Global instance doesn't // trash all the symbols just because there was an error override def hasErrors = false def reallyHasErrors = super.hasErrors + + private[this] val delayedMessages: LinkedHashMap[(Position, String), () => Unit] = + LinkedHashMap.empty + + /** Eliminates messages if both `pos` and `msg` are equal to existing element */ + def addDelayedMessage(pos: Position, msg: String, print: () => Unit): Unit = + delayedMessages += ((pos, msg) -> print) + + def printDelayedMessages(): Unit = delayedMessages.values.foreach(_.apply()) + + override def printSummary(): Unit = { + printDelayedMessages() + super.printSummary() + } } object ScalaDoc extends ScalaDoc { @@ -70,4 +91,20 @@ object ScalaDoc extends ScalaDoc { def main(args: Array[String]): Unit = sys exit { if (process(args)) 0 else 1 } + + implicit class SummaryReporter(val rep: Reporter) extends AnyVal { + /** Adds print lambda to ScalaDocReporter, executes it on other reporter */ + private[this] def summaryMessage(pos: Position, msg: String, print: () => Unit): Unit = rep match { + case r: ScalaDocReporter => r.addDelayedMessage(pos, msg, print) + case _ => print() + } + + def summaryEcho(pos: Position, msg: String): Unit = summaryMessage(pos, msg, () => rep.echo(pos, msg)) + def summaryError(pos: Position, msg: String): Unit = summaryMessage(pos, msg, () => rep.error(pos, msg)) + def summaryWarning(pos: Position, msg: String): Unit = summaryMessage(pos, msg, () => rep.warning(pos, msg)) + + def summaryEcho(msg: String): Unit = summaryEcho(NoPosition, msg) + def summaryError(msg: String): Unit = summaryError(NoPosition, msg) + def summaryWarning(msg: String): Unit = summaryWarning(NoPosition, msg) + } } diff --git a/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala b/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala index 47ddfb8aa9..8c646be9c6 100644 --- a/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala @@ -6,8 +6,8 @@ package scala.tools.nsc package doc -import scala.util.control.ControlThrowable import reporters.Reporter +import scala.util.control.ControlThrowable import scala.reflect.internal.util.BatchSourceFile /** A documentation processor controls the process of generating Scala @@ -105,17 +105,24 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor def generate() = { import doclet._ val docletClass = Class.forName(settings.docgenerator.value) // default is html.Doclet - val docletInstance = docletClass.newInstance().asInstanceOf[Generator] + val docletInstance = + docletClass + .getConstructors + .find { constr => + constr.getParameterTypes.length == 1 && + constr.getParameterTypes.apply(0) == classOf[scala.reflect.internal.Reporter] + } + .map(_.newInstance(reporter)) + .getOrElse{ + reporter.warning(null, "Doclets should be created with the Reporter constructor, otherwise logging reporters will not be shared by the creating parent") + docletClass.newInstance() + } + .asInstanceOf[Generator] docletInstance match { case universer: Universer => val universe = makeUniverse(Left(files)) getOrElse { throw NoCompilerRunException } universer setUniverse universe - - docletInstance match { - case indexer: Indexer => indexer setIndex model.IndexModelFactory.makeIndex(universe) - case _ => () - } case _ => () } docletInstance.generate() diff --git a/src/scaladoc/scala/tools/nsc/doc/Settings.scala b/src/scaladoc/scala/tools/nsc/doc/Settings.scala index 8a341a92d5..59380dd782 100644 --- a/src/scaladoc/scala/tools/nsc/doc/Settings.scala +++ b/src/scaladoc/scala/tools/nsc/doc/Settings.scala @@ -14,6 +14,9 @@ import scala.language.postfixOps * @param printMsg A function that prints the string, without any extra boilerplate of error */ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) extends scala.tools.nsc.Settings(error) { + // TODO 2.13 Remove + private def removalIn213 = "This flag is scheduled for removal in 2.13. If you have a case where you need this flag then please report a bug." + /** A setting that defines in which format the documentation is output. ''Note:'' this setting is currently always * `html`. */ val docformat = ChoiceSetting ( @@ -45,7 +48,7 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) val docfooter = StringSetting ( "-doc-footer", "footer", - "A footer on every Scaladoc page, by default the EPFL/Typesafe copyright notice. Can be overridden with a custom footer.", + "A footer on every Scaladoc page, by default the EPFL/Lightbend copyright notice. Can be overridden with a custom footer.", "" ) @@ -199,10 +202,11 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) "" ) + // TODO 2.13 Remove val docExpandAllTypes = BooleanSetting ( "-expand-all-types", "Expand all type aliases and abstract types into full template pages. (locally this can be done with the @template annotation)" - ) + ) withDeprecationMessage(removalIn213) val docGroups = BooleanSetting ( "-groups", diff --git a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala index 707d0c469f..d3b4bf8ff5 100644 --- a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala +++ b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala @@ -47,7 +47,8 @@ trait CommentFactoryBase { this: MemberLookupBase => groupDesc0: Map[String,Body] = Map.empty, groupNames0: Map[String,Body] = Map.empty, groupPrio0: Map[String,Body] = Map.empty, - hideImplicitConversions0: List[Body] = List.empty + hideImplicitConversions0: List[Body] = List.empty, + shortDescription0: List[Body] = List.empty ): Comment = new Comment { val body = body0 getOrElse Body(Seq.empty) val authors = authors0 @@ -90,9 +91,13 @@ trait CommentFactoryBase { this: MemberLookupBase => } } + override val shortDescription: Option[Text] = shortDescription0.lastOption collect { + case Body(List(Paragraph(Chain(List(Summary(Text(e))))))) if !e.trim.contains("\n") => Text(e) + } + override val hideImplicitConversions: List[String] = hideImplicitConversions0 flatMap { - case Body(List(Paragraph(Chain(List(Summary(Text(e))))))) if (!e.trim.contains("\n")) => List(e) + case Body(List(Paragraph(Chain(List(Summary(Text(e))))))) if !e.trim.contains("\n") => List(e) case _ => List() } } @@ -290,7 +295,7 @@ trait CommentFactoryBase { this: MemberLookupBase => } case line :: ls if (lastTagKey.isDefined) => { - val newtags = if (!line.isEmpty) { + val newtags = if (!line.isEmpty || inCodeBlock) { val key = lastTagKey.get val value = ((tags get key): @unchecked) match { @@ -397,7 +402,8 @@ trait CommentFactoryBase { this: MemberLookupBase => groupDesc0 = allSymsOneTag(SimpleTagKey("groupdesc")), groupNames0 = allSymsOneTag(SimpleTagKey("groupname")), groupPrio0 = allSymsOneTag(SimpleTagKey("groupprio")), - hideImplicitConversions0 = allTags(SimpleTagKey("hideImplicitConversion")) + hideImplicitConversions0 = allTags(SimpleTagKey("hideImplicitConversion")), + shortDescription0 = allTags(SimpleTagKey("shortDescription")) ) for ((key, _) <- bodyTags) diff --git a/src/scaladoc/scala/tools/nsc/doc/base/comment/Comment.scala b/src/scaladoc/scala/tools/nsc/doc/base/comment/Comment.scala index 81f8c3ec06..5d0b6782bc 100644 --- a/src/scaladoc/scala/tools/nsc/doc/base/comment/Comment.scala +++ b/src/scaladoc/scala/tools/nsc/doc/base/comment/Comment.scala @@ -47,9 +47,10 @@ abstract class Comment { Chain(List(inline) ++ stack.reverse) } - /** A shorter version of the body. Usually, this is the first sentence of the body. */ + /** A shorter version of the body. Either from `@shortDescription` or the + * first sentence of the body. */ def short: Inline = { - body.summary match { + shortDescription orElse body.summary match { case Some(s) => closeHtmlTags(s) case _ => @@ -62,7 +63,7 @@ abstract class Comment { /** A list of other resources to see, including links to other entities or * to external documentation. The empty list is used when no other resource - * is mentionned. */ + * is mentioned. */ def see: List[Body] /** A description of the result of the entity. Typically, this provides additional @@ -126,6 +127,9 @@ abstract class Comment { /** A list of implicit conversions to hide */ def hideImplicitConversions: List[String] + /** A short description used in the entity-view and search results */ + def shortDescription: Option[Text] + override def toString = body.toString + "\n" + (authors map ("@author " + _.toString)).mkString("\n") + diff --git a/src/scaladoc/scala/tools/nsc/doc/doclet/Generator.scala b/src/scaladoc/scala/tools/nsc/doc/doclet/Generator.scala index 42b56aa927..b4ede6d358 100644 --- a/src/scaladoc/scala/tools/nsc/doc/doclet/Generator.scala +++ b/src/scaladoc/scala/tools/nsc/doc/doclet/Generator.scala @@ -9,7 +9,6 @@ import scala.collection._ * to configure what data is actually available to the generator: * - A `Universer` provides a `Universe` data structure representing the interfaces and comments of the documented * program. - * - An `Indexer` provides precalculated indexing information about a universe. * To implement this class only requires defining method `generateImpl`. */ abstract class Generator { diff --git a/src/scaladoc/scala/tools/nsc/doc/doclet/Indexer.scala b/src/scaladoc/scala/tools/nsc/doc/doclet/Indexer.scala deleted file mode 100644 index 12fee69cca..0000000000 --- a/src/scaladoc/scala/tools/nsc/doc/doclet/Indexer.scala +++ /dev/null @@ -1,21 +0,0 @@ -package scala.tools.nsc -package doc -package doclet - -/** A `Generator` may implement the `Indexer` trait to gain access to pre-calculated indexing information */ -trait Indexer extends Generator with Universer { - - protected var indexField: Index = null - - def index: Index = indexField - - def setIndex(i: Index) { - assert(indexField == null) - indexField = i - } - - checks += { () => - indexField != null - } - -} diff --git a/src/scaladoc/scala/tools/nsc/doc/html/Doclet.scala b/src/scaladoc/scala/tools/nsc/doc/html/Doclet.scala index cad7cf3298..73a854e995 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/Doclet.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/Doclet.scala @@ -7,14 +7,19 @@ package scala.tools.nsc package doc package html +import scala.reflect.internal.Reporter import doclet._ /** The default doclet used by the scaladoc command line tool * when no user-provided doclet is provided. */ -class Doclet extends Generator with Universer with Indexer { +class Doclet(reporter: Reporter) extends Generator with Universer { - def generateImpl() { - new html.HtmlFactory(universe, index, new ScalaDocReporter(universe.settings)).generate() - } + @deprecated("Doclets should be created with the Reporter constructor. Otherwise logging reporters will not be shared by the creating parent", "2.12.0") + def this() = this(null) + def generateImpl() = + new html.HtmlFactory( + universe, + if (reporter != null) reporter else new ScalaDocReporter(universe.settings) + ).generate() } diff --git a/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala b/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala index 65c540d4c5..62620057cb 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala @@ -12,11 +12,12 @@ import java.io.{ File => JFile } import io.{ Streamable, Directory } import scala.collection._ import page.diagram._ +import scala.reflect.internal.Reporter /** A class that can generate Scaladoc sites to some fixed root folder. * @author David Bernard * @author Gilles Dubochet */ -class HtmlFactory(val universe: doc.Universe, index: doc.Index, val reporter: ScalaDocReporter) { +class HtmlFactory(val universe: doc.Universe, val reporter: Reporter) { import page.{IndexScript, EntityPage} /** The character encoding to be used for generated Scaladoc sites. @@ -33,7 +34,7 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index, val reporter: Sc "class_comp.svg", "object_comp.svg", "trait_comp.svg", - "permalink.svg", + "object_comp_trait.svg", "abstract_type.svg", "lato-v11-latin-100.eot", "lato-v11-latin-100.ttf", @@ -100,16 +101,10 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index, val reporter: Sc libResources foreach (s => copyResource("lib/" + s)) - IndexScript(universe, index) writeFor this + IndexScript(universe) writeFor this - if (index.hasDeprecatedMembers) - new page.DeprecatedIndex(universe, index, reporter) writeFor this try { writeTemplates(_ writeFor this) - - for (letter <- index.firstLetterIndex) { - new html.page.ReferenceIndex(letter._1, index, universe, reporter) writeFor this - } } finally { DiagramStats.printStats(universe.settings) universe.dotRunner.cleanup() diff --git a/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala b/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala index 0eb90d8942..6ad51f4f7e 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala @@ -13,6 +13,7 @@ import base._ import base.comment._ import model._ +import scala.reflect.internal.Reporter import scala.xml.NodeSeq import scala.xml.Elem import scala.xml.dtd.DocType @@ -27,7 +28,7 @@ abstract class HtmlPage extends Page { thisPage => protected def title: String /** ScalaDoc reporter for error handling */ - protected def reporter: ScalaDocReporter + protected def docletReporter: Reporter /** The page description */ protected def description: String = @@ -216,41 +217,16 @@ abstract class HtmlPage extends Page { thisPage => 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.svg", "lib")) } alt="Permalink" /> + <a href={ memberToUrl(template, isSelf) } title="Permalink"> + <i class="material-icons"></i> </a> </span> + def docEntityImageClass(tpl: DocTemplateEntity): String = + tpl.kind + tpl.companion.fold("")("-companion-" + _.kind) + def docEntityKindToCompanionTitle(ety: DocTemplateEntity, baseString: String = "See companion") = ety.companion match{ case Some(companion) => @@ -263,7 +239,7 @@ abstract class HtmlPage extends Page { thisPage => case None => baseString } - def companionAndPackage(tpl: DocTemplateEntity): Elem = + def companionAndPackage(tpl: DocTemplateEntity): NodeSeq = <span class="morelinks">{ tpl.companion match { case Some(companionTpl) => @@ -272,15 +248,9 @@ abstract class HtmlPage extends Page { thisPage => else if (companionTpl.isTrait) s"trait ${companionTpl.name}" else s"class ${companionTpl.name}" <div> - Related Docs: - <a href={relativeLinkTo(tpl.companion.get)} title={docEntityKindToCompanionTitle(tpl)}>{objClassTrait}</a> - <span class="divider">|</span> - {templateToHtml(tpl.inTemplate, s"package ${tpl.inTemplate.name}")} - </div> - case None => - <div>Related Doc: - {templateToHtml(tpl.inTemplate, s"package ${tpl.inTemplate.name}")} + Companion <a href={relativeLinkTo(companionTpl)} title={docEntityKindToCompanionTitle(tpl)}>{objClassTrait}</a> </div> + case None => NodeSeq.Empty } }</span> diff --git a/src/scaladoc/scala/tools/nsc/doc/html/Page.scala b/src/scaladoc/scala/tools/nsc/doc/html/Page.scala index 875d7919c2..c720c4939f 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/Page.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/Page.scala @@ -102,6 +102,11 @@ abstract class Page { relativize(thisPage.path.reverse, destPath.reverse).mkString("/") } + def hasCompanion(mbr: TemplateEntity): Boolean = mbr match { + case dtpl: DocTemplateEntity => dtpl.companion.isDefined + case _ => false + } + protected def inlineToStr(inl: comment.Inline): String = inl match { case comment.Chain(items) => items flatMap (inlineToStr(_)) mkString "" case comment.Italic(in) => inlineToStr(in) diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/DeprecatedIndex.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/DeprecatedIndex.scala deleted file mode 100644 index e8cb58c732..0000000000 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/DeprecatedIndex.scala +++ /dev/null @@ -1,60 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2007-2013 LAMP/EPFL - */ - -package scala -package tools -package nsc -package doc -package html -package page - -import doc.model._ - -class DeprecatedIndex(universe: Universe, index: doc.Index, rep: ScalaDocReporter) extends HtmlPage { - - def reporter = rep - - def path = List("deprecated-list.html") - - def title = { - val s = universe.settings - ( if (!s.doctitle.isDefault) s.doctitle.value else "" ) + - ( if (!s.docversion.isDefault) (" " + s.docversion.value) else "" ) - } - - def headers = - <xml:group> - <link href={ relativeLinkTo(List("ref-index.css", "lib")) } media="screen" type="text/css" rel="stylesheet"/> - <script type="text/javascript" src={ relativeLinkTo{List("jquery.js", "lib")} }></script> - </xml:group> - - - private def entry(name: String, methods: Iterable[MemberEntity]) = { - val occurrences = methods.filter(_.deprecation.isDefined).map(method => - templateToHtml(method.inDefinitionTemplates.head) - ).toList.distinct - - <div class="entry"> - <div class="name">{ name }</div> - <div class="occurrences">{ - for (owner <- occurrences) yield owner ++ scala.xml.Text(" ") - }</div> - </div> - } - - def deprecatedEntries = { - val available = ('_' +: ('a' to 'z')).flatMap(index.firstLetterIndex.get) - - for (group <- available; - value <- group if value._2.find(_.deprecation.isDefined).isDefined) - yield value - } - - def body = - <body>{ - for(value <- deprecatedEntries) yield - entry(value._1, value._2.view) - }</body> - -} diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala index 969e19c770..9dd2c2184d 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala @@ -13,6 +13,7 @@ package page import base._ import base.comment._ +import scala.reflect.internal.Reporter import scala.collection.mutable import scala.xml.{NodeSeq, Text, UnprefixedAttribute} import scala.language.postfixOps @@ -22,10 +23,12 @@ import model.diagram._ import diagram._ trait EntityPage extends HtmlPage { + import ScalaDoc.SummaryReporter + def universe: doc.Universe def generator: DiagramGenerator def tpl: DocTemplateEntity - def reporter: ScalaDocReporter + def docletReporter: Reporter override val path = templateToPath(tpl) @@ -71,9 +74,80 @@ trait EntityPage extends HtmlPage { <div id="member-results"></div> </div> </div> - <div id="content-container" style="-webkit-overflow-scrolling: touch;"> - <div id="content"> - { content } + <div id="content-scroll-container" style="-webkit-overflow-scrolling: touch;"> + <div id="content-container" style="-webkit-overflow-scrolling: touch;"> + <div id="subpackage-spacer"> + <div id="packages"> + <h1>Packages</h1> + <ul> + { + def entityToUl(mbr: TemplateEntity with MemberEntity, indentation: Int): NodeSeq = + if (mbr.isObject && hasCompanion(mbr)) + NodeSeq.Empty + else + <li class={"current-entities indented" + indentation}> + { + mbr match { + case dtpl: DocTemplateEntity => + dtpl.companion.fold(<span class="separator"></span>) { c: DocTemplateEntity => + <a class="object" href={relativeLinkTo(c)} title={c.comment.fold("")(com => inlineToStr(com.short))}></a> + } + case _ => <span class="separator"></span> + } + } + <a class={mbr.kind} href={relativeLinkTo(mbr)} title={mbr.comment.fold("")(com => inlineToStr(com.short))}></a> + <a href={relativeLinkTo(mbr)} title={mbr.comment.fold("")(com => inlineToStr(com.short))}> + {mbr.name} + </a> + </li> + + // Get path from root + val rootToParentLis = tpl.toRoot + .tail + .reverse + .zipWithIndex + .map { case (pack, ind) => + memberToHtml(pack, tpl, indentation = ind, isParent = (pack eq tpl.toRoot.tail.head)) + } + + val parent = tpl.toRoot match { + case _ :: parent :: _ if !parent.isRootPackage => Some(parent) + case _ => None + } + + val parentSub = parent.fold(Seq[TemplateEntity with MemberEntity](tpl)) { p => + p.templates.filter(_.isPackage).sortBy(_.name) + } + + // If current entity is a package, take its containing entities - otherwise take parent's containing entities + val currentPackageTpls = + if (tpl.isPackage) tpl.templates + else parent.fold(Seq.empty[TemplateEntity with MemberEntity])(p => p.templates) + + val (subsToTpl, subsAfterTpl) = parentSub.partition(_.name <= tpl.name) + + val subsToTplLis = subsToTpl.map(memberToHtml(_, tpl, indentation = rootToParentLis.length)) + val subsAfterTplLis = subsAfterTpl.map(memberToHtml(_, tpl, indentation = rootToParentLis.length)) + val currEntityLis = currentPackageTpls + .filter(x => !x.isPackage && (x.isTrait || x.isClass || x.isAbstractType || x.isObject)) + .sortBy(_.name) + .map(entityToUl(_, (if (tpl.isPackage) 0 else -1) + rootToParentLis.length)) + val currSubLis = tpl.templates + .filter(_.isPackage) + .sortBy(_.name) + .map(memberToHtml(_, tpl, indentation = rootToParentLis.length + 1)) + + if (subsToTpl.isEmpty && !tpl.isPackage) // current Entity is not a package, show packages before entity listing + rootToParentLis ++ subsToTplLis ++ subsAfterTplLis ++ currSubLis ++ currEntityLis + else + rootToParentLis ++ subsToTplLis ++ currSubLis ++ currEntityLis ++ subsAfterTplLis + } + </ul> + </div> + </div> + <div id="content"> + { content } + </div> </div> </div> </body> @@ -87,8 +161,7 @@ trait EntityPage extends HtmlPage { val version = universe.settings.docversion.value if (version.length > "XX.XX.XX-XXX".length) { - reporter.warning(null, - s"doc-version ($version) is too long to be displayed in the webview") + docletReporter.summaryWarning(s"doc-version ($version) was too long to be displayed in the webview, and will be left out. The max length is: XX.XX.XX-XXX") "" } else version } @@ -105,7 +178,7 @@ trait EntityPage extends HtmlPage { </div> val valueMembers = - tpl.methods ++ tpl.values ++ tpl.templates.filter(x => x.isObject || x.isPackage) sorted + tpl.methods ++ tpl.values ++ tpl.templates.filter(x => x.isObject) sorted val (absValueMembers, nonAbsValueMembers) = valueMembers partition (_.isAbstract) @@ -145,53 +218,53 @@ trait EntityPage extends HtmlPage { <body class={ tpl.kind + (if (tpl.isType) " type" else " value") }> <div id="definition"> { - val (src, alt) = docEntityKindToBigImage(tpl) - - val identifier = alt.toString.substring(0,2).toLowerCase + val imageClass = docEntityImageClass(tpl) tpl.companion match { case Some(companion) if (companion.visibility.isPublic && companion.inSource != None) => - <a href={relativeLinkTo(companion)} title={docEntityKindToCompanionTitle(tpl)}><div class={s"big-circle companion $identifier"}>{ identifier.substring(0,1) }</div></a> + <a href={relativeLinkTo(companion)} title={docEntityKindToCompanionTitle(tpl)}><div class={s"big-circle $imageClass"}>{ imageClass.substring(0,1) }</div></a> case _ => - <div class={ "big-circle " + alt.toString.toLowerCase }>{ identifier.substring(0,1) }</div> - }} + <div class={s"big-circle $imageClass"}>{ imageClass.substring(0,1) }</div> + } + } { owner } - <h1>{ displayName }</h1>{ - if (tpl.isPackage) NodeSeq.Empty else <h3>{companionAndPackage(tpl)}</h3> - }{ permalink(tpl) } - { signature(tpl, isSelf = true) } + <h1>{ displayName }{ permalink(tpl) }</h1> + { if (tpl.isPackage) NodeSeq.Empty else <h3>{companionAndPackage(tpl)}</h3> } </div> + { signature(tpl, isSelf = true) } + { memberToCommentHtml(tpl, tpl.inTemplate, isSelf = true) } + { if (valueMembers.filterNot(_.kind == "package").isEmpty) NodeSeq.Empty else <div id="mbrsel"> <div class='toggle'></div> <div id='memberfilter'> <i class="material-icons arrow"></i> <span class='input'> - <input id='mbrsel-input' placeholder='Filter members' type='text' accesskey='/'/> + <input id='mbrsel-input' placeholder='Filter all members' type='text' accesskey='/'/> </span> <i class="clear material-icons"></i> </div> - <div id='filterby'> - <div id="order"> - <span class="filtertype">Ordering</span> - <ol> - { - if (!universe.settings.docGroups.value || (tpl.members.map(_.group).distinct.length == 1)) - NodeSeq.Empty - else - <li class="group out"><span>Grouped</span></li> - } - <li class="alpha in"><span>Alphabetic</span></li> - { - if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) - NodeSeq.Empty - else - <li class="inherit out"><span>By Inheritance</span></li> - } - </ol> - </div> + <div id='filterby'> + <div id="order"> + <span class="filtertype">Ordering</span> + <ol> + { + if (!universe.settings.docGroups.value || (tpl.members.map(_.group).distinct.length == 1)) + NodeSeq.Empty + else + <li class="group out"><span>Grouped</span></li> + } + <li class="alpha in"><span>Alphabetic</span></li> + { + if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) + NodeSeq.Empty + else + <li class="inherit out"><span>By Inheritance</span></li> + } + </ol> + </div> { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) NodeSeq.Empty else { if (!tpl.linearizationTemplates.isEmpty) @@ -235,6 +308,7 @@ trait EntityPage extends HtmlPage { } </div> </div> + } <div id="template"> <div id="allMembers"> @@ -262,7 +336,12 @@ trait EntityPage extends HtmlPage { { if (concValueMembers.isEmpty) NodeSeq.Empty else <div class="values members"> <h3>{ if (absValueMembers.isEmpty) "Value Members" else "Concrete Value Members" }</h3> - <ol>{ concValueMembers map (memberToHtml(_, tpl)) }</ol> + <ol> + { + concValueMembers + .map(memberToHtml(_, tpl)) + } + </ol> </div> } @@ -329,14 +408,19 @@ trait EntityPage extends HtmlPage { { if (Set("epfl", "EPFL").contains(tpl.universe.settings.docfooter.value)) - <div id="footer">Scala programming documentation. Copyright (c) 2003-2016 <a href="http://www.epfl.ch" target="_top">EPFL</a>, with contributions from <a href="http://typesafe.com" target="_top">Typesafe</a>.</div> + <div id="footer">Scala programming documentation. Copyright (c) 2003-2016 <a href="http://www.epfl.ch" target="_top">EPFL</a>, with contributions from <a href="http://www.lightbend.com" target="_top">Lightbend</a>.</div> else <div id="footer"> { tpl.universe.settings.docfooter.value } </div> } </body> } - def memberToHtml(mbr: MemberEntity, inTpl: DocTemplateEntity): NodeSeq = { + def memberToHtml( + mbr: MemberEntity, + inTpl: DocTemplateEntity, + isParent: Boolean = false, + indentation: Int = 0 + ): NodeSeq = { // Sometimes it's same, do we need signatureCompat still? val sig = if (mbr.signature == mbr.signatureCompat) { <a id={ mbr.signature }/> @@ -346,6 +430,7 @@ trait EntityPage extends HtmlPage { val memberComment = memberToCommentHtml(mbr, inTpl, isSelf = false) <li name={ mbr.definitionName } visbl={ if (mbr.visibility.isProtected) "prt" else "pub" } + class={ s"indented$indentation " + (if (mbr eq inTpl) "current" else "") } data-isabs={ mbr.isAbstract.toString } fullComment={ if(memberComment.filter(_.label=="div").isEmpty) "no" else "yes" } group={ mbr.group }> @@ -504,7 +589,7 @@ trait EntityPage extends HtmlPage { <br/> ++ scala.xml.Text("To access this member you can use a ") ++ <a href="http://stackoverflow.com/questions/2087250/what-is-the-purpose-of-type-ascription-in-scala" target="_blank">type ascription</a> ++ scala.xml.Text(":") ++ - <br/> ++ <div class="cmt"><pre>{"(" + Template.lowerFirstLetter(tpl.name) + ": " + conv.targetType.name + ")." + mbr.name + params }</pre></div> + <br/> ++ <div class="cmt"><pre>{"(" + EntityPage.lowerFirstLetter(tpl.name) + ": " + conv.targetType.name + ")." + mbr.name + params }</pre></div> } val shadowingWarning: NodeSeq = @@ -695,7 +780,6 @@ trait EntityPage extends HtmlPage { case dtpl: DocTemplateEntity if isSelf && !isReduced && dtpl.linearizationTemplates.nonEmpty => <div class="toggleContainer block"> <span class="toggle"> - <i class="material-icons"></i> Linear Supertypes </span> <div class="superTypes hiddenContent">{ @@ -718,7 +802,6 @@ trait EntityPage extends HtmlPage { if (subs.nonEmpty) <div class="toggleContainer block"> <span class="toggle"> - <i class="material-icons"></i> Known Subclasses </span> <div class="subClasses hiddenContent">{ @@ -738,7 +821,6 @@ trait EntityPage extends HtmlPage { if (diagramSvg != NodeSeq.Empty) { <div class="toggleContainer block diagram-container" id={ id + "-container"}> <span class="toggle diagram-link"> - <i class="material-icons"></i> { description } </span> <div class="diagram" id={ id }>{ diagramSvg }</div> @@ -794,7 +876,6 @@ trait EntityPage extends HtmlPage { def inside(hasLinks: Boolean, nameLink: String = ""): NodeSeq = <xml:group> <span class="modifier_kind"> - <i class="material-icons unfold-arrow"></i> <span class="modifier">{ mbr.flags.map(flag => inlineToHtml(flag.text) ++ scala.xml.Text(" ")) }</span> <span class="kind">{ kindToString(mbr) }</span> </span> @@ -828,7 +909,9 @@ trait EntityPage extends HtmlPage { } } if (!nameLink.isEmpty) - <a href={nameLink}>{nameHtml}</a> + <a title={mbr.comment.fold("")(c => inlineToStr(c.short))} href={nameLink}> + {nameHtml} + </a> else nameHtml }{ def tparamsToHtml(mbr: Any): NodeSeq = mbr match { @@ -890,7 +973,7 @@ trait EntityPage extends HtmlPage { else NodeSeq.Empty case alt: MemberEntity with AliasType => - <span class="result"> = { typeToHtml(alt.alias, hasLinks) }</span> + <span class="result alias"> = { typeToHtml(alt.alias, hasLinks) }</span> case tpl: MemberTemplateEntity if !tpl.parentTypes.isEmpty => <span class="result"> extends { typeToHtml(tpl.parentTypes.map(_._2), hasLinks) }</span> @@ -1043,11 +1126,15 @@ object EntityPage { uni: doc.Universe, gen: DiagramGenerator, docTpl: DocTemplateEntity, - rep: ScalaDocReporter + rep: Reporter ): EntityPage = new EntityPage { def universe = uni def generator = gen def tpl = docTpl - def reporter = rep + def docletReporter = rep } + + /* Vlad: Lesson learned the hard way: don't put any stateful code that references the model here, + * it won't be garbage collected and you'll end up filling the heap with garbage */ + def lowerFirstLetter(s: String) = if (s.length >= 1) s.substring(0,1).toLowerCase() + s.substring(1) else s } diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/Index.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/Index.scala deleted file mode 100644 index 8204f413fd..0000000000 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/Index.scala +++ /dev/null @@ -1,75 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2007-2013 LAMP/EPFL - * @author David Bernard, Manohar Jonnalagedda, Felix Mulder - */ - -package scala.tools.nsc -package doc -package html -package page - -import model._ -import scala.collection._ -import scala.xml._ - -class Index(universe: doc.Universe, val index: doc.Index, rep: ScalaDocReporter) extends HtmlPage { - - def reporter = rep - - def path = List("index.html") - - def title = "" - - val headers = - <xml:group> - <link href={ relativeLinkTo{List("index.css", "lib")} } media="screen" type="text/css" rel="stylesheet"/> - <script type="text/javascript" src={ relativeLinkTo{List("jquery.js", "lib")} }></script> - <script type="text/javascript" src={ relativeLinkTo{List("index.js", "lib")} }></script> - <script type="text/javascript" src="index.js"></script> - <script type="text/javascript" src={ relativeLinkTo{List("scheduler.js", "lib")} }></script> - </xml:group> - - val body = - <body> - { search } - <div id="search-results"> - <div id="results-content"> - <div id="entity-results"></div> - <div id="member-results"></div> - </div> - </div> - <div id="content" style="-webkit-overflow-scrolling: touch;"> - <iframe id="template" name="template" src={ relativeLinkTo{List("package.html")} }/> - </div> - </body> - - def search = - <div id="search"> - <span id="doc-title"> - {universe.settings.doctitle.value} - <span id="doc-version"> - { - val version = universe.settings.docversion.value - - if (version.length > "XX.XX.XX-XXX".length) { - reporter.warning(null, - s"doc-version ($version) is too long to be displayed in the webview") - "" - } else version - } - </span> - </span> - <span class="close-results"><span class="left"><</span> Back</span> - <div id="textfilter"> - <span class="input"> - <input autocapitalize="none" placeholder="Search" id="index-input" type="text" accesskey="/"/> - <span class="clear">✖</span> - </span> - </div> - </div> - - def packageQualifiedName(ety: DocTemplateEntity): String = - if (ety.inTemplate.isPackage) ety.name - else (packageQualifiedName(ety.inTemplate) + "." + ety.name) - -} diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/IndexScript.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/IndexScript.scala index 2c38036bb6..6b24c0f568 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/IndexScript.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/page/IndexScript.scala @@ -12,7 +12,7 @@ import scala.tools.nsc.doc.model.{Package, DocTemplateEntity} import scala.tools.nsc.doc.html.{Page, HtmlFactory} import scala.util.parsing.json.{JSONObject, JSONArray, JSONType} -class IndexScript(universe: doc.Universe, index: doc.Index) extends Page { +class IndexScript(universe: doc.Universe) extends Page { import model._ import scala.tools.nsc.doc.base.comment.Text import scala.collection.immutable.Map @@ -30,17 +30,26 @@ class IndexScript(universe: doc.Universe, index: doc.Index) extends Page { case (pack, templates) => { val merged = mergeByQualifiedName(templates) - val ary = merged.keys.toList.sortBy(_.toLowerCase).map(key => { + val ary = merged.keys.toList.sortBy(_.toLowerCase).map { key => + /** One pair is generated for the class/trait and one for the + * companion object, both will have the same {"name": key} + * + * As such, we need to distinguish between the members that are + * generated by the object, and the members generated by the + * class/trait instance. Otherwise one of the member objects will be + * overwritten. + */ val pairs = merged(key).flatMap { t: DocTemplateEntity => + val kind = kindToString(t) Seq( - kindToString(t) -> relativeLinkTo(t), - "kind" -> kindToString(t), - "members" -> membersToJSON(t.members.filter(!_.isShadowedOrAmbiguousImplicit)), + kind -> relativeLinkTo(t), + "kind" -> kind, + s"members_$kind" -> membersToJSON(t.members.filter(!_.isShadowedOrAmbiguousImplicit), t), "shortDescription" -> shortDesc(t)) } JSONObject(Map(pairs : _*) + ("name" -> key)) - }) + } pack.qualifiedName -> JSONArray(ary) } @@ -83,10 +92,10 @@ class IndexScript(universe: doc.Universe, index: doc.Index) extends Page { } /** Returns the json representation of the supplied members */ - def membersToJSON(entities: List[MemberEntity]): JSONType = - JSONArray(entities map memberToJSON) + def membersToJSON(entities: List[MemberEntity], parent: DocTemplateEntity): JSONType = + JSONArray(entities map (memberToJSON(_, parent))) - private def memberToJSON(mbr: MemberEntity): JSONObject = { + private def memberToJSON(mbr: MemberEntity, parent: DocTemplateEntity): JSONObject = { /** This function takes a member and gets eventual parameters and the * return type. For example, the definition: * {{{ def get(key: A): Option[B] }}} @@ -100,6 +109,7 @@ class IndexScript(universe: doc.Universe, index: doc.Index) extends Page { } .mkString("(", ")(", "): " + d.resultType.name) case v: Val => ": " + v.resultType.name + case _ => "" } /** This function takes a member entity and return all modifiers in a @@ -119,15 +129,15 @@ class IndexScript(universe: doc.Universe, index: doc.Index) extends Page { */ def jsonObject(m: MemberEntity): JSONObject = JSONObject(Map( - "label" -> m.definitionName.replaceAll(".*#", ""), // member name + "label" -> "[^\\.]*\\.([^#]+#)?".r.replaceAllIn(m.definitionName, ""), // member name "member" -> m.definitionName.replaceFirst("#", "."), // full member name "tail" -> memberTail(m), - "kind" -> memberKindToString(m), // modifiers i.e. "abstract def" - "link" -> memberToUrl(m))) // permalink to the member + "kind" -> memberKindToString(m), // modifiers i.e. "abstract def" + "link" -> memberToUrl(m))) // permalink to the member mbr match { - case d: Def => jsonObject(d) - case v: Val => jsonObject(v) + case x @ (_: Def | _: Val | _: Object | _: AliasType) => jsonObject(x) + case e @ (_: Class | _: Trait) if parent.isRootPackage || !parent.isPackage => jsonObject(e) case m: MemberEntity => JSONObject(Map("member" -> m.definitionName, "error" -> "unsupported entity")) } @@ -140,6 +150,5 @@ class IndexScript(universe: doc.Universe, index: doc.Index) extends Page { } object IndexScript { - def apply(universe: doc.Universe, index: doc.Index) = - new IndexScript(universe, index) + def apply(universe: doc.Universe) = new IndexScript(universe) } diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/ReferenceIndex.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/ReferenceIndex.scala deleted file mode 100644 index 6780f17a8c..0000000000 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/ReferenceIndex.scala +++ /dev/null @@ -1,63 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2007-2013 LAMP/EPFL - * @author Pedro Furlanetto - */ - -package scala -package tools -package nsc -package doc -package html -package page - -import doc.model._ - -class ReferenceIndex(letter: Char, index: doc.Index, universe: Universe, rep: ScalaDocReporter) extends HtmlPage { - - def reporter = rep - - def path = List("index-"+letter+".html", "index") - - def title = { - val s = universe.settings - ( if (!s.doctitle.isDefault) s.doctitle.value else "" ) + - ( if (!s.docversion.isDefault) (" " + s.docversion.value) else "" ) - } - - def headers = - <xml:group> - <link href={ relativeLinkTo(List("ref-index.css", "lib")) } media="screen" type="text/css" rel="stylesheet"/> - <script type="text/javascript" src={ relativeLinkTo{List("jquery.js", "lib")} }></script> - </xml:group> - - - private def entry(name: String, methods: Iterable[MemberEntity]) = { - val occurrences = methods.map(method => { - val html = templateToHtml(method.inDefinitionTemplates.head) - if (method.deprecation.isDefined) { - <strike>{ html }</strike> - } else { - html - } - }).toList.distinct - - <div class="entry"> - <div class="name">{ - if (methods.find { ! _.deprecation.isDefined } != None) - name - else - <strike>{ name }</strike> - }</div> - <div class="occurrences">{ - for (owner <- occurrences) yield owner ++ scala.xml.Text(" ") - }</div> - </div> - } - - def body = - <body>{ - for(groups <- index.firstLetterIndex(letter)) yield - entry(groups._1, groups._2.view) - }</body> - -} diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala deleted file mode 100644 index f5e909cf90..0000000000 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala +++ /dev/null @@ -1,987 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2007-2013 LAMP/EPFL - * @author David Bernard, Manohar Jonnalagedda - */ - -package scala -package tools -package nsc -package doc -package html -package page - -import base._ -import base.comment._ - -import scala.collection.mutable -import scala.xml.{NodeSeq, Text, UnprefixedAttribute} -import scala.language.postfixOps - -import model._ -import model.diagram._ -import diagram._ - -class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemplateEntity, rep: ScalaDocReporter) extends HtmlPage { - - def reporter = rep - - val path = templateToPath(tpl) - - def title = { - val s = universe.settings - ( if (!s.doctitle.isDefault) s.doctitle.value + " " else "" ) + - ( if (!s.docversion.isDefault) s.docversion.value else "" ) + - ( if ((!s.doctitle.isDefault || !s.docversion.isDefault) && tpl.qualifiedName != "_root_") " - " + tpl.qualifiedName else "" ) - } - - val headers = - <xml:group> - <link href={ relativeLinkTo{List("template.css", "lib")} } media="screen" type="text/css" rel="stylesheet"/> - <link href={ relativeLinkTo{List("diagrams.css", "lib")} } media="screen" type="text/css" rel="stylesheet" id="diagrams-css" /> - <script type="text/javascript" src={ relativeLinkTo{List("jquery.js", "lib")} } id="jquery-js"></script> - <script type="text/javascript" src={ relativeLinkTo{List("jquery.panzoom.min.js", "lib")} }></script> - <script type="text/javascript" src={ relativeLinkTo{List("jquery.mousewheel.min.js", "lib")} }></script> - <script type="text/javascript" src={ relativeLinkTo{List("template.js", "lib")} }></script> - <script type="text/javascript" src={ relativeLinkTo{List("tools.tooltip.js", "lib")} }></script> - { if (universe.settings.docDiagrams.value) { - <script type="text/javascript" src={ relativeLinkTo{List("modernizr.custom.js", "lib")} }></script> - <script type="text/javascript" src={ relativeLinkTo{List("diagrams.js", "lib")} } id="diagrams-js"></script> - } else NodeSeq.Empty } - <script type="text/javascript"> - if(top === self) {{ - var url = '{ val p = templateToPath(tpl); "../" * (p.size - 1) + "index.html" }'; - var hash = '{ val p = templateToPath(tpl); (p.tail.reverse ::: List(p.head.replace(".html", ""))).mkString(".") }'; - var anchor = window.location.hash; - var anchor_opt = ''; - if (anchor.length { scala.xml.Unparsed(">=") /* unless we use Unparsed, it gets escaped and crashes the script */ } 1) - anchor_opt = '@' + anchor.substring(1); - window.location.href = url + '#' + hash + anchor_opt; - }} - </script> - </xml:group> - - val valueMembers = - tpl.methods ++ tpl.values ++ tpl.templates.filter(x => x.isObject || x.isPackage) sorted - - val (absValueMembers, nonAbsValueMembers) = - valueMembers partition (_.isAbstract) - - val (deprValueMembers, nonDeprValueMembers) = - nonAbsValueMembers partition (_.deprecation.isDefined) - - val (concValueMembers, shadowedImplicitMembers) = - nonDeprValueMembers partition (!_.isShadowedOrAmbiguousImplicit) - - val typeMembers = - tpl.abstractTypes ++ tpl.aliasTypes ++ tpl.templates.filter(x => x.isTrait || x.isClass) sorted (implicitly[Ordering[MemberEntity]]) - - val constructors = (tpl match { - case cls: Class => (cls.constructors: List[MemberEntity]).sorted - case _ => Nil - }) - - /* for body, there is a special case for AnyRef, otherwise AnyRef appears - * like a package/object this problem should be fixed, this implementation - * is just a patch. */ - val body = { - val templateName = if (tpl.isRootPackage) "root package" else tpl.name - val displayName = tpl.companion match { - case Some(companion) if (companion.visibility.isPublic && companion.inSource != None) => - <a href={relativeLinkTo(companion)} title={docEntityKindToCompanionTitle(tpl)}>{ templateName }</a> - case _ => - templateName - } - val owner = { - if (tpl.isRootPackage || tpl.inTemplate.isRootPackage) - NodeSeq.Empty - else - <p id="owner">{ templatesToHtml(tpl.inTemplate.toRoot.reverse.tail, scala.xml.Text(".")) }</p> - } - - <body class={ tpl.kind + (if (tpl.isType) " type" else " value") }> - <div id="definition"> - { - val (src, alt) = docEntityKindToBigImage(tpl) - - val identifier = alt.toString.substring(0,2).toLowerCase - - tpl.companion match { - case Some(companion) if (companion.visibility.isPublic && companion.inSource != None) => - <a href={relativeLinkTo(companion)} title={docEntityKindToCompanionTitle(tpl)}><div class={s"big-circle companion $identifier"}>{ identifier.substring(0,1) }</div></a> - case _ => - <div class={ "big-circle " + alt.toString.toLowerCase }>{ identifier.substring(0,1) }</div> - }} - { owner } - <h1>{ displayName }</h1>{ - if (tpl.isPackage) NodeSeq.Empty else <h3>{companionAndPackage(tpl)}</h3> - }{ permalink(tpl) } - { signature(tpl, isSelf = true) } - </div> - - { memberToCommentHtml(tpl, tpl.inTemplate, isSelf = true) } - - <div id="mbrsel"> - <div class='toggle'></div> - <div id='textfilter'><span class='input'><input id='mbrsel-input' placeholder='Filter members' type='text' accesskey='/'/></span><span class='clear'>✖</span></div> - <div id='filterby'> - { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty && (!universe.settings.docGroups.value || (tpl.members.map(_.group).distinct.length == 1))) - NodeSeq.Empty - else - <div id="order"> - <span class="filtertype">Ordering</span> - <ol> - { - if (!universe.settings.docGroups.value || (tpl.members.map(_.group).distinct.length == 1)) - NodeSeq.Empty - else - <li class="group out"><span>Grouped</span></li> - } - <li class="alpha in"><span>Alphabetic</span></li> - { - if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) - NodeSeq.Empty - else - <li class="inherit out"><span>By Inheritance</span></li> - } - </ol> - </div> - } - { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) NodeSeq.Empty else - { - if (!tpl.linearizationTemplates.isEmpty) - <div class="ancestors"> - <span class="filtertype">Inherited<br/> - </span> - <ol id="linearization"> - { (tpl :: tpl.linearizationTemplates).map(wte => <li class="in" name={ wte.qualifiedName }><span>{ wte.name }</span></li>) } - </ol> - </div> - else NodeSeq.Empty - } ++ { - if (!tpl.conversions.isEmpty) - <div class="ancestors"> - <span class="filtertype">Implicitly<br/> - </span> - <ol id="implicits"> { - tpl.conversions.map { conv => - val name = conv.conversionQualifiedName - val hide = universe.settings.hiddenImplicits(name) - <li class="in" name={ name } data-hidden={ hide.toString }><span>{ "by " + conv.conversionShortName }</span></li> - } - } - </ol> - </div> - else NodeSeq.Empty - } ++ - <div class="ancestors"> - <span class="filtertype"></span> - <ol> - <li class="hideall out"><span>Hide All</span></li> - <li class="showall in"><span>Show All</span></li> - </ol> - </div> - } - { - <div id="visbl"> - <span class="filtertype">Visibility</span> - <ol><li class="public in"><span>Public</span></li><li class="all out"><span>All</span></li></ol> - </div> - } - </div> - </div> - - <div id="template"> - <div id="allMembers"> - { if (constructors.isEmpty) NodeSeq.Empty else - <div id="constructors" class="members"> - <h3>Instance Constructors</h3> - <ol>{ constructors map (memberToHtml(_, tpl)) }</ol> - </div> - } - - { if (typeMembers.isEmpty) NodeSeq.Empty else - <div id="types" class="types members"> - <h3>Type Members</h3> - <ol>{ typeMembers map (memberToHtml(_, tpl)) }</ol> - </div> - } - - { if (absValueMembers.isEmpty) NodeSeq.Empty else - <div class="values members"> - <h3>Abstract Value Members</h3> - <ol>{ absValueMembers map (memberToHtml(_, tpl)) }</ol> - </div> - } - - { if (concValueMembers.isEmpty) NodeSeq.Empty else - <div class="values members"> - <h3>{ if (absValueMembers.isEmpty) "Value Members" else "Concrete Value Members" }</h3> - <ol>{ concValueMembers map (memberToHtml(_, tpl)) }</ol> - </div> - } - - { if (shadowedImplicitMembers.isEmpty) NodeSeq.Empty else - <div class="values members"> - <h3>Shadowed Implicit Value Members</h3> - <ol>{ shadowedImplicitMembers map (memberToHtml(_, tpl)) }</ol> - </div> - } - - { if (deprValueMembers.isEmpty) NodeSeq.Empty else - <div class="values members"> - <h3>Deprecated Value Members</h3> - <ol>{ deprValueMembers map (memberToHtml(_, tpl)) }</ol> - </div> - } - </div> - - <div id="inheritedMembers"> - { - // linearization - NodeSeq fromSeq (for ((superTpl, superType) <- (tpl.linearizationTemplates zip tpl.linearizationTypes)) yield - <div class="parent" name={ superTpl.qualifiedName }> - <h3>Inherited from { - typeToHtmlWithStupidTypes(tpl, superTpl, superType) - }</h3> - </div> - ) - } - { - // implicitly inherited - NodeSeq fromSeq (for (conversion <- (tpl.conversions)) yield - <div class="conversion" name={ conversion.conversionQualifiedName }> - <h3>Inherited by implicit conversion { conversion.conversionShortName } from - { typeToHtml(tpl.resultType, hasLinks = true) } to { typeToHtml(conversion.targetType, hasLinks = true) } - </h3> - </div> - ) - } - </div> - - <div id="groupedMembers"> - { - val allGroups = tpl.members.map(_.group).distinct - val orderedGroups = allGroups.map(group => (tpl.groupPriority(group), group)).sorted.map(_._2) - // linearization - NodeSeq fromSeq (for (group <- orderedGroups) yield - <div class="group" name={ group }> - <h3>{ tpl.groupName(group) }</h3> - { - tpl.groupDescription(group) match { - case Some(body) => <div class="comment cmt">{ bodyToHtml(body) }</div> - case _ => NodeSeq.Empty - } - } - </div> - ) - } - </div> - - </div> - - <div id="tooltip" ></div> - - { - if (Set("epfl", "EPFL").contains(tpl.universe.settings.docfooter.value)) - <div id="footer">Scala programming documentation. Copyright (c) 2003-2016 <a href="http://www.epfl.ch" target="_top">EPFL</a>, with contributions from <a href="http://typesafe.com" target="_top">Typesafe</a>.</div> - else - <div id="footer"> { tpl.universe.settings.docfooter.value } </div> - } - </body> - } - - def memberToHtml(mbr: MemberEntity, inTpl: DocTemplateEntity): NodeSeq = { - // Sometimes it's same, do we need signatureCompat still? - val sig = if (mbr.signature == mbr.signatureCompat) { - <a id={ mbr.signature }/> - } else { - <a id={ mbr.signature }/><a id={ mbr.signatureCompat }/> - } - - val memberComment = memberToCommentHtml(mbr, inTpl, isSelf = false) - <li name={ mbr.definitionName } visbl={ if (mbr.visibility.isProtected) "prt" else "pub" } - data-isabs={ mbr.isAbstract.toString } - fullComment={ if(memberComment.filter(_.label=="div").isEmpty) "no" else "yes" } - group={ mbr.group }> - { sig } - { signature(mbr, isSelf = false) } - { memberComment } - </li> - } - - def memberToCommentHtml(mbr: MemberEntity, inTpl: DocTemplateEntity, isSelf: Boolean): NodeSeq = { - mbr match { - case dte: DocTemplateEntity if isSelf => - // comment of class itself - <xml:group> - <div id="comment" class="fullcommenttop">{ memberToCommentBodyHtml(mbr, inTpl, isSelf = true) }</div> - </xml:group> - case _ => - // comment of non-class member or non-documentented inner class - val commentBody = memberToCommentBodyHtml(mbr, inTpl, isSelf = false) - if (commentBody.isEmpty) - NodeSeq.Empty - else { - val shortComment = memberToShortCommentHtml(mbr, isSelf) - val longComment = memberToUseCaseCommentHtml(mbr, isSelf) ++ memberToCommentBodyHtml(mbr, inTpl, isSelf) - - val includedLongComment = if (shortComment.text.trim == longComment.text.trim) - NodeSeq.Empty - else - <div class="fullcomment">{ longComment }</div> - - shortComment ++ includedLongComment - } - } - } - - def memberToUseCaseCommentHtml(mbr: MemberEntity, isSelf: Boolean): NodeSeq = { - mbr match { - case nte: NonTemplateMemberEntity if nte.isUseCase => - inlineToHtml(comment.Text("[use case] ")) - case _ => NodeSeq.Empty - } - } - - def memberToShortCommentHtml(mbr: MemberEntity, isSelf: Boolean): NodeSeq = - mbr.comment.fold(NodeSeq.Empty) { comment => - <p class="shortcomment cmt">{ memberToUseCaseCommentHtml(mbr, isSelf) }{ inlineToHtml(comment.short) }</p> - } - - def memberToInlineCommentHtml(mbr: MemberEntity, isSelf: Boolean): NodeSeq = - <p class="comment cmt">{ inlineToHtml(mbr.comment.get.short) }</p> - - def memberToCommentBodyHtml(mbr: MemberEntity, inTpl: DocTemplateEntity, isSelf: Boolean, isReduced: Boolean = false): NodeSeq = { - val s = universe.settings - - val memberComment = - if (mbr.comment.isEmpty) NodeSeq.Empty - else <div class="comment cmt">{ commentToHtml(mbr.comment) }</div> - - val authorComment = - if (! s.docAuthor || mbr.comment.isEmpty || - mbr.comment.isDefined && mbr.comment.get.authors.isEmpty) NodeSeq.Empty - else <div class="comment cmt"> - {if (mbr.comment.get.authors.size > 1) <h6>Authors:</h6> else <h6>Author:</h6>} - { mbr.comment.get.authors map bodyToHtml} - </div> - - val paramComments = { - val prs: List[ParameterEntity] = mbr match { - case cls: Class => cls.typeParams ::: cls.valueParams.flatten - case trt: Trait => trt.typeParams - case dfe: Def => dfe.typeParams ::: dfe.valueParams.flatten - case ctr: Constructor => ctr.valueParams.flatten - case _ => Nil - } - - def paramCommentToHtml(prs: List[ParameterEntity], comment: Comment): NodeSeq = prs match { - - case (tp: TypeParam) :: rest => - val paramEntry: NodeSeq = { - <dt class="tparam">{ tp.name }</dt><dd class="cmt">{ bodyToHtml(comment.typeParams(tp.name)) }</dd> - } - paramEntry ++ paramCommentToHtml(rest, comment) - - case (vp: ValueParam) :: rest => - val paramEntry: NodeSeq = { - <dt class="param">{ vp.name }</dt><dd class="cmt">{ bodyToHtml(comment.valueParams(vp.name)) }</dd> - } - paramEntry ++ paramCommentToHtml(rest, comment) - - case _ => - NodeSeq.Empty - } - - mbr.comment.fold(NodeSeq.Empty) { comment => - val cmtedPrs = prs filter { - case tp: TypeParam => comment.typeParams isDefinedAt tp.name - case vp: ValueParam => comment.valueParams isDefinedAt vp.name - } - if (cmtedPrs.isEmpty && comment.result.isEmpty) NodeSeq.Empty - else { - <dl class="paramcmts block">{ - paramCommentToHtml(cmtedPrs, comment) ++ ( - comment.result match { - case None => NodeSeq.Empty - case Some(cmt) => - <dt>returns</dt><dd class="cmt">{ bodyToHtml(cmt) }</dd> - }) - }</dl> - } - } - } - - val implicitInformation = mbr.byConversion match { - case Some(conv) => - <dt class="implicit">Implicit information</dt> ++ - { - val targetType = typeToHtml(conv.targetType, hasLinks = true) - val conversionMethod = conv.convertorMethod match { - case Left(member) => Text(member.name) - case Right(name) => Text(name) - } - - // strip off the package object endings, they make things harder to follow - val conversionOwnerQualifiedNane = conv.convertorOwner.qualifiedName.stripSuffix(".package") - val conversionOwner = templateToHtml(conv.convertorOwner, conversionOwnerQualifiedNane) - - val constraintText = conv.constraints match { - case Nil => - NodeSeq.Empty - case List(constraint) => - scala.xml.Text("This conversion will take place only if ") ++ constraintToHtml(constraint) ++ scala.xml.Text(".") - case List(constraint1, constraint2) => - scala.xml.Text("This conversion will take place only if ") ++ constraintToHtml(constraint1) ++ - scala.xml.Text(" and at the same time ") ++ constraintToHtml(constraint2) ++ scala.xml.Text(".") - case constraints => - <br/> ++ "This conversion will take place only if all of the following constraints are met:" ++ <br/> ++ { - var index = 0 - constraints map { constraint => scala.xml.Text({ index += 1; index } + ". ") ++ constraintToHtml(constraint) ++ <br/> } - } - } - - <dd> - This member is added by an implicit conversion from { typeToHtml(inTpl.resultType, hasLinks = true) } to - { targetType } performed by method { conversionMethod } in { conversionOwner }. - { constraintText } - </dd> - } ++ { - if (mbr.isShadowedOrAmbiguousImplicit) { - // These are the members that are shadowing or ambiguating the current implicit - // see ImplicitMemberShadowing trait for more information - val shadowingSuggestion = { - val params = mbr match { - case d: Def => d.valueParams map (_ map (_ name) mkString("(", ", ", ")")) mkString - case _ => "" // no parameters - } - <br/> ++ scala.xml.Text("To access this member you can use a ") ++ - <a href="http://stackoverflow.com/questions/2087250/what-is-the-purpose-of-type-ascription-in-scala" - target="_blank">type ascription</a> ++ scala.xml.Text(":") ++ - <br/> ++ <div class="cmt"><pre>{"(" + Template.lowerFirstLetter(tpl.name) + ": " + conv.targetType.name + ")." + mbr.name + params }</pre></div> - } - - val shadowingWarning: NodeSeq = - if (mbr.isShadowedImplicit) - scala.xml.Text("This implicitly inherited member is shadowed by one or more members in this " + - "class.") ++ shadowingSuggestion - else if (mbr.isAmbiguousImplicit) - scala.xml.Text("This implicitly inherited member is ambiguous. One or more implicitly " + - "inherited members have similar signatures, so calling this member may produce an ambiguous " + - "implicit conversion compiler error.") ++ shadowingSuggestion - else NodeSeq.Empty - - <dt class="implicit">Shadowing</dt> ++ - <dd>{ shadowingWarning }</dd> - - } else NodeSeq.Empty - } - case _ => - NodeSeq.Empty - } - - // --- start attributes block vals - val attributes: NodeSeq = { - val fvs: List[comment.Paragraph] = visibility(mbr).toList - if (fvs.isEmpty || isReduced) NodeSeq.Empty - else { - <dt>Attributes</dt> - <dd>{ fvs map { fv => { inlineToHtml(fv.text) ++ scala.xml.Text(" ") } } }</dd> - } - } - - val definitionClasses: NodeSeq = { - val inDefTpls = mbr.inDefinitionTemplates - if ((inDefTpls.tail.isEmpty && (inDefTpls.head == inTpl)) || isReduced) NodeSeq.Empty - else { - <dt>Definition Classes</dt> - <dd>{ templatesToHtml(inDefTpls, scala.xml.Text(" → ")) }</dd> - } - } - - val fullSignature: NodeSeq = { - mbr match { - case nte: NonTemplateMemberEntity if nte.isUseCase => - <div class="full-signature-block toggleContainer"> - <span class="toggle">Full Signature</span> - <div class="hiddenContent full-signature-usecase">{ signature(nte.useCaseOf.get,isSelf = true) }</div> - </div> - case _ => NodeSeq.Empty - } - } - - val selfType: NodeSeq = mbr match { - case dtpl: DocTemplateEntity if (isSelf && !dtpl.selfType.isEmpty && !isReduced) => - <dt>Self Type</dt> - <dd>{ typeToHtml(dtpl.selfType.get, hasLinks = true) }</dd> - case _ => NodeSeq.Empty - } - - val annotations: NodeSeq = { - // A list of annotations which don't show their arguments, e. g. because they are shown separately. - val annotationsWithHiddenArguments = List("deprecated", "Deprecated", "migration") - - def showArguments(annotation: Annotation) = - !(annotationsWithHiddenArguments.contains(annotation.qualifiedName)) - - if (!mbr.annotations.isEmpty) { - <dt>Annotations</dt> - <dd>{ - mbr.annotations.map { annot => - <xml:group> - <span class="name">@{ templateToHtml(annot.annotationClass) }</span>{ - if (showArguments(annot)) argumentsToHtml(annot.arguments) else NodeSeq.Empty - } - </xml:group> - } - } - </dd> - } else NodeSeq.Empty - } - - val sourceLink: NodeSeq = mbr match { - case dtpl: DocTemplateEntity if (isSelf && dtpl.sourceUrl.isDefined && dtpl.inSource.isDefined && !isReduced) => - val (absFile, _) = dtpl.inSource.get - <dt>Source</dt> - <dd>{ <a href={ dtpl.sourceUrl.get.toString } target="_blank">{ Text(absFile.file.getName) }</a> }</dd> - case _ => NodeSeq.Empty - } - - val deprecation: NodeSeq = - mbr.deprecation match { - case Some(deprecation) if !isReduced => - <dt>Deprecated</dt> - <dd class="cmt">{ bodyToHtml(deprecation) }</dd> - case _ => NodeSeq.Empty - } - - val migration: NodeSeq = - mbr.migration match { - case Some(migration) if !isReduced => - <dt>Migration</dt> - <dd class="cmt">{ bodyToHtml(migration) }</dd> - case _ => NodeSeq.Empty - } - - val mainComment: NodeSeq = mbr.comment match { - case Some(comment) if (! isReduced) => - def orEmpty[T](it: Iterable[T])(gen: =>NodeSeq): NodeSeq = - if (it.isEmpty) NodeSeq.Empty else gen - - val example = - orEmpty(comment.example) { - <div class="block">Example{ if (comment.example.length > 1) "s" else ""}: - <ol>{ - val exampleXml: List[NodeSeq] = for (ex <- comment.example) yield - <li class="cmt">{ bodyToHtml(ex) }</li> - exampleXml.reduceLeft(_ ++ Text(", ") ++ _) - }</ol> - </div> - } - - val version: NodeSeq = - orEmpty(comment.version) { - <dt>Version</dt> - <dd>{ for(body <- comment.version.toList) yield bodyToHtml(body) }</dd> - } - - val sinceVersion: NodeSeq = - orEmpty(comment.since) { - <dt>Since</dt> - <dd>{ for(body <- comment.since.toList) yield bodyToHtml(body) }</dd> - } - - val note: NodeSeq = - orEmpty(comment.note) { - <dt>Note</dt> - <dd>{ - val noteXml: List[NodeSeq] = for(note <- comment.note ) yield <span class="cmt">{bodyToHtml(note)}</span> - noteXml.reduceLeft(_ ++ Text(", ") ++ _) - }</dd> - } - - val seeAlso: NodeSeq = - orEmpty(comment.see) { - <dt>See also</dt> - <dd>{ - val seeXml: List[NodeSeq] = for(see <- comment.see ) yield <span class="cmt">{bodyToHtml(see)}</span> - seeXml.reduceLeft(_ ++ _) - }</dd> - } - - val exceptions: NodeSeq = - orEmpty(comment.throws) { - <dt>Exceptions thrown</dt> - <dd>{ - val exceptionsXml: List[NodeSeq] = - for((name, body) <- comment.throws.toList.sortBy(_._1) ) yield - <span class="cmt">{bodyToHtml(body)}</span> - exceptionsXml.reduceLeft(_ ++ Text("") ++ _) - }</dd> - } - - val todo: NodeSeq = - orEmpty(comment.todo) { - <dt>To do</dt> - <dd>{ - val todoXml: List[NodeSeq] = (for(todo <- comment.todo ) yield <span class="cmt">{bodyToHtml(todo)}</span> ) - todoXml.reduceLeft(_ ++ _) - }</dd> - } - - example ++ version ++ sinceVersion ++ exceptions ++ todo ++ note ++ seeAlso - - case _ => NodeSeq.Empty - } - // end attributes block vals --- - - val attributesInfo = implicitInformation ++ attributes ++ definitionClasses ++ fullSignature ++ selfType ++ annotations ++ deprecation ++ migration ++ sourceLink ++ mainComment - val attributesBlock = - if (attributesInfo.isEmpty) - NodeSeq.Empty - else - <dl class="attributes block"> { attributesInfo }</dl> - - val linearization = mbr match { - case dtpl: DocTemplateEntity if isSelf && !isReduced && dtpl.linearizationTemplates.nonEmpty => - <div class="toggleContainer block"> - <span class="toggle">Linear Supertypes</span> - <div class="superTypes hiddenContent">{ - typesToHtml(dtpl.linearizationTypes, hasLinks = true, sep = scala.xml.Text(", ")) - }</div> - </div> - case _ => NodeSeq.Empty - } - - val subclasses = mbr match { - case dtpl: DocTemplateEntity if isSelf && !isReduced => - val subs = mutable.HashSet.empty[DocTemplateEntity] - def transitive(dtpl: DocTemplateEntity) { - for (sub <- dtpl.directSubClasses if !(subs contains sub)) { - subs add sub - transitive(sub) - } - } - transitive(dtpl) - if (subs.nonEmpty) - <div class="toggleContainer block"> - <span class="toggle">Known Subclasses</span> - <div class="subClasses hiddenContent">{ - templatesToHtml(subs.toList.sorted(Entity.EntityOrdering), scala.xml.Text(", ")) - }</div> - </div> - else NodeSeq.Empty - case _ => NodeSeq.Empty - } - - def createDiagram(f: DocTemplateEntity => Option[Diagram], description: String, id: String): NodeSeq = - if (s.docDiagrams.value) mbr match { - case dtpl: DocTemplateEntity if isSelf && !isReduced => - val diagram = f(dtpl) - if (diagram.isDefined) { - val diagramSvg = generator.generate(diagram.get, tpl, this) - if (diagramSvg != NodeSeq.Empty) { - <div class="toggleContainer block diagram-container" id={ id + "-container"}> - <span class="toggle diagram-link">{ description }</span> - <div class="diagram" id={ id }>{ diagramSvg }</div> - <div id="diagram-controls" class="hiddenContent"> - <button id="diagram-zoom-out" class="diagram-btn"><i class="material-icons"></i></button> - <button id="diagram-zoom-in" class="diagram-btn"><i class="material-icons"></i></button> - <button title="Toggle full-screen" id="diagram-fs" class="diagram-btn to-full"><i class="material-icons"></i></button> - </div> - </div> - } else NodeSeq.Empty - } else NodeSeq.Empty - case _ => NodeSeq.Empty - } else NodeSeq.Empty // diagrams not generated - - val typeHierarchy = createDiagram(_.inheritanceDiagram, "Type Hierarchy", "inheritance-diagram") - val contentHierarchy = createDiagram(_.contentDiagram, "Content Hierarchy", "content-diagram") - - memberComment ++ authorComment ++ paramComments ++ attributesBlock ++ linearization ++ subclasses ++ typeHierarchy ++ contentHierarchy - } - - def boundsToHtml(hi: Option[TypeEntity], lo: Option[TypeEntity], hasLinks: Boolean): NodeSeq = { - def bound0(bnd: Option[TypeEntity], pre: String): NodeSeq = bnd match { - case None => NodeSeq.Empty - case Some(tpe) => scala.xml.Text(pre) ++ typeToHtml(tpe, hasLinks) - } - bound0(lo, " >: ") ++ bound0(hi, " <: ") - } - - def visibility(mbr: MemberEntity): Option[comment.Paragraph] = { - import comment._ - import comment.{ Text => CText } - mbr.visibility match { - case PrivateInInstance() => - Some(Paragraph(CText("private[this]"))) - case PrivateInTemplate(owner) if (owner == mbr.inTemplate) => - Some(Paragraph(CText("private"))) - case PrivateInTemplate(owner) => - Some(Paragraph(Chain(List(CText("private["), EntityLink(comment.Text(owner.qualifiedName), LinkToTpl(owner)), CText("]"))))) - case ProtectedInInstance() => - Some(Paragraph(CText("protected[this]"))) - case ProtectedInTemplate(owner) if (owner == mbr.inTemplate) => - Some(Paragraph(CText("protected"))) - case ProtectedInTemplate(owner) => - Some(Paragraph(Chain(List(CText("protected["), EntityLink(comment.Text(owner.qualifiedName), LinkToTpl(owner)), CText("]"))))) - case Public() => - None - } - } - - /** name, tparams, params, result */ - def signature(mbr: MemberEntity, isSelf: Boolean, isReduced: Boolean = false): NodeSeq = { - - def inside(hasLinks: Boolean, nameLink: String = ""): NodeSeq = - <xml:group> - <span class="modifier_kind"> - <span class="modifier">{ mbr.flags.map(flag => inlineToHtml(flag.text) ++ scala.xml.Text(" ")) }</span> - <span class="kind">{ kindToString(mbr) }</span> - </span> - <span class="symbol"> - { - val nameClass = - if (mbr.isImplicitlyInherited) - if (mbr.isShadowedOrAmbiguousImplicit) - "implicit shadowed" - else - "implicit" - else - "name" - - val nameHtml = { - val value = if (mbr.isConstructor) tpl.name else mbr.name - val span = if (mbr.deprecation.isDefined) - <span class={ nameClass + " deprecated"} title={"Deprecated: "+bodyToStr(mbr.deprecation.get)}>{ value }</span> - else - <span class={ nameClass }>{ value }</span> - val encoded = scala.reflect.NameTransformer.encode(value) - if (encoded != value) { - span % new UnprefixedAttribute("title", - "gt4s: " + encoded + - span.attribute("title").map( - node => ". " + node - ).getOrElse(""), - scala.xml.Null) - } else { - span - } - } - if (!nameLink.isEmpty) - <a href={nameLink}>{nameHtml}</a> - else nameHtml - }{ - def tparamsToHtml(mbr: Any): NodeSeq = mbr match { - case hk: HigherKinded => - val tpss = hk.typeParams - if (tpss.isEmpty) NodeSeq.Empty else { - def tparam0(tp: TypeParam): NodeSeq = - <span name={ tp.name }>{ tp.variance + tp.name }{ tparamsToHtml(tp) }{ boundsToHtml(tp.hi, tp.lo, hasLinks)}</span> - def tparams0(tpss: List[TypeParam]): NodeSeq = (tpss: @unchecked) match { - case tp :: Nil => tparam0(tp) - case tp :: tps => tparam0(tp) ++ Text(", ") ++ tparams0(tps) - } - <span class="tparams">[{ tparams0(tpss) }]</span> - } - case _ => NodeSeq.Empty - } - tparamsToHtml(mbr) - }{ - if (isReduced) NodeSeq.Empty else { - def paramsToHtml(vlsss: List[List[ValueParam]]): NodeSeq = { - def param0(vl: ValueParam): NodeSeq = - // notice the }{ in the next lines, they are necessary to avoid an undesired whitespace in output - <span name={ vl.name }>{ - Text(vl.name) - }{ Text(": ") ++ typeToHtml(vl.resultType, hasLinks) }{ - vl.defaultValue match { - case Some(v) => Text(" = ") ++ treeToHtml(v) - case None => NodeSeq.Empty - } - }</span> - - def params0(vlss: List[ValueParam]): NodeSeq = vlss match { - case Nil => NodeSeq.Empty - case vl :: Nil => param0(vl) - case vl :: vls => param0(vl) ++ Text(", ") ++ params0(vls) - } - def implicitCheck(vlss: List[ValueParam]): NodeSeq = vlss match { - case vl :: vls => if(vl.isImplicit) { <span class="implicit">implicit </span> } else Text("") - case _ => Text("") - } - vlsss map { vlss => <span class="params">({implicitCheck(vlss) ++ params0(vlss) })</span> } - } - mbr match { - case cls: Class => paramsToHtml(cls.valueParams) - case ctr: Constructor => paramsToHtml(ctr.valueParams) - case dfe: Def => paramsToHtml(dfe.valueParams) - case _ => NodeSeq.Empty - } - } - }{ if (isReduced) NodeSeq.Empty else { - mbr match { - case tme: MemberEntity if (tme.isDef || tme.isVal || tme.isLazyVal || tme.isVar) => - <span class="result">: { typeToHtml(tme.resultType, hasLinks) }</span> - - case abt: MemberEntity with AbstractType => - val b2s = boundsToHtml(abt.hi, abt.lo, hasLinks) - if (b2s != NodeSeq.Empty) - <span class="result">{ b2s }</span> - else NodeSeq.Empty - - case alt: MemberEntity with AliasType => - <span class="result"> = { typeToHtml(alt.alias, hasLinks) }</span> - - case tpl: MemberTemplateEntity if !tpl.parentTypes.isEmpty => - <span class="result"> extends { typeToHtml(tpl.parentTypes.map(_._2), hasLinks) }</span> - - case _ => NodeSeq.Empty - } - }} - </span> - </xml:group> - mbr match { - case dte: DocTemplateEntity if !isSelf => - <h4 class="signature">{ inside(hasLinks = true, nameLink = relativeLinkTo(dte)) }</h4> ++ permalink(dte, isSelf) - case _ if isSelf => - <h4 id="signature" class="signature">{ inside(hasLinks = true) }</h4> - case _ => - <h4 class="signature">{ inside(hasLinks = true) }</h4> ++ permalink(mbr) - } - - } - - /** */ - def treeToHtml(tree: TreeEntity): NodeSeq = { - - /** Makes text good looking in the html page : newlines and basic indentation, - * You must change this function if you want to improve pretty printing of default Values - */ - def codeStringToXml(text: String): NodeSeq = { - var goodLookingXml: NodeSeq = NodeSeq.Empty - var indent = 0 - for (c <- text) c match { - case '{' => indent+=1 - goodLookingXml ++= Text("{") - case '}' => indent-=1 - goodLookingXml ++= Text("}") - case '\n' => - goodLookingXml++= <br/> ++ indentation - case _ => goodLookingXml ++= Text(c.toString) - } - def indentation:NodeSeq = { - var indentXml = NodeSeq.Empty - for (x <- 1 to indent) indentXml ++= Text(" ") - indentXml - } - goodLookingXml - } - - var index = 0 - val str = tree.expression - val length = str.length - var myXml: NodeSeq = NodeSeq.Empty - for ((from, (member, to)) <- tree.refEntity.toSeq) { - if (index < from) { - myXml ++= codeStringToXml(str.substring(index,from)) - index = from - } - if (index == from) { - member match { - case mbr: DocTemplateEntity => - val link = relativeLinkTo(mbr) - myXml ++= <span class="name"><a href={link}>{str.substring(from, to)}</a></span> - case mbr: MemberEntity => - val anchor = "#" + mbr.signature - val link = relativeLinkTo(mbr.inTemplate) - myXml ++= <span class="name"><a href={link ++ anchor}>{str.substring(from, to)}</a></span> - } - index = to - } - } - - if (index <= length-1) - myXml ++= codeStringToXml(str.substring(index, length )) - - if (length < 36) - <span class="symbol">{ myXml }</span> - else - <span class="defval" name={ myXml }>{ "..." }</span> - } - - private def argumentsToHtml(argss: List[ValueArgument]): NodeSeq = { - def argumentsToHtml0(argss: List[ValueArgument]): NodeSeq = argss match { - case Nil => NodeSeq.Empty - case arg :: Nil => argumentToHtml(arg) - case arg :: args => argumentToHtml(arg) ++ scala.xml.Text(", ") ++ argumentsToHtml0(args) - } - <span class="args">({ argumentsToHtml0(argss) })</span> - } - - private def argumentToHtml(arg: ValueArgument): NodeSeq = { - <span> - { - arg.parameter match { - case Some(param) => Text(param.name + " = ") - case None => NodeSeq.Empty - } - } - { treeToHtml(arg.value) } - </span> - } - - private def bodyToStr(body: comment.Body): String = - body.blocks flatMap (blockToStr(_)) mkString "" - - private def blockToStr(block: comment.Block): String = block match { - case comment.Paragraph(in) => inlineToStr(in) - case _ => block.toString - } - - private def typeToHtmlWithStupidTypes(tpl: TemplateEntity, superTpl: TemplateEntity, superType: TypeEntity): NodeSeq = - if (tpl.universe.settings.useStupidTypes.value) - superTpl match { - case dtpl: DocTemplateEntity => - val sig = signature(dtpl, isSelf = false, isReduced = true) \ "_" - sig - case tpl: TemplateEntity => - Text(tpl.name) - } - else - typeToHtml(superType, hasLinks = true) - - private def constraintToHtml(constraint: Constraint): NodeSeq = constraint match { - case ktcc: KnownTypeClassConstraint => - scala.xml.Text(ktcc.typeExplanation(ktcc.typeParamName) + " (" + ktcc.typeParamName + ": ") ++ - templateToHtml(ktcc.typeClassEntity) ++ scala.xml.Text(")") - case tcc: TypeClassConstraint => - scala.xml.Text(tcc.typeParamName + " is ") ++ - <a href="http://stackoverflow.com/questions/2982276/what-is-a-context-bound-in-scala" target="_blank"> - context-bounded</a> ++ scala.xml.Text(" by " + tcc.typeClassEntity.qualifiedName + " (" + tcc.typeParamName + ": ") ++ - templateToHtml(tcc.typeClassEntity) ++ scala.xml.Text(")") - case impl: ImplicitInScopeConstraint => - scala.xml.Text("an implicit value of type ") ++ typeToHtml(impl.implicitType, hasLinks = true) ++ scala.xml.Text(" is in scope") - case eq: EqualTypeParamConstraint => - scala.xml.Text(eq.typeParamName + " is " + eq.rhs.name + " (" + eq.typeParamName + " =:= ") ++ - typeToHtml(eq.rhs, hasLinks = true) ++ scala.xml.Text(")") - case bt: BoundedTypeParamConstraint => - scala.xml.Text(bt.typeParamName + " is a superclass of " + bt.lowerBound.name + " and a subclass of " + - bt.upperBound.name + " (" + bt.typeParamName + " >: ") ++ - typeToHtml(bt.lowerBound, hasLinks = true) ++ scala.xml.Text(" <: ") ++ - typeToHtml(bt.upperBound, hasLinks = true) ++ scala.xml.Text(")") - case lb: LowerBoundedTypeParamConstraint => - scala.xml.Text(lb.typeParamName + " is a superclass of " + lb.lowerBound.name + " (" + lb.typeParamName + " >: ") ++ - typeToHtml(lb.lowerBound, hasLinks = true) ++ scala.xml.Text(")") - case ub: UpperBoundedTypeParamConstraint => - scala.xml.Text(ub.typeParamName + " is a subclass of " + ub.upperBound.name + " (" + ub.typeParamName + " <: ") ++ - typeToHtml(ub.upperBound, hasLinks = true) ++ scala.xml.Text(")") - } -} - -object Template { - /* Vlad: Lesson learned the hard way: don't put any stateful code that references the model here, - * it won't be garbage collected and you'll end up filling the heap with garbage */ - - def lowerFirstLetter(s: String) = if (s.length >= 1) s.substring(0,1).toLowerCase() + s.substring(1) else s -} diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/diagrams.css b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/diagrams.css index 8c20810784..08add0efa1 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/diagrams.css +++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/diagrams.css @@ -33,6 +33,10 @@ display: none; } +.diagram-container > span.toggle { + z-index: 9; +} + .diagram { overflow: hidden; padding-top:15px; @@ -73,7 +77,7 @@ z-index: 2; } -#inheritance-diagram-container.full-screen { +.diagram-container.full-screen { position: fixed !important; margin: 0; border-radius: 0; @@ -85,11 +89,11 @@ z-index: 10000; } -#inheritance-diagram-container.full-screen > span.toggle { +.diagram-container.full-screen > span.toggle { display: none; } -#inheritance-diagram-container.full-screen > div.diagram { +.diagram-container.full-screen > div.diagram { position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto; diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/diagrams.js b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/diagrams.js index 5d139c1080..b13732760a 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/diagrams.js +++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/diagrams.js @@ -60,8 +60,8 @@ $(document).ready(function() diagrams.initHighlighting(); $("button#diagram-fs").click(function() { - $("#inheritance-diagram-container").toggleClass("full-screen"); - $("#inheritance-diagram-container > div.diagram").css({ + $(".diagram-container").toggleClass("full-screen"); + $(".diagram-container > div.diagram").css({ height: $("svg").height() + "pt" }); @@ -155,7 +155,7 @@ diagrams.initHighlighting = function() */ diagrams.resize = function() { // available width - var availableWidth = $("body").width() - 100; + var availableWidth = $(".diagram-container").width(); $(".diagram-container").each(function() { // unregister click event on whole div @@ -163,7 +163,7 @@ diagrams.resize = function() { var diagramWidth = $(".diagram", this).data("width"); var diagramHeight = $(".diagram", this).data("height"); - if(diagramWidth > availableWidth) { + if (diagramWidth > availableWidth) { // resize diagram var height = diagramHeight / diagramWidth * availableWidth; $(".diagram svg", this).width(availableWidth); @@ -204,7 +204,7 @@ diagrams.toggle = function(container, dontAnimate) $("#diagram-controls", container).show(); - $("#inheritance-diagram-container").on('mousewheel.focal', function(e) { + $(".diagram-container").on('mousewheel.focal', function(e) { e.preventDefault(); var delta = e.delta || e.originalEvent.wheelDelta; var zoomOut = delta ? delta < 0 : e.originalEvent.deltaY > 0; diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.css b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.css index ea1c358149..d805ccc1ac 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.css +++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.css @@ -393,7 +393,7 @@ div#search-progress > div#progress-fill { color: #bbb; } -div#content-container { +div#content-scroll-container { position: absolute; top: 0; right: 0; @@ -404,12 +404,136 @@ div#content-container { overflow-y: auto; } +div#content-container { + max-width: 1140px; + margin: 0 auto; +} + div#content-container > div#content { -webkit-overflow-scrolling: touch; display: block; - overflow-y: auto; + overflow-y: hidden; max-width: 1140px; - margin: 5em auto 0; + margin: 4em auto 0; +} + +div#content-container > div#subpackage-spacer { + float: right; + height: 100%; + margin: 1.1rem 0.5rem 0 0.5em; + font-size: 0.8em; + min-width: 8rem; +} + +div#packages > h1 { + color: #103a51; +} + +div#packages > ul { + list-style-type: none; +} + +div#packages > ul > li { + position: relative; + margin: 0.5rem 0; + width: 100%; + border-radius: 0.2em; + min-height: 1.5em; + padding-left: 2em; +} + +div#packages > ul > li.current-entities { + margin: 0.3rem 0; +} + +div#packages > ul > li.current:hover { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + cursor: pointer; +} + +div#packages > ul > li.current-entities > *:nth-child(1), +div#packages > ul > li.current-entities > *:nth-child(2) { + float: left; + display: inline; + height: 1rem; + width: 1rem; + margin: 1px 0 0 0; + cursor: pointer; +} + +div#packages > ul > li > a.class { + background: url("class.svg") no-repeat center; + background-size: 0.9rem; +} + +div#packages > ul > li > a.trait { + background: url("trait.svg") no-repeat center; + background-size: 0.9rem; +} + +div#packages > ul > li > a.object { + background: url("object.svg") no-repeat center; + background-size: 0.9rem; +} + +div#packages > ul > li > a.abstract.type { + background: url("abstract_type.svg") no-repeat center; + background-size: 0.9rem; +} + +div#packages > ul > li > a { + text-decoration: none !important; + margin-left: 1px; + font-family: "Source Code Pro", "Monaco", "Ubuntu Mono Regular", "Lucida Console", monospace; + font-size: 0.9em; +} + +/* Indentation levels for packages */ +div#packages > ul > li.indented0 { padding-left: 0em; } +div#packages > ul > li.indented1 { padding-left: 1em; } +div#packages > ul > li.indented2 { padding-left: 2em; } +div#packages > ul > li.indented3 { padding-left: 3em; } +div#packages > ul > li.indented4 { padding-left: 4em; } +div#packages > ul > li.indented5 { padding-left: 5em; } +div#packages > ul > li.indented6 { padding-left: 6em; } +div#packages > ul > li.indented7 { padding-left: 7em; } +div#packages > ul > li.indented8 { padding-left: 8em; } +div#packages > ul > li.indented9 { padding-left: 9em; } +div#packages > ul > li.indented10 { padding-left: 10em; } +div#packages > ul > li.current.indented0 { padding-left: -0.5em } +div#packages > ul > li.current.indented1 { padding-left: 0.5em } +div#packages > ul > li.current.indented2 { padding-left: 1.5em } +div#packages > ul > li.current.indented3 { padding-left: 2.5em } +div#packages > ul > li.current.indented4 { padding-left: 3.5em } +div#packages > ul > li.current.indented5 { padding-left: 4.5em } +div#packages > ul > li.current.indented6 { padding-left: 5.5em } +div#packages > ul > li.current.indented7 { padding-left: 6.5em } +div#packages > ul > li.current.indented8 { padding-left: 7.5em } +div#packages > ul > li.current.indented9 { padding-left: 8.5em } +div#packages > ul > li.current.indented10 { padding-left: 9.5em } + +div#packages > ul > li.current > span.symbol { + border-left: 0.25em solid #72D0EB; + padding-left: 0.25em; +} + +div#packages > ul > li > span.symbol > a { + text-decoration: none; +} + +div#packages > ul > li > span.symbol > span.name { + font-weight: normal; +} + +div#packages > ul > li .fullcomment, +div#packages > ul > li .modifier_kind, +div#packages > ul > li .permalink, +div#packages > ul > li .shortcomment { + display: none; } div#search-results { @@ -554,7 +678,7 @@ div#results-content > div#member-results > ul.entities > li > ul.members > li > div#results-content > div#entity-results > ul.entities > li > ul.members > li > span.kind, div#results-content > div#entity-results > ul.entities > li > ul.members > li > span.tail { margin-right: 0.6em; - font-family: "Source Code Pro"; + font-family: "Source Code Pro", "Monaco", "Ubuntu Mono Regular", "Lucida Console", monospace; } div#results-content > div#member-results > ul.entities > li > ul.members > li > span.kind { @@ -564,7 +688,7 @@ div#results-content > div#member-results > ul.entities > li > ul.members > li > div#results-content > div#member-results > ul.entities > li > ul.members > li > a.label, div#results-content > div#entity-results > ul.entities > li > ul.members > li > a.label { color: #2C3D9B; - font-family: "Source Code Pro"; + font-family: "Source Code Pro", "Monaco", "Ubuntu Mono Regular", "Lucida Console", monospace; } /** Scrollpane settings needed for jquery.scrollpane.min.js */ @@ -710,6 +834,10 @@ only screen /* iPhone 6 */ and (max-device-width: 667px) and (-webkit-device-pixel-ratio: 2) { + div#content-container > div#subpackage-spacer { + display: none; + } + div#content-container > div#content { margin: 3.3em auto 0; } diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.js b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.js index caa6406bc5..1a2e62b314 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.js +++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.js @@ -21,6 +21,20 @@ var Index = {}; } })(Index); +/** Find query string from URL */ +var QueryString = function(key) { + if (QueryString.map === undefined) { // only calc once + QueryString.map = {}; + var keyVals = window.location.search.split("?").pop().split("&"); + keyVals.forEach(function(elem) { + var pair = elem.split("="); + if (pair.length == 2) QueryString.map[pair[0]] = pair[1]; + }); + } + + return QueryString.map[key]; +}; + $(document).ready(function() { // Clicking #doc-title returns the user to the root package $("#doc-title").click(function() { document.location = toRoot + "index.html" }); @@ -33,15 +47,17 @@ $(document).ready(function() { configureTextFilter(); - $("#index-input").on("focus", function(e) { - $("#textfilter > .input > .clear").show(); - }); - - $("#index-input").on("blur", function() { - setTimeout(function() { + $("#index-input").on("input", function(e) { + if($(this).val().length > 0) + $("#textfilter > .input > .clear").show(); + else $("#textfilter > .input > .clear").hide(); - }, 10); }); + + if (QueryString("search") !== undefined) { + $("#index-input").val(QueryString("search")); + searchAll(); + } }); /* Handles all key presses while scrolling around with keyboard shortcuts in search results */ @@ -98,7 +114,7 @@ function handleKeyNavigation() { scroller.scrollDown = function($elem) { var yPos = $elem.offset().top; // offset relative to viewport - if ($container.height() < yPos) { + if ($container.height() < yPos || (yPos - $("#search").height()) < 0) { $container.animate({ scrollTop: $container.scrollTop() + yPos - $("#search").height() - 10 }, 200); @@ -132,6 +148,7 @@ function handleKeyNavigation() { var $old = items.next(); $old.addClass("selected"); + scroller.scrollDown($old); $(window).bind("keydown", function(e) { switch ( e.keyCode ) { @@ -228,6 +245,8 @@ function configureTextFilter() { $("div#search-results").hide(); $("#search > span.close-results").hide(); $("#search > span#doc-title").show(); + + $(this).hide(); }); }); @@ -412,7 +431,12 @@ function handleSearchedPackage(res, regExp) { */ function searchEntity(entity, ul, regExp) { return new Promise(function(resolve, reject) { - var matchingMembers = $.grep(entity.members, function(member, i) { + var allMembers = + (entity.members_trait || []) + .concat(entity.members_class || []) + .concat(entity.members_object || []) + + var matchingMembers = $.grep(allMembers, function(member, i) { return regExp.test(member.label); }); @@ -482,7 +506,7 @@ function listItem(entity, regExp) { } else { var spacer = document.createElement("div"); spacer.className = "icon spacer"; - li.appendChild(spacer); + li.insertBefore(spacer, iconElem); } var ul = document.createElement("ul"); @@ -510,6 +534,11 @@ function searchAll() { return; } + // Replace ?search=X with current search string if not hosted locally on Chrome + try { + window.history.replaceState({}, "", "?search=" + searchStr); + } catch(e) {} + $("div#results-content > span.search-text").remove(); var memberResults = document.getElementById("member-results"); diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/object_comp_trait.svg b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/object_comp_trait.svg new file mode 100644 index 0000000000..56eccd03ba --- /dev/null +++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/object_comp_trait.svg @@ -0,0 +1,57 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="72px" height="72px" viewBox="0 0 72 72" version="1.1"> + <defs> + <filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-1"> + <feOffset dx="0" dy="4" in="SourceAlpha" result="shadowOffsetOuter1"/> + <feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"/> + <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.14 0" in="shadowBlurOuter1" type="matrix" result="shadowMatrixOuter1"/> + <feMerge> + <feMergeNode in="shadowMatrixOuter1"/> + <feMergeNode in="SourceGraphic"/> + </feMerge> + </filter> + <circle id="path-2" cx="32" cy="32" r="32"/> + <filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-4"> + <feOffset dx="0" dy="4" in="SourceAlpha" result="shadowOffsetOuter1"/> + <feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"/> + <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.14 0" in="shadowBlurOuter1" type="matrix" result="shadowMatrixOuter1"/> + <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetInner1"/> + <feGaussianBlur stdDeviation="0" in="shadowOffsetInner1" result="shadowBlurInner1"/> + <feComposite in="shadowBlurInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"/> + <feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.14 0" in="shadowInnerInner1" type="matrix" result="shadowMatrixInner1"/> + <feOffset dx="0" dy="-1" in="SourceAlpha" result="shadowOffsetInner2"/> + <feGaussianBlur stdDeviation="0" in="shadowOffsetInner2" result="shadowBlurInner2"/> + <feComposite in="shadowBlurInner2" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner2"/> + <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.14 0" in="shadowInnerInner2" type="matrix" result="shadowMatrixInner2"/> + <feMerge> + <feMergeNode in="shadowMatrixOuter1"/> + <feMergeNode in="SourceGraphic"/> + <feMergeNode in="shadowMatrixInner1"/> + <feMergeNode in="shadowMatrixInner2"/> + </feMerge> + </filter> + <path id="path-5" d="M32 61C49.673112 61 64 48.0162577 64 32 64 15.9837423 49.673112 3 32 3 14.326888 3 0 15.9837423 0 32 0 48.0162577 14.326888 61 32 61Z"/> + </defs> + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="Artboard-1" transform="translate(-298.000000, -91.000000)"> + <g id="BG" transform="translate(302.000000, 91.000000)"> + <g id="Icon"> + <mask id="mask-3" fill="white"> + <use xlink:href="#path-2"/> + </mask> + <use id="Mask" fill="#19AACF" filter="url(#filter-1)" xlink:href="#path-2"/> + <rect id="Rectangle-2" opacity="0.3" fill="#000000" mask="url(#mask-3)" x="-8" y="33" width="80" height="31"/> + <mask id="mask-6" fill="white"> + <use xlink:href="#path-5"/> + </mask> + <use id="Mask" fill="#2C6C8D" filter="url(#filter-4)" xlink:href="#path-5"/> + <text id="t" mask="url(#mask-6)" font-family="Open Sans, Helvetica Neueu, Sans-serif" font-size="40" font-weight="normal" fill="#FFFFFF"> + <tspan x="17" y="47"> + O + </tspan> + </text> + <rect id="Rectangle-2" opacity="0.190065299" fill="#000000" mask="url(#mask-6)" x="-8" y="2" width="80" height="31"/> + </g> + </g> + </g> + </g> +</svg> diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/permalink.svg b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/permalink.svg deleted file mode 100644 index a11d568d94..0000000000 --- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/permalink.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" fill="#ffffff" height="24" viewBox="0 0 24 24"><path d="M10.59,13.41C11,13.8 11,14.44 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C7.22,12.88 7.22,9.71 9.17,7.76V7.76L12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.59,9.17C9.41,10.34 9.41,12.24 10.59,13.41M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.78,11.12 16.78,14.29 14.83,16.24V16.24L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L13.41,14.83C14.59,13.66 14.59,11.76 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z" /></svg> diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/scheduler.js b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/scheduler.js index 750c9099fd..52fb1770ee 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/scheduler.js +++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/scheduler.js @@ -5,7 +5,7 @@ function Scheduler() { var scheduler = this; var resolution = 0; this.timeout = undefined; - this.queues = new Array(0); // an array of work pacakges indexed by index in the labels table. + this.queues = new Array(0); // an array of work packages indexed by index in the labels table. this.labels = new Array(0); // an indexed array of labels indexed by priority. This should be short. this.label = function(name, priority) { 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 2265f8f045..f222749dd2 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 @@ -40,7 +40,7 @@ body { text-align: center; color: #858484; bottom: 0; - height: 20px; + min-height: 20px; margin: 0 1em 0.5em; } @@ -53,10 +53,6 @@ body { text-decoration: none; } -body.trait div#definition { - background-color: #2e6d82; -} - #types ol li > p { margin-top: 5px; } @@ -71,8 +67,7 @@ body.trait div#definition { padding: 5px 0; padding: 0; margin: 0.5em; - border-radius: 0.2em; - box-shadow: 0 0 10px rgba(0,0,0,0.2); + min-height: 4.72em; } #definition > a > img { @@ -86,7 +81,8 @@ body.trait div#definition { } #definition > h1 { - color: white; + float: left; + color: #103a51; display: inline-block; overflow: hidden; margin-top: 10px; @@ -94,19 +90,28 @@ body.trait div#definition { } #definition h1 > a { - color: #fff !important; + color: #103a51 !important; text-decoration: none !important; } +#template ol > li > span.permalink > a > i { + transform: rotate(-45deg); +} + #definition #owner { - color: #ffffff; + color: #103a51; padding-top: 1.3em; font-size: 0.8em; overflow: hidden; } +#definition > h3 { + margin-top: 0.85em; + padding: 0; +} + #definition #owner > a { - color: #ffffff; + color: #103a51; } #definition #owner > a:hover { @@ -114,14 +119,13 @@ body.trait div#definition { } #signature { - background-color:#c2d2dc; + background-color: #c2d2dc; min-height: 18px; font-size: 0.9em; padding: 8px; - width: 100%; color: #103a51; - border-bottom-left-radius: 0.2em; - border-bottom-right-radius: 0.2em; + border-radius: 0.2em; + margin: 0 0.5rem; } #signature > span.modifier_kind { @@ -133,6 +137,10 @@ body.trait div#definition { padding-left: 0; } +span.symbol > a { + display: inline-block; +} + #signature > span.symbol { text-align: left; display: inline; @@ -147,7 +155,7 @@ body.trait div#definition { .toggleContainer .toggle { position: relative; color: #103a51; - margin-left: 2.3em; + margin-left: 0.3em; cursor: pointer; -webkit-touch-callout: none; -webkit-user-select: none; @@ -192,18 +200,6 @@ body.trait div#definition { opacity: 1; } -.value #definition { - background-color: #103A51; /* blue */ -} - -.type #definition { - background-color: rgba(49, 101, 85, 1); /* green */ -} - -.abstract.type #definition { - background-color: #447A90; -} - .big-circle { box-sizing: content-box; height: 5.7em; @@ -220,18 +216,22 @@ body.trait div#definition { background: url("class.svg") no-repeat center; } -.big-circle.cl.companion { +.big-circle.class-companion-object { background: url("class_comp.svg") no-repeat center; } -.big-circle.ob.companion { +.big-circle.object-companion-class { background: url("object_comp.svg") no-repeat center; } -.big-circle.tr.companion { +.big-circle.trait-companion-object { background: url("trait_comp.svg") no-repeat center; } +.big-circle.object-companion-trait { + background: url("object_comp_trait.svg") no-repeat center; +} + .big-circle.object { background: url("object.svg") no-repeat center; } @@ -250,12 +250,14 @@ body.abstract.type div.big-circle { #template { margin: 0.9em 0.75em 0.75em; - border-radius: 0.2em; - background-color: #fff; - -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.1); - box-shadow: 0 0 10px rgba(0,0,0,0.1); padding-bottom: 0.5em; - overflow: hidden; +} + +#template h3 { + color: #103a51; + height: 2em; + padding: 1em 1em 2em; + font-size: 1.2em; } #order { @@ -263,7 +265,7 @@ body.abstract.type div.big-circle { } h3 { - color: white; + color: #103a51; padding: 5px 10px; font-size: 1em; font-weight: bold; @@ -288,21 +290,10 @@ dl.attributes > dd { min-height: 15px; } -#template .values > h3 { - color: #2C475C; - height: 2em; - padding: 1em 1em 2em; - font-size: 1.2em; -} - .values ol li:last-child { margin-bottom: 5px; } -#template .types > h3 { - height: 18px; -} - #constructors > h3 { height: 2em; padding: 1em 1em 2em; @@ -333,30 +324,38 @@ dl.attributes > dd { /* Member cells */ div.members > ol { - background-color: white; list-style: none; - padding: 0 10px; } div.members > ol > li { display: table; width: 100%; position: relative; - background-color: #e1e7ed; + background-color: #fff; border-radius: 0.2em; color: #103a51; padding: 5px 0 5px; margin-bottom: 0.4em; - min-height: 2.8em; + min-height: 3.7em; + border-left: 0.25em solid white; + -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.1); + box-shadow: 0 0 10px rgba(0,0,0,0.1); + transition: 0.1s; } div.members > ol >li.selected, div.members > ol > li:hover { - background-color: #c2d2dc; + background-color: #dae7f0; + border-left-color: #dae7f0; +} + +div.members > ol >li[fullComment=yes].selected, +div.members > ol > li[fullComment=yes]:hover { + cursor: pointer; + border-left: 0.25em solid #72D0EB; } div.members > ol > li:last-child { - border: 0; padding: 5px 0 5px; } @@ -372,7 +371,7 @@ div.members > ol > li:last-child { } .signature { - font-family: "Source Code Pro"; + font-family: "Source Code Pro", "Monaco", "Ubuntu Mono Regular", "Lucida Console", monospace; font-size: 0.8rem; line-height: 18px; clear: both; @@ -380,7 +379,7 @@ div.members > ol > li:last-child { } .modifier_kind { - font-family: "Source Code Pro"; + font-family: "Source Code Pro", "Monaco", "Ubuntu Mono Regular", "Lucida Console", monospace; font-size: 0.8rem; padding-right: 0.5em; text-align: right; @@ -390,7 +389,7 @@ div.members > ol > li:last-child { } .symbol { - font-family: "Source Code Pro"; + font-family: "Source Code Pro", "Monaco", "Ubuntu Mono Regular", "Lucida Console", monospace; } a > .symbol > .name { @@ -418,6 +417,16 @@ span.symbol > span.name { font-weight: bold; } +#types > ol > li > span.symbol > span.result { + display: none; +} + +#types > ol > li > span.symbol > span.result.alias, +#types > ol > li:hover > span.symbol > span.result, +#types > ol > li.open > span.symbol > span.result { + display: inline; +} + .symbol > .implicit { display: inline-block; font-weight: bold; @@ -463,7 +472,7 @@ i.unfold-arrow { #template .values .name { font-weight: 600; - color: darkblue; + color: #315479; } #template .types .name { @@ -506,31 +515,35 @@ div#definition > h4#signature > span.modifier_kind > i.unfold-arrow, #definition .morelinks { text-align: right; - position: absolute; - top: 2.95em; - right: 1em; - width: 450px; - font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif; + font-family: "Source Code Pro", "Monaco", "Ubuntu Mono Regular", "Lucida Console", monospace; } #definition .morelinks a { - color: #EBEBEB; + color: #103a51; } #template .members li .permalink { position: absolute; - top: 5px; - right: 5px; + left: 0.25em; + top: 0.95em; } #definition .permalink { - position: absolute; - top: 10px; - right: 15px; + display: none; + color: black; } #definition .permalink a { - color: #EBEBEB; + color: #103a51; + transform: rotate(-45deg); +} + +#definition > h1 > span > a > i { + font-size: 1.4rem; +} + +#template ol > li > span.permalink > a > i { + color: #fff; } #template .members li .permalink, @@ -584,7 +597,7 @@ div#definition > h4#signature > span.modifier_kind > i.unfold-arrow, background-color: #fff; margin: 5px 0; display: block; - font-family: "Source Code Pro"; + font-family: "Source Code Pro", "Monaco", "Ubuntu Mono Regular", "Lucida Console", monospace; border-radius: 0.2em; overflow-x: auto; } @@ -719,6 +732,7 @@ div.fullcomment .block { } div.fullcommenttop .block { + position: relative; padding: 1em; margin: 0.5em 0; border-radius: 0.2em; @@ -727,6 +741,16 @@ div.fullcommenttop .block { box-shadow: 0 0 10px rgba(0,0,0,0.1); } +div.fullcommenttop .toggleContainer { + border-left: 0 solid #72D0EB; + transition: 0.1s; + cursor: pointer; +} + +div.fullcommenttop .toggleContainer:hover { + border-left: 0.25em solid #72D0EB; +} + div#comment, div#mbrsel, div#template, @@ -839,12 +863,9 @@ div.fullcomment dl.paramcmts > dd { margin: 0.8em; border-radius: 0.2em; background-color: #364550; - -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.1); - box-shadow: 0 0 10px rgba(0,0,0,0.1); + -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.2); + box-shadow: 0 0 10px rgba(0,0,0,0.2); position: relative; - margin-bottom: -1em; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; } #mbrsel > div.toggle { @@ -877,6 +898,12 @@ div.fullcomment dl.paramcmts > dd { left: 0; top: 0; color: #fff; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } #mbrsel > div.toggle.open > i { @@ -1025,7 +1052,7 @@ div.fullcomment dl.paramcmts > dd { /* Media query rules for smaller viewport */ @media only screen /* Large screen with a small window */ -and (max-width: 560px) +and (max-width: 650px) , screen /* HiDPI device like Nexus 5 */ and (max-device-width: 360px) @@ -1058,16 +1085,23 @@ and (-webkit-device-pixel-ratio: 2) height: 6em; } + #definition > h1 { + font-size: 1em; + margin-right: 0.3em; + } + + #definition > h3 { + float: left; + margin: 0.3em 0; + } + + #definition > #owner { + padding-top: 2.6em; + } + #definition .morelinks { text-align: left; - left: 7.2em; font-size: 0.8em; - top: auto; - bottom: 0.5em; - } - - #signature { - top: 6.7em; } .big-circle { 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 b0719b1ed5..64177a7723 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 @@ -4,10 +4,10 @@ var $panzoom = undefined; $(document).ready(function() { // Add zoom functionality to type inheritance diagram - $panzoom = $("#inheritance-diagram").panzoom({ + $panzoom = $(".diagram-container > .diagram").panzoom({ increment: 0.1, minScale: 1, - maxScale: 3, + maxScale: 7, transition: true, duration: 200, contain: 'invert', @@ -16,15 +16,10 @@ $(document).ready(function() { $zoomOut: $('#diagram-zoom-out'), }); - $("#template > div > div > ol > li > span > a").click(function(e) { - var href = $(this).attr("href"); - if (href.indexOf("#") != -1) { - e.preventDefault(); - location.hash = href.split("#").pop(); - $("#template > div > div > ol > li").removeClass("selected"); - var parent = $(this).parent().parent().addClass("selected"); - $("#content-container").animate({scrollTop: $("#content-container").scrollTop() + $(this).offset().top - $("#search").height() - 22}, 500); - } + var oldWidth = $("div#subpackage-spacer").width() + 1 + "px"; + $("div#packages > ul > li.current").click(function() { + $("div#subpackage-spacer").css({ "width": oldWidth }); + $("li.current-entities").toggle(); }); var controls = { @@ -58,21 +53,22 @@ $(document).ready(function() { function exposeMember(jqElem) { var jqElemParent = jqElem.parent(), parentName = jqElemParent.attr("name"), - linearizationName = /^([^#]*)(#.*)?$/gi.exec(parentName)[1]; + ancestorName = /^([^#]*)(#.*)?$/gi.exec(parentName)[1]; // switch visibility filter if necessary if (jqElemParent.attr("visbl") == "prt") { toggleVisibilityFilter(controls.visibility.all, controls.visibility.publicOnly); } - // toggle appropriate linearization buttons - if (linearizationName) { - $("#linearization li.out[name='" + linearizationName + "']").removeClass("out").addClass("in"); + // toggle appropriate ancestor filter buttons + if (ancestorName) { + $("#filterby li.out[name='" + ancestorName + "']").removeClass("out").addClass("in"); } filter(); jqElemParent.addClass("selected"); - $("#content-container").animate({scrollTop: jqElemParent.offset().top - $("#search").height() - 5 }, 1000); + commentToggleFct(jqElemParent); + $("#content-scroll-container").animate({scrollTop: $("#content-scroll-container").scrollTop() + jqElemParent.offset().top - $("#search").height() - 23 }, 1000); } var isHiddenClass = function (name) { @@ -140,10 +136,10 @@ $(document).ready(function() { }); $("#memberfilter > .clear").click(function() { $("#memberfilter input").attr("value", ""); + $(this).hide(); filter(); }); $(document).keydown(function(event) { - if (event.keyCode == 9) { // tab $("#index-input", window.parent.document).focus(); input.attr("value", ""); @@ -155,8 +151,7 @@ $(document).ready(function() { if ($(this).hasClass("in")) { $(this).removeClass("in"); $(this).addClass("out"); - } - else if ($(this).hasClass("out")) { + } else if ($(this).hasClass("out")) { $(this).removeClass("out"); $(this).addClass("in"); } @@ -167,8 +162,7 @@ $(document).ready(function() { if ($(this).hasClass("in")) { $(this).removeClass("in"); $(this).addClass("out"); - } - else if ($(this).hasClass("out")) { + } else if ($(this).hasClass("out")) { $(this).removeClass("out"); $(this).addClass("in"); } @@ -208,19 +202,16 @@ $(document).ready(function() { filter(); }); $("#order > ol > li.alpha").click(function() { - if ($(this).hasClass("out")) { + if ($(this).hasClass("out")) orderAlpha(); - } }) $("#order > ol > li.inherit").click(function() { - if ($(this).hasClass("out")) { + if ($(this).hasClass("out")) orderInherit(); - } }); $("#order > ol > li.group").click(function() { - if ($(this).hasClass("out")) { + if ($(this).hasClass("out")) orderGroup(); - } }); $("#groupedMembers").hide(); @@ -237,14 +228,14 @@ $(document).ready(function() { }); /* Add toggle arrows */ - //var docAllSigs = $("#template li").has(".fullcomment").find(".signature"); - // trying to speed things up a little bit - var docAllSigs = $("#template li[fullComment=yes] .modifier_kind"); - - function commentToggleFct(signature){ - var parent = signature.parent(); - var shortComment = $(".shortcomment", parent); - var fullComment = $(".fullcomment", parent); + $("#template li[fullComment=yes] .modifier_kind").addClass("closed"); + + function commentToggleFct(element){ + $("#template li.selected").removeClass("selected"); + element.toggleClass("open"); + var signature = element.find(".modifier_kind") + var shortComment = element.find(".shortcomment"); + var fullComment = element.find(".fullcomment"); var vis = $(":visible", fullComment); signature.toggleClass("closed").toggleClass("opened"); if (vis.length > 0) { @@ -266,15 +257,15 @@ $(document).ready(function() { } } }; - docAllSigs.addClass("closed"); - docAllSigs.click(function() { + + $("#template li[fullComment=yes]").click(function() { commentToggleFct($(this)); }); /* Linear super types and known subclasses */ function toggleShowContentFct(e){ e.toggleClass("open"); - var content = $(".hiddenContent", e.parent().get(0)); + var content = $(".hiddenContent", e); if(content.is(':visible')) { if (!isMobile()) content.slideUp(100); else content.hide(); @@ -284,12 +275,14 @@ $(document).ready(function() { } }; - $(".toggle:not(.diagram-link)").click(function() { + $(".toggleContainer:not(.diagram-container):not(.full-signature-block)").click(function() { toggleShowContentFct($(this)); }); - // Set parent window title - windowTitle(); + $(".toggleContainer.full-signature-block").click(function() { + toggleShowContentFct($(this)); + return false; + }); if ($("#order > ol > li.group").length == 1) { orderGroup(); }; @@ -299,22 +292,34 @@ $(document).ready(function() { return $(memberSelector); } - // highlight and jump to selected member + // highlight and jump to selected member if an anchor is provided if (window.location.hash) { var jqElem = findElementByHash(window.location.hash); - if (jqElem.length > 0) { + if (jqElem.length > 0) exposeMember(jqElem); - } } - $("#mbrsel-input").on("focus", function() { - $("#memberfilter > .clear").show(); + $("#template span.permalink").click(function(e) { + e.preventDefault(); + var href = $("a", this).attr("href"); + if (href.indexOf("#") != -1) { + var hash = href.split("#").pop() + try { + window.history.pushState({}, "", "#" + hash) + } catch (e) { + // fallback for file:// URLs, has worse scrolling behavior + location.hash = hash; + } + exposeMember(findElementByHash(hash)) + } + return false; }); - $("#mbrsel-input").on("blur", function() { - setTimeout(function() { + $("#mbrsel-input").on("input", function() { + if ($(this).val().length > 0) + $("#memberfilter > .clear").show(); + else $("#memberfilter > .clear").hide(); - }, 10); }); }); @@ -537,15 +542,6 @@ function filter() { return false; }; -function windowTitle() { - try { - parent.document.title=document.title; - } catch(e) { - // Chrome doesn't allow settings the parent's title when - // used on the local file system. - } -}; - /** Check if user agent is associated with a known mobile browser */ function isMobile() { return /Android|webOS|Mobi|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); diff --git a/src/scaladoc/scala/tools/nsc/doc/model/Entity.scala b/src/scaladoc/scala/tools/nsc/doc/model/Entity.scala index 524f94443d..757f13f79a 100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/Entity.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/Entity.scala @@ -455,7 +455,7 @@ trait ValueParam extends ParameterEntity { /** The type of this value parameter. */ def resultType: TypeEntity - /** The devault value of this value parameter, if it has been defined. */ + /** The default value of this value parameter, if it has been defined. */ def defaultValue: Option[TreeEntity] /** Whether this value parameter is implicit. */ diff --git a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala index bccb65aa5f..928cb34d30 100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala @@ -51,7 +51,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { lazy val dotRunner = new DotRunner(settings) } _modelFinished = true - // complete the links between model entities, everthing that couldn't have been done before + // complete the links between model entities, everything that couldn't have been done before universe.rootPackage.completeModel() Some(universe) filter (_.rootPackage != null) diff --git a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala index 830d902b68..e67a717257 100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala @@ -236,7 +236,7 @@ trait ModelFactoryImplicitSupport { try { // TODO: Not sure if `owner = sym.owner` is the right thing to do -- seems similar to what scalac should be doing val silentContext = context.make(owner = sym.owner).makeSilent(reportAmbiguousErrors = false) - val search = inferImplicit(EmptyTree, tpe, false, false, silentContext, false) + val search = inferImplicitByTypeSilent(tpe, silentContext) available = Some(search.tree != EmptyTree) } catch { case _: TypeError => diff --git a/src/scaladoc/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala b/src/scaladoc/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala index 3b7eb4e947..464cacc99a 100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala @@ -163,7 +163,7 @@ trait DiagramDirectiveParser { case Nil => defaultFilter - // compute the exact filters. By including the annotation, the diagram is autmatically added + // compute the exact filters. By including the annotation, the diagram is automatically added case _ => tFilter -= System.currentTimeMillis var hideDiagram0: Boolean = false diff --git a/src/scaladoc/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala b/src/scaladoc/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala index 86900f26c9..bbcb18353a 100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala @@ -63,7 +63,7 @@ trait DiagramFactory extends DiagramDirectiveParser { case d: TemplateImpl if !classExcluded(d) => NormalNode(makeType(d.sym.tpe, tpl), Some(d))() }.sortBy(_.tpl.get.name)(implicitly[Ordering[String]].reverse) - // outgoing implicit coversions + // outgoing implicit conversions lazy val outgoingImplicitNodes = tpl.outgoingImplicitlyConvertedClasses.map { case (outgoingTpl, outgoingType, conv) => ImplicitNode(outgoingType, Some(outgoingTpl))(implicitTooltip(from=tpl, to=tpl, conv=conv)) diff --git a/src/scalap/scala/tools/scalap/Main.scala b/src/scalap/scala/tools/scalap/Main.scala index 3d2bfd7251..6a37bbc270 100644 --- a/src/scalap/scala/tools/scalap/Main.scala +++ b/src/scalap/scala/tools/scalap/Main.scala @@ -8,17 +8,12 @@ package scala package tools.scalap -import java.io.{ PrintStream, OutputStreamWriter, ByteArrayOutputStream } +import java.io.{ByteArrayOutputStream, OutputStreamWriter, PrintStream} import scala.reflect.NameTransformer import scala.tools.nsc.Settings -import scala.tools.nsc.classpath.AggregateFlatClassPath -import scala.tools.nsc.classpath.FlatClassPathFactory -import scala.tools.nsc.io.AbstractFile -import scala.tools.nsc.settings.ClassPathRepresentationType -import scala.tools.nsc.util.ClassFileLookup -import scala.tools.nsc.util.ClassPath.DefaultJavaContext -import scala.tools.nsc.util.JavaClassPath -import scala.tools.util.PathResolverFactory +import scala.tools.nsc.classpath.{AggregateClassPath, ClassPathFactory} +import scala.tools.nsc.util.ClassPath +import scala.tools.util.PathResolver import scalax.rules.scalasig._ /**The main object used to execute scalap on the command-line. @@ -101,7 +96,7 @@ class Main { /** Executes scalap with the given arguments and classpath for the * class denoted by `classname`. */ - def process(args: Arguments, path: ClassFileLookup[AbstractFile])(classname: String): Unit = { + def process(args: Arguments, path: ClassPath)(classname: String): Unit = { // find the classfile val encName = classname match { case "scala.AnyRef" => "java.lang.Object" @@ -145,7 +140,6 @@ object Main extends Main { val verbose = "-verbose" val version = "-version" - val classPathImplType = "-YclasspathImpl" val disableFlatClassPathCaching = "-YdisableFlatCpCaching" val logClassPath = "-Ylog-classpath" } @@ -183,7 +177,6 @@ object Main extends Main { val settings = new Settings() - arguments getArgument opts.classPathImplType foreach settings.YclasspathImpl.tryToSetFromPropertyValue settings.YdisableFlatCpCaching.value = arguments contains opts.disableFlatClassPathCaching settings.Ylogcp.value = arguments contains opts.logClassPath @@ -205,21 +198,16 @@ object Main extends Main { .withOption(opts.help) .withOptionalArg(opts.classpath) .withOptionalArg(opts.cp) - // TODO three temporary, hidden options to be able to test different classpath representations - .withOptionalArg(opts.classPathImplType) + // TODO two temporary, hidden options to be able to test different classpath representations .withOption(opts.disableFlatClassPathCaching) .withOption(opts.logClassPath) .parse(args) private def createClassPath(cpArg: Option[String], settings: Settings) = cpArg match { - case Some(cp) => settings.YclasspathImpl.value match { - case ClassPathRepresentationType.Flat => - AggregateFlatClassPath(new FlatClassPathFactory(settings).classesInExpandedPath(cp)) - case ClassPathRepresentationType.Recursive => - new JavaClassPath(DefaultJavaContext.classesInExpandedPath(cp), DefaultJavaContext) - } + case Some(cp) => + AggregateClassPath(new ClassPathFactory(settings).classesInExpandedPath(cp)) case _ => settings.classpath.value = "." // include '.' in the default classpath SI-6669 - PathResolverFactory.create(settings).result + new PathResolver(settings).result } } diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSigPrinter.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSigPrinter.scala index 3bfb82a639..064c7ac34c 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSigPrinter.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ScalaSigPrinter.scala @@ -11,8 +11,6 @@ package scalax package rules package scalasig -import language.postfixOps - import java.io.{PrintStream, ByteArrayOutputStream} import java.util.regex.Pattern import scala.tools.scalap.scalax.util.StringUtil |