Skip to content

Commit e51a0da

Browse files
committed
Insert top level type variables before typing quote pattern
```scala case '{ type u; ($x: t, $y: t, $z: u) } ``` is desugared to ```scala case '{ type t; type u; ($x: `t`, $y: `t`, $z: `u`) } ``` Fixes scala#14708
1 parent 0ab89e6 commit e51a0da

File tree

4 files changed

+98
-5
lines changed

4 files changed

+98
-5
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1518,7 +1518,7 @@ object Trees {
15181518
paramss.mapConserve(transformParams)
15191519

15201520
protected def transformMoreCases(tree: Tree)(using Context): Tree = {
1521-
assert(ctx.reporter.errorsReported)
1521+
assert(ctx.reporter.errorsReported, tree)
15221522
tree
15231523
}
15241524
}

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

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import dotty.tools.dotc.core.Contexts._
1010
import dotty.tools.dotc.core.Decorators._
1111
import dotty.tools.dotc.core.Flags._
1212
import dotty.tools.dotc.core.NameKinds.PatMatGivenVarName
13+
import dotty.tools.dotc.core.NameOps.isVarPattern
1314
import dotty.tools.dotc.core.Names._
1415
import dotty.tools.dotc.core.StagingContext._
1516
import dotty.tools.dotc.core.StdNames._
@@ -24,7 +25,6 @@ import dotty.tools.dotc.util.Stats.record
2425
import dotty.tools.dotc.reporting.IllegalVariableInPatternAlternative
2526
import scala.collection.mutable
2627

27-
2828
/** Type quotes `'{ ... }` and splices `${ ... }` */
2929
trait QuotesAndSplices {
3030
self: Typer =>
@@ -394,9 +394,84 @@ trait QuotesAndSplices {
394394
}
395395
val quoted0 = desugar.quotedPattern(quoted, untpd.TypedSplice(TypeTree(quotedPt)))
396396
val quoteCtx = quoteContext.addMode(Mode.QuotedPattern).retractMode(Mode.Pattern)
397+
398+
def normalizeTypeBindings(quoted: untpd.Tree): untpd.Tree =
399+
val variables = mutable.Set.empty[TypeName] // TODO use stable order
400+
def normalizeType(tpt: untpd.Tree): untpd.Tree =
401+
new untpd.UntypedTreeMap {
402+
override def transform(tpt: untpd.Tree)(using Context) =
403+
tpt match
404+
case untpd.Ident(tpnme.WILDCARD_STAR) => tpt
405+
case tpt @ untpd.Ident(name) if !tpt.isBackquoted && name.isVarPattern =>
406+
variables += name.asTypeName
407+
tpt.pushAttachment(Trees.Backquoted, ())
408+
tpt
409+
case _: untpd.TypedSplice => tpt
410+
case _ =>
411+
super.transform(tpt)
412+
}.transform(tpt)
413+
val transformed: untpd.Tree = new untpd.UntypedTreeMap {
414+
override def transform(tree: untpd.Tree)(using Context) =
415+
// println("- " + tree.show)
416+
// println("- " + tree)
417+
tree match
418+
case untpd.Splice(_) => tree
419+
case untpd.Typed(expr, tpt) =>
420+
untpd.cpy.Typed(tree)(
421+
super.transform(expr),
422+
normalizeType(tpt)
423+
)
424+
case untpd.TypeApply(fn, targs) =>
425+
untpd.cpy.TypeApply(tree)(
426+
super.transform(fn),
427+
targs.map(normalizeType)
428+
)
429+
case _ => super.transform(tree)
430+
}.transform(quoted)
431+
432+
def typeBindingDefinedInSource: List[TypeName] =
433+
transformed match
434+
case untpd.Block(stats, _) =>
435+
stats.takeWhile {
436+
case untpd.TypeDef(name, _) => name.isVarPattern
437+
case _ => false
438+
}.map(_.asInstanceOf[untpd.TypeDef].name.asTypeName)
439+
case _ => Nil
440+
variables --= typeBindingDefinedInSource
441+
442+
// println("==============")
443+
// println(quoted.show)
444+
// println(quoted)
445+
// println("--------------")
446+
// println(transformed.show)
447+
// println(transformed)
448+
// println(" ")
449+
// println(variables)
450+
// println(" ")
451+
452+
if variables.isEmpty then transformed
453+
else
454+
transformed match
455+
case untpd.Block(stats, expr) =>
456+
val typeBindings = stats.takeWhile {
457+
case untpd.TypeDef(name, _) => name.isVarPattern
458+
case _ => false
459+
}
460+
variables --= typeBindings.map(_.asInstanceOf[untpd.TypeDef].name.asTypeName)
461+
untpd.cpy.Block(quoted)(
462+
variables.toList.map(name => untpd.TypeDef(name, untpd.TypeBoundsTree(untpd.EmptyTree, untpd.EmptyTree, untpd.EmptyTree))) ::: stats,
463+
expr
464+
)
465+
case _ =>
466+
untpd.cpy.Block(quoted)(
467+
variables.toList.map(name => untpd.TypeDef(name, untpd.TypeBoundsTree(untpd.EmptyTree, untpd.EmptyTree, untpd.EmptyTree))),
468+
transformed
469+
)
470+
471+
val quoted0normalized = normalizeTypeBindings(quoted0)
397472
val quoted1 =
398-
if quoted.isType then typedType(quoted0, WildcardType)(using quoteCtx)
399-
else typedExpr(quoted0, WildcardType)(using quoteCtx)
473+
if quoted.isType then typedType(quoted0normalized, WildcardType)(using quoteCtx)
474+
else typedExpr(quoted0normalized, WildcardType)(using quoteCtx)
400475

401476
val (typeBindings, shape, splices) = splitQuotePattern(quoted1)
402477

tests/neg-macros/quotedPatterns-5.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import scala.quoted.*
22
object Test {
33
def test(x: quoted.Expr[Int])(using Quotes): Unit = x match {
44
case '{ type t; 4 } => Type.of[t]
5-
case '{ type t; poly[t]($x); 4 } => // error: duplicate pattern variable: t
5+
case '{ type t; poly[t]($x); 4 } =>
66
case '{ type `t`; poly[`t`]($x); 4 } =>
77
Type.of[t] // error
88
case _ =>

tests/pos-macros/i14708.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import scala.quoted.*
2+
3+
object Main {
4+
def foo(a: Expr[Any])(using Quotes) = {
5+
a match {
6+
case '{ ($x: Set[t]).toSet } =>
7+
case '{ ($x: Set[t]).toSet[t] } =>
8+
case '{ val a = 1; a; ($x: Set[t]).toSet } =>
9+
case '{ type u; ($x: Set[`u`]).toSet } =>
10+
case '{ type t; ($x: Set[t]).toSet } =>
11+
case '{ varargs(${_}*) } =>
12+
case '{ type u; ($x: t, $y: t, $z: u) } =>
13+
case _ =>
14+
}
15+
}
16+
}
17+
18+
def varargs(x: Any*): Unit = ()

0 commit comments

Comments
 (0)