Skip to content

[ty] Fix structural pattern binding inference - #26411

Merged
charliermarsh merged 11 commits into
mainfrom
charlie/match-pattern-binding-followups
Jun 26, 2026
Merged

[ty] Fix structural pattern binding inference#26411
charliermarsh merged 11 commits into
mainfrom
charlie/match-pattern-binding-followups

Conversation

@charliermarsh

@charliermarsh charliermarsh commented Jun 26, 2026

Copy link
Copy Markdown
Member

Summary

Follow-up to #25941 and prerequisite for #25942.

The earlier PR extended successful-pattern binding inference to class and mapping patterns. This PR fixes edge cases in that analysis before the same structural machinery is reused for subject narrowing.

from typing import Literal

class IntPayload:
    tag: Literal["int"]
    payload: int

class StrPayload:
    tag: Literal["str"]
    payload: str

def handle(
    value: object,
    Pattern: type[IntPayload] | type[StrPayload],
) -> None:
    match value:
        case Pattern(tag="int", payload=item):
            reveal_type(item)  # int

Union-valued class expressions are now analyzed one class at a time, so a discriminating child pattern also selects the corresponding member types. Generic class patterns preserve specializations known from the subject, while unrelated non-final classes retain member types that can overlap through multiple inheritance. Runtime class matching uses an unknown specialization rather than a type-parameter default.

Mapping entries are extracted after the successful pattern has established the mapping interface, which keeps fully static object subjects from introducing Unknown. Later or-pattern alternatives use mutation-safe fallthrough types, so a failed sequence alternative does not leave stale shape constraints on a value captured by another alternative.

@astral-sh-bot astral-sh-bot Bot added the ty Multi-file analysis & type inference label Jun 26, 2026
@astral-sh-bot

astral-sh-bot Bot commented Jun 26, 2026

Copy link
Copy Markdown

Typing conformance results

No changes detected ✅

Current numbers
The percentage of diagnostics emitted that were expected errors held steady at 94.47%. The percentage of expected errors that received a diagnostic held steady at 89.19%. The number of fully passing files held steady at 95/134.

@astral-sh-bot

astral-sh-bot Bot commented Jun 26, 2026

Copy link
Copy Markdown

Memory usage report

Memory usage unchanged ✅

@astral-sh-bot

astral-sh-bot Bot commented Jun 26, 2026

Copy link
Copy Markdown

ecosystem-analyzer results

Lint rule Added Removed Changed
invalid-argument-type 25 0 0
not-iterable 5 0 0
unresolved-attribute 2 0 1
invalid-return-type 1 0 0
type-assertion-failure 0 1 0
Total 33 1 1

Flaky changes detected. This PR summary excludes flaky changes; see the HTML report for details.

