Skip to content

Commit 7c4c672

Browse files
authored
Merge pull request #8766 from dotty-staging/improve-quoted-pattern-type-inference
Improve quoted pattern type inference
2 parents ecf134d + 3acc97a commit 7c4c672

File tree

7 files changed

+49
-14
lines changed

7 files changed

+49
-14
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -969,7 +969,9 @@ trait Applications extends Compatibility {
969969
}
970970
}
971971
else {
972-
val app = realApply
972+
val app = tree.fun match
973+
case _: untpd.Splice if ctx.mode.is(Mode.QuotedPattern) => typedAppliedSplice(tree, pt)
974+
case _ => realApply
973975
app match {
974976
case Apply(fn @ Select(left, _), right :: Nil) if fn.hasType =>
975977
val op = fn.symbol

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import dotty.tools.dotc.core.Types._
1717
import dotty.tools.dotc.reporting._
1818
import dotty.tools.dotc.typer.Implicits._
1919
import dotty.tools.dotc.typer.Inferencing._
20+
import dotty.tools.dotc.typer.ProtoTypes._
2021
import dotty.tools.dotc.util.Spans._
2122
import dotty.tools.dotc.util.Stats.record
2223

@@ -108,6 +109,26 @@ trait QuotesAndSplices {
108109
}
109110
}
110111

112+
/** Types a splice applied to some arguments `$f(arg1, ..., argn)` in a quote pattern.
113+
*
114+
* The tree is desugared into `$f.apply(arg1, ..., argn)` where the expression `$f`
115+
* is expected to type as a function type `(T1, ..., Tn) => R`.
116+
* `Ti` is the type of the argument `argi` and R if the type of the prototype.
117+
* The prototype must be fully defined to be able to infer the type of `R`.
118+
*/
119+
def typedAppliedSplice(tree: untpd.Apply, pt: Type)(using Context): Tree = {
120+
assert(ctx.mode.is(Mode.QuotedPattern))
121+
val untpd.Apply(splice: untpd.Splice, args) = tree
122+
if (isFullyDefined(pt, ForceDegree.flipBottom)) then
123+
val typedArgs = args.map(arg => typedExpr(arg))
124+
val argTypes = typedArgs.map(_.tpe.widenTermRefExpr)
125+
val splice1 = typedSplice(splice, defn.FunctionOf(argTypes, pt))
126+
Apply(splice1.select(nme.apply), typedArgs).withType(pt).withSpan(tree.span)
127+
else
128+
ctx.error(i"Type must be fully defined.", splice.sourcePos)
129+
tree.withType(UnspecifiedErrorType)
130+
}
131+
111132
/** Translate ${ t: Type[T] }` into type `t.splice` while tracking the quotation level in the context */
112133
def typedTypSplice(tree: untpd.TypSplice, pt: Type)(using Context): Tree = {
113134
record("typedTypSplice")
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import scala.quoted._
2+
3+
def f(x: Expr[Int])(using QuoteContext) = x match {
4+
case '{ $f($a: Int): Int } =>
5+
val f1: Expr[Int => Int] = f
6+
val a1: Expr[Int] = a
7+
case '{ def a: Int = $f($b: Int); () } =>
8+
val f1: Expr[Int => Int] = f
9+
val b1: Expr[Int] = b
10+
case '{ val a: Int = 3; $f(a): Int } =>
11+
val f1: Expr[Int => Int] = f
12+
}

tests/run-macros/quote-matcher-symantics-2/quoted_1.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ object Macros {
2020

2121
case '{ ($x: DSL) * ($y: DSL) } => sym.times(lift(x), lift(y))
2222

23-
case '{ ($f: DSL => DSL)($x: DSL) } => sym.app(liftFun(f), lift(x))
23+
case '{ $f($x: DSL): DSL } => sym.app(liftFun(f), lift(x))
2424

25-
case '{ val x: DSL = $value; ($bodyFn: DSL => DSL)(x) } =>
25+
case '{ val x: DSL = $value; $bodyFn(x): DSL } =>
2626
UnsafeExpr.open(bodyFn) { (body1, close) =>
2727
val (i, nEnvVar) = freshEnvVar()
2828
lift(close(body1)(nEnvVar))(env + (i -> lift(value)))
@@ -37,7 +37,7 @@ object Macros {
3737
}
3838

3939
def liftFun(e: Expr[DSL => DSL])(implicit env: Map[Int, Expr[T]]): Expr[T => T] = e match {
40-
case '{ (x: DSL) => ($bodyFn: DSL => DSL)(x) } =>
40+
case '{ (x: DSL) => $bodyFn(x): DSL } =>
4141
sym.lam((y: Expr[T]) =>
4242
UnsafeExpr.open(bodyFn) { (body1, close) =>
4343
val (i, nEnvVar) = freshEnvVar()

tests/run-macros/quote-matcher-symantics-3/quoted_1.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,23 @@ object Macros {
3939
case '{ ($x: Int) <= ($y: Int) } =>
4040
'{ $sym.leq(${lift(x)}, ${lift(y)}).asInstanceOf[R[T]] }
4141

42-
case '{ ($f: $t => $u)($arg) } =>
42+
case '{ $f($arg: $t): $u } =>
4343
'{ $sym.app[$t, $u](${lift(f)}, ${lift(arg)}).asInstanceOf[R[T]] }
4444

4545
case '{ (if ($cond) $thenp else $elsep): $t } =>
4646
'{ $sym.ifThenElse[$t](${lift(cond)}, ${lift(thenp)}, ${lift(elsep)}) }.asInstanceOf[Expr[R[T]]]
4747

48-
case '{ (x0: Int) => ($bodyFn: Int => Any)(x0) } =>
48+
case '{ (x0: Int) => $bodyFn(x0): Any } =>
4949
val (i, nEnvVar) = freshEnvVar[Int]()
5050
val body2 = UnsafeExpr.open(bodyFn) { (body1, close) => close(body1)(nEnvVar) }
5151
'{ $sym.lam((x: R[Int]) => ${given Env = envWith(i, 'x)(using env); lift(body2)}).asInstanceOf[R[T]] }
5252

53-
case '{ (x0: Boolean) => ($bodyFn: Boolean => Any)(x0) } =>
53+
case '{ (x0: Boolean) => $bodyFn(x0): Any } =>
5454
val (i, nEnvVar) = freshEnvVar[Boolean]()
5555
val body2 = UnsafeExpr.open(bodyFn) { (body1, close) => close(body1)(nEnvVar) }
5656
'{ $sym.lam((x: R[Boolean]) => ${given Env = envWith(i, 'x)(using env); lift(body2)}).asInstanceOf[R[T]] }
5757

