Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions c2rust-transpile/src/translator/literals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}
Expand Down
6 changes: 4 additions & 2 deletions c2rust-transpile/src/translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
12 changes: 4 additions & 8 deletions c2rust-transpile/src/translator/pointers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
7 changes: 2 additions & 5 deletions c2rust-transpile/tests/snapshots/os-specific/wide_strings.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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") }
Copy link
Copy Markdown
Contributor

@kkysen kkysen Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, it seems like we're not using the portable override_ty here (c_int instead of wchar_t).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that a problem with this PR or more a general observation/note for the future?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a problem here, but I'm not sure why it's happening.

c_int is not always the same as wchar_t (https://github.com/search?q=repo%3Arust-lang%2Flibc+%22type+wchar_t+%3D%22&type=code), so on some platforms, this won't compile.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the AST, the variable itself has type const wchar_t* but its initializer expression is const int*? And Clang seems to think the latter can be directly assigned to the former without an implicit cast.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, it's the array-to-pointer decay. Because it's treated as a cast, it's not propagating the wchar_t type but instead casting to it.

Copy link
Copy Markdown
Contributor

@kkysen kkysen Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We haven't fully figured this out yet, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#1645 is meant to address that as far as I know. But is it necessary to have a fix before this can be merged? As far as I can tell, it wasn't broken by this PR.

.as_ptr() as *const wchar_t
};
#[no_mangle]
pub unsafe extern "C" fn func() {
unsafe {
let mut array: [wchar_t; 2] =
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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] =
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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] =
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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] =
Expand All @@ -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;
}
}
Loading