Skip to content

Commit 530326f

Browse files
committed
Add TypedOrTest as super type of Typed in reflection
TypeOrTest can match or construct type tests or ascriptions `x: T` in expressions or patterns. Unlike `Typed`, it contains a `Tree` instead of a `Term` which might be one of the patterns trees. Fixes #12222
1 parent 2ea3a87 commit 530326f

File tree

6 files changed

+93
-16
lines changed

6 files changed

+93
-16
lines changed

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

+31-1
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,8 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
345345
case x: (tpd.SeqLiteral & x.type) => Some(x)
346346
case x: (tpd.Inlined & x.type) => Some(x)
347347
case x: (tpd.NamedArg & x.type) => Some(x)
348+
case x: (tpd.Typed & x.type) =>
349+
TypedTypeTest.unapply(x) // Matches `Typed` but not `TypedOrTest`
348350
case _ => if x.isTerm then Some(x) else None
349351
end TermTypeTest
350352

@@ -655,7 +657,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
655657

656658
object TypedTypeTest extends TypeTest[Tree, Typed]:
657659
def unapply(x: Tree): Option[Typed & x.type] = x match
658-
case x: (tpd.Typed & x.type) => Some(x)
660+
case x: (tpd.Typed & x.type) =>
661+
x.expr match
662+
case TermTypeTest(_) => Some(x)
663+
case _ => None
659664
case _ => None
660665
end TypedTypeTest
661666

@@ -675,6 +680,31 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
675680
end extension
676681
end TypedMethods
677682

683+
type TypedOrTest = tpd.Typed
684+
685+
object TypedOrTestTypeTest extends TypeTest[Tree, TypedOrTest]:
686+
def unapply(x: Tree): Option[TypedOrTest & x.type] = x match
687+
case x: (tpd.Typed & x.type) => Some(x)
688+
case _ => None
689+
end TypedOrTestTypeTest
690+
691+
object TypedOrTest extends TypedOrTestModule:
692+
def apply(expr: Term, tpt: TypeTree): Typed =
693+
withDefaultPos(tpd.Typed(xCheckMacroValidExpr(expr), tpt))
694+
def copy(original: Tree)(expr: Term, tpt: TypeTree): Typed =
695+
tpd.cpy.Typed(original)(xCheckMacroValidExpr(expr), tpt)
696+
def unapply(x: Typed): (Term, TypeTree) =
697+
(x.expr, x.tpt)
698+
end TypedOrTest
699+
700+
given TypedOrTestMethods: TypedOrTestMethods with
701+
extension (self: Typed)
702+
def tree: Tree = self.expr
703+
def tpt: TypeTree = self.tpt
704+
end extension
705+
end TypedOrTestMethods
706+
707+
678708
type Assign = tpd.Assign
679709

680710
object AssignTypeTest extends TypeTest[Tree, Assign]:

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

+2
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ object Extractors {
172172
this += "Unapply(" += fun += ", " ++= implicits += ", " ++= patterns += ")"
173173
case Alternatives(patterns) =>
174174
this += "Alternatives(" ++= patterns += ")"
175+
case TypedOrTest(tree, tpt) =>
176+
this += "TypedOrTest(" += tree += ", " += tpt += ")"
175177
}
176178

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

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

