Skip to content

Commit

Permalink
Compile quote patterns directly into QuotePattern AST
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasstucki committed Jul 19, 2023
1 parent e1e8549 commit f444da9
Show file tree
Hide file tree
Showing 13 changed files with 213 additions and 301 deletions.
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ object desugar {
tree match
case untpd.Block(stats, expr) =>
val (untpdTypeVariables, otherStats) = stats.span {
case tdef @ untpd.TypeDef(name, _) => name.isVarPattern
case tdef @ untpd.TypeDef(name, _) => !tdef.isBackquoted && name.isVarPattern
case _ => false
}
val pattern = if otherStats.isEmpty then expr else untpd.cpy.Block(tree)(otherStats, expr)
Expand Down
28 changes: 28 additions & 0 deletions compiler/src/dotty/tools/dotc/quoted/QuotePatterns.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import dotty.tools.dotc.core.NameKinds.PatMatGivenVarName
import dotty.tools.dotc.core.Names.*
import dotty.tools.dotc.core.StdNames.*
import dotty.tools.dotc.core.Symbols.*
import dotty.tools.dotc.core.TypeOps.*
import dotty.tools.dotc.core.Types.*
import dotty.tools.dotc.reporting.IllegalVariableInPatternAlternative
import dotty.tools.dotc.transform.SymUtils._
Expand All @@ -24,6 +25,33 @@ import scala.collection.mutable
object QuotePatterns:
import tpd._

/** Check for restricted patterns */
def checkPattern(quotePattern: QuotePattern)(using Context): Unit = new tpd.TreeTraverser {
def traverse(tree: Tree)(using Context): Unit = tree match {
case _: SplicePattern =>
case tdef: TypeDef if tdef.symbol.isClass =>
val kind = if tdef.symbol.is(Module) then "objects" else "classes"
report.error(em"Implementation restriction: cannot match $kind", tree.srcPos)
case tree: NamedDefTree =>
if tree.name.is(NameKinds.WildcardParamName) then
report.warning(
"Use of `_` for lambda in quoted pattern. Use explicit lambda instead or use `$_` to match any term.",
tree.srcPos)
if tree.name.isTermName && !tree.nameSpan.isSynthetic && tree.name != nme.ANON_FUN && tree.name.startsWith("$") then
report.error("Names cannot start with $ quote pattern", tree.namePos)
traverseChildren(tree)
case _: Match =>
report.error("Implementation restriction: cannot match `match` expressions", tree.srcPos)
case _: Try =>
report.error("Implementation restriction: cannot match `try` expressions", tree.srcPos)
case _: Return =>
report.error("Implementation restriction: cannot match `return` statements", tree.srcPos)
case _ =>
traverseChildren(tree)
}

}.traverse(quotePattern.body)

/** Encode the quote pattern into an `unapply` that the pattern matcher can handle.
*
* A quote pattern
Expand Down
370 changes: 71 additions & 299 deletions compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions tests/neg-macros/i16522.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-- [E007] Type Mismatch Error: tests/neg-macros/i16522.scala:10:45 -----------------------------------------------------
10 | case '{HCons($h1: hd1, HCons($h2: hd2, $_ : tl))} => '{$h1.toString ++ $h2.toString} // error
| ^^^^^^^
| Found: tl
| Required: HList
|
| longer explanation available when compiling with `-explain`
16 changes: 16 additions & 0 deletions tests/neg-macros/i16522.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import scala.quoted.*

sealed trait HList
case class HCons[+HD, TL <: HList](hd: HD, tl: TL) extends HList
case object HNil extends HList

def showFirstTwoImpl(e: Expr[HList])(using Quotes): Expr[String] = {
e match {
case '{HCons($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString}
case '{HCons($h1: hd1, HCons($h2: hd2, $_ : tl))} => '{$h1.toString ++ $h2.toString} // error
case '{HCons[hd, HCons[sd, tl]]($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString}
case _ => '{""}
}
}

transparent inline def showFirstTwo(inline xs: HList) = ${ showFirstTwoImpl('xs) }
2 changes: 1 addition & 1 deletion tests/neg-macros/i6997b.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import scala.quoted.*
inline def mcr(x: => Any): Any = ${mcrImpl('x)}

def mcrImpl(body: Expr[Any])(using ctx: Quotes): Expr[Any] = {
val '{$x: $t} = body // error
val '{$x: $t} = body // error // error
'{
val tmp: $t = $x.asInstanceOf[$t] // error // error
println(tmp)
Expand Down
10 changes: 10 additions & 0 deletions tests/pos-macros/i14708.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import scala.quoted.*

object Main {
def foo(a: Expr[Any])(using Quotes) = {
a match {
case '{ ($x: Set[t]).toSet } =>
case _ =>
}
}
}
16 changes: 16 additions & 0 deletions tests/pos-macros/i16522.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import scala.quoted.*

sealed trait HList
case class HCons[+HD, TL <: HList](hd: HD, tl: TL) extends HList
case object HNil extends HList

def showFirstTwoImpl(e: Expr[HList])(using Quotes): Expr[String] = {
e match {
case '{HCons($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString}
case '{type tl <: HList; HCons($h1: hd1, HCons($h2: hd2, $_ : tl))} => '{$h1.toString ++ $h2.toString} // error
case '{HCons[hd, HCons[sd, tl]]($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString}
case _ => '{""}
}
}

transparent inline def showFirstTwo(inline xs: HList) = ${ showFirstTwoImpl('xs) }
10 changes: 10 additions & 0 deletions tests/pos-macros/i18125.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import scala.quoted.*

final class Foo[T](ns: T)

def foo(using Quotes)(x: Expr[Any]): Unit =
x match
case '{ new Foo($y: b) } =>
case '{ new Foo($y: List[b]) } =>
case '{ type b; new Foo($y: b) } =>

23 changes: 23 additions & 0 deletions tests/pos-macros/i18125b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package oolong.phobos

import scala.quoted.*
import scala.compiletime.*
import scala.annotation.StaticAnnotation

final class xmlns[T](ns: T) extends StaticAnnotation
trait Namespace[T]{
val getNamespace: String
}

object common{
private def extractFeildNamespace(using Quotes)(
fieldAnnotations: List[Expr[Any]],
): Expr[Option[String]] = {
import quotes.reflect.*

fieldAnnotations.collect { case '{ xmlns($namespace: b) } =>
'{ Some(summonInline[Namespace[b]].getNamespace) }
}
???
}
}
6 changes: 6 additions & 0 deletions tests/pos-macros/i18250.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import scala.quoted.*

def test(x: Expr[Any])(using Quotes): Unit =
x match
case '{ type t; type u <: t; () } =>
case '{ type t <: Comparable[t]; () } =>
13 changes: 13 additions & 0 deletions tests/pos-macros/mirrorQuotePattern.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import scala.deriving._
import scala.quoted._

private def derivedExpr[T](mirrorExpr: Expr[Mirror.Of[T]])(using Quotes, Type[T]): Expr[Any] = {
mirrorExpr match {
case '{ $mirrorExpr : Mirror.Sum { type MirroredElemTypes = mirroredElemTypes } } =>
'{ liftableSum[mirroredElemTypes]($mirrorExpr) }
case '{ type mirroredElemTypes; $mirrorExpr : Mirror.Sum { type MirroredElemTypes = mirroredElemTypes } } =>
'{ liftableSum[mirroredElemTypes]($mirrorExpr) }
}
}

def liftableSum[MElemTypes](mirror: Mirror.Sum { type MirroredElemTypes = MElemTypes }): Any = ???
11 changes: 11 additions & 0 deletions tests/pos-macros/mirrorQuotePattern2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import scala.deriving._
import scala.quoted._

private def derivedExpr(x: Expr[Any])(using Quotes): Unit =
x match
case '{ type mtp1; ($m1 : Mirror.Sum { type MirroredElemTypes = mtp1 } & Mirror.Of[Any], $m2 : Mirror.Sum { type MirroredElemTypes = mtp2 } & Mirror.Of[Any]); ??? } =>
'{ $m1: Mirror.Sum { type MirroredElemTypes = mtp1 } }
'{ $m2: Mirror.Sum { type MirroredElemTypes = mtp2 } }
// ^^^ FIXME
// Found: scala.deriving.Mirror.Sum{type MirroredElemTypes} & scala.deriving.Mirror.Of[Any]
// Required: scala.deriving.Mirror.Sum{type MirroredElemTypes = mtp2}

0 comments on commit f444da9

Please sign in to comment.