scala.reflect.macros.compiler.DefaultMacroCompiler.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala-compiler Show documentation
Show all versions of scala-compiler Show documentation
Compiler for the SubScript extension of the Scala Programming Language
The newest version!
package scala.reflect.macros
package compiler
import scala.tools.nsc.Global
abstract class DefaultMacroCompiler extends Resolvers
with Validators
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
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) [].[[]] // vanilla macro impl ref
* 2) [].[[]] // 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(Literal(Constant(null))))), 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 {
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)
EmptyTree
}
}
}