diff options
Diffstat (limited to 'src/compiler')
33 files changed, 1008 insertions, 637 deletions
diff --git a/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala b/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala new file mode 100644 index 0000000000..32c6da8007 --- /dev/null +++ b/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala @@ -0,0 +1,33 @@ +package scala.reflect.macros +package compiler + +import scala.tools.nsc.Global +import scala.reflect.macros.contexts.Context + +abstract class DefaultMacroCompiler extends Resolvers + with Validators + with Errors { + val global: Global + import global._ + + val typer: global.analyzer.Typer + private implicit val context0 = typer.context + val context = typer.context + + val macroDdef: DefDef + lazy val macroDef = macroDdef.symbol + + private case class MacroImplResolutionException(pos: Position, msg: String) extends Exception + def abort(pos: Position, msg: String) = throw MacroImplResolutionException(pos, msg) + + def resolveMacroImpl: Tree = { + try { + validateMacroImplRef() + macroImplRef + } catch { + case MacroImplResolutionException(pos, msg) => + context.error(pos, msg) + EmptyTree + } + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/macros/compiler/Errors.scala b/src/compiler/scala/reflect/macros/compiler/Errors.scala new file mode 100644 index 0000000000..dd3142127e --- /dev/null +++ b/src/compiler/scala/reflect/macros/compiler/Errors.scala @@ -0,0 +1,113 @@ +package scala.reflect.macros +package compiler + +import scala.compat.Platform.EOL +import scala.reflect.macros.util.Traces + +trait Errors extends Traces { + self: DefaultMacroCompiler => + + import global._ + import analyzer._ + import definitions._ + import typer.TyperErrorGen._ + import typer.infer.InferErrorGen._ + def globalSettings = global.settings + + // sanity check errors + + private def implRefError(message: String) = abort(macroDdef.pos, message) + + def MacroImplReferenceWrongShapeError() = implRefError( + "macro implementation reference has wrong shape. required:\n"+ + "macro [<static object>].<method name>[[<type args>]] or\n" + + "macro [<macro bundle>].<method name>[[<type args>]]") + + def MacroImplNotPublicError() = implRefError("macro implementation must be public") + + def MacroImplOverloadedError() = implRefError("macro implementation cannot be overloaded") + + def MacroImplWrongNumberOfTypeArgumentsError() = implRefError(TypedApplyWrongNumberOfTpeParametersErrorMessage(macroImplRef)) + + // compatibility errors + + // 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("WeakTypeTag", "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 checkConforms(slot: String, rtpe: Type, atpe: Type) = { + val verbose = macroDebugVerbose || settings.explaintypes.value + + def check(rtpe: Type, atpe: Type): Boolean = { + def success() = { if (verbose) println(rtpe + " <: " + atpe + "?" + EOL + "true"); true } + (rtpe, atpe) match { + case _ if rtpe eq atpe => success() + case (TypeRef(_, RepeatedParamClass, rtpe :: Nil), TypeRef(_, RepeatedParamClass, atpe :: Nil)) => check(rtpe, atpe) + case (ExprClassOf(_), TreeType()) => success() + case (TreeType(), ExprClassOf(_)) => success() + case _ => rtpe <:< atpe + } + } + + val ok = + if (verbose) withTypesExplained(check(rtpe, atpe)) + else check(rtpe, atpe) + if (!ok) { + if (!macroDebugVerbose) + explainTypes(rtpe, atpe) + compatibilityError("type mismatch for %s: %s does not conform to %s".format(slot, abbreviateCoreAliases(rtpe.toString), abbreviateCoreAliases(atpe.toString))) + } + } + + 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) + + def MacroImplNonTagImplicitParameters(params: List[Symbol]) = compatibilityError("macro implementations cannot have implicit parameters other than WeakTypeTag 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) = checkConforms("parameter " + rparam.name, rparam.tpe, atpe) + + def checkMacroImplResultTypeMismatch(atpe: Type, rret: Type) = checkConforms("return type", atpe, rret) + + def MacroImplParamNameMismatchError(aparam: Symbol, rparam: Symbol) = compatibilityError("parameter names differ: " + rparam.name + " != " + aparam.name) + + def MacroImplVarargMismatchError(aparam: Symbol, rparam: Symbol) = { + def fail(paramName: Name) = compatibilityError("types incompatible for parameter " + paramName + ": corresponding is not a vararg parameter") + if (isRepeated(rparam) && !isRepeated(aparam)) fail(rparam.name) + if (!isRepeated(rparam) && isRepeated(aparam)) fail(aparam.name) + } + + def MacroImplTargMismatchError(atargs: List[Type], atparams: List[Symbol]) = + compatibilityError(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) +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/macros/compiler/Resolvers.scala b/src/compiler/scala/reflect/macros/compiler/Resolvers.scala new file mode 100644 index 0000000000..3025eb52a1 --- /dev/null +++ b/src/compiler/scala/reflect/macros/compiler/Resolvers.scala @@ -0,0 +1,91 @@ +package scala.reflect.macros +package compiler + +import scala.reflect.internal.Flags._ +import scala.reflect.macros.TypecheckException + +trait Resolvers { + self: DefaultMacroCompiler => + + import global._ + import analyzer._ + import definitions._ + import treeInfo._ + import gen._ + + /** Determines the type of context implied by the macro def. + */ + val ctxTpe = MacroContextClass.tpe + + /** Resolves a macro impl reference provided in the right-hand side of the given macro definition. + * + * Acceptable shapes of the right-hand side: + * 1) [<static object>].<method name>[[<type args>]] // vanilla macro def + * 2) [<macro bundle>].<method name>[[<type args>]] // shiny new macro bundle + * + * Produces a tree, which represents a reference to a macro implementation if everything goes well, + * otherwise reports found errors and returns EmptyTree. The resulting tree should have the following format: + * + * qualifier.method[targs] + * + * Qualifier here might be omitted (local macro defs), be a static object (vanilla macro defs) + * or be a dummy instance of a macro bundle (e.g. new MyMacro(???).expand). + */ + lazy val macroImplRef: Tree = { + val (maybeBundleRef, methName, targs) = macroDdef.rhs match { + case Applied(methRef @ Select(bundleRef @ RefTree(qual, bundleName), methName), targs, Nil) => + (RefTree(qual, bundleName.toTypeName), methName, targs) + case Applied(Ident(methName), targs, Nil) => + (Ident(context.owner.enclClass), methName, targs) + case _ => + (EmptyTree, TermName(""), Nil) + } + + val untypedImplRef = typer.silent(_.typedType(maybeBundleRef)) match { + case SilentResultValue(result) if isMacroBundleType(result.tpe) => + val bundleClass = result.tpe.typeSymbol + if (!bundleClass.owner.isPackageClass) abort(macroDef.pos, "macro bundles can only be defined as top-level classes or traits") + + // synthesize the invoker, i.e. given a top-level `trait Foo extends Macro { def expand = ... } ` + // create a top-level definition `class FooInvoker(val c: Context) extends Foo` in MACRO_INVOKER_PACKAGE + val invokerPid = gen.mkUnattributedRef(nme.MACRO_INVOKER_PACKAGE) + val invokerName = TypeName(bundleClass.fullName.split('.').map(_.capitalize).mkString("") + nme.MACRO_INVOKER_SUFFIX) + val invokerFullName = TypeName(s"$invokerPid.$invokerName") + val existingInvoker = rootMirror.getClassIfDefined(invokerFullName) + if (!currentRun.compiles(existingInvoker)) { + def mkContextValDef(flags: Long) = ValDef(Modifiers(flags), nme.c, TypeTree(ctxTpe), EmptyTree) + val contextField = mkContextValDef(PARAMACCESSOR) + val contextParam = mkContextValDef(PARAM | PARAMACCESSOR) + val invokerCtor = DefDef(Modifiers(), nme.CONSTRUCTOR, Nil, List(List(contextParam)), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(())))) + val invoker = atPos(bundleClass.pos)(ClassDef(NoMods, invokerName, Nil, Template(List(Ident(bundleClass)), emptyValDef, List(contextField, invokerCtor)))) + currentRun.compileLate(PackageDef(invokerPid, List(invoker))) + } + + // synthesize the macro impl reference, which is going to look like: + // `new Foo$invoker(???).expand` plus the optional type arguments + val instanceOfInvoker = New(Select(invokerPid, invokerName), List(List(Select(scalaDot(nme.Predef), nme.???)))) + gen.mkTypeApply(Select(instanceOfInvoker, methName), targs) + case _ => + macroDdef.rhs + } + + val typedImplRef = typer.silent(_.typed(markMacroImplRef(untypedImplRef)), reportAmbiguousErrors = false) + typedImplRef match { + case SilentResultValue(success) => success + case SilentTypeError(err) => abort(err.errPos, err.errMsg) + } + } + + // FIXME: cannot write this concisely because of SI-7507 + // lazy val (isImplBundle, macroImplOwner, macroImpl, macroImplTargs) = + private lazy val dissectedMacroImplRef = + macroImplRef match { + case MacroImplReference(isBundle, owner, meth, targs) => (isBundle, owner, meth, targs) + case _ => MacroImplReferenceWrongShapeError() + } + lazy val isImplBundle = dissectedMacroImplRef._1 + lazy val isImplMethod = !isImplBundle + lazy val macroImplOwner = dissectedMacroImplRef._2 + lazy val macroImpl = dissectedMacroImplRef._3 + lazy val targs = dissectedMacroImplRef._4 +} diff --git a/src/compiler/scala/reflect/macros/compiler/Validators.scala b/src/compiler/scala/reflect/macros/compiler/Validators.scala new file mode 100644 index 0000000000..60cfc94a23 --- /dev/null +++ b/src/compiler/scala/reflect/macros/compiler/Validators.scala @@ -0,0 +1,193 @@ +package scala.reflect.macros +package compiler + +import java.util.UUID.randomUUID +import scala.reflect.internal.Flags._ +import scala.reflect.macros.TypecheckException + +trait Validators { + self: DefaultMacroCompiler => + + import global._ + import analyzer._ + import definitions._ + import treeInfo._ + import typer.infer._ + + def validateMacroImplRef() = { + sanityCheck() + if (macroImpl != Predef_???) checkMacroDefMacroImplCorrespondence() + } + + private def sanityCheck() = { + if (!macroImpl.isMethod) MacroImplReferenceWrongShapeError() + if (!macroImpl.isPublic) MacroImplNotPublicError() + if (macroImpl.isOverloaded) MacroImplOverloadedError() + if (macroImpl.typeParams.length != targs.length) MacroImplWrongNumberOfTypeArgumentsError() + val declaredInStaticObject = isImplMethod && (macroImplOwner.isStaticOwner || macroImplOwner.moduleClass.isStaticOwner) + val declaredInTopLevelClass = isImplBundle && macroImplOwner.owner.isPackageClass + if (!declaredInStaticObject && !declaredInTopLevelClass) MacroImplReferenceWrongShapeError() + } + + private def checkMacroDefMacroImplCorrespondence() = { + val atvars = atparams map freshVar + def atpeToRtpe(atpe: Type) = atpe.substSym(aparamss.flatten, rparamss.flatten).instantiateTypeParams(atparams, atvars) + + // we only check strict correspondence between value parameterss + // type parameters of macro defs and macro impls don't have to coincide with each other + val implicitParams = aparamss.flatten filter (_.isImplicit) + if (implicitParams.nonEmpty) MacroImplNonTagImplicitParameters(implicitParams) + if (aparamss.length != rparamss.length) MacroImplParamssMismatchError() + map2(aparamss, rparamss)((aparams, rparams) => { + if (aparams.length < rparams.length) MacroImplMissingParamsError(aparams, rparams) + if (rparams.length < aparams.length) MacroImplExtraParamsError(aparams, rparams) + }) + + try { + // cannot fuse this map2 and the map2 above because if aparamss.flatten != rparamss.flatten + // then `atpeToRtpe` is going to fail with an unsound substitution + map2(aparamss.flatten, rparamss.flatten)((aparam, rparam) => { + if (aparam.name != rparam.name && !rparam.isSynthetic) MacroImplParamNameMismatchError(aparam, rparam) + if (isRepeated(aparam) ^ isRepeated(rparam)) MacroImplVarargMismatchError(aparam, rparam) + val aparamtpe = aparam.tpe.dealias match { + case RefinedType(List(tpe), Scope(sym)) if tpe =:= ctxTpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe + case tpe => tpe + } + checkMacroImplParamTypeMismatch(atpeToRtpe(aparamtpe), rparam) + }) + + checkMacroImplResultTypeMismatch(atpeToRtpe(aret), rret) + + val maxLubDepth = lubDepth(aparamss.flatten map (_.tpe)) max lubDepth(rparamss.flatten map (_.tpe)) + val atargs = solvedTypes(atvars, atparams, atparams map varianceInType(aret), upper = false, depth = maxLubDepth) + val boundsOk = typer.silent(_.infer.checkBounds(macroDdef, NoPrefix, NoSymbol, atparams, atargs, "")) + boundsOk match { + case SilentResultValue(true) => // do nothing, success + case SilentResultValue(false) | SilentTypeError(_) => MacroImplTargMismatchError(atargs, atparams) + } + } catch { + case ex: NoInstance => MacroImplTparamInstantiationError(atparams, ex) + } + } + + // aXXX (e.g. aparamss) => characteristics of the actual macro impl signature extracted from the macro impl ("a" stands for "actual") + // rXXX (e.g. rparamss) => characteristics of the reference macro impl signature synthesized from the macro def ("r" stands for "reference") + // FIXME: cannot write this concisely because of SI-7507 + //lazy val MacroImplSig(atparams, aparamss, aret) = macroImplSig + //lazy val MacroImplSig(_, rparamss, rret) = referenceMacroImplSig + lazy val atparams = macroImplSig.tparams + lazy val aparamss = macroImplSig.paramss + lazy val aret = macroImplSig.ret + lazy val rparamss = referenceMacroImplSig.paramss + lazy val rret = referenceMacroImplSig.ret + + // Technically this can be just an alias to MethodType, but promoting it to a first-class entity + // provides better encapsulation and convenient syntax for pattern matching. + private case class MacroImplSig(tparams: List[Symbol], paramss: List[List[Symbol]], ret: Type) + + /** An actual macro implementation signature extracted from a macro implementation method. + * + * For the following macro impl: + * def fooBar[T: c.WeakTypeTag] + * (c: scala.reflect.macros.Context) + * (xs: c.Expr[List[T]]) + * : c.Expr[T] = ... + * + * This function will return: + * (c: scala.reflect.macros.Context)(xs: c.Expr[List[T]])c.Expr[T] + * + * Note that type tag evidence parameters are not included into the result. + * Type tag context bounds for macro impl tparams are optional. + * Therefore compatibility checks ignore such parameters, and we don't need to bother about them here. + * + * This method cannot be reduced to just macroImpl.info, because macro implementations might + * come in different shapes. If the implementation is an apply method of a Macro-compatible object, + * then it won't have (c: Context) in its parameters, but will rather refer to Macro.c. + * + * @param macroImpl The macro implementation symbol + */ + private lazy val macroImplSig: MacroImplSig = { + val tparams = macroImpl.typeParams + val paramss = transformTypeTagEvidenceParams(macroImplRef, (param, tparam) => NoSymbol) + val ret = macroImpl.info.finalResultType + MacroImplSig(tparams, paramss, ret) + } + + /** A reference macro implementation signature extracted from a given macro definition. + * + * For the following macro def: + * def foo[T](xs: List[T]): T = macro fooBar + * + * This function will return: + * (c: scala.reflect.macros.Context)(xs: c.Expr[List[T]])c.Expr[T] + * + * Note that type tag evidence parameters are not included into the result. + * Type tag context bounds for macro impl tparams are optional. + * Therefore compatibility checks ignore such parameters, and we don't need to bother about them here. + * + * Also note that we need a DefDef, not the corresponding MethodSymbol, because that symbol would be of no use for us. + * Macro signatures are verified when typechecking macro defs, which means that at that moment inspecting macroDef.info + * means asking for cyclic reference errors. + * + * We need macro implementation symbol as well, because the return type of the macro definition might be omitted, + * and in that case we'd need to infer it from the return type of the macro implementation. Luckily for us, we can + * use that symbol without a risk of running into cycles. + * + * @param typer Typechecker of `macroDdef` + * @param macroDdef The macro definition tree + * @param macroImpl The macro implementation symbol + */ + private lazy val referenceMacroImplSig: MacroImplSig = { + // had to move method's body to an object because of the recursive dependencies between sigma and param + object SigGenerator { + val cache = scala.collection.mutable.Map[Symbol, Symbol]() + val ctxPrefix = + if (isImplMethod) singleType(NoPrefix, makeParam(nme.macroContext, macroDdef.pos, ctxTpe, SYNTHETIC)) + else singleType(ThisType(macroImpl.owner), macroImpl.owner.tpe.member(nme.c)) + var paramss = + if (isImplMethod) List(ctxPrefix.termSymbol) :: mmap(macroDdef.vparamss)(param) + else mmap(macroDdef.vparamss)(param) + val macroDefRet = + if (!macroDdef.tpt.isEmpty) typer.typedType(macroDdef.tpt).tpe + else computeMacroDefTypeFromMacroImplRef(macroDdef, macroImplRef) + val implReturnType = sigma(increaseMetalevel(ctxPrefix, macroDefRet)) + + object SigmaTypeMap extends TypeMap { + def mapPrefix(pre: Type) = pre match { + case ThisType(sym) if sym == macroDef.owner => + singleType(singleType(ctxPrefix, MacroContextPrefix), ExprValue) + case SingleType(NoPrefix, sym) => + mfind(macroDdef.vparamss)(_.symbol == sym).fold(pre)(p => singleType(singleType(NoPrefix, param(p)), ExprValue)) + case _ => + mapOver(pre) + } + def apply(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) => + val pre1 = mapPrefix(pre) + val args1 = mapOverArgs(args, sym.typeParams) + if ((pre eq pre1) && (args eq args1)) tp + else typeRef(pre1, sym, args1) + case _ => + mapOver(tp) + } + } + def sigma(tpe: Type): Type = SigmaTypeMap(tpe) + + def makeParam(name: Name, pos: Position, tpe: Type, flags: Long) = + macroDef.newValueParameter(name.toTermName, pos, flags) setInfo tpe + def param(tree: Tree): Symbol = ( + cache.getOrElseUpdate(tree.symbol, { + val sym = tree.symbol + assert(sym.isTerm, s"sym = $sym, tree = $tree") + makeParam(sym.name, sym.pos, sigma(increaseMetalevel(ctxPrefix, sym.tpe)), sym.flags) + }) + ) + } + + import SigGenerator._ + macroLogVerbose(s"generating macroImplSigs for: $macroDdef") + val result = MacroImplSig(macroDdef.tparams map (_.symbol), paramss, implReturnType) + macroLogVerbose(s"result is: $result") + result + } +} diff --git a/src/compiler/scala/reflect/macros/runtime/Aliases.scala b/src/compiler/scala/reflect/macros/contexts/Aliases.scala index 1c6703aeee..cc64d97d85 100644 --- a/src/compiler/scala/reflect/macros/runtime/Aliases.scala +++ b/src/compiler/scala/reflect/macros/contexts/Aliases.scala @@ -1,5 +1,5 @@ package scala.reflect.macros -package runtime +package contexts trait Aliases { self: Context => diff --git a/src/compiler/scala/reflect/macros/contexts/Context.scala b/src/compiler/scala/reflect/macros/contexts/Context.scala new file mode 100644 index 0000000000..bd1d7d5248 --- /dev/null +++ b/src/compiler/scala/reflect/macros/contexts/Context.scala @@ -0,0 +1,29 @@ +package scala.reflect.macros +package contexts + +import scala.tools.nsc.Global + +abstract class Context extends scala.reflect.macros.Context + with Aliases + with Enclosures + with Names + with Reifiers + with FrontEnds + with Infrastructure + with Typers + with Parsers + with Evals + with ExprUtils + with Synthetics + with Traces { + + val universe: Global + + val mirror: universe.Mirror = universe.rootMirror + + val callsiteTyper: universe.analyzer.Typer + + val prefix: Expr[PrefixType] + + val expandee: Tree +} diff --git a/src/compiler/scala/reflect/macros/runtime/Enclosures.scala b/src/compiler/scala/reflect/macros/contexts/Enclosures.scala index f3f92550de..bb88c8d5e1 100644 --- a/src/compiler/scala/reflect/macros/runtime/Enclosures.scala +++ b/src/compiler/scala/reflect/macros/contexts/Enclosures.scala @@ -1,5 +1,5 @@ package scala.reflect.macros -package runtime +package contexts import scala.reflect.{ClassTag, classTag} diff --git a/src/compiler/scala/reflect/macros/runtime/Evals.scala b/src/compiler/scala/reflect/macros/contexts/Evals.scala index 1f7b5f2ff1..84928ddf86 100644 --- a/src/compiler/scala/reflect/macros/runtime/Evals.scala +++ b/src/compiler/scala/reflect/macros/contexts/Evals.scala @@ -1,5 +1,5 @@ package scala.reflect.macros -package runtime +package contexts import scala.reflect.runtime.{universe => ru} import scala.tools.reflect.ToolBox @@ -7,7 +7,7 @@ import scala.tools.reflect.ToolBox trait Evals { self: Context => - private lazy val evalMirror = ru.runtimeMirror(universe.analyzer.macroClassloader) + private lazy val evalMirror = ru.runtimeMirror(universe.analyzer.defaultMacroClassloader) private lazy val evalToolBox = evalMirror.mkToolBox() private lazy val evalImporter = ru.mkImporter(universe).asInstanceOf[ru.Importer { val from: universe.type }] diff --git a/src/compiler/scala/reflect/macros/runtime/ExprUtils.scala b/src/compiler/scala/reflect/macros/contexts/ExprUtils.scala index a719beed97..4846325d1e 100644 --- a/src/compiler/scala/reflect/macros/runtime/ExprUtils.scala +++ b/src/compiler/scala/reflect/macros/contexts/ExprUtils.scala @@ -1,5 +1,5 @@ package scala.reflect.macros -package runtime +package contexts trait ExprUtils { self: Context => diff --git a/src/compiler/scala/reflect/macros/runtime/FrontEnds.scala b/src/compiler/scala/reflect/macros/contexts/FrontEnds.scala index a6a198e1b4..fda05de09c 100644 --- a/src/compiler/scala/reflect/macros/runtime/FrontEnds.scala +++ b/src/compiler/scala/reflect/macros/contexts/FrontEnds.scala @@ -1,5 +1,7 @@ package scala.reflect.macros -package runtime +package contexts + +import scala.reflect.macros.runtime.AbortMacroException trait FrontEnds { self: Context => diff --git a/src/compiler/scala/reflect/macros/runtime/Infrastructure.scala b/src/compiler/scala/reflect/macros/contexts/Infrastructure.scala index 7781693822..df7aa4d2be 100644 --- a/src/compiler/scala/reflect/macros/runtime/Infrastructure.scala +++ b/src/compiler/scala/reflect/macros/contexts/Infrastructure.scala @@ -1,5 +1,5 @@ package scala.reflect.macros -package runtime +package contexts trait Infrastructure { self: Context => diff --git a/src/compiler/scala/reflect/macros/runtime/Names.scala b/src/compiler/scala/reflect/macros/contexts/Names.scala index 635e8bcd45..e535754a98 100644 --- a/src/compiler/scala/reflect/macros/runtime/Names.scala +++ b/src/compiler/scala/reflect/macros/contexts/Names.scala @@ -1,5 +1,5 @@ package scala.reflect.macros -package runtime +package contexts trait Names { self: Context => diff --git a/src/compiler/scala/reflect/macros/runtime/Parsers.scala b/src/compiler/scala/reflect/macros/contexts/Parsers.scala index 566bcde73d..3dab02beba 100644 --- a/src/compiler/scala/reflect/macros/runtime/Parsers.scala +++ b/src/compiler/scala/reflect/macros/contexts/Parsers.scala @@ -1,5 +1,5 @@ package scala.reflect.macros -package runtime +package contexts import scala.language.existentials import scala.tools.reflect.ToolBox diff --git a/src/compiler/scala/reflect/macros/runtime/Reifiers.scala b/src/compiler/scala/reflect/macros/contexts/Reifiers.scala index 7ec3457c6a..ecef1c7289 100644 --- a/src/compiler/scala/reflect/macros/runtime/Reifiers.scala +++ b/src/compiler/scala/reflect/macros/contexts/Reifiers.scala @@ -4,7 +4,7 @@ */ package scala.reflect.macros -package runtime +package contexts trait Reifiers { self: Context => diff --git a/src/compiler/scala/reflect/macros/runtime/Synthetics.scala b/src/compiler/scala/reflect/macros/contexts/Synthetics.scala index 73f3ab8d20..ada16a8113 100644 --- a/src/compiler/scala/reflect/macros/runtime/Synthetics.scala +++ b/src/compiler/scala/reflect/macros/contexts/Synthetics.scala @@ -3,9 +3,8 @@ */ package scala.reflect.macros -package runtime +package contexts -import java.util.UUID._ import scala.reflect.internal.Flags._ import scala.reflect.internal.util.BatchSourceFile import scala.reflect.io.VirtualFile @@ -42,11 +41,6 @@ trait Synthetics { else EmptyTree } - // TODO: provide a way to specify a pretty name for debugging purposes - private def randomFileName() = ( - "macroSynthetic-" + randomUUID().toString.replace("-", "") + ".scala" - ) - def introduceTopLevel[T: PackageSpec](packagePrototype: T, definition: universe.ImplDef): RefTree = introduceTopLevel(packagePrototype, List(definition)).head @@ -55,18 +49,7 @@ trait Synthetics { private def introduceTopLevel[T: PackageSpec](packagePrototype: T, definitions: List[universe.ImplDef]): List[RefTree] = { val code @ PackageDef(pid, _) = implicitly[PackageSpec[T]].mkPackageDef(packagePrototype, definitions) - val syntheticFileName = randomFileName() - // compatibility with SBT - // on the one hand, we need to specify some jfile here, otherwise sbt crashes with an NPE (SI-6870) - // on the other hand, we can't specify the obvious enclosingUnit, because then sbt somehow fails to run tests using type macros - // okay, now let's specify a guaranteedly non-existent file in an existing directory (so that we don't run into permission problems) - val relatedJfile = enclosingUnit.source.file.file - val fakeJfile = if (relatedJfile != null) new java.io.File(relatedJfile.getParent, syntheticFileName) else null - val virtualFile = new VirtualFile(syntheticFileName) { override def file = fakeJfile } - val sourceFile = new BatchSourceFile(virtualFile, code.toString) - val unit = new CompilationUnit(sourceFile) - unit.body = code - universe.currentRun.compileLate(unit) + universe.currentRun.compileLate(code) definitions map (definition => Select(pid, definition.name)) } diff --git a/src/compiler/scala/reflect/macros/runtime/Traces.scala b/src/compiler/scala/reflect/macros/contexts/Traces.scala index 0238e9f84e..df47f6ba81 100644 --- a/src/compiler/scala/reflect/macros/runtime/Traces.scala +++ b/src/compiler/scala/reflect/macros/contexts/Traces.scala @@ -1,5 +1,5 @@ package scala.reflect.macros -package runtime +package contexts trait Traces extends util.Traces { self: Context => diff --git a/src/compiler/scala/reflect/macros/runtime/Typers.scala b/src/compiler/scala/reflect/macros/contexts/Typers.scala index f60b8dfeb4..4a1122b913 100644 --- a/src/compiler/scala/reflect/macros/runtime/Typers.scala +++ b/src/compiler/scala/reflect/macros/contexts/Typers.scala @@ -1,5 +1,5 @@ package scala.reflect.macros -package runtime +package contexts import scala.reflect.internal.Mode @@ -24,7 +24,7 @@ trait Typers { // typechecking uses silent anyways (e.g. in typedSelect), so you'll only waste your time // I'd advise fixing the root cause: finding why the context is not set to report errors // (also see reflect.runtime.ToolBoxes.typeCheckExpr for a workaround that might work for you) - wrapper(callsiteTyper.silent(_.typed(tree, pt)) match { + wrapper(callsiteTyper.silent(_.typed(tree, pt), reportAmbiguousErrors = false) match { case universe.analyzer.SilentResultValue(result) => macroLogVerbose(result) result diff --git a/src/compiler/scala/reflect/macros/runtime/Context.scala b/src/compiler/scala/reflect/macros/runtime/Context.scala deleted file mode 100644 index 76c684f6d7..0000000000 --- a/src/compiler/scala/reflect/macros/runtime/Context.scala +++ /dev/null @@ -1,29 +0,0 @@ -package scala.reflect.macros -package runtime - -import scala.tools.nsc.Global - -abstract class Context extends scala.reflect.macros.Context - with Aliases - with Enclosures - with Names - with Reifiers - with FrontEnds - with Infrastructure - with Typers - with Parsers - with Evals - with ExprUtils - with Synthetics - with Traces { - - val universe: Global - - val mirror: universe.Mirror = universe.rootMirror - - val callsiteTyper: universe.analyzer.Typer - - val prefix: Expr[PrefixType] - - val expandee: Tree -} diff --git a/src/compiler/scala/reflect/macros/runtime/JavaReflectionRuntimes.scala b/src/compiler/scala/reflect/macros/runtime/JavaReflectionRuntimes.scala new file mode 100644 index 0000000000..3ef11fad9d --- /dev/null +++ b/src/compiler/scala/reflect/macros/runtime/JavaReflectionRuntimes.scala @@ -0,0 +1,31 @@ +package scala.reflect.macros +package runtime + +import scala.reflect.runtime.ReflectionUtils +import scala.reflect.macros.{Context => ApiContext} + +trait JavaReflectionRuntimes { + self: scala.tools.nsc.typechecker.Analyzer => + + trait JavaReflectionResolvers { + self: MacroRuntimeResolver => + + import global._ + + def resolveJavaReflectionRuntime(classLoader: ClassLoader): MacroRuntime = { + val implClass = Class.forName(className, true, classLoader) + val implMeths = implClass.getDeclaredMethods.find(_.getName == methName) + // relies on the fact that macro impls cannot be overloaded + // so every methName can resolve to at maximum one method + val implMeth = implMeths getOrElse { throw new NoSuchMethodException(s"$className.$methName") } + macroLogVerbose(s"successfully loaded macro impl as ($implClass, $implMeth)") + args => { + val implObj = + if (isBundle) implClass.getConstructor(classOf[ApiContext]).newInstance(args.c) + else ReflectionUtils.staticSingletonInstance(implClass) + val implArgs = if (isBundle) args.others else args.c +: args.others + implMeth.invoke(implObj, implArgs.asInstanceOf[Seq[AnyRef]]: _*) + } + } + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala b/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala new file mode 100644 index 0000000000..0f89163803 --- /dev/null +++ b/src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala @@ -0,0 +1,74 @@ +package scala.reflect.macros +package runtime + +import scala.collection.mutable.{Map => MutableMap} +import scala.reflect.internal.Flags._ +import scala.reflect.runtime.ReflectionUtils +import scala.tools.nsc.util.ScalaClassLoader +import scala.tools.nsc.util.AbstractFileClassLoader + +trait MacroRuntimes extends JavaReflectionRuntimes with ScalaReflectionRuntimes { + self: scala.tools.nsc.typechecker.Analyzer => + + import global._ + import definitions._ + + /** Produces a function that can be used to invoke macro implementation for a given macro definition: + * 1) Looks up macro implementation symbol in this universe. + * 2) Loads its enclosing class from the macro classloader. + * 3) Loads the companion of that enclosing class from the macro classloader. + * 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. + */ + private val macroRuntimesCache = perRunCaches.newWeakMap[Symbol, MacroRuntime] + def macroRuntime(macroDef: Symbol): MacroRuntime = { + macroLogVerbose(s"looking for macro implementation: $macroDef") + if (fastTrack contains macroDef) { + macroLogVerbose("macro expansion is serviced by a fast track") + fastTrack(macroDef) + } else { + macroRuntimesCache.getOrElseUpdate(macroDef, new MacroRuntimeResolver(macroDef).resolveRuntime()) + } + } + + /** Macro classloader that is used to resolve and run macro implementations. + * Loads classes from from -cp (aka the library classpath). + * Is also capable of detecting REPL and reusing its classloader. + * + * When -Xmacro-jit is enabled, we sometimes fallback to on-the-fly compilation of macro implementations, + * which compiles implementations into a virtual directory (very much like REPL does) and then conjures + * a classloader mapped to that virtual directory. + */ + lazy val defaultMacroClassloader: ClassLoader = findMacroClassLoader() + + /** Abstracts away resolution of macro runtimes. + */ + type MacroRuntime = MacroArgs => Any + class MacroRuntimeResolver(val macroDef: Symbol) extends JavaReflectionResolvers + with ScalaReflectionResolvers { + val binding = loadMacroImplBinding(macroDef) + val isBundle = binding.isBundle + val className = binding.className + val methName = binding.methName + + def resolveRuntime(): MacroRuntime = { + if (className == Predef_???.owner.javaClassName && methName == Predef_???.name.encoded) { + args => throw new AbortMacroException(args.c.enclosingPosition, "macro implementation is missing") + } else { + try { + macroLogVerbose(s"resolving macro implementation as $className.$methName (isBundle = $isBundle)") + macroLogVerbose(s"classloader is: ${ReflectionUtils.show(defaultMacroClassloader)}") + // resolveScalaReflectionRuntime(defaultMacroClassloader) + resolveJavaReflectionRuntime(defaultMacroClassloader) + } catch { + case ex: Exception => + macroLogVerbose(s"macro runtime failed to load: ${ex.toString}") + macroDef setFlag IS_ERROR + null + } + } + } + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/macros/runtime/ScalaReflectionRuntimes.scala b/src/compiler/scala/reflect/macros/runtime/ScalaReflectionRuntimes.scala new file mode 100644 index 0000000000..1999e525ff --- /dev/null +++ b/src/compiler/scala/reflect/macros/runtime/ScalaReflectionRuntimes.scala @@ -0,0 +1,33 @@ +package scala.reflect.macros +package runtime + +import scala.reflect.runtime.{universe => ru} + +trait ScalaReflectionRuntimes { + self: scala.tools.nsc.typechecker.Analyzer => + + trait ScalaReflectionResolvers { + self: MacroRuntimeResolver => + + import global._ + + def resolveScalaReflectionRuntime(classLoader: ClassLoader): MacroRuntime = { + val macroMirror: ru.JavaMirror = ru.runtimeMirror(classLoader) + val implContainerSym = macroMirror.classSymbol(Class.forName(className, true, classLoader)) + val implMethSym = implContainerSym.typeSignature.member(ru.TermName(methName)).asMethod + macroLogVerbose(s"successfully loaded macro impl as ($implContainerSym, $implMethSym)") + args => { + val implContainer = + if (isBundle) { + val implCtorSym = implContainerSym.typeSignature.member(ru.nme.CONSTRUCTOR).asMethod + macroMirror.reflectClass(implContainerSym).reflectConstructor(implCtorSym)(args.c) + } else { + macroMirror.reflectModule(implContainerSym.module.asModule).instance + } + val implMeth = macroMirror.reflect(implContainer).reflectMethod(implMethSym) + val implArgs = if (isBundle) args.others else args.c +: args.others + implMeth(implArgs: _*) + } + } + } +} diff --git a/src/compiler/scala/reflect/macros/runtime/package.scala b/src/compiler/scala/reflect/macros/runtime/package.scala new file mode 100644 index 0000000000..9ef8200760 --- /dev/null +++ b/src/compiler/scala/reflect/macros/runtime/package.scala @@ -0,0 +1,5 @@ +package scala.reflect.macros + +package object runtime { + type Context = scala.reflect.macros.contexts.Context +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/macros/util/Helpers.scala b/src/compiler/scala/reflect/macros/util/Helpers.scala new file mode 100644 index 0000000000..ada8efa833 --- /dev/null +++ b/src/compiler/scala/reflect/macros/util/Helpers.scala @@ -0,0 +1,71 @@ +package scala.reflect.macros +package util + +import scala.tools.nsc.typechecker.Analyzer + +trait Helpers { + self: Analyzer => + + import global._ + import definitions._ + + /** Transforms parameters lists of a macro impl. + * The `transform` function is invoked only for WeakTypeTag evidence parameters. + * + * The transformer takes two arguments: a value parameter from the parameter list + * and a type parameter that is witnesses by the value parameter. + * + * If the transformer returns a NoSymbol, the value parameter is not included from the result. + * If the transformer returns something else, this something else is included in the result instead of the value parameter. + * + * Despite of being highly esoteric, this function significantly simplifies signature analysis. + * For example, it can be used to strip macroImpl.paramss from the evidences (necessary when checking def <-> impl correspondence) + * or to streamline creation of the list of macro arguments. + */ + def transformTypeTagEvidenceParams(macroImplRef: Tree, transform: (Symbol, Symbol) => Symbol): List[List[Symbol]] = { + val treeInfo.MacroImplReference(isBundle, owner, macroImpl, _) = macroImplRef + val paramss = macroImpl.paramss + if (paramss.isEmpty || paramss.last.isEmpty) return paramss // no implicit parameters in the signature => nothing to do + val rc = + if (isBundle) macroImpl.owner.tpe.member(nme.c) + else { + def cparam = paramss.head.head + if (paramss.head.isEmpty || !(cparam.tpe <:< MacroContextClass.tpe)) return paramss // no context parameter in the signature => nothing to do + cparam + } + def transformTag(param: Symbol): Symbol = param.tpe.dealias match { + case TypeRef(SingleType(SingleType(_, ac), universe), WeakTypeTagClass, targ :: Nil) + if ac == rc && universe == MacroContextUniverse => + transform(param, targ.typeSymbol) + case _ => + param + } + val transformed = paramss.last map transformTag filter (_ ne NoSymbol) + if (transformed.isEmpty) paramss.init else paramss.init :+ transformed + } + + private def dealiasAndRewrap(tp: Type)(fn: Type => Type): Type = { + if (isRepeatedParamType(tp)) scalaRepeatedType(fn(tp.typeArgs.head.dealias)) + else fn(tp.dealias) + } + + /** Increases metalevel of the type, i.e. transforms: + * * T to c.Expr[T] + * + * @see Metalevels.scala for more information and examples about metalevels + */ + def increaseMetalevel(pre: Type, tp: Type): Type = dealiasAndRewrap(tp) { + case tp => typeRef(pre, MacroContextExprClass, List(tp)) + } + + /** Decreases metalevel of the type, i.e. transforms: + * * c.Expr[T] to T + * * Anything else to Any + * + * @see Metalevels.scala for more information and examples about metalevels + */ + def decreaseMetalevel(tp: Type): Type = dealiasAndRewrap(tp) { + case ExprClassOf(runtimeType) => runtimeType + case _ => AnyClass.tpe // so that macro impls with rhs = ??? don't screw up our inference + } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/Taggers.scala b/src/compiler/scala/reflect/reify/Taggers.scala index 9659134e5b..0bffe55403 100644 --- a/src/compiler/scala/reflect/reify/Taggers.scala +++ b/src/compiler/scala/reflect/reify/Taggers.scala @@ -1,7 +1,7 @@ package scala.reflect.reify import scala.reflect.macros.{ReificationException, UnexpectedReificationException, TypecheckException} -import scala.reflect.macros.runtime.Context +import scala.reflect.macros.contexts.Context abstract class Taggers { val c: Context diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index f1fccd6069..a08633ffc8 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -9,6 +9,7 @@ package nsc import java.io.{ File, FileOutputStream, PrintWriter, IOException, FileNotFoundException } import java.nio.charset.{ Charset, CharsetDecoder, IllegalCharsetNameException, UnsupportedCharsetException } +import java.util.UUID._ import scala.compat.Platform.currentTime import scala.collection.{ mutable, immutable } import io.{ SourceReader, AbstractFile, Path } @@ -16,6 +17,7 @@ import reporters.{ Reporter, ConsoleReporter } import util.{ ClassPath, MergedClassPath, StatisticsInfo, returning, stackTraceString, stackTraceHeadString } import scala.reflect.internal.util.{ OffsetPosition, SourceFile, NoSourceFile, BatchSourceFile, ScriptSourceFile } import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat } +import scala.reflect.io.VirtualFile import symtab.{ Flags, SymbolTable, SymbolLoaders, SymbolTrackers } import symtab.classfile.Pickler import plugins.Plugins @@ -1621,6 +1623,25 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } } + // TODO: provide a way to specify a pretty name for debugging purposes + private def randomFileName() = ( + "compileLateSynthetic-" + randomUUID().toString.replace("-", "") + ".scala" + ) + + def compileLate(code: PackageDef) { + // compatibility with SBT + // on the one hand, we need to specify some jfile here, otherwise sbt crashes with an NPE (SI-6870) + // on the other hand, we can't specify the obvious enclosingUnit, because then sbt somehow fails to run tests using type macros + // okay, now let's specify a guaranteedly non-existent file in an existing directory (so that we don't run into permission problems) + val syntheticFileName = randomFileName() + val fakeJfile = new java.io.File(syntheticFileName) + val virtualFile = new VirtualFile(syntheticFileName) { override def file = fakeJfile } + val sourceFile = new BatchSourceFile(virtualFile, code.toString) + val unit = new CompilationUnit(sourceFile) + unit.body = code + compileLate(unit) + } + /** Reset package class to state at typer (not sure what this * is needed for?) */ diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index dbcb6c19a0..fe1607c631 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -460,7 +460,7 @@ trait ContextErrors { def AbstractionFromVolatileTypeError(vd: ValDef) = issueNormalTypeError(vd, "illegal abstraction from value with volatile type "+vd.symbol.tpe) - private[ContextErrors] def TypedApplyWrongNumberOfTpeParametersErrorMessage(fun: Tree) = + private[scala] def TypedApplyWrongNumberOfTpeParametersErrorMessage(fun: Tree) = "wrong number of type parameters for "+treeSymTypeMsg(fun) def TypedApplyWrongNumberOfTpeParametersError(tree: Tree, fun: Tree) = { @@ -476,7 +476,7 @@ trait ContextErrors { // doTypeApply //tryNamesDefaults def NamedAndDefaultArgumentsNotSupportedForMacros(tree: Tree, fun: Tree) = - NormalTypeError(tree, "macros application do not support named and/or default arguments") + NormalTypeError(tree, "macro applications do not support named and/or default arguments") def TooManyArgsNamesDefaultsError(tree: Tree, fun: Tree) = NormalTypeError(tree, "too many arguments for "+treeSymTypeMsg(fun)) @@ -571,7 +571,7 @@ trait ContextErrors { //adapt def MissingArgsForMethodTpeError(tree: Tree, meth: Symbol) = { val message = - if (meth.isMacro) MacroPartialApplicationErrorMessage + if (meth.isMacro) MacroTooFewArgumentListsMessage else "missing arguments for " + meth.fullLocationString + ( if (meth.isConstructor) "" else ";\nfollow this method with `_' if you want to treat it as a partially applied function" @@ -692,7 +692,6 @@ trait ContextErrors { issueNormalTypeError(expandee, s"macro in $role role can only expand into $allowedExpansions") } - // same reason as for MacroBodyTypecheckException case object MacroExpansionException extends Exception with scala.util.control.ControlThrowable protected def macroExpansionError(expandee: Tree, msg: String, pos: Position = NoPosition) = { @@ -703,15 +702,24 @@ trait ContextErrors { throw MacroExpansionException } - def MacroPartialApplicationErrorMessage = "macros cannot be partially applied" - def MacroPartialApplicationError(expandee: Tree) = { + private def macroExpansionError2(expandee: Tree, msg: String) = { // macroExpansionError won't work => swallows positions, hence needed to do issueTypeError // kinda contradictory to the comment in `macroExpansionError`, but this is how it works - issueNormalTypeError(expandee, MacroPartialApplicationErrorMessage) + issueNormalTypeError(expandee, msg) setError(expandee) throw MacroExpansionException } + private def MacroTooFewArgumentListsMessage = "too few argument lists for macro invocation" + def MacroTooFewArgumentListsError(expandee: Tree) = macroExpansionError2(expandee, MacroTooFewArgumentListsMessage) + + private def MacroTooManyArgumentListsMessage = "too many argument lists for macro invocation" + def MacroTooManyArgumentListsError(expandee: Tree) = macroExpansionError2(expandee, MacroTooManyArgumentListsMessage) + + def MacroTooFewArgumentsError(expandee: Tree) = macroExpansionError2(expandee, "too few arguments for macro invocation") + + def MacroTooManyArgumentsError(expandee: Tree) = macroExpansionError2(expandee, "too many arguments for macro invocation") + def MacroGeneratedAbort(expandee: Tree, ex: AbortMacroException) = { // errors have been reported by the macro itself, so we do nothing here macroLogVerbose("macro expansion has been aborted") @@ -917,7 +925,7 @@ trait ContextErrors { kindErrors.toList.mkString("\n", ", ", "")) } - private[ContextErrors] def NotWithinBoundsErrorMessage(prefix: String, targs: List[Type], tparams: List[Symbol], explaintypes: Boolean) = { + private[scala] 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)) @@ -1224,142 +1232,4 @@ 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("WeakTypeTag", "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) { - withTypesExplained(rtpe <:< atpe) - } else rtpe <:< atpe - if (!ok) { - if (!macroDebugVerbose) - explainTypes(rtpe, atpe) - 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) = { - val treeInfo.Applied(implRef, _, _) = macroDdef.rhs - genericError(implRef, 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 MacroDefIsQmarkQmarkQmark() = { - macroLogVerbose("typecheck terminated unexpectedly: macro is ???") - throw MacroBodyTypecheckException - } - - 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 WeakTypeTag 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)) - - 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 5ac37251ee..6c4d1e20aa 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -1,6 +1,7 @@ package scala.tools.nsc package typechecker +import java.lang.Math.min import symtab.Flags._ import scala.tools.nsc.util._ import scala.reflect.runtime.ReflectionUtils @@ -9,7 +10,12 @@ import scala.reflect.ClassTag import scala.reflect.internal.util.Statistics import scala.reflect.macros.util._ import scala.util.control.ControlThrowable -import scala.reflect.macros.runtime.AbortMacroException +import scala.reflect.macros.runtime.{AbortMacroException, MacroRuntimes} +import scala.reflect.runtime.{universe => ru} +import scala.reflect.macros.compiler.DefaultMacroCompiler +import scala.tools.reflect.FastTrack +import scala.runtime.ScalaRunTime +import Fingerprint._ /** * Code to deal with macros, namely with: @@ -36,7 +42,7 @@ import scala.reflect.macros.runtime.AbortMacroException * (Expr(elems)) * (TypeTag(Int)) */ -trait Macros extends scala.tools.reflect.FastTrack with Traces { +trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { self: Analyzer => import global._ @@ -74,7 +80,9 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * Includes a path to load the implementation via Java reflection, * and various accounting information necessary when composing an argument list for the reflective invocation. */ - private case class MacroImplBinding( + case class MacroImplBinding( + // Is this macro impl a bundle (a trait extending Macro) or a vanilla def? + val isBundle: Boolean, // Java class name of the class that contains the macro implementation // is used to load the corresponding object with Java reflection className: String, @@ -82,14 +90,21 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // `className` and `methName` are all we need to reflectively invoke a macro implementation // because macro implementations cannot be overloaded methName: String, - // flattens the macro impl's parameter lists having symbols replaced with metadata - // currently metadata is an index of the type parameter corresponding to that type tag (if applicable) - // f.ex. for: def impl[T: WeakTypeTag, U: WeakTypeTag, V](c: Context)(x: c.Expr[T]): (U, V) = ??? - // `signature` will be equal to List(-1, -1, 0, 1) - signature: List[Int], + // flattens the macro impl's parameter lists having symbols replaced with their fingerprints + // currently fingerprints are calculated solely from types of the symbols: + // * c.Expr[T] => IMPLPARAM_EXPR + // * c.WeakTypeTag[T] => index of the type parameter corresponding to that type tag + // * everything else (e.g. scala.reflect.macros.Context) => IMPLPARAM_OTHER + // f.ex. for: def impl[T: WeakTypeTag, U, V: WeakTypeTag](c: Context)(x: c.Expr[T], y: c.Tree): (U, V) = ??? + // `signature` will be equal to List(List(Other), List(Lifted, Other), List(Tagged(0), Tagged(2))) + signature: List[List[Fingerprint]], // type arguments part of a macro impl ref (the right-hand side of a macro definition) // these trees don't refer to a macro impl, so we can pickle them as is - targs: List[Tree]) + targs: List[Tree]) { + + // Was this binding derived from a `def ... = macro ???` definition? + def is_??? = className == Predef_???.owner.javaClassName && methName == Predef_???.name.encoded + } /** Macro def -> macro impl bindings are serialized into a `macroImpl` annotation * with synthetic content that carries the payload described in `MacroImplBinding`. @@ -102,31 +117,35 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * * @scala.reflect.macros.internal.macroImpl( * `macro`( - * "signature" = List(-1), + * "isBundle" = false, + * "signature" = List(Other), * "methodName" = "impl", - * "versionFormat" = 1, + * "versionFormat" = <current version format>, * "className" = "Macros$")) */ - private object MacroImplBinding { - val versionFormat = 1 + object MacroImplBinding { + val versionFormat = 4.0 def pickleAtom(obj: Any): Tree = obj match { case list: List[_] => Apply(Ident(ListModule), list map pickleAtom) case s: String => Literal(Constant(s)) - case i: Int => Literal(Constant(i)) + case d: Double => Literal(Constant(d)) + case b: Boolean => Literal(Constant(b)) + case f: Fingerprint => Literal(Constant(f.value)) } def unpickleAtom(tree: Tree): Any = tree match { case Apply(list @ Ident(_), args) if list.symbol == ListModule => args map unpickleAtom case Literal(Constant(s: String)) => s - case Literal(Constant(i: Int)) => i + case Literal(Constant(d: Double)) => d + case Literal(Constant(b: Boolean)) => b + case Literal(Constant(i: Int)) => new Fingerprint(i) } def pickle(macroImplRef: Tree): Tree = { - val MacroImplReference(owner, macroImpl, targs) = macroImplRef - val paramss = macroImpl.paramss + val MacroImplReference(isBundle, owner, macroImpl, targs) = macroImplRef // todo. refactor when fixing SI-5498 def className: String = { @@ -142,13 +161,20 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { loop(owner) } - def signature: List[Int] = { - val transformed = transformTypeTagEvidenceParams(paramss, (param, tparam) => tparam) - transformed.flatten map (p => if (p.isTerm) -1 else p.paramPos) + def signature: List[List[Fingerprint]] = { + def fingerprint(tpe: Type): Fingerprint = tpe.dealiasWiden match { + case TypeRef(_, RepeatedParamClass, underlying :: Nil) => fingerprint(underlying) + case ExprClassOf(_) => Lifted + case _ => Other + } + + val transformed = transformTypeTagEvidenceParams(macroImplRef, (param, tparam) => tparam) + mmap(transformed)(p => if (p.isTerm) fingerprint(p.info) else Tagged(p.paramPos)) } val payload = List[(String, Any)]( "versionFormat" -> versionFormat, + "isBundle" -> isBundle, "className" -> className, "methodName" -> macroImpl.name.toString, "signature" -> signature @@ -188,362 +214,119 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { val Apply(_, pickledPayload) = wrapped val payload = pickledPayload.map{ case Assign(k, v) => (unpickleAtom(k), unpickleAtom(v)) }.toMap - val pickleVersionFormat = payload("versionFormat").asInstanceOf[Int] - if (versionFormat != pickleVersionFormat) throw new Error(s"macro impl binding format mismatch: expected $versionFormat, actual $pickleVersionFormat") + def fail(msg: String) = abort(s"bad macro impl binding: $msg") + def unpickle[T](field: String, clazz: Class[T]): T = { + def failField(msg: String) = fail(s"$field $msg") + if (!payload.contains(field)) failField("is supposed to be there") + val raw: Any = payload(field) + if (raw == null) failField(s"is not supposed to be null") + val expected = ScalaRunTime.box(clazz) + val actual = raw.getClass + if (!expected.isAssignableFrom(actual)) failField(s"has wrong type: expected $expected, actual $actual") + raw.asInstanceOf[T] + } + + val pickleVersionFormat = unpickle("versionFormat", classOf[Double]) + if (versionFormat != pickleVersionFormat) fail(s"expected version format $versionFormat, actual $pickleVersionFormat") - val className = payload("className").asInstanceOf[String] - val methodName = payload("methodName").asInstanceOf[String] - val signature = payload("signature").asInstanceOf[List[Int]] - MacroImplBinding(className, methodName, signature, targs) + val isBundle = unpickle("isBundle", classOf[Boolean]) + val className = unpickle("className", classOf[String]) + val methodName = unpickle("methodName", classOf[String]) + val signature = unpickle("signature", classOf[List[List[Fingerprint]]]) + MacroImplBinding(isBundle, className, methodName, signature, targs) } } - private def bindMacroImpl(macroDef: Symbol, macroImplRef: Tree): Unit = { + def bindMacroImpl(macroDef: Symbol, macroImplRef: Tree): Unit = { val pickle = MacroImplBinding.pickle(macroImplRef) macroDef withAnnotation AnnotationInfo(MacroImplAnnotation.tpe, List(pickle), Nil) } - private def loadMacroImplBinding(macroDef: Symbol): MacroImplBinding = { + def loadMacroImplBinding(macroDef: Symbol): MacroImplBinding = { val Some(AnnotationInfo(_, List(pickle), _)) = macroDef.getAnnotation(MacroImplAnnotation) MacroImplBinding.unpickle(pickle) } - /** Transforms parameters lists of a macro impl. - * The `transform` function is invoked only for WeakTypeTag evidence parameters. - * - * The transformer takes two arguments: a value parameter from the parameter list - * and a type parameter that is witnesses by the value parameter. - * - * If the transformer returns a NoSymbol, the value parameter is not included from the result. - * If the transformer returns something else, this something else is included in the result instead of the value parameter. - * - * Despite of being highly esoteric, this function significantly simplifies signature analysis. - * For example, it can be used to strip macroImpl.paramss from the evidences (necessary when checking def <-> impl correspondence) - * or to streamline creation of the list of macro arguments. - */ - private def transformTypeTagEvidenceParams(paramss: List[List[Symbol]], transform: (Symbol, Symbol) => Symbol): List[List[Symbol]] = { - if (paramss.isEmpty || paramss.last.isEmpty) return paramss // no implicit parameters in the signature => nothing to do - if (paramss.head.isEmpty || !(paramss.head.head.tpe <:< MacroContextClass.tpe)) return paramss // no context parameter in the signature => nothing to do - def transformTag(param: Symbol): Symbol = param.tpe.dealias match { - case TypeRef(SingleType(SingleType(NoPrefix, c), universe), WeakTypeTagClass, targ :: Nil) - if c == paramss.head.head && universe == MacroContextUniverse => - transform(param, targ.typeSymbol) - case _ => - param - } - val transformed = paramss.last map transformTag filter (_ ne NoSymbol) - if (transformed.isEmpty) paramss.init else paramss.init :+ transformed - } - - 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 + def computeMacroDefTypeFromMacroImplRef(macroDdef: DefDef, macroImplRef: Tree): Type = { + macroImplRef match { + case MacroImplReference(_, _, macroImpl, targs) => + // Step I. Transform c.Expr[T] to T and everything else to Any + var runtimeType = decreaseMetalevel(macroImpl.info.finalResultType) + + // Step II. Transform type parameters of a macro implementation into type arguments in a macro definition's body + runtimeType = runtimeType.substituteTypes(macroImpl.typeParams, targs map (_.tpe)) + + // Step III. Transform c.prefix.value.XXX to this.XXX and implParam.value.YYY to defParam.YYY + def unsigma(tpe: Type): Type = + transformTypeTagEvidenceParams(macroImplRef, (param, tparam) => NoSymbol) match { + case (implCtxParam :: Nil) :: implParamss => + val implToDef = flatMap2(implParamss, macroDdef.vparamss)(map2(_, _)((_, _))).toMap + object UnsigmaTypeMap extends TypeMap { + def apply(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) => + val pre1 = pre match { + case SingleType(SingleType(SingleType(NoPrefix, c), prefix), value) if c == implCtxParam && prefix == MacroContextPrefix && value == ExprValue => + ThisType(macroDdef.symbol.owner) + case SingleType(SingleType(NoPrefix, implParam), value) if value == ExprValue => + implToDef get implParam map (defParam => SingleType(NoPrefix, defParam.symbol)) getOrElse pre + case _ => + pre + } + val args1 = args map mapOver + TypeRef(pre1, sym, args1) case _ => - pre + mapOver(tp) } - val args1 = args map mapOver - TypeRef(pre1, sym, args1) - case _ => - mapOver(tp) - } - } - - UnsigmaTypeMap(tpe) - case _ => - tpe - } + } - unsigma(runtimeType) - } + UnsigmaTypeMap(tpe) + case _ => + tpe + } - /** A reference macro implementation signature compatible with a given macro definition. - * - * In the example above for the following macro def: - * def foo[T](xs: List[T]): T = macro fooBar - * - * This function will return: - * (c: scala.reflect.macros.Context)(xs: c.Expr[List[T]]): c.Expr[T] - * - * Note that type tag evidence parameters are not included into the result. - * Type tag context bounds for macro impl tparams are optional. - * Therefore compatibility checks ignore such parameters, and we don't need to bother about them here. - * - * @param macroDef The macro definition symbol - * @param tparams The type parameters of the macro definition - * @param vparamss The value parameters of the macro definition - * @param retTpe The return type of the macro definition - */ - 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 { - def WeakTagClass = getMember(MacroContextClass, tpnme.WeakTypeTag) - def ExprClass = getMember(MacroContextClass, tpnme.Expr) - val cache = scala.collection.mutable.Map[Symbol, Symbol]() - val ctxParam = makeParam(nme.macroContext, macroDef.pos, MacroContextClass.tpe, SYNTHETIC) - val paramss = List(ctxParam) :: mmap(vparamss)(param) - val implReturnType = typeRef(singleType(NoPrefix, ctxParam), ExprClass, List(sigma(retTpe))) - - object SigmaTypeMap extends TypeMap { - def mapPrefix(pre: Type) = pre match { - case ThisType(sym) if sym == macroDef.owner => - singleType(singleType(singleType(NoPrefix, ctxParam), MacroContextPrefix), ExprValue) - case SingleType(NoPrefix, sym) => - mfind(vparamss)(_.symbol == sym).fold(pre)(p => singleType(singleType(NoPrefix, param(p)), ExprValue)) - case _ => - mapOver(pre) - } - def apply(tp: Type): Type = tp match { - case TypeRef(pre, sym, args) => - val pre1 = mapPrefix(pre) - val args1 = mapOverArgs(args, sym.typeParams) - if ((pre eq pre1) && (args eq args1)) tp - else typeRef(pre1, sym, args1) - case _ => - mapOver(tp) - } - } - def sigma(tpe: Type): Type = SigmaTypeMap(tpe) - - def makeParam(name: Name, pos: Position, tpe: Type, flags: Long) = - macroDef.newValueParameter(name.toTermName, pos, flags) setInfo tpe - def implType(isType: Boolean, origTpe: Type): Type = { - def tsym = if (isType) WeakTagClass else ExprClass - def targ = origTpe.typeArgs.headOption getOrElse NoType - - if (isRepeatedParamType(origTpe)) - scalaRepeatedType(implType(isType, sigma(targ))) - else - typeRef(singleType(NoPrefix, ctxParam), tsym, List(sigma(origTpe))) - } - def param(tree: Tree): Symbol = ( - cache.getOrElseUpdate(tree.symbol, { - val sym = tree.symbol - makeParam(sym.name, sym.pos, implType(sym.isType, sym.tpe), sym.flags) - }) - ) + unsigma(runtimeType) + case _ => + ErrorType } - - import SigGenerator._ - macroLogVerbose(sm""" - |generating macroImplSigs for: $macroDef - |tparams are: $tparams - |vparamss are: $vparamss - |retTpe is: $retTpe - |macroImplSig is: $paramss, $implReturnType - """.trim) - (paramss, implReturnType) } - /** Verifies that the body of a macro def typechecks to a reference to a static public non-overloaded method, + /** Verifies that the body of a macro def typechecks to a reference to a static public non-overloaded method or a top-level macro bundle, * and that that method is signature-wise compatible with the given macro definition. * - * @return Typechecked rhs of the given macro definition if everything is okay. + * @return Macro impl reference for the given macro definition if everything is okay. * EmptyTree if an error occurs. */ - def typedMacroBody(typer: Typer, macroDdef: DefDef): Tree = - try new MacroTyper(typer, macroDdef).typed - catch { case MacroBodyTypecheckException => EmptyTree } - - class MacroTyper(val typer: Typer, val macroDdef: DefDef) extends MacroErrors { - private def typed1Expr(tree: Tree) = typer.typed1(tree, EXPRmode, WildcardType) - - // Phase I: sanity checks + def typedMacroBody(typer: Typer, macroDdef: DefDef): Tree = { val macroDef = macroDdef.symbol - macroLogVerbose("typechecking macro def %s at %s".format(macroDef, macroDdef.pos)) assert(macroDef.isMacro, macroDdef) - if (fastTrack contains macroDef) MacroDefIsFastTrack() - if (!typer.checkFeature(macroDdef.pos, MacrosFeature, immediate = true)) MacroFeatureNotEnabled() - - // we use typed1 instead of typed, because otherwise adapt is going to mess us up - // if adapt sees <qualifier>.<method>, it will want to perform eta-expansion and will fail - // unfortunately, this means that we have to manually trigger macro expansion - // because it's adapt which is responsible for automatic expansion during typechecking - def typecheckRhs(rhs: Tree): Tree = { - try { - // interestingly enough, just checking isErroneous doesn't cut it - // e.g. a "type arguments [U] do not conform to method foo's type parameter bounds" error - // doesn't manifest itself as an error in the resulting tree - val prevNumErrors = reporter.ERROR.count - var rhs1 = typed1Expr(rhs) - def rhsNeedsMacroExpansion = rhs1.symbol != null && rhs1.symbol.isTermMacro && !rhs1.symbol.isErroneous - while (rhsNeedsMacroExpansion) { - rhs1 = macroExpand1(typer, rhs1) match { - case Success(expanded) => - try { - val typechecked = typed1Expr(expanded) - macroLogVerbose("typechecked1:%n%s%n%s".format(typechecked, showRaw(typechecked))) - typechecked - } finally { - popMacroContext() - } - case Fallback(fallback) => - typed1Expr(fallback) - case Delayed(delayed) => - typer.instantiate(delayed, EXPRmode, WildcardType) - case Skipped(skipped) => - skipped - case Failure(failure) => - failure - } - } - val typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors - if (typecheckedWithErrors) MacroDefUntypeableBodyError() - rhs1 - } catch { - case ex: TypeError => - typer.reportTypeError(context, rhs.pos, ex) - MacroDefUntypeableBodyError() - } - } - - // Phase II: typecheck the right-hand side of the macro def - val typed = typecheckRhs(macroDdef.rhs) - typed match { - case MacroImplReference(_, meth, _) if meth == Predef_??? => - bindMacroImpl(macroDef, typed) - MacroDefIsQmarkQmarkQmark() - case MacroImplReference(owner, meth, targs) => - if (!meth.isMethod) MacroDefInvalidBodyError() - if (!meth.isPublic) MacroImplNotPublicError() - if (meth.isOverloaded) MacroImplOverloadedError() - if (!owner.isStaticOwner && !owner.moduleClass.isStaticOwner) MacroImplNotStaticError() - if (meth.typeParams.length != targs.length) MacroImplWrongNumberOfTypeArgumentsError(typed) - bindMacroImpl(macroDef, typed) - case _ => - MacroDefInvalidBodyError() - } - - // Phase III: check compatibility between the macro def and its macro impl - // this check ignores type tag evidence parameters, because type tag context bounds are optional - // aXXX (e.g. aparamss) => characteristics of the 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) - - try { - map2(aparamss, rparamss)((aparams, rparams) => { - if (aparams.length < rparams.length) MacroImplMissingParamsError(aparams, rparams) - if (rparams.length < aparams.length) MacroImplExtraParamsError(aparams, rparams) - }) - - // 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) - val aparamtpe = aparam.tpe.dealias match { - case RefinedType(List(tpe), Scope(sym)) if tpe =:= MacroContextClass.tpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe - case tpe => tpe - } - checkMacroImplParamTypeMismatch(atpeToRtpe(aparamtpe), rparam) - }) - - checkMacroImplResultTypeMismatch(atpeToRtpe(aret), rret) - - val maxLubDepth = lubDepth(aparamss.flatten map (_.tpe)) max lubDepth(rparamss.flatten map (_.tpe)) - val atargs = solvedTypes(atvars, atparams, atparams map varianceInType(aret), upper = false, depth = maxLubDepth) - val boundsOk = typer.silent(_.infer.checkBounds(macroDdef, NoPrefix, NoSymbol, atparams, atargs, "")) - boundsOk match { - case SilentResultValue(true) => // do nothing, success - case SilentResultValue(false) | SilentTypeError(_) => MacroImplTargMismatchError(atargs, atparams) - } - } catch { - case ex: NoInstance => MacroImplTparamInstantiationError(atparams, ex) - } - } - - /** Macro classloader that is used to resolve and run macro implementations. - * Loads classes from from -cp (aka the library classpath). - * Is also capable of detecting REPL and reusing its classloader. - */ - lazy val macroClassloader: ClassLoader = findMacroClassLoader() - /** Produces a function that can be used to invoke macro implementation for a given macro definition: - * 1) Looks up macro implementation symbol in this universe. - * 2) Loads its enclosing class from the macro classloader. - * 3) Loads the companion of that enclosing class from the macro classloader. - * 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. - */ - type MacroRuntime = MacroArgs => Any - private val macroRuntimesCache = perRunCaches.newWeakMap[Symbol, MacroRuntime]() - private def macroRuntime(macroDef: Symbol): MacroRuntime = { - macroLogVerbose(s"looking for macro implementation: $macroDef") + macroLogVerbose("typechecking macro def %s at %s".format(macroDef, macroDdef.pos)) if (fastTrack contains macroDef) { - macroLogVerbose("macro expansion is serviced by a fast track") - fastTrack(macroDef) + macroLogVerbose("typecheck terminated unexpectedly: macro is fast track") + assert(!macroDdef.tpt.isEmpty, "fast track macros must provide result type") + EmptyTree } else { - macroRuntimesCache.getOrElseUpdate(macroDef, { - val binding = loadMacroImplBinding(macroDef) - val className = binding.className - val methName = binding.methName - macroLogVerbose(s"resolved implementation as $className.$methName") - - if (binding.className == Predef_???.owner.fullName.toString && binding.methName == Predef_???.name.encoded) { - args => throw new AbortMacroException(args.c.enclosingPosition, "macro implementation is missing") - } else { - // I don't use Scala reflection here, because it seems to interfere with JIT magic - // whenever you instantiate a mirror (and not do anything with in, just instantiate), performance drops by 15-20% - // I'm not sure what's the reason - for me it's pure voodoo - // upd. my latest experiments show that everything's okay - // it seems that in 2.10.1 we can easily switch to Scala reflection - try { - macroLogVerbose(s"loading implementation class: $className") - macroLogVerbose(s"classloader is: ${ReflectionUtils.show(macroClassloader)}") - val implObj = ReflectionUtils.staticSingletonInstance(macroClassloader, className) - // relies on the fact that macro impls cannot be overloaded - // so every methName can resolve to at maximum one method - val implMeths = implObj.getClass.getDeclaredMethods.find(_.getName == methName) - val implMeth = implMeths getOrElse { throw new NoSuchMethodException(s"$className.$methName") } - macroLogVerbose(s"successfully loaded macro impl as ($implObj, $implMeth)") - args => implMeth.invoke(implObj, ((args.c +: args.others) map (_.asInstanceOf[AnyRef])): _*) - } catch { - case ex: Exception => - macroLogVerbose(s"macro runtime failed to load: ${ex.toString}") - macroDef setFlag IS_ERROR - null - } - } - }) + def fail() = { if (macroDef != null) macroDef setFlag IS_ERROR; macroDdef setType ErrorType; EmptyTree } + def success(macroImplRef: Tree) = { bindMacroImpl(macroDef, macroImplRef); macroImplRef } + + if (!typer.checkFeature(macroDdef.pos, MacrosFeature, immediate = true)) { + macroLogVerbose("typecheck terminated unexpectedly: language.experimental.macros feature is not enabled") + fail() + } else { + val macroDdef1: macroDdef.type = macroDdef + val typer1: typer.type = typer + val macroCompiler = new { + val global: self.global.type = self.global + val typer: self.global.analyzer.Typer = typer1.asInstanceOf[self.global.analyzer.Typer] + val macroDdef: self.global.DefDef = macroDdef1 + } with DefaultMacroCompiler + val macroImplRef = macroCompiler.resolveMacroImpl + if (macroImplRef.isEmpty) fail() else success(macroImplRef) + } } } - private def macroContext(typer: Typer, prefixTree: Tree, expandeeTree: Tree): MacroContext = { + def macroContext(typer: Typer, prefixTree: Tree, expandeeTree: Tree): MacroContext = { new { val universe: self.global.type = self.global val callsiteTyper: universe.analyzer.Typer = typer.asInstanceOf[global.analyzer.Typer] @@ -560,92 +343,111 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { case class MacroArgs(c: MacroContext, others: List[Any]) private def macroArgs(typer: Typer, expandee: Tree): MacroArgs = { - val macroDef = expandee.symbol - val prefixTree = expandee.collect{ case Select(qual, name) => qual }.headOption.getOrElse(EmptyTree) - val context = expandee.attachments.get[MacroRuntimeAttachment].flatMap(_.macroContext).getOrElse(macroContext(typer, prefixTree, expandee)) - var typeArgs = List[Tree]() - val exprArgs = ListBuffer[List[Expr[_]]]() - def collectMacroArgs(tree: Tree): Unit = tree match { - case Apply(fn, args) => - // todo. infer precise typetag for this Expr, namely the declared type of the corresponding macro impl argument - exprArgs.prepend(args map (arg => context.Expr[Nothing](arg)(TypeTag.Nothing))) - collectMacroArgs(fn) - case TypeApply(fn, args) => - typeArgs = args - collectMacroArgs(fn) - case _ => - } - collectMacroArgs(expandee) + val macroDef = expandee.symbol + val paramss = macroDef.paramss + val treeInfo.Applied(core, targs, argss) = expandee + val prefix = core match { case Select(qual, _) => qual; case _ => EmptyTree } + val context = expandee.attachments.get[MacroRuntimeAttachment].flatMap(_.macroContext).getOrElse(macroContext(typer, prefix, expandee)) - val argcDoesntMatch = macroDef.paramss.length != exprArgs.length - val nullaryArgsEmptyParams = exprArgs.isEmpty && macroDef.paramss == ListOfNil - if (argcDoesntMatch && !nullaryArgsEmptyParams) { typer.TyperErrorGen.MacroPartialApplicationError(expandee) } + macroLogVerbose(sm""" + |context: $context + |prefix: $prefix + |targs: $targs + |argss: $argss + |paramss: $paramss + """.trim) - val argss: List[List[Any]] = exprArgs.toList - macroLogVerbose(s"context: $context") - macroLogVerbose(s"argss: $argss") + import typer.TyperErrorGen._ + val isNullaryArgsEmptyParams = argss.isEmpty && paramss == ListOfNil + if (paramss.length < argss.length) MacroTooManyArgumentListsError(expandee) + if (paramss.length > argss.length && !isNullaryArgsEmptyParams) MacroTooFewArgumentListsError(expandee) - val preparedArgss: List[List[Any]] = + val macroImplArgs: List[Any] = if (fastTrack contains macroDef) { // Take a dry run of the fast track implementation - if (fastTrack(macroDef) validate expandee) argss - else typer.TyperErrorGen.MacroPartialApplicationError(expandee) + if (fastTrack(macroDef) validate expandee) argss.flatten + else MacroTooFewArgumentListsError(expandee) } else { - // if paramss have typetag context bounds, add an arglist to argss if necessary and instantiate the corresponding evidences - // consider the following example: - // - // class D[T] { - // class C[U] { - // def foo[V] = macro Impls.foo[T, U, V] - // } - // } - // - // val outer1 = new D[Int] - // 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 - // also, macro implementation reference may contain a regular type as a type argument, then we pass it verbatim - val binding = loadMacroImplBinding(macroDef) - macroLogVerbose(s"binding: $binding") - val tags = binding.signature filter (_ != -1) map (paramPos => { - val targ = binding.targs(paramPos).tpe.typeSymbol - val tpe = if (targ.isTypeParameterOrSkolem) { - if (targ.owner == macroDef) { - // doesn't work when macro def is compiled separately from its usages - // then targ is not a skolem and isn't equal to any of macroDef.typeParams - // val argPos = targ.deSkolemize.paramPos - val argPos = macroDef.typeParams.indexWhere(_.name == targ.name) - typeArgs(argPos).tpe + def calculateMacroArgs(binding: MacroImplBinding) = { + val signature = if (binding.isBundle) binding.signature else binding.signature.tail + macroLogVerbose(s"binding: $binding") + + // STEP I: prepare value arguments of the macro expansion + // wrap argss in c.Expr if necessary (i.e. if corresponding macro impl param is of type c.Expr[T]) + // expand varargs (nb! varargs can apply to any parameter section, not necessarily to the last one) + val trees = map3(argss, paramss, signature)((args, defParams, implParams) => { + val isVarargs = isVarArgsList(defParams) + if (isVarargs) { + if (defParams.length > args.length + 1) MacroTooFewArgumentsError(expandee) + } else { + if (defParams.length < args.length) MacroTooManyArgumentsError(expandee) + if (defParams.length > args.length) MacroTooFewArgumentsError(expandee) + } + + val wrappedArgs = mapWithIndex(args)((arg, j) => { + val fingerprint = implParams(min(j, implParams.length - 1)) + fingerprint match { + case Lifted => context.Expr[Nothing](arg)(TypeTag.Nothing) // TODO: SI-5752 + case _ => abort(s"unexpected fingerprint $fingerprint in $binding with paramss being $paramss " + + s"corresponding to arg $arg in $argss") + } + }) + + if (isVarargs) { + val (normal, varargs) = wrappedArgs splitAt (defParams.length - 1) + normal :+ varargs // pack all varargs into a single Seq argument (varargs Scala style) + } else wrappedArgs + }) + macroLogVerbose(s"trees: $trees") + + // STEP II: prepare type arguments of the macro expansion + // if paramss have typetag context bounds, add an arglist to argss if necessary and instantiate the corresponding evidences + // consider the following example: + // + // class D[T] { + // class C[U] { + // def foo[V] = macro Impls.foo[T, U, V] + // } + // } + // + // val outer1 = new D[Int] + // 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 + // also, macro implementation reference may contain a regular type as a type argument, then we pass it verbatim + val tags = signature.flatten collect { case f if f.isTag => f.paramPos } map (paramPos => { + val targ = binding.targs(paramPos).tpe.typeSymbol + val tpe = if (targ.isTypeParameterOrSkolem) { + if (targ.owner == macroDef) { + // doesn't work when macro def is compiled separately from its usages + // then targ is not a skolem and isn't equal to any of macroDef.typeParams + // val argPos = targ.deSkolemize.paramPos + val argPos = macroDef.typeParams.indexWhere(_.name == targ.name) + targs(argPos).tpe + } else + targ.tpe.asSeenFrom( + if (prefix == EmptyTree) macroDef.owner.tpe else prefix.tpe, + macroDef.owner) } else - targ.tpe.asSeenFrom( - if (prefixTree == EmptyTree) macroDef.owner.tpe else prefixTree.tpe, - macroDef.owner) - } else - targ.tpe - context.WeakTypeTag(tpe) - }) - macroLogVerbose(s"tags: $tags") - - // transforms argss taking into account varargness of paramss - // note that typetag context bounds are only declared on macroImpls - // so this optional arglist might not match macroDef's paramlist - // nb! varargs can apply to any parameter section, not necessarily to the last one - mapWithIndex(argss :+ tags)((as, i) => { - val mapsToParamss = macroDef.paramss.indices contains i - if (mapsToParamss) { - val ps = macroDef.paramss(i) - if (isVarArgsList(ps)) { - val (normal, varargs) = as splitAt (ps.length - 1) - normal :+ varargs // pack all varargs into a single List argument - } else as - } else as - }) + targ.tpe + context.WeakTypeTag(tpe) + }) + macroLogVerbose(s"tags: $tags") + + // if present, tags always come in a separate parameter/argument list + // that's because macro impls can't have implicit parameters other than c.WeakTypeTag[T] + (trees :+ tags).flatten + } + + val binding = loadMacroImplBinding(macroDef) + if (binding.is_???) Nil + else calculateMacroArgs(binding) } - macroLogVerbose(s"preparedArgss: $preparedArgss") - MacroArgs(context, preparedArgss.flatten) + macroLogVerbose(s"macroImplArgs: $macroImplArgs") + MacroArgs(context, macroImplArgs) } /** Keeps track of macros in-flight. @@ -680,7 +482,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * 1) If necessary desugars the `expandee` to fit into `macroExpand1` * * Then `macroExpand1`: - * 2) Checks whether the expansion needs to be delayed (see `mustDelayMacroExpansion`) + * 2) Checks whether the expansion needs to be delayed * 3) Loads macro implementation using `macroMirror` * 4) Synthesizes invocation arguments for the macro implementation * 5) Checks that the result is a tree or an expr bound to this universe @@ -997,3 +799,21 @@ object MacrosStats { val macroExpandCount = Statistics.newCounter ("#macro expansions", "typer") val macroExpandNanos = Statistics.newSubTimer("time spent in macroExpand", typerNanos) } + +class Fingerprint(val value: Int) extends AnyVal { + def paramPos = { assert(isTag, this); value } + def isTag = value >= 0 + def isOther = this == Other + def isExpr = this == Lifted + override def toString = this match { + case Other => "Other" + case Lifted => "Expr" + case _ => s"Tag($value)" + } +} + +object Fingerprint { + def Tagged(tparamPos: Int) = new Fingerprint(tparamPos) + val Other = new Fingerprint(-1) + val Lifted = new Fingerprint(-2) +} diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 4ccfb31878..c9849eebb5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -126,7 +126,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans val defaultMethodNames = defaultGetters map (sym => nme.defaultGetterToMethod(sym.name)) defaultMethodNames.toList.distinct foreach { name => - val methods = clazz.info.findMember(name, 0L, METHOD, false).alternatives + val methods = clazz.info.findMember(name, 0L, METHOD, stableOnly = false).alternatives def hasDefaultParam(tpe: Type): Boolean = tpe match { case MethodType(params, restpe) => (params exists (_.hasDefault)) || hasDefaultParam(restpe) case _ => false diff --git a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala index 64fcda3b80..c616ded704 100644 --- a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala +++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala @@ -10,7 +10,7 @@ trait StdAttachments { * At times we need to store this info, because macro expansion can be delayed until its targs are inferred. * After a macro application has been successfully expanded, this attachment is destroyed. */ - type UnaffiliatedMacroContext = scala.reflect.macros.runtime.Context + type UnaffiliatedMacroContext = scala.reflect.macros.contexts.Context type MacroContext = UnaffiliatedMacroContext { val universe: self.global.type } case class MacroRuntimeAttachment(delayed: Boolean, typerContext: Context, macroContext: Option[MacroContext]) @@ -127,4 +127,31 @@ trait StdAttachments { /** Determines whether the given tree has an associated SuperArgsAttachment. */ def hasSuperArgs(tree: Tree): Boolean = superArgs(tree).nonEmpty + + /** @see markMacroImplRef + */ + case object MacroImplRefAttachment + + /** Marks the tree as a macro impl reference, which is a naked reference to a method. + * + * This is necessary for typechecking macro impl references (see `DefaultMacroCompiler.defaultResolveMacroImpl`), + * because otherwise typing a naked reference will result in the "follow this method with `_' if you want to + * treat it as a partially applied function" errors. + * + * This mark suppresses adapt except for when the annottee is a macro application. + */ + def markMacroImplRef(tree: Tree): Tree = tree.updateAttachment(MacroImplRefAttachment) + + /** Unmarks the tree as a macro impl reference (see `markMacroImplRef` for more information). + * + * This is necessary when a tree that was previously deemed to be a macro impl reference, + * typechecks to be a macro application. Then we need to unmark it, expand it and try to treat + * its expansion as a macro impl reference. + */ + def unmarkMacroImplRef(tree: Tree): Tree = tree.removeAttachment[MacroImplRefAttachment.type] + + /** Determines whether a tree should or should not be adapted, + * because someone has put MacroImplRefAttachment on it. + */ + def isMacroImplRef(tree: Tree): Boolean = tree.attachments.get[MacroImplRefAttachment.type].isDefined }
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 238727d2e9..c9aa27cf49 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1234,7 +1234,10 @@ trait Typers extends Adaptations with Tags { } // begin adapt - tree.tpe match { + if (isMacroImplRef(tree)) { + if (treeInfo.isMacroApplication(tree)) adapt(unmarkMacroImplRef(tree), mode, pt, original) + else tree + } else tree.tpe match { case atp @ AnnotatedType(_, _, _) if canAdaptAnnotations(tree, this, mode, pt) => // (-1) adaptAnnotations(tree, this, mode, pt) case ct @ ConstantType(value) if mode.inNone(TYPEmode | FUNmode) && (ct <:< pt) && canAdaptConstantTypeToLiteral => // (0) @@ -5534,7 +5537,7 @@ trait Typers extends Adaptations with Tags { val isMacroBodyOkay = !tree.symbol.isErroneous && !(tree1 exists (_.isErroneous)) && tree1 != EmptyTree val shouldInheritMacroImplReturnType = ddef.tpt.isEmpty - if (isMacroBodyOkay && shouldInheritMacroImplReturnType) computeMacroDefTypeFromMacroImpl(ddef, tree1.symbol) else AnyTpe + if (isMacroBodyOkay && shouldInheritMacroImplReturnType) computeMacroDefTypeFromMacroImplRef(ddef, tree1) else AnyTpe } def transformedOr(tree: Tree, op: => Tree): Tree = transformed remove tree match { diff --git a/src/compiler/scala/tools/reflect/FastTrack.scala b/src/compiler/scala/tools/reflect/FastTrack.scala index 3cf19396ee..5a0ff4f6db 100644 --- a/src/compiler/scala/tools/reflect/FastTrack.scala +++ b/src/compiler/scala/tools/reflect/FastTrack.scala @@ -5,6 +5,7 @@ import scala.reflect.reify.Taggers import scala.tools.nsc.typechecker.{ Analyzer, Macros } import scala.reflect.runtime.Macros.currentMirror import scala.reflect.api.Universe +import scala.reflect.macros.compiler.DefaultMacroCompiler /** Optimizes system macro expansions by hardwiring them directly to their implementations * bypassing standard reflective load and invoke to avoid the overhead of Java/Scala reflection. diff --git a/src/compiler/scala/tools/reflect/MacroImplementations.scala b/src/compiler/scala/tools/reflect/MacroImplementations.scala index 109c148b7e..8e1bcb5f87 100644 --- a/src/compiler/scala/tools/reflect/MacroImplementations.scala +++ b/src/compiler/scala/tools/reflect/MacroImplementations.scala @@ -1,6 +1,6 @@ package scala.tools.reflect -import scala.reflect.macros.runtime.Context +import scala.reflect.macros.contexts.Context import scala.collection.mutable.ListBuffer import scala.collection.mutable.Stack import scala.reflect.internal.util.OffsetPosition diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 6145e61c4f..c53d10bd87 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -165,7 +165,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => transformDuringTyper(expr, withImplicitViewsDisabled = withImplicitViewsDisabled, withMacrosDisabled = withMacrosDisabled)( (currentTyper, expr) => { trace("typing (implicit views = %s, macros = %s): ".format(!withImplicitViewsDisabled, !withMacrosDisabled))(showAttributed(expr, true, true, settings.Yshowsymkinds.value)) - currentTyper.silent(_.typed(expr, pt)) match { + currentTyper.silent(_.typed(expr, pt), reportAmbiguousErrors = false) match { case analyzer.SilentResultValue(result) => trace("success: ")(showAttributed(result, true, true, settings.Yshowsymkinds.value)) result |