|
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 ( |
@@ -39,6 +40,7 @@ class CurrentType(NamedTuple): |
39 | 40 |
|
40 | 41 | class Frame: |
41 | 42 | """A Frame represents a specific point in the execution of a program. |
| 43 | +
|
42 | 44 | It carries information about the current types of expressions at |
43 | 45 | that point, arising either from assignments to those expressions |
44 | 46 | or the result of isinstance checks and other type narrowing |
@@ -97,7 +99,7 @@ class A: |
97 | 99 | # This maps an expression to a list of bound types for every item in the union type. |
98 | 100 | type_assignments: Assigns | None = None |
99 | 101 |
|
100 | | - def __init__(self) -> None: |
| 102 | + def __init__(self, options: Options) -> None: |
101 | 103 | # Each frame gets an increasing, distinct id. |
102 | 104 | self.next_id = 1 |
103 | 105 |
|
@@ -131,6 +133,11 @@ def __init__(self) -> None: |
131 | 133 | self.break_frames: list[int] = [] |
132 | 134 | self.continue_frames: list[int] = [] |
133 | 135 |
|
| 136 | + # If True, initial assignment to a simple variable (e.g. "x", but not "x.y") |
| 137 | + # is added to the binder. This allows more precise narrowing and more |
| 138 | + # flexible inference of variable types (--allow-redefinition-new). |
| 139 | + self.bind_all = options.allow_redefinition_new |
| 140 | + |
134 | 141 | def _get_id(self) -> int: |
135 | 142 | self.next_id += 1 |
136 | 143 | return self.next_id |
@@ -226,12 +233,20 @@ def update_from_options(self, frames: list[Frame]) -> bool: |
226 | 233 | for key in keys: |
227 | 234 | current_value = self._get(key) |
228 | 235 | resulting_values = [f.types.get(key, current_value) for f in frames] |
229 | | - if any(x is None for x in resulting_values): |
| 236 | + # Keys can be narrowed using two different semantics. The new semantics |
| 237 | + # is enabled for plain variables when bind_all is true, and it allows |
| 238 | + # variable types to be widened using subsequent assignments. This is |
| 239 | + # tricky to support for instance attributes (primarily due to deferrals), |
| 240 | + # so we don't use it for them. |
| 241 | + old_semantics = not self.bind_all or extract_var_from_literal_hash(key) is None |
| 242 | + if old_semantics and any(x is None for x in resulting_values): |
230 | 243 | # We didn't know anything about key before |
231 | 244 | # (current_value must be None), and we still don't |
232 | 245 | # know anything about key in at least one possible frame. |
233 | 246 | continue |
234 | 247 |
|
| 248 | + resulting_values = [x for x in resulting_values if x is not None] |
| 249 | + |
235 | 250 | if all_reachable and all( |
236 | 251 | x is not None and not x.from_assignment for x in resulting_values |
237 | 252 | ): |
@@ -278,7 +293,11 @@ def update_from_options(self, frames: list[Frame]) -> bool: |
278 | 293 | # still equivalent to such type). |
279 | 294 | if isinstance(type, UnionType): |
280 | 295 | type = collapse_variadic_union(type) |
281 | | - if isinstance(type, ProperType) and isinstance(type, UnionType): |
| 296 | + if ( |
| 297 | + old_semantics |
| 298 | + and isinstance(type, ProperType) |
| 299 | + and isinstance(type, UnionType) |
| 300 | + ): |
282 | 301 | # Simplify away any extra Any's that were added to the declared |
283 | 302 | # type when popping a frame. |
284 | 303 | simplified = UnionType.make_union( |
|
0 commit comments