Skip to content

fix(match): enum literal narrowing is order-sensitive in match statements#21196

Open
Bahtya wants to merge 1 commit intopython:masterfrom
Bahtya:fix/match-literal-enum-narrowing
Open

fix(match): enum literal narrowing is order-sensitive in match statements#21196
Bahtya wants to merge 1 commit intopython:masterfrom
Bahtya:fix/match-literal-enum-narrowing

Conversation

@Bahtya
Copy link
Copy Markdown

@Bahtya Bahtya commented Apr 9, 2026

Fix for #21187

Problem

When matching a union containing Literal[SomeEnum.VALUE] and other types (e.g., DummyClass), the order of match cases affected type narrowing results:

  • case DummyClass(): ... case MyEnum.RELEVANT: → ✅ works correctly
  • case MyEnum.RELEVANT: ... case DummyClass(): → ❌ false positive on assert_never

This is a regression from mypy 1.19 to 1.20.

Root Cause

visit_value_pattern in checkpattern.py uses narrow_type_by_identity_equality with "==" semantics. This function has a conservative enum ambiguity check that skips narrowing for StrEnum/IntEnum types because they can compare equal to str/int values. While this is correct for general equality comparisons (if x == MyEnum.RELEVANT:), it is overly conservative in match statements where the other union members (like DummyClass) are unrelated types.

Solution

For enum literal values without custom __eq__, bypass narrow_type_by_identity_equality and use conditional_types_with_intersection directly (same approach used by visit_singleton_pattern). This correctly narrows based on the literal value regardless of the enum's base type ambiguity with str/int.

Enums with custom __eq__ are excluded to preserve the existing narrowing behavior (where the type stays as the full enum type, not narrowed to a specific literal).

Testing

Fixes #21187

When matching a union containing Literal[SomeEnum.VALUE] and other types,
the order of match cases affected type narrowing results. Matching the
enum literal first (before other types) failed to narrow the subject type,
causing false positives with assert_never.

Root cause: visit_value_pattern used narrow_type_by_identity_equality with
'==' semantics, which has an overly-conservative enum ambiguity check for
StrEnum/IntEnum (since they can compare equal to str/int). In match
statements, this check is unnecessary when the other union members are
unrelated types.

Fix: For enum literal values without custom __eq__, bypass the identity
equality narrowing and use conditional_types_with_intersection directly,
which correctly narrows based on the literal value regardless of enum
base type ambiguity.

Fixes python#21187

Signed-off-by: bahtya <bahtyar153@qq.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[1.20 regression] Type narrowing is order sensitive in match-case statement with narrowed enum type

1 participant