Skip to content

Commit ccc7b84

Browse files
committed
Introduce Typed abstraction that supports patterns
1 parent fffa86d commit ccc7b84

File tree

16 files changed

+143
-59
lines changed

16 files changed

+143
-59
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ object Matcher {
178178

179179
/* Term hole */
180180
// Match a scala.internal.Quoted.patternHole typed as a repeated argument and return the scrutinee tree
181-
case (scrutinee @ Typed(s: Term, tpt1), Typed(TypeApply(patternHole, tpt :: Nil), tpt2))
181+
case (scrutinee @ Typed(s, tpt1), Typed(TypeApply(patternHole, tpt :: Nil), tpt2))
182182
if patternHole.symbol == patternHoleSymbol &&
183183
s.tpe <:< tpt.tpe &&
184184
tpt2.tpe.derivesFrom(defn.RepeatedParamClass) =>

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

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,11 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
186186

187187
object StatementTypeTest extends TypeTest[Tree, Statement]:
188188
def unapply(x: Tree): Option[Statement & x.type] = x match
189-
case _: tpd.PatternTree => None
190-
case _ =>
191-
TermTypeTest.unapply(x).orElse(DefinitionTypeTest.unapply(x))
189+
case TermTypeTest(x: x.type) => Some(x)
190+
case DefinitionTypeTest(x: x.type) => Some(x)
191+
case ImportTypeTest(x: x.type) => Some(x)
192+
case ExportTypeTest(x: x.type) => Some(x)
193+
case _ => None
192194
end StatementTypeTest
193195

194196
type Definition = tpd.MemberDef
@@ -331,16 +333,20 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
331333

332334
object TermTypeTest extends TypeTest[Tree, Term]:
333335
def unapply(x: Tree): Option[Term & x.type] = x match
334-
// Patterns
335-
case _ if UnapplyTypeTest.unapply(x).isDefined => None
336336
case _: tpd.PatternTree => None
337-
case x: (tpd.Ident & x.type) if x.isTerm && x.name == nme.WILDCARD => None
338-
// Terms
339-
case x: (tpd.Tree & x.type) if x.isTerm => Some(x)
337+
case x: (tpd.Ident & x.type) =>
338+
if x.isTerm && x.name != nme.WILDCARD then Some(x)
339+
else None
340+
case x: (tpd.Typed & x.type) =>
341+
x match
342+
case TypedTypeTest(_) => Some(x)
343+
case _ => None // `TypedTree` but not a `Typed`
340344
case x: (tpd.SeqLiteral & x.type) => Some(x)
341345
case x: (tpd.Inlined & x.type) => Some(x)
342346
case x: (tpd.NamedArg & x.type) => Some(x)
343-
case _ => None
347+
case _ =>
348+
if x.isTerm then Some(x)
349+
else None
344350
end TermTypeTest
345351

346352
object Term extends TermModule:
@@ -650,7 +656,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
650656

651657
object TypedTypeTest extends TypeTest[Tree, Typed]:
652658
def unapply(x: Tree): Option[Typed & x.type] = x match
653-
case x: (tpd.Typed & x.type) => Some(x)
659+
case x: (tpd.Typed & x.type) =>
660+
x.expr match
661+
case TermTypeTest(_) => Some(x)
662+
case _ => None
654663
case _ => None
655664
end TypedTypeTest
656665

@@ -666,7 +675,6 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
666675
given TypedMethods: TypedMethods with
667676
extension (self: Typed)
668677
def expr: Term = self.expr
669-
def tpt: TypeTree = self.tpt
670678
end extension
671679
end TypedMethods
672680

@@ -1416,6 +1424,30 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
14161424
def unapply(pattern: Wildcard): true = true
14171425
end Wildcard
14181426

1427+
type TypedTree = tpd.Typed
1428+
1429+
object TypedTreeTypeTest extends TypeTest[Tree, TypedTree]:
1430+
def unapply(x: Tree): Option[TypedTree & x.type] = x match
1431+
case x: (tpd.Typed & x.type) => Some(x)
1432+
case _ => None
1433+
end TypedTreeTypeTest
1434+
1435+
object TypedTree extends TypedTreeModule:
1436+
def apply(expr: Term, tpt: TypeTree): Typed =
1437+
withDefaultPos(tpd.Typed(xCheckMacroValidExpr(expr), tpt))
1438+
def copy(original: Tree)(expr: Term, tpt: TypeTree): Typed =
1439+
tpd.cpy.Typed(original)(xCheckMacroValidExpr(expr), tpt)
1440+
def unapply(x: Typed): (Term, TypeTree) =
1441+
(x.expr, x.tpt)
1442+
end TypedTree
1443+
1444+
given TypedTreeMethods: TypedTreeMethods with
1445+
extension (self: Typed)
1446+
def tree: Tree = self.expr
1447+
def tpt: TypeTree = self.tpt
1448+
end extension
1449+
end TypedTreeMethods
1450+
14191451
type Bind = tpd.Bind
14201452

14211453
object BindTypeTest extends TypeTest[Tree, Bind]:
@@ -1440,13 +1472,12 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
14401472
end extension
14411473
end BindMethods
14421474

1443-
type Unapply = tpd.UnApply | tpd.Typed // tpd.Typed containing a tpd.UnApply as expression
1475+
type Unapply = tpd.UnApply
14441476

14451477
object UnapplyTypeTest extends TypeTest[Tree, Unapply]:
14461478
def unapply(x: Tree): Option[Unapply & x.type] =
1447-
x match // keep in sync with UnapplyMethodsImpl.selfUnApply
1479+
x match
14481480
case x: (tpd.UnApply & x.type) => Some(x)
1449-
case x: (tpd.Typed & x.type) if x.expr.isInstanceOf[tpd.UnApply] => Some(x)
14501481
case _ => None
14511482
end UnapplyTypeTest
14521483

@@ -1459,14 +1490,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
14591490

14601491
given UnapplyMethods: UnapplyMethods with
14611492
extension (self: Unapply)
1462-
def fun: Term = selfUnApply(self).fun
1463-
def implicits: List[Term] = selfUnApply(self).implicits
1464-
def patterns: List[Tree] = effectivePatterns(selfUnApply(self).patterns)
1465-
end extension
1466-
private def selfUnApply(self: Unapply): tpd.UnApply =
1467-
self match // keep in sync with UnapplyTypeTest
1468-
case self: tpd.UnApply => self
1469-
case self: tpd.Typed => self.expr.asInstanceOf[tpd.UnApply]
1493+
def fun: Term = self.fun
1494+
def implicits: List[Term] = self.implicits
1495+
def patterns: List[Tree] = effectivePatterns(self.patterns)
1496+
end extension
14701497
private def effectivePatterns(patterns: List[Tree]): List[Tree] =
14711498
patterns match
14721499
case patterns0 :+ dotc.ast.Trees.SeqLiteral(elems, _) => patterns0 ::: elems

compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ object Extractors {
173173
this += "Unapply(" += fun += ", " ++= implicits += ", " ++= patterns += ")"
174174
case Alternatives(patterns) =>
175175
this += "Alternatives(" ++= patterns += ")"
176+
case TypedTree(tree, tpt) =>
177+
this += "TypedTree(" += tree += ", " += tpt += ")"
176178
}
177179

178180
def visitConstant(x: Constant): this.type = x match {

compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,15 @@ object SourceCode {
453453
printTypeOrAnnots(tpt.tpe)
454454
}
455455
}
456+
case TypedTree(tree1, tpt) =>
457+
printPattern(tree1)
458+
tree1 match
459+
case Wildcard() =>
460+
this += ":"
461+
printType(tpt.tpe)
462+
case _ => // Alternatives, Unapply, Bind
463+
this
464+
456465

457466
case Assign(lhs, rhs) =>
458467
printTree(lhs)
@@ -925,9 +934,13 @@ object SourceCode {
925934
case Alternatives(trees) =>
926935
inParens(printPatterns(trees, " | "))
927936

928-
case Typed(Wildcard(), tpt) =>
929-
this += "_: "
930-
printTypeTree(tpt)
937+
case TypedTree(tree1, tpt) =>
938+
tree1 match
939+
case Wildcard() =>
940+
this += "_: "
941+
printTypeTree(tpt)
942+
case _ =>
943+
printPattern(tree1)
931944

932945
case v: Term =>
933946
printTree(v)

library/src/scala/quoted/ExprMap.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ trait ExprMap:
5656
tree
5757
case New(tpt) =>
5858
New.copy(tree)(transformTypeTree(tpt)(owner))
59-
case Typed(expr: Term, tpt) =>
59+
case Typed(expr, tpt) =>
6060
val tp = tpt.tpe match
6161
case AppliedType(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "<repeated>"), List(tp0: TypeRepr)) =>
6262
TypeRepr.of[Seq].appliedTo(tp0)

library/src/scala/quoted/FromExpr.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ object FromExpr {
8686
def rec(tree: Term): Option[T] = tree match {
8787
case Block(stats, e) => if stats.isEmpty then rec(e) else None
8888
case Inlined(_, bindings, e) => if bindings.isEmpty then rec(e) else None
89-
case Typed(e: Term, _) => rec(e)
89+
case Typed(e, _) => rec(e)
9090
case _ =>
9191
tree.tpe.widenTermRefByName match
9292
case ConstantType(c) => Some(c.value.asInstanceOf[T])

library/src/scala/quoted/Quotes.scala

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
122122
* | +- Apply
123123
* | +- TypeApply
124124
* | +- Super
125-
* | +- Typed
126125
* | +- Assign
127126
* | +- Block
128127
* | +- Closure
@@ -135,7 +134,16 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
135134
* | +- Inlined
136135
* | +- SelectOuter
137136
* | +- While
137+
* | +---+- Typed
138+
* | /
139+
* +- TypedTree +------------------·
140+
* +- Wildcard
141+
* +- Bind
142+
* +- Unapply
143+
* +- Alternatives
138144
* |
145+
* +- CaseDef
146+
* +- TypeCaseDef
139147
* |
140148
* +- TypeTree ----+- Inferred
141149
* | +- TypeIdent
@@ -153,14 +161,6 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
153161
* |
154162
* +- TypeBoundsTree
155163
* +- WildcardTypeTree
156-
* |
157-
* +- CaseDef
158-
* |
159-
* +- TypeCaseDef
160-
* +- Wildcard
161-
* +- Bind
162-
* +- Unapply
163-
* +- Alternatives
164164
*
165165
* +- ParamClause -+- TypeParamClause
166166
* +- TermParamClause
@@ -1092,22 +1092,26 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
10921092
/** `TypeTest` that allows testing at runtime in a pattern match if a `Tree` is a `Typed` */
10931093
given TypedTypeTest: TypeTest[Tree, Typed]
10941094

1095-
/** Tree representing a type ascription `x: T` in the source code */
1096-
type Typed <: Term
1095+
/** Tree representing a type ascription `x: T` in the source code.
1096+
*
1097+
* Also represents a pattern that contains a term `x`.
1098+
* Other `: T` patterns use the more general `TypeTree`.
1099+
*/
1100+
type Typed <: Term & TypeTree
10971101

10981102
/** Module object of `type Typed` */
10991103
val Typed: TypedModule
11001104

11011105
/** Methods of the module object `val Typed` */
11021106
trait TypedModule { this: Typed.type =>
11031107

1104-
/** Create a type ascription `<x: Tree>: <tpt: TypeTree>` */
1105-
def apply(expr: Tree, tpt: TypeTree): Typed
1108+
/** Create a type ascription `<x: Term>: <tpt: TypeTree>` */
1109+
def apply(expr: Term, tpt: TypeTree): Typed
11061110

1107-
def copy(original: Tree)(expr: Tree, tpt: TypeTree): Typed
1111+
def copy(original: Tree)(expr: Term, tpt: TypeTree): Typed
11081112

1109-
/** Matches `<expr: Tree>: <tpt: TypeTree>` */
1110-
def unapply(x: Typed): (Tree, TypeTree)
1113+
/** Matches `<expr: Term>: <tpt: TypeTree>` */
1114+
def unapply(x: Typed): (Term, TypeTree)
11111115
}
11121116

11131117
/** Makes extension methods on `Typed` available without any imports */
@@ -1116,8 +1120,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
11161120
/** Extension methods of `Typed` */
11171121
trait TypedMethods:
11181122
extension (self: Typed)
1119-
def expr: Tree
1120-
def tpt: TypeTree
1123+
def expr: Term
11211124
end extension
11221125
end TypedMethods
11231126

@@ -2036,6 +2039,41 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
20362039
def unapply(pattern: Wildcard): true
20372040
}
20382041

2042+
/** `TypeTest` that allows testing at runtime in a pattern match if a `Tree` is a `TypedTree` */
2043+
given TypedTreeTypeTest: TypeTest[Tree, TypedTree]
2044+
2045+
/** Tree representing a type ascription or pattern `x: T` in the source code
2046+
*
2047+
* The tree `x` may contain a `Constant`, `Ref`, `Wildcard`, `Bind`, `Unapply` or `Alternatives`.
2048+
*/
2049+
type TypedTree <: Term
2050+
2051+
/** Module object of `type TypedTree` */
2052+
val TypedTree: TypedTreeModule
2053+
2054+
/** Methods of the module object `val TypedTree` */
2055+
trait TypedTreeModule { this: TypedTree.type =>
2056+
2057+
/** Create a type ascription `<x: Tree>: <tpt: TypeTree>` */
2058+
def apply(expr: Tree, tpt: TypeTree): TypedTree
2059+
2060+
def copy(original: Tree)(expr: Tree, tpt: TypeTree): TypedTree
2061+
2062+
/** Matches `<expr: Tree>: <tpt: TypeTree>` */
2063+
def unapply(x: TypedTree): (Tree, TypeTree)
2064+
}
2065+
2066+
/** Makes extension methods on `TypedTree` available without any imports */
2067+
given TypedTreeMethods: TypedTreeMethods
2068+
2069+
/** Extension methods of `TypedTree` */
2070+
trait TypedTreeMethods:
2071+
extension (self: TypedTree)
2072+
def tree: Tree
2073+
def tpt: TypeTree
2074+
end extension
2075+
end TypedTreeMethods
2076+
20392077
/** Pattern representing a `_ @ _` binding. */
20402078
type Bind <: Tree
20412079

@@ -4263,6 +4301,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
42634301
case Bind(_, body) => foldTree(x, body)(owner)
42644302
case Unapply(fun, implicits, patterns) => foldTrees(foldTrees(foldTree(x, fun)(owner), implicits)(owner), patterns)(owner)
42654303
case Alternatives(patterns) => foldTrees(x, patterns)(owner)
4304+
case TypedTree(tree1, tpt) => foldTree(foldTree(x, tree1)(owner), tpt)(owner)
4305+
42664306
}
42674307
}
42684308
end TreeAccumulator
@@ -4327,6 +4367,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
43274367
Unapply.copy(pattern)(transformTerm(pattern.fun)(owner), transformSubTrees(pattern.implicits)(owner), transformTrees(pattern.patterns)(owner))
43284368
case pattern: Alternatives =>
43294369
Alternatives.copy(pattern)(transformTrees(pattern.patterns)(owner))
4370+
case TypedTree(expr, tpt) =>
4371+
TypedTree.copy(tree)(transformTree(expr)(owner), transformTypeTree(tpt)(owner))
43304372
}
43314373
}
43324374

