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 | |
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')
7 files changed, 63 insertions, 40 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 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/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala index 28b95aa442..f4cbcb50fe 100644 --- a/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala +++ b/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala @@ -87,10 +87,11 @@ trait MemberHandlers { def definesTerm = Option.empty[TermName] def definesType = Option.empty[TypeName] - lazy val referencedNames = ImportVarsTraverser(member) - def importedNames = List[Name]() - def definedNames = definesTerm.toList ++ definesType.toList - def definedSymbols = List[Symbol]() + private lazy val _referencedNames = ImportVarsTraverser(member) + def referencedNames = _referencedNames + def importedNames = List[Name]() + def definedNames = definesTerm.toList ++ definesType.toList + def definedSymbols = List[Symbol]() def extraCodeToEvaluate(req: Request): String = "" def resultExtractionCode(req: Request): String = "" @@ -130,6 +131,7 @@ trait MemberHandlers { } abstract class MacroHandler(member: DefDef) extends MemberDefHandler(member) { + override def referencedNames = super.referencedNames.flatMap(name => List(name.toTermName, name.toTypeName)) override def definesValue = false override def definesTerm: Option[TermName] = Some(name.toTermName) override def definesType: Option[TypeName] = None |