From cbab356e2e9aa51cac09cc69508a7c1845f895c2 Mon Sep 17 00:00:00 2001 From: Hana Joo Date: Tue, 29 Apr 2025 08:45:17 -0700 Subject: [PATCH] Do not fail with deleted variables when a variable can be found in the global scope or in bulitins. PiperOrigin-RevId: 752751039 --- pytype/tests/test_typing1.py | 20 +++++++++++++++----- pytype/vm.py | 22 +++++++++++++++++++--- pytype/vm_utils.py | 4 ++++ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/pytype/tests/test_typing1.py b/pytype/tests/test_typing1.py index 1365f3525..5a0953549 100644 --- a/pytype/tests/test_typing1.py +++ b/pytype/tests/test_typing1.py @@ -686,16 +686,26 @@ def test_supported_construct_in_supported_version(self): (3, 12), "This only happens with 3.12 where it puts LOAD_FAST_AND_CLEAR op", ) - def test_not_deleted(self): - # TODO(b/414069834): This shouldn't be an error. - errors = self.CheckWithErrors(""" + def test_re_not_deleted_with_load_fast_and_clear(self): + self.Check(""" import re class Foo(): A = [re.compile(r) for r in (r'___', )] - B = re.compile(r"dummy") # name-error[e] + B = re.compile(r"dummy") + """) + + def test_deleted_x_normally(self): + errors = self.CheckWithErrors(""" + x = 1 + def f(): + x = 2 + del x + print(x) # name-error[e] """) - self.assertErrorRegexes(errors, {"e": r"Name 're' is not defined"}) + self.assertErrorRegexes( + errors, {"e": r"Name 'x' is not defined"} + ) if __name__ == "__main__": diff --git a/pytype/vm.py b/pytype/vm.py index cfafdf9fe..3510505ea 100644 --- a/pytype/vm.py +++ b/pytype/vm.py @@ -1681,19 +1681,35 @@ def _name_error_or_late_annotation(self, state, name): def byte_LOAD_NAME(self, state, op): """Load a name. Can be a local, global, or builtin.""" name = op.argval + deleted_var = None try: state, val = self.load_local(state, name) - except KeyError: + if vm_utils.is_deleted_name(state, val): + deleted_var = val + raise KeyError() + except KeyError as e_local: try: state, val = self.load_global(state, name) - except KeyError as e: + if vm_utils.is_deleted_name(state, val): + deleted_var = val + raise e_local + except KeyError as e_global: try: if self._is_private(name): # Private names must be explicitly imported. self.trace_opcode(op, name, None) - raise KeyError(name) from e + raise KeyError(name) from e_global state, val = self.load_builtin(state, name) + if vm_utils.is_deleted_name(state, val): + deleted_var = val + raise e_global except KeyError: + + if deleted_var is not None: + vm_utils.check_for_deleted(state, name, deleted_var, self.ctx) + self.trace_opcode(op, name, deleted_var) + return state.push(deleted_var) + if self._is_private(name) or not self.has_unknown_wildcard_imports: one_val = self._name_error_or_late_annotation(state, name) else: diff --git a/pytype/vm_utils.py b/pytype/vm_utils.py index ce14ea3c1..4bd2cb192 100644 --- a/pytype/vm_utils.py +++ b/pytype/vm_utils.py @@ -930,6 +930,10 @@ def call_inplace_operator(state, iname, x, y, ctx): return state, ret +def is_deleted_name(state, var): + return any(isinstance(x, abstract.Deleted) for x in var.Data(state.node)) + + def check_for_deleted(state, name, var, ctx): for x in var.Data(state.node): if isinstance(x, abstract.Deleted):