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
This commit is contained in:
Jeong, YunWon authored and GitHub committed 2026-03-02 16:47:16 +09:00
1 parent f95be1c82b
commit 2013d8f03e
2 files changed
+21 -2

No files matched your search

-1
View File
@@ -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 = [
+21 -1
View File
@@ -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)?;
}