Experimental version of macro definitions. Macro calls need to be done next. No review.

git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@26079 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
odersky 2011-11-28 11:01:12 +00:00
parent 7bce33982a
commit 0095854ff0
8 changed files with 188 additions and 65 deletions

View File

@ -243,10 +243,6 @@ trait Definitions extends reflect.api.StandardDefinitions {
def arrayCloneMethod = getMember(ScalaRunTimeModule, "array_clone")
def ensureAccessibleMethod = getMember(ScalaRunTimeModule, "ensureAccessible")
def scalaRuntimeSameElements = getMember(ScalaRunTimeModule, nme.sameElements)
lazy val ReflectRuntimeMirror = getModule("scala.reflect.runtime.Mirror")
def freeValueMethod = getMember(ReflectRuntimeMirror, "freeValue")
lazy val ReflectPackage = getPackageObject("scala.reflect")
def Reflect_mirror = getMember(ReflectPackage, "mirror")
// classes with special meanings
lazy val StringAddClass = getClass("scala.runtime.StringAdd")
@ -354,6 +350,13 @@ trait Definitions extends reflect.api.StandardDefinitions {
def methodCache_add = getMember(MethodCacheClass, nme.add_)
// scala.reflect
lazy val ReflectApiUniverse = getClass("scala.reflect.api.Universe")
lazy val ReflectRuntimeMirror = getModule("scala.reflect.runtime.Mirror")
def freeValueMethod = getMember(ReflectRuntimeMirror, "freeValue")
lazy val ReflectPackage = getPackageObject("scala.reflect")
def Reflect_mirror = getMember(ReflectPackage, "mirror")
lazy val PartialManifestClass = getClass("scala.reflect.ClassManifest")
lazy val PartialManifestModule = getModule("scala.reflect.ClassManifest")
lazy val FullManifestClass = getClass("scala.reflect.Manifest")

View File

@ -526,6 +526,8 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
// unfortunately having the CASEACCESSOR flag does not actually mean you
// are a case accessor (you can also be a field.)
def isCaseAccessorMethod = isMethod && isCaseAccessor
def isMacro = isMethod && hasFlag(MACRO)
/** Does this symbol denote the primary constructor of its enclosing class? */
final def isPrimaryConstructor =
@ -712,8 +714,8 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
def originalOwnerChain: List[Symbol] = this :: originalOwner.getOrElse(this, rawowner).originalOwnerChain
def enclClassChain: List[Symbol] = {
if (this eq NoSymbol) Nil
else if (isClass && !isPackageClass) this :: owner.enclClassChain
if ((this eq NoSymbol) || isPackageClass) Nil
else if (isClass) this :: owner.enclClassChain
else owner.enclClassChain
}

View File

@ -2408,6 +2408,7 @@ self =>
* FunDef ::= FunSig `:' Type `=' Expr
* | FunSig [nl] `{' Block `}'
* | this ParamClause ParamClauses (`=' ConstrExpr | [nl] ConstrBlock)
* | `macro' FunSig [`:' Type] `=' Expr
* FunDcl ::= FunSig [`:' Type]
* FunSig ::= id [FunTypeParamClause] ParamClauses
* }}}
@ -2426,35 +2427,46 @@ self =>
}
}
else {
var newmods = mods
val nameOffset = in.offset
val name = ident()
val result = atPos(start, if (name == nme.ERROR) start else nameOffset) {
// contextBoundBuf is for context bounded type parameters of the form
// [T : B] or [T : => B]; it contains the equivalent implicit parameter type,
// i.e. (B[T] or T => B)
val contextBoundBuf = new ListBuffer[Tree]
val tparams = typeParamClauseOpt(name, contextBoundBuf)
val vparamss = paramClauses(name, contextBoundBuf.toList, false)
newLineOptWhenFollowedBy(LBRACE)
var restype = fromWithinReturnType(typedOpt())
val rhs =
if (isStatSep || in.token == RBRACE) {
if (restype.isEmpty) restype = scalaUnitConstr
newmods |= Flags.DEFERRED
EmptyTree
} else if (restype.isEmpty && in.token == LBRACE) {
restype = scalaUnitConstr
blockExpr()
} else {
equalsExpr()
}
DefDef(newmods, name, tparams, vparamss, restype, rhs)
}
signalParseProgress(result.pos)
result
if (name == nme.macro_ && isIdent && settings.Xexperimental.value)
funDefRest(start, in.offset, mods | Flags.MACRO, ident())
else
funDefRest(start, nameOffset, mods, name)
}
}
def funDefRest(start: Int, nameOffset: Int, mods: Modifiers, name: Name): Tree = {
val result = atPos(start, if (name.toTermName == nme.ERROR) start else nameOffset) {
val isMacro = mods hasFlag Flags.MACRO
val isTypeMacro = isMacro && name.isTypeName
var newmods = mods
// contextBoundBuf is for context bounded type parameters of the form
// [T : B] or [T : => B]; it contains the equivalent implicit parameter type,
// i.e. (B[T] or T => B)
val contextBoundBuf = new ListBuffer[Tree]
val tparams = typeParamClauseOpt(name, contextBoundBuf)
val vparamss = paramClauses(name, contextBoundBuf.toList, false)
if (!isMacro) newLineOptWhenFollowedBy(LBRACE)
var restype = if (isTypeMacro) TypeTree() else fromWithinReturnType(typedOpt())
val rhs =
if (isMacro)
equalsExpr()
else if (isStatSep || in.token == RBRACE) {
if (restype.isEmpty) restype = scalaUnitConstr
newmods |= Flags.DEFERRED
EmptyTree
} else if (restype.isEmpty && in.token == LBRACE) {
restype = scalaUnitConstr
blockExpr()
} else {
equalsExpr()
}
DefDef(newmods, name, tparams, vparamss, restype, rhs)
}
signalParseProgress(result.pos)
result
}
/** {{{
* ConstrExpr ::= SelfInvocation
@ -2498,6 +2510,7 @@ self =>
/** {{{
* TypeDef ::= type Id [TypeParamClause] `=' Type
* | `macro' FunSig `=' Expr
* TypeDcl ::= type Id [TypeParamClause] TypeBounds
* }}}
*/
@ -2506,17 +2519,21 @@ self =>
newLinesOpt()
atPos(start, in.offset) {
val name = identForType()
// @M! a type alias as well as an abstract type may declare type parameters
val tparams = typeParamClauseOpt(name, null)
in.token match {
case EQUALS =>
in.nextToken()
TypeDef(mods, name, tparams, typ())
case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE =>
TypeDef(mods | Flags.DEFERRED, name, tparams, typeBounds())
case _ =>
syntaxErrorOrIncomplete("`=', `>:', or `<:' expected", true)
EmptyTree
if (name == nme.macro_.toTypeName && isIdent && settings.Xexperimental.value) {
funDefRest(start, in.offset, mods | Flags.MACRO, identForType())
} else {
// @M! a type alias as well as an abstract type may declare type parameters
val tparams = typeParamClauseOpt(name, null)
in.token match {
case EQUALS =>
in.nextToken()
TypeDef(mods, name, tparams, typ())
case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE =>
TypeDef(mods | Flags.DEFERRED, name, tparams, typeBounds())
case _ =>
syntaxErrorOrIncomplete("`=', `>:', or `<:' expected", true)
EmptyTree
}
}
}
}

