Skip to content

Commit a8d7615

Browse files
oderskynicolasstucki
authored andcommitted
Quoted patterns: type checking
1 parent 8f37686 commit a8d7615

File tree

6 files changed

+83
-10
lines changed

6 files changed

+83
-10
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,16 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
11781178
}
11791179
}
11801180

1181+
/** An extractor for typed splices */
1182+
object Splice {
1183+
def apply(tree: Tree)(implicit ctx: Context): Tree =
1184+
ref(defn.InternalQuoted_exprSplice).appliedTo(tree)
1185+
def unapply(tree: Tree)(implicit ctx: Context): Option[Tree] = tree match {
1186+
case Apply(fn, arg :: Nil) if fn.symbol == defn.InternalQuoted_exprSplice => Some(arg)
1187+
case _ => None
1188+
}
1189+
}
1190+
11811191
/** A key to be used in a context property that tracks enclosing inlined calls */
11821192
private val InlinedCalls = new Property.Key[List[Tree]]
11831193

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
9393
override def isType: Boolean = !isTerm
9494
}
9595
case class Throw(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree
96-
case class Quote(t: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree
96+
case class Quote(quoted: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree
9797
case class Splice(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree
9898
case class TypSplice(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TypTree
9999
case class DoWhile(body: Tree, cond: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree
@@ -492,9 +492,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
492492
case tree: Throw if expr eq tree.expr => tree
493493
case _ => finalize(tree, untpd.Throw(expr)(tree.source))
494494
}
495-
def Quote(tree: Tree)(t: Tree)(implicit ctx: Context): Tree = tree match {
496-
case tree: Quote if t eq tree.t => tree
497-
case _ => finalize(tree, untpd.Quote(t)(tree.source))
495+
def Quote(tree: Tree)(quoted: Tree)(implicit ctx: Context): Tree = tree match {
496+
case tree: Quote if quoted eq tree.quoted => tree
497+
case _ => finalize(tree, untpd.Quote(quoted)(tree.source))
498498
}
499499
def Splice(tree: Tree)(expr: Tree)(implicit ctx: Context): Tree = tree match {
500500
case tree: Splice if expr eq tree.expr => tree

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,7 @@ object Mode {
104104

105105
/** Are we trying to find a hidden implicit? */
106106
val FindHiddenImplicits: Mode = newMode(24, "FindHiddenImplicits")
107+
108+
/** Are we in a quote in a pattern? */
109+
val QuotedPattern: Mode = newMode(25, "QuotedPattern")
107110
}

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

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1930,17 +1930,51 @@ class Typer extends Namer
19301930
* while tracking the quotation level in the context.
19311931
*/
19321932
def typedQuote(tree: untpd.Quote, pt: Type)(implicit ctx: Context): Tree = track("typedQuote") {
1933-
tree.t match {
1933+
tree.quoted match {
19341934
case untpd.Splice(innerExpr) =>
19351935
ctx.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.sourcePos)
19361936
typed(innerExpr, pt)
1937-
case t if t.isType =>
1938-
typedTypeApply(untpd.TypeApply(untpd.ref(defn.InternalQuoted_typeQuoteR), List(tree.t)), pt)(quoteContext).withSpan(tree.span)
1939-
case t=>
1940-
typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprQuoteR), tree.t), pt)(quoteContext).withSpan(tree.span)
1937+
case quoted if quoted.isType =>
1938+
typedTypeApply(untpd.TypeApply(untpd.ref(defn.InternalQuoted_typeQuoteR), quoted :: Nil), pt)(quoteContext).withSpan(tree.span)
1939+
case quoted =>
1940+
if (ctx.mode.is(Mode.Pattern)) {
1941+
val exprPt = pt.baseType(defn.QuotedExprClass)
1942+
val quotedPt = if (exprPt.exists) exprPt.argTypesHi.head else defn.AnyType
1943+
val quoted1 = typedExpr(quoted, quotedPt)(quoteContext.addMode(Mode.QuotedPattern))
1944+
val (shape, splices) = splitQuotePattern(quoted1)
1945+
val splicePat = typed(untpd.Tuple(splices.map(untpd.TypedSplice(_))).withSpan(quoted.span))
1946+
val patType = TypeOps.tupleOf(splices.tpes)
1947+
UnApply(
1948+
ref(defn.QuotedMatcher_unapplyR).appliedToType(patType),
1949+
ref(defn.InternalQuoted_exprQuoteR).appliedToType(shape.tpe).appliedTo(shape) :: givenReflection :: Nil,
1950+
splicePat :: Nil,
1951+
pt)
1952+
}
1953+
else
1954+
typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprQuoteR), quoted), pt)(quoteContext).withSpan(tree.span)
1955+
}
1956+
}
1957+
1958+
def splitQuotePattern(quoted: Tree)(implicit ctx: Context): (Tree, List[Tree]) = {
1959+
object splitter extends tpd.TreeMap {
1960+
val patBuf = new mutable.ListBuffer[Tree]
1961+
override def transform(tree: Tree)(implicit ctx: Context) = tree match {
1962+
case Typed(Splice(pat), tpt) =>
1963+
val exprTpt = ref(defn.QuotedExprType).appliedToTypeTrees(tpt :: Nil)
1964+
transform(Splice(Typed(pat, exprTpt)))
1965+
case Splice(pat) =>
1966+
try tasty.TreePickler.Hole(patBuf.length, Nil)
1967+
finally patBuf += pat
1968+
case _ =>
1969+
super.transform(tree)
1970+
}
19411971
}
1972+
val result = splitter.transform(quoted)
1973+
(result, splitter.patBuf.toList)
19421974
}
19431975

1976+
def givenReflection(implicit ctx: Context): Tree = Literal(Constant(null)) // FIXME: fill in
1977+
19441978
/** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */
19451979
def typedSplice(tree: untpd.Splice, pt: Type)(implicit ctx: Context): Tree = track("typedSplice") {
19461980
checkSpliceOutsideQuote(tree)
@@ -1949,7 +1983,14 @@ class Typer extends Namer
19491983
ctx.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.sourcePos)
19501984
typed(innerExpr, pt)
19511985
case expr =>
1952-
typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprSpliceR), tree.expr), pt)(spliceContext).withSpan(tree.span)
1986+
if (ctx.mode.is(Mode.QuotedPattern)) {
1987+
fullyDefinedType(pt, "quoted pattern selector", tree.span)
1988+
val pat = typedPattern(expr, defn.QuotedExprType.appliedTo(pt))(
1989+
spliceContext.retractMode(Mode.QuotedPattern))
1990+
Splice(pat)
1991+
}
1992+
else
1993+
typedApply(untpd.Apply(untpd.ref(defn.InternalQuoted_exprSpliceR), tree.expr), pt)(spliceContext).withSpan(tree.span)
19531994
}
19541995
}
19551996

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package scala.runtime.quoted
2+
3+
import scala.quoted.Expr
4+
import scala.tasty.Reflection
5+
6+
/** THIS IS A PLACEHOLDER
7+
*/
8+
object Matcher {
9+
def unapply[Tup <: Tuple](scrut: Expr[_])(implicit pattern: Expr[_], reflection: Reflection): Option[Tup] = ???
10+
}

tests/pos/quotedPatterns.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
object Test {
2+
3+
val x = '{1 + 2}
4+
5+
x match {
6+
case '{1 + 2} =>
7+
case _ =>
8+
}
9+
}

0 commit comments

Comments
 (0)