diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2012-06-06 02:05:10 +0200 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2012-06-08 15:23:11 +0200 |
commit | 1708a7fffdb653a638927c2b4ff30a7a0be0f3fe (patch) | |
tree | 1d270cc0b3c4a7424f2444a4d1dca460699abb6a /src/compiler/scala/tools/nsc | |
parent | fb67a1d3aea159fd39e5c0fad14ffa089a5d6ba5 (diff) | |
download | scala-1708a7fffdb653a638927c2b4ff30a7a0be0f3fe.tar.gz scala-1708a7fffdb653a638927c2b4ff30a7a0be0f3fe.tar.bz2 scala-1708a7fffdb653a638927c2b4ff30a7a0be0f3fe.zip |
macros: refactoring of fast track infrastructure
As a result, hardwired macros don't need implementation stubs.
This is very important, because in a few commits scala.reflect.makro.Context
will move out from scala-library.jar.
Also adding fast track entries doesn't require jumping through hoops
with PDTs. It's as simple as defining PartialFunction[Tree, Any].
Diffstat (limited to 'src/compiler/scala/tools/nsc')
4 files changed, 127 insertions, 135 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index d43dceca58..8895642893 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -7,7 +7,6 @@ import scala.tools.nsc.util.ClassPath._ import scala.reflect.ReflectionUtils import scala.collection.mutable.ListBuffer import scala.compat.Platform.EOL -import scala.reflect.makro.runtime.{Context => MacroContext} import util.Statistics._ import scala.reflect.makro.util._ import java.lang.{Class => jClass} @@ -38,7 +37,7 @@ import java.lang.reflect.{Array => jArray, Method => jMethod} * (Expr(elems)) * (TypeTag(Int)) */ -trait Macros extends Traces { +trait Macros extends scala.tools.reflect.FastTrack with Traces { self: Analyzer => import global._ @@ -186,6 +185,12 @@ trait Macros extends Traces { import typer.context macroLogVerbose("typechecking macro def %s at %s".format(ddef.symbol, ddef.pos)) + if (fastTrack contains ddef.symbol) { + macroLogVerbose("typecheck terminated unexpectedly: macro is hardwired") + assert(!ddef.tpt.isEmpty, "hardwired macros must provide result type") + return EmptyTree + } + if (!typer.checkFeature(ddef.pos, MacrosFeature, immediate = true)) { macroLogVerbose("typecheck terminated unexpectedly: language.experimental.macros feature is not enabled") ddef.symbol setFlag IS_ERROR @@ -635,39 +640,8 @@ trait Macros extends Traces { * @return Some(runtime) if macro implementation can be loaded successfully from either of the mirrors, * None otherwise. */ - private type MacroRuntime = List[Any] => Any + type MacroRuntime = List[Any] => Any private val macroRuntimesCache = perRunCaches.newWeakMap[Symbol, Option[MacroRuntime]] - private lazy val fastTrack: Map[Symbol, MacroRuntime] = { - import scala.reflect.api.Universe - import scala.reflect.makro.internal._ - Map( // challenge: how can we factor out the common code? Does not seem to be easy. - MacroInternal_materializeArrayTag -> (args => { - assert(args.length == 3, args) - val c = args(0).asInstanceOf[MacroContext] - materializeArrayTag_impl(c)(args(1).asInstanceOf[c.Expr[Universe]])(args(2).asInstanceOf[c.TypeTag[_]]) - }), - MacroInternal_materializeErasureTag -> (args => { - assert(args.length == 3, args) - val c = args(0).asInstanceOf[MacroContext] - materializeErasureTag_impl(c)(args(1).asInstanceOf[c.Expr[Universe]])(args(2).asInstanceOf[c.TypeTag[_]]) - }), - MacroInternal_materializeClassTag -> (args => { - assert(args.length == 3, args) - val c = args(0).asInstanceOf[MacroContext] - materializeClassTag_impl(c)(args(1).asInstanceOf[c.Expr[Universe]])(args(2).asInstanceOf[c.TypeTag[_]]) - }), - MacroInternal_materializeTypeTag -> (args => { - assert(args.length == 3, args) - val c = args(0).asInstanceOf[MacroContext] - materializeTypeTag_impl(c)(args(1).asInstanceOf[c.Expr[Universe]])(args(2).asInstanceOf[c.TypeTag[_]]) - }), - MacroInternal_materializeConcreteTypeTag -> (args => { - assert(args.length == 3, args) - val c = args(0).asInstanceOf[MacroContext] - materializeConcreteTypeTag_impl(c)(args(1).asInstanceOf[c.Expr[Universe]])(args(2).asInstanceOf[c.TypeTag[_]]) - }) - ) - } private def macroRuntime(macroDef: Symbol): Option[MacroRuntime] = { macroTraceVerbose("looking for macro implementation: ")(macroDef) if (fastTrack contains macroDef) { @@ -794,15 +768,14 @@ trait Macros extends Traces { } } - /** Should become private again once we're done with migrating typetag generation from implicits */ - def macroContext(typer: Typer, prefixTree: Tree, expandeeTree: Tree): MacroContext { val mirror: global.type } = + private def macroContext(typer: Typer, prefixTree: Tree, expandeeTree: Tree): MacroContext = new { - val mirror: global.type = global + val mirror: self.global.type = self.global val callsiteTyper: mirror.analyzer.Typer = typer.asInstanceOf[global.analyzer.Typer] - // todo. infer precise typetag for this Expr, namely the PrefixType member of the Context refinement - val prefix = Expr(prefixTree)(TypeTag.Nothing) val expandee = expandeeTree - } with MacroContext { + } with UnaffiliatedMacroContext { + // todo. infer precise typetag for this Expr, namely the PrefixType member of the Context refinement + val prefix = Expr[Nothing](prefixTree)(TypeTag.Nothing) override def toString = "MacroContext(%s@%s +%d)".format(expandee.symbol.name, expandee.pos, enclosingMacros.length - 1 /* exclude myself */) } @@ -836,96 +809,111 @@ trait Macros extends Traces { val context = expandee.attachmentOpt[MacroAttachment].flatMap(_.macroContext).getOrElse(macroContext(typer, prefixTree, expandee)) var argss: List[List[Any]] = List(context) :: exprArgs.toList macroTraceVerbose("argss: ")(argss) + val rawArgss = + if (fastTrack contains macroDef) { + if (fastTrack(macroDef) validate argss) argss + else { + // if you're getting here, it's not necessarily partial application that is at fault + // for example, if a signature of a hardwired macro has been changed without updated FastTrack + // then the corresponding partial function in FastTrack will refuse to process the expandee + // validation will return false, and control flow will end up here + // however, for simplicity sake, I didn't introduce the notion of error handling to FastTrack + // so all kinds of validation errors produce `MacroPartialApplicationError` + typer.TyperErrorGen.MacroPartialApplicationError(expandee) + return None + } + } else { + val ann = macroDef.getAnnotation(MacroImplAnnotation).getOrElse(throw new Error("assertion failed. %s: %s".format(macroDef, macroDef.annotations))) + val macroImpl = ann.args(0).symbol + var paramss = macroImpl.paramss + val tparams = macroImpl.typeParams + macroTraceVerbose("paramss: ")(paramss) + + // we need to take care of all possible combos of nullary/empty-paramlist macro defs vs nullary/empty-arglist invocations + // nullary def + nullary invocation => paramss and argss match, everything is okay + // nullary def + empty-arglist invocation => illegal Scala code, impossible, everything is okay + // empty-paramlist def + nullary invocation => uh-oh, we need to append a List() to argss + // empty-paramlist def + empty-arglist invocation => paramss and argss match, everything is okay + // that's almost it, but we need to account for the fact that paramss might have context bounds that mask the empty last paramlist + val paramss_without_evidences = transformTypeTagEvidenceParams(paramss, (param, tparam) => None) + val isEmptyParamlistDef = paramss_without_evidences.nonEmpty && paramss_without_evidences.last.isEmpty + val isEmptyArglistInvocation = argss.nonEmpty && argss.last.isEmpty + if (isEmptyParamlistDef && !isEmptyArglistInvocation) { + macroLogVerbose("isEmptyParamlistDef && !isEmptyArglistInvocation: appending a List() to argss") + argss = argss :+ Nil + } - val ann = macroDef.getAnnotation(MacroImplAnnotation).getOrElse(throw new Error("assertion failed. %s: %s".format(macroDef, macroDef.annotations))) - val macroImpl = ann.args(0).symbol - var paramss = macroImpl.paramss - val tparams = macroImpl.typeParams - macroTraceVerbose("paramss: ")(paramss) - - // we need to take care of all possible combos of nullary/empty-paramlist macro defs vs nullary/empty-arglist invocations - // nullary def + nullary invocation => paramss and argss match, everything is okay - // nullary def + empty-arglist invocation => illegal Scala code, impossible, everything is okay - // empty-paramlist def + nullary invocation => uh-oh, we need to append a List() to argss - // empty-paramlist def + empty-arglist invocation => paramss and argss match, everything is okay - // that's almost it, but we need to account for the fact that paramss might have context bounds that mask the empty last paramlist - val paramss_without_evidences = transformTypeTagEvidenceParams(paramss, (param, tparam) => None) - val isEmptyParamlistDef = paramss_without_evidences.nonEmpty && paramss_without_evidences.last.isEmpty - val isEmptyArglistInvocation = argss.nonEmpty && argss.last.isEmpty - if (isEmptyParamlistDef && !isEmptyArglistInvocation) { - macroLogVerbose("isEmptyParamlistDef && !isEmptyArglistInvocation: appending a List() to argss") - argss = argss :+ Nil - } + // nb! check partial application against paramss without evidences + val numParamLists = paramss_without_evidences.length + val numArgLists = argss.length + if (numParamLists != numArgLists) { + typer.TyperErrorGen.MacroPartialApplicationError(expandee) + return None + } - // nb! check partial application against paramss without evidences - val numParamLists = paramss_without_evidences.length - val numArgLists = argss.length - if (numParamLists != numArgLists) { - typer.TyperErrorGen.MacroPartialApplicationError(expandee) - return None - } + // if paramss have typetag context bounds, add an arglist to argss if necessary and instantiate the corresponding evidences + // consider the following example: + // + // class D[T] { + // class C[U] { + // def foo[V] = macro Impls.foo[T, U, V] + // } + // } + // + // val outer1 = new D[Int] + // val outer2 = new outer1.C[String] + // outer2.foo[Boolean] + // + // then T and U need to be inferred from the lexical scope of the call using ``asSeenFrom'' + // whereas V won't be resolved by asSeenFrom and need to be loaded directly from ``expandee'' which needs to contain a TypeApply node + // also, macro implementation reference may contain a regular type as a type argument, then we pass it verbatim + val resolved = collection.mutable.Map[Symbol, Type]() + paramss = transformTypeTagEvidenceParams(paramss, (param, tparam) => { + val TypeApply(_, implRefTargs) = ann.args(0) + var implRefTarg = implRefTargs(tparam.paramPos).tpe.typeSymbol + val tpe = if (implRefTarg.isTypeParameterOrSkolem) { + if (implRefTarg.owner == macroDef) { + // [Eugene] doesn't work when macro def is compiled separately from its usages + // then implRefTarg is not a skolem and isn't equal to any of macroDef.typeParams + // val paramPos = implRefTarg.deSkolemize.paramPos + val paramPos = macroDef.typeParams.indexWhere(_.name == implRefTarg.name) + typeArgs(paramPos).tpe + } else + implRefTarg.tpe.asSeenFrom( + if (prefixTree == EmptyTree) macroDef.owner.tpe else prefixTree.tpe, + macroDef.owner) + } else + implRefTarg.tpe + macroLogVerbose("resolved tparam %s as %s".format(tparam, tpe)) + resolved(tparam) = tpe + param.tpe.typeSymbol match { + case definitions.TypeTagClass => + // do nothing + case definitions.ConcreteTypeTagClass => + if (!tpe.isConcrete) context.abort(context.enclosingPosition, "cannot create ConcreteTypeTag from a type %s having unresolved type parameters".format(tpe)) + // otherwise do nothing + case _ => + throw new Error("unsupported tpe: " + tpe) + } + Some(tparam) + }) + val tags = paramss.last takeWhile (_.isType) map (resolved(_)) map (tpe => { + // generally speaking, it's impossible to calculate erasure from a tpe here + // the tpe might be compiled by this run, so its jClass might not exist yet + // hence I just pass `null` instead and leave this puzzle to macro programmers + val ttag = TypeTag(tpe, null) + if (ttag.isConcrete) ttag.toConcrete else ttag + }) + if (paramss.lastOption map (params => !params.isEmpty && params.forall(_.isType)) getOrElse false) argss = argss :+ Nil + argss = argss.dropRight(1) :+ (tags ++ argss.last) // todo. add support for context bounds in argss - // if paramss have typetag context bounds, add an arglist to argss if necessary and instantiate the corresponding evidences - // consider the following example: - // - // class D[T] { - // class C[U] { - // def foo[V] = macro Impls.foo[T, U, V] - // } - // } - // - // val outer1 = new D[Int] - // val outer2 = new outer1.C[String] - // outer2.foo[Boolean] - // - // then T and U need to be inferred from the lexical scope of the call using ``asSeenFrom'' - // whereas V won't be resolved by asSeenFrom and need to be loaded directly from ``expandee'' which needs to contain a TypeApply node - // also, macro implementation reference may contain a regular type as a type argument, then we pass it verbatim - val resolved = collection.mutable.Map[Symbol, Type]() - paramss = transformTypeTagEvidenceParams(paramss, (param, tparam) => { - val TypeApply(_, implRefTargs) = ann.args(0) - var implRefTarg = implRefTargs(tparam.paramPos).tpe.typeSymbol - val tpe = if (implRefTarg.isTypeParameterOrSkolem) { - if (implRefTarg.owner == macroDef) { - // [Eugene] doesn't work when macro def is compiled separately from its usages - // then implRefTarg is not a skolem and isn't equal to any of macroDef.typeParams -// val paramPos = implRefTarg.deSkolemize.paramPos - val paramPos = macroDef.typeParams.indexWhere(_.name == implRefTarg.name) - typeArgs(paramPos).tpe - } else - implRefTarg.tpe.asSeenFrom( - if (prefixTree == EmptyTree) macroDef.owner.tpe else prefixTree.tpe, - macroDef.owner) - } else - implRefTarg.tpe - macroLogVerbose("resolved tparam %s as %s".format(tparam, tpe)) - resolved(tparam) = tpe - param.tpe.typeSymbol match { - case definitions.TypeTagClass => - // do nothing - case definitions.ConcreteTypeTagClass => - if (!tpe.isConcrete) context.abort(context.enclosingPosition, "cannot create ConcreteTypeTag from a type %s having unresolved type parameters".format(tpe)) - // otherwise do nothing - case _ => - throw new Error("unsupported tpe: " + tpe) + assert(argss.length == paramss.length, "argss: %s, paramss: %s".format(argss, paramss)) + val rawArgss = for ((as, ps) <- argss zip paramss) yield { + if (isVarArgsList(ps)) as.take(ps.length - 1) :+ as.drop(ps.length - 1) + else as + } + rawArgss } - Some(tparam) - }) - val tags = paramss.last takeWhile (_.isType) map (resolved(_)) map (tpe => { - // generally speaking, it's impossible to calculate erasure from a tpe here - // the tpe might be compiled by this run, so its jClass might not exist yet - // hence I just pass `null` instead and leave this puzzle to macro programmers - val ttag = TypeTag(tpe, null) - if (ttag.isConcrete) ttag.toConcrete else ttag - }) - if (paramss.lastOption map (params => !params.isEmpty && params.forall(_.isType)) getOrElse false) argss = argss :+ Nil - argss = argss.dropRight(1) :+ (tags ++ argss.last) // todo. add support for context bounds in argss - - assert(argss.length == paramss.length, "argss: %s, paramss: %s".format(argss, paramss)) - val rawArgss = for ((as, ps) <- argss zip paramss) yield { - if (isVarArgsList(ps)) as.take(ps.length - 1) :+ as.drop(ps.length - 1) - else as - } val rawArgs = rawArgss.flatten macroTraceVerbose("rawArgs: ")(rawArgs) Some(rawArgs) @@ -1124,7 +1112,10 @@ trait Macros extends Traces { else { macroLogLite("typechecking macro expansion %s at %s".format(expandee, expandee.pos)) macroArgs(typer, expandee).fold(failExpansion(): MacroExpansionResult) { - case args @ ((context: MacroContext) :: _) => + // [Eugene++] crashes virtpatmat: + // case args @ ((context: MacroContext) :: _) => + case args @ (context0 :: _) => + val context = context0.asInstanceOf[MacroContext] if (nowDelayed) { macroLogLite("macro expansion is delayed: %s".format(expandee)) delayed += expandee -> undetparams diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 6d9c9c4ce8..553294d0fe 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1309,9 +1309,9 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R } List(tree1) } - case Import(_, _) => Nil - case DefDef(mods, _, _, _, _, _) if (mods hasFlag MACRO) => Nil - case _ => List(transform(tree)) + case Import(_, _) => Nil + case DefDef(mods, _, _, _, _, _) if (mods hasFlag MACRO) || (tree.symbol hasFlag MACRO) => Nil + case _ => List(transform(tree)) } /* Check whether argument types conform to bounds of type parameters */ @@ -1496,7 +1496,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R private def transformCaseApply(tree: Tree, ifNot: => Unit) = { val sym = tree.symbol - + def isClassTypeAccessible(tree: Tree): Boolean = tree match { case TypeApply(fun, targs) => isClassTypeAccessible(fun) @@ -1505,7 +1505,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R // the companion class is actually not a ClassSymbol, but a reference to an abstract type. module.symbol.companionClass.isClass } - + val doTransform = sym.isSourceMethod && sym.isCase && diff --git a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala index 329a247106..3033230603 100644 --- a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala +++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala @@ -1,10 +1,10 @@ package scala.tools.nsc package typechecker -import scala.reflect.makro.runtime.{Context => MacroContext} - trait StdAttachments { self: Analyzer => + type UnaffiliatedMacroContext = scala.reflect.makro.runtime.Context + type MacroContext = UnaffiliatedMacroContext { val mirror: self.global.type } case class MacroAttachment(delayed: Boolean, typerContext: Context, macroContext: Option[MacroContext]) }
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 88118adbe5..a2ef06fe38 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -5213,7 +5213,8 @@ trait Typers extends Modes with Adaptations with Taggings { } val isMacroBodyOkay = !tree.symbol.isErroneous && !(tree1 exists (_.isErroneous)) - if (isMacroBodyOkay) computeMacroDefTypeFromMacroImpl(ddef, tree.symbol, tree1.symbol) else AnyClass.tpe + val shouldInheritMacroImplReturnType = ddef.tpt.isEmpty + if (isMacroBodyOkay && shouldInheritMacroImplReturnType) computeMacroDefTypeFromMacroImpl(ddef, tree.symbol, tree1.symbol) else AnyClass.tpe } def transformedOr(tree: Tree, op: => Tree): Tree = transformed.get(tree) match { |