Fix negative type narrowing for mapping patterns with multiple key entries - #11612
Fix negative type narrowing for mapping patterns with multiple key entries#11612Henry Su (hsusul) wants to merge 2 commits into
Conversation
…ypedDict subjects
|
🔒 Automated review in progress — Stella Huang (@StellaHuang95) is auto-reviewing this PR. |
| valuePattern.d.orPatterns.length === 1 && | ||
| (valuePattern.d.orPatterns[0].nodeType === ParseNodeType.PatternCapture || | ||
| valuePattern.d.orPatterns[0].nodeType === ParseNodeType.PatternValue) | ||
| ) { |
There was a problem hiding this comment.
Issue · Please address or respond
PatternValue is grouped with PatternCapture and therefore treated as an unconditional match (valueTypes = undefined). A value pattern such as Color.RED is an equality check, so a TypedDict whose required field contains Color.BLUE can fall through but is incorrectly eliminated here. Only treat captures as presence-only, or conservatively leave value patterns unsupported until their value can be compared soundly.
| case {"v": 1, "kind": "a"}: | ||
| reveal_type(msg, expected_text="MsgA") | ||
| case _: | ||
| reveal_type(msg, expected_text="MsgB") |
There was a problem hiding this comment.
Warning · Non-blocking recommendation
This test covers only multi-key literal matching. Add an enum/value-pattern regression that asserts the TypedDict is retained in the fallthrough branch; it would catch the unsound PatternValue narrowing above. Coverage for the new capture-value path would also lock in its intended behavior.
… and add value/capture pattern tests
|
Thank you Stella Huang (@StellaHuang95) for the review! I have updated the PR (commit
|
Stella Huang (StellaHuang95)
left a comment
There was a problem hiding this comment.
Approved via Review Center.
Rich Chiodo (rchiodo)
left a comment
There was a problem hiding this comment.
Approved via Review Center.
Rich Chiodo (rchiodo)
left a comment
There was a problem hiding this comment.
Approved via Review Center.
Minimal Reproduction Case
Current vs Expected Behavior
case {"v": 1, "kind": "a"}:), Pyright does not eliminate matchingTypedDictsubtypes from the subject in the fallthrough (case _:) branch. Consequently,reveal_type(msg)incase _:reportsMsgA | MsgB.case {"v": 1, "kind": "a"}:is guaranteed to beMsgA. The fallthroughcase _:branch cannot receiveMsgA, somsgmust be narrowed toMsgB.Rationale
In Python 3.10+ pattern matching (PEP 634), matching a subject against a mapping pattern checks whether the subject is a mapping and contains matching values for all specified key entries. When a subject is a union of
TypedDicttypes, aTypedDictsubtype whose literal field values match all key entries of a mapping pattern will always match thatcaseclause. In negative type narrowing (!isPositiveTest), such matchingTypedDictsubtypes must be eliminated from the remaining subject type in fallthrough branches.Root Cause
In
packages/pyright-internal/src/analyzer/patternMatching.ts,narrowTypeBasedOnMappingPatternpreviously hardcoded a single-key check (pattern.d.entries.length === 1) when!isPositiveTest. Mapping patterns with two or more key entries were skipped, preventing negative type narrowing for multi-key patterns.Proposed Fix
Generalize
narrowTypeBasedOnMappingPatternunder!isPositiveTestto parse all key entries inpattern.d.entries. If aTypedDictsubtype matches all key entries in the mapping pattern (matching string literal key names, literal value types, or wildcard/capture variables), eliminate thatTypedDictsubtype from the remaining type.Test Coverage
Added regression test
test_negative_narrowing3topackages/pyright-internal/src/tests/samples/matchMapping1.py.Validation Results
npx jest src/tests/typeEvaluator6.test.tspassed (148/148 tests passed).npx tsc --noEmitpassed with 0 errors.git diff --checkpassed with 0 formatting or whitespace errors.