Skip to content

Commit bb52aa8

Browse files
Merge pull request #8479 from dotty-staging/refactor-quoted-matching
Refactor scala.quoted.matching
2 parents f8e4ed3 + 7d86a21 commit bb52aa8

File tree

63 files changed

+232
-164
lines changed

Some content is hidden

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

63 files changed

+232
-164
lines changed

docs/docs/reference/metaprogramming/macros.md

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -565,13 +565,13 @@ sum
565565
### Find implicits within a macro
566566

567567
Similarly to the `summonFrom` construct, it is possible to make implicit search available
568-
in a quote context. For this we simply provide `scala.quoted.matching.summonExpr`:
568+
in a quote context. For this we simply provide `scala.quoted.Expr.summon`:
569569

570570
```scala
571571
inline def setFor[T]: Set[T] = ${ setForExpr[T] }
572572

573573
def setForExpr[T: Type](using QuoteContext): Expr[Set[T]] = {
574-
summonExpr[Ordering[T]] match {
574+
Expr.summon[Ordering[T]] match {
575575
case Some(ord) => '{ new TreeSet[T]()($ord) }
576576
case _ => '{ new HashSet[T] }
577577
}
@@ -614,23 +614,19 @@ In case all files are suspended due to cyclic dependencies the compilation will
614614

615615
It is possible to deconstruct or extract values out of `Expr` using pattern matching.
616616

617-
#### scala.quoted.matching
617+
`scala.quoted` contains objects that can help extracting values from `Expr`.
618618

619-
`scala.quoted.matching` contains objects that can help extracting values from `Expr`.
620-
621-
* `scala.quoted.matching.Const`: matches an expression of a literal value and returns the value.
622-
* `scala.quoted.matching.Value`: matches an expression of a value and returns the value.
623-
* `scala.quoted.matching.ExprSeq`: 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]]`.
624-
* `scala.quoted.matching.ConstSeq`: matches an explicit sequence of literal values and returns them.
625-
* `scala.quoted.matching.ValueSeq`: matches an explicit sequence of values and returns them.
619+
* `scala.quoted.Const`/`scala.quoted.Consts`: matches an expression of a literal value (or list of values) and returns the value (or list of values).
620+
* `scala.quoted.Value`/`scala.quoted.Values`: matches an expression of a value (or list of values) and returns the value (or list of values).
621+
* `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]]`.
626622

