Skip to content

Commit 3e30786

Browse files
Merge pull request scala#10125 from dotty-staging/fix-#10050
Fix scala#10050: Use lowercase pattern variable syntax for quoted patterns
2 parents b25b7cf + 2899e96 commit 3e30786

File tree

39 files changed

+212
-170
lines changed

39 files changed

+212
-170
lines changed

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -908,21 +908,26 @@ object desugar {
908908

909909
/** Transforms
910910
*
911-
* <mods> type $T >: Low <: Hi
912-
*
911+
* <mods> type t >: Low <: Hi
913912
* to
914913
*
915914
* @patternType <mods> type $T >: Low <: Hi
916915
*
917-
* if the type is a type splice.
916+
* if the type has a pattern variable name
918917
*/
919918
def quotedPatternTypeDef(tree: TypeDef)(using Context): TypeDef = {
920919
assert(ctx.mode.is(Mode.QuotedPattern))
921-
if (tree.name.startsWith("$") && !tree.isBackquoted) {
920+
if tree.name.isVarPattern && !tree.isBackquoted then
922921
val patternTypeAnnot = New(ref(defn.InternalQuotedPatterns_patternTypeAnnot.typeRef)).withSpan(tree.span)
923922
val mods = tree.mods.withAddedAnnotation(patternTypeAnnot)
924923
tree.withMods(mods)
925-
}
924+
else if tree.name.startsWith("$") && !tree.isBackquoted then
925+
report.error(
926+
"""Quoted pattern variable names starting with $ are not suported anymore.
927+
|Use lower cases type pattern name instead.
928+
|""".stripMargin,
929+
tree.srcPos)
930+
tree
926931
else tree
927932
}
928933

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +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$")
323+
val PatMatGivenVarName: UniqueNameKind = new UniqueNameKind("$given")
324324

325325
val LocalOptInlineLocalObj: UniqueNameKind = new UniqueNameKind("ilo")
326326

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ object NameOps {
8989
&& (n != true_)
9090
&& (n != null_))
9191
}
92-
} || name.is(PatMatVarName)
92+
} || name.is(PatMatGivenVarName)
9393

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

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

Lines changed: 46 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import dotty.tools.dotc.core.Constants._
1111
import dotty.tools.dotc.core.Contexts._
1212
import dotty.tools.dotc.core.Decorators._
1313
import dotty.tools.dotc.core.Flags._
14-
import dotty.tools.dotc.core.NameKinds.{UniqueName, PatMatVarName}
14+
import dotty.tools.dotc.core.NameKinds.{UniqueName, PatMatGivenVarName}
1515
import dotty.tools.dotc.core.Names._
1616
import dotty.tools.dotc.core.StagingContext._
1717
import dotty.tools.dotc.core.StdNames._
@@ -37,7 +37,7 @@ trait QuotesAndSplices {
3737

3838
import tpd._
3939

40-
/** Translate `'{ t }` into `scala.quoted.Expr.apply(t)` and `'[T]` into `scala.quoted.Type.apply[T]`
40+
/** Translate `'{ e }` into `scala.quoted.Expr.apply(e)` and `'[T]` into `scala.quoted.Type.apply[T]`
4141
* while tracking the quotation level in the context.
4242
*/
4343
def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = {
@@ -83,7 +83,7 @@ trait QuotesAndSplices {
8383
def spliceOwner(ctx: Context): Symbol =
8484
if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner
8585
val pat = typedPattern(tree.expr, defn.QuotedExprClass.typeRef.appliedTo(pt))(
86-
using spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx)))
86+
using spliceContext.retractMode(Mode.QuotedPattern).addMode(Mode.Pattern).withOwner(spliceOwner(ctx)))
8787
val baseType = pat.tpe.baseType(defn.QuotedExprClass)
8888
val argType = if baseType != NoType then baseType.argTypesHi.head else defn.NothingType
8989
ref(defn.InternalQuoted_exprSplice).appliedToType(argType).appliedTo(pat)
@@ -158,25 +158,11 @@ trait QuotesAndSplices {
158158
}
159159

160160
if ctx.mode.is(Mode.QuotedPattern) && level == 1 then
161-
def spliceOwner(ctx: Context): Symbol =
162-
if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner
163-
val (name, expr) = tree.expr match {
164-
case Ident(name) =>
165-
val nameOfSyntheticGiven = PatMatVarName.fresh()
166-
(name.toTypeName, untpd.cpy.Ident(tree.expr)(nameOfSyntheticGiven))
167-
case expr =>
168-
report.error("expected a name binding", expr.srcPos)
169-
("$error".toTypeName, expr)
170-
}
171-
172-
val typeSymInfo = pt match
173-
case pt: TypeBounds => pt
174-
case _ => TypeBounds.empty
175-
val typeSym = newSymbol(spliceOwner(ctx), name, EmptyFlags, typeSymInfo, NoSymbol, tree.expr.span)
176-
typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuotedPatterns_patternTypeAnnot.typeRef)).withSpan(tree.expr.span)))
177-
val pat = typedPattern(expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))(
178-
using spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx)))
179-
pat.select(tpnme.Underlying)
161+
report.error(
162+
"""`$` for quote pattern varable is not supported anymore.
163+
|Use lower cased variable name without the `$` instead.""".stripMargin,
164+
tree.srcPos)
165+
ref(defn.NothingType)
180166
else
181167
val tree1 = typedSelect(untpd.Select(tree.expr, tpnme.Underlying), pt)(using spliceContext).withSpan(tree.span)
182168
val msg = em"Consider using canonical type reference ${tree1.tpe} instead"
@@ -185,6 +171,24 @@ trait QuotesAndSplices {
185171
tree1
186172
}
187173

