Skip to content

Commit aa20ab0

Browse files
committed
Consume nested scope tables in optimized-out asserts
When -O flag removes assert statements, any nested scopes (generators, comprehensions, lambdas) inside the assert expression still have symbol tables in the sub_tables list. Without consuming them, the next_sub_table index gets misaligned, causing later scopes to use wrong symbol tables. Walk the skipped assert expression with an AST visitor to find and consume nested scope symbol tables, keeping the index aligned with AST traversal order.
1 parent f49af3f commit aa20ab0

2 files changed

Lines changed: 78 additions & 2 deletions

File tree

Lib/test/_test_multiprocessing.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5442,8 +5442,6 @@ def run_in_child(cls, start_method):
54425442
flags = (tuple(sys.flags), grandchild_flags)
54435443
print(json.dumps(flags))
54445444

5445-
# TODO: RUSTPYTHON - SyntaxError in subprocess after fork
5446-
@unittest.expectedFailure
54475445
def test_flags(self):
54485446
import json
54495447
# start child process using unusual flags

crates/codegen/src/compile.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2366,6 +2366,14 @@ impl Compiler {
23662366
);
23672367

23682368
self.switch_to_block(after_block);
2369+
} else {
2370+
// Optimized-out asserts still need to consume any nested
2371+
// scope symbol tables they contain so later nested scopes
2372+
// stay aligned with AST traversal order.
2373+
self.consume_skipped_nested_scopes_in_expr(test)?;
2374+
if let Some(expr) = msg {
2375+
self.consume_skipped_nested_scopes_in_expr(expr)?;
2376+
}
23692377
}
23702378
}
23712379
ast::Stmt::Break(_) => {
@@ -7605,6 +7613,62 @@ impl Compiler {
76057613
})
76067614
}
76077615

7616+
fn consume_next_subtable(&mut self) -> CompileResult<()> {
7617+
{
7618+
let _ = self.push_symbol_table()?;
7619+
}
7620+
let _ = self.pop_symbol_table();
7621+
Ok(())
7622+
}
7623+
7624+
fn consume_skipped_nested_scopes_in_expr(
7625+
&mut self,
7626+
expression: &ast::Expr,
7627+
) -> CompileResult<()> {
7628+
use ast::visitor::Visitor;
7629+
7630+
struct SkippedScopeVisitor<'a> {
7631+
compiler: &'a mut Compiler,
7632+
error: Option<CodegenError>,
7633+
}
7634+
7635+
impl SkippedScopeVisitor<'_> {
7636+
fn consume_scope(&mut self) {
7637+
if self.error.is_none() {
7638+
self.error = self.compiler.consume_next_subtable().err();
7639+
}
7640+
}
7641+
}
7642+
7643+
impl ast::visitor::Visitor<'_> for SkippedScopeVisitor<'_> {
7644+
fn visit_expr(&mut self, expr: &ast::Expr) {
7645+
if self.error.is_some() {
7646+
return;
7647+
}
7648+
7649+
match expr {
7650+
ast::Expr::Lambda(_)
7651+
| ast::Expr::ListComp(_)
7652+
| ast::Expr::SetComp(_)
7653+
| ast::Expr::DictComp(_)
7654+
| ast::Expr::Generator(_) => self.consume_scope(),
7655+
_ => ast::visitor::walk_expr(self, expr),
7656+
}
7657+
}
7658+
}
7659+
7660+
let mut visitor = SkippedScopeVisitor {
7661+
compiler: self,
7662+
error: None,
7663+
};
7664+
visitor.visit_expr(expression);
7665+
if let Some(err) = visitor.error {
7666+
Err(err)
7667+
} else {
7668+
Ok(())
7669+
}
7670+
}
7671+
76087672
fn compile_comprehension(
76097673
&mut self,
76107674
name: &str,
@@ -9184,4 +9248,18 @@ async def test():
91849248
"
91859249
));
91869250
}
9251+
9252+
#[test]
9253+
fn test_optimized_assert_preserves_nested_scope_order() {
9254+
compile_exec_optimized(
9255+
"\
9256+
class S:
9257+
def f(self, sequence):
9258+
_formats = [self._types_mapping[type(item)] for item in sequence]
9259+
_list_len = len(_formats)
9260+
assert sum(len(fmt) <= 8 for fmt in _formats) == _list_len
9261+
_recreation_codes = [self._extract_recreation_code(item) for item in sequence]
9262+
",
9263+
);
9264+
}
91879265
}

0 commit comments

Comments
 (0)