From 6c1fa6cecb666b1540b97b43623b019fb8f5f538 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 2 Sep 2026 12:19:52 +0200 Subject: [PATCH 01/11] transpile: Split off `side_effects.c` snapshot test --- c2rust-transpile/tests/snapshots.rs | 5 ++ c2rust-transpile/tests/snapshots/exprs.c | 65 -------------- .../tests/snapshots/side_effects.c | 62 +++++++++++++ ...shots__transpile@exprs.c.2021.clang15.snap | 75 ---------------- ...shots__transpile@exprs.c.2024.clang15.snap | 75 ---------------- ...transpile@side_effects.c.2021.clang15.snap | 88 +++++++++++++++++++ ...transpile@side_effects.c.2024.clang15.snap | 88 +++++++++++++++++++ 7 files changed, 243 insertions(+), 215 deletions(-) create mode 100644 c2rust-transpile/tests/snapshots/side_effects.c create mode 100644 c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2021.clang15.snap create mode 100644 c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2024.clang15.snap diff --git a/c2rust-transpile/tests/snapshots.rs b/c2rust-transpile/tests/snapshots.rs index 8fde688bd5..7a6fae2398 100644 --- a/c2rust-transpile/tests/snapshots.rs +++ b/c2rust-transpile/tests/snapshots.rs @@ -459,6 +459,11 @@ fn test_scalar_init() { .run(); } +#[test] +fn test_side_effects() { + transpile("side_effects.c").run(); +} + #[test] fn test_static_assert() { transpile("static_assert.c").run(); diff --git a/c2rust-transpile/tests/snapshots/exprs.c b/c2rust-transpile/tests/snapshots/exprs.c index 3fbcbc321d..328db7ff0a 100644 --- a/c2rust-transpile/tests/snapshots/exprs.c +++ b/c2rust-transpile/tests/snapshots/exprs.c @@ -2,71 +2,6 @@ int puts(const char *str); -static int side_effect(){ - puts("the return of side effect"); - return 0; -} - -static int* lvalue_side_effect(){ - puts("the return of side effect"); - static int VAL = 42; - return &VAL; -} - -void unary_without_side_effect(){ - int i = 5; - -i; - +i; - ~i; - !i; - &i; - *&i; - i++; - i--; - --i; - ++i; -} - -void unary_with_side_effect(){ - char *arr[1] = {0}; - - -side_effect(); - +side_effect(); - ~side_effect(); - !side_effect(); - &""[side_effect()]; -} - -void inc_decl_with_rvalue_side_effect() { - int arr[1] = {0}; - - // Increment/decrement, expression value not used - ++arr[side_effect()]; - --arr[side_effect()]; - arr[side_effect()]++; - arr[side_effect()]--; - - // Increment/decrement, expression value is used - int pre_inc = ++arr[side_effect()]; - int pre_dec = --arr[side_effect()]; - int post_inc = arr[side_effect()]++; - int post_dec = arr[side_effect()]--; -} - -void inc_decl_with_lvalue_side_effect() { - // Increment/decrement, expression value not used - ++*lvalue_side_effect(); - --*lvalue_side_effect(); - (*lvalue_side_effect())++; - (*lvalue_side_effect())--; - - // Increment/decrement, expression value is used - int pre_inc = ++*lvalue_side_effect(); - int pre_dec = --*lvalue_side_effect(); - int post_inc = (*lvalue_side_effect())++; - int post_dec = (*lvalue_side_effect())--; -} - void unsigned_compound_desugaring(void) { enum E { EA }; diff --git a/c2rust-transpile/tests/snapshots/side_effects.c b/c2rust-transpile/tests/snapshots/side_effects.c new file mode 100644 index 0000000000..405f848cca --- /dev/null +++ b/c2rust-transpile/tests/snapshots/side_effects.c @@ -0,0 +1,62 @@ +int side_effect(void); +int *lvalue_side_effect(void); + +void pure(void) { + int i = 5; + + -i; + +i; + ~i; + !i; + &i; + *&i; +} + +void pure_with_side_effect(){ + char *arr[1] = {0}; + + -side_effect(); + +side_effect(); + ~side_effect(); + !side_effect(); + &""[side_effect()]; +} + +void impure(void) { + int i = 5; + + i++; + i--; + --i; + ++i; +} + +void impure_with_side_effect() { + int arr[1] = {0}; + + // Increment/decrement, expression value not used + ++arr[side_effect()]; + --arr[side_effect()]; + arr[side_effect()]++; + arr[side_effect()]--; + + // Increment/decrement, expression value is used + int pre_inc = ++arr[side_effect()]; + int pre_dec = --arr[side_effect()]; + int post_inc = arr[side_effect()]++; + int post_dec = arr[side_effect()]--; +} + +void impure_with_lvalue_side_effect() { + // Increment/decrement, expression value not used + ++*lvalue_side_effect(); + --*lvalue_side_effect(); + (*lvalue_side_effect())++; + (*lvalue_side_effect())--; + + // Increment/decrement, expression value is used + int pre_inc = ++*lvalue_side_effect(); + int pre_dec = --*lvalue_side_effect(); + int post_inc = (*lvalue_side_effect())++; + int post_dec = (*lvalue_side_effect())--; +} diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.clang15.snap index 20942ba008..b3f01068e6 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.clang15.snap @@ -32,81 +32,6 @@ impl C2Rust_Unnamed { pub const B: Self = Self(1); pub const C: Self = Self(2); } -unsafe extern "C" fn side_effect() -> ::core::ffi::c_int { - puts(b"the return of side effect\0".as_ptr() as *const ::core::ffi::c_char); - return 0 as ::core::ffi::c_int; -} -unsafe extern "C" fn lvalue_side_effect() -> *mut ::core::ffi::c_int { - puts(b"the return of side effect\0".as_ptr() as *const ::core::ffi::c_char); - static mut VAL: ::core::ffi::c_int = 42 as ::core::ffi::c_int; - return &raw mut VAL; -} -#[no_mangle] -pub unsafe extern "C" fn unary_without_side_effect() { - let mut i: ::core::ffi::c_int = 5 as ::core::ffi::c_int; - -i; - i; - !i; - (i == 0) as ::core::ffi::c_int; - &raw mut i; - i; - i += 1; - i -= 1; - i -= 1; - i += 1; -} -#[no_mangle] -pub unsafe extern "C" fn unary_with_side_effect() { - let mut arr: [*mut ::core::ffi::c_char; 1] = [::core::ptr::null_mut::<::core::ffi::c_char>()]; - -side_effect(); - side_effect(); - !side_effect(); - (side_effect() == 0) as ::core::ffi::c_int; - (b"\0".as_ptr() as *const ::core::ffi::c_char).offset(side_effect() as isize); -} -#[no_mangle] -pub unsafe extern "C" fn inc_decl_with_rvalue_side_effect() { - let mut arr: [::core::ffi::c_int; 1] = [0 as ::core::ffi::c_int]; - arr[side_effect() as usize] += 1; - arr[side_effect() as usize] -= 1; - arr[side_effect() as usize] += 1; - arr[side_effect() as usize] -= 1; - let c2rust_lvalue_ptr = &raw mut arr[side_effect() as usize]; - *c2rust_lvalue_ptr += 1; - let mut pre_inc: ::core::ffi::c_int = *c2rust_lvalue_ptr; - let c2rust_lvalue_ptr_0 = &raw mut arr[side_effect() as usize]; - *c2rust_lvalue_ptr_0 -= 1; - let mut pre_dec: ::core::ffi::c_int = *c2rust_lvalue_ptr_0; - let c2rust_lvalue_ptr_1 = &raw mut arr[side_effect() as usize]; - let c2rust_fresh0 = *c2rust_lvalue_ptr_1; - *c2rust_lvalue_ptr_1 += 1; - let mut post_inc: ::core::ffi::c_int = c2rust_fresh0; - let c2rust_lvalue_ptr_2 = &raw mut arr[side_effect() as usize]; - let c2rust_fresh1 = *c2rust_lvalue_ptr_2; - *c2rust_lvalue_ptr_2 -= 1; - let mut post_dec: ::core::ffi::c_int = c2rust_fresh1; -} -#[no_mangle] -pub unsafe extern "C" fn inc_decl_with_lvalue_side_effect() { - *lvalue_side_effect() += 1; - *lvalue_side_effect() -= 1; - *lvalue_side_effect() += 1; - *lvalue_side_effect() -= 1; - let c2rust_lvalue_ptr = &raw mut *lvalue_side_effect(); - *c2rust_lvalue_ptr += 1; - let mut pre_inc: ::core::ffi::c_int = *c2rust_lvalue_ptr; - let c2rust_lvalue_ptr_0 = &raw mut *lvalue_side_effect(); - *c2rust_lvalue_ptr_0 -= 1; - let mut pre_dec: ::core::ffi::c_int = *c2rust_lvalue_ptr_0; - let c2rust_lvalue_ptr_1 = &raw mut *lvalue_side_effect(); - let c2rust_fresh2 = *c2rust_lvalue_ptr_1; - *c2rust_lvalue_ptr_1 += 1; - let mut post_inc: ::core::ffi::c_int = c2rust_fresh2; - let c2rust_lvalue_ptr_2 = &raw mut *lvalue_side_effect(); - let c2rust_fresh3 = *c2rust_lvalue_ptr_2; - *c2rust_lvalue_ptr_2 -= 1; - let mut post_dec: ::core::ffi::c_int = c2rust_fresh3; -} #[no_mangle] pub unsafe extern "C" fn unsigned_compound_desugaring() { let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.clang15.snap index 60ee7b1c96..0c463ce4f5 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.clang15.snap @@ -32,81 +32,6 @@ impl C2Rust_Unnamed { pub const B: Self = Self(1); pub const C: Self = Self(2); } -unsafe extern "C" fn side_effect() -> ::core::ffi::c_int { - puts(b"the return of side effect\0".as_ptr() as *const ::core::ffi::c_char); - return 0 as ::core::ffi::c_int; -} -unsafe extern "C" fn lvalue_side_effect() -> *mut ::core::ffi::c_int { - puts(b"the return of side effect\0".as_ptr() as *const ::core::ffi::c_char); - static mut VAL: ::core::ffi::c_int = 42 as ::core::ffi::c_int; - return &raw mut VAL; -} -#[unsafe(no_mangle)] -pub unsafe extern "C" fn unary_without_side_effect() { - let mut i: ::core::ffi::c_int = 5 as ::core::ffi::c_int; - -i; - i; - !i; - (i == 0) as ::core::ffi::c_int; - &raw mut i; - i; - i += 1; - i -= 1; - i -= 1; - i += 1; -} -#[unsafe(no_mangle)] -pub unsafe extern "C" fn unary_with_side_effect() { - let mut arr: [*mut ::core::ffi::c_char; 1] = [::core::ptr::null_mut::<::core::ffi::c_char>()]; - -side_effect(); - side_effect(); - !side_effect(); - (side_effect() == 0) as ::core::ffi::c_int; - (b"\0".as_ptr() as *const ::core::ffi::c_char).offset(side_effect() as isize); -} -#[unsafe(no_mangle)] -pub unsafe extern "C" fn inc_decl_with_rvalue_side_effect() { - let mut arr: [::core::ffi::c_int; 1] = [0 as ::core::ffi::c_int]; - arr[side_effect() as usize] += 1; - arr[side_effect() as usize] -= 1; - arr[side_effect() as usize] += 1; - arr[side_effect() as usize] -= 1; - let c2rust_lvalue_ptr = &raw mut arr[side_effect() as usize]; - *c2rust_lvalue_ptr += 1; - let mut pre_inc: ::core::ffi::c_int = *c2rust_lvalue_ptr; - let c2rust_lvalue_ptr_0 = &raw mut arr[side_effect() as usize]; - *c2rust_lvalue_ptr_0 -= 1; - let mut pre_dec: ::core::ffi::c_int = *c2rust_lvalue_ptr_0; - let c2rust_lvalue_ptr_1 = &raw mut arr[side_effect() as usize]; - let c2rust_fresh0 = *c2rust_lvalue_ptr_1; - *c2rust_lvalue_ptr_1 += 1; - let mut post_inc: ::core::ffi::c_int = c2rust_fresh0; - let c2rust_lvalue_ptr_2 = &raw mut arr[side_effect() as usize]; - let c2rust_fresh1 = *c2rust_lvalue_ptr_2; - *c2rust_lvalue_ptr_2 -= 1; - let mut post_dec: ::core::ffi::c_int = c2rust_fresh1; -} -#[unsafe(no_mangle)] -pub unsafe extern "C" fn inc_decl_with_lvalue_side_effect() { - *lvalue_side_effect() += 1; - *lvalue_side_effect() -= 1; - *lvalue_side_effect() += 1; - *lvalue_side_effect() -= 1; - let c2rust_lvalue_ptr = &raw mut *lvalue_side_effect(); - *c2rust_lvalue_ptr += 1; - let mut pre_inc: ::core::ffi::c_int = *c2rust_lvalue_ptr; - let c2rust_lvalue_ptr_0 = &raw mut *lvalue_side_effect(); - *c2rust_lvalue_ptr_0 -= 1; - let mut pre_dec: ::core::ffi::c_int = *c2rust_lvalue_ptr_0; - let c2rust_lvalue_ptr_1 = &raw mut *lvalue_side_effect(); - let c2rust_fresh2 = *c2rust_lvalue_ptr_1; - *c2rust_lvalue_ptr_1 += 1; - let mut post_inc: ::core::ffi::c_int = c2rust_fresh2; - let c2rust_lvalue_ptr_2 = &raw mut *lvalue_side_effect(); - let c2rust_fresh3 = *c2rust_lvalue_ptr_2; - *c2rust_lvalue_ptr_2 -= 1; - let mut post_dec: ::core::ffi::c_int = c2rust_fresh3; -} #[unsafe(no_mangle)] pub unsafe extern "C" fn unsigned_compound_desugaring() { let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2021.clang15.snap new file mode 100644 index 0000000000..b8f091081c --- /dev/null +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2021.clang15.snap @@ -0,0 +1,88 @@ +--- +source: c2rust-transpile/tests/snapshots.rs +expression: cat tests/snapshots/side_effects.2021.clang15.rs +--- +#![allow( + clippy::missing_safety_doc, + dead_code, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unused_assignments, + unused_mut +)] +#![feature(raw_ref_op)] +extern "C" { + fn side_effect() -> ::core::ffi::c_int; + fn lvalue_side_effect() -> *mut ::core::ffi::c_int; +} +#[no_mangle] +pub unsafe extern "C" fn pure() { + let mut i: ::core::ffi::c_int = 5 as ::core::ffi::c_int; + -i; + i; + !i; + (i == 0) as ::core::ffi::c_int; + &raw mut i; + i; +} +#[no_mangle] +pub unsafe extern "C" fn pure_with_side_effect() { + let mut arr: [*mut ::core::ffi::c_char; 1] = [::core::ptr::null_mut::<::core::ffi::c_char>()]; + -side_effect(); + side_effect(); + !side_effect(); + (side_effect() == 0) as ::core::ffi::c_int; + (b"\0".as_ptr() as *const ::core::ffi::c_char).offset(side_effect() as isize); +} +#[no_mangle] +pub unsafe extern "C" fn impure() { + let mut i: ::core::ffi::c_int = 5 as ::core::ffi::c_int; + i += 1; + i -= 1; + i -= 1; + i += 1; +} +#[no_mangle] +pub unsafe extern "C" fn impure_with_side_effect() { + let mut arr: [::core::ffi::c_int; 1] = [0 as ::core::ffi::c_int]; + arr[side_effect() as usize] += 1; + arr[side_effect() as usize] -= 1; + arr[side_effect() as usize] += 1; + arr[side_effect() as usize] -= 1; + let c2rust_lvalue_ptr = &raw mut arr[side_effect() as usize]; + *c2rust_lvalue_ptr += 1; + let mut pre_inc: ::core::ffi::c_int = *c2rust_lvalue_ptr; + let c2rust_lvalue_ptr_0 = &raw mut arr[side_effect() as usize]; + *c2rust_lvalue_ptr_0 -= 1; + let mut pre_dec: ::core::ffi::c_int = *c2rust_lvalue_ptr_0; + let c2rust_lvalue_ptr_1 = &raw mut arr[side_effect() as usize]; + let c2rust_fresh0 = *c2rust_lvalue_ptr_1; + *c2rust_lvalue_ptr_1 += 1; + let mut post_inc: ::core::ffi::c_int = c2rust_fresh0; + let c2rust_lvalue_ptr_2 = &raw mut arr[side_effect() as usize]; + let c2rust_fresh1 = *c2rust_lvalue_ptr_2; + *c2rust_lvalue_ptr_2 -= 1; + let mut post_dec: ::core::ffi::c_int = c2rust_fresh1; +} +#[no_mangle] +pub unsafe extern "C" fn impure_with_lvalue_side_effect() { + *lvalue_side_effect() += 1; + *lvalue_side_effect() -= 1; + *lvalue_side_effect() += 1; + *lvalue_side_effect() -= 1; + let c2rust_lvalue_ptr = &raw mut *lvalue_side_effect(); + *c2rust_lvalue_ptr += 1; + let mut pre_inc: ::core::ffi::c_int = *c2rust_lvalue_ptr; + let c2rust_lvalue_ptr_0 = &raw mut *lvalue_side_effect(); + *c2rust_lvalue_ptr_0 -= 1; + let mut pre_dec: ::core::ffi::c_int = *c2rust_lvalue_ptr_0; + let c2rust_lvalue_ptr_1 = &raw mut *lvalue_side_effect(); + let c2rust_fresh2 = *c2rust_lvalue_ptr_1; + *c2rust_lvalue_ptr_1 += 1; + let mut post_inc: ::core::ffi::c_int = c2rust_fresh2; + let c2rust_lvalue_ptr_2 = &raw mut *lvalue_side_effect(); + let c2rust_fresh3 = *c2rust_lvalue_ptr_2; + *c2rust_lvalue_ptr_2 -= 1; + let mut post_dec: ::core::ffi::c_int = c2rust_fresh3; +} diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2024.clang15.snap new file mode 100644 index 0000000000..18afccba2b --- /dev/null +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2024.clang15.snap @@ -0,0 +1,88 @@ +--- +source: c2rust-transpile/tests/snapshots.rs +expression: cat tests/snapshots/side_effects.2024.clang15.rs +--- +#![allow( + clippy::missing_safety_doc, + dead_code, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unsafe_op_in_unsafe_fn, + unused_assignments, + unused_mut +)] +unsafe extern "C" { + unsafe fn side_effect() -> ::core::ffi::c_int; + unsafe fn lvalue_side_effect() -> *mut ::core::ffi::c_int; +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pure() { + let mut i: ::core::ffi::c_int = 5 as ::core::ffi::c_int; + -i; + i; + !i; + (i == 0) as ::core::ffi::c_int; + &raw mut i; + i; +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pure_with_side_effect() { + let mut arr: [*mut ::core::ffi::c_char; 1] = [::core::ptr::null_mut::<::core::ffi::c_char>()]; + -side_effect(); + side_effect(); + !side_effect(); + (side_effect() == 0) as ::core::ffi::c_int; + (b"\0".as_ptr() as *const ::core::ffi::c_char).offset(side_effect() as isize); +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn impure() { + let mut i: ::core::ffi::c_int = 5 as ::core::ffi::c_int; + i += 1; + i -= 1; + i -= 1; + i += 1; +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn impure_with_side_effect() { + let mut arr: [::core::ffi::c_int; 1] = [0 as ::core::ffi::c_int]; + arr[side_effect() as usize] += 1; + arr[side_effect() as usize] -= 1; + arr[side_effect() as usize] += 1; + arr[side_effect() as usize] -= 1; + let c2rust_lvalue_ptr = &raw mut arr[side_effect() as usize]; + *c2rust_lvalue_ptr += 1; + let mut pre_inc: ::core::ffi::c_int = *c2rust_lvalue_ptr; + let c2rust_lvalue_ptr_0 = &raw mut arr[side_effect() as usize]; + *c2rust_lvalue_ptr_0 -= 1; + let mut pre_dec: ::core::ffi::c_int = *c2rust_lvalue_ptr_0; + let c2rust_lvalue_ptr_1 = &raw mut arr[side_effect() as usize]; + let c2rust_fresh0 = *c2rust_lvalue_ptr_1; + *c2rust_lvalue_ptr_1 += 1; + let mut post_inc: ::core::ffi::c_int = c2rust_fresh0; + let c2rust_lvalue_ptr_2 = &raw mut arr[side_effect() as usize]; + let c2rust_fresh1 = *c2rust_lvalue_ptr_2; + *c2rust_lvalue_ptr_2 -= 1; + let mut post_dec: ::core::ffi::c_int = c2rust_fresh1; +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn impure_with_lvalue_side_effect() { + *lvalue_side_effect() += 1; + *lvalue_side_effect() -= 1; + *lvalue_side_effect() += 1; + *lvalue_side_effect() -= 1; + let c2rust_lvalue_ptr = &raw mut *lvalue_side_effect(); + *c2rust_lvalue_ptr += 1; + let mut pre_inc: ::core::ffi::c_int = *c2rust_lvalue_ptr; + let c2rust_lvalue_ptr_0 = &raw mut *lvalue_side_effect(); + *c2rust_lvalue_ptr_0 -= 1; + let mut pre_dec: ::core::ffi::c_int = *c2rust_lvalue_ptr_0; + let c2rust_lvalue_ptr_1 = &raw mut *lvalue_side_effect(); + let c2rust_fresh2 = *c2rust_lvalue_ptr_1; + *c2rust_lvalue_ptr_1 += 1; + let mut post_inc: ::core::ffi::c_int = c2rust_fresh2; + let c2rust_lvalue_ptr_2 = &raw mut *lvalue_side_effect(); + let c2rust_fresh3 = *c2rust_lvalue_ptr_2; + *c2rust_lvalue_ptr_2 -= 1; + let mut post_dec: ::core::ffi::c_int = c2rust_fresh3; +} From c4e21f60d85af4ed3c91728752bb8ab062558d26 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 2 Sep 2026 12:30:34 +0200 Subject: [PATCH 02/11] transpile: Add more tests to `side_effects.c` --- .../tests/snapshots/side_effects.c | 19 +++++++++++ ...transpile@side_effects.c.2021.clang15.snap | 32 +++++++++++++++++++ ...transpile@side_effects.c.2024.clang15.snap | 32 +++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/c2rust-transpile/tests/snapshots/side_effects.c b/c2rust-transpile/tests/snapshots/side_effects.c index 405f848cca..8fc5245ac5 100644 --- a/c2rust-transpile/tests/snapshots/side_effects.c +++ b/c2rust-transpile/tests/snapshots/side_effects.c @@ -1,6 +1,11 @@ int side_effect(void); int *lvalue_side_effect(void); +struct S { + int x; + int y; +}; + void pure(void) { int i = 5; @@ -10,6 +15,14 @@ void pure(void) { !i; &i; *&i; + + // Compound literals + &(struct S){0, 2}; + -(struct S){0, 2}.x; + + // Builtins known to have no side effects + -__builtin_bswap32(1); + !__builtin_expect(0, 0); } void pure_with_side_effect(){ @@ -20,6 +33,12 @@ void pure_with_side_effect(){ ~side_effect(); !side_effect(); &""[side_effect()]; + + &(struct S){side_effect(), 2}; + -(struct S){side_effect(), 2}.x; + + -__builtin_bswap32(side_effect()); + !__builtin_expect(side_effect(), 0); } void impure(void) { diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2021.clang15.snap index b8f091081c..bbd0c8ee45 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2021.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2021.clang15.snap @@ -16,6 +16,12 @@ extern "C" { fn side_effect() -> ::core::ffi::c_int; fn lvalue_side_effect() -> *mut ::core::ffi::c_int; } +#[derive(Copy, Clone)] +#[repr(C)] +pub struct S { + pub x: ::core::ffi::c_int, + pub y: ::core::ffi::c_int, +} #[no_mangle] pub unsafe extern "C" fn pure() { let mut i: ::core::ffi::c_int = 5 as ::core::ffi::c_int; @@ -25,6 +31,18 @@ pub unsafe extern "C" fn pure() { (i == 0) as ::core::ffi::c_int; &raw mut i; i; + let mut c2rust_lvalue: S = S { + x: 0 as ::core::ffi::c_int, + y: 2 as ::core::ffi::c_int, + }; + &raw mut c2rust_lvalue; + -S { + x: 0 as ::core::ffi::c_int, + y: 2 as ::core::ffi::c_int, + } + .x; + (1 as ::core::ffi::c_uint).swap_bytes().wrapping_neg(); + (0 as ::core::ffi::c_long == 0) as ::core::ffi::c_int; } #[no_mangle] pub unsafe extern "C" fn pure_with_side_effect() { @@ -34,6 +52,20 @@ pub unsafe extern "C" fn pure_with_side_effect() { !side_effect(); (side_effect() == 0) as ::core::ffi::c_int; (b"\0".as_ptr() as *const ::core::ffi::c_char).offset(side_effect() as isize); + let mut c2rust_lvalue: S = S { + x: side_effect(), + y: 2 as ::core::ffi::c_int, + }; + &raw mut c2rust_lvalue; + -S { + x: side_effect(), + y: 2 as ::core::ffi::c_int, + } + .x; + (side_effect() as ::core::ffi::c_uint) + .swap_bytes() + .wrapping_neg(); + (side_effect() as ::core::ffi::c_long == 0) as ::core::ffi::c_int; } #[no_mangle] pub unsafe extern "C" fn impure() { diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2024.clang15.snap index 18afccba2b..7f885a1771 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2024.clang15.snap @@ -16,6 +16,12 @@ unsafe extern "C" { unsafe fn side_effect() -> ::core::ffi::c_int; unsafe fn lvalue_side_effect() -> *mut ::core::ffi::c_int; } +#[derive(Copy, Clone)] +#[repr(C)] +pub struct S { + pub x: ::core::ffi::c_int, + pub y: ::core::ffi::c_int, +} #[unsafe(no_mangle)] pub unsafe extern "C" fn pure() { let mut i: ::core::ffi::c_int = 5 as ::core::ffi::c_int; @@ -25,6 +31,18 @@ pub unsafe extern "C" fn pure() { (i == 0) as ::core::ffi::c_int; &raw mut i; i; + let mut c2rust_lvalue: S = S { + x: 0 as ::core::ffi::c_int, + y: 2 as ::core::ffi::c_int, + }; + &raw mut c2rust_lvalue; + -S { + x: 0 as ::core::ffi::c_int, + y: 2 as ::core::ffi::c_int, + } + .x; + (1 as ::core::ffi::c_uint).swap_bytes().wrapping_neg(); + (0 as ::core::ffi::c_long == 0) as ::core::ffi::c_int; } #[unsafe(no_mangle)] pub unsafe extern "C" fn pure_with_side_effect() { @@ -34,6 +52,20 @@ pub unsafe extern "C" fn pure_with_side_effect() { !side_effect(); (side_effect() == 0) as ::core::ffi::c_int; (b"\0".as_ptr() as *const ::core::ffi::c_char).offset(side_effect() as isize); + let mut c2rust_lvalue: S = S { + x: side_effect(), + y: 2 as ::core::ffi::c_int, + }; + &raw mut c2rust_lvalue; + -S { + x: side_effect(), + y: 2 as ::core::ffi::c_int, + } + .x; + (side_effect() as ::core::ffi::c_uint) + .swap_bytes() + .wrapping_neg(); + (side_effect() as ::core::ffi::c_long == 0) as ::core::ffi::c_int; } #[unsafe(no_mangle)] pub unsafe extern "C" fn impure() { From 606309f2040191b7bf9a8377057bed2d6e844b7b Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 15 Aug 2026 18:26:53 +0200 Subject: [PATCH 03/11] transpile: Rename `ExprContext::used` to `is_used` and document it --- c2rust-transpile/src/translator/mod.rs | 37 +++++++++++++++++++++----- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 4308e61bf5..50e4b9752a 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -122,7 +122,27 @@ pub enum ReplaceMode { /// Options that impact an expression and all of its subexpressions. #[derive(Copy, Clone, Debug)] pub struct ExprContext { - used: bool, + /// Whether the result value of the expression is used in a larger expression. + /// + /// When the result value is not used in a particular context, only the side effects of the + /// expression matter. The `stmts` field of `WithStmts` should hold any statements with side + /// effects, and the `val` field is expected to be discarded. It should not appear in the final + /// transpiler output, and may be an expression that panics when evaluated. + /// + /// - `.unused()` should be called for the top-level expression of an `ExprStmt`, the increment + /// expression of a `for` loop, the `lhs` of a comma operator expression, and other such cases. + /// + /// - `.used()` should be called if an expression is needed to evaluate the side effects of a + /// parent expression, such as the arguments of a function call (unless the function is known to + /// be pure), the operands of an assignment expression, the expression of a `return` statement, + /// etc. An expression that sets `.used()` for one of its subexpressions should handle the case + /// that its own context has `!is_used`, by moving its side effects into the `stmts` field; + /// the `convert_side_effects_expr` helper can be used for this purpose. + /// + /// - If an expression is pure (has no side effects), then it should inherit its `is_used` value + /// from its parent expression: if the parent expression is going to be discarded, then so are + /// all of its pure child expressions. + is_used: bool, /// In a Rust const context, for example in a static initializer or constant-like macro /// translation. @@ -152,19 +172,22 @@ pub struct ExprContext { impl ExprContext { pub fn used(self) -> Self { - ExprContext { used: true, ..self } + ExprContext { + is_used: true, + ..self + } } pub fn unused(self) -> Self { ExprContext { - used: false, + is_used: false, ..self } } pub fn is_used(&self) -> bool { - self.used + self.is_used } pub fn is_unused(&self) -> bool { - !self.used + !self.is_used } pub fn decay_ref(self) -> Self { ExprContext { @@ -876,7 +899,7 @@ pub fn translate( ) -> (String, Option, PragmaVec, CrateSet) { let mut t = Translation::new(ast_context, tcfg, main_file); let ctx = ExprContext { - used: true, + is_used: true, is_const: false, is_pattern: false, is_static: false, @@ -3916,7 +3939,7 @@ impl<'c> Translation<'c> { panic_msg: &str, ) -> WithStmts> { if ctx.is_unused() { - // Recall that if `used` is false, the `stmts` field of the output must contain + // Recall that if `!is_used`, the `stmts` field of the output must contain // all side-effects (and a function call can always have side-effects) expr.and_then(|expr| { WithStmts::new(vec![mk().semi_stmt(expr)], self.panic_or_err(panic_msg)) From 7c954b636fd09b44212fefbc7c4e02ec6a2a52ad Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 15 Aug 2026 18:39:53 +0200 Subject: [PATCH 04/11] transpile: Replace `ExprContext::is_used` and `is_unused` with direct access --- c2rust-transpile/src/translator/builtins.rs | 6 +++--- c2rust-transpile/src/translator/mod.rs | 21 +++++++------------ c2rust-transpile/src/translator/operators.rs | 8 +++---- .../src/translator/structs_unions.rs | 2 +- c2rust-transpile/src/translator/variadic.rs | 2 +- 5 files changed, 17 insertions(+), 22 deletions(-) diff --git a/c2rust-transpile/src/translator/builtins.rs b/c2rust-transpile/src/translator/builtins.rs index 765b4be1c3..e3b1c67415 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -330,7 +330,7 @@ impl<'c> Translation<'c> { } "__builtin_va_start" => { - if ctx.is_unused() && args.len() == 2 { + if !ctx.is_used && args.len() == 2 { if let Some(va_id) = self.match_vastart(args[0]) { if self.ast_context.get_decl(&va_id).is_some() { let dst = self.convert_expr(ctx.used(), args[0], None)?; @@ -352,7 +352,7 @@ impl<'c> Translation<'c> { Err(TranslationError::generic("Unsupported va_start")) } "__builtin_va_copy" => { - if ctx.is_unused() && args.len() == 2 { + if !ctx.is_used && args.len() == 2 { if let Some((_dst_va_id, _src_va_id)) = self.match_vacopy(args[0], args[1]) { let dst = self.convert_expr(ctx.used(), args[0], None)?; let src = self.convert_expr(ctx.used(), args[1], None)?; @@ -370,7 +370,7 @@ impl<'c> Translation<'c> { Err(TranslationError::generic("Unsupported va_copy")) } "__builtin_va_end" => { - if ctx.is_unused() && args.len() == 1 { + if !ctx.is_used && args.len() == 1 { if let Some(_va_id) = self.match_vaend(args[0]) { // nothing to do since the translated Rust `va_list` values get `Drop`'ed. return Ok(WithStmts::new_val(self.panic("va_end stub"))); diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 50e4b9752a..c57e556e36 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -183,12 +183,7 @@ impl ExprContext { ..self } } - pub fn is_used(&self) -> bool { - self.is_used - } - pub fn is_unused(&self) -> bool { - !self.is_used - } + pub fn decay_ref(self) -> Self { ExprContext { decay_ref: DecayRef::Yes, @@ -3326,10 +3321,10 @@ impl<'c> Translation<'c> { /// Translate a C expression into a Rust one, possibly collecting side-effecting statements /// to run before the expression. /// - /// `ctx.is_used()` informs us how the C expression we are translating is used in the C + /// `ctx.is_used` informs us how the C expression we are translating is used in the C /// program. /// - /// In the case that `ctx.is_unused()`, all side-effecting components will be in the + /// In the case that `!ctx.is_used`, all side-effecting components will be in the /// `stmts` field of the output and it is expected that the `val` field of the output will be /// ignored. /// @@ -3577,7 +3572,7 @@ impl<'c> Translation<'c> { let lhs = self.convert_expr(ctx, lhs, Some(override_ty.unwrap_or(ty)))?; let rhs = self.convert_expr(ctx, rhs, Some(override_ty.unwrap_or(ty)))?; - if ctx.is_unused() { + if !ctx.is_used { let is_unsafe = lhs.is_unsafe() || rhs.is_unsafe(); let then = mk().block(lhs.into_stmts()); let else_ = mk().block_expr(mk().block(rhs.into_stmts())); @@ -3604,7 +3599,7 @@ impl<'c> Translation<'c> { BinaryConditional(ty, lhs, rhs) => { let rhs = self.convert_expr(ctx, rhs, None)?; - if ctx.is_unused() { + if !ctx.is_used { let lhs = self .convert_condition(ctx, false, lhs)? .merge_unsafe(rhs.is_unsafe()); @@ -3938,7 +3933,7 @@ impl<'c> Translation<'c> { expr: WithStmts>, panic_msg: &str, ) -> WithStmts> { - if ctx.is_unused() { + if !ctx.is_used { // Recall that if `!is_used`, the `stmts` field of the output must contain // all side-effects (and a function call can always have side-effects) expr.and_then(|expr| { @@ -4008,7 +4003,7 @@ impl<'c> Translation<'c> { match as_semi_break_stmt(&stmt, &lbl) { Some(val) => { let block = mk().block_expr(match val { - Some(val) if ctx.is_used() => WithStmts::new(stmts, val).to_block(), + Some(val) if ctx.is_used => WithStmts::new(stmts, val).to_block(), _ => mk().block(stmts), }); @@ -4037,7 +4032,7 @@ impl<'c> Translation<'c> { )) } _ => { - if ctx.is_unused() { + if !ctx.is_used { let val = self.panic_or_err("Empty statement expression is not supposed to be used"); Ok(WithStmts::new_val(val)) diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index 4c2de512b8..f1e022f870 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -113,7 +113,7 @@ impl<'c> Translation<'c> { } } - if ctx.is_unused() { + if !ctx.is_used { Ok(self .convert_expr(ctx, lhs, Some(lhs_type_id))? .and_then_try(|_| self.convert_expr(ctx, rhs, Some(rhs_type_id)))? @@ -318,7 +318,7 @@ impl<'c> Translation<'c> { ) }); - let lhs_translation = if ctx.is_used() || compound_assignment_needs_desugaring { + let lhs_translation = if ctx.is_used || compound_assignment_needs_desugaring { self.name_reference_write_read(ctx, lhs)? } else { self.name_reference_write(ctx, lhs)?.map(|named_ref| { @@ -416,7 +416,7 @@ impl<'c> Translation<'c> { } }; - let assign_result = if ctx.is_used() { + let assign_result = if ctx.is_used { self.make_cast( ctx, result_type_id, @@ -672,7 +672,7 @@ impl<'c> Translation<'c> { }; // If we aren't going to be using the result, may as well do a simple pre-increment - let dont_yield_old_value = op.is_prefix() || ctx.is_unused(); + let dont_yield_old_value = op.is_prefix() || !ctx.is_used; let op = op.underlying_compound_assignment().unwrap(); if dont_yield_old_value { diff --git a/c2rust-transpile/src/translator/structs_unions.rs b/c2rust-transpile/src/translator/structs_unions.rs index e6c214111d..cc378958be 100644 --- a/c2rust-transpile/src/translator/structs_unions.rs +++ b/c2rust-transpile/src/translator/structs_unions.rs @@ -1053,7 +1053,7 @@ impl<'a> Translation<'a> { lrvalue: LRValue, override_ty: Option, ) -> TranslationResult>> { - if ctx.is_unused() { + if !ctx.is_used { return self.convert_expr(ctx, expr, None); } diff --git a/c2rust-transpile/src/translator/variadic.rs b/c2rust-transpile/src/translator/variadic.rs index a4824b2348..0396038bd8 100644 --- a/c2rust-transpile/src/translator/variadic.rs +++ b/c2rust-transpile/src/translator/variadic.rs @@ -238,7 +238,7 @@ impl<'c> Translation<'c> { .path_segment_with_args(mk().ident("arg"), mk().angle_bracketed_args(vec![arg_ty])); let mut val = mk().method_call_expr(val, path, vec![]); - if ctx.is_unused() { + if !ctx.is_used { WithStmts::new( vec![mk().semi_stmt(val)], self.panic_or_err("convert_vaarg unused"), From 878ca4a0a89f89d0ff7be618ccb5dab5116e8b6f Mon Sep 17 00:00:00 2001 From: Rua Date: Sun, 16 Aug 2026 11:28:29 +0200 Subject: [PATCH 05/11] transpile: Pass `used()` for conditions of control flow --- c2rust-transpile/src/cfg/mod.rs | 8 ++++---- c2rust-transpile/src/translator/mod.rs | 4 ++-- c2rust-transpile/src/translator/operators.rs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/c2rust-transpile/src/cfg/mod.rs b/c2rust-transpile/src/cfg/mod.rs index 94daca5eb0..eb178bd9cc 100644 --- a/c2rust-transpile/src/cfg/mod.rs +++ b/c2rust-transpile/src/cfg/mod.rs @@ -1491,7 +1491,7 @@ impl CfgBuilder { // Condition let (stmts, val) = translator - .convert_condition(ctx, true, scrutinee)? + .convert_condition(ctx.used(), true, scrutinee)? .discard_unsafe(); wip.extend(stmts); @@ -1564,7 +1564,7 @@ impl CfgBuilder { // Condition let (stmts, val) = translator - .convert_condition(ctx, true, condition)? + .convert_condition(ctx.used(), true, condition)? .discard_unsafe(); let cond_val = translator .ast_context @@ -1643,7 +1643,7 @@ impl CfgBuilder { // Condition let (stmts, val) = translator - .convert_condition(ctx, true, condition)? + .convert_condition(ctx.used(), true, condition)? .discard_unsafe(); let cond_val = translator .ast_context @@ -1698,7 +1698,7 @@ impl CfgBuilder { // Condition if let Some(cond) = condition { let (stmts, val) = translator - .convert_condition(ctx, true, cond)? + .convert_condition(ctx.used(), true, cond)? .discard_unsafe(); let cond_val = translator .ast_context diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index c57e556e36..cd83bec2ec 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -3567,7 +3567,7 @@ impl<'c> Translation<'c> { } Conditional(ty, cond, lhs, rhs) => { - let cond = self.convert_condition(ctx, true, cond)?; + let cond = self.convert_condition(ctx.used(), true, cond)?; let lhs = self.convert_expr(ctx, lhs, Some(override_ty.unwrap_or(ty)))?; let rhs = self.convert_expr(ctx, rhs, Some(override_ty.unwrap_or(ty)))?; @@ -3601,7 +3601,7 @@ impl<'c> Translation<'c> { if !ctx.is_used { let lhs = self - .convert_condition(ctx, false, lhs)? + .convert_condition(ctx.used(), false, lhs)? .merge_unsafe(rhs.is_unsafe()); Ok(lhs.and_then(|val| { diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index f1e022f870..c1b96e700a 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -27,8 +27,8 @@ impl<'c> Translation<'c> { } op if op.is_logical() => { - let lhs = self.convert_condition(ctx, true, lhs)?; - let rhs = self.convert_condition(ctx, true, rhs)?; + let lhs = self.convert_condition(ctx.used(), true, lhs)?; + let rhs = self.convert_condition(ctx.used(), true, rhs)?; Ok(lhs .map(|x| bool_to_int(mk().binary_expr(BinOp::from(op), x, rhs.to_expr()))) .and_then(|out| { From dbc4957d9719a3d1e823792b64468e32e53cd104 Mon Sep 17 00:00:00 2001 From: Rua Date: Sun, 16 Aug 2026 11:29:55 +0200 Subject: [PATCH 06/11] transpile: Pass `used()` for variable initializers --- c2rust-transpile/src/translator/literals.rs | 2 +- c2rust-transpile/src/translator/macros.rs | 2 +- c2rust-transpile/src/translator/mod.rs | 8 ++++---- c2rust-transpile/src/translator/simd.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/c2rust-transpile/src/translator/literals.rs b/c2rust-transpile/src/translator/literals.rs index e8c76c12a5..675000433b 100644 --- a/c2rust-transpile/src/translator/literals.rs +++ b/c2rust-transpile/src/translator/literals.rs @@ -211,7 +211,7 @@ impl<'c> Translation<'c> { // Translate the expression to be assigned to the fresh variable. // It will be assigned by value, so we don't need its address anymore. - let val = self.convert_expr(ctx.not_needs_address(), val, override_ty)?; + let val = self.convert_expr(ctx.used().not_needs_address(), val, override_ty)?; // If we are translating a static variable, // then the fresh variable should also be static. diff --git a/c2rust-transpile/src/translator/macros.rs b/c2rust-transpile/src/translator/macros.rs index 1a66b0c0d9..87e92e3802 100644 --- a/c2rust-transpile/src/translator/macros.rs +++ b/c2rust-transpile/src/translator/macros.rs @@ -80,7 +80,7 @@ impl<'c> Translation<'c> { .kind .get_type() .ok_or_else(|| format_err!("Invalid expression type"))?; - let expr = self.convert_expr(ctx, id, None)?; + let expr = self.convert_expr(ctx.used(), id, None)?; // Join ty and cur_ty to the smaller of the two types. If the // types are not cast-compatible, abort the fold. diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index cd83bec2ec..b4dd675396 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -2002,7 +2002,7 @@ impl<'c> Translation<'c> { typ: CQualTypeId, init: &mut Box, ) -> TranslationResult<()> { - let mut default_init = self.implicit_default_expr(ctx, typ.ctype)?.to_expr(); + let mut default_init = self.implicit_default_expr(ctx.used(), typ.ctype)?.to_expr(); std::mem::swap(init, &mut default_init); @@ -2694,7 +2694,7 @@ impl<'c> Translation<'c> { })?; let ConvertedVariable { ty, mutbl: _, init } = self.convert_variable(ctx, initializer, typ)?; - let default_init = self.implicit_default_expr(ctx, typ.ctype)?.to_expr(); + let default_init = self.implicit_default_expr(ctx.used(), typ.ctype)?.to_expr(); let comment = String::from("// Initialized in c2rust_run_static_initializers"); let span = self .comment_store @@ -2811,7 +2811,7 @@ impl<'c> Translation<'c> { init.into_value() }; - let zeroed = self.implicit_default_expr(ctx, typ.ctype)?; + let zeroed = self.implicit_default_expr(ctx.used(), typ.ctype)?; let zeroed = if ctx.is_const { zeroed.wrap_unsafe().to_pure_expr() } else { @@ -3025,7 +3025,7 @@ impl<'c> Translation<'c> { ) -> TranslationResult { let init = match initializer { Some(x) => self.convert_expr(ctx.used(), x, Some(typ)), - None => self.implicit_default_expr(ctx, typ.ctype), + None => self.implicit_default_expr(ctx.used(), typ.ctype), }; // Variable declarations for variable-length arrays use the type of a pointer to the diff --git a/c2rust-transpile/src/translator/simd.rs b/c2rust-transpile/src/translator/simd.rs index 1c72986ef8..d6349750be 100644 --- a/c2rust-transpile/src/translator/simd.rs +++ b/c2rust-transpile/src/translator/simd.rs @@ -289,7 +289,7 @@ impl<'c> Translation<'c> { ctype: CTypeId, len: usize, ) -> TranslationResult>> { - let param_translation = self.convert_exprs(ctx, ids, None)?; + let param_translation = self.convert_exprs(ctx.used(), ids, None)?; param_translation.and_then_try(|mut params| { let mut is_unsafe = false; From 744f761faa6037f5bd5d30b98d553498aabb290b Mon Sep 17 00:00:00 2001 From: Rua Date: Sun, 16 Aug 2026 10:53:19 +0200 Subject: [PATCH 07/11] transpile: Pass `used()` correctly for assignment operands --- .../src/translator/named_references.rs | 2 +- c2rust-transpile/src/translator/operators.rs | 17 ++++++++++------- .../src/translator/structs_unions.rs | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/c2rust-transpile/src/translator/named_references.rs b/c2rust-transpile/src/translator/named_references.rs index 0f5fbc6b7b..c3a8e474af 100644 --- a/c2rust-transpile/src/translator/named_references.rs +++ b/c2rust-transpile/src/translator/named_references.rs @@ -93,7 +93,7 @@ impl<'c> Translation<'c> { let is_pure = self.ast_context.is_expr_pure(reference); let read = |write| self.read(reference_ty, write); - let reference = self.convert_expr(ctx.used(), reference, Some(reference_ty))?; + let reference = self.convert_expr(ctx, reference, Some(reference_ty))?; reference.and_then_try(|reference| { if is_lvalue(&reference) && (is_pure || !uses_read) { let rvalue = uses_read.then(|| read(reference.clone())).transpose()?; diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index c1b96e700a..de7c0b17eb 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -319,12 +319,14 @@ impl<'c> Translation<'c> { }); let lhs_translation = if ctx.is_used || compound_assignment_needs_desugaring { - self.name_reference_write_read(ctx, lhs)? + self.name_reference_write_read(ctx.used(), lhs)? } else { - self.name_reference_write(ctx, lhs)?.map(|named_ref| { - named_ref - .map_rvalue(|()| self.panic_or_err("Volatile value is not supposed to be read")) - }) + self.name_reference_write(ctx.used(), lhs)? + .map(|named_ref| { + named_ref.map_rvalue(|()| { + self.panic_or_err("Volatile value is not supposed to be read") + }) + }) }; rhs_translation @@ -368,6 +370,7 @@ impl<'c> Translation<'c> { // Assignment expression itself let assign_stmt = if let Some(underlying_op) = op.underlying_assignment() { // Compound assignment + let ctx = ctx.used(); if self.compound_assignment_needs_desugaring( underlying_op, @@ -377,7 +380,7 @@ impl<'c> Translation<'c> { // Cast the lhs to the compute lhs type, do the compute, and then // cast the compute result to the final lhs type. let lhs = self.make_cast( - ctx.used(), + ctx, lhs_type_id, compute_lhs_type_id, WithStmts::new_val(read.clone()), @@ -688,7 +691,7 @@ impl<'c> Translation<'c> { Some(compute_res_type_id), ) } else { - self.name_reference_write_read(ctx, arg)? + self.name_reference_write_read(ctx.used(), arg)? .and_then(|lhs| { let val_name = self.renamer.borrow_mut().fresh(Namespaces::values()); let save_old_val = mk().local_stmt(Box::new(mk().local( diff --git a/c2rust-transpile/src/translator/structs_unions.rs b/c2rust-transpile/src/translator/structs_unions.rs index cc378958be..53a8b3371c 100644 --- a/c2rust-transpile/src/translator/structs_unions.rs +++ b/c2rust-transpile/src/translator/structs_unions.rs @@ -743,7 +743,7 @@ impl<'a> Translation<'a> { rhs_expr: Box, field_id: CDeclId, ) -> TranslationResult>> { - let named_reference = self.name_reference_write_read(ctx.bitfield_write(), lhs)?; + let named_reference = self.name_reference_write_read(ctx.used().bitfield_write(), lhs)?; named_reference.and_then_try( |NamedReference { lvalue: lhs_expr, .. From 35b1dd8d45a2a0589d3ea516f57716eb543bc4f4 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 2 Sep 2026 10:58:17 +0200 Subject: [PATCH 08/11] transpile: Handle `__builtin_bzero` side effects --- c2rust-transpile/src/translator/builtins.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/c2rust-transpile/src/translator/builtins.rs b/c2rust-transpile/src/translator/builtins.rs index e3b1c67415..bf952b7881 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -216,9 +216,14 @@ impl<'c> Translation<'c> { let n_stmts = self.convert_expr(ctx.used(), args[1], None)?; let write_bytes = mk().abs_path_expr(vec!["core", "ptr", "write_bytes"]); let zero = mk().lit_expr(mk().int_lit(0, "u8")); - Ok(ptr_stmts.and_then(|ptr| { + let call = ptr_stmts.and_then(|ptr| { n_stmts.map(|n| mk().call_expr(write_bytes, vec![ptr, zero, n])) - })) + }); + Ok(self.convert_side_effects_expr( + ctx, + call, + "__builtin_bzero value is not supposed to be used", + )) } // If the target does not support data prefetch, the address expression is evaluated if From e48d1581597f9a58ae2e160391f3fa3fe3d6fbf2 Mon Sep 17 00:00:00 2001 From: Rua Date: Sun, 16 Aug 2026 13:16:09 +0200 Subject: [PATCH 09/11] transpile: Set `is_used` to `false` by default at the top level --- c2rust-transpile/src/translator/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index b4dd675396..2f57fdddae 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -894,7 +894,7 @@ pub fn translate( ) -> (String, Option, PragmaVec, CrateSet) { let mut t = Translation::new(ast_context, tcfg, main_file); let ctx = ExprContext { - is_used: true, + is_used: false, is_const: false, is_pattern: false, is_static: false, From 300e4cd2ae78cac83751495e8cac145561826fab Mon Sep 17 00:00:00 2001 From: Rua Date: Fri, 21 Aug 2026 12:45:49 +0200 Subject: [PATCH 10/11] transpile: Shortcut compound literal also if unused --- c2rust-transpile/src/translator/literals.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/c2rust-transpile/src/translator/literals.rs b/c2rust-transpile/src/translator/literals.rs index 675000433b..ceb5b397c6 100644 --- a/c2rust-transpile/src/translator/literals.rs +++ b/c2rust-transpile/src/translator/literals.rs @@ -199,8 +199,8 @@ impl<'c> Translation<'c> { ) -> TranslationResult>> { // C compound literals are lvalues, but equivalent Rust expressions generally are not. // So if an address is needed, store it in an intermediate variable first. - if !ctx.needs_address || ctx.expanding_macro.is_some() { - return self.convert_expr(ctx, val, override_ty); + if !ctx.needs_address || !ctx.is_used || ctx.expanding_macro.is_some() { + return self.convert_expr(ctx.not_needs_address(), val, override_ty); } let fresh_name = self From 6dcf1fd37c4681ddfb7610ee6b6998f1f497fcc0 Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 5 Sep 2026 11:12:22 +0200 Subject: [PATCH 11/11] transpile: Remove `used()` for children of pure expressions --- c2rust-transpile/src/translator/builtins.rs | 36 +++++++-------- c2rust-transpile/src/translator/literals.rs | 8 ++-- c2rust-transpile/src/translator/mod.rs | 6 +-- c2rust-transpile/src/translator/operators.rs | 36 +++------------ c2rust-transpile/src/translator/pointers.rs | 18 ++++---- .../src/translator/structs_unions.rs | 4 +- ...hots__transpile@macros.c.2021.clang15.snap | 1 - ...hots__transpile@macros.c.2024.clang15.snap | 1 - ...transpile@side_effects.c.2021.clang15.snap | 44 ++++--------------- ...transpile@side_effects.c.2024.clang15.snap | 44 ++++--------------- ...ts__transpile@volatile.c.2021.clang15.snap | 4 +- ...ts__transpile@volatile.c.2024.clang15.snap | 4 +- 12 files changed, 60 insertions(+), 146 deletions(-) diff --git a/c2rust-transpile/src/translator/builtins.rs b/c2rust-transpile/src/translator/builtins.rs index bf952b7881..02ae3a8d06 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -33,16 +33,12 @@ impl<'c> Translation<'c> { rotate_method_name: &'static str, ) -> TranslationResult>> { // Emit `arg0.{method_name}(arg1)` - let arg0 = self.convert_expr(ctx.used(), args[0], None)?; - let arg1 = self.convert_expr(ctx.used(), args[1], None)?; + let arg0 = self.convert_expr(ctx, args[0], None)?; + let arg1 = self.convert_expr(ctx, args[1], None)?; Ok(arg0.zip(arg1).and_then(|(arg0, arg1)| { let arg1 = mk().cast_expr(arg1, mk().path_ty(vec!["u32"])); let method_call_expr = mk().method_call_expr(arg0, rotate_method_name, vec![arg1]); - self.convert_side_effects_expr( - ctx, - WithStmts::new_val(method_call_expr), - "Builtin is not supposed to be used", - ) + WithStmts::new_val(method_call_expr) })) } @@ -116,7 +112,7 @@ impl<'c> Translation<'c> { "__builtin_signbit" | "__builtin_signbitf" | "__builtin_signbitl" => { self.import_num_traits(args[0])?; - let val = self.convert_expr(ctx.used(), args[0], None)?; + let val = self.convert_expr(ctx, args[0], None)?; Ok(val.map(|v| { let val = mk().method_call_expr(v, "is_sign_negative", vec![]); @@ -124,7 +120,7 @@ impl<'c> Translation<'c> { })) } "__builtin_ffs" | "__builtin_ffsl" | "__builtin_ffsll" => { - let val = self.convert_expr(ctx.used(), args[0], None)?; + let val = self.convert_expr(ctx, args[0], None)?; Ok(val.map(|x| { let add = BinOp::Add(Default::default()); @@ -141,33 +137,33 @@ impl<'c> Translation<'c> { })) } "__builtin_clz" | "__builtin_clzl" | "__builtin_clzll" => { - let val = self.convert_expr(ctx.used(), args[0], None)?; + let val = self.convert_expr(ctx, args[0], None)?; Ok(val.map(|x| { let zeros = mk().method_call_expr(x, "leading_zeros", vec![]); mk().cast_expr(zeros, mk().path_ty(vec!["i32"])) })) } "__builtin_ctz" | "__builtin_ctzl" | "__builtin_ctzll" => { - let val = self.convert_expr(ctx.used(), args[0], None)?; + let val = self.convert_expr(ctx, args[0], None)?; Ok(val.map(|x| { let zeros = mk().method_call_expr(x, "trailing_zeros", vec![]); mk().cast_expr(zeros, mk().path_ty(vec!["i32"])) })) } "__builtin_bswap16" | "__builtin_bswap32" | "__builtin_bswap64" => { - let val = self.convert_expr(ctx.used(), args[0], None)?; + let val = self.convert_expr(ctx, args[0], None)?; Ok(val.map(|x| mk().method_call_expr(x, "swap_bytes", vec![]))) } "__builtin_fabs" | "__builtin_fabsf" | "__builtin_fabsl" => { self.import_num_traits(args[0])?; - let val = self.convert_expr(ctx.used(), args[0], None)?; + let val = self.convert_expr(ctx, args[0], None)?; Ok(val.map(|x| mk().method_call_expr(x, "abs", vec![]))) } "__builtin_isfinite" | "__builtin_isnan" => { self.import_num_traits(args[0])?; - let val = self.convert_expr(ctx.used(), args[0], None)?; + let val = self.convert_expr(ctx, args[0], None)?; let seg = match builtin_name { "__builtin_isfinite" => "is_finite", @@ -183,7 +179,7 @@ impl<'c> Translation<'c> { self.import_num_traits(args[0])?; // isinf_sign(x) -> fabs(x) == infinity ? (signbit(x) ? -1 : 1) : 0 - let val = self.convert_expr(ctx.used(), args[0], None)?; + let val = self.convert_expr(ctx, args[0], None)?; Ok(val.map(|x| { let inner_cond = mk().method_call_expr(x.clone(), "is_sign_positive", vec![]); let one = mk().lit_expr(mk().int_lit(1, "")); @@ -202,10 +198,10 @@ impl<'c> Translation<'c> { // https://github.com/llvm-mirror/llvm/blob/master/lib/CodeGen/IntrinsicLowering.cpp#L470 Ok(WithStmts::new_val(mk().lit_expr(mk().int_lit(1, "i32")))) } - "__builtin_expect" => self.convert_expr(ctx.used(), args[0], None), + "__builtin_expect" => self.convert_expr(ctx, args[0], None), "__builtin_popcount" | "__builtin_popcountl" | "__builtin_popcountll" => { - let val = self.convert_expr(ctx.used(), args[0], None)?; + let val = self.convert_expr(ctx, args[0], None)?; Ok(val.map(|x| { let zeros = mk().method_call_expr(x, "count_ones", vec![]); mk().cast_expr(zeros, mk().path_ty(vec!["i32"])) @@ -307,8 +303,8 @@ impl<'c> Translation<'c> { // We can't convert this to Rust, but it should be safe to always return -1/0 // (depending on the value of `type`), so we emit the following: // `(if (type & 2) == 0 { -1isize } else { 0isize }) as libc::size_t` - let ptr_arg = self.convert_expr(ctx.unused(), args[0], None)?; - let type_arg = self.convert_expr(ctx.used(), args[1], None)?; + let ptr_arg = self.convert_expr(ctx, args[0], None)?; + let type_arg = self.convert_expr(ctx, args[1], None)?; Ok(ptr_arg.and_then(|_| { type_arg.map(|type_arg| { let type_and_2 = mk().binary_expr( @@ -607,7 +603,7 @@ impl<'c> Translation<'c> { } // There's currently no way to replicate this functionality in Rust, so we just // pass the ptr input param in its place. - "__builtin_assume_aligned" => Ok(self.convert_expr(ctx.used(), args[0], None)?), + "__builtin_assume_aligned" => self.convert_expr(ctx, args[0], None), // Skip over, there's no way to implement it in Rust "__builtin_unwind_init" => Ok(WithStmts::new_val(self.panic_or_err("no value"))), "__builtin_unreachable" => Ok(WithStmts::new( diff --git a/c2rust-transpile/src/translator/literals.rs b/c2rust-transpile/src/translator/literals.rs index ceb5b397c6..043ac1f968 100644 --- a/c2rust-transpile/src/translator/literals.rs +++ b/c2rust-transpile/src/translator/literals.rs @@ -258,7 +258,7 @@ impl<'c> Translation<'c> { let to_array_element = |id: CExprId| -> TranslationResult<_> { let val = - self.convert_expr(ctx.used(), id, Some(CQualTypeId::new(element_type_id)))?; + self.convert_expr(ctx, id, Some(CQualTypeId::new(element_type_id)))?; val.try_map(|x| { // Array literals require all of their elements to be // the correct type; they will not use implicit casts to @@ -325,7 +325,7 @@ impl<'c> Translation<'c> { // * `ptr_extra_braces` // * `array_of_ptrs` // * `array_of_arrays` - self.convert_expr(ctx.used(), single, expected_type_id) + self.convert_expr(ctx, single, expected_type_id) } &[single] if is_zero_literal(single) && n > 1 => { // This was likely a C array of the form `int x[16] = { 0 }`. @@ -364,9 +364,9 @@ impl<'c> Translation<'c> { } ref kind if kind.is_scalar() => { if let Some(&first) = ids.first() { - self.convert_expr(ctx.used(), first, expected_type_id) + self.convert_expr(ctx, first, expected_type_id) } else { - self.implicit_default_expr(ctx.used(), result_type_id.ctype) + self.implicit_default_expr(ctx, result_type_id.ctype) } } ref t => Err(format_err!("Init list not implemented for {:?}", t).into()), diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 2f57fdddae..75f353bd07 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -2497,7 +2497,7 @@ impl<'c> Translation<'c> { let null_pointer_case = |ptr: CExprId, is_null: bool| -> TranslationResult>> { - let val = self.convert_expr(ctx.used().decay_ref(), ptr, None)?; + let val = self.convert_expr(ctx.decay_ref(), ptr, None)?; let ptr_type = self .ast_context .index_unwrap_parens(ptr) @@ -2552,7 +2552,7 @@ impl<'c> Translation<'c> { // in https://github.com/rust-lang/rust/issues/53772, you cant compare a reference (lhs) to // a ptr (rhs) (even though the reverse works!). We could also be smarter here and just // specify Yes for that particular case, given enough analysis. - let val = self.convert_expr(ctx.used().decay_ref(), cond_id, None)?; + let val = self.convert_expr(ctx.decay_ref(), cond_id, None)?; val.try_map(|e| self.match_bool(ctx, target, ty_id, e)) } } @@ -3221,7 +3221,7 @@ impl<'c> Translation<'c> { let elts = self.compute_size_of_type(ctx, expected_type_id, result_type_id, elts)?; return elts.and_then_try(|lhs| { - let len = self.convert_expr(ctx.used().not_static(), len, expected_type_id)?; + let len = self.convert_expr(ctx.not_static(), len, expected_type_id)?; Ok(len.map(|len| { let rhs = cast_int(len, "usize", true); mk().binary_expr(BinOp::Mul(Default::default()), lhs, rhs) diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index de7c0b17eb..50c422fe2c 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -575,7 +575,7 @@ impl<'c> Translation<'c> { arg: CExprId, ) -> TranslationResult>> { let expr_type_id = expected_type_id.unwrap_or(result_type_id); - let mut unary = match op { + match op { CUnOp::AddressOf => self.convert_address_of(ctx, expr_type_id, arg), CUnOp::PreIncrement @@ -586,46 +586,22 @@ impl<'c> Translation<'c> { } CUnOp::Deref => self.convert_deref(ctx, expr_type_id, arg), - CUnOp::Plus => self.convert_expr(ctx.used(), arg, expected_type_id), // promotion is explicit in the clang AST + CUnOp::Plus => self.convert_expr(ctx, arg, expected_type_id), // promotion is explicit in the clang AST CUnOp::Negate => self.convert_negate_operator(ctx, expr_type_id, arg), CUnOp::Complement => Ok(self - .convert_expr(ctx.used(), arg, expected_type_id)? + .convert_expr(ctx, arg, expected_type_id)? .map(|a| mk().unary_expr(UnOp::Not(Default::default()), a))), CUnOp::Not => { - let val = self.convert_condition(ctx.used(), false, arg)?; + let val = self.convert_condition(ctx, false, arg)?; Ok(val.map(|x| mk().cast_expr(x, mk().abs_path_ty(vec!["core", "ffi", "c_int"])))) } - CUnOp::Extension => { - let arg = self.convert_expr(ctx, arg, expected_type_id)?; - Ok(arg) - } + CUnOp::Extension => self.convert_expr(ctx, arg, expected_type_id), CUnOp::Real | CUnOp::Imag | CUnOp::Coawait => { panic!("Unsupported extension operator") } - }?; - - // Some unused unary operators (`-foo()`) may have side effects, so we need - // to add them to stmts when name is not increment/decrement operator. - // - // `UnOp::Extension` (`__extension__`) is another exception since - // it's a no-op around the inner expression. - if !matches!( - op, - CUnOp::PreDecrement - | CUnOp::PreIncrement - | CUnOp::PostDecrement - | CUnOp::PostIncrement - | CUnOp::Extension - ) { - unary = self.convert_side_effects_expr( - ctx, - unary, - "Unary expression is not supposed to be used", - ); } - Ok(unary) } fn convert_indecrement_operator( @@ -745,7 +721,7 @@ impl<'c> Translation<'c> { let val = self.mk_int_lit(ctx, expr_type_id, val, base, true)?; Ok(WithStmts::new_val(val)) } else { - let val = self.convert_expr(ctx.used(), arg_id, Some(expr_type_id))?; + let val = self.convert_expr(ctx, arg_id, Some(expr_type_id))?; let val = val.map(|val| { if is_unsigned_integral_type { wrapping_neg_expr(val) diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index 9647413fcd..b0a2829442 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -32,7 +32,7 @@ impl<'c> Translation<'c> { // Array subscript functions as a deref too. &CExprKind::ArraySubscript(_, lhs, rhs, _) => { return self.convert_array_subscript( - ctx.used().needs_address(), + ctx.needs_address(), Some(cqual_type), lhs, rhs, @@ -47,7 +47,7 @@ impl<'c> Translation<'c> { _ => (), } - let val = self.convert_expr(ctx.used().needs_address(), arg, None)?; + let val = self.convert_expr(ctx.needs_address(), arg, None)?; // & becomes a no-op when applied to a function. if self.ast_context.is_function_pointer(cqual_type.ctype) { @@ -198,10 +198,10 @@ impl<'c> Translation<'c> { let arg_expr_kind = &self.ast_context.index_unwrap_parens(arg).kind; if let &CExprKind::Unary(_, CUnOp::AddressOf, arg, _) = arg_expr_kind { - return self.convert_expr(ctx.used(), arg, None); + return self.convert_expr(ctx, arg, None); } - self.convert_expr(ctx.used().not_needs_address(), arg, None)? + self.convert_expr(ctx.not_needs_address(), arg, None)? .try_map(|val: Box| { if let CTypeKind::Function(..) = self.ast_context.resolve_type(cqual_type.ctype).kind @@ -291,13 +291,13 @@ impl<'c> Translation<'c> { ref other => panic!("Unexpected array type {:?}", other), }; - let array_rs = self.convert_expr(ctx.used().not_needs_address(), array_id, None)?; + let array_rs = self.convert_expr(ctx.not_needs_address(), array_id, None)?; // Don't dereference the offset if we're still within the variable portion let val = if let Some(elt_type_id) = var_elt_type_id { let target_type_id = self.ast_context.type_for_kind(&CTypeKind::SSize); let offset_rs = self.convert_expr_with_cast( - ctx.used().not_needs_address(), + ctx.not_needs_address(), CQualTypeId::new(target_type_id), offset_id, )?; @@ -307,7 +307,7 @@ impl<'c> Translation<'c> { } else { let target_type_id = self.ast_context.type_for_kind(&CTypeKind::Size); let offset_rs = self.convert_expr_with_cast( - ctx.used().not_needs_address(), + ctx.not_needs_address(), CQualTypeId::new(target_type_id), offset_id, )?; @@ -332,10 +332,10 @@ impl<'c> Translation<'c> { // LHS must be ref decayed for the offset method call's self param let pointer_rs = - self.convert_expr(ctx.used().not_needs_address().decay_ref(), pointer_id, None)?; + self.convert_expr(ctx.not_needs_address().decay_ref(), pointer_id, None)?; let target_type_id = self.ast_context.type_for_kind(&CTypeKind::SSize); let offset_rs = self.convert_expr_with_cast( - ctx.used().not_needs_address(), + ctx.not_needs_address(), CQualTypeId::new(target_type_id), offset_id, )?; diff --git a/c2rust-transpile/src/translator/structs_unions.rs b/c2rust-transpile/src/translator/structs_unions.rs index 53a8b3371c..bed4ca9e14 100644 --- a/c2rust-transpile/src/translator/structs_unions.rs +++ b/c2rust-transpile/src/translator/structs_unions.rs @@ -527,7 +527,7 @@ impl<'a> Translation<'a> { fields.push(field); } Both(field_id, (field_name, ty, bitfield_width, use_inner_type)) => { - let mut expr = self.convert_expr(ctx.used(), *field_id, Some(ty))?; + let mut expr = self.convert_expr(ctx, *field_id, Some(ty))?; if use_inner_type { // See comment above @@ -620,7 +620,7 @@ impl<'a> Translation<'a> { let val = if ids.is_empty() { self.implicit_default_expr(ctx, field_ty.ctype)? } else { - self.convert_expr(ctx.used(), ids[0], None)? + self.convert_expr(ctx, ids[0], None)? }; Ok(val.map(|v| { diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@macros.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@macros.c.2021.clang15.snap index 7e82464c90..abc9dbcfc3 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@macros.c.2021.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@macros.c.2021.clang15.snap @@ -342,7 +342,6 @@ pub unsafe extern "C" fn stmt_expr_inc() -> ::core::ffi::c_int { let mut b: *mut ::core::ffi::c_int = &raw mut a; ({ *b += 1; - *b; }); return ({ *b += 1; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@macros.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@macros.c.2024.clang15.snap index 1515d75618..26f9b3c97b 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@macros.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@macros.c.2024.clang15.snap @@ -342,7 +342,6 @@ pub unsafe extern "C" fn stmt_expr_inc() -> ::core::ffi::c_int { let mut b: *mut ::core::ffi::c_int = &raw mut a; ({ *b += 1; - *b; }); return ({ *b += 1; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2021.clang15.snap index bbd0c8ee45..761caef981 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2021.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2021.clang15.snap @@ -25,47 +25,19 @@ pub struct S { #[no_mangle] pub unsafe extern "C" fn pure() { let mut i: ::core::ffi::c_int = 5 as ::core::ffi::c_int; - -i; - i; - !i; - (i == 0) as ::core::ffi::c_int; - &raw mut i; - i; - let mut c2rust_lvalue: S = S { - x: 0 as ::core::ffi::c_int, - y: 2 as ::core::ffi::c_int, - }; - &raw mut c2rust_lvalue; - -S { - x: 0 as ::core::ffi::c_int, - y: 2 as ::core::ffi::c_int, - } - .x; - (1 as ::core::ffi::c_uint).swap_bytes().wrapping_neg(); - (0 as ::core::ffi::c_long == 0) as ::core::ffi::c_int; } #[no_mangle] pub unsafe extern "C" fn pure_with_side_effect() { let mut arr: [*mut ::core::ffi::c_char; 1] = [::core::ptr::null_mut::<::core::ffi::c_char>()]; - -side_effect(); side_effect(); - !side_effect(); - (side_effect() == 0) as ::core::ffi::c_int; - (b"\0".as_ptr() as *const ::core::ffi::c_char).offset(side_effect() as isize); - let mut c2rust_lvalue: S = S { - x: side_effect(), - y: 2 as ::core::ffi::c_int, - }; - &raw mut c2rust_lvalue; - -S { - x: side_effect(), - y: 2 as ::core::ffi::c_int, - } - .x; - (side_effect() as ::core::ffi::c_uint) - .swap_bytes() - .wrapping_neg(); - (side_effect() as ::core::ffi::c_long == 0) as ::core::ffi::c_int; + side_effect(); + side_effect(); + side_effect(); + side_effect(); + side_effect(); + side_effect(); + side_effect(); + side_effect(); } #[no_mangle] pub unsafe extern "C" fn impure() { diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2024.clang15.snap index 7f885a1771..278ee01be8 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@side_effects.c.2024.clang15.snap @@ -25,47 +25,19 @@ pub struct S { #[unsafe(no_mangle)] pub unsafe extern "C" fn pure() { let mut i: ::core::ffi::c_int = 5 as ::core::ffi::c_int; - -i; - i; - !i; - (i == 0) as ::core::ffi::c_int; - &raw mut i; - i; - let mut c2rust_lvalue: S = S { - x: 0 as ::core::ffi::c_int, - y: 2 as ::core::ffi::c_int, - }; - &raw mut c2rust_lvalue; - -S { - x: 0 as ::core::ffi::c_int, - y: 2 as ::core::ffi::c_int, - } - .x; - (1 as ::core::ffi::c_uint).swap_bytes().wrapping_neg(); - (0 as ::core::ffi::c_long == 0) as ::core::ffi::c_int; } #[unsafe(no_mangle)] pub unsafe extern "C" fn pure_with_side_effect() { let mut arr: [*mut ::core::ffi::c_char; 1] = [::core::ptr::null_mut::<::core::ffi::c_char>()]; - -side_effect(); side_effect(); - !side_effect(); - (side_effect() == 0) as ::core::ffi::c_int; - (b"\0".as_ptr() as *const ::core::ffi::c_char).offset(side_effect() as isize); - let mut c2rust_lvalue: S = S { - x: side_effect(), - y: 2 as ::core::ffi::c_int, - }; - &raw mut c2rust_lvalue; - -S { - x: side_effect(), - y: 2 as ::core::ffi::c_int, - } - .x; - (side_effect() as ::core::ffi::c_uint) - .swap_bytes() - .wrapping_neg(); - (side_effect() as ::core::ffi::c_long == 0) as ::core::ffi::c_int; + side_effect(); + side_effect(); + side_effect(); + side_effect(); + side_effect(); + side_effect(); + side_effect(); + side_effect(); } #[unsafe(no_mangle)] pub unsafe extern "C" fn impure() { diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@volatile.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@volatile.c.2021.clang15.snap index 76afa4c508..254073f015 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@volatile.c.2021.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@volatile.c.2021.clang15.snap @@ -35,10 +35,10 @@ pub unsafe extern "C" fn test_volatile() { .offset(-1), ); ::core::ptr::read_volatile::<::core::ffi::c_int>(&raw const vi); - -::core::ptr::read_volatile::<::core::ffi::c_int>(&raw const vi); + ::core::ptr::read_volatile::<::core::ffi::c_int>(&raw const vi); ::core::ptr::read_volatile::<::core::ffi::c_int>(&raw const vi); ::core::ptr::read_volatile::<::core::ffi::c_int>(pvi); ::core::ptr::read_volatile::<*mut ::core::ffi::c_int>(&raw const volatile_global_struct.p); ::core::ptr::read_volatile::<*mut ::core::ffi::c_int>(&raw const volatile_global_struct.p); - *::core::ptr::read_volatile::<*mut ::core::ffi::c_int>(&raw const volatile_global_struct.p); + ::core::ptr::read_volatile::<*mut ::core::ffi::c_int>(&raw const volatile_global_struct.p); } diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@volatile.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@volatile.c.2024.clang15.snap index 3880c0f79b..dcc68cbe75 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@volatile.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@volatile.c.2024.clang15.snap @@ -35,10 +35,10 @@ pub unsafe extern "C" fn test_volatile() { .offset(-1), ); ::core::ptr::read_volatile::<::core::ffi::c_int>(&raw const vi); - -::core::ptr::read_volatile::<::core::ffi::c_int>(&raw const vi); + ::core::ptr::read_volatile::<::core::ffi::c_int>(&raw const vi); ::core::ptr::read_volatile::<::core::ffi::c_int>(&raw const vi); ::core::ptr::read_volatile::<::core::ffi::c_int>(pvi); ::core::ptr::read_volatile::<*mut ::core::ffi::c_int>(&raw const volatile_global_struct.p); ::core::ptr::read_volatile::<*mut ::core::ffi::c_int>(&raw const volatile_global_struct.p); - *::core::ptr::read_volatile::<*mut ::core::ffi::c_int>(&raw const volatile_global_struct.p); + ::core::ptr::read_volatile::<*mut ::core::ffi::c_int>(&raw const volatile_global_struct.p); }