summaryrefslogtreecommitdiff
path: root/src/reflect/scala/reflect
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2013-10-02 16:47:11 +0200
committerAdriaan Moors <adriaan.moors@typesafe.com>2013-11-12 18:40:01 -0800
commitce37ae45e22463a3f1a2d659d6699f2977b26c6b (patch)
treec10f54142a9db9653c6c8518e579987093e753ed /src/reflect/scala/reflect
parentbeed16825e53077c40ff38b035bfaafb3a4e39d5 (diff)
downloadscala-ce37ae45e22463a3f1a2d659d6699f2977b26c6b.tar.gz
scala-ce37ae45e22463a3f1a2d659d6699f2977b26c6b.tar.bz2
scala-ce37ae45e22463a3f1a2d659d6699f2977b26c6b.zip
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 Some infrastructure work to make evolving macros easier: compile partest-extras with quick so they can use latest library/reflect/...
Diffstat (limited to 'src/reflect/scala/reflect')
-rw-r--r--src/reflect/scala/reflect/api/Exprs.scala2
-rw-r--r--src/reflect/scala/reflect/api/Importers.scala2
-rw-r--r--src/reflect/scala/reflect/api/Mirrors.scala6
-rw-r--r--src/reflect/scala/reflect/api/Universe.scala5
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala56
-rw-r--r--src/reflect/scala/reflect/internal/TreeInfo.scala14
-rw-r--r--src/reflect/scala/reflect/macros/Aliases.scala4
-rw-r--r--src/reflect/scala/reflect/macros/BlackboxContext.scala (renamed from src/reflect/scala/reflect/macros/Context.scala)36
-rw-r--r--src/reflect/scala/reflect/macros/BlackboxMacro.scala (renamed from src/reflect/scala/reflect/macros/Macro.scala)15
-rw-r--r--src/reflect/scala/reflect/macros/Enclosures.scala6
-rw-r--r--src/reflect/scala/reflect/macros/Evals.scala8
-rw-r--r--src/reflect/scala/reflect/macros/ExprUtils.scala4
-rw-r--r--src/reflect/scala/reflect/macros/FrontEnds.scala4
-rw-r--r--src/reflect/scala/reflect/macros/Infrastructure.scala4
-rw-r--r--src/reflect/scala/reflect/macros/Names.scala4
-rw-r--r--src/reflect/scala/reflect/macros/Parsers.scala4
-rw-r--r--src/reflect/scala/reflect/macros/Reifiers.scala4
-rw-r--r--src/reflect/scala/reflect/macros/Typers.scala6
-rw-r--r--src/reflect/scala/reflect/macros/WhiteboxContext.scala43
-rw-r--r--src/reflect/scala/reflect/macros/WhiteboxMacro.scala36
-rw-r--r--src/reflect/scala/reflect/macros/package.scala14
-rw-r--r--src/reflect/scala/reflect/runtime/JavaUniverseForce.scala7
-rw-r--r--src/reflect/scala/reflect/runtime/package.scala2
23 files changed, 210 insertions, 76 deletions
diff --git a/src/reflect/scala/reflect/api/Exprs.scala b/src/reflect/scala/reflect/api/Exprs.scala
index 5b6ff2325c..50c8aa8779 100644
--- a/src/reflect/scala/reflect/api/Exprs.scala
+++ b/src/reflect/scala/reflect/api/Exprs.scala
@@ -106,7 +106,7 @@ trait Exprs { self: Universe =>
*
* The corresponding macro implementation should have the following signature (note how the return type denotes path-dependency on x):
* {{{
- * object Impls { def foo_impl(c: Context)(x: c.Expr[X]): c.Expr[x.value.T] = ... }
+ * object Impls { def foo_impl(c: BlackboxContext)(x: c.Expr[X]): c.Expr[x.value.T] = ... }
* }}}
*/
@compileTimeOnly("cannot use value except for signatures of macro implementations")
diff --git a/src/reflect/scala/reflect/api/Importers.scala b/src/reflect/scala/reflect/api/Importers.scala
index 4182b7d0ba..e239b86452 100644
--- a/src/reflect/scala/reflect/api/Importers.scala
+++ b/src/reflect/scala/reflect/api/Importers.scala
@@ -34,7 +34,7 @@ package api
* {{{
* def staticEval[T](x: T) = macro staticEval[T]
*
- * def staticEval[T](c: scala.reflect.macros.Context)(x: c.Expr[T]) = {
+ * def staticEval[T](c: scala.reflect.macros.BlackboxContext)(x: c.Expr[T]) = {
* // creates a runtime reflection universe to host runtime compilation
* import scala.reflect.runtime.{universe => ru}
* val mirror = ru.runtimeMirror(c.libraryClassLoader)
diff --git a/src/reflect/scala/reflect/api/Mirrors.scala b/src/reflect/scala/reflect/api/Mirrors.scala
index ec128e31a3..a4cd531053 100644
--- a/src/reflect/scala/reflect/api/Mirrors.scala
+++ b/src/reflect/scala/reflect/api/Mirrors.scala
@@ -29,19 +29,19 @@ package api
* Compile-time `Mirror`s make use of only classloader `Mirror`s to load `Symbol`s
* by name.
*
- * The entry point to classloader `Mirror`s is via [[scala.reflect.macros.Context#mirror]].
+ * The entry point to classloader `Mirror`s is via [[scala.reflect.macros.BlackboxContext#mirror]] or [[scala.reflect.macros.WhiteboxContext#mirror]].
* Typical methods which use classloader `Mirror`s include [[scala.reflect.api.Mirror#staticClass]],
* [[scala.reflect.api.Mirror#staticModule]], and [[scala.reflect.api.Mirror#staticPackage]]. For
* example:
* {{{
- * import scala.reflect.macros.Context
+ * import scala.reflect.macros.BlackboxContext
*
* case class Location(filename: String, line: Int, column: Int)
*
* object Macros {
* def currentLocation: Location = macro impl
*
- * def impl(c: Context): c.Expr[Location] = {
+ * def impl(c: BlackboxContext): c.Expr[Location] = {
* import c.universe._
* val pos = c.macroApplication.pos
* val clsLocation = c.mirror.staticModule("Location") // get symbol of "Location" object
diff --git a/src/reflect/scala/reflect/api/Universe.scala b/src/reflect/scala/reflect/api/Universe.scala
index 77b4827eab..534f69a23e 100644
--- a/src/reflect/scala/reflect/api/Universe.scala
+++ b/src/reflect/scala/reflect/api/Universe.scala
@@ -41,10 +41,11 @@ package api
* res1: reflect.runtime.universe.Type = scala.Either[String,Int]
* }}}
*
- * To obtain a `Universe` for use within a Scala macro, use [[scala.reflect.macros.Context#universe]]. For example:
+ * To obtain a `Universe` for use within a Scala macro, use [[scala.reflect.macros.BlackboxContext#universe]].
+ * or [[scala.reflect.macros.WhiteboxContext#universe]]. For example:
* {{{
* def printf(format: String, params: Any*): Unit = macro impl
- * def impl(c: Context)(format: c.Expr[String], params: c.Expr[Any]*): c.Expr[Unit] = {
+ * def impl(c: BlackboxContext)(format: c.Expr[String], params: c.Expr[Any]*): c.Expr[Unit] = {
* import c.universe._
* ...
* }
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index 7cb051c63d..563f23cb3b 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -495,15 +495,18 @@ trait Definitions extends api.StandardDefinitions {
lazy val TreeCreatorClass = getClassIfDefined("scala.reflect.api.TreeCreator") // defined in scala-reflect.jar, so we need to be careful
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))
- def MacroContextUniverse = MacroContextClass.map(sym => getMemberMethod(sym, nme.universe))
- def MacroContextExprClass = MacroContextClass.map(sym => getTypeMember(sym, tpnme.Expr))
- def MacroContextWeakTypeTagClass = MacroContextClass.map(sym => getTypeMember(sym, tpnme.WeakTypeTag))
- def MacroContextTreeType = MacroContextClass.map(sym => getTypeMember(sym, tpnme.Tree))
+ lazy val BlackboxMacroClass = getClassIfDefined("scala.reflect.macros.BlackboxMacro") // 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.WhiteboxMacro") // 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.BlackboxContext") // defined in scala-reflect.jar, so we need to be careful
+ lazy val WhiteboxContextClass = getClassIfDefined("scala.reflect.macros.WhiteboxContext") // defined in scala-reflect.jar, so we need to be careful
+ def MacroContextPrefix = BlackboxContextClass.map(sym => getMemberMethod(sym, nme.prefix))
+ def MacroContextPrefixType = BlackboxContextClass.map(sym => getTypeMember(sym, tpnme.PrefixType))
+ def MacroContextUniverse = BlackboxContextClass.map(sym => getMemberMethod(sym, nme.universe))
+ def MacroContextExprClass = BlackboxContextClass.map(sym => getTypeMember(sym, tpnme.Expr))
+ def MacroContextWeakTypeTagClass = BlackboxContextClass.map(sym => getTypeMember(sym, tpnme.WeakTypeTag))
+ def MacroContextTreeType = BlackboxContextClass.map(sym => getTypeMember(sym, tpnme.Tree))
lazy val MacroImplAnnotation = requiredClass[scala.reflect.macros.internal.macroImpl]
lazy val StringContextClass = requiredClass[scala.StringContext]
@@ -593,18 +596,47 @@ trait Definitions extends api.StandardDefinitions {
def unspecializedTypeArgs(tp: Type): List[Type] =
(tp baseType unspecializedSymbol(tp.typeSymbolDirect)).typeArgs
+ object MacroContextType {
+ def unapply(tp: Type) = {
+ def isOneOfContextTypes(tp: Type) =
+ tp =:= BlackboxContextClass.tpe || tp =:= WhiteboxContextClass.tpe
+ def isPrefix(sym: Symbol) =
+ sym.allOverriddenSymbols.contains(MacroContextPrefixType)
+
+ tp.dealias match {
+ case RefinedType(List(tp), Scope(sym)) if isOneOfContextTypes(tp) && isPrefix(sym) => Some(tp)
+ case tp if isOneOfContextTypes(tp) => Some(tp)
+ case _ => None
+ }
+ }
+ }
+
+ def isMacroContextType(tp: Type) = MacroContextType.unapply(tp).isDefined
+
+ def isWhiteboxContextType(tp: Type) =
+ isMacroContextType(tp) && (tp <:< WhiteboxContextClass.tpe)
+
+ def mightBeMacroBundleType(tp: Type) =
+ tp.baseClasses.contains(WhiteboxMacroClass) ||
+ tp.baseClasses.contains(BlackboxMacroClass)
+
def isMacroBundleType(tp: Type) = tp.baseClasses match {
case _ :: proto :: _ if isMacroBundleProtoType(proto.tpe) => true
case _ => false
}
+ def isBlackboxMacroBundleType(tp: Type) =
+ isMacroBundleType(tp) && (tp <:< BlackboxMacroClass.tpe) && !(tp <:< WhiteboxMacroClass.tpe)
+
def isMacroBundleProtoType(tp: Type) = {
val sym = tp.typeSymbol
val isNonTrivial = tp != ErrorType && tp != NothingTpe && tp != NullTpe
- val isMacroCompatible = MacroClass != NoSymbol && tp.baseClasses.contains(MacroClass)
- val isBundlePrototype = sym != MacroClass && sym.isTrait && {
+ def subclasses(sym: Symbol) = sym != NoSymbol && tp.baseClasses.contains(sym)
+ val isMacroCompatible = subclasses(BlackboxMacroClass) ^ subclasses(WhiteboxMacroClass)
+ val isBundlePrototype = sym != BlackboxMacroClass && sym != WhiteboxMacroClass && sym.isTrait && {
val c = sym.info.member(nme.c)
- val cIsOk = c.overrideChain.contains(MacroContextValue) && c.isDeferred
+ def overrides(sym: Symbol) = c.overrideChain.contains(sym)
+ val cIsOk = (overrides(BlackboxMacroContextValue) || overrides(WhiteboxMacroContextValue)) && c.isDeferred
cIsOk && sym.isMonomorphicType
}
isNonTrivial && isMacroCompatible && isBundlePrototype
diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala
index 025965ad47..8982fd4246 100644
--- a/src/reflect/scala/reflect/internal/TreeInfo.scala
+++ b/src/reflect/scala/reflect/internal/TreeInfo.scala
@@ -18,7 +18,7 @@ abstract class TreeInfo {
val global: SymbolTable
import global._
- import definitions.{ isTupleSymbol, isVarArgsList, isCastSymbol, ThrowableClass, TupleClass, MacroContextClass, MacroContextPrefixType, uncheckedStableClass }
+ import definitions.{ isTupleSymbol, isVarArgsList, isCastSymbol, ThrowableClass, TupleClass, uncheckedStableClass, isBlackboxMacroBundleType, isWhiteboxContextType }
/* Does not seem to be used. Not sure what it does anyway.
def isOwnerDefinition(tree: Tree): Boolean = tree match {
@@ -822,13 +822,19 @@ abstract class TreeInfo {
case ref: RefTree => {
val qual = ref.qualifier
val isBundle = definitions.isMacroBundleType(qual.tpe)
+ val isBlackbox =
+ if (isBundle) isBlackboxMacroBundleType(qual.tpe)
+ else ref.symbol.paramss match {
+ case (c :: Nil) :: _ if isWhiteboxContextType(c.info) => false
+ case _ => true
+ }
val owner =
if (isBundle) qual.tpe.typeSymbol
else {
- val sym = if (qual.hasSymbolField) qual.symbol else NoSymbol
- if (sym.isModule) sym.moduleClass else sym
+ val qualSym = if (qual.hasSymbolField) qual.symbol else NoSymbol
+ if (qualSym.isModule) qualSym.moduleClass else qualSym
}
- Some((isBundle, owner, ref.symbol, dissectApplied(tree).targs))
+ Some((isBundle, isBlackbox, owner, ref.symbol, dissectApplied(tree).targs))
}
case _ => None
}
diff --git a/src/reflect/scala/reflect/macros/Aliases.scala b/src/reflect/scala/reflect/macros/Aliases.scala
index 9e05f343e6..ca599dbd49 100644
--- a/src/reflect/scala/reflect/macros/Aliases.scala
+++ b/src/reflect/scala/reflect/macros/Aliases.scala
@@ -5,11 +5,11 @@ package macros
/**
* <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span>
*
- * A slice of [[scala.reflect.macros.Context the Scala macros context]] that defines shorthands for the
+ * A slice of [[scala.reflect.macros.BlackboxContext the Scala macros context]] that defines shorthands for the
* most frequently used types and functions of the underlying compiler universe.
*/
trait Aliases {
- self: Context =>
+ self: BlackboxContext =>
/** The type of symbols representing declarations. */
type Symbol = universe.Symbol
diff --git a/src/reflect/scala/reflect/macros/Context.scala b/src/reflect/scala/reflect/macros/BlackboxContext.scala
index b0c816f4ad..2c77289866 100644
--- a/src/reflect/scala/reflect/macros/Context.scala
+++ b/src/reflect/scala/reflect/macros/BlackboxContext.scala
@@ -2,14 +2,10 @@ package scala
package reflect
package macros
-// todo. introduce context hierarchy
-// the most lightweight context should just expose the stuff from the SIP
-// the full context should include all traits from scala.reflect.macros (and probably reside in scala-compiler.jar)
-
/**
* <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span>
*
- * The Scala macros context.
+ * The blackbox Scala macros context.
*
* See [[scala.reflect.macros.package the overview page]] for a description of how macros work. This documentation
* entry provides information on the API available to macro writers.
@@ -27,17 +23,25 @@ package macros
* Other than that, macro contexts provide facilities for typechecking, exploring the compiler's symbol table and
* enclosing trees and compilation units, evaluating trees, logging warnings/errors and much more.
* Refer to the documentation of top-level traits in this package to learn the details.
+ *
+ * If a macro def refers to a macro impl that uses `BlackboxContext`, then this macro def becomes a blackbox macro,
+ * which means that its expansion will be upcast to its return type, enforcing faithfullness of that macro to its
+ * type signature. Whitebox macros, i.e. the ones defined with `WhiteboxContext`, aren't bound by this restriction,
+ * which enables a number of important use cases, but they are also going to enjoy less support than blackbox macros,
+ * so choose wisely. See the [[http://docs.scala-lang.org/overviews/macros.html Macros Guide]] for more information.
+ *
+ * @see `scala.reflect.macros.WhiteboxContext`
*/
-trait Context extends Aliases
- with Enclosures
- with Names
- with Reifiers
- with FrontEnds
- with Infrastructure
- with Typers
- with Parsers
- with Evals
- with ExprUtils {
+trait BlackboxContext extends Aliases
+ with Enclosures
+ with Names
+ with Reifiers
+ with FrontEnds
+ with Infrastructure
+ with Typers
+ with Parsers
+ with Evals
+ with ExprUtils {
/** The compile-time universe. */
val universe: Universe
@@ -59,7 +63,7 @@ trait Context extends Aliases
* scala> class Coll[T] {
* | def filter(p: T => Boolean): Coll[T] = macro M.filter[T]
* | }; object M {
- * | def filter[T](c: Context { type PrefixType = Coll[T] })
+ * | def filter[T](c: BlackboxContext { type PrefixType = Coll[T] })
* | (p: c.Expr[T => Boolean]): c.Expr[Coll[T]] =
* | {
* | println(c.prefix.tree)
diff --git a/src/reflect/scala/reflect/macros/Macro.scala b/src/reflect/scala/reflect/macros/BlackboxMacro.scala
index 44bedf483d..df142e9238 100644
--- a/src/reflect/scala/reflect/macros/Macro.scala
+++ b/src/reflect/scala/reflect/macros/BlackboxMacro.scala
@@ -6,15 +6,15 @@ package macros
*
* Traditionally macro implementations are defined as methods,
* but this trait provides an alternative way of encoding macro impls as
- * bundles, traits which extend `scala.reflect.macros.Macro`.
+ * bundles, traits which extend `scala.reflect.macros.BlackboxMacro` or`scala.reflect.macros.WhiteboxMacro` .
*
* Instead of:
*
- * def impl[T: c.WeakTypeTag](c: Context)(x: c.Expr[Int]) = ...
+ * def impl[T: c.WeakTypeTag](c: BlackboxContext)(x: c.Expr[Int]) = ...
*
* One can write:
*
- * trait Impl extends Macro {
+ * trait Impl extends BlackboxMacro {
* def apply[T: c.WeakTypeTag](x: c.Expr[Int]) = ...
* }
*
@@ -24,16 +24,13 @@ package macros
* are complex and need to be modularized. State of the art technique of addressing this need is quite heavyweight:
* http://docs.scala-lang.org/overviews/macros/overview.html#writing_bigger_macros.
*
- * However utility of this approach to writing macros isn't limited to just convenience.
- * When a macro implementation becomes not just a function, but a full-fledged module,
- * it can define callbacks that will be called by the compiler upon interesting events.
- * In subsequent commits I will add support for programmable type inference
+ * @see `scala.reflect.macros.WhiteboxMacro`
*/
-trait Macro {
+trait BlackboxMacro {
/** The context to be used by the macro implementation.
*
* Vanilla macro implementations have to carry it in their signatures, however when a macro is a full-fledged module,
* it can define the context next to the implementation, makes implementation signature more lightweight.
*/
- val c: Context
+ val c: BlackboxContext
}
diff --git a/src/reflect/scala/reflect/macros/Enclosures.scala b/src/reflect/scala/reflect/macros/Enclosures.scala
index d6ba5f39cd..c3d019ccf3 100644
--- a/src/reflect/scala/reflect/macros/Enclosures.scala
+++ b/src/reflect/scala/reflect/macros/Enclosures.scala
@@ -7,13 +7,13 @@ import scala.language.existentials // SI-6541
/**
* <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span>
*
- * A slice of [[scala.reflect.macros.Context the Scala macros context]] that exposes
+ * A slice of [[scala.reflect.macros.BlackboxContext the Scala macros context]] that exposes
* enclosing trees (method, class, compilation unit and currently compiled application),
* the enclosing position of the macro expansion, as well as macros and implicits
* that are currently in-flight.
*/
trait Enclosures {
- self: Context =>
+ self: BlackboxContext =>
/** The tree that undergoes macro expansion.
* Can be useful to get an offset or a range position of the entire tree being processed.
@@ -43,7 +43,7 @@ trait Enclosures {
* Unlike `openMacros`, this is a val, which means that it gets initialized when the context is created
* and always stays the same regardless of whatever happens during macro expansion.
*/
- def enclosingMacros: List[Context]
+ def enclosingMacros: List[BlackboxContext]
/** Information about one of the currently considered implicit candidates.
* Candidates are used in plural form, because implicit parameters may themselves have implicit parameters,
diff --git a/src/reflect/scala/reflect/macros/Evals.scala b/src/reflect/scala/reflect/macros/Evals.scala
index 70b2ab58d4..eb37e83cad 100644
--- a/src/reflect/scala/reflect/macros/Evals.scala
+++ b/src/reflect/scala/reflect/macros/Evals.scala
@@ -5,11 +5,11 @@ package macros
/**
* <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span>
*
- * A slice of [[scala.reflect.macros.Context the Scala macros context]] that provides
+ * A slice of [[scala.reflect.macros.BlackboxContext the Scala macros context]] that provides
* a facility to evaluate trees.
*/
trait Evals {
- self: Context =>
+ self: BlackboxContext =>
/** Takes a typed wrapper for a tree of type `T` and evaluates it to a value of type `T`.
*
@@ -21,12 +21,12 @@ trait Evals {
* mutates the tree in place, therefore the conventional approach is to `duplicate` the tree first.
*
* {{{
- * scala> def impl(c: Context)(x: c.Expr[String]) = {
+ * scala> def impl(c: BlackboxContext)(x: c.Expr[String]) = {
* | val x1 = c.Expr[String](c.resetAllAttrs(x.tree.duplicate))
* | println(s"compile-time value is: \${c.eval(x1)}")
* | x
* | }
- * impl: (c: Context)(x: c.Expr[String])c.Expr[String]
+ * impl: (c: BlackboxContext)(x: c.Expr[String])c.Expr[String]
*
* scala> def test(x: String) = macro impl
* test: (x: String)String
diff --git a/src/reflect/scala/reflect/macros/ExprUtils.scala b/src/reflect/scala/reflect/macros/ExprUtils.scala
index 76a8392b9c..58b61e446a 100644
--- a/src/reflect/scala/reflect/macros/ExprUtils.scala
+++ b/src/reflect/scala/reflect/macros/ExprUtils.scala
@@ -5,11 +5,11 @@ package macros
/**
* <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span>
*
- * A slice of [[scala.reflect.macros.Context the Scala macros context]] that defines shorthands for the
+ * A slice of [[scala.reflect.macros.BlackboxContext the Scala macros context]] that defines shorthands for the
* most common `Expr`-creating functions.
*/
trait ExprUtils {
- self: Context =>
+ self: BlackboxContext =>
/** Shorthand for `Literal(Constant(null))` in the underlying `universe`. */
@deprecated("Use quasiquotes instead", "2.11.0")
diff --git a/src/reflect/scala/reflect/macros/FrontEnds.scala b/src/reflect/scala/reflect/macros/FrontEnds.scala
index 6abd8c335b..3a910d89ad 100644
--- a/src/reflect/scala/reflect/macros/FrontEnds.scala
+++ b/src/reflect/scala/reflect/macros/FrontEnds.scala
@@ -5,12 +5,12 @@ package macros
/**
* <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span>
*
- * A slice of [[scala.reflect.macros.Context the Scala macros context]] that
+ * A slice of [[scala.reflect.macros.BlackboxContext the Scala macros context]] that
* provides facilities to communicate with the compiler's front end
* (emit warnings, errors and other sorts of messages).
*/
trait FrontEnds {
- self: Context =>
+ self: BlackboxContext =>
/** For sending a message which should not be labeled as a warning/error,
* but also shouldn't require -verbose to be visible.
diff --git a/src/reflect/scala/reflect/macros/Infrastructure.scala b/src/reflect/scala/reflect/macros/Infrastructure.scala
index eb63fb7b7f..b6585f94d2 100644
--- a/src/reflect/scala/reflect/macros/Infrastructure.scala
+++ b/src/reflect/scala/reflect/macros/Infrastructure.scala
@@ -5,11 +5,11 @@ package macros
/**
* <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span>
*
- * A slice of [[scala.reflect.macros.Context the Scala macros context]] that
+ * A slice of [[scala.reflect.macros.BlackboxContext the Scala macros context]] that
* provides facilities to communicate with the compiler's infrastructure.
*/
trait Infrastructure {
- self: Context =>
+ self: BlackboxContext =>
/** Exposes macro-specific settings as a list of strings.
* These settings are passed to the compiler via the "-Xmacro-settings:setting1,setting2...,settingN" command-line option.
diff --git a/src/reflect/scala/reflect/macros/Names.scala b/src/reflect/scala/reflect/macros/Names.scala
index 8773175561..6bd3e1a199 100644
--- a/src/reflect/scala/reflect/macros/Names.scala
+++ b/src/reflect/scala/reflect/macros/Names.scala
@@ -5,11 +5,11 @@ package macros
/**
* <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span>
*
- * A slice of [[scala.reflect.macros.Context the Scala macros context]] that
+ * A slice of [[scala.reflect.macros.BlackboxContext the Scala macros context]] that
* provides functions that generate unique names.
*/
trait Names {
- self: Context =>
+ self: BlackboxContext =>
/** Creates a unique string. */
@deprecated("Use freshName instead", "2.11.0")
diff --git a/src/reflect/scala/reflect/macros/Parsers.scala b/src/reflect/scala/reflect/macros/Parsers.scala
index 4232b05f8c..cbfb30f022 100644
--- a/src/reflect/scala/reflect/macros/Parsers.scala
+++ b/src/reflect/scala/reflect/macros/Parsers.scala
@@ -5,12 +5,12 @@ package macros
/**
* <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span>
*
- * A slice of [[scala.reflect.macros.Context the Scala macros context]] that
+ * A slice of [[scala.reflect.macros.BlackboxContext the Scala macros context]] that
* exposes functions to parse strings with Scala code into trees.
*/
@deprecated("Use quasiquotes instead", "2.11.0")
trait Parsers {
- self: Context =>
+ self: BlackboxContext =>
/** Parses a string with a Scala expression into an abstract syntax tree.
* Only works for expressions, i.e. parsing a package declaration will fail.
diff --git a/src/reflect/scala/reflect/macros/Reifiers.scala b/src/reflect/scala/reflect/macros/Reifiers.scala
index 6ebd2db730..67d10dc10a 100644
--- a/src/reflect/scala/reflect/macros/Reifiers.scala
+++ b/src/reflect/scala/reflect/macros/Reifiers.scala
@@ -5,11 +5,11 @@ package macros
/**
* <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span>
*
- * A slice of [[scala.reflect.macros.Context the Scala macros context]] that
+ * A slice of [[scala.reflect.macros.BlackboxContext the Scala macros context]] that
* exposes functions to save reflection artifacts for runtime.
*/
trait Reifiers {
- self: Context =>
+ self: BlackboxContext =>
/** Given a tree, generate a tree that when compiled and executed produces the original tree.
* For more information and examples see the documentation for `Universe.reify`.
diff --git a/src/reflect/scala/reflect/macros/Typers.scala b/src/reflect/scala/reflect/macros/Typers.scala
index d7aec9b3ef..ec90ee8fe6 100644
--- a/src/reflect/scala/reflect/macros/Typers.scala
+++ b/src/reflect/scala/reflect/macros/Typers.scala
@@ -5,11 +5,11 @@ package macros
/**
* <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span>
*
- * A slice of [[scala.reflect.macros.Context the Scala macros context]] that
+ * A slice of [[scala.reflect.macros.BlackboxContext the Scala macros context]] that
* partially exposes the type checker to macro writers.
*/
trait Typers {
- self: Context =>
+ self: BlackboxContext =>
/** Contexts that represent macros in-flight, including the current one. Very much like a stack trace, but for macros only.
* Can be useful for interoperating with other macros and for imposing compiler-friendly limits on macro expansion.
@@ -21,7 +21,7 @@ trait Typers {
* Unlike `enclosingMacros`, this is a def, which means that it gets recalculated on every invocation,
* so it might change depending on what is going on during macro expansion.
*/
- def openMacros: List[Context]
+ def openMacros: List[BlackboxContext]
/** Information about one of the currently considered implicit candidates.
* Candidates are used in plural form, because implicit parameters may themselves have implicit parameters,
diff --git a/src/reflect/scala/reflect/macros/WhiteboxContext.scala b/src/reflect/scala/reflect/macros/WhiteboxContext.scala
new file mode 100644
index 0000000000..76bc17746c
--- /dev/null
+++ b/src/reflect/scala/reflect/macros/WhiteboxContext.scala
@@ -0,0 +1,43 @@
+package scala
+package reflect
+package macros
+
+/**
+ * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span>
+ *
+ * The whitebox Scala macros context.
+ *
+ * See [[scala.reflect.macros.package the overview page]] for a description of how macros work. This documentation
+ * entry provides information on the API available to macro writers.
+ *
+ * A macro context wraps a compiler universe exposed in `universe` and having type [[scala.reflect.macros.Universe]].
+ * This type is a refinement over the generic reflection API provided in [[scala.reflect.api.Universe]]. The
+ * extended Universe provides mutability for reflection artifacts (e.g. macros can change types of compiler trees,
+ * add annotation to symbols representing definitions, etc) and exposes some internal compiler functionality
+ * such as `Symbol.deSkolemize` or `Tree.attachments`.
+ *
+ * Another fundamental part of a macro context is `macroApplication`, which provides access to the tree undergoing
+ * macro expansion. Parts of this tree can be found in arguments of the corresponding macro implementations and
+ * in `prefix`, but `macroApplication` gives the full picture.
+ *
+ * Other than that, macro contexts provide facilities for typechecking, exploring the compiler's symbol table and
+ * enclosing trees and compilation units, evaluating trees, logging warnings/errors and much more.
+ * Refer to the documentation of top-level traits in this package to learn the details.
+ *
+ * If a macro def refers to a macro impl that uses `WhiteboxContext`, then this macro def becomes a whitebox macro,
+ * gaining the ability to refine the type of its expansion beyond its official return type, which enables a number of important use cases.
+ * Blackbox macros, i.e. the ones defined with `BlackboxContext`, can't do that, so they are less powerful.
+ * However blackbox macros are also going to enjoy better support than whitebox macros, so choose wisely.
+ * See the [[http://docs.scala-lang.org/overviews/macros.html Macros Guide]] for more information.
+ *
+ * @see `scala.reflect.macros.BlackboxContext`
+ */
+trait WhiteboxContext extends BlackboxContext {
+ /** @inheritdoc
+ */
+ def openMacros: List[WhiteboxContext]
+
+ /** @inheritdoc
+ */
+ def enclosingMacros: List[WhiteboxContext]
+} \ No newline at end of file
diff --git a/src/reflect/scala/reflect/macros/WhiteboxMacro.scala b/src/reflect/scala/reflect/macros/WhiteboxMacro.scala
new file mode 100644
index 0000000000..1c581313eb
--- /dev/null
+++ b/src/reflect/scala/reflect/macros/WhiteboxMacro.scala
@@ -0,0 +1,36 @@
+package scala.reflect
+package macros
+
+/**
+ * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span>
+ *
+ * Traditionally macro implementations are defined as methods,
+ * but this trait provides an alternative way of encoding macro impls as
+ * bundles, traits which extend `scala.reflect.macros.BlackboxMacro` or`scala.reflect.macros.WhiteboxMacro` .
+ *
+ * Instead of:
+ *
+ * def impl[T: c.WeakTypeTag](c: WhiteboxContext)(x: c.Expr[Int]) = ...
+ *
+ * One can write:
+ *
+ * trait Impl extends WhiteboxMacro {
+ * def apply[T: c.WeakTypeTag](x: c.Expr[Int]) = ...
+ * }
+ *
+ * Without changing anything else at all.
+ *
+ * This language feature is useful in itself in cases when macro implementations
+ * are complex and need to be modularized. State of the art technique of addressing this need is quite heavyweight:
+ * http://docs.scala-lang.org/overviews/macros/overview.html#writing_bigger_macros.
+ *
+ * @see `scala.reflect.macros.BlackboxMacro`
+ */
+trait WhiteboxMacro {
+ /** The context to be used by the macro implementation.
+ *
+ * Vanilla macro implementations have to carry it in their signatures, however when a macro is a full-fledged module,
+ * it can define the context next to the implementation, makes implementation signature more lightweight.
+ */
+ val c: WhiteboxContext
+}
diff --git a/src/reflect/scala/reflect/macros/package.scala b/src/reflect/scala/reflect/macros/package.scala
index 2e2e8e79f8..6a8434a163 100644
--- a/src/reflect/scala/reflect/macros/package.scala
+++ b/src/reflect/scala/reflect/macros/package.scala
@@ -7,10 +7,22 @@ package reflect
* The base package for Scala macros.
*
* Macros are functions that are called by the compiler during compilation.
- * Within these functions the programmer has access to compiler APIs exposed in [[scala.reflect.macros.Context]].
+ * Within these functions the programmer has access to compiler APIs.
* For example, it is possible to generate, analyze and typecheck code.
*
* See the [[http://docs.scala-lang.org/overviews/macros.html Macros Guide]] on how to get started with Scala macros.
*/
package object macros {
+ /** The Scala macros context.
+ *
+ * In Scala 2.11, macros that were once the one are split into blackbox and whitebox macros,
+ * with the former being better supported and the latter being more powerful. You can read about
+ * the details of the split and the associated trade-offs in the [[http://docs.scala-lang.org/overviews/macros.html Macros Guide]].
+ *
+ * `scala.reflect.macros.Context` follows this tendency and turns into `scala.reflect.macros.BlackboxContext`
+ * and `scala.reflect.macros.WhiteboxContext`. The original `Context` is left in place for compatibility reasons,
+ * but it is now deprecated, nudging the users to choose between blackbox and whitebox macros.
+ */
+ @deprecated("Use BlackboxContext or WhiteboxContext instead", "2.11.0")
+ type Context = WhiteboxContext
} \ No newline at end of file
diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
index 4d69a6673c..50b5843c59 100644
--- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
+++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
@@ -314,8 +314,10 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
definitions.TypeCreatorClass
definitions.TreeCreatorClass
definitions.LiftableClass
- definitions.MacroClass
- definitions.MacroContextClass
+ definitions.BlackboxMacroClass
+ definitions.WhiteboxMacroClass
+ definitions.BlackboxContextClass
+ definitions.WhiteboxContextClass
definitions.MacroImplAnnotation
definitions.StringContextClass
definitions.QuasiquoteClass
@@ -334,6 +336,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
definitions.TupleClass
definitions.FunctionClass
definitions.AbstractFunctionClass
+ definitions.MacroContextType
definitions.ProductRootClass
definitions.Any_$eq$eq
definitions.Any_$bang$eq
diff --git a/src/reflect/scala/reflect/runtime/package.scala b/src/reflect/scala/reflect/runtime/package.scala
index 41c1310e17..3a7688aa2c 100644
--- a/src/reflect/scala/reflect/runtime/package.scala
+++ b/src/reflect/scala/reflect/runtime/package.scala
@@ -26,7 +26,7 @@ package object runtime {
package runtime {
private[scala] object Macros {
- def currentMirror(c: scala.reflect.macros.Context): c.Expr[universe.Mirror] = {
+ def currentMirror(c: scala.reflect.macros.BlackboxContext): c.Expr[universe.Mirror] = {
import c.universe._
val runtimeClass = c.reifyEnclosingRuntimeClass
if (runtimeClass.isEmpty) c.abort(c.enclosingPosition, "call site does not have an enclosing class")