summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2013-10-03 01:06:25 -0700
committerJason Zaugg <jzaugg@gmail.com>2013-10-03 01:06:25 -0700
commit44585a7ad43065e3d22df27cc0d17d1e9370b0f2 (patch)
treef7e053111e52e1734d34caea9c00d9e961832868 /src
parentb17619dd071c65925b1073b8470a33711f8aa9a5 (diff)
parentfe074bbca1a9a3ae7d46301d652c5bf0f34e2c6e (diff)
downloadscala-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')
-rw-r--r--src/compiler/scala/reflect/macros/compiler/Errors.scala4
-rw-r--r--src/compiler/scala/reflect/macros/compiler/Resolvers.scala44
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala20
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala18
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala4
-rw-r--r--src/reflect/scala/reflect/internal/TreeGen.scala3
-rw-r--r--src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala10
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