From 5ffdaf8065f87abc6b36cd18abba5e6ca6b3a5dc Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 28 Aug 2021 15:05:53 +0100 Subject: [PATCH] Unwiden scrutinee types, fixing match analysis --- .../tools/dotc/transform/patmat/Space.scala | 26 ++++++++++++++----- tests/patmat/i13342-testing.check | 5 ++++ tests/patmat/i13342-testing.scala | 18 +++++++++++++ tests/patmat/i13342.check | 1 + tests/patmat/i13342.scala | 10 +++++++ tests/pos-special/fatal-warnings/i3589b.scala | 2 +- 6 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 tests/patmat/i13342-testing.check create mode 100644 tests/patmat/i13342-testing.scala create mode 100644 tests/patmat/i13342.check create mode 100644 tests/patmat/i13342.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 42fa39749f9d..7ace1a77d4ec 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -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 @@ -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)) diff --git a/tests/patmat/i13342-testing.check b/tests/patmat/i13342-testing.check new file mode 100644 index 000000000000..5a1a1e80447f --- /dev/null +++ b/tests/patmat/i13342-testing.check @@ -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 diff --git a/tests/patmat/i13342-testing.scala b/tests/patmat/i13342-testing.scala new file mode 100644 index 000000000000..8ea22d79917f --- /dev/null +++ b/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 } diff --git a/tests/patmat/i13342.check b/tests/patmat/i13342.check new file mode 100644 index 000000000000..77e4f7cd4ed3 --- /dev/null +++ b/tests/patmat/i13342.check @@ -0,0 +1 @@ +8: Match case Unreachable diff --git a/tests/patmat/i13342.scala b/tests/patmat/i13342.scala new file mode 100644 index 000000000000..d706442dd692 --- /dev/null +++ b/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 + } +} diff --git a/tests/pos-special/fatal-warnings/i3589b.scala b/tests/pos-special/fatal-warnings/i3589b.scala index e6fb462736e1..115e74b8cee2 100644 --- a/tests/pos-special/fatal-warnings/i3589b.scala +++ b/tests/pos-special/fatal-warnings/i3589b.scala @@ -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