Skip to content

Fix reachability of non-reducing match type children #13251

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 1 commit into from
Sep 7, 2021
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
11 changes: 8 additions & 3 deletions compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -702,13 +702,18 @@ object TypeOps:
//
// 1. Replace type parameters in T with tvars
// 2. Replace `A.this.C` with `A#C` (see tests/patmat/i12681.scala)
// 3. Replace non-reducing MatchType with its bound
//
val approximateParent = new TypeMap {
val boundTypeParams = util.HashMap[TypeRef, TypeVar]()

def apply(tp: Type): Type = tp.dealias match {
case _: MatchType =>
tp // break cycles
case tp: MatchType =>
val reduced = tp.reduced
if reduced.exists then tp // break cycles
else mapOver(tp.bound) // if the match type doesn't statically reduce
// then to avoid it failing the <:<
// we'll approximate by widening to its bounds

case ThisType(tref: TypeRef) if !tref.symbol.isStaticOwner =>
tref
Expand All @@ -729,7 +734,7 @@ object TypeOps:
tv
end if

case AppliedType(tycon: TypeRef, _) if !tycon.dealias.typeSymbol.isClass =>
case tp @ AppliedType(tycon: TypeRef, _) if !tycon.dealias.typeSymbol.isClass && !tp.isMatchAlias =>

// In tests/patmat/i3645g.scala, we need to tell whether it's possible
// that K1 <: K[Foo]. If yes, we issue a warning; otherwise, no
Expand Down
5 changes: 1 addition & 4 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -427,10 +427,7 @@ object Types {
def isMatch(using Context): Boolean = stripped match {
case _: MatchType => true
case tp: HKTypeLambda => tp.resType.isMatch
case tp: AppliedType =>
tp.tycon match
case tycon: TypeRef => tycon.info.isInstanceOf[MatchAlias]
case _ => false
case tp: AppliedType => tp.isMatchAlias
case _ => false
}

Expand Down
89 changes: 89 additions & 0 deletions tests/patmat/i13189.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// original report
def foo(opt: Option[Tuple.Tail[NonEmptyTuple]]): Unit =
opt match
case None => ???
case Some(a) => ???


// again with a mini-Tuple with the extra NonEmptyTupExtra parent, to test transitivity
object WithExtraParent:
sealed trait Tup

object Tup {
type Tail[X <: NonEmptyTup] <: Tup = X match {
case _ **: xs => xs
}
}

object EmptyTup extends Tup

sealed trait NonEmptyTup extends Tup
sealed trait NonEmptyTupExtra extends NonEmptyTup
sealed abstract class **:[+H, +T <: Tup] extends NonEmptyTupExtra

object **: {
def unapply[H, T <: Tup](x: H **: T): (H, T) = null
}

def foo(opt: Option[Tup.Tail[NonEmptyTup]]): Unit =
opt match
case None => ???
case Some(a) => ???
end WithExtraParent


// again with a non-abstract parent
object WithNonAbstractParent:
sealed trait Tup

object Tup {
type Tail[X <: NonEmptyTup] <: Tup = X match {
case _ **: xs => xs
}
}

object EmptyTup extends Tup

sealed class NonEmptyTup extends Tup
sealed class **:[+H, +T <: Tup] extends NonEmptyTup

object **: {
def unapply[H, T <: Tup](x: H **: T): (H, T) = null
}

def foo(opt: Option[Tup.Tail[NonEmptyTup]]): Unit =
opt match
case None => ???
case Some(a) => ???
end WithNonAbstractParent


// again with multiple children, but an exhaustive match
object WithExhaustiveMatch:
sealed trait Tup

object Tup {
type Tail[X <: NonEmptyTup] <: Tup = X match {
case _ **: xs => xs
case _ *+: xs => xs
}
}

object EmptyTup extends Tup

sealed trait NonEmptyTup extends Tup
sealed abstract class **:[+H, +T <: Tup] extends NonEmptyTup
sealed abstract class *+:[+H, +T <: Tup] extends NonEmptyTup

object **: {
def unapply[H, T <: Tup](x: H **: T): (H, T) = null
}
object *+: {
def unapply[H, T <: Tup](x: H *+: T): (H, T) = null
}

def foo(opt: Option[Tup.Tail[NonEmptyTup]]): Unit =
opt match
case None => ???
case Some(a) => ???
end WithExhaustiveMatch