Fix #11463: Incorrect is behaviour with NewType#11496
Conversation
…literals A NewType instance is, at runtime, an instance of its base type. When narrowing `x is None` or `x is <literal>` for a NewType-typed value, the assignability check used the NewType subclass, so the base type (e.g. None or bool) was not considered assignable and the branch was incorrectly marked unreachable. Unwrap NewType instances to their underlying base type (recursively for nested NewTypes) when testing for None/literal overlap in narrowTypeForIsNone and narrowTypeForLiteralComparison. Fixes microsoft#11463 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Diff from mypy_primer, showing the effect of this PR on open source code: sympy (https://github.com/sympy/sympy)
- .../projects/sympy/sympy/stats/random_matrix_models.py:111:36 - error: Operator "*" not supported for types "Expr" and "tuple[Unknown, ...] | Unknown | Product | Basic | BooleanFalse | BooleanTrue | ComplexInfinity | Equality | Expr | Float | Infinity | Integer | NaN | NegativeInfinity | NegativeOne | Number | One | Rational | Zero"
+ .../projects/sympy/sympy/stats/random_matrix_models.py:111:36 - error: Operator "*" not supported for types "Expr" and "tuple[Unknown, ...] | Unknown | Product | Basic | BooleanFalse | BooleanTrue | Equality | Expr | NaN | One | Zero"
- .../projects/sympy/sympy/stats/rv.py:473:25 - error: Argument of type "Expr | Lambda | Zero | One | Integral | Unknown | Probability | tuple[Unknown, ...] | Sum | ZeroMatrix | NaN | Piecewise | Basic | NegativeOne | Integer | ComplexInfinity | Rational | Infinity | NegativeInfinity | Float | Number | int" cannot be assigned to parameter "args" of type "Expr | complex" in function "__new__"
- Type "Expr | Lambda | Zero | One | Integral | Unknown | Probability | tuple[Unknown, ...] | Sum | ZeroMatrix | NaN | Piecewise | Basic | NegativeOne | Integer | ComplexInfinity | Rational | Infinity | NegativeInfinity | Float | Number | int" is not assignable to type "Expr | complex"
+ .../projects/sympy/sympy/stats/rv.py:473:25 - error: Argument of type "Expr | Lambda | Zero | One | Integral | Unknown | NegativeOne | Integer | NaN | ComplexInfinity | Rational | Infinity | Number | NegativeInfinity | Probability | tuple[Unknown, ...] | Sum | ZeroMatrix | Piecewise | Basic | int" cannot be assigned to parameter "args" of type "Expr | complex" in function "__new__"
+ Type "Expr | Lambda | Zero | One | Integral | Unknown | NegativeOne | Integer | NaN | ComplexInfinity | Rational | Infinity | Number | NegativeInfinity | Probability | tuple[Unknown, ...] | Sum | ZeroMatrix | Piecewise | Basic | int" is not assignable to type "Expr | complex"
+ Type "Basic" is not assignable to type "Expr | complex"
+ "Basic" is not assignable to "Expr"
+ "Basic" is not assignable to "complex" (reportArgumentType)
+ .../projects/sympy/sympy/stats/rv.py:473:25 - error: Argument of type "Mul | Expr | Lambda | Zero | One | Integral | Unknown | NegativeOne | Integer | NaN | ComplexInfinity | Rational | Infinity | Number | NegativeInfinity | Probability | tuple[Unknown, ...] | Sum | ZeroMatrix | Piecewise | Basic | int" cannot be assigned to parameter "args" of type "Expr | complex" in function "__new__"
+ Type "Mul | Expr | Lambda | Zero | One | Integral | Unknown | NegativeOne | Integer | NaN | ComplexInfinity | Rational | Infinity | Number | NegativeInfinity | Probability | tuple[Unknown, ...] | Sum | ZeroMatrix | Piecewise | Basic | int" is not assignable to type "Expr | complex"
- Return type mismatch: base method returns type "Self@Basic", override returns type "Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Add | Zero | NaN | Piecewise | Integral | Any | Self@Expectation | Literal[0]"
+ Return type mismatch: base method returns type "Self@Basic", override returns type "Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Zero | NaN | Piecewise | Integral | Any | Self@Expectation | Literal[0]"
- Type "Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Add | Zero | NaN | Piecewise | Integral | Any | Expectation* | Literal[0]" is not assignable to type "Basic"
+ Type "Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Zero | NaN | Piecewise | Integral | Any | Expectation* | Literal[0]" is not assignable to type "Basic"
- Return type mismatch: base method returns type "Basic", override returns type "Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Add | Zero | NaN | Piecewise | Integral | Any | Literal[0]"
+ Return type mismatch: base method returns type "Basic", override returns type "Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Zero | NaN | Piecewise | Integral | Any | Literal[0]"
- Type "Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Add | Zero | NaN | Piecewise | Integral | Any | Literal[0]" is not assignable to type "Basic"
+ Type "Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Zero | NaN | Piecewise | Integral | Any | Literal[0]" is not assignable to type "Basic"
+ .../projects/sympy/sympy/stats/tests/test_continuous_rv.py:97:29 - error: Cannot access attribute "symbol" for class "ConditionalFiniteDomain"
+ Attribute "symbol" is unknown (reportAttributeAccessIssue)
- .../projects/sympy/sympy/stats/tests/test_continuous_rv.py:367:12 - error: Operator "-" not supported for types "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Add | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]" and "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Add | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]"
+ .../projects/sympy/sympy/stats/tests/test_continuous_rv.py:367:12 - error: Operator "-" not supported for types "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]" and "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]"
- .../projects/sympy/sympy/stats/tests/test_continuous_rv.py:415:21 - error: Argument of type "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Add | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]" cannot be assigned to parameter "expr" of type "Basic" in function "simplify"
+ .../projects/sympy/sympy/stats/tests/test_continuous_rv.py:415:21 - error: Argument of type "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]" cannot be assigned to parameter "expr" of type "Basic" in function "simplify"
- Type "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Add | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]" is not assignable to type "Basic"
+ Type "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]" is not assignable to type "Basic"
- .../projects/sympy/sympy/stats/tests/test_continuous_rv.py:1005:21 - error: Argument of type "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Add | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]" cannot be assigned to parameter "expr" of type "Basic" in function "simplify"
+ .../projects/sympy/sympy/stats/tests/test_continuous_rv.py:1005:21 - error: Argument of type "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]" cannot be assigned to parameter "expr" of type "Basic" in function "simplify"
- Type "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Add | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]" is not assignable to type "Basic"
+ Type "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]" is not assignable to type "Basic"
- .../projects/sympy/sympy/stats/tests/test_continuous_rv.py:1060:21 - error: Argument of type "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Add | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]" cannot be assigned to parameter "expr" of type "Basic" in function "simplify"
+ .../projects/sympy/sympy/stats/tests/test_continuous_rv.py:1060:21 - error: Argument of type "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]" cannot be assigned to parameter "expr" of type "Basic" in function "simplify"
- Type "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Add | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]" is not assignable to type "Basic"
+ Type "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]" is not assignable to type "Basic"
- .../projects/sympy/sympy/stats/tests/test_continuous_rv.py:1297:61 - error: Operator "**" not supported for types "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Add | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]" and "Literal[2]"
+ .../projects/sympy/sympy/stats/tests/test_continuous_rv.py:1297:61 - error: Operator "**" not supported for types "RandomSymbol | Basic | Expectation | Expr | tuple[Unknown, ...] | Unknown | Sum | ZeroMatrix | Zero | NaN | Piecewise | Integral | Any | ExpectationMatrix | Literal[0]" and "Literal[2]"
... (truncated 1476 lines) ...
|
|
Fix looks correct and the sample covers the key cases. Two non-blocking notes below: tighten the test to also assert the unreachable-diagnostic count, and clarify whether negative-direction ( |
|
This change overlaps with an already-landed fix on |
…o the on-disk getInnermostNewTypeBaseInstance helper Adopts main's complete NewType identity-narrowing fix (None/ellipsis/literal, brand-preserving, correct negative branches) and removes the divergent duplicate getNewTypeBaseInstance helper and newType8.py sample. Resolves review feedback on PR microsoft#11496. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
I reconciled this branch with the already-landed fix on |
Description
Fixes incorrect
isnarrowing forNewTypeinstances. At runtime aNewTypevalue is simply an instance of its base type, but Pyright treatedNewTypeinstances as never overlapping withNoneorboolliterals. This causedx is None/x is True/x is Falsebranches to be wrongly marked unreachable (and narrowing to be skipped) whenxwas aNewTypeoverNoneType,bool, etc.How you figured out what to do
The reported symptom was that comparing a
NewTypeinstance againstNone/bool literals never narrowed and the guarded branch was flagged unreachable. Tracing theisnarrowing logic intypeGuards.tsshowed that both theNonecheck (narrowTypeForIsNone) and the literal-comparison check (narrowTypeForLiteralComparison) compared the declaredNewTypeclass against the target, which never overlaps. The fix is to test the underlying base type instead, matching runtime behavior.Implementation
Added a helper
getNewTypeBaseInstanceintypeGuards.tsthat, given a class instance, recursively unwrapsNewTypeclasses (following the first base class) and returns the underlying base type instance — orundefinedif the type isn't aNewType.narrowTypeForIsNone: use the unwrapped base instance when testing for overlap withNone.narrowTypeForLiteralComparison: use the unwrapped base instance when testing for overlap with a literal (e.g.True/False).Non-
NewTypetypes fall back to the original subtype, so existing behavior is unchanged.Testing
Added sample
newType8.pycovering:NewTypeoverNoneTypenarrowed byis None→None.NewTypeoverboolnarrowed byis True/is False→Literal[True]/Literal[False].NewType(base is itself aNewType) narrowed byis None.NewTypeoverint, whereis Nonecorrectly remains unreachable (validating we don't over-narrow).Wired it up as
NewType8intypeEvaluator2.test.ts(expects 1 error from the intentional unreachable branch).Addresses
Fixes https://github.com/microsoft/pylance-release/issues/11463
Generated by fix_all_my_issues pipeline