summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2012-08-17 09:04:41 -0700
committerEugene Burmako <xeno.by@gmail.com>2012-08-17 09:04:41 -0700
commit7fc860963a4f76cb18e44c20f2bdfb49641033f3 (patch)
treefe3518939e0996115701c6c20eaaf1d4406797b3
parent722fc004732aca0b1d6b9143b960e5a8268c31a0 (diff)
parentddf27a1e0e4b9eae521651704e74298c33c9af6c (diff)
downloadscala-7fc860963a4f76cb18e44c20f2bdfb49641033f3.tar.gz
scala-7fc860963a4f76cb18e44c20f2bdfb49641033f3.tar.bz2
scala-7fc860963a4f76cb18e44c20f2bdfb49641033f3.zip
Merge pull request #1151 from scalamacros/topic/cleanup
more cleanup in Macros.scala
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala172
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala553
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala2
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala1
-rw-r--r--src/reflect/scala/reflect/internal/TreeInfo.scala21
-rw-r--r--test/files/neg/macro-invalidshape-a.check3
-rw-r--r--test/files/neg/macro-invalidshape-b.check3
-rw-r--r--test/files/neg/macro-invalidshape-c.check11
-rw-r--r--test/files/neg/macro-invalidsig-context-bounds.check11
-rw-r--r--test/files/neg/macro-invalidsig-implicit-params.check11
-rw-r--r--test/files/neg/macro-invalidsig-params-namemismatch.check1
-rw-r--r--test/files/neg/macro-invalidsig-tparams-badtype.check2
-rw-r--r--test/files/neg/macro-invalidsig-tparams-notparams-a.check2
-rw-r--r--test/files/neg/macro-invalidsig-tparams-notparams-b.check2
-rw-r--r--test/files/run/macro-def-path-dependent-d1.check (renamed from test/files/run/macro-def-path-dependent-d.check)0
-rw-r--r--test/files/run/macro-def-path-dependent-d1.flags (renamed from test/files/run/macro-def-path-dependent-d.flags)0
-rw-r--r--test/files/run/macro-def-path-dependent-d1/Impls_Macros_1.scala (renamed from test/files/run/macro-def-path-dependent-d/Impls_Macros_1.scala)0
-rw-r--r--test/files/run/macro-def-path-dependent-d1/Test_2.scala (renamed from test/files/run/macro-def-path-dependent-d/Test_2.scala)0
-rw-r--r--test/files/run/macro-def-path-dependent-d2.check1
-rw-r--r--test/files/run/macro-def-path-dependent-d2.flags1
-rw-r--r--test/files/run/macro-def-path-dependent-d2/Impls_1.scala7
-rw-r--r--test/files/run/macro-def-path-dependent-d2/Macros_2.scala7
-rw-r--r--test/files/run/macro-def-path-dependent-d2/Test_3.scala3
23 files changed, 392 insertions, 422 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index c7728ce389..5a110739cd 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -8,12 +8,15 @@ package typechecker
import scala.collection.{ mutable, immutable }
import scala.reflect.internal.util.StringOps.{ countElementsAsString, countAsString }
-import symtab.Flags.{ PRIVATE, PROTECTED }
+import symtab.Flags.{ PRIVATE, PROTECTED, IS_ERROR }
+import scala.compat.Platform.EOL
trait ContextErrors {
self: Analyzer =>
import global._
+ import definitions._
+ import treeInfo._
object ErrorKinds extends Enumeration {
type ErrorKind = Value
@@ -427,8 +430,11 @@ trait ContextErrors {
def AbstractionFromVolatileTypeError(vd: ValDef) =
issueNormalTypeError(vd, "illegal abstraction from value with volatile type "+vd.symbol.tpe)
+ private[ContextErrors] def TypedApplyWrongNumberOfTpeParametersErrorMessage(fun: Tree) =
+ "wrong number of type parameters for "+treeSymTypeMsg(fun)
+
def TypedApplyWrongNumberOfTpeParametersError(tree: Tree, fun: Tree) = {
- issueNormalTypeError(tree, "wrong number of type parameters for "+treeSymTypeMsg(fun))
+ issueNormalTypeError(tree, TypedApplyWrongNumberOfTpeParametersErrorMessage(fun))
setError(tree)
}
@@ -625,11 +631,11 @@ trait ContextErrors {
}
// cyclic errors
- def CyclicAliasingOrSubtypingError(errPos: Position, sym0: Symbol) =
- issueTypeError(PosAndMsgTypeError(errPos, "cyclic aliasing or subtyping involving "+sym0))
+ def CyclicAliasingOrSubtypingError(errPos: Position, sym0: Symbol) =
+ issueTypeError(PosAndMsgTypeError(errPos, "cyclic aliasing or subtyping involving "+sym0))
- def CyclicReferenceError(errPos: Position, lockedSym: Symbol) =
- issueTypeError(PosAndMsgTypeError(errPos, "illegal cyclic reference involving " + lockedSym))
+ def CyclicReferenceError(errPos: Position, lockedSym: Symbol) =
+ issueTypeError(PosAndMsgTypeError(errPos, "illegal cyclic reference involving " + lockedSym))
}
}
@@ -706,7 +712,7 @@ trait ContextErrors {
// side-effect on the tree, break the overloaded type cycle in infer
@inline
private def setErrorOnLastTry(lastTry: Boolean, tree: Tree) = if (lastTry) setError(tree)
-
+
def NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], pt: Type, lastTry: Boolean) = {
issueNormalTypeError(tree,
applyErrorMsg(tree, " cannot be applied to ", argtpes, pt))
@@ -719,7 +725,7 @@ trait ContextErrors {
def AmbiguousMethodAlternativeError(tree: Tree, pre: Type, best: Symbol,
firstCompeting: Symbol, argtpes: List[Type], pt: Type, lastTry: Boolean) = {
-
+
if (!(argtpes exists (_.isErroneous)) && !pt.isErroneous) {
val msg0 =
"argument types " + argtpes.mkString("(", ",", ")") +
@@ -729,7 +735,7 @@ trait ContextErrors {
setErrorOnLastTry(lastTry, tree)
} else setError(tree) // do not even try further attempts because they should all fail
// even if this is not the last attempt (because of the SO's possibility on the horizon)
-
+
}
def NoBestExprAlternativeError(tree: Tree, pt: Type, lastTry: Boolean) = {
@@ -753,21 +759,24 @@ trait ContextErrors {
kindErrors.toList.mkString("\n", ", ", ""))
}
- def NotWithinBounds(tree: Tree, prefix: String, targs: List[Type],
- tparams: List[Symbol], kindErrors: List[String]) = {
- if (settings.explaintypes.value) {
+ private[ContextErrors] def NotWithinBoundsErrorMessage(prefix: String, targs: List[Type], tparams: List[Symbol], explaintypes: Boolean) = {
+ if (explaintypes) {
val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, targs).bounds)
(targs, bounds).zipped foreach ((targ, bound) => explainTypes(bound.lo, targ))
(targs, bounds).zipped foreach ((targ, bound) => explainTypes(targ, bound.hi))
()
}
- issueNormalTypeError(tree,
- prefix + "type arguments " + targs.mkString("[", ",", "]") +
- " do not conform to " + tparams.head.owner + "'s type parameter bounds " +
- (tparams map (_.defString)).mkString("[", ",", "]"))
+ prefix + "type arguments " + targs.mkString("[", ",", "]") +
+ " do not conform to " + tparams.head.owner + "'s type parameter bounds " +
+ (tparams map (_.defString)).mkString("[", ",", "]")
}
+ def NotWithinBounds(tree: Tree, prefix: String, targs: List[Type],
+ tparams: List[Symbol], kindErrors: List[String]) =
+ issueNormalTypeError(tree,
+ NotWithinBoundsErrorMessage(prefix, targs, tparams, settings.explaintypes.value))
+
//substExpr
def PolymorphicExpressionInstantiationError(tree: Tree, undetparams: List[Symbol], pt: Type) =
issueNormalTypeError(tree,
@@ -1061,7 +1070,7 @@ trait ContextErrors {
setError(arg)
} else arg
}
-
+
def WarnAfterNonSilentRecursiveInference(param: Symbol, arg: Tree)(implicit context: Context) = {
val note = "type-checking the invocation of "+ param.owner +" checks if the named argument expression '"+ param.name + " = ...' is a valid assignment\n"+
"in the current scope. The resulting type inference error (see above) can be fixed by providing an explicit type in the local definition for "+ param.name +"."
@@ -1087,4 +1096,133 @@ trait ContextErrors {
setError(arg)
}
}
+
+ // using an exception here is actually a good idea
+ // because the lifespan of this exception is extremely small and controlled
+ // moreover exceptions let us avoid an avalanche of "if (!hasError) do stuff" checks
+ case object MacroBodyTypecheckException extends Exception with scala.util.control.ControlThrowable
+
+ trait MacroErrors {
+ self: MacroTyper =>
+
+ private implicit val context0 = typer.context
+ val context = typer.context
+
+ // helpers
+
+ private def lengthMsg(flavor: String, violation: String, extra: Symbol) = {
+ val noun = if (flavor == "value") "parameter" else "type parameter"
+ val message = noun + " lists have different length, " + violation + " extra " + noun
+ val suffix = if (extra ne NoSymbol) " " + extra.defString else ""
+ message + suffix
+ }
+
+ private def abbreviateCoreAliases(s: String): String = List("AbsTypeTag", "Expr").foldLeft(s)((res, x) => res.replace("c.universe." + x, "c." + x))
+
+ private def showMeth(pss: List[List[Symbol]], restpe: Type, abbreviate: Boolean) = {
+ var argsPart = (pss map (ps => ps map (_.defString) mkString ("(", ", ", ")"))).mkString
+ if (abbreviate) argsPart = abbreviateCoreAliases(argsPart)
+ var retPart = restpe.toString
+ if (abbreviate || macroDdef.tpt.tpe == null) retPart = abbreviateCoreAliases(retPart)
+ argsPart + ": " + retPart
+ }
+
+ // not exactly an error generator, but very related
+ // and I dearly wanted to push it away from Macros.scala
+ private def checkSubType(slot: String, rtpe: Type, atpe: Type) = {
+ val ok = if (macroDebugVerbose || settings.explaintypes.value) {
+ if (rtpe eq atpe) println(rtpe + " <: " + atpe + "?" + EOL + "true")
+ withTypesExplained(rtpe <:< atpe)
+ } else rtpe <:< atpe
+ if (!ok) {
+ compatibilityError("type mismatch for %s: %s does not conform to %s".format(slot, abbreviateCoreAliases(rtpe.toString), abbreviateCoreAliases(atpe.toString)))
+ }
+ }
+
+ // errors
+
+ private def fail() = {
+ // need to set the IS_ERROR flag to prohibit spurious expansions
+ if (macroDef != null) macroDef setFlag IS_ERROR
+ // not setting ErrorSymbol as in `infer.setError`, because we still need to know that it's a macro
+ // otherwise assignTypeToTree in Namers might fail if macroDdef.tpt == EmptyTree
+ macroDdef setType ErrorType
+ throw MacroBodyTypecheckException
+ }
+
+ private def genericError(tree: Tree, message: String) = {
+ issueNormalTypeError(tree, message)
+ fail()
+ }
+
+ private def implRefError(message: String) = genericError(methPart(macroDdef.rhs), message)
+
+ private def compatibilityError(message: String) =
+ implRefError(
+ "macro implementation has wrong shape:"+
+ "\n required: " + showMeth(rparamss, rret, abbreviate = true) +
+ "\n found : " + showMeth(aparamss, aret, abbreviate = false) +
+ "\n" + message)
+
+ // Phase I: sanity checks
+
+ def MacroDefIsFastTrack() = {
+ macroLogVerbose("typecheck terminated unexpectedly: macro is fast track")
+ assert(!macroDdef.tpt.isEmpty, "fast track macros must provide result type")
+ throw MacroBodyTypecheckException // don't call fail, because we don't need IS_ERROR
+ }
+
+ def MacroFeatureNotEnabled() = {
+ macroLogVerbose("typecheck terminated unexpectedly: language.experimental.macros feature is not enabled")
+ fail()
+ }
+
+ // Phase II: typecheck the right-hand side of the macro def
+
+ // do nothing, just fail. relevant typecheck errors have already been reported
+ def MacroDefUntypeableBodyError() = fail()
+
+ def MacroDefInvalidBodyError() = genericError(macroDdef, "macro body has wrong shape:\n required: macro [<implementation object>].<method name>[[<type args>]]")
+
+ def MacroImplNotPublicError() = implRefError("macro implementation must be public")
+
+ def MacroImplOverloadedError() = implRefError("macro implementation cannot be overloaded")
+
+ def MacroImplWrongNumberOfTypeArgumentsError(macroImplRef: Tree) = implRefError(typer.TyperErrorGen.TypedApplyWrongNumberOfTpeParametersErrorMessage(macroImplRef))
+
+ def MacroImplNotStaticError() = implRefError("macro implementation must be in statically accessible object")
+
+ // Phase III: check compatibility between the macro def and its macro impl
+ // aXXX (e.g. aparams) => characteristics of the macro impl ("a" stands for "actual")
+ // rXXX (e.g. rparams) => characteristics of a reference macro impl signature synthesized from the macro def ("r" stands for "reference")
+
+ def MacroImplNonTagImplicitParameters(params: List[Symbol]) = compatibilityError("macro implementations cannot have implicit parameters other than AbsTypeTag evidences")
+
+ def MacroImplParamssMismatchError() = compatibilityError("number of parameter sections differ")
+
+ def MacroImplExtraParamsError(aparams: List[Symbol], rparams: List[Symbol]) = compatibilityError(lengthMsg("value", "found", aparams(rparams.length)))
+
+ def MacroImplMissingParamsError(aparams: List[Symbol], rparams: List[Symbol]) = compatibilityError(abbreviateCoreAliases(lengthMsg("value", "required", rparams(aparams.length))))
+
+ def checkMacroImplParamTypeMismatch(atpe: Type, rparam: Symbol) = checkSubType("parameter " + rparam.name, rparam.tpe, atpe)
+
+ def checkMacroImplResultTypeMismatch(atpe: Type, rret: Type) = checkSubType("return type", atpe, rret)
+
+ def MacroImplParamNameMismatchError(aparam: Symbol, rparam: Symbol) = compatibilityError("parameter names differ: " + rparam.name + " != " + aparam.name)
+
+ def MacroImplVarargMismatchError(aparam: Symbol, rparam: Symbol) = {
+ if (isRepeated(rparam) && !isRepeated(aparam))
+ compatibilityError("types incompatible for parameter " + rparam.name + ": corresponding is not a vararg parameter")
+ if (!isRepeated(rparam) && isRepeated(aparam))
+ compatibilityError("types incompatible for parameter " + aparam.name + ": corresponding is not a vararg parameter")
+ }
+
+ def MacroImplTargMismatchError(atargs: List[Type], atparams: List[Symbol]) =
+ compatibilityError(typer.infer.InferErrorGen.NotWithinBoundsErrorMessage("", atargs, atparams, macroDebugVerbose || settings.explaintypes.value))
+
+ def MacroImplTparamInstantiationError(atparams: List[Symbol], ex: NoInstance) =
+ compatibilityError(
+ "type parameters "+(atparams map (_.defString) mkString ", ")+" cannot be instantiated\n"+
+ ex.getMessage)
+ }
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 02d95544e8..4601f63809 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -24,7 +24,7 @@ import scala.reflect.internal.util.Collections._
*
* Then fooBar needs to point to a static method of the following form:
*
- * def fooBar[T: c.AbsTypeTag]
+ * def fooBar[T: c.AbsTypeTag] // type tag annotation is optional
* (c: scala.reflect.macros.Context)
* (xs: c.Expr[List[T]])
* : c.Expr[T] = {
@@ -32,7 +32,7 @@ import scala.reflect.internal.util.Collections._
* }
*
* Then, if foo is called in qual.foo[Int](elems), where qual: D,
- * the macro application is expanded to a reflective invocation of fooBar with parameters
+ * the macro application is expanded to a reflective invocation of fooBar with parameters:
*
* (simpleMacroContext{ type PrefixType = D; val prefix = qual })
* (Expr(elems))
@@ -43,6 +43,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
import global._
import definitions._
+ import treeInfo.{isRepeatedParamType => _, _}
import MacrosStats._
def globalSettings = global.settings
@@ -212,11 +213,86 @@ 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 AbsTypeTag 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), AbsTypeTagClass, 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
+ }
+
+ def computeMacroDefTypeFromMacroImpl(macroDdef: DefDef, macroImpl: Symbol): Type = {
+ // Step I. Transform c.Expr[T] to T
+ var runtimeType = macroImpl.tpe.finalResultType.dealias match {
+ case TypeRef(_, ExprClass, runtimeType :: Nil) => runtimeType
+ case _ => AnyTpe // so that macro impls with rhs = ??? don't screw up our inference
+ }
+
+ // Step II. Transform type parameters of a macro implementation into type arguments in a macro definition's body
+ runtimeType = runtimeType.substituteTypes(macroImpl.typeParams, 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(macroImpl.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
+ case _ =>
+ pre
+ }
+ val args1 = args map mapOver
+ TypeRef(pre1, sym, args1)
+ case _ =>
+ mapOver(tp)
+ }
+ }
+
+ UnsigmaTypeMap(tpe)
+ case _ =>
+ tpe
+ }
+
+ unsigma(runtimeType)
+ }
+
/** A reference macro implementation signature compatible with a given macro definition.
*
- * In the example above:
+ * 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.
+ *
* @param macroDef The macro definition symbol
* @param tparams The type parameters of the macro definition
* @param vparamss The value parameters of the macro definition
@@ -225,33 +301,22 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
private def macroImplSig(macroDef: Symbol, tparams: List[TypeDef], vparamss: List[List[ValDef]], retTpe: Type): (List[List[Symbol]], Type) = {
// had to move method's body to an object because of the recursive dependencies between sigma and param
object SigGenerator {
- val hasThis = macroDef.owner.isClass
- val ownerTpe = macroDef.owner match {
- case owner if owner.isModuleClass => new UniqueThisType(macroDef.owner)
- case owner if owner.isClass => macroDef.owner.tpe
- case _ => NoType
- }
- val hasTparams = !tparams.isEmpty
-
def sigma(tpe: Type): Type = {
class SigmaTypeMap extends TypeMap {
def apply(tp: Type): Type = tp match {
case TypeRef(pre, sym, args) =>
val pre1 = pre match {
case ThisType(sym) if sym == macroDef.owner =>
- SingleType(SingleType(SingleType(NoPrefix, paramsCtx(0)), MacroContextPrefix), ExprValue)
+ SingleType(SingleType(SingleType(NoPrefix, ctxParam), MacroContextPrefix), ExprValue)
case SingleType(NoPrefix, sym) =>
mfind(vparamss)(_.symbol == sym) match {
- case Some(macroDefParam) =>
- SingleType(SingleType(NoPrefix, param(macroDefParam)), ExprValue)
- case _ =>
- pre
+ case Some(macroDefParam) => SingleType(SingleType(NoPrefix, param(macroDefParam)), ExprValue)
+ case _ => pre
}
case _ =>
pre
}
- val args1 = args map mapOver
- TypeRef(pre1, sym, args1)
+ TypeRef(pre1, sym, args map mapOver)
case _ =>
mapOver(tp)
}
@@ -281,11 +346,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
sigParam
})
- val paramsCtx = List(ctxParam)
- val paramsThis = List(makeParam(nme.macroThis, macroDef.pos, implType(false, ownerTpe), SYNTHETIC))
- val paramsTparams = tparams map param
- val paramssParams = mmap(vparamss)(param)
- val paramss = paramsCtx :: paramssParams
+ val paramss = List(ctxParam) :: mmap(vparamss)(param)
val implRetTpe = typeRef(singleType(NoPrefix, ctxParam), getMember(MacroContextClass, tpnme.Expr), List(sigma(retTpe)))
}
@@ -297,142 +358,23 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
macroTraceVerbose("macroImplSig is: ")(paramss, implRetTpe)
}
- /** Transforms parameters lists of a macro impl.
- * The `transform` function is invoked only for AbsTypeTag 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), typetag, targ :: Nil)
- if c == paramss.head.head && universe == MacroContextUniverse && typetag == AbsTypeTagClass =>
- transform(param, targ.typeSymbol)
- case _ =>
- param
- }
- val transformed = paramss.last map transformTag filter (_ ne NoSymbol)
- if (transformed.isEmpty) paramss.init else paramss.init :+ transformed
- }
-
- /** As specified above, body of a macro definition must reference its implementation.
- * This function verifies that the body indeed refers to a method, and that
- * the referenced macro implementation is compatible with the given macro definition.
+ /** Verifies that the body of a macro def typechecks to a reference to a static public non-overloaded method,
+ * and that that method is signature-wise compatible with the given macro definition.
*
- * This means that macro implementation (fooBar in example above) must:
- * 1) Refer to a statically accessible, non-overloaded method.
- * 2) Have the right parameter lists as outlined in the SIP / in the doc comment of this class.
- *
- * @return typechecked rhs of the given macro definition
+ * @return Typechecked rhs of the given macro definition if everything is okay.
+ * EmptyTree if an error occurs.
*/
- def typedMacroBody(typer: Typer, ddef: DefDef): Tree = {
- import typer.context
- macroLogVerbose("typechecking macro def %s at %s".format(ddef.symbol, ddef.pos))
-
- val macroDef = ddef.symbol
- val defpos = macroDef.pos
- val implpos = ddef.rhs.pos
- assert(macroDef.isTermMacro, ddef)
-
- if (fastTrack contains ddef.symbol) {
- macroLogVerbose("typecheck terminated unexpectedly: macro is hardwired")
- assert(!ddef.tpt.isEmpty, "hardwired macros must provide result type")
- return EmptyTree
- }
-
- if (!typer.checkFeature(ddef.pos, MacrosFeature, immediate = true)) {
- macroLogVerbose("typecheck terminated unexpectedly: language.experimental.macros feature is not enabled")
- ddef.symbol setFlag IS_ERROR
- return EmptyTree
- }
-
- implicit class AugmentedString(s: String) {
- def abbreviateCoreAliases: String = { // hack!
- var result = s
- result = result.replace("c.universe.AbsTypeTag", "c.AbsTypeTag")
- result = result.replace("c.universe.Expr", "c.Expr")
- result
- }
- }
-
- var _hasError = false
- def hasError = _hasError
- def setError(): Unit = {
- _hasError = true
- macroDef setFlag IS_ERROR
- }
- def reportError(pos: Position, msg: String) = {
- setError()
- context.error(pos, msg)
- }
-
- def invalidBodyError() =
- reportError(defpos,
- "macro body has wrong shape:" +
- "\n required: macro <reference to implementation object>.<implementation method name>" +
- "\n or : macro <implementation method name>")
- def validatePreTyper(rhs: Tree): Unit = rhs match {
- // we do allow macro invocations inside macro bodies
- // personally I don't mind if pre-typer tree is a macro invocation
- // that later resolves to a valid reference to a macro implementation
- // however, I don't think that invalidBodyError() should hint at that
- // let this be an Easter Egg :)
- case Apply(_, _) => ;
- case TypeApply(_, _) => ;
- case Super(_, _) => ;
- case This(_) => ;
- case Ident(_) => ;
- case Select(_, _) => ;
- case _ => invalidBodyError()
- }
- def validatePostTyper(rhs1: Tree): Unit = {
- def loop(tree: Tree): Unit = {
- def errorNotStatic() =
- reportError(implpos, "macro implementation must be in statically accessible object")
-
- def ensureRoot(sym: Symbol) =
- if (!sym.isModule && !sym.isModuleClass) errorNotStatic()
-
- def ensureModule(sym: Symbol) =
- if (!sym.isModule) errorNotStatic()
-
- tree match {
- case TypeApply(fun, _) =>
- loop(fun)
- case Super(qual, _) =>
- ensureRoot(macroDef.owner)
- loop(qual)
- case This(_) =>
- ensureRoot(tree.symbol)
- case Select(qual, name) if name.isTypeName =>
- loop(qual)
- case Select(qual, name) if name.isTermName =>
- if (tree.symbol != rhs1.symbol) ensureModule(tree.symbol)
- loop(qual)
- case Ident(name) if name.isTypeName =>
- ;
- case Ident(name) if name.isTermName =>
- if (tree.symbol != rhs1.symbol) ensureModule(tree.symbol)
- case _ =>
- invalidBodyError()
- }
- }
-
- loop(rhs1)
- }
-
- val rhs = ddef.rhs
- validatePreTyper(rhs)
- if (hasError) macroTraceVerbose("macro def failed to satisfy trivial preconditions: ")(macroDef)
+ 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 {
+ // Phase I: sanity checks
+ val macroDef = macroDdef.symbol
+ macroLogVerbose("typechecking macro def %s at %s".format(macroDef, macroDdef.pos))
+ assert(macroDef.isTermMacro, 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
@@ -440,11 +382,13 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
// 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 = if (hasError) EmptyTree else typer.typed1(rhs, EXPRmode, WildcardType)
- def typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors
+ var rhs1 = typer.typed1(rhs, EXPRmode, WildcardType)
def rhsNeedsMacroExpansion = rhs1.symbol != null && rhs1.symbol.isTermMacro && !rhs1.symbol.isErroneous
- while (!typecheckedWithErrors && rhsNeedsMacroExpansion) {
+ while (rhsNeedsMacroExpansion) {
rhs1 = macroExpand1(typer, rhs1) match {
case Success(expanded) =>
try {
@@ -460,233 +404,76 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
result
}
}
+ val typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors
+ if (typecheckedWithErrors) MacroDefUntypeableBodyError()
rhs1
} catch {
case ex: TypeError =>
typer.reportTypeError(context, rhs.pos, ex)
- typer.infer.setError(rhs)
+ MacroDefUntypeableBodyError()
}
}
- val prevNumErrors = reporter.ERROR.count // funnily enough, the isErroneous check is not enough
- var rhs1 = typecheckRhs(rhs)
- val macroImpl = rhs1.symbol
- def typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors
- if (typecheckedWithErrors) {
- setError()
- macroTraceVerbose("body of a macro def failed to typecheck: ")(ddef)
- } else {
- if (!hasError) {
- if (macroImpl == null) invalidBodyError()
- else {
- if (!macroImpl.isMethod) invalidBodyError()
- if (!macroImpl.isPublic) reportError(implpos, "macro implementation must be public")
- if (macroImpl.isOverloaded) reportError(implpos, "macro implementation cannot be overloaded")
- if (!macroImpl.typeParams.isEmpty && !rhs1.isInstanceOf[TypeApply]) reportError(implpos, "macro implementation reference needs type arguments")
- if (!hasError) validatePostTyper(rhs1)
- }
- }
- if (!hasError) {
- bindMacroImpl(macroDef, rhs1) // we must bind right over here, because return type inference needs this info
- }
- }
-
- if (!hasError) {
- def checkCompatibility(reqparamss: List[List[Symbol]], actparamss: List[List[Symbol]], reqres: Type, actres: Type): List[String] = {
- var hasError = false
- var errors = List[String]()
- def compatibilityError(msg: String) {
- hasError = true
- errors :+= msg
- }
-
- val flatreqparams = reqparamss.flatten
- val flatactparams = actparamss.flatten
- val tparams = macroImpl.typeParams
- val tvars = tparams map freshVar
- def lengthMsg(which: String, extra: Symbol) =
- "parameter lists have different length, "+which+" extra parameter "+extra.defString
- if (actparamss.length != reqparamss.length)
- compatibilityError("number of parameter sections differ")
-
- def checkSubType(slot: String, reqtpe: Type, acttpe: Type): Unit = {
- val ok = if (macroDebugVerbose) {
- if (reqtpe eq acttpe) println(reqtpe + " <: " + acttpe + "?" + EOL + "true")
- withTypesExplained(reqtpe <:< acttpe)
- } else reqtpe <:< acttpe
- if (!ok) {
- compatibilityError("type mismatch for %s: %s does not conform to %s".format(slot, reqtpe.toString.abbreviateCoreAliases, acttpe.toString.abbreviateCoreAliases))
- }
- }
-
- if (!hasError) {
- try {
- for ((rparams, aparams) <- reqparamss zip actparamss) {
- if (rparams.length < aparams.length)
- compatibilityError(lengthMsg("found", aparams(rparams.length)))
- if (aparams.length < rparams.length)
- compatibilityError(lengthMsg("required", rparams(aparams.length)).abbreviateCoreAliases)
- }
- // if the implementation signature is already deemed to be incompatible, we bail out
- // otherwise, high-order type magic employed below might crash in weird ways
- if (!hasError) {
- for ((rparams, aparams) <- reqparamss zip actparamss) {
- for ((rparam, aparam) <- rparams zip aparams) {
- def isRepeated(param: Symbol) = param.tpe.typeSymbol == RepeatedParamClass
- if (rparam.name != aparam.name && !rparam.isSynthetic) {
- val rparam1 = rparam
- val aparam1 = aparam
- compatibilityError("parameter names differ: "+rparam.name+" != "+aparam.name)
- }
- if (isRepeated(rparam) && !isRepeated(aparam))
- compatibilityError("types incompatible for parameter "+rparam.name+": corresponding is not a vararg parameter")
- if (!isRepeated(rparam) && isRepeated(aparam))
- compatibilityError("types incompatible for parameter "+aparam.name+": corresponding is not a vararg parameter")
- if (!hasError) {
- var atpe = aparam.tpe.substSym(flatactparams, flatreqparams).instantiateTypeParams(tparams, tvars)
- atpe = atpe.dealias // SI-5706
- // strip the { type PrefixType = ... } refinement off the Context or otherwise we get compatibility errors
- atpe = atpe match {
- case RefinedType(List(tpe), Scope(sym)) if tpe == MacroContextClass.tpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe
- case _ => atpe
- }
- checkSubType("parameter " + rparam.name, rparam.tpe, atpe)
- }
- }
- }
- }
- if (!hasError) {
- val atpe = actres.substSym(flatactparams, flatreqparams).instantiateTypeParams(tparams, tvars)
- checkSubType("return type", atpe, reqres)
- }
- if (!hasError) {
- val targs = solvedTypes(tvars, tparams, tparams map varianceInType(actres), false,
- lubDepth(flatactparams map (_.tpe)) max lubDepth(flatreqparams map (_.tpe)))
- val boundsOk = typer.silent(_.infer.checkBounds(ddef, NoPrefix, NoSymbol, tparams, targs, ""))
- boundsOk match {
- case SilentResultValue(true) => ;
- case SilentResultValue(false) | SilentTypeError(_) =>
- val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, targs).bounds)
- compatibilityError("type arguments " + targs.mkString("[", ",", "]") +
- " do not conform to " + tparams.head.owner + "'s type parameter bounds " +
- (tparams map (_.defString)).mkString("[", ",", "]"))
- }
- }
- } catch {
- case ex: NoInstance =>
- compatibilityError(
- "type parameters "+(tparams map (_.defString) mkString ", ")+" cannot be instantiated\n"+
- ex.getMessage)
- }
- }
-
- errors.toList
- }
-
- var actparamss = macroImpl.paramss
- actparamss = transformTypeTagEvidenceParams(actparamss, (param, tparam) => NoSymbol)
- val actres = macroImpl.tpe.finalResultType
- val implicitParams = actparamss.flatten filter (_.isImplicit)
- if (implicitParams.length > 0) {
- // prohibit implicit params on macro implementations
- // we don't have to do this, but it appears to be more clear than allowing them
- reportError(implicitParams.head.pos, "macro implementations cannot have implicit parameters other than AbsTypeTag evidences")
- macroTraceVerbose("macro def failed to satisfy trivial preconditions: ")(macroDef)
- }
-
- if (!hasError) {
- val rettpe = if (!ddef.tpt.isEmpty) typer.typedType(ddef.tpt).tpe else computeMacroDefTypeFromMacroImpl(ddef, macroDef, macroImpl)
- val (reqparamss, reqres) = macroImplSig(macroDef, ddef.tparams, ddef.vparamss, rettpe)
-
- def showMeth(pss: List[List[Symbol]], restpe: Type, abbreviate: Boolean) = {
- var argsPart = (pss map (ps => ps map (_.defString) mkString ("(", ", ", ")"))).mkString
- if (abbreviate) argsPart = argsPart.abbreviateCoreAliases
- var retPart = restpe.toString
- if (abbreviate || ddef.tpt.tpe == null) retPart = retPart.abbreviateCoreAliases
- argsPart + ": " + retPart
- }
- def compatibilityError(addendum: String) =
- reportError(implpos,
- "macro implementation has wrong shape:"+
- "\n required: "+showMeth(reqparamss, reqres, true) +
- "\n found : "+showMeth(actparamss, actres, false)+
- "\n"+addendum)
-
- val errors = checkCompatibility(reqparamss, actparamss, reqres, actres)
- if (errors.nonEmpty) compatibilityError(errors mkString "\n")
- }
+ // Phase II: typecheck the right-hand side of the macro def
+ val typed = typecheckRhs(macroDdef.rhs)
+ typed match {
+ 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()
}
- rhs1
- }
+ // 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 macro impl ("a" stands for "actual")
+ // rXXX (e.g. rparamss) => characteristics of a reference macro impl signature synthesized from the macro def ("r" stands for "reference")
+ val macroImpl = typed.symbol
+ val aparamss = transformTypeTagEvidenceParams(macroImpl.paramss, (param, tparam) => NoSymbol)
+ val aret = macroImpl.tpe.finalResultType
+ val macroDefRet =
+ if (!macroDdef.tpt.isEmpty) typer.typedType(macroDdef.tpt).tpe
+ else computeMacroDefTypeFromMacroImpl(macroDdef, macroImpl)
+ val (rparamss, rret) = macroImplSig(macroDef, macroDdef.tparams, macroDdef.vparamss, macroDefRet)
+
+ val implicitParams = aparamss.flatten filter (_.isImplicit)
+ if (implicitParams.nonEmpty) MacroImplNonTagImplicitParameters(implicitParams)
+ if (aparamss.length != rparamss.length) MacroImplParamssMismatchError()
+
+ val atparams = macroImpl.typeParams
+ val atvars = atparams map freshVar
+ def atpeToRtpe(atpe: Type) = atpe.substSym(aparamss.flatten, rparamss.flatten).instantiateTypeParams(atparams, atvars)
- def computeMacroDefTypeFromMacroImpl(macroDdef: DefDef, macroDef: Symbol, macroImpl: Symbol): Type = {
- // downgrade from metalevel-0 to metalevel-1
- var runtimeType = macroImpl.tpe.finalResultType.dealias match {
- case TypeRef(_, ExprClass, runtimeType :: Nil) => runtimeType
- case _ => AnyTpe
- }
+ try {
+ map2(aparamss, rparamss)((aparams, rparams) => {
+ if (aparams.length < rparams.length) MacroImplMissingParamsError(aparams, rparams)
+ if (rparams.length < aparams.length) MacroImplExtraParamsError(aparams, rparams)
+ })
- // transform type parameters of a macro implementation into type parameters of a macro definition
- runtimeType = runtimeType map {
- case TypeRef(pre, sym, args) =>
- // sym.paramPos is unreliable (see an example in `macroArgs`)
- val tparams = macroImpl.typeParams map (_.deSkolemize)
- val paramPos = tparams indexOf sym.deSkolemize
- val sym1 =
- if (paramPos == -1) sym
- else loadMacroImplBinding(macroDef).targs(paramPos).tpe.typeSymbol
- TypeRef(pre, sym1, args)
- case tpe =>
- tpe
- }
+ // cannot fuse these loops 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)
+ checkMacroImplParamTypeMismatch(stripOffPrefixTypeRefinement(atpeToRtpe(aparam.tpe)), rparam)
+ })
- // as stated in the spec, before being matched to macroimpl, type and value parameters of macrodef
- // undergo a special transformation, sigma, that adapts them to the different metalevel macroimpl lives in
- // as a result, we need to reverse this transformation when inferring macrodef ret from macroimpl ret
- def unsigma(tpe: Type): Type = {
- // unfortunately, we cannot dereference ``paramss'', because we're in the middle of inferring a type for ``macroDef''
-// val defParamss = macroDef.paramss
- val defParamss = mmap(macroDdef.vparamss)(_.symbol)
- var implParamss = macroImpl.paramss
- implParamss = transformTypeTagEvidenceParams(implParamss, (param, tparam) => NoSymbol)
-
- val implCtxParam = if (implParamss.length > 0 && implParamss(0).length > 0) implParamss(0)(0) else null
- def implParamToDefParam(implParam: Symbol): Symbol = {
- val indices = (((implParamss drop 1).zipWithIndex) map { case (implParams, index) => (index, implParams indexOf implParam) } filter (_._2 != -1)).headOption
- val defParam = indices flatMap {
- case (plistIndex, pIndex) =>
- if (defParamss.length <= plistIndex) None
- else if (defParamss(plistIndex).length <= pIndex) None
- else Some(defParamss(plistIndex)(pIndex))
- }
- defParam.orNull
- }
+ checkMacroImplResultTypeMismatch(atpeToRtpe(aret), rret)
- class UnsigmaTypeMap extends TypeMap {
- def apply(tp: Type): Type = tp match {
- case TypeRef(pre, sym, args) =>
- val pre1 = pre match {
- case SingleType(SingleType(SingleType(NoPrefix, param), prefix), value) if param == implCtxParam && prefix == MacroContextPrefix && value == ExprValue =>
- ThisType(macroDef.owner)
- case SingleType(SingleType(NoPrefix, param), value) if implParamToDefParam(param) != null && value == ExprValue =>
- val macroDefParam = implParamToDefParam(param)
- SingleType(NoPrefix, macroDefParam)
- case _ =>
- pre
- }
- val args1 = args map mapOver
- TypeRef(pre1, sym, args1)
- case _ =>
- mapOver(tp)
- }
+ 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)
}
-
- new UnsigmaTypeMap() apply tpe
+ } catch {
+ case ex: NoInstance => MacroImplTparamInstantiationError(atparams, ex)
}
- runtimeType = unsigma(runtimeType)
-
- runtimeType
}
/** Macro classloader that is used to resolve and run macro implementations.
@@ -719,7 +506,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
* 4) Resolves macro implementation within the loaded companion.
*
* @return Requested runtime if macro implementation can be loaded successfully from either of the mirrors,
- * null otherwise.
+ * `null` otherwise.
*/
type MacroRuntime = List[Any] => Any
private val macroRuntimesCache = perRunCaches.newWeakMap[Symbol, MacroRuntime]
@@ -774,9 +561,9 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
/** Calculate the arguments to pass to a macro implementation when expanding the provided tree.
*
* This includes inferring the exact type and instance of the macro context to pass, and also
- * allowing for missing parameter sections in macro implementation (see ``macroImplParamsss'' for more info).
+ * allowing for missing parameter sections in macro implementation (see `macroImplParamsss` for more info).
*
- * @return list of runtime objects to pass to the implementation obtained by ``macroRuntime''
+ * @return list of runtime objects to pass to the implementation obtained by `macroRuntime`
*/
private def macroArgs(typer: Typer, expandee: Tree): Option[List[Any]] = {
val macroDef = expandee.symbol
@@ -832,8 +619,8 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
// val outer2 = new outer1.C[String]
// outer2.foo[Boolean]
//
- // 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
+ // 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 filter (_ != -1) map (paramPos => {
val targ = binding.targs(paramPos).tpe.typeSymbol
@@ -877,14 +664,14 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
}
/** Keeps track of macros in-flight.
- * See more informations in comments to ``openMacros'' in ``scala.reflect.macros.Context''.
+ * See more informations in comments to `openMacros` in `scala.reflect.macros.Context`.
*/
var openMacros = List[MacroContext]()
def enclosingMacroPosition = openMacros map (_.macroApplication.pos) find (_ ne NoPosition) getOrElse NoPosition
/** Performs macro expansion:
- * 1) Checks whether the expansion needs to be delayed (see ``mustDelayMacroExpansion'')
- * 2) Loads macro implementation using ``macroMirror''
+ * 1) Checks whether the expansion needs to be delayed (see `mustDelayMacroExpansion`)
+ * 2) Loads macro implementation using `macroMirror`
* 3) Synthesizes invocation arguments for the macro implementation
* 4) Checks that the result is a tree bound to this universe
* 5) Typechecks the result against the return type of the macro definition
@@ -978,7 +765,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
Failure(expandee)
}
- /** Does the same as ``macroExpand'', but without typechecking the expansion
+ /** Does the same as `macroExpand`, but without typechecking the expansion
* Meant for internal use within the macro infrastructure, don't use it elsewhere.
*/
private def macroExpand1(typer: Typer, expandee: Tree): MacroExpansionResult =
@@ -1160,7 +947,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
try {
// [Eugene] is there a better way?
// [Paul] See Exceptional.scala and Origins.scala.
- val relevancyThreshold = realex.getStackTrace().indexWhere(este => este.getMethodName == "macroExpand1")
+ val relevancyThreshold = realex.getStackTrace().indexWhere(_.getMethodName endsWith "macroExpand1")
if (relevancyThreshold == -1) None
else {
var relevantElements = realex.getStackTrace().take(relevancyThreshold + 1)
@@ -1236,7 +1023,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
/** Performs macro expansion on all subtrees of a given tree.
* Innermost macros are expanded first, outermost macros are expanded last.
- * See the documentation for ``macroExpand'' for more information.
+ * See the documentation for `macroExpand` for more information.
*/
def macroExpandAll(typer: Typer, expandee: Tree): Tree =
new Transformer {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index b056b16e32..7df2f323e1 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -5452,7 +5452,7 @@ trait Typers extends Modes with Adaptations with Tags {
val isMacroBodyOkay = !tree.symbol.isErroneous && !(tree1 exists (_.isErroneous))
val shouldInheritMacroImplReturnType = ddef.tpt.isEmpty
- if (isMacroBodyOkay && shouldInheritMacroImplReturnType) computeMacroDefTypeFromMacroImpl(ddef, tree.symbol, tree1.symbol) else AnyClass.tpe
+ if (isMacroBodyOkay && shouldInheritMacroImplReturnType) computeMacroDefTypeFromMacroImpl(ddef, tree1.symbol) else AnyClass.tpe
}
def transformedOr(tree: Tree, op: => Tree): Tree = transformed.get(tree) match {
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index 7fa098a328..a1f44ace6e 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -380,6 +380,7 @@ trait Definitions extends api.StandardDefinitions {
def isScalaRepeatedParamType(tp: Type) = tp.typeSymbol == RepeatedParamClass
def isJavaRepeatedParamType(tp: Type) = tp.typeSymbol == JavaRepeatedParamClass
def isRepeatedParamType(tp: Type) = isScalaRepeatedParamType(tp) || isJavaRepeatedParamType(tp)
+ def isRepeated(param: Symbol) = isRepeatedParamType(param.tpe)
def isCastSymbol(sym: Symbol) = sym == Any_asInstanceOf || sym == Object_asInstanceOf
def isJavaVarArgsMethod(m: Symbol) = m.isMethod && isJavaVarArgs(m.info.params)
diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala
index 5e3f0c2aee..3a6acdcc2b 100644
--- a/src/reflect/scala/reflect/internal/TreeInfo.scala
+++ b/src/reflect/scala/reflect/internal/TreeInfo.scala
@@ -17,7 +17,7 @@ abstract class TreeInfo {
val global: SymbolTable
import global._
- import definitions.{ isVarArgsList, isCastSymbol, ThrowableClass, TupleClass }
+ import definitions.{ isVarArgsList, isCastSymbol, ThrowableClass, TupleClass, MacroContextClass, MacroContextPrefixType }
/* Does not seem to be used. Not sure what it does anyway.
def isOwnerDefinition(tree: Tree): Boolean = tree match {
@@ -589,4 +589,23 @@ abstract class TreeInfo {
object DynamicUpdate extends DynamicApplicationExtractor(_ == nme.updateDynamic)
object DynamicApplication extends DynamicApplicationExtractor(isApplyDynamicName)
object DynamicApplicationNamed extends DynamicApplicationExtractor(_ == nme.applyDynamicNamed)
+
+ object MacroImplReference {
+ private def refPart(tree: Tree): Tree = tree match {
+ case TypeApply(fun, _) => refPart(fun)
+ case ref: RefTree => ref
+ case _ => EmptyTree
+ }
+
+ def unapply(tree: Tree) = refPart(tree) match {
+ case ref: RefTree => Some((ref.qualifier.symbol, ref.symbol, typeArguments(tree)))
+ case _ => None
+ }
+ }
+
+ def stripOffPrefixTypeRefinement(tpe: Type): Type =
+ tpe.dealias match {
+ case RefinedType(List(tpe), Scope(sym)) if tpe == MacroContextClass.tpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe
+ case _ => tpe
+ }
}
diff --git a/test/files/neg/macro-invalidshape-a.check b/test/files/neg/macro-invalidshape-a.check
index 246b5c3226..098ec35a00 100644
--- a/test/files/neg/macro-invalidshape-a.check
+++ b/test/files/neg/macro-invalidshape-a.check
@@ -1,6 +1,5 @@
Macros_Test_2.scala:2: error: macro body has wrong shape:
- required: macro <reference to implementation object>.<implementation method name>
- or : macro <implementation method name>
+ required: macro [<implementation object>].<method name>[[<type args>]]
def foo(x: Any) = macro 2
^
one error found
diff --git a/test/files/neg/macro-invalidshape-b.check b/test/files/neg/macro-invalidshape-b.check
index 59701d023b..297ff69199 100644
--- a/test/files/neg/macro-invalidshape-b.check
+++ b/test/files/neg/macro-invalidshape-b.check
@@ -1,6 +1,5 @@
Macros_Test_2.scala:2: error: macro body has wrong shape:
- required: macro <reference to implementation object>.<implementation method name>
- or : macro <implementation method name>
+ required: macro [<implementation object>].<method name>[[<type args>]]
def foo(x: Any) = macro Impls.foo(null)(null)
^
one error found
diff --git a/test/files/neg/macro-invalidshape-c.check b/test/files/neg/macro-invalidshape-c.check
index 84d8c35222..6513df166e 100644
--- a/test/files/neg/macro-invalidshape-c.check
+++ b/test/files/neg/macro-invalidshape-c.check
@@ -1,6 +1,9 @@
-Macros_Test_2.scala:2: error: macro body has wrong shape:
- required: macro <reference to implementation object>.<implementation method name>
- or : macro <implementation method name>
+Macros_Test_2.scala:2: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
def foo(x: Any) = macro {2; Impls.foo}
- ^
+ ^
+Macros_Test_2.scala:2: error: missing arguments for method foo in object Impls;
+follow this method with `_' if you want to treat it as a partially applied function
+ def foo(x: Any) = macro {2; Impls.foo}
+ ^
+one warning found
one error found
diff --git a/test/files/neg/macro-invalidsig-context-bounds.check b/test/files/neg/macro-invalidsig-context-bounds.check
index 6c9482e537..5d42ebc515 100644
--- a/test/files/neg/macro-invalidsig-context-bounds.check
+++ b/test/files/neg/macro-invalidsig-context-bounds.check
@@ -1,4 +1,7 @@
-Impls_1.scala:5: error: macro implementations cannot have implicit parameters other than AbsTypeTag evidences
- def foo[U: c.AbsTypeTag: Numeric](c: Ctx) = {
- ^
-one error found
+Macros_Test_1.scala:2: error: macro implementation has wrong shape:
+ required: (c: scala.reflect.macros.Context): c.Expr[Any]
+ found : (c: scala.reflect.macros.Context)(implicit evidence$2: Numeric[U]): c.universe.Literal
+macro implementations cannot have implicit parameters other than AbsTypeTag evidences
+ def foo[U] = macro Impls.foo[U]
+ ^
+one error found
diff --git a/test/files/neg/macro-invalidsig-implicit-params.check b/test/files/neg/macro-invalidsig-implicit-params.check
index 98b3167b7a..62156770e4 100644
--- a/test/files/neg/macro-invalidsig-implicit-params.check
+++ b/test/files/neg/macro-invalidsig-implicit-params.check
@@ -1,4 +1,7 @@
-Impls_Macros_1.scala:5: error: macro implementations cannot have implicit parameters other than AbsTypeTag evidences
- def foo_targs[T, U: c.AbsTypeTag](c: Ctx)(implicit x: c.Expr[Int]) = {
- ^
-one error found
+Impls_Macros_1.scala:18: error: macro implementation has wrong shape:
+ required: (c: scala.reflect.macros.Context)(x: c.Expr[Int]): c.Expr[Unit]
+ found : (c: scala.reflect.macros.Context)(implicit x: c.Expr[Int]): c.Expr[Unit]
+macro implementations cannot have implicit parameters other than AbsTypeTag evidences
+ def foo_targs[U](x: Int) = macro Impls.foo_targs[T, U]
+ ^
+one error found
diff --git a/test/files/neg/macro-invalidsig-params-namemismatch.check b/test/files/neg/macro-invalidsig-params-namemismatch.check
index f2639d9350..00d781a2ac 100644
--- a/test/files/neg/macro-invalidsig-params-namemismatch.check
+++ b/test/files/neg/macro-invalidsig-params-namemismatch.check
@@ -2,7 +2,6 @@ Impls_Macros_1.scala:8: error: macro implementation has wrong shape:
required: (c: scala.reflect.macros.Context)(x: c.Expr[Int], y: c.Expr[Int]): c.Expr[Any]
found : (c: scala.reflect.macros.Context)(y: c.Expr[Int], x: c.Expr[Int]): Nothing
parameter names differ: x != y
-parameter names differ: y != x
def foo(x: Int, y: Int) = macro Impls.foo
^
one error found
diff --git a/test/files/neg/macro-invalidsig-tparams-badtype.check b/test/files/neg/macro-invalidsig-tparams-badtype.check
index e5e8366ba4..20c58ff2bb 100644
--- a/test/files/neg/macro-invalidsig-tparams-badtype.check
+++ b/test/files/neg/macro-invalidsig-tparams-badtype.check
@@ -3,5 +3,5 @@ Macros_Test_2.scala:2: error: macro implementation has wrong shape:
found : (c: scala.reflect.macros.Context)(U: c.universe.Type): Nothing
number of parameter sections differ
def foo[U] = macro Impls.foo[U]
- ^
+ ^
one error found
diff --git a/test/files/neg/macro-invalidsig-tparams-notparams-a.check b/test/files/neg/macro-invalidsig-tparams-notparams-a.check
index 5b4ef42ea5..5aee62b9e5 100644
--- a/test/files/neg/macro-invalidsig-tparams-notparams-a.check
+++ b/test/files/neg/macro-invalidsig-tparams-notparams-a.check
@@ -1,4 +1,4 @@
-Macros_Test_2.scala:2: error: macro implementation reference needs type arguments
+Macros_Test_2.scala:2: error: wrong number of type parameters for method foo: [U](c: scala.reflect.macros.Context)(implicit evidence$1: c.AbsTypeTag[U])Nothing
def foo = macro Impls.foo
^
one error found
diff --git a/test/files/neg/macro-invalidsig-tparams-notparams-b.check b/test/files/neg/macro-invalidsig-tparams-notparams-b.check
index 261e3b8293..e8de8a4c6b 100644
--- a/test/files/neg/macro-invalidsig-tparams-notparams-b.check
+++ b/test/files/neg/macro-invalidsig-tparams-notparams-b.check
@@ -1,4 +1,4 @@
-Macros_Test_2.scala:3: error: macro implementation reference needs type arguments
+Macros_Test_2.scala:3: error: wrong number of type parameters for method foo: [T, U, V](c: scala.reflect.macros.Context)(implicit evidence$1: c.AbsTypeTag[T], implicit evidence$2: c.AbsTypeTag[U], implicit V: c.AbsTypeTag[V])c.Expr[Unit]
def foo[V] = macro Impls.foo
^
one error found
diff --git a/test/files/run/macro-def-path-dependent-d.check b/test/files/run/macro-def-path-dependent-d1.check
index 1ea14b4e20..1ea14b4e20 100644
--- a/test/files/run/macro-def-path-dependent-d.check
+++ b/test/files/run/macro-def-path-dependent-d1.check
diff --git a/test/files/run/macro-def-path-dependent-d.flags b/test/files/run/macro-def-path-dependent-d1.flags
index cd66464f2f..cd66464f2f 100644
--- a/test/files/run/macro-def-path-dependent-d.flags
+++ b/test/files/run/macro-def-path-dependent-d1.flags
diff --git a/test/files/run/macro-def-path-dependent-d/Impls_Macros_1.scala b/test/files/run/macro-def-path-dependent-d1/Impls_Macros_1.scala
index 2daf6fc3fb..2daf6fc3fb 100644
--- a/test/files/run/macro-def-path-dependent-d/Impls_Macros_1.scala
+++ b/test/files/run/macro-def-path-dependent-d1/Impls_Macros_1.scala
diff --git a/test/files/run/macro-def-path-dependent-d/Test_2.scala b/test/files/run/macro-def-path-dependent-d1/Test_2.scala
index 7dffc5107d..7dffc5107d 100644
--- a/test/files/run/macro-def-path-dependent-d/Test_2.scala
+++ b/test/files/run/macro-def-path-dependent-d1/Test_2.scala
diff --git a/test/files/run/macro-def-path-dependent-d2.check b/test/files/run/macro-def-path-dependent-d2.check
new file mode 100644
index 0000000000..1ea14b4e20
--- /dev/null
+++ b/test/files/run/macro-def-path-dependent-d2.check
@@ -0,0 +1 @@
+it works
diff --git a/test/files/run/macro-def-path-dependent-d2.flags b/test/files/run/macro-def-path-dependent-d2.flags
new file mode 100644
index 0000000000..cd66464f2f
--- /dev/null
+++ b/test/files/run/macro-def-path-dependent-d2.flags
@@ -0,0 +1 @@
+-language:experimental.macros \ No newline at end of file
diff --git a/test/files/run/macro-def-path-dependent-d2/Impls_1.scala b/test/files/run/macro-def-path-dependent-d2/Impls_1.scala
new file mode 100644
index 0000000000..1cda64e43e
--- /dev/null
+++ b/test/files/run/macro-def-path-dependent-d2/Impls_1.scala
@@ -0,0 +1,7 @@
+import scala.reflect.runtime.universe._
+import scala.reflect.macros.Context
+import scala.reflect.api.Universe
+
+object Impls {
+ def materializeTypeTag_impl[T: c.AbsTypeTag](c: Context)(u: c.Expr[Universe])(e: c.Expr[T]): c.Expr[u.value.TypeTag[T]] = ???
+} \ No newline at end of file
diff --git a/test/files/run/macro-def-path-dependent-d2/Macros_2.scala b/test/files/run/macro-def-path-dependent-d2/Macros_2.scala
new file mode 100644
index 0000000000..65ce4d8bd2
--- /dev/null
+++ b/test/files/run/macro-def-path-dependent-d2/Macros_2.scala
@@ -0,0 +1,7 @@
+import scala.reflect.runtime.universe._
+import scala.reflect.macros.Context
+import scala.reflect.api.Universe
+
+object Macros {
+ def materializeTypeTag[T](u: Universe)(e: T) = macro Impls.materializeTypeTag_impl[T]
+} \ No newline at end of file
diff --git a/test/files/run/macro-def-path-dependent-d2/Test_3.scala b/test/files/run/macro-def-path-dependent-d2/Test_3.scala
new file mode 100644
index 0000000000..7dffc5107d
--- /dev/null
+++ b/test/files/run/macro-def-path-dependent-d2/Test_3.scala
@@ -0,0 +1,3 @@
+object Test extends App {
+ println("it works")
+} \ No newline at end of file