Skip to content
New issue

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

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

Already on GitHub? # to your account

function macro definition not output #1122

Open
GPHemsley opened this issue Sep 7, 2024 · 2 comments
Open

function macro definition not output #1122

GPHemsley opened this issue Sep 7, 2024 · 2 comments

Comments

@GPHemsley
Copy link
Contributor

GPHemsley commented Sep 7, 2024

I realize that it doesn't promise to compile, but when using --translate-fn-macros to maintain function macros, no line is included for the actual definition of the macro, leading to "cannot find macro `...` in this scope" errors instead of errors about the actual macro code.

Since the original impetus for the addition of this functionality was to possibly improve the rewrite effort (f1c1c38), it would be good to give the developer a pointer to where and how the macro should be defined.

Luckily, the tests already have a file that can demonstrate the issue:

./target/release/c2rust-transpile --translate-const-macros --translate-fn-macros tests/macros/src/define.c

In the corresponding output, TEST_FN_MACRO! and inc! are not defined anywhere.

--- tests/macros/src/define_without_fn.rs	2024-09-07 01:37:59.243037053 -0400
+++ tests/macros/src/define_with_fn.rs	2024-09-07 01:32:26.060152070 -0400
@@ -12,7 +12,7 @@
 }
 #[no_mangle]
 pub unsafe extern "C" fn test_fn_macro(mut x: libc::c_int) -> libc::c_int {
-    return x * x;
+    return TEST_FN_MACRO!(x);
 }
 pub const NULL: libc::c_int = 0 as libc::c_int;
 pub const TEST_CONST1: libc::c_int = 1 as libc::c_int;
@@ -64,16 +64,13 @@
     let mut a: libc::c_int = 0 as libc::c_int;
     let mut b: *mut libc::c_int = &mut a;
     ({
-        *b += 1;
-        *b;
-        *b;
-        *b
-    });
-    return ({
-        *b += 1;
+        let ref mut fresh0 = inc!(b);
+        *fresh0 += 1;
+        *fresh0;
         *b;
         *b
     });
+    return inc!(b);
 }
 #[no_mangle]
 pub unsafe extern "C" fn test_switch(mut x: libc::c_int) -> libc::c_int {

For reference, the original definitions and their relative placement:

#define TEST_FN_MACRO(x) ((x) * (x))
int test_fn_macro(int x) {
return TEST_FN_MACRO(x);
}

#define inc(ptr) ({\
(*ptr)++;\
*ptr;\
})
// Ensure the macro generated stmt expr block is codegen'd
int stmt_expr_inc(void) {
int a = 0;
int* b = &a;
// unused
inc(b);
// used
return inc(b);
}

@GPHemsley
Copy link
Contributor Author

I've been looking into this some, and it occurs to me that converting function-like C macros to Rust macros may not be the right thing to do.

Object-like C macros are turned into Rust constants. Shouldn't function-like C macros be turned into functions? The macro-ness of each has more to do with the C code than the Rust code; if macro behavior is still needed in Rust, can't that be handled inside the Rust function?

Or is the limitation specifically around the lack of a function signature?

Is there a reason this:

#define TEST_FN_MACRO(x) ((x) * (x))

can't be converted to this:

pub fn TEST_FN_MACRO(x: libc::c_int) -> libc::c_int { x * x }

?

@kkysen
Copy link
Contributor

kkysen commented Sep 15, 2024

In the general case, we can't always convert C macros to Rust functions because they can do very weird things. Even in this example, the C macro is generic over any type that can be multiplied to itself, so the correct Rust function should be generic with a Mul bound. Deducing that from the body of the macro or its use sites is difficult, though.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants