Fix match statement narrowing for self-matching class patterns with literal arguments (#21918)

Fixes #21780

This was likely because join_types was being used to combine two
possible remaining types (e.g. tuple[int, int] and int), and it would
collapse those into object instead of keeping both, which is why mypy
couldn't narrow down to a bottom type (Never).

This PR replaces join_types with make_simplified_union, and adds a
regression test based on the repro in the issue.

Could be a better fix than this, I would welcome it.
diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py
index 53fa75f..9d02262 100644
--- a/mypy/checkpattern.py
+++ b/mypy/checkpattern.py
@@ -605,7 +605,7 @@
                 if not is_uninhabited(pattern_type.type):
                     return PatternType(
                         pattern_type.type,
-                        join_types(rest_type, pattern_type.rest_type),
+                        make_simplified_union([rest_type, pattern_type.rest_type]),
                         pattern_type.captures,
                     )
                 captures = pattern_type.captures
diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test
index 2f5b2d7..01e490d 100644
--- a/test-data/unit/check-python310.test
+++ b/test-data/unit/check-python310.test
@@ -1905,6 +1905,32 @@
     case b:
         reveal_type(b)  # N: Revealed type is "builtins.int"
 
+[case testMatchClassPatternLiteralNegativeNarrowing]
+# flags: --strict-equality --warn-unreachable
+# See: https://github.com/python/mypy/issues/21780
+
+from typing import reveal_type, NoReturn
+
+def assert_never(x: NoReturn) -> None: ...
+
+def f(x: int | tuple[int, int] | None) -> float | None:
+    match x:
+        case None:
+            reveal_type(x) # N: Revealed type is "None"
+            return None
+        case int(0):
+            reveal_type(x) # N: Revealed type is "Literal[0]"
+            return 0.0
+        case int(bits):
+            reveal_type(x) # N: Revealed type is "builtins.int"
+            return bits / 8.0
+        case int(bits), int(seconds):
+            reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int]"
+            return bits / 8.0 / seconds
+        case _:
+            assert_never(x) # E: Statement is unreachable
+[builtins fixtures/ops.pyi]
+
 [case testMatchExhaustiveReturn]
 # flags: --strict-equality --warn-unreachable
 def foo(value) -> int: