Skip to content

Commit b8c67cf

Browse files
committed
Add runtime.quoted.Matcher
This allows to match a quote against another quote while extracting the contents of defined holes
1 parent a9ef81f commit b8c67cf

File tree

24 files changed

+1868
-15
lines changed

24 files changed

+1868
-15
lines changed

compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1691,21 +1691,7 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util.
16911691

16921692
/** Convert `Term` to an `Expr[T]` and check that it conforms to `T` */
16931693
def QuotedExpr_seal[T](self: Term)(tpe: scala.quoted.Type[T])(implicit ctx: Context): scala.quoted.Expr[T] = {
1694-
16951694
val expectedType = QuotedType_unseal(tpe).tpe
1696-
1697-
def etaExpand(term: Term): Term = term.tpe.widen match {
1698-
case mtpe: Types.MethodType if !mtpe.isParamDependent =>
1699-
val closureResType = mtpe.resType match {
1700-
case t: Types.MethodType => t.toFunctionType()
1701-
case t => t
1702-
}
1703-
val closureTpe = Types.MethodType(mtpe.paramNames, mtpe.paramInfos, closureResType)
1704-
val closureMethod = ctx.newSymbol(ctx.owner, nme.ANON_FUN, Synthetic | Method, closureTpe)
1705-
tpd.Closure(closureMethod, tss => etaExpand(new tpd.TreeOps(term).appliedToArgs(tss.head)))
1706-
case _ => term
1707-
}
1708-
17091695
val expanded = etaExpand(self)
17101696
if (expanded.tpe <:< expectedType) {
17111697
new scala.quoted.Exprs.TastyTreeExpr(expanded).asInstanceOf[scala.quoted.Expr[T]]
@@ -1718,6 +1704,22 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util.
17181704
}
17191705
}
17201706

1707+
/** Convert `Term` to an `Expr[_]` */
1708+
def QuotedExpr_seal2(self: Term)(implicit ctx: Context): scala.quoted.Expr[_] =
1709+
new scala.quoted.Exprs.TastyTreeExpr(etaExpand(self))
1710+
1711+
private def etaExpand(term: Term): Term = term.tpe.widen match {
1712+
case mtpe: Types.MethodType if !mtpe.isParamDependent =>
1713+
val closureResType = mtpe.resType match {
1714+
case t: Types.MethodType => t.toFunctionType()
1715+
case t => t
1716+
}
1717+
val closureTpe = Types.MethodType(mtpe.paramNames, mtpe.paramInfos, closureResType)
1718+
val closureMethod = ctx.newSymbol(ctx.owner, nme.ANON_FUN, Synthetic | Method, closureTpe)
1719+
tpd.Closure(closureMethod, tss => etaExpand(new tpd.TreeOps(term).appliedToArgs(tss.head)))
1720+
case _ => term
1721+
}
1722+
17211723
/** Convert `Type` to an `quoted.Type[T]` */
17221724
def QuotedType_seal(self: Type)(implicit ctx: Context): scala.quoted.Type[_] = {
17231725
val dummySpan = ctx.owner.span // FIXME
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
---
2+
layout: doc-page
3+
title: "Principled Meta Programming - Pattern matching"
4+
---
5+
6+
## Overview
7+
8+
Quote pattern matching allows to match a quote against another quote while extracting the contents of defined holes. This will be used in as desugaring of quoted patterns.
9+
10+
```scala
11+
def optimize(expr: Expr[Int]): Expr[Int] = expr match {
12+
case '{ 0 + $y } => optimize(y)
13+
case '{ ${Literal(a)} + ${Literal(b)} } => (a + b).toExpr
14+
case '{ ${x} + ${y @ Literal(_)} } => optimize('{ $y + $x })
15+
...
16+
case expr => expr
17+
}
18+
```
19+
20+
Where `x: Expr[Int]`, `y: Expr[Int]`, `a: Int` and `b: Int`.
21+
22+
23+
### Quote patterns
24+
25+
Quote extractors are patterns of the shape `'{ ... }` that can match an `Expr[T]`.
26+
27+
```scala
28+
(expr: Expr[T]) match {
29+
case '{ pattern } =>
30+
}
31+
```
32+
33+
They allow to extract expresions, types and identifiers
34+
35+
```scala
36+
(expr: Expr[T]) match {
37+
case '{ ... val x: T = $rhs ... } =>
38+
case '{ ... val $y: T = ... } =>
39+
case '{ ... val z: $t = ... } =>
40+
}
41+
```
42+
43+
Where `x: Expr[T]`, `y: Binging[T]` and `z: Type[T]`.
44+
45+
46+
#### Desugaring
47+
48+
A pattern with splices such as
49+
50+
51+
```scala
52+
(expr: Expr[T]) match {
53+
case '{ ... ($x: T) ... val $b: U = ... foo[$t] ... } =>
54+
}
55+
```
56+
57+
is transformed to a call to `Matcher.unapply` with the appropriate types for the splices in a tuple.
58+
59+
```scala
60+
(expr: Expr[T]) match {
61+
case scala.runtime.quoted.Matcher.unapply
62+
[... *: Expr[X] *: ... *: Binding[T] *: ... *: Type[V] *: ... *: Unit]
63+
(... *: x *: ... *: b *: ... *: t *: ... *: ())
64+
(/*implicits*/ '{ ... hole[T] ... bindHole[U] ... Hole[V] ... }, reflect) =>
65+
}
66+
```
67+
68+
69+
### Other expression patterns
70+
71+
```scala
72+
(expr: Expr[T]) match {
73+
case Literal(lit) => lit: T
74+
case Binding(binding) => binding: Binding[T]
75+
case IsValRef(expr2) => expr2: expr.type
76+
case IsDefRef(expr) => expr2: expr.type
77+
78+
case '{ ... ${Literal(lit)} ... }
79+
}
80+
```
81+
82+
83+
## Spec
84+
85+
### Quote pattern desugaring
86+
87+
Given a quote pattern `'{ pattern }` the desugaring is defined in terms of a splice types `T<pattern>`, a runtime pattern `P<patern>` and extracted bindings `E<pattern>`.
88+
```scala
89+
Matcher.unapply[T<pattern>](E<pattern>)(/*implicits*/ '{ P<pattern> }, reflect)
90+
```
91+
92+
93+
94+
`<pattern>` is defined recursively in the following way:
95+
96+
| Quote pattern syntax | Matcher pattern | Splice types | Extracted |
97+
| ------------- |:-------------:| ----------- | ----------- |
98+
| `<$x>` where `x: Expr[X]` | `Matcher.hole[X]` | `Tuple1[Expr[T]]` | `Tuple1[Expr[T]](x)` |
99+
| `<$t>` where `x: Type[X]` | `Matcher.HOLE[X]` | `Tuple1[Type[T]]` | `Tuple1[Type[T]](t)` |
100+
| `<$n: X>` where `n` is a name of `val`, `var`, `def` or parameter definition | `n: bindHole[X]` | `Tuple1[Binding[X]]` | `Tuple1[Binding[X]](n)` |
101+
| `<if (e1) e2 else e3>` | `if (<e1>) <e2> else <e3>` | `Concat[T<e1>, Concat[T<e2>, T<e3>]]` | E<`e1`> ++ E<`e2`> ++ E<`e3`>) |
102+
| `<while (e1) e2>` | `while (<e1>) <e2>` | | E<`e1`> ++ E<`e2`> |
103+
| `<f[T1, ..., Tn](e1, ..., em)>` | `<f>[<T1>, ..., <Tn>](<e1>, ..., <em>)` | | E<`f`> ++ E<`T1`> ++ ... ++ E<`Tn`> ++ E<`e1`> ++ ... ++ E<`em`> |
104+
| `<(e1: T1, ..., en: Tn) => body>` | `(<e1: T1>, ..., <en: Tn>) => <body>` | | E<`e1: T1`> ++ ... ++ E<`en: Tn`> ++ E<`body`> |
105+
| `<lhs = rhs>` | `lhs = <rhs>` | | E<`rhs`> |
106+
| `<new X>` | `new <X>` | | E<`X`> |
107+
| `<this>` | `this` | | |
108+
| `<qual.super[T]>` | `<qual>.super[<T>]` | | E<`qual`> ++ E<`T`) |
109+
| `<K[T1, ..., Tn]>` | `<K>[<T1>, ..., <Tn>]` | | E<`K`> ++ E<`T1`> ++ ... ++ E<`Tn`> |
110+
| `<x>` where `x` is an identifier | `x` | | |
111+
| `<T>` where `T` is an identifier | `T` | | |
112+
113+
114+
### Quote pattern match runtime
115+
116+
| Match | Condition |
117+
| ------------- |:-------------:|
118+
| matches(`s`, `hole[T]`) for `s` of type `S` | `S <:< T` |
119+
| matches(`T`, `Hole[T]`) | |
120+
| matches(`n: X1`, `n: bindHole[X2]`) | matches(`X1`, `X2`) |
121+
| matches(`if (s1) s2 else s3`, `if (p1) p2 else p3`) | matches(`s1`, `p1`) && matches(`s2`, `p2`) && matches(`s3`,`p3`) |
122+
| matches(`while (s1) s2`, `while (p1) p2`) | matches(`s1`, `p1`) && matches(`s2`, `p2`) |
123+
124+
| Match | Result |
125+
| ------------- |:-------------:|
126+
| result(`s`, `hole[T]`) for `s` of type `S` | `s` as an `Expr[T]` |
127+
| result(`T`, `Hole[T]`) | `T` as a `Type[T]` |
128+
| matches(`n: X1`, `n: bindHole[X2]`) | (`n` as a `Binding[X2]`) ++ result(`X1`, `X2`) |
129+
| result(`if (s1) s2 else s3`, `if (p1) p2 else p3`) | result(`s1`, `p1`) ++ result(`s2`, `p2`) ++ result(`s3`,`p3`) |
130+
| result(`while (s1) s2`, `while (p1) p2`) | result(`s1`, `p1`) ++ result(`s2`, `p2`) |

library/src-bootstrapped/scala/quoted/Type.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ sealed abstract class Type[T <: AnyKind] {
1313

1414
/** Some basic type tags, currently incomplete */
1515
object Type {
16-
1716
implicit def UnitTag: Type[Unit] = new TaggedType[Unit]
1817
implicit def BooleanTag: Type[Boolean] = new TaggedType[Boolean]
1918
implicit def ByteTag: Type[Byte] = new TaggedType[Byte]

0 commit comments

Comments
 (0)