summaryrefslogtreecommitdiff
path: root/src/reflect
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2013-01-19 12:28:19 +0100
committerEugene Burmako <xeno.by@gmail.com>2013-05-28 10:19:19 +0200
commit10229316dbf7afa7545d8e279b5960da6ee3db7d (patch)
treecfa50a3207aa5c1b2ce17ffba0fe1413a6f0dd53 /src/reflect
parente0bbe0af094b9055942c24fcaaa290a31415fa0a (diff)
downloadscala-10229316dbf7afa7545d8e279b5960da6ee3db7d.tar.gz
scala-10229316dbf7afa7545d8e279b5960da6ee3db7d.tar.bz2
scala-10229316dbf7afa7545d8e279b5960da6ee3db7d.zip
refactors macro compilation
Upgrades the way that macro defs are compiled by factoring out most of the logic in typedMacroBody and related errors in ContextErrors into an standalone cake. This leads to tighter cohesion and better code reuse as the cake is isolated from the rest of the compiler and is much easier to evolve than just a method body. Increased convenience of coding macro compilation allowed me to further clarify the implementation of the macro engine (e.g. take a look at Validators.scala) and to easily implement additional features, namely: 1) Parameters and return type of macro implementations can now be plain c.Tree's instead of previously mandatory c.Expr's. This makes macros more lightweight as there are a lot of situations when one doesn't need to splice macro params (the only motivation to use exprs over trees). Also as we're on the verge of having quasiquotes in trunk, there soon will be no reason to use exprs at all, since quasiquotes can splice everything. 2) Macro implementations can now be defined in bundles, standalone cakes built around a macro context: http://docs.scala-lang.org/overviews/macros/bundles.html. This further reduces boilerplate by simplifying implementations complex macros due to the fact that macro programmers no longer need to play path-dependent games to use helpers.
Diffstat (limited to 'src/reflect')
-rw-r--r--src/reflect/scala/reflect/api/Trees.scala14
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala14
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala5
-rw-r--r--src/reflect/scala/reflect/internal/TreeInfo.scala13
-rw-r--r--src/reflect/scala/reflect/internal/Trees.scala13
-rw-r--r--src/reflect/scala/reflect/macros/Macro.scala39
-rw-r--r--src/reflect/scala/reflect/runtime/JavaMirrors.scala4
7 files changed, 98 insertions, 4 deletions
diff --git a/src/reflect/scala/reflect/api/Trees.scala b/src/reflect/scala/reflect/api/Trees.scala
index f4ada814af..f7a6a68946 100644
--- a/src/reflect/scala/reflect/api/Trees.scala
+++ b/src/reflect/scala/reflect/api/Trees.scala
@@ -296,6 +296,20 @@ trait Trees { self: Universe =>
def name: Name
}
+ /** The constructor/extractor for `RefTree` instances.
+ * @group Extractors
+ */
+ val RefTree: RefTreeExtractor
+
+ /** An extractor class to create and pattern match with syntax `RefTree(qual, name)`.
+ * This AST node corresponds to either Ident, Select or SelectFromTypeTree.
+ * @group Extractors
+ */
+ abstract class RefTreeExtractor {
+ def apply(qualifier: Tree, name: Name): RefTree
+ def unapply(refTree: RefTree): Option[(Tree, Name)]
+ }
+
/** A tree which defines a symbol-carrying entity.
* @group Trees
* @template
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index 630572464d..5478c54eec 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -501,6 +501,13 @@ trait Definitions extends api.StandardDefinitions {
lazy val OptManifestClass = requiredClass[scala.reflect.OptManifest[_]]
lazy val NoManifest = requiredModule[scala.reflect.NoManifest.type]
+ lazy val TreesClass = getClassIfDefined("scala.reflect.api.Trees") // defined in scala-reflect.jar, so we need to be careful
+ lazy val TreesTreeType = if (TreesClass != NoSymbol) getTypeMember(TreesClass, tpnme.Tree) else NoSymbol
+ object TreeType {
+ def unapply(tpe: Type): Boolean = unapply(tpe.typeSymbol)
+ def unapply(sym: Symbol): Boolean = sym.overrideChain contains TreesTreeType
+ }
+
lazy val ExprsClass = getClassIfDefined("scala.reflect.api.Exprs") // defined in scala-reflect.jar, so we need to be careful
lazy val ExprClass = if (ExprsClass != NoSymbol) getMemberClass(ExprsClass, tpnme.Expr) else NoSymbol
def ExprSplice = if (ExprsClass != NoSymbol) getMemberMethod(ExprClass, nme.splice) else NoSymbol
@@ -533,6 +540,7 @@ 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 TreeCreatorClass = getClassIfDefined("scala.reflect.api.TreeCreator") // 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
lazy val MacroContextClass = getClassIfDefined("scala.reflect.macros.Context") // defined in scala-reflect.jar, so we need to be careful
def MacroContextPrefix = if (MacroContextClass != NoSymbol) getMemberMethod(MacroContextClass, nme.prefix) else NoSymbol
def MacroContextPrefixType = if (MacroContextClass != NoSymbol) getTypeMember(MacroContextClass, tpnme.PrefixType) else NoSymbol
@@ -650,6 +658,12 @@ trait Definitions extends api.StandardDefinitions {
}
def isTupleType(tp: Type) = isTupleTypeDirect(tp.dealiasWiden)
+ def isMacroBundleType(tp: Type) = {
+ val isNonTrivial = tp != ErrorType && tp != NothingTpe && tp != NullTpe
+ val isMacroCompatible = MacroClass != NoSymbol && tp <:< MacroClass.tpe
+ isNonTrivial && isMacroCompatible
+ }
+
lazy val ProductRootClass: ClassSymbol = requiredClass[scala.Product]
def Product_productArity = getMemberMethod(ProductRootClass, nme.productArity)
def Product_productElement = getMemberMethod(ProductRootClass, nme.productElement)
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index a20307882d..81fffc833c 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -8,6 +8,7 @@ package reflect
package internal
import java.security.MessageDigest
+import java.util.UUID.randomUUID
import Chars.isOperatorPart
import scala.annotation.switch
import scala.language.implicitConversions
@@ -290,6 +291,9 @@ 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"
@@ -584,6 +588,7 @@ trait StdNames {
val box: NameType = "box"
val build : NameType = "build"
val bytes: NameType = "bytes"
+ val c: NameType = "c"
val canEqual_ : NameType = "canEqual"
val checkInitialized: NameType = "checkInitialized"
val classOf: NameType = "classOf"
diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala
index e9ef9c7945..d1e8a04553 100644
--- a/src/reflect/scala/reflect/internal/TreeInfo.scala
+++ b/src/reflect/scala/reflect/internal/TreeInfo.scala
@@ -836,8 +836,17 @@ abstract class TreeInfo {
}
def unapply(tree: Tree) = refPart(tree) match {
- case ref: RefTree => Some((ref.qualifier.symbol, ref.symbol, dissectApplied(tree).targs))
- case _ => None
+ case ref: RefTree => {
+ val isBundle = definitions.isMacroBundleType(ref.qualifier.tpe)
+ val owner =
+ if (isBundle) ref.qualifier.tpe.typeSymbol
+ else {
+ val sym = ref.qualifier.symbol
+ if (sym.isModule) sym.moduleClass else sym
+ }
+ Some((isBundle, owner, ref.symbol, dissectApplied(tree).targs))
+ }
+ case _ => None
}
}
diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala
index de0b4e8247..979ee54b4f 100644
--- a/src/reflect/scala/reflect/internal/Trees.scala
+++ b/src/reflect/scala/reflect/internal/Trees.scala
@@ -253,6 +253,19 @@ trait Trees extends api.Trees { self: SymbolTable =>
def name: Name
}
+ object RefTree extends RefTreeExtractor {
+ def apply(qualifier: Tree, name: Name): RefTree = qualifier match {
+ case EmptyTree =>
+ Ident(name)
+ case qual if qual.isTerm =>
+ Select(qual, name)
+ case qual if qual.isType =>
+ assert(name.isTypeName, s"qual = $qual, name = $name")
+ SelectFromTypeTree(qual, name.toTypeName)
+ }
+ def unapply(refTree: RefTree): Option[(Tree, Name)] = Some((refTree.qualifier, refTree.name))
+ }
+
abstract class DefTree extends SymTree with NameTree with DefTreeApi {
def name: Name
override def isDef = true
diff --git a/src/reflect/scala/reflect/macros/Macro.scala b/src/reflect/scala/reflect/macros/Macro.scala
new file mode 100644
index 0000000000..44bedf483d
--- /dev/null
+++ b/src/reflect/scala/reflect/macros/Macro.scala
@@ -0,0 +1,39 @@
+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.Macro`.
+ *
+ * Instead of:
+ *
+ * def impl[T: c.WeakTypeTag](c: Context)(x: c.Expr[Int]) = ...
+ *
+ * One can write:
+ *
+ * trait Impl extends Macro {
+ * 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.
+ *
+ * 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
+ */
+trait Macro {
+ /** 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
+}
diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala
index adee155db0..a3684f602f 100644
--- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala
+++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala
@@ -972,8 +972,8 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni
javaTypeToValueClass(jclazz) orElse lookupClass
assert (cls.isType,
- sm"""${if (cls == NoSymbol) "not a type: symbol" else "no symbol could be"}
- | loaded from $jclazz in $owner with name $simpleName and classloader $classLoader""")
+ (if (cls != NoSymbol) s"not a type: symbol $cls" else "no symbol could be") +
+ s" loaded from $jclazz in $owner with name $simpleName and classloader $classLoader")
cls.asClass
}