Skip to content

Commit

Permalink
Unwiden scrutinee types, fixing match analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand committed Aug 28, 2021
1 parent 17141e8 commit 0eea749
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 10 deletions.
34 changes: 24 additions & 10 deletions compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Expand Up @@ -502,12 +502,13 @@ class SpaceEngine(using Context) extends SpaceLogic {

/** Is `tp1` a subtype of `tp2`? */
def isSubType(tp1: Type, tp2: Type): Boolean = {
debug.println(TypeComparer.explained(_.isSubType(tp1, tp2)))
//debug.println(TypeComparer.explained(_.isSubType(tp1, tp2)))
val res = if (ctx.explicitNulls) {
tp1 <:< tp2
} else {
(tp1 != constantNullType || tp2 == constantNullType) && tp1 <:< tp2
}
debug.println(i"$tp1 <:< $tp2 = $res")
res
}

Expand Down Expand Up @@ -663,7 +664,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
|| cls.isAllOf(JavaEnumTrait)
|| tp.isRef(defn.BooleanClass)
|| tp.isRef(defn.UnitClass)
debug.println(s"decomposable: ${tp.show} = $res")
//debug.println(s"decomposable: ${tp.show} = $res")
res

/** Show friendly type name with current scope in mind
Expand Down Expand Up @@ -838,14 +839,26 @@ class SpaceEngine(using Context) extends SpaceLogic {
}
}.apply(false, tp)

/** Return the underlying type of non-module, non-constant, non-enum case singleton types.
* Also widen ExprType to its result type.
* For example, with `val opt = None`, widen `opt.type` to `None.type`. */
def toUnderlying(tp: Type)(using Context): Type = trace(i"toUnderlying($tp)", show = true)(tp match {
case _: ConstantType => tp
case tp: TermRef if tp.symbol.is(Module) => tp
case tp: TermRef if tp.symbol.isAllOf(EnumCase) => tp
case tp: SingletonType => toUnderlying(tp.underlying)
case tp: ExprType => toUnderlying(tp.resultType)
case _ => tp
})

def checkExhaustivity(_match: Match): Unit = {
val Match(sel, cases) = _match
val selTyp = sel.tpe.widen.dealias
debug.println(i"checking exhaustivity of ${_match}")

if (!exhaustivityCheckable(sel)) return

debug.println("checking " + _match.show)
debug.println("selTyp = " + selTyp.show)
val selTyp = toUnderlying(sel.tpe).dealias
debug.println(i"selTyp = $selTyp")

val patternSpace = Or(cases.foldLeft(List.empty[Space]) { (acc, x) =>
val space = if (x.guard.isEmpty) project(x.pat) else Empty
Expand Down Expand Up @@ -878,13 +891,14 @@ class SpaceEngine(using Context) extends SpaceLogic {
&& !sel.tpe.widen.isRef(defn.QuotedTypeClass)

def checkRedundancy(_match: Match): Unit = {
debug.println(s"---------------checking redundant patterns ${_match.show}")

val Match(sel, cases) = _match
val selTyp = sel.tpe.widen.dealias
debug.println(i"checking redundancy in $_match")

if (!redundancyCheckable(sel)) return

val selTyp = toUnderlying(sel.tpe).dealias
debug.println(i"selTyp = $selTyp")

val targetSpace =
if !selTyp.classSymbol.isNullableClass then
project(selTyp)
Expand All @@ -897,7 +911,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
if (x.guard.isEmpty) project(x.pat)
else Empty

debug.println(s"${x.pat.show} ====> ${res}")
debug.println(s"${x.pat.show} ====> ${show(res)}")
res
}

Expand All @@ -912,7 +926,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
debug.println(s"prev: ${show(prevs)}")

var covered = simplify(intersect(curr, targetSpace))
debug.println(s"covered: $covered")
debug.println(s"covered: ${show(covered)}")

// `covered == Empty` may happen for primitive types with auto-conversion
// see tests/patmat/reader.scala tests/patmat/byte.scala
Expand Down
1 change: 1 addition & 0 deletions tests/patmat/i13342-testing.check
@@ -0,0 +1 @@
15: Pattern Match Exhaustivity: Thu, Fri
18 changes: 18 additions & 0 deletions tests/patmat/i13342-testing.scala
@@ -0,0 +1,18 @@
class C {
val bool: true = true
val not1: None.type = None

def t1 = true match { case true => "inline true" }
def t2 = bool match { case true => "valdef true" }
def t3 = None match { case None => "inline None" }
def t4 = not1 match { case None => "valdef None" }

val monday: Day.Mon.type = Day.Mon
val someday: Day = Day.Mon

def t5 = Day.Mon match { case Day.Mon => 1 case Day.Tue => 2 case Day.Wed => 3 }
def t6 = monday match { case Day.Mon => 1 case Day.Tue => 2 case Day.Wed => 3 }
def t7 = someday match { case Day.Mon => 1 case Day.Tue => 2 case Day.Wed => 3 }
}

enum Day { case Mon, Tue, Wed, Thu, Fri }
10 changes: 10 additions & 0 deletions tests/patmat/i13342.scala
@@ -0,0 +1,10 @@
class C {
def m(x: true) = x match { // was: match may not be exhaustive.\nIt would fail on pattern case: false
case true => println("the one true path")
}

def n(x: true) = x match {
case true => 1
case false => 2 // was: no reachability warning on this case
}
}

0 comments on commit 0eea749

Please sign in to comment.