-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add Expr.valueOrAbort
and reflect.report.errorAndAbort
#12056
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,6 +54,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => | |
* Emits an error and throws if the expression does not represent a value or possibly contains side effects. | ||
* Otherwise returns the value. | ||
*/ | ||
// TODO: deprecate in 3.1.0 and remove @experimental from valueOrAbort | ||
// @deprecated("Use valueOrThrow", "3.1.0") | ||
def valueOrError(using FromExpr[T]): T = | ||
val fromExpr = summon[FromExpr[T]] | ||
def reportError = | ||
|
@@ -62,6 +64,14 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => | |
given Quotes = Quotes.this | ||
fromExpr.unapply(self).getOrElse(reportError) | ||
|
||
/** Return the value of this expression. | ||
* | ||
* Emits an error and aborts if the expression does not represent a value or possibly contains side effects. | ||
* Otherwise returns the value. | ||
*/ | ||
@experimental | ||
def valueOrAbort(using FromExpr[T]): T | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd suggest the method to take a message for indicating the reason why it's aborted. It helps end-users in debugging and saves the efforts of macro-authors in writing error-handling code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The question then is if we should keep the version with the default error message. The idea if this signature was also to make the authors not need to write the error message. Currently the can do x.value match
case Some(n) => ...
case None => reflect.report.error("......", x) or just if x.valueOrAbort == 0 then ...
else the proposed one would need to add the message if x.valueOrAbort(".........") == 0 then
else ... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed, the version with the error message does not look better in code and can be a little annoying for prototyping. For macro authors, it's not clear that it's better. Macros can be subtle to use, more debuggability to end-users will make them more friendly for usage. However, if the gain for end-users is small, maybe it's not worth the complication.
We probably only want to keep one of the two. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could add an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Meanwhile, they can just define something like this in their projects extension [T: FromExpr](expr: Expr[T])
def valueOrAbortWith(msg: String)(using Quotes) =
x.value match
case Some(n) => n
case None => reflect.report.errorAndAbort(msg, x) Expr(1).valueOrAbortWith("expected a constant") There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, it's better to wait and do it later. |
||
end extension | ||
|
||
// Extension methods for `Expr[Any]` that take another explicit type parameter | ||
|
@@ -4169,12 +4179,30 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => | |
def error(msg: String, pos: Position): Unit | ||
|
||
/** Report an error at the position of the macro expansion and throw a StopMacroExpansion */ | ||
@experimental | ||
def errorAndAbort(msg: String): Nothing | ||
|
||
/** Report an error at the position of `expr` and throw a StopMacroExpansion */ | ||
@experimental | ||
def errorAndAbort(msg: String, expr: Expr[Any]): Nothing | ||
|
||
/** Report an error message at the given position and throw a StopMacroExpansion */ | ||
@experimental | ||
def errorAndAbort(msg: String, pos: Position): Nothing | ||
|
||
/** Report an error at the position of the macro expansion and throw a StopMacroExpansion */ | ||
// TODO: deprecate in 3.1.0 and remove @experimental from errorAndAbort | ||
// @deprecated("Use errorAndAbort", "3.1.0") | ||
def throwError(msg: String): Nothing | ||
|
||
/** Report an error at the position of `expr` */ | ||
/** Report an error at the position of `expr` and throw a StopMacroExpansion */ | ||
// TODO: deprecate in 3.1.0 and remove @experimental from errorAndAbort | ||
// @deprecated("Use errorAndAbort", "3.1.0") | ||
def throwError(msg: String, expr: Expr[Any]): Nothing | ||
|
||
/** Report an error message at the given position and throw a StopMacroExpansion */ | ||
// TODO: deprecate in 3.1.0 and remove @experimental from errorAndAbort | ||
// @deprecated("Use errorAndAbort", "3.1.0") | ||
def throwError(msg: String, pos: Position): Nothing | ||
|
||
/** Report a warning at the position of the macro expansion */ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,9 @@ | ||
package scala.quoted.runtime | ||
|
||
/** Throwable used to stop the expansion of a macro after an error was reported */ | ||
class StopMacroExpansion extends Throwable | ||
/** Throwable used to abort the expansion of a macro after an error was reported */ | ||
class StopMacroExpansion extends Throwable: | ||
|
||
// Do not fill the stacktrace for performance. | ||
// We know that the stacktrace will be ignored | ||
// and only the reported error message will be used. | ||
override def fillInStackTrace(): Throwable = this |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import scala.quoted.* | ||
trait Bar | ||
inline given Bar = ${ impl } | ||
def impl(using Quotes): Expr[Bar] = quotes.reflect.report.throwError("Failed to expand!") | ||
def impl(using Quotes): Expr[Bar] = quotes.reflect.report.errorAndAbort("Failed to expand!") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import scala.quoted._ | ||
trait Bar | ||
transparent inline given Bar = ${ impl } | ||
def impl(using Quotes): Expr[Bar] = quotes.reflect.report.throwError("Failed to expand!") | ||
def impl(using Quotes): Expr[Bar] = quotes.reflect.report.errorAndAbort("Failed to expand!") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
|
||
-- Error: tests/neg-macros/ill-abort/quoted_2.scala:1:15 --------------------------------------------------------------- | ||
1 |def test = fail() // error | ||
| ^^^^^^ | ||
|Macro expansion was aborted by the macro without any errors reported. Macros should issue errors to end-users to facilitate debugging when aborting a macro expansion. | ||
| This location contains code that was inlined from quoted_1.scala:3 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import scala.quoted.* | ||
|
||
inline def fail(): Unit = ${ impl } | ||
|
||
private def impl(using Quotes) : Expr[Unit] = | ||
// should never be done without reporting error before (see docs) | ||
throw new scala.quoted.runtime.StopMacroExpansion |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
def test = fail() // error |
Uh oh!
There was an error while loading. Please reload this page.