From 6f310d3e79ee3bc4a133ab06983a4e067ed8e80a Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 17 Mar 2026 12:12:15 +0100 Subject: [PATCH] transpile: Wrap transmuted string literals in `const` blocks when address is taken --- c2rust-transpile/src/translator/literals.rs | 17 +++++++++++++++-- c2rust-transpile/src/translator/mod.rs | 6 ++++-- c2rust-transpile/src/translator/pointers.rs | 12 ++++-------- .../tests/snapshots/os-specific/wide_strings.c | 7 ++----- ...ts__transpile@wide_strings.c.2021.linux.snap | 11 ++++++++++- ...ts__transpile@wide_strings.c.2021.macos.snap | 11 ++++++++++- ...ts__transpile@wide_strings.c.2024.linux.snap | 9 +++++++++ ...ts__transpile@wide_strings.c.2024.macos.snap | 9 +++++++++ 8 files changed, 63 insertions(+), 19 deletions(-) diff --git a/c2rust-transpile/src/translator/literals.rs b/c2rust-transpile/src/translator/literals.rs index 256b225de6..69622cbb29 100644 --- a/c2rust-transpile/src/translator/literals.rs +++ b/c2rust-transpile/src/translator/literals.rs @@ -113,16 +113,29 @@ impl<'c> Translation<'c> { if ctx.needs_address && element_size == 1 { // Unlike in C, Rust string literals are already references by default. - // So if the address needs to be taken, just make a bare literal. + // So if the address needs to be taken, just make a bare literal and let + // `convert_address_of_common` cast it to the appropriate type. + // Strings with element_size > 1 cannot be cast from a byte literal for + // alignment reasons, and need a transmute. Ok(WithStmts::new_val(val)) } else { // std::mem::transmute::<[u8; size], ctype>(*b"xxxx") let array_ty = mk().array_ty(mk().ident_ty("u8"), mk().lit_expr(len as u128)); - let val = transmute_expr( + let mut val = transmute_expr( array_ty, self.convert_type(ty.ctype)?, mk().unary_expr(UnOp::Deref(Default::default()), val), ); + + // A transmute creates a temporary, which cannot have its address taken without + // creating dangling pointers. Wrap it inside an inline `const` block, so that + // it will be const-promoted to 'static. + if ctx.needs_address { + self.use_feature("inline_const"); + let stmts = vec![mk().expr_stmt(val)]; + val = mk().const_block_expr(mk().const_block(stmts)); + } + Ok(WithStmts::new_unsafe_val(val)) } } diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 85dbb87264..1e9fec0c31 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -1594,8 +1594,10 @@ impl<'c> Translation<'c> { /// Called when translation makes use of a language feature that will require a feature-gate. pub fn use_feature(&self, feature: &'static str) { - if matches!(feature, "asm" | "label_break_value" | "raw_ref_op") - && self.tcfg.edition >= Edition2024 + if matches!( + feature, + "asm" | "inline_const" | "label_break_value" | "raw_ref_op" + ) && self.tcfg.edition >= Edition2024 { return; } diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index 763aefe6e2..8649349c22 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -139,15 +139,11 @@ impl<'c> Translation<'c> { } needs_cast = true; } - // Values that translate into temporaries can't be raw-borrowed in Rust, - // and must be regular-borrowed first. - // Borrowing in a const context will extend the lifetime to static. + // Values that translate into const temporaries can't be raw-borrowed in Rust. + // They must be regular-borrowed first, which will extend the lifetime to static. else if arg_is_macro - || ctx.is_const - && matches!( - arg_expr_kind, - Some(CExprKind::Literal(..) | CExprKind::CompoundLiteral(..)) - ) + || matches!(arg_expr_kind, Some(CExprKind::Literal(..))) + || ctx.is_const && matches!(arg_expr_kind, Some(CExprKind::CompoundLiteral(..))) { let arg_cty_kind = &self.ast_context.resolve_type(arg_cty.ctype).kind; diff --git a/c2rust-transpile/tests/snapshots/os-specific/wide_strings.c b/c2rust-transpile/tests/snapshots/os-specific/wide_strings.c index c0a09f2ed7..b9f767784e 100644 --- a/c2rust-transpile/tests/snapshots/os-specific/wide_strings.c +++ b/c2rust-transpile/tests/snapshots/os-specific/wide_strings.c @@ -3,16 +3,13 @@ wchar_t static_array[] = L"x"; wchar_t static_array_longer[3] = L"x"; wchar_t static_array_shorter[1] = L"xy"; -// Currently broken, see #1571 -// const wchar_t *static_ptr = L"x"; +const wchar_t *static_ptr = L"x"; void func() { wchar_t array[] = L"x"; wchar_t array_longer[3] = L"x"; wchar_t array_shorter[1] = L"xy"; - - // Currently broken, see #1571 - // const wchar_t *ptr = L"x"; + const wchar_t *ptr = L"x"; size_t len = wcslen(array); } diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@wide_strings.c.2021.linux.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@wide_strings.c.2021.linux.snap index cfb90cc2e7..6600a56f65 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@wide_strings.c.2021.linux.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@wide_strings.c.2021.linux.snap @@ -11,7 +11,7 @@ expression: cat tests/snapshots/os-specific/wide_strings.2021.linux.rs unused_mut )] #![deny(unsafe_op_in_unsafe_fn)] -#![feature(raw_ref_op)] +#![feature(inline_const, raw_ref_op)] extern "C" { fn wcslen(__s: *const wchar_t) -> ::core::ffi::c_ulong; } @@ -27,6 +27,11 @@ pub static mut static_array_longer: [wchar_t; 3] = pub static mut static_array_shorter: [wchar_t; 1] = unsafe { ::core::mem::transmute::<[u8; 4], [wchar_t; 1]>(*b"x\0\0\0") }; #[no_mangle] +pub static mut static_ptr: *const wchar_t = unsafe { + const { ::core::mem::transmute::<[u8; 8], [::core::ffi::c_int; 2]>(*b"x\0\0\0\0\0\0\0") } + .as_ptr() as *const wchar_t +}; +#[no_mangle] pub unsafe extern "C" fn func() { unsafe { let mut array: [wchar_t; 2] = @@ -35,6 +40,10 @@ pub unsafe extern "C" fn func() { ::core::mem::transmute::<[u8; 12], [wchar_t; 3]>(*b"x\0\0\0\0\0\0\0\0\0\0\0"); let mut array_shorter: [wchar_t; 1] = ::core::mem::transmute::<[u8; 4], [wchar_t; 1]>(*b"x\0\0\0"); + let mut ptr: *const wchar_t = const { + ::core::mem::transmute::<[u8; 8], [::core::ffi::c_int; 2]>(*b"x\0\0\0\0\0\0\0") + } + .as_ptr() as *const wchar_t; let mut len: size_t = wcslen(&raw mut array as *mut wchar_t) as size_t; } } diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@wide_strings.c.2021.macos.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@wide_strings.c.2021.macos.snap index d0869a36b4..7b8ccf116f 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@wide_strings.c.2021.macos.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@wide_strings.c.2021.macos.snap @@ -11,7 +11,7 @@ expression: cat tests/snapshots/os-specific/wide_strings.2021.macos.rs unused_mut )] #![deny(unsafe_op_in_unsafe_fn)] -#![feature(raw_ref_op)] +#![feature(inline_const, raw_ref_op)] extern "C" { fn wcslen(_: *const wchar_t) -> ::core::ffi::c_ulong; } @@ -29,6 +29,11 @@ pub static mut static_array_longer: [wchar_t; 3] = pub static mut static_array_shorter: [wchar_t; 1] = unsafe { ::core::mem::transmute::<[u8; 4], [wchar_t; 1]>(*b"x\0\0\0") }; #[no_mangle] +pub static mut static_ptr: *const wchar_t = unsafe { + const { ::core::mem::transmute::<[u8; 8], [::core::ffi::c_int; 2]>(*b"x\0\0\0\0\0\0\0") } + .as_ptr() as *const wchar_t +}; +#[no_mangle] pub unsafe extern "C" fn func() { unsafe { let mut array: [wchar_t; 2] = @@ -37,6 +42,10 @@ pub unsafe extern "C" fn func() { ::core::mem::transmute::<[u8; 12], [wchar_t; 3]>(*b"x\0\0\0\0\0\0\0\0\0\0\0"); let mut array_shorter: [wchar_t; 1] = ::core::mem::transmute::<[u8; 4], [wchar_t; 1]>(*b"x\0\0\0"); + let mut ptr: *const wchar_t = const { + ::core::mem::transmute::<[u8; 8], [::core::ffi::c_int; 2]>(*b"x\0\0\0\0\0\0\0") + } + .as_ptr() as *const wchar_t; let mut len: size_t = wcslen(&raw mut array as *mut wchar_t) as size_t; } } diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@wide_strings.c.2024.linux.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@wide_strings.c.2024.linux.snap index 1ca7a0e849..96c4825cf9 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@wide_strings.c.2024.linux.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@wide_strings.c.2024.linux.snap @@ -26,6 +26,11 @@ pub static mut static_array_longer: [wchar_t; 3] = pub static mut static_array_shorter: [wchar_t; 1] = unsafe { ::core::mem::transmute::<[u8; 4], [wchar_t; 1]>(*b"x\0\0\0") }; #[unsafe(no_mangle)] +pub static mut static_ptr: *const wchar_t = unsafe { + const { ::core::mem::transmute::<[u8; 8], [::core::ffi::c_int; 2]>(*b"x\0\0\0\0\0\0\0") } + .as_ptr() as *const wchar_t +}; +#[unsafe(no_mangle)] pub unsafe extern "C" fn func() { unsafe { let mut array: [wchar_t; 2] = @@ -34,6 +39,10 @@ pub unsafe extern "C" fn func() { ::core::mem::transmute::<[u8; 12], [wchar_t; 3]>(*b"x\0\0\0\0\0\0\0\0\0\0\0"); let mut array_shorter: [wchar_t; 1] = ::core::mem::transmute::<[u8; 4], [wchar_t; 1]>(*b"x\0\0\0"); + let mut ptr: *const wchar_t = const { + ::core::mem::transmute::<[u8; 8], [::core::ffi::c_int; 2]>(*b"x\0\0\0\0\0\0\0") + } + .as_ptr() as *const wchar_t; let mut len: size_t = wcslen(&raw mut array as *mut wchar_t) as size_t; } } diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@wide_strings.c.2024.macos.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@wide_strings.c.2024.macos.snap index 6a0731d676..0aebcb28a0 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@wide_strings.c.2024.macos.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@wide_strings.c.2024.macos.snap @@ -28,6 +28,11 @@ pub static mut static_array_longer: [wchar_t; 3] = pub static mut static_array_shorter: [wchar_t; 1] = unsafe { ::core::mem::transmute::<[u8; 4], [wchar_t; 1]>(*b"x\0\0\0") }; #[unsafe(no_mangle)] +pub static mut static_ptr: *const wchar_t = unsafe { + const { ::core::mem::transmute::<[u8; 8], [::core::ffi::c_int; 2]>(*b"x\0\0\0\0\0\0\0") } + .as_ptr() as *const wchar_t +}; +#[unsafe(no_mangle)] pub unsafe extern "C" fn func() { unsafe { let mut array: [wchar_t; 2] = @@ -36,6 +41,10 @@ pub unsafe extern "C" fn func() { ::core::mem::transmute::<[u8; 12], [wchar_t; 3]>(*b"x\0\0\0\0\0\0\0\0\0\0\0"); let mut array_shorter: [wchar_t; 1] = ::core::mem::transmute::<[u8; 4], [wchar_t; 1]>(*b"x\0\0\0"); + let mut ptr: *const wchar_t = const { + ::core::mem::transmute::<[u8; 8], [::core::ffi::c_int; 2]>(*b"x\0\0\0\0\0\0\0") + } + .as_ptr() as *const wchar_t; let mut len: size_t = wcslen(&raw mut array as *mut wchar_t) as size_t; } }