diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2013-10-03 01:06:25 -0700 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2013-10-03 01:06:25 -0700 |
commit | 44585a7ad43065e3d22df27cc0d17d1e9370b0f2 (patch) | |
tree | f7e053111e52e1734d34caea9c00d9e961832868 /src/compiler | |
parent | b17619dd071c65925b1073b8470a33711f8aa9a5 (diff) | |
parent | fe074bbca1a9a3ae7d46301d652c5bf0f34e2c6e (diff) | |
download | scala-44585a7ad43065e3d22df27cc0d17d1e9370b0f2.tar.gz scala-44585a7ad43065e3d22df27cc0d17d1e9370b0f2.tar.bz2 scala-44585a7ad43065e3d22df27cc0d17d1e9370b0f2.zip |
Merge pull request #2994 from xeno-by/topic/bundles
assorted fixes for bundles
Diffstat (limited to 'src/compiler')
-rw-r--r-- | src/compiler/scala/reflect/macros/compiler/Errors.scala | 4 | ||||
-rw-r--r-- | src/compiler/scala/reflect/macros/compiler/Resolvers.scala | 44 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/Global.scala | 20 |
3 files changed, 39 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..49a5c01ad7 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(bundleName + ".scala", 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/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index ef9d8a310e..92bfe6e0fe 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1692,19 +1692,19 @@ 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) { + /** Create and compile a synthetic compilation unit from the provided tree. + * + * This needs to create a virtual file underlying the compilation unit in order to appease SBT. + * However this file cannot have a randomly generated name, because then SBT 0.13 goes into a vicious loop + * as described on the mailing list: https://groups.google.com/forum/#!msg/scala-user/r1SgSoVfs0U/Wv4av0LOKukJ + * Therefore I have introduced an additional parameter that makes everyone specify meaningful file names. + */ + def compileLate(virtualFileName: String, 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 fakeJfile = new java.io.File(virtualFileName) + val virtualFile = new VirtualFile(virtualFileName) { override def file = fakeJfile } val sourceFile = new BatchSourceFile(virtualFile, code.toString) val unit = new CompilationUnit(sourceFile) unit.body = code |