Skip to content

Commit

Permalink
blackbox and whitebox macros
Browse files Browse the repository at this point in the history
This is the first commit in the series. This commit only:
1) Splits Context into BlackboxContext and WhiteboxContext
2) Splits Macro into BlackboxMacro and WhiteboxMacro
3) Introduces the isBundle property in the macro impl binding

Here we just teach the compiler that macros can now be blackbox and whitebox,
without actually imposing any restrictions on blackbox macros. These
restrictions will come in subsequent commits.

For description and documentation of the blackbox/whitebox separation
see the official macro guide at the scaladoc website:
http://docs.scala-lang.org/overviews/macros/blackbox-whitebox.html

Some infrastructure work to make evolving macros easier:
compile partest-extras with quick so they can use latest library/reflect/...
  • Loading branch information
xeno-by authored and adriaanm committed Nov 13, 2013
1 parent beed168 commit ce37ae4
Show file tree
Hide file tree
Showing 246 changed files with 788 additions and 619 deletions.
19 changes: 8 additions & 11 deletions build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1406,13 +1406,6 @@ TODO:
<target name="quick.swing" depends="quick.actors, quick.lib" if="has.java6">
<staged-build with="locker" stage="quick" project="swing"/> </target>

<target name="quick.partest-extras"
depends="quick.comp">
<!-- compile compiler-specific parts of partest -->
<staged-build with="starr" stage="quick" project="partest-extras" />
<staged-build with="starr" stage="quick" project="partest-javaagent" />
</target>


<target name="quick.plugins" depends="quick.comp">
<staged-uptodate stage="quick" project="continuations-plugin">
Expand Down Expand Up @@ -1504,10 +1497,14 @@ TODO:
<taskdef resource="scala/tools/ant/antlib.xml" classpathref="scaladoc.classpath"/>
</target>

<target name="pack.partest-extras" depends="quick.partest-extras">
<staged-pack project="partest-extras"/>
<staged-pack project="partest-javaagent"
manifest="${src.dir}/partest-javaagent/scala/tools/partest/javaagent/MANIFEST.MF"/>
<target name="pack.partest-extras" depends="quick.comp">
<!-- compile compiler-specific parts of partest -->
<staged-build with="quick" stage="quick" project="partest-extras" />
<staged-build with="quick" stage="quick" project="partest-javaagent" />

<staged-pack project="partest-extras"/>
<staged-pack project="partest-javaagent"
manifest="${src.dir}/partest-javaagent/scala/tools/partest/javaagent/MANIFEST.MF"/>
</target>

