Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

c2rust fails to handle increment or decrement operations within compound literals #1165

Open
Yeaseen opened this issue Nov 23, 2024 · 2 comments
Labels
bug Something isn't working

Comments

@Yeaseen
Copy link
Contributor

Yeaseen commented Nov 23, 2024

Description

The c2rust tool fails to translate valid C code that uses the post-increment operator (j++) inside a compound literal. This occurs even though the code is valid and compiles successfully with standard C compilers like GCC or Clang.

If a simple integer (e.g., 42) is used instead of j++, c2rust handles the compound literal without any issues.

Source C code

#include<stdio.h>
struct s { 
    int i; 
};

int f(void) {
    struct s *p;
    int j = 42;
    p = &((struct s){j++}); // Compound literal with address-of operator and post-increment operator
    return p->i;
}

int main() {
    int result = f();
    printf("Result of f: %d\n", result);
    return 0;
}

Actual Behavior

c2rust fails to translate the function f and gives the following error:

$ c2rust-transpile compile_commands.json -e -o transpiled_code --binary runner
Transpiling runner.c
error: Failed to translate f: Expected no statements in field expression

Expected Behavior

c2rust should translate the compound literal with j++ into equivalent Rust code.

Observation

Interestingly, c2rust can translate the following C code perfectly, and the translated rust code prints 42

#include<stdio.h>
struct s { 
    int i; 
};

int f(void) {
    struct s *p;
    p = &((struct s){42}); // Compound literal with address-of operator
    return p->i;
}

int main() {
    int result = f();
    print("%d\n", result);
    return 0;
}
@kkysen kkysen added the bug Something isn't working label Nov 23, 2024
@Yeaseen
Copy link
Contributor Author

Yeaseen commented Nov 23, 2024

I found post-decrement is also problematic, making it a general flaw.

#include<stdio.h>
struct s { 
    int i; 
};

int f(void) {
    struct s *p;
    int j = 42;
    p = &((struct s){j--}); // Compound literal with address-of operator
    return p->i;
}

int main() {
    int result = f();
    printf("Result of f: %d\n", result);
    return 0;
}

Same error.

@Yeaseen Yeaseen changed the title c2rust fails to handle post-increment (j++) within compound literals c2rust fails to handle increment or decrement operations within compound literals Nov 23, 2024
@fw-immunant
Copy link
Contributor

I believe it may just be enough to remove the offending assertion:

diff --git a/c2rust-transpile/src/translator/structs.rs b/c2rust-transpile/src/translator/structs.rs
index fae47c4d1..4eb75e18a 100644
--- a/c2rust-transpile/src/translator/structs.rs
+++ b/c2rust-transpile/src/translator/structs.rs
@@ -544,12 +544,6 @@ impl<'a> Translation<'a> {
                 Both(field_id, (field_name, _, bitfield_width, use_inner_type)) => {
                     let mut expr = self.convert_expr(ctx.used(), *field_id)?;
 
-                    if !expr.is_pure() {
-                        return Err(TranslationError::generic(
-                            "Expected no statements in field expression",
-                        ));
-                    }
-
                     if use_inner_type {
                         // See comment above
                         expr = expr.map(|fi| mk().anon_field_expr(fi, 0));

This results in the following generated code:

#![allow(dead_code, mutable_transmutes, non_camel_case_types, non_snake_case, non_upper_case_globals, unused_assignments, unused_mut)]
extern "C" {
    fn printf(_: *const libc::c_char, _: ...) -> libc::c_int;
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct s {
    pub i: libc::c_int,
}
#[no_mangle]
pub unsafe extern "C" fn f() -> libc::c_int {
    let mut p: *mut s = 0 as *mut s;
    let mut j: libc::c_int = 42 as libc::c_int;
    let fresh0 = j;
    j = j + 1;
    p = &mut {
        let mut init = s { i: fresh0 };
        init
    } as *mut s;
    return (*p).i;
}
unsafe fn main_0() -> libc::c_int {
    let mut result: libc::c_int = f();
    printf(b"Result of f: %d\n\0" as *const u8 as *const libc::c_char, result);
    return 0 as libc::c_int;
}
pub fn main() {
    unsafe { ::std::process::exit(main_0() as i32) }
}

There are two other places where we make the same assertion in convert_struct_literal, but these assert on the result of implicit_default_expr (which indeed should be pure) while it isn't reasonable to make this assertion on the translation of arbitrary expressions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants