summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/reflect/macros/contexts/Evals.scala9
-rw-r--r--src/compiler/scala/reflect/macros/contexts/Names.scala28
-rw-r--r--src/compiler/scala/reflect/macros/contexts/Typers.scala4
-rw-r--r--src/compiler/scala/reflect/reify/Reifier.scala14
-rw-r--r--src/compiler/scala/reflect/reify/codegen/GenTrees.scala2
-rw-r--r--src/compiler/scala/reflect/reify/codegen/GenTypes.scala6
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala4
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeGen.scala2
-rw-r--r--src/compiler/scala/tools/nsc/ast/Trees.scala55
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala119
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala7
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala20
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Members.scala8
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala38
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala25
-rw-r--r--src/compiler/scala/tools/nsc/transform/OverridingPairs.scala13
-rw-r--r--src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala36
-rw-r--r--src/compiler/scala/tools/nsc/transform/TailCalls.scala27
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Checkable.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala11
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala29
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala1
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala17
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala13
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala98
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Unapplies.scala4
-rw-r--r--src/compiler/scala/tools/reflect/FastTrack.scala6
-rw-r--r--src/compiler/scala/tools/reflect/FormatInterpolator.scala329
-rw-r--r--src/compiler/scala/tools/reflect/MacroImplementations.scala171
-rw-r--r--src/compiler/scala/tools/reflect/ToolBox.scala17
-rw-r--r--src/compiler/scala/tools/reflect/ToolBoxFactory.scala14
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Holes.scala97
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala37
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala46
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala10
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala63
46 files changed, 851 insertions, 562 deletions
diff --git a/src/compiler/scala/reflect/macros/contexts/Evals.scala b/src/compiler/scala/reflect/macros/contexts/Evals.scala
index 84928ddf86..180a998c39 100644
--- a/src/compiler/scala/reflect/macros/contexts/Evals.scala
+++ b/src/compiler/scala/reflect/macros/contexts/Evals.scala
@@ -12,7 +12,12 @@ trait Evals {
private lazy val evalImporter = ru.mkImporter(universe).asInstanceOf[ru.Importer { val from: universe.type }]
def eval[T](expr: Expr[T]): T = {
- val imported = evalImporter.importTree(expr.tree)
- evalToolBox.eval(imported).asInstanceOf[T]
+ expr.tree match {
+ case global.Literal(global.Constant(value)) =>
+ value.asInstanceOf[T]
+ case _ =>
+ val imported = evalImporter.importTree(expr.tree)
+ evalToolBox.eval(imported).asInstanceOf[T]
+ }
}
} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/macros/contexts/Names.scala b/src/compiler/scala/reflect/macros/contexts/Names.scala
index c2f14cf0f1..299af40b94 100644
--- a/src/compiler/scala/reflect/macros/contexts/Names.scala
+++ b/src/compiler/scala/reflect/macros/contexts/Names.scala
@@ -4,7 +4,9 @@ package contexts
trait Names {
self: Context =>
- def freshNameCreator = callsiteTyper.context.unit.fresh
+ import global._
+
+ def freshNameCreator = globalFreshNameCreator
def fresh(): String =
freshName()
@@ -16,11 +18,25 @@ trait Names {
freshName[NameType](name)
def freshName(): String =
- freshName("fresh$")
-
- def freshName(name: String): String =
- freshNameCreator.newName(name)
+ freshName(nme.FRESH_PREFIX)
+
+ def freshName(name: String): String = {
+ // In comparison with the first version of freshName, current "fresh" names
+ // at least can't clash with legible user-written identifiers and are much less likely to clash with each other.
+ // It is still not good enough however, because the counter gets reset every time we create a new Global.
+ //
+ // This would most certainly cause problems if Scala featured something like introduceTopLevel,
+ // but even for def macros this can lead to unexpected troubles. Imagine that one Global
+ // creates a term of an anonymous type with a member featuring a "fresh" name, and then another Global
+ // imports that term with a wildcard and then generates a "fresh" name of its own. Given unlucky
+ // circumstances these "fresh" names might end up clashing.
+ //
+ // TODO: hopefully SI-7823 will provide an ultimate answer to this problem.
+ // In the meanwhile I will also keep open the original issue: SI-6879 "c.freshName is broken".
+ val sortOfUniqueSuffix = freshNameCreator.newName(nme.FRESH_SUFFIX)
+ name + "$" + sortOfUniqueSuffix
+ }
def freshName[NameType <: Name](name: NameType): NameType =
- name.mapName(freshNameCreator.newName(_)).asInstanceOf[NameType]
+ name.mapName(freshName(_)).asInstanceOf[NameType]
} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/macros/contexts/Typers.scala b/src/compiler/scala/reflect/macros/contexts/Typers.scala
index 85204d0f1b..c1ab17027f 100644
--- a/src/compiler/scala/reflect/macros/contexts/Typers.scala
+++ b/src/compiler/scala/reflect/macros/contexts/Typers.scala
@@ -46,7 +46,7 @@ trait Typers {
universe.analyzer.inferImplicit(tree, viewTpe, true, callsiteTyper.context, silent, withMacrosDisabled, pos, (pos, msg) => throw TypecheckException(pos, msg))
}
- def resetAllAttrs(tree: Tree): Tree = universe.resetAllAttrs(universe.duplicateAndKeepPositions(tree))
+ def resetLocalAttrs(tree: Tree): Tree = universe.resetAttrs(universe.duplicateAndKeepPositions(tree))
- def resetLocalAttrs(tree: Tree): Tree = universe.resetLocalAttrs(universe.duplicateAndKeepPositions(tree))
+ def untypecheck(tree: Tree): Tree = resetLocalAttrs(tree)
}
diff --git a/src/compiler/scala/reflect/reify/Reifier.scala b/src/compiler/scala/reflect/reify/Reifier.scala
index ad0632f93e..6b0a0ee8d7 100644
--- a/src/compiler/scala/reflect/reify/Reifier.scala
+++ b/src/compiler/scala/reflect/reify/Reifier.scala
@@ -86,7 +86,7 @@ abstract class Reifier extends States
throw new Error("reifee %s of type %s is not supported".format(reifee, if (reifee == null) "null" else reifee.getClass.toString))
}
- // todo. why do we resetAllAttrs?
+ // todo. why do we reset attrs?
//
// typically we do some preprocessing before reification and
// the code emitted/moved around during preprocessing is very hard to typecheck, so we leave it as it is
@@ -109,19 +109,11 @@ abstract class Reifier extends States
//
// todo. this is a common problem with non-trivial macros in our current macro system
// needs to be solved some day
- // maybe try `resetLocalAttrs` once the dust settles
- var importantSymbols = Set[Symbol](
- NothingClass, AnyClass, SingletonClass, PredefModule, ScalaRunTimeModule, TypeCreatorClass, TreeCreatorClass, MirrorClass,
- ApiUniverseClass, JavaUniverseClass, ReflectRuntimePackage, runDefinitions.ReflectRuntimeCurrentMirror)
- importantSymbols ++= importantSymbols map (_.companionSymbol)
- importantSymbols ++= importantSymbols map (_.moduleClass)
- importantSymbols ++= importantSymbols map (_.linkedClassOfClass)
- def isImportantSymbol(sym: Symbol): Boolean = sym != null && sym != NoSymbol && importantSymbols(sym)
- val untyped = resetAllAttrs(result, leaveAlone = {
+ // upd. a new hope: https://groups.google.com/forum/#!topic/scala-internals/TtCTPlj_qcQ
+ val untyped = resetAttrs(result, leaveAlone = {
case ValDef(_, u, _, _) if u == nme.UNIVERSE_SHORT => true
case ValDef(_, m, _, _) if m == nme.MIRROR_SHORT => true
case tree if symtab.syms contains tree.symbol => true
- case tree if isImportantSymbol(tree.symbol) => true
case _ => false
})
diff --git a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala
index f6b3c42ca9..b082796757 100644
--- a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala
+++ b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala
@@ -140,7 +140,7 @@ trait GenTrees {
if (sym == NoSymbol) {
// this sometimes happens, e.g. for binds that don't have a body
// or for untyped code generated during previous phases
- // (see a comment in Reifiers about the latter, starting with "why do we resetAllAttrs?")
+ // (see a comment in Reifiers about the latter, starting with "why do we reset attrs?")
mirrorCall(nme.Ident, reify(name))
}
else if (!sym.isLocalToReifee) {
diff --git a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala
index 99b968be3b..a90a3a338b 100644
--- a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala
+++ b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala
@@ -60,7 +60,7 @@ trait GenTypes {
reifyProduct(tpe)
case tpe @ NullaryMethodType(restpe) =>
reifyProduct(tpe)
- case tpe @ AnnotatedType(anns, underlying, selfsym) =>
+ case tpe @ AnnotatedType(anns, underlying) =>
reifyAnnotatedType(tpe)
case _ =>
reifyToughType(tpe)
@@ -162,8 +162,8 @@ trait GenTypes {
/** Reify an annotated type, i.e. the one that makes us deal with AnnotationInfos */
private def reifyAnnotatedType(tpe: AnnotatedType): Tree = {
- val AnnotatedType(anns, underlying, selfsym) = tpe
- mirrorFactoryCall(nme.AnnotatedType, mkList(anns map reifyAnnotationInfo), reify(underlying), reify(selfsym))
+ val AnnotatedType(anns, underlying) = tpe
+ mirrorFactoryCall(nme.AnnotatedType, mkList(anns map reifyAnnotationInfo), reify(underlying))
}
/** Reify a tough type, i.e. the one that leads to creation of auxiliary symbols */
diff --git a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala
index c68b248240..eafecf9462 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala
@@ -619,7 +619,7 @@ abstract class TreeBrowsers {
toDocument(result) :: ")")
)
- case AnnotatedType(annots, tp, _) =>
+ case AnnotatedType(annots, tp) =>
Document.group(
Document.nest(4, "AnnotatedType(" :/:
annots.mkString("[", ",", "]") :/:
@@ -632,7 +632,7 @@ abstract class TreeBrowsers {
Document.group("(" :/: symsToDocument(tparams) :/: "), ") :/:
toDocument(result) :: ")"))
- case global.analyzer.ImportType(expr) =>
+ case ImportType(expr) =>
"ImportType(" + expr.toString + ")"
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
index 4ac6672727..a87a04472a 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
@@ -37,7 +37,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
NoSymbol
newImport NoPosition
setFlag SYNTHETIC
- setInfo analyzer.ImportType(qual)
+ setInfo ImportType(qual)
)
val importTree = (
Import(qual, selector)
diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala
index 24bce0636d..7a7d4ac0b2 100644
--- a/src/compiler/scala/tools/nsc/ast/Trees.scala
+++ b/src/compiler/scala/tools/nsc/ast/Trees.scala
@@ -176,13 +176,24 @@ trait Trees extends scala.reflect.internal.Trees { self: Global =>
}
}
- /** resets symbol and tpe fields in a tree, @see ResetAttrs
- */
-// def resetAllAttrs[A<:Tree](x:A): A = { new ResetAttrsTraverser().traverse(x); x }
-// def resetLocalAttrs[A<:Tree](x:A): A = { new ResetLocalAttrsTraverser().traverse(x); x }
-
- def resetAllAttrs(x: Tree, leaveAlone: Tree => Boolean = null): Tree = new ResetAttrs(false, leaveAlone).transform(x)
- def resetLocalAttrs(x: Tree, leaveAlone: Tree => Boolean = null): Tree = new ResetAttrs(true, leaveAlone).transform(x)
+ // Finally, noone resetAllAttrs it anymore, so I'm removing it from the compiler.
+ // Even though it's with great pleasure I'm doing that, I'll leave its body here to warn future generations about what happened in the past.
+ //
+ // So what actually happened in the past is that we used to have two flavors of resetAttrs: resetAllAttrs and resetLocalAttrs.
+ // resetAllAttrs destroyed all symbols and types in the tree in order to reset its state to something suitable for retypechecking
+ // and/or embedding into bigger trees / different lexical scopes. (Btw here's some background on why people would want to use
+ // reset attrs in the first place: https://groups.google.com/forum/#!topic/scala-internals/TtCTPlj_qcQ).
+ //
+ // However resetAllAttrs was more of a poison than of a treatment, because along with locally defined symbols that are the cause
+ // for almost every or maybe even every case of tree corruption, it erased external bindings that sometimes could not be restored.
+ // This is how we came up with resetLocalAttrs that left external bindings alone, and that was a big step forward.
+ // Then slowly but steadily we've evicted all usages of resetAllAttrs from our codebase in favor of resetLocalAttrs
+ // and have been living happily ever after.
+ //
+ // def resetAllAttrs(x: Tree, leaveAlone: Tree => Boolean = null): Tree = new ResetAttrs(localOnly = false, leaveAlone).transform(x)
+
+ /** @see ResetAttrs */
+ def resetAttrs(x: Tree, leaveAlone: Tree => Boolean = null): Tree = new ResetAttrs(leaveAlone).transform(x)
/** A transformer which resets symbol and tpe fields of all nodes in a given tree,
* with special treatment of:
@@ -193,7 +204,7 @@ trait Trees extends scala.reflect.internal.Trees { self: Global =>
*
* (bq:) This transformer has mutable state and should be discarded after use
*/
- private class ResetAttrs(localOnly: Boolean, leaveAlone: Tree => Boolean = null, keepLabels: Boolean = false) {
+ private class ResetAttrs(leaveAlone: Tree => Boolean = null) {
// this used to be based on -Ydebug, but the need for logging in this code is so situational
// that I've reverted to a hard-coded constant here.
val debug = false
@@ -277,29 +288,18 @@ trait Trees extends scala.reflect.internal.Trees { self: Global =>
// vetoXXX local variables declared below describe the conditions under which we cannot erase symbols.
//
// The first reason to not erase symbols is the threat of non-idempotency (SI-5464).
- // Here we take care of labels (SI-5562) and references to package classes (SI-5705).
+ // Here we take care of references to package classes (SI-5705).
// There are other non-idempotencies, but they are not worked around yet.
//
- // The second reason has to do with the fact that resetAttrs itself has limited usefulness.
- //
- // First of all, why do we need resetAttrs? Gor one, it's absolutely required to move trees around.
- // One cannot just take a typed tree from one lexical context and transplant it somewhere else.
- // Most likely symbols defined by those trees will become borked and the compiler will blow up (SI-5797).
- // To work around we just erase all symbols and types and then hope that we'll be able to correctly retypecheck.
- // For ones who're not affected by scalac Stockholm syndrome, this might seem to be an extremely naive fix, but well...
- //
- // Of course, sometimes erasing everything won't work, because if a given identifier got resolved to something
- // in one lexical scope, it can get resolved to something else.
- //
- // What do we do in these cases? Enter the workaround for the workaround: resetLocalAttrs, which only destroys
- // locally defined symbols, but doesn't touch references to stuff declared outside of a given tree.
- // That's what localOnly and vetoScope are for.
+ // The second reason has to do with the fact that resetAttrs needs to be less destructive.
+ // Erasing locally-defined symbols is useful to prevent tree corruption, but erasing external bindings is not,
+ // therefore we want to retain those bindings, especially given that restoring them can be impossible
+ // if we move these trees into lexical contexts different from their original locations.
if (dupl.hasSymbol) {
val sym = dupl.symbol
- val vetoScope = localOnly && !(locals contains sym)
- val vetoLabel = keepLabels && sym.isLabel
+ val vetoScope = !(locals contains sym)
val vetoThis = dupl.isInstanceOf[This] && sym.isPackageClass
- if (!(vetoScope || vetoLabel || vetoThis)) dupl.symbol = NoSymbol
+ if (!(vetoScope || vetoThis)) dupl.symbol = NoSymbol
}
dupl.clearType()
}
@@ -308,10 +308,9 @@ trait Trees extends scala.reflect.internal.Trees { self: Global =>
}
def transform(x: Tree): Tree = {
- if (localOnly)
new MarkLocals().traverse(x)
- if (localOnly && debug) {
+ if (debug) {
assert(locals.size == orderedLocals.size)
val msg = orderedLocals.toList filter {_ != NoSymbol} map {" " + _} mkString EOL
trace("locals (%d total): %n".format(orderedLocals.size))(msg)
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index 0728fff74f..e3d2bf14a0 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -33,7 +33,7 @@ trait ParsersCommon extends ScannersCommon { self =>
import global.{currentUnit => _, _}
def newLiteral(const: Any) = Literal(Constant(const))
- def literalUnit = newLiteral(())
+ def literalUnit = gen.mkSyntheticUnit()
/** This is now an abstract class, only to work around the optimizer:
* methods in traits are never inlined.
@@ -837,8 +837,14 @@ self =>
if (samePrecedence)
checkHeadAssoc(leftAssoc)
- def loop(top: Tree): Tree =
- if (canReduce) loop(finishBinaryOp(isExpr, popOpInfo(), top)) else top
+ def loop(top: Tree): Tree = if (canReduce) {
+ val info = popOpInfo()
+ if (!isExpr && info.targs.nonEmpty) {
+ syntaxError(info.offset, "type application is not allowed in pattern")
+ info.targs.foreach(_.setType(ErrorType))
+ }
+ loop(finishBinaryOp(isExpr, info, top))
+ } else top
loop(top)
}
@@ -1219,7 +1225,7 @@ self =>
// Like Swiss cheese, with holes
def stringCheese: Tree = atPos(in.offset) {
val start = in.offset
- val interpolator = in.name
+ val interpolator = in.name.encoded // ident() for INTERPOLATIONID
val partsBuf = new ListBuffer[Tree]
val exprBuf = new ListBuffer[Tree]
@@ -2131,8 +2137,6 @@ self =>
/* -------- PARAMETERS ------------------------------------------- */
- def allowTypelessParams = false
-
/** {{{
* ParamClauses ::= {ParamClause} [[nl] `(' implicit Params `)']
* ParamClause ::= [nl] `(' [Params] `)'
@@ -2147,56 +2151,6 @@ self =>
def paramClauses(owner: Name, contextBounds: List[Tree], ofCaseClass: Boolean): List[List[ValDef]] = {
var implicitmod = 0
var caseParam = ofCaseClass
- def param(): ValDef = {
- val start = in.offset
- val annots = annotations(skipNewLines = false)
- var mods = Modifiers(Flags.PARAM)
- if (owner.isTypeName) {
- mods = modifiers() | Flags.PARAMACCESSOR
- if (mods.isLazy) syntaxError("lazy modifier not allowed here. Use call-by-name parameters instead", skipIt = false)
- in.token match {
- case v @ (VAL | VAR) =>
- mods = mods withPosition (in.token.toLong, tokenRange(in))
- if (v == VAR) mods |= Flags.MUTABLE
- in.nextToken()
- case _ =>
- if (mods.flags != Flags.PARAMACCESSOR) accept(VAL)
- if (!caseParam) mods |= Flags.PrivateLocal
- }
- if (caseParam) mods |= Flags.CASEACCESSOR
- }
- val nameOffset = in.offset
- val name = ident()
- var bynamemod = 0
- val tpt =
- if (((settings.YmethodInfer && !owner.isTypeName) || allowTypelessParams) && in.token != COLON) {
- TypeTree()
- } else { // XX-METHOD-INFER
- accept(COLON)
- if (in.token == ARROW) {
- if (owner.isTypeName && !mods.hasLocalFlag)
- syntaxError(
- in.offset,
- (if (mods.isMutable) "`var'" else "`val'") +
- " parameters may not be call-by-name", skipIt = false)
- else if (implicitmod != 0)
- syntaxError(
- in.offset,
- "implicit parameters may not be call-by-name", skipIt = false)
- else bynamemod = Flags.BYNAMEPARAM
- }
- paramType()
- }
- val default =
- if (in.token == EQUALS) {
- in.nextToken()
- mods |= Flags.DEFAULTPARAM
- expr()
- } else EmptyTree
- atPos(start, if (name == nme.ERROR) start else nameOffset) {
- ValDef((mods | implicitmod.toLong | bynamemod) withAnnotations annots, name.toTermName, tpt, default)
- }
- }
def paramClause(): List[ValDef] = {
if (in.token == RPAREN)
return Nil
@@ -2205,7 +2159,7 @@ self =>
in.nextToken()
implicitmod = Flags.IMPLICIT
}
- commaSeparated(param())
+ commaSeparated(param(owner, implicitmod, caseParam ))
}
val vds = new ListBuffer[List[ValDef]]
val start = in.offset
@@ -2253,6 +2207,57 @@ self =>
}
}
+ def param(owner: Name, implicitmod: Int, caseParam: Boolean): ValDef = {
+ val start = in.offset
+ val annots = annotations(skipNewLines = false)
+ var mods = Modifiers(Flags.PARAM)
+ if (owner.isTypeName) {
+ mods = modifiers() | Flags.PARAMACCESSOR
+ if (mods.isLazy) syntaxError("lazy modifier not allowed here. Use call-by-name parameters instead", skipIt = false)
+ in.token match {
+ case v @ (VAL | VAR) =>
+ mods = mods withPosition (in.token.toLong, tokenRange(in))
+ if (v == VAR) mods |= Flags.MUTABLE
+ in.nextToken()
+ case _ =>
+ if (mods.flags != Flags.PARAMACCESSOR) accept(VAL)
+ if (!caseParam) mods |= Flags.PrivateLocal
+ }
+ if (caseParam) mods |= Flags.CASEACCESSOR
+ }
+ val nameOffset = in.offset
+ val name = ident()
+ var bynamemod = 0
+ val tpt =
+ if ((settings.YmethodInfer && !owner.isTypeName) && in.token != COLON) {
+ TypeTree()
+ } else { // XX-METHOD-INFER
+ accept(COLON)
+ if (in.token == ARROW) {
+ if (owner.isTypeName && !mods.hasLocalFlag)
+ syntaxError(
+ in.offset,
+ (if (mods.isMutable) "`var'" else "`val'") +
+ " parameters may not be call-by-name", skipIt = false)
+ else if (implicitmod != 0)
+ syntaxError(
+ in.offset,
+ "implicit parameters may not be call-by-name", skipIt = false)
+ else bynamemod = Flags.BYNAMEPARAM
+ }
+ paramType()
+ }
+ val default =
+ if (in.token == EQUALS) {
+ in.nextToken()
+ mods |= Flags.DEFAULTPARAM
+ expr()
+ } else EmptyTree
+ atPos(start, if (name == nme.ERROR) start else nameOffset) {
+ ValDef((mods | implicitmod.toLong | bynamemod) withAnnotations annots, name.toTermName, tpt, default)
+ }
+ }
+
/** {{{
* TypeParamClauseOpt ::= [TypeParamClause]
* TypeParamClause ::= `[' VariantTypeParam {`,' VariantTypeParam} `]']
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index 8011abc1ed..e8d46704c3 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -475,14 +475,17 @@ trait Scanners extends ScannersCommon {
if (token == INTERPOLATIONID) {
nextRawChar()
if (ch == '\"') {
- nextRawChar()
- if (ch == '\"') {
+ val lookahead = lookaheadReader
+ lookahead.nextChar()
+ if (lookahead.ch == '\"') {
+ nextRawChar() // now eat it
offset += 3
nextRawChar()
getStringPart(multiLine = true)
sepRegions = STRINGPART :: sepRegions // indicate string part
sepRegions = STRINGLIT :: sepRegions // once more to indicate multi line string part
} else {
+ nextChar()
token = STRINGLIT
strVal = ""
}
diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
index cfee988efc..525dcffb0c 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
@@ -56,7 +56,7 @@ abstract class TreeBuilder {
/** Create tree representing (unencoded) binary operation expression or pattern. */
def makeBinop(isExpr: Boolean, left: Tree, op: TermName, right: Tree, opPos: Position, targs: List[Tree] = Nil): Tree = {
- require(isExpr || targs.isEmpty, s"Incompatible args to makeBinop: !isExpr but targs=$targs")
+ require(isExpr || targs.isEmpty || targs.exists(_.isErroneous), s"Incompatible args to makeBinop: !isExpr but targs=$targs")
def mkSelection(t: Tree) = {
def sel = atPos(opPos union t.pos)(Select(stripParens(t), op.encode))
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index 1332d01dbd..b650cdfa09 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -46,8 +46,10 @@ abstract class GenICode extends SubComponent {
var unit: CompilationUnit = NoCompilationUnit
override def run() {
- scalaPrimitives.init()
- classes.clear()
+ if (!settings.isBCodeActive) {
+ scalaPrimitives.init()
+ classes.clear()
+ }
super.run()
}
@@ -1007,8 +1009,15 @@ abstract class GenICode extends SubComponent {
}
// emit conversion
- if (generatedType != expectedType)
- adapt(generatedType, expectedType, resCtx, tree.pos)
+ if (generatedType != expectedType) {
+ tree match {
+ case Literal(Constant(null)) if generatedType == NullReference =>
+ // literal null on the stack (as opposed to a boxed null, see SI-8233),
+ // we can bypass `adapt` which would otherwise emitt a redundant [DROP, CONSTANT(null)]
+ case _ =>
+ adapt(generatedType, expectedType, resCtx, tree.pos)
+ }
+ }
resCtx
}
@@ -1058,6 +1067,9 @@ abstract class GenICode extends SubComponent {
case (NothingReference, _) =>
ctx.bb.emit(THROW(ThrowableClass))
ctx.bb.enterIgnoreMode()
+ case (NullReference, REFERENCE(_)) =>
+ // SI-8223 we can't assume that the stack contains a `null`, it might contain a Null$
+ ctx.bb.emit(Seq(DROP(from), CONSTANT(Constant(null))))
case _ if from isAssignabledTo to =>
()
case (_, UNIT) =>
diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala
index 267fa15312..64146585e5 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala
@@ -21,7 +21,7 @@ trait Members {
import global._
- object NoCode extends Code(null, "NoCode") {
+ object NoCode extends Code(null, TermName("NoCode")) {
override def blocksList: List[BasicBlock] = Nil
}
@@ -29,8 +29,8 @@ trait Members {
* This class represents the intermediate code of a method or
* other multi-block piece of code, like exception handlers.
*/
- class Code(method: IMethod, name: String) {
- def this(method: IMethod) = this(method, method.symbol.decodedName.toString.intern)
+ class Code(method: IMethod, name: Name) {
+ def this(method: IMethod) = this(method, method.symbol.name)
/** The set of all blocks */
val blocks = mutable.ListBuffer[BasicBlock]()
@@ -82,7 +82,7 @@ trait Members {
}
/** This methods returns a string representation of the ICode */
- override def toString = "ICode '" + name + "'"
+ override def toString = "ICode '" + name.decoded + "'"
/* Compute a unique new label */
def nextLabel: Int = {
diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
index 633e71a756..a6d0d3b9fa 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
@@ -393,7 +393,7 @@ trait TypeKinds { self: ICodes =>
// if the first two cases exist because they do or as a defensive measure, but
// at the time I added it, RefinedTypes were indeed reaching here.
case ExistentialType(_, t) => toTypeKind(t)
- case AnnotatedType(_, t, _) => toTypeKind(t)
+ case AnnotatedType(_, t) => toTypeKind(t)
case RefinedType(parents, _) => parents map toTypeKind reduceLeft lub
// For sure WildcardTypes shouldn't reach here either, but when
// debugging such situations this may come in handy.
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
index 6b1bb5b220..18ccced75e 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
@@ -516,7 +516,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
// !!! Iulian says types which make no sense after erasure should not reach here, which includes the ExistentialType, AnnotatedType, RefinedType.
case ExistentialType(_, t) => toTypeKind(t) // TODO shouldn't get here but the following does: akka-actor/src/main/scala/akka/util/WildcardTree.scala
- case AnnotatedType(_, w, _) => toTypeKind(w) // TODO test/files/jvm/annotations.scala causes an AnnotatedType to reach here.
+ case AnnotatedType(_, w) => toTypeKind(w) // TODO test/files/jvm/annotations.scala causes an AnnotatedType to reach here.
case RefinedType(parents, _) => parents map toTypeKind reduceLeft jvmWiseLUB
// For sure WildcardTypes shouldn't reach here either, but when debugging such situations this may come in handy.
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 307f42c0bc..a3114a3d7b 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -161,7 +161,6 @@ trait ScalaSettings extends AbsScalaSettings
val nopredef = BooleanSetting ("-Yno-predef", "Compile without importing Predef.")
val noAdaptedArgs = BooleanSetting ("-Yno-adapted-args", "Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver.")
val Yrecursion = IntSetting ("-Yrecursion", "Set recursion depth used when locking symbols.", 0, Some((0, Int.MaxValue)), (_: String) => None)
- val selfInAnnots = BooleanSetting ("-Yself-in-annots", "Include a \"self\" identifier inside of annotations.")
val Xshowtrees = BooleanSetting ("-Yshow-trees", "(Requires -Xprint:) Print detailed ASTs in formatted form.")
val XshowtreesCompact
= BooleanSetting ("-Yshow-trees-compact", "(Requires -Xprint:) Print detailed ASTs in compact form.")
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
index ce3e7b1bb5..55f45257dc 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
@@ -55,23 +55,26 @@ abstract class Pickler extends SubComponent {
case _ =>
}
}
- // If there are any erroneous types in the tree, then we will crash
- // when we pickle it: so let's report an error instead. We know next
- // to nothing about what happened, but our supposition is a lot better
- // than "bad type: <error>" in terms of explanatory power.
- for (t <- unit.body) {
- if (t.isErroneous) {
- unit.error(t.pos, "erroneous or inaccessible type")
- return
- }
- if (!t.isDef && t.hasSymbolField && t.symbol.isTermMacro) {
- unit.error(t.pos, "macro has not been expanded")
- return
- }
+ try {
+ pickle(unit.body)
+ } catch {
+ case e: FatalError =>
+ for (t <- unit.body) {
+ // If there are any erroneous types in the tree, then we will crash
+ // when we pickle it: so let's report an error instead. We know next
+ // to nothing about what happened, but our supposition is a lot better
+ // than "bad type: <error>" in terms of explanatory power.
+ //
+ // OPT: do this only as a recovery after fatal error. Checking in advance was expensive.
+ if (t.isErroneous) {
+ if (settings.debug) e.printStackTrace()
+ unit.error(t.pos, "erroneous or inaccessible type")
+ return
+ }
+ }
+ throw e
}
-
- pickle(unit.body)
}
}
@@ -238,9 +241,8 @@ abstract class Pickler extends SubComponent {
case ExistentialType(tparams, restpe) =>
putType(restpe)
putSymbols(tparams)
- case AnnotatedType(_, underlying, selfsym) =>
+ case AnnotatedType(_, underlying) =>
putType(underlying)
- if (settings.selfInAnnots) putSymbol(selfsym)
tp.staticAnnotations foreach putAnnotation
case _ =>
throw new FatalError("bad type: " + tp + "(" + tp.getClass + ")")
@@ -450,7 +452,7 @@ abstract class Pickler extends SubComponent {
case PolyType(tparams, restpe) => writeRef(restpe); writeRefs(tparams)
case ExistentialType(tparams, restpe) => writeRef(restpe); writeRefs(tparams)
case StaticallyAnnotatedType(annots, tp) => writeRef(tp) ; writeRefs(annots)
- case AnnotatedType(_, tp, _) => writeTypeBody(tp) // write the underlying type if there are no static annotations
+ case AnnotatedType(_, tp) => writeTypeBody(tp) // write the underlying type if there are no static annotations
case CompoundType(parents, _, clazz) => writeRef(clazz); writeRefs(parents)
}
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index 6732900ef2..ccfddab94a 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -61,7 +61,7 @@ abstract class Erasure extends AddInterfaces
parents foreach traverse
case ClassInfoType(parents, _, _) =>
parents foreach traverse
- case AnnotatedType(_, atp, _) =>
+ case AnnotatedType(_, atp) =>
traverse(atp)
case _ =>
mapOver(tp)
@@ -92,11 +92,22 @@ abstract class Erasure extends AddInterfaces
// more rigorous way up front rather than catching it after the fact,
// but that will be more involved.
private def dotCleanup(sig: String): String = {
+ // OPT 50% of time in generic signatures (~1% of compile time) was in this method, hence the imperative rewrite.
var last: Char = '\u0000'
- sig map {
- case '.' if last != '>' => last = '.' ; '$'
- case ch => last = ch ; ch
+ var i = 0
+ val len = sig.length
+ val copy: Array[Char] = sig.toCharArray
+ var changed = false
+ while (i < sig.length) {
+ val ch = copy(i)
+ if (ch == '.' && last != '>') {
+ copy(i) = '$'
+ changed = true
+ }
+ last = ch
+ i += 1
}
+ if (changed) new String(copy) else sig
}
/** This object is only used for sanity testing when -check:genjvm is set.
@@ -302,7 +313,7 @@ abstract class Erasure extends AddInterfaces
boxedSig(parent)
case ClassInfoType(parents, _, _) =>
superSig(parents)
- case AnnotatedType(_, atp, _) =>
+ case AnnotatedType(_, atp) =>
jsig(atp, existentiallyBound, toplevel, primitiveOK)
case BoundedWildcardType(bounds) =>
println("something's wrong: "+sym0+":"+sym0.tpe+" has a bounded wildcard type")
@@ -522,6 +533,8 @@ abstract class Erasure extends AddInterfaces
class Eraser(_context: Context) extends Typer(_context) with TypeAdapter {
val typer = this.asInstanceOf[analyzer.Typer]
+ override protected def stabilize(tree: Tree, pre: Type, mode: Mode, pt: Type): Tree = tree
+
/** Replace member references as follows:
*
* - `x == y` for == in class Any becomes `x equals y` with equals in class Object.
@@ -760,7 +773,7 @@ abstract class Erasure extends AddInterfaces
|| super.exclude(sym)
|| !sym.hasTypeAt(currentRun.refchecksPhase.id)
)
- override def matches(sym1: Symbol, sym2: Symbol) = true
+ override def matches(lo: Symbol, high: Symbol) = true
}
def isErasureDoubleDef(pair: SymbolPair) = {
import pair._
diff --git a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala
index 4222c4d8c8..870eafbf20 100644
--- a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala
+++ b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala
@@ -24,15 +24,16 @@ abstract class OverridingPairs extends SymbolPairs {
/** Symbols to exclude: Here these are constructors and private/artifact symbols,
* including bridges. But it may be refined in subclasses.
*/
- override protected def exclude(sym: Symbol) = (sym hasFlag PRIVATE | ARTIFACT) || sym.isConstructor
+ override protected def exclude(sym: Symbol) = sym.isPrivateLocal || sym.isArtifact || sym.isConstructor
/** Types always match. Term symbols match if their member types
* relative to `self` match.
*/
- override protected def matches(sym1: Symbol, sym2: Symbol) = sym1.isType || (
- (sym1.owner != sym2.owner)
- && !exclude(sym2)
- && relatively.matches(sym1, sym2)
- )
+ override protected def matches(lo: Symbol, high: Symbol) = lo.isType || (
+ (lo.owner != high.owner) // don't try to form pairs from overloaded members
+ && !high.isPrivate // private or private[this] members never are overriden
+ && !exclude(lo) // this admits private, as one can't have a private member that matches a less-private member.
+ && relatively.matches(lo, high)
+ ) // TODO we don't call exclude(high), should we?
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
index c505d9dc5f..c59b726076 100644
--- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
+++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
@@ -306,6 +306,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
/** Return the specialized name of 'sym' in the given environment. It
* guarantees the same result regardless of the map order by sorting
* type variables alphabetically.
+ *
+ * !!! Is this safe in the face of the following?
+ * scala> trait T { def foo[A] = 0}; object O extends T { override def foo[B] = 0 }
*/
private def specializedName(sym: Symbol, env: TypeEnv): TermName = {
val tvars = (
@@ -391,13 +394,16 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
* enclosing member with the annotation.
*/
private def needsSpecialization(env: TypeEnv, sym: Symbol): Boolean = (
- !sym.ownerChain.exists(_ hasAnnotation UnspecializedClass) && (
+ !hasUnspecializableAnnotation(sym) && (
specializedTypeVars(sym).intersect(env.keySet).diff(wasSpecializedForTypeVars(sym)).nonEmpty
|| sym.isClassConstructor && (sym.enclClass.typeParams exists (_.isSpecialized))
|| isNormalizedMember(sym) && info(sym).typeBoundsIn(env)
)
)
+ private def hasUnspecializableAnnotation(sym: Symbol): Boolean =
+ sym.ownerChain.exists(_ hasAnnotation UnspecializedClass)
+
def isNormalizedMember(m: Symbol) = m.isSpecialized && (info get m exists {
case NormalizedMember(_) => true
case _ => false
@@ -433,12 +439,12 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
else
specializedTypeVars(sym.typeParams zip args collect { case (tp, arg) if tp.isSpecialized => arg })
- case PolyType(tparams, resTpe) => specializedTypeVars(resTpe :: tparams.map(_.info))
+ case PolyType(tparams, resTpe) => specializedTypeVars(resTpe :: mapList(tparams)(symInfo)) // OPT
// since this method may be run at phase typer (before uncurry, where NMTs are eliminated)
case NullaryMethodType(resTpe) => specializedTypeVars(resTpe)
- case MethodType(argSyms, resTpe) => specializedTypeVars(resTpe :: argSyms.map(_.tpe))
+ case MethodType(argSyms, resTpe) => specializedTypeVars(resTpe :: mapList(argSyms)(symTpe)) // OPT
case ExistentialType(_, res) => specializedTypeVars(res)
- case AnnotatedType(_, tp, _) => specializedTypeVars(tp)
+ case AnnotatedType(_, tp) => specializedTypeVars(tp)
case TypeBounds(lo, hi) => specializedTypeVars(lo :: hi :: Nil)
case RefinedType(parents, _) => parents flatMap specializedTypeVars toSet
case _ => immutable.Set.empty
@@ -907,16 +913,20 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
if (sym.isMethod) {
- val stvars = specializedTypeVars(sym)
- if (stvars.nonEmpty)
- debuglog("specialized %s on %s".format(sym.fullLocationString, stvars.map(_.name).mkString(", ")))
+ if (hasUnspecializableAnnotation(sym)) {
+ List()
+ } else {
+ val stvars = specializedTypeVars(sym)
+ if (stvars.nonEmpty)
+ debuglog("specialized %s on %s".format(sym.fullLocationString, stvars.map(_.name).mkString(", ")))
- val tps1 = if (sym.isConstructor) tps filter (sym.info.paramTypes contains _) else tps
- val tps2 = tps1 filter stvars
- if (!sym.isDeferred)
- addConcreteSpecMethod(sym)
+ val tps1 = if (sym.isConstructor) tps filter (sym.info.paramTypes contains _) else tps
+ val tps2 = tps1 filter stvars
+ if (!sym.isDeferred)
+ addConcreteSpecMethod(sym)
- specializeOn(tps2)
+ specializeOn(tps2)
+ }
}
else Nil
}
@@ -1098,7 +1108,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
case (ThisType(_), _) => unify(tp1.widen, tp2, env, strict)
case (_, ThisType(_)) => unify(tp1, tp2.widen, env, strict)
case (RefinedType(_, _), RefinedType(_, _)) => env
- case (AnnotatedType(_, tp1, _), tp2) => unify(tp2, tp1, env, strict)
+ case (AnnotatedType(_, tp1), tp2) => unify(tp2, tp1, env, strict)
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 _ =>
diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala
index b471d16ddd..5973c70583 100644
--- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala
+++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala
@@ -128,6 +128,7 @@ abstract class TailCalls extends Transform {
logResult(msg)(method.newValue(nme.THIS, pos, SYNTHETIC) setInfo currentClass.typeOfThis)
}
override def toString = s"${method.name} tparams=$tparams tailPos=$tailPos label=$label label info=${label.info}"
+
}
object EmptyTailContext extends TailContext {
@@ -185,6 +186,18 @@ abstract class TailCalls extends Transform {
private def noTailContext() = new ClonedTailContext(ctx, tailPos = false)
private def yesTailContext() = new ClonedTailContext(ctx, tailPos = true)
+
+ override def transformUnit(unit: CompilationUnit): Unit = {
+ try {
+ super.transformUnit(unit)
+ } finally {
+ // OPT clear these after each compilation unit
+ failPositions.clear()
+ failReasons.clear()
+ accessed.clear()
+ }
+ }
+
/** Rewrite this tree to contain no tail recursive calls */
def transform(tree: Tree, nctx: TailContext): Tree = {
val saved = ctx
@@ -218,12 +231,12 @@ abstract class TailCalls extends Transform {
*/
def fail(reason: String) = {
debuglog("Cannot rewrite recursive call at: " + fun.pos + " because: " + reason)
- failReasons(ctx) = reason
+ if (ctx.isMandatory) failReasons(ctx) = reason
treeCopy.Apply(tree, noTailTransform(target), transformArgs)
}
/* Position of failure is that of the tree being considered. */
def failHere(reason: String) = {
- failPositions(ctx) = fun.pos
+ if (ctx.isMandatory) failPositions(ctx) = fun.pos
fail(reason)
}
def rewriteTailCall(recv: Tree): Tree = {
@@ -237,7 +250,8 @@ abstract class TailCalls extends Transform {
if (!ctx.isEligible) fail("it is neither private nor final so can be overridden")
else if (!isRecursiveCall) {
- if (receiverIsSuper) failHere("it contains a recursive call targeting a supertype")
+ if (ctx.isMandatory && receiverIsSuper) // OPT expensive check, avoid unless we will actually report the error
+ failHere("it contains a recursive call targeting a supertype")
else failHere(defaultReason)
}
else if (!matchesTypeArgs) failHere("it is called recursively with different type arguments")
@@ -245,6 +259,11 @@ abstract class TailCalls extends Transform {
else if (!receiverIsSame) failHere("it changes type of 'this' on a polymorphic recursive call")
else rewriteTailCall(receiver)
}
+
+ def isEligible(tree: DefDef) = {
+ val sym = tree.symbol
+ !(sym.hasAccessorFlag || sym.isConstructor)
+ }
tree match {
case ValDef(_, _, _, _) =>
@@ -253,7 +272,7 @@ abstract class TailCalls extends Transform {
super.transform(tree)
- case dd @ DefDef(_, name, _, vparamss0, _, rhs0) if !dd.symbol.hasAccessorFlag =>
+ case dd @ DefDef(_, name, _, vparamss0, _, rhs0) if isEligible(dd) =>
val newCtx = new DefDefTailContext(dd)
if (newCtx.isMandatory && !(newCtx containsRecursiveCall rhs0))
unit.error(tree.pos, "@tailrec annotated method contains no recursive calls")
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index e193cf3de2..e7ea686bc8 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -105,12 +105,11 @@ abstract class UnCurry extends InfoTransform
*/
def isByNameRef(tree: Tree) = (
tree.isTerm
- && !byNameArgs(tree)
&& (tree.symbol ne null)
&& (isByName(tree.symbol))
+ && !byNameArgs(tree)
)
-
// ------- Handling non-local returns -------------------------------------------------
/** The type of a non-local return expression with given argument type */
diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala
index 94f8f509fc..b899cd8994 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala
@@ -186,7 +186,7 @@ trait Checkable {
* additional conditions holds:
* - either A or B is effectively final
* - neither A nor B is a trait (i.e. both are actual classes, not eligible for mixin)
- * - both A and B are sealed, and every possible pairing of their children is irreconcilable
+ * - both A and B are sealed/final, and every possible pairing of their children is irreconcilable
*
* TODO: the last two conditions of the last possibility (that the symbols are not of
* classes being compiled in the current run) are because this currently runs too early,
@@ -198,8 +198,9 @@ trait Checkable {
isEffectivelyFinal(sym1) // initialization important
|| isEffectivelyFinal(sym2)
|| !sym1.isTrait && !sym2.isTrait
- || sym1.isSealed && sym2.isSealed && allChildrenAreIrreconcilable(sym1, sym2) && !currentRun.compiles(sym1) && !currentRun.compiles(sym2)
+ || isSealedOrFinal(sym1) && isSealedOrFinal(sym2) && allChildrenAreIrreconcilable(sym1, sym2) && !currentRun.compiles(sym1) && !currentRun.compiles(sym2)
)
+ private def isSealedOrFinal(sym: Symbol) = sym.isSealed || sym.isFinal
private def isEffectivelyFinal(sym: Symbol): Boolean = (
// initialization important
sym.initialize.isEffectivelyFinal || (
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index 4d0eda2377..2043eb5d5d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -418,7 +418,7 @@ trait ContextErrors {
case TypeRef(_, _, arg :: _) if arg.typeSymbol == TupleClass(funArity) && funArity > 1 =>
sm"""|
|Note: The expected type requires a one-argument function accepting a $funArity-Tuple.
- | Consider a pattern matching anoynmous function, `{ case $example => ... }`"""
+ | Consider a pattern matching anonymous function, `{ case $example => ... }`"""
case _ => ""
}
case _ => ""
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index 53bc9a2772..5b7956a757 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -457,7 +457,9 @@ trait Contexts { self: Analyzer =>
c.prefix = prefixInChild
c.enclClass = if (isTemplateOrPackage) c else enclClass
c(ConstructorSuffix) = !isTemplateOrPackage && c(ConstructorSuffix)
- c.enclMethod = if (isDefDef) c else enclMethod
+
+ // SI-8245 `isLazy` need to skip lazy getters to ensure `return` binds to the right place
+ c.enclMethod = if (isDefDef && !owner.isLazy) c else enclMethod
registerContext(c.asInstanceOf[analyzer.Context])
debuglog("[context] ++ " + c.unit + " / " + tree.summaryString)
@@ -1303,7 +1305,7 @@ trait Contexts { self: Analyzer =>
var renamed = false
var selectors = tree.selectors
def current = selectors.head
- while (selectors.nonEmpty && result == NoSymbol) {
+ while ((selectors ne Nil) && result == NoSymbol) {
if (current.rename == name.toTermName)
result = qual.tpe.nonLocalMember( // new to address #2733: consider only non-local members for imports
if (name.isTypeName) current.name.toTypeName else current.name)
@@ -1353,9 +1355,8 @@ trait Contexts { self: Analyzer =>
override def toString = tree.toString
}
- case class ImportType(expr: Tree) extends Type {
- override def safeToString = "ImportType("+expr+")"
- }
+ type ImportType = global.ImportType
+ val ImportType = global.ImportType
}
object ContextMode {
diff --git a/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala b/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala
index 73572bcae9..1f1ccbe359 100644
--- a/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/DestructureTypes.scala
@@ -180,7 +180,7 @@ trait DestructureTypes {
case SuperType(thistp, supertp) => product(tp, this("this", thistp), this("super", supertp))
case ThisType(clazz) => product(tp, wrapAtom(clazz))
case TypeVar(inst, constr) => product(tp, this("inst", inst), typeConstraint(constr))
- case AnnotatedType(annotations, underlying, _) => annotatedType(annotations, underlying)
+ case AnnotatedType(annotations, underlying) => annotatedType(annotations, underlying)
case ExistentialType(tparams, underlying) => polyFunction(tparams, underlying)
case PolyType(tparams, restpe) => polyFunction(tparams, restpe)
case MethodType(params, restpe) => monoFunction(params, restpe)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 06a1e21e8b..776920ed42 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -137,10 +137,6 @@ trait Implicits {
private val improvesCache = perRunCaches.newMap[(ImplicitInfo, ImplicitInfo), Boolean]()
private val implicitSearchId = { var id = 1 ; () => try id finally id += 1 }
- private def isInvalidConversionTarget(tpe: Type): Boolean = tpe match {
- case Function1(_, out) => AnyRefClass.tpe <:< out
- case _ => false
- }
private def isInvalidConversionSource(tpe: Type): Boolean = tpe match {
case Function1(in, _) => in <:< NullClass.tpe
case _ => false
@@ -389,7 +385,7 @@ trait Implicits {
private def dominates(dtor: Type, dted: Type): Boolean = {
def core(tp: Type): Type = tp.dealiasWiden match {
case RefinedType(parents, defs) => intersectionType(parents map core, tp.typeSymbol.owner)
- case AnnotatedType(annots, tp, selfsym) => core(tp)
+ case AnnotatedType(annots, tp) => core(tp)
case ExistentialType(tparams, result) => core(result).subst(tparams, tparams map (t => core(t.info.bounds.hi)))
case PolyType(tparams, result) => core(result).subst(tparams, tparams map (t => core(t.info.bounds.hi)))
case _ => tp
@@ -629,7 +625,8 @@ trait Implicits {
// for instance, if we have `class C[T]` and `implicit def conv[T: Numeric](c: C[T]) = ???`
// then Scaladoc will give us something of type `C[T]`, and it would like to know
// that `conv` is potentially available under such and such conditions
- case tree if isImplicitMethodType(tree.tpe) && !isScalaDoc => applyImplicitArgs(tree)
+ case tree if isImplicitMethodType(tree.tpe) && !isScalaDoc =>
+ applyImplicitArgs(tree)
case tree => tree
}
case _ => fallback
@@ -791,7 +788,7 @@ trait Implicits {
final class LocalShadower extends Shadower {
val shadowed = util.HashSet[Name](512)
def addInfos(infos: Infos) {
- shadowed addEntries infos.map(_.name)
+ infos.foreach(i => shadowed.addEntry(i.name))
}
def isShadowed(name: Name) = shadowed(name)
}
@@ -808,7 +805,6 @@ trait Implicits {
private def isIneligible(info: ImplicitInfo) = (
info.isCyclicOrErroneous
|| isView && (info.sym eq Predef_conforms)
- || shadower.isShadowed(info.name)
|| (!context.macrosEnabled && info.sym.isTermMacro)
)
@@ -817,6 +813,7 @@ trait Implicits {
def survives(info: ImplicitInfo) = (
!isIneligible(info) // cyclic, erroneous, shadowed, or specially excluded
&& isPlausiblyCompatible(info.tpe, wildPt) // optimization to avoid matchesPt
+ && !shadower.isShadowed(info.name) // OPT rare, only check for plausible candidates
&& matchesPt(info) // stable and matches expected type
)
/** The implicits that are not valid because they come later in the source and
@@ -1060,7 +1057,7 @@ trait Implicits {
getParts(restpe)
case RefinedType(ps, _) =>
for (p <- ps) getParts(p)
- case AnnotatedType(_, t, _) =>
+ case AnnotatedType(_, t) =>
getParts(t)
case ExistentialType(_, t) =>
getParts(t)
@@ -1361,11 +1358,17 @@ trait Implicits {
if (context.ambiguousErrors)
context.issueAmbiguousError(AmbiguousImplicitTypeError(tree, msg))
}
- if (isInvalidConversionTarget(pt)) {
- maybeInvalidConversionError("the result type of an implicit conversion must be more specific than AnyRef")
- result = SearchFailure
+ pt match {
+ case Function1(_, out) =>
+ def prohibit(sym: Symbol) = if (sym.tpe <:< out) {
+ maybeInvalidConversionError(s"the result type of an implicit conversion must be more specific than ${sym.name}")
+ result = SearchFailure
+ }
+ prohibit(AnyRefClass)
+ if (settings.isScala211) prohibit(AnyValClass)
+ case _ => false
}
- else if (settings.isScala211 && isInvalidConversionSource(pt)) {
+ if (settings.isScala211 && isInvalidConversionSource(pt)) {
maybeInvalidConversionError("an expression of type Null is ineligible for implicit conversion")
result = SearchFailure
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index dd0923a696..997fd6fc65 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -595,6 +595,7 @@ trait Infer extends Checkable {
}
private[typechecker] def followApply(tp: Type): Type = tp match {
+ case _ if tp.isError => tp // SI-8228, `ErrorType nonPrivateMember nme.apply` returns an member with an erroneous type!
case NullaryMethodType(restp) =>
val restp1 = followApply(restp)
if (restp1 eq restp) tp else restp1
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 27e8698676..886dbed6d6 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -1069,8 +1069,9 @@ trait Namers extends MethodSynthesis {
}
def overriddenSymbol(resTp: Type) = {
+ lazy val schema: Type = methodTypeSchema(resTp) // OPT create once. Must be lazy to avoid cycles in neg/t5093.scala
intersectionType(methOwner.info.parents).nonPrivateMember(meth.name).filter { sym =>
- sym != NoSymbol && (site.memberType(sym) matches methodTypeSchema(resTp))
+ sym != NoSymbol && (site.memberType(sym) matches schema)
}
}
// TODO: see whether this or something similar would work instead:
@@ -1204,7 +1205,7 @@ trait Namers extends MethodSynthesis {
* flag.
*/
private def addDefaultGetters(meth: Symbol, ddef: DefDef, vparamss: List[List[ValDef]], tparams: List[TypeDef], overriddenSymbol: => Symbol) {
- val DefDef(_, _, rtparams0, rvparamss0, _, _) = resetLocalAttrs(ddef.duplicate)
+ val DefDef(_, _, rtparams0, rvparamss0, _, _) = resetAttrs(ddef.duplicate)
// having defs here is important to make sure that there's no sneaky tree sharing
// in methods with multiple default parameters
def rtparams = rtparams0.map(_.duplicate)
@@ -1291,7 +1292,7 @@ trait Namers extends MethodSynthesis {
return // fix #3649 (prevent crash in erroneous source code)
}
}
- val ClassDef(_, _, rtparams, _) = resetLocalAttrs(cdef.duplicate)
+ val ClassDef(_, _, rtparams, _) = resetAttrs(cdef.duplicate)
defTparams = rtparams.map(rt => copyTypeDef(rt)(mods = rt.mods &~ (COVARIANT | CONTRAVARIANT)))
nmr
}
@@ -1407,8 +1408,14 @@ trait Namers extends MethodSynthesis {
if (expr1.isErrorTyped)
ErrorType
else {
- if (!treeInfo.isStableIdentifierPattern(expr1))
- typer.TyperErrorGen.UnstableTreeError(expr1)
+ expr1 match {
+ case This(_) =>
+ // SI-8207 okay, typedIdent expands Ident(self) to C.this which doesn't satisfy the next case
+ // TODO should we change `typedIdent` not to expand to the `Ident` to a `This`?
+ case _ if treeInfo.isStableIdentifierPattern(expr1) =>
+ case _ =>
+ typer.TyperErrorGen.UnstableTreeError(expr1)
+ }
val newImport = treeCopy.Import(imp, expr1, selectors).asInstanceOf[Import]
checkSelectors(newImport)
diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
index 46ff98875f..6a4df415ae 100644
--- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
@@ -173,7 +173,7 @@ trait NamesDefaults { self: Analyzer =>
// setSymbol below is important because the 'selected' function might be overloaded. by
// assigning the correct method symbol, typedSelect will just assign the type. the reason
// to still call 'typed' is to correctly infer singleton types, SI-5259.
- val selectPos =
+ val selectPos =
if(qual.pos.isRange && baseFun.pos.isRange) qual.pos.union(baseFun.pos).withStart(Math.min(qual.pos.end, baseFun.pos.end))
else baseFun.pos
val f = blockTyper.typedOperator(Select(newQual, selected).setSymbol(baseFun1.symbol).setPos(selectPos))
@@ -287,7 +287,7 @@ trait NamesDefaults { self: Analyzer =>
}
else {
// TODO In 83c9c764b, we tried to a stable type here to fix SI-7234. But the resulting TypeTree over a
- // singleton type without an original TypeTree fails to retypecheck after a resetLocalAttrs (SI-7516),
+ // singleton type without an original TypeTree fails to retypecheck after a resetAttrs (SI-7516),
// which is important for (at least) macros.
arg.tpe
}
@@ -310,7 +310,7 @@ trait NamesDefaults { self: Analyzer =>
new ChangeOwnerTraverser(context.owner, sym) traverse arg // fixes #4502
if (repeated) arg match {
case WildcardStarArg(expr) => expr
- case _ => blockTyper typed gen.mkSeqApply(resetLocalAttrs(arg))
+ case _ => blockTyper typed gen.mkSeqApply(resetAttrs(arg))
} else arg
}
Some(atPos(body.pos)(ValDef(sym, body).setType(NoType)))
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 4ba8d56da0..2125e281f0 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -1414,7 +1414,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
case TypeRef(pre, sym, args) =>
tree match {
case tt: TypeTree if tt.original == null => // SI-7783 don't warn about inferred types
- // FIXME: reconcile this check with one in resetAllAttrs
+ // FIXME: reconcile this check with one in resetAttrs
case _ => checkUndesiredProperties(sym, tree.pos)
}
if(sym.isJavaDefined)
@@ -1427,7 +1427,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
private def checkTypeRefBounds(tp: Type, tree: Tree) = {
var skipBounds = false
tp match {
- case AnnotatedType(ann :: Nil, underlying, selfSym) if ann.symbol == UncheckedBoundsClass =>
+ case AnnotatedType(ann :: Nil, underlying) if ann.symbol == UncheckedBoundsClass =>
skipBounds = true
underlying
case TypeRef(pre, sym, args) =>
@@ -1470,7 +1470,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
}
doTypeTraversal(tree) {
- case tp @ AnnotatedType(annots, _, _) =>
+ case tp @ AnnotatedType(annots, _) =>
applyChecks(annots)
case tp =>
}
@@ -1596,6 +1596,10 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
}
}
+ private def checkUnexpandedMacro(t: Tree) =
+ if (!t.isDef && t.hasSymbolField && t.symbol.isTermMacro)
+ unit.error(t.pos, "macro has not been expanded")
+
override def transform(tree: Tree): Tree = {
val savedLocalTyper = localTyper
val savedCurrentApplication = currentApplication
@@ -1755,6 +1759,9 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
varianceValidator.traverse(tt.original) // See SI-7872
case _ =>
}
+
+ checkUnexpandedMacro(result)
+
result
} catch {
case ex: TypeError =>
diff --git a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala
index 995f98cc2c..57f27a05fd 100644
--- a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala
@@ -46,7 +46,7 @@ trait StdAttachments {
* The parameter is of type `Any`, because macros can expand both into trees and into annotations.
*/
def hasMacroExpansionAttachment(any: Any): Boolean = any match {
- case tree: Tree => tree.attachments.get[MacroExpansionAttachment].isDefined
+ case tree: Tree => tree.hasAttachment[MacroExpansionAttachment]
case _ => false
}
@@ -96,7 +96,7 @@ trait StdAttachments {
*/
def isMacroExpansionSuppressed(tree: Tree): Boolean =
( settings.Ymacroexpand.value == settings.MacroExpand.None // SI-6812
- || tree.attachments.get[SuppressMacroExpansionAttachment.type].isDefined
+ || tree.hasAttachment[SuppressMacroExpansionAttachment.type]
|| (tree match {
// we have to account for the fact that during typechecking an expandee might become wrapped,
// i.e. surrounded by an inferred implicit argument application or by an inferred type argument application.
@@ -150,7 +150,7 @@ trait StdAttachments {
/** Determines whether a tree should or should not be adapted,
* because someone has put MacroImplRefAttachment on it.
*/
- def isMacroImplRef(tree: Tree): Boolean = tree.attachments.get[MacroImplRefAttachment.type].isDefined
+ def isMacroImplRef(tree: Tree): Boolean = tree.hasAttachment[MacroImplRefAttachment.type]
/** Since mkInvoke, the applyDynamic/selectDynamic/etc desugarer, is disconnected
* from typedNamedApply, the applyDynamicNamed argument rewriter, the latter
diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
index b706e1af6b..06796eca8e 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
@@ -542,7 +542,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
case TypeRef(pre, _, _) => isThisType(pre)
case SingleType(pre, _) => isThisType(pre)
case RefinedType(parents, _) => parents exists isThisType
- case AnnotatedType(_, tp, _) => isThisType(tp)
+ case AnnotatedType(_, tp) => isThisType(tp)
case _ => false
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 9b7c79b614..10fe530445 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -555,7 +555,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
val qual = typedQualifier { atPos(tree.pos.makeTransparent) {
tree match {
- case Ident(_) => Ident(nme.PACKAGEkw)
+ case Ident(_) => Ident(rootMirror.getPackageObjectWithMember(pre, sym))
case Select(qual, _) => Select(qual, nme.PACKAGEkw)
case SelectFromTypeTree(qual, _) => Select(qual, nme.PACKAGEkw)
}
@@ -579,7 +579,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
* 3. Turn tree type into stable type if possible and required by context.
* 4. Give getClass calls a more precise type based on the type of the target of the call.
*/
- private def stabilize(tree: Tree, pre: Type, mode: Mode, pt: Type): Tree = {
+ protected def stabilize(tree: Tree, pre: Type, mode: Mode, pt: Type): Tree = {
+
// Side effect time! Don't be an idiot like me and think you
// can move "val sym = tree.symbol" before this line, because
// inferExprAlternative side-effects the tree's symbol.
@@ -831,7 +832,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
else tpr.typed(withImplicitArgs, mode, pt)
}
orElse { _ =>
- val resetTree = resetLocalAttrs(original)
+ val resetTree = resetAttrs(original)
debuglog(s"fallback on implicits: ${tree}/$resetTree")
val tree1 = typed(resetTree, mode)
// Q: `typed` already calls `pluginsTyped` and `adapt`. the only difference here is that
@@ -992,7 +993,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def adaptMismatchedSkolems() = {
def canIgnoreMismatch = (
!context.reportErrors && isPastTyper
- || tree.attachments.get[MacroExpansionAttachment].isDefined
+ || tree.hasAttachment[MacroExpansionAttachment]
)
def bound = pt match {
case ExistentialType(qs, _) => qs
@@ -1122,7 +1123,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (treeInfo.isMacroApplication(tree)) adapt(unmarkMacroImplRef(tree), mode, pt, original)
else tree
} else tree.tpe match {
- case atp @ AnnotatedType(_, _, _) if canAdaptAnnotations(tree, this, mode, pt) => // (-1)
+ case atp @ AnnotatedType(_, _) if canAdaptAnnotations(tree, this, mode, pt) => // (-1)
adaptAnnotations(tree, this, mode, pt)
case ct @ ConstantType(value) if mode.inNone(TYPEmode | FUNmode) && (ct <:< pt) && canAdaptConstantTypeToLiteral => // (0)
adaptConstant(value)
@@ -2232,14 +2233,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
DeprecatedParamNameError(p, n)
}
}
- }
- if (meth.isStructuralRefinementMember)
- checkMethodStructuralCompatible(ddef)
+ if (meth.isStructuralRefinementMember)
+ checkMethodStructuralCompatible(ddef)
- if (meth.isImplicit && !meth.isSynthetic) meth.info.paramss match {
- case List(param) :: _ if !param.isImplicit =>
- checkFeature(ddef.pos, ImplicitConversionsFeature, meth.toString)
- case _ =>
+ if (meth.isImplicit && !meth.isSynthetic) meth.info.paramss match {
+ case List(param) :: _ if !param.isImplicit =>
+ checkFeature(ddef.pos, ImplicitConversionsFeature, meth.toString)
+ case _ =>
+ }
}
treeCopy.DefDef(ddef, typedMods, ddef.name, tparams1, vparamss1, tpt1, rhs1) setType NoType
@@ -2306,7 +2307,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
context.scope.unlink(ldef.symbol)
val sym2 = namer.enterInScope(
context.owner.newLabel(ldef.name, ldef.pos) setInfo MethodType(List(), restpe))
- val rhs2 = typed(resetAllAttrs(ldef.rhs), restpe)
+ val LabelDef(_, _, rhs1) = resetAttrs(ldef)
+ val rhs2 = typed(rhs1, restpe)
ldef.params foreach (param => param setType param.symbol.tpe)
deriveLabelDef(ldef)(_ => rhs2) setSymbol sym2 setType restpe
}
@@ -2588,7 +2590,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
//
// Well behaved trees satisfy the property:
//
- // typed(tree) == typed(resetLocalAttrs(typed(tree))
+ // typed(tree) == typed(resetAttrs(typed(tree))
//
// Trees constructed without low-level symbol manipulation get this for free;
// references to local symbols are cleared by `ResetAttrs`, but bind to the
@@ -3411,7 +3413,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// instantiate dependent method types, must preserve singleton types where possible (stableTypeFor) -- example use case:
// val foo = "foo"; def precise(x: String)(y: x.type): x.type = {...}; val bar : foo.type = precise(foo)(foo)
// precise(foo) : foo.type => foo.type
- val restpe = mt.resultType(args1 map (arg => gen stableTypeFor arg orElse arg.tpe))
+ val restpe = mt.resultType(mapList(args1)(arg => gen stableTypeFor arg orElse arg.tpe))
def ifPatternSkipFormals(tp: Type) = tp match {
case MethodType(_, rtp) if (mode.inPatternMode) => rtp
case _ => tp
@@ -3493,7 +3495,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
/**
* Convert an annotation constructor call into an AnnotationInfo.
*/
- def typedAnnotation(ann: Tree, mode: Mode = EXPRmode, selfsym: Symbol = NoSymbol): AnnotationInfo = {
+ def typedAnnotation(ann: Tree, mode: Mode = EXPRmode): AnnotationInfo = {
var hasError: Boolean = false
val pending = ListBuffer[AbsTypeError]()
@@ -3542,7 +3544,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
reportAnnotationError(ArrayConstantsError(tree)); None
case ann @ Apply(Select(New(tpt), nme.CONSTRUCTOR), args) =>
- val annInfo = typedAnnotation(ann, mode, NoSymbol)
+ val annInfo = typedAnnotation(ann, mode)
val annType = annInfo.tpe
if (!annType.typeSymbol.isSubClass(pt.typeSymbol))
@@ -3654,28 +3656,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
}
else {
- val typedAnn = if (selfsym == NoSymbol) {
+ val typedAnn: Tree = {
// local dummy fixes SI-5544
val localTyper = newTyper(context.make(ann, context.owner.newLocalDummy(ann.pos)))
localTyper.typed(ann, mode, annType)
}
- else {
- // Since a selfsym is supplied, the annotation should have an extra
- // "self" identifier in scope for type checking. This is implemented
- // by wrapping the rhs in a function like "self => rhs" during type
- // checking, and then stripping the "self =>" and substituting in
- // the supplied selfsym.
- val funcparm = ValDef(NoMods, nme.self, TypeTree(selfsym.info), EmptyTree)
- // The .duplicate of annot.constr deals with problems that accur
- // if this annotation is later typed again, which the compiler
- // sometimes does. The problem is that "self" ident's within
- // annot.constr will retain the old symbol from the previous typing.
- val func = Function(funcparm :: Nil, ann.duplicate)
- val funcType = appliedType(FunctionClass(1), selfsym.info, annType)
- val Function(arg :: Nil, rhs) = typed(func, mode, funcType)
-
- rhs.substituteSymbols(arg.symbol :: Nil, selfsym :: Nil)
- }
def annInfo(t: Tree): AnnotationInfo = t match {
case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) =>
AnnotationInfo(annType, args, List()).setOriginal(typedAnn).setPos(t.pos)
@@ -3793,7 +3778,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
t match {
case ExistentialType(tparams, _) =>
boundSyms ++= tparams
- case AnnotatedType(annots, _, _) =>
+ case AnnotatedType(annots, _) =>
for (annot <- annots; arg <- annot.args) {
arg match {
case Ident(_) =>
@@ -3848,7 +3833,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// lifted out of typed1 because it's needed in typedImplicit0
protected def typedTypeApply(tree: Tree, mode: Mode, fun: Tree, args: List[Tree]): Tree = fun.tpe match {
case OverloadedType(pre, alts) =>
- inferPolyAlternatives(fun, args map (_.tpe))
+ inferPolyAlternatives(fun, mapList(args)(treeTpe))
val tparams = fun.symbol.typeParams //@M TODO: fun.symbol.info.typeParams ? (as in typedAppliedTypeTree)
val args1 = if (sameLength(args, tparams)) {
//@M: in case TypeApply we can't check the kind-arities of the type arguments,
@@ -3868,7 +3853,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
typedTypeApply(tree, mode, fun setType fun.tpe.widen, args)
case PolyType(tparams, restpe) if tparams.nonEmpty =>
if (sameLength(tparams, args)) {
- val targs = args map (_.tpe)
+ val targs = mapList(args)(treeTpe)
checkBounds(tree, NoPrefix, NoSymbol, tparams, targs, "")
if (isPredefClassOf(fun.symbol))
typedClassOf(tree, args.head, noGen = true)
@@ -4058,39 +4043,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (arg1.isType) {
// make sure the annotation is only typechecked once
if (ann.tpe == null) {
- // an annotated type
- val selfsym =
- if (!settings.selfInAnnots)
- NoSymbol
- else
- arg1.tpe.selfsym orElse {
- /* Implementation limitation: Currently this
- * can cause cyclical reference errors even
- * when the self symbol is not referenced at all.
- * Surely at least some of these cases can be
- * fixed by proper use of LazyType's. Lex tinkered
- * on this but did not succeed, so is leaving
- * it alone for now. Example code with the problem:
- * class peer extends Annotation
- * class NPE[T <: NPE[T] @peer]
- *
- * (Note: -Yself-in-annots must be on to see the problem)
- * */
- ( context.owner
- newLocalDummy (ann.pos)
- newValue (nme.self, ann.pos)
- setInfo (arg1.tpe.withoutAnnotations)
- )
- }
-
- val ainfo = typedAnnotation(ann, annotMode, selfsym)
- val atype0 = arg1.tpe.withAnnotation(ainfo)
- val atype =
- if ((selfsym != NoSymbol) && (ainfo.refsSymbol(selfsym)))
- atype0.withSelfsym(selfsym)
- else
- atype0 // do not record selfsym if
- // this annotation did not need it
+ val ainfo = typedAnnotation(ann, annotMode)
+ val atype = arg1.tpe.withAnnotation(ainfo)
if (ainfo.isErroneous)
// Erroneous annotations were already reported in typedAnnotation
@@ -4919,7 +4873,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
typedHigherKindedType(arg, mode, pt)
}
- val argtypes = args1 map (_.tpe)
+ val argtypes = mapList(args1)(treeTpe)
foreach2(args, tparams) { (arg, tparam) =>
// note: can't use args1 in selector, because Binds got replaced
diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
index ffac29b4b8..cc2d9141ce 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
@@ -52,13 +52,13 @@ trait Unapplies extends ast.TreeDSL {
}
private def constrParamss(cdef: ClassDef): List[List[ValDef]] = {
- val ClassDef(_, _, _, Template(_, _, body)) = resetLocalAttrs(cdef.duplicate)
+ val ClassDef(_, _, _, Template(_, _, body)) = resetAttrs(cdef.duplicate)
val DefDef(_, _, _, vparamss, _, _) = treeInfo firstConstructor body
vparamss
}
private def constrTparamsInvariant(cdef: ClassDef): List[TypeDef] = {
- val ClassDef(_, _, tparams, _) = resetLocalAttrs(cdef.duplicate)
+ val ClassDef(_, _, tparams, _) = resetAttrs(cdef.duplicate)
val tparamsInvariant = tparams.map(tparam => copyTypeDef(tparam)(mods = tparam.mods &~ (COVARIANT | CONTRAVARIANT)))
tparamsInvariant
}
diff --git a/src/compiler/scala/tools/reflect/FastTrack.scala b/src/compiler/scala/tools/reflect/FastTrack.scala
index bb0bbd79a3..8630ecf69e 100644
--- a/src/compiler/scala/tools/reflect/FastTrack.scala
+++ b/src/compiler/scala/tools/reflect/FastTrack.scala
@@ -20,8 +20,8 @@ trait FastTrack {
private implicit def context2taggers(c0: MacroContext): Taggers { val c: c0.type } =
new { val c: c0.type = c0 } with Taggers
- private implicit def context2macroimplementations(c0: MacroContext): MacroImplementations { val c: c0.type } =
- new { val c: c0.type = c0 } with MacroImplementations
+ private implicit def context2macroimplementations(c0: MacroContext): FormatInterpolator { val c: c0.type } =
+ new { val c: c0.type = c0 } with FormatInterpolator
private implicit def context2quasiquote(c0: MacroContext): QuasiquoteImpls { val c: c0.type } =
new { val c: c0.type = c0 } with QuasiquoteImpls
private def makeBlackbox(sym: Symbol)(pf: PartialFunction[Applied, MacroContext => Tree]) =
@@ -48,7 +48,7 @@ trait FastTrack {
makeBlackbox( materializeWeakTypeTag) { case Applied(_, ttag :: Nil, (u :: _) :: _) => _.materializeTypeTag(u, EmptyTree, ttag.tpe, concrete = false) },
makeBlackbox( materializeTypeTag) { case Applied(_, ttag :: Nil, (u :: _) :: _) => _.materializeTypeTag(u, EmptyTree, ttag.tpe, concrete = true) },
makeBlackbox( ApiUniverseReify) { case Applied(_, ttag :: Nil, (expr :: _) :: _) => c => c.materializeExpr(c.prefix.tree, EmptyTree, expr) },
- makeBlackbox( StringContext_f) { case Applied(Select(Apply(_, ps), _), _, args) => c => c.macro_StringInterpolation_f(ps, args.flatten, c.expandee.pos) },
+ makeBlackbox( StringContext_f) { case _ => _.interpolate },
makeBlackbox(ReflectRuntimeCurrentMirror) { case _ => c => currentMirror(c).tree },
makeWhitebox( QuasiquoteClass_api_apply) { case _ => _.expandQuasiquote },
makeWhitebox(QuasiquoteClass_api_unapply) { case _ => _.expandQuasiquote }
diff --git a/src/compiler/scala/tools/reflect/FormatInterpolator.scala b/src/compiler/scala/tools/reflect/FormatInterpolator.scala
new file mode 100644
index 0000000000..d5e674ebae
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/FormatInterpolator.scala
@@ -0,0 +1,329 @@
+package scala.tools.reflect
+
+import scala.reflect.macros.runtime.Context
+import scala.collection.mutable.{ ListBuffer, Stack }
+import scala.reflect.internal.util.Position
+import scala.PartialFunction.cond
+import scala.util.matching.Regex.Match
+
+import java.util.{ Formatter, Formattable, IllegalFormatException }
+
+abstract class FormatInterpolator {
+ val c: Context
+ val global: c.universe.type = c.universe
+
+ import c.universe.{ Match => _, _ }
+ import definitions._
+ import treeInfo.Applied
+
+ @inline private def truly(body: => Unit): Boolean = { body ; true }
+ @inline private def falsely(body: => Unit): Boolean = { body ; false }
+
+ private def fail(msg: String) = c.abort(c.enclosingPosition, msg)
+ private def bail(msg: String) = global.abort(msg)
+
+ def interpolate: Tree = c.macroApplication match {
+ //case q"$_(..$parts).f(..$args)" =>
+ case Applied(Select(Apply(_, parts), _), _, argss) =>
+ val args = argss.flatten
+ def badlyInvoked = (parts.length != args.length + 1) && truly {
+ def because(s: String) = s"too $s arguments for interpolated string"
+ val (p, msg) =
+ if (parts.length == 0) (c.prefix.tree.pos, "there are no parts")
+ else if (args.length + 1 < parts.length)
+ (if (args.isEmpty) c.enclosingPosition else args.last.pos, because("few"))
+ else (args(parts.length-1).pos, because("many"))
+ c.abort(p, msg)
+ }
+ if (badlyInvoked) c.macroApplication else interpolated(parts, args)
+ case other =>
+ bail(s"Unexpected application ${showRaw(other)}")
+ other
+ }
+
+ /** Every part except the first must begin with a conversion for
+ * the arg that preceded it. If the conversion is missing, "%s"
+ * is inserted.
+ *
+ * In any other position, the only permissible conversions are
+ * the literals (%% and %n) or an index reference (%1$ or %<).
+ *
+ * A conversion specifier has the form:
+ *
+ * [index$][flags][width][.precision]conversion
+ *
+ * 1) "...${smth}" => okay, equivalent to "...${smth}%s"
+ * 2) "...${smth}blahblah" => okay, equivalent to "...${smth}%sblahblah"
+ * 3) "...${smth}%" => error
+ * 4) "...${smth}%n" => okay, equivalent to "...${smth}%s%n"
+ * 5) "...${smth}%%" => okay, equivalent to "...${smth}%s%%"
+ * 6) "...${smth}[%legalJavaConversion]" => okay*
+ * 7) "...${smth}[%illegalJavaConversion]" => error
+ * *Legal according to [[http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Formatter.html]]
+ */
+ def interpolated(parts: List[Tree], args: List[Tree]) = {
+ val fstring = new StringBuilder
+ val evals = ListBuffer[ValDef]()
+ val ids = ListBuffer[Ident]()
+ val argStack = Stack(args: _*)
+
+ // create a tmp val and add it to the ids passed to format
+ def defval(value: Tree, tpe: Type): Unit = {
+ val freshName = TermName(c.freshName("arg$"))
+ evals += ValDef(Modifiers(), freshName, TypeTree(tpe) setPos value.pos.focus, value) setPos value.pos
+ ids += Ident(freshName)
+ }
+ // Append the nth part to the string builder, possibly prepending an omitted %s first.
+ // Sanity-check the % fields in this part.
+ def copyPart(part: Tree, n: Int): Unit = {
+ import SpecifierGroups.{ Spec, Index }
+ val s0 = part match {
+ case Literal(Constant(x: String)) => x
+ case _ => throw new IllegalArgumentException("internal error: argument parts must be a list of string literals")
+ }
+ val s = StringContext.treatEscapes(s0)
+ val ms = fpat findAllMatchIn s
+
+ def errorLeading(op: Conversion) = op.errorAt(Spec, s"conversions must follow a splice; ${Conversion.literalHelp}")
+
+ def first = n == 0
+ // a conversion for the arg is required
+ if (!first) {
+ val arg = argStack.pop()
+ def s_%() = {
+ fstring append "%s"
+ defval(arg, AnyTpe)
+ }
+ def accept(op: Conversion) = {
+ if (!op.isLeading) errorLeading(op)
+ op.accepts(arg) match {
+ case Some(tpe) => defval(arg, tpe)
+ case None =>
+ }
+ }
+ if (ms.hasNext) {
+ Conversion(ms.next, part.pos, args.size) match {
+ case Some(op) if op.isLiteral => s_%()
+ case Some(op) if op.indexed =>
+ if (op.index map (_ == n) getOrElse true) accept(op)
+ else {
+ // either some other arg num, or '<'
+ c.warning(op.groupPos(Index), "Index is not this arg")
+ s_%()
+ }
+ case Some(op) => accept(op)
+ case None =>
+ }
+ } else s_%()
+ }
+ // any remaining conversions must be either literals or indexed
+ while (ms.hasNext) {
+ Conversion(ms.next, part.pos, args.size) match {
+ case Some(op) if first && op.hasFlag('<') => op.badFlag('<', "No last arg")
+ case Some(op) if op.isLiteral || op.indexed => // OK
+ case Some(op) => errorLeading(op)
+ case None =>
+ }
+ }
+ fstring append s
+ }
+
+ parts.zipWithIndex foreach {
+ case (part, n) => copyPart(part, n)
+ }
+
+ //q"{..$evals; ${fstring.toString}.format(..$ids)}"
+ locally {
+ val expr =
+ Apply(
+ Select(
+ Literal(Constant(fstring.toString)),
+ newTermName("format")),
+ ids.toList
+ )
+ val p = c.macroApplication.pos
+ Block(evals.toList, atPos(p.focus)(expr)) setPos p.makeTransparent
+ }
+ }
+
+ val fpat = """%(?:(\d+)\$)?([-#+ 0,(\<]+)?(\d+)?(\.\d+)?([tT]?[%a-zA-Z])?""".r
+ object SpecifierGroups extends Enumeration { val Spec, Index, Flags, Width, Precision, CC = Value }
+
+ val stdContextTags = new { val tc: c.type = c } with StdContextTags
+ import stdContextTags._
+ val tagOfFormattable = typeTag[Formattable]
+
+ /** A conversion specifier matched by `m` in the string part at `pos`,
+ * with `argc` arguments to interpolate.
+ */
+ sealed trait Conversion {
+ def m: Match
+ def pos: Position
+ def argc: Int
+
+ import SpecifierGroups.{ Value => SpecGroup, _ }
+ private def maybeStr(g: SpecGroup) = Option(m group g.id)
+ private def maybeInt(g: SpecGroup) = maybeStr(g) map (_.toInt)
+ val index: Option[Int] = maybeInt(Index)
+ val flags: Option[String] = maybeStr(Flags)
+ val width: Option[Int] = maybeInt(Width)
+ val precision: Option[Int] = maybeStr(Precision) map (_.drop(1).toInt)
+ val op: String = maybeStr(CC) getOrElse ""
+
+ def cc: Char = if ("tT" contains op(0)) op(1) else op(0)
+
+ def indexed: Boolean = index.nonEmpty || hasFlag('<')
+ def isLiteral: Boolean = false
+ def isLeading: Boolean = m.start(0) == 0
+ def verify: Boolean = goodFlags && goodIndex
+ def accepts(arg: Tree): Option[Type]
+
+ val allFlags = "-#+ 0,(<"
+ def hasFlag(f: Char) = (flags getOrElse "") contains f
+ def hasAnyFlag(fs: String) = fs exists (hasFlag)
+
+ def badFlag(f: Char, msg: String) = {
+ val i = flags map (_.indexOf(f)) filter (_ >= 0) getOrElse 0
+ errorAtOffset(Flags, i, msg)
+ }
+ def groupPos(g: SpecGroup) = groupPosAt(g, 0)
+ def groupPosAt(g: SpecGroup, i: Int) = pos withPoint (pos.point + m.start(g.id) + i)
+ def errorAt(g: SpecGroup, msg: String) = c.error(groupPos(g), msg)
+ def errorAtOffset(g: SpecGroup, i: Int, msg: String) = c.error(groupPosAt(g, i), msg)
+
+ def noFlags = flags.isEmpty || falsely { errorAt(Flags, "flags not allowed") }
+ def noWidth = width.isEmpty || falsely { errorAt(Width, "width not allowed") }
+ def noPrecision = precision.isEmpty || falsely { errorAt(Precision, "precision not allowed") }
+ def only_-(msg: String) = {
+ val badFlags = (flags getOrElse "") filterNot { case '-' | '<' => true case _ => false }
+ badFlags.isEmpty || falsely { badFlag(badFlags(0), s"Only '-' allowed for $msg") }
+ }
+ protected def okFlags: String = allFlags
+ def goodFlags = {
+ val badFlags = flags map (_ filterNot (okFlags contains _))
+ for (bf <- badFlags; f <- bf) badFlag(f, s"Illegal flag '$f'")
+ badFlags.getOrElse("").isEmpty
+ }
+ def goodIndex = {
+ if (index.nonEmpty && hasFlag('<'))
+ c.warning(groupPos(Index), "Argument index ignored if '<' flag is present")
+ val okRange = index map (i => i > 0 && i <= argc) getOrElse true
+ okRange || hasFlag('<') || falsely { errorAt(Index, "Argument index out of range") }
+ }
+ /** Pick the type of an arg to format from among the variants
+ * supported by a conversion. This is the type of the temporary,
+ * so failure results in an erroneous assignment to the first variant.
+ * A more complete message would be nice.
+ */
+ def pickAcceptable(arg: Tree, variants: Type*): Option[Type] =
+ variants find (arg.tpe <:< _) orElse (
+ variants find (c.inferImplicitView(arg, arg.tpe, _) != EmptyTree)
+ ) orElse Some(variants(0))
+ }
+ object Conversion {
+ import SpecifierGroups.{ Spec, CC, Width }
+ def apply(m: Match, p: Position, n: Int): Option[Conversion] = {
+ def badCC(msg: String) = {
+ val dk = new ErrorXn(m, p)
+ val at = if (dk.op.isEmpty) Spec else CC
+ dk.errorAt(at, msg)
+ }
+ def cv(cc: Char) = cc match {
+ case 'b' | 'B' | 'h' | 'H' | 's' | 'S' =>
+ new GeneralXn(m, p, n)
+ case 'c' | 'C' =>
+ new CharacterXn(m, p, n)
+ case 'd' | 'o' | 'x' | 'X' =>
+ new IntegralXn(m, p, n)
+ case 'e' | 'E' | 'f' | 'g' | 'G' | 'a' | 'A' =>
+ new FloatingPointXn(m, p, n)
+ case 't' | 'T' =>
+ new DateTimeXn(m, p, n)
+ case '%' | 'n' =>
+ new LiteralXn(m, p, n)
+ case _ =>
+ badCC(s"illegal conversion character '$cc'")
+ null
+ }
+ Option(m group CC.id) map (cc => cv(cc(0))) match {
+ case Some(x) => Option(x) filter (_.verify)
+ case None =>
+ badCC(s"Missing conversion operator in '${m.matched}'; $literalHelp")
+ None
+ }
+ }
+ val literalHelp = "use %% for literal %, %n for newline"
+ }
+ class GeneralXn(val m: Match, val pos: Position, val argc: Int) extends Conversion {
+ def accepts(arg: Tree) = cc match {
+ case 's' | 'S' if hasFlag('#') => pickAcceptable(arg, tagOfFormattable.tpe)
+ case 'b' | 'B' => if (arg.tpe <:< NullTpe) Some(NullTpe) else Some(BooleanTpe)
+ case _ => Some(AnyTpe)
+ }
+ override protected def okFlags = cc match {
+ case 's' | 'S' => "-#<"
+ case _ => "-<"
+ }
+ }
+ class LiteralXn(val m: Match, val pos: Position, val argc: Int) extends Conversion {
+ import SpecifierGroups.Width
+ override val isLiteral = true
+ override def verify = op match {
+ case "%" => super.verify && noPrecision && truly(width foreach (_ => c.warning(groupPos(Width), "width ignored on literal")))
+ case "n" => noFlags && noWidth && noPrecision
+ }
+ override protected val okFlags = "-"
+ def accepts(arg: Tree) = None
+ }
+ class CharacterXn(val m: Match, val pos: Position, val argc: Int) extends Conversion {
+ override def verify = super.verify && noPrecision && only_-("c conversion")
+ def accepts(arg: Tree) = pickAcceptable(arg, CharTpe, ByteTpe, ShortTpe, IntTpe)
+ }
+ class IntegralXn(val m: Match, val pos: Position, val argc: Int) extends Conversion {
+ override def verify = {
+ def d_# = (cc == 'd' && hasFlag('#') &&
+ truly { badFlag('#', "# not allowed for d conversion") }
+ )
+ def x_comma = (cc != 'd' && hasFlag(',') &&
+ truly { badFlag(',', "',' only allowed for d conversion of integral types") }
+ )
+ super.verify && noPrecision && !d_# && !x_comma
+ }
+ override def accepts(arg: Tree) = {
+ def isBigInt = arg.tpe <:< tagOfBigInt.tpe
+ val maybeOK = "+ ("
+ def bad_+ = cond(cc) {
+ case 'o' | 'x' | 'X' if hasAnyFlag(maybeOK) && !isBigInt =>
+ maybeOK filter hasFlag foreach (badf =>
+ badFlag(badf, s"only use '$badf' for BigInt conversions to o, x, X"))
+ true
+ }
+ if (bad_+) None else pickAcceptable(arg, IntTpe, LongTpe, ByteTpe, ShortTpe, tagOfBigInt.tpe)
+ }
+ }
+ class FloatingPointXn(val m: Match, val pos: Position, val argc: Int) extends Conversion {
+ override def verify = super.verify && (cc match {
+ case 'a' | 'A' =>
+ val badFlags = ",(" filter hasFlag
+ noPrecision && badFlags.isEmpty || falsely {
+ badFlags foreach (badf => badFlag(badf, s"'$badf' not allowed for a, A"))
+ }
+ case _ => true
+ })
+ def accepts(arg: Tree) = pickAcceptable(arg, DoubleTpe, FloatTpe, tagOfBigDecimal.tpe)
+ }
+ class DateTimeXn(val m: Match, val pos: Position, val argc: Int) extends Conversion {
+ import SpecifierGroups.CC
+ def hasCC = (op.length == 2 ||
+ falsely { errorAt(CC, "Date/time conversion must have two characters") })
+ def goodCC = ("HIklMSLNpzZsQBbhAaCYyjmdeRTrDFc" contains cc) ||
+ falsely { errorAtOffset(CC, 1, s"'$cc' doesn't seem to be a date or time conversion") }
+ override def verify = super.verify && hasCC && goodCC && noPrecision && only_-("date/time conversions")
+ def accepts(arg: Tree) = pickAcceptable(arg, LongTpe, tagOfCalendar.tpe, tagOfDate.tpe)
+ }
+ class ErrorXn(val m: Match, val pos: Position) extends Conversion {
+ val argc = 0
+ override def verify = false
+ def accepts(arg: Tree) = None
+ }
+}
diff --git a/src/compiler/scala/tools/reflect/MacroImplementations.scala b/src/compiler/scala/tools/reflect/MacroImplementations.scala
deleted file mode 100644
index a9ed419b1e..0000000000
--- a/src/compiler/scala/tools/reflect/MacroImplementations.scala
+++ /dev/null
@@ -1,171 +0,0 @@
-package scala.tools.reflect
-
-import scala.reflect.macros.contexts.Context
-import scala.collection.mutable.ListBuffer
-import scala.collection.mutable.Stack
-import scala.reflect.internal.util.Position
-
-abstract class MacroImplementations {
- val c: Context
-
- import c.universe._
- import definitions._
-
- def macro_StringInterpolation_f(parts: List[Tree], args: List[Tree], origApplyPos: c.universe.Position): Tree = {
- // the parts all have the same position information (as the expression is generated by the compiler)
- // the args have correct position information
-
- // the following conditions can only be violated if invoked directly
- if (parts.length != args.length + 1) {
- if(parts.length == 0)
- c.abort(c.prefix.tree.pos, "too few parts")
- else if(args.length + 1 < parts.length)
- c.abort(if(args.length==0) c.enclosingPosition else args.last.pos,
- "too few arguments for interpolated string")
- else
- c.abort(args(parts.length-1).pos,
- "too many arguments for interpolated string")
- }
-
- val pi = parts.iterator
- val bldr = new java.lang.StringBuilder
- val evals = ListBuffer[ValDef]()
- val ids = ListBuffer[Ident]()
- val argStack = Stack(args : _*)
-
- def defval(value: Tree, tpe: Type): Unit = {
- val freshName = newTermName(c.freshName("arg$"))
- evals += ValDef(Modifiers(), freshName, TypeTree(tpe) setPos value.pos.focus, value) setPos value.pos
- ids += Ident(freshName)
- }
-
- def isFlag(ch: Char): Boolean = {
- ch match {
- case '-' | '#' | '+' | ' ' | '0' | ',' | '(' => true
- case _ => false
- }
- }
-
- def checkType(arg: Tree, variants: Type*): Option[Type] = {
- variants.find(arg.tpe <:< _).orElse(
- variants.find(c.inferImplicitView(arg, arg.tpe, _) != EmptyTree).orElse(
- Some(variants(0))
- )
- )
- }
-
- val stdContextTags = new { val tc: c.type = c } with StdContextTags
- import stdContextTags._
-
- def conversionType(ch: Char, arg: Tree): Option[Type] = {
- ch match {
- case 'b' | 'B' =>
- if(arg.tpe <:< NullTpe) Some(NullTpe) else Some(BooleanTpe)
- case 'h' | 'H' =>
- Some(AnyTpe)
- case 's' | 'S' =>
- Some(AnyTpe)
- case 'c' | 'C' =>
- checkType(arg, CharTpe, ByteTpe, ShortTpe, IntTpe)
- case 'd' | 'o' | 'x' | 'X' =>
- checkType(arg, IntTpe, LongTpe, ByteTpe, ShortTpe, tagOfBigInt.tpe)
- case 'e' | 'E' | 'g' | 'G' | 'f' | 'a' | 'A' =>
- checkType(arg, DoubleTpe, FloatTpe, tagOfBigDecimal.tpe)
- case 't' | 'T' =>
- checkType(arg, LongTpe, tagOfCalendar.tpe, tagOfDate.tpe)
- case _ => None
- }
- }
-
- def copyString(first: Boolean): Unit = {
- val strTree = pi.next()
- val rawStr = strTree match {
- case Literal(Constant(str: String)) => str
- case _ => throw new IllegalArgumentException("internal error: argument parts must be a list of string literals")
- }
- val str = StringContext.treatEscapes(rawStr)
- val strLen = str.length
- val strIsEmpty = strLen == 0
- def charAtIndexIs(idx: Int, ch: Char) = idx < strLen && str(idx) == ch
- def isPercent(idx: Int) = charAtIndexIs(idx, '%')
- def isConversion(idx: Int) = isPercent(idx) && !charAtIndexIs(idx + 1, 'n') && !charAtIndexIs(idx + 1, '%')
- var idx = 0
-
- def errorAtIndex(idx: Int, msg: String) = c.error(Position.offset(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,
- "conversions must follow a splice; use %% for literal %, %n for newline")
-
- // STEP 1: handle argument conversion
- // 1) "...${smth}" => okay, equivalent to "...${smth}%s"
- // 2) "...${smth}blahblah" => okay, equivalent to "...${smth}%sblahblah"
- // 3) "...${smth}%" => error
- // 4) "...${smth}%n" => okay, equivalent to "...${smth}%s%n"
- // 5) "...${smth}%%" => okay, equivalent to "...${smth}%s%%"
- // 6) "...${smth}[%legalJavaConversion]" => okay, according to http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Formatter.html
- // 7) "...${smth}[%illegalJavaConversion]" => error
- if (!first) {
- val arg = argStack.pop()
- if (isConversion(0)) {
- // PRE str is not empty and str(0) == '%'
- // argument index parameter is not allowed, thus parse
- // [flags][width][.precision]conversion
- var pos = 1
- while (pos < strLen && isFlag(str charAt pos)) pos += 1
- while (pos < strLen && Character.isDigit(str charAt pos)) pos += 1
- if (pos < strLen && str.charAt(pos) == '.') {
- pos += 1
- while (pos < strLen && Character.isDigit(str charAt pos)) pos += 1
- }
- if (pos < strLen) {
- conversionType(str charAt pos, arg) match {
- case Some(tpe) => defval(arg, tpe)
- case None => illegalConversionCharacter(pos)
- }
- } else {
- wrongConversionString(pos - 1)
- }
- idx = 1
- } else {
- bldr append "%s"
- defval(arg, AnyTpe)
- }
- }
-
- // STEP 2: handle the rest of the text
- // 1) %n tokens are left as is
- // 2) %% tokens are left as is
- // 3) other usages of percents are reported as errors
- if (!strIsEmpty) {
- while (idx < strLen) {
- if (isPercent(idx)) {
- if (isConversion(idx)) nonEscapedPercent(idx)
- else idx += 1 // skip n and % in %n and %%
- }
- idx += 1
- }
- bldr append (str take idx)
- }
- }
-
- copyString(first = true)
- while (pi.hasNext) {
- copyString(first = false)
- }
-
- val fstring = bldr.toString
-// val expr = c.reify(fstring.format((ids.map(id => Expr(id).eval)) : _*))
-// https://issues.scala-lang.org/browse/SI-5824, therefore
- val expr =
- Apply(
- Select(
- Literal(Constant(fstring)),
- newTermName("format")),
- List(ids: _* )
- )
-
- Block(evals.toList, atPos(origApplyPos.focus)(expr)) setPos origApplyPos.makeTransparent
- }
-
-}
diff --git a/src/compiler/scala/tools/reflect/ToolBox.scala b/src/compiler/scala/tools/reflect/ToolBox.scala
index 236b868842..02a458214f 100644
--- a/src/compiler/scala/tools/reflect/ToolBox.scala
+++ b/src/compiler/scala/tools/reflect/ToolBox.scala
@@ -71,20 +71,17 @@ trait ToolBox[U <: scala.reflect.api.Universe] {
*/
def inferImplicitView(tree: u.Tree, from: u.Type, to: u.Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: u.Position = u.NoPosition): u.Tree
- /** Recursively resets symbols and types in a given tree.
- *
- * Note that this does not revert the tree to its pre-typer shape.
- * For more info, read up https://issues.scala-lang.org/browse/SI-5464.
- */
- def resetAllAttrs(tree: u.Tree): u.Tree
-
/** Recursively resets locally defined symbols and types in a given tree.
- *
- * Note that this does not revert the tree to its pre-typer shape.
- * For more info, read up https://issues.scala-lang.org/browse/SI-5464.
+ * WARNING: Don't use this API, go for [[untypecheck]] instead.
*/
+ @deprecated("Use `tb.untypecheck` instead", "2.11.0")
def resetLocalAttrs(tree: u.Tree): u.Tree
+ /**
+ * @see [[scala.reflect.macros.Typers.untypecheck]]
+ */
+ def untypecheck(tree: u.Tree): u.Tree
+
/** .. */
def parse(code: String): u.Tree
diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
index af13b7d0ba..541a915adb 100644
--- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
+++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
@@ -236,7 +236,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
NoPosition))
trace("wrapped: ")(showAttributed(moduledef, true, true, settings.Yshowsymkinds.value))
- val cleanedUp = resetLocalAttrs(moduledef)
+ val cleanedUp = resetAttrs(moduledef)
trace("cleaned up: ")(showAttributed(cleanedUp, true, true, settings.Yshowsymkinds.value))
cleanedUp.asInstanceOf[ModuleDef]
}
@@ -385,22 +385,16 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
uitree
}
- def resetAllAttrs(tree: u.Tree): u.Tree = withCompilerApi { compilerApi =>
- import compilerApi._
- val ctree: compiler.Tree = importer.importTree(tree)
- val ttree: compiler.Tree = compiler.resetAllAttrs(ctree)
- val uttree = exporter.importTree(ttree)
- uttree
- }
-
def resetLocalAttrs(tree: u.Tree): u.Tree = withCompilerApi { compilerApi =>
import compilerApi._
val ctree: compiler.Tree = importer.importTree(tree)
- val ttree: compiler.Tree = compiler.resetLocalAttrs(ctree)
+ val ttree: compiler.Tree = compiler.resetAttrs(ctree)
val uttree = exporter.importTree(ttree)
uttree
}
+ def untypecheck(tree: u.Tree): u.Tree = resetLocalAttrs(tree)
+
def parse(code: String): u.Tree = withCompilerApi { compilerApi =>
import compilerApi._
if (compiler.settings.verbose) println("parsing "+code)
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
index 8a54519401..2027d43264 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
@@ -31,26 +31,28 @@ trait Holes { self: Quasiquotes =>
import definitions._
import universeTypes._
- protected lazy val IterableTParam = IterableClass.typeParams(0).asType.toType
- protected def inferParamImplicit(tfun: Type, targ: Type) = c.inferImplicitValue(appliedType(tfun, List(targ)), silent = true)
- protected def inferLiftable(tpe: Type): Tree = inferParamImplicit(liftableType, tpe)
- protected def inferUnliftable(tpe: Type): Tree = inferParamImplicit(unliftableType, tpe)
- protected def isLiftableType(tpe: Type) = inferLiftable(tpe) != EmptyTree
- protected def isNativeType(tpe: Type) =
+ private lazy val IterableTParam = IterableClass.typeParams(0).asType.toType
+ private def inferParamImplicit(tfun: Type, targ: Type) = c.inferImplicitValue(appliedType(tfun, List(targ)), silent = true)
+ private def inferLiftable(tpe: Type): Tree = inferParamImplicit(liftableType, tpe)
+ private def inferUnliftable(tpe: Type): Tree = inferParamImplicit(unliftableType, tpe)
+ private def isLiftableType(tpe: Type) = inferLiftable(tpe) != EmptyTree
+ private def isNativeType(tpe: Type) =
(tpe <:< treeType) || (tpe <:< nameType) || (tpe <:< modsType) ||
(tpe <:< flagsType) || (tpe <:< symbolType)
- protected def isBottomType(tpe: Type) =
+ private def isBottomType(tpe: Type) =
tpe <:< NothingClass.tpe || tpe <:< NullClass.tpe
- protected def stripIterable(tpe: Type, limit: Option[Cardinality] = None): (Cardinality, Type) =
+ private def extractIterableTParam(tpe: Type) =
+ IterableTParam.asSeenFrom(tpe, IterableClass)
+ private def stripIterable(tpe: Type, limit: Option[Cardinality] = None): (Cardinality, Type) =
if (limit.map { _ == NoDot }.getOrElse { false }) (NoDot, tpe)
else if (tpe != null && !isIterableType(tpe)) (NoDot, tpe)
else if (isBottomType(tpe)) (NoDot, tpe)
else {
- val targ = IterableTParam.asSeenFrom(tpe, IterableClass)
+ val targ = extractIterableTParam(tpe)
val (card, innerTpe) = stripIterable(targ, limit.map { _.pred })
(card.succ, innerTpe)
}
- protected def iterableTypeFromCard(n: Cardinality, tpe: Type): Type = {
+ private def iterableTypeFromCard(n: Cardinality, tpe: Type): Type = {
if (n == NoDot) tpe
else appliedType(IterableClass.toType, List(iterableTypeFromCard(n.pred, tpe)))
}
@@ -74,8 +76,7 @@ trait Holes { self: Quasiquotes =>
class ApplyHole(card: Cardinality, splicee: Tree) extends Hole {
val (strippedTpe, tpe): (Type, Type) = {
- if (stripIterable(splicee.tpe)._1.value < card.value) cantSplice()
- val (_, strippedTpe) = stripIterable(splicee.tpe, limit = Some(card))
+ val (strippedCard, strippedTpe) = stripIterable(splicee.tpe, limit = Some(card))
if (isBottomType(strippedTpe)) cantSplice()
else if (isNativeType(strippedTpe)) (strippedTpe, iterableTypeFromCard(card, strippedTpe))
else if (isLiftableType(strippedTpe)) (strippedTpe, iterableTypeFromCard(card, treeType))
@@ -88,14 +89,14 @@ trait Holes { self: Quasiquotes =>
else if (isLiftableType(itpe)) lifted(itpe)(tree)
else global.abort("unreachable")
if (card == NoDot) inner(strippedTpe)(splicee)
- else iterated(card, strippedTpe, inner(strippedTpe))(splicee)
+ else iterated(card, splicee, splicee.tpe)
}
val pos = splicee.pos
val cardinality = stripIterable(tpe)._1
- protected def cantSplice(): Nothing = {
+ private def cantSplice(): Nothing = {
val (iterableCard, iterableType) = stripIterable(splicee.tpe)
val holeCardMsg = if (card != NoDot) s" with $card" else ""
val action = "splice " + splicee.tpe + holeCardMsg
@@ -111,28 +112,66 @@ trait Holes { self: Quasiquotes =>
c.abort(splicee.pos, s"Can't $action, $advice")
}
- protected def lifted(tpe: Type)(tree: Tree): Tree = {
+ private def lifted(tpe: Type)(tree: Tree): Tree = {
val lifter = inferLiftable(tpe)
assert(lifter != EmptyTree, s"couldnt find a liftable for $tpe")
val lifted = Apply(lifter, List(tree))
atPos(tree.pos)(lifted)
}
- protected def iterated(card: Cardinality, tpe: Type, elementTransform: Tree => Tree = identity)(tree: Tree): Tree = {
- assert(card != NoDot)
- def reifyIterable(tree: Tree, n: Cardinality): Tree = {
- def loop(tree: Tree, n: Cardinality): Tree =
- if (n == NoDot) elementTransform(tree)
- else {
- val x: TermName = c.freshName()
- val wrapped = reifyIterable(Ident(x), n.pred)
- val xToWrapped = Function(List(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree)), wrapped)
- Select(Apply(Select(tree, nme.map), List(xToWrapped)), nme.toList)
- }
- if (tree.tpe != null && (tree.tpe <:< listTreeType || tree.tpe <:< listListTreeType)) tree
- else atPos(tree.pos)(loop(tree, n))
+ private def toStats(tree: Tree): Tree =
+ // q"$u.build.toStats($tree)"
+ Apply(Select(Select(u, nme.build), nme.toStats), tree :: Nil)
+
+ private def toList(tree: Tree, tpe: Type): Tree =
+ if (isListType(tpe)) tree
+ else Select(tree, nme.toList)
+
+ private def mapF(tree: Tree, f: Tree => Tree): Tree =
+ if (f(Ident(TermName("x"))) equalsStructure Ident(TermName("x"))) tree
+ else {
+ val x: TermName = c.freshName()
+ // q"$tree.map { $x => ${f(Ident(x))} }"
+ Apply(Select(tree, nme.map),
+ Function(ValDef(Modifiers(PARAM), x, TypeTree(), EmptyTree) :: Nil,
+ f(Ident(x))) :: Nil)
}
- reifyIterable(tree, card)
+
+ private object IterableType {
+ def unapply(tpe: Type): Option[Type] =
+ if (isIterableType(tpe)) Some(extractIterableTParam(tpe)) else None
+ }
+
+ private object LiftedType {
+ def unapply(tpe: Type): Option[Tree => Tree] =
+ if (tpe <:< treeType) Some(t => t)
+ else if (isLiftableType(tpe)) Some(lifted(tpe)(_))
+ else None
+ }
+
+ /** Map high-cardinality splice onto an expression that eveluates as a list of given cardinality.
+ *
+ * All possible combinations of representations are given in the table below:
+ *
+ * input output for T <: Tree output for T: Liftable
+ *
+ * ..${x: Iterable[T]} x.toList x.toList.map(lift)
+ * ..${x: T} toStats(x) toStats(lift(x))
+ *
+ * ...${x: Iterable[Iterable[T]]} x.toList { _.toList } x.toList.map { _.toList.map(lift) }
+ * ...${x: Iterable[T]} x.toList.map { toStats(_) } x.toList.map { toStats(lift(_)) }
+ * ...${x: T} toStats(x).map { toStats(_) } toStats(lift(x)).map { toStats(_) }
+ *
+ * For optimization purposes `x.toList` is represented as just `x` if it is statically known that
+ * x is not just an Iterable[T] but a List[T]. Similarly no mapping is performed if mapping function is
+ * known to be an identity.
+ */
+ private def iterated(card: Cardinality, tree: Tree, tpe: Type): Tree = (card, tpe) match {
+ case (DotDot, tpe @ IterableType(LiftedType(lift))) => mapF(toList(tree, tpe), lift)
+ case (DotDot, LiftedType(lift)) => toStats(lift(tree))
+ case (DotDotDot, tpe @ IterableType(inner)) => mapF(toList(tree, tpe), t => iterated(DotDot, t, inner))
+ case (DotDotDot, LiftedType(lift)) => mapF(toStats(lift(tree)), toStats)
+ case _ => global.abort("unreachable")
}
}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
index 1bd9323752..fcb8734644 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
@@ -80,8 +80,20 @@ trait Parsers { self: Quasiquotes =>
}
import treeBuilder.{global => _, unit => _, _}
+ def quasiquoteParam(name: Name, flags: FlagSet = NoFlags) =
+ ValDef(Modifiers(flags), name.toTermName, Ident(tpnme.QUASIQUOTE_PARAM), EmptyTree)
+
// q"def foo($x)"
- override def allowTypelessParams = true
+ override def param(owner: Name, implicitmod: Int, caseParam: Boolean): ValDef =
+ if (isHole && lookingAhead { in.token == COMMA || in.token == RPAREN }) {
+ quasiquoteParam(ident(), implicitmod)
+ } else super.param(owner, implicitmod, caseParam)
+
+ // q"($x) => ..." && q"class X { selfie => }
+ override def convertToParam(tree: Tree): ValDef = tree match {
+ case Ident(name) if isHole(name) => quasiquoteParam(name)
+ case _ => super.convertToParam(tree)
+ }
// q"foo match { case $x }"
override def caseClause(): CaseDef =
@@ -160,17 +172,26 @@ trait Parsers { self: Quasiquotes =>
}
}
- object TermParser extends Parser {
- def entryPoint = { parser =>
- parser.templateOrTopStatSeq() match {
- case head :: Nil => Block(Nil, head)
- case lst => gen.mkTreeOrBlock(lst)
- }
+ /** Wrapper around tree parsed in q"..." quote. Needed to support ..$ splicing on top-level. */
+ object Q {
+ def apply(tree: Tree): Block = Block(Nil, tree).updateAttachment(Q)
+ def unapply(tree: Tree): Option[Tree] = tree match {
+ case Block(Nil, contents) if tree.hasAttachment[Q.type] => Some(contents)
+ case _ => None
}
}
+ object TermParser extends Parser {
+ def entryPoint = parser => Q(gen.mkTreeOrBlock(parser.templateOrTopStatSeq()))
+ }
+
object TypeParser extends Parser {
- def entryPoint = _.typ()
+ def entryPoint = { parser =>
+ if (parser.in.token == EOF)
+ TypeTree()
+ else
+ parser.typ()
+ }
}
object CaseParser extends Parser {
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
index bdb44ad9a2..825d0c04f3 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
@@ -17,7 +17,6 @@ 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()
@@ -58,25 +57,27 @@ trait Placeholders { self: Quasiquotes =>
sb.toString
}
- class HoleMap {
- private var underlying = immutable.SortedMap[String, Hole]()
- private val accessed = mutable.Set[String]()
+ object holeMap {
+ private val underlying = mutable.LinkedHashMap.empty[String, Hole]
+ private val accessed = mutable.Set.empty[String]
def unused: Set[Name] = (underlying.keys.toSet -- accessed).map(TermName(_))
- def contains(key: Name) = underlying.contains(key.toString)
- def apply(key: Name) = {
- val s = key.toString
- accessed += s
- underlying(s)
- }
- def update(key: Name, hole: Hole) = {
+ def contains(key: Name): Boolean = underlying.contains(key.toString)
+ def apply(key: Name): Hole = {
+ val skey = key.toString
+ val value = underlying(skey)
+ accessed += skey
+ value
+ }
+ def update(key: Name, hole: Hole) =
underlying += key.toString -> hole
- }
- def get(key: Name) = {
- val s = key.toString
- accessed += s
- underlying.get(s)
- }
- def toList = underlying.toList
+ def get(key: Name): Option[Hole] = {
+ val skey = key.toString
+ underlying.get(skey).map { v =>
+ accessed += skey
+ v
+ }
+ }
+ def keysIterator: Iterator[TermName] = underlying.keysIterator.map(TermName(_))
}
// Step 2: Transform vanilla Scala AST into an AST with holes
@@ -95,7 +96,6 @@ trait Placeholders { self: Quasiquotes =>
case Ident(name) => name
case Bind(name, Ident(nme.WILDCARD)) => name
case TypeDef(_, name, List(), TypeBoundsTree(EmptyTree, EmptyTree)) => name
- case ValDef(_, name, TypeTree(), EmptyTree) => name
}
}
@@ -111,6 +111,12 @@ trait Placeholders { self: Quasiquotes =>
}
}
+ object ParamPlaceholder extends HolePlaceholder {
+ def matching = {
+ case ValDef(_, name, Ident(tpnme.QUASIQUOTE_PARAM), EmptyTree) => name
+ }
+ }
+
object TuplePlaceholder {
def unapply(tree: Tree): Option[List[Tree]] = tree match {
case Apply(Ident(nme.QUASIQUOTE_TUPLE), args) => Some(args)
@@ -174,4 +180,4 @@ trait Placeholders { self: Quasiquotes =>
case _ => None
}
}
-} \ No newline at end of file
+}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala
index 7d777ef7d5..396688c437 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala
@@ -11,12 +11,12 @@ abstract class Quasiquotes extends Parsers
val global: c.universe.type = c.universe
import c.universe._
- def debug(msg: String): Unit =
+ def debug(msg: => String): Unit =
if (settings.Yquasiquotedebug.value) println(msg)
lazy val (universe: Tree, args, parts, parse, reify, method) = c.macroApplication match {
case Apply(build.SyntacticTypeApplied(Select(Select(Apply(Select(universe0, _), List(Apply(_, parts0))), interpolator0), method0), _), args0) =>
- debug(s"\nparse prefix:\nuniverse=$universe0\nparts=$parts0\ninterpolator=$interpolator0\nmethod=$method0\nargs=$args0\n")
+ debug(s"parse prefix:\nuniverse=$universe0\nparts=$parts0\ninterpolator=$interpolator0\nmethod=$method0\nargs=$args0\n")
val parts1 = parts0.map {
case lit @ Literal(Constant(s: String)) => s -> lit.pos
case part => c.abort(part.pos, "Quasiquotes can only be used with literal strings")
@@ -43,12 +43,12 @@ abstract class Quasiquotes extends Parsers
lazy val universeTypes = new definitions.UniverseDependentTypes(universe)
def expandQuasiquote = {
- debug(s"\nmacro application:\n${c.macroApplication}\n")
- debug(s"\ncode to parse:\n$code\n")
+ debug(s"macro application:\n${c.macroApplication}\n")
+ debug(s"code to parse:\n$code\n")
val tree = parse(code)
debug(s"parsed:\n${showRaw(tree)}\n$tree\n")
val reified = reify(tree)
- val sreified =
+ def sreified =
reified
.toString
.replace("scala.reflect.runtime.`package`.universe.build.", "")
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
index 87ab52414c..017e966f63 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
@@ -29,7 +29,7 @@ trait Reifiers { self: Quasiquotes =>
/** Map that stores freshly generated names linked to the corresponding names in the reified tree.
* This information is used to reify names created by calls to freshTermName and freshTypeName.
*/
- var nameMap = collection.mutable.HashMap.empty[Name, Set[TermName]].withDefault { _ => Set() }
+ val nameMap = collection.mutable.HashMap.empty[Name, Set[TermName]].withDefault { _ => Set() }
/** Wraps expressions into:
* a block which starts with a sequence of vals that correspond
@@ -71,7 +71,7 @@ trait Reifiers { self: Quasiquotes =>
// q"..$freshdefs; $tree"
SyntacticBlock(freshdefs :+ tree)
} else {
- val freevars = holeMap.toList.map { case (name, _) => Ident(name) }
+ val freevars = holeMap.keysIterator.map(Ident(_)).toList
val isVarPattern = tree match { case Bind(name, Ident(nme.WILDCARD)) => true case _ => false }
val cases =
if(isVarPattern) {
@@ -137,6 +137,7 @@ trait Reifiers { self: Quasiquotes =>
case RefineStatPlaceholder(hole) => reifyRefineStat(hole)
case EarlyDefPlaceholder(hole) => reifyEarlyDef(hole)
case PackageStatPlaceholder(hole) => reifyPackageStat(hole)
+ case ParamPlaceholder(hole) => hole.tree
// for enumerators are checked not during splicing but during
// desugaring of the for loop in SyntacticFor & SyntacticForYield
case ForEnumPlaceholder(hole) => hole.tree
@@ -159,8 +160,12 @@ trait Reifiers { self: Quasiquotes =>
reifyBuildCall(nme.SyntacticObjectDef, mods, name, earlyDefs, parents, selfdef, body)
case SyntacticNew(earlyDefs, parents, selfdef, body) =>
reifyBuildCall(nme.SyntacticNew, earlyDefs, parents, selfdef, body)
- case SyntacticDefDef(mods, name, tparams, vparamss, tpt, rhs) =>
- reifyBuildCall(nme.SyntacticDefDef, mods, name, tparams, vparamss, tpt, rhs)
+ case SyntacticDefDef(mods, name, tparams, build.ImplicitParams(vparamss, implparams), tpt, rhs) =>
+ if (implparams.nonEmpty)
+ mirrorBuildCall(nme.SyntacticDefDef, reify(mods), reify(name), reify(tparams),
+ reifyBuildCall(nme.ImplicitParams, vparamss, implparams), reify(tpt), reify(rhs))
+ else
+ reifyBuildCall(nme.SyntacticDefDef, mods, name, tparams, vparamss, tpt, rhs)
case SyntacticValDef(mods, name, tpt, rhs) if tree != noSelfType =>
reifyBuildCall(nme.SyntacticValDef, mods, name, tpt, rhs)
case SyntacticVarDef(mods, name, tpt, rhs) =>
@@ -185,12 +190,18 @@ trait Reifiers { self: Quasiquotes =>
reifyBuildCall(nme.SyntacticFunction, args, body)
case SyntacticIdent(name, isBackquoted) =>
reifyBuildCall(nme.SyntacticIdent, name, isBackquoted)
- case Block(Nil, Placeholder(Hole(tree, DotDot))) =>
+ case SyntacticEmptyTypeTree() =>
+ reifyBuildCall(nme.SyntacticEmptyTypeTree)
+ case SyntacticImport(expr, selectors) =>
+ reifyBuildCall(nme.SyntacticImport, expr, selectors)
+ case Q(Placeholder(Hole(tree, DotDot))) =>
mirrorBuildCall(nme.SyntacticBlock, tree)
- case Block(Nil, other) =>
+ case Q(other) =>
reifyTree(other)
- case Block(stats, last) =>
- reifyBuildCall(nme.SyntacticBlock, stats :+ last)
+ // Syntactic block always matches so we have to be careful
+ // not to cause infinite recursion.
+ case block @ SyntacticBlock(stats) if block.isInstanceOf[Block] =>
+ reifyBuildCall(nme.SyntacticBlock, stats)
case Try(block, catches, finalizer) =>
reifyBuildCall(nme.SyntacticTry, block, catches, finalizer)
case Match(selector, cases) =>
@@ -298,7 +309,7 @@ trait Reifiers { self: Quasiquotes =>
* > reifyMultiCardinalityList(lst) { ... } { ... }
* q"List($foo, $bar) ++ ${holeMap(qq$f3948f9s$1).tree}"
*/
- def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree): Tree
+ def reifyMultiCardinalityList(xs: List[Any])(fill: PartialFunction[Any, Tree])(fallback: Any => Tree): Tree
/** Reifies arbitrary list filling ..$x and ...$y holeMap when they are put
* in the correct position. Fallbacks to regular reification for non-high cardinality
@@ -311,6 +322,8 @@ trait Reifiers { self: Quasiquotes =>
case EarlyDefPlaceholder(h @ Hole(_, DotDot)) => reifyEarlyDef(h)
case PackageStatPlaceholder(h @ Hole(_, DotDot)) => reifyPackageStat(h)
case ForEnumPlaceholder(Hole(tree, DotDot)) => tree
+ case ParamPlaceholder(Hole(tree, DotDot)) => tree
+ case List(ParamPlaceholder(Hole(tree, DotDotDot))) => tree
case List(Placeholder(Hole(tree, DotDotDot))) => tree
} {
reify(_)
@@ -352,10 +365,10 @@ trait Reifiers { self: Quasiquotes =>
}
class ApplyReifier extends Reifier(isReifyingExpressions = true) {
- def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree): Tree =
+ def reifyMultiCardinalityList(xs: List[Any])(fill: PartialFunction[Any, Tree])(fallback: Any => Tree): Tree =
if (xs.isEmpty) mkList(Nil)
else {
- def reifyGroup(group: List[T]): Tree = group match {
+ def reifyGroup(group: List[Any]): Tree = group match {
case List(elem) if fill.isDefinedAt(elem) => fill(elem)
case elems => mkList(elems.map(fallback))
}
@@ -394,14 +407,26 @@ trait Reifiers { self: Quasiquotes =>
}
class UnapplyReifier extends Reifier(isReifyingExpressions = false) {
- def reifyMultiCardinalityList[T](xs: List[T])(fill: PartialFunction[T, Tree])(fallback: T => Tree): Tree = xs match {
- case init :+ last if fill.isDefinedAt(last) =>
- init.foldRight[Tree](fill(last)) { (el, rest) =>
- val cons = Select(Select(Select(Ident(nme.scala_), nme.collection), nme.immutable), nme.CONS)
- Apply(cons, List(fallback(el), rest))
- }
- case _ =>
- mkList(xs.map(fallback))
+ private def collection = ScalaDot(nme.collection)
+ private def collectionColonPlus = Select(collection, nme.COLONPLUS)
+ private def collectionCons = Select(Select(collection, nme.immutable), nme.CONS)
+ private def collectionNil = Select(Select(collection, nme.immutable), nme.Nil)
+ // pq"$lhs :+ $rhs"
+ private def append(lhs: Tree, rhs: Tree) = Apply(collectionColonPlus, lhs :: rhs :: Nil)
+ // pq"$lhs :: $rhs"
+ private def cons(lhs: Tree, rhs: Tree) = Apply(collectionCons, lhs :: rhs :: Nil)
+
+ def reifyMultiCardinalityList(xs: List[Any])(fill: PartialFunction[Any, Tree])(fallback: Any => Tree): Tree = {
+ val grouped = group(xs) { (a, b) => !fill.isDefinedAt(a) && !fill.isDefinedAt(b) }
+ def appended(lst: List[Any], init: Tree) = lst.foldLeft(init) { (l, r) => append(l, fallback(r)) }
+ def prepended(lst: List[Any], init: Tree) = lst.foldRight(init) { (l, r) => cons(fallback(l), r) }
+ grouped match {
+ case init :: List(hole) :: last :: Nil if fill.isDefinedAt(hole) => appended(last, prepended(init, fill(hole)))
+ case init :: List(hole) :: Nil if fill.isDefinedAt(hole) => prepended(init, fill(hole))
+ case List(hole) :: last :: Nil if fill.isDefinedAt(hole) => appended(last, fill(hole))
+ case List(hole) :: Nil if fill.isDefinedAt(hole) => fill(hole)
+ case _ => prepended(xs, collectionNil)
+ }
}
override def reifyModifiers(m: Modifiers) =