diff --git a/docs/docs/reference/principled-meta-programming.md b/docs/docs/reference/principled-meta-programming.md index ea11ac33c3fc..a10f1a312bd7 100644 --- a/docs/docs/reference/principled-meta-programming.md +++ b/docs/docs/reference/principled-meta-programming.md @@ -507,7 +507,7 @@ The compiler takes an environment that maps variable names to Scala `Expr`s. def compile(e: Exp, env: Map[String, Expr[Int]]): Expr[Int] = e match { case Num(n) => - n + n.toExpr case Plus(e1, e2) => ’(~compile(e1, env) + ~compile(e2, env)) case Var(x) => @@ -520,24 +520,25 @@ Running `compile(letExp, Map())` would yield the following Scala code: ’{ val y = 3; (2 + y) + 4 } -The body of the first clause, `case Num(n) => n`, looks suspicious. `n` -is declared as an `Int`, yet the result of `compile` is declared to be -`Expr[Int]`. Shouldn’t `n` be quoted? In fact this would not +The body of the first clause, `case Num(n) => n.toExpr`, looks suspicious. `n` +is declared as an `Int`, yet it is conveted to an `Expr[Int]` with `toExpr`. +Shouldn’t `n` be quoted? In fact this would not work since replacing `n` by `’n` in the clause would not be phase correct. -What happens instead "under the hood" is an implicit conversion: `n` -is expanded to `scala.quoted.Expr.toExpr(n)`. The `toExpr` conversion -is defined in the companion object of class `Expr` as follows: +What happens instead "under the hood" is an extension method `toExpr` is added: `n.toExpr` +is expanded to `new scala.quoted.LiftExprOps(n).toExpr`. The `toExpr` extension +is defined in the companion object of class `Liftable` as follows: - object Expr { - implicit def toExpr[T](x: T)(implicit ev: Liftable[T]): Expr[T] = - ev.toExpr(x) + package object quoted { + implicit class LiftExprOps[T](val x: T) extends AnyVal { + def toExpr(implicit ev: Liftable[T]): Expr[T] = ev.toExpr(x) + } } -The conversion says that values of types implementing the `Liftable` -type class can be converted ("lifted") automatically to `Expr` -values. Dotty comes with instance definitions of `Liftable` for +The extension says that values of types implementing the `Liftable` type class can be +converted ("lifted") to `Expr` values using `toExpr` when `scala.quoted._` is imported. +Dotty comes with instance definitions of `Liftable` for several types including `Boolean`, `String`, and all primitive number types. For example, `Int` values can be converted to `Expr[Int]` values by wrapping the value in a `Literal` tree node. This makes use @@ -570,7 +571,7 @@ a `List` is liftable if its element type is: implicit def ListIsLiftable[T: Liftable]: Liftable[List[T]] = new { def toExpr(xs: List[T]): Expr[List[T]] = xs match { - case x :: xs1 => ’(~implicitly[Liftable[T]].toExpr(x) :: ~toExpr(xs1)) + case x :: xs1 => ’(~x.toExpr :: ~toExpr(xs1)) case Nil => ’(Nil: List[T]) } } diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index c1e4cbf9ff7c..7bde768c5501 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -10,8 +10,6 @@ sealed abstract class Expr[T] { } object Expr { - implicit def toExpr[T](x: T)(implicit ev: Liftable[T]): Expr[T] = - ev.toExpr(x) implicit class AsFunction[T, U](private val f: Expr[T => U]) extends AnyVal { def apply(x: Expr[T]): Expr[U] = new Exprs.FunctionAppliedTo[T, U](f, x) @@ -25,7 +23,7 @@ object Expr { object Exprs { /** An Expr backed by a pickled TASTY tree */ final class TastyExpr[T](val tasty: Pickled, val args: Seq[Any]) extends Expr[T] { - override def toString(): String = s"Expr()" + override def toString: String = s"Expr()" } /** An Expr backed by a value. diff --git a/library/src/scala/quoted/Liftable.scala b/library/src/scala/quoted/Liftable.scala index 2419f44405e1..e36b9bfa32cd 100644 --- a/library/src/scala/quoted/Liftable.scala +++ b/library/src/scala/quoted/Liftable.scala @@ -6,7 +6,7 @@ import scala.quoted.Exprs.ValueExpr * without going through an explicit `'(...)` operation. */ abstract class Liftable[T] { - implicit def toExpr(x: T): Expr[T] + def toExpr(x: T): Expr[T] } /** Some liftable base types. To be completed with at least all types @@ -15,11 +15,6 @@ abstract class Liftable[T] { * gives an alternative implementation using just the basic staging system. */ object Liftable { - - implicit class LiftExprOps[T](val x: T) extends AnyVal { - def toExpr(implicit liftable: Liftable[T]): Expr[T] = liftable.toExpr(x) - } - implicit def BooleanIsLiftable: Liftable[Boolean] = (x: Boolean) => new ValueExpr(x) implicit def ByteLiftable: Liftable[Byte] = (x: Byte) => new ValueExpr(x) implicit def CharIsLiftable: Liftable[Char] = (x: Char) => new ValueExpr(x) diff --git a/library/src/scala/quoted/Type.scala b/library/src/scala/quoted/Type.scala index 66ace17f1bee..0b7b2b469165 100644 --- a/library/src/scala/quoted/Type.scala +++ b/library/src/scala/quoted/Type.scala @@ -39,4 +39,4 @@ object Types { final class TreeType[Tree](val tree: Tree) extends quoted.Type[Any] { override def toString: String = s"Type()" } -} \ No newline at end of file +} diff --git a/library/src/scala/quoted/package.scala b/library/src/scala/quoted/package.scala new file mode 100644 index 000000000000..3a1be2f5f5e1 --- /dev/null +++ b/library/src/scala/quoted/package.scala @@ -0,0 +1,9 @@ +package scala + +package object quoted { + + implicit class LiftExprOps[T](val x: T) extends AnyVal { + def toExpr(implicit ev: Liftable[T]): Expr[T] = ev.toExpr(x) + } + +} diff --git a/tests/pos/i3898/quoted_1.scala b/tests/pos/i3898/quoted_1.scala index fc9ff2f97f48..61fa38781138 100644 --- a/tests/pos/i3898/quoted_1.scala +++ b/tests/pos/i3898/quoted_1.scala @@ -1,5 +1,5 @@ import scala.quoted._ object Macro { inline def ff(args: Any*): String = ~impl('(args)) - def impl(args: Expr[Seq[Any]]): Expr[String] = "" + def impl(args: Expr[Seq[Any]]): Expr[String] = '("") } diff --git a/tests/pos/i3898b/quoted_1.scala b/tests/pos/i3898b/quoted_1.scala index 821744e111ca..fb2072ab4cb9 100644 --- a/tests/pos/i3898b/quoted_1.scala +++ b/tests/pos/i3898b/quoted_1.scala @@ -1,5 +1,5 @@ import scala.quoted._ object Macro { inline def ff(x: Int, inline y: Int): String = ~impl('(x)) - def impl(x: Expr[Int]): Expr[String] = "" + def impl(x: Expr[Int]): Expr[String] = '("") } diff --git a/tests/pos/i3898c/quoted_1.scala b/tests/pos/i3898c/quoted_1.scala index 821744e111ca..fb2072ab4cb9 100644 --- a/tests/pos/i3898c/quoted_1.scala +++ b/tests/pos/i3898c/quoted_1.scala @@ -1,5 +1,5 @@ import scala.quoted._ object Macro { inline def ff(x: Int, inline y: Int): String = ~impl('(x)) - def impl(x: Expr[Int]): Expr[String] = "" + def impl(x: Expr[Int]): Expr[String] = '("") } diff --git a/tests/pos/i3916/Macro_1.scala b/tests/pos/i3916/Macro_1.scala index a981f575764a..3ad93b037a5a 100644 --- a/tests/pos/i3916/Macro_1.scala +++ b/tests/pos/i3916/Macro_1.scala @@ -14,7 +14,7 @@ object FInterpolation { } def fInterpolation(sc: StringContext, args: Seq[Expr[Any]]): Expr[String] = { - val str: Expr[String] = sc.parts.mkString("") + val str: Expr[String] = sc.parts.mkString("").toExpr val args1: Expr[Seq[Any]] = liftSeq(args) '{ (~str).format(~args1: _*) } } diff --git a/tests/pos/i4023b/Macro_1.scala b/tests/pos/i4023b/Macro_1.scala index 7aa305e3183a..e440b4e1923a 100644 --- a/tests/pos/i4023b/Macro_1.scala +++ b/tests/pos/i4023b/Macro_1.scala @@ -1,5 +1,5 @@ import scala.quoted._ object Macro { inline def ff[T](implicit t: Type[T]): Int = ~impl[T] - def impl[T]: Expr[Int] = 4 + def impl[T]: Expr[Int] = '(4) } diff --git a/tests/pos/quote-0.scala b/tests/pos/quote-0.scala index 9c44151eef36..c6b17d609e0f 100644 --- a/tests/pos/quote-0.scala +++ b/tests/pos/quote-0.scala @@ -1,4 +1,5 @@ import scala.quoted._ + import dotty.tools.dotc.quoted.Toolbox._ class Test { @@ -11,7 +12,7 @@ class Test { def assertImpl(expr: Expr[Boolean]) = '{ if !(~expr) then throw new AssertionError(s"failed assertion: ${~showExpr(expr)}") } - def showExpr[T](expr: Expr[T]): Expr[String] = expr.toString + def showExpr[T](expr: Expr[T]): Expr[String] = expr.toString.toExpr inline def power(inline n: Int, x: Double) = ~powerCode(n, '(x)) diff --git a/tests/pos/quote-interpolator-core/quoted_1.scala b/tests/pos/quote-interpolator-core/quoted_1.scala index bd24977de5a5..7b136b080957 100644 --- a/tests/pos/quote-interpolator-core/quoted_1.scala +++ b/tests/pos/quote-interpolator-core/quoted_1.scala @@ -17,7 +17,7 @@ object FInterpolation { } def fInterpolation(sc: StringContext, args: Seq[Expr[Any]]): Expr[String] = { - val str: Expr[String] = sc.parts.mkString("") + val str: Expr[String] = sc.parts.mkString("").toExpr val args1: Expr[Seq[Any]] = liftSeq(args) '{ (~str).format(~args1: _*) } } diff --git a/tests/pos/quote-lift.scala b/tests/pos/quote-lift.scala index 2616b524ccf3..1ec60672d34d 100644 --- a/tests/pos/quote-lift.scala +++ b/tests/pos/quote-lift.scala @@ -1,7 +1,6 @@ import scala.quoted._ object Test { - '{ ~(1: Expr[Int]) } '{ ~implicitly[Liftable[Int]].toExpr(1) } diff --git a/tests/pos/quote-liftable.scala b/tests/pos/quote-liftable.scala index 0388a1da6432..2edb101fdaa9 100644 --- a/tests/pos/quote-liftable.scala +++ b/tests/pos/quote-liftable.scala @@ -24,15 +24,15 @@ object Test { } } - (true: Expr[Boolean]) - (1: Expr[Byte]) - ('a': Expr[Char]) - (1: Expr[Short]) - (1: Expr[Int]) - (1L: Expr[Long]) - (1.0f: Expr[Float]) - (1.0: Expr[Double]) - ("abc": Expr[String]) + true.toExpr + 1.toExpr + 'a'.toExpr + 1.toExpr + 1.toExpr + 1L.toExpr + 1.0f.toExpr + 1.0.toExpr + "abc".toExpr - val xs: Expr[List[Int]] = 1 :: 2 :: 3 :: Nil + val xs: Expr[List[Int]] = (1 :: 2 :: 3 :: Nil).toExpr } diff --git a/tests/run-with-compiler/i4044b.scala b/tests/run-with-compiler/i4044b.scala index c4255d7d8d7a..a7fab6ff914c 100644 --- a/tests/run-with-compiler/i4044b.scala +++ b/tests/run-with-compiler/i4044b.scala @@ -1,4 +1,5 @@ import scala.quoted._ + import dotty.tools.dotc.quoted.Toolbox._ sealed abstract class VarRef[T] { @@ -21,7 +22,7 @@ object VarRef { object Test { def main(args: Array[String]): Unit = { - val q = VarRef(4)(varRef => '{ ~varRef.update(3); ~varRef.expr }) + val q = VarRef('(4))(varRef => '{ ~varRef.update('(3)); ~varRef.expr }) println(q.show) } } diff --git a/tests/run-with-compiler/quote-lib.scala b/tests/run-with-compiler/quote-lib.scala index 630b2df6d7d2..bef58a4a2174 100644 --- a/tests/run-with-compiler/quote-lib.scala +++ b/tests/run-with-compiler/quote-lib.scala @@ -12,7 +12,7 @@ import liftable.Exprs._ object Test { def main(args: Array[String]): Unit = { - val liftedUnit: Expr[Unit] = () + val liftedUnit: Expr[Unit] = '() letVal('(1))(a => '{ ~a + 1 }).show letLazyVal('(1))(a => '{ ~a + 1 }).show @@ -21,18 +21,18 @@ object Test { liftedWhile('(true))('{ println(1) }).show liftedDoWhile('{ println(1) })('(true)).show - val t1: Expr[Tuple1[Int]] = Tuple1(4) - val t2: Expr[(Int, Int)] = (2, 3) - val t3: Expr[(Int, Int, Int)] = (2, 3, 4) - val t4: Expr[(Int, Int, Int, Int)] = (2, 3, 4, 5) + val t1: Expr[Tuple1[Int]] = Tuple1(4).toExpr + val t2: Expr[(Int, Int)] = (2, 3).toExpr + val t3: Expr[(Int, Int, Int)] = (2, 3, 4).toExpr + val t4: Expr[(Int, Int, Int, Int)] = (2, 3, 4, 5).toExpr val list: List[Int] = List(1, 2, 3) - val liftedList: Expr[List[Int]] = list + val liftedList: Expr[List[Int]] = list.toExpr - liftedList.foldLeft[Int](0)('{ (acc: Int, x: Int) => acc + x }).show + liftedList.foldLeft[Int](0.toExpr)('{ (acc: Int, x: Int) => acc + x }).show liftedList.foreach('{ (x: Int) => println(x) }).show - list.unrolledFoldLeft[Int](0)('{ (acc: Int, x: Int) => acc + x }).show + list.unrolledFoldLeft[Int](0.toExpr)('{ (acc: Int, x: Int) => acc + x }).show list.unrolledForeach('{ (x: Int) => println(x) }).show println("quote lib ok") @@ -42,7 +42,6 @@ object Test { package liftable { import scala.quoted.Liftable - import scala.quoted.Liftable._ import scala.reflect.ClassTag object Exprs { @@ -113,7 +112,7 @@ package liftable { } def unrolledForeach(f: Expr[T => Unit]): Expr[Unit] = list match { case x :: xs => '{ (~f).apply(~x.toExpr); ~xs.unrolledForeach(f) } - case Nil => () + case Nil => '() } } diff --git a/tests/run-with-compiler/quote-run-constants-extract-1.scala b/tests/run-with-compiler/quote-run-constants-extract-1.scala index 2d8b5a151655..e019defeba11 100644 --- a/tests/run-with-compiler/quote-run-constants-extract-1.scala +++ b/tests/run-with-compiler/quote-run-constants-extract-1.scala @@ -5,7 +5,7 @@ import dotty.tools.dotc.quoted.Toolbox._ object Test { def main(args: Array[String]): Unit = { - (3: Expr[Int]) match { case Constant(n) => println(n) } + 3.toExpr match { case Constant(n) => println(n) } '(4) match { case Constant(n) => println(n) } '("abc") match { case Constant(n) => println(n) } '(null) match { case Constant(n) => println(n) } diff --git a/tests/run-with-compiler/quote-run-constants-extract-2.scala b/tests/run-with-compiler/quote-run-constants-extract-2.scala index 2108a6b604a2..8c3edbb72cfd 100644 --- a/tests/run-with-compiler/quote-run-constants-extract-2.scala +++ b/tests/run-with-compiler/quote-run-constants-extract-2.scala @@ -6,8 +6,8 @@ object Test { def main(args: Array[String]): Unit = { // 2 is a lifted constant - println(power(2, 3.0).show) - println(power(2, 3.0).run) + println(power(2.toExpr, 3.0.toExpr).show) + println(power(2.toExpr, 3.0.toExpr).run) } def power(n: Expr[Int], x: Expr[Double]): Expr[Double] = { diff --git a/tests/run-with-compiler/quote-run-constants-extract-3.scala b/tests/run-with-compiler/quote-run-constants-extract-3.scala index 2108a6b604a2..8c3edbb72cfd 100644 --- a/tests/run-with-compiler/quote-run-constants-extract-3.scala +++ b/tests/run-with-compiler/quote-run-constants-extract-3.scala @@ -6,8 +6,8 @@ object Test { def main(args: Array[String]): Unit = { // 2 is a lifted constant - println(power(2, 3.0).show) - println(power(2, 3.0).run) + println(power(2.toExpr, 3.0.toExpr).show) + println(power(2.toExpr, 3.0.toExpr).run) } def power(n: Expr[Int], x: Expr[Double]): Expr[Double] = { diff --git a/tests/run-with-compiler/quote-run-constants-extract-4.scala b/tests/run-with-compiler/quote-run-constants-extract-4.scala index ca31a75211c8..330549fb6f8e 100644 --- a/tests/run-with-compiler/quote-run-constants-extract-4.scala +++ b/tests/run-with-compiler/quote-run-constants-extract-4.scala @@ -7,8 +7,8 @@ object Test { def main(args: Array[String]): Unit = { // n is a lifted constant val n = 2 - println(power(n, 4.0).show) - println(power(n, 4.0).run) + println(power(n.toExpr, 4.0.toExpr).show) + println(power(n.toExpr, 4.0.toExpr).run) } def power(n: Expr[Int], x: Expr[Double]): Expr[Double] = { diff --git a/tests/run-with-compiler/quote-run-constants-extract-5.scala b/tests/run-with-compiler/quote-run-constants-extract-5.scala index 84632588cccb..3362a82ee4fe 100644 --- a/tests/run-with-compiler/quote-run-constants-extract-5.scala +++ b/tests/run-with-compiler/quote-run-constants-extract-5.scala @@ -6,8 +6,8 @@ object Test { def main(args: Array[String]): Unit = { // n is a constant in a quote - println(power('(2), 5.0).show) - println(power('(2), 5.0).run) + println(power('(2), 5.0.toExpr).show) + println(power('(2), 5.0.toExpr).run) } def power(n: Expr[Int], x: Expr[Double]): Expr[Double] = { diff --git a/tests/run-with-compiler/quote-run-constants-extract-6.scala b/tests/run-with-compiler/quote-run-constants-extract-6.scala index 3e8c8ab84fa2..50110602c844 100644 --- a/tests/run-with-compiler/quote-run-constants-extract-6.scala +++ b/tests/run-with-compiler/quote-run-constants-extract-6.scala @@ -7,8 +7,8 @@ object Test { def main(args: Array[String]): Unit = { // n2 is clearly not a constant val n2 = '{ println("foo"); 2 } - println(power(n2, 6.0).show) - println(power(n2, 6.0).run) + println(power(n2, 6.0.toExpr).show) + println(power(n2, 6.0.toExpr).run) } def power(n: Expr[Int], x: Expr[Double]): Expr[Double] = { diff --git a/tests/run-with-compiler/quote-run-constants.scala b/tests/run-with-compiler/quote-run-constants.scala index c612e08ca066..6e432baba955 100644 --- a/tests/run-with-compiler/quote-run-constants.scala +++ b/tests/run-with-compiler/quote-run-constants.scala @@ -8,35 +8,35 @@ object Test { def run[T](expr: Expr[T]): Unit = println(expr.run) def show[T](expr: Expr[T]): Unit = println(expr.show) - run(true) - run('a') - run('\n') - run('"') - run('\'') - run('\\') - run(1) - run(2) - run(3L) - run(4.0f) - run(5.0d) - run("xyz") + run(true.toExpr) + run('a'.toExpr) + run('\n'.toExpr) + run('"'.toExpr) + run('\''.toExpr) + run('\\'.toExpr) + run(1.toExpr) + run(2.toExpr) + run(3L.toExpr) + run(4.0f.toExpr) + run(5.0d.toExpr) + run("xyz".toExpr) println("======") - show(true) - show('a') - show('\n') - show('"') - show('\'') - show('\\') - show(1) - show(2) - show(3L) - show(4.0f) - show(5.0d) - show("xyz") - show("\n\\\"'") + show(true.toExpr) + show('a'.toExpr) + show('\n'.toExpr) + show('"'.toExpr) + show('\''.toExpr) + show('\\'.toExpr) + show(1.toExpr) + show(2.toExpr) + show(3L.toExpr) + show(4.0f.toExpr) + show(5.0d.toExpr) + show("xyz".toExpr) + show("\n\\\"'".toExpr) show("""abc - xyz""") + xyz""".toExpr) } } diff --git a/tests/run-with-compiler/quote-run-staged-interpreter.scala b/tests/run-with-compiler/quote-run-staged-interpreter.scala index 76311f2f0bd6..5f580fd82d21 100644 --- a/tests/run-with-compiler/quote-run-staged-interpreter.scala +++ b/tests/run-with-compiler/quote-run-staged-interpreter.scala @@ -1,4 +1,5 @@ import scala.quoted._ + import dotty.tools.dotc.quoted.Toolbox._ enum Exp { @@ -13,7 +14,7 @@ object Test { def compile(e: Exp, env: Map[String, Expr[Int]], keepLets: Boolean): Expr[Int] = { def compileImpl(e: Exp, env: Map[String, Expr[Int]]): Expr[Int] = e match { - case Num(n) => n + case Num(n) => n.toExpr case Plus(e1, e2) => '(~compileImpl(e1, env) + ~compileImpl(e2, env)) case Var(x) => env(x) case Let(x, e, body) => diff --git a/tests/run-with-compiler/quote-show-blocks-raw.scala b/tests/run-with-compiler/quote-show-blocks-raw.scala index f41be403d69b..bcd6480451dc 100644 --- a/tests/run-with-compiler/quote-show-blocks-raw.scala +++ b/tests/run-with-compiler/quote-show-blocks-raw.scala @@ -2,7 +2,6 @@ import dotty.tools.dotc.quoted.Toolbox._ import scala.quoted._ -import scala.quoted.Liftable._ object Test { def main(args: Array[String]): Unit = { diff --git a/tests/run-with-compiler/quote-show-blocks.scala b/tests/run-with-compiler/quote-show-blocks.scala index 0c99149934f8..1b84dde97a9c 100644 --- a/tests/run-with-compiler/quote-show-blocks.scala +++ b/tests/run-with-compiler/quote-show-blocks.scala @@ -2,7 +2,6 @@ import dotty.tools.dotc.quoted.Toolbox._ import scala.quoted._ -import scala.quoted.Liftable._ object Test { def main(args: Array[String]): Unit = { diff --git a/tests/run/quote-splice-interpret-1/Macro_1.scala b/tests/run/quote-splice-interpret-1/Macro_1.scala index 051c434a9a86..6ba8f0259e4e 100644 --- a/tests/run/quote-splice-interpret-1/Macro_1.scala +++ b/tests/run/quote-splice-interpret-1/Macro_1.scala @@ -1,14 +1,15 @@ import dotty.tools.dotc.ast.Trees.Import import scala.quoted._ + object Macros { sealed trait Nat case object Z extends Nat case class S[N <: Nat]() extends Nat inline def isZero(inline n: Int): Boolean = ~{ - if (n == 0) (true: Expr[Boolean]) - else (false: Expr[Boolean]) + if (n == 0) '(true) + else '(false) } }