summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/Macros.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Macros.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala443
1 files changed, 106 insertions, 337 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 93f73f1bbe..40f284c94c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -11,6 +11,8 @@ import scala.reflect.internal.util.Statistics
import scala.reflect.macros.util._
import scala.util.control.ControlThrowable
import scala.reflect.macros.runtime.AbortMacroException
+import scala.reflect.runtime.{universe => ru}
+import scala.reflect.macros.compiler.DefaultMacroCompiler
/**
* Code to deal with macros, namely with:
@@ -37,7 +39,7 @@ import scala.reflect.macros.runtime.AbortMacroException
* (Expr(elems))
* (TypeTag(Int))
*/
-trait Macros extends scala.tools.reflect.FastTrack with Traces {
+trait Macros extends scala.tools.reflect.FastTrack with Traces with Helpers {
self: Analyzer =>
import global._
@@ -76,6 +78,8 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
* and various accounting information necessary when composing an argument list for the reflective invocation.
*/
private case class MacroImplBinding(
+ // Is this macro impl a bundle (a trait extending Macro) or a vanilla def?
+ val isBundle: Boolean,
// Java class name of the class that contains the macro implementation
// is used to load the corresponding object with Java reflection
className: String,
@@ -110,19 +114,21 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
*
* @scala.reflect.macros.internal.macroImpl(
* `macro`(
+ * "isBundle" = false,
* "signature" = List(-1),
* "methodName" = "impl",
* "versionFormat" = <current version format>,
* "className" = "Macros$"))
*/
private object MacroImplBinding {
- val versionFormat = 2
+ val versionFormat = 3
def pickleAtom(obj: Any): Tree =
obj match {
case list: List[_] => Apply(Ident(ListModule), list map pickleAtom)
case s: String => Literal(Constant(s))
case i: Int => Literal(Constant(i))
+ case b: Boolean => Literal(Constant(b))
}
def unpickleAtom(tree: Tree): Any =
@@ -130,11 +136,11 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
case Apply(list @ Ident(_), args) if list.symbol == ListModule => args map unpickleAtom
case Literal(Constant(s: String)) => s
case Literal(Constant(i: Int)) => i
+ case Literal(Constant(b: Boolean)) => b
}
def pickle(macroImplRef: Tree): Tree = {
- val MacroImplReference(owner, macroImpl, targs) = macroImplRef
- val paramss = macroImpl.paramss
+ val MacroImplReference(isBundle, owner, macroImpl, targs) = macroImplRef
// todo. refactor when fixing SI-5498
def className: String = {
@@ -157,12 +163,13 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
case _ => IMPLPARAM_OTHER
}
- val transformed = transformTypeTagEvidenceParams(paramss, (param, tparam) => tparam)
+ val transformed = transformTypeTagEvidenceParams(macroImplRef, (param, tparam) => tparam)
mmap(transformed)(p => if (p.isTerm) fingerprint(p.info) else p.paramPos)
}
val payload = List[(String, Any)](
"versionFormat" -> versionFormat,
+ "isBundle" -> isBundle,
"className" -> className,
"methodName" -> macroImpl.name.toString,
"signature" -> signature
@@ -205,10 +212,11 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
val pickleVersionFormat = payload("versionFormat").asInstanceOf[Int]
if (versionFormat != pickleVersionFormat) throw new Error(s"macro impl binding format mismatch: expected $versionFormat, actual $pickleVersionFormat")
+ val isBundle = payload("isBundle").asInstanceOf[Boolean]
val className = payload("className").asInstanceOf[String]
val methodName = payload("methodName").asInstanceOf[String]
val signature = payload("signature").asInstanceOf[List[List[Int]]]
- MacroImplBinding(className, methodName, signature, targs)
+ MacroImplBinding(isBundle, className, methodName, signature, targs)
}
}
@@ -222,330 +230,82 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
MacroImplBinding.unpickle(pickle)
}
- /** Transforms parameters lists of a macro impl.
- * The `transform` function is invoked only for WeakTypeTag evidence parameters.
- *
- * The transformer takes two arguments: a value parameter from the parameter list
- * and a type parameter that is witnesses by the value parameter.
- *
- * If the transformer returns a NoSymbol, the value parameter is not included from the result.
- * If the transformer returns something else, this something else is included in the result instead of the value parameter.
- *
- * Despite of being highly esoteric, this function significantly simplifies signature analysis.
- * For example, it can be used to strip macroImpl.paramss from the evidences (necessary when checking def <-> impl correspondence)
- * or to streamline creation of the list of macro arguments.
- */
- private def transformTypeTagEvidenceParams(paramss: List[List[Symbol]], transform: (Symbol, Symbol) => Symbol): List[List[Symbol]] = {
- if (paramss.isEmpty || paramss.last.isEmpty) return paramss // no implicit parameters in the signature => nothing to do
- if (paramss.head.isEmpty || !(paramss.head.head.tpe <:< MacroContextClass.tpe)) return paramss // no context parameter in the signature => nothing to do
- def transformTag(param: Symbol): Symbol = param.tpe.dealias match {
- case TypeRef(SingleType(SingleType(NoPrefix, c), universe), WeakTypeTagClass, targ :: Nil)
- if c == paramss.head.head && universe == MacroContextUniverse =>
- transform(param, targ.typeSymbol)
- case _ =>
- param
- }
- val transformed = paramss.last map transformTag filter (_ ne NoSymbol)
- if (transformed.isEmpty) paramss.init else paramss.init :+ transformed
- }
-
- private def dealiasAndRewrap(tp: Type)(fn: Type => Type): Type = {
- if (isRepeatedParamType(tp)) scalaRepeatedType(fn(tp.typeArgs.head.dealias))
- else fn(tp.dealias)
- }
-
- /** Increases metalevel of the type, i.e. transforms:
- * * T to c.Expr[T]
- *
- * @see Metalevels.scala for more information and examples about metalevels
- */
- private def increaseMetalevel(c: Symbol, tp: Type): Type = dealiasAndRewrap(tp) {
- case tp => typeRef(SingleType(NoPrefix, c), MacroContextExprClass, List(tp))
- }
-
- /** Decreases metalevel of the type, i.e. transforms:
- * * c.Expr[T] to T
- *
- * @see Metalevels.scala for more information and examples about metalevels
- */
- private def decreaseMetalevel(tp: Type): Type = dealiasAndRewrap(tp) {
- case ExprClassOf(runtimeType) => runtimeType
- case _ => AnyClass.tpe // so that macro impls with rhs = ??? don't screw up our inference
- }
-
- def computeMacroDefTypeFromMacroImpl(macroDdef: DefDef, macroImplSig: MacroImplSig): Type = {
- // Step I. Transform c.Expr[T] to T and c.Tree to <untyped>
- var runtimeType = decreaseMetalevel(macroImplSig.ret)
-
- // Step II. Transform type parameters of a macro implementation into type arguments in a macro definition's body
- runtimeType = runtimeType.substituteTypes(macroImplSig.tparams, loadMacroImplBinding(macroDdef.symbol).targs.map(_.tpe))
-
- // Step III. Transform c.prefix.value.XXX to this.XXX and implParam.value.YYY to defParam.YYY
- def unsigma(tpe: Type): Type =
- transformTypeTagEvidenceParams(macroImplSig.paramss, (param, tparam) => NoSymbol) match {
- case (implCtxParam :: Nil) :: implParamss =>
- val implToDef = flatMap2(implParamss, macroDdef.vparamss)(map2(_, _)((_, _))).toMap
- object UnsigmaTypeMap extends TypeMap {
- def apply(tp: Type): Type = tp match {
- case TypeRef(pre, sym, args) =>
- val pre1 = pre match {
- case SingleType(SingleType(SingleType(NoPrefix, c), prefix), value) if c == implCtxParam && prefix == MacroContextPrefix && value == ExprValue =>
- ThisType(macroDdef.symbol.owner)
- case SingleType(SingleType(NoPrefix, implParam), value) if value == ExprValue =>
- implToDef get implParam map (defParam => SingleType(NoPrefix, defParam.symbol)) getOrElse pre
+ def computeMacroDefTypeFromMacroImplRef(macroDdef: DefDef, macroImplRef: Tree): Type = {
+ macroImplRef match {
+ case MacroImplReference(_, _, macroImpl, targs) =>
+ // Step I. Transform c.Expr[T] to T and everything else to Any
+ var runtimeType = decreaseMetalevel(macroImpl.info.finalResultType)
+
+ // Step II. Transform type parameters of a macro implementation into type arguments in a macro definition's body
+ runtimeType = runtimeType.substituteTypes(macroImpl.typeParams, targs map (_.tpe))
+
+ // Step III. Transform c.prefix.value.XXX to this.XXX and implParam.value.YYY to defParam.YYY
+ def unsigma(tpe: Type): Type =
+ transformTypeTagEvidenceParams(macroImplRef, (param, tparam) => NoSymbol) match {
+ case (implCtxParam :: Nil) :: implParamss =>
+ val implToDef = flatMap2(implParamss, macroDdef.vparamss)(map2(_, _)((_, _))).toMap
+ object UnsigmaTypeMap extends TypeMap {
+ def apply(tp: Type): Type = tp match {
+ case TypeRef(pre, sym, args) =>
+ val pre1 = pre match {
+ case SingleType(SingleType(SingleType(NoPrefix, c), prefix), value) if c == implCtxParam && prefix == MacroContextPrefix && value == ExprValue =>
+ ThisType(macroDdef.symbol.owner)
+ case SingleType(SingleType(NoPrefix, implParam), value) if value == ExprValue =>
+ implToDef get implParam map (defParam => SingleType(NoPrefix, defParam.symbol)) getOrElse pre
+ case _ =>
+ pre
+ }
+ val args1 = args map mapOver
+ TypeRef(pre1, sym, args1)
case _ =>
- pre
+ mapOver(tp)
}
- val args1 = args map mapOver
- TypeRef(pre1, sym, args1)
- case _ =>
- mapOver(tp)
- }
- }
-
- UnsigmaTypeMap(tpe)
- case _ =>
- tpe
- }
-
- unsigma(runtimeType)
- }
-
- /** Signature of a macro implementation, used to check def <-> impl correspondence.
- *
- * Technically it can be just an alias to MethodType, but promoting it to a first-class entity
- * provides better encapsulation and convenient syntax for pattern matching.
- */
- case class MacroImplSig(tparams: List[Symbol], paramss: List[List[Symbol]], ret: Type)
+ }
- /** An actual macro implementation signature extracted from a macro implementation method.
- *
- * In the example above for the following macro impl:
- * def fooBar[T: c.WeakTypeTag]
- * (c: scala.reflect.macros.Context)
- * (xs: c.Expr[List[T]])
- * : c.Expr[T] = ...
- *
- * This function will return:
- * (c: scala.reflect.macros.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 Macro-compatible object,
- * then it won't have (c: Context) in its parameters, but will rather refer to Macro.c.
- *
- * @param macroImpl The macro implementation symbol
- */
- def macroImplSig(macroImpl: Symbol): MacroImplSig = {
- val tparams = macroImpl.typeParams
- val paramss = transformTypeTagEvidenceParams(macroImpl.paramss, (param, tparam) => NoSymbol)
- val ret = macroImpl.info.finalResultType
- MacroImplSig(tparams, paramss, ret)
- }
+ UnsigmaTypeMap(tpe)
+ case _ =>
+ tpe
+ }
- /** A reference macro implementation signature extracted from a given macro definition.
- *
- * In the example above for the following macro def:
- * def foo[T](xs: List[T]): T = macro fooBar
- *
- * This function will return:
- * (c: scala.reflect.macros.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.
- *
- * Also note that we need a DefDef, not the corresponding MethodSymbol, because that symbol would be of no use for us.
- * Macro signatures are verified when typechecking macro defs, which means that at that moment inspecting macroDef.info
- * means asking for cyclic reference errors.
- *
- * We need macro implementation symbol as well, because the return type of the macro definition might be omitted,
- * and in that case we'd need to infer it from the return type of the macro implementation. Luckily for us, we can
- * use that symbol without a risk of running into cycles.
- *
- * @param typer Typechecker of `macroDdef`
- * @param macroDdef The macro definition tree
- * @param macroImpl The macro implementation symbol
- */
- def referenceMacroImplSig(typer: Typer, macroDdef: DefDef, macroImpl: Symbol): MacroImplSig = {
- // had to move method's body to an object because of the recursive dependencies between sigma and param
- object SigGenerator {
- val cache = scala.collection.mutable.Map[Symbol, Symbol]()
- val macroDef = macroDdef.symbol
- val ctxParam = makeParam(nme.macroContext, macroDdef.pos, MacroContextClass.tpe, SYNTHETIC)
- val paramss = List(ctxParam) :: mmap(macroDdef.vparamss)(param)
- val macroDefRet =
- if (!macroDdef.tpt.isEmpty) typer.typedType(macroDdef.tpt).tpe
- else computeMacroDefTypeFromMacroImpl(macroDdef, macroImplSig(macroImpl))
- val implReturnType = sigma(increaseMetalevel(ctxParam, macroDefRet))
-
- object SigmaTypeMap extends TypeMap {
- def mapPrefix(pre: Type) = pre match {
- case ThisType(sym) if sym == macroDef.owner =>
- singleType(singleType(singleType(NoPrefix, ctxParam), MacroContextPrefix), ExprValue)
- case SingleType(NoPrefix, sym) =>
- mfind(macroDdef.vparamss)(_.symbol == sym).fold(pre)(p => singleType(singleType(NoPrefix, param(p)), ExprValue))
- case _ =>
- mapOver(pre)
- }
- def apply(tp: Type): Type = tp match {
- case TypeRef(pre, sym, args) =>
- val pre1 = mapPrefix(pre)
- val args1 = mapOverArgs(args, sym.typeParams)
- if ((pre eq pre1) && (args eq args1)) tp
- else typeRef(pre1, sym, args1)
- case _ =>
- mapOver(tp)
- }
- }
- def sigma(tpe: Type): Type = SigmaTypeMap(tpe)
-
- def makeParam(name: Name, pos: Position, tpe: Type, flags: Long) =
- macroDef.newValueParameter(name.toTermName, pos, flags) setInfo tpe
- def param(tree: Tree): Symbol = (
- cache.getOrElseUpdate(tree.symbol, {
- val sym = tree.symbol
- assert(sym.isTerm, s"sym = $sym, tree = $tree")
- makeParam(sym.name, sym.pos, sigma(increaseMetalevel(ctxParam, sym.tpe)), sym.flags)
- })
- )
+ unsigma(runtimeType)
+ case _ =>
+ ErrorType
}
-
- import SigGenerator._
- val result = MacroImplSig(macroDdef.tparams map (_.symbol), paramss, implReturnType)
- macroLogVerbose(sm"""
- |generating macroImplSig for: $macroDdef
- |result is: $result
- """.trim)
- result
}
- /** Verifies that the body of a macro def typechecks to a reference to a static public non-overloaded method,
+ /** Verifies that the body of a macro def typechecks to a reference to a static public non-overloaded method or a top-level macro bundle,
* and that that method is signature-wise compatible with the given macro definition.
*
- * @return Typechecked rhs of the given macro definition if everything is okay.
+ * @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 =
- try new MacroTyper(typer, macroDdef).typed
- catch { case MacroBodyTypecheckException => EmptyTree }
-
- class MacroTyper(val typer: Typer, val macroDdef: DefDef) extends MacroErrors {
- private def typed1Expr(tree: Tree) = typer.typed1(tree, EXPRmode, WildcardType)
-
- // Phase I: sanity checks
+ def typedMacroBody(typer: Typer, macroDdef: DefDef): Tree = {
val macroDef = macroDdef.symbol
- macroLogVerbose("typechecking macro def %s at %s".format(macroDef, macroDdef.pos))
assert(macroDef.isMacro, macroDdef)
- if (fastTrack contains macroDef) MacroDefIsFastTrack()
- if (!typer.checkFeature(macroDdef.pos, MacrosFeature, immediate = true)) MacroFeatureNotEnabled()
-
- // we use typed1 instead of typed, because otherwise adapt is going to mess us up
- // if adapt sees <qualifier>.<method>, it will want to perform eta-expansion and will fail
- // unfortunately, this means that we have to manually trigger macro expansion
- // because it's adapt which is responsible for automatic expansion during typechecking
- def typecheckRhs(rhs: Tree): Tree = {
- try {
- // interestingly enough, just checking isErroneous doesn't cut it
- // e.g. a "type arguments [U] do not conform to method foo's type parameter bounds" error
- // doesn't manifest itself as an error in the resulting tree
- val prevNumErrors = reporter.ERROR.count
- var rhs1 = typed1Expr(rhs)
- def rhsNeedsMacroExpansion = rhs1.symbol != null && rhs1.symbol.isTermMacro && !rhs1.symbol.isErroneous
- while (rhsNeedsMacroExpansion) {
- rhs1 = macroExpand1(typer, rhs1) match {
- case Success(expanded) =>
- try {
- val typechecked = typed1Expr(expanded)
- macroLogVerbose("typechecked1:%n%s%n%s".format(typechecked, showRaw(typechecked)))
- typechecked
- } finally {
- popMacroContext()
- }
- case Fallback(fallback) =>
- typed1Expr(fallback)
- case Delayed(delayed) =>
- typer.instantiate(delayed, EXPRmode, WildcardType)
- case Skipped(skipped) =>
- skipped
- case Failure(failure) =>
- failure
- }
- }
- val typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors
- if (typecheckedWithErrors) MacroDefUntypeableBodyError()
- rhs1
- } catch {
- case ex: TypeError =>
- typer.reportTypeError(context, rhs.pos, ex)
- MacroDefUntypeableBodyError()
- }
- }
- // Phase II: typecheck the right-hand side of the macro def
- val typed = typecheckRhs(macroDdef.rhs)
- typed match {
- case MacroImplReference(_, meth, _) if meth == Predef_??? =>
- bindMacroImpl(macroDef, typed)
- MacroDefIsQmarkQmarkQmark()
- case MacroImplReference(owner, meth, targs) =>
- if (!meth.isMethod) MacroDefInvalidBodyError()
- if (!meth.isPublic) MacroImplNotPublicError()
- if (meth.isOverloaded) MacroImplOverloadedError()
- if (!owner.isStaticOwner && !owner.moduleClass.isStaticOwner) MacroImplNotStaticError()
- if (meth.typeParams.length != targs.length) MacroImplWrongNumberOfTypeArgumentsError(typed)
- bindMacroImpl(macroDef, typed)
- case _ =>
- MacroDefInvalidBodyError()
- }
-
- // Phase III: check compatibility between the macro def and its macro impl
- // this check ignores type tag evidence parameters, because type tag context bounds are optional
- // aXXX (e.g. aparamss) => characteristics of the actual macro impl signature extracted from the macro impl ("a" stands for "actual")
- // rXXX (e.g. rparamss) => characteristics of the reference macro impl signature synthesized from the macro def ("r" stands for "reference")
- val macroImpl = typed.symbol
- val MacroImplSig(atparams, aparamss, aret) = macroImplSig(macroImpl)
- val MacroImplSig(_, rparamss, rret) = referenceMacroImplSig(typer, macroDdef, macroImpl)
- val atvars = atparams map freshVar
- def atpeToRtpe(atpe: Type) = atpe.substSym(aparamss.flatten, rparamss.flatten).instantiateTypeParams(atparams, atvars)
-
- // we only check correspondence between value parameterss
- // type parameters of macro defs and macro impls don't have to coincide with each other
- val implicitParams = aparamss.flatten filter (_.isImplicit)
- if (implicitParams.nonEmpty) MacroImplNonTagImplicitParameters(implicitParams)
- if (aparamss.length != rparamss.length) MacroImplParamssMismatchError()
- map2(aparamss, rparamss)((aparams, rparams) => {
- if (aparams.length < rparams.length) MacroImplMissingParamsError(aparams, rparams)
- if (rparams.length < aparams.length) MacroImplExtraParamsError(aparams, rparams)
- })
-
- try {
- // cannot fuse this map2 and the map2 above because if aparamss.flatten != rparamss.flatten
- // then `atpeToRtpe` is going to fail with an unsound substitution
- map2(aparamss.flatten, rparamss.flatten)((aparam, rparam) => {
- if (aparam.name != rparam.name && !rparam.isSynthetic) MacroImplParamNameMismatchError(aparam, rparam)
- if (isRepeated(aparam) ^ isRepeated(rparam)) MacroImplVarargMismatchError(aparam, rparam)
- val aparamtpe = aparam.tpe.dealias match {
- case RefinedType(List(tpe), Scope(sym)) if tpe =:= MacroContextClass.tpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe
- case tpe => tpe
- }
- checkMacroImplParamTypeMismatch(atpeToRtpe(aparamtpe), rparam)
- })
-
- checkMacroImplResultTypeMismatch(atpeToRtpe(aret), rret)
-
- val maxLubDepth = lubDepth(aparamss.flatten map (_.tpe)) max lubDepth(rparamss.flatten map (_.tpe))
- val atargs = solvedTypes(atvars, atparams, atparams map varianceInType(aret), upper = false, depth = maxLubDepth)
- val boundsOk = typer.silent(_.infer.checkBounds(macroDdef, NoPrefix, NoSymbol, atparams, atargs, ""))
- boundsOk match {
- case SilentResultValue(true) => // do nothing, success
- case SilentResultValue(false) | SilentTypeError(_) => MacroImplTargMismatchError(atargs, atparams)
+ macroLogVerbose("typechecking macro def %s at %s".format(macroDef, macroDdef.pos))
+ if (fastTrack contains macroDef) {
+ macroLogVerbose("typecheck terminated unexpectedly: macro is fast track")
+ assert(!macroDdef.tpt.isEmpty, "fast track macros must provide result type")
+ EmptyTree
+ } else {
+ def fail() = { if (macroDef != null) macroDef setFlag IS_ERROR; macroDdef setType ErrorType; EmptyTree }
+ def success(macroImplRef: Tree) = { bindMacroImpl(macroDef, macroImplRef); macroImplRef }
+
+ if (!typer.checkFeature(macroDdef.pos, MacrosFeature, immediate = true)) {
+ macroLogVerbose("typecheck terminated unexpectedly: language.experimental.macros feature is not enabled")
+ fail()
+ } else {
+ val macroDdef1: macroDdef.type = macroDdef
+ val typer1: typer.type = typer
+ val macroCompiler = new {
+ val global: self.global.type = self.global
+ val typer: self.global.analyzer.Typer = typer1.asInstanceOf[self.global.analyzer.Typer]
+ val macroDdef: self.global.DefDef = macroDdef1
+ } with DefaultMacroCompiler
+ val macroImplRef = macroCompiler.resolveMacroImpl
+ if (macroImplRef.isEmpty) fail() else success(macroImplRef)
}
- } catch {
- case ex: NoInstance => MacroImplTparamInstantiationError(atparams, ex)
}
}
@@ -555,6 +315,10 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
*/
lazy val macroClassloader: ClassLoader = findMacroClassLoader()
+ /** Reflective mirror built from `macroClassloader`.
+ */
+ private lazy val macroMirror: ru.JavaMirror = ru.runtimeMirror(macroClassloader)
+
/** Produces a function that can be used to invoke macro implementation for a given macro definition:
* 1) Looks up macro implementation symbol in this universe.
* 2) Loads its enclosing class from the macro classloader.
@@ -574,28 +338,32 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
} else {
macroRuntimesCache.getOrElseUpdate(macroDef, {
val binding = loadMacroImplBinding(macroDef)
+ val isBundle = binding.isBundle
val className = binding.className
val methName = binding.methName
macroLogVerbose(s"resolved implementation as $className.$methName")
- if (binding.className == Predef_???.owner.fullName.toString && binding.methName == Predef_???.name.encoded) {
+ if (binding.className == Predef_???.owner.javaClassName && binding.methName == Predef_???.name.encoded) {
args => throw new AbortMacroException(args.c.enclosingPosition, "macro implementation is missing")
} else {
- // I don't use Scala reflection here, because it seems to interfere with JIT magic
- // whenever you instantiate a mirror (and not do anything with in, just instantiate), performance drops by 15-20%
- // I'm not sure what's the reason - for me it's pure voodoo
- // upd. my latest experiments show that everything's okay
- // it seems that in 2.10.1 we can easily switch to Scala reflection
try {
macroLogVerbose(s"loading implementation class: $className")
macroLogVerbose(s"classloader is: ${ReflectionUtils.show(macroClassloader)}")
- val implObj = ReflectionUtils.staticSingletonInstance(macroClassloader, className)
- // relies on the fact that macro impls cannot be overloaded
- // so every methName can resolve to at maximum one method
- val implMeths = implObj.getClass.getDeclaredMethods.find(_.getName == methName)
- val implMeth = implMeths getOrElse { throw new NoSuchMethodException(s"$className.$methName") }
- macroLogVerbose(s"successfully loaded macro impl as ($implObj, $implMeth)")
- args => implMeth.invoke(implObj, ((args.c +: args.others) map (_.asInstanceOf[AnyRef])): _*)
+ val implContainerSym = macroMirror.classSymbol(Class.forName(className, true, macroClassloader))
+ 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: _*)
+ }
} catch {
case ex: Exception =>
macroLogVerbose(s"macro runtime failed to load: ${ex.toString}")
@@ -640,31 +408,32 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
import typer.TyperErrorGen._
val isNullaryArgsEmptyParams = argss.isEmpty && paramss == ListOfNil
- if (paramss.length < argss.length) MacroTooManyArgumentLists(expandee)
- if (paramss.length > argss.length && !isNullaryArgsEmptyParams) MacroTooFewArgumentLists(expandee)
+ if (paramss.length < argss.length) MacroTooManyArgumentListsError(expandee)
+ if (paramss.length > argss.length && !isNullaryArgsEmptyParams) MacroTooFewArgumentListsError(expandee)
val macroImplArgs: List[Any] =
if (fastTrack contains macroDef) {
// Take a dry run of the fast track implementation
if (fastTrack(macroDef) validate expandee) argss.flatten
- else typer.TyperErrorGen.MacroTooFewArgumentLists(expandee)
+ else MacroTooFewArgumentListsError(expandee)
}
else {
val binding = loadMacroImplBinding(macroDef)
- if (binding.className == Predef_???.owner.fullName.toString && binding.methName == Predef_???.name.encoded) Nil
+ if (binding.className == Predef_???.owner.javaClassName && binding.methName == Predef_???.name.encoded) Nil
else {
+ val signature = if (binding.isBundle) binding.signature else binding.signature.tail
macroLogVerbose(s"binding: $binding")
// STEP I: prepare value arguments of the macro expansion
// wrap argss in c.Expr if necessary (i.e. if corresponding macro impl param is of type c.Expr[T])
// expand varargs (nb! varargs can apply to any parameter section, not necessarily to the last one)
- val trees = map3(argss, paramss, binding.signature.tail)((args, defParams, implParams) => {
+ val trees = map3(argss, paramss, signature)((args, defParams, implParams) => {
val isVarargs = isVarArgsList(defParams)
if (isVarargs) {
- if (defParams.length > args.length + 1) MacroTooFewArguments(expandee)
+ if (defParams.length > args.length + 1) MacroTooFewArgumentsError(expandee)
} else {
- if (defParams.length < args.length) MacroTooManyArguments(expandee)
- if (defParams.length > args.length) MacroTooFewArguments(expandee)
+ if (defParams.length < args.length) MacroTooManyArgumentsError(expandee)
+ if (defParams.length > args.length) MacroTooFewArgumentsError(expandee)
}
val wrappedArgs = mapWithIndex(args)((arg, j) => {
@@ -700,7 +469,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
// then T and U need to be inferred from the lexical scope of the call using `asSeenFrom`
// whereas V won't be resolved by asSeenFrom and need to be loaded directly from `expandee` which needs to contain a TypeApply node
// also, macro implementation reference may contain a regular type as a type argument, then we pass it verbatim
- val tags = binding.signature.flatten filter (_ >= IMPLPARAM_TAG) map (paramPos => {
+ val tags = signature.flatten filter (_ >= IMPLPARAM_TAG) map (paramPos => {
val targ = binding.targs(paramPos).tpe.typeSymbol
val tpe = if (targ.isTypeParameterOrSkolem) {
if (targ.owner == macroDef) {