]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir/src/interpret/terminator.rs
New upstream version 1.56.0~beta.4+dfsg1
[rustc.git] / compiler / rustc_mir / src / interpret / terminator.rs
CommitLineData
b7449926 1use std::borrow::Cow;
ba9703b0 2use std::convert::TryFrom;
b7449926 3
17df50a5
XL
4use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
5use rustc_middle::ty::layout::{self, TyAndLayout};
ba9703b0 6use rustc_middle::ty::Instance;
17df50a5
XL
7use rustc_middle::{
8 mir,
9 ty::{self, Ty},
10};
ba9703b0 11use rustc_target::abi::{self, LayoutOf as _};
b7449926
XL
12use rustc_target::spec::abi::Abi;
13
b7449926 14use super::{
136023e0
XL
15 FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, Scalar,
16 StackPopCleanup, StackPopUnwind,
b7449926
XL
17};
18
ba9703b0 19impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
17df50a5 20 fn fn_can_unwind(&self, attrs: CodegenFnAttrFlags, abi: Abi) -> bool {
94222f64 21 layout::fn_can_unwind(*self.tcx, attrs, abi)
17df50a5
XL
22 }
23
b7449926
XL
24 pub(super) fn eval_terminator(
25 &mut self,
26 terminator: &mir::Terminator<'tcx>,
dc9dc135 27 ) -> InterpResult<'tcx> {
ba9703b0 28 use rustc_middle::mir::TerminatorKind::*;
b7449926
XL
29 match terminator.kind {
30 Return => {
60c5eb7d 31 self.pop_stack_frame(/* unwinding */ false)?
b7449926
XL
32 }
33
60c5eb7d 34 Goto { target } => self.go_to_block(target),
b7449926 35
29967ef6 36 SwitchInt { ref discr, ref targets, switch_ty } => {
6a06907d 37 let discr = self.read_immediate(&self.eval_operand(discr, None)?)?;
b7449926 38 trace!("SwitchInt({:?})", *discr);
f035d41b 39 assert_eq!(discr.layout.ty, switch_ty);
b7449926
XL
40
41 // Branch to the `otherwise` case by default, if no match is found.
29967ef6
XL
42 assert!(!targets.iter().is_empty());
43 let mut target_block = targets.otherwise();
b7449926 44
29967ef6 45 for (const_int, target) in targets.iter() {
b7449926 46 // Compare using binary_op, to also support pointer values
dfeec247
XL
47 let res = self
48 .overflowing_binary_op(
49 mir::BinOp::Eq,
6a06907d
XL
50 &discr,
51 &ImmTy::from_uint(const_int, discr.layout),
dfeec247
XL
52 )?
53 .0;
b7449926 54 if res.to_bool()? {
29967ef6 55 target_block = target;
b7449926
XL
56 break;
57 }
58 }
59
60c5eb7d 60 self.go_to_block(target_block);
b7449926
XL
61 }
62
f035d41b 63 Call { ref func, ref args, destination, ref cleanup, from_hir_call: _, fn_span: _ } => {
ba9703b0 64 let old_stack = self.frame_idx();
f9f354fc 65 let old_loc = self.frame().loc;
b7449926 66 let func = self.eval_operand(func, None)?;
17df50a5 67 let (fn_val, abi, caller_can_unwind) = match *func.layout.ty.kind() {
b7449926
XL
68 ty::FnPtr(sig) => {
69 let caller_abi = sig.abi();
136023e0 70 let fn_ptr = self.read_pointer(&func)?;
94222f64 71 let fn_val = self.memory.get_fn(fn_ptr)?;
17df50a5
XL
72 (
73 fn_val,
74 caller_abi,
94222f64 75 self.fn_can_unwind(CodegenFnAttrFlags::empty(), caller_abi),
17df50a5 76 )
b7449926
XL
77 }
78 ty::FnDef(def_id, substs) => {
79 let sig = func.layout.ty.fn_sig(*self.tcx);
1b1a35ee
XL
80 (
81 FnVal::Instance(
82 self.resolve(ty::WithOptConstParam::unknown(def_id), substs)?,
83 ),
84 sig.abi(),
17df50a5 85 self.fn_can_unwind(self.tcx.codegen_fn_attrs(def_id).flags, sig.abi()),
1b1a35ee 86 )
b7449926 87 }
ba9703b0
XL
88 _ => span_bug!(
89 terminator.source_info.span,
90 "invalid callee of type {:?}",
91 func.layout.ty
92 ),
b7449926
XL
93 };
94 let args = self.eval_operands(args)?;
6a06907d 95 let dest_place;
60c5eb7d 96 let ret = match destination {
6a06907d
XL
97 Some((dest, ret)) => {
98 dest_place = self.eval_place(dest)?;
99 Some((&dest_place, ret))
100 }
60c5eb7d
XL
101 None => None,
102 };
17df50a5
XL
103 self.eval_fn_call(
104 fn_val,
105 abi,
106 &args[..],
107 ret,
108 match (cleanup, caller_can_unwind) {
109 (Some(cleanup), true) => StackPopUnwind::Cleanup(*cleanup),
110 (None, true) => StackPopUnwind::Skip,
111 (_, false) => StackPopUnwind::NotAllowed,
112 },
113 )?;
ba9703b0
XL
114 // Sanity-check that `eval_fn_call` either pushed a new frame or
115 // did a jump to another block.
f9f354fc 116 if self.frame_idx() == old_stack && self.frame().loc == old_loc {
ba9703b0
XL
117 span_bug!(terminator.source_info.span, "evaluating this call made no progress");
118 }
b7449926
XL
119 }
120
f035d41b
XL
121 Drop { place, target, unwind } => {
122 let place = self.eval_place(place)?;
b7449926 123 let ty = place.layout.ty;
f035d41b 124 trace!("TerminatorKind::drop: {:?}, type {}", place, ty);
b7449926 125
dc9dc135 126 let instance = Instance::resolve_drop_in_place(*self.tcx, ty);
6a06907d 127 self.drop_in_place(&place, instance, target, unwind)?;
b7449926
XL
128 }
129
dfeec247
XL
130 Assert { ref cond, expected, ref msg, target, cleanup } => {
131 let cond_val =
6a06907d 132 self.read_immediate(&self.eval_operand(cond, None)?)?.to_scalar()?.to_bool()?;
b7449926 133 if expected == cond_val {
60c5eb7d 134 self.go_to_block(target);
b7449926 135 } else {
ba9703b0 136 M::assert_panic(self, msg, cleanup)?;
b7449926
XL
137 }
138 }
139
ba9703b0 140 Abort => {
fc512014 141 M::abort(self, "the program aborted execution".to_owned())?;
ba9703b0
XL
142 }
143
60c5eb7d
XL
144 // When we encounter Resume, we've finished unwinding
145 // cleanup for the current stack frame. We pop it in order
146 // to continue unwinding the next frame
147 Resume => {
148 trace!("unwinding: resuming from cleanup");
149 // By definition, a Resume terminator means
150 // that we're unwinding
151 self.pop_stack_frame(/* unwinding */ true)?;
dfeec247
XL
152 return Ok(());
153 }
60c5eb7d
XL
154
155 // It is UB to ever encounter this.
156 Unreachable => throw_ub!(Unreachable),
157
158 // These should never occur for MIR we actually run.
ba9703b0 159 DropAndReplace { .. }
f035d41b 160 | FalseEdge { .. }
ba9703b0
XL
161 | FalseUnwind { .. }
162 | Yield { .. }
163 | GeneratorDrop => span_bug!(
164 terminator.source_info.span,
165 "{:#?} should have been eliminated by MIR pass",
166 terminator.kind
167 ),
f9f354fc
XL
168
169 // Inline assembly can't be interpreted.
170 InlineAsm { .. } => throw_unsup_format!("inline assembly is not supported"),
b7449926
XL
171 }
172
173 Ok(())
174 }
175
176 fn check_argument_compat(
a1dfa0c6 177 rust_abi: bool,
ba9703b0
XL
178 caller: TyAndLayout<'tcx>,
179 callee: TyAndLayout<'tcx>,
b7449926
XL
180 ) -> bool {
181 if caller.ty == callee.ty {
182 // No question
183 return true;
184 }
a1dfa0c6
XL
185 if !rust_abi {
186 // Don't risk anything
187 return false;
188 }
b7449926
XL
189 // Compare layout
190 match (&caller.abi, &callee.abi) {
a1dfa0c6
XL
191 // Different valid ranges are okay (once we enforce validity,
192 // that will take care to make it UB to leave the range, just
193 // like for transmute).
ba9703b0 194 (abi::Abi::Scalar(ref caller), abi::Abi::Scalar(ref callee)) => {
dfeec247
XL
195 caller.value == callee.value
196 }
197 (
ba9703b0
XL
198 abi::Abi::ScalarPair(ref caller1, ref caller2),
199 abi::Abi::ScalarPair(ref callee1, ref callee2),
dfeec247 200 ) => caller1.value == callee1.value && caller2.value == callee2.value,
b7449926 201 // Be conservative
dfeec247 202 _ => false,
b7449926
XL
203 }
204 }
205
206 /// Pass a single argument, checking the types for compatibility.
207 fn pass_argument(
208 &mut self,
a1dfa0c6 209 rust_abi: bool,
dfeec247 210 caller_arg: &mut impl Iterator<Item = OpTy<'tcx, M::PointerTag>>,
6a06907d 211 callee_arg: &PlaceTy<'tcx, M::PointerTag>,
dc9dc135 212 ) -> InterpResult<'tcx> {
a1dfa0c6 213 if rust_abi && callee_arg.layout.is_zst() {
b7449926
XL
214 // Nothing to do.
215 trace!("Skipping callee ZST");
216 return Ok(());
217 }
ba9703b0
XL
218 let caller_arg = caller_arg.next().ok_or_else(|| {
219 err_ub_format!("calling a function with fewer arguments than it requires")
220 })?;
a1dfa0c6 221 if rust_abi {
74b04a01 222 assert!(!caller_arg.layout.is_zst(), "ZSTs must have been already filtered out");
b7449926
XL
223 }
224 // Now, check
a1dfa0c6 225 if !Self::check_argument_compat(rust_abi, caller_arg.layout, callee_arg.layout) {
ba9703b0
XL
226 throw_ub_format!(
227 "calling a function with argument of type {:?} passing data of type {:?}",
228 callee_arg.layout.ty,
229 caller_arg.layout.ty
230 )
b7449926 231 }
0bf4aa26 232 // We allow some transmutes here
6a06907d 233 self.copy_op_transmute(&caller_arg, callee_arg)
b7449926
XL
234 }
235
236 /// Call this function -- pushing the stack frame and initializing the arguments.
237 fn eval_fn_call(
238 &mut self,
416331ca 239 fn_val: FnVal<'tcx, M::ExtraFnVal>,
b7449926 240 caller_abi: Abi,
0bf4aa26 241 args: &[OpTy<'tcx, M::PointerTag>],
6a06907d 242 ret: Option<(&PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>,
17df50a5 243 mut unwind: StackPopUnwind,
dc9dc135 244 ) -> InterpResult<'tcx> {
416331ca
XL
245 trace!("eval_fn_call: {:#?}", fn_val);
246
247 let instance = match fn_val {
248 FnVal::Instance(instance) => instance,
249 FnVal::Other(extra) => {
5869c6ff 250 return M::call_extra_fn(self, extra, caller_abi, args, ret, unwind);
416331ca
XL
251 }
252 };
b7449926 253
17df50a5
XL
254 let get_abi = |this: &Self, instance_ty: Ty<'tcx>| match instance_ty.kind() {
255 ty::FnDef(..) => instance_ty.fn_sig(*this.tcx).abi(),
256 ty::Closure(..) => Abi::RustCall,
257 ty::Generator(..) => Abi::Rust,
258 _ => span_bug!(this.cur_span(), "unexpected callee ty: {:?}", instance_ty),
259 };
260
60c5eb7d 261 // ABI check
17df50a5 262 let check_abi = |callee_abi: Abi| -> InterpResult<'tcx> {
60c5eb7d
XL
263 let normalize_abi = |abi| match abi {
264 Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic =>
dfeec247
XL
265 // These are all the same ABI, really.
266 {
267 Abi::Rust
268 }
269 abi => abi,
60c5eb7d
XL
270 };
271 if normalize_abi(caller_abi) != normalize_abi(callee_abi) {
ba9703b0 272 throw_ub_format!(
6a06907d
XL
273 "calling a function with ABI {} using caller ABI {}",
274 callee_abi.name(),
275 caller_abi.name()
ba9703b0 276 )
60c5eb7d 277 }
17df50a5
XL
278 Ok(())
279 };
60c5eb7d 280
b7449926
XL
281 match instance.def {
282 ty::InstanceDef::Intrinsic(..) => {
17df50a5
XL
283 if M::enforce_abi(self) {
284 check_abi(get_abi(self, instance.ty(*self.tcx, self.param_env)))?;
285 }
60c5eb7d 286 assert!(caller_abi == Abi::RustIntrinsic || caller_abi == Abi::PlatformIntrinsic);
ba9703b0 287 M::call_intrinsic(self, instance, args, ret, unwind)
b7449926 288 }
dfeec247
XL
289 ty::InstanceDef::VtableShim(..)
290 | ty::InstanceDef::ReifyShim(..)
291 | ty::InstanceDef::ClosureOnceShim { .. }
292 | ty::InstanceDef::FnPtrShim(..)
293 | ty::InstanceDef::DropGlue(..)
294 | ty::InstanceDef::CloneShim(..)
295 | ty::InstanceDef::Item(_) => {
b7449926 296 // We need MIR for this fn
5869c6ff
XL
297 let body =
298 match M::find_mir_or_eval_fn(self, instance, caller_abi, args, ret, unwind)? {
299 Some(body) => body,
300 None => return Ok(()),
301 };
b7449926 302
17df50a5
XL
303 // Check against the ABI of the MIR body we are calling (not the ABI of `instance`;
304 // these can differ when `find_mir_or_eval_fn` does something clever like resolve
305 // exported symbol names).
306 let callee_def_id = body.source.def_id();
307 let callee_abi = get_abi(self, self.tcx.type_of(callee_def_id));
308
309 if M::enforce_abi(self) {
310 check_abi(callee_abi)?;
311 }
312
313 if !matches!(unwind, StackPopUnwind::NotAllowed)
314 && !self
315 .fn_can_unwind(self.tcx.codegen_fn_attrs(callee_def_id).flags, callee_abi)
316 {
317 // The callee cannot unwind.
318 unwind = StackPopUnwind::NotAllowed;
319 }
320
b7449926
XL
321 self.push_stack_frame(
322 instance,
dc9dc135 323 body,
60c5eb7d 324 ret.map(|p| p.0),
dfeec247 325 StackPopCleanup::Goto { ret: ret.map(|p| p.1), unwind },
b7449926
XL
326 )?;
327
ba9703b0
XL
328 // If an error is raised here, pop the frame again to get an accurate backtrace.
329 // To this end, we wrap it all in a `try` block.
330 let res: InterpResult<'tcx> = try {
331 trace!(
332 "caller ABI: {:?}, args: {:#?}",
333 caller_abi,
334 args.iter()
335 .map(|arg| (arg.layout.ty, format!("{:?}", **arg)))
336 .collect::<Vec<_>>()
337 );
338 trace!(
339 "spread_arg: {:?}, locals: {:#?}",
340 body.spread_arg,
341 body.args_iter()
342 .map(|local| (
343 local,
344 self.layout_of_local(self.frame(), local, None).unwrap().ty
345 ))
346 .collect::<Vec<_>>()
347 );
348
349 // Figure out how to pass which arguments.
350 // The Rust ABI is special: ZST get skipped.
351 let rust_abi = match caller_abi {
352 Abi::Rust | Abi::RustCall => true,
353 _ => false,
354 };
355 // We have two iterators: Where the arguments come from,
356 // and where they go to.
357
358 // For where they come from: If the ABI is RustCall, we untuple the
359 // last incoming argument. These two iterators do not have the same type,
360 // so to keep the code paths uniform we accept an allocation
361 // (for RustCall ABI only).
362 let caller_args: Cow<'_, [OpTy<'tcx, M::PointerTag>]> =
363 if caller_abi == Abi::RustCall && !args.is_empty() {
364 // Untuple
6a06907d 365 let (untuple_arg, args) = args.split_last().unwrap();
ba9703b0
XL
366 trace!("eval_fn_call: Will pass last argument by untupling");
367 Cow::from(
368 args.iter()
369 .map(|&a| Ok(a))
370 .chain(
371 (0..untuple_arg.layout.fields.count())
372 .map(|i| self.operand_field(untuple_arg, i)),
373 )
374 .collect::<InterpResult<'_, Vec<OpTy<'tcx, M::PointerTag>>>>(
375 )?,
376 )
377 } else {
378 // Plain arg passing
379 Cow::from(args)
dfeec247 380 };
ba9703b0
XL
381 // Skip ZSTs
382 let mut caller_iter =
383 caller_args.iter().filter(|op| !rust_abi || !op.layout.is_zst()).copied();
384
385 // Now we have to spread them out across the callee's locals,
386 // taking into account the `spread_arg`. If we could write
387 // this is a single iterator (that handles `spread_arg`), then
388 // `pass_argument` would be the loop body. It takes care to
389 // not advance `caller_iter` for ZSTs.
390 for local in body.args_iter() {
391 let dest = self.eval_place(mir::Place::from(local))?;
392 if Some(local) == body.spread_arg {
393 // Must be a tuple
394 for i in 0..dest.layout.fields.count() {
6a06907d
XL
395 let dest = self.place_field(&dest, i)?;
396 self.pass_argument(rust_abi, &mut caller_iter, &dest)?;
b7449926 397 }
ba9703b0
XL
398 } else {
399 // Normal argument
6a06907d 400 self.pass_argument(rust_abi, &mut caller_iter, &dest)?;
b7449926 401 }
ba9703b0
XL
402 }
403 // Now we should have no more caller args
404 if caller_iter.next().is_some() {
405 throw_ub_format!("calling a function with more arguments than it expected")
406 }
407 // Don't forget to check the return type!
408 if let Some((caller_ret, _)) = ret {
409 let callee_ret = self.eval_place(mir::Place::return_place())?;
410 if !Self::check_argument_compat(
411 rust_abi,
412 caller_ret.layout,
413 callee_ret.layout,
414 ) {
415 throw_ub_format!(
416 "calling a function with return type {:?} passing \
417 return place of type {:?}",
418 callee_ret.layout.ty,
419 caller_ret.layout.ty
420 )
0bf4aa26 421 }
ba9703b0
XL
422 } else {
423 let local = mir::RETURN_PLACE;
424 let callee_layout = self.layout_of_local(self.frame(), local, None)?;
425 if !callee_layout.abi.is_uninhabited() {
426 throw_ub_format!("calling a returning function without a return place")
0bf4aa26 427 }
ba9703b0
XL
428 }
429 };
b7449926
XL
430 match res {
431 Err(err) => {
ba9703b0 432 self.stack_mut().pop();
b7449926
XL
433 Err(err)
434 }
ba9703b0 435 Ok(()) => Ok(()),
b7449926
XL
436 }
437 }
438 // cannot use the shim here, because that will only result in infinite recursion
439 ty::InstanceDef::Virtual(_, idx) => {
48663c56 440 let mut args = args.to_vec();
48663c56 441 // We have to implement all "object safe receivers". Currently we
1b1a35ee 442 // support built-in pointers `(&, &mut, Box)` as well as unsized-self. We do
48663c56 443 // not yet support custom self types.
1b1a35ee 444 // Also see `compiler/rustc_codegen_llvm/src/abi.rs` and `compiler/rustc_codegen_ssa/src/mir/block.rs`.
48663c56
XL
445 let receiver_place = match args[0].layout.ty.builtin_deref(true) {
446 Some(_) => {
447 // Built-in pointer.
6a06907d 448 self.deref_operand(&args[0])?
48663c56
XL
449 }
450 None => {
451 // Unsized self.
136023e0 452 args[0].assert_mem_place()
48663c56
XL
453 }
454 };
455 // Find and consult vtable
136023e0
XL
456 let vtable = self.scalar_to_ptr(receiver_place.vtable());
457 let fn_val = self.get_vtable_slot(vtable, u64::try_from(idx).unwrap())?;
b7449926 458
48663c56
XL
459 // `*mut receiver_place.layout.ty` is almost the layout that we
460 // want for args[0]: We have to project to field 0 because we want
461 // a thin pointer.
462 assert!(receiver_place.layout.is_unsized());
463 let receiver_ptr_ty = self.tcx.mk_mut_ptr(receiver_place.layout.ty);
94222f64 464 let this_receiver_ptr = self.layout_of(receiver_ptr_ty)?.field(self, 0);
48663c56 465 // Adjust receiver argument.
136023e0
XL
466 args[0] = OpTy::from(ImmTy::from_immediate(
467 Scalar::from_maybe_pointer(receiver_place.ptr, self).into(),
468 this_receiver_ptr,
469 ));
b7449926
XL
470 trace!("Patched self operand to {:#?}", args[0]);
471 // recurse with concrete function
136023e0 472 self.eval_fn_call(fn_val, caller_abi, &args, ret, unwind)
b7449926
XL
473 }
474 }
475 }
476
477 fn drop_in_place(
478 &mut self,
6a06907d 479 place: &PlaceTy<'tcx, M::PointerTag>,
b7449926 480 instance: ty::Instance<'tcx>,
b7449926 481 target: mir::BasicBlock,
dfeec247 482 unwind: Option<mir::BasicBlock>,
dc9dc135 483 ) -> InterpResult<'tcx> {
b7449926
XL
484 trace!("drop_in_place: {:?},\n {:?}, {:?}", *place, place.layout.ty, instance);
485 // We take the address of the object. This may well be unaligned, which is fine
486 // for us here. However, unaligned accesses will probably make the actual drop
487 // implementation fail -- a problem shared by rustc.
488 let place = self.force_allocation(place)?;
489
1b1a35ee 490 let (instance, place) = match place.layout.ty.kind() {
b7449926
XL
491 ty::Dynamic(..) => {
492 // Dropping a trait object.
6a06907d 493 self.unpack_dyn_trait(&place)?
b7449926
XL
494 }
495 _ => (instance, place),
496 };
497
ba9703b0 498 let arg = ImmTy::from_immediate(
136023e0 499 place.to_ref(self),
ba9703b0
XL
500 self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
501 );
b7449926
XL
502
503 let ty = self.tcx.mk_unit(); // return type is ()
136023e0 504 let dest = MPlaceTy::dangling(self.layout_of(ty)?);
b7449926
XL
505
506 self.eval_fn_call(
416331ca 507 FnVal::Instance(instance),
b7449926 508 Abi::Rust,
9fa01778 509 &[arg.into()],
6a06907d 510 Some((&dest.into(), target)),
17df50a5
XL
511 match unwind {
512 Some(cleanup) => StackPopUnwind::Cleanup(cleanup),
513 None => StackPopUnwind::Skip,
514 },
b7449926
XL
515 )
516 }
517}