627623
These could be used in the following way to optimize any call to `sum` that has statically known values.
628624
```scala
629625
inline def sum(inline args: Int*): Int = ${ sumExpr('args) }
630626
private def sumExpr(argsExpr: Expr[Seq[Int]])(using QuoteContext): Expr[Int] = argsExpr match {
631-
case ConstSeq(args) => // args is of type Seq[Int]
627+
case Varargs(Consts(args)) => // args is of type Seq[Int]
632628
Expr(args.sum) // precompute result of sum
633-
case ExprSeq(argExprs) => // argExprs is of type Seq[Expr[Int]]
629+
case Varargs(argExprs) => // argExprs is of type Seq[Expr[Int]]
634630
val staticSum: Int = argExprs.map {
635631
case Const(arg) => arg
636632
case _ => 0
@@ -666,12 +662,12 @@ private def optimizeExpr(body: Expr[Int])(using QuoteContext): Expr[Int] = body
666662
// Match a call to sum with an argument $n of type Int. n will be the Expr[Int] representing the argument.
667663
case '{ sum($n) } => n
668664
// Match a call to sum and extracts all its args in an `Expr[Seq[Int]]`
669-
case '{ sum(${ExprSeq(args)}: _*) } => sumExpr(args)
665+
case '{ sum(${Varargs(args)}: _*) } => sumExpr(args)
670666
case body => body
671667
}
672668
private def sumExpr(args1: Seq[Expr[Int]])(using QuoteContext): Expr[Int] = {
673669
def flatSumArgs(arg: Expr[Int]): Seq[Expr[Int]] = arg match {
674-
case '{ sum(${ExprSeq(subArgs)}: _*) } => subArgs.flatMap(flatSumArgs)
670+
case '{ sum(${Varargs(subArgs)}: _*) } => subArgs.flatMap(flatSumArgs)
675671
case arg => Seq(arg)
676672
}
677673
val args2 = args1.flatMap(flatSumArgs)
@@ -709,7 +705,7 @@ inline def (sc: StringContext).showMe(inline args: Any*): String = ${ showMeExpr
709705

710706
private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(using qctx: QuoteContext): Expr[String] = {
711707
argsExpr match {
712-
case ExprSeq(argExprs) =>
708+
case Varargs(argExprs) =>
713709
val argShowedExprs = argExprs.map {
714710
case '{ $arg: $tp } =>
715711
val showTp = '[Show[$tp]]
@@ -718,7 +714,7 @@ private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(using
718714
case None => qctx.error(s"could not find implicit for ${showTp.show}", arg); '{???}
719715
}
720716
}
721-
val newArgsExpr = Expr.ofSeq(argShowedExprs)
717+
val newArgsExpr = Varargs(argShowedExprs)
722718
'{ $sc.s($newArgsExpr: _*) }
723719
case _ =>
724720
// `new StringContext(...).showMeExpr(args: _*)` not an explicit `showMeExpr"..."`

library/src-bootstrapped/dotty/internal/StringContextMacro.scala

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
package dotty.internal
44

55
import scala.quoted._
6-
import scala.quoted.matching._
7-
import reflect._
86

97
object StringContextMacro {
108

@@ -65,9 +63,12 @@ object StringContextMacro {
6563

6664
def notStatic =
6765
qctx.throwError("Expected statically known String Context", strCtxExpr)
68-
def splitParts(seq: Expr[Seq[String]]) = (seq, seq) match {
69-
case (ExprSeq(p1), ConstSeq(p2)) => (p1.toList, p2.toList)
70-
case (_, _) => notStatic
66+
def splitParts(seq: Expr[Seq[String]]) = seq match {
67+
case Varargs(p1) =>
68+
p1 match
69+
case Consts(p2) => (p1.toList, p2.toList)
70+
case _ => notStatic
71+
case _ => notStatic
7172
}
7273
val (partsExpr, parts) = strCtxExpr match {
7374
case '{ StringContext($parts: _*) } => splitParts(parts)
@@ -76,7 +77,7 @@ object StringContextMacro {
7677
}
7778

7879
val args = argsExpr match {
79-
case ExprSeq(args) => args
80+
case Varargs(args) => args
8081
case _ => qctx.throwError("Expected statically known argument list", argsExpr)
8182
}
8283

library/src-non-bootstrapped/dotty/internal/StringContextMacro.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
package dotty.internal
44

55
import scala.quoted._
6-
import scala.quoted.matching._
7-
import reflect._
86

97
object StringContextMacro {
108

library/src/scala/quoted/matching/Const.scala renamed to library/src/scala/quoted/Const.scala

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
package scala.quoted
2-
package matching
32

4-
/** Matches expressions containing literal constant values and extracts the value.
5-
* It may match expressions of type Boolean, Byte, Short, Int, Long,
6-
* Float, Double, Char, String, ClassTag, scala.Symbol, Null and Unit.
7-
*
8-
* Usage:
9-
* ```
10-
* (x: Expr[B]) match {
11-
* case Const(value: B) => ...
12-
* }
13-
* ```
14-
*/
3+
/** Literal constant values */
154
object Const {
165

6+
/** Matches expressions containing literal constant values and extracts the value.
7+
* It may match expressions of type Boolean, Byte, Short, Int, Long,
8+
* Float, Double, Char, String, ClassTag, scala.Symbol, Null and Unit.
9+
*
10+
* Usage:
11+
* ```
12+
* (x: Expr[B]) match {
13+
* case Const(value: B) => ...
14+
* }
15+
* ```
16+
*/
1717
def unapply[T](expr: Expr[T])(using qctx: QuoteContext): Option[T] = {
1818
import qctx.tasty.{_, given _}
1919
def rec(tree: Term): Option[T] = tree match {

library/src/scala/quoted/Consts.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package scala.quoted
2+
3+
/** Literal constant values */
4+
object Consts {
5+
6+
/** Matches literal sequence of literal constant value expressions and return a sequence of values.
7+
*
8+
* Usage:
9+
* ```scala
10+
* inline def sum(args: Int*): Int = ${ sumExpr('args) }
11+
* def sumExpr(argsExpr: Expr[Seq[Int]])(usingusing QuoteContext): Expr[Int] = argsExpr match
12+
* case Varargs(Consts(args)) =>
13+
* // args: Seq[Int]
14+
* ...
15+
* }
16+
* ```
17+
*/
18+
def unapply[T](exprs: Seq[Expr[T]])(using qctx: QuoteContext): Option[Seq[T]] =
19+
exprs.foldRight(Option(List.empty[T])) { (elem, acc) =>
20+
(elem, acc) match {
21+
case (Const(value), Some(lst)) => Some(value :: lst)
22+
case (_, _) => None
23+
}
24+
}
25+
26+
}

library/src/scala/quoted/Expr.scala

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -100,25 +100,17 @@ object Expr {
100100
}
101101

102102
/** Lift a value into an expression containing the construction of that value */
103-
def apply[T: Liftable](x: T)(using qctx: QuoteContext): Expr[T] = summon[Liftable[T]].toExpr(x)
103+
def apply[T](x: T)(using qctx: QuoteContext, lift: Liftable[T]): Expr[T] = lift.toExpr(x)
104104

105105
/** Lifts this sequence of expressions into an expression of a sequence
106106
*
107107
* Transforms a sequence of expression
108108
* `Seq(e1, e2, ...)` where `ei: Expr[T]`
109109
* to an expression equivalent to
110110
* `'{ Seq($e1, $e2, ...) }` typed as an `Expr[Seq[T]]`
111-
*
112-
* Usage:
113-
* ```scala
114-
* '{ List(${Expr.ofSeq(List(1, 2, 3))}: _*) } // equvalent to '{ List(1, 2, 3) }
115111
* ```
116112
*/
117-
def ofSeq[T](xs: Seq[Expr[T]])(using tp: Type[T], qctx: QuoteContext): Expr[Seq[T]] = {
118-
import qctx.tasty.{_, given _}
119-
Repeated(xs.map[Term](_.unseal).toList, tp.unseal).seal.asInstanceOf[Expr[Seq[T]]]
120-
}
121-
113+
def ofSeq[T](xs: Seq[Expr[T]])(using tp: Type[T], qctx: QuoteContext): Expr[Seq[T]] = Varargs(xs)
122114

123115
/** Lifts this list of expressions into an expression of a list
124116
*
@@ -128,7 +120,7 @@ object Expr {
128120
* `'{ List($e1, $e2, ...) }` typed as an `Expr[List[T]]`
129121
*/
130122
def ofList[T](xs: Seq[Expr[T]])(using Type[T], QuoteContext): Expr[List[T]] =
131-
if (xs.isEmpty) '{ Nil } else '{ List(${ofSeq(xs)}: _*) }
123+
if (xs.isEmpty) '{ Nil } else '{ List(${Varargs(xs)}: _*) }
132124

133125
/** Lifts this sequence of expressions into an expression of a tuple
134126
*
@@ -186,7 +178,7 @@ object Expr {
186178
case Seq('{ $x1: $t1 }, '{ $x2: $t2 }, '{ $x3: $t3 }, '{ $x4: $t4 }, '{ $x5: $t5 }, '{ $x6: $t6 }, '{ $x7: $t7 }, '{ $x8: $t8 }, '{ $x9: $t9 }, '{ $x10: $t10 }, '{ $x11: $t11 }, '{ $x12: $t12 }, '{ $x13: $t13 }, '{ $x14: $t14 }, '{ $x15: $t15 }, '{ $x16: $t16 }, '{ $x17: $t17 }, '{ $x18: $t18 }, '{ $x19: $t19 }, '{ $x20: $t20 }, '{ $x21: $t21 }, '{ $x22: $t22 }) =>
187179
'{ Tuple22($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17, $x18, $x19, $x20, $x21, $x22) }
188180
case _ =>
189-
'{ Tuple.fromIArray(IArray(${ofSeq(seq)}: _*)) }
181+
'{ Tuple.fromIArray(IArray(${Varargs(seq)}: _*)) }
190182
}
191183
}
192184

@@ -197,4 +189,20 @@ object Expr {
197189
ofTuple(elems).cast[Tuple.InverseMap[T, Expr]]
198190
}
199191

192+
/** Find an implicit of type `T` in the current scope given by `qctx`.
193+
* Return `Some` containing the expression of the implicit or
194+
* `None` if implicit resolution failed.
195+
*
196+
* @tparam T type of the implicit parameter
197+
* @param tpe quoted type of the implicit parameter
198+
* @param qctx current context
199+
*/
200+
def summon[T](using tpe: Type[T])(using qctx: QuoteContext): Option[Expr[T]] = {
201+
import qctx.tasty.{_, given _}
202+
searchImplicit(tpe.unseal.tpe) match {
203+
case iss: ImplicitSearchSuccess => Some(iss.tree.seal.asInstanceOf[Expr[T]])
204+
case isf: ImplicitSearchFailure => None
205+
}
206+
}
207+
200208
}

