From cee2c4d6610e818a18dfb8f80c218fade0b917b3 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Fri, 10 Oct 2025 21:29:29 -0400 Subject: [PATCH] Fix ICE on offsetted ZST pointer A grep for `const_usize.*align` found the same code copied to rustc_codegen_gcc but I don't see other cases where we get this wrong. --- compiler/rustc_codegen_cranelift/src/constant.rs | 6 ++++-- compiler/rustc_codegen_gcc/src/common.rs | 5 +++-- compiler/rustc_codegen_llvm/src/common.rs | 5 +++-- tests/ui/consts/zst_no_llvm_alloc.rs | 14 ++++++++++++++ 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index faca92957e1a6..548f0db23d129 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -138,8 +138,10 @@ pub(crate) fn codegen_const_value<'tcx>( let base_addr = match fx.tcx.global_alloc(alloc_id) { GlobalAlloc::Memory(alloc) => { if alloc.inner().len() == 0 { - assert_eq!(offset, Size::ZERO); - fx.bcx.ins().iconst(fx.pointer_type, alloc.inner().align.bytes() as i64) + fx.bcx.ins().iconst( + fx.pointer_type, + alloc.inner().align.bytes().wrapping_add(offset.bytes()) as i64, + ) } else { let data_id = data_id_for_alloc_id( &mut fx.constants_cx, diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 28848ca61845c..6961e82acffdf 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -247,8 +247,9 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { // This avoids generating a zero-sized constant value and actually needing a // real address at runtime. if alloc.inner().len() == 0 { - assert_eq!(offset.bytes(), 0); - let val = self.const_usize(alloc.inner().align.bytes()); + let val = self.const_usize( + alloc.inner().align.bytes().wrapping_add(offset.bytes()), + ); return if matches!(layout.primitive(), Pointer(_)) { self.context.new_cast(None, val, ty) } else { diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 175fc8535ac3f..0fb82378da23f 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -281,8 +281,9 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { // This avoids generating a zero-sized constant value and actually needing a // real address at runtime. if alloc.inner().len() == 0 { - assert_eq!(offset.bytes(), 0); - let llval = self.const_usize(alloc.inner().align.bytes()); + let llval = self.const_usize( + alloc.inner().align.bytes().wrapping_add(offset.bytes()), + ); return if matches!(layout.primitive(), Pointer(_)) { unsafe { llvm::LLVMConstIntToPtr(llval, llty) } } else { diff --git a/tests/ui/consts/zst_no_llvm_alloc.rs b/tests/ui/consts/zst_no_llvm_alloc.rs index 1e92e3bbd4c1b..e2c7e26c455ad 100644 --- a/tests/ui/consts/zst_no_llvm_alloc.rs +++ b/tests/ui/consts/zst_no_llvm_alloc.rs @@ -5,6 +5,16 @@ struct Foo; static FOO: Foo = Foo; +// This tests for regression of https://github.com/rust-lang/rust/issues/147516 +// +// THe compiler will codegen `&Zst` without creating a real allocation, just a properly aligned +// `usize` (i.e., ptr::dangling). However, code can add an arbitrary offset from that base +// allocation. We confirm here that we correctly codegen that offset combined with the necessary +// alignment of the base &() as a 1-ZST and &Foo as a 4-ZST. +const A: *const () = (&() as *const ()).wrapping_byte_add(2); +const B: *const () = (&Foo as *const _ as *const ()).wrapping_byte_add(usize::MAX); +const C: *const () = (&Foo as *const _ as *const ()).wrapping_byte_add(2); + fn main() { // There's no stable guarantee that these are true. // However, we want them to be true so that our LLVM IR and runtime are a bit faster: @@ -15,6 +25,10 @@ fn main() { let x: &'static Foo = &Foo; assert_eq!(x as *const Foo as usize, 4); + assert_eq!(A.addr(), 3); + assert_eq!(B.addr(), 3); + assert_eq!(C.addr(), 6); + // The exact addresses returned by these library functions are not necessarily stable guarantees // but for now we assert that we're still matching. #[allow(dangling_pointers_from_temporaries)]