diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 98b70dfbb2..5aef2020d0 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -74,7 +74,6 @@ struct Compiler<'src> { source_code: SourceCode<'src>, // current_source_location: SourceLocation, current_source_range: TextRange, - qualified_path: Vec, done_with_future_stmts: DoneWithFuture, future_annotations: bool, ctx: CompileContext, @@ -326,7 +325,6 @@ impl<'src> Compiler<'src> { source_code, // current_source_location: SourceLocation::default(), current_source_range: TextRange::default(), - qualified_path: Vec::new(), done_with_future_stmts: DoneWithFuture::No, future_annotations: false, ctx: CompileContext { @@ -401,12 +399,8 @@ impl Compiler<'_> { .map(|(var, _)| var.clone()) .collect(); - // Calculate qualname based on the current qualified path - let qualname = if self.qualified_path.is_empty() { - Some(obj_name.clone()) - } else { - Some(self.qualified_path.join(".")) - }; + // Qualname will be set later by set_qualname + let qualname = None; // Get the private name from current scope if exists let private = self.code_stack.last().and_then(|info| info.private.clone()); @@ -467,6 +461,98 @@ impl Compiler<'_> { .to_u32() } + /// Set the qualified name for the current code object, based on CPython's compiler_set_qualname + fn set_qualname(&mut self) -> String { + let qualname = self.make_qualname(); + self.current_code_info().qualname = Some(qualname.clone()); + qualname + } + fn make_qualname(&mut self) -> String { + let stack_size = self.code_stack.len(); + assert!(stack_size >= 1); + + let current_obj_name = self.current_code_info().obj_name.clone(); + + // If we're at the module level (stack_size == 1), qualname is just the name + if stack_size <= 1 { + return current_obj_name; + } + + // Check parent scope + let mut parent_idx = stack_size - 2; + let mut parent = &self.code_stack[parent_idx]; + + // If parent is a type parameter scope, look at grandparent + if parent.obj_name.starts_with(" self.symbol_table_stack.len() { + // We might be in a situation where symbol table isn't pushed yet + // In this case, check the parent symbol table + if let Some(parent_table) = self.symbol_table_stack.last() { + if let Some(symbol) = parent_table.lookup(¤t_obj_name) { + if symbol.scope == SymbolScope::GlobalExplicit { + force_global = true; + } + } + } + } else if let Some(_current_table) = self.symbol_table_stack.last() { + // Mangle the name if necessary (for private names in classes) + let mangled_name = self.mangle(¤t_obj_name); + + // Look up in parent symbol table to check scope + if self.symbol_table_stack.len() >= 2 { + let parent_table = &self.symbol_table_stack[self.symbol_table_stack.len() - 2]; + if let Some(symbol) = parent_table.lookup(&mangled_name) { + if symbol.scope == SymbolScope::GlobalExplicit { + force_global = true; + } + } + } + } + + // Build the qualified name + if force_global { + // For global symbols, qualname is just the name + current_obj_name + } else { + // Check parent scope type + let parent_obj_name = &parent.obj_name; + + // Determine if parent is a function-like scope + let is_function_parent = parent.flags.contains(bytecode::CodeFlags::IS_OPTIMIZED) + && !parent_obj_name.starts_with("<") // Not a special scope like , , etc. + && parent_obj_name != ""; // Not the module scope + + if is_function_parent { + // For functions, append . to parent qualname + // Use parent's qualname if available, otherwise use parent_obj_name + let parent_qualname = parent.qualname.as_ref().unwrap_or(parent_obj_name); + format!("{parent_qualname}..{current_obj_name}") + } else { + // For classes and other scopes, use parent's qualname directly + // Use parent's qualname if available, otherwise use parent_obj_name + let parent_qualname = parent.qualname.as_ref().unwrap_or(parent_obj_name); + if parent_qualname == "" { + // Module level, just use the name + current_obj_name + } else { + // Concatenate parent qualname with current name + format!("{parent_qualname}.{current_obj_name}") + } + } + } + } + fn compile_program( &mut self, body: &ModModule, @@ -1547,13 +1633,8 @@ impl Compiler<'_> { }, }; - self.push_qualified_path(name); - let qualified_name = self.qualified_path.join("."); - - // Update the qualname in the current code info - self.code_stack.last_mut().unwrap().qualname = Some(qualified_name.clone()); - - self.push_qualified_path(""); + // Set qualname using the new method + let qualname = self.set_qualname(); let (doc_str, body) = split_doc(body, &self.opts); @@ -1582,8 +1663,6 @@ impl Compiler<'_> { } let code = self.pop_code_object(); - self.qualified_path.pop(); - self.qualified_path.pop(); self.ctx = prev_ctx; // Prepare generic type parameters: @@ -1646,7 +1725,7 @@ impl Compiler<'_> { code: Box::new(code), }); self.emit_load_const(ConstantData::Str { - value: qualified_name.into(), + value: qualname.into(), }); // Turn code object into function object: @@ -1758,21 +1837,6 @@ impl Compiler<'_> { loop_data: None, }; - // Check if the class is declared global - let symbol_table = self.symbol_table_stack.last().unwrap(); - let symbol = unwrap_internal( - self, - symbol_table - .lookup(name.as_ref()) - .ok_or_else(|| InternalError::MissingSymbol(name.to_owned())), - ); - let mut global_path_prefix = Vec::new(); - if symbol.scope == SymbolScope::GlobalExplicit { - global_path_prefix.append(&mut self.qualified_path); - } - self.push_qualified_path(name); - let qualified_name = self.qualified_path.join("."); - // If there are type params, we need to push a special symbol table just for them if let Some(type_params) = type_params { self.push_symbol_table(); @@ -1790,8 +1854,8 @@ impl Compiler<'_> { self.push_output(bytecode::CodeFlags::empty(), 0, 0, 0, name.to_owned()); - // Update the qualname in the current code info - self.code_stack.last_mut().unwrap().qualname = Some(qualified_name.clone()); + // Set qualname using the new method + let qualname = self.set_qualname(); // For class scopes, set u_private to the class name for name mangling self.code_stack.last_mut().unwrap().private = Some(name.to_owned()); @@ -1803,10 +1867,10 @@ impl Compiler<'_> { let dunder_module = self.name("__module__"); emit!(self, Instruction::StoreLocal(dunder_module)); self.emit_load_const(ConstantData::Str { - value: qualified_name.into(), + value: qualname.into(), }); - let qualname = self.name("__qualname__"); - emit!(self, Instruction::StoreLocal(qualname)); + let qualname_name = self.name("__qualname__"); + emit!(self, Instruction::StoreLocal(qualname_name)); self.load_docstring(doc_str); let doc = self.name("__doc__"); emit!(self, Instruction::StoreLocal(doc)); @@ -1848,9 +1912,6 @@ impl Compiler<'_> { self.emit_return_value(); let code = self.pop_code_object(); - - self.qualified_path.pop(); - self.qualified_path.append(global_path_prefix.as_mut()); self.ctx = prev_ctx; emit!(self, Instruction::LoadBuildClass); @@ -3606,8 +3667,8 @@ impl Compiler<'_> { let mut func_flags = self .enter_function(&name, parameters.as_deref().unwrap_or(&Default::default()))?; - // Lambda qualname should be - self.code_stack.last_mut().unwrap().qualname = Some(name.clone()); + // Set qualname for lambda + self.set_qualname(); self.ctx = CompileContext { loop_data: Option::None, @@ -4078,7 +4139,7 @@ impl Compiler<'_> { self.push_output(flags, 1, 1, 0, name.to_owned()); // Set qualname for comprehension - self.code_stack.last_mut().unwrap().qualname = Some(name.to_owned()); + self.set_qualname(); let arg0 = self.varname(".0")?; @@ -4336,10 +4397,6 @@ impl Compiler<'_> { .line_index(self.current_source_range.start()) } - fn push_qualified_path(&mut self, name: &str) { - self.qualified_path.push(name.to_owned()); - } - fn mark_generator(&mut self) { self.current_code_info().flags |= bytecode::CodeFlags::IS_GENERATOR }