diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2012-08-17 09:04:41 -0700 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2012-08-17 09:04:41 -0700 |
commit | 7fc860963a4f76cb18e44c20f2bdfb49641033f3 (patch) | |
tree | fe3518939e0996115701c6c20eaaf1d4406797b3 /src | |
parent | 722fc004732aca0b1d6b9143b960e5a8268c31a0 (diff) | |
parent | ddf27a1e0e4b9eae521651704e74298c33c9af6c (diff) | |
download | scala-7fc860963a4f76cb18e44c20f2bdfb49641033f3.tar.gz scala-7fc860963a4f76cb18e44c20f2bdfb49641033f3.tar.bz2 scala-7fc860963a4f76cb18e44c20f2bdfb49641033f3.zip |
Merge pull request #1151 from scalamacros/topic/cleanup
more cleanup in Macros.scala
Diffstat (limited to 'src')
5 files changed, 347 insertions, 402 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 + } } |