c2rust icon indicating copy to clipboard operation
c2rust copied to clipboard

[Miscompilation] C Statement Expression results in E0308 (type mismatch)

Open chisa22 opened this issue 3 weeks ago • 3 comments

I encountered a miscompilation bug when transpiling a C program that uses Statement Expressions (({ ... })) containing internal control flow (goto). c2rust correctly identifies the need for a block, but fails to ensure the block evaluates to a value. The generated Rust block ends with a statement (assignment) instead of an expression, causing a unit type () to be returned instead of the expected i32. Reproduction C Code

#include <stdio.h>

int main(void) {
    int input = 10;
    
    // C Statement Expression with internal goto
    int result = ({
        int val;
        if (input > 5) {
            val = 42;
            goto early_exit;
        }
        val = 0;
    early_exit:
        val; // The return value of the expression
    });

    printf("Result: %d\n", result);
    return 0;
}

Generated Rust Code

#![allow(
    dead_code,
    non_camel_case_types,
    non_snake_case,
    non_upper_case_globals,
    unused_assignments,
    unused_mut
)]
#![feature(label_break_value)]
extern "C" {
    fn printf(__format: *const ::core::ffi::c_char, ...) -> ::core::ffi::c_int;
}
unsafe fn main_0() -> ::core::ffi::c_int {
    let mut input: ::core::ffi::c_int = 10 as ::core::ffi::c_int;
    let mut val: ::core::ffi::c_int = 0;
    if input > 5 as ::core::ffi::c_int {
        val = 42 as ::core::ffi::c_int;
    } else {
        val = 0 as ::core::ffi::c_int;
    }
    let mut result: ::core::ffi::c_int = 'c_646: {
        let mut val: ::core::ffi::c_int = 0;
        if input > 5 as ::core::ffi::c_int {
            val = 42 as ::core::ffi::c_int;
        } else {
            val = 0 as ::core::ffi::c_int;
        }
    };
    printf(b"Result: %d\n\0" as *const u8 as *const ::core::ffi::c_char, result);
    return 0 as ::core::ffi::c_int;
}
pub fn main() {
    unsafe { ::std::process::exit(main_0() as i32) }
}

Compilation Errors Using rustc (Stable channel):

error[E0308]: mismatched types
  --> test.rs:23:44
   |
23 |           if input > 5 as ::core::ffi::c_int {
   |  ____________________________________________^
24 | |             val = 42 as ::core::ffi::c_int;
25 | |         } else {
   | |_________^ expected `i32`, found `()`

error[E0308]: mismatched types
  --> test.rs:25:16
   |
25 |           } else {
   |  ________________^
26 | |             val = 0 as ::core::ffi::c_int;
27 | |         }
   | |_________^ expected `i32`, found `()`

error: aborting due to 3 previous errors; 2 warnings emitted

Some errors have detailed explanations: E0308, E0554.
For more information about an error, try `rustc --explain E0308`.

Environment

  • c2rust version: v0.21.0
  • rustc version: 1.91.1

chisa22 avatar Dec 01 '25 08:12 chisa22

I can confirm this issue. I was able to minimize the reproducer to the following:

int main(void) {
    // C Statement Expression with internal label
    int result = ({
    early_exit:
        0; // The return value of the expression
    });
    return 0;
}

fw-immunant avatar Dec 01 '25 14:12 fw-immunant

Cc @randomPoison as the person most likely to have our control-flow processing fresh in their head.

fw-immunant avatar Dec 01 '25 15:12 fw-immunant

The issue here is happening before we get to relooper, something is going wrong when we create the CFG that's causing us to eat the final return of a statement expr. The issue specifically happens if the final statement of the statement expr, the thing that gets returned, is labeled. i.e. this is fine:

int result = ({
label:
    x++;
    0;
});

But this breaks:

int result = ({
    x++;
label:
    0;
});

Looking at the CFG we are feeding into relooper, it looks like we're doing something wrong with the return statement:

{
    "entries": "s_0",
    "nodes": {
        "s_0": {
            "body": [
                [
                    "x += 1;"
                ]
            ],
            "terminator": {
                "Jump": [
                    "s_6"
                ]
            }
        },
        "s_6": {
            "body": [
                [
                    "return;"
                ]
            ],
            "terminator": "End"
        }
    }
}

Note that the return statement doesn't return a value. I'm not familiar with the code for building the CFG though, so I'm not sure why this is happening.

randomPoison avatar Dec 01 '25 22:12 randomPoison