From b61af65d84b0fc0804ba65fe056648b7e94a96c2 Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 10 Oct 2024 22:18:26 +0300 Subject: [PATCH] Do not consider uninhabited constructors when performing exhaustive match checking --- .../dotty/tools/dotc/transform/patmat/Space.scala | 13 +++++++++++-- tests/pos/patmat-nothing-exhaustive.scala | 12 ++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 tests/pos/patmat-nothing-exhaustive.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 20b0099d82e2..905dc70e147a 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -649,8 +649,13 @@ object SpaceEngine { // <== refineUsingParent(NatT, class Succ, []) = Succ[NatT] // <== isSub(Succ[NatT] <:< Succ[Succ[]]) = false def getAppliedClass(tp: Type): Type = tp match + case tp @ AppliedType(tycon: TypeRef, _) if tycon.symbol.isClass => + // apply arguments if they contain Nothing, + // because it may affect further selection of children of the class + val argsNotContainsNothing = tp.args.forall(_.classSymbol != defn.NothingClass) + if argsNotContainsNothing then tp + else getAppliedClass(tycon.superType.applyIfParameterized(tp.args)) case tp @ AppliedType(_: HKTypeLambda, _) => tp - case tp @ AppliedType(tycon: TypeRef, _) if tycon.symbol.isClass => tp case tp @ AppliedType(tycon: TypeProxy, _) => getAppliedClass(tycon.superType.applyIfParameterized(tp.args)) case tp => tp val tp = getAppliedClass(tpOriginal) @@ -660,7 +665,11 @@ object SpaceEngine { else if tp.classSymbol == defn.TupleClass || tp.classSymbol == defn.NonEmptyTupleClass then List(child) // TupleN and TupleXXL classes are used for Tuple, but they aren't Tuple's children else if (child.is(Private) || child.is(Sealed)) && child.isOneOf(AbstractOrTrait) then getChildren(child) - else List(child) + else + // if a class contains a field of type Nothing, + // then it can be ignored in pattern matching, because it is impossible to obtain an instance of it + val noFieldsWithTypeNothing = child.typeRef.fields.forall(_.symbol.typeRef.classSymbol != defn.NothingClass) + if noFieldsWithTypeNothing then List(child) else Nil } val children = trace(i"getChildren($tp)")(getChildren(tp.classSymbol)) diff --git a/tests/pos/patmat-nothing-exhaustive.scala b/tests/pos/patmat-nothing-exhaustive.scala new file mode 100644 index 000000000000..025ea9c6f868 --- /dev/null +++ b/tests/pos/patmat-nothing-exhaustive.scala @@ -0,0 +1,12 @@ +//> using options -Xfatal-warnings -deprecation -feature + +enum TestAdt: + case Inhabited + case Uninhabited(no: Nothing) + +def test1(t: TestAdt): Int = t match + case TestAdt.Inhabited => 1 + +def test2(o: Option[Option[Nothing]]): Int = o match + case Some(None) => 1 + case None => 2