Skip to content

Commit

Permalink
changes bundles to be classes, not traits extending Macro
Browse files Browse the repository at this point in the history
Adjusts bundle notation to read `class Bundle(val c: Context)` instead of
`class Bundle extends Macro`. This avoids calling compileLate in the
macro compiler and associated tooling problems.
  • Loading branch information
xeno-by committed Jan 12, 2014
1 parent 5cc8f83 commit 3a689f5
Show file tree
Hide file tree
Showing 34 changed files with 108 additions and 251 deletions.
2 changes: 1 addition & 1 deletion src/compiler/scala/reflect/macros/compiler/Errors.scala
Expand Up @@ -51,7 +51,7 @@ trait Errors extends Traces {


def MacroBundleNonStaticError() = bundleRefError("macro bundles must be static") def MacroBundleNonStaticError() = bundleRefError("macro bundles must be static")


def MacroBundleWrongShapeError() = bundleRefError("macro bundles must be monomorphic traits extending either blackbox.Macro or whitebox.Macro and not implementing their `val c: Context` member") def MacroBundleWrongShapeError() = bundleRefError("macro bundles must be concrete classes having a single `val c: Context` parameter")


// compatibility errors // compatibility errors


Expand Down
35 changes: 5 additions & 30 deletions src/compiler/scala/reflect/macros/compiler/Resolvers.scala
Expand Up @@ -40,36 +40,11 @@ trait Resolvers {
} }


val untypedImplRef = typer.silent(_.typedTypeConstructor(maybeBundleRef)) match { val untypedImplRef = typer.silent(_.typedTypeConstructor(maybeBundleRef)) match {
case SilentResultValue(result) if mightBeMacroBundleType(result.tpe) => case SilentResultValue(result) if looksLikeMacroBundleType(result.tpe) =>
val bundleProto = result.tpe.typeSymbol val bundle = result.tpe.typeSymbol
val bundlePkg = bundleProto.enclosingPackageClass if (!isMacroBundleType(bundle.tpe)) MacroBundleWrongShapeError()
if (!isMacroBundleProtoType(bundleProto.tpe)) MacroBundleWrongShapeError() if (!bundle.owner.isStaticOwner) MacroBundleNonStaticError()
if (!bundleProto.owner.isStaticOwner) MacroBundleNonStaticError() atPos(macroDdef.rhs.pos)(gen.mkTypeApply(Select(New(bundle, Ident(Predef_???)), methName), targs))

// synthesize the bundle, i.e. given a static `trait Foo extends *box.Macro { def expand = ... } `
// create a top-level definition `class Foo$Bundle(val c: *box.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)) {
val contextType = if (isBlackboxMacroBundleType(bundleProto.tpe)) BlackboxContextClass.tpe else WhiteboxContextClass.tpe
def mkContextValDef(flags: Long) = ValDef(Modifiers(flags), nme.c, TypeTree(contextType), EmptyTree)
val contextField = mkContextValDef(PARAMACCESSOR)
val contextParam = mkContextValDef(PARAM | PARAMACCESSOR)
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 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 _ => case _ =>
macroDdef.rhs macroDdef.rhs
} }
Expand Down
6 changes: 3 additions & 3 deletions src/compiler/scala/reflect/macros/compiler/Validators.scala
Expand Up @@ -26,9 +26,9 @@ trait Validators {
if (macroImpl.isOverloaded) MacroImplOverloadedError() if (macroImpl.isOverloaded) MacroImplOverloadedError()
val implicitParams = aparamss.flatten filter (_.isImplicit) val implicitParams = aparamss.flatten filter (_.isImplicit)
if (implicitParams.nonEmpty) MacroImplNonTagImplicitParameters(implicitParams) if (implicitParams.nonEmpty) MacroImplNonTagImplicitParameters(implicitParams)
val declaredInStaticObject = isImplMethod && (macroImplOwner.isStaticOwner || macroImplOwner.moduleClass.isStaticOwner) val effectiveOwner = if (isImplMethod) macroImplOwner else macroImplOwner.owner
val declaredInTopLevelClass = isImplBundle && macroImplOwner.owner.isPackageClass val declaredInStaticObject = effectiveOwner.isStaticOwner || effectiveOwner.moduleClass.isStaticOwner
if (!declaredInStaticObject && !declaredInTopLevelClass) MacroImplReferenceWrongShapeError() if (!declaredInStaticObject) MacroImplReferenceWrongShapeError()
} }


