Skip to content

Commit f921b7c

Browse files
committed
Inhibit typer to insert contextual parameters into HOAS pattern arguments
* This commit introduces a new mode InQuotePatternHoasArgs. We use this mode when typing arguments of HOAS patterns so that typer will not insert contextual parameters. * This commit also Fix a bug where toExpr does not fully replace symbols.
1 parent 18f355d commit f921b7c

File tree

7 files changed

+40
-9
lines changed

7 files changed

+40
-9
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ object Mode {
4444
/** Are we looking for cyclic references? */
4545
val CheckCyclic: Mode = newMode(5, "CheckCyclic")
4646

47+
/** We are in arguments of HOAS pattern in quote pattern matching
48+
* e.g. x, y, z in a quote pattern '{ ... $a(x, y, z) ... }
49+
*
50+
* This mode keep typer from inserting contextual parameters to a contextual method without arguments.
51+
* (See tests/run-macros/i17905 for motivating examples)
52+
*/
53+
val InQuotePatternHoasArgs: Mode = newMode(6, "InQuotePatternHoasArgs")
54+
4755
/** We are in a pattern alternative */
4856
val InPatternAlternative: Mode = newMode(7, "InPatternAlternative")
4957

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,14 @@ trait QuotesAndSplices {
112112
if isFullyDefined(pt, ForceDegree.flipBottom) then
113113
def patternOuterContext(ctx: Context): Context =
114114
if (ctx.mode.is(Mode.QuotedPattern)) patternOuterContext(ctx.outer) else ctx
115-
val typedArgs = tree.args.map {
116-
case arg: untpd.Ident =>
117-
typedExpr(arg)
118-
case arg =>
119-
report.error("Open pattern expected an identifier", arg.srcPos)
120-
EmptyTree
115+
val typedArgs = withMode(Mode.InQuotePatternHoasArgs) {
116+
tree.args.map {
117+
case arg: untpd.Ident =>
118+
typedExpr(arg)
119+
case arg =>
120+
report.error("Open pattern expected an identifier", arg.srcPos)
121+
EmptyTree
122+
}
121123
}
122124
for arg <- typedArgs if arg.symbol.is(Mutable) do // TODO support these patterns. Possibly using scala.quoted.util.Var
123125
report.error("References to `var`s cannot be used in higher-order pattern", arg.srcPos)

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4106,8 +4106,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
41064106
wtp match {
41074107
case wtp: ExprType =>
41084108
readaptSimplified(tree.withType(wtp.resultType))
4109-
case wtp: MethodType if wtp.isImplicitMethod &&
4110-
({ resMatch = constrainResult(tree.symbol, wtp, sharpenedPt); resMatch } || !functionExpected) =>
4109+
case wtp: MethodType
4110+
if wtp.isImplicitMethod
4111+
&& ({ resMatch = constrainResult(tree.symbol, wtp, sharpenedPt); resMatch} || !functionExpected)
4112+
&& !ctx.mode.is(Mode.InQuotePatternHoasArgs) =>
41114113
if (resMatch || ctx.mode.is(Mode.ImplicitsEnabled))
41124114
adaptNoArgsImplicitMethod(wtp)
41134115
else

compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ object QuoteMatcher {
566566
* f has a method type `(x: Int): Int` and `f` maps to `g`, `p` should hold
567567
* `g.apply(0)` because the type of `g` is `Int => Int` due to eta expansion.
568568
*/
569-
case Apply(fun, args) if env.contains(tree.symbol) => transform(fun).select(nme.apply).appliedToArgs(args)
569+
case Apply(fun, args) if env.contains(tree.symbol) => transform(fun).select(nme.apply).appliedToArgs(args.map(transform))
570570
case tree: Ident => env.get(tree.symbol).flatMap(argsMap.get).getOrElse(tree)
571571
case tree => super.transform(tree)
572572
}.transform(tree)

tests/run-macros/i17905.check

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
case 1: [matched 1st case] another_given outside
2+
case 2: [matched 2nd case] given outside
3+
case 3: [matched 1st case] another_given outside

tests/run-macros/i17905/Macro_1.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import scala.quoted.*
2+
3+
inline def testCtxParam(inline body: Any) = ${ testCtxParamImpl('body) }
4+
def testCtxParamImpl(body: Expr[Any])(using Quotes): Expr[String] =
5+
body match
6+
case '{ given i: String = "given"; def g(using s: String) = "placeholder"; $a(g, i): String } =>
7+
'{ $a(((s: String) ?=> s"[matched 1st case] ${s}"), "another_given") }
8+
case '{ def g(using s: String) = "placeholder"; $a(g): String } =>
9+
'{ $a((s: String) ?=> s"[matched 2nd case] ${s}") }
10+
case _ => Expr("not matched")

tests/run-macros/i17905/Test_2.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@main def Test: Unit =
2+
println("case 1: " + testCtxParam { given String = "given"; def f(using t: String) = "placeholder"; f + " outside" })
3+
given String = "given"
4+
println("case 2: " + testCtxParam { def f(using t: String) = "placeholder"; f + " outside" })
5+
/* This is expected to match the first case. The current QuoteMatcher identifies a function with a contextual function. */
6+
println("case 3: " + testCtxParam { given i: String = "given"; def a(x: String) = "placeholder"; a(i) + " outside" } )

0 commit comments

Comments
 (0)