@@ -936,8 +936,17 @@ pub fn exec_tailcall(vm: &mut LuaVM, instr: u32) -> LuaResult<DispatchAction> {
936936 let a = Instruction :: get_a ( instr) as usize ;
937937 let b = Instruction :: get_b ( instr) as usize ;
938938
939- let frame = vm. frames . last ( ) . unwrap ( ) ;
940- let base = frame. base_ptr ;
939+ // Extract all frame information we'll need BEFORE taking mutable references
940+ let ( base, return_count, result_reg, function_value, pc) = {
941+ let frame = vm. frames . last ( ) . unwrap ( ) ;
942+ (
943+ frame. base_ptr ,
944+ frame. get_num_results ( ) ,
945+ frame. get_result_reg ( ) ,
946+ frame. function_value ,
947+ frame. pc ,
948+ )
949+ } ;
941950
942951 // Get function from R[A]
943952 let func = vm. register_stack [ base + a] ;
@@ -973,8 +982,8 @@ pub fn exec_tailcall(vm: &mut LuaVM, instr: u32) -> LuaResult<DispatchAction> {
973982 let max_stack_size = unsafe { ( * func_ptr) . borrow ( ) . chunk . max_stack_size } ;
974983
975984 // Pop current frame (tail call optimization)
976- let old_base = frame . base_ptr ;
977- let return_count = frame . get_num_results ( ) ;
985+ let old_base = base ; // Already extracted
986+ // return_count already extracted
978987 vm. frames . pop ( ) ;
979988
980989 // Create new frame at same location
@@ -1004,31 +1013,51 @@ pub fn exec_tailcall(vm: &mut LuaVM, instr: u32) -> LuaResult<DispatchAction> {
10041013 // C function: cannot use tail call optimization
10051014 // Convert to regular call
10061015
1007- // Get return information before function call
1008- let return_count = frame. get_num_results ( ) ;
1009- let result_reg = frame. get_result_reg ( ) ;
1010- let caller_base = frame. base_ptr ;
1016+ // CRITICAL: return_count, result_reg, caller_base already extracted
1017+ // These tell us where to write results in the CALLER's frame
1018+ let args_len = args. len ( ) ;
1019+
1020+ // IMPORTANT: For TAILCALL, we need the PARENT frame's base
1021+ // The current frame IS the tail-calling function
1022+ // When we pop it, we return to the parent
1023+ // result_reg is relative to parent's base
10111024
10121025 let c_func = func. as_cfunction ( )
10131026 . ok_or_else ( || LuaError :: RuntimeError ( "Invalid C function" . to_string ( ) ) ) ?;
10141027
1015- // Store function and arguments in registers for C function call
1016- vm. register_stack [ base + a] = func;
1028+ // Create temporary C function frame
1029+ let frame_id = vm. next_frame_id ;
1030+ vm. next_frame_id += 1 ;
1031+
1032+ // Set up arguments in a temporary stack space
1033+ let call_base = vm. register_stack . len ( ) ;
1034+ vm. ensure_stack_capacity ( call_base + args_len + 1 ) ;
1035+
1036+ vm. register_stack [ call_base] = func;
10171037 for ( i, arg) in args. iter ( ) . enumerate ( ) {
1018- vm. register_stack [ base + a + 1 + i] = * arg;
1038+ vm. register_stack [ call_base + 1 + i] = * arg;
10191039 }
1020-
1021- // Update frame top to include all arguments
1022- vm. current_frame_mut ( ) . top = a + 1 + args. len ( ) ;
1023-
1024- // Call C function
1040+
1041+ let temp_frame = LuaCallFrame :: new_c_function (
1042+ frame_id,
1043+ function_value,
1044+ pc,
1045+ call_base,
1046+ args_len + 1 ,
1047+ ) ;
1048+
1049+ // Push temp frame and call C function
1050+ vm. frames . push ( temp_frame) ;
10251051 let result = c_func ( vm) ?;
1026-
1027- // Pop current frame
1052+ vm. frames . pop ( ) ; // Pop temp frame
1053+
1054+ // NOW pop the tail-calling function's frame
10281055 vm. frames . pop ( ) ;
10291056
1030- // Write return values if there's a caller frame
1057+ // Write return values to PARENT frame
1058+ // CRITICAL: result_reg is relative to PARENT's base_ptr!
10311059 if !vm. frames . is_empty ( ) {
1060+ let parent_base = vm. current_frame ( ) . base_ptr ;
10321061 let vals = result. all_values ( ) ;
10331062 let count = if return_count == usize:: MAX {
10341063 vals. len ( )
@@ -1038,17 +1067,19 @@ pub fn exec_tailcall(vm: &mut LuaVM, instr: u32) -> LuaResult<DispatchAction> {
10381067
10391068 // Write return values
10401069 for i in 0 ..count {
1041- vm. register_stack [ caller_base + result_reg + i] = vals[ i] ;
1070+ vm. register_stack [ parent_base + result_reg + i] = vals[ i] ;
10421071 }
10431072
10441073 // Fill remaining expected values with nil
10451074 if return_count != usize:: MAX {
10461075 for i in count..return_count {
1047- vm. register_stack [ caller_base + result_reg + i] = LuaValue :: nil ( ) ;
1076+ vm. register_stack [ parent_base + result_reg + i] = LuaValue :: nil ( ) ;
10481077 }
10491078 }
1050-
1051- // No need to restore top, it will be handled by the next instruction
1079+
1080+ // CRITICAL: Update parent frame's top to reflect the number of return values
1081+ // This is essential for variable returns (return_count == usize::MAX)
1082+ vm. current_frame_mut ( ) . top = result_reg + count;
10521083 }
10531084
10541085 Ok ( DispatchAction :: Continue )
0 commit comments