|
7 | 7 | from typing_extensions import TypeAlias as _TypeAlias |
8 | 8 |
|
9 | 9 | from mypy.erasetype import remove_instance_last_known_values |
10 | | -from mypy.literals import Key, literal, literal_hash, subkeys |
| 10 | +from mypy.literals import Key, extract_var_from_literal_hash, literal, literal_hash, subkeys |
11 | 11 | from mypy.nodes import Expression, IndexExpr, MemberExpr, NameExpr, RefExpr, TypeInfo, Var |
| 12 | +from mypy.options import Options |
12 | 13 | from mypy.subtypes import is_same_type, is_subtype |
13 | 14 | from mypy.typeops import make_simplified_union |
14 | 15 | from mypy.types import ( |
@@ -40,6 +41,7 @@ class CurrentType(NamedTuple): |
40 | 41 |
|
41 | 42 | class Frame: |
42 | 43 | """A Frame represents a specific point in the execution of a program. |
| 44 | +
|
43 | 45 | It carries information about the current types of expressions at |
44 | 46 | that point, arising either from assignments to those expressions |
45 | 47 | or the result of isinstance checks and other type narrowing |
@@ -99,7 +101,7 @@ class A: |
99 | 101 | # This maps an expression to a list of bound types for every item in the union type. |
100 | 102 | type_assignments: Assigns | None = None |
101 | 103 |
|
102 | | - def __init__(self) -> None: |
| 104 | + def __init__(self, options: Options) -> None: |
103 | 105 | # Each frame gets an increasing, distinct id. |
104 | 106 | self.next_id = 1 |
105 | 107 |
|
@@ -133,6 +135,11 @@ def __init__(self) -> None: |
133 | 135 | self.break_frames: list[int] = [] |
134 | 136 | self.continue_frames: list[int] = [] |
135 | 137 |
|
| 138 | + # If True, initial assignment to a simple variable (e.g. "x", but not "x.y") |
| 139 | + # is added to the binder. This allows more precise narrowing and more |
| 140 | + # flexible inference of variable types (--allow-redefinition-new). |
| 141 | + self.bind_all = options.allow_redefinition_new |
| 142 | + |
136 | 143 | def _get_id(self) -> int: |
137 | 144 | self.next_id += 1 |
138 | 145 | return self.next_id |
@@ -238,12 +245,20 @@ def update_from_options(self, frames: list[Frame]) -> bool: |
238 | 245 | for key in keys: |
239 | 246 | current_value = self._get(key) |
240 | 247 | resulting_values = [f.types.get(key, current_value) for f in frames] |
241 | | - if any(x is None for x in resulting_values): |
| 248 | + # Keys can be narrowed using two different semantics. The new semantics |
| 249 | + # is enabled for plain variables when bind_all is true, and it allows |
| 250 | + # variable types to be widened using subsequent assignments. This is |
| 251 | + # tricky to support for instance attributes (primarily due to deferrals), |
| 252 | + # so we don't use it for them. |
| 253 | + old_semantics = not self.bind_all or extract_var_from_literal_hash(key) is None |
| 254 | + if old_semantics and any(x is None for x in resulting_values): |
242 | 255 | # We didn't know anything about key before |
243 | 256 | # (current_value must be None), and we still don't |
244 | 257 | # know anything about key in at least one possible frame. |
245 | 258 | continue |
246 | 259 |
|
| 260 | + resulting_values = [x for x in resulting_values if x is not None] |
| 261 | + |
247 | 262 | if all_reachable and all( |
248 | 263 | x is not None and not x.from_assignment for x in resulting_values |
249 | 264 | ): |
@@ -290,7 +305,11 @@ def update_from_options(self, frames: list[Frame]) -> bool: |
290 | 305 | # still equivalent to such type). |
291 | 306 | if isinstance(type, UnionType): |
292 | 307 | type = collapse_variadic_union(type) |
293 | | - if isinstance(type, ProperType) and isinstance(type, UnionType): |
| 308 | + if ( |
| 309 | + old_semantics |
| 310 | + and isinstance(type, ProperType) |
| 311 | + and isinstance(type, UnionType) |
| 312 | + ): |
294 | 313 | # Simplify away any extra Any's that were added to the declared |
295 | 314 | # type when popping a frame. |
296 | 315 | simplified = UnionType.make_union( |
|
0 commit comments