Skip to content

Update scala.quoted given syntax #8211

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions docs/docs/reference/metaprogramming/erased-terms.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ m.turnedOn.turnedOn // ERROR
// State is must be Off
```

Note that in the code above the actual implicit arguments for `IsOff` are never
Note that in the code above the actual context arguments for `IsOff` are never
used at runtime; they serve only to establish the right constraints at compile
time. As these terms are never used at runtime there is not real need to have
them around, but they still need to be present in some form in the generated
Expand Down Expand Up @@ -99,15 +99,15 @@ The following example is an extended implementation of a simple state machine
which can be in a state `On` or `Off`. The machine can change state from `Off`
to `On` with `turnedOn` only if it is currently `Off`, conversely from `On` to
`Off` with `turnedOff` only if it is currently `On`. These last constraint are
captured with the `IsOff[S]` and `IsOn[S]` implicit evidence only exist for
captured with the `IsOff[S]` and `IsOn[S]` given evidence only exist for
`IsOff[Off]` and `IsOn[On]`. For example, not allowing calling `turnedOff` on in
an `Off` state as we would require an evidence `IsOn[Off]` that will not be
found.

As the implicit evidences of `turnedOn` and `turnedOff` are not used in the
As the given evidences of `turnedOn` and `turnedOff` are not used in the
bodies of those functions we can mark them as `erased`. This will remove the
evidence parameters at runtime, but we would still evaluate the `isOn` and
`isOff` implicits that were found as arguments. As `isOn` and `isOff` are not
`isOff` givens that were found as arguments. As `isOn` and `isOff` are not
used except as `erased` arguments, we can mark them as `erased`, hence removing
the evaluation of the `isOn` and `isOff` evidences.

Expand All @@ -121,21 +121,21 @@ final class Off extends State
@implicitNotFound("State is must be Off")
class IsOff[S <: State]
object IsOff {
// def isOff will not be called at runtime for turnedOn, the compiler will only require that this evidence exists
implicit def isOff: IsOff[Off] = new IsOff[Off]
// will not be called at runtime for turnedOn, the compiler will only require that this evidence exists
given IsOff[Off] = new IsOff[Off]
}

@implicitNotFound("State is must be On")
class IsOn[S <: State]
object IsOn {
// erased val isOn will not exist at runtime, the compiler will only require that this evidence exists at compile time
erased implicit val isOn: IsOn[On] = new IsOn[On]
// will not exist at runtime, the compiler will only require that this evidence exists at compile time
erased given IsOn[On] = new IsOn[On]
}

class Machine[S <: State] private {
// ev will disappear from both functions
def turnedOn(given erased ev: IsOff[S]): Machine[On] = new Machine[On]
def turnedOff(given erased ev: IsOn[S]): Machine[Off] = new Machine[Off]
def turnedOn(using erased ev: IsOff[S]): Machine[On] = new Machine[On]
def turnedOff(using erased ev: IsOn[S]): Machine[Off] = new Machine[Off]
}

object Machine {
Expand Down
28 changes: 14 additions & 14 deletions docs/docs/reference/metaprogramming/macros.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ import scala.quoted._
inline def assert(expr: => Boolean): Unit =
${ assertImpl('expr) }

def assertImpl(expr: Expr[Boolean])(given QuoteContext) = '{
def assertImpl(expr: Expr[Boolean])(using QuoteContext) = '{
if (!$expr)
throw new AssertionError(s"failed assertion: ${${ showExpr(expr) }}")
}

def showExpr(expr: Expr[Boolean])(given QuoteContext): Expr[String] =
def showExpr(expr: Expr[Boolean])(using QuoteContext): Expr[String] =
'{ "<some source code>" } // Better implementation later in this document
```

