Skip to content

Commit 5cf53bc

Browse files
committed
Add Wildcard in reflection
This allows the creation of wildcard trees `_` in the refection API
1 parent 408e6d2 commit 5cf53bc

File tree

8 files changed

+96
-1
lines changed

8 files changed

+96
-1
lines changed

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

+14
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,20 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
448448
end extension
449449
end IdentMethods
450450

451+
type Wildcard = tpd.Ident
452+
453+
object WildcardTypeTest extends TypeTest[Tree, Wildcard]:
454+
def unapply(x: Tree): Option[Wildcard & x.type] = x match
455+
case x: (tpd.Ident & x.type) if x.name == nme.WILDCARD => Some(x)
456+
case _ => None
457+
end WildcardTypeTest
458+
459+
object Wildcard extends WildcardModule:
460+
def apply(): Wildcard =
461+
withDefaultPos(untpd.Ident(nme.WILDCARD).withType(dotc.core.Symbols.defn.AnyType))
462+
def unapply(pattern: Wildcard): true = true
463+
end Wildcard
464+
451465
type Select = tpd.Select
452466

453467
object SelectTypeTest extends TypeTest[Tree, Select]:

library/src/scala/quoted/Quotes.scala

+18-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
123123
* | | +- DefDef
124124
* | | +- ValDef
125125
* | |
126-
* | +- Term --------+- Ref -+- Ident
126+
* | +- Term --------+- Ref -+- Ident -+- Wildcard
127127
* | | +- Select
128128
* | |
129129
* | +- Literal
@@ -784,6 +784,23 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
784784
end extension
785785
end IdentMethods
786786

787+
/** Pattern representing a `_` wildcard. */
788+
type Wildcard <: Ident
789+
790+
/** `TypeTest` that allows testing at runtime in a pattern match if a `Tree` is a `Wildcard` */
791+
given WildcardTypeTest: TypeTest[Tree, Wildcard]
792+
793+
/** Module object of `type Wildcard` */
794+
val Wildcard: WildcardModule
795+
796+
/** Methods of the module object `val Wildcard` */
797+
trait WildcardModule { this: Wildcard.type =>
798+
/** Create a tree representing a `_` wildcard. */
799+
def apply(): Wildcard
800+
/** Match a tree representing a `_` wildcard. */
801+
def unapply(wildcard: Wildcard): true
802+
}
803+
787804
/** Tree representing a selection of definition with a given name on a given prefix */
788805
type Select <: Ref
789806

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import scala.quoted.*
2+
3+
object MatchTest {
4+
inline def test[T](inline obj: Any): Unit = ${testImpl('obj)}
5+
6+
def testImpl[T](objExpr: Expr[T])(using Quotes): Expr[Unit] = {
7+
import quotes.reflect.*
8+
// test that the extractors work
9+
val Inlined(None, Nil, Block(Nil, Match(param @ Ident("a"), List(CaseDef(Literal(IntConstant(1)), None, Block(Nil, Literal(UnitConstant()))), CaseDef(Wildcard(), None, Block(Nil, Literal(UnitConstant()))))))) = objExpr.asTerm
10+
// test that the constructors work
11+
Block(Nil, Match(param, List(CaseDef(Literal(IntConstant(1)), None, Block(Nil, Literal(UnitConstant()))), CaseDef(Wildcard(), None, Block(Nil, Literal(UnitConstant())))))).asExprOf[Unit]
12+
}
13+
}

tests/pos-macros/i12188b/Test_2.scala

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
def test(a: Int) = MatchTest.test {
2+
a match
3+
case 1 =>
4+
case _ =>
5+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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(Typed(Wildcard(), TypeIdent(defn.IntClass)), None, Block(Nil, Literal(UnitConstant())))))
12+
matchTree.asExprOf[Unit]
13+
}
14+
}

tests/pos-macros/i12188c/Test_2.scala

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
def test(a: Int) = MatchTest.test(a)

tests/run-macros/i12188/Macro_1.scala

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import scala.quoted.*
2+
3+
object MatchTest {
4+
inline def test[T](inline obj: T): String = ${testImpl('obj)}
5+
6+
def testImpl[T](objExpr: Expr[T])(using qctx: Quotes, t: Type[T]): Expr[String] = {
7+
import qctx.reflect.*
8+
9+
val obj = objExpr.asTerm
10+
val cases = obj.tpe.typeSymbol.children.map { child =>
11+
val subtype = TypeIdent(child)
12+
val bind = Symbol.newBind(Symbol.spliceOwner, "c", Flags.EmptyFlags, subtype.tpe)
13+
CaseDef(Bind(bind, Typed(Ref(bind), subtype)), None, Literal(StringConstant(subtype.show)))
14+
} ::: {
15+
CaseDef(Wildcard(), None, Literal(StringConstant("default")))
16+
} :: Nil
17+
val bind = Symbol.newBind(Symbol.spliceOwner, "o", Flags.EmptyFlags, obj.tpe)
18+
val result = Match(obj, cases)
19+
val code = result.show(using Printer.TreeAnsiCode)
20+
// println(code)
21+
result.asExprOf[String]
22+
}
23+
}

tests/run-macros/i12188/Test_2.scala

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
sealed trait P
2+
case class PC1(a: String) extends P
3+
case class PC2(b: Int) extends P
4+
5+
@main def Test =
6+
println(MatchTest.test(PC1("ab"): P))
7+
println(MatchTest.test(PC2(10): P))
8+
println(MatchTest.test(null: P))

0 commit comments

Comments
 (0)