174+
/** Type a pattern variable name `t` in quote pattern as `${given t$giveni: Type[t @ _]}`.
175+
* The resulting pattern is the split in `splitQuotePattern`.
176+
*/
177+
def typedQuotedTypeVar(tree: untpd.Ident, pt: Type)(using Context): Tree =
178+
def spliceOwner(ctx: Context): Symbol =
179+
if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner
180+
val name = tree.name.toTypeName
181+
val nameOfSyntheticGiven = PatMatGivenVarName.fresh(tree.name.toTermName)
182+
val expr = untpd.cpy.Ident(tree)(nameOfSyntheticGiven)
183+
val typeSymInfo = pt match
184+
case pt: TypeBounds => pt
185+
case _ => TypeBounds.empty
186+
val typeSym = newSymbol(spliceOwner(ctx), name, EmptyFlags, typeSymInfo, NoSymbol, tree.span)
187+
typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuotedPatterns_patternTypeAnnot.typeRef)).withSpan(tree.span)))
188+
val pat = typedPattern(expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))(
189+
using spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx)))
190+
pat.select(tpnme.Underlying)
191+
188192
private def checkSpliceOutsideQuote(tree: untpd.Tree)(using Context): Unit =
189193
if (level == 0 && !ctx.owner.ownersIterator.exists(_.is(Inline)))
190194
report.error("Splice ${...} outside quotes '{...} or inline method", tree.srcPos)
@@ -201,17 +205,17 @@ trait QuotesAndSplices {
201205
*
202206
* A quote pattern
203207
* ```
204-
* case '{ type ${given t: Type[$t @ _]}; ${ls: Expr[List[$t]]} } => ...
208+
* case '{ type ${given t$giveni: Type[t @ _]}; ${ls: Expr[List[t]]} } => ...
205209
* ```
206210
* will return
207211
* ```
208212
* (
209-
* Map(<$t>: Symbol -> <$t @ _>: Bind),
213+
* Map(<t$giveni>: Symbol -> <t @ _>: Bind),
210214
* <'{
211-
* @scala.internal.Quoted.patternType type $t
212-
* scala.internal.Quoted.patternHole[List[$t]]
215+
* @scala.internal.Quoted.patternType type t
216+
* scala.internal.Quoted.patternHole[List[t]]
213217
* }>: Tree,
214-
* List(<ls: Expr[List[$t]]>: Tree)
218+
* List(<ls: Expr[List[t]]>: Tree)
215219
* )
216220
* ```
217221
*/
@@ -278,7 +282,7 @@ trait QuotesAndSplices {
278282
tree
279283
case tdef: TypeDef =>
280284
if tdef.symbol.hasAnnotation(defn.InternalQuotedPatterns_patternTypeAnnot) then
281-
transformTypeBindingTypeDef(PatMatVarName.fresh(), tdef, typePatBuf)
285+
transformTypeBindingTypeDef(PatMatGivenVarName.fresh(tdef.name.toTermName), tdef, typePatBuf)
282286
else if tdef.symbol.isClass then
283287
val kind = if tdef.symbol.is(Module) then "objects" else "classes"
284288
report.error("Implementation restriction: cannot match " + kind, tree.srcPos)
@@ -358,38 +362,38 @@ trait QuotesAndSplices {
358362
* within the quotes become patterns again and typed accordingly.
359363
*
360364
* ```
361-
* case '{ ($ls: List[$t]) } =>
362-
* // `t` is of type `Type[T$1]` for some unknown T$1
363-
* // `t` is implicitly available
364-
* // `l` is of type `Expr[List[T$1]]`
365+
* case '{ ($ls: List[t]) } =>
366+
* // `t$giveni` is of type `Type[t]` for some unknown `t`
367+
* // `t$giveni` is implicitly available
368+
* // `ls` is of type `Expr[List[t]]`
365369
* '{ val h: $t = $ls.head }
366370
* ```
367371
*
368-
* For each type splice we will create a new type binding in the pattern match ($t @ _ in this case)
369-
* and a corresponding type in the quoted pattern as a hole (@patternType type $t in this case).
372+
* For each type splice we will create a new type binding in the pattern match (`t @ _` in this case)
373+
* and a corresponding type in the quoted pattern as a hole (`@patternType type t` in this case).
370374
* All these generated types are inserted at the start of the quoted code.
371375
*
372376
* After typing the tree will resemble
373377
*
374378
* ```
375-
* case '{ type ${given t: Type[$t @ _]}; ${ls: Expr[List[$t]]} } => ...
379+
* case '{ type ${given t$giveni: Type[t @ _]}; ${ls: Expr[List[t]]} } => ...
376380
* ```
377381
*
378382
* Then the pattern is _split_ into the expression contained in the pattern replacing the splices by holes,
379383
* and the patterns in the splices. All these are recombined into a call to `Matcher.unapply`.
380384
*
381385
* ```
382386
* case scala.internal.quoted.Expr.unapply[
383-
* Tuple1[$t @ _], // Type binging definition
384-
* Tuple2[Type[$t], Expr[List[$t]]] // Typing the result of the pattern match
387+
* Tuple1[t @ _], // Type binging definition
388+
* Tuple2[Type[t], Expr[List[t]]] // Typing the result of the pattern match
385389
* ](
386390
* Tuple2.unapply
387-
* [Type[$t], Expr[List[$t]]] //Propagated from the tuple above
388-
* (implicit t @ _, ls @ _: Expr[List[$t]]) // from the spliced patterns
391+
* [Type[t], Expr[List[t]]] //Propagated from the tuple above
392+
* (given t$giveni @ _, ls @ _: Expr[List[t]]) // from the spliced patterns
389393
* )(
390394
* '{ // Runtime quote Matcher.unapply uses to mach against. Expression directly inside the quoted pattern without the splices
391-
* @scala.internal.Quoted.patternType type $t
392-
* scala.internal.Quoted.patternHole[List[$t]]
395+
* @scala.internal.Quoted.patternType type t
396+
* scala.internal.Quoted.patternHole[List[t]]
393397
* },
394398
* true, // If there is at least one type splice. Used to instantiate the context with or without GADT constraints
395399
* x$2 // tasty.Reflection instance
@@ -409,7 +413,7 @@ trait QuotesAndSplices {
409413
case _ => defn.AnyType
410414
}
411415
val quoted0 = desugar.quotedPattern(quoted, untpd.TypedSplice(TypeTree(quotedPt)))
412-
val quoteCtx = quoteContext.addMode(Mode.QuotedPattern)
416+
val quoteCtx = quoteContext.addMode(Mode.QuotedPattern).retractMode(Mode.Pattern)
413417
val quoted1 =
414418
if quoted.isType then typedType(quoted0, WildcardType)(using quoteCtx)
415419
else typedExpr(quoted0, WildcardType)(using quoteCtx)

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -431,12 +431,16 @@ class Typer extends Namer
431431
val name = tree.name
432432
def kind = if (name.isTermName) "" else "type "
433433
typr.println(s"typed ident $kind$name in ${ctx.owner}")
434-
if (ctx.mode is Mode.Pattern) {
435-
if (name == nme.WILDCARD)
434+
if ctx.mode.is(Mode.Pattern) then
435+
if name == nme.WILDCARD then
436436
return tree.withType(pt)
437-
if (untpd.isVarPattern(tree) && name.isTermName)
437+
if untpd.isVarPattern(tree) && name.isTermName then
438438
return typed(desugar.patternVar(tree), pt)
439-
}
439+
else if ctx.mode.is(Mode.QuotedPattern) then
440+
if untpd.isVarPattern(tree) && name.isTypeName then
441+
return typedQuotedTypeVar(tree, pt)
442+
end if
443+
440444
// Shortcut for the root package, this is not just a performance
441445
// optimization, it also avoids forcing imports thus potentially avoiding
442446
// cyclic references.

docs/docs/reference/metaprogramming/macros.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -703,11 +703,11 @@ Sometimes it is necessary to get a more precise type for an expression. This can
703703
```scala
704704
def f(exp: Expr[Any])(using QuoteContext) =
705705
expr match
706-
case '{ $x: $T } =>
707-
// If the pattern match succeeds, then there is some type `T` such that
708-
// - `x` is bound to a variable of type `Expr[T]`
709-
// - `T` is bound to a new type T and a given instance `Type[T]` is provided for it
710-
// That is, we have `x: Expr[T]` and `given Type[T]`, for some (unknown) type `T`.
706+
case '{ $x: t } =>
707+
// If the pattern match succeeds, then there is some type `t` such that
708+
// - `x` is bound to a variable of type `Expr[t]`
709+
// - `t` is bound to a new type `t` and a given instance `Type[t]` is provided for it
710+
// That is, we have `x: Expr[t]` and `given Type[t]`, for some (unknown) type `t`.
711711
```
712712

713713
This might be used to then perform an implicit search as in:
@@ -720,8 +720,8 @@ private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(using
720720
argsExpr match {
721721
case Varargs(argExprs) =>
722722
val argShowedExprs = argExprs.map {
723-
case '{ $arg: $T } =>
724-
Expr.summon[Show[T]] match {
723+
case '{ $arg: t } =>
724+
Expr.summon[Show[t]] match {
725725
case Some(showExpr) => '{ $showExpr.show($arg) }
726726
case None => Reporting.error(s"could not find implicit for ${showTp.show}", arg); '{???}
727727
}

0 commit comments

Comments
 (0)