Skip to content

Commit a3c3396

Browse files
Merge pull request #10045 from dotty-staging/quoted-type-pattern
Make type of type hole available in quoted patterns
2 parents 2720af2 + 7bc0173 commit a3c3396

File tree

27 files changed

+101
-97
lines changed

27 files changed

+101
-97
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
160160

161161
/** Is tree a variable pattern? */
162162
def isVarPattern(pat: Tree): Boolean = unsplice(pat) match {
163-
case x: Ident => x.name.isVariableName && !isBackquoted(x)
163+
case x: Ident => x.name.isVarPattern && !isBackquoted(x)
164164
case _ => false
165165
}
166166

compiler/src/dotty/tools/dotc/core/NameKinds.scala

+1
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ object NameKinds {
320320
val PatMatStdBinderName: UniqueNameKind = new UniqueNameKind("x")
321321
val PatMatAltsName: UniqueNameKind = new UniqueNameKind("matchAlts")
322322
val PatMatResultName: UniqueNameKind = new UniqueNameKind("matchResult")
323+
val PatMatVarName: UniqueNameKind = new UniqueNameKind("ev$")
323324

324325
val LocalOptInlineLocalObj: UniqueNameKind = new UniqueNameKind("ilo")
325326

compiler/src/dotty/tools/dotc/core/NameOps.scala

+11-10
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,17 @@ object NameOps {
7979
case name: SimpleName => name.exists(isOperatorPart)
8080
case _ => false
8181

82-
/** Is name a variable name? */
83-
def isVariableName: Boolean = testSimple { n =>
84-
n.length > 0 && {
85-
val first = n.head
86-
(((first.isLower && first.isLetter) || first == '_')
87-
&& (n != false_)
88-
&& (n != true_)
89-
&& (n != null_))
90-
}
91-
}
82+
/** Is name of a variable pattern? */
83+
def isVarPattern: Boolean =
84+
testSimple { n =>
85+
n.length > 0 && {
86+
val first = n.head
87+
(((first.isLower && first.isLetter) || first == '_')
88+
&& (n != false_)
89+
&& (n != true_)
90+
&& (n != null_))
91+
}
92+
} || name.is(PatMatVarName)
9293

9394
def isOpAssignmentName: Boolean = name match {
9495
case raw.NE | raw.LE | raw.GE | EMPTY =>

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -2594,7 +2594,7 @@ object Parsers {
25942594
val givenMod = atSpan(in.skipToken())(Mod.Given())
25952595
atSpan(in.offset) {
25962596
in.token match {
2597-
case IDENTIFIER | USCORE if in.name.isVariableName =>
2597+
case IDENTIFIER | USCORE if in.name.isVarPattern =>
25982598
val name = in.name
25992599
in.nextToken()
26002600
accept(COLON)

compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ object PatternMatcher {
194194
case Typed(_, tpt) if tpt.tpe.isRepeatedParam => true
195195
case Bind(nme.WILDCARD, WildcardPattern()) => true // don't skip when binding an interesting symbol!
196196
case t if isWildcardArg(t) => true
197-
case x: Ident => x.name.isVariableName && !isBackquoted(x)
197+
case x: Ident => x.name.isVarPattern && !isBackquoted(x)
198198
case Alternative(ps) => ps.forall(unapply)
199199
case EmptyTree => true
200200
case _ => false

compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala

+13-11
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import dotty.tools.dotc.core.Constants._
99
import dotty.tools.dotc.core.Contexts._
1010
import dotty.tools.dotc.core.Decorators._
1111
import dotty.tools.dotc.core.Flags._
12-
import dotty.tools.dotc.core.NameKinds.UniqueName
12+
import dotty.tools.dotc.core.NameKinds.{UniqueName, PatMatVarName}
1313
import dotty.tools.dotc.core.Names._
1414
import dotty.tools.dotc.core.StagingContext._
1515
import dotty.tools.dotc.core.StdNames._
@@ -156,19 +156,21 @@ trait QuotesAndSplices {
156156
if ctx.mode.is(Mode.QuotedPattern) && level == 1 then
157157
def spliceOwner(ctx: Context): Symbol =
158158
if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner
159-
val name = tree.expr match {
160-
case Ident(name) => ("$" + name).toTypeName
159+
val (name, expr) = tree.expr match {
160+
case Ident(name) =>
161+
val nameOfSyntheticGiven = PatMatVarName.fresh()
162+
(name.toTypeName, untpd.cpy.Ident(tree.expr)(nameOfSyntheticGiven))
161163
case expr =>
162164
report.error("expected a name binding", expr.srcPos)
163-
"$error".toTypeName
165+
("$error".toTypeName, expr)
164166
}
165167

166168
val typeSymInfo = pt match
167169
case pt: TypeBounds => pt
168170
case _ => TypeBounds.empty
169171
val typeSym = newSymbol(spliceOwner(ctx), name, EmptyFlags, typeSymInfo, NoSymbol, tree.expr.span)
170172
typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuotedPatterns_patternTypeAnnot.typeRef)).withSpan(tree.expr.span)))
171-
val pat = typedPattern(tree.expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))(
173+
val pat = typedPattern(expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))(
172174
using spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx)))
173175
pat.select(tpnme.spliceType)
174176
else
@@ -214,7 +216,7 @@ trait QuotesAndSplices {
214216
def getBinding(sym: Symbol): Bind =
215217
typeBindings.getOrElseUpdate(sym, {
216218
val bindingBounds = sym.info
217-
val bsym = newPatternBoundSymbol(sym.name.toTypeName, bindingBounds, quoted.span)
219+
val bsym = newPatternBoundSymbol(sym.name.toString.stripPrefix("$").toTypeName, bindingBounds, quoted.span)
218220
Bind(bsym, untpd.Ident(nme.WILDCARD).withType(bindingBounds)).withSpan(quoted.span)
219221
})
220222

@@ -263,13 +265,14 @@ trait QuotesAndSplices {
263265
val sym = tree.tpe.dealias.typeSymbol
264266
if sym.exists then
265267
val tdef = TypeDef(sym.asType).withSpan(sym.span)
266-
freshTypeBindingsBuff += transformTypeBindingTypeDef(tdef, freshTypePatBuf)
268+
val nameOfSyntheticGiven = pat.symbol.name.toTermName
269+
freshTypeBindingsBuff += transformTypeBindingTypeDef(nameOfSyntheticGiven, tdef, freshTypePatBuf)
267270
TypeTree(tree.tpe.dealias).withSpan(tree.span)
268271
else
269272
tree
270273
case tdef: TypeDef =>
271274
if tdef.symbol.hasAnnotation(defn.InternalQuotedPatterns_patternTypeAnnot) then
272-
transformTypeBindingTypeDef(tdef, typePatBuf)
275+
transformTypeBindingTypeDef(PatMatVarName.fresh(), tdef, typePatBuf)
273276
else if tdef.symbol.isClass then
274277
val kind = if tdef.symbol.is(Module) then "objects" else "classes"
275278
report.error("Implementation restriction: cannot match " + kind, tree.srcPos)
@@ -305,13 +308,12 @@ trait QuotesAndSplices {
305308
super.transform(tree)
306309
}
307310

308-
private def transformTypeBindingTypeDef(tdef: TypeDef, buff: mutable.Builder[Tree, List[Tree]])(using Context): Tree = {
311+
private def transformTypeBindingTypeDef(nameOfSyntheticGiven: TermName, tdef: TypeDef, buff: mutable.Builder[Tree, List[Tree]])(using Context): Tree = {
309312
if (variance == -1)
310313
tdef.symbol.addAnnotation(Annotation(New(ref(defn.InternalQuotedPatterns_fromAboveAnnot.typeRef)).withSpan(tdef.span)))
311314
val bindingType = getBinding(tdef.symbol).symbol.typeRef
312315
val bindingTypeTpe = AppliedType(defn.QuotedTypeClass.typeRef, bindingType :: Nil)
313-
val bindName = tdef.name.toString.stripPrefix("$").toTermName
314-
val sym = newPatternBoundSymbol(bindName, bindingTypeTpe, tdef.span, flags = ImplicitTerm)(using ctx0)
316+
val sym = newPatternBoundSymbol(nameOfSyntheticGiven, bindingTypeTpe, tdef.span, flags = ImplicitTerm)(using ctx0)
315317
buff += Bind(sym, untpd.Ident(nme.WILDCARD).withType(bindingTypeTpe)).withSpan(tdef.span)
316318
super.transform(tdef)
317319
}

tests/neg-macros/quotedPatterns-5.scala

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import scala.quoted._
22
object Test {
3-
def test(x: quoted.Expr[Int])(using QuoteContext) = x match {
4-
case '{ type $t; poly[$t]($x); 4 } => ??? // error: duplicate pattern variable: $t
5-
case '{ type `$t`; poly[`$t`]($x); 4 } =>
6-
val tt: quoted.Type[_] = t // error
7-
???
3+
def test(x: quoted.Expr[Int])(using QuoteContext): Unit = x match {
4+
case '{ type $T; 4 } => Type[T]
5+
case '{ type $T; poly[$T]($x); 4 } => // error: duplicate pattern variable: T
6+
case '{ type `$T`; poly[`$T`]($x); 4 } =>
7+
Type[T] // error
88
case _ =>
99
}
1010

tests/pos-macros/i6997c.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ inline def mcr(x: => Any): Any = ${mcrImpl('x)}
66

77
def mcrImpl(body: Expr[Any])(using ctx: QuoteContext): Expr[Any] =
88
body match
9-
case '{$x: $t} =>
9+
case '{$x: $T} =>
1010
'{
11-
val tmp: $t = $x
11+
val tmp: T = $x
1212
println(tmp)
1313
tmp
1414
}

tests/pos-macros/i7264.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import scala.quoted._
22
class Foo {
33
def f[T2](t: Type[T2])(using QuoteContext) = t match {
4-
case '[ *:[Int, $t] ] =>
5-
'[ *:[Int, $t] ]
4+
case '[ *:[Int, $T] ] =>
5+
'[ *:[Int, T] ]
66
}
77
}

tests/pos-macros/i7264b.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ import scala.quoted._
22
class Foo {
33
def f[T2: Type](e: Expr[T2])(using QuoteContext) = e match {
44
case '{ $x: *:[Int, $t] } =>
5-
'[ *:[Int, $t] ]
5+
'[ *:[Int, t] ]
66
}
77
}

tests/pos-macros/i7264c.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import scala.quoted._
22
class Foo {
33
def f[T2: Type](e: Expr[T2])(using QuoteContext) = e match {
4-
case '{ $x: $t0 } =>
5-
t0 match
6-
case '[ *:[Int, $t] ] =>
7-
'[ *:[Int, $t] ]
4+
case '{ $x: $T0 } =>
5+
Type[T0] match
6+
case '[ *:[Int, $T] ] =>
7+
'[ *:[Int, T] ]
88
}
99
}

tests/pos-macros/tasty-constant-type/Macro_1.scala

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ object Macro {
44

55
trait AddInt[A <: Int, B <: Int] { type Out <: Int }
66

7-
transparent inline def ff[A <: Int, B <: Int](): AddInt[A, B] = ${ impl('[A], '[B]) }
7+
transparent inline def ff[A <: Int, B <: Int](): AddInt[A, B] = ${ impl[A, B] }
88

9-
def impl[A <: Int : Type, B <: Int : Type](a: Type[A], b: Type[B])(using qctx: QuoteContext) : Expr[AddInt[A, B]] = {
9+
def impl[A <: Int : Type, B <: Int : Type](using qctx: QuoteContext) : Expr[AddInt[A, B]] = {
1010
import qctx.reflect._
1111

12-
val ConstantType(Constant.Int(v1)) = a.unseal.tpe
13-
val ConstantType(Constant.Int(v2)) = b.unseal.tpe
12+
val ConstantType(Constant.Int(v1)) = Type.of[A]
13+
val ConstantType(Constant.Int(v2)) = Type.of[B]
1414

1515
Literal(Constant.Int(v1 + v2)).tpe.seal match
16-
case '[$t] => '{ null: AddInt[$a, $b] { type Out = $t } }
16+
case '[$T] => '{ null: AddInt[A, B] { type Out = T } }
1717
}
1818
}

tests/run-macros/flops-rewrite-3/Macro_1.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class CheckedTransformation(transform: PartialFunction[Expr[Any], Expr[Any]]) ex
5959
def apply[T: Type](e: Expr[T])(using QuoteContext): Expr[T] = {
6060
transform.applyOrElse(e, identity) match {
6161
case '{ $e2: T } => e2
62-
case '{ $e2: $t } =>
62+
case '{ $e2: $T } =>
6363
throw new Exception(
6464
s"""Transformed
6565
|${e.show}
@@ -69,7 +69,7 @@ class CheckedTransformation(transform: PartialFunction[Expr[Any], Expr[Any]]) ex
6969
|Expected type to be
7070
|${summon[Type[T]].show}
7171
|but was
72-
|${t.show}
72+
|${Type[T].show}
7373
""".stripMargin)
7474
}
7575
}

tests/run-macros/flops-rewrite/Macro_1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ private class Rewriter(preTransform: Expr[Any] => Expr[Any], postTransform: Expr
4949
|Expected type to be
5050
|${summon[Type[T]].show}
5151
|but was
52-
|${t.show}
52+
|${Type[t].show}
5353
""".stripMargin)
5454
}
5555
}

tests/run-macros/i7987/Macros_1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ object Macros {
77

88
def macroImpl[T]()(using qctx: QuoteContext): Expr[String] = {
99
Expr.summon[Mirror.Of[Some[Int]]] match
10-
case Some('{ $_ : $t }) => Expr(t.show)
10+
case Some('{ $_ : $T }) => Expr(Type[T].show)
1111
}
1212
}

tests/run-macros/i8007/Macro_1.scala

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import scala.quoted._
44

55
object Macro1 {
66

7-
def mirrorFields[T](t: Type[T])(using qctx: QuoteContext): List[String] =
8-
t match {
9-
case '[$field *: $fields] => field.show :: mirrorFields(fields)
7+
def mirrorFields[T: Type](using qctx: QuoteContext): List[String] =
8+
Type[T] match {
9+
case '[$Field *: $Fields] => Type[Field].show :: mirrorFields[Fields]
1010
case '[EmptyTuple] => Nil
1111
}
1212

@@ -22,8 +22,8 @@ object Macro1 {
2222
val mirrorTpe = '[Mirror.Of[T]]
2323

2424
Expr.summon(using mirrorTpe).get match {
25-
case '{ $m: Mirror.ProductOf[T]{ type MirroredElemLabels = $t } } => {
26-
Expr(mirrorFields(t))
25+
case '{ $m: Mirror.ProductOf[T]{ type MirroredElemLabels = $Elems } } => {
26+
Expr(mirrorFields[Elems])
2727
}
2828
}
2929
}

tests/run-macros/i8007/Macro_2.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import scala.quoted._
44

55
object Macro2 {
66

7-
def mirrorFields[T](t: Type[T])(using qctx: QuoteContext): List[String] =
7+
def mirrorFields[T](using t: Type[T])(using qctx: QuoteContext): List[String] =
88
t match {
9-
case '[$field *: $fields] => field.show.substring(1, field.show.length-1) :: mirrorFields(fields)
9+
case '[$Field *: $Fields] => Type[Field].show.substring(1, Type[Field].show.length-1) :: mirrorFields[Fields]
1010
case '[EmptyTuple] => Nil
1111
}
1212

@@ -24,8 +24,8 @@ object Macro2 {
2424
import qctx.reflect._
2525

2626
val fields = ev match {
27-
case '{ $m: Mirror.ProductOf[T] { type MirroredElemLabels = $t } } =>
28-
mirrorFields(t)
27+
case '{ $m: Mirror.ProductOf[T] { type MirroredElemLabels = $Labels } } =>
28+
mirrorFields[Labels]
2929
}
3030

3131
val body: Expr[T] => Expr[String] = elem =>

tests/run-macros/i8007/Macro_3.scala

+8-8
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ object Eq {
2525
def eqv(x: T, y: T): Boolean = body(x, y)
2626
}
2727

28-
def summonAll[T](t: Type[T])(using qctx: QuoteContext): List[Expr[Eq[_]]] = t match {
29-
case '[String *: $tpes] => '{ summon[Eq[String]] } :: summonAll(tpes)
30-
case '[Int *: $tpes] => '{ summon[Eq[Int]] } :: summonAll(tpes)
31-
case '[$tpe *: $tpes] => derived(using tpe, qctx) :: summonAll(tpes)
28+
def summonAll[T](using t: Type[T])(using qctx: QuoteContext): List[Expr[Eq[_]]] = t match {
29+
case '[String *: $Tpes] => '{ summon[Eq[String]] } :: summonAll[Tpes]
30+
case '[Int *: $Tpes] => '{ summon[Eq[Int]] } :: summonAll[Tpes]
31+
case '[$Tpe *: $Tpes] => derived[Tpe] :: summonAll[Tpes]
3232
case '[EmptyTuple] => Nil
3333
}
3434

@@ -38,8 +38,8 @@ object Eq {
3838
val ev: Expr[Mirror.Of[T]] = Expr.summon(using '[Mirror.Of[T]]).get
3939

4040
ev match {
41-
case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = $elementTypes }} =>
42-
val elemInstances = summonAll(elementTypes)
41+
case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = $ElementTypes }} =>
42+
val elemInstances = summonAll[ElementTypes]
4343
val eqProductBody: (Expr[T], Expr[T]) => Expr[Boolean] = (x, y) => {
4444
elemInstances.zipWithIndex.foldLeft(Expr(true: Boolean)) {
4545
case (acc, (elem, index)) =>
@@ -53,8 +53,8 @@ object Eq {
5353
eqProduct((x: T, y: T) => ${eqProductBody('x, 'y)})
5454
}
5555

56-
case '{ $m: Mirror.SumOf[T] { type MirroredElemTypes = $elementTypes }} =>
57-
val elemInstances = summonAll(elementTypes)
56+
case '{ $m: Mirror.SumOf[T] { type MirroredElemTypes = $ElementTypes }} =>
57+
val elemInstances = summonAll[ElementTypes]
5858
val eqSumBody: (Expr[T], Expr[T]) => Expr[Boolean] = (x, y) => {
5959
val ordx = '{ $m.ordinal($x) }
6060
val ordy = '{ $m.ordinal($y) }

tests/run-macros/quote-matcher-symantics-3/quoted_1.scala

+7-7
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ object Macros {
2020
object FromEnv {
2121
def unapply[T](e: Expr[Any])(using env: Env): Option[Expr[R[T]]] =
2222
e match
23-
case '{envVar[$t](${Const(id)})} =>
23+
case '{envVar[$_](${Const(id)})} =>
2424
env.get(id).asInstanceOf[Option[Expr[R[T]]]] // We can only add binds that have the same type as the refs
2525
case _ =>
2626
None
@@ -39,11 +39,11 @@ object Macros {
3939
case '{ ($x: Int) <= ($y: Int) } =>
4040
'{ $sym.leq(${lift(x)}, ${lift(y)}).asInstanceOf[R[T]] }
4141

42-
case '{ ${f}($arg: $t): $u } =>
43-
'{ $sym.app[$t, $u](${lift(f)}, ${lift(arg)}).asInstanceOf[R[T]] }
42+
case '{ ${f}($arg: $A): $B } =>
43+
'{ $sym.app[A, B](${lift(f)}, ${lift(arg)}).asInstanceOf[R[T]] }
4444

45-
case '{ (if ($cond) $thenp else $elsep): $t } =>
46-
'{ $sym.ifThenElse[$t](${lift(cond)}, ${lift(thenp)}, ${lift(elsep)}) }.asInstanceOf[Expr[R[T]]]
45+
case '{ (if ($cond) $thenp else $elsep): $A } =>
46+
'{ $sym.ifThenElse[A](${lift(cond)}, ${lift(thenp)}, ${lift(elsep)}) }.asInstanceOf[Expr[R[T]]]
4747

4848
case '{ (x0: Int) => $bodyFn(x0): Any } =>
4949
val (i, nEnvVar) = freshEnvVar[Int]()
@@ -60,8 +60,8 @@ object Macros {
6060
val body2 = UnsafeExpr.open(bodyFn) { (body1, close) => close(body1)(nEnvVar) }
6161
'{ $sym.lam((x: R[Int => Int]) => ${given Env = envWith(i, 'x)(using env); lift(body2)}).asInstanceOf[R[T]] }
6262

63-
case '{ Symantics.fix[$t, $u]($f) } =>
64-
'{ $sym.fix[$t, $u]((x: R[$t => $u]) => $sym.app(${lift(f)}, x)).asInstanceOf[R[T]] }
63+
case '{ Symantics.fix[$A, $B]($f) } =>
64+
'{ $sym.fix[A, B]((x: R[A => B]) => $sym.app(${lift(f)}, x)).asInstanceOf[R[T]] }
6565

6666
case FromEnv(expr) => expr.asInstanceOf[Expr[R[T]]]
6767

tests/run-macros/quote-matcher-type-bind/Macro_1.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ object Macros {
77

88
private def impl(x: Expr[Unit])(using QuoteContext): Expr[Unit] = {
99
x match {
10-
case '{ DSL.f[$t]($x) } => '{ DSL.g[$t]($x) }
11-
case '{ DSL.g[$t]($x) } => '{ DSL.f[$t]($x) }
10+
case '{ DSL.f[$T]($x) } => '{ DSL.g[T]($x) }
11+
case '{ DSL.g[$T]($x) } => '{ DSL.f[T]($x) }
1212
case _ => x
1313
}
1414
}

0 commit comments

Comments
 (0)