[ty] Infer types for names bound in sequence match patterns#25940
Conversation
Typing conformance resultsThe percentage of diagnostics emitted that were expected errors held steady at 94.37%. The percentage of expected errors that received a diagnostic held steady at 89.00%. The number of fully passing files held steady at 94/134. SummaryHow are test cases classified?Each test case represents one expected error annotation or a group of annotations sharing a tag. Counts are per test case, not per diagnostic — multiple diagnostics on the same line count as one. Required annotations (
True positives changed (2)2 diagnostics
|
Memory usage reportMemory usage unchanged ✅ |
|
| Lint rule | Added | Removed | Changed |
|---|---|---|---|
invalid-argument-type |
13 | 0 | 0 |
invalid-assignment |
1 | 0 | 3 |
type-assertion-failure |
0 | 0 | 3 |
unresolved-attribute |
1 | 0 | 1 |
invalid-return-type |
1 | 0 | 0 |
| Total | 16 | 0 | 7 |
Large timing changes:
| Project | Old Time | New Time | Change |
|---|---|---|---|
egglog-python |
0.20s | 0.31s | +54% |
Flaky changes detected. This PR summary excludes flaky changes; see the HTML report for details.
Raw diff (23 changes)
cibuildwheel (https://github.com/pypa/cibuildwheel)
+ cibuildwheel/options.py:428:50 error[invalid-argument-type] Argument to bound method `OptionFormat.format_list` is incorrect: Expected `Sequence[str | int]`, found `(Mapping[str, Sequence[str | int] | int] & Sequence[object] & ~str & ~bytes & ~bytearray) | (Sequence[str | int] & ~str & ~bytes & ~bytearray) | (int & Sequence[object])`
cwltool (https://github.com/common-workflow-language/cwltool)
+ cwltool/secrets.py:46:40 error[invalid-argument-type] Argument to bound method `SecretStore.has_secret` is incorrect: Expected `int | str | float | ... omitted 4 union elements`, found `object`
+ cwltool/secrets.py:50:40 error[invalid-argument-type] Argument to bound method `SecretStore.has_secret` is incorrect: Expected `int | str | float | ... omitted 4 union elements`, found `object`
+ cwltool/secrets.py:62:24 error[invalid-return-type] Return type does not match returned value: expected `int | str | float | ... omitted 4 union elements`, found `dict[object, int | str | float | ... omitted 4 union elements]`
+ cwltool/secrets.py:62:42 error[invalid-argument-type] Argument to bound method `SecretStore.retrieve` is incorrect: Expected `int | str | float | ... omitted 4 union elements`, found `object`
+ cwltool/secrets.py:64:39 error[invalid-argument-type] Argument to bound method `SecretStore.retrieve` is incorrect: Expected `int | str | float | ... omitted 4 union elements`, found `object`
+ cwltool/process.py:507:44 error[invalid-argument-type] Argument to function `var_spool_cwl_detector` is incorrect: Expected `int | str | float | ... omitted 5 union elements`, found `object`
+ cwltool/process.py:510:44 error[invalid-argument-type] Argument to function `var_spool_cwl_detector` is incorrect: Expected `int | str | float | ... omitted 5 union elements`, found `object`
egglog-python (https://github.com/egraphs-good/egglog-python)
- python/egglog/egraph_state.py:378:25 error[type-assertion-failure] Type `@Todo & ~Literal["delete"] & ~Literal["subsume"]` is not equivalent to `Never`
+ python/egglog/egraph_state.py:378:25 error[type-assertion-failure] Type `Unknown & ~Literal["delete"] & ~Literal["subsume"]` is not equivalent to `Never`
- python/egglog/egraph_state.py:623:25 error[type-assertion-failure] Type `@Todo & ~None & ~int & ~float & ~str` is not equivalent to `Never`
+ python/egglog/egraph_state.py:623:25 error[type-assertion-failure] Type `Unknown & ~None & ~int & ~float & ~str` is not equivalent to `Never`
- python/egglog/pretty.py:309:17 error[type-assertion-failure] Type `@Todo & ~None & ~int & ~float & ~str` is not equivalent to `Never`
+ python/egglog/pretty.py:309:17 error[type-assertion-failure] Type `Unknown & ~None & ~int & ~float & ~str` is not equivalent to `Never`
jax (https://github.com/google/jax)
+ jax/_src/pallas/mosaic_gpu/lowering.py:1666:55 error[invalid-argument-type] Argument to bound method `UnswizzleRef.commute_reshape` is incorrect: Expected `ShapedArray`, found `AbstractValue`
+ jax/experimental/mosaic/gpu/layout_inference.py:424:10 error[invalid-assignment] Object of type `WGStridedFragLayout & ~WGSplatFragLayout` is not assignable to `RegisterLayout`
+ jax/experimental/mosaic/gpu/layout_inference.py:425:54 error[unresolved-attribute] Object of type `RegisterLayout` has no attribute `vec_size`
+ jax/experimental/mosaic/gpu/layout_inference.py:426:51 error[invalid-argument-type] Argument to function `is_supported_strided_layout_broadcast` is incorrect: Expected `WGStridedFragLayout`, found `RegisterLayout`
kopf (https://github.com/nolar/kopf)
- kopf/_cogs/structs/dicts.py:244:29 error[unresolved-attribute] Object of type `(_T@walk & ~None) | Iterable[_T@walk | Iterable[_T@walk]]` has no attribute `obj`
+ kopf/_cogs/structs/dicts.py:244:29 error[unresolved-attribute] Attribute `obj` is not defined on `_T@walk & _dummy`, `Iterable[_T@walk | Iterable[_T@walk]] & _dummy` in union `(_T@walk & Unknown & ~None) | (Iterable[_T@walk | Iterable[_T@walk]] & Unknown) | (_T@walk & _dummy) | (Iterable[_T@walk | Iterable[_T@walk]] & _dummy)`
pytest (https://github.com/pytest-dev/pytest)
- src/_pytest/assertion/rewrite.py:1012:25 error[invalid-assignment] Invalid subscript assignment with key of type `@Todo` and value of type `expr` on object of type `dict[str, str]`
+ src/_pytest/assertion/rewrite.py:1012:25 error[invalid-assignment] Invalid subscript assignment with key of type `Unknown` and value of type `expr` on object of type `dict[str, str]`
- src/_pytest/assertion/rewrite.py:1110:17 error[invalid-assignment] Invalid subscript assignment with key of type `@Todo` and value of type `NamedExpr` on object of type `dict[str, str]`
+ src/_pytest/assertion/rewrite.py:1110:17 error[invalid-assignment] Invalid subscript assignment with key of type `Unknown` and value of type `NamedExpr` on object of type `dict[str, str]`
- src/_pytest/assertion/rewrite.py:1128:21 error[invalid-assignment] Invalid subscript assignment with key of type `@Todo` and value of type `NamedExpr` on object of type `dict[str, str]`
+ src/_pytest/assertion/rewrite.py:1128:21 error[invalid-assignment] Invalid subscript assignment with key of type `Unknown` and value of type `NamedExpr` on object of type `dict[str, str]`
schema_salad (https://github.com/common-workflow-language/schema_salad)
+ src/schema_salad/dotnet_codegen.py:509:45 error[invalid-argument-type] Argument to function `DotNetCodeGen.safe_name` is incorrect: Expected `str`, found `(dict[str, Any] & ~Top[MutableSequence[Unknown]]) | (str & ~Top[MutableSequence[Unknown]])`
+ src/schema_salad/java_codegen.py:571:45 error[invalid-argument-type] Argument to function `JavaCodeGen.safe_name` is incorrect: Expected `str`, found `(dict[str, Any] & ~Top[MutableSequence[Unknown]]) | (str & ~Top[MutableSequence[Unknown]])`
+ src/schema_salad/python_codegen.py:490:40 error[invalid-argument-type] Argument to function `PythonCodeGen.safe_name` is incorrect: Expected `str`, found `(dict[str, Any] & ~Top[MutableSequence[Unknown]]) | (str & ~Top[MutableSequence[Unknown]])`
+ src/schema_salad/typescript_codegen.py:444:40 error[invalid-argument-type] Argument to function `TypeScriptCodeGen.safe_name` is incorrect: Expected `str`, found `(dict[str, Any] & ~Top[MutableSequence[Unknown]]) | (str & ~Top[MutableSequence[Unknown]])`a923ba3 to
d3b7208
Compare
5721b8d to
7bd8d49
Compare
d3b7208 to
996f4e8
Compare
cd30fbb to
5919bfb
Compare
c35e207 to
0636474
Compare
5ac9446 to
e9ca170
Compare
0636474 to
4011765
Compare
6621128 to
3c57454
Compare
2a91303 to
279abdd
Compare
2d8ff2b to
bc33265
Compare
f81d5bb to
a1661c5
Compare
fb131bf to
7340670
Compare
e3b322e to
02a1ad2
Compare
7340670 to
7a36619
Compare
2292238 to
4f68cc8
Compare
4f68cc8 to
a334023
Compare
|
This PR covers sequences; #25941 extends the same behavior to class attributes and mappings. |
dhruvmanila
left a comment
There was a problem hiding this comment.
Overall, this change looks good to me. It’s a bit late here, and I’d like to spend more time on a few implementation details, but I don’t think that should block merging. Most of my comments are about expanding the test suite, they may uncover edge cases, but I don’t currently have any concerns to avoid merging this PR.
| def test_incompatible_declared_capture(subject: int) -> None: | ||
| item: str | ||
| match subject: | ||
| case item: # error: [invalid-assignment] |
There was a problem hiding this comment.
Unrelated, I like the diagnostic message of mypy in this specific case considering the surrounding context of a pattern capture. This is just something I wanted to highlight and doesn't need to be implemented.
error: Incompatible types in capture pattern (pattern captures type "int", variable has type "str")
There was a problem hiding this comment.
Is there a reason that most of these test cases have single case arms? Could we have either an additional case arm, which could just be a catch-all, testing (reveal_type) the other side of the narrowing? Or, do you think it's not worth it?
| def test_constructed_mutable_sequence_alias_does_not_keep_length( | ||
| value: list[int], | ||
| ) -> int: # error: [invalid-return-type] | ||
| match value.copy(): | ||
| case [_] as whole: | ||
| whole.append(2) | ||
| match whole: | ||
| case [_]: | ||
| return 1 | ||
| case _: | ||
| raise ValueError |
There was a problem hiding this comment.
I'm not sure what this test case is testing? The invalid-return-type is coming from the inner match not matching the case expression. Removing the whole.append leads to no change.
Same question for the next test case which is similar but uses nested sequence pattern.
|
Actually, Codex found a couple of issues: I think (1) is interesting, (2) will require
|
|
(2) is solved by a subsequent PR, intentionally not included here. |
|
(I will review the others; I thought I already fixed (1). Thanks!) |
Summary
This PR infers the types of names introduced by match patterns.
The implementation analyzes a successful match recursively: each pattern receives the type of the value it is matching, computes the type that can successfully match, and passes the appropriate extracted type to its children. A capture binds the type it receives; an
aspattern binds the successful type of its complete nested pattern.The analysis also distinguishes facts established while a pattern is being evaluated from the stable type assigned to an alias. An alias for a mutable sequence keeps only its sequence type, because later mutation can invalidate the observed shape; exact tuples can retain facts learned from their children. For example, we don't narrow here:
But we do narrow for an exact tuple: