Skip to content

Commit 3e00a0d

Browse files
authored
Compile quote patterns directly into QuotePattern AST (#18133)
Fixes #14708 Fixes #16522 Fixes #18125 Fixes #18250
2 parents 45e08cb + 5664ff2 commit 3e00a0d

13 files changed

+223
-308
lines changed

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

+5-2
Original file line numberDiff line numberDiff line change
@@ -381,11 +381,14 @@ object desugar {
381381
tree match
382382
case untpd.Block(stats, expr) =>
383383
val (untpdTypeVariables, otherStats) = stats.span {
384-
case tdef @ untpd.TypeDef(name, _) => name.isVarPattern
384+
case tdef @ untpd.TypeDef(name, _) => !tdef.isBackquoted && name.isVarPattern
385385
case _ => false
386386
}
387+
val untpdCaseTypeVariables = untpdTypeVariables.asInstanceOf[List[untpd.TypeDef]].map {
388+
tdef => tdef.withMods(tdef.mods | Case)
389+
}
387390
val pattern = if otherStats.isEmpty then expr else untpd.cpy.Block(tree)(otherStats, expr)
388-
(untpdTypeVariables.asInstanceOf[List[untpd.TypeDef]], pattern)
391+
(untpdCaseTypeVariables, pattern)
389392
case _ =>
390393
(Nil, tree)
391394

compiler/src/dotty/tools/dotc/quoted/QuotePatterns.scala

+28
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import dotty.tools.dotc.core.NameKinds.PatMatGivenVarName
1515
import dotty.tools.dotc.core.Names.*
1616
import dotty.tools.dotc.core.StdNames.*
1717
import dotty.tools.dotc.core.Symbols.*
18+
import dotty.tools.dotc.core.TypeOps.*
1819
import dotty.tools.dotc.core.Types.*
1920
import dotty.tools.dotc.reporting.IllegalVariableInPatternAlternative
2021
import dotty.tools.dotc.transform.SymUtils._
@@ -24,6 +25,33 @@ import scala.collection.mutable
2425
object QuotePatterns:
2526
import tpd._
2627

28+
/** Check for restricted patterns */
29+
def checkPattern(quotePattern: QuotePattern)(using Context): Unit = new tpd.TreeTraverser {
30+
def traverse(tree: Tree)(using Context): Unit = tree match {
31+
case _: SplicePattern =>
32+
case tdef: TypeDef if tdef.symbol.isClass =>
33+
val kind = if tdef.symbol.is(Module) then "objects" else "classes"
34+
report.error(em"Implementation restriction: cannot match $kind", tree.srcPos)
35+
case tree: NamedDefTree =>
36+
if tree.name.is(NameKinds.WildcardParamName) then
37+
report.warning(
38+
"Use of `_` for lambda in quoted pattern. Use explicit lambda instead or use `$_` to match any term.",
39+
tree.srcPos)
40+
if tree.name.isTermName && !tree.nameSpan.isSynthetic && tree.name != nme.ANON_FUN && tree.name.startsWith("$") then
41+
report.error("Names cannot start with $ quote pattern", tree.namePos)
42+
traverseChildren(tree)
43+
case _: Match =>
44+
report.error("Implementation restriction: cannot match `match` expressions", tree.srcPos)
45+
case _: Try =>
46+
report.error("Implementation restriction: cannot match `try` expressions", tree.srcPos)
47+
case _: Return =>
48+
report.error("Implementation restriction: cannot match `return` statements", tree.srcPos)
49+
case _ =>
50+
traverseChildren(tree)
51+
}
52+
53+
}.traverse(quotePattern.body)
54+
2755
/** Encode the quote pattern into an `unapply` that the pattern matcher can handle.
2856
*
2957
* A quote pattern

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

+78-305
Large diffs are not rendered by default.

tests/neg-macros/i16522.check

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- [E007] Type Mismatch Error: tests/neg-macros/i16522.scala:10:45 -----------------------------------------------------
2+
10 | case '{HCons($h1: hd1, HCons($h2: hd2, $_ : tl))} => '{$h1.toString ++ $h2.toString} // error
3+
| ^^^^^^^
4+
| Found: tl
5+
| Required: HList
6+
|
7+
| longer explanation available when compiling with `-explain`

tests/neg-macros/i16522.scala

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import scala.quoted.*
2+
3+
sealed trait HList
4+
case class HCons[+HD, TL <: HList](hd: HD, tl: TL) extends HList
5+
case object HNil extends HList
6+
7+
def showFirstTwoImpl(e: Expr[HList])(using Quotes): Expr[String] = {
8+
e match {
9+
case '{HCons($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString}
10+
case '{HCons($h1: hd1, HCons($h2: hd2, $_ : tl))} => '{$h1.toString ++ $h2.toString} // error
11+
case '{HCons[hd, HCons[sd, tl]]($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString}
12+
case _ => '{""}
13+
}
14+
}
15+
16+
transparent inline def showFirstTwo(inline xs: HList) = ${ showFirstTwoImpl('xs) }

tests/neg-macros/i6997b.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import scala.quoted.*
55
inline def mcr(x: => Any): Any = ${mcrImpl('x)}
66

77
def mcrImpl(body: Expr[Any])(using ctx: Quotes): Expr[Any] = {
8-
val '{$x: $t} = body // error
8+
val '{$x: $t} = body // error // error
99
'{
1010
val tmp: $t = $x.asInstanceOf[$t] // error // error
1111
println(tmp)

tests/pos-macros/i14708.scala

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
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 _ =>
8+
}
9+
}
10+
}

tests/pos-macros/i16522.scala

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import scala.quoted.*
2+
3+
sealed trait HList
4+
case class HCons[+HD, TL <: HList](hd: HD, tl: TL) extends HList
5+
case object HNil extends HList
6+
7+
def showFirstTwoImpl(e: Expr[HList])(using Quotes): Expr[String] = {
8+
e match {
9+
case '{HCons($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString}
10+
case '{type tl <: HList; HCons($h1: hd1, HCons($h2: hd2, $_ : tl))} => '{$h1.toString ++ $h2.toString} // error
11+
case '{HCons[hd, HCons[sd, tl]]($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString}
12+
case _ => '{""}
13+
}
14+
}
15+
16+
transparent inline def showFirstTwo(inline xs: HList) = ${ showFirstTwoImpl('xs) }

tests/pos-macros/i18125.scala

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import scala.quoted.*
2+
3+
final class Foo[T](ns: T)
4+
5+
def foo(using Quotes)(x: Expr[Any]): Unit =
6+
x match
7+
case '{ new Foo($y: b) } =>
8+
case '{ new Foo($y: List[b]) } =>
9+
case '{ type b; new Foo($y: b) } =>
10+

tests/pos-macros/i18125b.scala

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package oolong.phobos
2+
3+
import scala.quoted.*
4+
import scala.compiletime.*
5+
import scala.annotation.StaticAnnotation
6+
7+
final class xmlns[T](ns: T) extends StaticAnnotation
8+
trait Namespace[T]{
9+
val getNamespace: String
10+
}
11+
12+
object common{
13+
private def extractFeildNamespace(using Quotes)(
14+
fieldAnnotations: List[Expr[Any]],
15+
): Expr[Option[String]] = {
16+
import quotes.reflect.*
17+
18+
fieldAnnotations.collect { case '{ xmlns($namespace: b) } =>
19+
'{ Some(summonInline[Namespace[b]].getNamespace) }
20+
}
21+
???
22+
}
23+
}

tests/pos-macros/i18250.scala

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import scala.quoted.*
2+
3+
def test(x: Expr[Any])(using Quotes): Unit =
4+
x match
5+
case '{ type t; type u <: t; () } =>
6+
case '{ type t <: Comparable[t]; () } =>
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import scala.deriving._
2+
import scala.quoted._
3+
4+
private def derivedExpr[T](mirrorExpr: Expr[Mirror.Of[T]])(using Quotes, Type[T]): Expr[Any] = {
5+
mirrorExpr match {
6+
case '{ $mirrorExpr : Mirror.Sum { type MirroredElemTypes = mirroredElemTypes } } =>
7+
'{ liftableSum[mirroredElemTypes]($mirrorExpr) }
8+
case '{ type mirroredElemTypes; $mirrorExpr : Mirror.Sum { type MirroredElemTypes = mirroredElemTypes } } =>
9+
'{ liftableSum[mirroredElemTypes]($mirrorExpr) }
10+
}
11+
}
12+
13+
def liftableSum[MElemTypes](mirror: Mirror.Sum { type MirroredElemTypes = MElemTypes }): Any = ???
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import scala.deriving._
2+
import scala.quoted._
3+
4+
private def derivedExpr(x: Expr[Any])(using Quotes): Unit =
5+
x match
6+
case '{ type mtp1; ($m1 : Mirror.Sum { type MirroredElemTypes = mtp1 } & Mirror.Of[Any], $m2 : Mirror.Sum { type MirroredElemTypes = mtp2 } & Mirror.Of[Any]); ??? } =>
7+
val _: Expr[Mirror.Sum { type MirroredElemTypes = mtp1 } & Mirror.Of[Any]] = m1
8+
val _: Expr[Mirror.Sum { type MirroredElemTypes = mtp2 } & Mirror.Of[Any]] = m2
9+
'{ $m1: Mirror.Sum { type MirroredElemTypes = mtp1 } }
10+
'{ $m2: Mirror.Sum { type MirroredElemTypes = mtp2 } }

0 commit comments

Comments
 (0)