summaryrefslogtreecommitdiff
path: root/src/compiler/scala/reflect
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2014-02-13 02:34:14 +0100
committerEugene Burmako <xeno.by@gmail.com>2014-02-13 04:06:06 +0100
commitc85435d91e26b97e22470af74138b4e8d4c3ae41 (patch)
tree9f2d9e7b2dda559db2faadf4deb20aa07f2c858f /src/compiler/scala/reflect
parent00067482917cbe7ac337550e1f565fea966c2d78 (diff)
downloadscala-c85435d91e26b97e22470af74138b4e8d4c3ae41.tar.gz
scala-c85435d91e26b97e22470af74138b4e8d4c3ae41.tar.bz2
scala-c85435d91e26b97e22470af74138b4e8d4c3ae41.zip
SI-8270 unconfuses bundles and vanilla macros
This fixes a mistake in macro impl ref typechecking that used to have an heuristic to figure out whether it looks at a bundle method ref or at a vanilla object method ref. Under some circumstances the heuristic could fail, and then the macro engine would reject perfectly good macro impls. Now every macro impl ref is typechecked twice - once as a bundle method ref and once as a vanilla object method ref. Results are then analyzed, checked against ambiguities (which are now correctly reported instead of incorrectly prioritizing towards bundles) and delivered to the macro engine. The only heuristic left in place is the one that's used to report errors. If both bundle and vanilla typechecks fail, then if a bundle candidate looks sufficiently similar to a bundle, a bundle typecheck error is reported providing some common bundle definition hints.
Diffstat (limited to 'src/compiler/scala/reflect')
-rw-r--r--src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala68
-rw-r--r--src/compiler/scala/reflect/macros/compiler/Errors.scala173
-rw-r--r--src/compiler/scala/reflect/macros/compiler/Resolvers.scala78
-rw-r--r--src/compiler/scala/reflect/macros/compiler/Validators.scala344
4 files changed, 357 insertions, 306 deletions
diff --git a/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala b/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala
index 2e82e34bd9..1413065a27 100644
--- a/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala
+++ b/src/compiler/scala/reflect/macros/compiler/DefaultMacroCompiler.scala
@@ -8,6 +8,11 @@ abstract class DefaultMacroCompiler extends Resolvers
with Errors {
val global: Global
import global._
+ import analyzer._
+ import treeInfo._
+ import definitions._
+ val runDefinitions = currentRun.runDefinitions
+ import runDefinitions.{Predef_???, _}
val typer: global.analyzer.Typer
val context = typer.context
@@ -15,13 +20,72 @@ abstract class DefaultMacroCompiler extends Resolvers
val macroDdef: DefDef
lazy val macroDef = macroDdef.symbol
+ case class MacroImplRefCompiler(untypedMacroImplRef: Tree, isImplBundle: Boolean) extends Resolver with Validator with Error
private case class MacroImplResolutionException(pos: Position, msg: String) extends Exception
def abort(pos: Position, msg: String) = throw MacroImplResolutionException(pos, msg)
+ /** Resolves a macro impl reference provided in the right-hand side of the given macro definition.
+ *
+ * Acceptable shapes of the right-hand side:
+ * 1) [<static object>].<method name>[[<type args>]] // vanilla macro impl ref
+ * 2) [<macro bundle>].<method name>[[<type args>]] // shiny new macro bundle impl ref
+ *
+ * Produces a tree, which represents a reference to a macro implementation if everything goes well,
+ * otherwise reports found errors and returns EmptyTree. The resulting tree should have the following format:
+ *
+ * qualifier.method[targs]
+ *
+ * Qualifier here might be omitted (local macro defs), be a static object (vanilla macro defs)
+ * or be a dummy instance of a macro bundle (e.g. new MyMacro(???).expand).
+ */
def resolveMacroImpl: Tree = {
+ def tryCompile(compiler: MacroImplRefCompiler): scala.util.Try[Tree] = {
+ try { compiler.validateMacroImplRef(); scala.util.Success(compiler.macroImplRef) }
+ catch { case ex: MacroImplResolutionException => scala.util.Failure(ex) }
+ }
+ val vanillaImplRef = MacroImplRefCompiler(macroDdef.rhs.duplicate, isImplBundle = false)
+ val (maybeBundleRef, methName, targs) = macroDdef.rhs.duplicate match {
+ case Applied(Select(Applied(RefTree(qual, bundleName), _, Nil), methName), targs, Nil) =>
+ (RefTree(qual, bundleName.toTypeName), methName, targs)
+ case Applied(Ident(methName), targs, Nil) =>
+ (Ident(context.owner.enclClass), methName, targs)
+ case _ =>
+ (EmptyTree, TermName(""), Nil)
+ }
+ val bundleImplRef = MacroImplRefCompiler(
+ atPos(macroDdef.rhs.pos)(gen.mkTypeApply(Select(New(maybeBundleRef, List(List(Ident(Predef_???)))), methName), targs)),
+ isImplBundle = true
+ )
+ val vanillaResult = tryCompile(vanillaImplRef)
+ val bundleResult = tryCompile(bundleImplRef)
+
+ def ensureUnambiguousSuccess() = {
+ // we now face a hard choice of whether to report ambiguity:
+ // 1) when there are eponymous methods in both bundle and object
+ // 2) when both references to eponymous methods are resolved successfully
+ // doing #1 would cause less confusion in the long run, but it would also cause more frequent source incompatibilities
+ // e.g. it would fail to compile https://github.com/ReifyIt/basis
+ // therefore here we go for #2
+ // if (vanillaImplRef.looksCredible && bundleImplRef.looksCredible) MacroImplAmbiguousError()
+ if (vanillaResult.isSuccess && bundleResult.isSuccess) MacroImplAmbiguousError()
+ }
+
+ def reportMostAppropriateFailure() = {
+ typer.silent(_.typedTypeConstructor(maybeBundleRef)) match {
+ case SilentResultValue(result) if looksLikeMacroBundleType(result.tpe) =>
+ val bundle = result.tpe.typeSymbol
+ if (!isMacroBundleType(bundle.tpe)) MacroBundleWrongShapeError()
+ if (!bundle.owner.isStaticOwner) MacroBundleNonStaticError()
+ bundleResult.get
+ case _ =>
+ vanillaResult.get
+ }
+ }
+
try {
- validateMacroImplRef()
- macroImplRef
+ if (vanillaResult.isSuccess || bundleResult.isSuccess) ensureUnambiguousSuccess()
+ if (vanillaResult.isFailure && bundleResult.isFailure) reportMostAppropriateFailure()
+ vanillaResult.orElse(bundleResult).get
} catch {
case MacroImplResolutionException(pos, msg) =>
context.error(pos, msg)
diff --git a/src/compiler/scala/reflect/macros/compiler/Errors.scala b/src/compiler/scala/reflect/macros/compiler/Errors.scala
index 280baa2a42..490ab3657a 100644
--- a/src/compiler/scala/reflect/macros/compiler/Errors.scala
+++ b/src/compiler/scala/reflect/macros/compiler/Errors.scala
@@ -13,12 +13,9 @@ trait Errors extends Traces {
import treeInfo._
import typer.TyperErrorGen._
import typer.infer.InferErrorGen._
- private val runDefinitions = currentRun.runDefinitions
import runDefinitions._
def globalSettings = global.settings
- // sanity check errors
-
private def implRefError(message: String) = {
val Applied(culprit, _, _) = macroDdef.rhs
abort(culprit.pos, message)
@@ -33,111 +30,125 @@ trait Errors extends Traces {
abort(culprit.pos, message)
}
- def MacroImplReferenceWrongShapeError() = implRefError(
+ def MacroImplAmbiguousError() = implRefError(
+ "macro implementation reference is ambiguous: makes sense both as\n"+
+ "a macro bundle method reference and a vanilla object method reference")
+
+ def MacroBundleNonStaticError() = bundleRefError("macro bundles must be static")
+
+ def MacroBundleWrongShapeError() = bundleRefError("macro bundles must be concrete classes having a single constructor with a `val c: Context` parameter")
+
+ trait Error {
+ self: MacroImplRefCompiler =>
+
+ // sanity check errors
+
+ def MacroImplReferenceWrongShapeError() = implRefError(
"macro implementation reference has wrong shape. required:\n"+
"macro [<static object>].<method name>[[<type args>]] or\n" +
"macro [<macro bundle>].<method name>[[<type args>]]")
- def MacroImplWrongNumberOfTypeArgumentsError() = {
- val diagnostic = if (macroImpl.typeParams.length > targs.length) "has too few type arguments" else "has too many arguments"
- implRefError(s"macro implementation reference $diagnostic for " + treeSymTypeMsg(macroImplRef))
- }
-
- def MacroImplNotPublicError() = implRefError("macro implementation must be public")
+ def MacroImplWrongNumberOfTypeArgumentsError() = {
+ val diagnostic = if (macroImpl.typeParams.length > targs.length) "has too few type arguments" else "has too many arguments"
+ implRefError(s"macro implementation reference $diagnostic for " + treeSymTypeMsg(macroImplRef))
+ }
- def MacroImplOverloadedError() = implRefError("macro implementation cannot be overloaded")
+ private def macroImplementationWording =
+ if (isImplBundle) "bundle implementation"
+ else "macro implementation"
- def MacroImplNonTagImplicitParameters(params: List[Symbol]) = implRefError("macro implementations cannot have implicit parameters other than WeakTypeTag evidences")
+ def MacroImplNotPublicError() = implRefError(s"${macroImplementationWording} must be public")
- def MacroBundleNonStaticError() = bundleRefError("macro bundles must be static")
+ def MacroImplOverloadedError() = implRefError(s"${macroImplementationWording} cannot be overloaded")
- def MacroBundleWrongShapeError() = bundleRefError("macro bundles must be concrete classes having a single constructor with a `val c: Context` parameter")
+ def MacroImplNonTagImplicitParameters(params: List[Symbol]) = implRefError(s"${macroImplementationWording}s cannot have implicit parameters other than WeakTypeTag evidences")
- // compatibility errors
+ // compatibility errors
- // helpers
+ // helpers
- private def lengthMsg(flavor: String, violation: String, extra: Symbol) = {
- val noun = if (flavor == "value") "parameter" else "type parameter"
- val message = noun + " lists have different length, " + violation + " extra " + noun
- val suffix = if (extra ne NoSymbol) " " + extra.defString else ""
- message + suffix
- }
+ private def lengthMsg(flavor: String, violation: String, extra: Symbol) = {
+ val noun = if (flavor == "value") "parameter" else "type parameter"
+ val message = noun + " lists have different length, " + violation + " extra " + noun
+ val suffix = if (extra ne NoSymbol) " " + extra.defString else ""
+ message + suffix
+ }
- private def abbreviateCoreAliases(s: String): String = {
- val coreAliases = List("WeakTypeTag", "Expr", "Tree")
- coreAliases.foldLeft(s)((res, x) => res.replace("c.universe." + x, "c." + x))
- }
+ private def abbreviateCoreAliases(s: String): String = {
+ val coreAliases = List("WeakTypeTag", "Expr", "Tree")
+ coreAliases.foldLeft(s)((res, x) => res.replace("c.universe." + x, "c." + x))
+ }
- private def showMeth(pss: List[List[Symbol]], restpe: Type, abbreviate: Boolean, untype: Boolean) = {
- def preprocess(tpe: Type) = if (untype) untypeMetalevel(tpe) else tpe
- var pssPart = (pss map (ps => ps map (p => p.defStringSeenAs(preprocess(p.info))) mkString ("(", ", ", ")"))).mkString
- if (abbreviate) pssPart = abbreviateCoreAliases(pssPart)
- var retPart = preprocess(restpe).toString
- if (abbreviate || macroDdef.tpt.tpe == null) retPart = abbreviateCoreAliases(retPart)
- pssPart + ": " + retPart
- }
+ private def showMeth(pss: List[List[Symbol]], restpe: Type, abbreviate: Boolean, untype: Boolean) = {
+ def preprocess(tpe: Type) = if (untype) untypeMetalevel(tpe) else tpe
+ var pssPart = (pss map (ps => ps map (p => p.defStringSeenAs(preprocess(p.info))) mkString ("(", ", ", ")"))).mkString
+ if (abbreviate) pssPart = abbreviateCoreAliases(pssPart)
+ var retPart = preprocess(restpe).toString
+ if (abbreviate || macroDdef.tpt.tpe == null) retPart = abbreviateCoreAliases(retPart)
+ pssPart + ": " + retPart
+ }
- // not exactly an error generator, but very related
- // and I dearly wanted to push it away from Macros.scala
- private def checkConforms(slot: String, rtpe: Type, atpe: Type) = {
- val verbose = macroDebugVerbose
-
- def check(rtpe: Type, atpe: Type): Boolean = {
- def success() = { if (verbose) println(rtpe + " <: " + atpe + "?" + EOL + "true"); true }
- (rtpe, atpe) match {
- case _ if rtpe eq atpe => success()
- case (TypeRef(_, RepeatedParamClass, rtpe :: Nil), TypeRef(_, RepeatedParamClass, atpe :: Nil)) => check(rtpe, atpe)
- case (ExprClassOf(_), TreeType()) if rtpe.prefix =:= atpe.prefix => success()
- case (SubtreeType(), ExprClassOf(_)) if rtpe.prefix =:= atpe.prefix => success()
- case _ => rtpe <:< atpe
+ // not exactly an error generator, but very related
+ // and I dearly wanted to push it away from Macros.scala
+ private def checkConforms(slot: String, rtpe: Type, atpe: Type) = {
+ val verbose = macroDebugVerbose
+
+ def check(rtpe: Type, atpe: Type): Boolean = {
+ def success() = { if (verbose) println(rtpe + " <: " + atpe + "?" + EOL + "true"); true }
+ (rtpe, atpe) match {
+ case _ if rtpe eq atpe => success()
+ case (TypeRef(_, RepeatedParamClass, rtpe :: Nil), TypeRef(_, RepeatedParamClass, atpe :: Nil)) => check(rtpe, atpe)
+ case (ExprClassOf(_), TreeType()) if rtpe.prefix =:= atpe.prefix => success()
+ case (SubtreeType(), ExprClassOf(_)) if rtpe.prefix =:= atpe.prefix => success()
+ case _ => rtpe <:< atpe
+ }
}
- }
- val ok =
- if (verbose) withTypesExplained(check(rtpe, atpe))
- else check(rtpe, atpe)
- if (!ok) {
- if (!verbose) explainTypes(rtpe, atpe)
- val msg = {
- val ss = Seq(rtpe, atpe) map (this abbreviateCoreAliases _.toString)
- s"type mismatch for $slot: ${ss(0)} does not conform to ${ss(1)}"
+ val ok =
+ if (verbose) withTypesExplained(check(rtpe, atpe))
+ else check(rtpe, atpe)
+ if (!ok) {
+ if (!verbose) explainTypes(rtpe, atpe)
+ val msg = {
+ val ss = Seq(rtpe, atpe) map (this abbreviateCoreAliases _.toString)
+ s"type mismatch for $slot: ${ss(0)} does not conform to ${ss(1)}"
+ }
+ compatibilityError(msg)
}
- compatibilityError(msg)
}
- }
- private def compatibilityError(message: String) =
- implRefError(
- "macro implementation has incompatible shape:"+
- "\n required: " + showMeth(rparamss, rret, abbreviate = true, untype = false) +
- "\n or : " + showMeth(rparamss, rret, abbreviate = true, untype = true) +
- "\n found : " + showMeth(aparamss, aret, abbreviate = false, untype = false) +
- "\n" + message)
+ private def compatibilityError(message: String) =
+ implRefError(
+ s"${macroImplementationWording} has incompatible shape:"+
+ "\n required: " + showMeth(rparamss, rret, abbreviate = true, untype = false) +
+ "\n or : " + showMeth(rparamss, rret, abbreviate = true, untype = true) +
+ "\n found : " + showMeth(aparamss, aret, abbreviate = false, untype = false) +
+ "\n" + message)
- def MacroImplParamssMismatchError() = compatibilityError("number of parameter sections differ")
+ def MacroImplParamssMismatchError() = compatibilityError("number of parameter sections differ")
- def MacroImplExtraParamsError(aparams: List[Symbol], rparams: List[Symbol]) = compatibilityError(lengthMsg("value", "found", aparams(rparams.length)))
+ def MacroImplExtraParamsError(aparams: List[Symbol], rparams: List[Symbol]) = compatibilityError(lengthMsg("value", "found", aparams(rparams.length)))
- def MacroImplMissingParamsError(aparams: List[Symbol], rparams: List[Symbol]) = compatibilityError(abbreviateCoreAliases(lengthMsg("value", "required", rparams(aparams.length))))
+ def MacroImplMissingParamsError(aparams: List[Symbol], rparams: List[Symbol]) = compatibilityError(abbreviateCoreAliases(lengthMsg("value", "required", rparams(aparams.length))))
- def checkMacroImplParamTypeMismatch(atpe: Type, rparam: Symbol) = checkConforms("parameter " + rparam.name, rparam.tpe, atpe)
+ def checkMacroImplParamTypeMismatch(atpe: Type, rparam: Symbol) = checkConforms("parameter " + rparam.name, rparam.tpe, atpe)
- def checkMacroImplResultTypeMismatch(atpe: Type, rret: Type) = checkConforms("return type", atpe, rret)
+ def checkMacroImplResultTypeMismatch(atpe: Type, rret: Type) = checkConforms("return type", atpe, rret)
- def MacroImplParamNameMismatchError(aparam: Symbol, rparam: Symbol) = compatibilityError("parameter names differ: " + rparam.name + " != " + aparam.name)
+ def MacroImplParamNameMismatchError(aparam: Symbol, rparam: Symbol) = compatibilityError("parameter names differ: " + rparam.name + " != " + aparam.name)
- def MacroImplVarargMismatchError(aparam: Symbol, rparam: Symbol) = {
- def fail(paramName: Name) = compatibilityError("types incompatible for parameter " + paramName + ": corresponding is not a vararg parameter")
- if (isRepeated(rparam) && !isRepeated(aparam)) fail(rparam.name)
- if (!isRepeated(rparam) && isRepeated(aparam)) fail(aparam.name)
- }
+ def MacroImplVarargMismatchError(aparam: Symbol, rparam: Symbol) = {
+ def fail(paramName: Name) = compatibilityError("types incompatible for parameter " + paramName + ": corresponding is not a vararg parameter")
+ if (isRepeated(rparam) && !isRepeated(aparam)) fail(rparam.name)
+ if (!isRepeated(rparam) && isRepeated(aparam)) fail(aparam.name)
+ }
- def MacroImplTargMismatchError(atargs: List[Type], atparams: List[Symbol]) =
- compatibilityError(NotWithinBoundsErrorMessage("", atargs, atparams, macroDebugVerbose || settings.explaintypes.value))
+ def MacroImplTargMismatchError(atargs: List[Type], atparams: List[Symbol]) =
+ compatibilityError(NotWithinBoundsErrorMessage("", atargs, atparams, macroDebugVerbose || settings.explaintypes.value))
- def MacroImplTparamInstantiationError(atparams: List[Symbol], e: NoInstance) = {
- val badps = atparams map (_.defString) mkString ", "
- compatibilityError(f"type parameters $badps cannot be instantiated%n${e.getMessage}")
+ def MacroImplTparamInstantiationError(atparams: List[Symbol], e: NoInstance) = {
+ val badps = atparams map (_.defString) mkString ", "
+ compatibilityError(f"type parameters $badps cannot be instantiated%n${e.getMessage}")
+ }
}
}
diff --git a/src/compiler/scala/reflect/macros/compiler/Resolvers.scala b/src/compiler/scala/reflect/macros/compiler/Resolvers.scala
index d35f1c32a9..807fb688a0 100644
--- a/src/compiler/scala/reflect/macros/compiler/Resolvers.scala
+++ b/src/compiler/scala/reflect/macros/compiler/Resolvers.scala
@@ -12,61 +12,35 @@ trait Resolvers {
import definitions._
import treeInfo._
import gen._
- private val runDefinitions = currentRun.runDefinitions
- import runDefinitions.{Predef_???, _}
+ import runDefinitions._
- /** Resolves a macro impl reference provided in the right-hand side of the given macro definition.
- *
- * Acceptable shapes of the right-hand side:
- * 1) [<static object>].<method name>[[<type args>]] // vanilla macro def
- * 2) [<macro bundle>].<method name>[[<type args>]] // shiny new macro bundle
- *
- * Produces a tree, which represents a reference to a macro implementation if everything goes well,
- * otherwise reports found errors and returns EmptyTree. The resulting tree should have the following format:
- *
- * qualifier.method[targs]
- *
- * Qualifier here might be omitted (local macro defs), be a static object (vanilla macro defs)
- * or be a dummy instance of a macro bundle (e.g. new MyMacro(???).expand).
- */
- lazy val macroImplRef: Tree = {
- val (maybeBundleRef, methName, targs) = macroDdef.rhs match {
- case Applied(Select(Applied(RefTree(qual, bundleName), _, Nil), methName), targs, Nil) =>
- (RefTree(qual, bundleName.toTypeName), methName, targs)
- case Applied(Ident(methName), targs, Nil) =>
- (Ident(context.owner.enclClass), methName, targs)
- case _ =>
- (EmptyTree, TermName(""), Nil)
- }
+ trait Resolver {
+ self: MacroImplRefCompiler =>
- val untypedImplRef = typer.silent(_.typedTypeConstructor(maybeBundleRef)) match {
- case SilentResultValue(result) if looksLikeMacroBundleType(result.tpe) =>
- val bundle = result.tpe.typeSymbol
- if (!isMacroBundleType(bundle.tpe)) MacroBundleWrongShapeError()
- if (!bundle.owner.isStaticOwner) MacroBundleNonStaticError()
- atPos(macroDdef.rhs.pos)(gen.mkTypeApply(Select(New(bundle, Ident(Predef_???)), methName), targs))
- case _ =>
- macroDdef.rhs
- }
+ val isImplBundle: Boolean
+ val isImplMethod = !isImplBundle
- val typedImplRef = typer.silent(_.typed(markMacroImplRef(untypedImplRef)), reportAmbiguousErrors = false)
- typedImplRef match {
- case SilentResultValue(success) => success
- case SilentTypeError(err) => abort(err.errPos, err.errMsg)
+ lazy val looksCredible: Boolean = {
+ val Applied(core, _, _) = untypedMacroImplRef
+ typer.silent(_.typed(markMacroImplRef(core)), reportAmbiguousErrors = false).nonEmpty
}
- }
- // FIXME: cannot write this concisely because of SI-7507
- // lazy val (isImplBundle, macroImplOwner, macroImpl, macroImplTargs) =
- private lazy val dissectedMacroImplRef =
- macroImplRef match {
- case MacroImplReference(isBundle, isBlackbox, owner, meth, targs) => (isBundle, isBlackbox, owner, meth, targs)
- case _ => MacroImplReferenceWrongShapeError()
- }
- lazy val isImplBundle = dissectedMacroImplRef._1
- lazy val isImplMethod = !isImplBundle
- lazy val isImplBlackbox = dissectedMacroImplRef._2
- lazy val macroImplOwner = dissectedMacroImplRef._3
- lazy val macroImpl = dissectedMacroImplRef._4
- lazy val targs = dissectedMacroImplRef._5
+ lazy val macroImplRef: Tree =
+ typer.silent(_.typed(markMacroImplRef(untypedMacroImplRef)), reportAmbiguousErrors = false) match {
+ case SilentResultValue(success) => success
+ case SilentTypeError(err) => abort(err.errPos, err.errMsg)
+ }
+
+ // FIXME: cannot write this concisely because of SI-7507
+ // lazy val (_, macroImplOwner, macroImpl, macroImplTargs) =
+ private lazy val dissectedMacroImplRef =
+ macroImplRef match {
+ case MacroImplReference(isBundle, isBlackbox, owner, meth, targs) => (isBlackbox, owner, meth, targs)
+ case _ => MacroImplReferenceWrongShapeError()
+ }
+ lazy val isImplBlackbox = dissectedMacroImplRef._1
+ lazy val macroImplOwner = dissectedMacroImplRef._2
+ lazy val macroImpl = dissectedMacroImplRef._3
+ lazy val targs = dissectedMacroImplRef._4
+ }
}
diff --git a/src/compiler/scala/reflect/macros/compiler/Validators.scala b/src/compiler/scala/reflect/macros/compiler/Validators.scala
index 02c1f7c431..fc118028dd 100644
--- a/src/compiler/scala/reflect/macros/compiler/Validators.scala
+++ b/src/compiler/scala/reflect/macros/compiler/Validators.scala
@@ -1,9 +1,7 @@
package scala.reflect.macros
package compiler
-import java.util.UUID.randomUUID
import scala.reflect.internal.Flags._
-import scala.reflect.macros.TypecheckException
trait Validators {
self: DefaultMacroCompiler =>
@@ -11,189 +9,193 @@ trait Validators {
import global._
import analyzer._
import definitions._
- private val runDefinitions = currentRun.runDefinitions
import runDefinitions.{Predef_???, _}
- def validateMacroImplRef() = {
- sanityCheck()
- if (macroImpl != Predef_???) checkMacroDefMacroImplCorrespondence()
- }
+ trait Validator {
+ self: MacroImplRefCompiler =>
- private def sanityCheck() = {
- if (!macroImpl.isMethod) MacroImplReferenceWrongShapeError()
- if (macroImpl.typeParams.length != targs.length) MacroImplWrongNumberOfTypeArgumentsError()
- if (!macroImpl.isPublic) MacroImplNotPublicError()
- if (macroImpl.isOverloaded) MacroImplOverloadedError()
- val implicitParams = aparamss.flatten filter (_.isImplicit)
- if (implicitParams.nonEmpty) MacroImplNonTagImplicitParameters(implicitParams)
- val effectiveOwner = if (isImplMethod) macroImplOwner else macroImplOwner.owner
- val declaredInStaticObject = effectiveOwner.isStaticOwner || effectiveOwner.moduleClass.isStaticOwner
- if (!declaredInStaticObject) MacroImplReferenceWrongShapeError()
- }
+ def validateMacroImplRef() = {
+ sanityCheck()
+ if (macroImpl != Predef_???) checkMacroDefMacroImplCorrespondence()
+ }
- private def checkMacroDefMacroImplCorrespondence() = {
- val atvars = atparams map freshVar
- def atpeToRtpe(atpe: Type) = atpe.substSym(aparamss.flatten, rparamss.flatten).instantiateTypeParams(atparams, atvars)
-
- // we only check strict correspondence between value parameterss
- // type parameters of macro defs and macro impls don't have to coincide with each other
- if (aparamss.length != rparamss.length) MacroImplParamssMismatchError()
- map2(aparamss, rparamss)((aparams, rparams) => {
- if (aparams.length < rparams.length) MacroImplMissingParamsError(aparams, rparams)
- if (rparams.length < aparams.length) MacroImplExtraParamsError(aparams, rparams)
- })
-
- try {
- // cannot fuse this map2 and the map2 above because if aparamss.flatten != rparamss.flatten
- // then `atpeToRtpe` is going to fail with an unsound substitution
- map2(aparamss.flatten, rparamss.flatten)((aparam, rparam) => {
- if (aparam.name != rparam.name && !rparam.isSynthetic) MacroImplParamNameMismatchError(aparam, rparam)
- if (isRepeated(aparam) ^ isRepeated(rparam)) MacroImplVarargMismatchError(aparam, rparam)
- val aparamtpe = aparam.tpe match {
- case MacroContextType(tpe) => tpe
- case tpe => tpe
- }
- checkMacroImplParamTypeMismatch(atpeToRtpe(aparamtpe), rparam)
+ private def sanityCheck() = {
+ if (!macroImpl.isMethod) MacroImplReferenceWrongShapeError()
+ if (macroImpl.typeParams.length != targs.length) MacroImplWrongNumberOfTypeArgumentsError()
+ if (!macroImpl.isPublic) MacroImplNotPublicError()
+ if (macroImpl.isOverloaded) MacroImplOverloadedError()
+ val implicitParams = aparamss.flatten filter (_.isImplicit)
+ if (implicitParams.nonEmpty) MacroImplNonTagImplicitParameters(implicitParams)
+ val effectiveOwner = if (isImplMethod) macroImplOwner else macroImplOwner.owner
+ val effectivelyStatic = effectiveOwner.isStaticOwner || effectiveOwner.moduleClass.isStaticOwner
+ val correctBundleness = if (isImplMethod) macroImplOwner.isModuleClass else macroImplOwner.isClass && !macroImplOwner.isModuleClass
+ if (!effectivelyStatic || !correctBundleness) MacroImplReferenceWrongShapeError()
+ }
+
+ private def checkMacroDefMacroImplCorrespondence() = {
+ val atvars = atparams map freshVar
+ def atpeToRtpe(atpe: Type) = atpe.substSym(aparamss.flatten, rparamss.flatten).instantiateTypeParams(atparams, atvars)
+
+ // we only check strict correspondence between value parameterss
+ // type parameters of macro defs and macro impls don't have to coincide with each other
+ if (aparamss.length != rparamss.length) MacroImplParamssMismatchError()
+ map2(aparamss, rparamss)((aparams, rparams) => {
+ if (aparams.length < rparams.length) MacroImplMissingParamsError(aparams, rparams)
+ if (rparams.length < aparams.length) MacroImplExtraParamsError(aparams, rparams)
})
- checkMacroImplResultTypeMismatch(atpeToRtpe(aret), rret)
+ try {
+ // cannot fuse this map2 and the map2 above because if aparamss.flatten != rparamss.flatten
+ // then `atpeToRtpe` is going to fail with an unsound substitution
+ map2(aparamss.flatten, rparamss.flatten)((aparam, rparam) => {
+ if (aparam.name != rparam.name && !rparam.isSynthetic) MacroImplParamNameMismatchError(aparam, rparam)
+ if (isRepeated(aparam) ^ isRepeated(rparam)) MacroImplVarargMismatchError(aparam, rparam)
+ val aparamtpe = aparam.tpe match {
+ case MacroContextType(tpe) => tpe
+ case tpe => tpe
+ }
+ checkMacroImplParamTypeMismatch(atpeToRtpe(aparamtpe), rparam)
+ })
- val maxLubDepth = lubDepth(aparamss.flatten map (_.tpe)) max lubDepth(rparamss.flatten map (_.tpe))
- val atargs = solvedTypes(atvars, atparams, atparams map varianceInType(aret), upper = false, maxLubDepth)
- val boundsOk = typer.silent(_.infer.checkBounds(macroDdef, NoPrefix, NoSymbol, atparams, atargs, ""))
- boundsOk match {
- case SilentResultValue(true) => // do nothing, success
- case SilentResultValue(false) | SilentTypeError(_) => MacroImplTargMismatchError(atargs, atparams)
+ checkMacroImplResultTypeMismatch(atpeToRtpe(aret), rret)
+
+ val maxLubDepth = lubDepth(aparamss.flatten map (_.tpe)) max lubDepth(rparamss.flatten map (_.tpe))
+ val atargs = solvedTypes(atvars, atparams, atparams map varianceInType(aret), upper = false, maxLubDepth)
+ val boundsOk = typer.silent(_.infer.checkBounds(macroDdef, NoPrefix, NoSymbol, atparams, atargs, ""))
+ boundsOk match {
+ case SilentResultValue(true) => // do nothing, success
+ case SilentResultValue(false) | SilentTypeError(_) => MacroImplTargMismatchError(atargs, atparams)
+ }
+ } catch {
+ case ex: NoInstance => MacroImplTparamInstantiationError(atparams, ex)
}
- } catch {
- case ex: NoInstance => MacroImplTparamInstantiationError(atparams, ex)
}
- }
- // aXXX (e.g. aparamss) => characteristics of the actual macro impl signature extracted from the macro impl ("a" stands for "actual")
- // rXXX (e.g. rparamss) => characteristics of the reference macro impl signature synthesized from the macro def ("r" stands for "reference")
- // FIXME: cannot write this concisely because of SI-7507
- //lazy val MacroImplSig(atparams, aparamss, aret) = macroImplSig
- //lazy val MacroImplSig(_, rparamss, rret) = referenceMacroImplSig
- lazy val atparams = macroImplSig.tparams
- lazy val aparamss = macroImplSig.paramss
- lazy val aret = macroImplSig.ret
- lazy val rparamss = referenceMacroImplSig.paramss
- lazy val rret = referenceMacroImplSig.ret
-
- // Technically this can be just an alias to MethodType, but promoting it to a first-class entity
- // provides better encapsulation and convenient syntax for pattern matching.
- private case class MacroImplSig(tparams: List[Symbol], paramss: List[List[Symbol]], ret: Type) {
- private def tparams_s = if (tparams.isEmpty) "" else tparams.map(_.defString).mkString("[", ", ", "]")
- private def paramss_s = paramss map (ps => ps.map(s => s"${s.name}: ${s.tpe_*}").mkString("(", ", ", ")")) mkString ""
- override def toString = "MacroImplSig(" + tparams_s + paramss_s + ret + ")"
- }
+ // aXXX (e.g. aparamss) => characteristics of the actual macro impl signature extracted from the macro impl ("a" stands for "actual")
+ // rXXX (e.g. rparamss) => characteristics of the reference macro impl signature synthesized from the macro def ("r" stands for "reference")
+ // FIXME: cannot write this concisely because of SI-7507
+ //lazy val MacroImplSig(atparams, aparamss, aret) = macroImplSig
+ //lazy val MacroImplSig(_, rparamss, rret) = referenceMacroImplSig
+ lazy val atparams = macroImplSig.tparams
+ lazy val aparamss = macroImplSig.paramss
+ lazy val aret = macroImplSig.ret
+ lazy val rparamss = referenceMacroImplSig.paramss
+ lazy val rret = referenceMacroImplSig.ret
+
+ // Technically this can be just an alias to MethodType, but promoting it to a first-class entity
+ // provides better encapsulation and convenient syntax for pattern matching.
+ private case class MacroImplSig(tparams: List[Symbol], paramss: List[List[Symbol]], ret: Type) {
+ private def tparams_s = if (tparams.isEmpty) "" else tparams.map(_.defString).mkString("[", ", ", "]")
+ private def paramss_s = paramss map (ps => ps.map(s => s"${s.name}: ${s.tpe_*}").mkString("(", ", ", ")")) mkString ""
+ override def toString = "MacroImplSig(" + tparams_s + paramss_s + ret + ")"
+ }
- /** An actual macro implementation signature extracted from a macro implementation method.
- *
- * For the following macro impl:
- * def fooBar[T: c.WeakTypeTag]
- * (c: scala.reflect.macros.blackbox.Context)
- * (xs: c.Expr[List[T]])
- * : c.Expr[T] = ...
- *
- * This function will return:
- * (c: scala.reflect.macros.blackbox.Context)(xs: c.Expr[List[T]])c.Expr[T]
- *
- * Note that type tag evidence parameters are not included into the result.
- * Type tag context bounds for macro impl tparams are optional.
- * Therefore compatibility checks ignore such parameters, and we don't need to bother about them here.
- *
- * This method cannot be reduced to just macroImpl.info, because macro implementations might
- * come in different shapes. If the implementation is an apply method of a *box.Macro-compatible object,
- * then it won't have (c: *box.Context) in its parameters, but will rather refer to *boxMacro.c.
- *
- * @param macroImpl The macro implementation symbol
- */
- private lazy val macroImplSig: MacroImplSig = {
- val tparams = macroImpl.typeParams
- val paramss = transformTypeTagEvidenceParams(macroImplRef, (param, tparam) => NoSymbol)
- val ret = macroImpl.info.finalResultType
- MacroImplSig(tparams, paramss, ret)
- }
+ /** An actual macro implementation signature extracted from a macro implementation method.
+ *
+ * For the following macro impl:
+ * def fooBar[T: c.WeakTypeTag]
+ * (c: scala.reflect.macros.blackbox.Context)
+ * (xs: c.Expr[List[T]])
+ * : c.Expr[T] = ...
+ *
+ * This function will return:
+ * (c: scala.reflect.macros.blackbox.Context)(xs: c.Expr[List[T]])c.Expr[T]
+ *
+ * Note that type tag evidence parameters are not included into the result.
+ * Type tag context bounds for macro impl tparams are optional.
+ * Therefore compatibility checks ignore such parameters, and we don't need to bother about them here.
+ *
+ * This method cannot be reduced to just macroImpl.info, because macro implementations might
+ * come in different shapes. If the implementation is an apply method of a *box.Macro-compatible object,
+ * then it won't have (c: *box.Context) in its parameters, but will rather refer to *boxMacro.c.
+ *
+ * @param macroImpl The macro implementation symbol
+ */
+ private lazy val macroImplSig: MacroImplSig = {
+ val tparams = macroImpl.typeParams
+ val paramss = transformTypeTagEvidenceParams(macroImplRef, (param, tparam) => NoSymbol)
+ val ret = macroImpl.info.finalResultType
+ MacroImplSig(tparams, paramss, ret)
+ }
- /** A reference macro implementation signature extracted from a given macro definition.
- *
- * For the following macro def:
- * def foo[T](xs: List[T]): T = macro fooBar
- *
- * This function will return:
- * (c: scala.reflect.macros.blackbox.Context)(xs: c.Expr[List[T]])c.Expr[T] or
- * (c: scala.reflect.macros.whitebox.Context)(xs: c.Expr[List[T]])c.Expr[T]
- *
- * Note that type tag evidence parameters are not included into the result.
- * Type tag context bounds for macro impl tparams are optional.
- * Therefore compatibility checks ignore such parameters, and we don't need to bother about them here.
- *
- * Also note that we need a DefDef, not the corresponding MethodSymbol, because that symbol would be of no use for us.
- * Macro signatures are verified when typechecking macro defs, which means that at that moment inspecting macroDef.info
- * means asking for cyclic reference errors.
- *
- * We need macro implementation symbol as well, because the return type of the macro definition might be omitted,
- * and in that case we'd need to infer it from the return type of the macro implementation. Luckily for us, we can
- * use that symbol without a risk of running into cycles.
- *
- * @param typer Typechecker of `macroDdef`
- * @param macroDdef The macro definition tree
- * @param macroImpl The macro implementation symbol
- */
- private lazy val referenceMacroImplSig: MacroImplSig = {
- // had to move method's body to an object because of the recursive dependencies between sigma and param
- object SigGenerator {
- val cache = scala.collection.mutable.Map[Symbol, Symbol]()
- val ctxTpe = if (isImplBlackbox) BlackboxContextClass.tpe else WhiteboxContextClass.tpe
- val ctxPrefix =
- if (isImplMethod) singleType(NoPrefix, makeParam(nme.macroContext, macroDdef.pos, ctxTpe, SYNTHETIC))
- else singleType(ThisType(macroImpl.owner), macroImpl.owner.tpe.member(nme.c))
- val paramss =
- if (isImplMethod) List(ctxPrefix.termSymbol) :: mmap(macroDdef.vparamss)(param)
- else mmap(macroDdef.vparamss)(param)
- val macroDefRet =
- if (!macroDdef.tpt.isEmpty) typer.typedType(macroDdef.tpt).tpe
- else computeMacroDefTypeFromMacroImplRef(macroDdef, macroImplRef) orElse AnyTpe
- val implReturnType = sigma(increaseMetalevel(ctxPrefix, macroDefRet))
-
- object SigmaTypeMap extends TypeMap {
- def mapPrefix(pre: Type) = pre match {
- case ThisType(sym) if sym == macroDef.owner =>
- singleType(singleType(ctxPrefix, MacroContextPrefix), ExprValue)
- case SingleType(NoPrefix, sym) =>
- mfind(macroDdef.vparamss)(_.symbol == sym).fold(pre)(p => singleType(singleType(NoPrefix, param(p)), ExprValue))
- case _ =>
- mapOver(pre)
- }
- def apply(tp: Type): Type = tp match {
- case TypeRef(pre, sym, args) =>
- val pre1 = mapPrefix(pre)
- val args1 = mapOverArgs(args, sym.typeParams)
- if ((pre eq pre1) && (args eq args1)) tp
- else typeRef(pre1, sym, args1)
- case _ =>
- mapOver(tp)
+ /** A reference macro implementation signature extracted from a given macro definition.
+ *
+ * For the following macro def:
+ * def foo[T](xs: List[T]): T = macro fooBar
+ *
+ * This function will return:
+ * (c: scala.reflect.macros.blackbox.Context)(xs: c.Expr[List[T]])c.Expr[T] or
+ * (c: scala.reflect.macros.whitebox.Context)(xs: c.Expr[List[T]])c.Expr[T]
+ *
+ * Note that type tag evidence parameters are not included into the result.
+ * Type tag context bounds for macro impl tparams are optional.
+ * Therefore compatibility checks ignore such parameters, and we don't need to bother about them here.
+ *
+ * Also note that we need a DefDef, not the corresponding MethodSymbol, because that symbol would be of no use for us.
+ * Macro signatures are verified when typechecking macro defs, which means that at that moment inspecting macroDef.info
+ * means asking for cyclic reference errors.
+ *
+ * We need macro implementation symbol as well, because the return type of the macro definition might be omitted,
+ * and in that case we'd need to infer it from the return type of the macro implementation. Luckily for us, we can
+ * use that symbol without a risk of running into cycles.
+ *
+ * @param typer Typechecker of `macroDdef`
+ * @param macroDdef The macro definition tree
+ * @param macroImpl The macro implementation symbol
+ */
+ private lazy val referenceMacroImplSig: MacroImplSig = {
+ // had to move method's body to an object because of the recursive dependencies between sigma and param
+ object SigGenerator {
+ val cache = scala.collection.mutable.Map[Symbol, Symbol]()
+ val ctxTpe = if (isImplBlackbox) BlackboxContextClass.tpe else WhiteboxContextClass.tpe
+ val ctxPrefix =
+ if (isImplMethod) singleType(NoPrefix, makeParam(nme.macroContext, macroDdef.pos, ctxTpe, SYNTHETIC))
+ else singleType(ThisType(macroImpl.owner), macroImpl.owner.tpe.member(nme.c))
+ val paramss =
+ if (isImplMethod) List(ctxPrefix.termSymbol) :: mmap(macroDdef.vparamss)(param)
+ else mmap(macroDdef.vparamss)(param)
+ val macroDefRet =
+ if (!macroDdef.tpt.isEmpty) typer.typedType(macroDdef.tpt).tpe
+ else computeMacroDefTypeFromMacroImplRef(macroDdef, macroImplRef) orElse AnyTpe
+ val implReturnType = sigma(increaseMetalevel(ctxPrefix, macroDefRet))
+
+ object SigmaTypeMap extends TypeMap {
+ def mapPrefix(pre: Type) = pre match {
+ case ThisType(sym) if sym == macroDef.owner =>
+ singleType(singleType(ctxPrefix, MacroContextPrefix), ExprValue)
+ case SingleType(NoPrefix, sym) =>
+ mfind(macroDdef.vparamss)(_.symbol == sym).fold(pre)(p => singleType(singleType(NoPrefix, param(p)), ExprValue))
+ case _ =>
+ mapOver(pre)
+ }
+ def apply(tp: Type): Type = tp match {
+ case TypeRef(pre, sym, args) =>
+ val pre1 = mapPrefix(pre)
+ val args1 = mapOverArgs(args, sym.typeParams)
+ if ((pre eq pre1) && (args eq args1)) tp
+ else typeRef(pre1, sym, args1)
+ case _ =>
+ mapOver(tp)
+ }
}
+ def sigma(tpe: Type): Type = SigmaTypeMap(tpe)
+
+ def makeParam(name: Name, pos: Position, tpe: Type, flags: Long) =
+ macroDef.newValueParameter(name.toTermName, pos, flags) setInfo tpe
+ def param(tree: Tree): Symbol = (
+ cache.getOrElseUpdate(tree.symbol, {
+ val sym = tree.symbol
+ assert(sym.isTerm, s"sym = $sym, tree = $tree")
+ makeParam(sym.name, sym.pos, sigma(increaseMetalevel(ctxPrefix, sym.tpe)), sym.flags)
+ })
+ )
}
- def sigma(tpe: Type): Type = SigmaTypeMap(tpe)
-
- def makeParam(name: Name, pos: Position, tpe: Type, flags: Long) =
- macroDef.newValueParameter(name.toTermName, pos, flags) setInfo tpe
- def param(tree: Tree): Symbol = (
- cache.getOrElseUpdate(tree.symbol, {
- val sym = tree.symbol
- assert(sym.isTerm, s"sym = $sym, tree = $tree")
- makeParam(sym.name, sym.pos, sigma(increaseMetalevel(ctxPrefix, sym.tpe)), sym.flags)
- })
- )
- }
- import SigGenerator._
- macroLogVerbose(s"generating macroImplSigs for: $macroDdef")
- val result = MacroImplSig(macroDdef.tparams map (_.symbol), paramss, implReturnType)
- macroLogVerbose(s"result is: $result")
- result
+ import SigGenerator._
+ macroLogVerbose(s"generating macroImplSigs for: $macroDdef")
+ val result = MacroImplSig(macroDdef.tparams map (_.symbol), paramss, implReturnType)
+ macroLogVerbose(s"result is: $result")
+ result
+ }
}
}