@@ -4377,7 +4419,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
43774419
case New(tpt) =>
43784420
New.copy(tree)(transformTypeTree(tpt)(owner))
43794421
case Typed(expr, tpt) =>
4380-
Typed.copy(tree)(transformTree(expr)(owner), transformTypeTree(tpt)(owner))
4422+
Typed.copy(tree)(transformTerm(expr)(owner), transformTypeTree(tpt)(owner))
43814423
case tree: NamedArg =>
43824424
NamedArg.copy(tree)(tree.name, transformTerm(tree.value)(owner))
43834425
case Assign(lhs, rhs) =>

library/src/scala/quoted/Varargs.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ object Varargs {
4242
import quotes.reflect._
4343
def rec(tree: Term): Option[Seq[Expr[T]]] = tree match {
4444
case Repeated(elems, _) => Some(elems.map(x => x.asExpr.asInstanceOf[Expr[T]]))
45-
case Typed(e: Term, _) => rec(e)
45+
case Typed(e, _) => rec(e)
4646
case Block(Nil, e) => rec(e)
4747
case Inlined(_, Nil, e) => rec(e)
4848
case _ => None

tests/neg-macros/i11483/Macro_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ object X {
5151
)
5252
case Apply(f,List()) =>
5353
Apply(TypeApply(Select.unique(m,"pure"),List(Inferred(t.tpe.widen))),List(t))
54-
case Typed(x: Term,tp) => Typed(processTree(x,m), Inferred(TypeRepr.of[F].appliedTo(tp.tpe)) )
54+
case Typed(x,tp) => Typed(processTree(x,m), Inferred(TypeRepr.of[F].appliedTo(tp.tpe)) )
5555
case _ => throw new RuntimeException(s"tree not recoginized: $t")
5656
r
5757

tests/pos-macros/i10910/Macro_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ object X:
4848
case Inlined(x,bindings,body) => transform(body) match
4949
case Left(unchanged) => Left(term)
5050
case Right(changed) => Right(Inlined(x,bindings,changed))
51-
case Typed(arg: Term,tp) => transform(arg)
51+
case Typed(arg,tp) => transform(arg)
5252
case Ident(x) => Left(term)
5353
case l@Literal(x) => Left(l)
5454
case other =>

tests/pos-macros/i11401/X_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ object X:
5353
val nFun = processLambda[T](fun)
5454
Apply(Apply(TypeApply(Select.unique(x,"fold_async"),targs),List(state)),List(nFun))
5555
case Apply(TypeApply(Ident("await"),targs),List(body)) => body
56-
case Typed(x: Term,tp) => Typed(processTree(x), Inferred(TypeRepr.of[Future].appliedTo(tp.tpe)) )
56+
case Typed(x,tp) => Typed(processTree(x), Inferred(TypeRepr.of[Future].appliedTo(tp.tpe)) )
5757
case _ => throw new RuntimeException(s"tree not recoginized: $t")
5858
val checker = new TreeMap() {}
5959
checker.transformTerm(r)(Symbol.spliceOwner)

tests/pos-macros/i12188c/Macro_1.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ object MatchTest {
88
val matchTree = Match(a.asTerm, List(
99
CaseDef(Literal(IntConstant(1)), None, Block(Nil, Literal(UnitConstant()))),
1010
CaseDef(Alternatives(List(Literal(IntConstant(2)), Literal(IntConstant(3)), Literal(IntConstant(4)))), None, Block(Nil, Literal(UnitConstant()))),
11-
CaseDef(Typed(Alternatives(List(Literal(IntConstant(4)), Literal(IntConstant(5)))), TypeIdent(defn.IntClass)), None, Block(Nil, Literal(UnitConstant()))),
12-
CaseDef(Typed(Wildcard(), TypeIdent(defn.IntClass)), None, Block(Nil, Literal(UnitConstant())))))
11+
CaseDef(TypedTree(Alternatives(List(Literal(IntConstant(4)), Literal(IntConstant(5)))), TypeIdent(defn.IntClass)), None, Block(Nil, Literal(UnitConstant()))),
12+
CaseDef(TypedTree(Wildcard(), TypeIdent(defn.IntClass)), None, Block(Nil, Literal(UnitConstant())))))
1313
matchTree.asExprOf[Unit]
1414
}
1515
}

0 commit comments

Comments
 (0)