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/compiler/scala/tools/nsc/typechecker | |
parent | 0bea2ab5f6b211a83bbf14ea46fe57b8163c6334 (diff) | |
download | scala-311d813910a2ec590b11b84c28fac2ae6e086270.tar.gz scala-311d813910a2ec590b11b84c28fac2ae6e086270.tar.bz2 scala-311d813910a2ec590b11b84c28fac2ae6e086270.zip |
Experimental version of macro definitions.
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker')
4 files changed, 119 insertions, 19 deletions
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) |