Skip to content

Commit 7be9199

Browse files
Merge pull request #4129 from dotty-staging/remove-implicit-conversion-toExpr
Remove implicit conversion from liftable types to Expr[T]
2 parents ad002ff + d65148f commit 7be9199

24 files changed

+89
-87
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.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+
package object quoted {
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 `scala.quoted._` 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: 1 addition & 1 deletion
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

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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ object FInterpolation {
1414
}
1515

1616
def fInterpolation(sc: StringContext, args: Seq[Expr[Any]]): Expr[String] = {
17-
val str: Expr[String] = sc.parts.mkString("")
17+
val str: Expr[String] = sc.parts.mkString("").toExpr
1818
val args1: Expr[Seq[Any]] = liftSeq(args)
1919
'{ (~str).format(~args1: _*) }
2020
}

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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import scala.quoted._
2+
23
import dotty.tools.dotc.quoted.Toolbox._
34

45
class Test {
@@ -11,7 +12,7 @@ class Test {
1112
def assertImpl(expr: Expr[Boolean]) =
1213
'{ if !(~expr) then throw new AssertionError(s"failed assertion: ${~showExpr(expr)}") }
1314

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

1617
inline def power(inline n: Int, x: Double) = ~powerCode(n, '(x))
1718

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ object FInterpolation {
1717
}
1818

1919
def fInterpolation(sc: StringContext, args: Seq[Expr[Any]]): Expr[String] = {
20-
val str: Expr[String] = sc.parts.mkString("")
20+
val str: Expr[String] = sc.parts.mkString("").toExpr
2121
val args1: Expr[Seq[Any]] = liftSeq(args)
2222
'{ (~str).format(~args1: _*) }
2323
}

tests/pos/quote-lift.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import scala.quoted._
22

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

65
'{ ~implicitly[Liftable[Int]].toExpr(1) }
76

tests/pos/quote-liftable.scala

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ object Test {
2424
}
2525
}
2626

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])
27+
true.toExpr
28+
1.toExpr
29+
'a'.toExpr
30+
1.toExpr
31+
1.toExpr
32+
1L.toExpr
33+
1.0f.toExpr
34+
1.0.toExpr
35+
"abc".toExpr
3636

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

tests/run-with-compiler/i4044b.scala

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

45
sealed abstract class VarRef[T] {
@@ -21,7 +22,7 @@ object VarRef {
2122

2223
object Test {
2324
def main(args: Array[String]): Unit = {
24-
val q = VarRef(4)(varRef => '{ ~varRef.update(3); ~varRef.expr })
25+
val q = VarRef('(4))(varRef => '{ ~varRef.update('(3)); ~varRef.expr })
2526
println(q.show)
2627
}
2728
}

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import liftable.Exprs._
1212
object Test {
1313
def main(args: Array[String]): Unit = {
1414

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

1717
letVal('(1))(a => '{ ~a + 1 }).show
1818
letLazyVal('(1))(a => '{ ~a + 1 }).show
@@ -21,18 +21,18 @@ object Test {
2121
liftedWhile('(true))('{ println(1) }).show
2222
liftedDoWhile('{ println(1) })('(true)).show
2323

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)
24+
val t1: Expr[Tuple1[Int]] = Tuple1(4).toExpr
25+
val t2: Expr[(Int, Int)] = (2, 3).toExpr
26+
val t3: Expr[(Int, Int, Int)] = (2, 3, 4).toExpr
27+
val t4: Expr[(Int, Int, Int, Int)] = (2, 3, 4, 5).toExpr
2828

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

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

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

3838
println("quote lib ok")
@@ -112,7 +112,7 @@ package liftable {
112112
}
113113
def unrolledForeach(f: Expr[T => Unit]): Expr[Unit] = list match {
114114
case x :: xs => '{ (~f).apply(~x.toExpr); ~xs.unrolledForeach(f) }
115-
case Nil => ()
115+
case Nil => '()
116116
}
117117
}
118118

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import dotty.tools.dotc.quoted.Toolbox._
55
object Test {
66

77
def main(args: Array[String]): Unit = {
8-
(3: Expr[Int]) match { case Constant(n) => println(n) }
8+
3.toExpr match { case Constant(n) => println(n) }
99
'(4) match { case Constant(n) => println(n) }
1010
'("abc") match { case Constant(n) => println(n) }
1111
'(null) match { case Constant(n) => println(n) }

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ object Test {
66

77
def main(args: Array[String]): Unit = {
88
// 2 is a lifted constant
9-
println(power(2, 3.0).show)
10-
println(power(2, 3.0).run)
9+
println(power(2.toExpr, 3.0.toExpr).show)
10+
println(power(2.toExpr, 3.0.toExpr).run)
1111
}
1212

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

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ object Test {
66

77
def main(args: Array[String]): Unit = {
88
// 2 is a lifted constant
9-
println(power(2, 3.0).show)
10-
println(power(2, 3.0).run)
9+
println(power(2.toExpr, 3.0.toExpr).show)
10+
println(power(2.toExpr, 3.0.toExpr).run)
1111
}
1212

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

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ object Test {
77
def main(args: Array[String]): Unit = {
88
// n is a lifted constant
99
val n = 2
10-
println(power(n, 4.0).show)
11-
println(power(n, 4.0).run)
10+
println(power(n.toExpr, 4.0.toExpr).show)
11+
println(power(n.toExpr, 4.0.toExpr).run)
1212
}
1313

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

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ object Test {
66

77
def main(args: Array[String]): Unit = {
88
// n is a constant in a quote
9-
println(power('(2), 5.0).show)
10-
println(power('(2), 5.0).run)
9+
println(power('(2), 5.0.toExpr).show)
10+
println(power('(2), 5.0.toExpr).run)
1111
}
1212

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

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ object Test {
77
def main(args: Array[String]): Unit = {
88
// n2 is clearly not a constant
99
val n2 = '{ println("foo"); 2 }
10-
println(power(n2, 6.0).show)
11-
println(power(n2, 6.0).run)
10+
println(power(n2, 6.0.toExpr).show)
11+
println(power(n2, 6.0.toExpr).run)
1212
}
1313

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

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

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,35 @@ object Test {
88
def run[T](expr: Expr[T]): Unit = println(expr.run)
99
def show[T](expr: Expr[T]): Unit = println(expr.show)
1010

11-
run(true)
12-
run('a')
13-
run('\n')
14-
run('"')
15-
run('\'')
16-
run('\\')
17-
run(1)
18-
run(2)
19-
run(3L)
20-
run(4.0f)
21-
run(5.0d)
22-
run("xyz")
11+
run(true.toExpr)
12+
run('a'.toExpr)
13+
run('\n'.toExpr)
14+
run('"'.toExpr)
15+
run('\''.toExpr)
16+
run('\\'.toExpr)
17+
run(1.toExpr)
18+
run(2.toExpr)
19+
run(3L.toExpr)
20+
run(4.0f.toExpr)
21+
run(5.0d.toExpr)
22+
run("xyz".toExpr)
2323

2424
println("======")
2525

26-
show(true)
27-
show('a')
28-
show('\n')
29-
show('"')
30-
show('\'')
31-
show('\\')
32-
show(1)
33-
show(2)
34-
show(3L)
35-
show(4.0f)
36-
show(5.0d)
37-
show("xyz")
38-
show("\n\\\"'")
26+
show(true.toExpr)
27+
show('a'.toExpr)
28+
show('\n'.toExpr)
29+
show('"'.toExpr)
30+
show('\''.toExpr)
31+
show('\\'.toExpr)
32+
show(1.toExpr)
33+
show(2.toExpr)
34+
show(3L.toExpr)
35+
show(4.0f.toExpr)
36+
show(5.0d.toExpr)
37+
show("xyz".toExpr)
38+
show("\n\\\"'".toExpr)
3939
show("""abc
40-
xyz""")
40+
xyz""".toExpr)
4141
}
4242
}

tests/run-with-compiler/quote-run-staged-interpreter.scala

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

45
enum Exp {
@@ -13,7 +14,7 @@ object Test {
1314

1415
def compile(e: Exp, env: Map[String, Expr[Int]], keepLets: Boolean): Expr[Int] = {
1516
def compileImpl(e: Exp, env: Map[String, Expr[Int]]): Expr[Int] = e match {
16-
case Num(n) => n
17+
case Num(n) => n.toExpr
1718
case Plus(e1, e2) => '(~compileImpl(e1, env) + ~compileImpl(e2, env))
1819
case Var(x) => env(x)
1920
case Let(x, e, body) =>

0 commit comments

Comments
 (0)