Skip to content

Commit 61fbc6a

Browse files
authored
Rollup merge of #71005 - jonas-schievink:no-place-like-return, r=oli-obk
Reading from the return place is fine Const eval thinks that reading from local `_0` is UB, but it isn't. `_0` is just a normal local like any other, and codegen handles it that way too. The only special thing is that the `Return` terminator will read from it. I've hit these errors while working on an NRVO pass that can merge other locals with `_0` in #71003. r? @oli-obk
2 parents 66f7a5d + 415fd0c commit 61fbc6a

File tree

13 files changed

+86
-156
lines changed

13 files changed

+86
-156
lines changed

Diff for: src/librustc_middle/mir/interpret/error.rs

-3
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,6 @@ pub enum UndefinedBehaviorInfo {
361361
InvalidUndefBytes(Option<Pointer>),
362362
/// Working with a local that is not currently live.
363363
DeadLocal,
364-
/// Trying to read from the return place of a function.
365-
ReadFromReturnPlace,
366364
}
367365

368366
impl fmt::Debug for UndefinedBehaviorInfo {
@@ -424,7 +422,6 @@ impl fmt::Debug for UndefinedBehaviorInfo {
424422
"using uninitialized data, but this operation requires initialized memory"
425423
),
426424
DeadLocal => write!(f, "accessing a dead local variable"),
427-
ReadFromReturnPlace => write!(f, "reading from return place"),
428425
}
429426
}
430427
}

Diff for: src/librustc_middle/mir/interpret/value.rs

-5
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,6 @@ impl<'tcx, Tag> Scalar<Tag> {
188188
}
189189
}
190190

191-
#[inline]
192-
pub fn null_ptr(cx: &impl HasDataLayout) -> Self {
193-
Scalar::Raw { data: 0, size: cx.data_layout().pointer_size.bytes() as u8 }
194-
}
195-
196191
#[inline]
197192
pub fn zst() -> Self {
198193
Scalar::Raw { data: 0, size: 0 }

Diff for: src/librustc_mir/interpret/eval_context.rs

+31-45
Original file line numberDiff line numberDiff line change
@@ -628,35 +628,30 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
628628
let frame = M::init_frame_extra(self, pre_frame)?;
629629
self.stack_mut().push(frame);
630630

631-
// don't allocate at all for trivial constants
632-
if body.local_decls.len() > 1 {
633-
// Locals are initially uninitialized.
634-
let dummy = LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) };
635-
let mut locals = IndexVec::from_elem(dummy, &body.local_decls);
636-
// Return place is handled specially by the `eval_place` functions, and the
637-
// entry in `locals` should never be used. Make it dead, to be sure.
638-
locals[mir::RETURN_PLACE].value = LocalValue::Dead;
639-
// Now mark those locals as dead that we do not want to initialize
640-
match self.tcx.def_kind(instance.def_id()) {
641-
// statics and constants don't have `Storage*` statements, no need to look for them
642-
//
643-
// FIXME: The above is likely untrue. See
644-
// <https://github.com/rust-lang/rust/pull/70004#issuecomment-602022110>. Is it
645-
// okay to ignore `StorageDead`/`StorageLive` annotations during CTFE?
646-
Some(DefKind::Static | DefKind::Const | DefKind::AssocConst) => {}
647-
_ => {
648-
// Mark locals that use `Storage*` annotations as dead on function entry.
649-
let always_live = AlwaysLiveLocals::new(self.body());
650-
for local in locals.indices() {
651-
if !always_live.contains(local) {
652-
locals[local].value = LocalValue::Dead;
653-
}
631+
// Locals are initially uninitialized.
632+
let dummy = LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) };
633+
let mut locals = IndexVec::from_elem(dummy, &body.local_decls);
634+
635+
// Now mark those locals as dead that we do not want to initialize
636+
match self.tcx.def_kind(instance.def_id()) {
637+
// statics and constants don't have `Storage*` statements, no need to look for them
638+
//
639+
// FIXME: The above is likely untrue. See
640+
// <https://github.com/rust-lang/rust/pull/70004#issuecomment-602022110>. Is it
641+
// okay to ignore `StorageDead`/`StorageLive` annotations during CTFE?
642+
Some(DefKind::Static | DefKind::Const | DefKind::AssocConst) => {}
643+
_ => {
644+
// Mark locals that use `Storage*` annotations as dead on function entry.
645+
let always_live = AlwaysLiveLocals::new(self.body());
646+
for local in locals.indices() {
647+
if !always_live.contains(local) {
648+
locals[local].value = LocalValue::Dead;
654649
}
655650
}
656651
}
657-
// done
658-
self.frame_mut().locals = locals;
659652
}
653+
// done
654+
self.frame_mut().locals = locals;
660655

661656
M::after_stack_push(self)?;
662657
info!("ENTERING({}) {}", self.frame_idx(), self.frame().instance);
@@ -734,6 +729,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
734729
let frame =
735730
self.stack_mut().pop().expect("tried to pop a stack frame, but there were none");
736731

732+
if !unwinding {
733+
// Copy the return value to the caller's stack frame.
734+
if let Some(return_place) = frame.return_place {
735+
let op = self.access_local(&frame, mir::RETURN_PLACE, None)?;
736+
self.copy_op_transmute(op, return_place)?;
737+
self.dump_place(*return_place);
738+
} else {
739+
throw_ub!(Unreachable);
740+
}
741+
}
742+
737743
// Now where do we jump next?
738744

739745
// Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
@@ -759,7 +765,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
759765
self.deallocate_local(local.value)?;
760766
}
761767

