diff options
author | Martin Odersky <odersky@gmail.com> | 2011-11-28 11:01:12 +0000 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2011-11-28 11:01:12 +0000 |
commit | 311d813910a2ec590b11b84c28fac2ae6e086270 (patch) | |
tree | 4a35454e77a8d09d822a7b7c3126b10c7c3861bb /src | |
parent | 0bea2ab5f6b211a83bbf14ea46fe57b8163c6334 (diff) | |
download | scala-311d813910a2ec590b11b84c28fac2ae6e086270.tar.gz scala-311d813910a2ec590b11b84c28fac2ae6e086270.tar.bz2 scala-311d813910a2ec590b11b84c28fac2ae6e086270.zip |
Experimental version of macro definitions.
Diffstat (limited to 'src')
8 files changed, 187 insertions, 64 deletions
diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 1cc3d580d9..4d71d2a769 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -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") diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 058108c285..a59ea2c1bf 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -527,6 +527,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => // 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 = isConstructor && owner.primaryConstructor == this @@ -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 } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index cfe8716d9f..e27d5cacda 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -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,36 +2427,47 @@ 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 * | ConstrBlock @@ -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 + } } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index e3786c154f..16d55c26ca 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -20,6 +20,7 @@ trait Analyzer extends AnyRef with EtaExpansion with SyntheticMethods with Unapplies + with Macros with NamesDefaults with TypeDiagnostics { diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala new file mode 100644 index 0000000000..05eadb03ae --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -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 = ??? + +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index d503371f5d..4c85830311 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -59,7 +59,7 @@ 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, @@ -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) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 62ad78c64d..f9c056e16d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -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) diff --git a/src/library/scala/reflect/api/Trees.scala b/src/library/scala/reflect/api/Trees.scala index 478c47a210..20f20890e8 100644 --- a/src/library/scala/reflect/api/Trees.scala +++ b/src/library/scala/reflect/api/Trees.scala @@ -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. |