diff options
Diffstat (limited to 'src/compiler')
33 files changed, 324 insertions, 366 deletions
diff --git a/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala b/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala index 32c6da8007..2e82e34bd9 100644 --- a/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala +++ b/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala @@ -2,7 +2,6 @@ package scala.reflect.macros package compiler import scala.tools.nsc.Global -import scala.reflect.macros.contexts.Context abstract class DefaultMacroCompiler extends Resolvers with Validators @@ -11,7 +10,6 @@ abstract class DefaultMacroCompiler extends Resolvers import global._ val typer: global.analyzer.Typer - private implicit val context0 = typer.context val context = typer.context val macroDdef: DefDef diff --git a/src/compiler/scala/reflect/macros/compiler/Validators.scala b/src/compiler/scala/reflect/macros/compiler/Validators.scala index 60cfc94a23..fafd79d1d7 100644 --- a/src/compiler/scala/reflect/macros/compiler/Validators.scala +++ b/src/compiler/scala/reflect/macros/compiler/Validators.scala @@ -11,8 +11,6 @@ trait Validators { import global._ import analyzer._ import definitions._ - import treeInfo._ - import typer.infer._ def validateMacroImplRef() = { sanityCheck() diff --git a/src/compiler/scala/reflect/macros/contexts/Context.scala b/src/compiler/scala/reflect/macros/contexts/Context.scala index bd1d7d5248..1355a839d9 100644 --- a/src/compiler/scala/reflect/macros/contexts/Context.scala +++ b/src/compiler/scala/reflect/macros/contexts/Context.scala @@ -14,7 +14,6 @@ abstract class Context extends scala.reflect.macros.Context with Parsers with Evals with ExprUtils - with Synthetics with Traces { val universe: Global diff --git a/src/compiler/scala/reflect/macros/contexts/Synthetics.scala b/src/compiler/scala/reflect/macros/contexts/Synthetics.scala deleted file mode 100644 index ada16a8113..0000000000 --- a/src/compiler/scala/reflect/macros/contexts/Synthetics.scala +++ /dev/null @@ -1,66 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - */ - -package scala.reflect.macros -package contexts - -import scala.reflect.internal.Flags._ -import scala.reflect.internal.util.BatchSourceFile -import scala.reflect.io.VirtualFile - -trait Synthetics { - self: Context => - - import global._ - import mirror.wrapMissing - - // getClassIfDefined and getModuleIfDefined cannot be used here - // because they don't work for stuff declared in the empty package - // (as specified in SLS, code inside non-empty packages cannot see - // declarations from the empty package, so compiler internals - // default to ignoring contents of the empty package) - // to the contrast, staticModule and staticClass are designed - // to be a part of the reflection API and, therefore, they - // correctly resolve all names - private def topLevelSymbol(name: Name): Symbol = wrapMissing { - if (name.isTermName) mirror.staticModule(name.toString) - else mirror.staticClass(name.toString) - } - - def topLevelDef(name: Name): Tree = - enclosingRun.units.toList.map(_.body).flatMap { - // it's okay to check `stat.symbol` here, because currently macros expand strictly after namer - // which means that by the earliest time one can call this method all top-level definitions will have already been entered - case PackageDef(_, stats) => stats filter (stat => stat.symbol != NoSymbol && stat.symbol == topLevelSymbol(name)) - case _ => Nil // should never happen, but better be safe than sorry - }.headOption getOrElse EmptyTree - - def topLevelRef(name: Name): Tree = { - if (topLevelDef(name).nonEmpty) gen.mkUnattributedRef(name) - else EmptyTree - } - - def introduceTopLevel[T: PackageSpec](packagePrototype: T, definition: universe.ImplDef): RefTree = - introduceTopLevel(packagePrototype, List(definition)).head - - def introduceTopLevel[T: PackageSpec](packagePrototype: T, definitions: universe.ImplDef*): List[RefTree] = - introduceTopLevel(packagePrototype, definitions.toList) - - private def introduceTopLevel[T: PackageSpec](packagePrototype: T, definitions: List[universe.ImplDef]): List[RefTree] = { - val code @ PackageDef(pid, _) = implicitly[PackageSpec[T]].mkPackageDef(packagePrototype, definitions) - universe.currentRun.compileLate(code) - definitions map (definition => Select(pid, definition.name)) - } - - protected def mkPackageDef(name: String, stats: List[Tree]) = gen.mkPackageDef(name, stats) - - protected def mkPackageDef(name: TermName, stats: List[Tree]) = gen.mkPackageDef(name.toString, stats) - - protected def mkPackageDef(tree: RefTree, stats: List[Tree]) = PackageDef(tree, stats) - - protected def mkPackageDef(sym: Symbol, stats: List[Tree]) = { - assert(sym hasFlag PACKAGE, s"expected a package or package class symbol, found: $sym") - gen.mkPackageDef(sym.fullName.toString, stats) - } -} diff --git a/src/compiler/scala/reflect/macros/runtime/JavaReflectionRuntimes.scala b/src/compiler/scala/reflect/macros/runtime/JavaReflectionRuntimes.scala index 3ef11fad9d..450cb4d9ea 100644 --- a/src/compiler/scala/reflect/macros/runtime/JavaReflectionRuntimes.scala +++ b/src/compiler/scala/reflect/macros/runtime/JavaReflectionRuntimes.scala @@ -10,8 +10,6 @@ trait JavaReflectionRuntimes { trait JavaReflectionResolvers { self: MacroRuntimeResolver => - import global._ - def resolveJavaReflectionRuntime(classLoader: ClassLoader): MacroRuntime = { val implClass = Class.forName(className, true, classLoader) val implMeths = implClass.getDeclaredMethods.find(_.getName == methName) diff --git a/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala b/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala index 0f89163803..ffdbe11151 100644 --- a/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala +++ b/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala @@ -1,11 +1,8 @@ package scala.reflect.macros package runtime -import scala.collection.mutable.{Map => MutableMap} import scala.reflect.internal.Flags._ import scala.reflect.runtime.ReflectionUtils -import scala.tools.nsc.util.ScalaClassLoader -import scala.tools.nsc.util.AbstractFileClassLoader trait MacroRuntimes extends JavaReflectionRuntimes with ScalaReflectionRuntimes { self: scala.tools.nsc.typechecker.Analyzer => diff --git a/src/compiler/scala/reflect/macros/runtime/ScalaReflectionRuntimes.scala b/src/compiler/scala/reflect/macros/runtime/ScalaReflectionRuntimes.scala index 1999e525ff..50f64310f8 100644 --- a/src/compiler/scala/reflect/macros/runtime/ScalaReflectionRuntimes.scala +++ b/src/compiler/scala/reflect/macros/runtime/ScalaReflectionRuntimes.scala @@ -9,8 +9,6 @@ trait ScalaReflectionRuntimes { trait ScalaReflectionResolvers { self: MacroRuntimeResolver => - import global._ - def resolveScalaReflectionRuntime(classLoader: ClassLoader): MacroRuntime = { val macroMirror: ru.JavaMirror = ru.runtimeMirror(classLoader) val implContainerSym = macroMirror.classSymbol(Class.forName(className, true, classLoader)) diff --git a/src/compiler/scala/reflect/macros/util/Helpers.scala b/src/compiler/scala/reflect/macros/util/Helpers.scala index 9b7680717e..f12582a3a1 100644 --- a/src/compiler/scala/reflect/macros/util/Helpers.scala +++ b/src/compiler/scala/reflect/macros/util/Helpers.scala @@ -23,7 +23,7 @@ trait Helpers { * or to streamline creation of the list of macro arguments. */ def transformTypeTagEvidenceParams(macroImplRef: Tree, transform: (Symbol, Symbol) => Symbol): List[List[Symbol]] = { - val treeInfo.MacroImplReference(isBundle, owner, macroImpl, _) = macroImplRef + val treeInfo.MacroImplReference(isBundle, _, macroImpl, _) = macroImplRef val paramss = macroImpl.paramss if (paramss.isEmpty || paramss.last.isEmpty) return paramss // no implicit parameters in the signature => nothing to do val rc = @@ -44,11 +44,6 @@ trait Helpers { if (transformed.isEmpty) paramss.init else paramss.init :+ transformed } - private def dealiasAndRewrap(tp: Type)(fn: Type => Type): Type = { - if (isRepeatedParamType(tp)) scalaRepeatedType(fn(tp.typeArgs.head.dealias)) - else fn(tp.dealias) - } - /** Increases metalevel of the type, i.e. transforms: * * T to c.Expr[T] * diff --git a/src/compiler/scala/tools/ant/sabbus/Settings.scala b/src/compiler/scala/tools/ant/sabbus/Settings.scala index 4cbc03d8d4..a86af73fe3 100644 --- a/src/compiler/scala/tools/ant/sabbus/Settings.scala +++ b/src/compiler/scala/tools/ant/sabbus/Settings.scala @@ -93,7 +93,7 @@ class Settings { case _ => false } - override lazy val hashCode: Int = Seq( + override lazy val hashCode: Int = Seq[Any]( gBf, uncheckedBf, classpathBf, diff --git a/src/compiler/scala/tools/cmd/CommandLine.scala b/src/compiler/scala/tools/cmd/CommandLine.scala index e44752eb6e..781cc564cb 100644 --- a/src/compiler/scala/tools/cmd/CommandLine.scala +++ b/src/compiler/scala/tools/cmd/CommandLine.scala @@ -24,13 +24,13 @@ class CommandLine(val spec: Reference, val originalArgs: List[String]) extends C val Terminator = "--" val ValueForUnaryOption = "true" // so if --opt is given, x(--opt) = true - def mapForUnary(opt: String) = Map(opt -> ValueForUnaryOption) + def mapForUnary(opt: String) = Map(fromOpt(opt) -> ValueForUnaryOption) def errorFn(msg: String) = println(msg) /** argMap is option -> argument (or "" if it is a unary argument) * residualArgs are what is left after removing the options and their args. */ - lazy val (argMap, residualArgs) = { + lazy val (argMap, residualArgs): (Map[String, String], List[String]) = { val residualBuffer = new ListBuffer[String] def loop(args: List[String]): Map[String, String] = { @@ -72,7 +72,7 @@ class CommandLine(val spec: Reference, val originalArgs: List[String]) extends C if (x2 == Terminator) mapForUnary(x1) ++ residual(xs) else if (isUnaryOption(x1)) mapForUnary(x1) ++ loop(args.tail) - else if (isBinaryOption(x1)) Map(x1 -> x2) ++ loop(xs) + else if (isBinaryOption(x1)) Map(fromOpt(x1) -> x2) ++ loop(xs) else if (isUnknown(x1)) loop(args.tail) else residual(List(x1)) ++ loop(args.tail) } diff --git a/src/compiler/scala/tools/cmd/CommandLineParser.scala b/src/compiler/scala/tools/cmd/CommandLineParser.scala index ef55178594..6132eff557 100644 --- a/src/compiler/scala/tools/cmd/CommandLineParser.scala +++ b/src/compiler/scala/tools/cmd/CommandLineParser.scala @@ -40,16 +40,16 @@ object CommandLineParser { // parse `in` for an argument, return it and the remainder of the input (or an error message) // (argument may be in single/double quotes, taking escaping into account, quotes are stripped) private def argument(in: String): Either[String, (String, String)] = in match { - case DoubleQuoted(arg, rest) => Right(arg, rest) - case SingleQuoted(arg, rest) => Right(arg, rest) - case Word(arg, rest) => Right(arg, rest) - case _ => Left("Illegal argument: "+ in) + case DoubleQuoted(arg, rest) => Right((arg, rest)) + case SingleQuoted(arg, rest) => Right((arg, rest)) + case Word(arg, rest) => Right((arg, rest)) + case _ => Left(s"Illegal argument: $in") } // parse a list of whitespace-separated arguments (ignoring whitespace in quoted arguments) @tailrec private def commandLine(in: String, accum: List[String] = Nil): Either[String, (List[String], String)] = { val trimmed = in.trim - if (trimmed.isEmpty) Right(accum.reverse, "") + if (trimmed.isEmpty) Right((accum.reverse, "")) else argument(trimmed) match { case Right((arg, next)) => (next span Character.isWhitespace) match { diff --git a/src/compiler/scala/tools/cmd/Opt.scala b/src/compiler/scala/tools/cmd/Opt.scala index 2c193128f1..df3d0c4462 100644 --- a/src/compiler/scala/tools/cmd/Opt.scala +++ b/src/compiler/scala/tools/cmd/Opt.scala @@ -26,10 +26,10 @@ object Opt { trait Implicit { def name: String def programInfo: Info - protected def opt = toOpt(name) + protected def opt = fromOpt(name) def --? : Boolean // --opt is set - def --> (body: => Unit): Unit // if --opt is set, execute body + def --> (body: => Unit): Boolean // if --opt is set, execute body def --| : Option[String] // --opt <arg: String> is optional, result is Option[String] def --^[T: FromString] : Option[T] // --opt <arg: T> is optional, result is Option[T] @@ -51,7 +51,7 @@ object Opt { import options._ def --? = { addUnary(opt) ; false } - def --> (body: => Unit) = { addUnary(opt) } + def --> (body: => Unit) = { addUnary(opt) ; false } def --| = { addBinary(opt) ; None } def --^[T: FromString] = { addBinary(opt) ; None } @@ -65,7 +65,7 @@ object Opt { class Instance(val programInfo: Info, val parsed: CommandLine, val name: String) extends Implicit with Error { def --? = parsed isSet opt - def --> (body: => Unit) = if (parsed isSet opt) body + def --> (body: => Unit) = { val isSet = parsed isSet opt ; if (isSet) body ; isSet } def --| = parsed get opt def --^[T: FromString] = { val fs = implicitly[FromString[T]] diff --git a/src/compiler/scala/tools/cmd/Reference.scala b/src/compiler/scala/tools/cmd/Reference.scala index ec2a414065..62b6c893cf 100644 --- a/src/compiler/scala/tools/cmd/Reference.scala +++ b/src/compiler/scala/tools/cmd/Reference.scala @@ -23,13 +23,13 @@ trait Reference extends Spec { def helpMsg = options.helpMsg def propertyArgs: List[String] = Nil - def isUnaryOption(s: String) = unary contains toOpt(s) - def isBinaryOption(s: String) = binary contains toOpt(s) - def isExpandOption(s: String) = expansionMap contains toOpt(s) + def isUnaryOption(s: String) = unary contains fromOpt(s) + def isBinaryOption(s: String) = binary contains fromOpt(s) + def isExpandOption(s: String) = expansionMap contains fromOpt(s) - def expandArg(arg: String) = expansionMap.getOrElse(fromOpt(arg), List(arg)) + def expandArg(arg: String): List[String] = expansionMap.getOrElse(fromOpt(arg), List(arg)) - protected def help(str: => String) = addHelp(() => str) + protected def help(str: => String): Unit = addHelp(() => str) type ThisCommandLine <: CommandLine @@ -53,20 +53,20 @@ object Reference { def helpFormatStr = " %-" + longestArg + "s %s" def defaultFormatStr = (" " * (longestArg + 7)) + "%s" - def addUnary(s: String) = _unary +:= s - def addBinary(s: String) = _binary +:= s + def addUnary(s: String): Unit = _unary +:= s + def addBinary(s: String): Unit = _binary +:= s def addExpand(opt: String, expanded: List[String]) = _expand += (opt -> expanded) - def mapHelp(g: String => String) = { + def mapHelp(g: String => String): Unit = { val idx = _help.length - 1 val f = _help(idx) _help(idx) = () => g(f()) } - def addHelp(f: () => String) = _help += f + def addHelp(f: () => String): Unit = _help += f def addHelpAlias(f: () => String) = mapHelp { s => val str = "alias for '%s'" format f() def noHelp = (helpFormatStr.format("", "")).length == s.length @@ -74,13 +74,13 @@ object Reference { s + str2 } - def addHelpDefault(f: () => String) = mapHelp { s => + def addHelpDefault(f: () => String): Unit = mapHelp { s => val str = "(default: %s)" format f() if (s.length + str.length < MaxLine) s + " " + str else defaultFormatStr.format(s, str) } - def addHelpEnvDefault(name: String) = mapHelp { s => + def addHelpEnvDefault(name: String): Unit = mapHelp { s => val line1 = "%s (default: %s)".format(s, 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 index 7e01afac2b..842851b4f6 100644 --- a/src/compiler/scala/tools/cmd/gen/AnyVals.scala +++ b/src/compiler/scala/tools/cmd/gen/AnyVals.scala @@ -6,24 +6,23 @@ package scala.tools.cmd package gen -/** Code generation of the AnyVal types and their companions. - */ +/** Code generation of the AnyVal types and their companions. */ trait AnyValReps { self: AnyVals => - sealed abstract class AnyValNum(name: String, repr: Option[String], javaEquiv: String) extends AnyValRep(name,repr,javaEquiv) { + sealed abstract class AnyValNum(name: String, repr: Option[String], javaEquiv: String) + extends AnyValRep(name,repr,javaEquiv) { case class Op(op : String, doc : String) private def companionCoercions(tos: AnyValRep*) = { tos.toList map (to => - """implicit def @javaequiv@2%s(x: @name@): %s = x.to%s""".format(to.javaEquiv, to.name, to.name) + s"implicit def @javaequiv@2${to.javaEquiv}(x: @name@): ${to.name} = x.to${to.name}" ) } - def coercionCommentExtra = "" - def coercionComment = """ -/** Language mandated coercions from @name@ to "wider" types.%s - */""".format(coercionCommentExtra) + def coercionComment = +"""/** Language mandated coercions from @name@ to "wider" types. */ +import scala.language.implicitConversions""" def implicitCoercions: List[String] = { val coercions = this match { @@ -41,12 +40,8 @@ trait AnyValReps { def isCardinal: Boolean = isIntegerType(this) def unaryOps = { val ops = List( - Op("+", "/**\n" + - " * Returns this value, unmodified.\n" + - " */"), - Op("-", "/**\n" + - " * Returns the negation of this value.\n" + - " */")) + Op("+", "/** Returns this value, unmodified. */"), + Op("-", "/** Returns the negation of this value. */")) if(isCardinal) Op("~", "/**\n" + @@ -95,7 +90,7 @@ trait AnyValReps { " */")) else Nil - def shiftOps = + def shiftOps = if (isCardinal) List( Op("<<", "/**\n" + @@ -127,20 +122,20 @@ trait AnyValReps { " */")) else Nil - def comparisonOps = List( - Op("==", "/**\n * Returns `true` if this value is equal to x, `false` otherwise.\n */"), - Op("!=", "/**\n * Returns `true` if this value is not equal to x, `false` otherwise.\n */"), - Op("<", "/**\n * Returns `true` if this value is less than x, `false` otherwise.\n */"), - Op("<=", "/**\n * Returns `true` if this value is less than or equal to x, `false` otherwise.\n */"), - Op(">", "/**\n * Returns `true` if this value is greater than x, `false` otherwise.\n */"), - Op(">=", "/**\n * Returns `true` if this value is greater than or equal to x, `false` otherwise.\n */")) + def comparisonOps = List( + Op("==", "/** Returns `true` if this value is equal to x, `false` otherwise. */"), + Op("!=", "/** Returns `true` if this value is not equal to x, `false` otherwise. */"), + Op("<", "/** Returns `true` if this value is less than x, `false` otherwise. */"), + Op("<=", "/** Returns `true` if this value is less than or equal to x, `false` otherwise. */"), + Op(">", "/** Returns `true` if this value is greater than x, `false` otherwise. */"), + Op(">=", "/** Returns `true` if this value is greater than or equal to x, `false` otherwise. */")) def otherOps = List( - Op("+", "/**\n * Returns the sum of this value and `x`.\n */"), - Op("-", "/**\n * Returns the difference of this value and `x`.\n */"), - Op("*", "/**\n * Returns the product of this value and `x`.\n */"), - Op("/", "/**\n * Returns the quotient of this value and `x`.\n */"), - Op("%", "/**\n * Returns the remainder of the division of this value by `x`.\n */")) + Op("+", "/** Returns the sum of this value and `x`. */"), + Op("-", "/** Returns the difference of this value and `x`. */"), + Op("*", "/** Returns the product of this value and `x`. */"), + Op("/", "/** Returns the quotient of this value and `x`. */"), + Op("%", "/** Returns the remainder of the division of this value by `x`. */")) // Given two numeric value types S and T , the operation type of S and T is defined as follows: // If both S and T are subrange types then the operation type of S and T is Int. @@ -278,8 +273,7 @@ trait AnyValReps { } trait AnyValTemplates { - def headerTemplate = (""" -/* __ *\ + def headerTemplate = """/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** @@ -287,12 +281,13 @@ trait AnyValTemplates { ** |/ ** \* */ -%s -package scala +// DO NOT EDIT, CHANGES WILL BE LOST +// This auto-generated code can be modified in scala.tools.cmd.gen. +// Afterwards, running tools/codegen-anyvals regenerates this source file. -import scala.language.implicitConversions +package scala -""".trim.format(timestampString) + "\n\n") +""" def classDocTemplate = (""" /** `@name@`@representation@ (equivalent to Java's `@javaequiv@` primitive type) is a @@ -304,8 +299,6 @@ import scala.language.implicitConversions */ """.trim + "\n") - def timestampString = "// DO NOT EDIT, CHANGES WILL BE LOST.\n" - def allCompanions = """ /** Transform a value type into a boxed reference type. *@boxRunTimeDoc@ @@ -324,20 +317,17 @@ def box(x: @name@): @boxed@ = @boxImpl@ */ def unbox(x: java.lang.Object): @name@ = @unboxImpl@ -/** The String representation of the scala.@name@ companion object. - */ +/** The String representation of the scala.@name@ companion object. */ override def toString = "object scala.@name@" """ def nonUnitCompanions = "" // todo def cardinalCompanion = """ -/** The smallest value representable as a @name@. - */ +/** The smallest value representable as a @name@. */ final val MinValue = @boxed@.MIN_VALUE -/** The largest value representable as a @name@. - */ +/** The largest value representable as a @name@. */ final val MaxValue = @boxed@.MAX_VALUE """ @@ -372,18 +362,16 @@ class AnyVals extends AnyValReps with AnyValTemplates { object D extends AnyValNum("Double", Some("64-bit IEEE-754 floating point number"), "double") object Z extends AnyValRep("Boolean", None, "boolean") { def classLines = """ -/** - * Negates a Boolean expression. - * - * - `!a` results in `false` if and only if `a` evaluates to `true` and - * - `!a` results in `true` if and only if `a` evaluates to `false`. - * - * @return the negated expression - */ +/** Negates a Boolean expression. + * + * - `!a` results in `false` if and only if `a` evaluates to `true` and + * - `!a` results in `true` if and only if `a` evaluates to `false`. + * + * @return the negated expression + */ def unary_! : Boolean -/** - * Compares two Boolean expressions and returns `true` if they evaluate to the same value. +/** Compares two Boolean expressions and returns `true` if they evaluate to the same value. * * `a == b` returns `true` if and only if * - `a` and `b` are `true` or @@ -400,8 +388,7 @@ def ==(x: Boolean): Boolean */ def !=(x: Boolean): Boolean -/** - * Compares two Boolean expressions and returns `true` if one or both of them evaluate to true. +/** Compares two Boolean expressions and returns `true` if one or both of them evaluate to true. * * `a || b` returns `true` if and only if * - `a` is `true` or @@ -414,8 +401,7 @@ def !=(x: Boolean): Boolean */ def ||(x: Boolean): Boolean -/** - * Compares two Boolean expressions and returns `true` if both of them evaluate to true. +/** Compares two Boolean expressions and returns `true` if both of them evaluate to true. * * `a && b` returns `true` if and only if * - `a` and `b` are `true`. @@ -430,8 +416,7 @@ def &&(x: Boolean): Boolean // def ||(x: => Boolean): Boolean // def &&(x: => Boolean): Boolean -/** - * Compares two Boolean expressions and returns `true` if one or both of them evaluate to true. +/** Compares two Boolean expressions and returns `true` if one or both of them evaluate to true. * * `a | b` returns `true` if and only if * - `a` is `true` or @@ -442,8 +427,7 @@ def &&(x: Boolean): Boolean */ def |(x: Boolean): Boolean -/** - * Compares two Boolean expressions and returns `true` if both of them evaluate to true. +/** Compares two Boolean expressions and returns `true` if both of them evaluate to true. * * `a & b` returns `true` if and only if * - `a` and `b` are `true`. @@ -452,8 +436,7 @@ def |(x: Boolean): Boolean */ def &(x: Boolean): Boolean -/** - * Compares two Boolean expressions and returns `true` if they evaluate to a different value. +/** Compares two Boolean expressions and returns `true` if they evaluate to a different value. * * `a ^ b` returns `true` if and only if * - `a` is `true` and `b` is `false` or @@ -499,5 +482,3 @@ override def getClass(): Class[Boolean] = null def make() = values map (x => (x.name, x.make())) } - -object AnyVals extends AnyVals { } diff --git a/src/compiler/scala/tools/cmd/gen/Codegen.scala b/src/compiler/scala/tools/cmd/gen/Codegen.scala index b49322ab4a..c3aa527ef2 100644 --- a/src/compiler/scala/tools/cmd/gen/Codegen.scala +++ b/src/compiler/scala/tools/cmd/gen/Codegen.scala @@ -6,11 +6,9 @@ package scala.tools.cmd package gen -import scala.language.postfixOps - class Codegen(args: List[String]) extends { val parsed = CodegenSpec(args: _*) -} with CodegenSpec with Instance { } +} with CodegenSpec with Instance object Codegen { def echo(msg: String) = Console println msg @@ -31,7 +29,7 @@ object Codegen { val av = new AnyVals { } av.make() foreach { case (name, code ) => - val file = out / (name + ".scala") toFile; + val file = (out / (name + ".scala")).toFile echo("Writing: " + file) file writeAll code } diff --git a/src/compiler/scala/tools/cmd/package.scala b/src/compiler/scala/tools/cmd/package.scala index 7d67fa738b..9754becf10 100644 --- a/src/compiler/scala/tools/cmd/package.scala +++ b/src/compiler/scala/tools/cmd/package.scala @@ -13,19 +13,19 @@ package object cmd { implicit def implicitConversions = scala.language.implicitConversions implicit def postfixOps = scala.language.postfixOps - private[cmd] def debug(msg: String) = println(msg) + private[cmd] def debug(msg: String): Unit = println(msg) def runAndExit(body: => Unit): Nothing = { body sys.exit(0) } - def toOpt(s: String) = if (s startsWith "--") s else "--" + s - def fromOpt(s: String) = s stripPrefix "--" - def toArgs(line: String) = CommandLineParser tokenize line - def fromArgs(args: List[String]) = args mkString " " + def toOpt(s: String): String = if (s startsWith "--") s else "--" + s + def fromOpt(s: String): String = s stripPrefix "--" + def toArgs(line: String): List[String] = CommandLineParser tokenize line + def fromArgs(args: List[String]): String = args mkString " " - def stripQuotes(s: String) = { + def stripQuotes(s: String): String = { def isQuotedBy(c: Char) = s.length > 0 && s.head == c && s.last == c if (List('"', '\'') exists isQuotedBy) s.tail.init else s } diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index b52e6fdf57..efe436f004 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -57,8 +57,8 @@ trait CompilationUnits { self: Global => // SBT compatibility (SI-6875) // // imagine we have a file named A.scala, which defines a trait named Foo and a module named Main - // Main contains a call to a macro, which calls c.introduceTopLevel to define a mock for Foo - // c.introduceTopLevel creates a virtual file Virt35af32.scala, which contains a class named FooMock extending Foo, + // Main contains a call to a macro, which calls compileLate to define a mock for Foo + // compileLate creates a virtual file Virt35af32.scala, which contains a class named FooMock extending Foo, // and macro expansion instantiates FooMock. the stage is now set. let's see what happens next. // // without this workaround in scalac or without being patched itself, sbt will think that diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index a4093f9afa..94270e4cf3 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -334,22 +334,27 @@ self => def parseStartRule: () => Tree - /** This is the general parse entry point. - */ - def parse(): Tree = { - val t = parseStartRule() + def parseRule[T](rule: this.type => T): T = { + val t = rule(this) accept(EOF) t } + /** This is the general parse entry point. + */ + def parse(): Tree = parseRule(_.parseStartRule()) + + /** This is alternative entry point for repl, script runner, toolbox and quasiquotes. + */ + def parseStats(): List[Tree] = parseRule(_.templateStats()) + /** This is the parse entry point for code which is not self-contained, e.g. * a script which is a series of template statements. They will be * swaddled in Trees until the AST is equivalent to the one returned * by compilationUnit(). */ def scriptBody(): Tree = { - val stmts = templateStats() - accept(EOF) + val stmts = parseStats() def mainModuleName = newTermName(settings.script.value) /* If there is only a single object template in the file and it has a @@ -563,8 +568,8 @@ self => and } - def expectedMsg(token: Int): String = - token2string(token) + " expected but " +token2string(in.token) + " found." + def expectedMsgTemplate(exp: String, fnd: String) = s"$exp expected but $fnd found." + def expectedMsg(token: Int): String = expectedMsgTemplate(token2string(token), token2string(in.token)) /** Consume one token of the specified type, or signal an error if it is not there. */ def accept(token: Int): Int = { @@ -627,6 +632,8 @@ self => def isAnnotation: Boolean = in.token == AT + def isCaseDefStart: Boolean = in.token == CASE + def isLocalModifier: Boolean = in.token match { case ABSTRACT | FINAL | SEALED | IMPLICIT | LAZY => true case _ => false @@ -1137,32 +1144,70 @@ self => }) } - private def interpolatedString(inPattern: Boolean): Tree = atPos(in.offset) { - val start = in.offset - val interpolator = in.name + /** Handle placeholder syntax. + * If evaluating the tree produces placeholders, then make it a function. + */ + private def withPlaceholders(tree: =>Tree, isAny: Boolean): Tree = { + val savedPlaceholderParams = placeholderParams + placeholderParams = List() + var res = tree + if (placeholderParams.nonEmpty && !isWildcard(res)) { + res = atPos(res.pos)(Function(placeholderParams.reverse, res)) + if (isAny) placeholderParams foreach (_.tpt match { + case tpt @ TypeTree() => tpt setType definitions.AnyTpe + case _ => // some ascription + }) + placeholderParams = List() + } + placeholderParams = placeholderParams ::: savedPlaceholderParams + res + } - val partsBuf = new ListBuffer[Tree] - val exprBuf = new ListBuffer[Tree] + /** Consume a USCORE and create a fresh synthetic placeholder param. */ + private def freshPlaceholder(): Tree = { + val start = in.offset + val pname = freshName("x$") in.nextToken() - while (in.token == STRINGPART) { - partsBuf += literal() - exprBuf += ( - if (inPattern) dropAnyBraces(pattern()) - else in.token match { - case IDENTIFIER => atPos(in.offset)(Ident(ident())) - case LBRACE => expr() - case THIS => in.nextToken(); atPos(in.offset)(This(tpnme.EMPTY)) - case _ => syntaxErrorOrIncompleteAnd("error in interpolated string: identifier or block expected", skipIt = true)(EmptyTree) - } - ) - } - if (in.token == STRINGLIT) partsBuf += literal() + val id = atPos(start)(Ident(pname)) + val param = atPos(id.pos.focus)(gen.mkSyntheticParam(pname.toTermName)) + placeholderParams = param :: placeholderParams + id + } + + private def interpolatedString(inPattern: Boolean): Tree = { + def errpolation() = syntaxErrorOrIncompleteAnd("error in interpolated string: identifier or block expected", + skipIt = true)(EmptyTree) + // Like Swiss cheese, with holes + def stringCheese: Tree = atPos(in.offset) { + val start = in.offset + val interpolator = in.name - val t1 = atPos(o2p(start)) { Ident(nme.StringContext) } - val t2 = atPos(start) { Apply(t1, partsBuf.toList) } - t2 setPos t2.pos.makeTransparent - val t3 = Select(t2, interpolator) setPos t2.pos - atPos(start) { Apply(t3, exprBuf.toList) } + val partsBuf = new ListBuffer[Tree] + val exprBuf = new ListBuffer[Tree] + in.nextToken() + while (in.token == STRINGPART) { + partsBuf += literal() + exprBuf += ( + if (inPattern) dropAnyBraces(pattern()) + else in.token match { + case IDENTIFIER => atPos(in.offset)(Ident(ident())) + //case USCORE => freshPlaceholder() // ifonly etapolation + case LBRACE => expr() // dropAnyBraces(expr0(Local)) + case THIS => in.nextToken(); atPos(in.offset)(This(tpnme.EMPTY)) + case _ => errpolation() + } + ) + } + if (in.token == STRINGLIT) partsBuf += literal() + + val t1 = atPos(o2p(start)) { Ident(nme.StringContext) } + val t2 = atPos(start) { Apply(t1, partsBuf.toList) } + t2 setPos t2.pos.makeTransparent + val t3 = Select(t2, interpolator) setPos t2.pos + atPos(start) { Apply(t3, exprBuf.toList) } + } + if (inPattern) stringCheese + else withPlaceholders(stringCheese, isAny = true) // strinterpolator params are Any* by definition } /* ------------- NEW LINES ------------------------------------------------- */ @@ -1260,18 +1305,7 @@ self => */ def expr(): Tree = expr(Local) - def expr(location: Int): Tree = { - val savedPlaceholderParams = placeholderParams - placeholderParams = List() - var res = expr0(location) - if (!placeholderParams.isEmpty && !isWildcard(res)) { - res = atPos(res.pos){ Function(placeholderParams.reverse, res) } - placeholderParams = List() - } - placeholderParams = placeholderParams ::: savedPlaceholderParams - res - } - + def expr(location: Int): Tree = withPlaceholders(expr0(location), isAny = false) def expr0(location: Int): Tree = (in.token: @scala.annotation.switch) match { case IF => @@ -1298,7 +1332,7 @@ self => in.nextToken() if (in.token != LBRACE) catchFromExpr() else inBracesOrNil { - if (in.token == CASE) caseClauses() + if (isCaseDefStart) caseClauses() else catchFromExpr() } } @@ -1520,13 +1554,7 @@ self => case IDENTIFIER | BACKQUOTED_IDENT | THIS | SUPER => path(thisOK = true, typeOK = false) case USCORE => - val start = in.offset - val pname = freshName("x$") - in.nextToken() - val id = atPos(start) (Ident(pname)) - val param = atPos(id.pos.focus){ gen.mkSyntheticParam(pname.toTermName) } - placeholderParams = param :: placeholderParams - id + freshPlaceholder() case LPAREN => atPos(in.offset)(makeParens(commaSeparated(expr()))) case LBRACE => @@ -1613,7 +1641,7 @@ self => */ def blockExpr(): Tree = atPos(in.offset) { inBraces { - if (in.token == CASE) Match(EmptyTree, caseClauses()) + if (isCaseDefStart) Match(EmptyTree, caseClauses()) else block() } } @@ -2605,7 +2633,7 @@ self => case EQUALS => in.nextToken() TypeDef(mods, name, tparams, typ()) - case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE => + case t if t == SUPERTYPE || t == SUBTYPE || t == COMMA || t == RBRACE || isStatSep(t) => TypeDef(mods | Flags.DEFERRED, name, tparams, typeBounds()) case _ => syntaxErrorOrIncompleteAnd("`=', `>:', or `<:' expected", skipIt = true)(EmptyTree) @@ -2906,27 +2934,14 @@ self => stats.toList } - /** Informal - for the repl and other direct parser accessors. - */ - def templateStats(): List[Tree] = templateStatSeq(isPre = false)._2 match { - case Nil => EmptyTree.asList - case stats => stats - } - /** {{{ - * TemplateStatSeq ::= [id [`:' Type] `=>'] TemplateStat {semi TemplateStat} - * TemplateStat ::= Import - * | Annotations Modifiers Def - * | Annotations Modifiers Dcl - * | Expr1 - * | super ArgumentExprs {ArgumentExprs} - * | + * TemplateStatSeq ::= [id [`:' Type] `=>'] TemplateStats * }}} * @param isPre specifies whether in early initializer (true) or not (false) */ def templateStatSeq(isPre : Boolean): (ValDef, List[Tree]) = checkNoEscapingPlaceholders { var self: ValDef = emptyValDef - val stats = new ListBuffer[Tree] + var firstOpt: Option[Tree] = None if (isExprIntro) { in.flushDoc val first = expr(InTemplate) // @S: first statement is potentially converted so cannot be stubbed. @@ -2943,10 +2958,25 @@ self => } in.nextToken() } else { - stats += first + firstOpt = Some(first) acceptStatSepOpt() } } + (self, firstOpt ++: templateStats()) + } + + /** {{{ + * TemplateStats ::= TemplateStat {semi TemplateStat} + * TemplateStat ::= Import + * | Annotations Modifiers Def + * | Annotations Modifiers Dcl + * | Expr1 + * | super ArgumentExprs {ArgumentExprs} + * | + * }}} + */ + def templateStats(): List[Tree] = { + val stats = new ListBuffer[Tree] while (!isStatSeqEnd) { if (in.token == IMPORT) { in.flushDoc @@ -2961,7 +2991,14 @@ self => } acceptStatSepOpt() } - (self, stats.toList) + stats.toList + } + + /** Informal - for the repl and other direct parser accessors. + */ + def templateStatsCompat(): List[Tree] = templateStats() match { + case Nil => EmptyTree.asList + case stats => stats } /** {{{ @@ -3026,14 +3063,14 @@ self => */ def blockStatSeq(): List[Tree] = checkNoEscapingPlaceholders { val stats = new ListBuffer[Tree] - while (!isStatSeqEnd && in.token != CASE) { + while (!isStatSeqEnd && !isCaseDefStart) { if (in.token == IMPORT) { stats ++= importClause() acceptStatSep() } else if (isExprIntro) { stats += statement(InBlock) - if (in.token != RBRACE && in.token != CASE) acceptStatSep() + if (in.token != RBRACE && !isCaseDefStart) acceptStatSep() } else if (isDefIntro || isLocalModifier || isAnnotation) { if (in.token == IMPLICIT) { diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 2a8412b105..6957f85689 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -741,6 +741,10 @@ trait Scanners extends ScannersCommon { finishStringPart() nextRawChar() next.token = LBRACE + } else if (ch == '_') { + finishStringPart() + nextRawChar() + next.token = USCORE } else if (Character.isUnicodeIdentifierStart(ch)) { finishStringPart() do { diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 666f19851d..ed694023d7 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -39,7 +39,7 @@ abstract class TreeBuilder { * x becomes x @ _ * x: T becomes x @ (_: T) */ - private object patvarTransformer extends Transformer { + object patvarTransformer extends Transformer { override def transform(tree: Tree): Tree = tree match { case Ident(name) if (treeInfo.isVarPattern(tree) && name != nme.WILDCARD) => atPos(tree.pos)(Bind(name, atPos(tree.pos.focus) (Ident(nme.WILDCARD)))) diff --git a/src/compiler/scala/tools/nsc/io/DaemonThreadFactory.scala b/src/compiler/scala/tools/nsc/io/DaemonThreadFactory.scala deleted file mode 100644 index 98c3d27202..0000000000 --- a/src/compiler/scala/tools/nsc/io/DaemonThreadFactory.scala +++ /dev/null @@ -1,21 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package io - -import java.util.concurrent._ - -class DaemonThreadFactory extends ThreadFactory { - def newThread(r: Runnable): Thread = { - val thread = new Thread(r) - thread setDaemon true - thread - } -} - -object DaemonThreadFactory { - def newPool() = Executors.newCachedThreadPool(new DaemonThreadFactory) -}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/io/package.scala b/src/compiler/scala/tools/nsc/io/package.scala index 0b2db115fb..5f2f90c284 100644 --- a/src/compiler/scala/tools/nsc/io/package.scala +++ b/src/compiler/scala/tools/nsc/io/package.scala @@ -5,8 +5,6 @@ package scala.tools.nsc -import java.util.concurrent.{ Future, Callable } -import java.util.{ Timer, TimerTask } import scala.language.implicitConversions package object io { @@ -29,16 +27,4 @@ package object io { type JFile = java.io.File implicit def enrichManifest(m: JManifest): Jar.WManifest = Jar.WManifest(m) - private lazy val daemonThreadPool = DaemonThreadFactory.newPool() - - def runnable(body: => Unit): Runnable = new Runnable { override def run() = body } - def callable[T](body: => T): Callable[T] = new Callable[T] { override def call() = body } - def spawn[T](body: => T): Future[T] = daemonThreadPool submit callable(body) - - def newThread(f: Thread => Unit)(body: => Unit): Thread = { - val thread = new Thread(runnable(body)) - f(thread) - thread.start - thread - } } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index c6ea6b23e5..454c9db73c 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -74,7 +74,6 @@ abstract class ClassfileParser { def srcfile = srcfile0 private def optimized = settings.optimise.value - private def currentIsTopLevel = !(currentClass.decodedName containsChar '$') // u1, u2, and u4 are what these data types are called in the JVM spec. // They are an unsigned byte, unsigned char, and unsigned int respectively. @@ -349,7 +348,7 @@ abstract class ClassfileParser { /** Throws an exception signaling a bad tag at given address. */ protected def errorBadTag(start: Int) = - abort("bad constant pool tag ${in.buf(start)} at byte $start") + abort(s"bad constant pool tag ${in.buf(start)} at byte $start") } private def loadClassSymbol(name: Name): Symbol = { diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 1b6443a4cb..f3a22a2cee 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -1340,6 +1340,7 @@ trait Contexts { self: Analyzer => } object ContextMode { + import scala.language.implicitConversions private implicit def liftIntBitsToContextState(bits: Int): ContextMode = apply(bits) def apply(bits: Int): ContextMode = new ContextMode(bits) final val NOmode: ContextMode = 0 diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index c87de8839f..4265efc839 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -815,7 +815,7 @@ trait Implicits { if (search.isDivergent && countdown > 0) { countdown -= 1 implicitSym = i.sym - log("discarding divergent implicit ${implicitSym} during implicit search") + log(s"discarding divergent implicit $implicitSym during implicit search") SearchFailure } else search } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 06892053fa..8ca0d82e93 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -700,7 +700,7 @@ trait Infer extends Checkable { tp nonPrivateMember nme.apply match { case NoSymbol => tp case sym if !sym.isOverloaded && sym.isPublic => OverloadedType(tp, sym.alternatives) - case sym => OverloadedType(tp, sym filter (_.isPublic) alternatives) + case sym => OverloadedType(tp, sym.filter(_.isPublic).alternatives) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 6b9537e27d..b3675d6a82 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -589,18 +589,23 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { /** Expands a term macro used in apply role as `M(2)(3)` in `val x = M(2)(3)`. * @see MacroExpander */ - def macroExpandApply(typer: Typer, expandee: Tree, mode: Mode, pt: Type) = { + def macroExpandApply(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = { object expander extends TermMacroExpander(APPLY_ROLE, typer, expandee, mode, pt) { override def onSuccess(expanded: Tree) = { // prematurely annotate the tree with a macro expansion attachment // so that adapt called indirectly by typer.typed knows that it needs to apply the existential fixup linkExpandeeAndExpanded(expandee, expanded) - var expectedTpe = expandee.tpe - if (isNullaryInvocation(expandee)) expectedTpe = expectedTpe.finalResultType + // approximation is necessary for whitebox macros to guide type inference + // read more in the comments for onDelayed below + def approximate(tp: Type) = { + val undetparams = tp collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol } + deriveTypeWithWildcards(undetparams)(tp) + } + val macroPtApprox = approximate(if (isNullaryInvocation(expandee)) expandee.tpe.finalResultType else expandee.tpe) // `macroExpandApply` is called from `adapt`, where implicit conversions are disabled // therefore we need to re-enable the conversions back temporarily - if (macroDebugVerbose) println(s"typecheck #1 (against expectedTpe = $expectedTpe): $expanded") - val expanded1 = typer.context.withImplicitsEnabled(typer.typed(expanded, mode, expectedTpe)) + if (macroDebugVerbose) println(s"typecheck #1 (against macroPtApprox = $macroPtApprox): $expanded") + val expanded1 = typer.context.withImplicitsEnabled(typer.typed(expanded, mode, macroPtApprox)) if (expanded1.isErrorTyped) { if (macroDebugVerbose) println(s"typecheck #1 has failed: ${typer.context.reportBuffer.errors}") expanded1 @@ -612,6 +617,8 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { } } override def onDelayed(delayed: Tree) = { + // =========== THE SITUATION =========== + // // If we've been delayed (i.e. bailed out of the expansion because of undetermined type params present in the expandee), // then there are two possible situations we're in: // 1) We're in POLYmode, when the typer tests the waters wrt type inference @@ -627,12 +634,43 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { // the undetermined type params. Therefore we need to do something ourselves or otherwise this // expandee will forever remaing not expanded (see SI-5692). A traditional way out of this conundrum // is to call `instantiate` and let the inferencer try to find the way out. It works for simple cases, - // but sometimes, if the inferencer lacks information, it will be forced to approximate. This prevents - // an important class of macros, fundep materializers, from working, which I perceive is a problem we need to solve. - // For details see SI-7470. + // but sometimes, if the inferencer lacks information, it will be forced to approximate. + // + // =========== THE PROBLEM =========== + // + // Consider the following example (thanks, Miles!): + // + // Iso represents an isomorphism between two datatypes: + // 1) An arbitrary one (e.g. a random case class) + // 2) A uniform representation for all datatypes (e.g. an HList) + // + // trait Iso[T, U] { + // def to(t : T) : U + // def from(u : U) : T + // } + // implicit def materializeIso[T, U]: Iso[T, U] = macro ??? + // + // case class Foo(i: Int, s: String, b: Boolean) + // def foo[C, L](c: C)(implicit iso: Iso[C, L]): L = iso.to(c) + // foo(Foo(23, "foo", true)) + // + // In the snippet above, even though we know that there's a fundep going from T to U + // (in a sense that a datatype's uniform representation is unambiguously determined by the datatype, + // e.g. for Foo it will be Int :: String :: Boolean :: HNil), there's no way to convey this information + // to the typechecker. Therefore the typechecker will infer Nothing for L, which is hardly what we want. + // + // =========== THE SOLUTION =========== + // + // To give materializers a chance to say their word before vanilla inference kicks in, + // we infer as much as possible (e.g. in the example above even though L is hopeless, C still can be inferred to Foo) + // and then trigger macro expansion with the undetermined type parameters still there. + // Thanks to that the materializer can take a look at what's going on and react accordingly. val shouldInstantiate = typer.context.undetparams.nonEmpty && !mode.inPolyMode - if (shouldInstantiate) typer.instantiatePossiblyExpectingUnit(delayed, mode, pt) - else delayed + if (shouldInstantiate) { + forced += delayed + typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), pt, keepNothings = false) + macroExpandApply(typer, delayed, mode, pt) + } else delayed } } expander(expandee) @@ -750,10 +788,12 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { * 2) undetparams (sym.isTypeParameter && !sym.isSkolem) */ var hasPendingMacroExpansions = false + private val forced = perRunCaches.newWeakSet[Tree] private val delayed = perRunCaches.newWeakMap[Tree, scala.collection.mutable.Set[Int]]() private def isDelayed(expandee: Tree) = delayed contains expandee private def calculateUndetparams(expandee: Tree): scala.collection.mutable.Set[Int] = - delayed.get(expandee).getOrElse { + if (forced(expandee)) scala.collection.mutable.Set[Int]() + else delayed.getOrElse(expandee, { val calculated = scala.collection.mutable.Set[Symbol]() expandee foreach (sub => { def traverse(sym: Symbol) = if (sym != null && (undetparams contains sym.id)) calculated += sym @@ -762,7 +802,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { }) macroLogVerbose("calculateUndetparams: %s".format(calculated)) calculated map (_.id) - } + }) private val undetparams = perRunCaches.newSet[Int]() def notifyUndetparamsAdded(newUndets: List[Symbol]): Unit = { undetparams ++= newUndets map (_.id) diff --git a/src/compiler/scala/tools/reflect/MacroImplementations.scala b/src/compiler/scala/tools/reflect/MacroImplementations.scala index 8e1bcb5f87..4e3761454d 100644 --- a/src/compiler/scala/tools/reflect/MacroImplementations.scala +++ b/src/compiler/scala/tools/reflect/MacroImplementations.scala @@ -94,7 +94,8 @@ abstract class MacroImplementations { def errorAtIndex(idx: Int, msg: String) = c.error(new OffsetPosition(strTree.pos.source, strTree.pos.point + idx), msg) def wrongConversionString(idx: Int) = errorAtIndex(idx, "wrong conversion string") def illegalConversionCharacter(idx: Int) = errorAtIndex(idx, "illegal conversion character") - def nonEscapedPercent(idx: Int) = errorAtIndex(idx, "percent signs not directly following splicees must be escaped") + def nonEscapedPercent(idx: Int) = errorAtIndex(idx, + "conversions must follow a splice; use %% for literal %, %n for newline") // STEP 1: handle argument conversion // 1) "...${smth}" => okay, equivalent to "...${smth}%s" diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index afaca3396c..8d2f200e99 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -269,17 +269,13 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => } def parse(code: String): Tree = { - val run = new Run reporter.reset() - val wrappedCode = "object wrapper {" + EOL + code + EOL + "}" - val file = new BatchSourceFile("<toolbox>", wrappedCode) + val file = new BatchSourceFile("<toolbox>", code) val unit = new CompilationUnit(file) - phase = run.parserPhase - val parser = newUnitParser(unit) - val wrappedTree = parser.parse() + val parsed = newUnitParser(unit).parseStats() throwIfErrors() - val PackageDef(_, List(ModuleDef(_, _, Template(_, _, _ :: parsed)))) = wrappedTree parsed match { + case Nil => EmptyTree case expr :: Nil => expr case stats :+ expr => Block(stats, expr) } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index 9a6ba56c18..18a806e5ff 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -17,32 +17,38 @@ trait Parsers { self: Quasiquotes => abstract class Parser extends { val global: self.global.type = self.global } with ScalaParser { - /** Wraps given code to obtain a desired parser mode. - * This way we can just re-use standard parser entry point. - */ - def wrapCode(code: String): String = - s"object wrapper { self => $EOL $code $EOL }" - - def unwrapTree(wrappedTree: Tree): Tree = { - val PackageDef(_, List(ModuleDef(_, _, Template(_, _, _ :: parsed)))) = wrappedTree - parsed match { - case tree :: Nil => tree - case stats :+ tree => Block(stats, tree) - } - } - def parse(code: String): Tree = { try { - val wrapped = wrapCode(code) - debug(s"wrapped code\n=${wrapped}\n") - val file = new BatchSourceFile(nme.QUASIQUOTE_FILE, wrapped) - val tree = new QuasiquoteParser(file).parse() - unwrapTree(tree) + val file = new BatchSourceFile(nme.QUASIQUOTE_FILE, code) + new QuasiquoteParser(file).parseRule(entryPoint) } catch { - case mi: MalformedInput => c.abort(c.macroApplication.pos, s"syntax error: ${mi.msg}") + case mi: MalformedInput => c.abort(correspondingPosition(mi.offset), mi.msg) + } + } + + def correspondingPosition(offset: Int): Position = { + val posMapList = posMap.toList + def containsOffset(start: Int, end: Int) = start <= offset && offset <= end + def fallbackPosition = posMapList match { + case (pos1, (start1, end1)) :: _ if start1 > offset => pos1 + case _ :+ ((pos2, (start2, end2))) if offset > end2 => pos2.withPoint(pos2.point + (end2 - start2)) } + posMapList.sliding(2).collect { + case (pos1, (start1, end1)) :: _ if containsOffset(start1, end1) => (pos1, offset - start1) + case (pos1, (_, end1)) :: (_, (start2, _)) :: _ if containsOffset(end1, start2) => (pos1, end1) + case _ :: (pos2, (start2, end2)) :: _ if containsOffset(start2, end2) => (pos2, offset - start2) + }.map { case (pos, offset) => + pos.withPoint(pos.point + offset) + }.toList.headOption.getOrElse(fallbackPosition) } + override def token2string(token: Int): String = token match { + case EOF => "end of quote" + case _ => super.token2string(token) + } + + def entryPoint: QuasiquoteParser => Tree + class QuasiquoteParser(source0: SourceFile) extends SourceFileParser(source0) { override val treeBuilder = new ParserTreeBuilder { // q"(..$xs)" @@ -73,9 +79,11 @@ trait Parsers { self: Quasiquotes => } else super.caseClause() - def isHole = isIdent && holeMap.contains(in.name) + def isHole: Boolean = isIdent && holeMap.contains(in.name) - override def isAnnotation: Boolean = super.isAnnotation || (isHole && lookingAhead { isAnnotation }) + override def isAnnotation: Boolean = super.isAnnotation || (isHole && lookingAhead { isAnnotation }) + + override def isCaseDefStart: Boolean = super.isCaseDefStart || (in.token == EOF) override def isModifier: Boolean = super.isModifier || (isHole && lookingAhead { isModifier }) @@ -85,6 +93,12 @@ trait Parsers { self: Quasiquotes => override def isDclIntro: Boolean = super.isDclIntro || (isHole && lookingAhead { isDclIntro }) + override def isStatSep(token: Int) = token == EOF || super.isStatSep(token) + + override def expectedMsg(token: Int): String = + if (isHole) expectedMsgTemplate(token2string(token), "splicee") + else super.expectedMsg(token) + // $mods def foo // $mods T override def readAnnots(annot: => Tree): List[Tree] = in.token match { @@ -101,34 +115,26 @@ trait Parsers { self: Quasiquotes => } } - object TermParser extends Parser - - object CaseParser extends Parser { - override def wrapCode(code: String) = super.wrapCode("something match { case " + code + " }") - - override def unwrapTree(wrappedTree: Tree): Tree = { - val Match(_, head :: tail) = super.unwrapTree(wrappedTree) - if (tail.nonEmpty) - c.abort(c.macroApplication.pos, "Can't parse more than one casedef, consider generating a match tree instead") - head + object TermParser extends Parser { + def entryPoint = _.templateStats() match { + case Nil => EmptyTree + case tree :: Nil => tree + case stats :+ tree => Block(stats, tree) } } - object PatternParser extends Parser { - override def wrapCode(code: String) = super.wrapCode("something match { case " + code + " => }") - - override def unwrapTree(wrappedTree: Tree): Tree = { - val Match(_, List(CaseDef(pat, _, _))) = super.unwrapTree(wrappedTree) - pat - } + object TypeParser extends Parser { + def entryPoint = _.typ() } - object TypeParser extends Parser { - override def wrapCode(code: String) = super.wrapCode("type T = " + code) + object CaseParser extends Parser { + def entryPoint = _.caseClause() + } - override def unwrapTree(wrappedTree: Tree): Tree = { - val TypeDef(_, _, _, rhs) = super.unwrapTree(wrappedTree) - rhs + object PatternParser extends Parser { + def entryPoint = { parser => + val pat = parser.noSeq.pattern1() + parser.treeBuilder.patvarTransformer.transform(pat) } } }
\ No newline at end of file diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala index b680c25f76..b3ac1e293a 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala @@ -17,18 +17,31 @@ trait Placeholders { self: Quasiquotes => // Step 1: Transform Scala source with holes into vanilla Scala source lazy val holeMap = new HoleMap() + lazy val posMap = mutable.ListMap[Position, (Int, Int)]() lazy val code = { val sb = new StringBuilder() val sessionSuffix = randomUUID().toString.replace("-", "").substring(0, 8) + "$" - foreach2(args, parts.init) { (tree, p) => - val (part, cardinality) = parseDots(p) + def appendPart(value: String, pos: Position) = { + val start = sb.length + sb.append(value) + val end = sb.length + posMap += pos -> (start, end) + } + + def appendHole(tree: Tree, cardinality: Cardinality) = { val placeholderName = c.freshName(TermName(nme.QUASIQUOTE_PREFIX + sessionSuffix)) - sb.append(part) sb.append(placeholderName) holeMap(placeholderName) = Hole(tree, cardinality) } - sb.append(parts.last) + + foreach2(args, parts.init) { case (tree, (p, pos)) => + val (part, cardinality) = parseDots(p) + appendPart(part, pos) + appendHole(tree, cardinality) + } + val (p, pos) = parts.last + appendPart(p, pos) sb.toString } diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala index fe954e0bfd..ee99a5e280 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala @@ -17,7 +17,7 @@ abstract class Quasiquotes extends Parsers lazy val (universe: Tree, args, parts, parse, reify) = c.macroApplication match { case Apply(Select(Select(Apply(Select(universe0, _), List(Apply(_, parts0))), interpolator0), method0), args0) => val parts1 = parts0.map { - case Literal(Constant(s: String)) => s + case lit @ Literal(Constant(s: String)) => s -> lit.pos case part => c.abort(part.pos, "Quasiquotes can only be used with literal strings") } val reify0 = method0 match { diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala index bdd6a02043..82f2c5dc74 100644 --- a/src/compiler/scala/tools/util/PathResolver.scala +++ b/src/compiler/scala/tools/util/PathResolver.scala @@ -8,8 +8,8 @@ package tools package util import scala.tools.reflect.WrappedProperties.AccessControl -import scala.tools.nsc.{ Settings, GenericRunnerSettings } -import scala.tools.nsc.util.{ ClassPath, JavaClassPath, ScalaClassLoader } +import scala.tools.nsc.{ Settings } +import scala.tools.nsc.util.{ ClassPath, JavaClassPath } import scala.reflect.io.{ File, Directory, Path, AbstractFile } import ClassPath.{ JavaContext, DefaultJavaContext, join, split } import PartialFunction.condOpt |