Skip to content

Commit 1b5cdeb

Browse files
committed
Try some compromise variant
1 parent 46b682c commit 1b5cdeb

10 files changed

+57
-52
lines changed

mypy/binder.py

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from mypy.types import (
1515
AnyType,
1616
Instance,
17+
NoneType,
1718
PartialType,
1819
ProperType,
1920
TupleType,
@@ -341,12 +342,34 @@ def assign_type(self, expr: Expression, type: Type, declared_type: Type | None)
341342
# times?
342343
return
343344

344-
enclosing_type = get_proper_type(self.most_recent_enclosing_type(expr, type))
345-
if isinstance(enclosing_type, AnyType):
346-
# If x is Any and y is int, after x = y we do not infer that x is int,
347-
# instead we keep it Any. This behavior is unsafe, but it exists since
348-
# long time, so we will keep it until someone complains.
349-
self.put(expr, enclosing_type)
345+
p_declared = get_proper_type(declared_type)
346+
p_type = get_proper_type(type)
347+
if isinstance(p_type, AnyType):
348+
# Any type requires some special casing, for both historical reasons,
349+
# and to optimise user experience without sacrificing correctness too much.
350+
if isinstance(expr, RefExpr) and isinstance(expr.node, Var) and expr.node.is_inferred:
351+
# First case: a local/global variable without explicit annotation,
352+
# in this case we just assign Any (essentially following the SSA logic).
353+
self.put(expr, type)
354+
elif isinstance(p_declared, UnionType) and any(
355+
isinstance(get_proper_type(item), NoneType) for item in p_declared.items
356+
):
357+
# Second case: explicit optional type, in this case we optimize for a common
358+
# pattern when an untyped value used as a fallback replacing None.
359+
new_items = [
360+
type if isinstance(get_proper_type(item), NoneType) else item
361+
for item in p_declared.items
362+
]
363+
self.put(expr, UnionType(new_items))
364+
elif isinstance(p_declared, UnionType) and any(
365+
isinstance(get_proper_type(item), AnyType) for item in p_declared.items
366+
):
367+
# Third case: a union already containing Any (most likely from an un-imported
368+
# name), in this case we allow assigning Any as well.
369+
self.put(expr, type)
370+
else:
371+
# In all other cases we don't narrow to Any to minimize false negatives.
372+
self.put(expr, declared_type)
350373
else:
351374
self.put(expr, type)
352375

@@ -368,19 +391,6 @@ def invalidate_dependencies(self, expr: BindableExpression) -> None:
368391
for dep in self.dependencies.get(key, set()):
369392
self._cleanse_key(dep)
370393

371-
def most_recent_enclosing_type(self, expr: BindableExpression, type: Type) -> Type | None:
372-
type = get_proper_type(type)
373-
if isinstance(type, AnyType):
374-
return get_declaration(expr)
375-
key = literal_hash(expr)
376-
assert key is not None
377-
enclosers = [get_declaration(expr)] + [
378-
f.types[key].type
379-
for f in self.frames
380-
if key in f.types and is_subtype(type, f.types[key][0])
381-
]
382-
return enclosers[-1]
383-
384394
def allow_jump(self, index: int) -> None:
385395
# self.frames and self.options_on_return have different lengths
386396
# so make sure the index is positive

mypy/fastparse.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1106,7 +1106,9 @@ def make_argument(
11061106
if argument_elide_name(arg.arg):
11071107
pos_only = True
11081108

1109-
argument = Argument(Var(arg.arg, arg_type), arg_type, self.visit(default), kind, pos_only)
1109+
var = Var(arg.arg, arg_type)
1110+
var.is_inferred = False
1111+
argument = Argument(var, arg_type, self.visit(default), kind, pos_only)
11101112
argument.set_line(
11111113
arg.lineno,
11121114
arg.col_offset,

mypyc/test-data/irbuild-any.test

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ def f(a: Any, n: int, c: C) -> None:
3737
c.n = a
3838
a = n
3939
n = a
40-
a.a = n
4140
[out]
4241
def f(a, n, c):
4342
a :: object
@@ -49,10 +48,6 @@ def f(a, n, c):
4948
r3 :: bool
5049
r4 :: object
5150
r5 :: int
52-
r6 :: object
53-
r7 :: str
54-
r8 :: i32
55-
r9 :: bit
5651
L0:
5752
r0 = box(int, n)
5853
c.a = r0; r1 = is_error
@@ -62,10 +57,6 @@ L0:
6257
a = r4
6358
r5 = unbox(int, a)
6459
n = r5
65-
r6 = box(int, n)
66-
r7 = 'a'
67-
r8 = PyObject_SetAttr(a, r7, r6)
68-
r9 = r8 >= 0 :: signed
6960
return 1
7061

7162
[case testCoerceAnyInOps]

test-data/unit/check-dynamic-typing.test

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,10 @@ d = None # All ok
320320
d = t
321321
d = g
322322
d = A
323-
t = d
324-
f = d
323+
324+
d1: Any
325+
t = d1
326+
f = d1
325327
[builtins fixtures/tuple.pyi]
326328

327329

test-data/unit/check-isinstance.test

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1919,13 +1919,12 @@ if isinstance(x, str, 1): # E: Too many arguments for "isinstance"
19191919
from typing import Any
19201920

19211921
def narrow_any_to_str_then_reassign_to_int() -> None:
1922-
v = 1 # type: Any
1922+
v: Any = 1
19231923

19241924
if isinstance(v, str):
19251925
reveal_type(v) # N: Revealed type is "builtins.str"
19261926
v = 2
1927-
reveal_type(v) # N: Revealed type is "Any"
1928-
1927+
reveal_type(v) # N: Revealed type is "builtins.int"
19291928
[builtins fixtures/isinstance.pyi]
19301929

19311930
[case testNarrowTypeAfterInList]

test-data/unit/check-optional.test

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -723,9 +723,10 @@ def f():
723723
def g(x: Optional[int]) -> int:
724724
if x is None:
725725
reveal_type(x) # N: Revealed type is "None"
726+
# As a special case for Unions containing None, during
726727
x = f()
727-
reveal_type(x) # N: Revealed type is "Any"
728-
reveal_type(x) # N: Revealed type is "Union[Any, builtins.int]"
728+
reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]"
729+
reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]"
729730
return x
730731
[builtins fixtures/bool.pyi]
731732

@@ -739,9 +740,10 @@ def g(x: Optional[int]) -> int:
739740
reveal_type(x) # N: Revealed type is "None"
740741
x = 1
741742
reveal_type(x) # N: Revealed type is "builtins.int"
743+
# Same as above, even after we've assigned to x
742744
x = f()
743-
reveal_type(x) # N: Revealed type is "Any"
744-
reveal_type(x) # N: Revealed type is "Union[Any, builtins.int]"
745+
reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]"
746+
reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]"
745747
return x
746748
[builtins fixtures/bool.pyi]
747749

