immunant / c2rust

Migrate C code to Rust
https://c2rust.com/
Other
4.03k stars 244 forks source link

c2rust generates invalid Rust code for compound literals with pointers requiring mutability #1166

Open Yeaseen opened 1 week ago

Yeaseen commented 1 week ago

Description

c2rust produces invalid Rust code when translating compound literals involving pointers and expressions (e.g., condition--). The generated Rust code incorrectly declares a temporary variable as immutable (let) while attempting to create a mutable reference (&mut) to it, leading to a Rust compiler error.

Source C code

int main() {
    int condition = 1; 
    int *single_value = &(int){condition--}; 
    return 0;
}

Expected Behavior

c2rust should generate valid Rust code that handles the mutability requirements of the compound literal, resulting in successful compilation.

Translated Rust code

#![allow(dead_code, mutable_transmutes, non_camel_case_types, non_snake_case, non_upper_case_globals, unused_assignments, unused_mut)]
use ::transpiled_code::*;
unsafe fn main_0() -> libc::c_int {
    let mut condition: libc::c_int = 1 as libc::c_int;
    let fresh0 = condition;
    condition = condition - 1;
    let mut single_value: *mut libc::c_int = &mut fresh0 as *mut libc::c_int;
    return 0 as libc::c_int;
}
pub fn main() {
    unsafe { ::std::process::exit(main_0() as i32) }
}

Cargo build failure

The translated Rust code incorrectly declares a temporary variable (fresh0) as immutable and attempts to create a mutable reference to it, causing the following Rust compile-time error.

error[E0596]: cannot borrow `fresh0` as mutable, as it is not declared as mutable
 --> src/runner.rs:7:46
  |
7 |     let mut single_value: *mut libc::c_int = &mut fresh0 as *mut libc::c_int;
  |                                              ^^^^^^^^^^^ cannot borrow as mutable

Observation

I think &(int){condition--} involves modifying condition, but the temporary value created by the compound literal (fresh0) does not need to be mutable, as it is never modified in the original C code. Instead, c2rust should declare fresh0 as immutable and adjust the pointer type accordingly to align with C semantics.

let mut condition: libc::c_int = 1 as libc::c_int;
let fresh0 = condition; // fresh0 is immutable
condition = condition - 1; // condition is decremented
let single_value: *const libc::c_int = &fresh0; // Use a const pointer
Yeaseen commented 1 day ago

@kkysen this persists in v0.19.0 as well. I would appreciate it if you would confirm this at your convenience. Thank you.