Skip to content

Commit 1ef1765

Browse files
Merge pull request #8876 from dotty-staging/add-syntax-for-higher-order-quote-pattern-holes
Add syntax for higher order quote pattern holes
2 parents 5404cc7 + 3ea2841 commit 1ef1765

File tree

21 files changed

+276
-136
lines changed

21 files changed

+276
-136
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -933,7 +933,7 @@ object desugar {
933933
def quotedPatternTypeDef(tree: TypeDef)(implicit ctx: Context): TypeDef = {
934934
assert(ctx.mode.is(Mode.QuotedPattern))
935935
if (tree.name.startsWith("$") && !tree.isBackquoted) {
936-
val patternBindHoleAnnot = New(ref(defn.InternalQuoted_patternTypeAnnot.typeRef)).withSpan(tree.span)
936+
val patternBindHoleAnnot = New(ref(defn.InternalQuotedMatcher_patternTypeAnnot.typeRef)).withSpan(tree.span)
937937
val mods = tree.mods.withAddedAnnotation(patternBindHoleAnnot)
938938
tree.withMods(mods)
939939
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
103103
}
104104
case class Throw(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree
105105
case class Quote(quoted: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree
106-
case class Splice(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree
106+
case class Splice(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree {
107+
def isInBraces: Boolean = span.end != expr.span.end
108+
}
107109
case class TypSplice(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TypTree
108110
case class ForYield(enums: List[Tree], expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree
109111
case class ForDo(enums: List[Tree], body: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -694,11 +694,14 @@ class Definitions {
694694
@tu lazy val InternalQuoted_exprSplice : Symbol = InternalQuotedModule.requiredMethod("exprSplice")
695695
@tu lazy val InternalQuoted_exprNestedSplice : Symbol = InternalQuotedModule.requiredMethod("exprNestedSplice")
696696
@tu lazy val InternalQuoted_typeQuote : Symbol = InternalQuotedModule.requiredMethod("typeQuote")
697-
@tu lazy val InternalQuoted_patternHole: Symbol = InternalQuotedModule.requiredMethod("patternHole")
698-
@tu lazy val InternalQuoted_patternTypeAnnot: ClassSymbol = InternalQuotedModule.requiredClass("patternType")
699697
@tu lazy val InternalQuoted_QuoteTypeTagAnnot: ClassSymbol = InternalQuotedModule.requiredClass("quoteTypeTag")
700-
@tu lazy val InternalQuoted_fromAboveAnnot: ClassSymbol = InternalQuotedModule.requiredClass("fromAbove")
701698

699+
@tu lazy val InternalQuotedMatcher: Symbol = ctx.requiredModule("scala.internal.quoted.Matcher")
700+
@tu lazy val InternalQuotedMatcher_patternHole: Symbol = InternalQuotedMatcher.requiredMethod("patternHole")
701+
@tu lazy val InternalQuotedMatcher_patternHigherOrderHole: Symbol = InternalQuotedMatcher.requiredMethod("patternHigherOrderHole")
702+
@tu lazy val InternalQuotedMatcher_higherOrderHole: Symbol = InternalQuotedMatcher.requiredMethod("higherOrderHole")
703+
@tu lazy val InternalQuotedMatcher_patternTypeAnnot: ClassSymbol = InternalQuotedMatcher.requiredClass("patternType")
704+
@tu lazy val InternalQuotedMatcher_fromAboveAnnot: ClassSymbol = InternalQuotedMatcher.requiredClass("fromAbove")
702705

703706
@tu lazy val InternalQuotedExprModule: Symbol = ctx.requiredModule("scala.internal.quoted.Expr")
704707
@tu lazy val InternalQuotedExpr_unapply: Symbol = InternalQuotedExprModule.requiredMethod(nme.unapply)

compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1982,9 +1982,10 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend
19821982
def Definitions_TupleClass(arity: Int): Symbol = defn.TupleType(arity).classSymbol.asClass
19831983
def Definitions_isTupleClass(sym: Symbol): Boolean = defn.isTupleClass(sym)
19841984

1985-
def Definitions_InternalQuoted_patternHole: Symbol = defn.InternalQuoted_patternHole
1986-
def Definitions_InternalQuoted_patternTypeAnnot: Symbol = defn.InternalQuoted_patternTypeAnnot
1987-
def Definitions_InternalQuoted_fromAboveAnnot: Symbol = defn.InternalQuoted_fromAboveAnnot
1985+
def Definitions_InternalQuotedMatcher_patternHole: Symbol = defn.InternalQuotedMatcher_patternHole
1986+
def Definitions_InternalQuotedMatcher_higherOrderHole: Symbol = defn.InternalQuotedMatcher_higherOrderHole
1987+
def Definitions_InternalQuotedMatcher_patternTypeAnnot: Symbol = defn.InternalQuotedMatcher_patternTypeAnnot
1988+
def Definitions_InternalQuotedMatcher_fromAboveAnnot: Symbol = defn.InternalQuotedMatcher_fromAboveAnnot
19881989

19891990
// Types
19901991

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

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,27 @@ trait QuotesAndSplices {
119119
def typedAppliedSplice(tree: untpd.Apply, pt: Type)(using Context): Tree = {
120120
assert(ctx.mode.is(Mode.QuotedPattern))
121121
val untpd.Apply(splice: untpd.Splice, args) = tree
122-
if (isFullyDefined(pt, ForceDegree.flipBottom)) then
122+
if !isFullyDefined(pt, ForceDegree.flipBottom) then
123+
ctx.error(i"Type must be fully defined.", splice.sourcePos)
124+
tree.withType(UnspecifiedErrorType)
125+
else if splice.isInBraces then // ${x}(...) match an application
123126
val typedArgs = args.map(arg => typedExpr(arg))
124127
val argTypes = typedArgs.map(_.tpe.widenTermRefExpr)
125128
val splice1 = typedSplice(splice, defn.FunctionOf(argTypes, pt))
126129
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+
else // $x(...) higher-order quasipattern
131+
val typedArgs = args.map {
132+
case arg: untpd.Ident =>
133+
typedExpr(arg)
134+
case arg =>
135+
ctx.error("Open patttern exprected an identifier", arg.sourcePos)
136+
EmptyTree
137+
}
138+
if args.isEmpty then
139+
ctx.error("Missing arguments for open pattern", tree.sourcePos)
140+
val argTypes = typedArgs.map(_.tpe.widenTermRefExpr)
141+
val typedPat = typedSplice(splice, defn.FunctionOf(argTypes, pt))
142+
ref(defn.InternalQuotedMatcher_patternHigherOrderHole).appliedToType(pt).appliedTo(typedPat, SeqLiteral(typedArgs, TypeTree(defn.AnyType)))
130143
}
131144

132145
/** Translate ${ t: Type[T] }` into type `t.splice` while tracking the quotation level in the context */
@@ -154,7 +167,7 @@ trait QuotesAndSplices {
154167
case pt: TypeBounds => pt
155168
case _ => TypeBounds.empty
156169
val typeSym = ctx.newSymbol(spliceOwner(ctx), name, EmptyFlags, typeSymInfo, NoSymbol, tree.expr.span)
157-
typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuoted_patternTypeAnnot.typeRef)).withSpan(tree.expr.span)))
170+
typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuotedMatcher_patternTypeAnnot.typeRef)).withSpan(tree.expr.span)))
158171
val pat = typedPattern(tree.expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))(
159172
using spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx)))
160173
pat.select(tpnme.splice)
@@ -224,8 +237,16 @@ trait QuotesAndSplices {
224237
val exprTpt = AppliedTypeTree(TypeTree(defn.QuotedExprClass.typeRef), tpt1 :: Nil)
225238
val newSplice = ref(defn.InternalQuoted_exprSplice).appliedToType(tpt1.tpe).appliedTo(Typed(pat, exprTpt))
226239
transform(newSplice)
240+
case Apply(TypeApply(fn, targs), Apply(sp, pat :: Nil) :: args :: Nil) if fn.symbol == defn.InternalQuotedMatcher_patternHigherOrderHole =>
241+
try ref(defn.InternalQuotedMatcher_higherOrderHole.termRef).appliedToTypeTrees(targs).appliedTo(args).withSpan(tree.span)
242+
finally {
243+
val patType = pat.tpe.widen
244+
val patType1 = patType.translateFromRepeated(toArray = false)
245+
val pat1 = if (patType eq patType1) pat else pat.withType(patType1)
246+
patBuf += pat1
247+
}
227248
case Apply(fn, pat :: Nil) if fn.symbol == defn.InternalQuoted_exprSplice =>
228-
try ref(defn.InternalQuoted_patternHole.termRef).appliedToType(tree.tpe).withSpan(tree.span)
249+
try ref(defn.InternalQuotedMatcher_patternHole.termRef).appliedToType(tree.tpe).withSpan(tree.span)
229250
finally {
230251
val patType = pat.tpe.widen
231252
val patType1 = patType.translateFromRepeated(toArray = false)
@@ -241,7 +262,7 @@ trait QuotesAndSplices {
241262
else
242263
tree
243264
case tdef: TypeDef =>
244-
if tdef.symbol.hasAnnotation(defn.InternalQuoted_patternTypeAnnot) then
265+
if tdef.symbol.hasAnnotation(defn.InternalQuotedMatcher_patternTypeAnnot) then
245266
transformTypeBindingTypeDef(tdef, typePatBuf)
246267
else if tdef.symbol.isClass then
247268
val kind = if tdef.symbol.is(Module) then "objects" else "classes"
@@ -276,7 +297,7 @@ trait QuotesAndSplices {
276297

277298
private def transformTypeBindingTypeDef(tdef: TypeDef, buff: mutable.Builder[Tree, List[Tree]])(using Context): Tree = {
278299
if (variance == -1)
279-
tdef.symbol.addAnnotation(Annotation(New(ref(defn.InternalQuoted_fromAboveAnnot.typeRef)).withSpan(tdef.span)))
300+
tdef.symbol.addAnnotation(Annotation(New(ref(defn.InternalQuotedMatcher_fromAboveAnnot.typeRef)).withSpan(tdef.span)))
280301
val bindingType = getBinding(tdef.symbol).symbol.typeRef
281302
val bindingTypeTpe = AppliedType(defn.QuotedTypeClass.typeRef, bindingType :: Nil)
282303
val bindName = tdef.name.toString.stripPrefix("$").toTermName

docs/docs/reference/metaprogramming/macros.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,5 +744,51 @@ trait Show[-T] {
744744
}
745745
```
746746

747+
#### Open code patterns
748+
749+
Quote pattern matching also provides higher-order patterns to match open terms. If a quoted term contains a definition,
750+
then the rest of the quote can refer to this definition.
751+
```
752+
'{
753+
val x: Int = 4
754+
x * x
755+
}
756+
```
757+
758+
To match such a term we need to match the definition and the rest of the code, but we need to expicilty state that the rest of the code may refer to this definition.
759+
```scala
760+
case '{ val y: Int = $x; $body(y): Int } =>
761+
```
762+
Here `$x` will match any closed expression while `$body(y)` will match expression that is closed under `y`. Then
763+
the subxpression of type `Expr[Int]` is bound to `body` as an `Expr[Int => Int]`. The extra argument represents the references to `y`. Usually this expression is used in compination with `Expr.betaReduce` to replace the extra argument.
764+
765+
```scala
766+
inline def eval(inline e: Int): Int = ${ evalExpr('e) }
767+
768+
private def evalExpr(using QuoteContext)(e: Expr[Int]): Expr[Int] = {
769+
e match {
770+
case '{ val y: Int = $x; $body(y): Int } =>
771+
// body: Expr[Int => Int] where the argument represents references to y
772+
evalExpr(Expr.betaReduce(body)(evalExpr(x)))
773+
case '{ ($x: Int) * ($y: Int) } =>
774+
(x, y) match
775+
case (Const(a), Const(b)) => Expr(a * b)
776+
case _ => e
777+
case _ => e
778+
}
779+
}
780+
```
781+
782+
```scala
783+
eval { // expands to the code: (16: Int)
784+
val x: Int = 4
785+
x * x
786+
}
787+
```
788+
789+
We can also close over several bindings using `$b(a1, a2, ..., an)`.
790+
To match an actual application we can use braces on the function part `${b}(a1, a2, ..., an)`.
791+
792+
747793
### More details
748794
[More details](./macros-spec.md)

library/src-bootstrapped/scala/internal/quoted/CompileTime.scala

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,6 @@ object CompileTime {
2424
@compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.typeQuote`")
2525
def typeQuote[T <: AnyKind]: QuoteContext ?=> Type[T] = ???
2626

27-
/** A splice in a quoted pattern is desugared by the compiler into a call to this method */
28-
@compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.patternHole`")
29-
def patternHole[T]: T = ???
30-
31-
// TODO remove
32-
/** A splice of a name in a quoted pattern is desugared by wrapping getting this annotation */
33-
@compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.patternBindHole`")
34-
class patternBindHole extends Annotation
35-
36-
/** A splice of a name in a quoted pattern is that marks the definition of a type splice */
37-
@compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.patternType`")
38-
class patternType extends Annotation
39-
40-
/** A type pattern that must be aproximated from above */
41-
@compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.fromAbove`")
42-
class fromAbove extends Annotation
43-
4427
/** Artifact of pickled type splices
4528
*
4629
* During quote reification a quote `'{ ... F[$t] ... }` will be transformed into

library/src/scala/internal/quoted/Matcher.scala

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package scala.internal.quoted
22

33
import scala.annotation.internal.sharable
4+
import scala.annotation.{Annotation, compileTimeOnly}
45

56
import scala.quoted._
67

@@ -94,7 +95,32 @@ import scala.quoted._
9495
*
9596
* ```
9697
*/
97-
private[quoted] object Matcher {
98+
object Matcher {
99+
100+
/** A splice in a quoted pattern is desugared by the compiler into a call to this method */
101+
@compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.patternHole`")
102+
def patternHole[T]: T = ???
103+
104+
@compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.patternHigherOrderHole`")
105+
/** A higher order splice in a quoted pattern is desugared by the compiler into a call to this method */
106+
def patternHigherOrderHole[U](pat: Any, args: Any*): U = ???
107+
108+
@compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.higherOrderHole`")
109+
/** A higher order splice in a quoted pattern is desugared by the compiler into a call to this method */
110+
def higherOrderHole[U](args: Any*): U = ???
111+
112+
// TODO remove
113+
/** A splice of a name in a quoted pattern is desugared by wrapping getting this annotation */
114+
@compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.patternBindHole`")
115+
class patternBindHole extends Annotation
116+
117+
/** A splice of a name in a quoted pattern is that marks the definition of a type splice */
118+
@compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.patternType`")
119+
class patternType extends Annotation
120+
121+
/** A type pattern that must be aproximated from above */
122+
@compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.fromAbove`")
123+
class fromAbove extends Annotation
98124

99125
class QuoteMatcher[QCtx <: QuoteContext & Singleton](using val qctx: QCtx) {
100126
// TODO improve performance
@@ -164,13 +190,13 @@ private[quoted] object Matcher {
164190
private def hasFromAboveAnnotation(sym: Symbol) = sym.annots.exists(isFromAboveAnnotation)
165191

166192
private def isPatternTypeAnnotation(tree: Tree): Boolean = tree match {
167-
case New(tpt) => tpt.symbol == internal.Definitions_InternalQuoted_patternTypeAnnot
168-
case annot => annot.symbol.owner == internal.Definitions_InternalQuoted_patternTypeAnnot
193+
case New(tpt) => tpt.symbol == internal.Definitions_InternalQuotedMatcher_patternTypeAnnot
194+
case annot => annot.symbol.owner == internal.Definitions_InternalQuotedMatcher_patternTypeAnnot
169195
}
170196

171197
private def isFromAboveAnnotation(tree: Tree): Boolean = tree match {
172-
case New(tpt) => tpt.symbol == internal.Definitions_InternalQuoted_fromAboveAnnot
173-
case annot => annot.symbol.owner == internal.Definitions_InternalQuoted_fromAboveAnnot
198+
case New(tpt) => tpt.symbol == internal.Definitions_InternalQuotedMatcher_fromAboveAnnot
199+
case annot => annot.symbol.owner == internal.Definitions_InternalQuotedMatcher_fromAboveAnnot
174200
}
175201

176202
/** Check that all trees match with `mtch` and concatenate the results with &&& */
@@ -226,23 +252,23 @@ private[quoted] object Matcher {
226252
/* Term hole */
227253
// Match a scala.internal.Quoted.patternHole typed as a repeated argument and return the scrutinee tree
228254
case (scrutinee @ Typed(s, tpt1), Typed(TypeApply(patternHole, tpt :: Nil), tpt2))
229-
if patternHole.symbol == internal.Definitions_InternalQuoted_patternHole &&
255+
if patternHole.symbol == internal.Definitions_InternalQuotedMatcher_patternHole &&
230256
s.tpe <:< tpt.tpe &&
231257
tpt2.tpe.derivesFrom(defn.RepeatedParamClass) =>
232258
matched(scrutinee.seal)
233259

234260
/* Term hole */
235261
// Match a scala.internal.Quoted.patternHole and return the scrutinee tree
236262
case (ClosedPatternTerm(scrutinee), TypeApply(patternHole, tpt :: Nil))
237-
if patternHole.symbol == internal.Definitions_InternalQuoted_patternHole &&
263+
if patternHole.symbol == internal.Definitions_InternalQuotedMatcher_patternHole &&
238264
scrutinee.tpe <:< tpt.tpe =>
239265
matched(scrutinee.seal)
240266

241267
/* Higher order term hole */
242268
// Matches an open term and wraps it into a lambda that provides the free variables
243-
// TODO do not encode with `hole`. Maybe use `higherOrderHole[(T1, ..., Tn) => R]((x1: T1, ..., xn: Tn)): R`
244-
case (scrutinee, pattern @ Apply(Select(TypeApply(patternHole, List(Inferred())), "apply"), args0 @ IdentArgs(args)))
245-
if patternHole.symbol == internal.Definitions_InternalQuoted_patternHole =>
269+
case (scrutinee, pattern @ Apply(TypeApply(Ident("higherOrderHole"), List(Inferred())), Repeated(args, _) :: Nil))
270+
if pattern.symbol == internal.Definitions_InternalQuotedMatcher_higherOrderHole =>
271+
246272
def bodyFn(lambdaArgs: List[Tree]): Tree = {
247273
val argsMap = args.map(_.symbol).zip(lambdaArgs.asInstanceOf[List[Term]]).toMap
248274
new TreeMap {
@@ -252,8 +278,11 @@ private[quoted] object Matcher {
252278
case tree => super.transformTerm(tree)
253279
}.transformTree(scrutinee)
254280
}
255-
val names = args.map(_.name)
256-
val argTypes = args0.map(x => x.tpe.widenTermRefExpr)
281+
val names = args.map {
282+
case Block(List(DefDef("$anonfun", _, _, _, Some(Apply(Ident(name), _)))), _) => name
283+
case arg => arg.symbol.name
284+
}
285+
val argTypes = args.map(x => x.tpe.widenTermRefExpr)
257286
val resType = pattern.tpe
258287
val res = Lambda(MethodType(names)(_ => argTypes, _ => resType), bodyFn)
259288
matched(res.seal)

library/src/scala/tasty/reflect/CompilerInterface.scala

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1511,14 +1511,17 @@ trait CompilerInterface {
15111511
def Definitions_TupleClass(arity: Int): Symbol
15121512
def Definitions_isTupleClass(sym: Symbol): Boolean
15131513

1514-
/** Symbol of scala.internal.Quoted.patternHole */
1515-
def Definitions_InternalQuoted_patternHole: Symbol
1514+
/** Symbol of scala.internal.CompileTime.patternHole */
1515+
def Definitions_InternalQuotedMatcher_patternHole: Symbol
15161516

1517-
/** Symbol of scala.internal.Quoted.patternType */
1518-
def Definitions_InternalQuoted_patternTypeAnnot: Symbol
1517+
/** Symbol of scala.internal.CompileTime.higherOrderHole */
1518+
def Definitions_InternalQuotedMatcher_higherOrderHole: Symbol
15191519

1520-
/** Symbol of scala.internal.Quoted.fromAbove */
1521-
def Definitions_InternalQuoted_fromAboveAnnot: Symbol
1520+
/** Symbol of scala.internal.CompileTime.patternType */
1521+
def Definitions_InternalQuotedMatcher_patternTypeAnnot: Symbol
1522+
1523+
/** Symbol of scala.internal.CompileTime.fromAbove */
1524+
def Definitions_InternalQuotedMatcher_fromAboveAnnot: Symbol
15221525

15231526
def Definitions_UnitType: Type
15241527
def Definitions_ByteType: Type
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import scala.quoted._
2+
3+
def f(using QuoteContext)(x: Expr[Any]) = x match {
4+
case '{ identity($y(x)) } => // error: access to value x from wrong staging level
5+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import scala.quoted._
2+
3+
def f(using QuoteContext)(x: Expr[Any]) = x match {
4+
case '{ val a: Int = 3; $y(identity(a)) } => // error: Exprected an identifier
5+
case '{ identity($y()) } => // error: Missing arguments for open pattern
6+
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import scala.quoted._
22

33
def f(x: Expr[Int])(using QuoteContext) = x match {
4-
case '{ $f($a: Int): Int } =>
4+
case '{ ${f}($a: Int): Int } =>
55
val f1: Expr[Int => Int] = f
66
val a1: Expr[Int] = a
7-
case '{ def a: Int = $f($b: Int); () } =>
7+
case '{ def a: Int = ${f}($b: Int); () } =>
88
val f1: Expr[Int => Int] = f
99
val b1: Expr[Int] = b
10-
case '{ val a: Int = 3; $f(a): Int } =>
10+
case '{ val a: Int = 3; ${f}(a): Int } =>
1111
val f1: Expr[Int => Int] = f
1212
}

0 commit comments

Comments
 (0)