Skip to content

Commit 3514ee2

Browse files
committed
Add Expr.valueOrAbort and reflect.report.errorAndAbort
Provides a homogeneous and unambiguous concept for stopping the expansion of a macro. The advantages of this naming are * Consistent name suffixes `xyzAbort` * `report.e` will show auto-completion for all variants of `error` and `errorAndAbort` * `Abort` cannot be confused with other kinds of error handling in this API
1 parent 85a03ee commit 3514ee2

File tree

59 files changed

+171
-136
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+171
-136
lines changed

compiler/src/dotty/tools/dotc/transform/Splicer.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,11 @@ object Splicer {
6363
catch {
6464
case ex: CompilationUnit.SuspendException =>
6565
throw ex
66+
case ex: scala.quoted.runtime.AbortExpansion if ctx.reporter.hasErrors =>
67+
// errors have been emitted
68+
EmptyTree
6669
case ex: scala.quoted.runtime.StopMacroExpansion if ctx.reporter.hasErrors =>
67-
// errors have been emitted
70+
// errors have been emitted
6871
EmptyTree
6972
case ex: StopInterpretation =>
7073
report.error(ex.msg, ex.pos)
@@ -423,6 +426,8 @@ object Splicer {
423426
throw new StopInterpretation(sw.toString, pos)
424427
case ex: InvocationTargetException =>
425428
ex.getTargetException match {
429+
case ex: scala.quoted.runtime.AbortExpansion =>
430+
throw ex
426431
case ex: scala.quoted.runtime.StopMacroExpansion =>
427432
throw ex
428433
case MissingClassDefinedInCurrentRun(sym) if ctx.compilationUnit.isSuspendable =>

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
4545
def matches(that: scala.quoted.Expr[Any]): Boolean =
4646
treeMatch(reflect.asTerm(self), reflect.asTerm(that)).nonEmpty
4747

48+
def value(using fromExpr: FromExpr[T]): Option[T] =
49+
fromExpr.unapply(self)(using QuotesImpl.this)
50+
51+
def valueOrAbort(using fromExpr: FromExpr[T]): T =
52+
def reportError =
53+
val tree = reflect.asTerm(self)
54+
val code = reflect.Printer.TreeCode.show(tree)
55+
val msg = s"Expected a known value. \n\nThe value of: $code\ncould not be extracted using $fromExpr"
56+
reflect.report.throwError(msg, self)
57+
fromExpr.unapply(self)(using QuotesImpl.this).getOrElse(reportError)
58+
4859
end extension
4960

5061
extension (self: scala.quoted.Expr[Any])
@@ -2750,17 +2761,17 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
27502761
def error(msg: String, pos: Position): Unit =
27512762
dotc.report.error(msg, pos)
27522763

2753-
def throwError(msg: String): Nothing =
2764+
def errorAndAbort(msg: String): Nothing =
27542765
error(msg)
2755-
throw new scala.quoted.runtime.StopMacroExpansion
2766+
throw new scala.quoted.runtime.AbortExpansion
27562767

2757-
def throwError(msg: String, expr: Expr[Any]): Nothing =
2768+
def errorAndAbort(msg: String, expr: Expr[Any]): Nothing =
27582769
error(msg, expr)
2759-
throw new scala.quoted.runtime.StopMacroExpansion
2770+
throw new scala.quoted.runtime.AbortExpansion
27602771

2761-
def throwError(msg: String, pos: Position): Nothing =
2772+
def errorAndAbort(msg: String, pos: Position): Nothing =
27622773
error(msg, pos)
2763-
throw new scala.quoted.runtime.StopMacroExpansion
2774+
throw new scala.quoted.runtime.AbortExpansion
27642775

27652776
def warning(msg: String): Unit =
27662777
dotc.report.warning(msg, Position.ofMacroExpansion)

docs/docs/reference/metaprogramming/macros.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -454,8 +454,8 @@ either a constant or is a parameter that will be a constant when instantiated. T
454454
aspect is also important for macro expansion.
455455

456456
To get values out of expressions containing constants `Expr` provides the method
457-
`value` (or `valueOrError`). This will convert the `Expr[T]` into a `Some[T]` (or `T`) when the
458-
expression contains value. Otherwise it will retrun `None` (or emit an error).
457+
`value` (or `valueOrAbort`). This will convert the `Expr[T]` into a `Some[T]` (or `T`) when the
458+
expression contains value. Otherwise it will return `None` (or emit an error).
459459
To avoid having incidental val bindings generated by the inlining of the `def`
460460
it is recommended to use an inline parameter. To illustrate this, consider an
461461
implementation of the `power` function that makes use of a statically known exponent:
@@ -628,7 +628,7 @@ transparent inline def defaultOf(inline str: String) =
628628
${ defaultOfImpl('str) }
629629

630630
def defaultOfImpl(strExpr: Expr[String])(using Quotes): Expr[Any] =
631-
strExpr.valueOrError match
631+
strExpr.valueOrAbort match
632632
case "int" => '{1}
633633
case "string" => '{"a"}
634634

library/src/scala/quoted/Expr.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ object Expr {
4646
* // value: T
4747
* ```
4848
*
49-
* To directly get the value of an expression `expr: Expr[T]` consider using `expr.value`/`expr.valueOrError` instead.
49+
* To directly get the value of an expression `expr: Expr[T]` consider using `expr.value`/`expr.valueOrAbort` instead.
5050
* @syntax markdown
5151
*/
5252
def unapply[T](x: Expr[T])(using FromExpr[T])(using Quotes): Option[T] =

library/src/scala/quoted/Exprs.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ object Exprs:
1414
* ...
1515
* }
1616
* ```
17-
* To directly get the value of all expressions in a sequence `exprs: Seq[Expr[T]]` consider using `exprs.map(_.value)`/`exprs.map(_.valueOrError)` instead.
17+
* To directly get the value of all expressions in a sequence `exprs: Seq[Expr[T]]` consider using `exprs.map(_.value)`/`exprs.map(_.valueOrAbort)` instead.
1818
*/
1919
def unapply[T](exprs: Seq[Expr[T]])(using FromExpr[T])(using Quotes): Option[Seq[T]] =
2020
val builder = Seq.newBuilder[T]

library/src/scala/quoted/Quotes.scala

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,22 +44,22 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
4444
* Returns `None` if the expression does not represent a value or possibly contains side effects.
4545
* Otherwise returns the `Some` of the value.
4646
*/
47-
def value(using FromExpr[T]): Option[T] =
48-
given Quotes = Quotes.this
49-
summon[FromExpr[T]].unapply(self)
47+
def value(using FromExpr[T]): Option[T]
5048

5149
/** Return the value of this expression.
5250
*
5351
* Emits an error and throws if the expression does not represent a value or possibly contains side effects.
5452
* Otherwise returns the value.
5553
*/
56-
def valueOrError(using FromExpr[T]): T =
57-
val fromExpr = summon[FromExpr[T]]
58-
def reportError =
59-
val msg = s"Expected a known value. \n\nThe value of: ${self.show}\ncould not be extracted using $fromExpr"
60-
reflect.report.throwError(msg, self)
61-
given Quotes = Quotes.this
62-
fromExpr.unapply(self).getOrElse(reportError)
54+
@deprecated("Use valueOrThrow", "3.0.0-RC3")
55+
def valueOrError(using FromExpr[T]): T = valueOrAbort
56+
57+
/** Return the value of this expression.
58+
*
59+
* Emits an error and aborts if the expression does not represent a value or possibly contains side effects.
60+
* Otherwise returns the value.
61+
*/
62+
def valueOrAbort(using FromExpr[T]): T
6363

6464
end extension
6565

@@ -4112,14 +4112,23 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
41124112
/** Report an error message at the given position */
41134113
def error(msg: String, pos: Position): Unit
41144114

4115-
/** Report an error at the position of the macro expansion and throw a StopMacroExpansion */
4116-
def throwError(msg: String): Nothing
4115+
/** Report an error at the position of the macro expansion and throw a AbortExpansion */
4116+
def errorAndAbort(msg: String): Nothing
41174117

4118-
/** Report an error at the position of `expr` */
4119-
def throwError(msg: String, expr: Expr[Any]): Nothing
4118+
/** Report an error at the position of `expr` and throw a AbortExpansion */
4119+
def errorAndAbort(msg: String, expr: Expr[Any]): Nothing
4120+
4121+
/** Report an error message at the given position and throw a AbortExpansion */
4122+
def errorAndAbort(msg: String, pos: Position): Nothing
4123+
4124+
/** Report an error at the position of the macro expansion and throw a AbortExpansion */
4125+
def throwError(msg: String): Nothing = errorAndAbort(msg)
4126+
4127+
/** Report an error at the position of `expr` and throw a AbortExpansion */
4128+
def throwError(msg: String, expr: Expr[Any]): Nothing = errorAndAbort(msg, expr)
41204129

4121-
/** Report an error message at the given position and throw a StopMacroExpansion */
4122-
def throwError(msg: String, pos: Position): Nothing
4130+
/** Report an error message at the given position and throw a AbortExpansion */
4131+
def throwError(msg: String, pos: Position): Nothing = errorAndAbort(msg, pos)
41234132

41244133
/** Report a warning at the position of the macro expansion */
41254134
def warning(msg: String): Unit
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package scala.quoted.runtime
2+
3+
/** Throwable used to stop the quote expansion after an error was reported. */
4+
class AbortExpansion extends Throwable:
5+
6+
// Do not fill the stacktrace for performance.
7+
// We know that the stacktrace will be ignored
8+
// and only the reported error message will be used.
9+
override def fillInStackTrace(): Throwable = this
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
package scala.quoted.runtime
22

33
/** Throwable used to stop the expansion of a macro after an error was reported */
4+
@deprecated("Use AbortExpansion", "3.0.0-RC3")
45
class StopMacroExpansion extends Throwable

tests/bench/power-macro/PowerMacro.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ object PowerMacro {
55
inline def power(inline n: Long, x: Double) = ${ powerCode('n, 'x) }
66

77
def powerCode(n: Expr[Long], x: Expr[Double])(using Quotes): Expr[Double] =
8-
powerCode(n.valueOrError, x)
8+
powerCode(n.valueOrAbort, x)
99

1010
def powerCode(n: Long, x: Expr[Double])(using Quotes): Expr[Double] =
1111
if (n == 0) '{1.0}

tests/neg-macros/i9014/Macros_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import scala.quoted.*
22
trait Bar
33
inline given Bar = ${ impl }
4-
def impl(using Quotes): Expr[Bar] = quotes.reflect.report.throwError("Failed to expand!")
4+
def impl(using Quotes): Expr[Bar] = quotes.reflect.report.errorAndAbort("Failed to expand!")

0 commit comments

Comments
 (0)