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

0 commit comments

Comments
 (0)