Skip to content

Commit 844fee6

Browse files
committed
blackbox and whitebox macros
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
1 parent 52e1f38 commit 844fee6

File tree

244 files changed

+778
-606
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

244 files changed

+778
-606
lines changed

src/compiler/scala/reflect/macros/compiler/Errors.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ trait Errors extends Traces {
3333

3434
def MacroBundleNonStaticError() = implRefError("macro bundles must be static")
3535

36-
def MacroBundleWrongShapeError() = implRefError("macro bundles must be monomorphic traits extending scala.reflect.macros.Macro and not implementing its `val c: Context` member")
36+
def MacroBundleWrongShapeError() = implRefError("macro bundles must be monomorphic traits extending either BlackboxMacro or WhiteboxMacro and not implementing their `val c: BlackboxContext/WhiteboxContext` member")
3737

3838
// compatibility errors
3939

src/compiler/scala/reflect/macros/compiler/Resolvers.scala

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@ trait Resolvers {
1515
private val runDefinitions = currentRun.runDefinitions
1616
import runDefinitions.{Predef_???, _}
1717

18-
/** Determines the type of context implied by the macro def.
19-
*/
20-
val ctxTpe = MacroContextClass.tpe
21-
2218
/** Resolves a macro impl reference provided in the right-hand side of the given macro definition.
2319
*
2420
* Acceptable shapes of the right-hand side:
@@ -44,22 +40,23 @@ trait Resolvers {
4440
}
4541

4642
val untypedImplRef = typer.silent(_.typedTypeConstructor(maybeBundleRef)) match {
47-
case SilentResultValue(result) if result.tpe.baseClasses.contains(MacroClass) =>
43+
case SilentResultValue(result) if mightBeMacroBundleType(result.tpe) =>
4844
val bundleProto = result.tpe.typeSymbol
4945
val bundlePkg = bundleProto.enclosingPackageClass
5046
if (!isMacroBundleProtoType(bundleProto.tpe)) MacroBundleWrongShapeError()
5147
if (!bundleProto.owner.isStaticOwner) MacroBundleNonStaticError()
5248

5349
// synthesize the bundle, i.e. given a static `trait Foo extends Macro { def expand = ... } `
54-
// create a top-level definition `class Foo$Bundle(val c: Context) extends Foo` in a package next to `Foo`
50+
// create a top-level definition `class Foo$Bundle(val c: BlackboxContext/WhiteboxContext) extends Foo` in a package next to `Foo`
5551
val bundlePid = gen.mkUnattributedRef(bundlePkg)
5652
val bundlePrefix =
5753
if (bundlePkg == EmptyPackageClass) bundleProto.fullName('$')
5854
else bundleProto.fullName('$').substring(bundlePkg.fullName('$').length + 1)
5955
val bundleName = TypeName(bundlePrefix + tpnme.MACRO_BUNDLE_SUFFIX)
6056
val existingBundle = bundleProto.enclosingPackageClass.info.decl(bundleName)
6157
if (!currentRun.compiles(existingBundle)) {
62-
def mkContextValDef(flags: Long) = ValDef(Modifiers(flags), nme.c, TypeTree(ctxTpe), EmptyTree)
58+
val contextType = if (isBlackboxMacroBundleType(bundleProto.tpe)) BlackboxContextClass.tpe else WhiteboxContextClass.tpe
59+
def mkContextValDef(flags: Long) = ValDef(Modifiers(flags), nme.c, TypeTree(contextType), EmptyTree)
6360
val contextField = mkContextValDef(PARAMACCESSOR)
6461
val contextParam = mkContextValDef(PARAM | PARAMACCESSOR)
6562
val bundleCtor = DefDef(Modifiers(), nme.CONSTRUCTOR, Nil, List(List(contextParam)), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(()))))
@@ -88,12 +85,13 @@ trait Resolvers {
8885
// lazy val (isImplBundle, macroImplOwner, macroImpl, macroImplTargs) =
8986
private lazy val dissectedMacroImplRef =
9087
macroImplRef match {
91-
case MacroImplReference(isBundle, owner, meth, targs) => (isBundle, owner, meth, targs)
88+
case MacroImplReference(isBundle, isBlackbox, owner, meth, targs) => (isBundle, isBlackbox, owner, meth, targs)
9289
case _ => MacroImplReferenceWrongShapeError()
9390
}
9491
lazy val isImplBundle = dissectedMacroImplRef._1
9592
lazy val isImplMethod = !isImplBundle
96-
lazy val macroImplOwner = dissectedMacroImplRef._2
97-
lazy val macroImpl = dissectedMacroImplRef._3
98-
lazy val targs = dissectedMacroImplRef._4
93+
lazy val isImplBlackbox = dissectedMacroImplRef._2
94+
lazy val macroImplOwner = dissectedMacroImplRef._3
95+
lazy val macroImpl = dissectedMacroImplRef._4
96+
lazy val targs = dissectedMacroImplRef._5
9997
}

src/compiler/scala/reflect/macros/compiler/Validators.scala

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ trait Validators {
4949
map2(aparamss.flatten, rparamss.flatten)((aparam, rparam) => {
5050
if (aparam.name != rparam.name && !rparam.isSynthetic) MacroImplParamNameMismatchError(aparam, rparam)
5151
if (isRepeated(aparam) ^ isRepeated(rparam)) MacroImplVarargMismatchError(aparam, rparam)
52-
val aparamtpe = aparam.tpe.dealias match {
53-
case RefinedType(List(tpe), Scope(sym)) if tpe =:= ctxTpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe
52+
val aparamtpe = aparam.tpe match {
53+
case MacroContextType(tpe) => tpe
5454
case tpe => tpe
5555
}
5656
checkMacroImplParamTypeMismatch(atpeToRtpe(aparamtpe), rparam)
@@ -93,20 +93,20 @@ trait Validators {
9393
*
9494
* For the following macro impl:
9595
* def fooBar[T: c.WeakTypeTag]
96-
* (c: scala.reflect.macros.Context)
96+
* (c: scala.reflect.macros.BlackboxContext)
9797
* (xs: c.Expr[List[T]])
9898
* : c.Expr[T] = ...
9999
*
100100
* This function will return:
101-
* (c: scala.reflect.macros.Context)(xs: c.Expr[List[T]])c.Expr[T]
101+
* (c: scala.reflect.macros.BlackboxContext)(xs: c.Expr[List[T]])c.Expr[T]
102102
*
103103
* Note that type tag evidence parameters are not included into the result.
104104
* Type tag context bounds for macro impl tparams are optional.
105105
* Therefore compatibility checks ignore such parameters, and we don't need to bother about them here.
106106
*
107107
* This method cannot be reduced to just macroImpl.info, because macro implementations might
108-
* come in different shapes. If the implementation is an apply method of a Macro-compatible object,
109-
* then it won't have (c: Context) in its parameters, but will rather refer to Macro.c.
108+
* come in different shapes. If the implementation is an apply method of a BlackboxMacro/WhiteboxMacro-compatible object,
109+
* then it won't have (c: BlackboxContext/WhiteboxContext) in its parameters, but will rather refer to BlackboxMacro/WhiteboxMacro.c.
110110
*
111111
* @param macroImpl The macro implementation symbol
112112
*/
@@ -123,7 +123,8 @@ trait Validators {
123123
* def foo[T](xs: List[T]): T = macro fooBar
124124
*
125125
* This function will return:
126-
* (c: scala.reflect.macros.Context)(xs: c.Expr[List[T]])c.Expr[T]
126+
* (c: scala.reflect.macros.BlackboxContext)(xs: c.Expr[List[T]])c.Expr[T] or
127+
* (c: scala.reflect.macros.WhiteboxContext)(xs: c.Expr[List[T]])c.Expr[T]
127128
*
128129
* Note that type tag evidence parameters are not included into the result.
129130
* Type tag context bounds for macro impl tparams are optional.
@@ -145,6 +146,7 @@ trait Validators {
145146
// had to move method's body to an object because of the recursive dependencies between sigma and param
146147
object SigGenerator {
147148
val cache = scala.collection.mutable.Map[Symbol, Symbol]()
149+
val ctxTpe = if (isImplBlackbox) BlackboxContextClass.tpe else WhiteboxContextClass.tpe
148150
val ctxPrefix =
149151
if (isImplMethod) singleType(NoPrefix, makeParam(nme.macroContext, macroDdef.pos, ctxTpe, SYNTHETIC))
150152
else singleType(ThisType(macroImpl.owner), macroImpl.owner.tpe.member(nme.c))

src/compiler/scala/reflect/macros/contexts/Context.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ package contexts
33

44
import scala.tools.nsc.Global
55

6-
abstract class Context extends scala.reflect.macros.Context
6+
abstract class Context extends scala.reflect.macros.BlackboxContext
7+
with scala.reflect.macros.WhiteboxContext
78
with Aliases
89
with Enclosures
910
with Names

src/compiler/scala/reflect/macros/runtime/MacroRuntimes.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ trait MacroRuntimes extends JavaReflectionRuntimes with ScalaReflectionRuntimes
4545
type MacroRuntime = MacroArgs => Any
4646
class MacroRuntimeResolver(val macroDef: Symbol) extends JavaReflectionResolvers
4747
with ScalaReflectionResolvers {
48-
val binding = loadMacroImplBinding(macroDef)
48+
val binding = loadMacroImplBinding(macroDef).get
4949
val isBundle = binding.isBundle
5050
val className = binding.className
5151
val methName = binding.methName

src/compiler/scala/reflect/macros/util/Helpers.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ trait Helpers {
2727
import runDefinitions._
2828

2929
val MacroContextUniverse = definitions.MacroContextUniverse
30-
val treeInfo.MacroImplReference(isBundle, _, macroImpl, _) = macroImplRef
30+
val treeInfo.MacroImplReference(isBundle, _, _, macroImpl, _) = macroImplRef
3131
val paramss = macroImpl.paramss
3232
val ContextParam = paramss match {
33-
case Nil | _ :+ Nil => NoSymbol // no implicit parameters in the signature => nothing to do
34-
case _ if isBundle => macroImpl.owner.tpe member nme.c
35-
case (cparam :: _) :: _ if cparam.tpe <:< MacroContextClass.tpe => cparam
36-
case _ => NoSymbol // no context parameter in the signature => nothing to do
33+
case Nil | _ :+ Nil => NoSymbol // no implicit parameters in the signature => nothing to do
34+
case _ if isBundle => macroImpl.owner.tpe member nme.c
35+
case (cparam :: _) :: _ if isMacroContextType(cparam.tpe) => cparam
36+
case _ => NoSymbol // no context parameter in the signature => nothing to do
3737
}
3838
def transformTag(param: Symbol): Symbol = param.tpe.dealias match {
3939
case TypeRef(SingleType(SingleType(_, ContextParam), MacroContextUniverse), WeakTypeTagClass, targ :: Nil) => transform(param, targ.typeSymbol)

src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,9 @@ trait ContextErrors {
527527
def TooManyArgsPatternError(fun: Tree) =
528528
NormalTypeError(fun, "too many arguments for unapply pattern, maximum = "+definitions.MaxTupleArity)
529529

530+
def BlackboxExtractorExpansion(fun: Tree) =
531+
NormalTypeError(fun, "extractor macros can only be whitebox")
532+
530533
def WrongShapeExtractorExpansion(fun: Tree) =
531534
NormalTypeError(fun, "extractor macros can only expand into extractor calls")
532535

src/compiler/scala/tools/nsc/typechecker/Implicits.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,7 @@ trait Implicits {
11471147
gen.mkAttributedThis(thisSym)
11481148
case _ =>
11491149
// if `pre` is not a PDT, e.g. if someone wrote
1150-
// implicitly[scala.reflect.macros.Context#TypeTag[Int]]
1150+
// implicitly[scala.reflect.macros.BlackboxContext#TypeTag[Int]]
11511151
// then we need to fail, because we don't know the prefix to use during type reification
11521152
// upd. we also need to fail silently, because this is a very common situation
11531153
// e.g. quite often we're searching for BaseUniverse#TypeTag, e.g. for a type tag in any universe

src/compiler/scala/tools/nsc/typechecker/Macros.scala

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import Fingerprint._
2929
* Then fooBar needs to point to a static method of the following form:
3030
*
3131
* def fooBar[T: c.WeakTypeTag] // type tag annotation is optional
32-
* (c: scala.reflect.macros.Context)
32+
* (c: scala.reflect.macros.BlackboxContext)
3333
* (xs: c.Expr[List[T]])
3434
* : c.Expr[T] = {
3535
* ...
@@ -67,7 +67,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
6767
*
6868
* This solution is very simple, but unfortunately it's also lacking. If we use it, then
6969
* signatures of macro defs become transitively dependent on scala-reflect.jar
70-
* (because they refer to macro impls, and macro impls refer to scala.reflect.macros.Context defined in scala-reflect.jar).
70+
* (because they refer to macro impls, and macro impls refer to scala.reflect.macros.BlackboxContext/WhiteboxContext defined in scala-reflect.jar).
7171
* More details can be found in comments to https://issues.scala-lang.org/browse/SI-5940.
7272
*
7373
* Therefore we have to avoid putting macro impls into binding pickles and come up with our own serialization format.
@@ -81,54 +81,57 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
8181
* and various accounting information necessary when composing an argument list for the reflective invocation.
8282
*/
8383
case class MacroImplBinding(
84-
// Is this macro impl a bundle (a trait extending Macro) or a vanilla def?
85-
val isBundle: Boolean,
86-
// Java class name of the class that contains the macro implementation
87-
// is used to load the corresponding object with Java reflection
88-
className: String,
89-
// method name of the macro implementation
90-
// `className` and `methName` are all we need to reflectively invoke a macro implementation
91-
// because macro implementations cannot be overloaded
92-
methName: String,
93-
// flattens the macro impl's parameter lists having symbols replaced with their fingerprints
94-
// currently fingerprints are calculated solely from types of the symbols:
95-
// * c.Expr[T] => LiftedTyped
96-
// * c.Tree => LiftedUntyped
97-
// * c.WeakTypeTag[T] => Tagged(index of the type parameter corresponding to that type tag)
98-
// * everything else (e.g. scala.reflect.macros.Context) => Other
99-
// f.ex. for: def impl[T: WeakTypeTag, U, V: WeakTypeTag](c: Context)(x: c.Expr[T], y: c.Tree): (U, V) = ???
100-
// `signature` will be equal to List(List(Other), List(LiftedTyped, LiftedUntyped), List(Tagged(0), Tagged(2)))
101-
signature: List[List[Fingerprint]],
102-
// type arguments part of a macro impl ref (the right-hand side of a macro definition)
103-
// these trees don't refer to a macro impl, so we can pickle them as is
104-
targs: List[Tree]) {
105-
84+
// Is this macro impl a bundle (a trait extending BlackboxMacro or WhiteboxMacro) or a vanilla def?
85+
val isBundle: Boolean,
86+
// Is this macro impl blackbox (i.e. having BlackboxContext in its signature)?
87+
val isBlackbox: Boolean,
88+
// Java class name of the class that contains the macro implementation
89+
// is used to load the corresponding object with Java reflection
90+
className: String,
91+
// method name of the macro implementation
92+
// `className` and `methName` are all we need to reflectively invoke a macro implementation
93+
// because macro implementations cannot be overloaded
94+
methName: String,
95+
// flattens the macro impl's parameter lists having symbols replaced with their fingerprints
96+
// currently fingerprints are calculated solely from types of the symbols:
97+
// * c.Expr[T] => LiftedTyped
98+
// * c.Tree => LiftedUntyped
99+
// * c.WeakTypeTag[T] => Tagged(index of the type parameter corresponding to that type tag)
100+
// * everything else (e.g. scala.reflect.macros.BlackboxContext/WhiteboxContext) => Other
101+
// f.ex. for: def impl[T: WeakTypeTag, U, V: WeakTypeTag](c: BlackboxContext)(x: c.Expr[T], y: c.Tree): (U, V) = ???
102+
// `signature` will be equal to List(List(Other), List(LiftedTyped, LiftedUntyped), List(Tagged(0), Tagged(2)))
103+
signature: List[List[Fingerprint]],
104+
// type arguments part of a macro impl ref (the right-hand side of a macro definition)
105+
// these trees don't refer to a macro impl, so we can pickle them as is
106+
targs: List[Tree]) {
106107
// Was this binding derived from a `def ... = macro ???` definition?
107108
def is_??? = {
108109
val Predef_??? = currentRun.runDefinitions.Predef_???
109110
className == Predef_???.owner.javaClassName && methName == Predef_???.name.encoded
110111
}
112+
def isWhitebox = !isBlackbox
111113
}
112114

113115
/** Macro def -> macro impl bindings are serialized into a `macroImpl` annotation
114116
* with synthetic content that carries the payload described in `MacroImplBinding`.
115117
*
116118
* For example, for a pair of macro definition and macro implementation:
117-
* def impl(c: scala.reflect.macros.Context): c.Expr[Unit] = ???
119+
* def impl(c: scala.reflect.macros.BlackboxContext): c.Expr[Unit] = ???
118120
* def foo: Unit = macro impl
119121
*
120122
* We will have the following annotation added on the macro definition `foo`:
121123
*
122124
* @scala.reflect.macros.internal.macroImpl(
123125
* `macro`(
124126
* "isBundle" = false,
127+
* "isBlackbox" = true,
125128
* "signature" = List(Other),
126129
* "methodName" = "impl",
127130
* "versionFormat" = <current version format>,
128131
* "className" = "Macros$"))
129132
*/
130133
object MacroImplBinding {
131-
val versionFormat = 5.0
134+
val versionFormat = 6.0
132135

133136
def pickleAtom(obj: Any): Tree =
134137
obj match {
@@ -151,7 +154,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
151154
def pickle(macroImplRef: Tree): Tree = {
152155
val runDefinitions = currentRun.runDefinitions
153156
import runDefinitions._
154-
val MacroImplReference(isBundle, owner, macroImpl, targs) = macroImplRef
157+
val MacroImplReference(isBundle, isBlackbox, owner, macroImpl, targs) = macroImplRef
155158

156159
// todo. refactor when fixing SI-5498
157160
def className: String = {
@@ -182,6 +185,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
182185
val payload = List[(String, Any)](
183186
"versionFormat" -> versionFormat,
184187
"isBundle" -> isBundle,
188+
"isBlackbox" -> isBlackbox,
185189
"className" -> className,
186190
"methodName" -> macroImpl.name.toString,
187191
"signature" -> signature
@@ -237,10 +241,11 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
237241
if (versionFormat != pickleVersionFormat) fail(s"expected version format $versionFormat, actual $pickleVersionFormat")
238242

239243
val isBundle = unpickle("isBundle", classOf[Boolean])
244+
val isBlackbox = unpickle("isBlackbox", classOf[Boolean])
240245
val className = unpickle("className", classOf[String])
241246
val methodName = unpickle("methodName", classOf[String])
242247
val signature = unpickle("signature", classOf[List[List[Fingerprint]]])
243-
MacroImplBinding(isBundle, className, methodName, signature, targs)
248+
MacroImplBinding(isBundle, isBlackbox, className, methodName, signature, targs)
244249
}
245250
}
246251

@@ -249,14 +254,17 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
249254
macroDef withAnnotation AnnotationInfo(MacroImplAnnotation.tpe, List(pickle), Nil)
250255
}
251256

252-
def loadMacroImplBinding(macroDef: Symbol): MacroImplBinding = {
253-
val Some(AnnotationInfo(_, List(pickle), _)) = macroDef.getAnnotation(MacroImplAnnotation)
254-
MacroImplBinding.unpickle(pickle)
255-
}
257+
def loadMacroImplBinding(macroDef: Symbol): Option[MacroImplBinding] =
258+
macroDef.getAnnotation(MacroImplAnnotation) collect {
259+
case AnnotationInfo(_, List(pickle), _) => MacroImplBinding.unpickle(pickle)
260+
}
261+
262+
def isBlackbox(expandee: Tree): Boolean = isBlackbox(dissectApplied(expandee).core.symbol)
263+
def isBlackbox(macroDef: Symbol): Boolean = loadMacroImplBinding(macroDef).map(_.isBlackbox).getOrElse(false)
256264

257265
def computeMacroDefTypeFromMacroImplRef(macroDdef: DefDef, macroImplRef: Tree): Type = {
258266
macroImplRef match {
259-
case MacroImplReference(_, _, macroImpl, targs) =>
267+
case MacroImplReference(_, _, _, macroImpl, targs) =>
260268
// Step I. Transform c.Expr[T] to T and everything else to Any
261269
var runtimeType = decreaseMetalevel(macroImpl.info.finalResultType)
262270

@@ -450,7 +458,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
450458
(trees :+ tags).flatten
451459
}
452460

453-
val binding = loadMacroImplBinding(macroDef)
461+
val binding = loadMacroImplBinding(macroDef).get
454462
if (binding.is_???) Nil
455463
else calculateMacroArgs(binding)
456464
}
@@ -459,7 +467,7 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
459467
}
460468

461469
/** Keeps track of macros in-flight.
462-
* See more informations in comments to `openMacros` in `scala.reflect.macros.Context`.
470+
* See more informations in comments to `openMacros` in `scala.reflect.macros.WhiteboxContext`.
463471
*/
464472
private var _openMacros = List[MacroContext]()
465473
def openMacros = _openMacros

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1111,7 +1111,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
11111111
}
11121112
if (tree.isType)
11131113
adaptType()
1114-
else if (mode.typingExprNotFun && treeInfo.isMacroApplication(tree))
1114+
else if (mode.typingExprNotFun && treeInfo.isMacroApplication(tree) && !isMacroExpansionSuppressed(tree))
11151115
macroExpandApply(this, tree, mode, pt)
11161116
else if (mode.typingConstructorPattern)
11171117
typedConstructorPattern(tree, pt)

0 commit comments

Comments
 (0)