Skip to content

Commit a26cebe

Browse files
committed
check position of typeparam and document example
1 parent 747b2c7 commit a26cebe

File tree

4 files changed

+64
-7
lines changed

4 files changed

+64
-7
lines changed

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,15 @@ class VarianceChecker(using Context) {
167167
def checkVariance(sym: Symbol, pos: SrcPos) = Validator.validateDefinition(sym) match {
168168
case Some(VarianceError(tvar, required)) =>
169169
def msg =
170+
val towner = tvar.owner
170171
val enumAddendum =
171-
if sym.owner.isAllOf(EnumCase) && sym.owner.isClass then
172-
i"\nenum case ${sym.owner} may require explicit type parameters to resolve this issue"
172+
if towner.isAllOf(EnumCase) && towner.isClass
173+
&& tvar.span.exists && towner.span.exists
174+
&& tvar.span.start < towner.span.start // implies that `tvar` was not user declared
175+
then
176+
val example =
177+
"See an example at http://dotty.epfl.ch/docs/reference/enums/adts.html#parameter-variance-of-enums"
178+
i"\nenum case ${towner} requires explicit declaration of $tvar to resolve this issue.\n$example"
173179
else
174180
""
175181
i"${varianceLabel(tvar.flags)} $tvar occurs in ${varianceLabel(required)} position in type ${sym.info} of $sym$enumAddendum"

docs/docs/reference/enums/adts.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,43 @@ object Option {
7878
}
7979
```
8080

81+
### Parameter Variance of Enums
82+
83+
By default, parameterized cases of enums with type parameters will copy the type parameters of their parent, along
84+
with any variance notations. As usual, it is important to use type parameters carefully when they are variant, as shown
85+
below:
86+
87+
The following `View` enum has a contravariant type parameter `T` and a single case `Refl`, representing a function
88+
mapping a type `T` to itself:
89+
```scala
90+
enum View[-T]:
91+
case Refl(f: T => T)
92+
```
93+
The definition of `Refl` is incorrect, as it uses contravariant type `T` in the covariant result position of a
94+
function type, leading to the following error:
95+
```scala
96+
-- Error: View.scala:2:12 --------
97+
2 | case Refl(f: T => T)
98+
| ^^^^^^^^^
99+
|contravariant type T occurs in covariant position in type T => T of value f
100+
|enum case class Refl requires explicit declaration of type T to resolve this issue.
101+
```
102+
Because `Refl` does not declare explicit parameters, it looks to the compiler like the following:
103+
```scala
104+
enum View[-T]:
105+
case Refl[/*synthetic*/-T1](f: T1 => T1) extends View[T1]
106+
```
107+
108+
The compiler has inferred for `Refl` the contravariant type parameter `T1`, following `T` in `View`. We can now clearly see that `Refl`
109+
needs to declare its own type parameters to satisfy the variance condition, and can remedy the error by the following
110+
change to `Refl`:
111+
112+
```diff
113+
enum View[-T]:
114+
- case Refl(f: T => T)
115+
+ case Refl[T](f: T => T) extends View[T]
116+
```
117+
81118
Enumerations and ADTs have been presented as two different
82119
concepts. But since they share the same syntactic construct, they can
83120
be seen simply as two ends of a spectrum and it is perfectly possible
Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
-- Error: tests/neg-custom-args/fatal-warnings/enum-variance.scala:2:12 ------------------------------------------------
2-
2 | case Refl(f: T => T) // error: covariant T in contravariant position... enum case may require explicit type parameters
2+
2 | case Refl(f: T => T) // error: enum case class Refl requires explicit declaration of type T
33
| ^^^^^^^^^
4-
| covariant type T occurs in contravariant position in type T => T of value f
5-
| enum case class Refl may require explicit type parameters to resolve this issue
4+
| contravariant type T occurs in covariant position in type T => T of value f
5+
| enum case class Refl requires explicit declaration of type T to resolve this issue.
6+
| See an example at http://dotty.epfl.ch/docs/reference/enums/adts.html#parameter-variance-of-enums
7+
-- Error: tests/neg-custom-args/fatal-warnings/enum-variance.scala:5:16 ------------------------------------------------
8+
5 | case Refl[-T](f: T => T) extends ExplicitView[T] // error: contravariant type T occurs in covariant position
9+
| ^^^^^^^^^
10+
| contravariant type T occurs in covariant position in type T => T of value f
Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,11 @@
1-
enum View[+T]:
2-
case Refl(f: T => T) // error: covariant T in contravariant position... enum case may require explicit type parameters
1+
enum View[-T]:
2+
case Refl(f: T => T) // error: enum case class Refl requires explicit declaration of type T
3+
4+
enum ExplicitView[-T]: // desugared version of View
5+
case Refl[-T](f: T => T) extends ExplicitView[T] // error: contravariant type T occurs in covariant position
6+
7+
enum InvariantView[-T, +U] extends (T => U):
8+
case Refl[T](f: T => T) extends InvariantView[T, T]
9+
10+
final def apply(t: T): U = this match
11+
case refl: Refl[t] => refl.f(t)

0 commit comments

Comments
 (0)