summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/reflect/macros/compiler/Errors.scala2
-rw-r--r--src/compiler/scala/reflect/macros/compiler/Resolvers.scala35
-rw-r--r--src/compiler/scala/reflect/macros/compiler/Validators.scala18
-rw-r--r--src/compiler/scala/reflect/macros/contexts/Context.scala4
-rw-r--r--src/compiler/scala/reflect/macros/contexts/Enclosures.scala4
-rw-r--r--src/compiler/scala/reflect/macros/runtime/JavaReflectionRuntimes.scala15
-rw-r--r--src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala14
-rw-r--r--src/compiler/scala/reflect/macros/runtime/ScalaReflectionRuntimes.scala31
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala19
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala20
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala1
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala1
-rw-r--r--src/compiler/scala/tools/nsc/javac/JavaParsers.scala7
-rw-r--r--src/compiler/scala/tools/nsc/plugins/Plugin.scala5
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala16
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/CleanUp.scala43
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala11
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala164
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala78
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/PatternExpander.scala155
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala6
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala154
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala283
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala8
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala383
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala69
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala146
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala10
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala62
32 files changed, 1063 insertions, 707 deletions
diff --git a/src/compiler/scala/reflect/macros/compiler/Errors.scala b/src/compiler/scala/reflect/macros/compiler/Errors.scala
index 4c30a9a85c..280baa2a42 100644
--- a/src/compiler/scala/reflect/macros/compiler/Errors.scala
+++ b/src/compiler/scala/reflect/macros/compiler/Errors.scala
@@ -51,7 +51,7 @@ trait Errors extends Traces {
def MacroBundleNonStaticError() = bundleRefError("macro bundles must be static")
- def MacroBundleWrongShapeError() = bundleRefError("macro bundles must be monomorphic traits extending either BlackboxMacro or WhiteboxMacro and not implementing their `val c: BlackboxContext/WhiteboxContext` member")
+ def MacroBundleWrongShapeError() = bundleRefError("macro bundles must be concrete classes having a single constructor with a `val c: Context` parameter")
// compatibility errors
diff --git a/src/compiler/scala/reflect/macros/compiler/Resolvers.scala b/src/compiler/scala/reflect/macros/compiler/Resolvers.scala
index e4851632a5..d35f1c32a9 100644
--- a/src/compiler/scala/reflect/macros/compiler/Resolvers.scala
+++ b/src/compiler/scala/reflect/macros/compiler/Resolvers.scala
@@ -40,36 +40,11 @@ trait Resolvers {
}
val untypedImplRef = typer.silent(_.typedTypeConstructor(maybeBundleRef)) match {
- case SilentResultValue(result) if mightBeMacroBundleType(result.tpe) =>
- val bundleProto = result.tpe.typeSymbol
- val bundlePkg = bundleProto.enclosingPackageClass
- if (!isMacroBundleProtoType(bundleProto.tpe)) MacroBundleWrongShapeError()
- if (!bundleProto.owner.isStaticOwner) MacroBundleNonStaticError()
-
- // synthesize the bundle, i.e. given a static `trait Foo extends Macro { def expand = ... } `
- // create a top-level definition `class Foo$Bundle(val c: BlackboxContext/WhiteboxContext) extends Foo` in a package next to `Foo`
- val bundlePid = gen.mkUnattributedRef(bundlePkg)
- val bundlePrefix =
- if (bundlePkg == EmptyPackageClass) bundleProto.fullName('$')
- else bundleProto.fullName('$').substring(bundlePkg.fullName('$').length + 1)
- val bundleName = TypeName(bundlePrefix + tpnme.MACRO_BUNDLE_SUFFIX)
- val existingBundle = bundleProto.enclosingPackageClass.info.decl(bundleName)
- if (!currentRun.compiles(existingBundle)) {
- val contextType = if (isBlackboxMacroBundleType(bundleProto.tpe)) BlackboxContextClass.tpe else WhiteboxContextClass.tpe
- def mkContextValDef(flags: Long) = ValDef(Modifiers(flags), nme.c, TypeTree(contextType), EmptyTree)
- val contextField = mkContextValDef(PARAMACCESSOR)
- val contextParam = mkContextValDef(PARAM | PARAMACCESSOR)
- val bundleCtor = DefDef(Modifiers(), nme.CONSTRUCTOR, Nil, List(List(contextParam)), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(()))))
- val bundleParent = gen.mkAppliedTypeTree(Ident(bundleProto), bundleProto.typeParams.map(sym => Ident(sym.name)))
- val bundleTemplate = Template(List(bundleParent), noSelfType, List(contextField, bundleCtor))
- val bundle = atPos(bundleProto.pos)(ClassDef(NoMods, bundleName, bundleProto.typeParams.map(TypeDef(_)), bundleTemplate))
- currentRun.compileLate(bundleName + ".scala", PackageDef(bundlePid, List(bundle)))
- }
-
- // synthesize the macro impl reference, which is going to look like:
- // `new FooBundle(???).macroName` plus the optional type arguments
- val bundleInstance = New(Select(bundlePid, bundleName), List(List(Ident(Predef_???))))
- atPos(macroDdef.rhs.pos)(gen.mkTypeApply(Select(bundleInstance, methName), targs))
+ case SilentResultValue(result) if looksLikeMacroBundleType(result.tpe) =>
+ val bundle = result.tpe.typeSymbol
+ if (!isMacroBundleType(bundle.tpe)) MacroBundleWrongShapeError()
+ if (!bundle.owner.isStaticOwner) MacroBundleNonStaticError()
+ atPos(macroDdef.rhs.pos)(gen.mkTypeApply(Select(New(bundle, Ident(Predef_???)), methName), targs))
case _ =>
macroDdef.rhs
}
diff --git a/src/compiler/scala/reflect/macros/compiler/Validators.scala b/src/compiler/scala/reflect/macros/compiler/Validators.scala
index 5936b52890..02c1f7c431 100644
--- a/src/compiler/scala/reflect/macros/compiler/Validators.scala
+++ b/src/compiler/scala/reflect/macros/compiler/Validators.scala
@@ -26,9 +26,9 @@ trait Validators {
if (macroImpl.isOverloaded) MacroImplOverloadedError()
val implicitParams = aparamss.flatten filter (_.isImplicit)
if (implicitParams.nonEmpty) MacroImplNonTagImplicitParameters(implicitParams)
- val declaredInStaticObject = isImplMethod && (macroImplOwner.isStaticOwner || macroImplOwner.moduleClass.isStaticOwner)
- val declaredInTopLevelClass = isImplBundle && macroImplOwner.owner.isPackageClass
- if (!declaredInStaticObject && !declaredInTopLevelClass) MacroImplReferenceWrongShapeError()
+ val effectiveOwner = if (isImplMethod) macroImplOwner else macroImplOwner.owner
+ val declaredInStaticObject = effectiveOwner.isStaticOwner || effectiveOwner.moduleClass.isStaticOwner
+ if (!declaredInStaticObject) MacroImplReferenceWrongShapeError()
}
private def checkMacroDefMacroImplCorrespondence() = {
@@ -93,20 +93,20 @@ trait Validators {
*
* For the following macro impl:
* def fooBar[T: c.WeakTypeTag]
- * (c: scala.reflect.macros.BlackboxContext)
+ * (c: scala.reflect.macros.blackbox.Context)
* (xs: c.Expr[List[T]])
* : c.Expr[T] = ...
*
* This function will return:
- * (c: scala.reflect.macros.BlackboxContext)(xs: c.Expr[List[T]])c.Expr[T]
+ * (c: scala.reflect.macros.blackbox.Context)(xs: c.Expr[List[T]])c.Expr[T]
*
* Note that type tag evidence parameters are not included into the result.
* Type tag context bounds for macro impl tparams are optional.
* Therefore compatibility checks ignore such parameters, and we don't need to bother about them here.
*
* This method cannot be reduced to just macroImpl.info, because macro implementations might
- * come in different shapes. If the implementation is an apply method of a BlackboxMacro/WhiteboxMacro-compatible object,
- * then it won't have (c: BlackboxContext/WhiteboxContext) in its parameters, but will rather refer to BlackboxMacro/WhiteboxMacro.c.
+ * come in different shapes. If the implementation is an apply method of a *box.Macro-compatible object,
+ * then it won't have (c: *box.Context) in its parameters, but will rather refer to *boxMacro.c.
*
* @param macroImpl The macro implementation symbol
*/
@@ -123,8 +123,8 @@ trait Validators {
* def foo[T](xs: List[T]): T = macro fooBar
*
* This function will return:
- * (c: scala.reflect.macros.BlackboxContext)(xs: c.Expr[List[T]])c.Expr[T] or
- * (c: scala.reflect.macros.WhiteboxContext)(xs: c.Expr[List[T]])c.Expr[T]
+ * (c: scala.reflect.macros.blackbox.Context)(xs: c.Expr[List[T]])c.Expr[T] or
+ * (c: scala.reflect.macros.whitebox.Context)(xs: c.Expr[List[T]])c.Expr[T]
*
* Note that type tag evidence parameters are not included into the result.
* Type tag context bounds for macro impl tparams are optional.
diff --git a/src/compiler/scala/reflect/macros/contexts/Context.scala b/src/compiler/scala/reflect/macros/contexts/Context.scala
index 7b79b52a18..87dac18849 100644
--- a/src/compiler/scala/reflect/macros/contexts/Context.scala
+++ b/src/compiler/scala/reflect/macros/contexts/Context.scala
@@ -3,8 +3,8 @@ package contexts
import scala.tools.nsc.Global
-abstract class Context extends scala.reflect.macros.BlackboxContext
- with scala.reflect.macros.WhiteboxContext
+abstract class Context extends scala.reflect.macros.blackbox.Context
+ with scala.reflect.macros.whitebox.Context
with Aliases
with Enclosures
with Names
diff --git a/src/compiler/scala/reflect/macros/contexts/Enclosures.scala b/src/compiler/scala/reflect/macros/contexts/Enclosures.scala
index bb88c8d5e1..5e931817b5 100644
--- a/src/compiler/scala/reflect/macros/contexts/Enclosures.scala
+++ b/src/compiler/scala/reflect/macros/contexts/Enclosures.scala
@@ -8,10 +8,6 @@ trait Enclosures {
import universe._
- type MacroRole = analyzer.MacroRole
- def APPLY_ROLE = analyzer.APPLY_ROLE
- def macroRole: MacroRole
-
private lazy val site = callsiteTyper.context
private lazy val enclTrees = site.enclosingContextChain map (_.tree)
private lazy val enclPoses = enclosingMacros map (_.macroApplication.pos) filterNot (_ eq NoPosition)
diff --git a/src/compiler/scala/reflect/macros/runtime/JavaReflectionRuntimes.scala b/src/compiler/scala/reflect/macros/runtime/JavaReflectionRuntimes.scala
index 450cb4d9ea..ecdd48db22 100644
--- a/src/compiler/scala/reflect/macros/runtime/JavaReflectionRuntimes.scala
+++ b/src/compiler/scala/reflect/macros/runtime/JavaReflectionRuntimes.scala
@@ -2,7 +2,9 @@ package scala.reflect.macros
package runtime
import scala.reflect.runtime.ReflectionUtils
-import scala.reflect.macros.{Context => ApiContext}
+import scala.reflect.macros.blackbox.{Context => BlackboxContext}
+import scala.reflect.macros.whitebox.{Context => WhiteboxContext}
+import java.lang.reflect.{Constructor => jConstructor}
trait JavaReflectionRuntimes {
self: scala.tools.nsc.typechecker.Analyzer =>
@@ -19,8 +21,15 @@ trait JavaReflectionRuntimes {
macroLogVerbose(s"successfully loaded macro impl as ($implClass, $implMeth)")
args => {
val implObj =
- if (isBundle) implClass.getConstructor(classOf[ApiContext]).newInstance(args.c)
- else ReflectionUtils.staticSingletonInstance(implClass)
+ if (isBundle) {
+ def isMacroContext(clazz: Class[_]) = clazz == classOf[BlackboxContext] || clazz == classOf[WhiteboxContext]
+ def isBundleCtor(ctor: jConstructor[_]) = ctor.getParameterTypes match {
+ case Array(param) if isMacroContext(param) => true
+ case _ => false
+ }
+ val Array(bundleCtor) = implClass.getConstructors.filter(isBundleCtor)
+ bundleCtor.newInstance(args.c)
+ } else ReflectionUtils.staticSingletonInstance(implClass)
val implArgs = if (isBundle) args.others else args.c +: args.others
implMeth.invoke(implObj, implArgs.asInstanceOf[Seq[AnyRef]]: _*)
}
diff --git a/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala b/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala
index 7de3341304..5fd9c0db34 100644
--- a/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala
+++ b/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala
@@ -4,7 +4,7 @@ package runtime
import scala.reflect.internal.Flags._
import scala.reflect.runtime.ReflectionUtils
-trait MacroRuntimes extends JavaReflectionRuntimes with ScalaReflectionRuntimes {
+trait MacroRuntimes extends JavaReflectionRuntimes {
self: scala.tools.nsc.typechecker.Analyzer =>
import global._
@@ -19,8 +19,14 @@ trait MacroRuntimes extends JavaReflectionRuntimes with ScalaReflectionRuntimes
* @return Requested runtime if macro implementation can be loaded successfully from either of the mirrors,
* `null` otherwise.
*/
+ def macroRuntime(expandee: Tree): MacroRuntime = pluginsMacroRuntime(expandee)
+
+ /** Default implementation of `macroRuntime`.
+ * Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsMacroRuntime for more details)
+ */
private val macroRuntimesCache = perRunCaches.newWeakMap[Symbol, MacroRuntime]
- def macroRuntime(macroDef: Symbol): MacroRuntime = {
+ def standardMacroRuntime(expandee: Tree): MacroRuntime = {
+ val macroDef = expandee.symbol
macroLogVerbose(s"looking for macro implementation: $macroDef")
if (fastTrack contains macroDef) {
macroLogVerbose("macro expansion is serviced by a fast track")
@@ -43,8 +49,7 @@ trait MacroRuntimes extends JavaReflectionRuntimes with ScalaReflectionRuntimes
/** Abstracts away resolution of macro runtimes.
*/
type MacroRuntime = MacroArgs => Any
- class MacroRuntimeResolver(val macroDef: Symbol) extends JavaReflectionResolvers
- with ScalaReflectionResolvers {
+ class MacroRuntimeResolver(val macroDef: Symbol) extends JavaReflectionResolvers {
val binding = loadMacroImplBinding(macroDef).get
val isBundle = binding.isBundle
val className = binding.className
@@ -57,7 +62,6 @@ trait MacroRuntimes extends JavaReflectionRuntimes with ScalaReflectionRuntimes
try {
macroLogVerbose(s"resolving macro implementation as $className.$methName (isBundle = $isBundle)")
macroLogVerbose(s"classloader is: ${ReflectionUtils.show(defaultMacroClassloader)}")
- // resolveScalaReflectionRuntime(defaultMacroClassloader)
resolveJavaReflectionRuntime(defaultMacroClassloader)
} catch {
case ex: Exception =>
diff --git a/src/compiler/scala/reflect/macros/runtime/ScalaReflectionRuntimes.scala b/src/compiler/scala/reflect/macros/runtime/ScalaReflectionRuntimes.scala
deleted file mode 100644
index 50f64310f8..0000000000
--- a/src/compiler/scala/reflect/macros/runtime/ScalaReflectionRuntimes.scala
+++ /dev/null
@@ -1,31 +0,0 @@
-package scala.reflect.macros
-package runtime
-
-import scala.reflect.runtime.{universe => ru}
-
-trait ScalaReflectionRuntimes {
- self: scala.tools.nsc.typechecker.Analyzer =>
-
- trait ScalaReflectionResolvers {
- self: MacroRuntimeResolver =>
-
- def resolveScalaReflectionRuntime(classLoader: ClassLoader): MacroRuntime = {
- val macroMirror: ru.JavaMirror = ru.runtimeMirror(classLoader)
- val implContainerSym = macroMirror.classSymbol(Class.forName(className, true, classLoader))
- val implMethSym = implContainerSym.typeSignature.member(ru.TermName(methName)).asMethod
- macroLogVerbose(s"successfully loaded macro impl as ($implContainerSym, $implMethSym)")
- args => {
- val implContainer =
- if (isBundle) {
- val implCtorSym = implContainerSym.typeSignature.member(ru.nme.CONSTRUCTOR).asMethod
- macroMirror.reflectClass(implContainerSym).reflectConstructor(implCtorSym)(args.c)
- } else {
- macroMirror.reflectModule(implContainerSym.module.asModule).instance
- }
- val implMeth = macroMirror.reflect(implContainer).reflectMethod(implMethSym)
- val implArgs = if (isBundle) args.others else args.c +: args.others
- implMeth(implArgs: _*)
- }
- }
- }
-}
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 5f079a428b..5492e563dd 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -1710,25 +1710,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
}
}
- /** Create and compile a synthetic compilation unit from the provided tree.
- *
- * This needs to create a virtual file underlying the compilation unit in order to appease SBT.
- * However this file cannot have a randomly generated name, because then SBT 0.13 goes into a vicious loop
- * as described on the mailing list: https://groups.google.com/forum/#!msg/scala-user/r1SgSoVfs0U/Wv4av0LOKukJ
- * Therefore I have introduced an additional parameter that makes everyone specify meaningful file names.
- */
- def compileLate(virtualFileName: String, code: PackageDef) {
- // compatibility with SBT
- // on the one hand, we need to specify some jfile here, otherwise sbt crashes with an NPE (SI-6870)
- // on the other hand, we can't specify the obvious enclosingUnit, because then sbt somehow fails to run tests using type macros
- val fakeJfile = new java.io.File(virtualFileName)
- val virtualFile = new VirtualFile(virtualFileName) { override def file = fakeJfile }
- val sourceFile = new BatchSourceFile(virtualFile, code.toString)
- val unit = new CompilationUnit(sourceFile)
- unit.body = code
- compileLate(unit)
- }
-
/** Reset package class to state at typer (not sure what this
* is needed for?)
*/
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index 61ea9230a7..d122a1a207 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -623,15 +623,6 @@ self =>
syntaxError(tpt.pos, "no * parameter type allowed here", skipIt = false)
}
- /** Check that tree is a legal clause of a forSome. */
- def checkLegalExistential(t: Tree) = t match {
- case TypeDef(_, _, _, TypeBoundsTree(_, _)) |
- ValDef(_, _, _, EmptyTree) | EmptyTree =>
- ;
- case _ =>
- syntaxError(t.pos, "not a legal existential clause", skipIt = false)
- }
-
/* -------------- TOKEN CLASSES ------------------------------------------- */
def isModifier: Boolean = in.token match {
@@ -885,9 +876,14 @@ self =>
}
}
private def makeExistentialTypeTree(t: Tree) = {
- val whereClauses = refinement()
- whereClauses foreach checkLegalExistential
- ExistentialTypeTree(t, whereClauses)
+ // EmptyTrees in the result of refinement() stand for parse errors
+ // so it's okay for us to filter them out here
+ ExistentialTypeTree(t, refinement() flatMap {
+ case t @ TypeDef(_, _, _, TypeBoundsTree(_, _)) => Some(t)
+ case t @ ValDef(_, _, _, EmptyTree) => Some(t)
+ case EmptyTree => None
+ case _ => syntaxError(t.pos, "not a legal existential clause", skipIt = false); None
+ })
}
/** {{{
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala
index 5be5abd895..dd2d63ad17 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala
@@ -835,6 +835,7 @@ abstract class BCodeTypes extends BCodeIdiomatic {
if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0,
if (sym.isArtifact) ACC_SYNTHETIC else 0,
if (sym.isClass && !sym.isInterface) ACC_SUPER else 0,
+ if (sym.hasEnumFlag) ACC_ENUM else 0,
if (sym.isVarargsMethod) ACC_VARARGS else 0,
if (sym.hasFlag(symtab.Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0
)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index e92f8c2541..7e1a82a155 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -244,6 +244,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0,
if (sym.isArtifact) ACC_SYNTHETIC else 0,
if (sym.isClass && !sym.isInterface) ACC_SUPER else 0,
+ if (sym.hasEnumFlag) ACC_ENUM else 0,
if (sym.isVarargsMethod) ACC_VARARGS else 0,
if (sym.hasFlag(Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0
)
diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala
index 7932dd3459..9875d27047 100644
--- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala
+++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala
@@ -792,7 +792,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
val superclazz =
AppliedTypeTree(javaLangDot(tpnme.Enum), List(enumType))
addCompanionObject(consts ::: statics ::: predefs, atPos(pos) {
- ClassDef(mods, name, List(),
+ ClassDef(mods | Flags.ENUM, name, List(),
makeTemplate(superclazz :: interfaces, body))
})
}
@@ -811,10 +811,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
skipAhead()
accept(RBRACE)
}
- // The STABLE flag is to signal to namer that this was read from a
- // java enum, and so should be given a Constant type (thereby making
- // it usable in annotations.)
- ValDef(Modifiers(Flags.STABLE | Flags.JAVA | Flags.STATIC), name.toTermName, enumType, blankExpr)
+ ValDef(Modifiers(Flags.ENUM | Flags.STABLE | Flags.JAVA | Flags.STATIC), name.toTermName, enumType, blankExpr)
}
}
diff --git a/src/compiler/scala/tools/nsc/plugins/Plugin.scala b/src/compiler/scala/tools/nsc/plugins/Plugin.scala
index 183752d4a2..7837f9a11a 100644
--- a/src/compiler/scala/tools/nsc/plugins/Plugin.scala
+++ b/src/compiler/scala/tools/nsc/plugins/Plugin.scala
@@ -126,10 +126,11 @@ object Plugin {
}
/** Load all plugins specified by the arguments.
- * Each of `jars` must be a valid plugin archive or exploded archive.
+ * Each location of `paths` must be a valid plugin archive or exploded archive.
+ * Each of `paths` must define one plugin.
* Each of `dirs` may be a directory containing arbitrary plugin archives.
* Skips all plugins named in `ignoring`.
- * A single classloader is created and used to load all of them.
+ * A classloader is created to load each plugin.
*/
def loadAllFrom(
paths: List[List[Path]],
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 7568c789fb..6ec364bcb6 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -94,11 +94,11 @@ trait ScalaSettings extends AbsScalaSettings
val nouescape = BooleanSetting ("-Xno-uescape", "Disable handling of \\u unicode escapes.")
val Xnojline = BooleanSetting ("-Xnojline", "Do not use JLine for editing.")
val Xverify = BooleanSetting ("-Xverify", "Verify generic signatures in generated bytecode (asm backend only.)")
- val plugin = MultiStringSetting("-Xplugin", "file", "Load one or more plugins from files.")
- val disable = MultiStringSetting("-Xplugin-disable", "plugin", "Disable the given plugin(s).")
+ val plugin = MultiStringSetting("-Xplugin", "paths", "Load a plugin from each classpath.")
+ val disable = MultiStringSetting("-Xplugin-disable", "plugin", "Disable plugins by name.")
val showPlugins = BooleanSetting ("-Xplugin-list", "Print a synopsis of loaded plugins.")
- val require = MultiStringSetting("-Xplugin-require", "plugin", "Abort unless the given plugin(s) are available.")
- val pluginsDir = StringSetting ("-Xpluginsdir", "path", "Path to search compiler plugins.", Defaults.scalaPluginPath)
+ val require = MultiStringSetting("-Xplugin-require", "plugin", "Abort if a named plugin is not loaded.")
+ val pluginsDir = StringSetting ("-Xpluginsdir", "path", "Path to search for plugin archives.", Defaults.scalaPluginPath)
val Xprint = PhasesSetting ("-Xprint", "Print out program after")
val writeICode = PhasesSetting ("-Xprint-icode", "Log internal icode to *.icode files after", "icode")
val Xprintpos = BooleanSetting ("-Xprint-pos", "Print tree positions, as offsets.")
@@ -172,7 +172,8 @@ trait ScalaSettings extends AbsScalaSettings
val Yrangepos = BooleanSetting ("-Yrangepos", "Use range positions for syntax trees.")
val Ymemberpos = StringSetting ("-Yshow-member-pos", "output style", "Show start and end positions of members", "") withPostSetHook (_ => Yrangepos.value = true)
val Yreifycopypaste = BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.")
- val Ymacronoexpand = BooleanSetting ("-Ymacro-no-expand", "Don't expand macros. Might be useful for scaladoc and presentation compiler, but will crash anything which uses macros and gets past typer.")
+ val Ymacroexpand = ChoiceSetting ("-Ymacro-expand", "policy", "Control expansion of macros, useful for scaladoc and presentation compiler", List(MacroExpand.Normal, MacroExpand.None, MacroExpand.Discard), MacroExpand.Normal)
+ val Ymacronoexpand = BooleanSetting ("-Ymacro-no-expand", "Don't expand macros. Might be useful for scaladoc and presentation compiler, but will crash anything which uses macros and gets past typer.") withDeprecationMessage(s"Use ${Ymacroexpand.name}:${MacroExpand.None}") withPostSetHook(_ => Ymacroexpand.value = MacroExpand.None)
val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup")
val Yreplclassbased = BooleanSetting ("-Yrepl-class-based", "Use classes to wrap REPL snippets instead of objects")
val Yreploutdir = StringSetting ("-Yrepl-outdir", "path", "Write repl-generated classfiles to given output directory (use \"\" to generate a temporary dir)" , "")
@@ -249,4 +250,9 @@ trait ScalaSettings extends AbsScalaSettings
def isBCodeAskedFor = (Ybackend.value != "GenASM")
def isICodeAskedFor = ((Ybackend.value == "GenASM") || optimiseSettings.exists(_.value) || writeICode.isSetByUser)
+ object MacroExpand {
+ val None = "none"
+ val Normal = "normal"
+ val Discard = "discard"
+ }
}
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
index 2b96961291..664645e53e 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
@@ -515,7 +515,7 @@ abstract class ClassfileParser {
val info = readType()
val sym = ownerForFlags(jflags).newValue(name.toTermName, NoPosition, sflags)
- // Note: the info may be overrwritten later with a generic signature
+ // Note: the info may be overwritten later with a generic signature
// parsed from SignatureATTR
sym setInfo {
if (jflags.isEnum) ConstantType(Constant(sym))
diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala
index 9738769db9..f14fce5de9 100644
--- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala
+++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala
@@ -481,18 +481,33 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
* For instance, say we have a Scala class:
*
* class Cls {
- * // ...
- * def someSymbol = `symbolic
- * // ...
+ * def someSymbol1 = 'Symbolic1
+ * def someSymbol2 = 'Symbolic2
+ * def sameSymbol1 = 'Symbolic1
+ * val someSymbol3 = 'Symbolic3
* }
*
* After transformation, this class looks like this:
*
* class Cls {
- * private "static" val <some_name>$symbolic = Symbol("symbolic")
- * // ...
- * def someSymbol = <some_name>$symbolic
- * // ...
+ * private <static> var symbol$1: scala.Symbol
+ * private <static> var symbol$2: scala.Symbol
+ * private <static> var symbol$3: scala.Symbol
+ * private val someSymbol3: scala.Symbol
+ *
+ * private <static> def <clinit> = {
+ * symbol$1 = Symbol.apply("Symbolic1")
+ * symbol$2 = Symbol.apply("Symbolic2")
+ * }
+ *
+ * private def <init> = {
+ * someSymbol3 = symbol$3
+ * }
+ *
+ * def someSymbol1 = symbol$1
+ * def someSymbol2 = symbol$2
+ * def sameSymbol1 = symbol$1
+ * val someSymbol3 = someSymbol3
* }
*
* The reasoning behind this transformation is the following. Symbols get interned - they are stored
@@ -502,17 +517,17 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
* is accessed only once during class loading, and after that, the unique symbol is in the static
* member. Hence, it is cheap to both reach the unique symbol and do equality checks on it.
*
- * And, finally, be advised - scala symbol literal and the Symbol class of the compiler
+ * And, finally, be advised - Scala's Symbol literal (scala.Symbol) and the Symbol class of the compiler
* have little in common.
*/
case Apply(fn, (arg @ Literal(Constant(symname: String))) :: Nil) if fn.symbol == Symbol_apply =>
def transformApply = {
- // add the symbol name to a map if it's not there already
- val rhs = gen.mkMethodCall(Symbol_apply, arg :: Nil)
- val staticFieldSym = getSymbolStaticField(tree.pos, symname, rhs, tree)
- // create a reference to a static field
- val ntree = typedWithPos(tree.pos)(REF(staticFieldSym))
- super.transform(ntree)
+ // add the symbol name to a map if it's not there already
+ val rhs = gen.mkMethodCall(Symbol_apply, arg :: Nil)
+ val staticFieldSym = getSymbolStaticField(tree.pos, symname, rhs, tree)
+ // create a reference to a static field
+ val ntree = typedWithPos(tree.pos)(REF(staticFieldSym))
+ super.transform(ntree)
}
transformApply
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 844774e75f..ef50ae276f 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -457,12 +457,11 @@ abstract class UnCurry extends InfoTransform
else
super.transform(tree)
case UnApply(fn, args) =>
- val fn1 = transform(fn)
- val args1 = transformTrees(fn.symbol.name match {
- case nme.unapply => args
- case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, localTyper.expectedPatternTypes(fn, args))
- case _ => sys.error("internal error: UnApply node has wrong symbol")
- })
+ val fn1 = transform(fn)
+ val args1 = fn.symbol.name match {
+ case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, patmat.alignPatterns(tree).expectedTypes)
+ case _ => args
+ }
treeCopy.UnApply(tree, fn1, args1)
case Apply(fn, args) =>
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
index 63f4a4bf25..699e98f963 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala
@@ -31,6 +31,30 @@ trait MatchTranslation {
trait MatchTranslator extends TreeMakers with TreeMakerWarnings {
import typer.context
+ /** A conservative approximation of which patterns do not discern anything.
+ * They are discarded during the translation.
+ */
+ object WildcardPattern {
+ def unapply(pat: Tree): Boolean = pat match {
+ case Bind(nme.WILDCARD, WildcardPattern()) => true // don't skip when binding an interesting symbol!
+ case Star(WildcardPattern()) => true
+ case x: Ident => treeInfo.isVarPattern(x)
+ case Alternative(ps) => ps forall unapply
+ case EmptyTree => true
+ case _ => false
+ }
+ }
+
+ object PatternBoundToUnderscore {
+ def unapply(pat: Tree): Boolean = pat match {
+ case Bind(nme.WILDCARD, _) => true // don't skip when binding an interesting symbol!
+ case Ident(nme.WILDCARD) => true
+ case Alternative(ps) => ps forall unapply
+ case Typed(PatternBoundToUnderscore(), _) => true
+ case _ => false
+ }
+ }
+
object SymbolBound {
def unapply(tree: Tree): Option[(Symbol, Tree)] = tree match {
case Bind(_, expr) if hasSym(tree) => Some(tree.symbol -> expr)
@@ -86,10 +110,8 @@ trait MatchTranslation {
// example check: List[Int] <:< ::[Int]
private def extractorStep(): TranslationStep = {
- import extractor.{ paramType, treeMaker }
- if (!extractor.isTyped)
- ErrorUtils.issueNormalTypeError(tree, "Could not typecheck extractor call: "+ extractor)(context)
-
+ def paramType = extractor.aligner.wholeType
+ import extractor.treeMaker
// chain a type-testing extractor before the actual extractor call
// it tests the type, checks the outer pointer and casts to the expected type
// TODO: the outer check is mandated by the spec for case classes, but we do it for user-defined unapplies as well [SPEC]
@@ -355,36 +377,20 @@ trait MatchTranslation {
object ExtractorCall {
// TODO: check unargs == args
def apply(tree: Tree): ExtractorCall = tree match {
- case UnApply(unfun, args) => new ExtractorCallRegular(unfun, args) // extractor
- case Apply(fun, args) => new ExtractorCallProd(fun, args) // case class
+ case UnApply(unfun, args) => new ExtractorCallRegular(alignPatterns(tree), unfun, args) // extractor
+ case Apply(fun, args) => new ExtractorCallProd(alignPatterns(tree), fun, args) // case class
}
}
- abstract class ExtractorCall {
+ abstract class ExtractorCall(val aligner: PatternAligned) {
+ import aligner._
def fun: Tree
def args: List[Tree]
- val nbSubPats = args.length
- val starLength = if (hasStar) 1 else 0
- val nonStarLength = args.length - starLength
-
- // everything okay, captain?
- def isTyped: Boolean
- def isSeq: Boolean
-
- private def hasStar = nbSubPats > 0 && isStar(args.last)
- private def isNonEmptySeq = nbSubPats > 0 && isSeq
-
- /** This is special cased so that a single pattern will accept any extractor
- * result, even if it's a tuple (SI-6675)
- */
- def isSingle = nbSubPats == 1 && !isSeq
-
- // to which type should the previous binder be casted?
- def paramType : Type
-
- protected def rawSubPatTypes: List[Type]
- protected def resultType: Type
+ // don't go looking for selectors if we only expect one pattern
+ def rawSubPatTypes = aligner.extractedTypes
+ def resultInMonad = if (isBool) UnitTpe else typeOfMemberNamedGet(resultType)
+ def resultType = fun.tpe.finalResultType
/** Create the TreeMaker that embodies this extractor call
*
@@ -407,24 +413,14 @@ trait MatchTranslation {
lazy val ignoredSubPatBinders: Set[Symbol] = subPatBinders zip args collect { case (b, PatternBoundToUnderscore()) => b } toSet
// do repeated-parameter expansion to match up with the expected number of arguments (in casu, subpatterns)
- private def nonStarSubPatTypes = formalTypes(rawInit :+ repeatedType, nonStarLength)
+ private def nonStarSubPatTypes = aligner.typedNonStarPatterns map (_.tpe)
- def subPatTypes: List[Type] = (
- if (rawSubPatTypes.isEmpty || !isSeq) rawSubPatTypes
- else if (hasStar) nonStarSubPatTypes :+ sequenceType
- else nonStarSubPatTypes
- )
-
- private def rawGet = typeOfMemberNamedGetOrSelf(resultType)
- private def rawInit = rawSubPatTypes dropRight 1
- protected def sequenceType = typeOfLastSelectorOrSelf(rawGet)
- protected def elementType = elementTypeOfLastSelectorOrSelf(rawGet)
- protected def repeatedType = scalaRepeatedType(elementType)
+ def subPatTypes: List[Type] = typedPatterns map (_.tpe)
- // rawSubPatTypes.last is the Seq, thus there are `rawSubPatTypes.length - 1` non-seq elements in the tuple
- protected def firstIndexingBinder = rawSubPatTypes.length - 1
- protected def lastIndexingBinder = nbSubPats - 1 - starLength
- protected def expectedLength = lastIndexingBinder - firstIndexingBinder + 1
+ // there are `productArity` non-seq elements in the tuple.
+ protected def firstIndexingBinder = productArity
+ protected def expectedLength = elementArity
+ protected def lastIndexingBinder = totalArity - starArity - 1
private def productElemsToN(binder: Symbol, n: Int): List[Tree] = 1 to n map tupleSel(binder) toList
private def genTake(binder: Symbol, n: Int): List[Tree] = (0 until n).toList map (codegen index seqTree(binder))
@@ -438,12 +434,12 @@ trait MatchTranslation {
// referenced by `binder`
protected def subPatRefsSeq(binder: Symbol): List[Tree] = {
def lastTrees: List[Tree] = (
- if (!hasStar) Nil
+ if (!aligner.isStar) Nil
else if (expectedLength == 0) seqTree(binder) :: Nil
else genDrop(binder, expectedLength)
)
// this error-condition has already been checked by checkStarPatOK:
- // if(isSeq) assert(firstIndexingBinder + nbIndexingIndices + (if(lastIsStar) 1 else 0) == nbSubPats, "(resultInMonad, ts, subPatTypes, subPats)= "+(resultInMonad, ts, subPatTypes, subPats))
+ // if(isSeq) assert(firstIndexingBinder + nbIndexingIndices + (if(lastIsStar) 1 else 0) == totalArity, "(resultInMonad, ts, subPatTypes, subPats)= "+(resultInMonad, ts, subPatTypes, subPats))
// [1] there are `firstIndexingBinder` non-seq tuple elements preceding the Seq
// [2] then we have to index the binder that represents the sequence for the remaining subpatterns, except for...
@@ -457,8 +453,10 @@ trait MatchTranslation {
// the trees that select the subpatterns on the extractor's result, referenced by `binder`
// require (nbSubPats > 0 && (!lastIsStar || isSeq))
- protected def subPatRefs(binder: Symbol): List[Tree] =
- if (isNonEmptySeq) subPatRefsSeq(binder) else productElemsToN(binder, nbSubPats)
+ protected def subPatRefs(binder: Symbol): List[Tree] = (
+ if (totalArity > 0 && isSeq) subPatRefsSeq(binder)
+ else productElemsToN(binder, totalArity)
+ )
private def compareInts(t1: Tree, t2: Tree) =
gen.mkMethodCall(termMember(ScalaPackage, "math"), TermName("signum"), Nil, (t1 INT_- t2) :: Nil)
@@ -478,7 +476,7 @@ trait MatchTranslation {
// when the last subpattern is a wildcard-star the expectedLength is but a lower bound
// (otherwise equality is required)
def compareOp: (Tree, Tree) => Tree =
- if (hasStar) _ INT_>= _
+ if (aligner.isStar) _ INT_>= _
else _ INT_== _
// `if (binder != null && $checkExpectedLength [== | >=] 0) then else zero`
@@ -487,26 +485,14 @@ trait MatchTranslation {
def checkedLength: Option[Int] =
// no need to check unless it's an unapplySeq and the minimal length is non-trivially satisfied
- if (!isSeq || expectedLength < starLength) None
+ if (!isSeq || expectedLength < starArity) None
else Some(expectedLength)
}
// TODO: to be called when there's a def unapplyProd(x: T): U
// U must have N members _1,..., _N -- the _i are type checked, call their type Ti,
// for now only used for case classes -- pretending there's an unapplyProd that's the identity (and don't call it)
- class ExtractorCallProd(val fun: Tree, val args: List[Tree]) extends ExtractorCall {
- private def constructorTp = fun.tpe
-
- def isTyped = fun.isTyped
-
- // to which type should the previous binder be casted?
- def paramType = constructorTp.finalResultType
- def resultType = fun.tpe.finalResultType
-
- def isSeq = isVarArgTypes(rawSubPatTypes)
-
- protected def rawSubPatTypes = constructorTp.paramTypes
-
+ class ExtractorCallProd(aligner: PatternAligned, val fun: Tree, val args: List[Tree]) extends ExtractorCall(aligner) {
/** Create the TreeMaker that embodies this extractor call
*
* `binder` has been casted to `paramType` if necessary
@@ -535,20 +521,11 @@ trait MatchTranslation {
if (accessors isDefinedAt (i-1)) REF(binder) DOT accessors(i-1)
else codegen.tupleSel(binder)(i) // this won't type check for case classes, as they do not inherit ProductN
}
-
- override def toString() = s"ExtractorCallProd($fun:${fun.tpe} / ${fun.symbol} / args=$args)"
}
- class ExtractorCallRegular(extractorCallIncludingDummy: Tree, val args: List[Tree]) extends ExtractorCall {
+ class ExtractorCallRegular(aligner: PatternAligned, extractorCallIncludingDummy: Tree, val args: List[Tree]) extends ExtractorCall(aligner) {
val Unapplied(fun) = extractorCallIncludingDummy
- def tpe = fun.tpe
- def paramType = firstParamType(tpe)
- def resultType = tpe.finalResultType
- def isTyped = (tpe ne NoType) && fun.isTyped && (resultInMonad ne ErrorType)
- def isSeq = fun.symbol.name == nme.unapplySeq
- def isBool = resultType =:= BooleanTpe
-
/** Create the TreeMaker that embodies this extractor call
*
* `binder` has been casted to `paramType` if necessary
@@ -571,7 +548,7 @@ trait MatchTranslation {
ExtractorTreeMaker(extractorApply, lengthGuard(binder), binder)(
subPatBinders,
subPatRefs(binder),
- isBool,
+ aligner.isBool,
checkedLength,
patBinderOrCasted,
ignoredSubPatBinders
@@ -583,9 +560,9 @@ trait MatchTranslation {
else super.seqTree(binder)
// the trees that select the subpatterns on the extractor's result, referenced by `binder`
- // require (nbSubPats > 0 && (!lastIsStar || isSeq))
+ // require (totalArity > 0 && (!lastIsStar || isSeq))
override protected def subPatRefs(binder: Symbol): List[Tree] =
- if (isSingle) REF(binder) :: Nil // special case for extractors
+ if (aligner.isSingle) REF(binder) :: Nil // special case for extractors
else super.subPatRefs(binder)
protected def spliceApply(binder: Symbol): Tree = {
@@ -606,40 +583,7 @@ trait MatchTranslation {
splice transform extractorCallIncludingDummy
}
- // what's the extractor's result type in the monad? It is the type of its nullary member `get`.
- protected lazy val resultInMonad: Type = if (isBool) UnitTpe else typeOfMemberNamedGet(resultType)
-
- protected lazy val rawSubPatTypes = (
- if (isBool) Nil
- else if (isSingle) resultInMonad :: Nil // don't go looking for selectors if we only expect one pattern
- else typesOfSelectorsOrSelf(resultInMonad)
- )
-
- override def toString() = s"ExtractorCallRegular($fun: $tpe / ${fun.symbol})"
- }
-
- /** A conservative approximation of which patterns do not discern anything.
- * They are discarded during the translation.
- */
- object WildcardPattern {
- def unapply(pat: Tree): Boolean = pat match {
- case Bind(nme.WILDCARD, WildcardPattern()) => true // don't skip when binding an interesting symbol!
- case Star(WildcardPattern()) => true
- case x: Ident => treeInfo.isVarPattern(x)
- case Alternative(ps) => ps forall unapply
- case EmptyTree => true
- case _ => false
- }
- }
-
- object PatternBoundToUnderscore {
- def unapply(pat: Tree): Boolean = pat match {
- case Bind(nme.WILDCARD, _) => true // don't skip when binding an interesting symbol!
- case Ident(nme.WILDCARD) => true
- case Alternative(ps) => ps forall unapply
- case Typed(PatternBoundToUnderscore(), _) => true
- case _ => false
- }
+ override def rawSubPatTypes = aligner.extractor.varargsTypes
}
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
index 7df03044aa..a80f158949 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
@@ -395,8 +395,10 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging {
debug.patmat("TTTM"+((prevBinder, extractorArgTypeTest, testedBinder, expectedTp, nextBinderTp)))
lazy val outerTestNeeded = (
- !((expectedTp.prefix eq NoPrefix) || expectedTp.prefix.typeSymbol.isPackageClass)
- && needsOuterTest(expectedTp, testedBinder.info, matchOwner))
+ (expectedTp.prefix ne NoPrefix)
+ && !expectedTp.prefix.typeSymbol.isPackageClass
+ && needsOuterTest(expectedTp, testedBinder.info, matchOwner)
+ )
// the logic to generate the run-time test that follows from the fact that
// a `prevBinder` is expected to have type `expectedTp`
@@ -406,44 +408,52 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging {
def renderCondition(cs: TypeTestCondStrategy): cs.Result = {
import cs._
- def default =
- // do type test first to ensure we won't select outer on null
- if (outerTestNeeded) and(typeTest(testedBinder, expectedTp), outerTest(testedBinder, expectedTp))
- else typeTest(testedBinder, expectedTp)
-
// propagate expected type
def expTp(t: Tree): t.type = t setType expectedTp
+ def testedWide = testedBinder.info.widen
+ def expectedWide = expectedTp.widen
+ def isAnyRef = testedWide <:< AnyRefTpe
+ def isAsExpected = testedWide <:< expectedTp
+ def isExpectedPrimitiveType = isAsExpected && isPrimitiveValueType(expectedTp)
+ def isExpectedReferenceType = isAsExpected && (expectedTp <:< AnyRefTpe)
+ def mkNullTest = nonNullTest(testedBinder)
+ def mkOuterTest = outerTest(testedBinder, expectedTp)
+ def mkTypeTest = typeTest(testedBinder, expectedWide)
+
+ def mkEqualsTest(lhs: Tree): cs.Result = equalsTest(lhs, testedBinder)
+ def mkEqTest(lhs: Tree): cs.Result = eqTest(lhs, testedBinder)
+ def addOuterTest(res: cs.Result): cs.Result = if (outerTestNeeded) and(res, mkOuterTest) else res
+
+ // If we conform to expected primitive type:
+ // it cannot be null and cannot have an outer pointer. No further checking.
+ // If we conform to expected reference type:
+ // have to test outer and non-null
+ // If we do not conform to expected type:
+ // have to test type and outer (non-null is implied by successful type test)
+ def mkDefault = (
+ if (isExpectedPrimitiveType) tru
+ else addOuterTest(
+ if (isExpectedReferenceType) mkNullTest
+ else mkTypeTest
+ )
+ )
+
// true when called to type-test the argument to an extractor
// don't do any fancy equality checking, just test the type
- if (extractorArgTypeTest) default
+ // TODO: verify that we don't need to special-case Array
+ // I think it's okay:
+ // - the isInstanceOf test includes a test for the element type
+ // - Scala's arrays are invariant (so we don't drop type tests unsoundly)
+ if (extractorArgTypeTest) mkDefault
else expectedTp match {
- // TODO: [SPEC] the spec requires `eq` instead of `==` for singleton types
- // this implies sym.isStable
- case SingleType(_, sym) => and(equalsTest(gen.mkAttributedQualifier(expectedTp), testedBinder), typeTest(testedBinder, expectedTp.widen))
- // must use == to support e.g. List() == Nil
- case ThisType(sym) if sym.isModule => and(equalsTest(CODE.REF(sym), testedBinder), typeTest(testedBinder, expectedTp.widen))
- case ConstantType(Constant(null)) if testedBinder.info.widen <:< AnyRefTpe
- => eqTest(expTp(CODE.NULL), testedBinder)
- case ConstantType(const) => equalsTest(expTp(Literal(const)), testedBinder)
- case ThisType(sym) => eqTest(expTp(This(sym)), testedBinder)
-
- // TODO: verify that we don't need to special-case Array
- // I think it's okay:
- // - the isInstanceOf test includes a test for the element type
- // - Scala's arrays are invariant (so we don't drop type tests unsoundly)
- case _ if testedBinder.info.widen <:< expectedTp =>
- // if the expected type is a primitive value type, it cannot be null and it cannot have an outer pointer
- // since the types conform, no further checking is required
- if (isPrimitiveValueType(expectedTp)) tru
- // have to test outer and non-null only when it's a reference type
- else if (expectedTp <:< AnyRefTpe) {
- // do non-null check first to ensure we won't select outer on null
- if (outerTestNeeded) and(nonNullTest(testedBinder), outerTest(testedBinder, expectedTp))
- else nonNullTest(testedBinder)
- } else default
-
- case _ => default
+ // TODO: [SPEC] the spec requires `eq` instead of `==` for singleton types - this implies sym.isStable
+ case SingleType(_, sym) => and(mkEqualsTest(gen.mkAttributedQualifier(expectedTp)), mkTypeTest)
+ case ThisType(sym) if sym.isModule => and(mkEqualsTest(CODE.REF(sym)), mkTypeTest) // must use == to support e.g. List() == Nil
+ case ConstantType(Constant(null)) if isAnyRef => mkEqTest(expTp(CODE.NULL))
+ case ConstantType(const) => mkEqualsTest(expTp(Literal(const)))
+ case ThisType(sym) => mkEqTest(expTp(This(sym)))
+ case _ => mkDefault
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternExpander.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternExpander.scala
new file mode 100644
index 0000000000..e84ccbf754
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternExpander.scala
@@ -0,0 +1,155 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2013 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala
+package tools
+package nsc
+package transform
+package patmat
+
+/** An extractor returns: F1, F2, ..., Fi, opt[Seq[E] or E*]
+ * A case matches: P1, P2, ..., Pj, opt[Seq[E]]
+ * Put together: P1/F1, P2/F2, ... Pi/Fi, Pi+1/E, Pi+2/E, ... Pj/E, opt[Seq[E]]
+ *
+ * Here Pm/Fi is the last pattern to match the fixed arity section.
+ *
+ * productArity: the value of i, i.e. the number of non-sequence types in the extractor
+ * nonStarArity: the value of j, i.e. the number of non-star patterns in the case definition
+ * elementArity: j - i, i.e. the number of non-star patterns which must match sequence elements
+ * starArity: 1 or 0 based on whether there is a star (sequence-absorbing) pattern
+ * totalArity: nonStarArity + starArity, i.e. the number of patterns in the case definition
+ *
+ * Note that productArity is a function only of the extractor, and
+ * nonStar/star/totalArity are all functions of the patterns. The key
+ * value for aligning and typing the patterns is elementArity, as it
+ * is derived from both sets of information.
+ */
+trait PatternExpander[Pattern, Type] {
+ /** You'll note we're not inside the cake. "Pattern" and "Type" are
+ * arbitrary types here, and NoPattern and NoType arbitrary values.
+ */
+ def NoPattern: Pattern
+ def NoType: Type
+
+ /** It's not optimal that we're carrying both sequence and repeated
+ * type here, but the implementation requires more unraveling before
+ * it can be avoided.
+ *
+ * sequenceType is Seq[T], elementType is T, repeatedType is T*.
+ */
+ sealed case class Repeated(sequenceType: Type, elementType: Type, repeatedType: Type) {
+ def exists = elementType != NoType
+
+ def elementList = if (exists) elementType :: Nil else Nil
+ def sequenceList = if (exists) sequenceType :: Nil else Nil
+ def repeatedList = if (exists) repeatedType :: Nil else Nil
+
+ override def toString = s"${elementType}*"
+ }
+ object NoRepeated extends Repeated(NoType, NoType, NoType) {
+ override def toString = "<none>"
+ }
+
+ final case class Patterns(fixed: List[Pattern], star: Pattern) {
+ def hasStar = star != NoPattern
+ def starArity = if (hasStar) 1 else 0
+ def nonStarArity = fixed.length
+ def totalArity = nonStarArity + starArity
+ def starPatterns = if (hasStar) star :: Nil else Nil
+ def all = fixed ::: starPatterns
+
+ override def toString = all mkString ", "
+ }
+
+ /** An 'extractor' can be a case class or an unapply or unapplySeq method.
+ * Decoding what it is that they extract takes place before we arrive here,
+ * so that this class can concentrate only on the relationship between
+ * patterns and types.
+ *
+ * In a case class, the class is the unextracted type and the fixed and
+ * repeated types are derived from its constructor parameters.
+ *
+ * In an unapply, this is reversed: the parameter to the unapply is the
+ * unextracted type, and the other types are derived based on the return
+ * type of the unapply method.
+ *
+ * In other words, this case class and unapply are encoded the same:
+ *
+ * case class Foo(x: Int, y: Int, zs: Char*)
+ * def unapplySeq(x: Foo): Option[(Int, Int, Seq[Char])]
+ *
+ * Both are Extractor(Foo, Int :: Int :: Nil, Repeated(Seq[Char], Char, Char*))
+ *
+ * @param whole The type in its unextracted form
+ * @param fixed The non-sequence types which are extracted
+ * @param repeated The sequence type which is extracted
+ */
+ final case class Extractor(whole: Type, fixed: List[Type], repeated: Repeated) {
+ require(whole != NoType, s"expandTypes($whole, $fixed, $repeated)")
+
+ def productArity = fixed.length
+ def hasSeq = repeated.exists
+ def elementType = repeated.elementType
+ def sequenceType = repeated.sequenceType
+ def allTypes = fixed ::: repeated.sequenceList
+ def varargsTypes = fixed ::: repeated.repeatedList
+ def isErroneous = allTypes contains NoType
+
+ private def typeStrings = fixed.map("" + _) ::: ( if (hasSeq) List("" + repeated) else Nil )
+
+ def offeringString = if (isErroneous) "<error>" else typeStrings match {
+ case Nil => "Boolean"
+ case tp :: Nil => tp
+ case tps => tps.mkString("(", ", ", ")")
+ }
+ override def toString = "%s => %s".format(whole, offeringString)
+ }
+
+ final case class TypedPat(pat: Pattern, tpe: Type) {
+ override def toString = s"$pat: $tpe"
+ }
+
+ /** If elementArity is...
+ * 0: A perfect match between extractor and the fixed patterns.
+ * If there is a star pattern it will match any sequence.
+ * > 0: There are more patterns than products. There will have to be a
+ * sequence which can populate at least <elementArity> patterns.
+ * < 0: There are more products than patterns: compile time error.
+ */
+ final case class Aligned(patterns: Patterns, extractor: Extractor) {
+ def elementArity = patterns.nonStarArity - productArity
+ def productArity = extractor.productArity
+ def starArity = patterns.starArity
+ def totalArity = patterns.totalArity
+
+ def wholeType = extractor.whole
+ def sequenceType = extractor.sequenceType
+ def productTypes = extractor.fixed
+ def extractedTypes = extractor.allTypes
+ def typedNonStarPatterns = products ::: elements
+ def typedPatterns = typedNonStarPatterns ::: stars
+
+ def isBool = !isSeq && productArity == 0
+ def isSingle = !isSeq && totalArity == 1
+ def isStar = patterns.hasStar
+ def isSeq = extractor.hasSeq
+
+ private def typedAsElement(pat: Pattern) = TypedPat(pat, extractor.elementType)
+ private def typedAsSequence(pat: Pattern) = TypedPat(pat, extractor.sequenceType)
+ private def productPats = patterns.fixed take productArity
+ private def elementPats = patterns.fixed drop productArity
+ private def products = (productPats, productTypes).zipped map TypedPat
+ private def elements = elementPats map typedAsElement
+ private def stars = patterns.starPatterns map typedAsSequence
+
+ override def toString = s"""
+ |Aligned {
+ | patterns $patterns
+ | extractor $extractor
+ | arities $productArity/$elementArity/$starArity // product/element/star
+ | typed ${typedPatterns mkString ", "}
+ |}""".stripMargin.trim
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
index 394ba98f17..f6c960d089 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala
@@ -34,7 +34,8 @@ import scala.reflect.internal.util.Position
* - recover GADT typing by locally inserting implicit witnesses to type equalities derived from the current case, and considering these witnesses during subtyping (?)
* - recover exhaustivity/unreachability of user-defined extractors by partitioning the types they match on using an HList or similar type-level structure
*/
-trait PatternMatching extends Transform with TypingTransformers
+trait PatternMatching extends Transform
+ with TypingTransformers
with Debugging
with Interface
with MatchTranslation
@@ -45,7 +46,8 @@ trait PatternMatching extends Transform with TypingTransformers
with Solving
with MatchAnalysis
with MatchOptimization
- with MatchWarnings {
+ with MatchWarnings
+ with ScalacPatternExpanders {
import global._
val phaseName: String = "patmat"
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala b/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala
new file mode 100644
index 0000000000..7858cb5586
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala
@@ -0,0 +1,154 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2013 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala
+package tools
+package nsc
+package transform
+package patmat
+
+/** This is scalac-specific logic layered on top of the scalac-agnostic
+ * "matching products to patterns" logic defined in PatternExpander.
+ */
+trait ScalacPatternExpanders {
+ val global: Global
+
+ import global._
+ import definitions._
+ import treeInfo._
+
+ type PatternAligned = ScalacPatternExpander#Aligned
+
+ implicit class AlignedOps(val aligned: PatternAligned) {
+ import aligned._
+ def expectedTypes = typedPatterns map (_.tpe)
+ def unexpandedFormals = extractor.varargsTypes
+ }
+ trait ScalacPatternExpander extends PatternExpander[Tree, Type] {
+ def NoPattern = EmptyTree
+ def NoType = global.NoType
+
+ def newPatterns(patterns: List[Tree]): Patterns = patterns match {
+ case init :+ last if isStar(last) => Patterns(init, last)
+ case _ => Patterns(patterns, NoPattern)
+ }
+ def elementTypeOf(tpe: Type) = {
+ val seq = repeatedToSeq(tpe)
+
+ ( typeOfMemberNamedHead(seq)
+ orElse typeOfMemberNamedApply(seq)
+ orElse definitions.elementType(ArrayClass, seq)
+ )
+ }
+ def newExtractor(whole: Type, fixed: List[Type], repeated: Repeated): Extractor =
+ logResult(s"newExtractor($whole, $fixed, $repeated")(Extractor(whole, fixed, repeated))
+
+ // Turn Seq[A] into Repeated(Seq[A], A, A*)
+ def repeatedFromSeq(seqType: Type): Repeated = {
+ val elem = elementTypeOf(seqType)
+ val repeated = scalaRepeatedType(elem)
+
+ Repeated(seqType, elem, repeated)
+ }
+ // Turn A* into Repeated(Seq[A], A, A*)
+ def repeatedFromVarargs(repeated: Type): Repeated =
+ Repeated(repeatedToSeq(repeated), repeatedToSingle(repeated), repeated)
+
+ /** In this case we are basing the pattern expansion on a case class constructor.
+ * The argument is the MethodType carried by the primary constructor.
+ */
+ def applyMethodTypes(method: Type): Extractor = {
+ val whole = method.finalResultType
+
+ method.paramTypes match {
+ case init :+ last if isScalaRepeatedParamType(last) => newExtractor(whole, init, repeatedFromVarargs(last))
+ case tps => newExtractor(whole, tps, NoRepeated)
+ }
+ }
+
+ /** In this case, expansion is based on an unapply or unapplySeq method.
+ * Unfortunately the MethodType does not carry the information of whether
+ * it was unapplySeq, so we have to funnel that information in separately.
+ */
+ def unapplyMethodTypes(method: Type, isSeq: Boolean): Extractor = {
+ val whole = firstParamType(method)
+ val result = method.finalResultType
+ val expanded = (
+ if (result =:= BooleanTpe) Nil
+ else typeOfMemberNamedGet(result) match {
+ case rawGet if !hasSelectors(rawGet) => rawGet :: Nil
+ case rawGet => typesOfSelectors(rawGet)
+ }
+ )
+ expanded match {
+ case init :+ last if isSeq => newExtractor(whole, init, repeatedFromSeq(last))
+ case tps => newExtractor(whole, tps, NoRepeated)
+ }
+ }
+ }
+ object alignPatterns extends ScalacPatternExpander {
+ /** Converts a T => (A, B, C) extractor to a T => ((A, B, CC)) extractor.
+ */
+ def tupleExtractor(extractor: Extractor): Extractor =
+ extractor.copy(fixed = tupleType(extractor.fixed) :: Nil)
+
+ private def validateAligned(tree: Tree, aligned: Aligned): Aligned = {
+ import aligned._
+
+ def owner = tree.symbol.owner
+ def offering = extractor.offeringString
+ def symString = tree.symbol.fullLocationString
+ def offerString = if (extractor.isErroneous) "" else s" offering $offering"
+ def arityExpected = ( if (extractor.hasSeq) "at least " else "" ) + productArity
+
+ def err(msg: String) = currentUnit.error(tree.pos, msg)
+ def warn(msg: String) = currentUnit.warning(tree.pos, msg)
+ def arityError(what: String) = err(s"$what patterns for $owner$offerString: expected $arityExpected, found $totalArity")
+
+ if (isStar && !isSeq)
+ err("Star pattern must correspond with varargs or unapplySeq")
+ else if (elementArity < 0)
+ arityError("not enough")
+ else if (elementArity > 0 && !extractor.hasSeq)
+ arityError("too many")
+
+ aligned
+ }
+
+ def apply(sel: Tree, args: List[Tree]): Aligned = {
+ val fn = sel match {
+ case Unapplied(fn) => fn
+ case _ => sel
+ }
+ val patterns = newPatterns(args)
+ val isSeq = sel.symbol.name == nme.unapplySeq
+ val isUnapply = sel.symbol.name == nme.unapply
+ val extractor = sel.symbol.name match {
+ case nme.unapply => unapplyMethodTypes(fn.tpe, isSeq = false)
+ case nme.unapplySeq => unapplyMethodTypes(fn.tpe, isSeq = true)
+ case _ => applyMethodTypes(fn.tpe)
+ }
+
+ /** Rather than let the error that is SI-6675 pollute the entire matching
+ * process, we will tuple the extractor before creation Aligned so that
+ * it contains known good values.
+ */
+ def productArity = extractor.productArity
+ def acceptMessage = if (extractor.isErroneous) "" else s" to hold ${extractor.offeringString}"
+ val requiresTupling = isUnapply && patterns.totalArity == 1 && productArity > 1
+
+ if (settings.lint && requiresTupling && effectivePatternArity(args) == 1)
+ currentUnit.warning(sel.pos, s"${sel.symbol.owner} expects $productArity patterns$acceptMessage but crushing into $productArity-tuple to fit single pattern (SI-6675)")
+
+ val normalizedExtractor = if (requiresTupling) tupleExtractor(extractor) else extractor
+ validateAligned(fn, Aligned(patterns, normalizedExtractor))
+ }
+
+ def apply(tree: Tree): Aligned = tree match {
+ case Apply(fn, args) => apply(fn, args)
+ case UnApply(fn, args) => apply(fn, args)
+ }
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala
index 54e4fefc15..fa6e5399eb 100644
--- a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala
@@ -13,7 +13,6 @@ package typechecker
trait AnalyzerPlugins { self: Analyzer =>
import global._
-
trait AnalyzerPlugin {
/**
* Selectively activate this analyzer plugin, e.g. according to the compiler phase.
@@ -156,6 +155,117 @@ trait AnalyzerPlugins { self: Analyzer =>
def pluginsTypedReturn(tpe: Type, typer: Typer, tree: Return, pt: Type): Type = tpe
}
+ /**
+ * @define nonCumulativeReturnValueDoc Returns `None` if the plugin doesn't want to customize the default behavior
+ * or something else if the plugin knows better that the implementation provided in scala-compiler.jar.
+ * If multiple plugins return a non-empty result, it's going to be a compilation error.
+ */
+ trait MacroPlugin {
+ /**
+ * Selectively activate this analyzer plugin, e.g. according to the compiler phase.
+ *
+ * Note that the current phase can differ from the global compiler phase (look for `enteringPhase`
+ * invocations in the compiler). For instance, lazy types created by the UnPickler are completed
+ * at the phase in which their symbol is created. Observations show that this can even be the
+ * parser phase. Since symbol completion can trigger subtyping, typing etc, your plugin might
+ * need to be active also in phases other than namer and typer.
+ *
+ * Typically, this method can be implemented as
+ *
+ * global.phase.id < global.currentRun.picklerPhase.id
+ */
+ def isActive(): Boolean = true
+
+ /**
+ * Typechecks the right-hand side of a macro definition (which typically features
+ * a mere reference to a macro implementation).
+ *
+ * Default implementation provided in `self.standardTypedMacroBody` makes sure that the rhs
+ * resolves to a reference to a method in either a static object or a macro bundle,
+ * verifies that the referred method is compatible with the macro def and upon success
+ * attaches a macro impl binding to the macro def's symbol.
+ *
+ * $nonCumulativeReturnValueDoc.
+ */
+ def pluginsTypedMacroBody(typer: Typer, ddef: DefDef): Option[Tree] = None
+
+ /**
+ * Expands an application of a def macro (i.e. of a symbol that has the MACRO flag set),
+ * possibly using the current typer mode and the provided prototype.
+ *
+ * Default implementation provided in `self.standardMacroExpand` figures out whether the `expandee`
+ * needs to be expanded right away or its expansion has to be delayed until all undetermined
+ * parameters are inferred, then loads the macro implementation using `self.pluginsMacroRuntime`,
+ * prepares the invocation arguments for the macro implementation using `self.pluginsMacroArgs`,
+ * and finally calls into the macro implementation. After the call returns, it typechecks
+ * the expansion and performs some bookkeeping.
+ *
+ * This method is typically implemented if your plugin requires significant changes to the macro engine.
+ * If you only need to customize the macro context, consider implementing `pluginsMacroArgs`.
+ * If you only need to customize how macro implementation are invoked, consider going for `pluginsMacroRuntime`.
+ *
+ * $nonCumulativeReturnValueDoc.
+ */
+ def pluginsMacroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Option[Tree] = None
+
+ /**
+ * Computes the arguments that need to be passed to the macro impl corresponding to a particular expandee.
+ *
+ * Default implementation provided in `self.standardMacroArgs` instantiates a `scala.reflect.macros.contexts.Context`,
+ * gathers type and value arguments of the macro application and throws them together into `MacroArgs`.
+ *
+ * $nonCumulativeReturnValueDoc.
+ */
+ def pluginsMacroArgs(typer: Typer, expandee: Tree): Option[MacroArgs] = None
+
+ /**
+ * Summons a function that encapsulates macro implementation invocations for a particular expandee.
+ *
+ * Default implementation provided in `self.standardMacroRuntime` returns a function that
+ * loads the macro implementation binding from the macro definition symbol,
+ * then uses either Java or Scala reflection to acquire the method that corresponds to the impl,
+ * and then reflectively calls into that method.
+ *
+ * $nonCumulativeReturnValueDoc.
+ */
+ def pluginsMacroRuntime(expandee: Tree): Option[MacroRuntime] = None
+
+ /**
+ * Creates a symbol for the given tree in lexical context encapsulated by the given namer.
+ *
+ * Default implementation provided in `namer.standardEnterSym` handles MemberDef's and Imports,
+ * doing nothing for other trees (DocDef's are seen through and rewrapped). Typical implementation
+ * of `enterSym` for a particular tree flavor creates a corresponding symbol, assigns it to the tree,
+ * enters the symbol into scope and then might even perform some code generation.
+ *
+ * $nonCumulativeReturnValueDoc.
+ */
+ def pluginsEnterSym(namer: Namer, tree: Tree): Boolean = false
+
+ /**
+ * Makes sure that for the given class definition, there exists a companion object definition.
+ *
+ * Default implementation provided in `namer.standardEnsureCompanionObject` looks up a companion symbol for the class definition
+ * and then checks whether the resulting symbol exists or not. If it exists, then nothing else is done.
+ * If not, a synthetic object definition is created using the provided factory, which is then entered into namer's scope.
+ *
+ * $nonCumulativeReturnValueDoc.
+ */
+ def pluginsEnsureCompanionObject(namer: Namer, cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Option[Symbol] = None
+
+ /**
+ * Prepares a list of statements for being typechecked by performing domain-specific type-agnostic code synthesis.
+ *
+ * Trees passed into this method are going to be named, but not typed.
+ * In particular, you can rely on the compiler having called `enterSym` on every stat prior to passing calling this method.
+ *
+ * Default implementation does nothing. Current approaches to code syntheses (generation of underlying fields
+ * for getters/setters, creation of companion objects for case classes, etc) are too disparate and ad-hoc
+ * to be treated uniformly, so I'm leaving this for future work.
+ */
+ def pluginsEnterStats(typer: Typer, stats: List[Tree]): List[Tree] = stats
+ }
+
/** A list of registered analyzer plugins */
@@ -167,59 +277,158 @@ trait AnalyzerPlugins { self: Analyzer =>
analyzerPlugins = plugin :: analyzerPlugins
}
+ private abstract class CumulativeOp[T] {
+ def default: T
+ def accumulate: (T, AnalyzerPlugin) => T
+ }
+
+ private def invoke[T](op: CumulativeOp[T]): T = {
+ if (analyzerPlugins.isEmpty) op.default
+ else analyzerPlugins.foldLeft(op.default)((current, plugin) =>
+ if (!plugin.isActive()) current else op.accumulate(current, plugin))
+ }
/** @see AnalyzerPlugin.pluginsPt */
def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Mode): Type =
+ // performance opt
if (analyzerPlugins.isEmpty) pt
- else analyzerPlugins.foldLeft(pt)((pt, plugin) =>
- if (!plugin.isActive()) pt else plugin.pluginsPt(pt, typer, tree, mode))
+ else invoke(new CumulativeOp[Type] {
+ def default = pt
+ def accumulate = (pt, p) => p.pluginsPt(pt, typer, tree, mode)
+ })
/** @see AnalyzerPlugin.pluginsTyped */
- def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Mode, pt: Type): Type = {
- // support deprecated methods in annotation checkers
- val annotCheckersTpe = addAnnotations(tree, tpe)
- if (analyzerPlugins.isEmpty) annotCheckersTpe
- else analyzerPlugins.foldLeft(annotCheckersTpe)((tpe, plugin) =>
- if (!plugin.isActive()) tpe else plugin.pluginsTyped(tpe, typer, tree, mode, pt))
- }
+ def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Mode, pt: Type): Type =
+ // performance opt
+ if (analyzerPlugins.isEmpty) addAnnotations(tree, tpe)
+ else invoke(new CumulativeOp[Type] {
+ // support deprecated methods in annotation checkers
+ def default = addAnnotations(tree, tpe)
+ def accumulate = (tpe, p) => p.pluginsTyped(tpe, typer, tree, mode, pt)
+ })
/** @see AnalyzerPlugin.pluginsTypeSig */
- def pluginsTypeSig(tpe: Type, typer: Typer, defTree: Tree, pt: Type): Type =
- if (analyzerPlugins.isEmpty) tpe
- else analyzerPlugins.foldLeft(tpe)((tpe, plugin) =>
- if (!plugin.isActive()) tpe else plugin.pluginsTypeSig(tpe, typer, defTree, pt))
+ def pluginsTypeSig(tpe: Type, typer: Typer, defTree: Tree, pt: Type): Type = invoke(new CumulativeOp[Type] {
+ def default = tpe
+ def accumulate = (tpe, p) => p.pluginsTypeSig(tpe, typer, defTree, pt)
+ })
/** @see AnalyzerPlugin.pluginsTypeSigAccessor */
- def pluginsTypeSigAccessor(tpe: Type, typer: Typer, tree: ValDef, sym: Symbol): Type =
- if (analyzerPlugins.isEmpty) tpe
- else analyzerPlugins.foldLeft(tpe)((tpe, plugin) =>
- if (!plugin.isActive()) tpe else plugin.pluginsTypeSigAccessor(tpe, typer, tree, sym))
+ def pluginsTypeSigAccessor(tpe: Type, typer: Typer, tree: ValDef, sym: Symbol): Type = invoke(new CumulativeOp[Type] {
+ def default = tpe
+ def accumulate = (tpe, p) => p.pluginsTypeSigAccessor(tpe, typer, tree, sym)
+ })
/** @see AnalyzerPlugin.canAdaptAnnotations */
- def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Boolean = {
+ def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Boolean = invoke(new CumulativeOp[Boolean] {
// support deprecated methods in annotation checkers
- val annotCheckersExists = global.canAdaptAnnotations(tree, mode, pt)
- annotCheckersExists || {
- if (analyzerPlugins.isEmpty) false
- else analyzerPlugins.exists(plugin =>
- plugin.isActive() && plugin.canAdaptAnnotations(tree, typer, mode, pt))
- }
- }
+ def default = global.canAdaptAnnotations(tree, mode, pt)
+ def accumulate = (curr, p) => curr || p.canAdaptAnnotations(tree, typer, mode, pt)
+ })
/** @see AnalyzerPlugin.adaptAnnotations */
- def adaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Tree = {
+ def adaptAnnotations(tree: Tree, typer: Typer, mode: Mode, pt: Type): Tree = invoke(new CumulativeOp[Tree] {
// support deprecated methods in annotation checkers
- val annotCheckersTree = global.adaptAnnotations(tree, mode, pt)
- if (analyzerPlugins.isEmpty) annotCheckersTree
- else analyzerPlugins.foldLeft(annotCheckersTree)((tree, plugin) =>
- if (!plugin.isActive()) tree else plugin.adaptAnnotations(tree, typer, mode, pt))
- }
+ def default = global.adaptAnnotations(tree, mode, pt)
+ def accumulate = (tree, p) => p.adaptAnnotations(tree, typer, mode, pt)
+ })
/** @see AnalyzerPlugin.pluginsTypedReturn */
- def pluginsTypedReturn(tpe: Type, typer: Typer, tree: Return, pt: Type): Type = {
- val annotCheckersType = adaptTypeOfReturn(tree.expr, pt, tpe)
- if (analyzerPlugins.isEmpty) annotCheckersType
- else analyzerPlugins.foldLeft(annotCheckersType)((tpe, plugin) =>
- if (!plugin.isActive()) tpe else plugin.pluginsTypedReturn(tpe, typer, tree, pt))
+ def pluginsTypedReturn(tpe: Type, typer: Typer, tree: Return, pt: Type): Type = invoke(new CumulativeOp[Type] {
+ def default = adaptTypeOfReturn(tree.expr, pt, tpe)
+ def accumulate = (tpe, p) => p.pluginsTypedReturn(tpe, typer, tree, pt)
+ })
+
+ /** A list of registered macro plugins */
+ private var macroPlugins: List[MacroPlugin] = Nil
+
+ /** Registers a new macro plugin */
+ def addMacroPlugin(plugin: MacroPlugin) {
+ if (!macroPlugins.contains(plugin))
+ macroPlugins = plugin :: macroPlugins
+ }
+
+ private abstract class NonCumulativeOp[T] {
+ def position: Position
+ def description: String
+ def default: T
+ def custom(plugin: MacroPlugin): Option[T]
+ }
+
+ private def invoke[T](op: NonCumulativeOp[T]): T = {
+ if (macroPlugins.isEmpty) op.default
+ else {
+ val results = macroPlugins.filter(_.isActive()).map(plugin => (plugin, op.custom(plugin)))
+ results.flatMap { case (p, Some(result)) => Some((p, result)); case _ => None } match {
+ case (p1, _) :: (p2, _) :: _ => typer.context.error(op.position, s"both $p1 and $p2 want to ${op.description}"); op.default
+ case (_, custom) :: Nil => custom
+ case Nil => op.default
+ }
+ }
+ }
+
+ /** @see MacroPlugin.pluginsTypedMacroBody */
+ def pluginsTypedMacroBody(typer: Typer, ddef: DefDef): Tree = invoke(new NonCumulativeOp[Tree] {
+ def position = ddef.pos
+ def description = "typecheck this macro definition"
+ def default = standardTypedMacroBody(typer, ddef)
+ def custom(plugin: MacroPlugin) = plugin.pluginsTypedMacroBody(typer, ddef)
+ })
+
+ /** @see MacroPlugin.pluginsMacroExpand */
+ def pluginsMacroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = invoke(new NonCumulativeOp[Tree] {
+ def position = expandee.pos
+ def description = "expand this macro application"
+ def default = standardMacroExpand(typer, expandee, mode, pt)
+ def custom(plugin: MacroPlugin) = plugin.pluginsMacroExpand(typer, expandee, mode, pt)
+ })
+
+ /** @see MacroPlugin.pluginsMacroArgs */
+ def pluginsMacroArgs(typer: Typer, expandee: Tree): MacroArgs = invoke(new NonCumulativeOp[MacroArgs] {
+ def position = expandee.pos
+ def description = "compute macro arguments for this macro application"
+ def default = standardMacroArgs(typer, expandee)
+ def custom(plugin: MacroPlugin) = plugin.pluginsMacroArgs(typer, expandee)
+ })
+
+ /** @see MacroPlugin.pluginsMacroRuntime */
+ def pluginsMacroRuntime(expandee: Tree): MacroRuntime = invoke(new NonCumulativeOp[MacroRuntime] {
+ def position = expandee.pos
+ def description = "compute macro runtime for this macro application"
+ def default = standardMacroRuntime(expandee)
+ def custom(plugin: MacroPlugin) = plugin.pluginsMacroRuntime(expandee)
+ })
+
+ /** @see MacroPlugin.pluginsEnterSym */
+ def pluginsEnterSym(namer: Namer, tree: Tree): Context =
+ if (macroPlugins.isEmpty) namer.standardEnterSym(tree)
+ else invoke(new NonCumulativeOp[Context] {
+ def position = tree.pos
+ def description = "enter a symbol for this tree"
+ def default = namer.standardEnterSym(tree)
+ def custom(plugin: MacroPlugin) = {
+ val hasExistingSym = tree.symbol != NoSymbol
+ val result = plugin.pluginsEnterSym(namer, tree)
+ if (result && hasExistingSym) Some(namer.context)
+ else if (result && tree.isInstanceOf[Import]) Some(namer.context.make(tree))
+ else if (result) Some(namer.context)
+ else None
+ }
+ })
+
+ /** @see MacroPlugin.pluginsEnsureCompanionObject */
+ def pluginsEnsureCompanionObject(namer: Namer, cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Symbol = invoke(new NonCumulativeOp[Symbol] {
+ def position = cdef.pos
+ def description = "enter a companion symbol for this tree"
+ def default = namer.standardEnsureCompanionObject(cdef, creator)
+ def custom(plugin: MacroPlugin) = plugin.pluginsEnsureCompanionObject(namer, cdef, creator)
+ })
+
+ /** @see MacroPlugin.pluginsEnterStats */
+ def pluginsEnterStats(typer: Typer, stats: List[Tree]): List[Tree] = {
+ // performance opt
+ if (macroPlugins.isEmpty) stats
+ else macroPlugins.foldLeft(stats)((current, plugin) =>
+ if (!plugin.isActive()) current else plugin.pluginsEnterStats(typer, stats))
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index 719d04a7f9..4d0eda2377 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -625,8 +625,7 @@ trait ContextErrors {
setError(tree)
}
- def CaseClassConstructorError(tree: Tree) = {
- val baseMessage = tree.symbol + " is not a case class constructor, nor does it have an unapply/unapplySeq method"
+ def CaseClassConstructorError(tree: Tree, baseMessage: String) = {
val addendum = directUnapplyMember(tree.symbol.info) match {
case sym if hasMultipleNonImplicitParamLists(sym) => s"\nNote: ${sym.defString} exists in ${tree.symbol}, but it cannot be used as an extractor due to its second non-implicit parameter list"
case _ => ""
@@ -726,8 +725,9 @@ trait ContextErrors {
NormalTypeError(expandee, "too many argument lists for " + fun)
}
- def MacroInvalidExpansionError(expandee: Tree, role: String, allowedExpansions: String) = {
- issueNormalTypeError(expandee, s"macro in $role role can only expand into $allowedExpansions")
+ def MacroIncompatibleEngineError(macroEngine: String) = {
+ val message = s"macro cannot be expanded, because it was compiled by an incompatible macro engine $macroEngine"
+ issueNormalTypeError(lastTreeToTyper, message)
}
case object MacroExpansionException extends Exception with scala.util.control.ControlThrowable
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index fdec1edcc0..19fba639e3 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -1145,7 +1145,7 @@ trait Implicits {
gen.mkAttributedThis(thisSym)
case _ =>
// if `pre` is not a PDT, e.g. if someone wrote
- // implicitly[scala.reflect.macros.BlackboxContext#TypeTag[Int]]
+ // implicitly[scala.reflect.macros.blackbox.Context#TypeTag[Int]]
// then we need to fail, because we don't know the prefix to use during type reification
// upd. we also need to fail silently, because this is a very common situation
// e.g. quite often we're searching for BaseUniverse#TypeTag, e.g. for a type tag in any universe
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 0d46a96b81..cf82d6baac 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -29,7 +29,7 @@ import Fingerprint._
* Then fooBar needs to point to a static method of the following form:
*
* def fooBar[T: c.WeakTypeTag] // type tag annotation is optional
- * (c: scala.reflect.macros.BlackboxContext)
+ * (c: scala.reflect.macros.blackbox.Context)
* (xs: c.Expr[List[T]])
* : c.Expr[T] = {
* ...
@@ -67,7 +67,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
*
* This solution is very simple, but unfortunately it's also lacking. If we use it, then
* signatures of macro defs become transitively dependent on scala-reflect.jar
- * (because they refer to macro impls, and macro impls refer to scala.reflect.macros.BlackboxContext/WhiteboxContext defined in scala-reflect.jar).
+ * (because they refer to macro impls, and macro impls refer to *box.Context defined in scala-reflect.jar).
* More details can be found in comments to https://issues.scala-lang.org/browse/SI-5940.
*
* Therefore we have to avoid putting macro impls into binding pickles and come up with our own serialization format.
@@ -81,9 +81,9 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
* and various accounting information necessary when composing an argument list for the reflective invocation.
*/
case class MacroImplBinding(
- // Is this macro impl a bundle (a trait extending BlackboxMacro or WhiteboxMacro) or a vanilla def?
+ // Is this macro impl a bundle (a trait extending *box.Macro) or a vanilla def?
val isBundle: Boolean,
- // Is this macro impl blackbox (i.e. having BlackboxContext in its signature)?
+ // Is this macro impl blackbox (i.e. having blackbox.Context in its signature)?
val isBlackbox: Boolean,
// Java class name of the class that contains the macro implementation
// is used to load the corresponding object with Java reflection
@@ -97,8 +97,8 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
// * c.Expr[T] => LiftedTyped
// * c.Tree => LiftedUntyped
// * c.WeakTypeTag[T] => Tagged(index of the type parameter corresponding to that type tag)
- // * everything else (e.g. scala.reflect.macros.BlackboxContext/WhiteboxContext) => Other
- // f.ex. for: def impl[T: WeakTypeTag, U, V: WeakTypeTag](c: BlackboxContext)(x: c.Expr[T], y: c.Tree): (U, V) = ???
+ // * everything else (e.g. *box.Context) => Other
+ // f.ex. for: def impl[T: WeakTypeTag, U, V: WeakTypeTag](c: blackbox.Context)(x: c.Expr[T], y: c.Tree): (U, V) = ???
// `signature` will be equal to List(List(Other), List(LiftedTyped, LiftedUntyped), List(Tagged(0), Tagged(2)))
signature: List[List[Fingerprint]],
// type arguments part of a macro impl ref (the right-hand side of a macro definition)
@@ -116,23 +116,22 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
* with synthetic content that carries the payload described in `MacroImplBinding`.
*
* For example, for a pair of macro definition and macro implementation:
- * def impl(c: scala.reflect.macros.BlackboxContext): c.Expr[Unit] = ???
+ * def impl(c: scala.reflect.macros.blackbox.Context): c.Expr[Unit] = ???
* def foo: Unit = macro impl
*
* We will have the following annotation added on the macro definition `foo`:
*
* @scala.reflect.macros.internal.macroImpl(
* `macro`(
+ * "macroEngine" = <current macro engine>,
* "isBundle" = false,
* "isBlackbox" = true,
* "signature" = List(Other),
* "methodName" = "impl",
- * "versionFormat" = <current version format>,
* "className" = "Macros$"))
*/
+ def macroEngine = "v7.0 (implemented in Scala 2.11.0-M8)"
object MacroImplBinding {
- val versionFormat = 6.0
-
def pickleAtom(obj: Any): Tree =
obj match {
case list: List[_] => Apply(Ident(ListModule), list map pickleAtom)
@@ -183,12 +182,12 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
}
val payload = List[(String, Any)](
- "versionFormat" -> versionFormat,
- "isBundle" -> isBundle,
- "isBlackbox" -> isBlackbox,
- "className" -> className,
- "methodName" -> macroImpl.name.toString,
- "signature" -> signature
+ "macroEngine" -> macroEngine,
+ "isBundle" -> isBundle,
+ "isBlackbox" -> isBlackbox,
+ "className" -> className,
+ "methodName" -> macroImpl.name.toString,
+ "signature" -> signature
)
// the shape of the nucleus is chosen arbitrarily. it doesn't carry any payload.
@@ -237,8 +236,8 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
raw.asInstanceOf[T]
}
- val pickleVersionFormat = unpickle("versionFormat", classOf[Double])
- if (versionFormat != pickleVersionFormat) fail(s"expected version format $versionFormat, actual $pickleVersionFormat")
+ val macroEngine = unpickle("macroEngine", classOf[String])
+ if (self.macroEngine != macroEngine) typer.TyperErrorGen.MacroIncompatibleEngineError(macroEngine)
val isBundle = unpickle("isBundle", classOf[Boolean])
val isBlackbox = unpickle("isBlackbox", classOf[Boolean])
@@ -315,7 +314,12 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
* @return Macro impl reference for the given macro definition if everything is okay.
* EmptyTree if an error occurs.
*/
- def typedMacroBody(typer: Typer, macroDdef: DefDef): Tree = {
+ def typedMacroBody(typer: Typer, macroDdef: DefDef): Tree = pluginsTypedMacroBody(typer, macroDdef)
+
+ /** Default implementation of `typedMacroBody`.
+ * Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsTypedMacroBody for more details)
+ */
+ def standardTypedMacroBody(typer: Typer, macroDdef: DefDef): Tree = {
val macroDef = macroDdef.symbol
assert(macroDef.isMacro, macroDdef)
@@ -350,7 +354,6 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
val universe: self.global.type = self.global
val callsiteTyper: universe.analyzer.Typer = typer.asInstanceOf[global.analyzer.Typer]
val expandee = universe.analyzer.macroExpanderAttachment(expandeeTree).original orElse duplicateAndKeepPositions(expandeeTree)
- val macroRole = universe.analyzer.macroExpanderAttachment(expandeeTree).role
} with UnaffiliatedMacroContext {
val prefix = Expr[Nothing](prefixTree)(TypeTag.Nothing)
override def toString = "MacroContext(%s@%s +%d)".format(expandee.symbol.name, expandee.pos, enclosingMacros.length - 1 /* exclude myself */)
@@ -360,8 +363,12 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
/** Calculate the arguments to pass to a macro implementation when expanding the provided tree.
*/
case class MacroArgs(c: MacroContext, others: List[Any])
+ def macroArgs(typer: Typer, expandee: Tree): MacroArgs = pluginsMacroArgs(typer, expandee)
- private def macroArgs(typer: Typer, expandee: Tree): MacroArgs = {
+ /** Default implementation of `macroArgs`.
+ * Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsMacroArgs for more details)
+ */
+ def standardMacroArgs(typer: Typer, expandee: Tree): MacroArgs = {
val macroDef = expandee.symbol
val paramss = macroDef.paramss
val treeInfo.Applied(core, targs, argss) = expandee
@@ -471,20 +478,14 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
}
/** Keeps track of macros in-flight.
- * See more informations in comments to `openMacros` in `scala.reflect.macros.WhiteboxContext`.
+ * See more informations in comments to `openMacros` in `scala.reflect.macros.whitebox.Context`.
*/
- private var _openMacros = List[MacroContext]()
+ var _openMacros = List[MacroContext]()
def openMacros = _openMacros
- private def pushMacroContext(c: MacroContext) = _openMacros ::= c
- private def popMacroContext() = _openMacros = _openMacros.tail
+ def pushMacroContext(c: MacroContext) = _openMacros ::= c
+ def popMacroContext() = _openMacros = _openMacros.tail
def enclosingMacroPosition = openMacros map (_.macroApplication.pos) find (_ ne NoPosition) getOrElse NoPosition
- /** Describes the role that the macro expandee is performing.
- */
- type MacroRole = scala.tools.nsc.typechecker.MacroRole
- final def APPLY_ROLE = MacroRole.Apply
- final def UNAPPLY_ROLE = MacroRole.Unapply
-
/** Performs macro expansion:
*
* ========= Expandable trees =========
@@ -527,30 +528,24 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
* the expandee with an error marker set if the expansion has been cancelled due malformed arguments or implementation
* the expandee with an error marker set if there has been an error
*/
- private abstract class MacroExpander[Result: ClassTag](val role: MacroRole, val typer: Typer, val expandee: Tree) {
- def allowExpandee(expandee: Tree): Boolean = true
- def allowExpanded(expanded: Tree): Boolean = true
- def allowedExpansions: String = "anything"
- def allowResult(result: Result): Boolean = true
-
- def onSuccess(expanded: Tree): Result
- def onFallback(expanded: Tree): Result
- def onSuppressed(expandee: Tree): Result = expandee match { case expandee: Result => expandee }
- def onDelayed(expanded: Tree): Result = expanded match { case expanded: Result => expanded }
- def onSkipped(expanded: Tree): Result = expanded match { case expanded: Result => expanded }
- def onFailure(expanded: Tree): Result = { typer.infer.setError(expandee); expandee match { case expandee: Result => expandee } }
-
- def apply(desugared: Tree): Result = {
+ abstract class MacroExpander(val typer: Typer, val expandee: Tree) {
+ def onSuccess(expanded: Tree): Tree
+ def onFallback(expanded: Tree): Tree
+ def onSuppressed(expandee: Tree): Tree = expandee
+ def onDelayed(expanded: Tree): Tree = expanded
+ def onSkipped(expanded: Tree): Tree = expanded
+ def onFailure(expanded: Tree): Tree = { typer.infer.setError(expandee); expandee }
+
+ def apply(desugared: Tree): Tree = {
if (isMacroExpansionSuppressed(desugared)) onSuppressed(expandee)
else expand(desugared)
}
- protected def expand(desugared: Tree): Result = {
+ protected def expand(desugared: Tree): Tree = {
def showDetailed(tree: Tree) = showRaw(tree, printIds = true, printTypes = true)
def summary() = s"expander = $this, expandee = ${showDetailed(expandee)}, desugared = ${if (expandee == desugared) () else showDetailed(desugared)}"
if (macroDebugVerbose) println(s"macroExpand: ${summary()}")
- assert(allowExpandee(expandee), summary())
- linkExpandeeAndDesugared(expandee, desugared, role)
+ linkExpandeeAndDesugared(expandee, desugared)
val start = if (Statistics.canEnable) Statistics.startTimer(macroExpandNanos) else null
if (Statistics.canEnable) Statistics.incCounter(macroExpandCount)
@@ -562,21 +557,17 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
onFailure(typer.infer.setError(expandee))
} else try {
val expanded = {
- val runtime = macroRuntime(expandee.symbol)
+ val runtime = macroRuntime(expandee)
if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime)
else macroExpandWithoutRuntime(typer, expandee)
}
expanded match {
case Success(expanded) =>
- if (allowExpanded(expanded)) {
- // also see http://groups.google.com/group/scala-internals/browse_thread/thread/492560d941b315cc
- val expanded1 = try onSuccess(duplicateAndKeepPositions(expanded)) finally popMacroContext()
- if (!hasMacroExpansionAttachment(expanded1)) linkExpandeeAndExpanded(expandee, expanded1)
- if (allowResult(expanded1)) expanded1 else onFailure(expanded)
- } else {
- typer.TyperErrorGen.MacroInvalidExpansionError(expandee, role.name, allowedExpansions)
- onFailure(expanded)
- }
+ // also see http://groups.google.com/group/scala-internals/browse_thread/thread/492560d941b315cc
+ val expanded1 = try onSuccess(duplicateAndKeepPositions(expanded)) finally popMacroContext()
+ if (!hasMacroExpansionAttachment(expanded1)) linkExpandeeAndExpanded(expandee, expanded1)
+ if (settings.Ymacroexpand.value == settings.MacroExpand.Discard) expandee.setType(expanded1.tpe)
+ else expanded1
case Fallback(fallback) => onFallback(fallback)
case Delayed(delayed) => onDelayed(delayed)
case Skipped(skipped) => onSkipped(skipped)
@@ -592,151 +583,136 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
}
}
- /** Expands a tree that carries a term, which happens to be a term macro.
- * @see MacroExpander
- */
- private abstract class TermMacroExpander(role: MacroRole, typer: Typer, expandee: Tree, mode: Mode, pt: Type)
- extends MacroExpander[Tree](role, typer, expandee) {
- override def allowedExpansions: String = "term trees"
- override def allowExpandee(expandee: Tree) = expandee.isTerm
- override def onSuccess(expanded: Tree) = typer.typed(expanded, mode, pt)
- override def onFallback(fallback: Tree) = typer.typed(fallback, mode, pt)
- }
-
/** Expands a term macro used in apply role as `M(2)(3)` in `val x = M(2)(3)`.
* @param outerPt Expected type that comes from enclosing context (something that's traditionally called `pt`).
* @param innerPt Expected type that comes from the signature of a macro def, possibly wildcarded to help type inference.
- * @see MacroExpander
*/
- def macroExpandApply(typer: Typer, expandee: Tree, mode: Mode, outerPt: Type): Tree = {
- object expander extends TermMacroExpander(APPLY_ROLE, typer, expandee, mode, outerPt) {
- lazy val innerPt = {
- val tp = if (isNullaryInvocation(expandee)) expandee.tpe.finalResultType else expandee.tpe
- if (isBlackbox(expandee)) tp
- else {
- // approximation is necessary for whitebox macros to guide type inference
- // read more in the comments for onDelayed below
- val undetparams = tp collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol }
- deriveTypeWithWildcards(undetparams)(tp)
- }
+ class DefMacroExpander(typer: Typer, expandee: Tree, mode: Mode, outerPt: Type)
+ extends MacroExpander(typer, expandee) {
+ lazy val innerPt = {
+ val tp = if (isNullaryInvocation(expandee)) expandee.tpe.finalResultType else expandee.tpe
+ if (isBlackbox(expandee)) tp
+ else {
+ // approximation is necessary for whitebox macros to guide type inference
+ // read more in the comments for onDelayed below
+ val undetparams = tp collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol }
+ deriveTypeWithWildcards(undetparams)(tp)
}
- override def onSuccess(expanded0: Tree) = {
- // prematurely annotate the tree with a macro expansion attachment
- // so that adapt called indirectly by typer.typed knows that it needs to apply the existential fixup
- linkExpandeeAndExpanded(expandee, expanded0)
-
- def typecheck(label: String, tree: Tree, pt: Type): Tree = {
- if (tree.isErrorTyped) tree
- else {
- if (macroDebugVerbose) println(s"$label (against pt = $pt): $tree")
- // `macroExpandApply` is called from `adapt`, where implicit conversions are disabled
- // therefore we need to re-enable the conversions back temporarily
- val result = typer.context.withImplicitsEnabled(typer.typed(tree, mode, pt))
- if (result.isErrorTyped && macroDebugVerbose) println(s"$label has failed: ${typer.context.reportBuffer.errors}")
- result
- }
- }
+ }
+ override def onSuccess(expanded0: Tree) = {
+ // prematurely annotate the tree with a macro expansion attachment
+ // so that adapt called indirectly by typer.typed knows that it needs to apply the existential fixup
+ linkExpandeeAndExpanded(expandee, expanded0)
- if (isBlackbox(expandee)) {
- val expanded1 = atPos(enclosingMacroPosition.makeTransparent)(Typed(expanded0, TypeTree(innerPt)))
- typecheck("blackbox typecheck", expanded1, outerPt)
- } else {
- val expanded1 = expanded0
- val expanded2 = typecheck("whitebox typecheck #1", expanded1, outerPt)
- typecheck("whitebox typecheck #2", expanded2, innerPt)
+ def typecheck(label: String, tree: Tree, pt: Type): Tree = {
+ if (tree.isErrorTyped) tree
+ else {
+ if (macroDebugVerbose) println(s"$label (against pt = $pt): $tree")
+ // `macroExpandApply` is called from `adapt`, where implicit conversions are disabled
+ // therefore we need to re-enable the conversions back temporarily
+ val result = typer.context.withImplicitsEnabled(typer.typed(tree, mode, pt))
+ if (result.isErrorTyped && macroDebugVerbose) println(s"$label has failed: ${typer.context.reportBuffer.errors}")
+ result
}
}
- override def onDelayed(delayed: Tree) = {
- // =========== THE SITUATION ===========
- //
- // If we've been delayed (i.e. bailed out of the expansion because of undetermined type params present in the expandee),
- // then there are two possible situations we're in:
- // 1) We're in POLYmode, when the typer tests the waters wrt type inference
- // (e.g. as in typedArgToPoly in doTypedApply).
- // 2) We're out of POLYmode, which means that the typer is out of tricks to infer our type
- // (e.g. if we're an argument to a function call, then this means that no previous argument lists
- // can determine our type variables for us).
- //
- // Situation #1 is okay for us, since there's no pressure. In POLYmode we're just verifying that
- // there's nothing outrageously wrong with our undetermined type params (from what I understand!).
- //
- // Situation #2 requires measures to be taken. If we're in it, then noone's going to help us infer
- // the undetermined type params. Therefore we need to do something ourselves or otherwise this
- // expandee will forever remaing not expanded (see SI-5692). A traditional way out of this conundrum
- // is to call `instantiate` and let the inferencer try to find the way out. It works for simple cases,
- // but sometimes, if the inferencer lacks information, it will be forced to approximate.
- //
- // =========== THE PROBLEM ===========
- //
- // Consider the following example (thanks, Miles!):
- //
- // Iso represents an isomorphism between two datatypes:
- // 1) An arbitrary one (e.g. a random case class)
- // 2) A uniform representation for all datatypes (e.g. an HList)
- //
- // trait Iso[T, U] {
- // def to(t : T) : U
- // def from(u : U) : T
- // }
- // implicit def materializeIso[T, U]: Iso[T, U] = macro ???
- //
- // case class Foo(i: Int, s: String, b: Boolean)
- // def foo[C, L](c: C)(implicit iso: Iso[C, L]): L = iso.to(c)
- // foo(Foo(23, "foo", true))
- //
- // In the snippet above, even though we know that there's a fundep going from T to U
- // (in a sense that a datatype's uniform representation is unambiguously determined by the datatype,
- // e.g. for Foo it will be Int :: String :: Boolean :: HNil), there's no way to convey this information
- // to the typechecker. Therefore the typechecker will infer Nothing for L, which is hardly what we want.
- //
- // =========== THE SOLUTION (ENABLED ONLY FOR WHITEBOX MACROS) ===========
- //
- // To give materializers a chance to say their word before vanilla inference kicks in,
- // we infer as much as possible (e.g. in the example above even though L is hopeless, C still can be inferred to Foo)
- // and then trigger macro expansion with the undetermined type parameters still there.
- // Thanks to that the materializer can take a look at what's going on and react accordingly.
- val shouldInstantiate = typer.context.undetparams.nonEmpty && !mode.inPolyMode
- if (shouldInstantiate) {
- if (isBlackbox(expandee)) typer.instantiatePossiblyExpectingUnit(delayed, mode, outerPt)
- else {
- forced += delayed
- typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), outerPt, keepNothings = false)
- macroExpandApply(typer, delayed, mode, outerPt)
- }
- } else delayed
+
+ if (isBlackbox(expandee)) {
+ val expanded1 = atPos(enclosingMacroPosition.makeTransparent)(Typed(expanded0, TypeTree(innerPt)))
+ typecheck("blackbox typecheck", expanded1, outerPt)
+ } else {
+ val expanded1 = expanded0
+ val expanded2 = typecheck("whitebox typecheck #1", expanded1, outerPt)
+ typecheck("whitebox typecheck #2", expanded2, innerPt)
}
}
- expander(expandee)
+ override def onDelayed(delayed: Tree) = {
+ // =========== THE SITUATION ===========
+ //
+ // If we've been delayed (i.e. bailed out of the expansion because of undetermined type params present in the expandee),
+ // then there are two possible situations we're in:
+ // 1) We're in POLYmode, when the typer tests the waters wrt type inference
+ // (e.g. as in typedArgToPoly in doTypedApply).
+ // 2) We're out of POLYmode, which means that the typer is out of tricks to infer our type
+ // (e.g. if we're an argument to a function call, then this means that no previous argument lists
+ // can determine our type variables for us).
+ //
+ // Situation #1 is okay for us, since there's no pressure. In POLYmode we're just verifying that
+ // there's nothing outrageously wrong with our undetermined type params (from what I understand!).
+ //
+ // Situation #2 requires measures to be taken. If we're in it, then noone's going to help us infer
+ // the undetermined type params. Therefore we need to do something ourselves or otherwise this
+ // expandee will forever remaing not expanded (see SI-5692). A traditional way out of this conundrum
+ // is to call `instantiate` and let the inferencer try to find the way out. It works for simple cases,
+ // but sometimes, if the inferencer lacks information, it will be forced to approximate.
+ //
+ // =========== THE PROBLEM ===========
+ //
+ // Consider the following example (thanks, Miles!):
+ //
+ // Iso represents an isomorphism between two datatypes:
+ // 1) An arbitrary one (e.g. a random case class)
+ // 2) A uniform representation for all datatypes (e.g. an HList)
+ //
+ // trait Iso[T, U] {
+ // def to(t : T) : U
+ // def from(u : U) : T
+ // }
+ // implicit def materializeIso[T, U]: Iso[T, U] = macro ???
+ //
+ // case class Foo(i: Int, s: String, b: Boolean)
+ // def foo[C, L](c: C)(implicit iso: Iso[C, L]): L = iso.to(c)
+ // foo(Foo(23, "foo", true))
+ //
+ // In the snippet above, even though we know that there's a fundep going from T to U
+ // (in a sense that a datatype's uniform representation is unambiguously determined by the datatype,
+ // e.g. for Foo it will be Int :: String :: Boolean :: HNil), there's no way to convey this information
+ // to the typechecker. Therefore the typechecker will infer Nothing for L, which is hardly what we want.
+ //
+ // =========== THE SOLUTION (ENABLED ONLY FOR WHITEBOX MACROS) ===========
+ //
+ // To give materializers a chance to say their word before vanilla inference kicks in,
+ // we infer as much as possible (e.g. in the example above even though L is hopeless, C still can be inferred to Foo)
+ // and then trigger macro expansion with the undetermined type parameters still there.
+ // Thanks to that the materializer can take a look at what's going on and react accordingly.
+ val shouldInstantiate = typer.context.undetparams.nonEmpty && !mode.inPolyMode
+ if (shouldInstantiate) {
+ if (isBlackbox(expandee)) typer.instantiatePossiblyExpectingUnit(delayed, mode, outerPt)
+ else {
+ forced += delayed
+ typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), outerPt, keepNothings = false)
+ macroExpand(typer, delayed, mode, outerPt)
+ }
+ } else delayed
+ }
+ override def onFallback(fallback: Tree) = typer.typed(fallback, mode, outerPt)
}
- /** Expands a term macro used in unapply role as `u.Quasiquote(StringContext("", "")).q.unapply(x)` in `case q"$x" => ...`.
- * @see MacroExpander
+ /** Expands a term macro used in apply role as `M(2)(3)` in `val x = M(2)(3)`.
+ * @see DefMacroExpander
*/
- def macroExpandUnapply(typer: Typer, original: Tree, fun: Tree, unapply: Symbol, args: List[Tree], mode: Mode, pt: Type) = {
- val expandee = treeCopy.Apply(original, gen.mkAttributedSelect(fun, unapply), args)
- object expander extends TermMacroExpander(UNAPPLY_ROLE, typer, expandee, mode, pt) {
- override def allowedExpansions: String = "unapply trees"
- override def allowExpandee(expandee: Tree) = expandee.isInstanceOf[Apply]
- private def unsupported(what: String) = abort("unapply macros currently don't support " + what)
- override def onFallback(fallback: Tree) = unsupported("fallback")
- override def onDelayed(delayed: Tree) = unsupported("advanced interaction with type inference")
- }
- expander(original)
+ def macroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = pluginsMacroExpand(typer, expandee, mode, pt)
+
+ /** Default implementation of `macroExpand`.
+ * Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsMacroExpand for more details)
+ */
+ def standardMacroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = {
+ val expander = new DefMacroExpander(typer, expandee, mode, pt)
+ expander(expandee)
}
- private sealed abstract class MacroStatus(val result: Tree)
- private case class Success(expanded: Tree) extends MacroStatus(expanded)
- private case class Fallback(fallback: Tree) extends MacroStatus(fallback) { currentRun.seenMacroExpansionsFallingBack = true }
- private case class Delayed(delayed: Tree) extends MacroStatus(delayed)
- private case class Skipped(skipped: Tree) extends MacroStatus(skipped)
- private case class Failure(failure: Tree) extends MacroStatus(failure)
- private def Delay(expanded: Tree) = Delayed(expanded)
- private def Skip(expanded: Tree) = Skipped(expanded)
+ sealed abstract class MacroStatus(val result: Tree)
+ case class Success(expanded: Tree) extends MacroStatus(expanded)
+ case class Fallback(fallback: Tree) extends MacroStatus(fallback) { currentRun.seenMacroExpansionsFallingBack = true }
+ case class Delayed(delayed: Tree) extends MacroStatus(delayed)
+ case class Skipped(skipped: Tree) extends MacroStatus(skipped)
+ case class Failure(failure: Tree) extends MacroStatus(failure)
+ def Delay(expanded: Tree) = Delayed(expanded)
+ def Skip(expanded: Tree) = Skipped(expanded)
/** Expands a macro when a runtime (i.e. the macro implementation) can be successfully loaded
* Meant for internal use within the macro infrastructure, don't use it elsewhere.
*/
- private def macroExpandWithRuntime(typer: Typer, expandee: Tree, runtime: MacroRuntime): MacroStatus = {
+ def macroExpandWithRuntime(typer: Typer, expandee: Tree, runtime: MacroRuntime): MacroStatus = {
val wasDelayed = isDelayed(expandee)
val undetparams = calculateUndetparams(expandee)
val nowDelayed = !typer.context.macrosEnabled || undetparams.nonEmpty
@@ -767,7 +743,31 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
macroLogLite("" + expanded + "\n" + showRaw(expanded))
val freeSyms = expanded.freeTerms ++ expanded.freeTypes
freeSyms foreach (sym => MacroFreeSymbolError(expandee, sym))
- Success(atPos(enclosingMacroPosition.focus)(expanded))
+ // Macros might have spliced arguments with range positions into non-compliant
+ // locations, notably, under a tree without a range position. Or, they might
+ // splice a tree that `resetAttrs` has assigned NoPosition.
+ //
+ // Here, we just convert all positions in the tree to offset positions, and
+ // convert NoPositions to something sensible.
+ //
+ // Given that the IDE now sees the expandee (by using -Ymacro-expand:discard),
+ // this loss of position fidelity shouldn't cause any real problems.
+ //
+ // Alternatively, we could pursue a way to exclude macro expansions from position
+ // invariant checking, or find a way not to touch expansions that happen to validate.
+ //
+ // This would be useful for cases like:
+ //
+ // macro1 { macro2 { "foo" } }
+ //
+ // to allow `macro1` to see the range position of the "foo".
+ val expandedPos = enclosingMacroPosition.focus
+ def fixPosition(pos: Position) =
+ if (pos == NoPosition) expandedPos else pos.focus
+ expanded.foreach(t => t.pos = fixPosition(t.pos))
+
+ val result = atPos(enclosingMacroPosition.focus)(expanded)
+ Success(result)
}
expanded match {
case expanded: Expr[_] if expandee.symbol.isTermMacro => validateResultingTree(expanded.tree)
@@ -793,7 +793,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
/** Expands a macro when a runtime (i.e. the macro implementation) cannot be loaded
* Meant for internal use within the macro infrastructure, don't use it elsewhere.
*/
- private def macroExpandWithoutRuntime(typer: Typer, expandee: Tree): MacroStatus = {
+ def macroExpandWithoutRuntime(typer: Typer, expandee: Tree): MacroStatus = {
import typer.TyperErrorGen._
val fallbackSym = expandee.symbol.nextOverriddenSymbol orElse MacroImplementationNotFoundError(expandee)
macroLogLite(s"falling back to: $fallbackSym")
@@ -871,7 +871,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
context.implicitsEnabled = typer.context.implicitsEnabled
context.enrichmentEnabled = typer.context.enrichmentEnabled
context.macrosEnabled = typer.context.macrosEnabled
- macroExpandApply(newTyper(context), tree, EXPRmode, WildcardType)
+ macroExpand(newTyper(context), tree, EXPRmode, WildcardType)
case _ =>
tree
})
@@ -902,12 +902,3 @@ object Fingerprint {
val LiftedTyped = new Fingerprint(-2)
val LiftedUntyped = new Fingerprint(-3)
}
-
-class MacroRole private[MacroRole](val name: String) extends AnyVal {
- override def toString = name
-}
-
-object MacroRole {
- val Apply = new MacroRole("apply")
- val Unapply = new MacroRole("unapply")
-}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 86bb99e7fa..27e8698676 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -22,7 +22,7 @@ trait Namers extends MethodSynthesis {
import global._
import definitions._
- private var _lockedCount = 0
+ var _lockedCount = 0
def lockedCount = this._lockedCount
/** Replaces any Idents for which cond is true with fresh TypeTrees().
@@ -107,8 +107,8 @@ trait Namers extends MethodSynthesis {
}
protected def owner = context.owner
- private def contextFile = context.unit.source.file
- private def typeErrorHandler[T](tree: Tree, alt: T): PartialFunction[Throwable, T] = {
+ def contextFile = context.unit.source.file
+ def typeErrorHandler[T](tree: Tree, alt: T): PartialFunction[Throwable, T] = {
case ex: TypeError =>
// H@ need to ensure that we handle only cyclic references
TypeSigError(tree, ex)
@@ -122,10 +122,31 @@ trait Namers extends MethodSynthesis {
|| (vd.mods.isPrivateLocal && !vd.mods.isCaseAccessor)
|| (vd.name startsWith nme.OUTER)
|| (context.unit.isJava)
+ || isEnumConstant(vd)
)
+
def noFinishGetterSetter(vd: ValDef) = (
(vd.mods.isPrivateLocal && !vd.mods.isLazy) // all lazy vals need accessors, even private[this]
- || vd.symbol.isModuleVar)
+ || vd.symbol.isModuleVar
+ || isEnumConstant(vd))
+
+ /** Determines whether this field holds an enum constant.
+ * To qualify, the following conditions must be met:
+ * - The field's class has the ENUM flag set
+ * - The field's class extends java.lang.Enum
+ * - The field has the ENUM flag set
+ * - The field is static
+ * - The field is stable
+ */
+ def isEnumConstant(vd: ValDef) = {
+ val ownerHasEnumFlag =
+ // Necessary to check because scalac puts Java's static members into the companion object
+ // while Scala's enum constants live directly in the class.
+ // We don't check for clazz.superClass == JavaEnumClass, because this causes a illegal
+ // cyclic reference error. See the commit message for details.
+ if (context.unit.isJava) owner.companionClass.hasEnumFlag else owner.hasEnumFlag
+ vd.mods.hasAllFlags(ENUM | STABLE | STATIC) && ownerHasEnumFlag
+ }
def setPrivateWithin[T <: Symbol](tree: Tree, sym: T, mods: Modifiers): T =
if (sym.isPrivateLocal || !mods.hasAccessBoundary) sym
@@ -243,7 +264,12 @@ trait Namers extends MethodSynthesis {
validate(sym2.companionClass)
}
- def enterSym(tree: Tree): Context = {
+ def enterSym(tree: Tree): Context = pluginsEnterSym(this, tree)
+
+ /** Default implementation of `enterSym`.
+ * Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsEnterSym for more details)
+ */
+ def standardEnterSym(tree: Tree): Context = {
def dispatch() = {
var returnContext = this.context
tree match {
@@ -309,7 +335,7 @@ trait Namers extends MethodSynthesis {
* be transferred to the symbol as they are, supply a mask containing
* the flags to keep.
*/
- private def createMemberSymbol(tree: MemberDef, name: Name, mask: Long): Symbol = {
+ def createMemberSymbol(tree: MemberDef, name: Name, mask: Long): Symbol = {
val pos = tree.pos
val isParameter = tree.mods.isParameter
val flags = tree.mods.flags & mask
@@ -327,14 +353,14 @@ trait Namers extends MethodSynthesis {
else owner.newValue(name.toTermName, pos, flags)
}
}
- private def createFieldSymbol(tree: ValDef): TermSymbol =
+ def createFieldSymbol(tree: ValDef): TermSymbol =
owner.newValue(tree.localName, tree.pos, tree.mods.flags & FieldFlags | PrivateLocal)
- private def createImportSymbol(tree: Tree) =
+ def createImportSymbol(tree: Tree) =
NoSymbol.newImport(tree.pos) setInfo completerOf(tree)
/** All PackageClassInfoTypes come from here. */
- private def createPackageSymbol(pos: Position, pid: RefTree): Symbol = {
+ def createPackageSymbol(pos: Position, pid: RefTree): Symbol = {
val pkgOwner = pid match {
case Ident(_) => if (owner.isEmptyPackageClass) rootMirror.RootClass else owner
case Select(qual: RefTree, _) => createPackageSymbol(pos, qual).moduleClass
@@ -393,7 +419,7 @@ trait Namers extends MethodSynthesis {
/** Given a ClassDef or ModuleDef, verifies there isn't a companion which
* has been defined in a separate file.
*/
- private def validateCompanionDefs(tree: ImplDef) {
+ def validateCompanionDefs(tree: ImplDef) {
val sym = tree.symbol orElse { return }
val ctx = if (context.owner.isPackageObjectClass) context.outer else context
val module = if (sym.isModule) sym else ctx.scope lookupModule tree.name
@@ -466,7 +492,13 @@ trait Namers extends MethodSynthesis {
* class definition tree.
* @return the companion object symbol.
*/
- def ensureCompanionObject(cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Symbol = {
+ def ensureCompanionObject(cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Symbol =
+ pluginsEnsureCompanionObject(this, cdef, creator)
+
+ /** Default implementation of `ensureCompanionObject`.
+ * Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsEnsureCompanionObject for more details)
+ */
+ def standardEnsureCompanionObject(cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Symbol = {
val m = companionSymbolOf(cdef.symbol, context)
// @luc: not sure why "currentRun.compiles(m)" is needed, things breaks
// otherwise. documentation welcome.
@@ -609,11 +641,7 @@ trait Namers extends MethodSynthesis {
else
enterGetterSetter(tree)
- // When java enums are read from bytecode, they are known to have
- // constant types by the jvm flag and assigned accordingly. When
- // they are read from source, the java parser marks them with the
- // STABLE flag, and now we receive that signal.
- if (tree.symbol hasAllFlags STABLE | JAVA)
+ if (isEnumConstant(tree))
tree.symbol setInfo ConstantType(Constant(tree.symbol))
}
@@ -828,9 +856,10 @@ trait Namers extends MethodSynthesis {
* assigns the type to the tpt's node. Returns the type.
*/
private def assignTypeToTree(tree: ValOrDefDef, defnTyper: Typer, pt: Type): Type = {
- val rhsTpe =
- if (tree.symbol.isTermMacro) defnTyper.computeMacroDefType(tree, pt)
- else defnTyper.computeType(tree.rhs, pt)
+ val rhsTpe = tree match {
+ case ddef: DefDef if tree.symbol.isTermMacro => defnTyper.computeMacroDefType(ddef, pt)
+ case _ => defnTyper.computeType(tree.rhs, pt)
+ }
val defnTpe = widenIfNecessary(tree.symbol, rhsTpe, pt)
tree.tpt defineType defnTpe setPos tree.pos.focus
@@ -1620,7 +1649,7 @@ trait Namers extends MethodSynthesis {
val tree: Tree
}
- def mkTypeCompleter(t: Tree)(c: Symbol => Unit) = new LockingTypeCompleter {
+ def mkTypeCompleter(t: Tree)(c: Symbol => Unit) = new LockingTypeCompleter with FlagAgnosticCompleter {
val tree = t
def completeImpl(sym: Symbol) = c(sym)
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
index 069d6d5fb2..41c656f8ce 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
@@ -78,26 +78,34 @@ trait PatternTypers {
// Do some ad-hoc overloading resolution and update the tree's symbol and type
// do not update the symbol if the tree's symbol's type does not define an unapply member
// (e.g. since it's some method that returns an object with an unapply member)
- val fun = inPlaceAdHocOverloadingResolution(fun0)(hasUnapplyMember)
- def caseClass = fun.tpe.typeSymbol.linkedClassOfClass
+ val fun = inPlaceAdHocOverloadingResolution(fun0)(hasUnapplyMember)
+ val caseClass = fun.tpe.typeSymbol.linkedClassOfClass
+ val member = unapplyMember(fun.tpe)
+ def resultType = (fun.tpe memberType member).finalResultType
+ def isEmptyType = resultOfMatchingMethod(resultType, nme.isEmpty)()
+ def isOkay = (
+ resultType.isErroneous
+ || (resultType <:< BooleanTpe)
+ || (isEmptyType <:< BooleanTpe)
+ || member.isMacro
+ || member.isOverloaded // the whole overloading situation is over the rails
+ )
// Dueling test cases: pos/overloaded-unapply.scala, run/case-class-23.scala, pos/t5022.scala
// A case class with 23+ params has no unapply method.
- // A case class constructor be overloaded with unapply methods in the companion.
- if (caseClass.isCase && !unapplyMember(fun.tpe).isOverloaded)
+ // A case class constructor may be overloaded with unapply methods in the companion.
+ if (caseClass.isCase && !member.isOverloaded)
logResult(s"convertToCaseConstructor($fun, $caseClass, pt=$pt)")(convertToCaseConstructor(fun, caseClass, pt))
- else if (hasUnapplyMember(fun))
+ else if (!reallyExists(member))
+ CaseClassConstructorError(fun, s"${fun.symbol} is not a case class, nor does it have an unapply/unapplySeq member")
+ else if (isOkay)
fun
+ else if (isEmptyType == NoType)
+ CaseClassConstructorError(fun, s"an unapply result must have a member `def isEmpty: Boolean")
else
- CaseClassConstructorError(fun)
+ CaseClassConstructorError(fun, s"an unapply result must have a member `def isEmpty: Boolean (found: def isEmpty: $isEmptyType)")
}
- def expectedPatternTypes(fun: Tree, args: List[Tree]): List[Type] =
- newExtractorShape(fun, args).expectedPatternTypes
-
- def typedPatternArgs(fun: Tree, args: List[Tree], mode: Mode): List[Tree] =
- typedArgsForFormals(args, newExtractorShape(fun, args).formals, mode)
-
def typedArgsForFormals(args: List[Tree], formals: List[Type], mode: Mode): List[Tree] = {
def typedArgWithFormal(arg: Tree, pt: Type) = {
val newMode = if (isByNameParamType(pt)) mode.onlySticky else mode.onlySticky | BYVALmode
@@ -158,109 +166,6 @@ trait PatternTypers {
case _ => wrapClassTagUnapply(treeTyped, extractor, tpe)
}
}
-
- def newExtractorShape(fun: Tree, args: List[Tree]): ExtractorShape = ExtractorShape(fun, args)
-
- case class CaseClassInfo(clazz: Symbol, classType: Type) {
- def constructor = clazz.primaryConstructor
- def constructorType = classType.prefix memberType clazz memberType constructor
- def accessors = clazz.caseFieldAccessors
- }
- object NoCaseClassInfo extends CaseClassInfo(NoSymbol, NoType) {
- override def toString = "NoCaseClassInfo"
- }
-
- case class UnapplyMethodInfo(unapply: Symbol, tpe: Type) {
- def name = unapply.name
- def isUnapplySeq = name == nme.unapplySeq
- def unapplyType = tpe memberType method
- def resultType = tpe.finalResultType
- def method = unapplyMember(tpe)
- def paramType = firstParamType(unapplyType)
- def rawGet = if (isBool) UnitTpe else typeOfMemberNamedGetOrSelf(resultType)
- def rawTypes = if (isBool) Nil else typesOfSelectorsOrSelf(rawGet)
- def isBool = resultType =:= BooleanTpe // aka "Tuple0" or "Option[Unit]"
- }
-
- object NoUnapplyMethodInfo extends UnapplyMethodInfo(NoSymbol, NoType) {
- override def toString = "NoUnapplyMethodInfo"
- }
-
- case class ExtractorShape(fun: Tree, args: List[Tree]) {
- def pos = fun.pos
- private def symbol = fun.symbol
- private def tpe = fun.tpe
-
- val ccInfo = tpe.typeSymbol.linkedClassOfClass match {
- case clazz if clazz.isCase => CaseClassInfo(clazz, tpe)
- case _ => NoCaseClassInfo
- }
- val exInfo = UnapplyMethodInfo(symbol, tpe)
- import exInfo.{ rawGet, rawTypes, isUnapplySeq }
-
- override def toString = s"ExtractorShape($fun, $args)"
-
- def unapplyMethod = exInfo.method
- def unapplyType = exInfo.unapplyType
- def unapplyParamType = exInfo.paramType
- def enclClass = symbol.enclClass
-
- // TODO - merge these. The difference between these two methods is that expectedPatternTypes
- // expands the list of types so it is the same length as the number of patterns, whereas formals
- // leaves the varargs type unexpanded.
- def formals = (
- if (isUnapplySeq) productTypes :+ varargsType
- else if (elementArity == 0) productTypes
- else if (isSingle) squishIntoOne()
- else wrongArity(patternFixedArity)
- )
- def expectedPatternTypes = elementArity match {
- case 0 => productTypes
- case _ if elementArity > 0 && isUnapplySeq => productTypes ::: elementTypes
- case _ if productArity > 1 && patternFixedArity == 1 => squishIntoOne()
- case _ => wrongArity(patternFixedArity)
- }
-
- def elementType = elementTypeOfLastSelectorOrSelf(rawGet)
-
- private def hasBogusExtractor = directUnapplyMember(tpe).exists && !unapplyMethod.exists
- private def expectedArity = "" + productArity + ( if (isUnapplySeq) "+" else "")
- private def wrongArityMsg(n: Int) = (
- if (hasBogusExtractor) s"$enclClass does not define a valid extractor method"
- else s"wrong number of patterns for $enclClass offering $rawTypes_s: expected $expectedArity, found $n"
- )
- private def rawTypes_s = rawTypes match {
- case Nil => "()"
- case tp :: Nil => "" + tp
- case tps => tps.mkString("(", ", ", ")")
- }
-
- private def err(msg: String) = { unit.error(pos, msg) ; throw new TypeError(msg) }
- private def wrongArity(n: Int) = err(wrongArityMsg(n))
-
- def squishIntoOne() = {
- if (settings.lint)
- unit.warning(pos, s"$enclClass expects $expectedArity patterns to hold $rawGet but crushing into $productArity-tuple to fit single pattern (SI-6675)")
-
- rawGet :: Nil
- }
- // elementArity is the number of non-sequence patterns minus the
- // the number of non-sequence product elements returned by the extractor.
- // If it is zero, there is a perfect match between those parts, and
- // if there is a wildcard star it will match any sequence.
- // If it is positive, there are more patterns than products,
- // so a sequence will have to fill in the elements. If it is negative,
- // there are more products than patterns, which is a compile time error.
- def elementArity = patternFixedArity - productArity
- def patternFixedArity = treeInfo effectivePatternArity args
- def productArity = productTypes.size
- def isSingle = !isUnapplySeq && (patternFixedArity == 1)
-
- def productTypes = if (isUnapplySeq) rawTypes dropRight 1 else rawTypes
- def elementTypes = List.fill(elementArity)(elementType)
- def varargsType = scalaRepeatedType(elementType)
- }
-
private class VariantToSkolemMap extends TypeMap(trackVariance = true) {
private val skolemBuffer = mutable.ListBuffer[TypeSymbol]()
@@ -365,10 +270,12 @@ trait PatternTypers {
case OverloadedType(_, _) => OverloadedUnapplyError(fun) ; ErrorType
case _ => UnapplyWithSingleArgError(fun) ; ErrorType
}
- val shape = newExtractorShape(fun, args)
- import shape.{ unapplyParamType, unapplyType, unapplyMethod }
+ val unapplyMethod = unapplyMember(fun.tpe)
+ val unapplyType = fun.tpe memberType unapplyMethod
+ val unapplyParamType = firstParamType(unapplyType)
+ def isSeq = unapplyMethod.name == nme.unapplySeq
- def extractor = extractorForUncheckedType(shape.pos, unapplyParamType)
+ def extractor = extractorForUncheckedType(fun.pos, unapplyParamType)
def canRemedy = unapplyParamType match {
case RefinedType(_, decls) if !decls.isEmpty => false
case RefinedType(parents, _) if parents exists isUncheckable => false
@@ -400,7 +307,8 @@ trait PatternTypers {
// the union of the expected type and the inferred type of the argument to unapply
val glbType = glb(ensureFullyDefined(pt) :: unapplyArg.tpe_* :: Nil)
val wrapInTypeTest = canRemedy && !(fun1.symbol.owner isNonBottomSubClass ClassTagClass)
- val args1 = typedPatternArgs(fun1, args, mode)
+ val formals = patmat.alignPatterns(fun1, args).unexpandedFormals
+ val args1 = typedArgsForFormals(args, formals, mode)
val result = UnApply(fun1, args1) setPos tree.pos setType glbType
if (wrapInTypeTest)
diff --git a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala
index 14f47a00fd..995f98cc2c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala
@@ -16,7 +16,7 @@ trait StdAttachments {
/** Scratchpad for the macro expander, which is used to store all intermediate data except the details about the runtime.
*/
- case class MacroExpanderAttachment(original: Tree, desugared: Tree, role: MacroRole)
+ case class MacroExpanderAttachment(original: Tree, desugared: Tree)
/** Loads underlying MacroExpanderAttachment from a macro expandee or returns a default value for that attachment.
*/
@@ -24,15 +24,15 @@ trait StdAttachments {
tree.attachments.get[MacroExpanderAttachment] getOrElse {
tree match {
case Apply(fn, _) if tree.isInstanceOf[ApplyToImplicitArgs] => macroExpanderAttachment(fn)
- case _ => MacroExpanderAttachment(tree, EmptyTree, APPLY_ROLE)
+ case _ => MacroExpanderAttachment(tree, EmptyTree)
}
}
/** After macro expansion is completed, links the expandee and the expansion result
* by annotating them both with a `MacroExpansionAttachment`.
*/
- def linkExpandeeAndDesugared(expandee: Tree, desugared: Tree, role: MacroRole): Unit = {
- val metadata = MacroExpanderAttachment(expandee, desugared, role)
+ def linkExpandeeAndDesugared(expandee: Tree, desugared: Tree): Unit = {
+ val metadata = MacroExpanderAttachment(expandee, desugared)
expandee updateAttachment metadata
desugared updateAttachment metadata
}
@@ -95,7 +95,7 @@ trait StdAttachments {
/** Determines whether a tree should not be expanded, because someone has put SuppressMacroExpansionAttachment on it or one of its children.
*/
def isMacroExpansionSuppressed(tree: Tree): Boolean =
- ( settings.Ymacronoexpand.value // SI-6812
+ ( settings.Ymacroexpand.value == settings.MacroExpand.None // SI-6812
|| tree.attachments.get[SuppressMacroExpansionAttachment.type].isDefined
|| (tree match {
// we have to account for the fact that during typechecking an expandee might become wrapped,
diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
index f0252251f7..9516f94135 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
@@ -380,7 +380,7 @@ trait SyntheticMethods extends ast.TreeDSL {
val original = ddef.symbol
val newAcc = deriveMethod(ddef.symbol, name => context.unit.freshTermName(name + "$")) { newAcc =>
newAcc.makePublic
- newAcc resetFlag (ACCESSOR | PARAMACCESSOR)
+ newAcc resetFlag (ACCESSOR | PARAMACCESSOR | OVERRIDE)
ddef.rhs.duplicate
}
// TODO: shouldn't the next line be: `original resetFlag CASEACCESSOR`?
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index dbe85f4f5a..6b5afce993 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1102,7 +1102,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (tree.isType)
adaptType()
else if (mode.typingExprNotFun && treeInfo.isMacroApplication(tree) && !isMacroExpansionSuppressed(tree))
- macroExpandApply(this, tree, mode, pt)
+ macroExpand(this, tree, mode, pt)
else if (mode.typingConstructorPattern)
typedConstructorPattern(tree, pt)
else if (shouldInsertApply(tree))
@@ -1853,12 +1853,15 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
protected def enterSym(txt: Context, tree: Tree): Context =
- if (txt eq context) namer.enterSym(tree)
- else newNamer(txt).enterSym(tree)
+ if (txt eq context) namer enterSym tree
+ else newNamer(txt) enterSym tree
/** <!-- 2 --> Check that inner classes do not inherit from Annotation
*/
- def typedTemplate(templ: Template, parents1: List[Tree]): Template = {
+ def typedTemplate(templ0: Template, parents1: List[Tree]): Template = {
+ val templ = templ0
+ // please FIXME: uncommenting this line breaks everything
+ // val templ = treeCopy.Template(templ0, templ0.body, templ0.self, templ0.parents)
val clazz = context.owner
clazz.annotations.map(_.completeInfo())
if (templ.symbol == NoSymbol)
@@ -1886,7 +1889,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
)
// the following is necessary for templates generated later
assert(clazz.info.decls != EmptyScope, clazz)
- enterSyms(context.outer.make(templ, clazz, clazz.info.decls), templ.body)
+ val body1 = pluginsEnterStats(this, templ.body)
+ enterSyms(context.outer.make(templ, clazz, clazz.info.decls), body1)
if (!templ.isErrorTyped) // if `parentTypes` has invalidated the template, don't validate it anymore
validateParentClasses(parents1, selfType)
if (clazz.isCase)
@@ -1900,26 +1904,26 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members
checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType])
- val body = {
- val body =
- if (isPastTyper || reporter.hasErrors) templ.body
- else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _))
- val primaryCtor = treeInfo.firstConstructor(body)
+ val body2 = {
+ val body2 =
+ if (isPastTyper || reporter.hasErrors) body1
+ else body1 flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _))
+ val primaryCtor = treeInfo.firstConstructor(body2)
val primaryCtor1 = primaryCtor match {
case DefDef(_, _, _, _, _, Block(earlyVals :+ global.pendingSuperCall, unit)) =>
val argss = superArgs(parents1.head) getOrElse Nil
- val pos = wrappingPos(parents1.head.pos, argss.flatten)
+ val pos = wrappingPos(parents1.head.pos, primaryCtor :: argss.flatten).makeTransparent
val superCall = atPos(pos)(PrimarySuperCall(argss))
deriveDefDef(primaryCtor)(block => Block(earlyVals :+ superCall, unit) setPos pos) setPos pos
case _ => primaryCtor
}
- body mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat }
+ body2 mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat }
}
- val body1 = typedStats(body, templ.symbol)
+ val body3 = typedStats(body2, templ.symbol)
if (clazz.info.firstParent.typeSymbol == AnyValClass)
- validateDerivedValueClass(clazz, body1)
+ validateDerivedValueClass(clazz, body3)
if (clazz.isTrait) {
for (decl <- clazz.info.decls if decl.isTerm && decl.isEarlyInitialized) {
@@ -1927,7 +1931,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
}
- treeCopy.Template(templ, parents1, self1, body1) setType clazz.tpe_*
+ treeCopy.Template(templ, parents1, self1, body3) setType clazz.tpe_*
}
/** Remove definition annotations from modifiers (they have been saved
@@ -2309,10 +2313,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
}
- def typedBlock(block: Block, mode: Mode, pt: Type): Block = {
+ def typedBlock(block0: Block, mode: Mode, pt: Type): Block = {
val syntheticPrivates = new ListBuffer[Symbol]
try {
- namer.enterSyms(block.stats)
+ namer.enterSyms(block0.stats)
+ val block = treeCopy.Block(block0, pluginsEnterStats(this, block0.stats), block0.expr)
for (stat <- block.stats) enterLabelDef(stat)
if (phaseId(currentPeriod) <= currentRun.typerPhase.id) {
@@ -3797,7 +3802,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
protected def typedExistentialTypeTree(tree: ExistentialTypeTree, mode: Mode): Tree = {
for (wc <- tree.whereClauses)
- if (wc.symbol == NoSymbol) { namer.enterSym(wc); wc.symbol setFlag EXISTENTIAL }
+ if (wc.symbol == NoSymbol) { namer enterSym wc; wc.symbol setFlag EXISTENTIAL }
else context.scope enter wc.symbol
val whereClauses1 = typedStats(tree.whereClauses, context.owner)
for (vd @ ValDef(_, _, _, _) <- whereClauses1)
@@ -4948,7 +4953,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val sym: Symbol = tree.symbol
if ((sym ne null) && (sym ne NoSymbol)) sym.initialize
- def typedPackageDef(pdef: PackageDef) = {
+ def typedPackageDef(pdef0: PackageDef) = {
+ val pdef = treeCopy.PackageDef(pdef0, pdef0.pid, pluginsEnterStats(this, pdef0.stats))
val pid1 = typedQualifier(pdef.pid).asInstanceOf[RefTree]
assert(sym.moduleClass ne NoSymbol, sym)
val stats1 = newTyper(context.make(tree, sym.moduleClass, sym.info.decls))
@@ -5494,25 +5500,23 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
tpe
}
- def computeMacroDefType(tree: Tree, pt: Type): Type = {
+ def computeMacroDefType(ddef: DefDef, pt: Type): Type = {
assert(context.owner.isMacro, context.owner)
- assert(tree.symbol.isMacro, tree.symbol)
- assert(tree.isInstanceOf[DefDef], tree.getClass)
- val ddef = tree.asInstanceOf[DefDef]
+ assert(ddef.symbol.isMacro, ddef.symbol)
- val tree1 =
+ val rhs1 =
if (transformed contains ddef.rhs) {
// macro defs are typechecked in `methodSig` (by calling this method) in order to establish their link to macro implementation asap
// if a macro def doesn't have explicitly specified return type, this method will be called again by `assignTypeToTree`
// here we guard against this case
transformed(ddef.rhs)
} else {
- val tree1 = typedMacroBody(this, ddef)
- transformed(ddef.rhs) = tree1
- tree1
+ val rhs1 = typedMacroBody(this, ddef)
+ transformed(ddef.rhs) = rhs1
+ rhs1
}
- val isMacroBodyOkay = !tree.symbol.isErroneous && !(tree1 exists (_.isErroneous)) && tree1 != EmptyTree
+ val isMacroBodyOkay = !ddef.symbol.isErroneous && !(rhs1 exists (_.isErroneous)) && rhs1 != EmptyTree
val shouldInheritMacroImplReturnType = ddef.tpt.isEmpty
if (isMacroBodyOkay && shouldInheritMacroImplReturnType) {
val commonMessage = "macro defs must have explicitly specified return types"
@@ -5524,7 +5528,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val explanation = s"inference of $inferredType from macro impl's c.Expr[$inferredType] is deprecated and is going to stop working in 2.12"
unit.deprecationWarning(ddef.pos, s"$commonMessage ($explanation)")
}
- computeMacroDefTypeFromMacroImplRef(ddef, tree1) match {
+ computeMacroDefTypeFromMacroImplRef(ddef, rhs1) match {
case ErrorType => ErrorType
case NothingTpe => NothingTpe
case NoType => reportFailure(); AnyTpe