Skip to content

Commit afc5af3

Browse files
committed
Support custom val symbol in ValDef.let
1 parent e23f815 commit afc5af3

File tree

5 files changed

+69
-0
lines changed

5 files changed

+69
-0
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,11 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
305305
val vdefs = terms.map(term => tpd.SyntheticValDef("x".toTermName, term)(using ctx1))
306306
val refs = vdefs.map(vdef => tpd.ref(vdef.symbol).asInstanceOf[Ref])
307307
Block(vdefs, body(refs))
308+
309+
def let(owner: Symbol, symbol: Symbol, rhs: Term)(body: Ref => Term): Term =
310+
val vdef = apply(symbol, Some(rhs)).changeOwner(owner)
311+
val ref = tpd.ref(vdef.symbol).asInstanceOf[Ref]
312+
Block(List(vdef), body(ref))
308313
end ValDef
309314

310315
given ValDefMethods: ValDefMethods with

library/src/scala/quoted/Quotes.scala

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,41 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
607607

608608
/** Creates a block `{ val x1 = <terms(0): Term>; ...; val xn = <terms(n-1): Term>; <body(List(x1, ..., xn)): Term> }` */
609609
def let(owner: Symbol, terms: List[Term])(body: List[Ref] => Term): Term
610+
611+
/**
612+
* Creates a block `{ val x = <rhs: Term>; <body(x): Term> }`
613+
* with the given symbol for the `val` (allowing to specify `Flags`).
614+
*
615+
* Usage:
616+
* ```scala sc:nocompile
617+
* val tpe = TypeRepr.of[String]
618+
*
619+
* val valSym = Symbol.newVal(
620+
* Symbol.spliceOwner,
621+
* "myValName",
622+
* tpe,
623+
* Flags.Lazy,
624+
* Symbol.noSymbol
625+
* )
626+
*
627+
* ValDef.let(Symbol.spliceOwner, valSym, Expr("foo")) { v =>
628+
* '{ println(v) }.asTerm
629+
* }
630+
* ```
631+
*
632+
* In this way, it's possible to create an `Ref` to the `val`
633+
* before its definition (required for recursive/lazy definitions).
634+
*
635+
* ```scala sc:nocompile
636+
* // After `tpe` and `valSym` but before `let` call
637+
* val earlyRef = Typed(Ref(valSym), Inferred(tpe))
638+
* ```
639+
*
640+
* @param symbol the `val` symbol
641+
* @see `Symbol.newVal`
642+
*/
643+
@experimental
644+
def let(owner: Symbol, symbol: Symbol, rhs: Term)(body: Ref => Term): Term
610645
}
611646

612647
/** Makes extension methods on `ValDef` available without any imports */

tests/run-macros/i13929.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
early=i13929
2+
vref=i13929

tests/run-macros/i13929/Macro_1.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import scala.quoted.*
2+
3+
inline def testSymLet[T](f: T) = ${ testSymLetImpl[T]('f) }
4+
5+
def testSymLetImpl[T: Type](f: Expr[T])(using Quotes): Expr[Unit] = {
6+
import quotes.reflect.*
7+
8+
val tpe = TypeRepr.of[T]
9+
10+
val valSym = Symbol.newVal(
11+
Symbol.spliceOwner,
12+
"myVal",
13+
tpe,
14+
Flags.Lazy,
15+
Symbol.noSymbol
16+
)
17+
18+
val earlyRef = Typed(Ref(valSym), Inferred(tpe))
19+
20+
ValDef.let(Symbol.spliceOwner, valSym, f) { vref =>
21+
'{
22+
println("early=" + ${earlyRef.asExpr}.toString())
23+
println("vref=" + ${vref.asExpr}.toString())
24+
}.asTerm
25+
}.asExprOf[Unit]
26+
}

tests/run-macros/i13929/Test_2.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@main def Test = testSymLet("i13929")

0 commit comments

Comments
 (0)