@@ -754,11 +756,9 @@ def g(x: Optional[int]) -> int:
754756
if x is not None:
755757
return x
756758
reveal_type(x) # N: Revealed type is "None"
757-
if bool():
758-
x = f()
759-
reveal_type(x) # N: Revealed type is "Any"
760-
return x
761-
return x # E: Incompatible return value type (got "None", expected "int")
759+
x = f()
760+
reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]"
761+
return x
762762
[builtins fixtures/bool.pyi]
763763

764764
[case testStrictOptionalCovarianceCrossModule]

test-data/unit/check-parameter-specification.test

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,9 @@ class C(Generic[P]):
343343
a = kwargs
344344
args = kwargs # E: Incompatible types in assignment (expression has type "P.kwargs", variable has type "P.args")
345345
kwargs = args # E: Incompatible types in assignment (expression has type "P.args", variable has type "P.kwargs")
346-
args = a
347-
kwargs = a
346+
a1: Any
347+
args = a1
348+
kwargs = a1
348349
[builtins fixtures/dict.pyi]
349350

350351
[case testParamSpecSubtypeChecking2]

test-data/unit/check-redefine.test

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,8 @@ def f() -> None:
193193
_, _ = 1, ''
194194
if 1:
195195
_, _ = '', 1
196-
reveal_type(_) # N: Revealed type is "Any"
196+
# This is unintentional but probably fine. No one is going to read _ value.
197+
reveal_type(_) # N: Revealed type is "builtins.int"
197198

198199
[case testRedefineWithBreakAndContinue]
199200
# flags: --allow-redefinition

test-data/unit/check-unions.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ x: Union[int, str]
528528
a: Any
529529
if bool():
530530
x = a
531-
reveal_type(x) # N: Revealed type is "Any"
531+
reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]"
532532
reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]"
533533
[builtins fixtures/bool.pyi]
534534

test-data/unit/check-unreachable-code.test

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -631,12 +631,11 @@ class Child(Parent):
631631
def bar(self) -> int:
632632
if 1:
633633
self = super(Child, self).something()
634-
reveal_type(self) # N: Revealed type is "Any"
635-
# TODO: we should probably make this unreachable similar to above.
634+
reveal_type(self) # N: Revealed type is "__main__.Child"
636635
if self is None:
637-
reveal_type(self) # N: Revealed type is "Never"
638-
return None # E: Incompatible return value type (got "None", expected "int")
639-
reveal_type(self) # N: Revealed type is "Any"
636+
reveal_type(self)
637+
return None
638+
reveal_type(self) # N: Revealed type is "__main__.Child"
640639
return 3
641640
[builtins fixtures/isinstance.pyi]
642641

0 commit comments

Comments
 (0)