Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add flag to allow more flexible variable redefinition #18727

Open
wants to merge 80 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
9735391
WIP some initial prototyping
JukkaL Jan 3, 2025
946d4d5
WIP add failing test case
JukkaL Jan 28, 2025
7f7e25f
WIP minimal support for merging control flow
JukkaL Jan 29, 2025
0c9f049
Add globals test case
JukkaL Jan 29, 2025
9935537
Add class body test case
JukkaL Jan 29, 2025
fd12f85
Require --local-partial-types
JukkaL Jan 29, 2025
b489b6a
Fix optional types
JukkaL Jan 29, 2025
d2b7558
Add partial type test cases
JukkaL Jan 29, 2025
63bf0af
Pass options consistently
JukkaL Jan 29, 2025
ec02654
Fix interaction with Final
JukkaL Jan 29, 2025
4290857
Add annotated variable test case
JukkaL Jan 29, 2025
5c8ab17
Add test
JukkaL Jan 29, 2025
c116451
Add failing test
JukkaL Jan 29, 2025
9794308
Only use type context in assignment if inference fails without type c…
JukkaL Jan 30, 2025
a355473
Always use type annotation as context
JukkaL Jan 30, 2025
8162a45
Add while loop test case
JukkaL Jan 31, 2025
b1f75a7
Fix type inference in loops
JukkaL Jan 31, 2025
62dfbb0
Update tests
JukkaL Feb 5, 2025
bb6a246
Remove underscore special case
JukkaL Feb 5, 2025
51c61c7
Don't perform renaming when using new semantics
JukkaL Feb 5, 2025
0384795
Fix for loops
JukkaL Feb 6, 2025
a003c83
WIP failing tests
JukkaL Feb 6, 2025
a35870e
Add try/except test case
JukkaL Feb 6, 2025
dea9148
Add match statement test
JukkaL Feb 6, 2025
2b104dc
Add simple nested function test case
JukkaL Feb 6, 2025
6a05baf
Update globals tests case
JukkaL Feb 6, 2025
b58be6a
Add assignment expression test case
JukkaL Feb 6, 2025
5b31950
Add lambda test case
JukkaL Feb 6, 2025
bcaace1
Tests for imports
JukkaL Feb 6, 2025
ad90125
Add operator assignment test case
JukkaL Feb 6, 2025
106577d
WIP add break/continue tests (failing)
JukkaL Feb 6, 2025
ea6f13b
WIP del test (failing)
JukkaL Feb 6, 2025
71deb24
Add return test (failing)
JukkaL Feb 7, 2025
b3cc74a
Fix binder issues
JukkaL Feb 7, 2025
172bcf2
Various binder fixes and test updates
JukkaL Feb 10, 2025
9353132
Add default arg test case
JukkaL Feb 10, 2025
13b7aa3
Add max iterations check to processing loops
JukkaL Feb 10, 2025
f1223df
Fix try statement within loop
JukkaL Feb 10, 2025
d4445de
Fix indexed literals
JukkaL Feb 10, 2025
b3b1e9e
Put types of parameters and annotated variables to the binder
JukkaL Feb 10, 2025
06ed1e9
Fix self check
JukkaL Feb 10, 2025
9f48a19
Update tests
JukkaL Feb 10, 2025
f5a1915
Improve repr of Var nodes
JukkaL Feb 12, 2025
6ca4d76
Fix match statements by reusing dummy Var node
JukkaL Feb 12, 2025
9fde15f
Fix crash from inferring union with partial type item
JukkaL Feb 12, 2025
abf4eac
Add tests
JukkaL Feb 12, 2025
71cdb61
Add test cases for "del"
JukkaL Feb 12, 2025
69fca07
Rename flag to --allow-redefinition-new
JukkaL Feb 20, 2025
5ee1868
Rename tests
JukkaL Feb 20, 2025
b8a106d
Suppress from --help, since this is experimental
JukkaL Feb 20, 2025
bf75cc4
Add and update tests
JukkaL Feb 20, 2025
9585353
Minor tweaks
JukkaL Feb 20, 2025
0efa85d
Refactor
JukkaL Feb 20, 2025
4af080c
Black and ruff
JukkaL Feb 20, 2025
c478514
More tweaks
JukkaL Feb 20, 2025
fb9421f
Prevent wideding of variable defined in outer scope
JukkaL Feb 21, 2025
a29a3dc
Add comment
JukkaL Feb 21, 2025
ff7557b
Fix issue
JukkaL Feb 21, 2025
10b6082
Some polish
JukkaL Feb 21, 2025
e8789e0
Don't widen final variables
JukkaL Feb 21, 2025
9494ddc
Fix incremental mode
JukkaL Feb 21, 2025
fed6d45
Simplify
JukkaL Feb 21, 2025
aaefd4b
Detect invalid per-module options
JukkaL Feb 21, 2025
d7c35e4
Fix typo
JukkaL Feb 24, 2025
5bd6def
Skip failing match statement test on 3.9
JukkaL Feb 24, 2025
3e38e7d
Don't infer infinitely complex types in loops
JukkaL Feb 27, 2025
65f4785
WIP add failing test cases
JukkaL Mar 12, 2025
179b846
Avoid inferring a partial type for "_"
JukkaL Mar 12, 2025
2b711e2
Fix binder issue with unions containing Any
JukkaL Mar 12, 2025
14524fe
Improve error message
JukkaL Mar 12, 2025
37098d6
Address review
JukkaL Mar 12, 2025
808f6e3
Address more review comments
JukkaL Mar 12, 2025
4317961
Update comment
JukkaL Mar 12, 2025
460ebe4
Update comments
JukkaL Mar 12, 2025
2c6a144
Address more comments
JukkaL Mar 12, 2025
daf9c75
Add TODO comments
JukkaL Mar 12, 2025
69fd839
TEMPORARY: Enable --allow-redefinition-new and --local-partial-types
JukkaL Mar 12, 2025
a5adfd7
Preserve TypedDicty type context more aggressively for backward compat
JukkaL Mar 14, 2025
2dba308
Backward compatibility fix
JukkaL Mar 14, 2025
ac852e6
Fix self check
JukkaL Mar 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions mypy/binder.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
from typing_extensions import TypeAlias as _TypeAlias

from mypy.erasetype import remove_instance_last_known_values
from mypy.literals import Key, literal, literal_hash, subkeys
from mypy.literals import Key, extract_var_from_literal_hash, literal, literal_hash, subkeys
from mypy.nodes import Expression, IndexExpr, MemberExpr, NameExpr, RefExpr, TypeInfo, Var
from mypy.options import Options
from mypy.subtypes import is_same_type, is_subtype
from mypy.typeops import make_simplified_union
from mypy.types import (
Expand Down Expand Up @@ -39,6 +40,7 @@ class CurrentType(NamedTuple):

class Frame:
"""A Frame represents a specific point in the execution of a program.

It carries information about the current types of expressions at
that point, arising either from assignments to those expressions
or the result of isinstance checks and other type narrowing
Expand Down Expand Up @@ -97,7 +99,7 @@ class A:
# This maps an expression to a list of bound types for every item in the union type.
type_assignments: Assigns | None = None

def __init__(self) -> None:
def __init__(self, options: Options) -> None:
# Each frame gets an increasing, distinct id.
self.next_id = 1

Expand Down Expand Up @@ -131,6 +133,11 @@ def __init__(self) -> None:
self.break_frames: list[int] = []
self.continue_frames: list[int] = []

# If True, initial assignment to a simple variable (e.g. "x", but not "x.y")
# is added to the binder. This allows more precise narrowing and more
# flexible inference of variable types (--allow-redefinition-new).
self.bind_all = options.allow_redefinition_new

def _get_id(self) -> int:
self.next_id += 1
return self.next_id
Expand Down Expand Up @@ -226,12 +233,20 @@ def update_from_options(self, frames: list[Frame]) -> bool:
for key in keys:
current_value = self._get(key)
resulting_values = [f.types.get(key, current_value) for f in frames]
if any(x is None for x in resulting_values):
# Keys can be narrowed using two different semantics. The new semantics
# is enabled for plain variables when bind_all is true, and it allows
# variable types to be widened using subsequent assignments. This is
# tricky to support for instance attributes (primarily due to deferrals),
# so we don't use it for them.
old_semantics = not self.bind_all or extract_var_from_literal_hash(key) is None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite tricky and with time we may forget what old_semantics mean. I think it is worth adding a comment with motivating code example here.

if old_semantics and any(x is None for x in resulting_values):
# We didn't know anything about key before
# (current_value must be None), and we still don't
# know anything about key in at least one possible frame.
continue

resulting_values = [x for x in resulting_values if x is not None]

if all_reachable and all(
x is not None and not x.from_assignment for x in resulting_values
):
Expand Down Expand Up @@ -278,7 +293,11 @@ def update_from_options(self, frames: list[Frame]) -> bool:
# still equivalent to such type).
if isinstance(type, UnionType):
type = collapse_variadic_union(type)
if isinstance(type, ProperType) and isinstance(type, UnionType):
if (
old_semantics
and isinstance(type, ProperType)
and isinstance(type, UnionType)
):
# Simplify away any extra Any's that were added to the declared
# type when popping a frame.
simplified = UnionType.make_union(
Expand Down
6 changes: 4 additions & 2 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -2240,8 +2240,10 @@ def semantic_analysis_pass1(self) -> None:
# TODO: Do this while constructing the AST?
self.tree.names = SymbolTable()
if not self.tree.is_stub:
# Always perform some low-key variable renaming
self.tree.accept(LimitedVariableRenameVisitor())
if not self.options.allow_redefinition_new:
# Perform some low-key variable renaming when assignments can't
# widen inferred types
self.tree.accept(LimitedVariableRenameVisitor())
if options.allow_redefinition:
# Perform more renaming across the AST to allow variable redefinitions
self.tree.accept(VariableRenameVisitor())
Expand Down
Loading
Loading