Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions Lib/test/test_future_stmt/test_future.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,9 +459,6 @@ def test_infinity_numbers(self):
self.assertAnnotationEqual("('inf', 1e1000, 'infxxx', 1e1000j)", expected=f"('inf', {inf}, 'infxxx', {infj})")
self.assertAnnotationEqual("(1e1000, (1e1000j,))", expected=f"({inf}, ({infj},))")

# TODO: RUSTPYTHON
# AssertionError: SyntaxError not raised
@unittest.expectedFailure
def test_annotation_with_complex_target(self):
with self.assertRaises(SyntaxError):
exec(
Expand Down
98 changes: 48 additions & 50 deletions crates/codegen/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,6 @@ enum NameUsage {
Store,
Delete,
}

fn is_forbidden_name(name: &str) -> bool {
// See https://docs.python.org/3/library/constants.html#built-in-constants
const BUILTIN_CONSTANTS: &[&str] = &["__debug__"];

BUILTIN_CONSTANTS.contains(&name)
}

/// Main structure holding the state of compilation.
struct Compiler {
code_stack: Vec<ir::CodeInfo>,
Expand All @@ -130,10 +122,6 @@ struct Compiler {
ctx: CompileContext,
opts: CompileOpts,
in_annotation: bool,
// PEP 649: Track if we're inside a conditional block (if/for/while/etc.)
in_conditional_block: bool,
// PEP 649: Next index for conditional annotation tracking
next_conditional_annotation_index: u32,
}

enum DoneWithFuture {
Expand Down Expand Up @@ -424,6 +412,8 @@ impl Compiler {
in_inlined_comp: false,
fblock: Vec::with_capacity(MAXBLOCKS),
symbol_table_index: 0, // Module is always the first symbol table
in_conditional_block: 0,
next_conditional_annotation_index: 0,
};
Self {
code_stack: vec![module_code],
Expand All @@ -441,8 +431,6 @@ impl Compiler {
},
opts,
in_annotation: false,
in_conditional_block: false,
next_conditional_annotation_index: 0,
}
}

Expand Down Expand Up @@ -1045,6 +1033,8 @@ impl Compiler {
in_inlined_comp: false,
fblock: Vec::with_capacity(MAXBLOCKS),
symbol_table_index: key,
in_conditional_block: 0,
next_conditional_annotation_index: 0,
};

// Push the old compiler unit on the stack (like PyCapsule)
Expand Down Expand Up @@ -1525,11 +1515,7 @@ impl Compiler {
self._name_inner(name, |i| &mut i.metadata.names)
}
fn varname(&mut self, name: &str) -> CompileResult<bytecode::NameIdx> {
if Self::is_forbidden_arg_name(name) {
return Err(self.error(CodegenErrorType::SyntaxError(format!(
"cannot assign to {name}",
))));
}
// Note: __debug__ checks are now handled in symboltable phase
Ok(self._name_inner(name, |i| &mut i.metadata.varnames))
}
fn _name_inner(
Expand Down Expand Up @@ -1814,15 +1800,6 @@ impl Compiler {
symboltable::mangle_name(private, name)
}

fn check_forbidden_name(&mut self, name: &str, usage: NameUsage) -> CompileResult<()> {
let msg = match usage {
NameUsage::Store if is_forbidden_name(name) => "cannot assign to",
NameUsage::Delete if is_forbidden_name(name) => "cannot delete",
_ => return Ok(()),
};
Err(self.error(CodegenErrorType::SyntaxError(format!("{msg} {name}"))))
}

// = compiler_nameop
fn compile_name(&mut self, name: &str, usage: NameUsage) -> CompileResult<()> {
enum NameOp {
Expand All @@ -1834,7 +1811,6 @@ impl Compiler {
}

let name = self.mangle(name);
self.check_forbidden_name(&name, usage)?;

// Special handling for __debug__
if NameUsage::Load == usage && name == "__debug__" {
Expand Down Expand Up @@ -2135,6 +2111,7 @@ impl Compiler {
elif_else_clauses,
..
}) => {
self.enter_conditional_block();
match elif_else_clauses.as_slice() {
// Only if
[] => {
Expand Down Expand Up @@ -2182,6 +2159,7 @@ impl Compiler {
self.switch_to_block(after_block);
}
}
self.leave_conditional_block();
}
Stmt::While(StmtWhile {
test, body, orelse, ..
Expand Down Expand Up @@ -2228,11 +2206,13 @@ impl Compiler {
is_star,
..
}) => {
self.enter_conditional_block();
if *is_star {
self.compile_try_star_except(body, handlers, orelse, finalbody)?
} else {
self.compile_try_statement(body, handlers, orelse, finalbody)?
}
self.leave_conditional_block();
}
Stmt::FunctionDef(StmtFunctionDef {
name,
Expand Down Expand Up @@ -2432,7 +2412,6 @@ impl Compiler {
match &expression {
Expr::Name(ExprName { id, .. }) => self.compile_name(id.as_str(), NameUsage::Delete)?,
Expr::Attribute(ExprAttribute { value, attr, .. }) => {
self.check_forbidden_name(attr.as_str(), NameUsage::Delete)?;
self.compile_expression(value)?;
let idx = self.name(attr.as_str());
emit!(self, Instruction::DeleteAttr { idx });
Expand Down Expand Up @@ -3451,10 +3430,6 @@ impl Compiler {
Ok(())
}

fn is_forbidden_arg_name(name: &str) -> bool {
is_forbidden_name(name)
}

/// Compile default arguments
// = compiler_default_arguments
fn compile_default_arguments(
Expand Down Expand Up @@ -4485,6 +4460,8 @@ impl Compiler {
}

fn compile_while(&mut self, test: &Expr, body: &[Stmt], orelse: &[Stmt]) -> CompileResult<()> {
self.enter_conditional_block();

let while_block = self.new_block();
let else_block = self.new_block();
let after_block = self.new_block();
Expand Down Expand Up @@ -4513,6 +4490,8 @@ impl Compiler {
// Note: PopBlock is no longer emitted for loops
self.compile_statements(orelse)?;
self.switch_to_block(after_block);

self.leave_conditional_block();
Ok(())
}

Expand All @@ -4522,6 +4501,8 @@ impl Compiler {
body: &[Stmt],
is_async: bool,
) -> CompileResult<()> {
self.enter_conditional_block();

// Python 3.12+ style with statement:
//
// BEFORE_WITH # TOS: ctx_mgr -> [__exit__, __enter__ result]
Expand Down Expand Up @@ -4734,6 +4715,7 @@ impl Compiler {
// ===== After block =====
self.switch_to_block(after_block);

self.leave_conditional_block();
Ok(())
}

Expand All @@ -4745,6 +4727,8 @@ impl Compiler {
orelse: &[Stmt],
is_async: bool,
) -> CompileResult<()> {
self.enter_conditional_block();

// Start loop
let for_block = self.new_block();
let else_block = self.new_block();
Expand Down Expand Up @@ -4812,6 +4796,7 @@ impl Compiler {

self.switch_to_block(after_block);

self.leave_conditional_block();
Ok(())
}

Expand Down Expand Up @@ -5784,8 +5769,10 @@ impl Compiler {
}

fn compile_match(&mut self, subject: &Expr, cases: &[MatchCase]) -> CompileResult<()> {
self.enter_conditional_block();
let mut pattern_context = PatternContext::new();
self.compile_match_inner(subject, cases, &mut pattern_context)?;
self.leave_conditional_block();
Ok(())
}

Expand Down Expand Up @@ -5946,16 +5933,22 @@ impl Compiler {
} else {
// PEP 649: Handle conditional annotations
if self.current_symbol_table().has_conditional_annotations {
// Determine if this annotation is conditional
let is_module = self.current_symbol_table().typ == CompilerScope::Module;
let is_conditional = is_module || self.in_conditional_block;
// Allocate an index for every annotation when has_conditional_annotations
// This keeps indices aligned with compile_module_annotate's enumeration
let code_info = self.current_code_info();
let annotation_index = code_info.next_conditional_annotation_index;
code_info.next_conditional_annotation_index += 1;

// Determine if this annotation is conditional
// Module and Class scopes both need all annotations tracked
let scope_type = self.current_symbol_table().typ;
let in_conditional_block = self.current_code_info().in_conditional_block > 0;
let is_conditional =
matches!(scope_type, CompilerScope::Module | CompilerScope::Class)
|| in_conditional_block;

// Only add to __conditional_annotations__ set if actually conditional
if is_conditional {
// Get the current annotation index and increment
let annotation_index = self.next_conditional_annotation_index;
self.next_conditional_annotation_index += 1;

// Add index to __conditional_annotations__ set
let cond_annotations_name = self.name("__conditional_annotations__");
emit!(self, Instruction::LoadName(cond_annotations_name));
self.emit_load_const(ConstantData::Integer {
Expand All @@ -5980,7 +5973,6 @@ impl Compiler {
self.compile_subscript(value, slice, *ctx)?;
}
Expr::Attribute(ExprAttribute { value, attr, .. }) => {
self.check_forbidden_name(attr.as_str(), NameUsage::Store)?;
self.compile_expression(value)?;
let idx = self.name(attr.as_str());
emit!(self, Instruction::StoreAttr { idx });
Expand Down Expand Up @@ -6075,7 +6067,6 @@ impl Compiler {
}
Expr::Attribute(ExprAttribute { value, attr, .. }) => {
let attr = attr.as_str();
self.check_forbidden_name(attr, NameUsage::Store)?;
self.compile_expression(value)?;
emit!(self, Instruction::Copy { index: 1_u32 });
let idx = self.name(attr);
Expand Down Expand Up @@ -6903,12 +6894,6 @@ impl Compiler {
let (size, unpack) = self.gather_elements(additional_positional, &arguments.args)?;
let has_double_star = arguments.keywords.iter().any(|k| k.arg.is_none());

for keyword in &arguments.keywords {
if let Some(name) = &keyword.arg {
self.check_forbidden_name(name.as_str(), NameUsage::Store)?;
}
}

if unpack || has_double_star {
// Create a tuple with positional args:
if unpack {
Expand Down Expand Up @@ -7511,6 +7496,19 @@ impl Compiler {
self.code_stack.last_mut().expect("no code on stack")
}

/// Enter a conditional block (if/for/while/match/try/with)
/// PEP 649: Track conditional annotation context
fn enter_conditional_block(&mut self) {
self.current_code_info().in_conditional_block += 1;
}

/// Leave a conditional block
fn leave_conditional_block(&mut self) {
let code_info = self.current_code_info();
debug_assert!(code_info.in_conditional_block > 0);
code_info.in_conditional_block -= 1;
}

/// Compile break or continue statement with proper fblock cleanup.
/// compiler_break, compiler_continue
/// This handles unwinding through With blocks and exception handlers.
Expand Down
10 changes: 10 additions & 0 deletions crates/codegen/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@ pub struct CodeInfo {

// Reference to the symbol table for this scope
pub symbol_table_index: usize,

// PEP 649: Track nesting depth inside conditional blocks (if/for/while/etc.)
// u_in_conditional_block
pub in_conditional_block: u32,

// PEP 649: Next index for conditional annotation tracking
// u_next_conditional_annotation_index
pub next_conditional_annotation_index: u32,
}

impl CodeInfo {
Expand All @@ -171,6 +179,8 @@ impl CodeInfo {
in_inlined_comp: _,
fblock: _,
symbol_table_index: _,
in_conditional_block: _,
next_conditional_annotation_index: _,
} = self;

let CodeUnitMetadata {
Expand Down
Loading
Loading