Skip to content

Commit 7529485

Browse files
committed
Fix Typed abstraction
The typed expression can contain non-Term trees: Wildcard, Alternatives, Bind and Unapply. The solution is to add a TypedTree supertype of Typed that contains a Tree.
1 parent fa80ce1 commit 7529485

File tree

12 files changed

+145
-35
lines changed

12 files changed

+145
-35
lines changed

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

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,11 +333,19 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
333333

334334
object TermTypeTest extends TypeTest[Tree, Term]:
335335
def unapply(x: Tree): Option[Term & x.type] = x match
336-
case x: tpd.PatternTree => None
336+
case x: (tpd.Ident & x.type) =>
337+
if x.isTerm && x.name != nme.WILDCARD then Some(x)
338+
else None
339+
case x: (tpd.Typed & x.type) =>
340+
// Matches `Typed` but not `TypedTree`
341+
TypedTypeTest.unapply(x)
337342
case x: (tpd.SeqLiteral & x.type) => Some(x)
338343
case x: (tpd.Inlined & x.type) => Some(x)
339344
case x: (tpd.NamedArg & x.type) => Some(x)
340-
case _ => if x.isTerm then Some(x) else None
345+
case x: tpd.PatternTree => None
346+
case _ =>
347+
if x.isTerm then Some(x)
348+
else None
341349
end TermTypeTest
342350

343351
object Term extends TermModule:
@@ -647,7 +655,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
647655

648656
object TypedTypeTest extends TypeTest[Tree, Typed]:
649657
def unapply(x: Tree): Option[Typed & x.type] = x match
650-
case x: (tpd.Typed & x.type) => Some(x)
658+
case x: (tpd.Typed & x.type) =>
659+
x.expr match
660+
case TermTypeTest(_) => Some(x)
661+
case _ => None
651662
case _ => None
652663
end TypedTypeTest
653664

@@ -1413,6 +1424,30 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
14131424
def unapply(pattern: Wildcard): true = true
14141425
end Wildcard
14151426

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+
14161451
type Bind = tpd.Bind
14171452

14181453
object BindTypeTest extends TypeTest[Tree, Bind]:

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: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ object SourceCode {
428428
inSquare(this += id)
429429
this
430430

431-
case Typed(term, tpt) =>
431+
case Typed(term: Term, tpt) =>
432432
tpt.tpe match {
433433
case Types.Repeated(_) =>
434434
printTree(term)
@@ -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)
@@ -928,9 +937,13 @@ object SourceCode {
928937
case Alternatives(trees) =>
929938
inParens(printPatterns(trees, " | "))
930939

931-
case Typed(Wildcard(), tpt) =>
932-
this += "_: "
933-
printTypeTree(tpt)
940+
case TypedTree(tree1, tpt) =>
941+
tree1 match
942+
case Wildcard() =>
943+
this += "_: "
944+
printTypeTree(tpt)
945+
case _ =>
946+
printPattern(tree1)
934947

935948
case v: Term =>
936949
printTree(v)

library/src/scala/quoted/Quotes.scala

Lines changed: 55 additions & 12 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
@@ -1094,8 +1094,12 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
10941094
/** `TypeTest` that allows testing at runtime in a pattern match if a `Tree` is a `Typed` */
10951095
given TypedTypeTest: TypeTest[Tree, Typed]
10961096

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

11001104
/** Module object of `type Typed` */
11011105
val Typed: TypedModule
@@ -2038,6 +2042,41 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
20382042
def unapply(pattern: Wildcard): true
20392043
}
20402044

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

@@ -4265,6 +4304,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
42654304
case Bind(_, body) => foldTree(x, body)(owner)
42664305
case Unapply(fun, implicits, patterns) => foldTrees(foldTrees(foldTree(x, fun)(owner), implicits)(owner), patterns)(owner)
42674306
case Alternatives(patterns) => foldTrees(x, patterns)(owner)
4307+
case TypedTree(tree1, tpt) => foldTree(foldTree(x, tree1)(owner), tpt)(owner)
4308+
42684309
}
42694310
}
42704311
end TreeAccumulator
@@ -4329,6 +4370,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
43294370
Unapply.copy(pattern)(transformTerm(pattern.fun)(owner), transformSubTrees(pattern.implicits)(owner), transformTrees(pattern.patterns)(owner))
43304371
case pattern: Alternatives =>
43314372
Alternatives.copy(pattern)(transformTrees(pattern.patterns)(owner))
4373+
case TypedTree(expr, tpt) =>
4374+
TypedTree.copy(tree)(transformTree(expr)(owner), transformTypeTree(tpt)(owner))
43324375
}
43334376
}
43344377