View File

@ -20,6 +20,7 @@ trait Analyzer extends AnyRef
with EtaExpansion
with SyntheticMethods
with Unapplies
with Macros
with NamesDefaults
with TypeDiagnostics
{

View File

@ -0,0 +1,68 @@
package scala.tools.nsc
package typechecker
import symtab.Flags._
trait Macros { self: Analyzer =>
import global._
import definitions._
def macroMethName(name: Name) =
newTermName((if (name.isTypeName) "type" else "def") + "macro$" + name)
def macroMeth(mac: Symbol): Symbol = {
var owner = mac.owner
if (!owner.isModuleClass) owner = owner.companionModule.moduleClass
owner.info.decl(macroMethName(mac.name))
}
/**
* The definition of the method implementing a macro. Example:
* Say we have
*
* def macro foo[T](xs: List[T]): T = expr
*
* Then the following macro method is generated for `foo`:
*
* def foo(glob: scala.reflect.api.Universe)
* (_this: glob.Tree)
* (T: glob.Type)
* (xs: glob.Tree): glob.Tree = {
* implicit val $glob = glob
* expr
* }
*/
def macroMethDef(mdef: DefDef): Tree = {
def paramDef(name: Name, tpt: Tree) = ValDef(Modifiers(PARAM), name, tpt, EmptyTree)
val universeType = TypeTree(ReflectApiUniverse.tpe)
val globParam = paramDef("glob", universeType)
def globSelect(name: Name) = Select(Ident("glob"), name)
def globTree = globSelect(newTypeName("Tree"))
def globType = globSelect(newTypeName("Type"))
val thisParam = paramDef("_this", globTree)
def tparamInMacro(tdef: TypeDef) = paramDef(tdef.name.toTermName, globType)
def vparamInMacro(vdef: ValDef): ValDef = paramDef(vdef.name, globTree)
def wrapImplicit(tree: Tree) = atPos(tree.pos) {
Block(List(ValDef(Modifiers(IMPLICIT), "$glob", universeType, Ident("glob"))), tree)
}
treeCopy.DefDef(
mdef,
mods = mdef.mods &~ MACRO,
name = mdef.name.toTermName,
tparams = List(),
vparamss = List(globParam) :: List(thisParam) :: (mdef.tparams map tparamInMacro) ::
(mdef.vparamss map (_ map vparamInMacro)),
tpt = globTree,
wrapImplicit(mdef.rhs))
}
def addMacroMethods(templ: Template, namer: Namer): Unit = {
for (ddef @ DefDef(mods, _, _, _, _, _) <- templ.body if mods hasFlag MACRO) {
namer.enterSyntheticSym(macroMethDef(ddef))
}
}
def macroExpand(tree: Tree): Tree = ???
}

View File

@ -59,8 +59,8 @@ trait Namers extends MethodSynthesis {
// is stored in this map. The map is cleared lazily, i.e. when the new symbol
// is created with the same name, the old one (if present) is wiped out, or the
// entry is deleted when it is used and no longer needed.
private val caseClassOfModuleClass = perRunCaches.newWeakMap[Symbol, WeakReference[ClassDef]]()
private val classOfModuleClass = perRunCaches.newWeakMap[Symbol, WeakReference[ClassDef]]()
// Default getters of constructors are added to the companion object in the
// typeCompleter of the constructor (methodSig). To compute the signature,
// we need the ClassDef. To create and enter the symbols into the companion
@ -421,8 +421,8 @@ trait Namers extends MethodSynthesis {
* class definition tree.
* @return the companion object symbol.
*/
def ensureCompanionObject(tree: ClassDef, creator: => Tree): Symbol = {
val m = companionModuleOf(tree.symbol, context)
def ensureCompanionObject(cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Symbol = {
val m = companionModuleOf(cdef.symbol, context)
// @luc: not sure why "currentRun.compiles(m)" is needed, things breaks
// otherwise. documentation welcome.
//
@ -435,8 +435,16 @@ trait Namers extends MethodSynthesis {
// Map(class Foo -> Nil)
// What exactly this implies and whether this is a sensible way to
// enforce it, I don't know.
//
// @martin: currentRun.compiles is needed because we might have a stale
// companion object from another run in scope. In that case we should still
// overwrite the object. I.e.
// Compile run #1: object Foo { ... }
// Compile run #2: case class Foo ...
// The object Foo is still in scope, but because it is not compiled in current run
// it should be ditched and a new one created.
if (m != NoSymbol && currentRun.compiles(m)) m
else enterSyntheticSym(creator)
else enterSyntheticSym(creator(cdef))
}
private def checkSelectors(tree: Import): Unit = {
@ -599,6 +607,11 @@ trait Namers extends MethodSynthesis {
enterCopyMethodOrGetter(tree, tparams)
else
sym setInfo completerOf(tree, tparams)
if (mods hasFlag MACRO) {
if (!(sym.owner.isClass && sym.owner.isStatic))
context.error(tree.pos, "macro definition must appear in globally accessible class")
}
}
def enterClassDef(tree: ClassDef) {
@ -610,17 +623,25 @@ trait Namers extends MethodSynthesis {
if (treeInfo.firstConstructorArgs(impl.body).size > MaxFunctionArity)
context.error(tree.pos, "Implementation restriction: case classes cannot have more than " + MaxFunctionArity + " parameters.")
val m = ensureCompanionObject(tree, caseModuleDef(tree))
caseClassOfModuleClass(m.moduleClass) = new WeakReference(tree)
val m = ensureCompanionObject(tree, caseModuleDef)
classOfModuleClass(m.moduleClass) = new WeakReference(tree)
}
val hasDefault = impl.body exists {
case DefDef(_, nme.CONSTRUCTOR, _, vparamss, _, _) => listutil.mexists(vparamss)(_.mods.hasDefault)
case _ => false
}
if (hasDefault) {
val m = ensureCompanionObject(tree, companionModuleDef(tree))
val m = ensureCompanionObject(tree)
classAndNamerOfModule(m) = (tree, null)
}
val hasMacro = impl.body exists {
case DefDef(mods, _, _, _, _, _) => mods hasFlag MACRO
case _ => false
}
if (hasMacro) {
val m = ensureCompanionObject(tree)
classOfModuleClass(m.moduleClass) = new WeakReference(tree)
}
val owner = tree.symbol.owner
if (owner.isPackageObjectClass) {
context.unit.warning(tree.pos,
@ -816,25 +837,27 @@ trait Namers extends MethodSynthesis {
// add apply and unapply methods to companion objects of case classes,
// unless they exist already; here, "clazz" is the module class
if (clazz.isModuleClass) {
Namers.this.caseClassOfModuleClass get clazz foreach { cdefRef =>
Namers.this.classOfModuleClass get clazz foreach { cdefRef =>
val cdef = cdefRef()
addApplyUnapply(cdef, templateNamer)
caseClassOfModuleClass -= clazz
if (cdef.mods.isCase) addApplyUnapply(cdef, templateNamer)
addMacroMethods(cdef.impl, templateNamer)
classOfModuleClass -= clazz
}
addMacroMethods(templ, templateNamer)
}
// add the copy method to case classes; this needs to be done here, not in SyntheticMethods, because
// the namer phase must traverse this copy method to create default getters for its parameters.
// here, clazz is the ClassSymbol of the case class (not the module).
// @check: this seems to work only if the type completer of the class runs before the one of the
// module class: the one from the module class removes the entry from caseClassOfModuleClass (see above).
// module class: the one from the module class removes the entry from classOfModuleClass (see above).
if (clazz.isClass && !clazz.hasModuleFlag) {
val modClass = companionModuleOf(clazz, context).moduleClass
Namers.this.caseClassOfModuleClass get modClass map { cdefRef =>
Namers.this.classOfModuleClass get modClass map { cdefRef =>
val cdef = cdefRef()
def hasCopy(decls: Scope) = (decls lookup nme.copy) != NoSymbol
if (!hasCopy(decls) &&
if (cdef.mods.isCase && !hasCopy(decls) &&
!parents.exists(p => hasCopy(p.typeSymbol.info.decls)) &&
!parents.flatMap(_.baseClasses).distinct.exists(bc => hasCopy(bc.info.decls)))
addCopyMethod(cdef, templateNamer)
@ -977,13 +1000,16 @@ trait Namers extends MethodSynthesis {
thisMethodType({
val rt = (
if (tpt.isEmpty) {
if (!tpt.isEmpty) {
typer.typedType(tpt).tpe
} else if (meth.isMacro) {
AnyClass.tpe
} else {
// replace deSkolemized symbols with skolemized ones
// (for resultPt computed by looking at overridden symbol, right?)
val pt = resultPt.substSym(tparamSyms, tparams map (_.symbol))
assignTypeToTree(ddef, typer, pt)
}
else typer.typedType(tpt).tpe
)
// #2382: return type of default getters are always @uncheckedVariance
if (meth.hasDefaultFlag)

View File

@ -755,7 +755,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
case Block(_, tree1) => tree1.symbol
case _ => tree.symbol
}
if (!meth.isConstructor && isFunctionType(pt)) { // (4.2)
if (!meth.isConstructor && !meth.isMacro && isFunctionType(pt)) { // (4.2)
debuglog("eta-expanding " + tree + ":" + tree.tpe + " to " + pt)
checkParamsConvertible(tree.pos, tree.tpe)
val tree0 = etaExpand(context.unit, tree)
@ -1676,6 +1676,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
*/
def typedDefDef(ddef: DefDef): DefDef = {
val meth = ddef.symbol.initialize
if (meth.isMacro) return ddef
reenterTypeParams(ddef.tparams)
reenterValueParams(ddef.vparamss)
@ -2100,13 +2101,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
var e1 = scope.lookupNextEntry(e)
while ((e1 ne null) && e1.owner == scope) {
if (!accesses(e.sym, e1.sym) && !accesses(e1.sym, e.sym) &&
(e.sym.isType || inBlock || (e.sym.tpe matches e1.sym.tpe)))
(e.sym.isType || inBlock || (e.sym.tpe matches e1.sym.tpe) || e.sym.isMacro && e1.sym.isMacro))
// default getters are defined twice when multiple overloads have defaults. an
// error for this is issued in RefChecks.checkDefaultsInOverloaded
if (!e.sym.isErroneous && !e1.sym.isErroneous && !e.sym.hasDefaultFlag &&
!e.sym.hasAnnotation(BridgeClass) && !e1.sym.hasAnnotation(BridgeClass)) {
error(e.sym.pos, e1.sym+" is defined twice"+
{if(!settings.debug.value) "" else " in "+unit.toString})
{if(!settings.debug.value) "" else " in "+unit.toString}+
{if (e.sym.isMacro && e1.sym.isMacro) " \n(note that macros cannot be overloaded)" else ""})
scope.unlink(e1) // need to unlink to avoid later problems with lub; see #2779
}
e1 = scope.lookupNextEntry(e1)
@ -3442,7 +3444,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
// (calling typed1 more than once for the same tree)
if (checked ne res) typed { atPos(tree.pos)(checked) }
else res
} else res
} else if (fun2.hasSymbol && fun2.symbol.isMacro)
typed1(macroExpand(res), mode, pt)
else
res
case ex: TypeError =>
fun match {
case Select(qual, name)

View File

@ -315,7 +315,7 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe =>
/** A common base class for ValDefs and DefDefs.
*/
abstract class ValOrDefDef extends MemberDef {
def name: TermName
def name: Name // can't be a TermName because macros can be type names.
def tpt: Tree
def rhs: Tree
}
@ -325,9 +325,10 @@ trait Trees /*extends reflect.generic.Trees*/ { self: Universe =>
*/
case class ValDef(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree) extends ValOrDefDef
/** A method definition.
/** A method or macro definition.
* @param name The name of the method or macro. Can be a type name in case this is a type macro
*/
case class DefDef(mods: Modifiers, name: TermName, tparams: List[TypeDef],
case class DefDef(mods: Modifiers, name: Name, tparams: List[TypeDef],
vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree) extends ValOrDefDef
/** An abstract type, a type parameter, or a type alias.