diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 708944434015..e5a69eb3335c 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -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 } @@ -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 @@ -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 @@ -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) @@ -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 } @@ -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 diff --git a/tests/patmat/i13342-testing.check b/tests/patmat/i13342-testing.check new file mode 100644 index 000000000000..88418cde03a0 --- /dev/null +++ b/tests/patmat/i13342-testing.check @@ -0,0 +1 @@ +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.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 + } +}