@@ -176,7 +176,7 @@ pub fn compile_program(
176
176
. map_err ( |e| e. into_codegen_error ( source_code. path . to_owned ( ) ) ) ?;
177
177
let mut compiler = Compiler :: new ( opts, source_code, "<module>" . to_owned ( ) ) ;
178
178
compiler. compile_program ( ast, symbol_table) ?;
179
- let code = compiler. pop_code_object ( ) ;
179
+ let code = compiler. exit_scope ( ) ;
180
180
trace ! ( "Compilation completed: {code:?}" ) ;
181
181
Ok ( code)
182
182
}
@@ -191,7 +191,7 @@ pub fn compile_program_single(
191
191
. map_err ( |e| e. into_codegen_error ( source_code. path . to_owned ( ) ) ) ?;
192
192
let mut compiler = Compiler :: new ( opts, source_code, "<module>" . to_owned ( ) ) ;
193
193
compiler. compile_program_single ( & ast. body , symbol_table) ?;
194
- let code = compiler. pop_code_object ( ) ;
194
+ let code = compiler. exit_scope ( ) ;
195
195
trace ! ( "Compilation completed: {code:?}" ) ;
196
196
Ok ( code)
197
197
}
@@ -205,7 +205,7 @@ pub fn compile_block_expression(
205
205
. map_err ( |e| e. into_codegen_error ( source_code. path . to_owned ( ) ) ) ?;
206
206
let mut compiler = Compiler :: new ( opts, source_code, "<module>" . to_owned ( ) ) ;
207
207
compiler. compile_block_expr ( & ast. body , symbol_table) ?;
208
- let code = compiler. pop_code_object ( ) ;
208
+ let code = compiler. exit_scope ( ) ;
209
209
trace ! ( "Compilation completed: {code:?}" ) ;
210
210
Ok ( code)
211
211
}
@@ -219,7 +219,7 @@ pub fn compile_expression(
219
219
. map_err ( |e| e. into_codegen_error ( source_code. path . to_owned ( ) ) ) ?;
220
220
let mut compiler = Compiler :: new ( opts, source_code, "<module>" . to_owned ( ) ) ;
221
221
compiler. compile_eval ( ast, symbol_table) ?;
222
- let code = compiler. pop_code_object ( ) ;
222
+ let code = compiler. exit_scope ( ) ;
223
223
Ok ( code)
224
224
}
225
225
@@ -404,55 +404,121 @@ impl Compiler<'_> {
404
404
self . symbol_table_stack . pop ( ) . expect ( "compiler bug" )
405
405
}
406
406
407
- fn push_output (
407
+ /// Enter a new scope
408
+ // = compiler_enter_scope
409
+ fn enter_scope (
408
410
& mut self ,
409
- flags : bytecode:: CodeFlags ,
410
- posonlyarg_count : u32 ,
411
- arg_count : u32 ,
412
- kwonlyarg_count : u32 ,
413
- obj_name : String ,
414
- ) {
411
+ name : & str ,
412
+ scope_type : SymbolTableType ,
413
+ key : usize , // In RustPython, we use the index in symbol_table_stack as key
414
+ lineno : u32 ,
415
+ ) -> CompileResult < ( ) > {
416
+ // Create location
417
+ let location = ruff_source_file:: SourceLocation {
418
+ row : OneIndexed :: new ( lineno as usize ) . unwrap_or ( OneIndexed :: MIN ) ,
419
+ column : OneIndexed :: new ( 1 ) . unwrap ( ) ,
420
+ } ;
421
+
422
+ // Allocate a new compiler unit
423
+
424
+ // In Rust, we'll create the structure directly
415
425
let source_path = self . source_code . path . to_owned ( ) ;
416
- let first_line_number = self . get_source_line_number ( ) ;
417
426
418
- // Get the private name from current scope if exists
419
- let private = self . code_stack . last ( ) . and_then ( |info| info. private . clone ( ) ) ;
427
+ // Lookup symbol table entry using key (_PySymtable_Lookup)
428
+ let ste = if key < self . symbol_table_stack . len ( ) {
429
+ & self . symbol_table_stack [ key]
430
+ } else {
431
+ return Err ( self . error ( CodegenErrorType :: SyntaxError (
432
+ "unknown symbol table entry" . to_owned ( ) ,
433
+ ) ) ) ;
434
+ } ;
420
435
421
- let table = self . push_symbol_table ( ) ;
436
+ // Use varnames from symbol table (already collected in definition order)
437
+ let varname_cache: IndexSet < String > = ste. varnames . iter ( ) . cloned ( ) . collect ( ) ;
422
438
423
- let cellvar_cache = table
439
+ // Build cellvars using dictbytype (CELL scope, sorted)
440
+ let mut cellvar_cache = IndexSet :: default ( ) ;
441
+ let mut cell_names: Vec < _ > = ste
424
442
. symbols
425
443
. iter ( )
426
444
. filter ( |( _, s) | s. scope == SymbolScope :: Cell )
427
- . map ( |( var , _) | var . clone ( ) )
445
+ . map ( |( name , _) | name . clone ( ) )
428
446
. collect ( ) ;
429
- let freevar_cache = table
447
+ cell_names. sort ( ) ;
448
+ for name in cell_names {
449
+ cellvar_cache. insert ( name) ;
450
+ }
451
+
452
+ // Handle implicit __class__ cell if needed
453
+ if ste. needs_class_closure {
454
+ // Cook up an implicit __class__ cell
455
+ debug_assert_eq ! ( scope_type, SymbolTableType :: Class ) ;
456
+ cellvar_cache. insert ( "__class__" . to_string ( ) ) ;
457
+ }
458
+
459
+ // Handle implicit __classdict__ cell if needed
460
+ if ste. needs_classdict {
461
+ // Cook up an implicit __classdict__ cell
462
+ debug_assert_eq ! ( scope_type, SymbolTableType :: Class ) ;
463
+ cellvar_cache. insert ( "__classdict__" . to_string ( ) ) ;
464
+ }
465
+
466
+ // Build freevars using dictbytype (FREE scope, offset by cellvars size)
467
+ let mut freevar_cache = IndexSet :: default ( ) ;
468
+ let mut free_names: Vec < _ > = ste
430
469
. symbols
431
470
. iter ( )
432
471
. filter ( |( _, s) | {
433
472
s. scope == SymbolScope :: Free || s. flags . contains ( SymbolFlags :: FREE_CLASS )
434
473
} )
435
- . map ( |( var , _) | var . clone ( ) )
474
+ . map ( |( name , _) | name . clone ( ) )
436
475
. collect ( ) ;
476
+ free_names. sort ( ) ;
477
+ for name in free_names {
478
+ freevar_cache. insert ( name) ;
479
+ }
480
+
481
+ // Initialize u_metadata fields
482
+ let ( flags, posonlyarg_count, arg_count, kwonlyarg_count) = match scope_type {
483
+ SymbolTableType :: Module => ( bytecode:: CodeFlags :: empty ( ) , 0 , 0 , 0 ) ,
484
+ SymbolTableType :: Class => ( bytecode:: CodeFlags :: empty ( ) , 0 , 0 , 0 ) ,
485
+ SymbolTableType :: Function | SymbolTableType :: Lambda => (
486
+ bytecode:: CodeFlags :: NEW_LOCALS | bytecode:: CodeFlags :: IS_OPTIMIZED ,
487
+ 0 , // Will be set later in enter_function
488
+ 0 , // Will be set later in enter_function
489
+ 0 , // Will be set later in enter_function
490
+ ) ,
491
+ SymbolTableType :: Comprehension => (
492
+ bytecode:: CodeFlags :: NEW_LOCALS | bytecode:: CodeFlags :: IS_OPTIMIZED ,
493
+ 0 ,
494
+ 1 , // comprehensions take one argument (.0)
495
+ 0 ,
496
+ ) ,
497
+ SymbolTableType :: TypeParams => (
498
+ bytecode:: CodeFlags :: NEW_LOCALS | bytecode:: CodeFlags :: IS_OPTIMIZED ,
499
+ 0 ,
500
+ 0 ,
501
+ 0 ,
502
+ ) ,
503
+ } ;
437
504
438
- // Initialize varname_cache from SymbolTable::varnames
439
- let varname_cache: IndexSet < String > = table. varnames . iter ( ) . cloned ( ) . collect ( ) ;
440
-
441
- // Qualname will be set later by set_qualname
442
- let qualname = None ;
443
-
444
- // Check if this is a class scope
445
- let is_class_scope = table. typ == SymbolTableType :: Class ;
505
+ // Get private name from parent scope
506
+ let private = if !self . code_stack . is_empty ( ) {
507
+ self . code_stack . last ( ) . unwrap ( ) . private . clone ( )
508
+ } else {
509
+ None
510
+ } ;
446
511
447
- let info = ir:: CodeInfo {
512
+ // Create the new compilation unit
513
+ let code_info = ir:: CodeInfo {
448
514
flags,
449
- source_path,
515
+ source_path : source_path . clone ( ) ,
450
516
private,
451
517
blocks : vec ! [ ir:: Block :: default ( ) ] ,
452
- current_block : ir :: BlockIdx ( 0 ) ,
518
+ current_block : BlockIdx ( 0 ) ,
453
519
metadata : ir:: CodeUnitMetadata {
454
- name : obj_name ,
455
- qualname,
520
+ name : name . to_owned ( ) ,
521
+ qualname : None , // Will be set below
456
522
consts : IndexSet :: default ( ) ,
457
523
names : IndexSet :: default ( ) ,
458
524
varnames : varname_cache,
@@ -462,20 +528,93 @@ impl Compiler<'_> {
462
528
argcount : arg_count,
463
529
posonlyargcount : posonlyarg_count,
464
530
kwonlyargcount : kwonlyarg_count,
465
- firstlineno : first_line_number ,
531
+ firstlineno : OneIndexed :: new ( lineno as usize ) . unwrap_or ( OneIndexed :: MIN ) ,
466
532
} ,
467
- static_attributes : if is_class_scope {
533
+ static_attributes : if scope_type == SymbolTableType :: Class {
468
534
Some ( IndexSet :: default ( ) )
469
535
} else {
470
536
None
471
537
} ,
472
538
in_inlined_comp : false ,
473
539
fblock : Vec :: with_capacity ( MAXBLOCKS ) ,
474
540
} ;
475
- self . code_stack . push ( info) ;
541
+
542
+ // Push the old compiler unit on the stack (like PyCapsule)
543
+ // This happens before setting qualname
544
+ self . code_stack . push ( code_info) ;
545
+
546
+ // Set qualname after pushing (uses compiler_set_qualname logic)
547
+ if scope_type != SymbolTableType :: Module {
548
+ self . set_qualname ( ) ;
549
+ }
550
+
551
+ // Emit RESUME instruction
552
+ let _resume_loc = if scope_type == SymbolTableType :: Module {
553
+ // Module scope starts with lineno 0
554
+ ruff_source_file:: SourceLocation {
555
+ row : OneIndexed :: MIN ,
556
+ column : OneIndexed :: MIN ,
557
+ }
558
+ } else {
559
+ location
560
+ } ;
561
+
562
+ // Set the source range for the RESUME instruction
563
+ // For now, just use an empty range at the beginning
564
+ self . current_source_range = TextRange :: default ( ) ;
565
+ emit ! (
566
+ self ,
567
+ Instruction :: Resume {
568
+ arg: bytecode:: ResumeType :: AtFuncStart as u32
569
+ }
570
+ ) ;
571
+
572
+ if scope_type == SymbolTableType :: Module {
573
+ // This would be loc.lineno = -1 in CPython
574
+ // We handle this differently in RustPython
575
+ }
576
+
577
+ Ok ( ( ) )
578
+ }
579
+
580
+ fn push_output (
581
+ & mut self ,
582
+ flags : bytecode:: CodeFlags ,
583
+ posonlyarg_count : u32 ,
584
+ arg_count : u32 ,
585
+ kwonlyarg_count : u32 ,
586
+ obj_name : String ,
587
+ ) {
588
+ // First push the symbol table
589
+ let table = self . push_symbol_table ( ) ;
590
+ let scope_type = table. typ ;
591
+
592
+ // The key is the current position in the symbol table stack
593
+ let key = self . symbol_table_stack . len ( ) - 1 ;
594
+
595
+ // Get the line number
596
+ let lineno = self . get_source_line_number ( ) . get ( ) ;
597
+
598
+ // Call enter_scope which does most of the work
599
+ if let Err ( e) = self . enter_scope ( & obj_name, scope_type, key, lineno. to_u32 ( ) ) {
600
+ // In the current implementation, push_output doesn't return an error,
601
+ // so we panic here. This maintains the same behavior.
602
+ panic ! ( "enter_scope failed: {e:?}" ) ;
603
+ }
604
+
605
+ // Override the values that push_output sets explicitly
606
+ // enter_scope sets default values based on scope_type, but push_output
607
+ // allows callers to specify exact values
608
+ if let Some ( info) = self . code_stack . last_mut ( ) {
609
+ info. flags = flags;
610
+ info. metadata . argcount = arg_count;
611
+ info. metadata . posonlyargcount = posonlyarg_count;
612
+ info. metadata . kwonlyargcount = kwonlyarg_count;
613
+ }
476
614
}
477
615
478
- fn pop_code_object ( & mut self ) -> CodeObject {
616
+ // compiler_exit_scope
617
+ fn exit_scope ( & mut self ) -> CodeObject {
479
618
let table = self . pop_symbol_table ( ) ;
480
619
assert ! ( table. sub_tables. is_empty( ) ) ;
481
620
let pop = self . code_stack . pop ( ) ;
@@ -755,7 +894,7 @@ impl Compiler<'_> {
755
894
}
756
895
757
896
fn mangle < ' a > ( & self , name : & ' a str ) -> Cow < ' a , str > {
758
- // Use u_private from current code unit for name mangling
897
+ // Use private from current code unit for name mangling
759
898
let private = self
760
899
. code_stack
761
900
. last ( )
@@ -1758,14 +1897,6 @@ impl Compiler<'_> {
1758
1897
. consts
1759
1898
. insert_full ( ConstantData :: None ) ;
1760
1899
1761
- // Emit RESUME instruction at function start
1762
- emit ! (
1763
- self ,
1764
- Instruction :: Resume {
1765
- arg: bytecode:: ResumeType :: AtFuncStart as u32
1766
- }
1767
- ) ;
1768
-
1769
1900
self . compile_statements ( body) ?;
1770
1901
1771
1902
// Emit None at end:
@@ -1778,7 +1909,7 @@ impl Compiler<'_> {
1778
1909
}
1779
1910
}
1780
1911
1781
- let code = self . pop_code_object ( ) ;
1912
+ let code = self . exit_scope ( ) ;
1782
1913
self . ctx = prev_ctx;
1783
1914
1784
1915
// Prepare generic type parameters:
@@ -2030,7 +2161,7 @@ impl Compiler<'_> {
2030
2161
2031
2162
self . emit_return_value ( ) ;
2032
2163
2033
- let code = self . pop_code_object ( ) ;
2164
+ let code = self . exit_scope ( ) ;
2034
2165
self . ctx = prev_ctx;
2035
2166
2036
2167
emit ! ( self , Instruction :: LoadBuildClass ) ;
@@ -3820,7 +3951,7 @@ impl Compiler<'_> {
3820
3951
3821
3952
self . compile_expression ( body) ?;
3822
3953
self . emit_return_value ( ) ;
3823
- let code = self . pop_code_object ( ) ;
3954
+ let code = self . exit_scope ( ) ;
3824
3955
if self . build_closure ( & code) {
3825
3956
func_flags |= bytecode:: MakeFunctionFlags :: CLOSURE ;
3826
3957
}
@@ -4369,7 +4500,7 @@ impl Compiler<'_> {
4369
4500
self . emit_return_value ( ) ;
4370
4501
4371
4502
// Fetch code for listcomp function:
4372
- let code = self . pop_code_object ( ) ;
4503
+ let code = self . exit_scope ( ) ;
4373
4504
4374
4505
self . ctx = prev_ctx;
4375
4506
@@ -5076,7 +5207,7 @@ mod tests {
5076
5207
. unwrap ( ) ;
5077
5208
let mut compiler = Compiler :: new ( opts, source_code, "<module>" . to_owned ( ) ) ;
5078
5209
compiler. compile_program ( & ast, symbol_table) . unwrap ( ) ;
5079
- compiler. pop_code_object ( )
5210
+ compiler. exit_scope ( )
5080
5211
}
5081
5212
5082
5213
macro_rules! assert_dis_snapshot {
0 commit comments