@@ -115,6 +115,75 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
115115 }
116116 }
117117
118+ TailCall { ref func, ref args, fn_span : _ } => {
119+ // FIXME: a lot of code here is duplicated with normal calls, can we refactor this?
120+ let old_frame_idx = self . frame_idx ( ) ;
121+ let func = self . eval_operand ( func, None ) ?;
122+ let args = self . eval_operands ( args) ?;
123+
124+ let fn_sig_binder = func. layout . ty . fn_sig ( * self . tcx ) ;
125+ let fn_sig =
126+ self . tcx . normalize_erasing_late_bound_regions ( self . param_env , fn_sig_binder) ;
127+ let extra_args = & args[ fn_sig. inputs ( ) . len ( ) ..] ;
128+ let extra_args =
129+ self . tcx . mk_type_list_from_iter ( extra_args. iter ( ) . map ( |arg| arg. layout . ty ) ) ;
130+
131+ let ( fn_val, fn_abi, with_caller_location) = match * func. layout . ty . kind ( ) {
132+ ty:: FnPtr ( _sig) => {
133+ let fn_ptr = self . read_pointer ( & func) ?;
134+ let fn_val = self . get_ptr_fn ( fn_ptr) ?;
135+ ( fn_val, self . fn_abi_of_fn_ptr ( fn_sig_binder, extra_args) ?, false )
136+ }
137+ ty:: FnDef ( def_id, substs) => {
138+ let instance = self . resolve ( def_id, substs) ?;
139+ (
140+ FnVal :: Instance ( instance) ,
141+ self . fn_abi_of_instance ( instance, extra_args) ?,
142+ instance. def . requires_caller_location ( * self . tcx ) ,
143+ )
144+ }
145+ _ => span_bug ! (
146+ terminator. source_info. span,
147+ "invalid callee of type {:?}" ,
148+ func. layout. ty
149+ ) ,
150+ } ;
151+
152+ // FIXME(explicit_tail_calls): maybe we need the drop here?...
153+
154+ // This is the "canonical" implementation of tails calls,
155+ // a pop of the current stack frame, followed by a normal call
156+ // which pushes a new stack frame, with the return address from
157+ // the popped stack frame.
158+ //
159+ // Note that we can't use `pop_stack_frame` as it "executes"
160+ // the goto to the return block, but we don't want to,
161+ // only the tail called function should return to the current
162+ // return block.
163+ let Some ( prev_frame) = self . stack_mut ( ) . pop ( )
164+ else { span_bug ! ( terminator. source_info. span, "empty stack while evaluating this tail call" ) } ;
165+
166+ let StackPopCleanup :: Goto { ret, unwind } = prev_frame. return_to_block
167+ else { span_bug ! ( terminator. source_info. span, "tail call with the root stack frame" ) } ;
168+
169+ self . eval_fn_call (
170+ fn_val,
171+ ( fn_sig. abi , fn_abi) ,
172+ & args,
173+ with_caller_location,
174+ & prev_frame. return_place ,
175+ ret,
176+ unwind,
177+ ) ?;
178+
179+ if self . frame_idx ( ) != old_frame_idx {
180+ span_bug ! (
181+ terminator. source_info. span,
182+ "evaluating this tail call pushed a new stack frame"
183+ ) ;
184+ }
185+ }
186+
118187 Drop { place, target, unwind, replace : _ } => {
119188 let frame = self . frame ( ) ;
120189 let ty = place. ty ( & frame. body . local_decls , * self . tcx ) . ty ;
0 commit comments