Skip to content

Commit

Permalink
Merge pull request #3982 from dotty-staging/fix-#3976
Browse files Browse the repository at this point in the history
Fix #3976: Fix Eq method for enums with higher-kinded parameters
  • Loading branch information
odersky authored Feb 12, 2018
2 parents 5943331 + 706304f commit e2f72cd
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 18 deletions.
25 changes: 21 additions & 4 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -341,8 +341,21 @@ object desugar {
(if (args.isEmpty) tycon else AppliedTypeTree(tycon, args))
.withPos(cdef.pos.startPos)

def appliedRef(tycon: Tree, tparams: List[TypeDef] = constrTparams) =
appliedTypeTree(tycon, tparams map refOfDef)
def appliedRef(tycon: Tree, tparams: List[TypeDef] = constrTparams, widenHK: Boolean = false) = {
val targs = for (tparam <- tparams) yield {
val targ = refOfDef(tparam)
def fullyApplied(tparam: Tree): Tree = tparam match {
case TypeDef(_, LambdaTypeTree(tparams, body)) =>
AppliedTypeTree(targ, tparams.map(_ => TypeBoundsTree(EmptyTree, EmptyTree)))
case TypeDef(_, rhs: DerivedTypeTree) =>
fullyApplied(rhs.watched)
case _ =>
targ
}
if (widenHK) fullyApplied(tparam) else targ
}
appliedTypeTree(tycon, targs)
}

// a reference to the class type bound by `cdef`, with type parameters coming from the constructor
val classTypeRef = appliedRef(classTycon)
Expand Down Expand Up @@ -431,12 +444,16 @@ object desugar {
//
// implicit def eqInstance[T1$1, ..., Tn$1, T1$2, ..., Tn$2](implicit
// ev1: Eq[T1$1, T1$2], ..., evn: Eq[Tn$1, Tn$2]])
// : Eq[C[T1$1, ..., Tn$1], C[T1$2, ..., Tn$2]] = Eq
// : Eq[C[T1$, ..., Tn$1], C[T1$2, ..., Tn$2]] = Eq
//
// If any of the T_i are higher-kinded, say `Ti[X1 >: L1 <: U1, ..., Xm >: Lm <: Um]`,
// the corresponding type parameters for $ev_i are `Ti$1[_, ..., _], Ti$2[_, ..., _]`
// (with m underscores `_`).
def eqInstance = {
val leftParams = constrTparams.map(derivedTypeParam(_, "$1"))
val rightParams = constrTparams.map(derivedTypeParam(_, "$2"))
val subInstances = (leftParams, rightParams).zipped.map((param1, param2) =>
appliedRef(ref(defn.EqType), List(param1, param2)))
appliedRef(ref(defn.EqType), List(param1, param2), widenHK = true))
DefDef(
name = nme.eqInstance,
tparams = leftParams ++ rightParams,
Expand Down
32 changes: 18 additions & 14 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1754,21 +1754,25 @@ object Types {

val idx = typeParams.indexOf(param)

assert(args.nonEmpty,
i"""bad parameter reference $this at ${ctx.phase}
|the parameter is ${param.showLocated} but the prefix $prefix
|does not define any corresponding arguments.""")

val argInfo = args(idx) match {
case arg: TypeBounds =>
val v = param.paramVariance
val pbounds = param.paramInfo
if (v > 0 && pbounds.loBound.dealias.isBottomType) TypeAlias(arg.hiBound & rebase(pbounds.hiBound))
else if (v < 0 && pbounds.hiBound.dealias.isTopType) TypeAlias(arg.loBound | rebase(pbounds.loBound))
else arg recoverable_& rebase(pbounds)
case arg => TypeAlias(arg)
if (idx < args.length) {
val argInfo = args(idx) match {
case arg: TypeBounds =>
val v = param.paramVariance
val pbounds = param.paramInfo
if (v > 0 && pbounds.loBound.dealias.isBottomType) TypeAlias(arg.hiBound & rebase(pbounds.hiBound))
else if (v < 0 && pbounds.hiBound.dealias.isTopType) TypeAlias(arg.loBound | rebase(pbounds.loBound))
else arg recoverable_& rebase(pbounds)
case arg => TypeAlias(arg)
}
param.derivedSingleDenotation(param, argInfo)
}
else {
assert(ctx.reporter.errorsReported,
i"""bad parameter reference $this at ${ctx.phase}
|the parameter is ${param.showLocated} but the prefix $prefix
|does not define any corresponding arguments.""")
NoDenotation
}
param.derivedSingleDenotation(param, argInfo)
}

/** Reload denotation by computing the member with the reference's name as seen
Expand Down
58 changes: 58 additions & 0 deletions tests/neg/i3976.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
object Test {
enum Hoge[F[_]] {
case A extends Hoge[List]
case B extends Hoge[[X] => String]
}
import Hoge._

A == A
A == (B: Hoge[_])

A == B // error: cannot be compared

class C

A == "" // error: cannot be compared
A == new C // error: cannot be compared

}

object Test2 {
enum Hoge[F[G[_]]] {
case A extends Hoge[[F[_]] => F[Int]]
case B extends Hoge[[F[_]] => F[String]]
}
import Hoge._

A == A
A == (B: Hoge[_])

A == B

class C

A == "" // error: cannot be compared
A == new C // error: cannot be compared

}

object Test3 {
enum Hoge[F[G[_]]] {
case A extends Hoge[[X] => List] // error: wrong kind
case B extends Hoge[[X] => [Y] => String] // error: wrong kind
}
import Hoge._

A == A
A == (B: Hoge[_])

A == B

class C

A == ""
A == new C

}


10 changes: 10 additions & 0 deletions tests/pos/i3976.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
object Test {
enum Hoge[F[_]] {
case A extends Hoge[List]
case B extends Hoge[[X] => String]
}
import Hoge._

A == A
A == (B: Hoge[_])
}

0 comments on commit e2f72cd

Please sign in to comment.