library/src/scala/quoted/matching/Lambda.scala renamed to library/src/scala/quoted/Lambda.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
package scala.quoted
2-
package matching
32

43
/** Lambda expression extractor */
54
object Lambda {

library/src/scala/quoted/matching/Value.scala renamed to library/src/scala/quoted/Value.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
package scala.quoted
2-
package matching
32

43
/** Value expressions */
54
object Value {

library/src/scala/quoted/ValueOfExpr.scala

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package scala.quoted
22

3-
import scala.quoted.matching._
4-
53
/** A typeclass for types that can be turned from a `quoted.Expr[T]` to a `T` */
64
trait ValueOfExpr[T] {
75

@@ -29,7 +27,7 @@ object ValueOfExpr {
2927

3028
private class PrimitiveValueOfExpr[T <: Unit | Null | Int | Boolean | Byte | Short | Int | Long | Float | Double | Char | String] extends ValueOfExpr[T] {
3129
/** Lift a quoted primitive value `'{ n }` into `n` */
32-
def apply(x: Expr[T])(using qctx: QuoteContext): Option[T] = matching.Const.unapply(x)
30+
def apply(x: Expr[T])(using qctx: QuoteContext): Option[T] = Const.unapply(x)
3331
}
3432

3533
given Option_delegate[T](using Type[T], ValueOfExpr[T]) as ValueOfExpr[Option[T]] = new {
@@ -44,8 +42,8 @@ object ValueOfExpr {
4442

4543
given StringContext_delegate as ValueOfExpr[StringContext] = new {
4644
def apply(x: Expr[StringContext])(using qctx: QuoteContext): Option[StringContext] = x match {
47-
case '{ new StringContext(${ConstSeq(args)}: _*) } => Some(StringContext(args: _*))
48-
case '{ StringContext(${ConstSeq(args)}: _*) } => Some(StringContext(args: _*))
45+
case '{ new StringContext(${Varargs(Consts(args))}: _*) } => Some(StringContext(args: _*))
46+
case '{ StringContext(${Varargs(Consts(args))}: _*) } => Some(StringContext(args: _*))
4947
case _ => None
5048
}
5149
override def toString(): String = "scala.quoted.ValueOfExpr.Tuple1_delegate"
@@ -272,9 +270,9 @@ object ValueOfExpr {
272270

273271
given Seq_delegate[T](using Type[T], ValueOfExpr[T]) as ValueOfExpr[Seq[T]] = new {
274272
def apply(x: Expr[Seq[T]])(using qctx: QuoteContext): Option[Seq[T]] = x match {
275-
case ValueSeq(elems) => Some(elems)
276-
case '{ scala.collection.Seq[T](${ValueSeq(elems)}: _*) } => Some(elems)
277-
case '{ scala.collection.immutable.Seq[T](${ValueSeq(elems)}: _*) } => Some(elems)
273+
case Varargs(Values(elems)) => Some(elems)
274+
case '{ scala.collection.Seq[T](${Varargs(Values(elems))}: _*) } => Some(elems)
275+
case '{ scala.collection.immutable.Seq[T](${Varargs(Values(elems))}: _*) } => Some(elems)
278276
case _ => None
279277
}
280278
override def toString(): String = "scala.quoted.ValueOfExpr.Seq_delegate"

library/src/scala/quoted/Values.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package scala.quoted
2+
3+
/** Value expressions */
4+
object Values {
5+
6+
/** Matches literal sequence of literal constant value expressions and return a sequence of values.
7+
*
8+
* Usage:
9+
* ```scala
10+
* inline def sum(args: Int*): Int = ${ sumExpr('args) }
11+
* def sumExpr(argsExpr: Expr[Seq[Int]])(using QuoteContext): Expr[Int] = argsExpr match
12+
* case Varargs(Values(args)) =>
13+
* // args: Seq[Int]
14+
* ...
15+
* }
16+
* ```
17+
*/
18+
def unapply[T](exprs: Seq[Expr[T]])(using valueOf: ValueOfExpr[T], qctx: QuoteContext): Option[Seq[T]] =
19+
exprs.foldRight(Option(List.empty[T])) { (elem, acc) =>
20+
(elem, acc) match {
21+
case (Value(value), Some(lst)) => Some(value :: lst)
22+
case (_, _) => None
23+
}
24+
}
25+
}

library/src/scala/quoted/matching/ExprSeq.scala renamed to library/src/scala/quoted/Varargs.scala

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,33 @@
11
package scala.quoted
2-
package matching
32

43
/** Literal sequence of expressions */
5-
object ExprSeq {
4+
object Varargs {
5+
6+
/** Lifts this sequence of expressions into an expression of a sequence
7+
*
8+
* Transforms a sequence of expression
9+
* `Seq(e1, e2, ...)` where `ei: Expr[T]`
10+
* to an expression equivalent to
11+
* `'{ Seq($e1, $e2, ...) }` typed as an `Expr[Seq[T]]`
12+
*
13+
* Usage:
14+
* ```scala
15+
* '{ List(${Varargs(List(1, 2, 3))}: _*) } // equvalent to '{ List(1, 2, 3) }
16+
* ```
17+
*/
18+
def apply[T](xs: Seq[Expr[T]])(using tp: Type[T], qctx: QuoteContext): Expr[Seq[T]] = {
19+
import qctx.tasty.{_, given _}
20+
Repeated(xs.map[Term](_.unseal).toList, tp.unseal).seal.asInstanceOf[Expr[Seq[T]]]
21+
}
622

723
/** Matches a literal sequence of expressions and return a sequence of expressions.
824
*
925
* Usage:
1026
* ```scala
1127
* inline def sum(args: Int*): Int = ${ sumExpr('args) }
1228
* def sumExpr(argsExpr: Expr[Seq[Int]])(using QuoteContext): Expr[Int] = argsExpr match
13-
* case ExprSeq(argExprs) =>
14-
* // argExprs: Seq[Expr[Int]]
29+
* case Varargs(argVarargs) =>
30+
* // argVarargs: Seq[Expr[Int]]
1531
* ...
1632
* }
1733
* ```

library/src/scala/quoted/matching/ConstSeq.scala

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,11 @@ object ConstSeq {
1616
* }
1717
* ```
1818
*/
19-
def unapply[T](expr: Expr[Seq[T]])(using qctx: QuoteContext): Option[Seq[T]] = expr match {
20-
case ExprSeq(elems) =>
21-
elems.foldRight(Option(List.empty[T])) { (elem, acc) =>
22-
(elem, acc) match {
23-
case (Const(value), Some(lst)) => Some(value :: lst)
24-
case (_, _) => None
25-
}
26-
}
27-
case _ => None
28-
}
19+
@deprecated("use scala.quoted.Varargs(scala.quoted.Const(_)) instead", "0.23.0")
20+
def unapply[T](expr: Expr[Seq[T]])(using qctx: QuoteContext): Option[Seq[T]] =
21+
import scala.quoted.Const
22+
expr match
23+
case Varargs(Consts(elems)) => Some(elems)
24+
case _ => None
2925

3026
}

0 commit comments

Comments
 (0)