Skip to content

Commit 41a4277

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 #14708
1 parent 0ab89e6 commit 41a4277

File tree

6 files changed

+100
-7
lines changed

6 files changed

+100
-7
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+
val typeNormalizer = new untpd.UntypedTreeMap {
401+
override def transform(tpt: untpd.Tree)(using Context) =
402+
tpt match
403+
case untpd.Ident(tpnme.WILDCARD_STAR) => tpt
404+
case tpt @ untpd.Ident(name) if name.isTypeName && !tpt.isBackquoted && name.isVarPattern =>
405+
variables += name.asTypeName
406+
tpt.pushAttachment(Trees.Backquoted, ())
407+
tpt
408+
case _: untpd.TypedSplice => tpt
409+
case _ => super.transform(tpt)
410+
}
411+
val termNormalizer = new untpd.UntypedTreeMap {
412+
override def transform(tree: untpd.Tree)(using Context) =
413+
tree match
414+
case untpd.Splice(_) => tree
415+
case untpd.Typed(expr, tpt) =>
416+
untpd.cpy.Typed(tree)(
417+
super.transform(expr),
418+
typeNormalizer.transform(tpt)
419+
)
420+
case untpd.TypeApply(fn, targs) =>
421+
untpd.cpy.TypeApply(tree)(
422+
super.transform(fn),
423+
targs.map(typeNormalizer.transform)
424+
)
425+
case _ => super.transform(tree)
426+
}
427+
428+
val transformed: untpd.Tree =
429+
if quoted.isType then quoted // FIXME: typeNormalizer.transform(quoted)
430+
else termNormalizer.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 = ()

tests/pos-macros/i16265.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ class Foo(val value: Int)
44

55
def foo(exprs: Expr[Any])(using Quotes): Any =
66
exprs match
7-
case '{ $tuple: (Foo *: tail) } =>
7+
case '{ type tail <: Tuple; $tuple: (Foo *: tail) } => // FIXME infer bounds of tail
88
val x = '{ ${tuple}.head.value }
99
???

tests/pos-macros/i7264b.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import scala.quoted.*
22
class Foo {
33
def f[T2: Type](e: Expr[T2])(using Quotes) = e match {
4-
case '{ $x: *:[Int, t] } =>
4+
case '{ type t <: Tuple; $x: *:[Int, t] } => // FIXME infer bounds of tail
55
Type.of[ *:[Int, t] ]
66
}
77
}

0 commit comments

Comments
 (0)