Skip to content

Commit 06c0e96

Browse files
committed
Drop the restriction on instantiated TypeVars
Drop the restriction that instantiated TypeVars cannot be conversion targets
1 parent c487723 commit 06c0e96

File tree

7 files changed

+113
-35
lines changed

7 files changed

+113
-35
lines changed

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -456,10 +456,10 @@ object Types extends TypeUtils {
456456
* no `implicitConversions` language import is necessary?
457457
*/
458458
def isConversionTargetType(using Context): Boolean =
459-
dealias(KeepTypeVars | KeepOpaques).match
459+
dealias(KeepOpaques).match
460460
case tp: TypeRef =>
461461
tp.symbol.isClass && tp.symbol.is(Into)
462-
case tp @ AppliedType(tycon: TypeRef, _) =>
462+
case tp @ AppliedType(tycon, _) =>
463463
isInto || tycon.isConversionTargetType
464464
case tp: AndOrType =>
465465
tp.tp1.isConversionTargetType && tp.tp2.isConversionTargetType
@@ -1503,7 +1503,7 @@ object Types extends TypeUtils {
15031503
val tycon1 = tycon.dealias(keeps)
15041504
if tycon1 ne tycon then app.superType.dealias(keeps)
15051505
else this
1506-
case tp: TypeVar if (keeps & KeepTypeVars) == 0 =>
1506+
case tp: TypeVar =>
15071507
val tp1 = tp.instanceOpt
15081508
if tp1.exists then tp1.dealias(keeps) else tp
15091509
case tp: AnnotatedType =>
@@ -7086,7 +7086,6 @@ object Types extends TypeUtils {
70867086
private val KeepAnnots = 1
70877087
private val KeepRefiningAnnots = 2
70887088
private val KeepOpaques = 4
7089-
private val KeepTypeVars = 8
70907089

70917090
// ----- Debug ---------------------------------------------------------
70927091

docs/_docs/reference/experimental/into.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -156,23 +156,21 @@ possibility that someone might want to add an implicit conversion to it.
156156

157157
## Details: Conversion target types
158158

159-
The description so far said that conversions are allowed if the target type
160-
161-
A conversion target type is one of the following:
159+
To make the preceding descriptions more precise: An implicit conversion is permitted without an `implicitConversions` language import if the target type is a valid conversion target type. A valid conversion target type is one of the following:
162160

163161
- a type of the form `into[T]`,
164162
- a reference `p.C` to a class or trait `C` that is declared with an `into` modifier,
165163
which can also be followed by type arguments,
166-
- a type alias of a conversion target type,
167-
- a match type that reduces to a conversion target type,
168-
- an annotated type `T @ann` where `T` is a conversion target type,
169-
- a refined type `T {...}` where `T` is a conversion target type,
170-
- a union `T | U` if two conversion target types `T` and `U`,
171-
- an intersection `T & U` if two conversion target types `T` and `U`,
172-
- an instance of a type parameter that is explicitly instantiated to a conversion target type.
164+
- a type alias of a valid conversion target type,
165+
- a match type that reduces to a valid conversion target type,
166+
- an annotated type `T @ann` where `T` is a valid conversion target type,
167+
- a refined type `T {...}` where `T` is a valid conversion target type,
168+
- a union `T | U` of two valid conversion target types `T` and `U`,
169+
- an intersection `T & U` of two valid conversion target types `T` and `U`,
170+
- an instance of a type parameter that is explicitly instantiated to a valid conversion target type.
173171

174172

175-
Inferred type parameters do not count as conversion target types. For instance, consider:
173+
Type parameters that are not fully instantiated do not count as valid conversion target types. For instance, consider:
176174

177175
```scala
178176
trait Token
@@ -184,12 +182,13 @@ Inferred type parameters do not count as conversion target types. For instance,
184182
This type-checks since the target type of the list elements is the type parameter of the `List.apply` method which is explicitly instantiated to `into[Keyword]`. On the other hand, if we continue the example as follows we get an error:
185183
```scala
186184
val ifKW: into[Keyword] = "if"
187-
List(ifKW, "then", "else") // error
185+
val ys: List[into[Keyword]] = List(ifKW, "then", "else")
188186
```
189-
Here, the type variable of `List.apply` is not explicitly instantiated, but is inferred to have type `into[Keyword]`. This is not enough to allow
187+
Here, the type variable of `List.apply` is not explicitly instantiated
188+
when we check the `List(...)` arguments (it is just upper-bounded by the target type `into[Keyword]`). This is not enough to allow
190189
implicit conversions on the second and third arguments.
191190

192-
Subclasses of `into` classes or traits do not count as conversion target types. For instance, consider:
191+
Subclasses of `into` classes or traits do not count as valid conversion target types. For instance, consider:
193192

194193
```scala
195194
into trait T
@@ -203,3 +202,4 @@ g(1) // error
203202
```
204203
The call `f("abc")` type-checks since `f`'s parameter type `T` is `into`.
205204
But the call `g("abc")` does not type-check since `g`'s parameter type `C` is not `into`. It does not matter that `C` extends a trait `T` that is `into`.
205+

tests/neg/into-inferred.check

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
-- [E007] Type Mismatch Error: tests/neg/into-inferred.scala:34:32 -----------------------------------------------------
2+
34 | val l1: List[into[Keyword]] = l :+ "then" :+ "else" // error
3+
| ^^^^^^^^^^^^^^^^^^^^^
4+
| Found: List[Conversion.into[Keyword] | String]
5+
| Required: List[Conversion.into[Keyword]]
6+
|
7+
| longer explanation available when compiling with `-explain`
8+
-- Feature Warning: tests/neg/into-inferred.scala:22:43 ----------------------------------------------------------------
9+
22 | val ys: List[into[Keyword]] = List(ifKW, "then", "else") // warn // warn
10+
| ^^^^^^
11+
| Use of implicit conversion given instance given_Conversion_String_Keyword in object Test should be enabled
12+
| by adding the import clause 'import scala.language.implicitConversions'
13+
| or by setting the compiler option -language:implicitConversions.
14+
| See the Scala docs for value scala.language.implicitConversions for a discussion
15+
| why the feature should be explicitly enabled.
16+
-- Feature Warning: tests/neg/into-inferred.scala:22:51 ----------------------------------------------------------------
17+
22 | val ys: List[into[Keyword]] = List(ifKW, "then", "else") // warn // warn
18+
| ^^^^^^
19+
| Use of implicit conversion given instance given_Conversion_String_Keyword in object Test should be enabled
20+
| by adding the import clause 'import scala.language.implicitConversions'
21+
| or by setting the compiler option -language:implicitConversions.
22+
| See the Scala docs for value scala.language.implicitConversions for a discussion
23+
| why the feature should be explicitly enabled.
24+
-- Feature Warning: tests/neg/into-inferred.scala:35:42 ----------------------------------------------------------------
25+
35 | val l2: List[into[Keyword]] = l ++ List("then", "else") // warn // warn
26+
| ^^^^^^
27+
| Use of implicit conversion given instance given_Conversion_String_Keyword in object Test should be enabled
28+
| by adding the import clause 'import scala.language.implicitConversions'
29+
| or by setting the compiler option -language:implicitConversions.
30+
| See the Scala docs for value scala.language.implicitConversions for a discussion
31+
| why the feature should be explicitly enabled.
32+
-- Feature Warning: tests/neg/into-inferred.scala:35:50 ----------------------------------------------------------------
33+
35 | val l2: List[into[Keyword]] = l ++ List("then", "else") // warn // warn
34+
| ^^^^^^
35+
| Use of implicit conversion given instance given_Conversion_String_Keyword in object Test should be enabled
36+
| by adding the import clause 'import scala.language.implicitConversions'
37+
| or by setting the compiler option -language:implicitConversions.
38+
| See the Scala docs for value scala.language.implicitConversions for a discussion
39+
| why the feature should be explicitly enabled.

tests/neg/into-inferred.scala

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//> using options -feature
2+
import language.experimental.into
3+
import Conversion.{into, underlying}
4+
5+
trait Token
6+
class Keyword(str: String)
7+
case class Phrase(words: into[Keyword]*)
8+
9+
object Test:
10+
given Conversion[String, Keyword] = Keyword(_)
11+
12+
val xs = List[into[Keyword]]("if", "then", "else") // ok
13+
val _: List[Keyword] = xs.map(_.underlying)
14+
15+
val p = Phrase("if", "then", "else") // ok
16+
val ws = p.words
17+
val _: Seq[Keyword] = ws
18+
19+
val p2 = Phrase(xs*) // ok
20+
21+
val ifKW: into[Keyword] = "if"
22+
val ys: List[into[Keyword]] = List(ifKW, "then", "else") // warn // warn
23+
24+
val s = Set(ifKW)
25+
val s1 = s + "then" + "else"
26+
val _: Set[into[Keyword]] = s1
27+
val s2 = s ++ List("then", "else")
28+
val s3: Set[into[Keyword] | String] = s2
29+
val s4 = s3.map(_.underlying)
30+
val _: Set[Keyword | String] = s4
31+
32+
33+
val l = List(ifKW)
34+
val l1: List[into[Keyword]] = l :+ "then" :+ "else" // error
35+
val l2: List[into[Keyword]] = l ++ List("then", "else") // warn // warn
36+
37+

tests/warn/into-as-mod.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,9 @@ object Test:
1313
def g(x: C) = ()
1414
f(1) // ok
1515
g(1) // warn
16+
17+
into class Keyword(str: String)
18+
given stringToKeyword: Conversion[String, Keyword] = Keyword(_)
19+
20+
val dclKeywords = Set[Keyword]("def", "val") // ok
21+
val keywords = dclKeywords + "if" + "then" + "else" // ok

tests/warn/into-inferred.check

Lines changed: 0 additions & 16 deletions
This file was deleted.

tests/warn/into-inferred.scala

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//> using options -feature
2-
32
import language.experimental.into
43
import Conversion.{into, underlying}
54

@@ -21,3 +20,17 @@ object Test:
2120

2221
val ifKW: into[Keyword] = "if"
2322
val ys: List[into[Keyword]] = List(ifKW, "then", "else") // warn // warn
23+
24+
val s = Set(ifKW)
25+
val s1 = s + "then" + "else"
26+
val _: Set[into[Keyword]] = s1
27+
val s2 = s ++ List("then", "else")
28+
val s3: Set[into[Keyword] | String] = s2
29+
val s4 = s3.map(_.underlying)
30+
val _: Set[Keyword | String] = s4
31+
32+
val l = List(ifKW)
33+
val l1: List[into[Keyword]] = l :+ "then" :+ "else" // error
34+
val l2: List[into[Keyword]] = l ++ List("then", "else") // warn // warn
35+
36+

0 commit comments

Comments
 (0)