diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs
index bcad7855c3736..6f8b0c1445a16 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 3741f31927e94..f5ae434d00cec 100644
--- a/src/librustc_mir/interpret/operand.rs
+++ b/src/librustc_mir/interpret/operand.rs
@@ -242,13 +242,11 @@ impl<'mir, 'tcx, 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) => {
@@ -261,8 +259,8 @@ impl<'mir, 'tcx, 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`.