diff --git a/docs/docs/reference/metaprogramming/macros.md b/docs/docs/reference/metaprogramming/macros.md index 8e3e8a715ef6..c19fdf3bd5d3 100644 --- a/docs/docs/reference/metaprogramming/macros.md +++ b/docs/docs/reference/metaprogramming/macros.md @@ -634,15 +634,15 @@ It is possible to deconstruct or extract values out of `Expr` using pattern matc * `scala.quoted.Expr`/`scala.quoted.Exprs`: matches an expression of a value (or list of values) and returns the value (or list of values). * `scala.quoted.Const`/`scala.quoted.Consts`: Same as `Expr`/`Exprs` but only works on primitive values. -* `scala.quoted.Varargs`: matches an explicit sequence of expressions and returns them. These sequences are useful to get individual `Expr[T]` out of a varargs expression of type `Expr[Seq[T]]`. +* `scala.quoted.Varargs`: matches an explicit sequence of expresions and returns them. These sequences are useful to get individual `Expr[T]` out of a varargs expression of type `Expr[Seq[T]]`. These could be used in the following way to optimize any call to `sum` that has statically known values. ```scala inline def sum(inline args: Int*): Int = ${ sumExpr('args) } private def sumExpr(argsExpr: Expr[Seq[Int]])(using Quotes): Expr[Int] = argsExpr match { - case Varargs(args @ Exprs(argValues)) => - // args is of type Seq[Expr[Int]] + case Varargs(argExprs @ Exprs(argValues)) => + // argExprs is of type Seq[Expr[Int]] // argValues is of type Seq[Int] Expr(argValues.sum) // precompute result of sum case Varargs(argExprs) => // argExprs is of type Seq[Expr[Int]] diff --git a/library/src-bootstrapped/scala/quoted/Expr.scala b/library/src-bootstrapped/scala/quoted/Expr.scala index 7f4212910986..06a0af294e7b 100644 --- a/library/src-bootstrapped/scala/quoted/Expr.scala +++ b/library/src-bootstrapped/scala/quoted/Expr.scala @@ -32,7 +32,7 @@ object Expr { Block(statements.map(Term.of), Term.of(expr)).asExpr.asInstanceOf[Expr[T]] } - /** Creates an expression that will construct the value `x` */ + /** Lift a value into an expression containing the construction of that value */ def apply[T](x: T)(using ToExpr[T])(using Quotes): Expr[T] = scala.Predef.summon[ToExpr[T]].apply(x) diff --git a/library/src-bootstrapped/scala/quoted/FromExpr.scala b/library/src-bootstrapped/scala/quoted/FromExpr.scala index 6ee462cd237f..5d8bd49e2ff5 100644 --- a/library/src-bootstrapped/scala/quoted/FromExpr.scala +++ b/library/src-bootstrapped/scala/quoted/FromExpr.scala @@ -136,8 +136,8 @@ object FromExpr { */ given StringContextFromExpr: FromExpr[StringContext] with { def unapply(x: Expr[StringContext])(using Quotes) = x match { - case '{ new StringContext(${Varargs(Consts(args))}: _*) } => Some(StringContext(args: _*)) - case '{ StringContext(${Varargs(Consts(args))}: _*) } => Some(StringContext(args: _*)) + case '{ new StringContext(${Varargs(Exprs(args))}: _*) } => Some(StringContext(args: _*)) + case '{ StringContext(${Varargs(Exprs(args))}: _*) } => Some(StringContext(args: _*)) case _ => None } } diff --git a/library/src-non-bootstrapped/scala/quoted/Expr.scala b/library/src-non-bootstrapped/scala/quoted/Expr.scala index bf2b8f025002..36aa12337865 100644 --- a/library/src-non-bootstrapped/scala/quoted/Expr.scala +++ b/library/src-non-bootstrapped/scala/quoted/Expr.scala @@ -1,3 +1,7 @@ package scala.quoted abstract class Expr[+T] private[scala] + +object Expr: + def apply[T](x: T)(using ToExpr[T])(using Quotes): Expr[T] = ??? + def unapply[T](x: Expr[T])(using FromExpr[T])(using Quotes): Option[T] = ??? diff --git a/library/src-non-bootstrapped/scala/quoted/ToExpr.scala b/library/src-non-bootstrapped/scala/quoted/ToExpr.scala new file mode 100644 index 000000000000..6bbca2d69747 --- /dev/null +++ b/library/src-non-bootstrapped/scala/quoted/ToExpr.scala @@ -0,0 +1,4 @@ +package scala.quoted + +trait ToExpr[T]: + def apply(x: T)(using Quotes): T diff --git a/library/src/scala/quoted/Const.scala b/library/src/scala/quoted/Const.scala deleted file mode 100644 index 2b19792e127d..000000000000 --- a/library/src/scala/quoted/Const.scala +++ /dev/null @@ -1,38 +0,0 @@ -package scala.quoted - -/** Literal constant values */ -object Const { - - /** Matches expressions containing literal constant values and extracts the value. - * - * - Converts expression containg literal values to their values: - * - `'{1}` -> `1`, `'{2}` -> `2`, ... - * - For all primitive types and `String` - * - * Usage: - * ``` - * case '{ ... ${expr @ Const(value)}: T ...} => - * // expr: Expr[T] - * // value: T - * ``` - * - * To directly unlift an expression `expr: Expr[T]` consider using `expr.value`/`expr.valueOrError` insead. - */ - def unapply[T](expr: Expr[T])(using Quotes): Option[T] = { - import quotes.reflect._ - def rec(tree: Term): Option[T] = tree match { - case Literal(c) => - c match - case Constant.Null() => None - case Constant.Unit() => None - case Constant.ClassOf(_) => None - case _ => Some(c.value.asInstanceOf[T]) - case Block(Nil, e) => rec(e) - case Typed(e, _) => rec(e) - case Inlined(_, Nil, e) => rec(e) - case _ => None - } - rec(Term.of(expr)) - } - -} diff --git a/library/src/scala/quoted/Consts.scala b/library/src/scala/quoted/Consts.scala deleted file mode 100644 index 5b2708185412..000000000000 --- a/library/src/scala/quoted/Consts.scala +++ /dev/null @@ -1,28 +0,0 @@ -package scala.quoted - -/** Literal constant values */ -object Consts { - - /** Matches literal sequence of literal constant value expressions and return a sequence of values. - * - * Usage: - * ```scala - * inline def sum(args: Int*): Int = ${ sumExpr('args) } - * def sumExpr(argsExpr: Expr[Seq[Int]])(usingusing Quotes): Expr[Int] = argsExpr match - * case Varargs(Consts(args)) => - * // args: Seq[Int] - * ... - * } - * ``` - * - * To directly unlift all expressions in a sequence `exprs: Seq[Expr[T]]` consider using `exprs.map(_.value)`/`exprs.map(_.valueOrError)` insead. - */ - def unapply[T](exprs: Seq[Expr[T]])(using Quotes): Option[Seq[T]] = - exprs.foldRight(Option(List.empty[T])) { (elem, acc) => - (elem, acc) match { - case (Const(value), Some(lst)) => Some(value :: lst) - case (_, _) => None - } - } - -} diff --git a/library/src/scala/quoted/Exprs.scala b/library/src/scala/quoted/Exprs.scala index 7e71ab411055..8a4eeaaba105 100644 --- a/library/src/scala/quoted/Exprs.scala +++ b/library/src/scala/quoted/Exprs.scala @@ -1,5 +1,6 @@ package scala.quoted +/** Value expressions */ object Exprs: /** Matches literal sequence of literal constant value expressions and return a sequence of values. @@ -9,7 +10,6 @@ object Exprs: * inline def sum(args: Int*): Int = ${ sumExpr('args) } * def sumExpr(argsExpr: Expr[Seq[Int]])(using Quotes): Expr[Int] = argsExpr match * case Varargs(Exprs(args)) => - * case Varargs(Exprs(args)) => * // args: Seq[Int] * ... * } diff --git a/tests/bench/string-interpolation-macro/Macro.scala b/tests/bench/string-interpolation-macro/Macro.scala index 9b2f2f208108..6bf0aefa08ef 100644 --- a/tests/bench/string-interpolation-macro/Macro.scala +++ b/tests/bench/string-interpolation-macro/Macro.scala @@ -9,7 +9,7 @@ object Macro { var res: Expr[String] = null for _ <- 0 to 5_000 do (strCtxExpr, argsExpr) match { - case ('{ StringContext(${Varargs(Consts(parts))}: _*) }, Varargs(Consts(args))) => + case ('{ StringContext(${Varargs(Exprs(parts))}: _*) }, Varargs(Exprs(args))) => res = Expr(StringContext(parts: _*).s(args: _*)) case _ => ??? } diff --git a/tests/neg-macros/i6432/Macro_1.scala b/tests/neg-macros/i6432/Macro_1.scala index dfb26889fd44..667796fd230b 100644 --- a/tests/neg-macros/i6432/Macro_1.scala +++ b/tests/neg-macros/i6432/Macro_1.scala @@ -9,7 +9,7 @@ object Macro { import quotes.reflect._ sc match { case '{ StringContext(${Varargs(parts)}: _*) } => - for (part @ Const(s) <- parts) + for (part @ Expr(s) <- parts) report.error(s, Term.of(part).pos) } '{} diff --git a/tests/neg-macros/i6432b/Macro_1.scala b/tests/neg-macros/i6432b/Macro_1.scala index dfb26889fd44..667796fd230b 100644 --- a/tests/neg-macros/i6432b/Macro_1.scala +++ b/tests/neg-macros/i6432b/Macro_1.scala @@ -9,7 +9,7 @@ object Macro { import quotes.reflect._ sc match { case '{ StringContext(${Varargs(parts)}: _*) } => - for (part @ Const(s) <- parts) + for (part @ Expr(s) <- parts) report.error(s, Term.of(part).pos) } '{} diff --git a/tests/neg-macros/inline-macro-staged-interpreter/Macro_1.scala b/tests/neg-macros/inline-macro-staged-interpreter/Macro_1.scala index 6aa873114fa2..038fab2d88b6 100644 --- a/tests/neg-macros/inline-macro-staged-interpreter/Macro_1.scala +++ b/tests/neg-macros/inline-macro-staged-interpreter/Macro_1.scala @@ -10,7 +10,7 @@ object E { implicit def ev1[T: Type]: FromExpr[E[T]] = new FromExpr { def unapply(x: Expr[E[T]])(using Quotes) = x match { - case '{ I(${Const(n)}) } => Some(I(n).asInstanceOf[E[T]]) + case '{ I(${Expr(n)}) } => Some(I(n).asInstanceOf[E[T]]) case '{ Plus[T](${Value(x)}, ${Value(y)})(using $op) } if op.matches('{Plus2.IPlus}) => Some(Plus(x, y)(using Plus2.IPlus.asInstanceOf[Plus2[T]]).asInstanceOf[E[T]]) case _ => None } diff --git a/tests/neg-with-compiler/GenericNumLits/EvenFromDigitsImpl_1.scala b/tests/neg-with-compiler/GenericNumLits/EvenFromDigitsImpl_1.scala index 22fb66a3f2d1..25917e75f5ca 100644 --- a/tests/neg-with-compiler/GenericNumLits/EvenFromDigitsImpl_1.scala +++ b/tests/neg-with-compiler/GenericNumLits/EvenFromDigitsImpl_1.scala @@ -4,8 +4,8 @@ import scala.quoted._ import Even._ object EvenFromDigitsImpl: - def apply(digits: Expr[String])(using Quotes): Expr[Even] = digits match { - case Const(ds) => + def apply(digits: Expr[String])(using Quotes): Expr[Even] = digits.unlift match { + case Some(ds) => val ev = try evenFromDigits(ds) catch { diff --git a/tests/run-macros/expr-map-1/Macro_1.scala b/tests/run-macros/expr-map-1/Macro_1.scala index 660ba1588264..0a0859266040 100644 --- a/tests/run-macros/expr-map-1/Macro_1.scala +++ b/tests/run-macros/expr-map-1/Macro_1.scala @@ -9,7 +9,7 @@ private def stringRewriter(e: Expr[Any])(using Quotes): Expr[Any] = private object StringRewriter extends ExprMap { def transform[T](e: Expr[T])(using Type[T])(using Quotes): Expr[T] = e match - case Const(s: String) => + case '{ ${Expr(s)}: String } => Expr(s.reverse) match case '{ $x: T } => x case _ => e // e had a singlton String type diff --git a/tests/run-macros/flops-rewrite-2/Macro_1.scala b/tests/run-macros/flops-rewrite-2/Macro_1.scala index 8c6a0f49ecc0..a5da85da98a5 100644 --- a/tests/run-macros/flops-rewrite-2/Macro_1.scala +++ b/tests/run-macros/flops-rewrite-2/Macro_1.scala @@ -26,9 +26,9 @@ private def rewriteMacro[T: Type](x: Expr[T])(using Quotes): Expr[T] = { case (_, Some(_)) => '{ $y * $x } case _ => '{ $x * $y } } - case '{ power(${Const(x)}, ${Const(y)}) } => + case '{ power(${Expr(x)}, ${Expr(y)}) } => Expr(power(x, y)) - case '{ power($x, ${Const(y)}) } => + case '{ power($x, ${Expr(y)}) } => if y == 0 then '{1} else '{ times($x, power($x, ${Expr(y-1)})) } }), diff --git a/tests/run-macros/flops-rewrite-3/Macro_1.scala b/tests/run-macros/flops-rewrite-3/Macro_1.scala index 406b0af22c82..ccb53b38a4ce 100644 --- a/tests/run-macros/flops-rewrite-3/Macro_1.scala +++ b/tests/run-macros/flops-rewrite-3/Macro_1.scala @@ -25,9 +25,9 @@ private def rewriteMacro[T: Type](x: Expr[T])(using Quotes): Expr[T] = { case (_, Some(_)) => '{ $y * $x } case _ => '{ $x * $y } } - case '{ power(${Const(x)}, ${Const(y)}) } => + case '{ power(${Expr(x)}, ${Expr(y)}) } => Expr(power(x, y)) - case '{ power($x, ${Const(y)}) } => + case '{ power($x, ${Expr(y)}) } => if y == 0 then '{1} else '{ times($x, power($x, ${Expr(y-1)})) } } diff --git a/tests/run-macros/inline-macro-staged-interpreter/Macro_1.scala b/tests/run-macros/inline-macro-staged-interpreter/Macro_1.scala index d88b2e53f2c7..48f71e9f45f1 100644 --- a/tests/run-macros/inline-macro-staged-interpreter/Macro_1.scala +++ b/tests/run-macros/inline-macro-staged-interpreter/Macro_1.scala @@ -11,8 +11,8 @@ object E { implicit def ev1[T: Type]: FromExpr[E[T]] = new FromExpr { // TODO use type class derivation def unapply(x: Expr[E[T]])(using Quotes) = (x match { - case '{ I(${Const(n)}) } => Some(I(n)) - case '{ D(${Const(n)}) } => Some(D(n)) + case '{ I(${Expr(n)}) } => Some(I(n)) + case '{ D(${Expr(n)}) } => Some(D(n)) case '{ Plus[Int](${Value(x)}, ${Value(y)})(using $op) } => Some(Plus(x, y)(using Plus2.IPlus)) case '{ Plus[Double](${Value(x)}, ${Value(y)})(using $op) } => Some(Plus(x, y)(using Plus2.DPlus)) case '{ Times[Int](${Value(x)}, ${Value(y)})(using $op) } => Some(Times(x, y)(using Times2.ITimes)) diff --git a/tests/run-macros/quote-matcher-string-interpolator-3/quoted_1.scala b/tests/run-macros/quote-matcher-string-interpolator-3/quoted_1.scala index 8c51f0d89480..9ba5ea61c69c 100644 --- a/tests/run-macros/quote-matcher-string-interpolator-3/quoted_1.scala +++ b/tests/run-macros/quote-matcher-string-interpolator-3/quoted_1.scala @@ -8,7 +8,7 @@ object Macros { private def impl(self: Expr[StringContext], args: Expr[Seq[String]])(using Quotes): Expr[String] = { self match { - case '{ StringContext(${Varargs(Consts(parts))}: _*) } => + case '{ StringContext(${Varargs(Exprs(parts))}: _*) } => val upprerParts: List[String] = parts.toList.map(_.toUpperCase) val upprerPartsExpr: Expr[List[String]] = Expr.ofList(upprerParts.map(Expr(_))) '{ StringContext($upprerPartsExpr: _*).s($args: _*) } diff --git a/tests/run-macros/quote-matcher-symantics-1/quoted_1.scala b/tests/run-macros/quote-matcher-symantics-1/quoted_1.scala index 3859f395866b..e5cc3fd1e47a 100644 --- a/tests/run-macros/quote-matcher-symantics-1/quoted_1.scala +++ b/tests/run-macros/quote-matcher-symantics-1/quoted_1.scala @@ -10,7 +10,7 @@ object Macros { def lift(e: Expr[DSL]): Expr[T] = e match { - case '{ LitDSL(${ Const(c) }) } => + case '{ LitDSL(${ Expr(c) }) } => '{ $sym.value(${Expr(c)}) } case '{ ($x: DSL) + ($y: DSL) } => diff --git a/tests/run-macros/quote-matcher-symantics-2/quoted_1.scala b/tests/run-macros/quote-matcher-symantics-2/quoted_1.scala index 1211f35f42e4..6fc3b4827d30 100644 --- a/tests/run-macros/quote-matcher-symantics-2/quoted_1.scala +++ b/tests/run-macros/quote-matcher-symantics-2/quoted_1.scala @@ -21,7 +21,7 @@ object Macros { def lift(e: Expr[DSL])(implicit env: Map[Int, Expr[T]]): Expr[T] = e match { - case '{ LitDSL(${Const(c)}) } => sym.value(c) + case '{ LitDSL(${Expr(c)}) } => sym.value(c) case '{ ($x: DSL) + ($y: DSL) } => sym.plus(lift(x), lift(y)) @@ -35,7 +35,7 @@ object Macros { lift(close(body1)(nEnvVar))(env + (i -> lift(value))) } - case '{ envVar(${Const(i)}) } => env(i) + case '{ envVar(${Expr(i)}) } => env(i) case _ => import quotes.reflect._ diff --git a/tests/run-macros/quote-matcher-symantics-3/quoted_1.scala b/tests/run-macros/quote-matcher-symantics-3/quoted_1.scala index 8951f84963cb..0a98fca77ed4 100644 --- a/tests/run-macros/quote-matcher-symantics-3/quoted_1.scala +++ b/tests/run-macros/quote-matcher-symantics-3/quoted_1.scala @@ -18,15 +18,15 @@ object Macros { object FromEnv { def unapply[T](e: Expr[Any])(using env: Env): Option[Expr[R[T]]] = e match - case '{envVar[t](${Const(id)})} => + case '{envVar[t](${Expr(id)})} => env.get(id).asInstanceOf[Option[Expr[R[T]]]] // We can only add binds that have the same type as the refs case _ => None } def lift[T: Type](e: Expr[T])(using env: Env): Expr[R[T]] = ((e: Expr[Any]) match { - case Const(e: Int) => '{ $sym.int(${Expr(e)}).asInstanceOf[R[T]] } - case Const(e: Boolean) => '{ $sym.bool(${Expr(e)}).asInstanceOf[R[T]] } + case '{ ${Expr(e)}: Int } => '{ $sym.int(${Expr(e)}).asInstanceOf[R[T]] } + case '{ ${Expr(e)}: Boolean } => '{ $sym.bool(${Expr(e)}).asInstanceOf[R[T]] } case '{ ($x: Int) + ($y: Int) } => '{ $sym.add(${lift(x)}, ${lift(y)}).asInstanceOf[R[T]] } diff --git a/tests/run-macros/quoted-matching-docs/Macro_1.scala b/tests/run-macros/quoted-matching-docs/Macro_1.scala index bd4423e99fd8..84d61983836c 100644 --- a/tests/run-macros/quoted-matching-docs/Macro_1.scala +++ b/tests/run-macros/quoted-matching-docs/Macro_1.scala @@ -9,7 +9,7 @@ private def sumExprShow(argsExpr: Expr[Seq[Int]]) (using Quotes): Expr[String] = private def sumExpr(argsExpr: Expr[Seq[Int]])(using Quotes) : Expr[Int] = { UnsafeExpr.underlyingArgument(argsExpr) match { - case Varargs(Consts(args)) => // args is of type Seq[Int] + case Varargs(Exprs(args)) => // args is of type Seq[Int] Expr(args.sum) // precompute result of sum case Varargs(argExprs) => // argExprs is of type Seq[Expr[Int]] val staticSum: Int = argExprs.map(_.value.getOrElse(0)).sum diff --git a/tests/run-macros/tasty-extractors-constants-1.check b/tests/run-macros/tasty-extractors-constants-1.check index c2a18854a8bd..a6c17759fbdd 100644 --- a/tests/run-macros/tasty-extractors-constants-1.check +++ b/tests/run-macros/tasty-extractors-constants-1.check @@ -2,4 +2,3 @@ 4 abc null -OK diff --git a/tests/run-macros/tasty-extractors-constants-1/quoted_1.scala b/tests/run-macros/tasty-extractors-constants-1/quoted_1.scala index 8f53d7a39e3c..c65587c414d5 100644 --- a/tests/run-macros/tasty-extractors-constants-1/quoted_1.scala +++ b/tests/run-macros/tasty-extractors-constants-1/quoted_1.scala @@ -11,12 +11,12 @@ object Macros { val buff = new StringBuilder def stagedPrintln(x: Any): Unit = buff append java.util.Objects.toString(x) append "\n" - Expr(3) match { case Const(n) => stagedPrintln(n) } - '{4} match { case Const(n) => stagedPrintln(n) } - '{"abc"} match { case Const(n) => stagedPrintln(n) } + Expr(3) match { case Expr(n) => stagedPrintln(n) } + '{4} match { case Expr(n) => stagedPrintln(n) } + '{"abc"} match { case Expr(n) => stagedPrintln(n) } '{null} match { case '{null} => stagedPrintln(null) } - '{new Object} match { case Const(n) => println(n); case _ => stagedPrintln("OK") } + // '{new Object} match { case Expr(n) => println(n); case _ => stagedPrintln("OK") } '{print(${Expr(buff.result())})} }