diff options
Diffstat (limited to 'src')
5 files changed, 47 insertions, 26 deletions
diff --git a/src/compiler/scala/reflect/macros/compiler/Errors.scala b/src/compiler/scala/reflect/macros/compiler/Errors.scala index 6ec111cf7c..9b56e417e2 100644 --- a/src/compiler/scala/reflect/macros/compiler/Errors.scala +++ b/src/compiler/scala/reflect/macros/compiler/Errors.scala @@ -29,6 +29,10 @@ trait Errors extends Traces { def MacroImplWrongNumberOfTypeArgumentsError() = implRefError(TypedApplyWrongNumberOfTpeParametersErrorMessage(macroImplRef)) + def MacroBundleNonStaticError() = implRefError("macro bundles must be static") + + def MacroBundleWrongShapeError() = implRefError("macro bundles must be monomorphic traits extending scala.reflect.macros.Macro and not implementing its `val c: Context` member") + // compatibility errors // helpers diff --git a/src/compiler/scala/reflect/macros/compiler/Resolvers.scala b/src/compiler/scala/reflect/macros/compiler/Resolvers.scala index 46c4e24817..f950059be4 100644 --- a/src/compiler/scala/reflect/macros/compiler/Resolvers.scala +++ b/src/compiler/scala/reflect/macros/compiler/Resolvers.scala @@ -9,7 +9,7 @@ trait Resolvers { import global._ import analyzer._ - import definitions._ + import definitions.{EmptyPackageClass => _, _} import treeInfo._ import gen._ @@ -33,7 +33,7 @@ trait Resolvers { */ lazy val macroImplRef: Tree = { val (maybeBundleRef, methName, targs) = macroDdef.rhs match { - case Applied(methRef @ Select(bundleRef @ RefTree(qual, bundleName), methName), targs, Nil) => + case Applied(Select(Applied(RefTree(qual, bundleName), _, Nil), methName), targs, Nil) => (RefTree(qual, bundleName.toTypeName), methName, targs) case Applied(Ident(methName), targs, Nil) => (Ident(context.owner.enclClass), methName, targs) @@ -41,30 +41,36 @@ trait Resolvers { (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") + val untypedImplRef = typer.silent(_.typedTypeConstructor(maybeBundleRef)) match { + case SilentResultValue(result) if result.tpe.baseClasses.contains(MacroClass) => + val bundleProto = result.tpe.typeSymbol + val bundlePkg = bundleProto.enclosingPackageClass + if (!isMacroBundleProtoType(bundleProto.tpe)) MacroBundleWrongShapeError() + if (!bundleProto.owner.isStaticOwner) MacroBundleNonStaticError() - // 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)) { + // synthesize the bundle, i.e. given a static `trait Foo extends Macro { def expand = ... } ` + // create a top-level definition `class Foo$Bundle(val c: Context) extends Foo` in a package next to `Foo` + val bundlePid = gen.mkUnattributedRef(bundlePkg) + val bundlePrefix = + if (bundlePkg == EmptyPackageClass) bundleProto.fullName('$') + else bundleProto.fullName('$').substring(bundlePkg.fullName('$').length + 1) + val bundleName = TypeName(bundlePrefix + tpnme.MACRO_BUNDLE_SUFFIX) + val existingBundle = bundleProto.enclosingPackageClass.info.decl(bundleName) + if (!currentRun.compiles(existingBundle)) { 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)), noSelfType, List(contextField, invokerCtor)))) - currentRun.compileLate(PackageDef(invokerPid, List(invoker))) + val bundleCtor = DefDef(Modifiers(), nme.CONSTRUCTOR, Nil, List(List(contextParam)), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(())))) + val bundleParent = gen.mkAppliedTypeTree(Ident(bundleProto), bundleProto.typeParams.map(sym => Ident(sym.name))) + val bundleTemplate = Template(List(bundleParent), noSelfType, List(contextField, bundleCtor)) + val bundle = atPos(bundleProto.pos)(ClassDef(NoMods, bundleName, bundleProto.typeParams.map(TypeDef(_)), bundleTemplate)) + currentRun.compileLate(PackageDef(bundlePid, List(bundle))) } // 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) + // `new FooBundle(???).macroName` plus the optional type arguments + val bundleInstance = New(Select(bundlePid, bundleName), List(List(Ident(Predef_???)))) + atPos(macroDdef.rhs.pos)(gen.mkTypeApply(Select(bundleInstance, methName), targs)) case _ => macroDdef.rhs } diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 7555df6775..7b2e40b59c 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -541,6 +541,7 @@ trait Definitions extends api.StandardDefinitions { lazy val LiftableClass = getClassIfDefined("scala.reflect.api.Liftable") // defined in scala-reflect.jar, so we need to be careful lazy val MacroClass = getClassIfDefined("scala.reflect.macros.Macro") // defined in scala-reflect.jar, so we need to be careful + def MacroContextValue = MacroClass.map(sym => getMemberValue(sym, nme.c)) lazy val MacroContextClass = getClassIfDefined("scala.reflect.macros.Context") // defined in scala-reflect.jar, so we need to be careful def MacroContextPrefix = MacroContextClass.map(sym => getMemberMethod(sym, nme.prefix)) def MacroContextPrefixType = MacroContextClass.map(sym => getTypeMember(sym, tpnme.PrefixType)) @@ -640,10 +641,21 @@ trait Definitions extends api.StandardDefinitions { def unspecializedTypeArgs(tp: Type): List[Type] = (tp baseType unspecializedSymbol(tp.typeSymbolDirect)).typeArgs - def isMacroBundleType(tp: Type) = { + def isMacroBundleType(tp: Type) = tp.baseClasses match { + case _ :: proto :: _ if isMacroBundleProtoType(proto.tpe) => true + case _ => false + } + + def isMacroBundleProtoType(tp: Type) = { + val sym = tp.typeSymbol val isNonTrivial = tp != ErrorType && tp != NothingTpe && tp != NullTpe - val isMacroCompatible = MacroClass != NoSymbol && tp <:< MacroClass.tpe - isNonTrivial && isMacroCompatible + val isMacroCompatible = MacroClass != NoSymbol && tp.baseClasses.contains(MacroClass) + val isBundlePrototype = sym != MacroClass && sym.isTrait && { + val c = sym.info.member(nme.c) + val cIsOk = c.overrideChain.contains(MacroContextValue) && c.isDeferred + cIsOk && sym.isMonomorphicType + } + isNonTrivial && isMacroCompatible && isBundlePrototype } def isIterableType(tp: Type) = tp <:< classExistentialType(IterableClass) diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index c1fd5b3cd6..0aee71c26e 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -214,6 +214,7 @@ trait StdNames { final val WILDCARD_STAR: NameType = "_*" final val REIFY_TREECREATOR_PREFIX: NameType = "$treecreator" final val REIFY_TYPECREATOR_PREFIX: NameType = "$typecreator" + final val MACRO_BUNDLE_SUFFIX: NameType = "$Bundle" final val Any: NameType = "Any" final val AnyVal: NameType = "AnyVal" @@ -307,9 +308,6 @@ trait StdNames { val FAKE_LOCAL_THIS: NameType = "this$" val LAZY_LOCAL: NameType = "$lzy" val LAZY_SLOW_SUFFIX: NameType = "$lzycompute" - val MACRO_INVOKER_PACKAGE: NameType = "scala.reflect.macros.synthetic" - // TODO: if I use dollars in MACRO_INVOKER_SUFFIX, as in "$Invoker$", then Scala reflection fails to load implementations - val MACRO_INVOKER_SUFFIX: NameType = "Invoker" val UNIVERSE_BUILD_PREFIX: NameType = "$u.build." val UNIVERSE_PREFIX: NameType = "$u." val UNIVERSE_SHORT: NameType = "$u" diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index e44461f964..720d8bfe4a 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -209,7 +209,8 @@ abstract class TreeGen extends macros.TreeBuilder { /** Builds a type application node if args.nonEmpty, returns fun otherwise. */ def mkTypeApply(fun: Tree, targs: List[Tree]): Tree = if (targs.isEmpty) fun else TypeApply(fun, targs) - + def mkAppliedTypeTree(fun: Tree, targs: List[Tree]): Tree = + if (targs.isEmpty) fun else AppliedTypeTree(fun, targs) def mkAttributedTypeApply(target: Tree, method: Symbol, targs: List[Type]): Tree = mkTypeApply(mkAttributedSelect(target, method), targs map TypeTree) |