diff options
25 files changed, 216 insertions, 29 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) diff --git a/test/files/neg/macro-bundle-abstract.check b/test/files/neg/macro-bundle-abstract.check new file mode 100644 index 0000000000..4b07adcc95 --- /dev/null +++ b/test/files/neg/macro-bundle-abstract.check @@ -0,0 +1,4 @@ +macro-bundle-abstract.scala:5: error: class Bundle$Bundle needs to be abstract, since method deferred in trait Bundle of type => Int is not defined +trait Bundle extends Macro { + ^ +one error found diff --git a/test/files/neg/macro-bundle-abstract.scala b/test/files/neg/macro-bundle-abstract.scala new file mode 100644 index 0000000000..2b302045da --- /dev/null +++ b/test/files/neg/macro-bundle-abstract.scala @@ -0,0 +1,12 @@ +import scala.language.experimental.macros +import scala.reflect.macros.Macro +import scala.reflect.macros.Context + +trait Bundle extends Macro { + def deferred: Int + def impl = ??? +} + +object Macros { + def foo = macro Bundle.impl +}
\ No newline at end of file diff --git a/test/files/neg/macro-bundle-class.check b/test/files/neg/macro-bundle-class.check new file mode 100644 index 0000000000..92695390ab --- /dev/null +++ b/test/files/neg/macro-bundle-class.check @@ -0,0 +1,4 @@ +macro-bundle-class.scala:10: error: macro bundles must be monomorphic traits extending scala.reflect.macros.Macro and not implementing its `val c: Context` member + def foo = macro Bundle.impl + ^ +one error found diff --git a/test/files/neg/macro-bundle-class.scala b/test/files/neg/macro-bundle-class.scala new file mode 100644 index 0000000000..4b92cdd40f --- /dev/null +++ b/test/files/neg/macro-bundle-class.scala @@ -0,0 +1,11 @@ +import scala.language.experimental.macros +import scala.reflect.macros.Macro +import scala.reflect.macros.Context + +class Bundle(val c: Context) extends Macro { + def impl = ??? +} + +object Macros { + def foo = macro Bundle.impl +}
\ No newline at end of file diff --git a/test/files/neg/macro-bundle-nonmacro.check b/test/files/neg/macro-bundle-nonmacro.check new file mode 100644 index 0000000000..5a265b5724 --- /dev/null +++ b/test/files/neg/macro-bundle-nonmacro.check @@ -0,0 +1,4 @@ +macro-bundle-nonmacro.scala:8: error: not found: value Bundle + def foo = Bundle.impl + ^ +one error found diff --git a/test/files/neg/macro-bundle-nonmacro.scala b/test/files/neg/macro-bundle-nonmacro.scala new file mode 100644 index 0000000000..c7d99f4582 --- /dev/null +++ b/test/files/neg/macro-bundle-nonmacro.scala @@ -0,0 +1,9 @@ +import scala.language.experimental.macros + +trait Bundle { + def impl = ??? +} + +object Macros { + def foo = Bundle.impl +}
\ No newline at end of file diff --git a/test/files/neg/macro-bundle-object.check b/test/files/neg/macro-bundle-object.check new file mode 100644 index 0000000000..e122001427 --- /dev/null +++ b/test/files/neg/macro-bundle-object.check @@ -0,0 +1,7 @@ +macro-bundle-object.scala:11: error: macro implementation has wrong shape: + required: (c: scala.reflect.macros.Context): c.Expr[Any] + found : : Nothing +number of parameter sections differ + def foo = macro Bundle.impl + ^ +one error found diff --git a/test/files/neg/macro-bundle-object.scala b/test/files/neg/macro-bundle-object.scala new file mode 100644 index 0000000000..98c4238a62 --- /dev/null +++ b/test/files/neg/macro-bundle-object.scala @@ -0,0 +1,12 @@ +import scala.language.experimental.macros +import scala.reflect.macros.Macro +import scala.reflect.macros.Context + +object Bundle extends Macro { + val c: Context = ??? + def impl = ??? +} + +object Macros { + def foo = macro Bundle.impl +}
\ No newline at end of file diff --git a/test/files/neg/macro-bundle-polymorphic.check b/test/files/neg/macro-bundle-polymorphic.check new file mode 100644 index 0000000000..204bd30bca --- /dev/null +++ b/test/files/neg/macro-bundle-polymorphic.check @@ -0,0 +1,10 @@ +macro-bundle-polymorphic.scala:10: error: macro bundles must be monomorphic traits extending scala.reflect.macros.Macro and not implementing its `val c: Context` member + def foo = macro Bundle.impl + ^ +macro-bundle-polymorphic.scala:11: error: macro bundles must be monomorphic traits extending scala.reflect.macros.Macro and not implementing its `val c: Context` member + def foo = macro Bundle[Int].impl + ^ +macro-bundle-polymorphic.scala:12: error: macro bundles must be monomorphic traits extending scala.reflect.macros.Macro and not implementing its `val c: Context` member + def foo = macro Bundle[Int, Nothing].impl + ^ +three errors found diff --git a/test/files/neg/macro-bundle-polymorphic.scala b/test/files/neg/macro-bundle-polymorphic.scala new file mode 100644 index 0000000000..0468d841bd --- /dev/null +++ b/test/files/neg/macro-bundle-polymorphic.scala @@ -0,0 +1,13 @@ +import scala.language.experimental.macros +import scala.reflect.macros.Macro +import scala.reflect.macros.Context + +trait Bundle[T] extends Macro { + def impl = ??? +} + +object Macros { + def foo = macro Bundle.impl + def foo = macro Bundle[Int].impl + def foo = macro Bundle[Int, Nothing].impl +}
\ No newline at end of file diff --git a/test/files/neg/macro-bundle-trait.check b/test/files/neg/macro-bundle-trait.check new file mode 100644 index 0000000000..972788c577 --- /dev/null +++ b/test/files/neg/macro-bundle-trait.check @@ -0,0 +1,4 @@ +macro-bundle-trait.scala:11: error: macro bundles must be monomorphic traits extending scala.reflect.macros.Macro and not implementing its `val c: Context` member + def foo = macro Bundle.impl + ^ +one error found diff --git a/test/files/neg/macro-bundle-trait.scala b/test/files/neg/macro-bundle-trait.scala new file mode 100644 index 0000000000..ddc87f6db3 --- /dev/null +++ b/test/files/neg/macro-bundle-trait.scala @@ -0,0 +1,12 @@ +import scala.language.experimental.macros +import scala.reflect.macros.Macro +import scala.reflect.macros.Context + +trait Bundle extends Macro { + val c: Context = ??? + def impl = ??? +} + +object Macros { + def foo = macro Bundle.impl +}
\ No newline at end of file diff --git a/test/files/run/macro-bundle-static.check b/test/files/run/macro-bundle-static.check new file mode 100644 index 0000000000..37c8eaf27a --- /dev/null +++ b/test/files/run/macro-bundle-static.check @@ -0,0 +1,6 @@ +() +Int +() +true +IntInt +true diff --git a/test/files/run/macro-bundle-static/Impls_Macros_1.scala b/test/files/run/macro-bundle-static/Impls_Macros_1.scala new file mode 100644 index 0000000000..831dac6df5 --- /dev/null +++ b/test/files/run/macro-bundle-static/Impls_Macros_1.scala @@ -0,0 +1,31 @@ +import scala.reflect.macros.Context +import scala.reflect.macros.Macro +import scala.language.experimental.macros + +object Enclosing { + trait Impl extends Macro { + def mono = c.literalUnit + def poly[T: c.WeakTypeTag] = c.literal(c.weakTypeOf[T].toString) + def weird = macro mono + } +} + +object Macros { + def mono = macro Enclosing.Impl.mono + def poly[T] = macro Enclosing.Impl.poly[T] +} + +package pkg { + object Enclosing { + trait Impl extends Macro { + def mono = c.literalTrue + def poly[T: c.WeakTypeTag] = c.literal(c.weakTypeOf[T].toString + c.weakTypeOf[T].toString) + def weird = macro mono + } + } + + object Macros { + def mono = macro Enclosing.Impl.mono + def poly[T] = macro Enclosing.Impl.poly[T] + } +}
\ No newline at end of file diff --git a/test/files/run/macro-bundle-static/Test_2.scala b/test/files/run/macro-bundle-static/Test_2.scala new file mode 100644 index 0000000000..72160f6ec2 --- /dev/null +++ b/test/files/run/macro-bundle-static/Test_2.scala @@ -0,0 +1,8 @@ +object Test extends App { + println(Macros.mono) + println(Macros.poly[Int]) + println(new Enclosing.Impl{val c = ???}.weird) + println(pkg.Macros.mono) + println(pkg.Macros.poly[Int]) + println(new pkg.Enclosing.Impl{val c = ???}.weird) +}
\ No newline at end of file diff --git a/test/files/run/macro-bundle-toplevel.check b/test/files/run/macro-bundle-toplevel.check new file mode 100644 index 0000000000..37c8eaf27a --- /dev/null +++ b/test/files/run/macro-bundle-toplevel.check @@ -0,0 +1,6 @@ +() +Int +() +true +IntInt +true diff --git a/test/files/run/macro-bundle.flags b/test/files/run/macro-bundle-toplevel.flags index cd66464f2f..cd66464f2f 100644 --- a/test/files/run/macro-bundle.flags +++ b/test/files/run/macro-bundle-toplevel.flags diff --git a/test/files/run/macro-bundle/Impls_Macros_1.scala b/test/files/run/macro-bundle-toplevel/Impls_Macros_1.scala index 3f651c9a43..676935682e 100644 --- a/test/files/run/macro-bundle/Impls_Macros_1.scala +++ b/test/files/run/macro-bundle-toplevel/Impls_Macros_1.scala @@ -10,4 +10,17 @@ trait Impl extends Macro { object Macros { def mono = macro Impl.mono def poly[T] = macro Impl.poly[T] +} + +package pkg { + trait Impl extends Macro { + def mono = c.literalTrue + def poly[T: c.WeakTypeTag] = c.literal(c.weakTypeOf[T].toString + c.weakTypeOf[T].toString) + def weird = macro mono + } + + object Macros { + def mono = macro Impl.mono + def poly[T] = macro Impl.poly[T] + } }
\ No newline at end of file diff --git a/test/files/run/macro-bundle/Test_2.scala b/test/files/run/macro-bundle-toplevel/Test_2.scala index 428f809f9d..139cc5bef2 100644 --- a/test/files/run/macro-bundle/Test_2.scala +++ b/test/files/run/macro-bundle-toplevel/Test_2.scala @@ -2,4 +2,7 @@ object Test extends App { println(Macros.mono) println(Macros.poly[Int]) println(new Impl{val c = ???}.weird) + println(pkg.Macros.mono) + println(pkg.Macros.poly[Int]) + println(new pkg.Impl{val c = ???}.weird) }
\ No newline at end of file diff --git a/test/files/run/macro-bundle.check b/test/files/run/macro-bundle.check deleted file mode 100644 index 2107454960..0000000000 --- a/test/files/run/macro-bundle.check +++ /dev/null @@ -1,3 +0,0 @@ -() -Int -() |