+7-3
Original file line numberDiff line numberDiff line change
@@ -928,9 +928,13 @@ object SourceCode {
928928
case Alternatives(trees) =>
929929
inParens(printPatterns(trees, " | "))
930930

931-
case Typed(Ident("_"), tpt) =>
932-
this += "_: "
933-
printTypeTree(tpt)
931+
case TypedOrTest(tree1, tpt) =>
932+
tree1 match
933+
case Ident("_") =>
934+
this += "_: "
935+
printTypeTree(tpt)
936+
case _ =>
937+
printPattern(tree1)
934938

935939
case v: Term =>
936940
printTree(v)

library/src/scala/quoted/Quotes.scala

+49-11
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
133133
* | +- Apply
134134
* | +- TypeApply
135135
* | +- Super
136-
* | +- Typed
137136
* | +- Assign
138137
* | +- Block
139138
* | +- Closure
@@ -146,7 +145,15 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
146145
* | +- Inlined
147146
* | +- SelectOuter
148147
* | +- While
148+
* | +---+- Typed
149+
* | /
150+
* +- TypedOrTest +----------------·
151+
* +- Bind
152+
* +- Unapply
153+
* +- Alternatives
149154
* |
155+
* +- CaseDef
156+
* +- TypeCaseDef
150157
* |
151158
* +- TypeTree ----+- Inferred
152159
* | +- TypeIdent
@@ -164,13 +171,6 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
164171
* |
165172
* +- TypeBoundsTree
166173
* +- WildcardTypeTree
167-
* |
168-
* +- CaseDef
169-
* |
170-
* +- TypeCaseDef
171-
* +- Bind
172-
* +- Unapply
173-
* +- Alternatives
174174
*
175175
* +- ParamClause -+- TypeParamClause
176176
* +- TermParamClause
@@ -1120,8 +1120,12 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
11201120
/** `TypeTest` that allows testing at runtime in a pattern match if a `Tree` is a `Typed` */
11211121
given TypedTypeTest: TypeTest[Tree, Typed]
11221122

1123-
/** Tree representing a type ascription `x: T` in the source code */
1124-
type Typed <: Term
1123+
/** Tree representing a type ascription `x: T` in the source code.
1124+
*
1125+
* Also represents a pattern that contains a term `x`.
1126+
* Other `: T` patterns use the more general `TypedOrTest`.
1127+
*/
1128+
type Typed <: Term & TypedOrTest
11251129

11261130
/** Module object of `type Typed` */
11271131
val Typed: TypedModule
@@ -1568,6 +1572,38 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
15681572
end extension
15691573
end WhileMethods
15701574

1575+
/** `TypeTest` that allows testing at runtime in a pattern match if a `Tree` is a `TypedOrTest` */
1576+
given TypedOrTestTypeTest: TypeTest[Tree, TypedOrTest]
1577+
1578+
/** Tree representing a type ascription or type test pattern `x: T` in the source code. */
1579+
type TypedOrTest <: Tree
1580+
1581+
/** Module object of `type TypedOrTest` */
1582+
val TypedOrTest: TypedOrTestModule
1583+
1584+
/** Methods of the module object `val TypedOrTest` */
1585+
trait TypedOrTestModule { this: TypedOrTest.type =>
1586+
1587+
/** Create a type ascription `<x: Tree>: <tpt: TypeTree>` */
1588+
def apply(expr: Tree, tpt: TypeTree): TypedOrTest
1589+
1590+
def copy(original: Tree)(expr: Tree, tpt: TypeTree): TypedOrTest
1591+
1592+
/** Matches `<expr: Tree>: <tpt: TypeTree>` */
1593+
def unapply(x: TypedOrTest): (Tree, TypeTree)
1594+
}
1595+
1596+
/** Makes extension methods on `TypedOrTest` available without any imports */
1597+
given TypedOrTestMethods: TypedOrTestMethods
1598+
1599+
/** Extension methods of `TypedOrTest` */
1600+
trait TypedOrTestMethods:
1601+
extension (self: TypedOrTest)
1602+
def tree: Tree
1603+
def tpt: TypeTree
1604+
end extension
1605+
end TypedOrTestMethods
1606+
15711607
// ----- TypeTrees ------------------------------------------------
15721608

15731609
/** Type tree representing a type written in the source */
@@ -4406,6 +4442,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
44064442
Unapply.copy(pattern)(transformTerm(pattern.fun)(owner), transformSubTrees(pattern.implicits)(owner), transformTrees(pattern.patterns)(owner))
44074443
case pattern: Alternatives =>
44084444
Alternatives.copy(pattern)(transformTrees(pattern.patterns)(owner))
4445+
case TypedOrTest(inner, tpt) =>
4446+
TypedOrTest.copy(tree)(transformTree(inner)(owner), transformTypeTree(tpt)(owner))
44094447
}
44104448
}
44114449

@@ -4456,7 +4494,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
44564494
case New(tpt) =>
44574495
New.copy(tree)(transformTypeTree(tpt)(owner))
44584496
case Typed(expr, tpt) =>
4459-
Typed.copy(tree)(/*FIXME #12222: transformTerm(expr)(owner)*/transformTree(expr)(owner).asInstanceOf[Term], transformTypeTree(tpt)(owner))
4497+
Typed.copy(tree)(transformTerm(expr)(owner), transformTypeTree(tpt)(owner))
44604498
case tree: NamedArg =>
44614499
NamedArg.copy(tree)(tree.name, transformTerm(tree.value)(owner))
44624500
case Assign(lhs, rhs) =>

project/MiMaFilters.scala

+3
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,8 @@ object MiMaFilters {
1313
exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SourceFileMethods.path"),
1414
exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#UnapplyModule.apply"),
1515
exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#UnapplyModule.apply"),
16+
exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.TypedOrTestTypeTest"),
17+
exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.TypedOrTest"),
18+
exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule.TypedOrTestMethods"),
1619
)
1720
}

tests/run-staging/i5161.check

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
run : Some(2)
22
show : scala.Tuple2.apply[scala.Option[scala.Int], scala.Option[scala.Int]](scala.Some.apply[scala.Int](1), scala.Some.apply[scala.Int](1)) match {
3-
case scala.Tuple2((scala.Some(x): scala.Some[scala.Int]), (scala.Some(y): scala.Some[scala.Int])) =>
3+
case scala.Tuple2(scala.Some(x), scala.Some(y)) =>
44
scala.Some.apply[scala.Int](x.+(y))
55
case _ =>
66
scala.None

0 commit comments

Comments
 (0)