Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 43 additions & 14 deletions src/dotty/tools/dotc/typer/Inferencing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ object Inferencing {
constr.println(s"qualifying undet vars: ${constraint.uninstVars filter qualifies map (tvar => s"$tvar / ${tvar.show}")}, constraint: ${constraint.show}")

val vs = variances(tp, qualifies)
var changed = false
val hasUnreportedErrors = ctx.typerState.reporter match {
case r: StoreReporter if r.hasErrors => true
case _ => false
Expand Down Expand Up @@ -253,17 +252,13 @@ object Inferencing {
if (v != 0) {
typr.println(s"interpolate ${if (v == 1) "co" else "contra"}variant ${tvar.show} in ${tp.show}")
tvar.instantiate(fromBelow = v == 1)
changed = true
}
}
if (changed) // instantiations might have uncovered new typevars to interpolate
interpolateUndetVars(tree, ownedBy)
else
for (tvar <- constraint.uninstVars)
if (!(vs contains tvar) && qualifies(tvar)) {
typr.println(s"instantiating non-occurring ${tvar.show} in ${tp.show}")
tvar.instantiate(fromBelow = true)
}
for (tvar <- constraint.uninstVars)
if (!(vs contains tvar) && qualifies(tvar)) {
typr.println(s"instantiating non-occurring ${tvar.show} in ${tp.show} / $tp")
tvar.instantiate(fromBelow = true)
}
}
if (constraint.uninstVars exists qualifies) interpolate()
}
Expand Down Expand Up @@ -306,20 +301,54 @@ object Inferencing {
* we want to instantiate U to x.type right away. No need to wait further.
*/
private def variances(tp: Type, include: TypeVar => Boolean)(implicit ctx: Context): VarianceMap = Stats.track("variances") {
val accu = new TypeAccumulator[VarianceMap] {
val constraint = ctx.typerState.constraint

object accu extends TypeAccumulator[VarianceMap] {
def setVariance(v: Int) = variance = v
def apply(vmap: VarianceMap, t: Type): VarianceMap = t match {
case t: TypeVar if !t.isInstantiated && (ctx.typerState.constraint contains t) && include(t) =>
case t: TypeVar
if !t.isInstantiated && (ctx.typerState.constraint contains t) && include(t) =>
val v = vmap(t)
if (v == null) vmap.updated(t, variance)
else if (v == variance) vmap
else if (v == variance || v == 0) vmap
else vmap.updated(t, 0)
case _ =>
foldOver(vmap, t)
}
override def applyToPrefix(vmap: VarianceMap, t: NamedType) =
apply(vmap, t.prefix)
}
accu(SimpleMap.Empty, tp)

/** Include in `vmap` type variables occurring in the constraints of type variables
* already in `vmap`. Specifically:
* - if `tvar` is covariant in `vmap`, include all variables in its lower bound
* (because they influence the minimal solution of `tvar`),
* - if `tvar` is contravariant in `vmap`, include all variables in its upper bound
* at flipped variances (because they influence the maximal solution of `tvar`),
* - if `tvar` is nonvariant in `vmap`, include all variables in its upper and lower
* bounds as non-variant.
* Do this in a fixpoint iteration until `vmap` stabilizes.
*/
def propagate(vmap: VarianceMap): VarianceMap = {
var vmap1 = vmap
def traverse(tp: Type) = { vmap1 = accu(vmap1, tp) }
vmap.foreachBinding { (tvar, v) =>
val param = tvar.origin
val e = constraint.entry(param)
accu.setVariance(v)
if (v >= 0) {
traverse(e.bounds.lo)
constraint.lower(param).foreach(p => traverse(constraint.typeVarOfParam(p)))
}
if (v <= 0) {
traverse(e.bounds.hi)
constraint.upper(param).foreach(p => traverse(constraint.typeVarOfParam(p)))
}
}
if (vmap1 eq vmap) vmap else propagate(vmap1)
}

propagate(accu(SimpleMap.Empty, tp))
}
}

Expand Down
19 changes: 19 additions & 0 deletions tests/pos/i1500.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
sealed trait HList
sealed trait HNil extends HList
sealed trait ::[+H, +T <: HList] extends HList

case class Size[L <: HList](value: Int)

object Size {
implicit val caseHNil: Size[HNil] = Size(0)
implicit def caseHCons[H, T <: HList](implicit e: Size[T]): Size[H :: T] = Size(e.value + 1)
}

object HListTest {
def main(args: Array[String]): Unit = {
assert(implicitly[Size[HNil]].value == 0)
assert(implicitly[Size[Int :: HNil]].value == 1)
assert(implicitly[Size[Int :: Int :: HNil]].value == 2)
assert(implicitly[Size[Int :: Int :: Int :: HNil]].value == 3)
}
}