58-
case '{ (x0: Int => Int) => ($bodyFn: (Int => Int) => Any)(x0) } =>
58+
case '{ (x0: Int => Int) => $bodyFn(x0): Any } =>
5959
val (i, nEnvVar) = freshEnvVar[Int => Int]()
6060
val body2 = UnsafeExpr.open(bodyFn) { (body1, close) => close(body1)(nEnvVar) }
6161
'{ $sym.lam((x: R[Int => Int]) => ${given Env = envWith(i, 'x)(using env); lift(body2)}).asInstanceOf[R[T]] }

tests/run-macros/quote-matching-open/Macro_1.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ object Macro {
66

77
def impl(x: Expr[Any])(using QuoteContext): Expr[Any] = {
88
x match {
9-
case '{ (x: Int) => ($body: Int => Int)(x) } => UnsafeExpr.open(body) { (body, close) => close(body)(Expr(2)) }
10-
case '{ (x1: Int, x2: Int) => ($body: (Int, Int) => Int)(x1, x2) } => UnsafeExpr.open(body) { (body, close) => close(body)(Expr(2), Expr(3)) }
11-
case '{ (x1: Int, x2: Int, x3: Int) => ($body: (Int, Int, Int) => Int)(x1, x2, x3) } => UnsafeExpr.open(body) { (body, close) => close(body)(Expr(2), Expr(3), Expr(4)) }
9+
case '{ (x: Int) => $body(x): Int } => UnsafeExpr.open(body) { (body, close) => close(body)(Expr(2)) }
10+
case '{ (x1: Int, x2: Int) => $body(x1, x2): Int } => UnsafeExpr.open(body) { (body, close) => close(body)(Expr(2), Expr(3)) }
11+
case '{ (x1: Int, x2: Int, x3: Int) => $body(x1, x2, x3): Int } => UnsafeExpr.open(body) { (body, close) => close(body)(Expr(2), Expr(3), Expr(4)) }
1212
}
1313
}
1414

tests/run-macros/quoted-pattern-open-expr/Macro_1.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ inline def test(inline e: Int): String = ${testExpr('e)}
55
private def testExpr(e: Expr[Int])(using QuoteContext): Expr[String] = {
66
e match {
77
case '{ val y: Int = 4; $body } => Expr("Matched closed\n" + body.show)
8-
case '{ val y: Int = 4; ($body: Int => Int)(y) } => Expr("Matched open\n" + body.show)
9-
case '{ val y: Int => Int = x => x + 1; ($body: (Int => Int) => Int)(y) } => Expr("Matched open\n" + body.show)
10-
case '{ def g(x: Int): Int = ($body: (Int => Int, Int) => Int)(g, x); g(5) } => Expr("Matched open\n" + body.show)
8+
case '{ val y: Int = 4; $body(y): Int } => Expr("Matched open\n" + body.show)
9+
case '{ val y: Int => Int = x => x + 1; $body(y): Int } => Expr("Matched open\n" + body.show)
10+
case '{ def g(x: Int): Int = $body(g, x); g(5) } => Expr("Matched open\n" + body.show)
1111
}
1212
}

0 commit comments

Comments
 (0)