diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 9b2b7196fc0e9..0d0ed465c1cc6 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -400,7 +400,18 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // We can still be zero-sized in this branch, in which case we have to // return `None`. - if size.bytes() == 0 { None } else { Some(ptr) } + if size.bytes() == 0 { + // We may be reading from a static. + // In order to ensure that `static FOO: Type = FOO;` causes a cycle error + // instead of magically pulling *any* ZST value from the ether, we need to + // actually access the referenced allocation. The caller is likely + // to short-circuit on `None`, so we trigger the access here to + // make sure it happens. + self.get_raw(ptr.alloc_id)?; + None + } else { + Some(ptr) + } } }) } diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 8188106b5f187..4d76a23005065 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -248,13 +248,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } }; + let alloc = self.memory.get_raw(ptr.alloc_id)?; + match mplace.layout.abi { Abi::Scalar(..) => { - let scalar = self.memory.get_raw(ptr.alloc_id)?.read_scalar( - self, - ptr, - mplace.layout.size, - )?; + let scalar = alloc.read_scalar(self, ptr, mplace.layout.size)?; Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout })) } Abi::ScalarPair(ref a, ref b) => { @@ -267,8 +265,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let b_offset = a_size.align_to(b.align(self).abi); assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields let b_ptr = ptr.offset(b_offset, self)?; - let a_val = self.memory.get_raw(ptr.alloc_id)?.read_scalar(self, a_ptr, a_size)?; - let b_val = self.memory.get_raw(ptr.alloc_id)?.read_scalar(self, b_ptr, b_size)?; + let a_val = alloc.read_scalar(self, a_ptr, a_size)?; + let b_val = alloc.read_scalar(self, b_ptr, b_size)?; Ok(Some(ImmTy { imm: Immediate::ScalarPair(a_val, b_val), layout: mplace.layout })) } _ => Ok(None), diff --git a/src/test/ui/consts/recursive-zst-static.rs b/src/test/ui/consts/recursive-zst-static.rs index df7562bd9f5d2..768df58e1e32e 100644 --- a/src/test/ui/consts/recursive-zst-static.rs +++ b/src/test/ui/consts/recursive-zst-static.rs @@ -1,6 +1,10 @@ -// build-pass +// This test ensures that we do not allow ZST statics to initialize themselves without ever +// actually creating a value of that type. This is important, as the ZST may have private fields +// that users can reasonably expect to only get initialized by their own code. Thus unsafe code +// can depend on this fact and will thus do unsound things when it is violated. +// See https://github.com/rust-lang/rust/issues/71078 for more details. -static FOO: () = FOO; +static FOO: () = FOO; //~ cycle detected when const-evaluating `FOO` fn main() { FOO diff --git a/src/test/ui/consts/recursive-zst-static.stderr b/src/test/ui/consts/recursive-zst-static.stderr new file mode 100644 index 0000000000000..e21dcf691ab0a --- /dev/null +++ b/src/test/ui/consts/recursive-zst-static.stderr @@ -0,0 +1,21 @@ +error[E0391]: cycle detected when const-evaluating `FOO` + --> $DIR/recursive-zst-static.rs:7:18 + | +LL | static FOO: () = FOO; + | ^^^ + | +note: ...which requires const-evaluating `FOO`... + --> $DIR/recursive-zst-static.rs:7:1 + | +LL | static FOO: () = FOO; + | ^^^^^^^^^^^^^^^^^^^^^ + = note: ...which again requires const-evaluating `FOO`, completing the cycle +note: cycle used when const-evaluating + checking `FOO` + --> $DIR/recursive-zst-static.rs:7:1 + | +LL | static FOO: () = FOO; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0391`.