|
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