Skip to content

Commit 5616145

Browse files
committed
Remove implicit conversion from liftable types to Expr[T]
Use `import scala.quotes.Liftable._` and `x.toExpr` notation instead.
1 parent 2e6e6d3 commit 5616145

24 files changed

+107
-88
lines changed

docs/docs/reference/principled-meta-programming.md

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ The compiler takes an environment that maps variable names to Scala `Expr`s.
507507

508508
def compile(e: Exp, env: Map[String, Expr[Int]]): Expr[Int] = e match {
509509
case Num(n) =>
510-
n
510+
n.toExpr
511511
case Plus(e1, e2) =>
512512
’(~compile(e1, env) + ~compile(e2, env))
513513
case Var(x) =>
@@ -520,24 +520,25 @@ Running `compile(letExp, Map())` would yield the following Scala code:
520520

521521
’{ val y = 3; (2 + y) + 4 }
522522

523-
The body of the first clause, `case Num(n) => n`, looks suspicious. `n`
524-
is declared as an `Int`, yet the result of `compile` is declared to be
525-
`Expr[Int]`. Shouldn’t `n` be quoted? In fact this would not
523+
The body of the first clause, `case Num(n) => n.toExpr`, looks suspicious. `n`
524+
is declared as an `Int`, yet it is conveted to an `Expr[Int]` with `toExpr`.
525+
Shouldn’t `n` be quoted? In fact this would not
526526
work since replacing `n` by `’n` in the clause would not be phase
527527
correct.
528528

529-
What happens instead "under the hood" is an implicit conversion: `n`
530-
is expanded to `scala.quoted.Expr.toExpr(n)`. The `toExpr` conversion
531-
is defined in the companion object of class `Expr` as follows:
529+
What happens instead "under the hood" is an extension method `toExpr` is added: `n.toExpr`
530+
is expanded to `new scala.quoted.Liftable.LiftExprOps(n).toExpr`. The `toExpr` extension
531+
is defined in the companion object of class `Liftable` as follows:
532532

533-
object Expr {
534-
implicit def toExpr[T](x: T)(implicit ev: Liftable[T]): Expr[T] =
535-
ev.toExpr(x)
533+
object Liftable {
534+
implicit class LiftExprOps[T](val x: T) extends AnyVal {
535+
def toExpr(implicit ev: Liftable[T]): Expr[T] = ev.toExpr(x)
536+
}
536537
}
537538

538-
The conversion says that values of types implementing the `Liftable`
539-
type class can be converted ("lifted") automatically to `Expr`
540-
values. Dotty comes with instance definitions of `Liftable` for
539+
The extension says that values of types implementing the `Liftable` type class can be
540+
converted ("lifted") to `Expr` values using `toExpr` when `Liftable._` is imported.
541+
Dotty comes with instance definitions of `Liftable` for
541542
several types including `Boolean`, `String`, and all primitive number
542543
types. For example, `Int` values can be converted to `Expr[Int]`
543544
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:
570571

571572
implicit def ListIsLiftable[T: Liftable]: Liftable[List[T]] = new {
572573
def toExpr(xs: List[T]): Expr[List[T]] = xs match {
573-
case x :: xs1 => ’(~implicitly[Liftable[T]].toExpr(x) :: ~toExpr(xs1))
574+
case x :: xs1 => ’(~x.toExpr :: ~toExpr(xs1))
574575
case Nil => ’(Nil: List[T])
575576
}
576577
}

library/src/scala/quoted/Expr.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ sealed abstract class Expr[T] {
1010
}
1111

1212
object Expr {
13-
implicit def toExpr[T](x: T)(implicit ev: Liftable[T]): Expr[T] =
14-
ev.toExpr(x)
1513

1614
implicit class AsFunction[T, U](private val f: Expr[T => U]) extends AnyVal {
1715
def apply(x: Expr[T]): Expr[U] = new Exprs.FunctionAppliedTo[T, U](f, x)
@@ -25,7 +23,7 @@ object Expr {
2523
object Exprs {
2624
/** An Expr backed by a pickled TASTY tree */
2725
final class TastyExpr[T](val tasty: Pickled, val args: Seq[Any]) extends Expr[T] {
28-
override def toString(): String = s"Expr(<pickled>)"
26+
override def toString: String = s"Expr(<pickled>)"
2927
}
3028

3129
/** An Expr backed by a value.

library/src/scala/quoted/Liftable.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import scala.quoted.Exprs.ValueExpr
66
* without going through an explicit `'(...)` operation.
77
*/
88
abstract class Liftable[T] {
9-
implicit def toExpr(x: T): Expr[T]
9+
def toExpr(x: T): Expr[T]
1010
}
1111

1212
/** Some liftable base types. To be completed with at least all types
@@ -17,7 +17,7 @@ abstract class Liftable[T] {
1717
object Liftable {
1818

1919
implicit class LiftExprOps[T](val x: T) extends AnyVal {
20-
def toExpr(implicit liftable: Liftable[T]): Expr[T] = liftable.toExpr(x)
20+
def toExpr(implicit ev: Liftable[T]): Expr[T] = ev.toExpr(x)
2121
}
2222

2323
implicit def BooleanIsLiftable: Liftable[Boolean] = (x: Boolean) => new ValueExpr(x)

library/src/scala/quoted/Type.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,4 @@ object Types {
3939
final class TreeType[Tree](val tree: Tree) extends quoted.Type[Any] {
4040
override def toString: String = s"Type(<raw>)"
4141
}
42-
}
42+
}

tests/pos/i3898/quoted_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import scala.quoted._
22
object Macro {
33
inline def ff(args: Any*): String = ~impl('(args))
4-
def impl(args: Expr[Seq[Any]]): Expr[String] = ""
4+
def impl(args: Expr[Seq[Any]]): Expr[String] = '("")
55
}

tests/pos/i3898b/quoted_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import scala.quoted._
22
object Macro {
33
inline def ff(x: Int, inline y: Int): String = ~impl('(x))
4-
def impl(x: Expr[Int]): Expr[String] = ""
4+
def impl(x: Expr[Int]): Expr[String] = '("")
55
}

tests/pos/i3898c/quoted_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import scala.quoted._
22
object Macro {
33
inline def ff(x: Int, inline y: Int): String = ~impl('(x))
4-
def impl(x: Expr[Int]): Expr[String] = ""
4+
def impl(x: Expr[Int]): Expr[String] = '("")
55
}

tests/pos/i3916/Macro_1.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import scala.quoted._
2+
import scala.quoted.Liftable._
23

34
class FInterpolatorHelper(val sc: StringContext) extends AnyVal {
45
inline def ff(arg1: Any): String = ~FInterpolation.fInterpolation(sc, Seq('(arg1)))
@@ -14,7 +15,7 @@ object FInterpolation {
1415
}
1516

1617
def fInterpolation(sc: StringContext, args: Seq[Expr[Any]]): Expr[String] = {
17-
val str: Expr[String] = sc.parts.mkString("")
18+
val str: Expr[String] = sc.parts.mkString("").toExpr
1819
val args1: Expr[Seq[Any]] = liftSeq(args)
1920
'{ (~str).format(~args1: _*) }
2021
}

tests/pos/i4023b/Macro_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import scala.quoted._
22
object Macro {
33
inline def ff[T](implicit t: Type[T]): Int = ~impl[T]
4-
def impl[T]: Expr[Int] = 4
4+
def impl[T]: Expr[Int] = '(4)
55
}

tests/pos/quote-0.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import scala.quoted._
2+
import scala.quoted.Liftable._
3+
24
import dotty.tools.dotc.quoted.Toolbox._
35

46
class Test {
@@ -11,7 +13,7 @@ class Test {
1113
def assertImpl(expr: Expr[Boolean]) =
1214
'{ if !(~expr) then throw new AssertionError(s"failed assertion: ${~showExpr(expr)}") }
1315

14-
def showExpr[T](expr: Expr[T]): Expr[String] = expr.toString
16+
def showExpr[T](expr: Expr[T]): Expr[String] = expr.toString.toExpr
1517

1618
inline def power(inline n: Int, x: Double) = ~powerCode(n, '(x))
1719

tests/pos/quote-interpolator-core/quoted_1.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import scala.quoted._
2+
import scala.quoted.Liftable._
23

34
// This test checks the correct interpretation of the inlined value class
45

@@ -17,7 +18,7 @@ object FInterpolation {
1718
}
1819

1920
def fInterpolation(sc: StringContext, args: Seq[Expr[Any]]): Expr[String] = {
20-
val str: Expr[String] = sc.parts.mkString("")
21+
val str: Expr[String] = sc.parts.mkString("").toExpr
2122
val args1: Expr[Seq[Any]] = liftSeq(args)
2223
'{ (~str).format(~args1: _*) }
2324
}

tests/pos/quote-lift.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import scala.quoted._
2+
import scala.quoted.Liftable._
23

34
object Test {
4-
'{ ~(1: Expr[Int]) }
55

66
'{ ~implicitly[Liftable[Int]].toExpr(1) }
77

tests/pos/quote-liftable.scala

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import scala.quoted._
22

3+
import scala.quoted.Liftable._
4+
35
object Test {
46

57
implicit def IntIsLiftable: Liftable[Int] = new {
@@ -24,15 +26,15 @@ object Test {
2426
}
2527
}
2628

27-
(true: Expr[Boolean])
28-
(1: Expr[Byte])
29-
('a': Expr[Char])
30-
(1: Expr[Short])
31-
(1: Expr[Int])
32-
(1L: Expr[Long])
33-
(1.0f: Expr[Float])
34-
(1.0: Expr[Double])
35-
("abc": Expr[String])
29+
true.toExpr
30+
1.toExpr
31+
'a'.toExpr
32+
1.toExpr
33+
1.toExpr
34+
1L.toExpr
35+
1.0f.toExpr
36+
1.0.toExpr
37+
"abc".toExpr
3638

37-
val xs: Expr[List[Int]] = 1 :: 2 :: 3 :: Nil
39+
val xs: Expr[List[Int]] = (1 :: 2 :: 3 :: Nil).toExpr
3840
}

tests/run-with-compiler/i4044b.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import scala.quoted._
2+
import scala.quoted.Liftable._
3+
24
import dotty.tools.dotc.quoted.Toolbox._
35

46
sealed abstract class VarRef[T] {
@@ -21,7 +23,7 @@ object VarRef {
2123

2224
object Test {
2325
def main(args: Array[String]): Unit = {
24-
val q = VarRef(4)(varRef => '{ ~varRef.update(3); ~varRef.expr })
26+
val q = VarRef('(4))(varRef => '{ ~varRef.update('(3)); ~varRef.expr })
2527
println(q.show)
2628
}
2729
}

tests/run-with-compiler/quote-lib.scala

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
import scala.quoted._
3+
import scala.quoted.Liftable._
34
import dotty.tools.dotc.quoted.Toolbox._
45

56
import liftable.Units._
@@ -12,7 +13,7 @@ import liftable.Exprs._
1213
object Test {
1314
def main(args: Array[String]): Unit = {
1415

15-
val liftedUnit: Expr[Unit] = ()
16+
val liftedUnit: Expr[Unit] = '()
1617

1718
letVal('(1))(a => '{ ~a + 1 }).show
1819
letLazyVal('(1))(a => '{ ~a + 1 }).show
@@ -21,18 +22,18 @@ object Test {
2122
liftedWhile('(true))('{ println(1) }).show
2223
liftedDoWhile('{ println(1) })('(true)).show
2324

24-
val t1: Expr[Tuple1[Int]] = Tuple1(4)
25-
val t2: Expr[(Int, Int)] = (2, 3)
26-
val t3: Expr[(Int, Int, Int)] = (2, 3, 4)
27-
val t4: Expr[(Int, Int, Int, Int)] = (2, 3, 4, 5)
25+
val t1: Expr[Tuple1[Int]] = Tuple1(4).toExpr
26+
val t2: Expr[(Int, Int)] = (2, 3).toExpr
27+
val t3: Expr[(Int, Int, Int)] = (2, 3, 4).toExpr
28+
val t4: Expr[(Int, Int, Int, Int)] = (2, 3, 4, 5).toExpr
2829

2930
val list: List[Int] = List(1, 2, 3)
30-
val liftedList: Expr[List[Int]] = list
31+
val liftedList: Expr[List[Int]] = list.toExpr
3132

32-
liftedList.foldLeft[Int](0)('{ (acc: Int, x: Int) => acc + x }).show
33+
liftedList.foldLeft[Int](0.toExpr)('{ (acc: Int, x: Int) => acc + x }).show
3334
liftedList.foreach('{ (x: Int) => println(x) }).show
3435

35-
list.unrolledFoldLeft[Int](0)('{ (acc: Int, x: Int) => acc + x }).show
36+
list.unrolledFoldLeft[Int](0.toExpr)('{ (acc: Int, x: Int) => acc + x }).show
3637
list.unrolledForeach('{ (x: Int) => println(x) }).show
3738

3839
println("quote lib ok")
@@ -113,7 +114,7 @@ package liftable {
113114
}
114115
def unrolledForeach(f: Expr[T => Unit]): Expr[Unit] = list match {
115116
case x :: xs => '{ (~f).apply(~x.toExpr); ~xs.unrolledForeach(f) }
116-
case Nil => ()
117+
case Nil => '()
117118
}
118119
}
119120

tests/run-with-compiler/quote-run-constants-extract-1.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import scala.quoted._
2+
import scala.quoted.Liftable._
23

34
import dotty.tools.dotc.quoted.Toolbox._
45

56
object Test {
67

78
def main(args: Array[String]): Unit = {
8-
(3: Expr[Int]) match { case Constant(n) => println(n) }
9+
3.toExpr match { case Constant(n) => println(n) }
910
'(4) match { case Constant(n) => println(n) }
1011
'("abc") match { case Constant(n) => println(n) }
1112
'(null) match { case Constant(n) => println(n) }

tests/run-with-compiler/quote-run-constants-extract-2.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import scala.quoted._
2+
import scala.quoted.Liftable._
23

34
import dotty.tools.dotc.quoted.Toolbox._
45

56
object Test {
67

78
def main(args: Array[String]): Unit = {
89
// 2 is a lifted constant
9-
println(power(2, 3.0).show)
10-
println(power(2, 3.0).run)
10+
println(power(2.toExpr, 3.0.toExpr).show)
11+
println(power(2.toExpr, 3.0.toExpr).run)
1112
}
1213

1314
def power(n: Expr[Int], x: Expr[Double]): Expr[Double] = {

tests/run-with-compiler/quote-run-constants-extract-3.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import scala.quoted._
2+
import scala.quoted.Liftable._
23

34
import dotty.tools.dotc.quoted.Toolbox._
45

56
object Test {
67

78
def main(args: Array[String]): Unit = {
89
// 2 is a lifted constant
9-
println(power(2, 3.0).show)
10-
println(power(2, 3.0).run)
10+
println(power(2.toExpr, 3.0.toExpr).show)
11+
println(power(2.toExpr, 3.0.toExpr).run)
1112
}
1213

1314
def power(n: Expr[Int], x: Expr[Double]): Expr[Double] = {

tests/run-with-compiler/quote-run-constants-extract-4.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import scala.quoted._
2+
import scala.quoted.Liftable._
23

34
import dotty.tools.dotc.quoted.Toolbox._
45

@@ -7,8 +8,8 @@ object Test {
78
def main(args: Array[String]): Unit = {
89
// n is a lifted constant
910
val n = 2
10-
println(power(n, 4.0).show)
11-
println(power(n, 4.0).run)
11+
println(power(n.toExpr, 4.0.toExpr).show)
12+
println(power(n.toExpr, 4.0.toExpr).run)
1213
}
1314

1415
def power(n: Expr[Int], x: Expr[Double]): Expr[Double] = {

tests/run-with-compiler/quote-run-constants-extract-5.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import scala.quoted._
2+
import scala.quoted.Liftable._
23

34
import dotty.tools.dotc.quoted.Toolbox._
45

56
object Test {
67

78
def main(args: Array[String]): Unit = {
89
// n is a constant in a quote
9-
println(power('(2), 5.0).show)
10-
println(power('(2), 5.0).run)
10+
println(power('(2), 5.0.toExpr).show)
11+
println(power('(2), 5.0.toExpr).run)
1112
}
1213

1314
def power(n: Expr[Int], x: Expr[Double]): Expr[Double] = {

tests/run-with-compiler/quote-run-constants-extract-6.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import scala.quoted._
2+
import scala.quoted.Liftable._
23

34
import dotty.tools.dotc.quoted.Toolbox._
45

@@ -7,8 +8,8 @@ object Test {
78
def main(args: Array[String]): Unit = {
89
// n2 is clearly not a constant
910
val n2 = '{ println("foo"); 2 }
10-
println(power(n2, 6.0).show)
11-
println(power(n2, 6.0).run)
11+
println(power(n2, 6.0.toExpr).show)
12+
println(power(n2, 6.0.toExpr).run)
1213
}
1314

1415
def power(n: Expr[Int], x: Expr[Double]): Expr[Double] = {

0 commit comments

Comments
 (0)