762-
let return_place = frame.return_place;
763768
if M::after_stack_pop(self, frame, unwinding)? == StackPopJump::NoJump {
764769
// The hook already did everything.
765770
// We want to skip the `info!` below, hence early return.
@@ -772,25 +777,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
772777
self.unwind_to_block(unwind);
773778
} else {
774779
// Follow the normal return edge.
775-
// Validate the return value. Do this after deallocating so that we catch dangling
776-
// references.
777-
if let Some(return_place) = return_place {
778-
if M::enforce_validity(self) {
779-
// Data got changed, better make sure it matches the type!
780-
// It is still possible that the return place held invalid data while
781-
// the function is running, but that's okay because nobody could have
782-
// accessed that same data from the "outside" to observe any broken
783-
// invariant -- that is, unless a function somehow has a ptr to
784-
// its return place... but the way MIR is currently generated, the
785-
// return place is always a local and then this cannot happen.
786-
self.validate_operand(self.place_to_op(return_place)?)?;
787-
}
788-
} else {
789-
// Uh, that shouldn't happen... the function did not intend to return
790-
throw_ub!(Unreachable);
791-
}
792-
793-
// Jump to new block -- *after* validation so that the spans make more sense.
794780
if let Some(ret) = next_block {
795781
self.return_to_block(ret)?;
796782
}

Diff for: src/librustc_mir/interpret/operand.rs

+5-11
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
419419
local: mir::Local,
420420
layout: Option<TyAndLayout<'tcx>>,
421421
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
422-
assert_ne!(local, mir::RETURN_PLACE);
423422
let layout = self.layout_of_local(frame, local, layout)?;
424423
let op = if layout.is_zst() {
425424
// Do not read from ZST, they might not be initialized
@@ -454,16 +453,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
454453
place: mir::Place<'tcx>,
455454
layout: Option<TyAndLayout<'tcx>>,
456455
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
457-
let base_op = match place.local {
458-
mir::RETURN_PLACE => throw_ub!(ReadFromReturnPlace),
459-
local => {
460-
// Do not use the layout passed in as argument if the base we are looking at
461-
// here is not the entire place.
462-
let layout = if place.projection.is_empty() { layout } else { None };
463-
464-
self.access_local(self.frame(), local, layout)?
465-
}
466-
};
456+
// Do not use the layout passed in as argument if the base we are looking at
457+
// here is not the entire place.
458+
let layout = if place.projection.is_empty() { layout } else { None };
459+
460+
let base_op = self.access_local(self.frame(), place.local, layout)?;
467461

468462
let op = place
469463
.projection

Diff for: src/librustc_mir/interpret/place.rs

+4-41
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,6 @@ impl<Tag> MemPlace<Tag> {
135135
MemPlace { ptr, align, meta: MemPlaceMeta::None }
136136
}
137137

138-
/// Produces a Place that will error if attempted to be read from or written to
139-
#[inline(always)]
140-
fn null(cx: &impl HasDataLayout) -> Self {
141-
Self::from_scalar_ptr(Scalar::null_ptr(cx), Align::from_bytes(1).unwrap())
142-
}
143-
144138
#[inline(always)]
145139
pub fn from_ptr(ptr: Pointer<Tag>, align: Align) -> Self {
146140
Self::from_scalar_ptr(ptr.into(), align)
@@ -260,12 +254,6 @@ impl<'tcx, Tag: ::std::fmt::Debug + Copy> OpTy<'tcx, Tag> {
260254
}
261255

262256
impl<Tag: ::std::fmt::Debug> Place<Tag> {
263-
/// Produces a Place that will error if attempted to be read from or written to
264-
#[inline(always)]
265-
fn null(cx: &impl HasDataLayout) -> Self {
266-
Place::Ptr(MemPlace::null(cx))
267-
}
268-
269257
#[inline]
270258
pub fn assert_mem_place(self) -> MemPlace<Tag> {
271259
match self {
@@ -641,35 +629,10 @@ where
641629
&mut self,
642630
place: mir::Place<'tcx>,
643631
) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
644-
let mut place_ty = match place.local {
645-
mir::RETURN_PLACE => {
646-
// `return_place` has the *caller* layout, but we want to use our
647-
// `layout to verify our assumption. The caller will validate
648-
// their layout on return.
649-
PlaceTy {
650-
place: match self.frame().return_place {
651-
Some(p) => *p,
652-
// Even if we don't have a return place, we sometimes need to
653-
// create this place, but any attempt to read from / write to it
654-
// (even a ZST read/write) needs to error, so let us make this
655-
// a NULL place.
656-
//
657-
// FIXME: Ideally we'd make sure that the place projections also
658-
// bail out.
659-
None => Place::null(&*self),
660-
},
661-
layout: self.layout_of(
662-
self.subst_from_current_frame_and_normalize_erasing_regions(
663-
self.frame().body.return_ty(),
664-
),
665-
)?,
666-
}
667-
}
668-
local => PlaceTy {
669-
// This works even for dead/uninitialized locals; we check further when writing
670-
place: Place::Local { frame: self.frame_idx(), local },
671-
layout: self.layout_of_local(self.frame(), local, None)?,
672-
},
632+
let mut place_ty = PlaceTy {
633+
// This works even for dead/uninitialized locals; we check further when writing
634+
place: Place::Local { frame: self.frame_idx(), local: place.local },
635+
layout: self.layout_of_local(self.frame(), place.local, None)?,
673636
};
674637

675638
for elem in place.projection.iter() {

Diff for: src/librustc_mir/interpret/terminator.rs

-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
1919
use rustc_middle::mir::TerminatorKind::*;
2020
match terminator.kind {
2121
Return => {
22-
self.frame().return_place.map(|r| self.dump_place(*r));
2322
self.pop_stack_frame(/* unwinding */ false)?
2423
}
2524

Diff for: src/librustc_mir/transform/const_prop.rs

+7-11
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,6 @@ struct ConstPropagator<'mir, 'tcx> {
330330
// by accessing them through `ecx` instead.
331331
source_scopes: IndexVec<SourceScope, SourceScopeData>,
332332
local_decls: IndexVec<Local, LocalDecl<'tcx>>,
333-
ret: Option<OpTy<'tcx, ()>>,
334333
// Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
335334
// the last known `SourceInfo` here and just keep revisiting it.
336335
source_info: Option<SourceInfo>,
@@ -402,22 +401,19 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
402401
source_scopes: body.source_scopes.clone(),
403402
//FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
404403
local_decls: body.local_decls.clone(),
405-
ret: ret.map(Into::into),
406404
source_info: None,
407405
}
408406
}
409407

410408
fn get_const(&self, local: Local) -> Option<OpTy<'tcx>> {
411-
if local == RETURN_PLACE {
412-
// Try to read the return place as an immediate so that if it is representable as a
413-
// scalar, we can handle it as such, but otherwise, just return the value as is.
414-
return match self.ret.map(|ret| self.ecx.try_read_immediate(ret)) {
415-
Some(Ok(Ok(imm))) => Some(imm.into()),
416-
_ => self.ret,
417-
};
418-
}
409+
let op = self.ecx.access_local(self.ecx.frame(), local, None).ok();
419410

420-
self.ecx.access_local(self.ecx.frame(), local, None).ok()
411+
// Try to read the local as an immediate so that if it is representable as a scalar, we can
412+
// handle it as such, but otherwise, just return the value as is.
413+
match op.map(|ret| self.ecx.try_read_immediate(ret)) {
414+
Some(Ok(Ok(imm))) => Some(imm.into()),
415+
_ => op,
416+
}
421417
}
422418

423419
fn remove_const(&mut self, local: Local) {

Diff for: src/test/codegen/consts.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010
// CHECK: @STATIC = {{.*}}, align 4
1111

1212
// This checks the constants from inline_enum_const
13-
// CHECK: @alloc5 = {{.*}}, align 2
13+
// CHECK: @alloc7 = {{.*}}, align 2
1414

1515
// This checks the constants from {low,high}_align_const, they share the same
1616
// constant, but the alignment differs, so the higher one should be used
17-
// CHECK: [[LOW_HIGH:@[0-9]+]] = {{.*}} getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* @alloc15, i32 0, i32 0, i32 0), {{.*}}
17+
// CHECK: [[LOW_HIGH:@[0-9]+]] = {{.*}} getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* @alloc19, i32 0, i32 0, i32 0), {{.*}}
1818

1919
#[derive(Copy, Clone)]
2020
// repr(i16) is required for the {low,high}_align_const test

Diff for: src/test/incremental/hashes/enum_constructors.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -274,29 +274,29 @@ pub enum Clike2 {
274274
// Change constructor path (C-like) --------------------------------------
275275
#[cfg(cfail1)]
276276
pub fn change_constructor_path_c_like() {
277-
let _ = Clike::B;
277+
let _x = Clike::B;
278278
}
279279

280280
#[cfg(not(cfail1))]
281281
#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built,typeck_tables_of")]
282282
#[rustc_clean(cfg="cfail3")]
283283
pub fn change_constructor_path_c_like() {
284-
let _ = Clike2::B;
284+
let _x = Clike2::B;
285285
}
286286

287287

288288

289289
// Change constructor variant (C-like) --------------------------------------
290290
#[cfg(cfail1)]
291291
pub fn change_constructor_variant_c_like() {
292-
let _ = Clike::A;
292+
let _x = Clike::A;
293293
}
294294

295295
#[cfg(not(cfail1))]
296296
#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,mir_built")]
297297
#[rustc_clean(cfg="cfail3")]
298298
pub fn change_constructor_variant_c_like() {
299-
let _ = Clike::C;
299+
let _x = Clike::C;
300300
}
301301

302302

Diff for: src/test/mir-opt/const_allocation2/32bit/rustc.main.ConstProp.after.mir

+15-15
Original file line numberDiff line numberDiff line change
@@ -30,41 +30,41 @@ fn main() -> () {
3030
}
3131

3232
alloc0 (static: FOO, size: 8, align: 4) {
33-
alloc24+0╼ 03 00 00 00 │ ╾──╼....
33+
alloc25+0╼ 03 00 00 00 │ ╾──╼....
3434
}
3535

36-
alloc24 (size: 48, align: 4) {
37-
0x00 │ 00 00 00 00 __ __ __ __ ╾alloc9+0─╼ 00 00 00 00 │ ....░░░░╾──╼....
38-
0x10 │ 00 00 00 00 __ __ __ __ ╾alloc14+0╼ 02 00 00 00 │ ....░░░░╾──╼....
39-
0x20 │ 01 00 00 00 2a 00 00 00 ╾alloc22+0╼ 03 00 00 00 │ ....*...╾──╼....
36+
alloc25 (size: 48, align: 4) {
37+
0x00 │ 00 00 00 00 __ __ __ __ ╾alloc10+0╼ 00 00 00 00 │ ....░░░░╾──╼....
38+
0x10 │ 00 00 00 00 __ __ __ __ ╾alloc15+0╼ 02 00 00 00 │ ....░░░░╾──╼....
39+
0x20 │ 01 00 00 00 2a 00 00 00 ╾alloc23+0╼ 03 00 00 00 │ ....*...╾──╼....
4040
}
4141

42-
alloc9 (size: 0, align: 4) {}
42+
alloc10 (size: 0, align: 4) {}
4343

44-
alloc14 (size: 8, align: 4) {
45-
alloc12+0╼ ╾alloc13+0╼ │ ╾──╼╾──╼
44+
alloc15 (size: 8, align: 4) {
45+
alloc13+0╼ ╾alloc14+0╼ │ ╾──╼╾──╼
4646
}
4747

48-
alloc12 (size: 1, align: 1) {
48+
alloc13 (size: 1, align: 1) {
4949
05 │ .
5050
}
5151

52-
alloc13 (size: 1, align: 1) {
52+
alloc14 (size: 1, align: 1) {
5353
06 │ .
5454
}
5555

56-
alloc22 (size: 12, align: 4) {
57-
alloc18+3╼ ╾alloc19+0╼ ╾alloc21+2╼ │ ╾──╼╾──╼╾──╼
56+
alloc23 (size: 12, align: 4) {
57+
alloc19+3╼ ╾alloc20+0╼ ╾alloc22+2╼ │ ╾──╼╾──╼╾──╼
5858
}
5959

60-
alloc18 (size: 4, align: 1) {
60+
alloc19 (size: 4, align: 1) {
6161
2a 45 15 6f │ *E.o
6262
}
6363

64-
alloc19 (size: 1, align: 1) {
64+
alloc20 (size: 1, align: 1) {
6565
2a │ *
6666
}
6767

68-
alloc21 (size: 4, align: 1) {
68+
alloc22 (size: 4, align: 1) {
6969
2a 45 15 6f │ *E.o
7070
}

0 commit comments

Comments
 (0)