Skip to content

Commit d00940c

Browse files
authored
transpile: Extend lifetime of compound literals whose address is taken (#1407)
- Fixes #1217. I've only looked at fixing it in function context, as it's not possible to add statements to static initializers ("Expected no side-effects in static initializer" panic). But I'm not sure if the issue exists for statics anyway, because Rust is forced to extend the lifetime of the temporary to static in such a context. There is also an issue with how this line compiles in the snapshot test: ```c char *static_char_ptr = (char[]) { "hello" }; ``` It gets translated into an array in which the first element is the translated string, and then five zero integer literals. That's clearly broken and doesn't compile in Rust, so I commented out that line. Since my fix doesn't change how statics are handled, I believe that's an existing bug in c2rust that needs its own fix.
2 parents b785cab + 860770d commit d00940c

File tree

3 files changed

+124
-2
lines changed

3 files changed

+124
-2
lines changed

c2rust-transpile/src/translator/mod.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3559,7 +3559,9 @@ impl<'c> Translation<'c> {
35593559
CastKind::BitCast | CastKind::PointerToIntegral | CastKind::NoOp => {
35603560
ctx.decay_ref = DecayRef::Yes
35613561
}
3562-
CastKind::FunctionToPointerDecay | CastKind::BuiltinFnToFnPtr => {
3562+
CastKind::ArrayToPointerDecay
3563+
| CastKind::FunctionToPointerDecay
3564+
| CastKind::BuiltinFnToFnPtr => {
35633565
ctx.needs_address = true;
35643566
}
35653567
_ => {}
@@ -4067,7 +4069,41 @@ impl<'c> Translation<'c> {
40674069

40684070
Paren(_, val) => self.convert_expr(ctx, val, override_ty),
40694071

4070-
CompoundLiteral(_, val) => self.convert_expr(ctx, val, override_ty),
4072+
CompoundLiteral(qty, val) => {
4073+
let val = self.convert_expr(ctx, val, override_ty)?;
4074+
4075+
if !ctx.needs_address() || ctx.is_static || ctx.is_const {
4076+
// Statics and consts have their intermediates' lifetimes extended.
4077+
return Ok(val);
4078+
}
4079+
4080+
// C compound literals are lvalues, but equivalent Rust expressions generally are not.
4081+
// So if an address is needed, store it in an intermediate variable first.
4082+
let fresh_name = self.renamer.borrow_mut().fresh();
4083+
let fresh_ty = self.convert_type(override_ty.unwrap_or(qty).ctype)?;
4084+
4085+
val.and_then(|val| {
4086+
let fresh_stmt = {
4087+
let mutbl = if qty.qualifiers.is_const {
4088+
Mutability::Immutable
4089+
} else {
4090+
Mutability::Mutable
4091+
};
4092+
4093+
let local = mk().local(
4094+
mk().set_mutbl(mutbl).ident_pat(&fresh_name),
4095+
Some(fresh_ty),
4096+
Some(val),
4097+
);
4098+
mk().local_stmt(Box::new(local))
4099+
};
4100+
4101+
Ok(WithStmts::new(
4102+
vec![fresh_stmt],
4103+
mk().ident_expr(fresh_name),
4104+
))
4105+
})
4106+
}
40714107

40724108
InitList(ty, ref ids, opt_union_field_id, _) => {
40734109
self.convert_init_list(ctx, ty, ids, opt_union_field_id)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
int static_single_int = (int) { 42 };
2+
int *static_single_int_ptr = &((int) { 42 });
3+
int *static_int_ptr_to_array = (int[]) { 42, 9001 };
4+
// Currently generates broken Rust code, see
5+
// https://github.com/immunant/c2rust/issues/1410
6+
//char *static_char_ptr_to_array = (char[]) { "hello" };
7+
int (*static_int_array_ptr)[2] = &(int[]) { 42, 9001 };
8+
// As above
9+
//char (*static_char_array_ptr)[6] = &(char[]) { "hello" };
10+
11+
#define SINGLE_INT ((int) { 42 })
12+
#define INT_ARRAY ((int[]) { 42, 9001 })
13+
#define CHAR_ARRAY ((char[]) { "hello" })
14+
15+
void local_compound_literals() {
16+
int single_int = (int) { 42 };
17+
int *single_int_ptr = &((int) { 42 });
18+
int *int_ptr_to_array = (int[]) { 42, 9001 };
19+
char *char_ptr_to_array = (char[]) { "hello" };
20+
int (*int_array_ptr)[2] = &(int[]) { 42, 9001 };
21+
char (*char_array_ptr)[6] = &(char[]) { "hello" };
22+
23+
int macro_single_int = SINGLE_INT;
24+
int *macro_single_int_ptr = &SINGLE_INT;
25+
int *macro_int_ptr_to_array = INT_ARRAY;
26+
char *macro_char_ptr_to_array = CHAR_ARRAY;
27+
int (*macro_int_array_ptr)[2] = &INT_ARRAY;
28+
char (*macro_char_array_ptr)[6] = &CHAR_ARRAY;
29+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
source: c2rust-transpile/tests/snapshots.rs
3+
expression: cat tests/snapshots/compound_literals.rs
4+
input_file: c2rust-transpile/tests/snapshots/compound_literals.c
5+
---
6+
#![allow(
7+
dead_code,
8+
non_camel_case_types,
9+
non_snake_case,
10+
non_upper_case_globals,
11+
unused_assignments,
12+
unused_mut
13+
)]
14+
#[no_mangle]
15+
pub static mut static_single_int: ::core::ffi::c_int = 42;
16+
#[no_mangle]
17+
pub static mut static_single_int_ptr: *mut ::core::ffi::c_int =
18+
&42 as *const ::core::ffi::c_int as *mut ::core::ffi::c_int;
19+
#[no_mangle]
20+
pub static mut static_int_ptr_to_array: *mut ::core::ffi::c_int = [42, 9001].as_ptr() as *mut _;
21+
#[no_mangle]
22+
pub static mut static_int_array_ptr: *mut [::core::ffi::c_int; 2] =
23+
&[42, 9001] as *const [::core::ffi::c_int; 2] as *mut [::core::ffi::c_int; 2];
24+
pub const SINGLE_INT: ::core::ffi::c_int = 42 as ::core::ffi::c_int;
25+
pub const INT_ARRAY: [::core::ffi::c_int; 2] =
26+
[42 as ::core::ffi::c_int, 9001 as ::core::ffi::c_int];
27+
pub const CHAR_ARRAY: [::core::ffi::c_char; 6] =
28+
unsafe { ::core::mem::transmute::<[u8; 6], [::core::ffi::c_char; 6]>(*b"hello\0") };
29+
#[no_mangle]
30+
pub unsafe extern "C" fn local_compound_literals() {
31+
let mut single_int: ::core::ffi::c_int = 42 as ::core::ffi::c_int;
32+
let mut fresh0: ::core::ffi::c_int = 42 as ::core::ffi::c_int;
33+
let mut single_int_ptr: *mut ::core::ffi::c_int = &mut fresh0 as *mut ::core::ffi::c_int;
34+
let mut fresh1: [::core::ffi::c_int; 2] =
35+
[42 as ::core::ffi::c_int, 9001 as ::core::ffi::c_int];
36+
let mut int_ptr_to_array: *mut ::core::ffi::c_int = fresh1.as_mut_ptr();
37+
let mut fresh2: [::core::ffi::c_char; 6] =
38+
::core::mem::transmute::<[u8; 6], [::core::ffi::c_char; 6]>(*b"hello\0");
39+
let mut char_ptr_to_array: *mut ::core::ffi::c_char = fresh2.as_mut_ptr();
40+
let mut fresh3: [::core::ffi::c_int; 2] =
41+
[42 as ::core::ffi::c_int, 9001 as ::core::ffi::c_int];
42+
let mut int_array_ptr: *mut [::core::ffi::c_int; 2] =
43+
&mut fresh3 as *mut [::core::ffi::c_int; 2];
44+
let mut fresh4: [::core::ffi::c_char; 6] =
45+
::core::mem::transmute::<[u8; 6], [::core::ffi::c_char; 6]>(*b"hello\0");
46+
let mut char_array_ptr: *mut [::core::ffi::c_char; 6] =
47+
&mut fresh4 as *mut [::core::ffi::c_char; 6];
48+
let mut macro_single_int: ::core::ffi::c_int = SINGLE_INT;
49+
let mut macro_single_int_ptr: *mut ::core::ffi::c_int =
50+
&mut SINGLE_INT as *mut ::core::ffi::c_int;
51+
let mut macro_int_ptr_to_array: *mut ::core::ffi::c_int = INT_ARRAY.as_mut_ptr();
52+
let mut macro_char_ptr_to_array: *mut ::core::ffi::c_char = CHAR_ARRAY.as_mut_ptr();
53+
let mut macro_int_array_ptr: *mut [::core::ffi::c_int; 2] =
54+
&mut INT_ARRAY as *mut [::core::ffi::c_int; 2];
55+
let mut macro_char_array_ptr: *mut [::core::ffi::c_char; 6] =
56+
&mut CHAR_ARRAY as *mut [::core::ffi::c_char; 6];
57+
}

0 commit comments

Comments
 (0)