c2rust icon indicating copy to clipboard operation
c2rust copied to clipboard

Incorrect Use of `.offset_from(1)` When Transpiling Pointer Decrement on Struct Field

Open Yeaseen opened this issue 7 months ago • 1 comments

Description

C2Rust incorrectly translates valid C code containing a pointer decrement on a volatile struct field, such as --(global.p);, into Rust using .offset_from(1), which is invalid and causes a type mismatch at compile time.

C code:

int arr[10];
struct S { int * volatile p; } global;
int func(int *restrict r) {
    --(global.p);
    return *r;
}

int main() {
     return 0;
}

Translated Rust Code

#[derive(Copy, Clone)]
#[repr(C)]
pub struct S {
    pub p: *mut libc::c_int,
}
#[no_mangle]
pub static mut arr: [libc::c_int; 10] = [0; 10];
#[no_mangle]
pub static mut global: S = S {
    p: 0 as *const libc::c_int as *mut libc::c_int,
};
#[no_mangle]
pub unsafe extern "C" fn func(mut r: *mut libc::c_int) -> libc::c_int {
    ::core::ptr::write_volatile(
        &mut global.p as *mut *mut libc::c_int,
        (::core::ptr::read_volatile::<
            *mut libc::c_int,
        >(&global.p as *const *mut libc::c_int))
            .offset_from(1) as *mut libc::c_int,
    );
    ::core::ptr::read_volatile::<*mut libc::c_int>(&global.p as *const *mut libc::c_int);
    return *r;
}

Cargo build error:

error[E0308]: mismatched types
  --> src/runner.rs:29:26
   |
29 |             .offset_from(1) as *mut libc::c_int,
   |              ----------- ^ expected `*const i32`, found `usize`
   |              |
   |              arguments to this method are incorrect
   |
   = note: expected raw pointer `*const i32`
                     found type `usize`
note: method defined here
  --> /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/ptr/mut_ptr.rs:856:25

c2rust version: C2Rust 0.20.0

Yeaseen avatar May 09 '25 05:05 Yeaseen

I think I found the source of the problem: https://github.com/immunant/c2rust/blob/12dfbc7526e857b51a2d4dd9046f7d25811d52b6/c2rust-transpile/src/translator/operators.rs#L800

ty is passed here as the type of the "implied" operand of the decrement operator. In other words, the type of the 1 in foo -= 1. For numeric types, that works, since the subtracted value is the same type as the variable. But when the type is a pointer, the subtracted value is an integer, not another pointer. So it's trying to translate this as pointer-pointer subtraction instead.

Rua avatar Nov 12 '25 18:11 Rua