Skip to content

Unwiden scrutinee types, fixing match analysis #13409

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 13, 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
26 changes: 20 additions & 6 deletions compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -858,14 +858,27 @@ 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, and rewrap any annotation wrappers.
* 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 AnnotatedType(tp, annot) => AnnotatedType(toUnderlying(tp), annot)
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 @@ -898,13 +911,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 isNullable = selTyp.classSymbol.isNullableClass
val targetSpace = if isNullable
then project(OrType(selTyp, constantNullType, soft = false))
Expand Down
5 changes: 5 additions & 0 deletions tests/patmat/i13342-testing.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
13: Match case Unreachable
13: Match case Unreachable
14: Match case Unreachable
14: Match case Unreachable
15: Pattern Match Exhaustivity: Thu, Fri
18 changes: 18 additions & 0 deletions tests/patmat/i13342-testing.scala
Original file line number Diff line number Diff line change
@@ -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 }
1 change: 1 addition & 0 deletions tests/patmat/i13342.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
8: Match case Unreachable
10 changes: 10 additions & 0 deletions tests/patmat/i13342.scala
Original file line number Diff line number Diff line change
@@ -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
}
}
2 changes: 1 addition & 1 deletion tests/pos-special/fatal-warnings/i3589b.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class Test {
def test(x: 1) = (x: @annotation.switch) match {
def test(x: 1 | 2 | 3) = (x: @annotation.switch) match {
case 1 => 1
case 2 => 2
case 3 => 3
Expand Down