Skip to content

Commit bc39a01

Browse files
committed
Add scala.Conversion
as a replacement for DottyPredef.ImplicitConversion
1 parent 5ea1ee6 commit bc39a01

File tree

12 files changed

+54
-51
lines changed

12 files changed

+54
-51
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,9 +389,6 @@ class Definitions {
389389

390390
def DottyPredefModule(implicit ctx: Context): Symbol = DottyPredefModuleRef.symbol
391391

392-
lazy val Predef_ImplicitConversionR: TypeRef = DottyPredefModule.requiredClass("ImplicitConversion").typeRef
393-
def Predef_ImplicitConversion(implicit ctx: Context): Symbol = Predef_ImplicitConversionR.symbol
394-
395392
lazy val DottyArraysModuleRef: TermRef = ctx.requiredModuleRef("dotty.runtime.Arrays")
396393
def DottyArraysModule(implicit ctx: Context): Symbol = DottyArraysModuleRef.symbol
397394
def newGenericArrayMethod(implicit ctx: Context): TermSymbol = DottyArraysModule.requiredMethod("newGenericArray")
@@ -592,6 +589,8 @@ class Definitions {
592589
def StringBuilderClass(implicit ctx: Context): ClassSymbol = StringBuilderType.symbol.asClass
593590
lazy val MatchErrorType: TypeRef = ctx.requiredClassRef("scala.MatchError")
594591
def MatchErrorClass(implicit ctx: Context): ClassSymbol = MatchErrorType.symbol.asClass
592+
lazy val ConversionType: TypeRef = ctx.requiredClass("scala.Conversion").typeRef
593+
def ConversionClass(implicit ctx: Context): ClassSymbol = ConversionType.symbol.asClass
595594

596595
lazy val StringAddType: TypeRef = ctx.requiredClassRef("scala.runtime.StringAdd")
597596
def StringAddClass(implicit ctx: Context): ClassSymbol = StringAddType.symbol.asClass

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ trait Checking {
622622
if !mt.isImplicitMethod && !sym.is(Synthetic) => // it's a conversion
623623
check()
624624
case AppliedType(tycon, _)
625-
if tycon.derivesFrom(defn.Predef_ImplicitConversion) && !sym.is(Synthetic) =>
625+
if tycon.derivesFrom(defn.ConversionClass) && !sym.is(Synthetic) =>
626626
check()
627627
case _ =>
628628
}

compiler/src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,9 @@ object Implicits {
126126
// this in Predef:
127127
//
128128
// implicit def convertIfConforms[A, B](x: A)(implicit ev: A <:< B): B = ev(a)
129-
// implicit def convertIfConverter[A, B](x: A)(implicit ev: ImplicitConversion[A, B]): B = ev(a)
129+
// implicit def convertIfConverter[A, B](x: A)(implicit ev: Conversion[A, B]): B = ev(a)
130130
//
131-
// (Once `<:<` inherits from `ImplicitConversion` we only need the 2nd one.)
131+
// (Once `<:<` inherits from `Conversion` we only need the 2nd one.)
132132
// But clauses like this currently slow down implicit search a lot, because
133133
// they are eligible for all pairs of types, and therefore are tried too often.
134134
// We emulate instead these conversions directly in the search.
@@ -138,7 +138,7 @@ object Implicits {
138138
// We keep the old behavior under -language:Scala2.
139139
val isFunctionInS2 =
140140
ctx.scala2Mode && tpw.derivesFrom(defn.FunctionClass(1)) && ref.symbol != defn.Predef_conforms
141-
val isImplicitConversion = tpw.derivesFrom(defn.Predef_ImplicitConversion)
141+
val isImplicitConversion = tpw.derivesFrom(defn.ConversionClass)
142142
val isConforms = // An implementation of <:< counts as a view, except that $conforms is always omitted
143143
tpw.derivesFrom(defn.Predef_Conforms) && ref.symbol != defn.Predef_conforms
144144
val hasExtensions = resType match {

docs/docs/reference/changed-features/implicit-conversions-spec.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,17 @@ An implicit conversion, or _view_, from type `S` to type `T` is
99
defined by either:
1010

1111
- An `implicit def` which has type `S => T` or `(=> S) => T`
12-
- An implicit value which has type `ImplicitConversion[S, T]`
12+
- An implicit value which has type `Conversion[S, T]`
1313

14-
The standard library defines an abstract class `ImplicitConversion`:
14+
The standard library defines an abstract class `Conversion`:
1515

1616
```scala
17-
abstract class ImplicitConversion[-T, +U] extends Function1[T, U]
17+
package scala
18+
@java.lang.FunctionalInterface
19+
abstract class Conversion[-T, +U] extends Function1[T, U]
1820
```
1921

20-
Function literals are automatically converted to `ImplicitConversion`
21-
values.
22+
Function literals are automatically converted to `Conversion` values.
2223

2324
Views are applied in three situations:
2425

@@ -60,14 +61,14 @@ val x: String = 0 // Compiles in Scala2 (uses `conv1`),
6061

6162
In Scala 2, implicit values of a function type would be considered as
6263
potential views. In Scala 3, these implicit value need to have type
63-
`ImplicitConversion`:
64+
`Conversion`:
6465

6566
```scala
6667
// Scala 2:
6768
def foo(x: Int)(implicit conv: Int => String): String = x
6869

6970
// Becomes with Scala 3:
70-
def foo(x: Int)(implicit conv: ImplicitConversion[Int, String]): String = x
71+
def foo(x: Int)(implicit conv: Conversion[Int, String]): String = x
7172

7273
// Call site is unchanged:
7374
foo(4)(_.toString)
@@ -76,7 +77,7 @@ foo(4)(_.toString)
7677
implicit val myConverter: Int => String = _.toString
7778

7879
// Becomes with Scala 3:
79-
implicit val myConverter: ImplicitConversion[Int, String] = _.toString
80+
implicit val myConverter: Conversion[Int, String] = _.toString
8081
```
8182

8283
Note that implicit conversions are also affected by the [changes to
@@ -85,7 +86,7 @@ Scala 3.
8586

8687
## Motivation for the changes
8788

88-
The introduction of `ImplicitConversion` in Scala 3 and the decision to
89+
The introduction of `Conversion` in Scala 3 and the decision to
8990
restrict implicit values of this type to be considered as potential
9091
views comes from the desire to remove surprising behavior from the
9192
language:
@@ -101,12 +102,12 @@ This snippet contains a type error. The right hand side of `val x`
101102
does not conform to type `String`. In Scala 2, the compiler will use
102103
`m` as an implicit conversion from `Int` to `String`, whereas Scala 3
103104
will report a type error, because Map isn't an instance of
104-
`ImplicitConversion`.
105+
`Conversion`.
105106

106107
## Migration path
107108

108109
Implicit values that are used as views should see their type changed
109-
to `ImplicitConversion`.
110+
to `Conversion`.
110111

111112
For the migration of implicit conversions that are affected by the
112113
changes to implicit resolution, refer to the [Changes in Implicit

docs/docs/reference/changed-features/implicit-conversions.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ case).
1919
This conversion can be either:
2020

2121
1. An `implicit def` of type `T => S` or `(=> T) => S`
22-
1. An implicit value of type `ImplicitConversion[T, S]`
22+
1. An implicit value of type `scala.Conversion[T, S]`
2323

2424
Defining an implicit conversion will emit a warning unless the import
2525
`scala.language.implicitConversions` is in scope, or the flag
@@ -37,14 +37,14 @@ implicit def int2Integer(x: Int): java.lang.Integer =
3737
x.asInstanceOf[java.lang.Integer]
3838
```
3939

40-
The second example shows how to use `ImplicitConversion` to define an
40+
The second example shows how to use `Conversion` to define an
4141
`Ordering` for an arbitrary type, given existing `Ordering`s for other
4242
types:
4343

4444
```scala
4545
import scala.language.implicitConversions
4646
implicit def ordT[T, S](
47-
implicit conv: ImplicitConversion[T, S],
47+
implicit conv: Conversion[T, S],
4848
ordS: Ordering[S]
4949
): Ordering[T] = {
5050
// `ordS` compares values of type `S`, but we can convert from `T` to `S`
@@ -54,7 +54,7 @@ implicit def ordT[T, S](
5454
class A(val x: Int) // The type for which we want an `Ordering`
5555

5656
// Convert `A` to a type for which an `Ordering` is available:
57-
implicit val AToInt: ImplicitConversion[A, Int] = _.x
57+
implicit val AToInt: Conversion[A, Int] = _.x
5858

5959
implicitly[Ordering[Int]] // Ok, exists in the standard library
6060
implicitly[Ordering[A]] // Ok, will use the implicit conversion from

docs/docs/reference/instances/discussion/replacing-implicits.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,19 +79,19 @@ lazy implicit val intOrd2: Ord[Int] = new IntOrd
7979

8080
## Implicit Conversions and Classes
8181

82-
The only use cases that are not yet covered by the proposal are implicit conversions and implicit classes. We do not propose to use `instance` in place of `implicit` for these, since that would bring back the uncomfortable similarity between implicit conversions and parameterized implicit aliases. However, there is a way to drop implicit conversions entirely. Scala 3 already [defines](https://github.com/lampepfl/dotty/pull/2065) a class `ImplicitConversion` whose instances are available as implicit conversions.
82+
The only use cases that are not yet covered by the proposal are implicit conversions and implicit classes. We do not propose to use `instance` in place of `implicit` for these, since that would bring back the uncomfortable similarity between implicit conversions and parameterized implicit aliases. However, there is a way to drop implicit conversions entirely. Scala 3 already [defines](https://github.com/lampepfl/dotty/pull/2065) a class `Conversion` whose instances are available as implicit conversions.
8383
```scala
84-
abstract class ImplicitConversion[-T, +U] extends Function1[T, U]
84+
abstract class Conversion[-T, +U] extends Function1[T, U]
8585
```
8686
One can define all implicit conversions as instances of this class. E.g.
8787
```scala
88-
instance StringToToken of ImplicitConversion[String, Token] {
88+
instance StringToToken of Conversion[String, Token] {
8989
def apply(str: String): Token = new KeyWord(str)
9090
}
9191
```
9292
The fact that this syntax is more verbose than simple implicit defs could be a welcome side effect since it might dampen any over-enthusiasm for defining implicit conversions.
9393

94-
That leaves implicit classes. Most use cases of implicit classes are probably already covered by extension methods. For the others, one could always fall back to a pair of a regular class and an `ImplicitConversion` instance. It would be good to do a survey to find out how many classes would be affected.
94+
That leaves implicit classes. Most use cases of implicit classes are probably already covered by extension methods. For the others, one could always fall back to a pair of a regular class and an `Conversion` instance. It would be good to do a survey to find out how many classes would be affected.
9595

9696
## Summoning an Instance
9797

docs/docs/reference/instances/instance-defs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ instance ListOrd[T: Ord] for Ord[List[T]] {
2929
}
3030
```
3131

32-
Instance can be seen as shorthands for what is currently expressed with implicit object and method definitions.
32+
Instance definitions can be seen as shorthands for what is currently expressed with implicit object and method definitions.
3333
For instance, the definition of instance `IntOrd` above defines an implicit value of type `Ord[Int]`. It is hence equivalent
3434
to the following implicit object definition:
3535
```scala

library/src/dotty/DottyPredef.scala

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,6 @@ import scala.forceInline
44

55
object DottyPredef {
66

7-
/** A class for implicit values that can serve as implicit conversions
8-
* The implicit resolution algorithm will act as if there existed
9-
* the additional implicit definition:
10-
*
11-
* def $implicitConversion[T, U](x: T)(c: ImplicitConversion[T, U]): U = c(x)
12-
*
13-
* However, the presence of this definition would slow down implicit search since
14-
* its outermost type matches any pair of types. Therefore, implicit search
15-
* contains a special case in `Implicits#discardForView` which emulates the
16-
* conversion in a more efficient way.
17-
*
18-
* Note that this is a SAM class - function literals are automatically converted
19-
* to `ImplicitConversion` values.
20-
*
21-
* Also note that in bootstrapped dotty, `Predef.<:<` should inherit from
22-
* `ImplicitConversion`. This would cut the number of special cases in
23-
* `discardForView` from two to one.
24-
*/
25-
abstract class ImplicitConversion[-T, +U] extends Function1[T, U]
267

278
@forceInline final def assert(assertion: => Boolean, message: => Any): Unit = {
289
if (!assertion)

library/src/scala/Conversion.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package scala
2+
3+
/** A class for implicit values that can serve as implicit conversions
4+
* The implicit resolution algorithm will act as if there existed
5+
* the additional implicit definition:
6+
*
7+
* def $implicitConversion[T, U](x: T)(c: Conversion[T, U]): U = c(x)
8+
*
9+
* However, the presence of this definition would slow down implicit search since
10+
* its outermost type matches any pair of types. Therefore, implicit search
11+
* contains a special case in `Implicits#discardForView` which emulates the
12+
* conversion in a more efficient way.
13+
*
14+
* Note that this is a SAM class - function literals are automatically converted
15+
* to the `Conversion` values.
16+
*
17+
* Also note that in bootstrapped dotty, `Predef.<:<` should inherit from
18+
* `Conversion`. This would cut the number of special cases in `discardForView`
19+
* from two to one.
20+
*/
21+
@java.lang.FunctionalInterface
22+
abstract class Conversion[-T, +U] extends Function1[T, U]

tests/neg-custom-args/impl-conv/A.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package implConv
33
object A {
44

55
implicit def s2i(x: String): Int = Integer.parseInt(x) // error: feature
6-
implicit val i2s: ImplicitConversion[Int, String] = _.toString // error: feature
6+
implicit val i2s: Conversion[Int, String] = _.toString // error: feature
77

88
implicit class Foo(x: String) {
99
def foo = x.length

tests/pos/reference/instances.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ object Instances extends Common {
146146

147147
class Token(str: String)
148148

149-
instance StringToToken of ImplicitConversion[String, Token] {
149+
instance StringToToken of Conversion[String, Token] {
150150
def apply(str: String): Token = new Token(str)
151151
}
152152

tests/run/t8280.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,14 @@ object Moop3 {
7474
// Dotty deviation. This fails for Dotty with ambiguity error for similar reasons as ob1.
7575
}
7676
object ob2 {
77-
implicit val f1: ImplicitConversion[Int, String] = _ => "Int"
77+
implicit val f1: Conversion[Int, String] = _ => "Int"
7878
implicit def f2(x: Long): String = "Long"
7979

8080
println(5: String)
8181
}
8282
object ob3 {
83-
implicit val f1: ImplicitConversion[Int, String] = _ => "Int"
84-
implicit val f2: ImplicitConversion[Long, String] = _ => "Long"
83+
implicit val f1: Conversion[Int, String] = _ => "Int"
84+
implicit val f2: Conversion[Long, String] = _ => "Long"
8585

8686
println((5: Int): String)
8787
// println(5: String) // error: ambiguity, since both f1 and f2 are applicable to 5.

0 commit comments

Comments
 (0)