@@ -115,6 +115,75 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
115
115
}
116
116
}
117
117
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
+
118
187
Drop { place, target, unwind, replace : _ } => {
119
188
let frame = self . frame ( ) ;
120
189
let ty = place. ty ( & frame. body . local_decls , * self . tcx ) . ty ;
0 commit comments