Fix tuple slice type evaluation for slices with start >= stop - #11615
Fix tuple slice type evaluation for slices with start >= stop#11615Henry Su (hsusul) wants to merge 1 commit into
Conversation
|
Please mirror this fix in |
|
Thanks Stella Huang (@StellaHuang95)! is in the internal Pylance repository and isn't present in the open-source tree, but this change in is complete and will mirror cleanly into Pylance when synced. |
|
Diff from mypy_primer, showing the effect of this PR on open source code: sympy (https://github.com/sympy/sympy)
- .../projects/sympy/sympy/solvers/diophantine/diophantine.py:176:44 - error: Cannot access attribute "expand" for class "Basic"
- Attribute "expand" is unknown (reportAttributeAccessIssue)
- .../projects/sympy/sympy/solvers/diophantine/tests/test_diophantine.py:314:30 - error: Cannot access attribute "as_independent" for class "Basic"
- Attribute "as_independent" is unknown (reportAttributeAccessIssue)
- .../projects/sympy/sympy/solvers/diophantine/tests/test_diophantine.py:383:30 - error: Cannot access attribute "as_independent" for class "Basic"
- Attribute "as_independent" is unknown (reportAttributeAccessIssue)
- .../projects/sympy/sympy/solvers/ode/hypergeometric.py:247:67 - error: Operator "**" not supported for types "Basic" and "Literal[2]" (reportOperatorIssue)
- .../projects/sympy/sympy/solvers/ode/nonhomogeneous.py:225:45 - error: Cannot access attribute "has" for class "tuple[Expr, int]"
- Attribute "has" is unknown (reportAttributeAccessIssue)
+ .../projects/sympy/sympy/solvers/ode/nonhomogeneous.py:223:22 - error: No overloads for "__new__" match the provided arguments (reportCallIssue)
+ .../projects/sympy/sympy/solvers/ode/nonhomogeneous.py:223:25 - error: Argument of type "CRootOf | tuple[Expr, int]" cannot be assigned to parameter "arg" of type "Expr" in function "__new__"
+ Type "CRootOf | tuple[Expr, int]" is not assignable to type "Expr"
+ "tuple[Expr, int]" is not assignable to "Expr" (reportArgumentType)
+ .../projects/sympy/sympy/solvers/ode/nonhomogeneous.py:238:40 - error: No overloads for "__new__" match the provided arguments (reportCallIssue)
+ .../projects/sympy/sympy/solvers/ode/nonhomogeneous.py:238:50 - error: Argument of type "CRootOf | tuple[Expr, int]" cannot be assigned to parameter "arg" of type "Expr" in function "__new__"
+ Type "CRootOf | tuple[Expr, int]" is not assignable to type "Expr"
+ "tuple[Expr, int]" is not assignable to "Expr" (reportArgumentType)
- .../projects/sympy/sympy/solvers/ode/nonhomogeneous.py:468:28 - error: Argument of type "Expr | Unknown | None" cannot be assigned to parameter "expr" of type "Expr" in function "make_args"
+ .../projects/sympy/sympy/solvers/ode/nonhomogeneous.py:468:28 - error: Argument of type "Unknown | None" cannot be assigned to parameter "expr" of type "Expr" in function "make_args"
- Type "Expr | Unknown | None" is not assignable to type "Expr"
+ Type "Unknown | None" is not assignable to type "Expr"
- .../projects/sympy/sympy/solvers/ode/ode.py:1579:38 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:1580:38 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:1590:9 - error: No overloads for "update" match the provided arguments (reportCallIssue)
- .../projects/sympy/sympy/solvers/ode/ode.py:1590:12 - error: "update" is not a known attribute of "None" (reportOptionalMemberAccess)
- .../projects/sympy/sympy/solvers/ode/ode.py:1590:19 - error: Argument of type "Unknown | dict[Unknown, Unknown] | None" cannot be assigned to parameter "m" of type "Iterable[tuple[str, Unknown]]" in function "update"
- Type "Unknown | dict[Unknown, Unknown] | None" is not assignable to type "Iterable[tuple[str, Unknown]]"
- "None" is incompatible with protocol "Iterable[tuple[str, Unknown]]"
- "__iter__" is not present (reportArgumentType)
- .../projects/sympy/sympy/solvers/ode/ode.py:1597:43 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:1597:64 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:1603:9 - error: No overloads for "update" match the provided arguments (reportCallIssue)
- .../projects/sympy/sympy/solvers/ode/ode.py:1603:12 - error: "update" is not a known attribute of "None" (reportOptionalMemberAccess)
- .../projects/sympy/sympy/solvers/ode/ode.py:1603:19 - error: Argument of type "Unknown | dict[Unknown, Unknown] | None" cannot be assigned to parameter "m" of type "Iterable[tuple[str, Unknown]]" in function "update"
- Type "Unknown | dict[Unknown, Unknown] | None" is not assignable to type "Iterable[tuple[str, Unknown]]"
- "None" is incompatible with protocol "Iterable[tuple[str, Unknown]]"
- "__iter__" is not present (reportArgumentType)
- .../projects/sympy/sympy/solvers/ode/ode.py:1610:43 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:1610:74 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:1616:9 - error: No overloads for "update" match the provided arguments (reportCallIssue)
- .../projects/sympy/sympy/solvers/ode/ode.py:1616:12 - error: "update" is not a known attribute of "None" (reportOptionalMemberAccess)
- .../projects/sympy/sympy/solvers/ode/ode.py:1616:19 - error: Argument of type "Unknown | dict[Unknown, Unknown] | None" cannot be assigned to parameter "m" of type "Iterable[tuple[str, Unknown]]" in function "update"
- Type "Unknown | dict[Unknown, Unknown] | None" is not assignable to type "Iterable[tuple[str, Unknown]]"
- "None" is incompatible with protocol "Iterable[tuple[str, Unknown]]"
- "__iter__" is not present (reportArgumentType)
- .../projects/sympy/sympy/solvers/ode/ode.py:1623:49 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:1623:70 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
+ .../projects/sympy/sympy/solvers/ode/ode.py:1753:36 - error: Cannot access attribute "lhs" for class "Expr"
+ Attribute "lhs" is unknown (reportAttributeAccessIssue)
+ .../projects/sympy/sympy/solvers/ode/ode.py:1753:55 - error: Cannot access attribute "rhs" for class "Expr"
+ Attribute "rhs" is unknown (reportAttributeAccessIssue)
+ .../projects/sympy/sympy/solvers/ode/ode.py:1754:36 - error: Cannot access attribute "lhs" for class "Expr"
+ Attribute "lhs" is unknown (reportAttributeAccessIssue)
+ .../projects/sympy/sympy/solvers/ode/ode.py:1755:17 - error: No overloads for "__setitem__" match the provided arguments (reportCallIssue)
+ .../projects/sympy/sympy/solvers/ode/ode.py:1755:17 - error: Argument of type "Equality | BooleanFalse | BooleanTrue | Unknown | Expr" cannot be assigned to parameter "value" of type "Equality | BooleanFalse | BooleanTrue" in function "__setitem__"
+ Type "Equality | BooleanFalse | BooleanTrue | Unknown | Expr" is not assignable to type "Equality | BooleanFalse | BooleanTrue"
+ Type "Expr" is not assignable to type "Equality | BooleanFalse | BooleanTrue"
+ "Expr" is not assignable to "Equality"
+ "Expr" is not assignable to "BooleanFalse"
+ "Expr" is not assignable to "BooleanTrue" (reportArgumentType)
- .../projects/sympy/sympy/solvers/ode/ode.py:3442:7 - error: "update" is not a known attribute of "None" (reportOptionalMemberAccess)
- .../projects/sympy/sympy/solvers/ode/ode.py:3442:38 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:3443:7 - error: "update" is not a known attribute of "None" (reportOptionalMemberAccess)
- .../projects/sympy/sympy/solvers/ode/ode.py:3443:38 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:3444:14 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:3445:14 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:3446:14 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:3458:50 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:3459:50 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:3460:50 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:3500:5 - error: No overloads for "update" match the provided arguments (reportCallIssue)
- .../projects/sympy/sympy/solvers/ode/ode.py:3500:7 - error: "update" is not a known attribute of "None" (reportOptionalMemberAccess)
- .../projects/sympy/sympy/solvers/ode/ode.py:3500:14 - error: Argument of type "Unknown | dict[Unknown, Unknown] | None" cannot be assigned to parameter "m" of type "Iterable[tuple[str, Unknown]]" in function "update"
- Type "Unknown | dict[Unknown, Unknown] | None" is not assignable to type "Iterable[tuple[str, Unknown]]"
- "None" is incompatible with protocol "Iterable[tuple[str, Unknown]]"
- "__iter__" is not present (reportArgumentType)
- .../projects/sympy/sympy/solvers/ode/ode.py:3501:18 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:3501:43 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:3502:9 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:3502:16 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
- .../projects/sympy/sympy/solvers/ode/ode.py:3502:24 - error: Object of type "None" is not subscriptable (reportOptionalSubscript)
... (truncated 1342 lines) ...
|
|
🔒 Automated review in progress — Rich Chiodo (@rchiodo) is auto-reviewing this PR. |
| if (startValue === undefined || endValue === undefined || endValue < startValue) { | ||
| if (startValue === undefined || endValue === undefined) { | ||
| return undefined; | ||
| } |
There was a problem hiding this comment.
Warning · Non-blocking recommendation
t[True:False] and t[2:1:None] still infer an unbounded tuple instead of tuple[()]. Normalize literal booleans as integer bounds, treat an explicit None step like an omitted step, and add regression assertions for both cases.
[verified]
| reveal_type(b, expected_text="tuple[()]") | ||
|
|
||
| c = t[5:1] | ||
| reveal_type(c, expected_text="tuple[()]") |
There was a problem hiding this comment.
Warning · Non-blocking recommendation
tuple13.py already owns tuple slice-expression coverage, including empty and reversed slices. Move these cases there and remove the additional sample registration so the behavior remains specified in one place.
[verified]
|
When this change is vendored into Pylance, update its parallel async tuple evaluator as well; it still has the old reversed-bound guard and would otherwise diverge. |
Rich Chiodo (rchiodo)
left a comment
There was a problem hiding this comment.
Approved via Review Center.
Summary
Fixes type evaluation of fixed-length tuple slicing when
start >= stop(e.g.t[2:1],t[-1:-2],t[5:1]).In Python runtime, slicing a tuple with
start >= stopproduces an empty tuple()of length 0.Previously,
getSlicedTupleTypereturnedundefinedwheneverendValue < startValue. WhengetSlicedTupleTypereturnedundefined, Pyright fell back to evaluatingtuple.__getitem__(slice), which degraded the inferred type of a fixed-length heterogeneous tuple slice (such ast[2:1]wheret: tuple[int, str, bool]) to an unbounded union typetuple[int | str | bool, ...].Behavior Change
Pyright now correctly evaluates fixed-length tuple slicing with
start >= stopto an empty tuple typetuple[()].Implementation Details
In
packages/pyright-internal/src/analyzer/tuples.ts,getSlicedTupleTypeno longer returnsundefinedwhenendValue < startValue. Instead, it checksstartValue < endValueand uses an empty array[]whenstartValue >= endValue, which cleanly specializes totuple[()].Validation
tuple20.pyand test caseTuple20intypeEvaluator8.test.ts.packages/pyright-internal(npm run test): 24 test suites passed (798 passed, 0 failed).npm run check): 4 packages typechecked cleanly with 0 errors.git diff --check: 0 issues found.