@@ -4379,7 +4422,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
43794422
case New(tpt) =>
43804423
New.copy(tree)(transformTypeTree(tpt)(owner))
43814424
case Typed(expr, tpt) =>
4382-
Typed.copy(tree)(/*FIXME #12222: transformTerm(expr)(owner)*/transformTree(expr)(owner).asInstanceOf[Term], transformTypeTree(tpt)(owner))
4425+
Typed.copy(tree)(transformTerm(expr)(owner), transformTypeTree(tpt)(owner))
43834426
case tree: NamedArg =>
43844427
NamedArg.copy(tree)(tree.name, transformTerm(tree.value)(owner))
43854428
case Assign(lhs, rhs) =>

scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ trait BasicSupport:
1616
val dri = annotTerm.tpe.typeSymbol.dri
1717
def inner(t: Term): List[Annotation.AnnotationParameter] = t match {
1818
case i: Ident => List(Annotation.LinkParameter(None, i.tpe.typeSymbol.dri, i.name))
19-
case Typed(term, tpeTree) => inner(term)
19+
case Typed(term: Term, tpeTree) => inner(term)
2020
case SeqLiteral(args, tpeTree) => args.map(_.asInstanceOf[Term]).flatMap(inner)
2121
case Literal(constant) => List(Annotation.PrimitiveParameter(None, constant.show))
2222
case NamedArg(name, Literal(constant)) => List(Annotation.PrimitiveParameter(Some(name), constant.show))

tests/pos-macros/i11401/X_1.scala

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class SLSelect[S]:
1616

1717
def fold[S](s0:S)(step: (S,SLSelect[S])=> S): S = {
1818
???
19-
}
19+
}
2020

2121
def fold_async[S](s0:S)(step: (S,SLSelect[S])=> Future[S]): Future[S] = {
2222
???
@@ -27,7 +27,7 @@ class SLSelect[S]:
2727
await(s0.onRead(ch)(f).runAsync())
2828

2929
def runAsync(): Future[S] = ???
30-
30+
3131

3232

3333
object X:
@@ -36,30 +36,30 @@ object X:
3636
processImpl[T]('f)
3737
}
3838

39-
def processImpl[T:Type](t:Expr[T])(using Quotes):Expr[Future[T]] =
39+
def processImpl[T:Type](t:Expr[T])(using Quotes):Expr[Future[T]] =
4040
import quotes.reflect._
4141
val r = processTree[T](t.asTerm)
4242
r.asExprOf[Future[T]]
4343

44-
45-
def processTree[T:Type](using Quotes)(t: quotes.reflect.Term):quotes.reflect.Term =
44+
45+
def processTree[T:Type](using Quotes)(t: quotes.reflect.Term):quotes.reflect.Term =
4646
import quotes.reflect._
4747
val r: Term = t match
4848
case Inlined(_,List(),body) => processTree(body)
49-
case Inlined(d,bindings,body) =>
49+
case Inlined(d,bindings,body) =>
5050
Inlined(d,bindings,processTree[T](body))
5151
case Block(stats,expr) => Block(stats,processTree(expr))
5252
case Apply(Apply(TypeApply(Select(x,"fold"),targs),List(state)),List(fun)) =>
53-
val nFun = processLambda[T](fun)
53+
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
5656
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)
6060
r
61-
62-
def processLambda[T:Type](using Quotes)(fun: quotes.reflect.Term):quotes.reflect.Term =
61+
62+
def processLambda[T:Type](using Quotes)(fun: quotes.reflect.Term):quotes.reflect.Term =
6363
import quotes.reflect._
6464

6565
def changeArgs(oldArgs:List[Tree], newArgs:List[Tree], body:Term, owner: Symbol):Term =

tests/pos-macros/i12188b/Macro_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ object MatchTest {
1010
// test that the constructors work
1111
Block(Nil, Match(param, List(CaseDef(Literal(IntConstant(1)), None, Block(Nil, Literal(UnitConstant()))), CaseDef(Wildcard(), None, Block(Nil, Literal(UnitConstant())))))).asExprOf[Unit]
1212
}
13-
}
13+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import scala.quoted.*
2+
3+
object MatchTest {
4+
inline def test(a: Int): Unit = ${testImpl('a)}
5+
6+
def testImpl(a: Expr[Any])(using Quotes): Expr[Unit] = {
7+
import quotes.reflect.*
8+
val matchTree = Match(a.asTerm, List(
9+
CaseDef(Literal(IntConstant(1)), None, Block(Nil, Literal(UnitConstant()))),
10+
CaseDef(Alternatives(List(Literal(IntConstant(2)), Literal(IntConstant(3)), Literal(IntConstant(4)))), 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())))))
13+
matchTree.asExprOf[Unit]
14+
}
15+
}

tests/pos-macros/i12188c/Test_2.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
def test(a: Int) = MatchTest.test(a)

tests/run-macros/tasty-extractors-1.check

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,16 @@ TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")
4343
Inlined(None, Nil, Match(Literal(StringConstant("b")), List(CaseDef(Bind("n", Wildcard()), None, Block(Nil, Literal(UnitConstant()))))))
4444
TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")
4545

46-
Inlined(None, Nil, Match(Literal(StringConstant("c")), List(CaseDef(Bind("n", Typed(Wildcard(), TypeIdent("String"))), None, Block(Nil, Literal(UnitConstant()))))))
46+
Inlined(None, Nil, Match(Literal(StringConstant("c")), List(CaseDef(Bind("n", TypedTree(Wildcard(), TypeIdent("String"))), None, Block(Nil, Literal(UnitConstant()))))))
4747
TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")
4848

4949
Inlined(None, Nil, Match(Literal(StringConstant("e")), List(CaseDef(Wildcard(), None, Block(Nil, Literal(UnitConstant()))))))
5050
TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")
5151

52-
Inlined(None, Nil, Match(Literal(StringConstant("f")), List(CaseDef(Typed(Wildcard(), TypeIdent("String")), None, Block(Nil, Literal(UnitConstant()))))))
52+
Inlined(None, Nil, Match(Literal(StringConstant("f")), List(CaseDef(TypedTree(Wildcard(), TypeIdent("String")), None, Block(Nil, Literal(UnitConstant()))))))
5353
TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")
5454

55-
Inlined(None, Nil, Match(Typed(Literal(StringConstant("g")), TypeIdent("Any")), List(CaseDef(Alternatives(List(Typed(Wildcard(), TypeIdent("String")), Typed(Wildcard(), TypeIdent("Int")))), None, Block(Nil, Literal(UnitConstant()))))))
55+
Inlined(None, Nil, Match(Typed(Literal(StringConstant("g")), TypeIdent("Any")), List(CaseDef(Alternatives(List(TypedTree(Wildcard(), TypeIdent("String")), TypedTree(Wildcard(), TypeIdent("Int")))), None, Block(Nil, Literal(UnitConstant()))))))
5656
TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")
5757

5858
Inlined(None, Nil, Match(Literal(StringConstant("h")), List(CaseDef(Wildcard(), Some(Literal(BooleanConstant(false))), Block(Nil, Literal(UnitConstant()))))))

0 commit comments

Comments
 (0)