From 7a532850831bbf7c5bd9a55916a9fa954367116b Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 27 Jun 2026 14:09:37 +0300 Subject: [PATCH 01/12] ASSIGNED -> DEF_LOCAL --- crates/codegen/src/compile.rs | 2 +- crates/codegen/src/symboltable.rs | 20 ++++++++++---------- crates/vm/src/stdlib/_symtable.rs | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index 711ac392694..8e2e28b1baf 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -10809,7 +10809,7 @@ impl<'warnings> Compiler<'warnings> { } let is_local = sym .flags - .intersects(SymbolFlags::ASSIGNED | SymbolFlags::ITER) + .intersects(SymbolFlags::DEF_LOCAL | SymbolFlags::ITER) && !sym.flags.contains(SymbolFlags::NONLOCAL); if is_local { pushed_locals.push(name.clone()); diff --git a/crates/codegen/src/symboltable.rs b/crates/codegen/src/symboltable.rs index a78b49a6e90..79d3f773c10 100644 --- a/crates/codegen/src/symboltable.rs +++ b/crates/codegen/src/symboltable.rs @@ -290,7 +290,7 @@ bitflags! { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct SymbolFlags: u16 { const REFERENCED = 0x001; // USE - const ASSIGNED = 0x002; // DEF_LOCAL + const DEF_LOCAL = 2; const PARAMETER = 0x004; // DEF_PARAM const ANNOTATED = 0x008; // DEF_ANNOT const IMPORTED = 0x010; // DEF_IMPORT @@ -314,7 +314,7 @@ bitflags! { const COMP_ITER = 0x400; // DEF_COMP_ITER const COMP_CELL = 0x800; // DEF_COMP_CELL const TYPE_PARAM = 0x1000; // DEF_TYPE_PARAM - const BOUND = Self::ASSIGNED.bits() | Self::PARAMETER.bits() | Self::IMPORTED.bits() | Self::ITER.bits() | Self::TYPE_PARAM.bits(); + const BOUND = Self::DEF_LOCAL.bits() | Self::PARAMETER.bits() | Self::IMPORTED.bits() | Self::ITER.bits() | Self::TYPE_PARAM.bits(); } } @@ -3123,7 +3123,7 @@ impl SymbolTableBuilder { .symbols .entry(mangled.clone()) .or_insert_with(|| Symbol::new(mangled.as_str())); - symbol.flags.insert(SymbolFlags::ASSIGNED); + symbol.flags.insert(SymbolFlags::DEF_LOCAL); return Ok(()); } CompilerScope::Module => { @@ -3283,7 +3283,7 @@ impl SymbolTableBuilder { location, }); } - if flags.contains(SymbolFlags::ASSIGNED) { + if flags.contains(SymbolFlags::DEF_LOCAL) { return Err(SymbolTableError { error: format!( "name '{name}' is assigned to before global declaration" @@ -3311,7 +3311,7 @@ impl SymbolTableBuilder { location, }); } - if flags.contains(SymbolFlags::ASSIGNED) { + if flags.contains(SymbolFlags::DEF_LOCAL) { return Err(SymbolTableError { error: format!( "name '{name}' is assigned to before nonlocal declaration" @@ -3370,7 +3370,7 @@ impl SymbolTableBuilder { flags.insert(SymbolFlags::NONLOCAL); } SymbolUsage::Imported => { - flags.insert(SymbolFlags::ASSIGNED | SymbolFlags::IMPORTED); + flags.insert(SymbolFlags::DEF_LOCAL | SymbolFlags::IMPORTED); } SymbolUsage::Parameter => { flags.insert(SymbolFlags::PARAMETER); @@ -3389,13 +3389,13 @@ impl SymbolTableBuilder { } } SymbolUsage::AnnotationAssigned => { - flags.insert(SymbolFlags::ASSIGNED | SymbolFlags::ANNOTATED); + flags.insert(SymbolFlags::DEF_LOCAL | SymbolFlags::ANNOTATED); } SymbolUsage::Assigned => { - flags.insert(SymbolFlags::ASSIGNED); + flags.insert(SymbolFlags::DEF_LOCAL); } SymbolUsage::AssignedNamedExprInComprehension => { - flags.insert(SymbolFlags::ASSIGNED | SymbolFlags::ASSIGNED_IN_COMPREHENSION); + flags.insert(SymbolFlags::DEF_LOCAL | SymbolFlags::ASSIGNED_IN_COMPREHENSION); } SymbolUsage::Global => { symbol.scope = SymbolScope::GlobalExplicit; @@ -3408,7 +3408,7 @@ impl SymbolTableBuilder { flags.insert(SymbolFlags::ITER | SymbolFlags::COMP_ITER); } SymbolUsage::TypeParam => { - flags.insert(SymbolFlags::ASSIGNED | SymbolFlags::TYPE_PARAM); + flags.insert(SymbolFlags::DEF_LOCAL | SymbolFlags::TYPE_PARAM); } } diff --git a/crates/vm/src/stdlib/_symtable.rs b/crates/vm/src/stdlib/_symtable.rs index adcddeacc1f..9b275b5baca 100644 --- a/crates/vm/src/stdlib/_symtable.rs +++ b/crates/vm/src/stdlib/_symtable.rs @@ -284,7 +284,7 @@ mod _symtable { #[pymethod] const fn is_assigned(&self) -> bool { - self.symbol.flags.contains(SymbolFlags::ASSIGNED) + self.symbol.flags.contains(SymbolFlags::DEF_LOCAL) } #[pymethod] From d84717a81210966b909a8abfd74a2a42c6d07e9e Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 27 Jun 2026 14:23:00 +0300 Subject: [PATCH 02/12] GLOBAL -> DEF_GLOBAL --- crates/codegen/src/compile.rs | 2 +- crates/codegen/src/symboltable.rs | 23 ++++++++++++----------- crates/vm/src/stdlib/_symtable.rs | 4 ++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index 8e2e28b1baf..dadd379888c 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -3048,7 +3048,7 @@ impl<'warnings> Compiler<'warnings> { .rev() .find(|table| table.typ == CompilerScope::Class) .and_then(|table| table.lookup(name.as_ref())) - .is_some_and(|symbol| symbol.flags.contains(SymbolFlags::GLOBAL)); + .is_some_and(|symbol| symbol.flags.contains(SymbolFlags::DEF_GLOBAL)); ( symbol.map(|s| s.scope), diff --git a/crates/codegen/src/symboltable.rs b/crates/codegen/src/symboltable.rs index 79d3f773c10..9c76900125f 100644 --- a/crates/codegen/src/symboltable.rs +++ b/crates/codegen/src/symboltable.rs @@ -289,6 +289,7 @@ impl From for i32 { bitflags! { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct SymbolFlags: u16 { + const DEF_GLOBAL = 0x200; const REFERENCED = 0x001; // USE const DEF_LOCAL = 2; const PARAMETER = 0x004; // DEF_PARAM @@ -310,7 +311,6 @@ bitflags! { /// return x // is_free_class /// ``` const FREE_CLASS = 0x100; // DEF_FREE_CLASS - const GLOBAL = 0x200; // DEF_GLOBAL const COMP_ITER = 0x400; // DEF_COMP_ITER const COMP_CELL = 0x800; // DEF_COMP_CELL const TYPE_PARAM = 0x1000; // DEF_TYPE_PARAM @@ -846,7 +846,7 @@ impl SymbolTableAnalyzer { } else if let Some(scope) = class_entry .and_then(|class_symbols| class_symbols.get(&symbol.name)) .and_then(|class_sym| { - if class_sym.flags.contains(SymbolFlags::GLOBAL) { + if class_sym.flags.contains(SymbolFlags::DEF_GLOBAL) { Some(SymbolScope::GlobalExplicit) } else if class_sym.is_bound() && class_sym.scope != SymbolScope::Free { // If name is bound in enclosing class, use GlobalImplicit @@ -1833,9 +1833,10 @@ impl SymbolTableBuilder { .last() .is_some_and(|table| table.typ != CompilerScope::Module) && let Some(flags) = existing_flags - && flags.intersects(SymbolFlags::GLOBAL | SymbolFlags::NONLOCAL) + && flags + .intersects(SymbolFlags::DEF_GLOBAL | SymbolFlags::NONLOCAL) { - let usage = if flags.contains(SymbolFlags::GLOBAL) { + let usage = if flags.contains(SymbolFlags::DEF_GLOBAL) { "global" } else { "nonlocal" @@ -3105,14 +3106,14 @@ impl SymbolTableBuilder { let parent_is_global = self.tables[table_idx] .symbols .get(mangled.as_str()) - .is_some_and(|symbol| symbol.flags.contains(SymbolFlags::GLOBAL)); + .is_some_and(|symbol| symbol.flags.contains(SymbolFlags::DEF_GLOBAL)); let current = self.tables.last_mut().unwrap(); let current_symbol = current .symbols .entry(mangled.clone()) .or_insert_with(|| Symbol::new(mangled.as_str())); if parent_is_global { - current_symbol.flags.insert(SymbolFlags::GLOBAL); + current_symbol.flags.insert(SymbolFlags::DEF_GLOBAL); current_symbol.scope = SymbolScope::GlobalExplicit; } else { current_symbol.flags.insert(SymbolFlags::NONLOCAL); @@ -3132,14 +3133,14 @@ impl SymbolTableBuilder { .symbols .entry(mangled.clone()) .or_insert_with(|| Symbol::new(mangled.as_str())); - current_symbol.flags.insert(SymbolFlags::GLOBAL); + current_symbol.flags.insert(SymbolFlags::DEF_GLOBAL); current_symbol.scope = SymbolScope::GlobalExplicit; let symbol = self.tables[table_idx] .symbols .entry(mangled.clone()) .or_insert_with(|| Symbol::new(mangled.as_str())); - symbol.flags.insert(SymbolFlags::GLOBAL); + symbol.flags.insert(SymbolFlags::DEF_GLOBAL); symbol.scope = SymbolScope::GlobalExplicit; return Ok(()); } @@ -3322,9 +3323,9 @@ impl SymbolTableBuilder { } SymbolUsage::AnnotationAssigned if current_scope != CompilerScope::Module - && flags.intersects(SymbolFlags::GLOBAL | SymbolFlags::NONLOCAL) => + && flags.intersects(SymbolFlags::DEF_GLOBAL | SymbolFlags::NONLOCAL) => { - let usage = if flags.contains(SymbolFlags::GLOBAL) { + let usage = if flags.contains(SymbolFlags::DEF_GLOBAL) { "global" } else { "nonlocal" @@ -3399,7 +3400,7 @@ impl SymbolTableBuilder { } SymbolUsage::Global => { symbol.scope = SymbolScope::GlobalExplicit; - flags.insert(SymbolFlags::GLOBAL); + flags.insert(SymbolFlags::DEF_GLOBAL); } SymbolUsage::Used => { flags.insert(SymbolFlags::REFERENCED); diff --git a/crates/vm/src/stdlib/_symtable.rs b/crates/vm/src/stdlib/_symtable.rs index 9b275b5baca..f5e890e509d 100644 --- a/crates/vm/src/stdlib/_symtable.rs +++ b/crates/vm/src/stdlib/_symtable.rs @@ -20,10 +20,10 @@ mod _symtable { // https://github.com/python/cpython/blob/6cb20a219a860eaf687b2d968b41c480c7461909/Include/internal/pycore_symtable.h#L156 #[pyattr] - pub(super) const DEF_GLOBAL: i32 = 1; + pub(super) const DEF_GLOBAL: i32 = SymbolFlags::DEF_GLOBAL.bits() as i32; #[pyattr] - pub(super) const DEF_LOCAL: i32 = 2; + pub(super) const DEF_LOCAL: i32 = SymbolFlags::DEF_LOCAL.bits() as i32; #[pyattr] pub(super) const DEF_PARAM: i32 = 2 << 1; From a75c55bf2ce0f90b239f6b8aba0ea28f5e42efc9 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 27 Jun 2026 14:25:54 +0300 Subject: [PATCH 03/12] REFERENCED -> USE --- crates/codegen/src/symboltable.rs | 21 +++++++++++---------- crates/vm/src/stdlib/_symtable.rs | 4 ++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/crates/codegen/src/symboltable.rs b/crates/codegen/src/symboltable.rs index 9c76900125f..a34893c2f1e 100644 --- a/crates/codegen/src/symboltable.rs +++ b/crates/codegen/src/symboltable.rs @@ -161,7 +161,7 @@ impl SymbolTable { .or_insert_with(|| Symbol::new(name)); symbol .flags - .insert(SymbolFlags::PARAMETER | SymbolFlags::REFERENCED); + .insert(SymbolFlags::PARAMETER | SymbolFlags::USE); if !self.varnames.iter().any(|varname| varname == name) { self.varnames.push(name.to_owned()); } @@ -290,8 +290,9 @@ bitflags! { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct SymbolFlags: u16 { const DEF_GLOBAL = 0x200; - const REFERENCED = 0x001; // USE const DEF_LOCAL = 2; + const USE = 0x001; + const PARAMETER = 0x004; // DEF_PARAM const ANNOTATED = 0x008; // DEF_ANNOT const IMPORTED = 0x010; // DEF_IMPORT @@ -1307,7 +1308,7 @@ impl SymbolTableBuilder { symbol.scope = SymbolScope::Free; symbol .flags - .insert(SymbolFlags::REFERENCED | SymbolFlags::FREE_CLASS); + .insert(SymbolFlags::USE | SymbolFlags::FREE_CLASS); } fn add_conditional_annotations_freevar(&mut self) { @@ -1320,7 +1321,7 @@ impl SymbolTableBuilder { symbol.scope = SymbolScope::Free; symbol .flags - .insert(SymbolFlags::REFERENCED | SymbolFlags::FREE_CLASS); + .insert(SymbolFlags::USE | SymbolFlags::FREE_CLASS); } /// Walk up the scope chain to determine if we're inside an async function. @@ -3272,7 +3273,7 @@ impl SymbolTableBuilder { location, }); } - if flags.contains(SymbolFlags::REFERENCED) { + if flags.contains(SymbolFlags::USE) { return Err(SymbolTableError { error: format!("name '{name}' is used prior to global declaration"), location, @@ -3300,7 +3301,7 @@ impl SymbolTableBuilder { location, }); } - if flags.contains(SymbolFlags::REFERENCED) { + if flags.contains(SymbolFlags::USE) { return Err(SymbolTableError { error: format!("name '{name}' is used prior to nonlocal declaration"), location, @@ -3403,7 +3404,7 @@ impl SymbolTableBuilder { flags.insert(SymbolFlags::DEF_GLOBAL); } SymbolUsage::Used => { - flags.insert(SymbolFlags::REFERENCED); + flags.insert(SymbolFlags::USE); } SymbolUsage::Iter => { flags.insert(SymbolFlags::ITER | SymbolFlags::COMP_ITER); @@ -3621,7 +3622,7 @@ mod tests { assert!( format .flags - .contains(SymbolFlags::PARAMETER | SymbolFlags::REFERENCED), + .contains(SymbolFlags::PARAMETER | SymbolFlags::USE), "CPython symtable_enter_block() adds both DEF_PARAM and USE for annotation-like .format" ); @@ -3637,7 +3638,7 @@ mod tests { assert!( format .flags - .contains(SymbolFlags::PARAMETER | SymbolFlags::REFERENCED), + .contains(SymbolFlags::PARAMETER | SymbolFlags::USE), "CPython TypeAliasBlock .format has DEF_PARAM | USE" ); @@ -3658,7 +3659,7 @@ mod tests { assert!( format .flags - .contains(SymbolFlags::PARAMETER | SymbolFlags::REFERENCED), + .contains(SymbolFlags::PARAMETER | SymbolFlags::USE), "CPython TypeVariableBlock .format has DEF_PARAM | USE" ); } diff --git a/crates/vm/src/stdlib/_symtable.rs b/crates/vm/src/stdlib/_symtable.rs index f5e890e509d..cf3187313f5 100644 --- a/crates/vm/src/stdlib/_symtable.rs +++ b/crates/vm/src/stdlib/_symtable.rs @@ -32,7 +32,7 @@ mod _symtable { pub(super) const DEF_NONLOCAL: i32 = 2 << 2; #[pyattr] - pub(super) const USE: i32 = 2 << 3; + pub(super) const USE: i32 = SymbolFlags::USE.bits() as i32; #[pyattr] pub(super) const DEF_FREE: i32 = 2 << 4; @@ -279,7 +279,7 @@ mod _symtable { #[pymethod] const fn is_referenced(&self) -> bool { - self.symbol.flags.contains(SymbolFlags::REFERENCED) + self.symbol.flags.contains(SymbolFlags::USE) } #[pymethod] From 70e834ce39fe79158c6cfb2ffde54d13298fd237 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 27 Jun 2026 14:29:33 +0300 Subject: [PATCH 04/12] PARAMETER -> DEF_PARAM --- crates/codegen/src/compile.rs | 4 ++-- crates/codegen/src/symboltable.rs | 24 ++++++++++++------------ crates/vm/src/stdlib/_symtable.rs | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index dadd379888c..76a231a7d7c 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -10804,7 +10804,7 @@ impl<'warnings> Compiler<'warnings> { let mut pushed_locals: Vec = Vec::new(); let mut fast_hidden_locals: Vec = Vec::new(); for (name, sym) in &comp_table.symbols { - if sym.flags.contains(SymbolFlags::PARAMETER) { + if sym.flags.contains(SymbolFlags::DEF_PARAM) { continue; // skip .0 } let is_local = sym @@ -10824,7 +10824,7 @@ impl<'warnings> Compiler<'warnings> { // module/class scopes, also enable temporary fast locals for // comprehension-bound names only. for (name, comp_sym) in &comp_table.symbols { - if comp_sym.flags.contains(SymbolFlags::PARAMETER) { + if comp_sym.flags.contains(SymbolFlags::DEF_PARAM) { continue; // skip .0 } let comp_scope = comp_sym.scope; diff --git a/crates/codegen/src/symboltable.rs b/crates/codegen/src/symboltable.rs index a34893c2f1e..dacae811f09 100644 --- a/crates/codegen/src/symboltable.rs +++ b/crates/codegen/src/symboltable.rs @@ -161,7 +161,7 @@ impl SymbolTable { .or_insert_with(|| Symbol::new(name)); symbol .flags - .insert(SymbolFlags::PARAMETER | SymbolFlags::USE); + .insert(SymbolFlags::DEF_PARAM | SymbolFlags::USE); if !self.varnames.iter().any(|varname| varname == name) { self.varnames.push(name.to_owned()); } @@ -291,9 +291,9 @@ bitflags! { pub struct SymbolFlags: u16 { const DEF_GLOBAL = 0x200; const DEF_LOCAL = 2; + const DEF_PARAM = 0x004; const USE = 0x001; - const PARAMETER = 0x004; // DEF_PARAM const ANNOTATED = 0x008; // DEF_ANNOT const IMPORTED = 0x010; // DEF_IMPORT const NONLOCAL = 0x020; // DEF_NONLOCAL @@ -315,7 +315,7 @@ bitflags! { const COMP_ITER = 0x400; // DEF_COMP_ITER const COMP_CELL = 0x800; // DEF_COMP_CELL const TYPE_PARAM = 0x1000; // DEF_TYPE_PARAM - const BOUND = Self::DEF_LOCAL.bits() | Self::PARAMETER.bits() | Self::IMPORTED.bits() | Self::ITER.bits() | Self::TYPE_PARAM.bits(); + const BOUND = Self::DEF_LOCAL.bits() | Self::DEF_PARAM.bits() | Self::IMPORTED.bits() | Self::ITER.bits() | Self::TYPE_PARAM.bits(); } } @@ -444,7 +444,7 @@ fn inline_comprehension( let mut removed_class_implicits = IndexSet::default(); for (name, sub_symbol) in &comp.symbols { // Skip the .0 parameter - if sub_symbol.flags.contains(SymbolFlags::PARAMETER) { + if sub_symbol.flags.contains(SymbolFlags::DEF_PARAM) { continue; } @@ -3250,7 +3250,7 @@ impl SymbolTableBuilder { if matches!( role, SymbolUsage::Parameter | SymbolUsage::AnnotationParameter - ) && flags.contains(SymbolFlags::PARAMETER) + ) && flags.contains(SymbolFlags::DEF_PARAM) { return Err(SymbolTableError { error: format!("duplicate argument '{original_name}' in function definition"), @@ -3267,7 +3267,7 @@ impl SymbolTableBuilder { } match role { SymbolUsage::Global if !symbol.is_global() => { - if flags.contains(SymbolFlags::PARAMETER) { + if flags.contains(SymbolFlags::DEF_PARAM) { return Err(SymbolTableError { error: format!("name '{name}' is parameter and global"), location, @@ -3295,7 +3295,7 @@ impl SymbolTableBuilder { } } SymbolUsage::Nonlocal => { - if flags.contains(SymbolFlags::PARAMETER) { + if flags.contains(SymbolFlags::DEF_PARAM) { return Err(SymbolTableError { error: format!("name '{name}' is parameter and nonlocal"), location, @@ -3375,7 +3375,7 @@ impl SymbolTableBuilder { flags.insert(SymbolFlags::DEF_LOCAL | SymbolFlags::IMPORTED); } SymbolUsage::Parameter => { - flags.insert(SymbolFlags::PARAMETER); + flags.insert(SymbolFlags::DEF_PARAM); // Parameters are always added to varnames first let name_str = symbol.name.clone(); if !self.current_varnames.contains(&name_str) { @@ -3383,7 +3383,7 @@ impl SymbolTableBuilder { } } SymbolUsage::AnnotationParameter => { - flags.insert(SymbolFlags::PARAMETER | SymbolFlags::ANNOTATED); + flags.insert(SymbolFlags::DEF_PARAM | SymbolFlags::ANNOTATED); // Annotated parameters are also added to varnames let name_str = symbol.name.clone(); if !self.current_varnames.contains(&name_str) { @@ -3622,7 +3622,7 @@ mod tests { assert!( format .flags - .contains(SymbolFlags::PARAMETER | SymbolFlags::USE), + .contains(SymbolFlags::DEF_PARAM | SymbolFlags::USE), "CPython symtable_enter_block() adds both DEF_PARAM and USE for annotation-like .format" ); @@ -3638,7 +3638,7 @@ mod tests { assert!( format .flags - .contains(SymbolFlags::PARAMETER | SymbolFlags::USE), + .contains(SymbolFlags::DEF_PARAM | SymbolFlags::USE), "CPython TypeAliasBlock .format has DEF_PARAM | USE" ); @@ -3659,7 +3659,7 @@ mod tests { assert!( format .flags - .contains(SymbolFlags::PARAMETER | SymbolFlags::USE), + .contains(SymbolFlags::DEF_PARAM | SymbolFlags::USE), "CPython TypeVariableBlock .format has DEF_PARAM | USE" ); } diff --git a/crates/vm/src/stdlib/_symtable.rs b/crates/vm/src/stdlib/_symtable.rs index cf3187313f5..566c2d9371d 100644 --- a/crates/vm/src/stdlib/_symtable.rs +++ b/crates/vm/src/stdlib/_symtable.rs @@ -26,7 +26,7 @@ mod _symtable { pub(super) const DEF_LOCAL: i32 = SymbolFlags::DEF_LOCAL.bits() as i32; #[pyattr] - pub(super) const DEF_PARAM: i32 = 2 << 1; + pub(super) const DEF_PARAM: i32 = SymbolFlags::DEF_PARAM.bits() as i32; #[pyattr] pub(super) const DEF_NONLOCAL: i32 = 2 << 2; @@ -289,7 +289,7 @@ mod _symtable { #[pymethod] const fn is_parameter(&self) -> bool { - self.symbol.flags.contains(SymbolFlags::PARAMETER) + self.symbol.flags.contains(SymbolFlags::DEF_PARAM) } #[pymethod] From fd2a934d1adc660a74e862340b50f19bf52382aa Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 27 Jun 2026 14:36:08 +0300 Subject: [PATCH 05/12] expose symbol flags directly as a pyattr --- crates/vm/src/stdlib/_symtable.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/crates/vm/src/stdlib/_symtable.rs b/crates/vm/src/stdlib/_symtable.rs index 566c2d9371d..c0113661bc5 100644 --- a/crates/vm/src/stdlib/_symtable.rs +++ b/crates/vm/src/stdlib/_symtable.rs @@ -29,31 +29,28 @@ mod _symtable { pub(super) const DEF_PARAM: i32 = SymbolFlags::DEF_PARAM.bits() as i32; #[pyattr] - pub(super) const DEF_NONLOCAL: i32 = 2 << 2; + pub(super) const DEF_NONLOCAL: i32 = SymbolFlags::NONLOCAL.bits() as i32; #[pyattr] pub(super) const USE: i32 = SymbolFlags::USE.bits() as i32; #[pyattr] - pub(super) const DEF_FREE: i32 = 2 << 4; + pub(super) const DEF_FREE_CLASS: i32 = SymbolFlags::FREE_CLASS.bits() as i32; #[pyattr] - pub(super) const DEF_FREE_CLASS: i32 = 2 << 5; + pub(super) const DEF_IMPORT: i32 = SymbolFlags::IMPORTED.bits() as i32; #[pyattr] - pub(super) const DEF_IMPORT: i32 = 2 << 6; + pub(super) const DEF_ANNOT: i32 = SymbolFlags::ANNOTATED.bits() as i32; #[pyattr] - pub(super) const DEF_ANNOT: i32 = 2 << 7; + pub(super) const DEF_COMP_ITER: i32 = SymbolFlags::COMP_ITER.bits() as i32; #[pyattr] - pub(super) const DEF_COMP_ITER: i32 = 2 << 8; + pub(super) const DEF_TYPE_PARAM: i32 = SymbolFlags::TYPE_PARAM.bits() as i32; #[pyattr] - pub(super) const DEF_TYPE_PARAM: i32 = 2 << 9; - - #[pyattr] - pub(super) const DEF_COMP_CELL: i32 = 2 << 10; + pub(super) const DEF_COMP_CELL: i32 = SymbolFlags::COMP_CELL.bits() as i32; #[pyattr] pub(super) const DEF_BOUND: i32 = DEF_LOCAL | DEF_PARAM | DEF_IMPORT; From 5d36d384f91eeee8ed3392ccaf38d6fb95bc0950 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 27 Jun 2026 14:39:25 +0300 Subject: [PATCH 06/12] FREE_CLASS -> DEF_FREE_CLASS --- crates/codegen/src/compile.rs | 10 ++++----- crates/codegen/src/symboltable.rs | 37 ++++++++++++++++++------------- crates/vm/src/stdlib/_symtable.rs | 2 +- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index 76a231a7d7c..454de6e10ab 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -1667,10 +1667,10 @@ impl<'warnings> Compiler<'warnings> { } // Check if __class__ is available as a cell/free variable - // The scope must be Free (from enclosing class) or have FREE_CLASS flag + // The scope must be Free (from enclosing class) or have DEF_FREE_CLASS flag if let Some(symbol) = table.lookup("__class__") { if symbol.scope != SymbolScope::Free - && !symbol.flags.contains(SymbolFlags::FREE_CLASS) + && !symbol.flags.contains(SymbolFlags::DEF_FREE_CLASS) { return None; } @@ -1836,9 +1836,9 @@ impl<'warnings> Compiler<'warnings> { .filter(|(_, s)| { s.scope == SymbolScope::Free || (scope_type != CompilerScope::Class - && s.flags.contains(SymbolFlags::FREE_CLASS)) + && s.flags.contains(SymbolFlags::DEF_FREE_CLASS)) || (scope_type == CompilerScope::Class - && s.flags.contains(SymbolFlags::FREE_CLASS) + && s.flags.contains(SymbolFlags::DEF_FREE_CLASS) && self.has_enclosing_non_module_code_scope()) }) .filter(|(name, symbol)| { @@ -5513,7 +5513,7 @@ impl<'warnings> Compiler<'warnings> { Some(symbol) => match symbol.scope { SymbolScope::Cell => Ok(SymbolScope::Cell), SymbolScope::Free => Ok(SymbolScope::Free), - _ if symbol.flags.contains(SymbolFlags::FREE_CLASS) => Ok(SymbolScope::Free), + _ if symbol.flags.contains(SymbolFlags::DEF_FREE_CLASS) => Ok(SymbolScope::Free), _ => Err(CodegenErrorType::SyntaxError(format!( "get_ref_type: invalid scope for '{name}'" ))), diff --git a/crates/codegen/src/symboltable.rs b/crates/codegen/src/symboltable.rs index dacae811f09..b7a842a1c9d 100644 --- a/crates/codegen/src/symboltable.rs +++ b/crates/codegen/src/symboltable.rs @@ -294,6 +294,18 @@ bitflags! { const DEF_PARAM = 0x004; const USE = 0x001; + /// indicates that the symbol is a free variable in a class method from the scope that the + /// class is defined in, e.g.: + /// ```python + /// def foo(x): + /// class A: + /// def method(self): + /// return x // is_free_class + /// ``` + const DEF_FREE_CLASS = 0x100; + + + const ANNOTATED = 0x008; // DEF_ANNOT const IMPORTED = 0x010; // DEF_IMPORT const NONLOCAL = 0x020; // DEF_NONLOCAL @@ -303,15 +315,6 @@ bitflags! { // indicates that the symbol is used a bound iterator variable. We distinguish this case // from normal assignment to detect disallowed re-assignment to iterator variables. const ITER = 0x080; - /// indicates that the symbol is a free variable in a class method from the scope that the - /// class is defined in, e.g.: - /// ```python - /// def foo(x): - /// class A: - /// def method(self): - /// return x // is_free_class - /// ``` - const FREE_CLASS = 0x100; // DEF_FREE_CLASS const COMP_ITER = 0x400; // DEF_COMP_ITER const COMP_CELL = 0x800; // DEF_COMP_CELL const TYPE_PARAM = 0x1000; // DEF_TYPE_PARAM @@ -731,7 +734,9 @@ impl SymbolTableAnalyzer { } // Collect free variables from this scope - if symbol.scope == SymbolScope::Free || symbol.flags.contains(SymbolFlags::FREE_CLASS) { + if symbol.scope == SymbolScope::Free + || symbol.flags.contains(SymbolFlags::DEF_FREE_CLASS) + { newfree.insert(symbol.name.clone()); } } @@ -764,7 +769,7 @@ impl SymbolTableAnalyzer { if symbol_table.typ == CompilerScope::Class || symbol_table.can_see_class_scope { for name in &newfree { if let Some(symbol) = symbol_table.symbols.get_mut(name) { - symbol.flags.insert(SymbolFlags::FREE_CLASS); + symbol.flags.insert(SymbolFlags::DEF_FREE_CLASS); } } } @@ -948,10 +953,10 @@ impl SymbolTableAnalyzer { for (table, typ, _skip) in self.tables.iter_mut().rev().take(decl_depth) { if let CompilerScope::Class = typ { if let Some(free_class) = table.get_mut(name) { - free_class.flags.insert(SymbolFlags::FREE_CLASS) + free_class.flags.insert(SymbolFlags::DEF_FREE_CLASS) } else { let mut symbol = Symbol::new(name); - symbol.flags.insert(SymbolFlags::FREE_CLASS); + symbol.flags.insert(SymbolFlags::DEF_FREE_CLASS); symbol.scope = SymbolScope::Free; table.insert(name.to_owned(), symbol); } @@ -990,7 +995,7 @@ impl SymbolTableAnalyzer { } let sym = st.symbols.get(name)?; if sym.scope == SymbolScope::Free - || (sym.flags.contains(SymbolFlags::FREE_CLASS) + || (sym.flags.contains(SymbolFlags::DEF_FREE_CLASS) && !matches!(st_typ, CompilerScope::Module)) { if st_typ == CompilerScope::Class && name != "__class__" { @@ -1308,7 +1313,7 @@ impl SymbolTableBuilder { symbol.scope = SymbolScope::Free; symbol .flags - .insert(SymbolFlags::USE | SymbolFlags::FREE_CLASS); + .insert(SymbolFlags::USE | SymbolFlags::DEF_FREE_CLASS); } fn add_conditional_annotations_freevar(&mut self) { @@ -1321,7 +1326,7 @@ impl SymbolTableBuilder { symbol.scope = SymbolScope::Free; symbol .flags - .insert(SymbolFlags::USE | SymbolFlags::FREE_CLASS); + .insert(SymbolFlags::USE | SymbolFlags::DEF_FREE_CLASS); } /// Walk up the scope chain to determine if we're inside an async function. diff --git a/crates/vm/src/stdlib/_symtable.rs b/crates/vm/src/stdlib/_symtable.rs index c0113661bc5..d9d23395b4e 100644 --- a/crates/vm/src/stdlib/_symtable.rs +++ b/crates/vm/src/stdlib/_symtable.rs @@ -35,7 +35,7 @@ mod _symtable { pub(super) const USE: i32 = SymbolFlags::USE.bits() as i32; #[pyattr] - pub(super) const DEF_FREE_CLASS: i32 = SymbolFlags::FREE_CLASS.bits() as i32; + pub(super) const DEF_FREE_CLASS: i32 = SymbolFlags::DEF_FREE_CLASS.bits() as i32; #[pyattr] pub(super) const DEF_IMPORT: i32 = SymbolFlags::IMPORTED.bits() as i32; From c3b424c6184d5e40e17dcdd8f5c921354358b258 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 27 Jun 2026 14:41:53 +0300 Subject: [PATCH 07/12] IMPORTED -> DEF_IMPORT --- crates/codegen/src/compile.rs | 2 +- crates/codegen/src/symboltable.rs | 6 +++--- crates/vm/src/stdlib/_symtable.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index 454de6e10ab..d5bb48e768a 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -1389,7 +1389,7 @@ impl<'warnings> Compiler<'warnings> { self.symbol_table_stack .first() .and_then(|table| table.symbols.get(name)) - .is_some_and(|sym| sym.flags.contains(SymbolFlags::IMPORTED)) + .is_some_and(|sym| sym.flags.contains(SymbolFlags::DEF_IMPORT)) } /// Get the cell-relative index of a free variable. diff --git a/crates/codegen/src/symboltable.rs b/crates/codegen/src/symboltable.rs index b7a842a1c9d..6b0657b68d2 100644 --- a/crates/codegen/src/symboltable.rs +++ b/crates/codegen/src/symboltable.rs @@ -303,11 +303,11 @@ bitflags! { /// return x // is_free_class /// ``` const DEF_FREE_CLASS = 0x100; + const DEF_IMPORT = 0x010; const ANNOTATED = 0x008; // DEF_ANNOT - const IMPORTED = 0x010; // DEF_IMPORT const NONLOCAL = 0x020; // DEF_NONLOCAL // indicates if the symbol gets a value assigned by a named expression in a comprehension // this is required to correct the scope in the analysis. @@ -318,7 +318,7 @@ bitflags! { const COMP_ITER = 0x400; // DEF_COMP_ITER const COMP_CELL = 0x800; // DEF_COMP_CELL const TYPE_PARAM = 0x1000; // DEF_TYPE_PARAM - const BOUND = Self::DEF_LOCAL.bits() | Self::DEF_PARAM.bits() | Self::IMPORTED.bits() | Self::ITER.bits() | Self::TYPE_PARAM.bits(); + const BOUND = Self::DEF_LOCAL.bits() | Self::DEF_PARAM.bits() | Self::DEF_IMPORT.bits() | Self::ITER.bits() | Self::TYPE_PARAM.bits(); } } @@ -3377,7 +3377,7 @@ impl SymbolTableBuilder { flags.insert(SymbolFlags::NONLOCAL); } SymbolUsage::Imported => { - flags.insert(SymbolFlags::DEF_LOCAL | SymbolFlags::IMPORTED); + flags.insert(SymbolFlags::DEF_LOCAL | SymbolFlags::DEF_IMPORT); } SymbolUsage::Parameter => { flags.insert(SymbolFlags::DEF_PARAM); diff --git a/crates/vm/src/stdlib/_symtable.rs b/crates/vm/src/stdlib/_symtable.rs index d9d23395b4e..dcb44c2e7d3 100644 --- a/crates/vm/src/stdlib/_symtable.rs +++ b/crates/vm/src/stdlib/_symtable.rs @@ -38,7 +38,7 @@ mod _symtable { pub(super) const DEF_FREE_CLASS: i32 = SymbolFlags::DEF_FREE_CLASS.bits() as i32; #[pyattr] - pub(super) const DEF_IMPORT: i32 = SymbolFlags::IMPORTED.bits() as i32; + pub(super) const DEF_IMPORT: i32 = SymbolFlags::DEF_IMPORT.bits() as i32; #[pyattr] pub(super) const DEF_ANNOT: i32 = SymbolFlags::ANNOTATED.bits() as i32; @@ -260,7 +260,7 @@ mod _symtable { #[pymethod] const fn is_imported(&self) -> bool { - self.symbol.flags.contains(SymbolFlags::IMPORTED) + self.symbol.flags.contains(SymbolFlags::DEF_IMPORT) } #[pymethod] From 3c2480bc1beaa4cf04e01f9c749e4d720b12d5d7 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 27 Jun 2026 14:43:39 +0300 Subject: [PATCH 08/12] ANNOTATED -> DEF_ANNOT --- crates/codegen/src/symboltable.rs | 10 +++++----- crates/vm/src/stdlib/_symtable.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/codegen/src/symboltable.rs b/crates/codegen/src/symboltable.rs index 6b0657b68d2..c1465428877 100644 --- a/crates/codegen/src/symboltable.rs +++ b/crates/codegen/src/symboltable.rs @@ -304,10 +304,10 @@ bitflags! { /// ``` const DEF_FREE_CLASS = 0x100; const DEF_IMPORT = 0x010; + const DEF_ANNOT = 0x008; - const ANNOTATED = 0x008; // DEF_ANNOT const NONLOCAL = 0x020; // DEF_NONLOCAL // indicates if the symbol gets a value assigned by a named expression in a comprehension // this is required to correct the scope in the analysis. @@ -3284,7 +3284,7 @@ impl SymbolTableBuilder { location, }); } - if flags.contains(SymbolFlags::ANNOTATED) { + if flags.contains(SymbolFlags::DEF_ANNOT) { return Err(SymbolTableError { error: format!("annotated name '{name}' can't be global"), location, @@ -3312,7 +3312,7 @@ impl SymbolTableBuilder { location, }); } - if flags.contains(SymbolFlags::ANNOTATED) { + if flags.contains(SymbolFlags::DEF_ANNOT) { return Err(SymbolTableError { error: format!("annotated name '{name}' can't be nonlocal"), location, @@ -3388,7 +3388,7 @@ impl SymbolTableBuilder { } } SymbolUsage::AnnotationParameter => { - flags.insert(SymbolFlags::DEF_PARAM | SymbolFlags::ANNOTATED); + flags.insert(SymbolFlags::DEF_PARAM | SymbolFlags::DEF_ANNOT); // Annotated parameters are also added to varnames let name_str = symbol.name.clone(); if !self.current_varnames.contains(&name_str) { @@ -3396,7 +3396,7 @@ impl SymbolTableBuilder { } } SymbolUsage::AnnotationAssigned => { - flags.insert(SymbolFlags::DEF_LOCAL | SymbolFlags::ANNOTATED); + flags.insert(SymbolFlags::DEF_LOCAL | SymbolFlags::DEF_ANNOT); } SymbolUsage::Assigned => { flags.insert(SymbolFlags::DEF_LOCAL); diff --git a/crates/vm/src/stdlib/_symtable.rs b/crates/vm/src/stdlib/_symtable.rs index dcb44c2e7d3..b59923f1bfb 100644 --- a/crates/vm/src/stdlib/_symtable.rs +++ b/crates/vm/src/stdlib/_symtable.rs @@ -41,7 +41,7 @@ mod _symtable { pub(super) const DEF_IMPORT: i32 = SymbolFlags::DEF_IMPORT.bits() as i32; #[pyattr] - pub(super) const DEF_ANNOT: i32 = SymbolFlags::ANNOTATED.bits() as i32; + pub(super) const DEF_ANNOT: i32 = SymbolFlags::DEF_ANNOT.bits() as i32; #[pyattr] pub(super) const DEF_COMP_ITER: i32 = SymbolFlags::COMP_ITER.bits() as i32; @@ -301,7 +301,7 @@ mod _symtable { #[pymethod] const fn is_annotated(&self) -> bool { - self.symbol.flags.contains(SymbolFlags::ANNOTATED) + self.symbol.flags.contains(SymbolFlags::DEF_ANNOT) } #[pymethod] From d25ca855574fff0bdcee923351475ba066782919 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 27 Jun 2026 14:48:09 +0300 Subject: [PATCH 09/12] DEF_COMP_ITER DEF_COMP_CELL DEF_TYPE_PARAM --- crates/codegen/src/compile.rs | 2 +- crates/codegen/src/symboltable.rs | 25 +++++++++++++------------ crates/vm/src/stdlib/_symtable.rs | 6 +++--- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index d5bb48e768a..71a13140854 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -1788,7 +1788,7 @@ impl<'warnings> Compiler<'warnings> { .symbols .iter() .filter(|(_, s)| { - s.scope == SymbolScope::Cell || s.flags.contains(SymbolFlags::COMP_CELL) + s.scope == SymbolScope::Cell || s.flags.contains(SymbolFlags::DEF_COMP_CELL) }) .map(|(name, _)| name.clone()) .collect(); diff --git a/crates/codegen/src/symboltable.rs b/crates/codegen/src/symboltable.rs index c1465428877..a586d1e5d7d 100644 --- a/crates/codegen/src/symboltable.rs +++ b/crates/codegen/src/symboltable.rs @@ -305,6 +305,9 @@ bitflags! { const DEF_FREE_CLASS = 0x100; const DEF_IMPORT = 0x010; const DEF_ANNOT = 0x008; + const DEF_COMP_ITER = 0x400; + const DEF_TYPE_PARAM = 0x1000; + const DEF_COMP_CELL = 0x800; @@ -315,10 +318,7 @@ bitflags! { // indicates that the symbol is used a bound iterator variable. We distinguish this case // from normal assignment to detect disallowed re-assignment to iterator variables. const ITER = 0x080; - const COMP_ITER = 0x400; // DEF_COMP_ITER - const COMP_CELL = 0x800; // DEF_COMP_CELL - const TYPE_PARAM = 0x1000; // DEF_TYPE_PARAM - const BOUND = Self::DEF_LOCAL.bits() | Self::DEF_PARAM.bits() | Self::DEF_IMPORT.bits() | Self::ITER.bits() | Self::TYPE_PARAM.bits(); + const BOUND = Self::DEF_LOCAL.bits() | Self::DEF_PARAM.bits() | Self::DEF_IMPORT.bits() | Self::ITER.bits() | Self::DEF_TYPE_PARAM.bits(); } } @@ -453,7 +453,7 @@ fn inline_comprehension( // Track inlined cells if sub_symbol.scope == SymbolScope::Cell - || sub_symbol.flags.contains(SymbolFlags::COMP_CELL) + || sub_symbol.flags.contains(SymbolFlags::DEF_COMP_CELL) { inlined_cells.insert(name.clone()); } @@ -711,7 +711,7 @@ impl SymbolTableAnalyzer { for symbol in symbol_table.symbols.values_mut() { if inlined_cells.contains(&symbol.name) { - symbol.flags.insert(SymbolFlags::COMP_CELL); + symbol.flags.insert(SymbolFlags::DEF_COMP_CELL); } } @@ -806,7 +806,7 @@ impl SymbolTableAnalyzer { if symbol.flags.contains(SymbolFlags::NONLOCAL) { for (symbols, _typ, _skip) in self.tables.iter().rev() { if let Some(sym) = symbols.get(&symbol.name) { - if sym.flags.contains(SymbolFlags::TYPE_PARAM) { + if sym.flags.contains(SymbolFlags::DEF_TYPE_PARAM) { return Err(SymbolTableError { error: format!( "nonlocal binding not allowed for type parameter '{}'", @@ -836,7 +836,7 @@ impl SymbolTableAnalyzer { SymbolScope::Unknown => { // Try hard to figure out what the scope of this symbol is. let scope = if symbol.is_bound() { - if symbol.flags.contains(SymbolFlags::COMP_CELL) + if symbol.flags.contains(SymbolFlags::DEF_COMP_CELL) && matches!(st_typ, CompilerScope::Module | CompilerScope::Class) { // CPython keeps comprehension-only cells in @@ -3264,7 +3264,8 @@ impl SymbolTableBuilder { } // Role already set.. - if matches!(role, SymbolUsage::TypeParam) && flags.contains(SymbolFlags::TYPE_PARAM) { + if matches!(role, SymbolUsage::TypeParam) && flags.contains(SymbolFlags::DEF_TYPE_PARAM) + { return Err(SymbolTableError { error: format!("duplicate type parameter '{name}'"), location, @@ -3412,10 +3413,10 @@ impl SymbolTableBuilder { flags.insert(SymbolFlags::USE); } SymbolUsage::Iter => { - flags.insert(SymbolFlags::ITER | SymbolFlags::COMP_ITER); + flags.insert(SymbolFlags::ITER | SymbolFlags::DEF_COMP_ITER); } SymbolUsage::TypeParam => { - flags.insert(SymbolFlags::DEF_LOCAL | SymbolFlags::TYPE_PARAM); + flags.insert(SymbolFlags::DEF_LOCAL | SymbolFlags::DEF_TYPE_PARAM); } } @@ -3564,7 +3565,7 @@ mod tests { .expect("missing comprehension iteration target"); assert!( - symbol.flags.contains(SymbolFlags::COMP_ITER), + symbol.flags.contains(SymbolFlags::DEF_COMP_ITER), "CPython symtable_add_def_helper sets DEF_COMP_ITER on comprehension iteration targets" ); } diff --git a/crates/vm/src/stdlib/_symtable.rs b/crates/vm/src/stdlib/_symtable.rs index b59923f1bfb..a09aefc6146 100644 --- a/crates/vm/src/stdlib/_symtable.rs +++ b/crates/vm/src/stdlib/_symtable.rs @@ -44,13 +44,13 @@ mod _symtable { pub(super) const DEF_ANNOT: i32 = SymbolFlags::DEF_ANNOT.bits() as i32; #[pyattr] - pub(super) const DEF_COMP_ITER: i32 = SymbolFlags::COMP_ITER.bits() as i32; + pub(super) const DEF_COMP_ITER: i32 = SymbolFlags::DEF_COMP_ITER.bits() as i32; #[pyattr] - pub(super) const DEF_TYPE_PARAM: i32 = SymbolFlags::TYPE_PARAM.bits() as i32; + pub(super) const DEF_TYPE_PARAM: i32 = SymbolFlags::DEF_TYPE_PARAM.bits() as i32; #[pyattr] - pub(super) const DEF_COMP_CELL: i32 = SymbolFlags::COMP_CELL.bits() as i32; + pub(super) const DEF_COMP_CELL: i32 = SymbolFlags::DEF_COMP_CELL.bits() as i32; #[pyattr] pub(super) const DEF_BOUND: i32 = DEF_LOCAL | DEF_PARAM | DEF_IMPORT; From 037f05ee61b36487067c2e4f51a1f15dc7d79a60 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 27 Jun 2026 14:53:20 +0300 Subject: [PATCH 10/12] DEF_NONLOCAL DEF_BOUND --- crates/codegen/src/compile.rs | 2 +- crates/codegen/src/symboltable.rs | 26 +++++++++++++++++--------- crates/vm/src/stdlib/_symtable.rs | 6 +++--- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index 71a13140854..70fe91653b8 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -10810,7 +10810,7 @@ impl<'warnings> Compiler<'warnings> { let is_local = sym .flags .intersects(SymbolFlags::DEF_LOCAL | SymbolFlags::ITER) - && !sym.flags.contains(SymbolFlags::NONLOCAL); + && !sym.flags.contains(SymbolFlags::DEF_NONLOCAL); if is_local { pushed_locals.push(name.clone()); } diff --git a/crates/codegen/src/symboltable.rs b/crates/codegen/src/symboltable.rs index a586d1e5d7d..61f1d5d2205 100644 --- a/crates/codegen/src/symboltable.rs +++ b/crates/codegen/src/symboltable.rs @@ -292,6 +292,7 @@ bitflags! { const DEF_GLOBAL = 0x200; const DEF_LOCAL = 2; const DEF_PARAM = 0x004; + const DEF_NONLOCAL = 0x020; const USE = 0x001; /// indicates that the symbol is a free variable in a class method from the scope that the @@ -308,17 +309,22 @@ bitflags! { const DEF_COMP_ITER = 0x400; const DEF_TYPE_PARAM = 0x1000; const DEF_COMP_CELL = 0x800; + const DEF_BOUND = ( + Self::DEF_LOCAL.bits() + | Self::DEF_PARAM.bits() + | Self::DEF_IMPORT.bits() + | Self::ITER.bits() + | Self::DEF_TYPE_PARAM.bits() + ); - const NONLOCAL = 0x020; // DEF_NONLOCAL // indicates if the symbol gets a value assigned by a named expression in a comprehension // this is required to correct the scope in the analysis. const ASSIGNED_IN_COMPREHENSION = 0x040; // indicates that the symbol is used a bound iterator variable. We distinguish this case // from normal assignment to detect disallowed re-assignment to iterator variables. const ITER = 0x080; - const BOUND = Self::DEF_LOCAL.bits() | Self::DEF_PARAM.bits() | Self::DEF_IMPORT.bits() | Self::ITER.bits() | Self::DEF_TYPE_PARAM.bits(); } } @@ -358,7 +364,7 @@ impl Symbol { #[must_use] pub const fn is_bound(&self) -> bool { - self.flags.intersects(SymbolFlags::BOUND) + self.flags.intersects(SymbolFlags::DEF_BOUND) } } @@ -803,7 +809,7 @@ impl SymbolTableAnalyzer { }); } // Check if the nonlocal binding refers to a type parameter - if symbol.flags.contains(SymbolFlags::NONLOCAL) { + if symbol.flags.contains(SymbolFlags::DEF_NONLOCAL) { for (symbols, _typ, _skip) in self.tables.iter().rev() { if let Some(sym) = symbols.get(&symbol.name) { if sym.flags.contains(SymbolFlags::DEF_TYPE_PARAM) { @@ -1839,8 +1845,9 @@ impl SymbolTableBuilder { .last() .is_some_and(|table| table.typ != CompilerScope::Module) && let Some(flags) = existing_flags - && flags - .intersects(SymbolFlags::DEF_GLOBAL | SymbolFlags::NONLOCAL) + && flags.intersects( + SymbolFlags::DEF_GLOBAL | SymbolFlags::DEF_NONLOCAL, + ) { let usage = if flags.contains(SymbolFlags::DEF_GLOBAL) { "global" @@ -3122,7 +3129,7 @@ impl SymbolTableBuilder { current_symbol.flags.insert(SymbolFlags::DEF_GLOBAL); current_symbol.scope = SymbolScope::GlobalExplicit; } else { - current_symbol.flags.insert(SymbolFlags::NONLOCAL); + current_symbol.flags.insert(SymbolFlags::DEF_NONLOCAL); current_symbol.scope = SymbolScope::Free; } @@ -3330,7 +3337,8 @@ impl SymbolTableBuilder { } SymbolUsage::AnnotationAssigned if current_scope != CompilerScope::Module - && flags.intersects(SymbolFlags::DEF_GLOBAL | SymbolFlags::NONLOCAL) => + && flags + .intersects(SymbolFlags::DEF_GLOBAL | SymbolFlags::DEF_NONLOCAL) => { let usage = if flags.contains(SymbolFlags::DEF_GLOBAL) { "global" @@ -3375,7 +3383,7 @@ impl SymbolTableBuilder { match role { SymbolUsage::Nonlocal => { symbol.scope = SymbolScope::Free; - flags.insert(SymbolFlags::NONLOCAL); + flags.insert(SymbolFlags::DEF_NONLOCAL); } SymbolUsage::Imported => { flags.insert(SymbolFlags::DEF_LOCAL | SymbolFlags::DEF_IMPORT); diff --git a/crates/vm/src/stdlib/_symtable.rs b/crates/vm/src/stdlib/_symtable.rs index a09aefc6146..38d63208d9e 100644 --- a/crates/vm/src/stdlib/_symtable.rs +++ b/crates/vm/src/stdlib/_symtable.rs @@ -29,7 +29,7 @@ mod _symtable { pub(super) const DEF_PARAM: i32 = SymbolFlags::DEF_PARAM.bits() as i32; #[pyattr] - pub(super) const DEF_NONLOCAL: i32 = SymbolFlags::NONLOCAL.bits() as i32; + pub(super) const DEF_NONLOCAL: i32 = SymbolFlags::DEF_NONLOCAL.bits() as i32; #[pyattr] pub(super) const USE: i32 = SymbolFlags::USE.bits() as i32; @@ -53,7 +53,7 @@ mod _symtable { pub(super) const DEF_COMP_CELL: i32 = SymbolFlags::DEF_COMP_CELL.bits() as i32; #[pyattr] - pub(super) const DEF_BOUND: i32 = DEF_LOCAL | DEF_PARAM | DEF_IMPORT; + pub(super) const DEF_BOUND: i32 = SymbolFlags::DEF_BOUND.bits() as i32; #[pyattr] pub(super) const SCOPE_MASK: i32 = DEF_GLOBAL | DEF_LOCAL | DEF_PARAM | DEF_NONLOCAL; @@ -271,7 +271,7 @@ mod _symtable { #[pymethod] const fn is_nonlocal(&self) -> bool { - self.symbol.flags.contains(SymbolFlags::NONLOCAL) + self.symbol.flags.contains(SymbolFlags::DEF_NONLOCAL) } #[pymethod] From 6678195a707ae414a3e3cf5a300a506993c05a17 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 27 Jun 2026 14:57:27 +0300 Subject: [PATCH 11/12] Align flag values --- crates/codegen/src/symboltable.rs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/crates/codegen/src/symboltable.rs b/crates/codegen/src/symboltable.rs index 61f1d5d2205..40901d65791 100644 --- a/crates/codegen/src/symboltable.rs +++ b/crates/codegen/src/symboltable.rs @@ -289,11 +289,11 @@ impl From for i32 { bitflags! { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct SymbolFlags: u16 { - const DEF_GLOBAL = 0x200; - const DEF_LOCAL = 2; - const DEF_PARAM = 0x004; - const DEF_NONLOCAL = 0x020; - const USE = 0x001; + const DEF_GLOBAL = 1; + const DEF_LOCAL = 2; + const DEF_PARAM = 2 << 1; + const DEF_NONLOCAL = 2 << 2; + const USE = 2 << 3; /// indicates that the symbol is a free variable in a class method from the scope that the /// class is defined in, e.g.: @@ -303,12 +303,12 @@ bitflags! { /// def method(self): /// return x // is_free_class /// ``` - const DEF_FREE_CLASS = 0x100; - const DEF_IMPORT = 0x010; - const DEF_ANNOT = 0x008; - const DEF_COMP_ITER = 0x400; - const DEF_TYPE_PARAM = 0x1000; - const DEF_COMP_CELL = 0x800; + const DEF_FREE_CLASS = 2 << 5; + const DEF_IMPORT = 2 << 6; + const DEF_ANNOT = 2 << 7; + const DEF_COMP_ITER = 2 << 8; + const DEF_TYPE_PARAM = 2 << 9; + const DEF_COMP_CELL = 2 << 10; const DEF_BOUND = ( Self::DEF_LOCAL.bits() | Self::DEF_PARAM.bits() @@ -318,13 +318,14 @@ bitflags! { ); + // TODO: Remove these, RustPython specific // indicates if the symbol gets a value assigned by a named expression in a comprehension // this is required to correct the scope in the analysis. - const ASSIGNED_IN_COMPREHENSION = 0x040; + const ASSIGNED_IN_COMPREHENSION = 2 << 11; // indicates that the symbol is used a bound iterator variable. We distinguish this case // from normal assignment to detect disallowed re-assignment to iterator variables. - const ITER = 0x080; + const ITER = 2 << 12; } } From 560b9cb03b8913237fd7482ee391dae3260071fd Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 27 Jun 2026 15:00:00 +0300 Subject: [PATCH 12/12] unmark passing tests --- Lib/test/test_symtable.py | 3 --- crates/codegen/src/symboltable.rs | 1 - 2 files changed, 4 deletions(-) diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py index b55adab6baf..8cd1da1e972 100644 --- a/Lib/test/test_symtable.py +++ b/Lib/test/test_symtable.py @@ -269,7 +269,6 @@ def test_globals(self): self.assertTrue(self.top.lookup("some_non_assigned_global_var").is_global()) self.assertTrue(self.top.lookup("some_assigned_global_var").is_global()) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_nonlocal(self): self.assertFalse(self.spam.lookup("some_var").is_nonlocal()) self.assertTrue(self.other_internal.lookup("some_var").is_nonlocal()) @@ -288,7 +287,6 @@ def test_local(self): def test_free(self): self.assertTrue(self.internal.lookup("x").is_free()) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_referenced(self): self.assertTrue(self.internal.lookup("x").is_referenced()) self.assertTrue(self.spam.lookup("internal").is_referenced()) @@ -358,7 +356,6 @@ def test_annotated(self): ' x: int', 'test', 'exec') - @unittest.expectedFailure # TODO: RUSTPYTHON def test_imported(self): self.assertTrue(self.top.lookup("sys").is_imported()) diff --git a/crates/codegen/src/symboltable.rs b/crates/codegen/src/symboltable.rs index 40901d65791..52e9e3ba6a2 100644 --- a/crates/codegen/src/symboltable.rs +++ b/crates/codegen/src/symboltable.rs @@ -294,7 +294,6 @@ bitflags! { const DEF_PARAM = 2 << 1; const DEF_NONLOCAL = 2 << 2; const USE = 2 << 3; - /// indicates that the symbol is a free variable in a class method from the scope that the /// class is defined in, e.g.: /// ```python