@@ -74,7 +74,6 @@ struct Compiler<'src> {
7474 source_code : SourceCode < ' src > ,
7575 // current_source_location: SourceLocation,
7676 current_source_range : TextRange ,
77- qualified_path : Vec < String > ,
7877 done_with_future_stmts : DoneWithFuture ,
7978 future_annotations : bool ,
8079 ctx : CompileContext ,
@@ -326,7 +325,6 @@ impl<'src> Compiler<'src> {
326325 source_code,
327326 // current_source_location: SourceLocation::default(),
328327 current_source_range : TextRange :: default ( ) ,
329- qualified_path : Vec :: new ( ) ,
330328 done_with_future_stmts : DoneWithFuture :: No ,
331329 future_annotations : false ,
332330 ctx : CompileContext {
@@ -401,12 +399,8 @@ impl Compiler<'_> {
401399 . map ( |( var, _) | var. clone ( ) )
402400 . collect ( ) ;
403401
404- // Calculate qualname based on the current qualified path
405- let qualname = if self . qualified_path . is_empty ( ) {
406- Some ( obj_name. clone ( ) )
407- } else {
408- Some ( self . qualified_path . join ( "." ) )
409- } ;
402+ // Qualname will be set later by set_qualname
403+ let qualname = None ;
410404
411405 // Get the private name from current scope if exists
412406 let private = self . code_stack . last ( ) . and_then ( |info| info. private . clone ( ) ) ;
@@ -467,6 +461,98 @@ impl Compiler<'_> {
467461 . to_u32 ( )
468462 }
469463
464+ /// Set the qualified name for the current code object, based on CPython's compiler_set_qualname
465+ fn set_qualname ( & mut self ) -> String {
466+ let qualname = self . make_qualname ( ) ;
467+ self . current_code_info ( ) . qualname = Some ( qualname. clone ( ) ) ;
468+ qualname
469+ }
470+ fn make_qualname ( & mut self ) -> String {
471+ let stack_size = self . code_stack . len ( ) ;
472+ assert ! ( stack_size >= 1 ) ;
473+
474+ let current_obj_name = self . current_code_info ( ) . obj_name . clone ( ) ;
475+
476+ // If we're at the module level (stack_size == 1), qualname is just the name
477+ if stack_size <= 1 {
478+ return current_obj_name;
479+ }
480+
481+ // Check parent scope
482+ let mut parent_idx = stack_size - 2 ;
483+ let mut parent = & self . code_stack [ parent_idx] ;
484+
485+ // If parent is a type parameter scope, look at grandparent
486+ if parent. obj_name . starts_with ( "<generic parameters of " ) {
487+ if stack_size == 2 {
488+ // If we're immediately within the module after type params,
489+ // qualname is just the name
490+ return current_obj_name;
491+ }
492+ parent_idx = stack_size - 3 ;
493+ parent = & self . code_stack [ parent_idx] ;
494+ }
495+
496+ // Check if this is a global class/function
497+ let mut force_global = false ;
498+ if stack_size > self . symbol_table_stack . len ( ) {
499+ // We might be in a situation where symbol table isn't pushed yet
500+ // In this case, check the parent symbol table
501+ if let Some ( parent_table) = self . symbol_table_stack . last ( ) {
502+ if let Some ( symbol) = parent_table. lookup ( & current_obj_name) {
503+ if symbol. scope == SymbolScope :: GlobalExplicit {
504+ force_global = true ;
505+ }
506+ }
507+ }
508+ } else if let Some ( _current_table) = self . symbol_table_stack . last ( ) {
509+ // Mangle the name if necessary (for private names in classes)
510+ let mangled_name = self . mangle ( & current_obj_name) ;
511+
512+ // Look up in parent symbol table to check scope
513+ if self . symbol_table_stack . len ( ) >= 2 {
514+ let parent_table = & self . symbol_table_stack [ self . symbol_table_stack . len ( ) - 2 ] ;
515+ if let Some ( symbol) = parent_table. lookup ( & mangled_name) {
516+ if symbol. scope == SymbolScope :: GlobalExplicit {
517+ force_global = true ;
518+ }
519+ }
520+ }
521+ }
522+
523+ // Build the qualified name
524+ if force_global {
525+ // For global symbols, qualname is just the name
526+ current_obj_name
527+ } else {
528+ // Check parent scope type
529+ let parent_obj_name = & parent. obj_name ;
530+
531+ // Determine if parent is a function-like scope
532+ let is_function_parent = parent. flags . contains ( bytecode:: CodeFlags :: IS_OPTIMIZED )
533+ && !parent_obj_name. starts_with ( "<" ) // Not a special scope like <lambda>, <listcomp>, etc.
534+ && parent_obj_name != "<module>" ; // Not the module scope
535+
536+ if is_function_parent {
537+ // For functions, append .<locals> to parent qualname
538+ // Use parent's qualname if available, otherwise use parent_obj_name
539+ let parent_qualname = parent. qualname . as_ref ( ) . unwrap_or ( parent_obj_name) ;
540+ format ! ( "{parent_qualname}.<locals>.{current_obj_name}" )
541+ } else {
542+ // For classes and other scopes, use parent's qualname directly
543+ // Use parent's qualname if available, otherwise use parent_obj_name
544+ let parent_qualname = parent. qualname . as_ref ( ) . unwrap_or ( parent_obj_name) ;
545+ if parent_qualname == "<module>" {
546+ // Module level, just use the name
547+ current_obj_name
548+ } else {
549+ // Concatenate parent qualname with current name
550+ format ! ( "{parent_qualname}.{current_obj_name}" )
551+ }
552+ }
553+ }
554+ }
555+
470556 fn compile_program (
471557 & mut self ,
472558 body : & ModModule ,
@@ -1547,13 +1633,8 @@ impl Compiler<'_> {
15471633 } ,
15481634 } ;
15491635
1550- self . push_qualified_path ( name) ;
1551- let qualified_name = self . qualified_path . join ( "." ) ;
1552-
1553- // Update the qualname in the current code info
1554- self . code_stack . last_mut ( ) . unwrap ( ) . qualname = Some ( qualified_name. clone ( ) ) ;
1555-
1556- self . push_qualified_path ( "<locals>" ) ;
1636+ // Set qualname using the new method
1637+ let qualname = self . set_qualname ( ) ;
15571638
15581639 let ( doc_str, body) = split_doc ( body, & self . opts ) ;
15591640
@@ -1582,8 +1663,6 @@ impl Compiler<'_> {
15821663 }
15831664
15841665 let code = self . pop_code_object ( ) ;
1585- self . qualified_path . pop ( ) ;
1586- self . qualified_path . pop ( ) ;
15871666 self . ctx = prev_ctx;
15881667
15891668 // Prepare generic type parameters:
@@ -1646,7 +1725,7 @@ impl Compiler<'_> {
16461725 code : Box :: new ( code) ,
16471726 } ) ;
16481727 self . emit_load_const ( ConstantData :: Str {
1649- value : qualified_name . into ( ) ,
1728+ value : qualname . into ( ) ,
16501729 } ) ;
16511730
16521731 // Turn code object into function object:
@@ -1758,21 +1837,6 @@ impl Compiler<'_> {
17581837 loop_data : None ,
17591838 } ;
17601839
1761- // Check if the class is declared global
1762- let symbol_table = self . symbol_table_stack . last ( ) . unwrap ( ) ;
1763- let symbol = unwrap_internal (
1764- self ,
1765- symbol_table
1766- . lookup ( name. as_ref ( ) )
1767- . ok_or_else ( || InternalError :: MissingSymbol ( name. to_owned ( ) ) ) ,
1768- ) ;
1769- let mut global_path_prefix = Vec :: new ( ) ;
1770- if symbol. scope == SymbolScope :: GlobalExplicit {
1771- global_path_prefix. append ( & mut self . qualified_path ) ;
1772- }
1773- self . push_qualified_path ( name) ;
1774- let qualified_name = self . qualified_path . join ( "." ) ;
1775-
17761840 // If there are type params, we need to push a special symbol table just for them
17771841 if let Some ( type_params) = type_params {
17781842 self . push_symbol_table ( ) ;
@@ -1790,8 +1854,8 @@ impl Compiler<'_> {
17901854
17911855 self . push_output ( bytecode:: CodeFlags :: empty ( ) , 0 , 0 , 0 , name. to_owned ( ) ) ;
17921856
1793- // Update the qualname in the current code info
1794- self . code_stack . last_mut ( ) . unwrap ( ) . qualname = Some ( qualified_name . clone ( ) ) ;
1857+ // Set qualname using the new method
1858+ let qualname = self . set_qualname ( ) ;
17951859
17961860 // For class scopes, set u_private to the class name for name mangling
17971861 self . code_stack . last_mut ( ) . unwrap ( ) . private = Some ( name. to_owned ( ) ) ;
@@ -1803,10 +1867,10 @@ impl Compiler<'_> {
18031867 let dunder_module = self . name ( "__module__" ) ;
18041868 emit ! ( self , Instruction :: StoreLocal ( dunder_module) ) ;
18051869 self . emit_load_const ( ConstantData :: Str {
1806- value : qualified_name . into ( ) ,
1870+ value : qualname . into ( ) ,
18071871 } ) ;
1808- let qualname = self . name ( "__qualname__" ) ;
1809- emit ! ( self , Instruction :: StoreLocal ( qualname ) ) ;
1872+ let qualname_name = self . name ( "__qualname__" ) ;
1873+ emit ! ( self , Instruction :: StoreLocal ( qualname_name ) ) ;
18101874 self . load_docstring ( doc_str) ;
18111875 let doc = self . name ( "__doc__" ) ;
18121876 emit ! ( self , Instruction :: StoreLocal ( doc) ) ;
@@ -1848,9 +1912,6 @@ impl Compiler<'_> {
18481912 self . emit_return_value ( ) ;
18491913
18501914 let code = self . pop_code_object ( ) ;
1851-
1852- self . qualified_path . pop ( ) ;
1853- self . qualified_path . append ( global_path_prefix. as_mut ( ) ) ;
18541915 self . ctx = prev_ctx;
18551916
18561917 emit ! ( self , Instruction :: LoadBuildClass ) ;
@@ -3606,8 +3667,8 @@ impl Compiler<'_> {
36063667 let mut func_flags = self
36073668 . enter_function ( & name, parameters. as_deref ( ) . unwrap_or ( & Default :: default ( ) ) ) ?;
36083669
3609- // Lambda qualname should be < lambda>
3610- self . code_stack . last_mut ( ) . unwrap ( ) . qualname = Some ( name . clone ( ) ) ;
3670+ // Set qualname for lambda
3671+ self . set_qualname ( ) ;
36113672
36123673 self . ctx = CompileContext {
36133674 loop_data : Option :: None ,
@@ -4078,7 +4139,7 @@ impl Compiler<'_> {
40784139 self . push_output ( flags, 1 , 1 , 0 , name. to_owned ( ) ) ;
40794140
40804141 // Set qualname for comprehension
4081- self . code_stack . last_mut ( ) . unwrap ( ) . qualname = Some ( name . to_owned ( ) ) ;
4142+ self . set_qualname ( ) ;
40824143
40834144 let arg0 = self . varname ( ".0" ) ?;
40844145
@@ -4336,10 +4397,6 @@ impl Compiler<'_> {
43364397 . line_index ( self . current_source_range . start ( ) )
43374398 }
43384399
4339- fn push_qualified_path ( & mut self , name : & str ) {
4340- self . qualified_path . push ( name. to_owned ( ) ) ;
4341- }
4342-
43434400 fn mark_generator ( & mut self ) {
43444401 self . current_code_info ( ) . flags |= bytecode:: CodeFlags :: IS_GENERATOR
43454402 }
0 commit comments