@@ -1028,13 +1028,16 @@ impl Compiler {
10281028 if preserve_tos {
10291029 emit ! ( self , Instruction :: Swap { index: 2 } ) ;
10301030 }
1031+ // Stack after swap: [..., return_value, __exit__] or [..., __exit__]
10311032
1032- // Call __exit__(None, None, None) - compiler_call_exit_with_nones
1033- // Stack : [..., __exit__] or [..., return_value, __exit__ ]
1033+ // Call __exit__(None, None, None)
1034+ // Call protocol : [callable, self_or_null, arg1, arg2, arg3 ]
10341035 emit ! ( self , Instruction :: PushNull ) ;
1036+ // Stack: [..., __exit__, NULL]
10351037 self . emit_load_const ( ConstantData :: None ) ;
10361038 self . emit_load_const ( ConstantData :: None ) ;
10371039 self . emit_load_const ( ConstantData :: None ) ;
1040+ // Stack: [..., __exit__, NULL, None, None, None]
10381041 emit ! ( self , Instruction :: Call { nargs: 3 } ) ;
10391042
10401043 // For async with, await the result
@@ -2088,6 +2091,8 @@ impl Compiler {
20882091 Ok ( ( ) )
20892092 }
20902093
2094+ /// Push decorators onto the stack in source order.
2095+ /// For @dec1 @dec2 def foo(): stack becomes [dec1, NULL, dec2, NULL]
20912096 fn prepare_decorators ( & mut self , decorator_list : & [ Decorator ] ) -> CompileResult < ( ) > {
20922097 for decorator in decorator_list {
20932098 self . compile_expression ( & decorator. expression ) ?;
@@ -2096,8 +2101,11 @@ impl Compiler {
20962101 Ok ( ( ) )
20972102 }
20982103
2104+ /// Apply decorators in reverse order (LIFO from stack).
2105+ /// Stack [dec1, NULL, dec2, NULL, func] -> dec2(func) -> dec1(dec2(func))
2106+ /// The forward loop works because each Call pops from TOS, naturally
2107+ /// applying decorators bottom-up (innermost first).
20992108 fn apply_decorators ( & mut self , decorator_list : & [ Decorator ] ) {
2100- // Apply decorators - each pops [decorator, NULL, arg] and pushes result
21012109 for _ in decorator_list {
21022110 emit ! ( self , Instruction :: Call { nargs: 1 } ) ;
21032111 }
@@ -3300,21 +3308,29 @@ impl Compiler {
33003308 // Make closure for type params code
33013309 self . make_closure ( type_params_code, bytecode:: MakeFunctionFlags :: empty ( ) ) ?;
33023310
3303- // Call the closure
3304- // Call expects stack: [callable, self_or_null, arg1, ..., argN]
3311+ // Call the type params closure with defaults/kwdefaults as arguments.
3312+ // Call protocol: [callable, self_or_null, arg1, ..., argN]
3313+ // We need to reorder: [args..., closure] -> [closure, NULL, args...]
3314+ // Using Swap operations to move closure down and insert NULL.
3315+ // Note: num_typeparam_args is at most 2 (defaults tuple, kwdefaults dict).
33053316 if num_typeparam_args > 0 {
3306- // Stack: [arg1, ..., argN, closure]
3307- // Need: [closure, NULL, arg1, ..., argN]
3308- let reverse_amount = ( num_typeparam_args + 1 ) as u32 ;
3309- emit ! (
3310- self ,
3311- Instruction :: Reverse {
3312- amount: reverse_amount
3317+ match num_typeparam_args {
3318+ 1 => {
3319+ // Stack: [arg1, closure]
3320+ emit ! ( self , Instruction :: Swap { index: 2 } ) ; // [closure, arg1]
3321+ emit ! ( self , Instruction :: PushNull ) ; // [closure, arg1, NULL]
3322+ emit ! ( self , Instruction :: Swap { index: 2 } ) ; // [closure, NULL, arg1]
33133323 }
3314- ) ;
3315- // Stack: [closure, argN, ..., arg1]
3316- emit ! ( self , Instruction :: PushNull ) ;
3317- // Stack: [closure, argN, ..., arg1, NULL]
3324+ 2 => {
3325+ // Stack: [arg1, arg2, closure]
3326+ emit ! ( self , Instruction :: Swap { index: 3 } ) ; // [closure, arg2, arg1]
3327+ emit ! ( self , Instruction :: Swap { index: 2 } ) ; // [closure, arg1, arg2]
3328+ emit ! ( self , Instruction :: PushNull ) ; // [closure, arg1, arg2, NULL]
3329+ emit ! ( self , Instruction :: Swap { index: 3 } ) ; // [closure, NULL, arg2, arg1]
3330+ emit ! ( self , Instruction :: Swap { index: 2 } ) ; // [closure, NULL, arg1, arg2]
3331+ }
3332+ _ => unreachable ! ( "only defaults and kwdefaults are supported" ) ,
3333+ }
33183334 emit ! (
33193335 self ,
33203336 Instruction :: Call {
@@ -3325,7 +3341,6 @@ impl Compiler {
33253341 // Stack: [closure]
33263342 emit ! ( self , Instruction :: PushNull ) ;
33273343 // Stack: [closure, NULL]
3328- // Call pops: args (0), then self_or_null (NULL), then callable (closure)
33293344 emit ! ( self , Instruction :: Call { nargs: 0 } ) ;
33303345 }
33313346 }
@@ -3705,49 +3720,91 @@ impl Compiler {
37053720 self . make_closure ( class_code, func_flags) ?;
37063721 self . emit_load_const ( ConstantData :: Str { value : name. into ( ) } ) ;
37073722
3708- // Compile original bases
3709- let base_count = if let Some ( arguments) = arguments {
3710- for arg in & arguments. args {
3711- self . compile_expression ( arg) ?;
3723+ // Compile bases and call __build_class__
3724+ // Check for starred bases or **kwargs
3725+ let has_starred = arguments
3726+ . is_some_and ( |args| args. args . iter ( ) . any ( |arg| matches ! ( arg, Expr :: Starred ( _) ) ) ) ;
3727+ let has_double_star =
3728+ arguments. is_some_and ( |args| args. keywords . iter ( ) . any ( |kw| kw. arg . is_none ( ) ) ) ;
3729+
3730+ if has_starred || has_double_star {
3731+ // Use CallFunctionEx for *bases or **kwargs
3732+ // Stack has: [__build_class__, NULL, class_func, name]
3733+ // Need to build: args tuple = (class_func, name, *bases, .generic_base)
3734+
3735+ // Compile bases with gather_elements (handles starred)
3736+ let ( size, unpack) = if let Some ( arguments) = arguments {
3737+ self . gather_elements ( 2 , & arguments. args ) ? // 2 = class_func + name already on stack
3738+ } else {
3739+ // Just class_func and name (no bases)
3740+ ( 2 , false )
3741+ } ;
3742+
3743+ // Add .generic_base as final base
3744+ emit ! ( self , Instruction :: LoadName ( dot_generic_base) ) ;
3745+
3746+ // Build args tuple
3747+ if unpack {
3748+ // Starred: gather_elements produced tuples on stack
3749+ emit ! ( self , Instruction :: BuildTuple { size: 1 } ) ; // (.generic_base,)
3750+ emit ! ( self , Instruction :: BuildTupleFromTuples { size: size + 1 } ) ;
3751+ } else {
3752+ // No starred: individual elements on stack
3753+ // size includes class_func + name + bases count, +1 for .generic_base
3754+ emit ! ( self , Instruction :: BuildTuple { size: size + 1 } ) ;
3755+ }
3756+
3757+ // Build kwargs if needed
3758+ let has_kwargs = arguments. is_some_and ( |args| !args. keywords . is_empty ( ) ) ;
3759+ if has_kwargs {
3760+ self . compile_keywords ( & arguments. unwrap ( ) . keywords ) ?;
37123761 }
3713- arguments . args . len ( )
3762+ emit ! ( self , Instruction :: CallFunctionEx { has_kwargs } ) ;
37143763 } else {
3715- 0
3716- } ;
3764+ // Simple case: no starred bases, no **kwargs
3765+ // Compile bases normally
3766+ let base_count = if let Some ( arguments) = arguments {
3767+ for arg in & arguments. args {
3768+ self . compile_expression ( arg) ?;
3769+ }
3770+ arguments. args . len ( )
3771+ } else {
3772+ 0
3773+ } ;
37173774
3718- // Load .generic_base as the last base
3719- emit ! ( self , Instruction :: LoadName ( dot_generic_base) ) ;
3775+ // Load .generic_base as the last base
3776+ emit ! ( self , Instruction :: LoadName ( dot_generic_base) ) ;
37203777
3721- let nargs = 2 + u32:: try_from ( base_count) . expect ( "too many base classes" ) + 1 ; // function, name, bases..., generic_base
3778+ let nargs = 2 + u32:: try_from ( base_count) . expect ( "too many base classes" ) + 1 ;
37223779
3723- // Handle keyword arguments
3724- if let Some ( arguments) = arguments
3725- && !arguments. keywords . is_empty ( )
3726- {
3727- let mut kwarg_names = vec ! [ ] ;
3728- for keyword in & arguments. keywords {
3729- let name = keyword
3730- . arg
3731- . as_ref ( )
3732- . expect ( "keyword argument name must be set" ) ;
3733- kwarg_names. push ( ConstantData :: Str {
3734- value : name. as_str ( ) . into ( ) ,
3780+ // Handle keyword arguments (no **kwargs here)
3781+ if let Some ( arguments) = arguments
3782+ && !arguments. keywords . is_empty ( )
3783+ {
3784+ let mut kwarg_names = vec ! [ ] ;
3785+ for keyword in & arguments. keywords {
3786+ let name = keyword. arg . as_ref ( ) . expect (
3787+ "keyword argument name must be set (no **kwargs in this branch)" ,
3788+ ) ;
3789+ kwarg_names. push ( ConstantData :: Str {
3790+ value : name. as_str ( ) . into ( ) ,
3791+ } ) ;
3792+ self . compile_expression ( & keyword. value ) ?;
3793+ }
3794+ self . emit_load_const ( ConstantData :: Tuple {
3795+ elements : kwarg_names,
37353796 } ) ;
3736- self . compile_expression ( & keyword. value ) ?;
3797+ emit ! (
3798+ self ,
3799+ Instruction :: CallKw {
3800+ nargs: nargs
3801+ + u32 :: try_from( arguments. keywords. len( ) )
3802+ . expect( "too many keyword arguments" )
3803+ }
3804+ ) ;
3805+ } else {
3806+ emit ! ( self , Instruction :: Call { nargs } ) ;
37373807 }
3738- self . emit_load_const ( ConstantData :: Tuple {
3739- elements : kwarg_names,
3740- } ) ;
3741- emit ! (
3742- self ,
3743- Instruction :: CallKw {
3744- nargs: nargs
3745- + u32 :: try_from( arguments. keywords. len( ) )
3746- . expect( "too many keyword arguments" )
3747- }
3748- ) ;
3749- } else {
3750- emit ! ( self , Instruction :: Call { nargs } ) ;
37513808 }
37523809
37533810 // Return the created class
0 commit comments