Skip to content

Fix #3443: adapt child compatibility check #3446

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 11, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
40 changes: 21 additions & 19 deletions compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -606,20 +606,19 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
}
}

val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) }
val protoTp1 = thisTypeMap(tp1.appliedTo(tvars))

// replace type parameter references with fresh type vars or bounds
// replace type parameter references with bounds
val typeParamMap = new TypeMap {
def apply(t: Type): Type = t match {

case tp: TypeRef if tp.symbol.is(TypeParam) && tp.underlying.isInstanceOf[TypeBounds] =>
// See tests/patmat/gadt.scala tests/patmat/exhausting.scala
val bound =
if (variance == 0) tp.underlying.bounds // non-variant case is not well-founded
else if (variance == 1) TypeBounds.upper(tp)
else TypeBounds.lower(tp)
newTypeVar(bound)
val exposed =
if (variance == 0) newTypeVar(tp.underlying.bounds)
else if (variance == 1) expose(tp, true)
else expose(tp, false)

debug.println(s"$tp exposed to =====> " + exposed)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could put $exposed inside the string

exposed
case tp: RefinedType if tp.refinedInfo.isInstanceOf[TypeBounds] =>
// Ideally, we would expect type inference to do the job
// Check tests/patmat/t9657.scala
Expand All @@ -629,6 +628,9 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
}
}

val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) }
val protoTp1 = thisTypeMap(tp1.appliedTo(tvars))

if (protoTp1 <:< tp2 && isFullyDefined(protoTp1, ForceDegree.noBottom)) protoTp1
else {
val protoTp2 = typeParamMap(tp2)
Expand Down Expand Up @@ -778,41 +780,41 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
*
* A <: X :> Y B <: U :> V M { type T <: A :> B } ~~> M { type T <: X :> V }
*/
def expose(tp: Type, refineCtx: Boolean = false, up: Boolean = true): Type = tp match {
def expose(tp: Type, up: Boolean = true): Type = tp match {
Copy link
Contributor

@OlivierBlanvillain OlivierBlanvillain Nov 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove the default = true here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just removed the whole method :)

case tp: AppliedType =>
tp.derivedAppliedType(expose(tp.tycon, refineCtx, up), tp.args.map(expose(_, refineCtx, up)))
tp.derivedAppliedType(expose(tp.tycon, up), tp.args.map(expose(_, up)))

case tp: TypeAlias =>
val hi = expose(tp.alias, refineCtx, up)
val lo = expose(tp.alias, refineCtx, up)
val hi = expose(tp.alias, up)
val lo = expose(tp.alias, up)

if (hi =:= lo)
tp.derivedTypeAlias(hi)
else
tp.derivedTypeBounds(lo, hi)

case tp @ TypeBounds(lo, hi) =>
tp.derivedTypeBounds(expose(lo, refineCtx, false), expose(hi, refineCtx, true))
tp.derivedTypeBounds(expose(lo, false), expose(hi, true))

case tp: RefinedType =>
tp.derivedRefinedType(
expose(tp.parent),
tp.refinedName,
expose(tp.refinedInfo, true, up)
expose(tp.refinedInfo, up)
)
case tp: TypeProxy if refineCtx =>
case tp: TypeProxy =>
tp.underlying match {
case TypeBounds(lo, hi) =>
expose(if (up) hi else lo, refineCtx, up)
expose(if (up) hi else lo, up)
case _ =>
tp
}

case OrType(tp1, tp2) =>
OrType(expose(tp1, refineCtx, up), expose(tp2, refineCtx, up))
OrType(expose(tp1, up), expose(tp2, up))

case AndType(tp1, tp2) =>
AndType(expose(tp1, refineCtx, up), expose(tp2, refineCtx, up))
AndType(expose(tp1, up), expose(tp2, up))

case _ => tp
}
Expand Down
19 changes: 19 additions & 0 deletions tests/patmat/i3443.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
object Test {
// shapeless.Coproduct
sealed trait Coproduct extends Product with Serializable
sealed trait :+:[+H, +T <: Coproduct] extends Coproduct
final case class Inl[+H, +T <: Coproduct](head : H) extends :+:[H, T]
final case class Inr[+H, +T <: Coproduct](tail : T) extends :+:[H, T]
sealed trait CNil extends Coproduct

// Note that this only appears when T is a type parameter. Replaying T with
// a concrete type (CNil or another :+:) leads to accurate warnnings
def f[T <: Coproduct](fa: Int :+: T) =
fa match {
case Inl(x) => 1
case Inr(x) => 2 // Dotty thinks this unreachable, but it is!
}

def main(args: Array[String]): Unit =
assert(f(Inr(Inl(-1))) == 2) // Example of reachability
}