summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/reflect/macros/runtime/AbortMacroException.scala3
-rw-r--r--src/compiler/scala/reflect/macros/runtime/FrontEnds.scala5
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala111
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala334
-rw-r--r--src/reflect/scala/reflect/internal/TreeInfo.scala11
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala6
-rw-r--r--test/files/neg/macro-invalidusage-badbounds-a.check (renamed from test/files/neg/macro-invalidusage-badbounds.check)0
-rw-r--r--test/files/neg/macro-invalidusage-badbounds-a.flags (renamed from test/files/neg/macro-invalidusage-badbounds.flags)0
-rw-r--r--test/files/neg/macro-invalidusage-badbounds-a/Impls_1.scala5
-rw-r--r--test/files/neg/macro-invalidusage-badbounds-a/Macros_Test_2.scala (renamed from test/files/neg/macro-invalidusage-badbounds/Macros_Test_2.scala)0
-rw-r--r--test/pending/neg/macro-invalidusage-badbounds-b.check4
-rw-r--r--test/pending/neg/macro-invalidusage-badbounds-b.flags1
-rw-r--r--test/pending/neg/macro-invalidusage-badbounds-b/Impls_1.scala (renamed from test/files/neg/macro-invalidusage-badbounds/Impls_1.scala)0
-rw-r--r--test/pending/neg/macro-invalidusage-badbounds-b/Macros_Test_2.scala8
15 files changed, 229 insertions, 265 deletions
diff --git a/src/compiler/scala/reflect/macros/runtime/AbortMacroException.scala b/src/compiler/scala/reflect/macros/runtime/AbortMacroException.scala
index f45dde8a85..4e4d88c0be 100644
--- a/src/compiler/scala/reflect/macros/runtime/AbortMacroException.scala
+++ b/src/compiler/scala/reflect/macros/runtime/AbortMacroException.scala
@@ -2,5 +2,6 @@ package scala.reflect.macros
package runtime
import scala.reflect.internal.util.Position
+import scala.util.control.ControlThrowable
-class AbortMacroException(val pos: Position, val msg: String) extends Throwable(msg)
+class AbortMacroException(val pos: Position, val msg: String) extends Throwable(msg) with ControlThrowable \ No newline at end of file
diff --git a/src/compiler/scala/reflect/macros/runtime/FrontEnds.scala b/src/compiler/scala/reflect/macros/runtime/FrontEnds.scala
index c5c1c84cde..9f328eb82b 100644
--- a/src/compiler/scala/reflect/macros/runtime/FrontEnds.scala
+++ b/src/compiler/scala/reflect/macros/runtime/FrontEnds.scala
@@ -35,10 +35,7 @@ trait FrontEnds extends scala.tools.reflect.FrontEnds {
def error(pos: Position, msg: String): Unit = callsiteTyper.context.error(pos, msg)
- def abort(pos: Position, msg: String): Nothing = {
- callsiteTyper.context.error(pos, msg)
- throw new AbortMacroException(pos, msg)
- }
+ def abort(pos: Position, msg: String): Nothing = throw new AbortMacroException(pos, msg)
def interactive(): Unit = universe.reporter match {
case reporter: scala.tools.nsc.reporters.AbstractReporter => reporter.displayPrompt()
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 574129a2f1..80e9ede271 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -1220,8 +1220,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
var reportedFeature = Set[Symbol]()
- /** A flag whether macro expansions failed */
- var macroExpansionFailed = false
+ /** Has any macro expansion used a fallback during this run? */
+ var seenMacroExpansionsFallingBack = false
/** To be initialized from firstPhase. */
private var terminalPhase: Phase = NoPhase
@@ -1511,7 +1511,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
else {
allConditionalWarnings foreach (_.summarize)
- if (macroExpansionFailed)
+ if (seenMacroExpansionsFallingBack)
warning("some macros could not be expanded and code fell back to overridden methods;"+
"\nrecompiling with generated classfiles on the classpath might help.")
// todo: migrationWarnings
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index 5a110739cd..99ce22ddb9 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -10,6 +10,8 @@ import scala.collection.{ mutable, immutable }
import scala.reflect.internal.util.StringOps.{ countElementsAsString, countAsString }
import symtab.Flags.{ PRIVATE, PROTECTED, IS_ERROR }
import scala.compat.Platform.EOL
+import scala.reflect.runtime.ReflectionUtils
+import scala.reflect.macros.runtime.AbortMacroException
trait ContextErrors {
self: Analyzer =>
@@ -323,16 +325,6 @@ trait ContextErrors {
setError(tree)
}
- def MacroEtaError(tree: Tree) = {
- issueNormalTypeError(tree, "macros cannot be eta-expanded")
- setError(tree)
- }
-
- def MacroPartialApplicationError(tree: Tree) = {
- issueNormalTypeError(tree, "macros cannot be partially applied")
- setError(tree)
- }
-
//typedReturn
def ReturnOutsideOfDefError(tree: Tree) = {
issueNormalTypeError(tree, "return outside method definition")
@@ -636,6 +628,105 @@ trait ContextErrors {
def CyclicReferenceError(errPos: Position, lockedSym: Symbol) =
issueTypeError(PosAndMsgTypeError(errPos, "illegal cyclic reference involving " + lockedSym))
+
+ // macro-related errors (also see MacroErrors below)
+
+ def MacroEtaError(tree: Tree) = {
+ issueNormalTypeError(tree, "macros cannot be eta-expanded")
+ setError(tree)
+ }
+
+ // same reason as for MacroBodyTypecheckException
+ case object MacroExpansionException extends Exception with scala.util.control.ControlThrowable
+
+ private def macroExpansionError(expandee: Tree, msg: String = null, pos: Position = NoPosition) = {
+ def msgForLog = if (msg != null && (msg contains "exception during macro expansion")) msg.split(EOL).drop(1).headOption.getOrElse("?") else msg
+ macroLogLite("macro expansion has failed: %s".format(msgForLog))
+ val errorPos = if (pos != NoPosition) pos else (if (expandee.pos != NoPosition) expandee.pos else enclosingMacroPosition)
+ if (msg != null) context.error(pos, msg) // issueTypeError(PosAndMsgTypeError(..)) won't work => swallows positions
+ setError(expandee)
+ throw MacroExpansionException
+ }
+
+ def MacroPartialApplicationError(expandee: Tree) = {
+ // macroExpansionError won't work => swallows positions, hence needed to do issueTypeError
+ // kinda contradictory to the comment in `macroExpansionError`, but this is how it works
+ issueNormalTypeError(expandee, "macros cannot be partially applied")
+ setError(expandee)
+ throw MacroExpansionException
+ }
+
+ def MacroGeneratedAbort(expandee: Tree, ex: AbortMacroException) = {
+ // errors have been reported by the macro itself, so we do nothing here
+ macroLogVerbose("macro expansion has been aborted")
+ macroExpansionError(expandee, ex.msg, ex.pos)
+ }
+
+ def MacroGeneratedTypeError(expandee: Tree, err: TypeError = null) =
+ if (err == null) {
+ // errors have been reported by the macro itself, so we do nothing here
+ macroExpansionError(expandee, null)
+ } else {
+ macroLogLite("macro expansion has failed: %s at %s".format(err.msg, err.pos))
+ throw err // this error must be propagated, don't report
+ }
+
+ def MacroGeneratedException(expandee: Tree, ex: Throwable) = {
+ val realex = ReflectionUtils.unwrapThrowable(ex)
+ val message = {
+ try {
+ // [Eugene] is there a better way?
+ // [Paul] See Exceptional.scala and Origins.scala.
+ val relevancyThreshold = realex.getStackTrace().indexWhere(_.getMethodName endsWith "macroExpand1")
+ if (relevancyThreshold == -1) None
+ else {
+ var relevantElements = realex.getStackTrace().take(relevancyThreshold + 1)
+ def isMacroInvoker(este: StackTraceElement) = este.isNativeMethod || (este.getClassName != null && (este.getClassName contains "fastTrack"))
+ var threshold = relevantElements.reverse.indexWhere(isMacroInvoker) + 1
+ while (threshold != relevantElements.length && isMacroInvoker(relevantElements(relevantElements.length - threshold - 1))) threshold += 1
+ relevantElements = relevantElements dropRight threshold
+
+ realex.setStackTrace(relevantElements)
+ val message = new java.io.StringWriter()
+ realex.printStackTrace(new java.io.PrintWriter(message))
+ Some(EOL + message)
+ }
+ } catch {
+ // if the magic above goes boom, just fall back to uninformative, but better than nothing, getMessage
+ case ex: Throwable =>
+ None
+ }
+ } getOrElse {
+ val msg = realex.getMessage
+ if (msg != null) msg else realex.getClass.getName
+ }
+ macroExpansionError(expandee, "exception during macro expansion: " + message)
+ }
+
+ def MacroFreeSymbolError(expandee: Tree, sym: FreeSymbol) = {
+ def template(kind: String) = (
+ s"Macro expansion contains free $kind variable %s. Have you forgotten to use %s? "
+ + s"If you have troubles tracking free $kind variables, consider using -Xlog-free-${kind}s"
+ )
+ val forgotten = (
+ if (sym.isTerm) "splice when splicing this variable into a reifee"
+ else "c.AbsTypeTag annotation for this type parameter"
+ )
+ macroExpansionError(expandee, template(sym.name.nameKind).format(sym.name + " " + sym.origin, forgotten))
+ }
+
+ def MacroExpansionIsNotExprError(expandee: Tree, expanded: Any) =
+ macroExpansionError(expandee,
+ "macro must return a compiler-specific expr; returned value is " + (
+ if (expanded == null) "null"
+ else if (expanded.isInstanceOf[Expr[_]]) " Expr, but it doesn't belong to this compiler's universe"
+ else " of " + expanded.getClass
+ ))
+
+ def MacroImplementationNotFoundError(expandee: Tree) =
+ macroExpansionError(expandee,
+ "macro implementation not found: " + expandee.symbol.name + " " +
+ "(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)")
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 4601f63809..ed38c1750b 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -12,6 +12,8 @@ import scala.reflect.macros.util._
import java.lang.{Class => jClass}
import java.lang.reflect.{Array => jArray, Method => jMethod}
import scala.reflect.internal.util.Collections._
+import scala.util.control.ControlThrowable
+import scala.reflect.macros.runtime.AbortMacroException
/**
* Code to deal with macros, namely with:
@@ -553,19 +555,13 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
val callsiteTyper: universe.analyzer.Typer = typer.asInstanceOf[global.analyzer.Typer]
val expandee = expandeeTree
} 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 */)
}
/** Calculate the arguments to pass to a macro implementation when expanding the provided tree.
- *
- * This includes inferring the exact type and instance of the macro context to pass, and also
- * allowing for missing parameter sections in macro implementation (see `macroImplParamsss` for more info).
- *
- * @return list of runtime objects to pass to the implementation obtained by `macroRuntime`
*/
- private def macroArgs(typer: Typer, expandee: Tree): Option[List[Any]] = {
+ private def macroArgs(typer: Typer, expandee: Tree): List[Any] = {
val macroDef = expandee.symbol
val prefixTree = expandee.collect{ case Select(qual, name) => qual }.headOption.getOrElse(EmptyTree)
val context = expandee.attachments.get[MacroRuntimeAttachment].flatMap(_.macroContext).getOrElse(macroContext(typer, prefixTree, expandee))
@@ -585,23 +581,15 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
val argcDoesntMatch = macroDef.paramss.length != exprArgs.length
val nullaryArgsEmptyParams = exprArgs.isEmpty && macroDef.paramss == ListOfNil
- if (argcDoesntMatch && !nullaryArgsEmptyParams) { typer.TyperErrorGen.MacroPartialApplicationError(expandee); return None }
+ if (argcDoesntMatch && !nullaryArgsEmptyParams) { typer.TyperErrorGen.MacroPartialApplicationError(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 typer.TyperErrorGen.MacroPartialApplicationError(expandee)
} else {
val binding = loadMacroImplBinding(macroDef)
macroTraceVerbose("binding: ")(binding)
@@ -658,9 +646,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
} else as
})
}
- val rawArgs = rawArgss.flatten
- macroTraceVerbose("rawArgs: ")(rawArgs)
- Some(rawArgs)
+ macroTraceVerbose("rawArgs: ")(rawArgss.flatten)
}
/** Keeps track of macros in-flight.
@@ -669,6 +655,15 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
var openMacros = List[MacroContext]()
def enclosingMacroPosition = openMacros map (_.macroApplication.pos) find (_ ne NoPosition) getOrElse NoPosition
+ private sealed abstract class MacroExpansionResult
+ private case class Success(expanded: Tree) extends MacroExpansionResult
+ private case class Fallback(fallback: Tree) extends MacroExpansionResult { currentRun.seenMacroExpansionsFallingBack = true }
+ private case class Other(result: Tree) extends MacroExpansionResult
+ private def Delay(expanded: Tree) = Other(expanded)
+ private def Skip(expanded: Tree) = Other(expanded)
+ private def Cancel(expandee: Tree) = Other(expandee)
+ private def Failure(expandee: Tree) = Other(expandee)
+
/** Performs macro expansion:
* 1) Checks whether the expansion needs to be delayed (see `mustDelayMacroExpansion`)
* 2) Loads macro implementation using `macroMirror`
@@ -692,48 +687,25 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
* the expandee with an error marker set if there has been an error
*/
def macroExpand(typer: Typer, expandee: Tree, mode: Int = EXPRmode, pt: Type = WildcardType): Tree = {
- def fail(what: String, tree: Tree): Tree = {
- val err = typer.context.errBuffer.head
- this.fail(typer, tree, err.errPos, "failed to %s: %s".format(what, err.errMsg))
- return expandee
- }
val start = Statistics.startTimer(macroExpandNanos)
Statistics.incCounter(macroExpandCount)
try {
macroExpand1(typer, expandee) match {
- case Success(expanded0) =>
+ case Success(expanded) =>
try {
- val expanded = expanded0 // virtpatmat swallows the local for expandee from the match
- // so I added this dummy local for the ease of debugging
- var expectedTpe = expandee.tpe
-
- val isNullaryInvocation = expandee match {
- case TypeApply(Select(_, _), _) => true
- case TypeApply(Ident(_), _) => true
- case Select(_, _) => true
- case Ident(_) => true
- case _ => false
+ def typecheck(phase: String, tree: Tree, pt: Type): Tree = {
+ if (tree.isErroneous) return tree
+ macroLogVerbose(s"typechecking against $phase $pt: $expanded")
+ val numErrors = reporter.ERROR.count
+ def hasNewErrors = reporter.ERROR.count > numErrors
+ val result = typer.context.withImplicitsEnabled(typer.typed(tree, EXPRmode, pt))
+ macroTraceVerbose(s"""${if (hasNewErrors) "failed to typecheck" else "successfully typechecked"} against $phase $pt:\n$result\n""")(result)
}
- if (isNullaryInvocation) expectedTpe match {
- case NullaryMethodType(restpe) =>
- macroTraceVerbose("nullary invocation of a nullary method. unwrapping expectedTpe from " + expectedTpe + " to: ")(restpe)
- expectedTpe = restpe
- case MethodType(Nil, restpe) =>
- macroTraceVerbose("nullary invocation of a method with an empty parameter list. unwrapping expectedTpe from " + expectedTpe + " to: ")(restpe)
- expectedTpe = restpe
- case _ => ;
- }
-
- macroLogVerbose("typechecking1 against %s: %s".format(expectedTpe, expanded))
- var typechecked = typer.context.withImplicitsEnabled(typer.typed(expanded, EXPRmode, expectedTpe))
- if (typer.context.hasErrors) fail("typecheck against macro def return type", expanded)
- macroLogVerbose("typechecked1:%n%s%n%s".format(typechecked, showRaw(typechecked)))
-
- macroLogVerbose("typechecking2 against %s: %s".format(pt, expanded))
- typechecked = typer.context.withImplicitsEnabled(typer.typed(typechecked, EXPRmode, pt))
- if (typer.context.hasErrors) fail("typecheck against expected type", expanded)
- macroLogVerbose("typechecked2:%n%s%n%s".format(typechecked, showRaw(typechecked)))
+ var expectedTpe = expandee.tpe
+ if (isNullaryInvocation(expandee)) expectedTpe = expectedTpe.finalResultType
+ var typechecked = typecheck("macro def return type", expanded, expectedTpe)
+ typechecked = typecheck("expected type", typechecked, pt)
typechecked addAttachment MacroExpansionAttachment(expandee)
} finally {
openMacros = openMacros.tail
@@ -748,230 +720,108 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces {
}
}
- private sealed abstract class MacroExpansionResult extends Product with Serializable
- private case class Success(expanded: Tree) extends MacroExpansionResult
- private case class Fallback(fallback: Tree) extends MacroExpansionResult
- private case class Other(result: Tree) extends MacroExpansionResult
- private def Delay(expanded: Tree) = Other(expanded)
- private def Skip(expanded: Tree) = Other(expanded)
- private def Cancel(expandee: Tree) = Other(expandee)
- private def Failure(expandee: Tree) = Other(expandee)
- private def fail(typer: Typer, expandee: Tree, pos: Position = NoPosition, msg: String = null) = {
- def msgForLog = if (msg != null && (msg contains "exception during macro expansion")) msg.split(EOL).drop(1).headOption.getOrElse("?") else msg
- macroLogLite("macro expansion has failed: %s".format(msgForLog))
- val errorPos = if (pos != NoPosition) pos else (if (expandee.pos != NoPosition) expandee.pos else enclosingMacroPosition)
- if (msg != null) typer.context.error(errorPos, msg)
- typer.infer.setError(expandee)
- Failure(expandee)
- }
-
/** Does the same as `macroExpand`, but without typechecking the expansion
* Meant for internal use within the macro infrastructure, don't use it elsewhere.
*/
private def macroExpand1(typer: Typer, expandee: Tree): MacroExpansionResult =
- // InfoLevel.Verbose examines and prints out infos of symbols
- // by the means of this'es these symbols can climb up the lexical scope
- // when these symbols will be examined by a node printer
- // they will enumerate and analyze their children (ask for infos and tpes)
- // if one of those children involves macro expansion, things might get nasty
- // that's why I'm temporarily turning this behavior off
+ // verbose printing might cause recursive macro expansions, so I'm shutting it down here
withInfoLevel(nodePrinters.InfoLevel.Quiet) {
- // if a macro implementation is incompatible or any of the arguments are erroneous
- // there is no sense to expand the macro itself => it will only make matters worse
if (expandee.symbol.isErroneous || (expandee exists (_.isErroneous))) {
val reason = if (expandee.symbol.isErroneous) "not found or incompatible macro implementation" else "erroneous arguments"
macroTraceVerbose("cancelled macro expansion because of %s: ".format(reason))(expandee)
return Cancel(typer.infer.setError(expandee))
}
- val runtime = macroRuntime(expandee.symbol)
- if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime)
- else macroExpandWithoutRuntime(typer, expandee)
+ try {
+ val runtime = macroRuntime(expandee.symbol)
+ if (runtime != null) macroExpandWithRuntime(typer, expandee, runtime)
+ else macroExpandWithoutRuntime(typer, expandee)
+ } catch {
+ case typer.TyperErrorGen.MacroExpansionException => Failure(expandee)
+ }
}
/** Expands a macro when a runtime (i.e. the macro implementation) can be successfully loaded
* Meant for internal use within the macro infrastructure, don't use it elsewhere.
*/
private def macroExpandWithRuntime(typer: Typer, expandee: Tree, runtime: MacroRuntime): MacroExpansionResult = {
- def issueFreeError(sym: FreeSymbol) = {
- val template = (
- "Macro expansion contains free @kind@ variable %s. Have you forgotten to use %s? "
- + "If you have troubles tracking free @kind@ variables, consider using -Xlog-free-@kind@s"
- )
- val forgotten = (
- if (sym.isTerm) "splice when splicing this variable into a reifee"
- else "c.AbsTypeTag annotation for this type parameter"
- )
- typer.context.error(expandee.pos,
- template.replaceAllLiterally("@kind@", sym.name.nameKind).format(
- sym.name + " " + sym.origin, forgotten)
- )
- }
- def macroExpandInternal = {
- val wasDelayed = isDelayed(expandee)
- val undetparams = calculateUndetparams(expandee)
- val nowDelayed = !typer.context.macrosEnabled || undetparams.nonEmpty
-
- def failExpansion(msg: String = null) = fail(typer, expandee, msg = msg)
+ import typer.TyperErrorGen._
+ try {
def performExpansion(args: List[Any]): MacroExpansionResult = {
val numErrors = reporter.ERROR.count
def hasNewErrors = reporter.ERROR.count > numErrors
-
val expanded = runtime(args)
+ if (hasNewErrors) MacroGeneratedTypeError(expandee)
- if (hasNewErrors)
- failExpansion() // errors have been reported by the macro itself
- else expanded match {
+ expanded match {
case expanded: Expr[_] =>
macroLogVerbose("original:")
macroLogLite("" + expanded.tree + "\n" + showRaw(expanded.tree))
-
- expanded.tree.freeTerms foreach issueFreeError
- expanded.tree.freeTypes foreach issueFreeError
- if (hasNewErrors) failExpansion()
-
- // inherit the position from the first position-ful expandee in macro callstack
- // this is essential for sane error messages
- // now macro expansion gets typechecked against the macro definition return type
- // however, this happens in macroExpand, not here in macroExpand1
- else Success(atPos(enclosingMacroPosition.focus)(expanded.tree))
+ val freeSyms = expanded.tree.freeTerms ++ expanded.tree.freeTypes
+ freeSyms foreach (sym => MacroFreeSymbolError(expandee, sym))
+ Success(atPos(enclosingMacroPosition.focus)(expanded.tree))
case _ =>
- failExpansion(
- "macro must return a compiler-specific expr; returned value is " + (
- if (expanded.isInstanceOf[Expr[_]]) " Expr, but it doesn't belong to this compiler's universe"
- else " of " + expanded.getClass
- )
- )
+ MacroExpansionIsNotExprError(expandee, expanded)
}
}
- if (wasDelayed) {
- if (nowDelayed) Delay(expandee)
- else Skip(macroExpandAll(typer, expandee))
- }
- else {
- macroLogLite("typechecking macro expansion %s at %s".format(expandee, expandee.pos))
- macroArgs(typer, expandee).fold(failExpansion(): MacroExpansionResult) {
- args => (args: @unchecked) match {
- // crashes virtpatmat:
- // case args @ ((context: MacroContext) :: _) =>
- // todo. extract a minimized test case
- case args @ (context0 :: _) =>
- val context = context0.asInstanceOf[MacroContext]
- if (nowDelayed) {
- macroLogLite("macro expansion is delayed: %s".format(expandee))
- delayed += expandee -> undetparams
- // need to save typer context for `macroExpandAll`
- // need to save macro context to preserve enclosures
- expandee addAttachment MacroRuntimeAttachment(delayed = true, typerContext = typer.context, macroContext = Some(context.asInstanceOf[MacroContext]))
- Delay(expandee)
- }
- else {
- // adding stuff to openMacros is easy, but removing it is a nightmare
- // it needs to be sprinkled over several different code locations
- // why? https://github.com/scala/scala/commit/bd3eacbae21f39b1ac7fe8ade4ed71fa98e1a28d#L2R1137
- // todo. will be improved
- openMacros ::= context
- var isSuccess = false
- try performExpansion(args) match {
- case x: Success => isSuccess = true ; x
- case x => x
- }
- finally {
- expandee.removeAttachment[MacroRuntimeAttachment]
- if (!isSuccess) openMacros = openMacros.tail
- }
- }
- }
+ val wasDelayed = isDelayed(expandee)
+ val undetparams = calculateUndetparams(expandee)
+ val nowDelayed = !typer.context.macrosEnabled || undetparams.nonEmpty
+
+ if (wasDelayed || nowDelayed) {
+ if (!wasDelayed && nowDelayed) {
+ macroLogLite("macro expansion is delayed: %s".format(expandee))
+ delayed += expandee -> undetparams
+ val macroContext = macroArgs(typer, expandee).head.asInstanceOf[MacroContext]
+ expandee addAttachment MacroRuntimeAttachment(delayed = true, typerContext = typer.context, macroContext = Some(macroContext))
+ Delay(expandee)
+ } else if (wasDelayed && nowDelayed) {
+ Delay(expandee)
+ } else /* if (wasDelayed && !nowDelayed) */ {
+ Skip(macroExpandAll(typer, expandee))
+ }
+ } else {
+ macroLogLite("performing macro expansion %s at %s".format(expandee, expandee.pos))
+ val args = macroArgs(typer, expandee)
+ openMacros ::= args.head.asInstanceOf[MacroContext]
+ var isSuccess = false
+ try performExpansion(args) match {
+ case x: Success => isSuccess = true ; x
+ case x => x
+ } finally {
+ expandee.removeAttachment[MacroRuntimeAttachment]
+ if (!isSuccess) openMacros = openMacros.tail
}
}
+ } catch {
+ case ex: Throwable =>
+ val realex = ReflectionUtils.unwrapThrowable(ex)
+ realex match {
+ case ex: AbortMacroException => MacroGeneratedAbort(expandee, ex)
+ case ex: ControlThrowable => throw ex
+ case ex: TypeError => MacroGeneratedTypeError(expandee, ex)
+ case _ => MacroGeneratedException(expandee, realex)
+ }
}
-
- try macroExpandInternal
- catch { case ex: Throwable => handleMacroExpansionException(typer, expandee, ex) }
}
+ /** Expands a macro when a runtime (i.e. the macro implementation) cannot be loaded
+ * Meant for internal use within the macro infrastructure, don't use it elsewhere.
+ */
private def macroExpandWithoutRuntime(typer: Typer, expandee: Tree): MacroExpansionResult = {
- val macroDef = expandee.symbol
- def notFound() = {
- typer.context.error(expandee.pos, "macro implementation not found: " + macroDef.name + " " +
- "(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)")
- None
- }
- def fallBackToOverridden(tree: Tree): Option[Tree] = {
+ val fallbackSym = expandee.symbol.allOverriddenSymbols.headOption.getOrElse(NoSymbol)
+ if (fallbackSym == NoSymbol) typer.TyperErrorGen.MacroImplementationNotFoundError(expandee)
+ macroTraceLite("falling back to: ")(fallbackSym)
+
+ def mkFallbackTree(tree: Tree): Tree = {
tree match {
- case Select(qual, name) if (macroDef.isTermMacro) =>
- macroDef.allOverriddenSymbols match {
- case first :: _ =>
- Some(Select(qual, name) setPos tree.pos setSymbol first)
- case _ =>
- macroTraceVerbose("macro is not overridden: ")(tree)
- notFound()
- }
- case Apply(fn, args) =>
- fallBackToOverridden(fn) match {
- case Some(fn1) => Some(Apply(fn1, args) setPos tree.pos)
- case _ => None
- }
- case TypeApply(fn, args) =>
- fallBackToOverridden(fn) match {
- case Some(fn1) => Some(TypeApply(fn1, args) setPos tree.pos)
- case _ => None
- }
- case _ =>
- macroTraceVerbose("unexpected tree in fallback: ")(tree)
- notFound()
+ case Select(qual, name) => Select(qual, name) setPos tree.pos setSymbol fallbackSym
+ case Apply(fn, args) => Apply(mkFallbackTree(fn), args) setPos tree.pos
+ case TypeApply(fn, args) => TypeApply(mkFallbackTree(fn), args) setPos tree.pos
}
}
- fallBackToOverridden(expandee) match {
- case Some(tree1) =>
- macroTraceLite("falling back to: ")(tree1)
- currentRun.macroExpansionFailed = true
- Fallback(tree1)
- case None =>
- fail(typer, expandee)
- }
- }
-
- private def handleMacroExpansionException(typer: Typer, expandee: Tree, ex: Throwable): MacroExpansionResult = {
- val realex = ReflectionUtils.unwrapThrowable(ex)
- realex match {
- case realex: reflect.macros.runtime.AbortMacroException =>
- macroLogVerbose("macro expansion has failed: %s".format(realex.msg))
- fail(typer, expandee) // error has been reported by abort
- case err: TypeError =>
- macroLogLite("macro expansion has failed: %s at %s".format(err.msg, err.pos))
- throw err // error should be propagated, don't report
- case _ =>
- val message = {
- try {
- // [Eugene] is there a better way?
- // [Paul] See Exceptional.scala and Origins.scala.
- val relevancyThreshold = realex.getStackTrace().indexWhere(_.getMethodName endsWith "macroExpand1")
- if (relevancyThreshold == -1) None
- else {
- var relevantElements = realex.getStackTrace().take(relevancyThreshold + 1)
- def isMacroInvoker(este: StackTraceElement) = este.isNativeMethod || (este.getClassName != null && (este.getClassName contains "fastTrack"))
- var threshold = relevantElements.reverse.indexWhere(isMacroInvoker) + 1
- while (threshold != relevantElements.length && isMacroInvoker(relevantElements(relevantElements.length - threshold - 1))) threshold += 1
- relevantElements = relevantElements dropRight threshold
-
- realex.setStackTrace(relevantElements)
- val message = new java.io.StringWriter()
- realex.printStackTrace(new java.io.PrintWriter(message))
- Some(EOL + message)
- }
- } catch {
- // if the magic above goes boom, just fall back to uninformative, but better than nothing, getMessage
- case ex: Throwable =>
- None
- }
- } getOrElse {
- val msg = realex.getMessage
- if (msg != null) msg else realex.getClass.getName
- }
- fail(typer, expandee, msg = "exception during macro expansion: " + message)
- }
+ Fallback(mkFallbackTree(expandee))
}
/** Without any restrictions on macro expansion, macro applications will expand at will,
diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala
index 3a6acdcc2b..3a930a195b 100644
--- a/src/reflect/scala/reflect/internal/TreeInfo.scala
+++ b/src/reflect/scala/reflect/internal/TreeInfo.scala
@@ -603,9 +603,10 @@ abstract class TreeInfo {
}
}
- def stripOffPrefixTypeRefinement(tpe: Type): Type =
- tpe.dealias match {
- case RefinedType(List(tpe), Scope(sym)) if tpe == MacroContextClass.tpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe
- case _ => tpe
- }
+ def isNullaryInvocation(tree: Tree): Boolean =
+ tree.symbol != null && tree.symbol.isMethod && (tree match {
+ case TypeApply(fun, _) => isNullaryInvocation(fun)
+ case tree: RefTree => true
+ case _ => false
+ })
}
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index 9514898ce5..52dd4a55cf 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -6939,6 +6939,12 @@ trait Types extends api.Types { self: SymbolTable =>
else (ps :+ SerializableClass.tpe).toList
)
+ def stripOffPrefixTypeRefinement(tpe: Type): Type =
+ tpe.dealias match {
+ case RefinedType(List(tpe), Scope(sym)) if tpe == MacroContextClass.tpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe
+ case _ => tpe
+ }
+
def objToAny(tp: Type): Type =
if (!phase.erasedTypes && tp.typeSymbol == ObjectClass) AnyClass.tpe
else tp
diff --git a/test/files/neg/macro-invalidusage-badbounds.check b/test/files/neg/macro-invalidusage-badbounds-a.check
index fd0b64533e..fd0b64533e 100644
--- a/test/files/neg/macro-invalidusage-badbounds.check
+++ b/test/files/neg/macro-invalidusage-badbounds-a.check
diff --git a/test/files/neg/macro-invalidusage-badbounds.flags b/test/files/neg/macro-invalidusage-badbounds-a.flags
index cd66464f2f..cd66464f2f 100644
--- a/test/files/neg/macro-invalidusage-badbounds.flags
+++ b/test/files/neg/macro-invalidusage-badbounds-a.flags
diff --git a/test/files/neg/macro-invalidusage-badbounds-a/Impls_1.scala b/test/files/neg/macro-invalidusage-badbounds-a/Impls_1.scala
new file mode 100644
index 0000000000..6ee71a3628
--- /dev/null
+++ b/test/files/neg/macro-invalidusage-badbounds-a/Impls_1.scala
@@ -0,0 +1,5 @@
+import scala.reflect.macros.{Context => Ctx}
+
+object Impls {
+ def foo[U <: String](c: Ctx) = c.literalUnit
+}
diff --git a/test/files/neg/macro-invalidusage-badbounds/Macros_Test_2.scala b/test/files/neg/macro-invalidusage-badbounds-a/Macros_Test_2.scala
index 3139599108..3139599108 100644
--- a/test/files/neg/macro-invalidusage-badbounds/Macros_Test_2.scala
+++ b/test/files/neg/macro-invalidusage-badbounds-a/Macros_Test_2.scala
diff --git a/test/pending/neg/macro-invalidusage-badbounds-b.check b/test/pending/neg/macro-invalidusage-badbounds-b.check
new file mode 100644
index 0000000000..fd0b64533e
--- /dev/null
+++ b/test/pending/neg/macro-invalidusage-badbounds-b.check
@@ -0,0 +1,4 @@
+Macros_Test_2.scala:7: error: type arguments [Int] do not conform to macro method foo's type parameter bounds [U <: String]
+ foo[Int]
+ ^
+one error found
diff --git a/test/pending/neg/macro-invalidusage-badbounds-b.flags b/test/pending/neg/macro-invalidusage-badbounds-b.flags
new file mode 100644
index 0000000000..cd66464f2f
--- /dev/null
+++ b/test/pending/neg/macro-invalidusage-badbounds-b.flags
@@ -0,0 +1 @@
+-language:experimental.macros \ No newline at end of file
diff --git a/test/files/neg/macro-invalidusage-badbounds/Impls_1.scala b/test/pending/neg/macro-invalidusage-badbounds-b/Impls_1.scala
index 89020de7dd..89020de7dd 100644
--- a/test/files/neg/macro-invalidusage-badbounds/Impls_1.scala
+++ b/test/pending/neg/macro-invalidusage-badbounds-b/Impls_1.scala
diff --git a/test/pending/neg/macro-invalidusage-badbounds-b/Macros_Test_2.scala b/test/pending/neg/macro-invalidusage-badbounds-b/Macros_Test_2.scala
new file mode 100644
index 0000000000..3139599108
--- /dev/null
+++ b/test/pending/neg/macro-invalidusage-badbounds-b/Macros_Test_2.scala
@@ -0,0 +1,8 @@
+object Macros {
+ def foo[U <: String] = macro Impls.foo[U]
+}
+
+object Test extends App {
+ import Macros._
+ foo[Int]
+} \ No newline at end of file