diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index c70b957da130e..3fd414579e6bb 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -886,24 +886,13 @@ fn inline_call<'tcx, I: Inliner<'tcx>>( // Place could result in two different locations if `f` // writes to `i`. To prevent this we need to create a temporary // borrow of the place and pass the destination as `*temp` instead. - fn dest_needs_borrow(place: Place<'_>) -> bool { - for elem in place.projection.iter() { - match elem { - ProjectionElem::Deref | ProjectionElem::Index(_) => return true, - _ => {} - } - } - - false - } - - let dest = if dest_needs_borrow(destination) { + // + // This must be a raw pointer: a mutable reference could be invalidated by + // reading the arguments later, and would be invalid if the destination type + // is uninhabited. + let dest = if !destination.is_stable_offset() { trace!("creating temp for return destination"); - let dest = Rvalue::Ref( - tcx.lifetimes.re_erased, - BorrowKind::Mut { kind: MutBorrowKind::Default }, - destination, - ); + let dest = Rvalue::RawPtr(RawPtrKind::Mut, destination); let dest_ty = dest.ty(caller_body, tcx); let temp = Place::from(new_call_temp(caller_body, callsite, dest_ty, return_block)); caller_body[callsite.block].statements.push(Statement::new( diff --git a/tests/mir-opt/inline/indirect_destination.rs b/tests/mir-opt/inline/indirect_destination.rs index 4246eef08f704..08e34419bdf7a 100644 --- a/tests/mir-opt/inline/indirect_destination.rs +++ b/tests/mir-opt/inline/indirect_destination.rs @@ -11,7 +11,7 @@ use core::intrinsics::mir::*; // CHECK-LABEL: fn f( // CHECK: bb1: { // CHECK-NEXT: StorageLive([[A:.*]]); -// CHECK-NEXT: [[A]] = &mut (*_1); +// CHECK-NEXT: [[A]] = &raw mut (*_1); // CHECK-NEXT: StorageLive([[B:.*]]); // CHECK-NEXT: [[B]] = const 42_u8; // CHECK-NEXT: (*[[A]]) = move [[B]]; @@ -40,3 +40,54 @@ fn g() -> u8 { } } } + +// Reading the argument must not invalidate the saved destination pointer. +#[custom_mir(dialect = "runtime", phase = "initial")] +// CHECK-LABEL: fn indexed( +// CHECK: [[DEST:_[0-9]+]] = &raw mut _2[_1]; +// CHECK: [[ARG:_[0-9]+]] = copy _2[_1]; +// CHECK-NEXT: [[RET:_[0-9]+]] = copy [[ARG]]; +// CHECK-NEXT: (*[[DEST]]) = move [[RET]]; +pub fn indexed(index: usize) -> u32 { + mir! { + let values: [u32; 2]; + { + values = [41, 99]; + Call(values[index] = identity(values[index]), ReturnTo(done), UnwindContinue()) + } + done = { + RET = values[index]; + Return() + } + } +} + +#[inline(always)] +fn identity(value: u32) -> u32 { + value +} + +// Saving an uninhabited destination must not construct an invalid reference. +#[custom_mir(dialect = "runtime", phase = "initial")] +// CHECK-LABEL: fn uninhabited( +// CHECK: [[PTR:_[0-9]+]] = &raw mut _1; +// CHECK: [[DEST:_[0-9]+]] = &raw mut (*[[PTR]]); +// CHECK: panic_fmt +pub fn uninhabited() { + mir! { + let slot: !; + let ptr: *mut !; + { + ptr = &raw mut slot; + Call(*ptr = fail(), ReturnTo(done), UnwindContinue()) + } + done = { + Return() + } + } +} + +#[inline(always)] +fn fail() -> ! { + panic!("expected panic") +}