Fix bool narrowing for numeric literal patterns - #11570
Conversation
|
🔒 Automated review in progress — @rchiodo is auto-reviewing this PR. |
rchiodo
left a comment
There was a problem hiding this comment.
Approved via Review Center.
|
Diff from mypy_primer, showing the effect of this PR on open source code: sympy (https://github.com/sympy/sympy)
+ .../projects/sympy/sympy/solvers/ode/subscheck.py:150:48 - error: Argument of type "int | Unknown | str" cannot be assigned to parameter "order" of type "str" in function "checkodesol"
+ Type "int | Unknown | str" is not assignable to type "str"
+ "int" is not assignable to "str" (reportArgumentType)
- .../projects/sympy/sympy/solvers/ode/systems.py:1167:37 - error: Operator "*" not supported for types "Literal[2]" and "Unknown | Self@Basic | bool | Expr | Basic"
+ .../projects/sympy/sympy/solvers/ode/systems.py:1167:37 - error: Operator "*" not supported for types "Literal[2]" and "Unknown | Expr | Basic | bool"
- Operator "*" not supported for types "Literal[2]" and "Basic*"
- .../projects/sympy/sympy/solvers/ode/systems.py:1167:37 - error: Operator "*" not supported for types "Unknown | int | Expr" and "Unknown | Self@Basic | bool | Expr | Basic"
+ .../projects/sympy/sympy/solvers/ode/systems.py:1167:37 - error: Operator "*" not supported for types "Unknown | Expr | int" and "Unknown | Expr | Basic | bool"
- Operator "*" not supported for types "int" and "Basic*"
- Operator "*" not supported for types "int" and "Basic"
- Operator "*" not supported for types "Expr" and "Basic*"
+ Operator "*" not supported for types "Expr" and "Basic"
+ Operator "*" not supported for types "int" and "Basic" (reportOperatorIssue)
- .../projects/sympy/sympy/solvers/ode/systems.py:1169:30 - error: Operator "*" not supported for types "Literal[2]" and "Unknown | Self@Basic | bool | Expr | Basic"
+ .../projects/sympy/sympy/solvers/ode/systems.py:1169:30 - error: Operator "*" not supported for types "Literal[2]" and "Unknown | Expr | Basic | bool"
- Operator "*" not supported for types "Literal[2]" and "Basic*"
- .../projects/sympy/sympy/solvers/ode/systems.py:1169:30 - error: Operator "*" not supported for types "Unknown | int | Expr" and "Unknown | Self@Basic | bool | Expr | Basic"
+ .../projects/sympy/sympy/solvers/ode/systems.py:1169:30 - error: Operator "*" not supported for types "Unknown | Expr | int" and "Unknown | Expr | Basic | bool"
- Operator "*" not supported for types "int" and "Basic*"
- Operator "*" not supported for types "int" and "Basic"
- Operator "*" not supported for types "Expr" and "Basic*"
+ Operator "*" not supported for types "Expr" and "Basic"
+ Operator "*" not supported for types "int" and "Basic" (reportOperatorIssue)
- .../projects/sympy/sympy/solvers/ode/systems.py:1169:49 - error: Operator "*" not supported for types "Literal[2]" and "Unknown | Self@Basic | bool | Expr | Basic"
+ .../projects/sympy/sympy/solvers/ode/systems.py:1169:49 - error: Operator "*" not supported for types "Literal[2]" and "Unknown | Expr | Basic | bool"
- Operator "*" not supported for types "Literal[2]" and "Basic*"
- .../projects/sympy/sympy/solvers/ode/systems.py:1169:49 - error: Operator "*" not supported for types "Unknown | int | Expr" and "Unknown | Self@Basic | bool | Expr | Basic"
+ .../projects/sympy/sympy/solvers/ode/systems.py:1169:49 - error: Operator "*" not supported for types "Unknown | Expr | int" and "Unknown | Expr | Basic | bool"
- Operator "*" not supported for types "int" and "Basic*"
- Operator "*" not supported for types "int" and "Basic"
- Operator "*" not supported for types "Expr" and "Basic*"
+ Operator "*" not supported for types "Expr" and "Basic"
+ Operator "*" not supported for types "int" and "Basic" (reportOperatorIssue)
+ .../projects/sympy/sympy/stats/crv.py:369:24 - error: Operator "*" not supported for types "Expr" and "Unknown | Basic"
+ .../projects/sympy/sympy/stats/crv.py:379:25 - error: Operator "*" not supported for types "Expr" and "Unknown | Basic"
+ .../projects/sympy/sympy/stats/crv.py:392:29 - error: Operator "-" not supported for types "Unknown | Basic" and "Dummy"
+ Operator "-" not supported for types "Basic" and "Dummy" (reportOperatorIssue)
+ .../projects/sympy/sympy/stats/crv_types.py:1289:16 - error: Operator "-" not supported for types "Unknown | Basic | Any" and "Expr"
+ Operator "-" not supported for types "Basic" and "Expr" (reportOperatorIssue)
+ .../projects/sympy/sympy/stats/crv_types.py:3358:23 - error: Operator "*" not supported for types "Expr" and "Unknown | Basic | Any"
+ .../projects/sympy/sympy/stats/drv.py:266:18 - error: No overloads for "sum" match the provided arguments (reportCallIssue)
+ .../projects/sympy/sympy/stats/drv.py:266:22 - error: Argument of type "Generator[Unknown | Basic, None, None]" cannot be assigned to parameter "iterable" of type "Iterable[_SupportsSumNoDefaultT@sum]" in function "sum"
+ "Generator[Unknown | Basic, None, None]" is not assignable to "Iterable[_SupportsSumNoDefaultT@sum]"
+ Type parameter "_T_co@Iterable" is covariant, but "Unknown | Basic" is not a subtype of "_SupportsSumNoDefaultT@sum"
+ Type "Unknown | Basic" is not assignable to type "_SupportsSumWithNoDefaultGiven"
+ Type "Unknown | Basic" is not assignable to type "_SupportsSumWithNoDefaultGiven"
+ "Basic" is incompatible with protocol "_SupportsSumWithNoDefaultGiven" (reportArgumentType)
- .../projects/sympy/sympy/stats/drv.py:269:22 - error: Argument of type "Generator[tuple[Unknown, ...] | Unknown | Sum | Expr | ZeroMatrix | Add | Zero | NaN | Piecewise | Basic | int | None, None, None]" cannot be assigned to parameter "iterable" of type "Iterable[_SupportsSumNoDefaultT@sum]" in function "sum"
+ .../projects/sympy/sympy/stats/drv.py:269:22 - error: Argument of type "Generator[tuple[Unknown, ...] | Unknown | Sum | Expr | ZeroMatrix | Zero | NaN | Piecewise | Basic | int | None, None, None]" cannot be assigned to parameter "iterable" of type "Iterable[_SupportsSumNoDefaultT@sum]" in function "sum"
- "Generator[tuple[Unknown, ...] | Unknown | Sum | Expr | ZeroMatrix | Add | Zero | NaN | Piecewise | Basic | int | None, None, None]" is not assignable to "Iterable[_SupportsSumNoDefaultT@sum]"
+ "Generator[tuple[Unknown, ...] | Unknown | Sum | Expr | ZeroMatrix | Zero | NaN | Piecewise | Basic | int | None, None, None]" is not assignable to "Iterable[_SupportsSumNoDefaultT@sum]"
- Type parameter "_T_co@Iterable" is covariant, but "tuple[Unknown, ...] | Unknown | Sum | Expr | ZeroMatrix | Add | Zero | NaN | Piecewise | Basic | int | None" is not a subtype of "_SupportsSumNoDefaultT@sum"
+ Type parameter "_T_co@Iterable" is covariant, but "tuple[Unknown, ...] | Unknown | Sum | Expr | ZeroMatrix | Zero | NaN | Piecewise | Basic | int | None" is not a subtype of "_SupportsSumNoDefaultT@sum"
- Type "tuple[Unknown, ...] | Unknown | Sum | Expr | ZeroMatrix | Add | Zero | NaN | Piecewise | Basic | int | None" is not assignable to type "_SupportsSumWithNoDefaultGiven"
+ Type "tuple[Unknown, ...] | Unknown | Sum | Expr | ZeroMatrix | Zero | NaN | Piecewise | Basic | int | None" is not assignable to type "_SupportsSumWithNoDefaultGiven"
- Type "tuple[Unknown, ...] | Unknown | Sum | Expr | ZeroMatrix | Add | Zero | NaN | Piecewise | Basic | int | None" is not assignable to type "_SupportsSumWithNoDefaultGiven"
+ Type "tuple[Unknown, ...] | Unknown | Sum | Expr | ZeroMatrix | Zero | NaN | Piecewise | Basic | int | None" is not assignable to type "_SupportsSumWithNoDefaultGiven"
- .../projects/sympy/sympy/stats/drv_types.py:293:16 - error: Operator "*" not supported for types "Expr" and "tuple[Unknown, ...] | Unknown | Sum | Expr | ZeroMatrix | Add | Zero | NaN | Piecewise | Basic"
+ .../projects/sympy/sympy/stats/drv_types.py:293:16 - error: Operator "*" not supported for types "Expr" and "tuple[Unknown, ...] | Unknown | Sum | Expr | ZeroMatrix | Zero | NaN | Piecewise | Basic"
- .../projects/sympy/sympy/stats/frv.py:172:24 - error: Cannot access attribute "lhs" for class "Basic*"
+ .../projects/sympy/sympy/stats/frv.py:172:24 - error: Cannot access attribute "lhs" for class "Basic"
- .../projects/sympy/sympy/stats/frv.py:172:35 - error: Cannot access attribute "rhs" for class "Basic*"
+ .../projects/sympy/sympy/stats/frv.py:172:35 - error: Cannot access attribute "rhs" for class "Basic"
- Return type mismatch: base method returns type "bool", override returns type "Unknown | Basic* | Literal[False]"
+ Return type mismatch: base method returns type "bool", override returns type "Unknown | Basic | Literal[False]"
- Type "Unknown | Basic* | Literal[False]" is not assignable to type "bool"
+ Type "Unknown | Basic | Literal[False]" is not assignable to type "bool"
- "Basic*" is not assignable to "bool" (reportIncompatibleMethodOverride)
+ "Basic" is not assignable to "bool" (reportIncompatibleMethodOverride)
+ .../projects/sympy/sympy/stats/frv.py:401:34 - error: Operator "*" not supported for types "Unknown | Basic | Literal[0]" and "Expr"
+ Operator "*" not supported for types "Basic" and "Expr" (reportOperatorIssue)
+ .../projects/sympy/sympy/stats/frv.py:411:34 - error: Operator "*" not supported for types "Unknown | Basic | Literal[0]" and "Expr"
+ Operator "*" not supported for types "Basic" and "Expr" (reportOperatorIssue)
- .../projects/sympy/sympy/stats/joint_rv_types.py:576:27 - error: Argument of type "One | NegativeOne | Zero | Integer | NaN | ComplexInfinity | Rational | Unknown | Number | Expr" cannot be assigned to parameter "stop" of type "SupportsIndex" in function "__new__"
+ .../projects/sympy/sympy/stats/joint_rv_types.py:576:27 - error: Argument of type "One | NegativeOne | Zero | Integer | NaN | ComplexInfinity | Rational | Unknown | Infinity | NegativeInfinity | Float | Number | Expr" cannot be assigned to parameter "stop" of type "SupportsIndex" in function "__new__"
- Type "One | NegativeOne | Zero | Integer | NaN | ComplexInfinity | Rational | Unknown | Number | Expr" is not assignable to type "SupportsIndex"
... (truncated 527 lines) ...
|
The sympy mypy_primer diffs are not caused by this PR — they're pre-existing pyright non-determinismI built pyright at this PR's base ( The two builds produce identical diagnostics on sympy:
So on a deterministic machine the change has zero effect on sympy — a real, PR-caused diff would have reproduced. Why that's expected from the code: the new
What the primer diff actually is: analysis-order / recursion-resolution non-determinism over sympy's recursive
Bottom line: expected primer noise, not a new bug. |
|
Thanks for the PR |
Summary
Literal[False]andLiteral[True]when matching the numeric literal patterns0and1.case Falsedoes not matchLiteral[0].Problem
Numeric literal patterns use equality at runtime. This means
case 0matches both0andFalse, andcase 1matches both1andTrue.Pyright previously narrowed the matched branch to
Literal[0]and the fallthrough branch toLiteral[False], which is unsound relative to runtime behavior.Root cause
Literal-pattern narrowing relied on assignability between the pattern literal and subject literal. That does not capture Python's equality relationship between
0/Falseand1/True.Solution
Handle the asymmetric numeric-pattern equality case explicitly:
0or1retains the equal bool literal in the positive branch;Tests
Added focused coverage for:
case 0overLiteral[0, False];case 1overLiteral[1, True];case Falseretaining the identity-based distinction fromLiteral[0].Validation
npm run typechecknpm run build:cli:devnpm run checknpm run buildinpackages/pyright-internalnpx jest -t 'MatchLiteral1' --forceExit --runInBandnpx jest src/tests/typeEvaluator6.test.ts --runInBandnpm test -- --runInBand(62 suites, 2549 tests)Compatibility and risk
The change is limited to literal-pattern narrowing for int patterns against bool literal subjects. Tests cover both positive and negative branches and the inverse singleton-bool boundary.
Documentation
No documentation changes are needed; this aligns analyzer behavior with Python's existing structural pattern-matching semantics.
Related issues and prior work
#11026 and #11028 addressed broader literal-pattern narrowing involving non-literal and supertype subjects. This change covers the distinct bool/int literal equality edge case.