From 2013d8f03ead8cd52c126950dac871fa9cd311ec Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" <69878+youknowone@users.noreply.github.com> Date: Mon, 2 Mar 2026 16:47:16 +0900 Subject: [PATCH] Fix symbol table sub_table desync for non-simple annotation targets (#7300) * Fix symbol table sub_table desync for non-simple annotation targets Non-simple annotations (subscript/attribute/parenthesized targets like `a[0]: expr`) were scanned in the annotation scope during symbol table analysis, creating sub_tables for any comprehensions. But codegen only compiles simple name annotations into __annotate__, so those sub_tables were never consumed. This caused subsequent simple annotations' comprehension sub_tables to get the wrong index, resulting in "the symbol 'X' must be present in the symbol table" errors. Fix: skip entering annotation scope for non-simple annotations since they are never compiled into __annotate__. * Validate forbidden expressions in non-simple annotation targets Fix cspell "desynchronize" warning and validate yield/await/named/async comprehension expressions in non-simple annotations without creating annotation scopes. * Restore in_annotation flag before propagating error --- Lib/test/test_type_annotations.py | 1 - crates/codegen/src/symboltable.rs | 22 +++++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_type_annotations.py b/Lib/test/test_type_annotations.py index a7b87bb2e..4c58fade1 100644 --- a/Lib/test/test_type_annotations.py +++ b/Lib/test/test_type_annotations.py @@ -776,7 +776,6 @@ class ConditionalAnnotationTests(unittest.TestCase): class RegressionTests(unittest.TestCase): # gh-132479 - @unittest.expectedFailure # TODO: RUSTPYTHON; SyntaxError: the symbol 'unique_name_6' must be present in the symbol table def test_complex_comprehension_inlining(self): # Test that the various repro cases from the issue don't crash cases = [ diff --git a/crates/codegen/src/symboltable.rs b/crates/codegen/src/symboltable.rs index 22bea20fa..0d868bc04 100644 --- a/crates/codegen/src/symboltable.rs +++ b/crates/codegen/src/symboltable.rs @@ -1053,6 +1053,11 @@ impl SymbolTableBuilder { /// Annotation and TypeParams scopes act as async barriers (always non-async). /// Comprehension scopes are transparent (inherit parent's async context). fn is_in_async_context(&self) -> bool { + // Annotations are evaluated in a non-async scope even when + // the enclosing function is async. + if self.in_annotation { + return false; + } for table in self.tables.iter().rev() { match table.typ { CompilerScope::AsyncFunction => return true, @@ -1462,7 +1467,22 @@ impl SymbolTableBuilder { self.scan_expression(target, ExpressionContext::Store)?; } } - self.scan_annotation(annotation)?; + // Only scan annotation in annotation scope for simple name targets. + // Non-simple annotations (subscript, attribute, parenthesized) are + // never compiled into __annotate__, so scanning them would create + // sub_tables that cause mismatch in the annotation scope's sub_table index. + let is_simple_name = *simple && matches!(&**target, Expr::Name(_)); + if is_simple_name { + self.scan_annotation(annotation)?; + } else { + // Still validate annotation for forbidden expressions + // (yield, await, named) even for non-simple targets. + let was_in_annotation = self.in_annotation; + self.in_annotation = true; + let result = self.scan_expression(annotation, ExpressionContext::Load); + self.in_annotation = was_in_annotation; + result?; + } if let Some(value) = value { self.scan_expression(value, ExpressionContext::Load)?; }