<target name="pack.bin" depends="pack.core, pack.modules, pack.partest-extras">
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/reflect/macros/compiler/Errors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ trait Errors extends Traces {

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")
def MacroBundleWrongShapeError() = implRefError("macro bundles must be monomorphic traits extending either BlackboxMacro or WhiteboxMacro and not implementing their `val c: BlackboxContext/WhiteboxContext` member")

// compatibility errors

Expand Down
20 changes: 9 additions & 11 deletions src/compiler/scala/reflect/macros/compiler/Resolvers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ trait Resolvers {
private val runDefinitions = currentRun.runDefinitions
import runDefinitions.{Predef_???, _}

/** Determines the type of context implied by the macro def.
*/
val ctxTpe = MacroContextClass.tpe

/** Resolves a macro impl reference provided in the right-hand side of the given macro definition.
*
* Acceptable shapes of the right-hand side:
Expand All @@ -44,22 +40,23 @@ trait Resolvers {
}

val untypedImplRef = typer.silent(_.typedTypeConstructor(maybeBundleRef)) match {
case SilentResultValue(result) if result.tpe.baseClasses.contains(MacroClass) =>
case SilentResultValue(result) if mightBeMacroBundleType(result.tpe) =>
val bundleProto = result.tpe.typeSymbol
val bundlePkg = bundleProto.enclosingPackageClass
if (!isMacroBundleProtoType(bundleProto.tpe)) MacroBundleWrongShapeError()
if (!bundleProto.owner.isStaticOwner) MacroBundleNonStaticError()

// 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`
// create a top-level definition `class Foo$Bundle(val c: BlackboxContext/WhiteboxContext) 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 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(()))))
Expand Down Expand Up @@ -88,12 +85,13 @@ trait Resolvers {
// lazy val (isImplBundle, macroImplOwner, macroImpl, macroImplTargs) =
private lazy val dissectedMacroImplRef =
macroImplRef match {
case MacroImplReference(isBundle, owner, meth, targs) => (isBundle, owner, meth, targs)
case MacroImplReference(isBundle, isBlackbox, owner, meth, targs) => (isBundle, isBlackbox, owner, meth, targs)
case _ => MacroImplReferenceWrongShapeError()
}
lazy val isImplBundle = dissectedMacroImplRef._1
lazy val isImplMethod = !isImplBundle
lazy val macroImplOwner = dissectedMacroImplRef._2
lazy val macroImpl = dissectedMacroImplRef._3
lazy val targs = dissectedMacroImplRef._4
lazy val isImplBlackbox = dissectedMacroImplRef._2
lazy val macroImplOwner = dissectedMacroImplRef._3
lazy val macroImpl = dissectedMacroImplRef._4
lazy val targs = dissectedMacroImplRef._5
}
16 changes: 9 additions & 7 deletions src/compiler/scala/reflect/macros/compiler/Validators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ trait Validators {
map2(aparamss.flatten, rparamss.flatten)((aparam, rparam) => {
if (aparam.name != rparam.name && !rparam.isSynthetic) MacroImplParamNameMismatchError(aparam, rparam)
if (isRepeated(aparam) ^ isRepeated(rparam)) MacroImplVarargMismatchError(aparam, rparam)
val aparamtpe = aparam.tpe.dealias match {
case RefinedType(List(tpe), Scope(sym)) if tpe =:= ctxTpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe
val aparamtpe = aparam.tpe match {
case MacroContextType(tpe) => tpe
case tpe => tpe
}
checkMacroImplParamTypeMismatch(atpeToRtpe(aparamtpe), rparam)
Expand Down Expand Up @@ -93,20 +93,20 @@ trait Validators {
*
* For the following macro impl:
* def fooBar[T: c.WeakTypeTag]
* (c: scala.reflect.macros.Context)
* (c: scala.reflect.macros.BlackboxContext)
* (xs: c.Expr[List[T]])
* : c.Expr[T] = ...
*
* This function will return:
* (c: scala.reflect.macros.Context)(xs: c.Expr[List[T]])c.Expr[T]
* (c: scala.reflect.macros.BlackboxContext)(xs: c.Expr[List[T]])c.Expr[T]
*
* Note that type tag evidence parameters are not included into the result.
* Type tag context bounds for macro impl tparams are optional.
* Therefore compatibility checks ignore such parameters, and we don't need to bother about them here.
*
* This method cannot be reduced to just macroImpl.info, because macro implementations might
* come in different shapes. If the implementation is an apply method of a Macro-compatible object,
* then it won't have (c: Context) in its parameters, but will rather refer to Macro.c.
* come in different shapes. If the implementation is an apply method of a BlackboxMacro/WhiteboxMacro-compatible object,
* then it won't have (c: BlackboxContext/WhiteboxContext) in its parameters, but will rather refer to BlackboxMacro/WhiteboxMacro.c.
*
* @param macroImpl The macro implementation symbol
*/
Expand All @@ -123,7 +123,8 @@ trait Validators {
* def foo[T](xs: List[T]): T = macro fooBar
*
* This function will return:
* (c: scala.reflect.macros.Context)(xs: c.Expr[List[T]])c.Expr[T]
* (c: scala.reflect.macros.BlackboxContext)(xs: c.Expr[List[T]])c.Expr[T] or
* (c: scala.reflect.macros.WhiteboxContext)(xs: c.Expr[List[T]])c.Expr[T]
*
* Note that type tag evidence parameters are not included into the result.
* Type tag context bounds for macro impl tparams are optional.
Expand All @@ -145,6 +146,7 @@ trait Validators {
// had to move method's body to an object because of the recursive dependencies between sigma and param
object SigGenerator {
val cache = scala.collection.mutable.Map[Symbol, Symbol]()
val ctxTpe = if (isImplBlackbox) BlackboxContextClass.tpe else WhiteboxContextClass.tpe
val ctxPrefix =
if (isImplMethod) singleType(NoPrefix, makeParam(nme.macroContext, macroDdef.pos, ctxTpe, SYNTHETIC))
else singleType(ThisType(macroImpl.owner), macroImpl.owner.tpe.member(nme.c))
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/scala/reflect/macros/contexts/Context.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package contexts

import scala.tools.nsc.Global

abstract class Context extends scala.reflect.macros.Context
abstract class Context extends scala.reflect.macros.BlackboxContext
with scala.reflect.macros.WhiteboxContext
with Aliases
with Enclosures
with Names
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ trait MacroRuntimes extends JavaReflectionRuntimes with ScalaReflectionRuntimes
type MacroRuntime = MacroArgs => Any
class MacroRuntimeResolver(val macroDef: Symbol) extends JavaReflectionResolvers
with ScalaReflectionResolvers {
val binding = loadMacroImplBinding(macroDef)
val binding = loadMacroImplBinding(macroDef).get
val isBundle = binding.isBundle
val className = binding.className
val methName = binding.methName
Expand Down
10 changes: 5 additions & 5 deletions src/compiler/scala/reflect/macros/util/Helpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ trait Helpers {
import runDefinitions._

val MacroContextUniverse = definitions.MacroContextUniverse
val treeInfo.MacroImplReference(isBundle, _, macroImpl, _) = macroImplRef
val treeInfo.MacroImplReference(isBundle, _, _, macroImpl, _) = macroImplRef
val paramss = macroImpl.paramss
val ContextParam = paramss match {
case Nil | _ :+ Nil => NoSymbol // no implicit parameters in the signature => nothing to do
case _ if isBundle => macroImpl.owner.tpe member nme.c
case (cparam :: _) :: _ if cparam.tpe <:< MacroContextClass.tpe => cparam
case _ => NoSymbol // no context parameter in the signature => nothing to do
case Nil | _ :+ Nil => NoSymbol // no implicit parameters in the signature => nothing to do
case _ if isBundle => macroImpl.owner.tpe member nme.c
case (cparam :: _) :: _ if isMacroContextType(cparam.tpe) => cparam
case _ => NoSymbol // no context parameter in the signature => nothing to do
}
def transformTag(param: Symbol): Symbol = param.tpe.dealias match {
case TypeRef(SingleType(SingleType(_, ContextParam), MacroContextUniverse), WeakTypeTagClass, targ :: Nil) => transform(param, targ.typeSymbol)
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,9 @@ trait ContextErrors {
def TooManyArgsPatternError(fun: Tree) =
NormalTypeError(fun, "too many arguments for unapply pattern, maximum = "+definitions.MaxTupleArity)

def BlackboxExtractorExpansion(fun: Tree) =
NormalTypeError(fun, "extractor macros can only be whitebox")

def WrongShapeExtractorExpansion(fun: Tree) =
NormalTypeError(fun, "extractor macros can only expand into extractor calls")

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/typechecker/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1147,7 +1147,7 @@ trait Implicits {
gen.mkAttributedThis(thisSym)
case _ =>
// if `pre` is not a PDT, e.g. if someone wrote
// implicitly[scala.reflect.macros.Context#TypeTag[Int]]
// implicitly[scala.reflect.macros.BlackboxContext#TypeTag[Int]]
// then we need to fail, because we don't know the prefix to use during type reification
// upd. we also need to fail silently, because this is a very common situation
// e.g. quite often we're searching for BaseUniverse#TypeTag, e.g. for a type tag in any universe
Expand Down
Loading

0 comments on commit ce37ae4

Please sign in to comment.