private def checkMacroDefMacroImplCorrespondence() = { private def checkMacroDefMacroImplCorrespondence() = {
Expand Down
Expand Up @@ -2,7 +2,7 @@ package scala.reflect.macros
package runtime package runtime


import scala.reflect.runtime.ReflectionUtils import scala.reflect.runtime.ReflectionUtils
import scala.reflect.macros.{Context => ApiContext} import scala.reflect.macros.blackbox.{Context => ApiContext}


trait JavaReflectionRuntimes { trait JavaReflectionRuntimes {
self: scala.tools.nsc.typechecker.Analyzer => self: scala.tools.nsc.typechecker.Analyzer =>
Expand All @@ -19,7 +19,7 @@ trait JavaReflectionRuntimes {
macroLogVerbose(s"successfully loaded macro impl as ($implClass, $implMeth)") macroLogVerbose(s"successfully loaded macro impl as ($implClass, $implMeth)")
args => { args => {
val implObj = val implObj =
if (isBundle) implClass.getConstructor(classOf[ApiContext]).newInstance(args.c) if (isBundle) implClass.getConstructors().head.newInstance(args.c)
else ReflectionUtils.staticSingletonInstance(implClass) else ReflectionUtils.staticSingletonInstance(implClass)
val implArgs = if (isBundle) args.others else args.c +: args.others val implArgs = if (isBundle) args.others else args.c +: args.others
implMeth.invoke(implObj, implArgs.asInstanceOf[Seq[AnyRef]]: _*) implMeth.invoke(implObj, implArgs.asInstanceOf[Seq[AnyRef]]: _*)
Expand Down
19 changes: 0 additions & 19 deletions src/compiler/scala/tools/nsc/Global.scala
Expand Up @@ -1710,25 +1710,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
} }
} }


/** 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
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
compileLate(unit)
}

/** Reset package class to state at typer (not sure what this /** Reset package class to state at typer (not sure what this
* is needed for?) * is needed for?)
*/ */
Expand Down
44 changes: 19 additions & 25 deletions src/reflect/scala/reflect/internal/Definitions.scala
Expand Up @@ -482,10 +482,6 @@ trait Definitions extends api.StandardDefinitions {
lazy val TypeCreatorClass = getClassIfDefined("scala.reflect.api.TypeCreator") // defined in scala-reflect.jar, so we need to be careful lazy val TypeCreatorClass = getClassIfDefined("scala.reflect.api.TypeCreator") // defined in scala-reflect.jar, so we need to be careful
lazy val TreeCreatorClass = getClassIfDefined("scala.reflect.api.TreeCreator") // defined in scala-reflect.jar, so we need to be careful lazy val TreeCreatorClass = getClassIfDefined("scala.reflect.api.TreeCreator") // defined in scala-reflect.jar, so we need to be careful


lazy val BlackboxMacroClass = getClassIfDefined("scala.reflect.macros.blackbox.Macro") // defined in scala-reflect.jar, so we need to be careful
def BlackboxMacroContextValue = BlackboxMacroClass.map(sym => getMemberValue(sym, nme.c))
lazy val WhiteboxMacroClass = getClassIfDefined("scala.reflect.macros.whitebox.Macro") // defined in scala-reflect.jar, so we need to be careful
def WhiteboxMacroContextValue = WhiteboxMacroClass.map(sym => getMemberValue(sym, nme.c))
lazy val BlackboxContextClass = getClassIfDefined("scala.reflect.macros.blackbox.Context") // defined in scala-reflect.jar, so we need to be careful lazy val BlackboxContextClass = getClassIfDefined("scala.reflect.macros.blackbox.Context") // defined in scala-reflect.jar, so we need to be careful
lazy val WhiteboxContextClass = getClassIfDefined("scala.reflect.macros.whitebox.Context") // defined in scala-reflect.jar, so we need to be careful lazy val WhiteboxContextClass = getClassIfDefined("scala.reflect.macros.whitebox.Context") // defined in scala-reflect.jar, so we need to be careful
def MacroContextPrefix = BlackboxContextClass.map(sym => getMemberMethod(sym, nme.prefix)) def MacroContextPrefix = BlackboxContextClass.map(sym => getMemberMethod(sym, nme.prefix))
Expand Down Expand Up @@ -603,32 +599,30 @@ trait Definitions extends api.StandardDefinitions {
def isWhiteboxContextType(tp: Type) = def isWhiteboxContextType(tp: Type) =
isMacroContextType(tp) && (tp <:< WhiteboxContextClass.tpe) isMacroContextType(tp) && (tp <:< WhiteboxContextClass.tpe)


def mightBeMacroBundleType(tp: Type) = private def macroBundleParamInfo(tp: Type) = {
tp.baseClasses.contains(WhiteboxMacroClass) || val ctor = tp.erasure.typeSymbol.primaryConstructor
tp.baseClasses.contains(BlackboxMacroClass) ctor.paramss match {

case List(List(c)) =>
def isMacroBundleType(tp: Type) = tp.baseClasses match { val sym = c.info.typeSymbol
case _ :: proto :: _ if isMacroBundleProtoType(proto.tpe) => true val isContextCompatible = sym.isNonBottomSubClass(BlackboxContextClass) || sym.isNonBottomSubClass(WhiteboxContextClass)
case _ => false if (isContextCompatible) c.info else NoType
case _ =>
NoType
}
} }


def isBlackboxMacroBundleType(tp: Type) = def looksLikeMacroBundleType(tp: Type) =
isMacroBundleType(tp) && (tp <:< BlackboxMacroClass.tpe) && !(tp <:< WhiteboxMacroClass.tpe) macroBundleParamInfo(tp) != NoType


def isMacroBundleProtoType(tp: Type) = { def isMacroBundleType(tp: Type) = {
val sym = tp.typeSymbol val isContextCompatible = macroBundleParamInfo(tp) != NoType
val isNonTrivial = tp != ErrorType && tp != NothingTpe && tp != NullTpe val nonAbstract = !tp.erasure.typeSymbol.isAbstractClass
def subclasses(sym: Symbol) = sym != NoSymbol && tp.baseClasses.contains(sym) isContextCompatible && nonAbstract
val isMacroCompatible = subclasses(BlackboxMacroClass) ^ subclasses(WhiteboxMacroClass)
val isBundlePrototype = sym != BlackboxMacroClass && sym != WhiteboxMacroClass && sym.isTrait && {
val c = sym.info.member(nme.c)
def overrides(sym: Symbol) = c.overrideChain.contains(sym)
val cIsOk = (overrides(BlackboxMacroContextValue) || overrides(WhiteboxMacroContextValue)) && c.isDeferred
cIsOk && sym.isMonomorphicType
}
isNonTrivial && isMacroCompatible && isBundlePrototype
} }


def isBlackboxMacroBundleType(tp: Type) =
isMacroBundleType(tp) && (macroBundleParamInfo(tp) <:< BlackboxContextClass.tpe)

def isIterableType(tp: Type) = tp <:< classExistentialType(IterableClass) def isIterableType(tp: Type) = tp <:< classExistentialType(IterableClass)


// These "direct" calls perform no dealiasing. They are most needed when // These "direct" calls perform no dealiasing. They are most needed when
Expand Down
40 changes: 0 additions & 40 deletions src/reflect/scala/reflect/macros/blackbox/Macro.scala

This file was deleted.

40 changes: 0 additions & 40 deletions src/reflect/scala/reflect/macros/whitebox/Macro.scala

This file was deleted.

2 changes: 0 additions & 2 deletions src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
Expand Up @@ -319,8 +319,6 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
definitions.MirrorClass definitions.MirrorClass
definitions.TypeCreatorClass definitions.TypeCreatorClass
definitions.TreeCreatorClass definitions.TreeCreatorClass
definitions.BlackboxMacroClass
definitions.WhiteboxMacroClass
definitions.BlackboxContextClass definitions.BlackboxContextClass
definitions.WhiteboxContextClass definitions.WhiteboxContextClass
definitions.MacroImplAnnotation definitions.MacroImplAnnotation
Expand Down
6 changes: 3 additions & 3 deletions test/files/neg/macro-bundle-abstract.check
@@ -1,4 +1,4 @@
macro-bundle-abstract.scala:4: error: class Bundle$Bundle needs to be abstract, since method deferred in trait Bundle of type => Int is not defined macro-bundle-abstract.scala:10: error: macro bundles must be concrete classes having a single `val c: Context` parameter
trait Bundle extends Macro { def foo = macro Bundle.impl
^ ^
one error found one error found
4 changes: 2 additions & 2 deletions test/files/neg/macro-bundle-abstract.scala
@@ -1,7 +1,7 @@
import scala.language.experimental.macros import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Macro import scala.reflect.macros.blackbox.Context


trait Bundle extends Macro { abstract class Bundle(c: Context) {
def deferred: Int def deferred: Int
def impl = ??? def impl = ???
} }
Expand Down
4 changes: 0 additions & 4 deletions test/files/neg/macro-bundle-class.check

This file was deleted.

10 changes: 0 additions & 10 deletions test/files/neg/macro-bundle-class.scala

This file was deleted.

4 changes: 0 additions & 4 deletions test/files/neg/macro-bundle-mixbox.check

This file was deleted.

11 changes: 0 additions & 11 deletions test/files/neg/macro-bundle-mixbox.scala

This file was deleted.

4 changes: 4 additions & 0 deletions test/files/neg/macro-bundle-noncontext.check
@@ -0,0 +1,4 @@
macro-bundle-noncontext.scala:8: error: not found: value Bundle
def foo = Bundle.impl
^
one error found
@@ -1,6 +1,6 @@
import scala.language.experimental.macros import scala.language.experimental.macros


trait Bundle { class Bundle {
def impl = ??? def impl = ???
} }


Expand Down
4 changes: 0 additions & 4 deletions test/files/neg/macro-bundle-nonmacro.check

This file was deleted.

4 changes: 2 additions & 2 deletions test/files/neg/macro-bundle-object.scala
@@ -1,7 +1,7 @@
import scala.language.experimental.macros import scala.language.experimental.macros
import scala.reflect.macros.blackbox._ import scala.reflect.macros.blackbox.Context


object Bundle extends Macro { object Bundle {
val c: Context = ??? val c: Context = ???
def impl = ??? def impl = ???
} }
Expand Down
10 changes: 0 additions & 10 deletions test/files/neg/macro-bundle-polymorphic.check

This file was deleted.

12 changes: 0 additions & 12 deletions test/files/neg/macro-bundle-polymorphic.scala

This file was deleted.

2 changes: 1 addition & 1 deletion test/files/neg/macro-bundle-trait.check
@@ -1,4 +1,4 @@
macro-bundle-trait.scala:10: error: macro bundles must be monomorphic traits extending either blackbox.Macro or whitebox.Macro and not implementing their `val c: Context` member macro-bundle-trait.scala:10: error: not found: value Bundle
def foo = macro Bundle.impl def foo = macro Bundle.impl
^ ^
one error found one error found
4 changes: 2 additions & 2 deletions test/files/neg/macro-bundle-trait.scala
@@ -1,7 +1,7 @@
import scala.language.experimental.macros import scala.language.experimental.macros
import scala.reflect.macros.blackbox._ import scala.reflect.macros.blackbox.Context


trait Bundle extends Macro { trait Bundle {
val c: Context = ??? val c: Context = ???
def impl = ??? def impl = ???
} }
Expand Down
4 changes: 2 additions & 2 deletions test/files/neg/macro-quasiquotes/Macros_1.scala
@@ -1,7 +1,7 @@
import language.experimental.macros import language.experimental.macros
import scala.reflect.macros.blackbox.Macro import scala.reflect.macros.blackbox.Context


trait Impls extends Macro { class Impls(val c: Context) {
import c.universe._ import c.universe._
def impl1(x: Expr[Int]) = q"println(x)" def impl1(x: Expr[Int]) = q"println(x)"
def impl2(x: Tree) = q"println(x)" def impl2(x: Tree) = q"println(x)"
Expand Down

0 comments on commit 3a689f5

Please sign in to comment.