Raw diff (35 changes)
egglog-python (https://github.com/egraphs-good/egglog-python)
- python/egglog/egraph_state.py:623:25 error[type-assertion-failure] Type `Value` is not equivalent to `Never`
+ python/egglog/egraph_state.py:150:45 error[unresolved-attribute] Attribute `id` is not defined on `None` in union `BackOffDecl | None`
+ python/egglog/thunk.py:59:24 error[invalid-return-type] Return type does not match returned value: expected `T@__call__`, found `object`

jax (https://github.com/google/jax)
+ jax/experimental/mosaic/gpu/constraints.py:638:44 error[invalid-argument-type] Argument to bound method `IsTransferableTmemRegisters.is_valid_tmem_transfer` is incorrect: Expected `TMEMLayout`, found `TiledLayout | WGSplatFragLayout | WGStridedFragLayout`
+ jax/experimental/mosaic/gpu/constraints.py:640:44 error[invalid-argument-type] Argument to bound method `IsTransferableTmemRegisters.is_valid_tmem_transfer` is incorrect: Expected `TMEMLayout`, found `TiledLayout | WGSplatFragLayout | WGStridedFragLayout`
+ jax/experimental/mosaic/gpu/constraints.py:823:18 error[unresolved-attribute] Attribute `base_tile_shape` is not defined on `WGSplatFragLayout`, `WGStridedFragLayout` in union `WGSplatFragLayout | WGStridedFragLayout | TiledLayout`

pylint (https://github.com/pycqa/pylint)
- pylint/checkers/classes/class_checker.py:946:54 error[unresolved-attribute] Attribute `name` is not defined on `NodeNG` in union `NodeNG | UninferableBase | Proxy`
+ pylint/checkers/classes/class_checker.py:946:54 error[unresolved-attribute] Attribute `name` is not defined on `NodeNG` in union `NodeNG | UninferableBase | Proxy | Any`

schema_salad (https://github.com/common-workflow-language/schema_salad)
+ src/schema_salad/dotnet_codegen.py:428:38 error[invalid-argument-type] Argument to bound method `DotNetCodeGen.type_loader` is incorrect: Expected `list[Any] | dict[str, Any] | str`, found `object`
+ src/schema_salad/dotnet_codegen.py:438:38 error[invalid-argument-type] Argument to bound method `DotNetCodeGen.type_loader` is incorrect: Expected `list[Any] | dict[str, Any] | str`, found `object`
+ src/schema_salad/dotnet_codegen.py:482:46 error[invalid-argument-type] Argument to function `DotNetCodeGen.safe_name` is incorrect: Expected `str`, found `object`
+ src/schema_salad/dotnet_codegen.py:491:59 error[not-iterable] Object of type `object` is not iterable
+ src/schema_salad/java_codegen.py:432:38 error[invalid-argument-type] Argument to bound method `JavaCodeGen.type_loader` is incorrect: Expected `list[Any] | dict[str, Any] | str`, found `object`
+ src/schema_salad/java_codegen.py:449:38 error[invalid-argument-type] Argument to bound method `JavaCodeGen.type_loader` is incorrect: Expected `list[Any] | dict[str, Any] | str`, found `object`
+ src/schema_salad/java_codegen.py:476:60 error[invalid-argument-type] Argument to function `JavaCodeGen.safe_name` is incorrect: Expected `str`, found `object`
+ src/schema_salad/java_codegen.py:479:54 error[invalid-argument-type] Argument to function `JavaCodeGen.safe_name` is incorrect: Expected `str`, found `object`
+ src/schema_salad/java_codegen.py:480:45 error[invalid-argument-type] Argument to function `JavaCodeGen.safe_name` is incorrect: Expected `str`, found `object`
+ src/schema_salad/java_codegen.py:501:46 error[invalid-argument-type] Argument to function `JavaCodeGen.safe_name` is incorrect: Expected `str`, found `object`
+ src/schema_salad/java_codegen.py:510:53 error[not-iterable] Object of type `object` is not iterable
+ src/schema_salad/python_codegen.py:394:46 error[invalid-argument-type] Argument to bound method `PythonCodeGen.type_loader` is incorrect: Expected `list[Any] | dict[str, Any] | str`, found `object`
+ src/schema_salad/python_codegen.py:402:46 error[invalid-argument-type] Argument to bound method `PythonCodeGen.type_loader` is incorrect: Expected `list[Any] | dict[str, Any] | str`, found `object`
+ src/schema_salad/python_codegen.py:427:28 error[not-iterable] Object of type `object` is not iterable
+ src/schema_salad/python_codegen.py:440:40 error[invalid-argument-type] Argument to function `PythonCodeGen.safe_name` is incorrect: Expected `str`, found `object`
+ src/schema_salad/python_codegen.py:442:80 error[not-iterable] Object of type `object` is not iterable
+ src/schema_salad/python_codegen.py:443:44 error[invalid-argument-type] Argument to function `PythonCodeGen.safe_name` is incorrect: Expected `str`, found `object`
+ src/schema_salad/python_codegen.py:452:40 error[invalid-argument-type] Argument to function `PythonCodeGen.safe_name` is incorrect: Expected `str`, found `object`
+ src/schema_salad/python_codegen.py:454:44 error[invalid-argument-type] Argument to function `PythonCodeGen.safe_name` is incorrect: Expected `str`, found `object`
+ src/schema_salad/python_codegen.py:468:46 error[invalid-argument-type] Argument to function `PythonCodeGen.safe_name` is incorrect: Expected `str`, found `object`
+ src/schema_salad/python_codegen.py:472:74 error[invalid-argument-type] Argument to bound method `PythonCodeGen.type_loader` is incorrect: Expected `list[Any] | dict[str, Any] | str`, found `object`
+ src/schema_salad/typescript_codegen.py:364:46 error[invalid-argument-type] Argument to bound method `TypeScriptCodeGen.type_loader` is incorrect: Expected `list[Any] | dict[str, Any] | str`, found `object`
+ src/schema_salad/typescript_codegen.py:373:46 error[invalid-argument-type] Argument to bound method `TypeScriptCodeGen.type_loader` is incorrect: Expected `list[Any] | dict[str, Any] | str`, found `object`
+ src/schema_salad/typescript_codegen.py:397:40 error[invalid-argument-type] Argument to function `TypeScriptCodeGen.safe_name` is incorrect: Expected `str`, found `object`
+ src/schema_salad/typescript_codegen.py:399:44 error[invalid-argument-type] Argument to function `TypeScriptCodeGen.safe_name` is incorrect: Expected `str`, found `object`
+ src/schema_salad/typescript_codegen.py:407:68 error[invalid-argument-type] Argument to function `TypeScriptCodeGen.safe_name` is incorrect: Expected `str`, found `object`
+ src/schema_salad/typescript_codegen.py:417:53 error[invalid-argument-type] Argument to function `TypeScriptCodeGen.safe_name` is incorrect: Expected `str`, found `object`
+ src/schema_salad/typescript_codegen.py:425:67 error[not-iterable] Object of type `object` is not iterable

Full report with detailed diff (timing results)

@charliermarsh
charliermarsh force-pushed the charlie/match-pattern-binding-followups branch from c6b372a to d7d7719 Compare June 26, 2026 18:50
@charliermarsh
charliermarsh force-pushed the charlie/match-pattern-binding-followups branch from d7d7719 to 5762cc5 Compare June 26, 2026 19:22
@charliermarsh
charliermarsh force-pushed the charlie/match-pattern-binding-followups branch from 5762cc5 to 8335c7e Compare June 26, 2026 19:27
@charliermarsh
charliermarsh marked this pull request as ready for review June 26, 2026 20:01
@charliermarsh
charliermarsh requested a review from a team as a code owner June 26, 2026 20:01
@charliermarsh
charliermarsh merged commit a582555 into main Jun 26, 2026
62 checks passed
@charliermarsh
charliermarsh deleted the charlie/match-pattern-binding-followups branch June 26, 2026 20:01

@dhruvmanila dhruvmanila left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good 👍

charliermarsh added a commit that referenced this pull request Jul 7, 2026
## Summary

#25941 and #26411 added support for inferring the types of names bound
by structural patterns, e.g., `value` in `case {"tag": "int", "value":
value}`.

This PR uses the same pattern analysis to narrow the match subject
itself, e.g., `payload` in `match payload`:

```python
from typing import Literal, TypedDict

class IntPayload(TypedDict):
    tag: Literal["int"]
    value: int

class StrPayload(TypedDict):
    tag: Literal["str"]
    value: str

def handle(payload: IntPayload | StrPayload) -> None:
    match payload:
        case {"tag": "int", "value": value}:
            reveal_type(value)    # int
            reveal_type(payload)  # IntPayload
```

We analyze each possible subject type against the complete nested class,
mapping, sequence, or `or` pattern, then keep the types that can match.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants