Skip to content

Commit e2f72cd

Browse files
authored
Merge pull request #3982 from dotty-staging/fix-#3976
Fix #3976: Fix Eq method for enums with higher-kinded parameters
2 parents 5943331 + 706304f commit e2f72cd

File tree

4 files changed

+107
-18
lines changed

4 files changed

+107
-18
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -341,8 +341,21 @@ object desugar {
341341
(if (args.isEmpty) tycon else AppliedTypeTree(tycon, args))
342342
.withPos(cdef.pos.startPos)
343343

344-
def appliedRef(tycon: Tree, tparams: List[TypeDef] = constrTparams) =
345-
appliedTypeTree(tycon, tparams map refOfDef)
344+
def appliedRef(tycon: Tree, tparams: List[TypeDef] = constrTparams, widenHK: Boolean = false) = {
345+
val targs = for (tparam <- tparams) yield {
346+
val targ = refOfDef(tparam)
347+
def fullyApplied(tparam: Tree): Tree = tparam match {
348+
case TypeDef(_, LambdaTypeTree(tparams, body)) =>
349+
AppliedTypeTree(targ, tparams.map(_ => TypeBoundsTree(EmptyTree, EmptyTree)))
350+
case TypeDef(_, rhs: DerivedTypeTree) =>
351+
fullyApplied(rhs.watched)
352+
case _ =>
353+
targ
354+
}
355+
if (widenHK) fullyApplied(tparam) else targ
356+
}
357+
appliedTypeTree(tycon, targs)
358+
}
346359

347360
// a reference to the class type bound by `cdef`, with type parameters coming from the constructor
348361
val classTypeRef = appliedRef(classTycon)
@@ -431,12 +444,16 @@ object desugar {
431444
//
432445
// implicit def eqInstance[T1$1, ..., Tn$1, T1$2, ..., Tn$2](implicit
433446
// ev1: Eq[T1$1, T1$2], ..., evn: Eq[Tn$1, Tn$2]])
434-
// : Eq[C[T1$1, ..., Tn$1], C[T1$2, ..., Tn$2]] = Eq
447+
// : Eq[C[T1$, ..., Tn$1], C[T1$2, ..., Tn$2]] = Eq
448+
//
449+
// If any of the T_i are higher-kinded, say `Ti[X1 >: L1 <: U1, ..., Xm >: Lm <: Um]`,
450+
// the corresponding type parameters for $ev_i are `Ti$1[_, ..., _], Ti$2[_, ..., _]`
451+
// (with m underscores `_`).
435452
def eqInstance = {
436453
val leftParams = constrTparams.map(derivedTypeParam(_, "$1"))
437454
val rightParams = constrTparams.map(derivedTypeParam(_, "$2"))
438455
val subInstances = (leftParams, rightParams).zipped.map((param1, param2) =>
439-
appliedRef(ref(defn.EqType), List(param1, param2)))
456+
appliedRef(ref(defn.EqType), List(param1, param2), widenHK = true))
440457
DefDef(
441458
name = nme.eqInstance,
442459
tparams = leftParams ++ rightParams,

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

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1754,21 +1754,25 @@ object Types {
17541754

17551755
val idx = typeParams.indexOf(param)
17561756

1757-
assert(args.nonEmpty,
1758-
i"""bad parameter reference $this at ${ctx.phase}
1759-
|the parameter is ${param.showLocated} but the prefix $prefix
1760-
|does not define any corresponding arguments.""")
1761-
1762-
val argInfo = args(idx) match {
1763-
case arg: TypeBounds =>
1764-
val v = param.paramVariance
1765-
val pbounds = param.paramInfo
1766-
if (v > 0 && pbounds.loBound.dealias.isBottomType) TypeAlias(arg.hiBound & rebase(pbounds.hiBound))
1767-
else if (v < 0 && pbounds.hiBound.dealias.isTopType) TypeAlias(arg.loBound | rebase(pbounds.loBound))
1768-
else arg recoverable_& rebase(pbounds)
1769-
case arg => TypeAlias(arg)
1757+
if (idx < args.length) {
1758+
val argInfo = args(idx) match {
1759+
case arg: TypeBounds =>
1760+
val v = param.paramVariance
1761+
val pbounds = param.paramInfo
1762+
if (v > 0 && pbounds.loBound.dealias.isBottomType) TypeAlias(arg.hiBound & rebase(pbounds.hiBound))
1763+
else if (v < 0 && pbounds.hiBound.dealias.isTopType) TypeAlias(arg.loBound | rebase(pbounds.loBound))
1764+
else arg recoverable_& rebase(pbounds)
1765+
case arg => TypeAlias(arg)
1766+
}
1767+
param.derivedSingleDenotation(param, argInfo)
1768+
}
1769+
else {
1770+
assert(ctx.reporter.errorsReported,
1771+
i"""bad parameter reference $this at ${ctx.phase}
1772+
|the parameter is ${param.showLocated} but the prefix $prefix
1773+
|does not define any corresponding arguments.""")
1774+
NoDenotation
17701775
}
1771-
param.derivedSingleDenotation(param, argInfo)
17721776
}
17731777

17741778
/** Reload denotation by computing the member with the reference's name as seen

tests/neg/i3976.scala

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
object Test {
2+
enum Hoge[F[_]] {
3+
case A extends Hoge[List]
4+
case B extends Hoge[[X] => String]
5+
}
6+
import Hoge._
7+
8+
A == A
9+
A == (B: Hoge[_])
10+
11+
A == B // error: cannot be compared
12+
13+
class C
14+
15+
A == "" // error: cannot be compared
16+
A == new C // error: cannot be compared
17+
18+
}
19+
20+
object Test2 {
21+
enum Hoge[F[G[_]]] {
22+
case A extends Hoge[[F[_]] => F[Int]]
23+
case B extends Hoge[[F[_]] => F[String]]
24+
}
25+
import Hoge._
26+
27+
A == A
28+
A == (B: Hoge[_])
29+
30+
A == B
31+
32+
class C
33+
34+
A == "" // error: cannot be compared
35+
A == new C // error: cannot be compared
36+
37+
}
38+
39+
object Test3 {
40+
enum Hoge[F[G[_]]] {
41+
case A extends Hoge[[X] => List] // error: wrong kind
42+
case B extends Hoge[[X] => [Y] => String] // error: wrong kind
43+
}
44+
import Hoge._
45+
46+
A == A
47+
A == (B: Hoge[_])
48+
49+
A == B
50+
51+
class C
52+
53+
A == ""
54+
A == new C
55+
56+
}
57+
58+

tests/pos/i3976.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
object Test {
2+
enum Hoge[F[_]] {
3+
case A extends Hoge[List]
4+
case B extends Hoge[[X] => String]
5+
}
6+
import Hoge._
7+
8+
A == A
9+
A == (B: Hoge[_])
10+
}

0 commit comments

Comments
 (0)