Expand Down Expand Up @@ -171,7 +171,7 @@ Indeed, the definition of `reflect` above uses `T` in the next stage, there is a
quote but no splice between the parameter binding of `T` and its
usage. But the code can be rewritten by adding a binding of a `Type[T]` tag:
```scala
def reflect[T, U](f: Expr[T] => Expr[U])(given t: Type[T]): Expr[T => U] =
def reflect[T, U](f: Expr[T] => Expr[U])(using t: Type[T]): Expr[T => U] =
'{ (x: $t) => ${ f('x) } }
```
In this version of `reflect`, the type of `x` is now the result of
Expand Down Expand Up @@ -250,7 +250,7 @@ package quoted

object Expr {
...
def apply[T: Liftable](x: T)(given QuoteContext): Expr[T] = summon[Liftable[T]].toExpr(x)
def apply[T: Liftable](x: T)(using QuoteContext): Expr[T] = summon[Liftable[T]].toExpr(x)
...
}
```
Expand Down Expand Up @@ -304,7 +304,7 @@ analogue of lifting.

Using lifting, we can now give the missing definition of `showExpr` in the introductory example:
```scala
def showExpr[T](expr: Expr[T])(given QuoteContext): Expr[String] = {
def showExpr[T](expr: Expr[T])(using QuoteContext): Expr[String] = {
val code: String = expr.show
Expr(code)
}
Expand Down Expand Up @@ -425,12 +425,12 @@ implementation of the `power` function that makes use of a statically known expo
```scala
inline def power(x: Double, inline n: Int) = ${ powerCode('x, 'n) }

private def powerCode(x: Expr[Double], n: Expr[Int])(given QuoteContext): Expr[Double] =
private def powerCode(x: Expr[Double], n: Expr[Int])(using QuoteContext): Expr[Double] =
n.getValue match
case Some(m) => powerCode(x, m)
case None => '{ Math.pow($x, $y) }

private def powerCode(x: Expr[Double], n: Int)(given QuoteContext): Expr[Double] =
private def powerCode(x: Expr[Double], n: Int)(using QuoteContext): Expr[Double] =
if (n == 0) '{ 1.0 }
else if (n == 1) x
else if (n % 2 == 0) '{ val y = $x * $x; ${ powerCode('y, n / 2) } }
Expand Down Expand Up @@ -570,7 +570,7 @@ in a quote context. For this we simply provide `scala.quoted.matching.summonExpr
```scala
inline def setFor[T]: Set[T] = ${ setForExpr[T] }

def setForExpr[T: Type](given QuoteContext): Expr[Set[T]] = {
def setForExpr[T: Type](using QuoteContext): Expr[Set[T]] = {
summonExpr[Ordering[T]] match {
case Some(ord) => '{ new TreeSet[T]()($ord) }
case _ => '{ new HashSet[T] }
Expand All @@ -587,7 +587,7 @@ inline method that can calculate either a value of type `Int` or a value of type
```scala
inline def defaultOf(inline str: String) <: Any = ${ defaultOfImpl('str) }

def defaultOfImpl(strExpr: Expr[String])(given QuoteContext): Expr[Any] =
def defaultOfImpl(strExpr: Expr[String])(using QuoteContext): Expr[Any] =
strExpr.value match
case "int" => '{1}
case "string" => '{"a"}
Expand Down Expand Up @@ -627,7 +627,7 @@ In `scala.quoted.matching` contains object that can help extract values from `Ex
These could be used in the following way to optimize any call to `sum` that has statically known values.
```scala
inline def sum(args: =>Int*): Int = ${ sumExpr('args) }
private def sumExpr(argsExpr: Expr[Seq[Int]])(given QuoteContext): Expr[Int] = argsExpr.underlyingArgument match {
private def sumExpr(argsExpr: Expr[Seq[Int]])(using QuoteContext): Expr[Int] = argsExpr.underlyingArgument match {
case ConstSeq(args) => // args is of type Seq[Int]
Expr(args.sum) // precompute result of sum
case ExprSeq(argExprs) => // argExprs is of type Seq[Expr[Int]]
Expand Down Expand Up @@ -660,7 +660,7 @@ optimize {
```scala
def sum(args: Int*): Int = args.sum
inline def optimize(arg: Int): Int = ${ optimizeExpr('arg) }
private def optimizeExpr(body: Expr[Int])(given QuoteContext): Expr[Int] = body match {
private def optimizeExpr(body: Expr[Int])(using QuoteContext): Expr[Int] = body match {
// Match a call to sum without any arguments
case '{ sum() } => Expr(0)
// Match a call to sum with an argument $n of type Int. n will be the Expr[Int] representing the argument.
Expand All @@ -669,7 +669,7 @@ private def optimizeExpr(body: Expr[Int])(given QuoteContext): Expr[Int] = body
case '{ sum(${ExprSeq(args)}: _*) } => sumExpr(args)
case body => body
}
private def sumExpr(args1: Seq[Expr[Int]])(given QuoteContext): Expr[Int] = {
private def sumExpr(args1: Seq[Expr[Int]])(using QuoteContext): Expr[Int] = {
def flatSumArgs(arg: Expr[Int]): Seq[Expr[Int]] = arg match {
case '{ sum(${ExprSeq(subArgs)}: _*) } => subArgs.flatMap(flatSumArgs)
case arg => Seq(arg)
Expand All @@ -692,7 +692,7 @@ private def sumExpr(args1: Seq[Expr[Int]])(given QuoteContext): Expr[Int] = {
Sometimes it is necessary to get a more precise type for an expression. This can be achived using the following pattern match.

```scala
def f(exp: Expr[Any])(given QuoteContext) =
def f(exp: Expr[Any])(using QuoteContext) =
expr match
case '{ $x: $t } =>
// If the pattern match succeeds, then there is some type `T` such that
Expand All @@ -707,7 +707,7 @@ This might be used to then perform an implicit search as in:
```scala
inline def (sc: StringContext).showMe(args: =>Any*): String = ${ showMeExpr('sc, 'args) }

private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(given qctx: QuoteContext): Expr[String] = {
private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(using qctx: QuoteContext): Expr[String] = {
argsExpr match {
case ExprSeq(argExprs) =>
val argShowedExprs = argExprs.map {
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/metaprogramming/staging.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ On the other hand `withQuoteContext` provides a `QuoteContext` without evauating
```scala
package scala.quoted.staging

def run[T](expr:(given QuoteContext) => Expr[T])(given toolbox: Toolbox): T = ...
def run[T](expr: QuoteContext ?=> Expr[T])(using toolbox: Toolbox): T = ...

def withQuoteContext[T](thunk:(given QuoteContext) => T)(given toolbox: Toolbox): T = ...
def withQuoteContext[T](thunk: QuoteContext ?=> T)(using toolbox: Toolbox): T = ...
```

## Create a new Dotty project with staging enabled
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/reference/metaprogramming/tasty-reflect.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import scala.quoted._

inline def natConst(x: => Int): Int = ${natConstImpl('{x})}

def natConstImpl(x: Expr[Int])(given qctx: QuoteContext): Expr[Int] = {
def natConstImpl(x: Expr[Int])(using qctx: QuoteContext): Expr[Int] = {
import qctx.tasty.{_, given}
...
}
Expand All @@ -43,7 +43,7 @@ respectively. It will also import all extractors and methods on TASTy Reflect
trees. For example the `Literal(_)` extractor used below.

```scala
def natConstImpl(x: Expr[Int])(given qctx: QuoteContext): Expr[Int] = {
def natConstImpl(x: Expr[Int])(using qctx: QuoteContext): Expr[Int] = {
import qctx.tasty.{_, given}
val xTree: Term = x.unseal
xTree match {
Expand Down Expand Up @@ -80,7 +80,7 @@ operation expression passed while calling the `macro` below.
```scala
inline def macro(param: => Boolean): Unit = ${ macroImpl('param) }

def macroImpl(param: Expr[Boolean])(given qctx: QuoteContext): Expr[Unit] = {
def macroImpl(param: Expr[Boolean])(using qctx: QuoteContext): Expr[Unit] = {
import qctx.tasty.{_, given}
import util._

Expand Down
8 changes: 4 additions & 4 deletions docs/docs/reference/other-new-features/tupled-function.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ The compiler will synthesize an instance of `TupledFunction[F, G]` if:
* `F` is a function type of arity `N`
* `G` is a function with a single tuple argument of size `N` and it's types are equal to the arguments of `F`
* The return type of `F` is equal to the return type of `G`
* `F` and `G` are the same kind of function (both are `(...) => R` or both are `(given ...) => R`)
* `F` and `G` are the same kind of function (both are `(...) => R` or both are `(...) ?=> R`)
* If only one of `F` or `G` is instantiated the second one is inferred.

Examples
Expand All @@ -43,7 +43,7 @@ Examples
* @tparam Args the tuple type with the same types as the function arguments of F
* @tparam R the return type of F
*/
def [F, Args <: Tuple, R](f: F).tupled(given tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f)
def [F, Args <: Tuple, R](f: F).tupled(using tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f)
```

`TupledFunction` can be used to generalize the `Function.untupled` methods to functions of any arities ([full example](https://github.com/lampepfl/dotty/blob/master/tests/run/tupled-function-untupled.scala))
Expand All @@ -58,7 +58,7 @@ def [F, Args <: Tuple, R](f: F).tupled(given tf: TupledFunction[F, Args => R]):
* @tparam Args the tuple type with the same types as the function arguments of F
* @tparam R the return type of F
*/
def [F, Args <: Tuple, R](f: Args => R).untupled(given tf: TupledFunction[F, Args => R]): F = tf.untupled(f)
def [F, Args <: Tuple, R](f: Args => R).untupled(using tf: TupledFunction[F, Args => R]): F = tf.untupled(f)
```

`TupledFunction` can also be used to generalize the [`Tuple1.compose`](https://github.com/lampepfl/dotty/blob/master/tests/run/tupled-function-compose.scala) and [`Tuple1.andThen`](https://github.com/lampepfl/dotty/blob/master/tests/run/tupled-function-andThen.scala) methods to compose functions of larger arities and with functions that return tuples.
Expand All @@ -72,7 +72,7 @@ def [F, Args <: Tuple, R](f: Args => R).untupled(given tf: TupledFunction[F, Arg
* @tparam GArgs the tuple type with the same types as the function arguments of G
* @tparam R the return type of F
*/
def [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F).compose(g: G)(given tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = {
def [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F).compose(g: G)(using tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = {
(x: GArgs) => tf.tupled(f)(tg.tupled(g)(x))
}
```
4 changes: 2 additions & 2 deletions library/src/scala/internal/quoted/CompileTime.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ object CompileTime {

/** A term quote is desugared by the compiler into a call to this method */
@compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.exprQuote`")
def exprQuote[T](x: T): (given QuoteContext) => Expr[T] = ???
def exprQuote[T](x: T): QuoteContext ?=> Expr[T] = ???

/** A term splice is desugared by the compiler into a call to this method */
@compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.exprSplice`")
def exprSplice[T](x: (given QuoteContext) => Expr[T]): T = ???
def exprSplice[T](x: QuoteContext ?=> Expr[T]): T = ???

/** A type quote is desugared by the compiler into a call to this method */
@compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.typeQuote`")
Expand Down
2 changes: 1 addition & 1 deletion library/src/scala/internal/quoted/Expr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ object Expr {
* @param qctx the current QuoteContext
* @return None if it did not match, `Some(tup)` if it matched where `tup` contains `Expr[Ti]``
*/
def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutineeExpr: scala.quoted.Expr[_])(implicit patternExpr: scala.quoted.Expr[_],
def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutineeExpr: scala.quoted.Expr[_])(using patternExpr: scala.quoted.Expr[_],
hasTypeSplices: Boolean, qctx: QuoteContext): Option[Tup] = {
import qctx.tasty.{_, given}
new Matcher.QuoteMatcher[qctx.type].termMatch(scrutineeExpr.unseal, patternExpr.unseal, hasTypeSplices).asInstanceOf[Option[Tup]]
Expand Down
Loading