From b889a6a69b9d1d6c698d15614ba7f92912aa9a5f Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 3 Sep 2026 10:40:49 +0200 Subject: [PATCH 01/12] transpile: Fix indentation in `macros.c` snapshot test --- c2rust-transpile/tests/snapshots/macros.c | 376 +++++++++++----------- 1 file changed, 188 insertions(+), 188 deletions(-) diff --git a/c2rust-transpile/tests/snapshots/macros.c b/c2rust-transpile/tests/snapshots/macros.c index 88e2283a42..6183cd2d71 100644 --- a/c2rust-transpile/tests/snapshots/macros.c +++ b/c2rust-transpile/tests/snapshots/macros.c @@ -3,7 +3,7 @@ #include struct S { - int i; + int i; }; #define LITERAL_INT 0xFFFF @@ -39,130 +39,130 @@ struct S { #define MEMBER LITERAL_STRUCT.i #define STMT_EXPR \ - ({ \ - int builtin = BUILTIN; \ - char indexing = INDEXING; \ - float mixed = MIXED_ARITHMETIC; \ - for (int i = 0; i < builtin; i++) { \ - mixed += (float)indexing; \ - } \ - mixed; \ - }) + ({ \ + int builtin = BUILTIN; \ + char indexing = INDEXING; \ + float mixed = MIXED_ARITHMETIC; \ + for (int i = 0; i < builtin; i++) { \ + mixed += (float)indexing; \ + } \ + mixed; \ + }) void local_muts() { - int literal_int = LITERAL_INT; - bool literal_bool = LITERAL_BOOL; - float literal_float = LITERAL_FLOAT; - char literal_char = LITERAL_CHAR; - const char *literal_str_ptr = LITERAL_STR; - char literal_str[] = LITERAL_STR; - int literal_array[] = LITERAL_ARRAY; - struct S literal_struct = LITERAL_STRUCT; - - int nested_int = NESTED_INT; - bool nested_bool = NESTED_BOOL; - float nested_float = NESTED_FLOAT; - char nested_char = NESTED_CHAR; - const char *nested_str_ptr = NESTED_STR; - char nested_str[] = NESTED_STR; - int nested_array[] = NESTED_ARRAY; - struct S nested_struct = NESTED_STRUCT; - - int negative_int = NEGATIVE_INT; - int int_arithmetic = INT_ARITHMETIC; - float mixed_arithmetic = MIXED_ARITHMETIC; - int parens = PARENS; - const char *ptr_arithmetic = PTR_ARITHMETIC; - unsigned long long widening_cast = WIDENING_CAST; - char narrowing_cast = NARROWING_CAST; - double conversion_cast = CONVERSION_CAST; - char indexing = INDEXING; - const char *str_concatenation_ptr = STR_CONCATENATION; - char str_concatenation[] = STR_CONCATENATION; - int builtin = BUILTIN; - const char *ref_indexing = REF_MACRO; - const struct S *ref_struct = REF_LITERAL; - int ternary = TERNARY; - int member = MEMBER; - float stmt_expr = STMT_EXPR; + int literal_int = LITERAL_INT; + bool literal_bool = LITERAL_BOOL; + float literal_float = LITERAL_FLOAT; + char literal_char = LITERAL_CHAR; + const char *literal_str_ptr = LITERAL_STR; + char literal_str[] = LITERAL_STR; + int literal_array[] = LITERAL_ARRAY; + struct S literal_struct = LITERAL_STRUCT; + + int nested_int = NESTED_INT; + bool nested_bool = NESTED_BOOL; + float nested_float = NESTED_FLOAT; + char nested_char = NESTED_CHAR; + const char *nested_str_ptr = NESTED_STR; + char nested_str[] = NESTED_STR; + int nested_array[] = NESTED_ARRAY; + struct S nested_struct = NESTED_STRUCT; + + int negative_int = NEGATIVE_INT; + int int_arithmetic = INT_ARITHMETIC; + float mixed_arithmetic = MIXED_ARITHMETIC; + int parens = PARENS; + const char *ptr_arithmetic = PTR_ARITHMETIC; + unsigned long long widening_cast = WIDENING_CAST; + char narrowing_cast = NARROWING_CAST; + double conversion_cast = CONVERSION_CAST; + char indexing = INDEXING; + const char *str_concatenation_ptr = STR_CONCATENATION; + char str_concatenation[] = STR_CONCATENATION; + int builtin = BUILTIN; + const char *ref_indexing = REF_MACRO; + const struct S *ref_struct = REF_LITERAL; + int ternary = TERNARY; + int member = MEMBER; + float stmt_expr = STMT_EXPR; } void local_consts() { - const int literal_int = LITERAL_INT; - const bool literal_bool = LITERAL_BOOL; - const float literal_float = LITERAL_FLOAT; - const char literal_char = LITERAL_CHAR; - const char *const literal_str_ptr = LITERAL_STR; - const char literal_str[] = LITERAL_STR; - const int literal_array[] = LITERAL_ARRAY; - const struct S literal_struct = LITERAL_STRUCT; - - const int nested_int = NESTED_INT; - const bool nested_bool = NESTED_BOOL; - const float nested_float = NESTED_FLOAT; - const char nested_char = NESTED_CHAR; - const char *const nested_str_ptr = NESTED_STR; - const char nested_str[] = NESTED_STR; - const int nested_array[] = NESTED_ARRAY; - const struct S nested_struct = NESTED_STRUCT; - - const int negative_int = NEGATIVE_INT; - const int int_arithmetic = INT_ARITHMETIC; - const float mixed_arithmetic = MIXED_ARITHMETIC; - const int parens = PARENS; - const char *const ptr_arithmetic = PTR_ARITHMETIC; - const unsigned long long widening_cast = WIDENING_CAST; - const char narrowing_cast = NARROWING_CAST; - const double conversion_cast = CONVERSION_CAST; - const char indexing = INDEXING; - const char *const str_concatenation_ptr = STR_CONCATENATION; - const char str_concatenation[] = STR_CONCATENATION; - const int builtin = BUILTIN; - const char *const ref_indexing = REF_MACRO; - const struct S *const ref_struct = REF_LITERAL; - const int ternary = TERNARY; - const int member = MEMBER; - const float stmt_expr = STMT_EXPR; + const int literal_int = LITERAL_INT; + const bool literal_bool = LITERAL_BOOL; + const float literal_float = LITERAL_FLOAT; + const char literal_char = LITERAL_CHAR; + const char *const literal_str_ptr = LITERAL_STR; + const char literal_str[] = LITERAL_STR; + const int literal_array[] = LITERAL_ARRAY; + const struct S literal_struct = LITERAL_STRUCT; + + const int nested_int = NESTED_INT; + const bool nested_bool = NESTED_BOOL; + const float nested_float = NESTED_FLOAT; + const char nested_char = NESTED_CHAR; + const char *const nested_str_ptr = NESTED_STR; + const char nested_str[] = NESTED_STR; + const int nested_array[] = NESTED_ARRAY; + const struct S nested_struct = NESTED_STRUCT; + + const int negative_int = NEGATIVE_INT; + const int int_arithmetic = INT_ARITHMETIC; + const float mixed_arithmetic = MIXED_ARITHMETIC; + const int parens = PARENS; + const char *const ptr_arithmetic = PTR_ARITHMETIC; + const unsigned long long widening_cast = WIDENING_CAST; + const char narrowing_cast = NARROWING_CAST; + const double conversion_cast = CONVERSION_CAST; + const char indexing = INDEXING; + const char *const str_concatenation_ptr = STR_CONCATENATION; + const char str_concatenation[] = STR_CONCATENATION; + const int builtin = BUILTIN; + const char *const ref_indexing = REF_MACRO; + const struct S *const ref_struct = REF_LITERAL; + const int ternary = TERNARY; + const int member = MEMBER; + const float stmt_expr = STMT_EXPR; } // TODO These are declared in the global scope and thus clash, // which is an error for statics. #if 0 void local_static_consts() { - static const int literal_int = LITERAL_INT; - static const bool literal_bool = LITERAL_BOOL; - static const float literal_float = LITERAL_FLOAT; - static const char literal_char = LITERAL_CHAR; - static const char *const literal_str_ptr = LITERAL_STR; - static const char literal_str[] = LITERAL_STR; - static const int literal_array[] = LITERAL_ARRAY; - static const struct S literal_struct = LITERAL_STRUCT; - - static const int nested_int = NESTED_INT; - static const bool nested_bool = NESTED_BOOL; - static const float nested_float = NESTED_FLOAT; - static const char nested_char = NESTED_CHAR; - static const char *const nested_str_ptr = NESTED_STR; - static const char nested_str[] = NESTED_STR; - static const int nested_array[] = NESTED_ARRAY; - static const struct S nested_struct = NESTED_STRUCT; - - static const int int_arithmetic = INT_ARITHMETIC; - static const float mixed_arithmetic = MIXED_ARITHMETIC; - static const int parens = PARENS; - static const char *const ptr_arithmetic = PTR_ARITHMETIC; - static const unsigned long long widening_cast = WIDENING_CAST; - static const char narrowing_cast = NARROWING_CAST; - static const double conversion_cast = CONVERSION_CAST; - static const char indexing = INDEXING; - static const char *const str_concatenation_ptr = STR_CONCATENATION; - static const char str_concatenation[] = STR_CONCATENATION; - static const int builtin = BUILTIN; - static const char *const ref_indexing = REF_MACRO; - static const struct S *const ref_struct = REF_LITERAL; - static const int ternary = TERNARY; - static const int member = MEMBER; - static const float stmt_expr = STMT_EXPR; + static const int literal_int = LITERAL_INT; + static const bool literal_bool = LITERAL_BOOL; + static const float literal_float = LITERAL_FLOAT; + static const char literal_char = LITERAL_CHAR; + static const char *const literal_str_ptr = LITERAL_STR; + static const char literal_str[] = LITERAL_STR; + static const int literal_array[] = LITERAL_ARRAY; + static const struct S literal_struct = LITERAL_STRUCT; + + static const int nested_int = NESTED_INT; + static const bool nested_bool = NESTED_BOOL; + static const float nested_float = NESTED_FLOAT; + static const char nested_char = NESTED_CHAR; + static const char *const nested_str_ptr = NESTED_STR; + static const char nested_str[] = NESTED_STR; + static const int nested_array[] = NESTED_ARRAY; + static const struct S nested_struct = NESTED_STRUCT; + + static const int int_arithmetic = INT_ARITHMETIC; + static const float mixed_arithmetic = MIXED_ARITHMETIC; + static const int parens = PARENS; + static const char *const ptr_arithmetic = PTR_ARITHMETIC; + static const unsigned long long widening_cast = WIDENING_CAST; + static const char narrowing_cast = NARROWING_CAST; + static const double conversion_cast = CONVERSION_CAST; + static const char indexing = INDEXING; + static const char *const str_concatenation_ptr = STR_CONCATENATION; + static const char str_concatenation[] = STR_CONCATENATION; + static const int builtin = BUILTIN; + static const char *const ref_indexing = REF_MACRO; + static const struct S *const ref_struct = REF_LITERAL; + static const int ternary = TERNARY; + static const int member = MEMBER; + static const float stmt_expr = STMT_EXPR; } #endif @@ -207,42 +207,42 @@ static const int global_static_const_member = MEMBER; // static const float global_static_const_stmt_expr = STMT_EXPR; // Statement expression not allowed at file scope. void global_static_consts() { - // Need to use `static`s or else they'll be removed when translated. - (void)global_static_const_literal_int; - (void)global_static_const_literal_bool; - (void)global_static_const_literal_float; - (void)global_static_const_literal_char; - (void)global_static_const_literal_str_ptr; - (void)global_static_const_literal_str; - (void)global_static_const_literal_array; - (void)global_static_const_literal_struct; - - (void)global_static_const_nested_int; - (void)global_static_const_nested_bool; - (void)global_static_const_nested_float; - (void)global_static_const_nested_char; - (void)global_static_const_nested_str_ptr; - (void)global_static_const_nested_str; - (void)global_static_const_nested_array; - (void)global_static_const_nested_struct; - - (void)global_static_const_negative_int; - (void)global_static_const_int_arithmetic; - (void)global_static_const_mixed_arithmetic; - (void)global_static_const_parens; - (void)global_static_const_ptr_arithmetic; - (void)global_static_const_widening_cast; - (void)global_static_const_narrowing_cast; - (void)global_static_const_conversion_cast; - (void)global_static_const_indexing; - (void)global_static_const_str_concatenation_ptr; - (void)global_static_const_str_concatenation; - (void)global_static_const_builtin; - (void)global_static_const_ref_indexing; - (void)global_static_const_ref_struct; - (void)global_static_const_ternary; - (void)global_static_const_member; - // (void)global_static_const_stmt_expr; + // Need to use `static`s or else they'll be removed when translated. + (void)global_static_const_literal_int; + (void)global_static_const_literal_bool; + (void)global_static_const_literal_float; + (void)global_static_const_literal_char; + (void)global_static_const_literal_str_ptr; + (void)global_static_const_literal_str; + (void)global_static_const_literal_array; + (void)global_static_const_literal_struct; + + (void)global_static_const_nested_int; + (void)global_static_const_nested_bool; + (void)global_static_const_nested_float; + (void)global_static_const_nested_char; + (void)global_static_const_nested_str_ptr; + (void)global_static_const_nested_str; + (void)global_static_const_nested_array; + (void)global_static_const_nested_struct; + + (void)global_static_const_negative_int; + (void)global_static_const_int_arithmetic; + (void)global_static_const_mixed_arithmetic; + (void)global_static_const_parens; + (void)global_static_const_ptr_arithmetic; + (void)global_static_const_widening_cast; + (void)global_static_const_narrowing_cast; + (void)global_static_const_conversion_cast; + (void)global_static_const_indexing; + (void)global_static_const_str_concatenation_ptr; + (void)global_static_const_str_concatenation; + (void)global_static_const_builtin; + (void)global_static_const_ref_indexing; + (void)global_static_const_ref_struct; + (void)global_static_const_ternary; + (void)global_static_const_member; + // (void)global_static_const_stmt_expr; } // global consts @@ -295,19 +295,19 @@ int test_fn_macro(int x) { return TEST_FN_MACRO(x); } #define TEST_PARENS (TEST_CONST2 + 1) * 3 int reference_define() { - int x = TEST_CONST1; - x += TEST_CONST2; - if (3 < TEST_PARENS) - x += TEST_PARENS; - return x; + int x = TEST_CONST1; + x += TEST_CONST2; + if (3 < TEST_PARENS) + x += TEST_PARENS; + return x; } // Exercise an edge case where a struct initializer needs to be in an unsafe // block struct fn_ptrs { - void *v; - int (*fn1)(void); - int (*fn2)(int); + void *v; + int (*fn1)(void); + int (*fn2)(int); }; typedef int (*fn_ptr_ty)(char); @@ -330,52 +330,52 @@ typedef long zstd_platform_dependent_type; #define ZSTD_WINDOWLOG_MAX_32 30 #define ZSTD_WINDOWLOG_MAX_64 31 #define ZSTD_WINDOWLOG_MAX \ - ((int)(sizeof(zstd_platform_dependent_type) == 4 ? ZSTD_WINDOWLOG_MAX_32 \ - : ZSTD_WINDOWLOG_MAX_64)) + ((int)(sizeof(zstd_platform_dependent_type) == 4 ? ZSTD_WINDOWLOG_MAX_32 \ + : ZSTD_WINDOWLOG_MAX_64)) U64 test_zstd() { - // This static assert was causing us trouble by somehow giving a valid - // expression for ZSTD_WINDOWLOG_MAX which shouldn't be possible to translate - // to a const. - ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX <= 31); - return ZSTD_WINDOWLOG_MAX; + // This static assert was causing us trouble by somehow giving a valid + // expression for ZSTD_WINDOWLOG_MAX which shouldn't be possible to translate + // to a const. + ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX <= 31); + return ZSTD_WINDOWLOG_MAX; } #define inc(ptr) \ - ({ \ - (*ptr)++; \ - *ptr; \ - }) + ({ \ + (*ptr)++; \ + *ptr; \ + }) // Ensure the macro generated stmt expr block is codegen'd int stmt_expr_inc(void) { - int a = 0; - int *b = &a; + int a = 0; + int *b = &a; - // unused - inc(b); + // unused + inc(b); - // used - return inc(b); + // used + return inc(b); } int test_switch(int x) { - switch (x) { - case TEST_CONST1: - return 10; - case TEST_NESTED: - return 20; - } - - return 0; + switch (x) { + case TEST_CONST1: + return 10; + case TEST_NESTED: + return 20; + } + + return 0; } // From #853. #define silk_int16_MIN ((short)0x8000) int test_silk_int16_MIN() { - // _Static_assert((int)silk_int16_MIN == -0x8000, "mistranslated"); - char _null = ""[((int)silk_int16_MIN + 0x8000)]; - return silk_int16_MIN; // Should be -0x8000 + // _Static_assert((int)silk_int16_MIN == -0x8000, "mistranslated"); + char _null = ""[((int)silk_int16_MIN + 0x8000)]; + return silk_int16_MIN; // Should be -0x8000 } // From #803. @@ -396,7 +396,7 @@ bool use_portable_type(uintptr_t len) { return len <= UINTPTR_MAX / 2; } // From `curl`'s `curl_ntlm_core.c`. struct ntlmdata { - unsigned int target_info_len; + unsigned int target_info_len; }; // Should not translate since it references an out-of-scope `ntlm` variable. @@ -409,5 +409,5 @@ unsigned int ntlm_v2_blob_len(struct ntlmdata *ntlm) { return NTLMv2_BLOB_LEN; } #define LATE_INIT_VAR ({ int i; i = 1; i; }) int late_init_var() { - return LATE_INIT_VAR; + return LATE_INIT_VAR; } From c521ec54ee9996a204b0502ebd55a1fafb7e7598 Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 3 Sep 2026 10:56:50 +0200 Subject: [PATCH 02/12] transpile: Add tests to `macro.c` for mutability of compound literals --- c2rust-transpile/tests/snapshots/macros.c | 22 ++++++++++++++++- ...hots__transpile@macros.c.2021.clang15.snap | 24 ++++++++++++++++++- ...hots__transpile@macros.c.2024.clang15.snap | 24 ++++++++++++++++++- 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/c2rust-transpile/tests/snapshots/macros.c b/c2rust-transpile/tests/snapshots/macros.c index 6183cd2d71..04618bf041 100644 --- a/c2rust-transpile/tests/snapshots/macros.c +++ b/c2rust-transpile/tests/snapshots/macros.c @@ -12,7 +12,8 @@ struct S { #define LITERAL_CHAR 'x' #define LITERAL_STR "hello" #define LITERAL_ARRAY {1, 2, 3} -#define LITERAL_STRUCT ((struct S){.i = 5}) +#define LITERAL_STRUCT ((const struct S){.i = 5}) +#define LITERAL_STRUCT_MUT ((struct S){.i = 5}) #define NESTED_INT LITERAL_INT #define NESTED_BOOL LITERAL_BOOL @@ -35,8 +36,10 @@ struct S { #define BUILTIN __builtin_clz(LITERAL_INT) #define REF_MACRO &INDEXING #define REF_LITERAL &LITERAL_STRUCT +#define REF_LITERAL_MUT &LITERAL_STRUCT_MUT #define TERNARY LITERAL_BOOL ? 1 : 2 #define MEMBER LITERAL_STRUCT.i +#define MEMBER_MUT LITERAL_STRUCT_MUT.i #define STMT_EXPR \ ({ \ @@ -58,6 +61,7 @@ void local_muts() { char literal_str[] = LITERAL_STR; int literal_array[] = LITERAL_ARRAY; struct S literal_struct = LITERAL_STRUCT; + struct S literal_struct_mut = LITERAL_STRUCT_MUT; int nested_int = NESTED_INT; bool nested_bool = NESTED_BOOL; @@ -82,8 +86,10 @@ void local_muts() { int builtin = BUILTIN; const char *ref_indexing = REF_MACRO; const struct S *ref_struct = REF_LITERAL; + struct S *ref_struct_mut = REF_LITERAL_MUT; int ternary = TERNARY; int member = MEMBER; + int member_mut = MEMBER_MUT; float stmt_expr = STMT_EXPR; } @@ -96,6 +102,7 @@ void local_consts() { const char literal_str[] = LITERAL_STR; const int literal_array[] = LITERAL_ARRAY; const struct S literal_struct = LITERAL_STRUCT; + const struct S literal_struct_mut = LITERAL_STRUCT_MUT; const int nested_int = NESTED_INT; const bool nested_bool = NESTED_BOOL; @@ -120,8 +127,10 @@ void local_consts() { const int builtin = BUILTIN; const char *const ref_indexing = REF_MACRO; const struct S *const ref_struct = REF_LITERAL; + struct S *const ref_struct_mut = REF_LITERAL_MUT; const int ternary = TERNARY; const int member = MEMBER; + const int member_mut = MEMBER_MUT; const float stmt_expr = STMT_EXPR; } @@ -137,6 +146,7 @@ void local_static_consts() { static const char literal_str[] = LITERAL_STR; static const int literal_array[] = LITERAL_ARRAY; static const struct S literal_struct = LITERAL_STRUCT; + static const struct S literal_struct_mut = LITERAL_STRUCT_MUT; static const int nested_int = NESTED_INT; static const bool nested_bool = NESTED_BOOL; @@ -160,6 +170,7 @@ void local_static_consts() { static const int builtin = BUILTIN; static const char *const ref_indexing = REF_MACRO; static const struct S *const ref_struct = REF_LITERAL; + static struct S *const ref_struct_mut = REF_LITERAL_MUT; static const int ternary = TERNARY; static const int member = MEMBER; static const float stmt_expr = STMT_EXPR; @@ -176,6 +187,7 @@ static const char *const global_static_const_literal_str_ptr = LITERAL_STR; static const char global_static_const_literal_str[] = LITERAL_STR; static const int global_static_const_literal_array[] = LITERAL_ARRAY; static const struct S global_static_const_literal_struct = LITERAL_STRUCT; +static const struct S global_static_const_literal_struct_mut = LITERAL_STRUCT_MUT; static const int global_static_const_nested_int = NESTED_INT; static const bool global_static_const_nested_bool = NESTED_BOOL; @@ -202,8 +214,10 @@ static const char global_static_const_str_concatenation[] = STR_CONCATENATION; static const int global_static_const_builtin = BUILTIN; static const char *const global_static_const_ref_indexing = REF_MACRO; static const struct S *const global_static_const_ref_struct = REF_LITERAL; +static struct S *const global_static_const_ref_struct_mut = REF_LITERAL_MUT; static const int global_static_const_ternary = TERNARY; static const int global_static_const_member = MEMBER; +static const int global_static_const_member_mut = MEMBER_MUT; // static const float global_static_const_stmt_expr = STMT_EXPR; // Statement expression not allowed at file scope. void global_static_consts() { @@ -216,6 +230,7 @@ void global_static_consts() { (void)global_static_const_literal_str; (void)global_static_const_literal_array; (void)global_static_const_literal_struct; + (void)global_static_const_literal_struct_mut; (void)global_static_const_nested_int; (void)global_static_const_nested_bool; @@ -240,8 +255,10 @@ void global_static_consts() { (void)global_static_const_builtin; (void)global_static_const_ref_indexing; (void)global_static_const_ref_struct; + (void)global_static_const_ref_struct_mut; (void)global_static_const_ternary; (void)global_static_const_member; + (void)global_static_const_member_mut; // (void)global_static_const_stmt_expr; } @@ -255,6 +272,7 @@ const char *const global_const_literal_str_ptr = LITERAL_STR; const char global_const_literal_str[] = LITERAL_STR; const int global_const_literal_array[] = LITERAL_ARRAY; const struct S global_const_literal_struct = LITERAL_STRUCT; +const struct S global_const_literal_struct_mut = LITERAL_STRUCT_MUT; const int global_const_nested_int = NESTED_INT; const bool global_const_nested_bool = NESTED_BOOL; @@ -279,8 +297,10 @@ const char global_const_str_concatenation[] = STR_CONCATENATION; const int global_const_builtin = BUILTIN; const char *const global_const_ref_indexing = REF_MACRO; const struct S *const global_const_ref_struct = REF_LITERAL; +struct S *const global_const_ref_struct_mut = REF_LITERAL_MUT; const int global_const_ternary = TERNARY; const int global_const_member = MEMBER; +const int global_const_member_mut = MEMBER_MUT; // const float global_const_stmt_expr = STMT_EXPR; // Statement expression not allowed at file scope. typedef unsigned long long U64; 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..add8008011 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 @@ -51,6 +51,9 @@ pub const LITERAL_ARRAY: [::core::ffi::c_int; 3] = [ pub const LITERAL_STRUCT: S = S { i: 5 as ::core::ffi::c_int, }; +pub const LITERAL_STRUCT_MUT: S = S { + i: 5 as ::core::ffi::c_int, +}; pub const NESTED_INT: ::core::ffi::c_int = LITERAL_INT; pub const NESTED_BOOL: ::core::ffi::c_int = LITERAL_BOOL; pub const NESTED_FLOAT: ::core::ffi::c_double = LITERAL_FLOAT; @@ -82,13 +85,15 @@ pub const REF_MACRO: *const ::core::ffi::c_char = unsafe { .as_ptr() .offset(LITERAL_FLOAT as ::core::ffi::c_int as isize) }; -pub const REF_LITERAL: *mut S = &LITERAL_STRUCT as *const S as *mut S; +pub const REF_LITERAL: *const S = &LITERAL_STRUCT as *const S; +pub const REF_LITERAL_MUT: *mut S = &LITERAL_STRUCT_MUT as *const S as *mut S; pub const TERNARY: ::core::ffi::c_int = if LITERAL_BOOL != 0 { 1 as ::core::ffi::c_int } else { 2 as ::core::ffi::c_int }; pub const MEMBER: ::core::ffi::c_int = LITERAL_STRUCT.i; +pub const MEMBER_MUT: ::core::ffi::c_int = LITERAL_STRUCT_MUT.i; #[no_mangle] pub unsafe extern "C" fn local_muts() { let mut literal_int: ::core::ffi::c_int = LITERAL_INT; @@ -99,6 +104,7 @@ pub unsafe extern "C" fn local_muts() { let mut literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; let mut literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; let mut literal_struct: S = LITERAL_STRUCT; + let mut literal_struct_mut: S = LITERAL_STRUCT_MUT; let mut nested_int: ::core::ffi::c_int = NESTED_INT; let mut nested_bool: bool = NESTED_BOOL != 0; let mut nested_float: ::core::ffi::c_float = NESTED_FLOAT as ::core::ffi::c_float; @@ -122,8 +128,10 @@ pub unsafe extern "C" fn local_muts() { (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; let mut ref_indexing: *const ::core::ffi::c_char = REF_MACRO; let mut ref_struct: *const S = REF_LITERAL; + let mut ref_struct_mut: *mut S = REF_LITERAL_MUT; let mut ternary: ::core::ffi::c_int = TERNARY; let mut member: ::core::ffi::c_int = MEMBER; + let mut member_mut: ::core::ffi::c_int = MEMBER_MUT; let mut stmt_expr: ::core::ffi::c_float = ({ let mut builtin_0: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; @@ -147,6 +155,7 @@ pub unsafe extern "C" fn local_consts() { let literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; let literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; let literal_struct: S = LITERAL_STRUCT; + let literal_struct_mut: S = LITERAL_STRUCT_MUT; let nested_int: ::core::ffi::c_int = NESTED_INT; let nested_bool: bool = NESTED_BOOL != 0; let nested_float: ::core::ffi::c_float = NESTED_FLOAT as ::core::ffi::c_float; @@ -169,8 +178,10 @@ pub unsafe extern "C" fn local_consts() { let builtin: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; let ref_indexing: *const ::core::ffi::c_char = REF_MACRO; let ref_struct: *const S = REF_LITERAL; + let ref_struct_mut: *mut S = REF_LITERAL_MUT; let ternary: ::core::ffi::c_int = TERNARY; let member: ::core::ffi::c_int = MEMBER; + let member_mut: ::core::ffi::c_int = MEMBER_MUT; let stmt_expr: ::core::ffi::c_float = ({ let mut builtin_0: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; @@ -194,6 +205,7 @@ static mut global_static_const_literal_str_ptr: *const ::core::ffi::c_char = LIT static mut global_static_const_literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; static mut global_static_const_literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; static mut global_static_const_literal_struct: S = LITERAL_STRUCT; +static mut global_static_const_literal_struct_mut: S = LITERAL_STRUCT_MUT; static mut global_static_const_nested_int: ::core::ffi::c_int = NESTED_INT; static mut global_static_const_nested_bool: bool = NESTED_BOOL != 0; static mut global_static_const_nested_float: ::core::ffi::c_float = @@ -223,8 +235,10 @@ static mut global_static_const_builtin: ::core::ffi::c_int = static mut global_static_const_ref_indexing: *const ::core::ffi::c_char = ::core::ptr::null::<::core::ffi::c_char>(); static mut global_static_const_ref_struct: *const S = REF_LITERAL; +static mut global_static_const_ref_struct_mut: *mut S = REF_LITERAL_MUT; static mut global_static_const_ternary: ::core::ffi::c_int = 0; static mut global_static_const_member: ::core::ffi::c_int = 0; +static mut global_static_const_member_mut: ::core::ffi::c_int = 0; #[no_mangle] pub unsafe extern "C" fn global_static_consts() {} #[no_mangle] @@ -245,6 +259,8 @@ pub static mut global_const_literal_array: [::core::ffi::c_int; 3] = LITERAL_ARR #[no_mangle] pub static mut global_const_literal_struct: S = LITERAL_STRUCT; #[no_mangle] +pub static mut global_const_literal_struct_mut: S = LITERAL_STRUCT_MUT; +#[no_mangle] pub static mut global_const_nested_int: ::core::ffi::c_int = NESTED_INT; #[no_mangle] pub static mut global_const_nested_bool: bool = NESTED_BOOL != 0; @@ -295,10 +311,14 @@ pub static mut global_const_ref_indexing: *const ::core::ffi::c_char = #[no_mangle] pub static mut global_const_ref_struct: *const S = REF_LITERAL; #[no_mangle] +pub static mut global_const_ref_struct_mut: *mut S = REF_LITERAL_MUT; +#[no_mangle] pub static mut global_const_ternary: ::core::ffi::c_int = 0; #[no_mangle] pub static mut global_const_member: ::core::ffi::c_int = 0; #[no_mangle] +pub static mut global_const_member_mut: ::core::ffi::c_int = 0; +#[no_mangle] pub unsafe extern "C" fn test_fn_macro(mut x: ::core::ffi::c_int) -> ::core::ffi::c_int { return x * x; } @@ -403,11 +423,13 @@ unsafe extern "C" fn c2rust_run_static_initializers() { global_static_const_ref_indexing = REF_MACRO; global_static_const_ternary = TERNARY; global_static_const_member = MEMBER; + global_static_const_member_mut = MEMBER_MUT; global_const_ptr_arithmetic = PTR_ARITHMETIC; global_const_indexing = INDEXING; global_const_ref_indexing = REF_MACRO; global_const_ternary = TERNARY; global_const_member = MEMBER; + global_const_member_mut = MEMBER_MUT; } #[used] #[cfg_attr(target_os = "linux", link_section = ".init_array")] 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..ca1b737b1f 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 @@ -51,6 +51,9 @@ pub const LITERAL_ARRAY: [::core::ffi::c_int; 3] = [ pub const LITERAL_STRUCT: S = S { i: 5 as ::core::ffi::c_int, }; +pub const LITERAL_STRUCT_MUT: S = S { + i: 5 as ::core::ffi::c_int, +}; pub const NESTED_INT: ::core::ffi::c_int = LITERAL_INT; pub const NESTED_BOOL: ::core::ffi::c_int = LITERAL_BOOL; pub const NESTED_FLOAT: ::core::ffi::c_double = LITERAL_FLOAT; @@ -82,13 +85,15 @@ pub const REF_MACRO: *const ::core::ffi::c_char = unsafe { .as_ptr() .offset(LITERAL_FLOAT as ::core::ffi::c_int as isize) }; -pub const REF_LITERAL: *mut S = &LITERAL_STRUCT as *const S as *mut S; +pub const REF_LITERAL: *const S = &LITERAL_STRUCT as *const S; +pub const REF_LITERAL_MUT: *mut S = &LITERAL_STRUCT_MUT as *const S as *mut S; pub const TERNARY: ::core::ffi::c_int = if LITERAL_BOOL != 0 { 1 as ::core::ffi::c_int } else { 2 as ::core::ffi::c_int }; pub const MEMBER: ::core::ffi::c_int = LITERAL_STRUCT.i; +pub const MEMBER_MUT: ::core::ffi::c_int = LITERAL_STRUCT_MUT.i; #[unsafe(no_mangle)] pub unsafe extern "C" fn local_muts() { let mut literal_int: ::core::ffi::c_int = LITERAL_INT; @@ -99,6 +104,7 @@ pub unsafe extern "C" fn local_muts() { let mut literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; let mut literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; let mut literal_struct: S = LITERAL_STRUCT; + let mut literal_struct_mut: S = LITERAL_STRUCT_MUT; let mut nested_int: ::core::ffi::c_int = NESTED_INT; let mut nested_bool: bool = NESTED_BOOL != 0; let mut nested_float: ::core::ffi::c_float = NESTED_FLOAT as ::core::ffi::c_float; @@ -122,8 +128,10 @@ pub unsafe extern "C" fn local_muts() { (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; let mut ref_indexing: *const ::core::ffi::c_char = REF_MACRO; let mut ref_struct: *const S = REF_LITERAL; + let mut ref_struct_mut: *mut S = REF_LITERAL_MUT; let mut ternary: ::core::ffi::c_int = TERNARY; let mut member: ::core::ffi::c_int = MEMBER; + let mut member_mut: ::core::ffi::c_int = MEMBER_MUT; let mut stmt_expr: ::core::ffi::c_float = ({ let mut builtin_0: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; @@ -147,6 +155,7 @@ pub unsafe extern "C" fn local_consts() { let literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; let literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; let literal_struct: S = LITERAL_STRUCT; + let literal_struct_mut: S = LITERAL_STRUCT_MUT; let nested_int: ::core::ffi::c_int = NESTED_INT; let nested_bool: bool = NESTED_BOOL != 0; let nested_float: ::core::ffi::c_float = NESTED_FLOAT as ::core::ffi::c_float; @@ -169,8 +178,10 @@ pub unsafe extern "C" fn local_consts() { let builtin: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; let ref_indexing: *const ::core::ffi::c_char = REF_MACRO; let ref_struct: *const S = REF_LITERAL; + let ref_struct_mut: *mut S = REF_LITERAL_MUT; let ternary: ::core::ffi::c_int = TERNARY; let member: ::core::ffi::c_int = MEMBER; + let member_mut: ::core::ffi::c_int = MEMBER_MUT; let stmt_expr: ::core::ffi::c_float = ({ let mut builtin_0: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; @@ -194,6 +205,7 @@ static mut global_static_const_literal_str_ptr: *const ::core::ffi::c_char = LIT static mut global_static_const_literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; static mut global_static_const_literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; static mut global_static_const_literal_struct: S = LITERAL_STRUCT; +static mut global_static_const_literal_struct_mut: S = LITERAL_STRUCT_MUT; static mut global_static_const_nested_int: ::core::ffi::c_int = NESTED_INT; static mut global_static_const_nested_bool: bool = NESTED_BOOL != 0; static mut global_static_const_nested_float: ::core::ffi::c_float = @@ -223,8 +235,10 @@ static mut global_static_const_builtin: ::core::ffi::c_int = static mut global_static_const_ref_indexing: *const ::core::ffi::c_char = ::core::ptr::null::<::core::ffi::c_char>(); static mut global_static_const_ref_struct: *const S = REF_LITERAL; +static mut global_static_const_ref_struct_mut: *mut S = REF_LITERAL_MUT; static mut global_static_const_ternary: ::core::ffi::c_int = 0; static mut global_static_const_member: ::core::ffi::c_int = 0; +static mut global_static_const_member_mut: ::core::ffi::c_int = 0; #[unsafe(no_mangle)] pub unsafe extern "C" fn global_static_consts() {} #[unsafe(no_mangle)] @@ -245,6 +259,8 @@ pub static mut global_const_literal_array: [::core::ffi::c_int; 3] = LITERAL_ARR #[unsafe(no_mangle)] pub static mut global_const_literal_struct: S = LITERAL_STRUCT; #[unsafe(no_mangle)] +pub static mut global_const_literal_struct_mut: S = LITERAL_STRUCT_MUT; +#[unsafe(no_mangle)] pub static mut global_const_nested_int: ::core::ffi::c_int = NESTED_INT; #[unsafe(no_mangle)] pub static mut global_const_nested_bool: bool = NESTED_BOOL != 0; @@ -295,10 +311,14 @@ pub static mut global_const_ref_indexing: *const ::core::ffi::c_char = #[unsafe(no_mangle)] pub static mut global_const_ref_struct: *const S = REF_LITERAL; #[unsafe(no_mangle)] +pub static mut global_const_ref_struct_mut: *mut S = REF_LITERAL_MUT; +#[unsafe(no_mangle)] pub static mut global_const_ternary: ::core::ffi::c_int = 0; #[unsafe(no_mangle)] pub static mut global_const_member: ::core::ffi::c_int = 0; #[unsafe(no_mangle)] +pub static mut global_const_member_mut: ::core::ffi::c_int = 0; +#[unsafe(no_mangle)] pub unsafe extern "C" fn test_fn_macro(mut x: ::core::ffi::c_int) -> ::core::ffi::c_int { return x * x; } @@ -403,11 +423,13 @@ unsafe extern "C" fn c2rust_run_static_initializers() { global_static_const_ref_indexing = REF_MACRO; global_static_const_ternary = TERNARY; global_static_const_member = MEMBER; + global_static_const_member_mut = MEMBER_MUT; global_const_ptr_arithmetic = PTR_ARITHMETIC; global_const_indexing = INDEXING; global_const_ref_indexing = REF_MACRO; global_const_ternary = TERNARY; global_const_member = MEMBER; + global_const_member_mut = MEMBER_MUT; } #[used] #[cfg_attr(target_os = "linux", unsafe(link_section = ".init_array"))] From 821e105993350aa3172e21c4fbab7f9f46e42737 Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 3 Sep 2026 16:33:45 +0200 Subject: [PATCH 03/12] transpile: In `macros.c`, put statics first to avoid #1994 --- c2rust-transpile/tests/snapshots/macros.c | 250 +++++++++--------- ...hots__transpile@macros.c.2021.clang15.snap | 210 ++++++++------- ...hots__transpile@macros.c.2024.clang15.snap | 210 ++++++++------- 3 files changed, 331 insertions(+), 339 deletions(-) diff --git a/c2rust-transpile/tests/snapshots/macros.c b/c2rust-transpile/tests/snapshots/macros.c index 04618bf041..9299dd4649 100644 --- a/c2rust-transpile/tests/snapshots/macros.c +++ b/c2rust-transpile/tests/snapshots/macros.c @@ -52,131 +52,6 @@ struct S { mixed; \ }) -void local_muts() { - int literal_int = LITERAL_INT; - bool literal_bool = LITERAL_BOOL; - float literal_float = LITERAL_FLOAT; - char literal_char = LITERAL_CHAR; - const char *literal_str_ptr = LITERAL_STR; - char literal_str[] = LITERAL_STR; - int literal_array[] = LITERAL_ARRAY; - struct S literal_struct = LITERAL_STRUCT; - struct S literal_struct_mut = LITERAL_STRUCT_MUT; - - int nested_int = NESTED_INT; - bool nested_bool = NESTED_BOOL; - float nested_float = NESTED_FLOAT; - char nested_char = NESTED_CHAR; - const char *nested_str_ptr = NESTED_STR; - char nested_str[] = NESTED_STR; - int nested_array[] = NESTED_ARRAY; - struct S nested_struct = NESTED_STRUCT; - - int negative_int = NEGATIVE_INT; - int int_arithmetic = INT_ARITHMETIC; - float mixed_arithmetic = MIXED_ARITHMETIC; - int parens = PARENS; - const char *ptr_arithmetic = PTR_ARITHMETIC; - unsigned long long widening_cast = WIDENING_CAST; - char narrowing_cast = NARROWING_CAST; - double conversion_cast = CONVERSION_CAST; - char indexing = INDEXING; - const char *str_concatenation_ptr = STR_CONCATENATION; - char str_concatenation[] = STR_CONCATENATION; - int builtin = BUILTIN; - const char *ref_indexing = REF_MACRO; - const struct S *ref_struct = REF_LITERAL; - struct S *ref_struct_mut = REF_LITERAL_MUT; - int ternary = TERNARY; - int member = MEMBER; - int member_mut = MEMBER_MUT; - float stmt_expr = STMT_EXPR; -} - -void local_consts() { - const int literal_int = LITERAL_INT; - const bool literal_bool = LITERAL_BOOL; - const float literal_float = LITERAL_FLOAT; - const char literal_char = LITERAL_CHAR; - const char *const literal_str_ptr = LITERAL_STR; - const char literal_str[] = LITERAL_STR; - const int literal_array[] = LITERAL_ARRAY; - const struct S literal_struct = LITERAL_STRUCT; - const struct S literal_struct_mut = LITERAL_STRUCT_MUT; - - const int nested_int = NESTED_INT; - const bool nested_bool = NESTED_BOOL; - const float nested_float = NESTED_FLOAT; - const char nested_char = NESTED_CHAR; - const char *const nested_str_ptr = NESTED_STR; - const char nested_str[] = NESTED_STR; - const int nested_array[] = NESTED_ARRAY; - const struct S nested_struct = NESTED_STRUCT; - - const int negative_int = NEGATIVE_INT; - const int int_arithmetic = INT_ARITHMETIC; - const float mixed_arithmetic = MIXED_ARITHMETIC; - const int parens = PARENS; - const char *const ptr_arithmetic = PTR_ARITHMETIC; - const unsigned long long widening_cast = WIDENING_CAST; - const char narrowing_cast = NARROWING_CAST; - const double conversion_cast = CONVERSION_CAST; - const char indexing = INDEXING; - const char *const str_concatenation_ptr = STR_CONCATENATION; - const char str_concatenation[] = STR_CONCATENATION; - const int builtin = BUILTIN; - const char *const ref_indexing = REF_MACRO; - const struct S *const ref_struct = REF_LITERAL; - struct S *const ref_struct_mut = REF_LITERAL_MUT; - const int ternary = TERNARY; - const int member = MEMBER; - const int member_mut = MEMBER_MUT; - const float stmt_expr = STMT_EXPR; -} - -// TODO These are declared in the global scope and thus clash, -// which is an error for statics. -#if 0 -void local_static_consts() { - static const int literal_int = LITERAL_INT; - static const bool literal_bool = LITERAL_BOOL; - static const float literal_float = LITERAL_FLOAT; - static const char literal_char = LITERAL_CHAR; - static const char *const literal_str_ptr = LITERAL_STR; - static const char literal_str[] = LITERAL_STR; - static const int literal_array[] = LITERAL_ARRAY; - static const struct S literal_struct = LITERAL_STRUCT; - static const struct S literal_struct_mut = LITERAL_STRUCT_MUT; - - static const int nested_int = NESTED_INT; - static const bool nested_bool = NESTED_BOOL; - static const float nested_float = NESTED_FLOAT; - static const char nested_char = NESTED_CHAR; - static const char *const nested_str_ptr = NESTED_STR; - static const char nested_str[] = NESTED_STR; - static const int nested_array[] = NESTED_ARRAY; - static const struct S nested_struct = NESTED_STRUCT; - - static const int int_arithmetic = INT_ARITHMETIC; - static const float mixed_arithmetic = MIXED_ARITHMETIC; - static const int parens = PARENS; - static const char *const ptr_arithmetic = PTR_ARITHMETIC; - static const unsigned long long widening_cast = WIDENING_CAST; - static const char narrowing_cast = NARROWING_CAST; - static const double conversion_cast = CONVERSION_CAST; - static const char indexing = INDEXING; - static const char *const str_concatenation_ptr = STR_CONCATENATION; - static const char str_concatenation[] = STR_CONCATENATION; - static const int builtin = BUILTIN; - static const char *const ref_indexing = REF_MACRO; - static const struct S *const ref_struct = REF_LITERAL; - static struct S *const ref_struct_mut = REF_LITERAL_MUT; - static const int ternary = TERNARY; - static const int member = MEMBER; - static const float stmt_expr = STMT_EXPR; -} -#endif - // global static consts static const int global_static_const_literal_int = LITERAL_INT; @@ -303,6 +178,131 @@ const int global_const_member = MEMBER; const int global_const_member_mut = MEMBER_MUT; // const float global_const_stmt_expr = STMT_EXPR; // Statement expression not allowed at file scope. +void local_muts() { + int literal_int = LITERAL_INT; + bool literal_bool = LITERAL_BOOL; + float literal_float = LITERAL_FLOAT; + char literal_char = LITERAL_CHAR; + const char *literal_str_ptr = LITERAL_STR; + char literal_str[] = LITERAL_STR; + int literal_array[] = LITERAL_ARRAY; + struct S literal_struct = LITERAL_STRUCT; + struct S literal_struct_mut = LITERAL_STRUCT_MUT; + + int nested_int = NESTED_INT; + bool nested_bool = NESTED_BOOL; + float nested_float = NESTED_FLOAT; + char nested_char = NESTED_CHAR; + const char *nested_str_ptr = NESTED_STR; + char nested_str[] = NESTED_STR; + int nested_array[] = NESTED_ARRAY; + struct S nested_struct = NESTED_STRUCT; + + int negative_int = NEGATIVE_INT; + int int_arithmetic = INT_ARITHMETIC; + float mixed_arithmetic = MIXED_ARITHMETIC; + int parens = PARENS; + const char *ptr_arithmetic = PTR_ARITHMETIC; + unsigned long long widening_cast = WIDENING_CAST; + char narrowing_cast = NARROWING_CAST; + double conversion_cast = CONVERSION_CAST; + char indexing = INDEXING; + const char *str_concatenation_ptr = STR_CONCATENATION; + char str_concatenation[] = STR_CONCATENATION; + int builtin = BUILTIN; + const char *ref_indexing = REF_MACRO; + const struct S *ref_struct = REF_LITERAL; + struct S *ref_struct_mut = REF_LITERAL_MUT; + int ternary = TERNARY; + int member = MEMBER; + int member_mut = MEMBER_MUT; + float stmt_expr = STMT_EXPR; +} + +void local_consts() { + const int literal_int = LITERAL_INT; + const bool literal_bool = LITERAL_BOOL; + const float literal_float = LITERAL_FLOAT; + const char literal_char = LITERAL_CHAR; + const char *const literal_str_ptr = LITERAL_STR; + const char literal_str[] = LITERAL_STR; + const int literal_array[] = LITERAL_ARRAY; + const struct S literal_struct = LITERAL_STRUCT; + const struct S literal_struct_mut = LITERAL_STRUCT_MUT; + + const int nested_int = NESTED_INT; + const bool nested_bool = NESTED_BOOL; + const float nested_float = NESTED_FLOAT; + const char nested_char = NESTED_CHAR; + const char *const nested_str_ptr = NESTED_STR; + const char nested_str[] = NESTED_STR; + const int nested_array[] = NESTED_ARRAY; + const struct S nested_struct = NESTED_STRUCT; + + const int negative_int = NEGATIVE_INT; + const int int_arithmetic = INT_ARITHMETIC; + const float mixed_arithmetic = MIXED_ARITHMETIC; + const int parens = PARENS; + const char *const ptr_arithmetic = PTR_ARITHMETIC; + const unsigned long long widening_cast = WIDENING_CAST; + const char narrowing_cast = NARROWING_CAST; + const double conversion_cast = CONVERSION_CAST; + const char indexing = INDEXING; + const char *const str_concatenation_ptr = STR_CONCATENATION; + const char str_concatenation[] = STR_CONCATENATION; + const int builtin = BUILTIN; + const char *const ref_indexing = REF_MACRO; + const struct S *const ref_struct = REF_LITERAL; + struct S *const ref_struct_mut = REF_LITERAL_MUT; + const int ternary = TERNARY; + const int member = MEMBER; + const int member_mut = MEMBER_MUT; + const float stmt_expr = STMT_EXPR; +} + +// TODO These are declared in the global scope and thus clash, +// which is an error for statics. +#if 0 +void local_static_consts() { + static const int literal_int = LITERAL_INT; + static const bool literal_bool = LITERAL_BOOL; + static const float literal_float = LITERAL_FLOAT; + static const char literal_char = LITERAL_CHAR; + static const char *const literal_str_ptr = LITERAL_STR; + static const char literal_str[] = LITERAL_STR; + static const int literal_array[] = LITERAL_ARRAY; + static const struct S literal_struct = LITERAL_STRUCT; + static const struct S literal_struct_mut = LITERAL_STRUCT_MUT; + + static const int nested_int = NESTED_INT; + static const bool nested_bool = NESTED_BOOL; + static const float nested_float = NESTED_FLOAT; + static const char nested_char = NESTED_CHAR; + static const char *const nested_str_ptr = NESTED_STR; + static const char nested_str[] = NESTED_STR; + static const int nested_array[] = NESTED_ARRAY; + static const struct S nested_struct = NESTED_STRUCT; + + static const int int_arithmetic = INT_ARITHMETIC; + static const float mixed_arithmetic = MIXED_ARITHMETIC; + static const int parens = PARENS; + static const char *const ptr_arithmetic = PTR_ARITHMETIC; + static const unsigned long long widening_cast = WIDENING_CAST; + static const char narrowing_cast = NARROWING_CAST; + static const double conversion_cast = CONVERSION_CAST; + static const char indexing = INDEXING; + static const char *const str_concatenation_ptr = STR_CONCATENATION; + static const char str_concatenation[] = STR_CONCATENATION; + static const int builtin = BUILTIN; + static const char *const ref_indexing = REF_MACRO; + static const struct S *const ref_struct = REF_LITERAL; + static struct S *const ref_struct_mut = REF_LITERAL_MUT; + static const int ternary = TERNARY; + static const int member = MEMBER; + static const float stmt_expr = STMT_EXPR; +} +#endif + typedef unsigned long long U64; #define TEST_FN_MACRO(x) ((x) * (x)) 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 add8008011..cb48c14b39 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 @@ -48,12 +48,8 @@ pub const LITERAL_ARRAY: [::core::ffi::c_int; 3] = [ 2 as ::core::ffi::c_int, 3 as ::core::ffi::c_int, ]; -pub const LITERAL_STRUCT: S = S { - i: 5 as ::core::ffi::c_int, -}; -pub const LITERAL_STRUCT_MUT: S = S { - i: 5 as ::core::ffi::c_int, -}; +pub const LITERAL_STRUCT: S = S { i: 5 }; +pub const LITERAL_STRUCT_MUT: S = S { i: 5 }; pub const NESTED_INT: ::core::ffi::c_int = LITERAL_INT; pub const NESTED_BOOL: ::core::ffi::c_int = LITERAL_BOOL; pub const NESTED_FLOAT: ::core::ffi::c_double = LITERAL_FLOAT; @@ -94,107 +90,6 @@ pub const TERNARY: ::core::ffi::c_int = if LITERAL_BOOL != 0 { }; pub const MEMBER: ::core::ffi::c_int = LITERAL_STRUCT.i; pub const MEMBER_MUT: ::core::ffi::c_int = LITERAL_STRUCT_MUT.i; -#[no_mangle] -pub unsafe extern "C" fn local_muts() { - let mut literal_int: ::core::ffi::c_int = LITERAL_INT; - let mut literal_bool: bool = LITERAL_BOOL != 0; - let mut literal_float: ::core::ffi::c_float = LITERAL_FLOAT as ::core::ffi::c_float; - let mut literal_char: ::core::ffi::c_char = LITERAL_CHAR as ::core::ffi::c_char; - let mut literal_str_ptr: *const ::core::ffi::c_char = LITERAL_STR.as_ptr(); - let mut literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; - let mut literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; - let mut literal_struct: S = LITERAL_STRUCT; - let mut literal_struct_mut: S = LITERAL_STRUCT_MUT; - let mut nested_int: ::core::ffi::c_int = NESTED_INT; - let mut nested_bool: bool = NESTED_BOOL != 0; - let mut nested_float: ::core::ffi::c_float = NESTED_FLOAT as ::core::ffi::c_float; - let mut nested_char: ::core::ffi::c_char = NESTED_CHAR as ::core::ffi::c_char; - let mut nested_str_ptr: *const ::core::ffi::c_char = NESTED_STR.as_ptr(); - let mut nested_str: [::core::ffi::c_char; 6] = NESTED_STR; - let mut nested_array: [::core::ffi::c_int; 3] = NESTED_ARRAY; - let mut nested_struct: S = NESTED_STRUCT; - let mut negative_int: ::core::ffi::c_int = NEGATIVE_INT; - let mut int_arithmetic: ::core::ffi::c_int = INT_ARITHMETIC; - let mut mixed_arithmetic: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; - let mut parens: ::core::ffi::c_int = PARENS; - let mut ptr_arithmetic: *const ::core::ffi::c_char = PTR_ARITHMETIC; - let mut widening_cast: ::core::ffi::c_ulonglong = WIDENING_CAST; - let mut narrowing_cast: ::core::ffi::c_char = NARROWING_CAST; - let mut conversion_cast: ::core::ffi::c_double = CONVERSION_CAST; - let mut indexing: ::core::ffi::c_char = INDEXING; - let mut str_concatenation_ptr: *const ::core::ffi::c_char = STR_CONCATENATION.as_ptr(); - let mut str_concatenation: [::core::ffi::c_char; 18] = STR_CONCATENATION; - let mut builtin: ::core::ffi::c_int = - (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; - let mut ref_indexing: *const ::core::ffi::c_char = REF_MACRO; - let mut ref_struct: *const S = REF_LITERAL; - let mut ref_struct_mut: *mut S = REF_LITERAL_MUT; - let mut ternary: ::core::ffi::c_int = TERNARY; - let mut member: ::core::ffi::c_int = MEMBER; - let mut member_mut: ::core::ffi::c_int = MEMBER_MUT; - let mut stmt_expr: ::core::ffi::c_float = ({ - let mut builtin_0: ::core::ffi::c_int = - (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; - let mut indexing_0: ::core::ffi::c_char = INDEXING; - let mut mixed: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; - let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; - while i < builtin_0 { - mixed += indexing_0 as ::core::ffi::c_float; - i += 1; - } - mixed - }); -} -#[no_mangle] -pub unsafe extern "C" fn local_consts() { - let literal_int: ::core::ffi::c_int = LITERAL_INT; - let literal_bool: bool = LITERAL_BOOL != 0; - let literal_float: ::core::ffi::c_float = LITERAL_FLOAT as ::core::ffi::c_float; - let literal_char: ::core::ffi::c_char = LITERAL_CHAR as ::core::ffi::c_char; - let literal_str_ptr: *const ::core::ffi::c_char = LITERAL_STR.as_ptr(); - let literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; - let literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; - let literal_struct: S = LITERAL_STRUCT; - let literal_struct_mut: S = LITERAL_STRUCT_MUT; - let nested_int: ::core::ffi::c_int = NESTED_INT; - let nested_bool: bool = NESTED_BOOL != 0; - let nested_float: ::core::ffi::c_float = NESTED_FLOAT as ::core::ffi::c_float; - let nested_char: ::core::ffi::c_char = NESTED_CHAR as ::core::ffi::c_char; - let nested_str_ptr: *const ::core::ffi::c_char = NESTED_STR.as_ptr(); - let nested_str: [::core::ffi::c_char; 6] = NESTED_STR; - let nested_array: [::core::ffi::c_int; 3] = NESTED_ARRAY; - let nested_struct: S = NESTED_STRUCT; - let negative_int: ::core::ffi::c_int = NEGATIVE_INT; - let int_arithmetic: ::core::ffi::c_int = INT_ARITHMETIC; - let mixed_arithmetic: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; - let parens: ::core::ffi::c_int = PARENS; - let ptr_arithmetic: *const ::core::ffi::c_char = PTR_ARITHMETIC; - let widening_cast: ::core::ffi::c_ulonglong = WIDENING_CAST; - let narrowing_cast: ::core::ffi::c_char = NARROWING_CAST; - let conversion_cast: ::core::ffi::c_double = CONVERSION_CAST; - let indexing: ::core::ffi::c_char = INDEXING; - let str_concatenation_ptr: *const ::core::ffi::c_char = STR_CONCATENATION.as_ptr(); - let str_concatenation: [::core::ffi::c_char; 18] = STR_CONCATENATION; - let builtin: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; - let ref_indexing: *const ::core::ffi::c_char = REF_MACRO; - let ref_struct: *const S = REF_LITERAL; - let ref_struct_mut: *mut S = REF_LITERAL_MUT; - let ternary: ::core::ffi::c_int = TERNARY; - let member: ::core::ffi::c_int = MEMBER; - let member_mut: ::core::ffi::c_int = MEMBER_MUT; - let stmt_expr: ::core::ffi::c_float = ({ - let mut builtin_0: ::core::ffi::c_int = - (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; - let mut indexing_0: ::core::ffi::c_char = INDEXING; - let mut mixed: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; - let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; - while i < builtin_0 { - mixed += indexing_0 as ::core::ffi::c_float; - i += 1; - } - mixed - }); -} static mut global_static_const_literal_int: ::core::ffi::c_int = LITERAL_INT; static mut global_static_const_literal_bool: bool = LITERAL_BOOL != 0; static mut global_static_const_literal_float: ::core::ffi::c_float = @@ -319,6 +214,107 @@ pub static mut global_const_member: ::core::ffi::c_int = 0; #[no_mangle] pub static mut global_const_member_mut: ::core::ffi::c_int = 0; #[no_mangle] +pub unsafe extern "C" fn local_muts() { + let mut literal_int: ::core::ffi::c_int = LITERAL_INT; + let mut literal_bool: bool = LITERAL_BOOL != 0; + let mut literal_float: ::core::ffi::c_float = LITERAL_FLOAT as ::core::ffi::c_float; + let mut literal_char: ::core::ffi::c_char = LITERAL_CHAR as ::core::ffi::c_char; + let mut literal_str_ptr: *const ::core::ffi::c_char = LITERAL_STR.as_ptr(); + let mut literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; + let mut literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; + let mut literal_struct: S = LITERAL_STRUCT; + let mut literal_struct_mut: S = LITERAL_STRUCT_MUT; + let mut nested_int: ::core::ffi::c_int = NESTED_INT; + let mut nested_bool: bool = NESTED_BOOL != 0; + let mut nested_float: ::core::ffi::c_float = NESTED_FLOAT as ::core::ffi::c_float; + let mut nested_char: ::core::ffi::c_char = NESTED_CHAR as ::core::ffi::c_char; + let mut nested_str_ptr: *const ::core::ffi::c_char = NESTED_STR.as_ptr(); + let mut nested_str: [::core::ffi::c_char; 6] = NESTED_STR; + let mut nested_array: [::core::ffi::c_int; 3] = NESTED_ARRAY; + let mut nested_struct: S = NESTED_STRUCT; + let mut negative_int: ::core::ffi::c_int = NEGATIVE_INT; + let mut int_arithmetic: ::core::ffi::c_int = INT_ARITHMETIC; + let mut mixed_arithmetic: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; + let mut parens: ::core::ffi::c_int = PARENS; + let mut ptr_arithmetic: *const ::core::ffi::c_char = PTR_ARITHMETIC; + let mut widening_cast: ::core::ffi::c_ulonglong = WIDENING_CAST; + let mut narrowing_cast: ::core::ffi::c_char = NARROWING_CAST; + let mut conversion_cast: ::core::ffi::c_double = CONVERSION_CAST; + let mut indexing: ::core::ffi::c_char = INDEXING; + let mut str_concatenation_ptr: *const ::core::ffi::c_char = STR_CONCATENATION.as_ptr(); + let mut str_concatenation: [::core::ffi::c_char; 18] = STR_CONCATENATION; + let mut builtin: ::core::ffi::c_int = + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + let mut ref_indexing: *const ::core::ffi::c_char = REF_MACRO; + let mut ref_struct: *const S = REF_LITERAL; + let mut ref_struct_mut: *mut S = REF_LITERAL_MUT; + let mut ternary: ::core::ffi::c_int = TERNARY; + let mut member: ::core::ffi::c_int = MEMBER; + let mut member_mut: ::core::ffi::c_int = MEMBER_MUT; + let mut stmt_expr: ::core::ffi::c_float = ({ + let mut builtin_0: ::core::ffi::c_int = + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + let mut indexing_0: ::core::ffi::c_char = INDEXING; + let mut mixed: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; + let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; + while i < builtin_0 { + mixed += indexing_0 as ::core::ffi::c_float; + i += 1; + } + mixed + }); +} +#[no_mangle] +pub unsafe extern "C" fn local_consts() { + let literal_int: ::core::ffi::c_int = LITERAL_INT; + let literal_bool: bool = LITERAL_BOOL != 0; + let literal_float: ::core::ffi::c_float = LITERAL_FLOAT as ::core::ffi::c_float; + let literal_char: ::core::ffi::c_char = LITERAL_CHAR as ::core::ffi::c_char; + let literal_str_ptr: *const ::core::ffi::c_char = LITERAL_STR.as_ptr(); + let literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; + let literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; + let literal_struct: S = LITERAL_STRUCT; + let literal_struct_mut: S = LITERAL_STRUCT_MUT; + let nested_int: ::core::ffi::c_int = NESTED_INT; + let nested_bool: bool = NESTED_BOOL != 0; + let nested_float: ::core::ffi::c_float = NESTED_FLOAT as ::core::ffi::c_float; + let nested_char: ::core::ffi::c_char = NESTED_CHAR as ::core::ffi::c_char; + let nested_str_ptr: *const ::core::ffi::c_char = NESTED_STR.as_ptr(); + let nested_str: [::core::ffi::c_char; 6] = NESTED_STR; + let nested_array: [::core::ffi::c_int; 3] = NESTED_ARRAY; + let nested_struct: S = NESTED_STRUCT; + let negative_int: ::core::ffi::c_int = NEGATIVE_INT; + let int_arithmetic: ::core::ffi::c_int = INT_ARITHMETIC; + let mixed_arithmetic: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; + let parens: ::core::ffi::c_int = PARENS; + let ptr_arithmetic: *const ::core::ffi::c_char = PTR_ARITHMETIC; + let widening_cast: ::core::ffi::c_ulonglong = WIDENING_CAST; + let narrowing_cast: ::core::ffi::c_char = NARROWING_CAST; + let conversion_cast: ::core::ffi::c_double = CONVERSION_CAST; + let indexing: ::core::ffi::c_char = INDEXING; + let str_concatenation_ptr: *const ::core::ffi::c_char = STR_CONCATENATION.as_ptr(); + let str_concatenation: [::core::ffi::c_char; 18] = STR_CONCATENATION; + let builtin: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + let ref_indexing: *const ::core::ffi::c_char = REF_MACRO; + let ref_struct: *const S = REF_LITERAL; + let ref_struct_mut: *mut S = REF_LITERAL_MUT; + let ternary: ::core::ffi::c_int = TERNARY; + let member: ::core::ffi::c_int = MEMBER; + let member_mut: ::core::ffi::c_int = MEMBER_MUT; + let stmt_expr: ::core::ffi::c_float = ({ + let mut builtin_0: ::core::ffi::c_int = + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + let mut indexing_0: ::core::ffi::c_char = INDEXING; + let mut mixed: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; + let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; + while i < builtin_0 { + mixed += indexing_0 as ::core::ffi::c_float; + i += 1; + } + mixed + }); +} +#[no_mangle] pub unsafe extern "C" fn test_fn_macro(mut x: ::core::ffi::c_int) -> ::core::ffi::c_int { return x * x; } 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 ca1b737b1f..5ceac46f1b 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 @@ -48,12 +48,8 @@ pub const LITERAL_ARRAY: [::core::ffi::c_int; 3] = [ 2 as ::core::ffi::c_int, 3 as ::core::ffi::c_int, ]; -pub const LITERAL_STRUCT: S = S { - i: 5 as ::core::ffi::c_int, -}; -pub const LITERAL_STRUCT_MUT: S = S { - i: 5 as ::core::ffi::c_int, -}; +pub const LITERAL_STRUCT: S = S { i: 5 }; +pub const LITERAL_STRUCT_MUT: S = S { i: 5 }; pub const NESTED_INT: ::core::ffi::c_int = LITERAL_INT; pub const NESTED_BOOL: ::core::ffi::c_int = LITERAL_BOOL; pub const NESTED_FLOAT: ::core::ffi::c_double = LITERAL_FLOAT; @@ -94,107 +90,6 @@ pub const TERNARY: ::core::ffi::c_int = if LITERAL_BOOL != 0 { }; pub const MEMBER: ::core::ffi::c_int = LITERAL_STRUCT.i; pub const MEMBER_MUT: ::core::ffi::c_int = LITERAL_STRUCT_MUT.i; -#[unsafe(no_mangle)] -pub unsafe extern "C" fn local_muts() { - let mut literal_int: ::core::ffi::c_int = LITERAL_INT; - let mut literal_bool: bool = LITERAL_BOOL != 0; - let mut literal_float: ::core::ffi::c_float = LITERAL_FLOAT as ::core::ffi::c_float; - let mut literal_char: ::core::ffi::c_char = LITERAL_CHAR as ::core::ffi::c_char; - let mut literal_str_ptr: *const ::core::ffi::c_char = LITERAL_STR.as_ptr(); - let mut literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; - let mut literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; - let mut literal_struct: S = LITERAL_STRUCT; - let mut literal_struct_mut: S = LITERAL_STRUCT_MUT; - let mut nested_int: ::core::ffi::c_int = NESTED_INT; - let mut nested_bool: bool = NESTED_BOOL != 0; - let mut nested_float: ::core::ffi::c_float = NESTED_FLOAT as ::core::ffi::c_float; - let mut nested_char: ::core::ffi::c_char = NESTED_CHAR as ::core::ffi::c_char; - let mut nested_str_ptr: *const ::core::ffi::c_char = NESTED_STR.as_ptr(); - let mut nested_str: [::core::ffi::c_char; 6] = NESTED_STR; - let mut nested_array: [::core::ffi::c_int; 3] = NESTED_ARRAY; - let mut nested_struct: S = NESTED_STRUCT; - let mut negative_int: ::core::ffi::c_int = NEGATIVE_INT; - let mut int_arithmetic: ::core::ffi::c_int = INT_ARITHMETIC; - let mut mixed_arithmetic: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; - let mut parens: ::core::ffi::c_int = PARENS; - let mut ptr_arithmetic: *const ::core::ffi::c_char = PTR_ARITHMETIC; - let mut widening_cast: ::core::ffi::c_ulonglong = WIDENING_CAST; - let mut narrowing_cast: ::core::ffi::c_char = NARROWING_CAST; - let mut conversion_cast: ::core::ffi::c_double = CONVERSION_CAST; - let mut indexing: ::core::ffi::c_char = INDEXING; - let mut str_concatenation_ptr: *const ::core::ffi::c_char = STR_CONCATENATION.as_ptr(); - let mut str_concatenation: [::core::ffi::c_char; 18] = STR_CONCATENATION; - let mut builtin: ::core::ffi::c_int = - (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; - let mut ref_indexing: *const ::core::ffi::c_char = REF_MACRO; - let mut ref_struct: *const S = REF_LITERAL; - let mut ref_struct_mut: *mut S = REF_LITERAL_MUT; - let mut ternary: ::core::ffi::c_int = TERNARY; - let mut member: ::core::ffi::c_int = MEMBER; - let mut member_mut: ::core::ffi::c_int = MEMBER_MUT; - let mut stmt_expr: ::core::ffi::c_float = ({ - let mut builtin_0: ::core::ffi::c_int = - (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; - let mut indexing_0: ::core::ffi::c_char = INDEXING; - let mut mixed: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; - let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; - while i < builtin_0 { - mixed += indexing_0 as ::core::ffi::c_float; - i += 1; - } - mixed - }); -} -#[unsafe(no_mangle)] -pub unsafe extern "C" fn local_consts() { - let literal_int: ::core::ffi::c_int = LITERAL_INT; - let literal_bool: bool = LITERAL_BOOL != 0; - let literal_float: ::core::ffi::c_float = LITERAL_FLOAT as ::core::ffi::c_float; - let literal_char: ::core::ffi::c_char = LITERAL_CHAR as ::core::ffi::c_char; - let literal_str_ptr: *const ::core::ffi::c_char = LITERAL_STR.as_ptr(); - let literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; - let literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; - let literal_struct: S = LITERAL_STRUCT; - let literal_struct_mut: S = LITERAL_STRUCT_MUT; - let nested_int: ::core::ffi::c_int = NESTED_INT; - let nested_bool: bool = NESTED_BOOL != 0; - let nested_float: ::core::ffi::c_float = NESTED_FLOAT as ::core::ffi::c_float; - let nested_char: ::core::ffi::c_char = NESTED_CHAR as ::core::ffi::c_char; - let nested_str_ptr: *const ::core::ffi::c_char = NESTED_STR.as_ptr(); - let nested_str: [::core::ffi::c_char; 6] = NESTED_STR; - let nested_array: [::core::ffi::c_int; 3] = NESTED_ARRAY; - let nested_struct: S = NESTED_STRUCT; - let negative_int: ::core::ffi::c_int = NEGATIVE_INT; - let int_arithmetic: ::core::ffi::c_int = INT_ARITHMETIC; - let mixed_arithmetic: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; - let parens: ::core::ffi::c_int = PARENS; - let ptr_arithmetic: *const ::core::ffi::c_char = PTR_ARITHMETIC; - let widening_cast: ::core::ffi::c_ulonglong = WIDENING_CAST; - let narrowing_cast: ::core::ffi::c_char = NARROWING_CAST; - let conversion_cast: ::core::ffi::c_double = CONVERSION_CAST; - let indexing: ::core::ffi::c_char = INDEXING; - let str_concatenation_ptr: *const ::core::ffi::c_char = STR_CONCATENATION.as_ptr(); - let str_concatenation: [::core::ffi::c_char; 18] = STR_CONCATENATION; - let builtin: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; - let ref_indexing: *const ::core::ffi::c_char = REF_MACRO; - let ref_struct: *const S = REF_LITERAL; - let ref_struct_mut: *mut S = REF_LITERAL_MUT; - let ternary: ::core::ffi::c_int = TERNARY; - let member: ::core::ffi::c_int = MEMBER; - let member_mut: ::core::ffi::c_int = MEMBER_MUT; - let stmt_expr: ::core::ffi::c_float = ({ - let mut builtin_0: ::core::ffi::c_int = - (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; - let mut indexing_0: ::core::ffi::c_char = INDEXING; - let mut mixed: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; - let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; - while i < builtin_0 { - mixed += indexing_0 as ::core::ffi::c_float; - i += 1; - } - mixed - }); -} static mut global_static_const_literal_int: ::core::ffi::c_int = LITERAL_INT; static mut global_static_const_literal_bool: bool = LITERAL_BOOL != 0; static mut global_static_const_literal_float: ::core::ffi::c_float = @@ -319,6 +214,107 @@ pub static mut global_const_member: ::core::ffi::c_int = 0; #[unsafe(no_mangle)] pub static mut global_const_member_mut: ::core::ffi::c_int = 0; #[unsafe(no_mangle)] +pub unsafe extern "C" fn local_muts() { + let mut literal_int: ::core::ffi::c_int = LITERAL_INT; + let mut literal_bool: bool = LITERAL_BOOL != 0; + let mut literal_float: ::core::ffi::c_float = LITERAL_FLOAT as ::core::ffi::c_float; + let mut literal_char: ::core::ffi::c_char = LITERAL_CHAR as ::core::ffi::c_char; + let mut literal_str_ptr: *const ::core::ffi::c_char = LITERAL_STR.as_ptr(); + let mut literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; + let mut literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; + let mut literal_struct: S = LITERAL_STRUCT; + let mut literal_struct_mut: S = LITERAL_STRUCT_MUT; + let mut nested_int: ::core::ffi::c_int = NESTED_INT; + let mut nested_bool: bool = NESTED_BOOL != 0; + let mut nested_float: ::core::ffi::c_float = NESTED_FLOAT as ::core::ffi::c_float; + let mut nested_char: ::core::ffi::c_char = NESTED_CHAR as ::core::ffi::c_char; + let mut nested_str_ptr: *const ::core::ffi::c_char = NESTED_STR.as_ptr(); + let mut nested_str: [::core::ffi::c_char; 6] = NESTED_STR; + let mut nested_array: [::core::ffi::c_int; 3] = NESTED_ARRAY; + let mut nested_struct: S = NESTED_STRUCT; + let mut negative_int: ::core::ffi::c_int = NEGATIVE_INT; + let mut int_arithmetic: ::core::ffi::c_int = INT_ARITHMETIC; + let mut mixed_arithmetic: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; + let mut parens: ::core::ffi::c_int = PARENS; + let mut ptr_arithmetic: *const ::core::ffi::c_char = PTR_ARITHMETIC; + let mut widening_cast: ::core::ffi::c_ulonglong = WIDENING_CAST; + let mut narrowing_cast: ::core::ffi::c_char = NARROWING_CAST; + let mut conversion_cast: ::core::ffi::c_double = CONVERSION_CAST; + let mut indexing: ::core::ffi::c_char = INDEXING; + let mut str_concatenation_ptr: *const ::core::ffi::c_char = STR_CONCATENATION.as_ptr(); + let mut str_concatenation: [::core::ffi::c_char; 18] = STR_CONCATENATION; + let mut builtin: ::core::ffi::c_int = + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + let mut ref_indexing: *const ::core::ffi::c_char = REF_MACRO; + let mut ref_struct: *const S = REF_LITERAL; + let mut ref_struct_mut: *mut S = REF_LITERAL_MUT; + let mut ternary: ::core::ffi::c_int = TERNARY; + let mut member: ::core::ffi::c_int = MEMBER; + let mut member_mut: ::core::ffi::c_int = MEMBER_MUT; + let mut stmt_expr: ::core::ffi::c_float = ({ + let mut builtin_0: ::core::ffi::c_int = + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + let mut indexing_0: ::core::ffi::c_char = INDEXING; + let mut mixed: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; + let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; + while i < builtin_0 { + mixed += indexing_0 as ::core::ffi::c_float; + i += 1; + } + mixed + }); +} +#[unsafe(no_mangle)] +pub unsafe extern "C" fn local_consts() { + let literal_int: ::core::ffi::c_int = LITERAL_INT; + let literal_bool: bool = LITERAL_BOOL != 0; + let literal_float: ::core::ffi::c_float = LITERAL_FLOAT as ::core::ffi::c_float; + let literal_char: ::core::ffi::c_char = LITERAL_CHAR as ::core::ffi::c_char; + let literal_str_ptr: *const ::core::ffi::c_char = LITERAL_STR.as_ptr(); + let literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; + let literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; + let literal_struct: S = LITERAL_STRUCT; + let literal_struct_mut: S = LITERAL_STRUCT_MUT; + let nested_int: ::core::ffi::c_int = NESTED_INT; + let nested_bool: bool = NESTED_BOOL != 0; + let nested_float: ::core::ffi::c_float = NESTED_FLOAT as ::core::ffi::c_float; + let nested_char: ::core::ffi::c_char = NESTED_CHAR as ::core::ffi::c_char; + let nested_str_ptr: *const ::core::ffi::c_char = NESTED_STR.as_ptr(); + let nested_str: [::core::ffi::c_char; 6] = NESTED_STR; + let nested_array: [::core::ffi::c_int; 3] = NESTED_ARRAY; + let nested_struct: S = NESTED_STRUCT; + let negative_int: ::core::ffi::c_int = NEGATIVE_INT; + let int_arithmetic: ::core::ffi::c_int = INT_ARITHMETIC; + let mixed_arithmetic: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; + let parens: ::core::ffi::c_int = PARENS; + let ptr_arithmetic: *const ::core::ffi::c_char = PTR_ARITHMETIC; + let widening_cast: ::core::ffi::c_ulonglong = WIDENING_CAST; + let narrowing_cast: ::core::ffi::c_char = NARROWING_CAST; + let conversion_cast: ::core::ffi::c_double = CONVERSION_CAST; + let indexing: ::core::ffi::c_char = INDEXING; + let str_concatenation_ptr: *const ::core::ffi::c_char = STR_CONCATENATION.as_ptr(); + let str_concatenation: [::core::ffi::c_char; 18] = STR_CONCATENATION; + let builtin: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + let ref_indexing: *const ::core::ffi::c_char = REF_MACRO; + let ref_struct: *const S = REF_LITERAL; + let ref_struct_mut: *mut S = REF_LITERAL_MUT; + let ternary: ::core::ffi::c_int = TERNARY; + let member: ::core::ffi::c_int = MEMBER; + let member_mut: ::core::ffi::c_int = MEMBER_MUT; + let stmt_expr: ::core::ffi::c_float = ({ + let mut builtin_0: ::core::ffi::c_int = + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + let mut indexing_0: ::core::ffi::c_char = INDEXING; + let mut mixed: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; + let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; + while i < builtin_0 { + mixed += indexing_0 as ::core::ffi::c_float; + i += 1; + } + mixed + }); +} +#[unsafe(no_mangle)] pub unsafe extern "C" fn test_fn_macro(mut x: ::core::ffi::c_int) -> ::core::ffi::c_int { return x * x; } From 848ff3e043e0f191b80114d0e7848e4c54ca7122 Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 3 Sep 2026 19:04:56 +0200 Subject: [PATCH 04/12] transpile: Conservatively disallow mutable lvalues inside macros --- c2rust-transpile/src/translator/literals.rs | 6 ++- c2rust-transpile/src/translator/mod.rs | 15 ++++-- c2rust-transpile/src/translator/operators.rs | 3 +- c2rust-transpile/src/translator/pointers.rs | 19 +++++++- .../src/translator/structs_unions.rs | 7 +++ ...pile@compound_literals.c.2021.clang15.snap | 31 ++++++------ ...pile@compound_literals.c.2024.clang15.snap | 31 ++++++------ ...hots__transpile@macros.c.2021.clang15.snap | 47 +++++++++++++------ ...hots__transpile@macros.c.2024.clang15.snap | 45 ++++++++++++------ 9 files changed, 138 insertions(+), 66 deletions(-) diff --git a/c2rust-transpile/src/translator/literals.rs b/c2rust-transpile/src/translator/literals.rs index e8c76c12a5..ca7ba5a573 100644 --- a/c2rust-transpile/src/translator/literals.rs +++ b/c2rust-transpile/src/translator/literals.rs @@ -197,9 +197,13 @@ impl<'c> Translation<'c> { val: CExprId, override_ty: Option, ) -> TranslationResult>> { + if !qty.qualifiers.is_const && ctx.expanding_macro.is_some() { + return Err("mutable lvalues are not supported inside macros".into()); + } + // 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() { + if !ctx.needs_address { return self.convert_expr(ctx, val, override_ty); } diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 4308e61bf5..f1aab97179 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -3544,8 +3544,8 @@ impl<'c> Translation<'c> { matches!(expr_kind, CExprKind::ExplicitCast(..)), ), - Unary(result_type_id, op, arg, _lrvalue) => { - self.convert_unary_operator(ctx, override_ty, result_type_id, op, arg) + Unary(result_type_id, op, arg, lrvalue) => { + self.convert_unary_operator(ctx, override_ty, result_type_id, op, arg, lrvalue) } Conditional(ty, cond, lhs, rhs) => { @@ -3637,8 +3637,8 @@ impl<'c> Translation<'c> { ) .map_err(|e| e.add_loc(self.ast_context.display_loc(src_loc))), - ArraySubscript(_, lhs, rhs, lrvalue) => self - .convert_array_subscript(ctx, override_ty, lhs, rhs, lrvalue, true) + ArraySubscript(result_type_id, lhs, rhs, lrvalue) => self + .convert_array_subscript(ctx, override_ty, result_type_id, lhs, rhs, lrvalue, true) .map_err(|e| e.add_loc(self.ast_context.display_loc(src_loc))), Call(call_expr_ty, func, ref args) => { @@ -3759,6 +3759,8 @@ impl<'c> Translation<'c> { .get_decl(&decl_id) .ok_or_else(|| format_err!("Missing declref {:?}", decl_id))? .kind; + + #[allow(unreachable_code)] // TODO temporary (see below). if ctx.expanding_macro.is_some() { // TODO Determining which declarations have been declared within the scope of the const macro expr // vs. which are out-of-scope of the const macro is non-trivial, @@ -3768,7 +3770,10 @@ impl<'c> Translation<'c> { "Cannot yet refer to declarations in a const expr", )); - #[allow(unreachable_code)] // TODO temporary (see above). + if matches!(lrvalue, LRValue::LValue) && !result_type_id.qualifiers.is_const { + return Err("mutable lvalues are not supported inside macros".into()); + } + if let CDeclKind::Variable { has_static_duration: true, .. diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index 4c2de512b8..18651aa000 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -570,6 +570,7 @@ impl<'c> Translation<'c> { result_type_id: CQualTypeId, op: CUnOp, arg: CExprId, + lrvalue: LRValue, ) -> TranslationResult>> { let expr_type_id = expected_type_id.unwrap_or(result_type_id); let mut unary = match op { @@ -582,7 +583,7 @@ impl<'c> Translation<'c> { self.convert_indecrement_operator(ctx, expected_type_id, result_type_id, op, arg) } - CUnOp::Deref => self.convert_deref(ctx, expr_type_id, arg), + CUnOp::Deref => self.convert_deref(ctx, expr_type_id, arg, lrvalue), CUnOp::Plus => self.convert_expr(ctx.used(), arg, expected_type_id), // promotion is explicit in the clang AST CUnOp::Negate => self.convert_negate_operator(ctx, expr_type_id, arg), diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index 9647413fcd..0723aa0959 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -30,10 +30,11 @@ impl<'c> Translation<'c> { return self.convert_expr(ctx, *target, None) } // Array subscript functions as a deref too. - &CExprKind::ArraySubscript(_, lhs, rhs, _) => { + &CExprKind::ArraySubscript(result_type_id, lhs, rhs, _) => { return self.convert_array_subscript( ctx.used().needs_address(), Some(cqual_type), + result_type_id, lhs, rhs, LRValue::RValue, // if we bypass the deref, we stay an RValue @@ -194,7 +195,15 @@ impl<'c> Translation<'c> { ctx: ExprContext, cqual_type: CQualTypeId, arg: CExprId, + lrvalue: LRValue, ) -> TranslationResult>> { + if matches!(lrvalue, LRValue::LValue) + && !cqual_type.qualifiers.is_const + && ctx.expanding_macro.is_some() + { + return Err("mutable lvalues are not supported inside macros".into()); + } + let arg_expr_kind = &self.ast_context.index_unwrap_parens(arg).kind; if let &CExprKind::Unary(_, CUnOp::AddressOf, arg, _) = arg_expr_kind { @@ -219,11 +228,19 @@ impl<'c> Translation<'c> { &self, ctx: ExprContext, expected_type_id: Option, + result_type_id: CQualTypeId, lhs: CExprId, rhs: CExprId, lrvalue: LRValue, deref: bool, ) -> TranslationResult>> { + if matches!(lrvalue, LRValue::LValue) + && !result_type_id.qualifiers.is_const + && ctx.expanding_macro.is_some() + { + return Err("mutable lvalues are not supported inside macros".into()); + } + let (pointer_id, offset_id) = if self.ast_context.expr_is_indexable(lhs) { (lhs, rhs) } else { diff --git a/c2rust-transpile/src/translator/structs_unions.rs b/c2rust-transpile/src/translator/structs_unions.rs index e6c214111d..74a86311b9 100644 --- a/c2rust-transpile/src/translator/structs_unions.rs +++ b/c2rust-transpile/src/translator/structs_unions.rs @@ -1053,6 +1053,13 @@ impl<'a> Translation<'a> { lrvalue: LRValue, override_ty: Option, ) -> TranslationResult>> { + if matches!(lrvalue, LRValue::LValue) + && !qual_ty.qualifiers.is_const + && ctx.expanding_macro.is_some() + { + return Err("mutable lvalues are not supported inside macros".into()); + } + if ctx.is_unused() { return self.convert_expr(ctx, expr, None); } diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.2021.clang15.snap index f9a7cb7388..5578f4336b 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.2021.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.2021.clang15.snap @@ -40,11 +40,6 @@ static mut c2rust_lvalue_4: [::core::ffi::c_char; 6] = #[no_mangle] pub static mut static_char_array_ptr: *mut [::core::ffi::c_char; 6] = unsafe { &raw const c2rust_lvalue_4 as *mut [::core::ffi::c_char; 6] }; -pub const SINGLE_INT: ::core::ffi::c_int = 42 as ::core::ffi::c_int; -pub const INT_ARRAY: [::core::ffi::c_int; 2] = - [42 as ::core::ffi::c_int, 9001 as ::core::ffi::c_int]; -pub const CHAR_ARRAY: [::core::ffi::c_char; 6] = - unsafe { ::core::mem::transmute::<[u8; 6], [::core::ffi::c_char; 6]>(*b"hello\0") }; #[no_mangle] pub unsafe extern "C" fn local_compound_literals() { let mut single_int: ::core::ffi::c_int = 42 as ::core::ffi::c_int; @@ -64,15 +59,23 @@ pub unsafe extern "C" fn local_compound_literals() { let mut c2rust_lvalue_9: [::core::ffi::c_char; 6] = ::core::mem::transmute::<[u8; 6], [::core::ffi::c_char; 6]>(*b"hello\0"); let mut char_array_ptr: *mut [::core::ffi::c_char; 6] = &raw mut c2rust_lvalue_9; - let mut macro_single_int: ::core::ffi::c_int = SINGLE_INT; - let mut macro_single_int_ptr: *mut ::core::ffi::c_int = - &mut SINGLE_INT as *mut ::core::ffi::c_int; - let mut macro_int_ptr_to_array: *mut ::core::ffi::c_int = INT_ARRAY.as_mut_ptr(); - let mut macro_char_ptr_to_array: *mut ::core::ffi::c_char = CHAR_ARRAY.as_mut_ptr(); - let mut macro_int_array_ptr: *mut [::core::ffi::c_int; 2] = - &mut INT_ARRAY as *mut [::core::ffi::c_int; 2]; - let mut macro_char_array_ptr: *mut [::core::ffi::c_char; 6] = - &mut CHAR_ARRAY as *mut [::core::ffi::c_char; 6]; + let mut macro_single_int: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut c2rust_lvalue_10: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut macro_single_int_ptr: *mut ::core::ffi::c_int = &raw mut c2rust_lvalue_10; + let mut c2rust_lvalue_11: [::core::ffi::c_int; 2] = + [42 as ::core::ffi::c_int, 9001 as ::core::ffi::c_int]; + let mut macro_int_ptr_to_array: *mut ::core::ffi::c_int = + &raw mut c2rust_lvalue_11 as *mut ::core::ffi::c_int; + let mut c2rust_lvalue_12: [::core::ffi::c_char; 6] = + ::core::mem::transmute::<[u8; 6], [::core::ffi::c_char; 6]>(*b"hello\0"); + let mut macro_char_ptr_to_array: *mut ::core::ffi::c_char = + &raw mut c2rust_lvalue_12 as *mut ::core::ffi::c_char; + let mut c2rust_lvalue_13: [::core::ffi::c_int; 2] = + [42 as ::core::ffi::c_int, 9001 as ::core::ffi::c_int]; + let mut macro_int_array_ptr: *mut [::core::ffi::c_int; 2] = &raw mut c2rust_lvalue_13; + let mut c2rust_lvalue_14: [::core::ffi::c_char; 6] = + ::core::mem::transmute::<[u8; 6], [::core::ffi::c_char; 6]>(*b"hello\0"); + let mut macro_char_array_ptr: *mut [::core::ffi::c_char; 6] = &raw mut c2rust_lvalue_14; } #[no_mangle] pub unsafe extern "C" fn deref_addrof() { diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.2024.clang15.snap index c005935de1..22ce7a4807 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.2024.clang15.snap @@ -40,11 +40,6 @@ static mut c2rust_lvalue_4: [::core::ffi::c_char; 6] = #[unsafe(no_mangle)] pub static mut static_char_array_ptr: *mut [::core::ffi::c_char; 6] = &raw const c2rust_lvalue_4 as *mut [::core::ffi::c_char; 6]; -pub const SINGLE_INT: ::core::ffi::c_int = 42 as ::core::ffi::c_int; -pub const INT_ARRAY: [::core::ffi::c_int; 2] = - [42 as ::core::ffi::c_int, 9001 as ::core::ffi::c_int]; -pub const CHAR_ARRAY: [::core::ffi::c_char; 6] = - unsafe { ::core::mem::transmute::<[u8; 6], [::core::ffi::c_char; 6]>(*b"hello\0") }; #[unsafe(no_mangle)] pub unsafe extern "C" fn local_compound_literals() { let mut single_int: ::core::ffi::c_int = 42 as ::core::ffi::c_int; @@ -64,15 +59,23 @@ pub unsafe extern "C" fn local_compound_literals() { let mut c2rust_lvalue_9: [::core::ffi::c_char; 6] = ::core::mem::transmute::<[u8; 6], [::core::ffi::c_char; 6]>(*b"hello\0"); let mut char_array_ptr: *mut [::core::ffi::c_char; 6] = &raw mut c2rust_lvalue_9; - let mut macro_single_int: ::core::ffi::c_int = SINGLE_INT; - let mut macro_single_int_ptr: *mut ::core::ffi::c_int = - &mut SINGLE_INT as *mut ::core::ffi::c_int; - let mut macro_int_ptr_to_array: *mut ::core::ffi::c_int = INT_ARRAY.as_mut_ptr(); - let mut macro_char_ptr_to_array: *mut ::core::ffi::c_char = CHAR_ARRAY.as_mut_ptr(); - let mut macro_int_array_ptr: *mut [::core::ffi::c_int; 2] = - &mut INT_ARRAY as *mut [::core::ffi::c_int; 2]; - let mut macro_char_array_ptr: *mut [::core::ffi::c_char; 6] = - &mut CHAR_ARRAY as *mut [::core::ffi::c_char; 6]; + let mut macro_single_int: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut c2rust_lvalue_10: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut macro_single_int_ptr: *mut ::core::ffi::c_int = &raw mut c2rust_lvalue_10; + let mut c2rust_lvalue_11: [::core::ffi::c_int; 2] = + [42 as ::core::ffi::c_int, 9001 as ::core::ffi::c_int]; + let mut macro_int_ptr_to_array: *mut ::core::ffi::c_int = + &raw mut c2rust_lvalue_11 as *mut ::core::ffi::c_int; + let mut c2rust_lvalue_12: [::core::ffi::c_char; 6] = + ::core::mem::transmute::<[u8; 6], [::core::ffi::c_char; 6]>(*b"hello\0"); + let mut macro_char_ptr_to_array: *mut ::core::ffi::c_char = + &raw mut c2rust_lvalue_12 as *mut ::core::ffi::c_char; + let mut c2rust_lvalue_13: [::core::ffi::c_int; 2] = + [42 as ::core::ffi::c_int, 9001 as ::core::ffi::c_int]; + let mut macro_int_array_ptr: *mut [::core::ffi::c_int; 2] = &raw mut c2rust_lvalue_13; + let mut c2rust_lvalue_14: [::core::ffi::c_char; 6] = + ::core::mem::transmute::<[u8; 6], [::core::ffi::c_char; 6]>(*b"hello\0"); + let mut macro_char_array_ptr: *mut [::core::ffi::c_char; 6] = &raw mut c2rust_lvalue_14; } #[unsafe(no_mangle)] pub unsafe extern "C" fn deref_addrof() { 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 cb48c14b39..f87033df87 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 @@ -49,7 +49,6 @@ pub const LITERAL_ARRAY: [::core::ffi::c_int; 3] = [ 3 as ::core::ffi::c_int, ]; pub const LITERAL_STRUCT: S = S { i: 5 }; -pub const LITERAL_STRUCT_MUT: S = S { i: 5 }; pub const NESTED_INT: ::core::ffi::c_int = LITERAL_INT; pub const NESTED_BOOL: ::core::ffi::c_int = LITERAL_BOOL; pub const NESTED_FLOAT: ::core::ffi::c_double = LITERAL_FLOAT; @@ -82,14 +81,12 @@ pub const REF_MACRO: *const ::core::ffi::c_char = unsafe { .offset(LITERAL_FLOAT as ::core::ffi::c_int as isize) }; pub const REF_LITERAL: *const S = &LITERAL_STRUCT as *const S; -pub const REF_LITERAL_MUT: *mut S = &LITERAL_STRUCT_MUT as *const S as *mut S; pub const TERNARY: ::core::ffi::c_int = if LITERAL_BOOL != 0 { 1 as ::core::ffi::c_int } else { 2 as ::core::ffi::c_int }; pub const MEMBER: ::core::ffi::c_int = LITERAL_STRUCT.i; -pub const MEMBER_MUT: ::core::ffi::c_int = LITERAL_STRUCT_MUT.i; static mut global_static_const_literal_int: ::core::ffi::c_int = LITERAL_INT; static mut global_static_const_literal_bool: bool = LITERAL_BOOL != 0; static mut global_static_const_literal_float: ::core::ffi::c_float = @@ -100,7 +97,7 @@ static mut global_static_const_literal_str_ptr: *const ::core::ffi::c_char = LIT static mut global_static_const_literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; static mut global_static_const_literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; static mut global_static_const_literal_struct: S = LITERAL_STRUCT; -static mut global_static_const_literal_struct_mut: S = LITERAL_STRUCT_MUT; +static mut global_static_const_literal_struct_mut: S = S { i: 5 }; static mut global_static_const_nested_int: ::core::ffi::c_int = NESTED_INT; static mut global_static_const_nested_bool: bool = NESTED_BOOL != 0; static mut global_static_const_nested_float: ::core::ffi::c_float = @@ -130,7 +127,9 @@ static mut global_static_const_builtin: ::core::ffi::c_int = static mut global_static_const_ref_indexing: *const ::core::ffi::c_char = ::core::ptr::null::<::core::ffi::c_char>(); static mut global_static_const_ref_struct: *const S = REF_LITERAL; -static mut global_static_const_ref_struct_mut: *mut S = REF_LITERAL_MUT; +static mut c2rust_lvalue: S = S { i: 5 }; +static mut global_static_const_ref_struct_mut: *mut S = + unsafe { &raw const c2rust_lvalue as *mut S }; static mut global_static_const_ternary: ::core::ffi::c_int = 0; static mut global_static_const_member: ::core::ffi::c_int = 0; static mut global_static_const_member_mut: ::core::ffi::c_int = 0; @@ -154,7 +153,7 @@ pub static mut global_const_literal_array: [::core::ffi::c_int; 3] = LITERAL_ARR #[no_mangle] pub static mut global_const_literal_struct: S = LITERAL_STRUCT; #[no_mangle] -pub static mut global_const_literal_struct_mut: S = LITERAL_STRUCT_MUT; +pub static mut global_const_literal_struct_mut: S = S { i: 5 }; #[no_mangle] pub static mut global_const_nested_int: ::core::ffi::c_int = NESTED_INT; #[no_mangle] @@ -205,8 +204,10 @@ pub static mut global_const_ref_indexing: *const ::core::ffi::c_char = ::core::ptr::null::<::core::ffi::c_char>(); #[no_mangle] pub static mut global_const_ref_struct: *const S = REF_LITERAL; +static mut c2rust_lvalue_0: S = S { i: 5 }; #[no_mangle] -pub static mut global_const_ref_struct_mut: *mut S = REF_LITERAL_MUT; +pub static mut global_const_ref_struct_mut: *mut S = + unsafe { &raw const c2rust_lvalue_0 as *mut S }; #[no_mangle] pub static mut global_const_ternary: ::core::ffi::c_int = 0; #[no_mangle] @@ -223,7 +224,9 @@ pub unsafe extern "C" fn local_muts() { let mut literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; let mut literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; let mut literal_struct: S = LITERAL_STRUCT; - let mut literal_struct_mut: S = LITERAL_STRUCT_MUT; + let mut literal_struct_mut: S = S { + i: 5 as ::core::ffi::c_int, + }; let mut nested_int: ::core::ffi::c_int = NESTED_INT; let mut nested_bool: bool = NESTED_BOOL != 0; let mut nested_float: ::core::ffi::c_float = NESTED_FLOAT as ::core::ffi::c_float; @@ -247,10 +250,16 @@ pub unsafe extern "C" fn local_muts() { (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; let mut ref_indexing: *const ::core::ffi::c_char = REF_MACRO; let mut ref_struct: *const S = REF_LITERAL; - let mut ref_struct_mut: *mut S = REF_LITERAL_MUT; + let mut c2rust_lvalue_1: S = S { + i: 5 as ::core::ffi::c_int, + }; + let mut ref_struct_mut: *mut S = &raw mut c2rust_lvalue_1; let mut ternary: ::core::ffi::c_int = TERNARY; let mut member: ::core::ffi::c_int = MEMBER; - let mut member_mut: ::core::ffi::c_int = MEMBER_MUT; + let mut member_mut: ::core::ffi::c_int = S { + i: 5 as ::core::ffi::c_int, + } + .i; let mut stmt_expr: ::core::ffi::c_float = ({ let mut builtin_0: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; @@ -274,7 +283,9 @@ pub unsafe extern "C" fn local_consts() { let literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; let literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; let literal_struct: S = LITERAL_STRUCT; - let literal_struct_mut: S = LITERAL_STRUCT_MUT; + let literal_struct_mut: S = S { + i: 5 as ::core::ffi::c_int, + }; let nested_int: ::core::ffi::c_int = NESTED_INT; let nested_bool: bool = NESTED_BOOL != 0; let nested_float: ::core::ffi::c_float = NESTED_FLOAT as ::core::ffi::c_float; @@ -297,10 +308,16 @@ pub unsafe extern "C" fn local_consts() { let builtin: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; let ref_indexing: *const ::core::ffi::c_char = REF_MACRO; let ref_struct: *const S = REF_LITERAL; - let ref_struct_mut: *mut S = REF_LITERAL_MUT; + let mut c2rust_lvalue_1: S = S { + i: 5 as ::core::ffi::c_int, + }; + let ref_struct_mut: *mut S = &raw mut c2rust_lvalue_1; let ternary: ::core::ffi::c_int = TERNARY; let member: ::core::ffi::c_int = MEMBER; - let member_mut: ::core::ffi::c_int = MEMBER_MUT; + let member_mut: ::core::ffi::c_int = S { + i: 5 as ::core::ffi::c_int, + } + .i; let stmt_expr: ::core::ffi::c_float = ({ let mut builtin_0: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; @@ -419,13 +436,13 @@ unsafe extern "C" fn c2rust_run_static_initializers() { global_static_const_ref_indexing = REF_MACRO; global_static_const_ternary = TERNARY; global_static_const_member = MEMBER; - global_static_const_member_mut = MEMBER_MUT; + global_static_const_member_mut = S { i: 5 }.i; global_const_ptr_arithmetic = PTR_ARITHMETIC; global_const_indexing = INDEXING; global_const_ref_indexing = REF_MACRO; global_const_ternary = TERNARY; global_const_member = MEMBER; - global_const_member_mut = MEMBER_MUT; + global_const_member_mut = S { i: 5 }.i; } #[used] #[cfg_attr(target_os = "linux", link_section = ".init_array")] 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 5ceac46f1b..1a7531bec4 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 @@ -49,7 +49,6 @@ pub const LITERAL_ARRAY: [::core::ffi::c_int; 3] = [ 3 as ::core::ffi::c_int, ]; pub const LITERAL_STRUCT: S = S { i: 5 }; -pub const LITERAL_STRUCT_MUT: S = S { i: 5 }; pub const NESTED_INT: ::core::ffi::c_int = LITERAL_INT; pub const NESTED_BOOL: ::core::ffi::c_int = LITERAL_BOOL; pub const NESTED_FLOAT: ::core::ffi::c_double = LITERAL_FLOAT; @@ -82,14 +81,12 @@ pub const REF_MACRO: *const ::core::ffi::c_char = unsafe { .offset(LITERAL_FLOAT as ::core::ffi::c_int as isize) }; pub const REF_LITERAL: *const S = &LITERAL_STRUCT as *const S; -pub const REF_LITERAL_MUT: *mut S = &LITERAL_STRUCT_MUT as *const S as *mut S; pub const TERNARY: ::core::ffi::c_int = if LITERAL_BOOL != 0 { 1 as ::core::ffi::c_int } else { 2 as ::core::ffi::c_int }; pub const MEMBER: ::core::ffi::c_int = LITERAL_STRUCT.i; -pub const MEMBER_MUT: ::core::ffi::c_int = LITERAL_STRUCT_MUT.i; static mut global_static_const_literal_int: ::core::ffi::c_int = LITERAL_INT; static mut global_static_const_literal_bool: bool = LITERAL_BOOL != 0; static mut global_static_const_literal_float: ::core::ffi::c_float = @@ -100,7 +97,7 @@ static mut global_static_const_literal_str_ptr: *const ::core::ffi::c_char = LIT static mut global_static_const_literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; static mut global_static_const_literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; static mut global_static_const_literal_struct: S = LITERAL_STRUCT; -static mut global_static_const_literal_struct_mut: S = LITERAL_STRUCT_MUT; +static mut global_static_const_literal_struct_mut: S = S { i: 5 }; static mut global_static_const_nested_int: ::core::ffi::c_int = NESTED_INT; static mut global_static_const_nested_bool: bool = NESTED_BOOL != 0; static mut global_static_const_nested_float: ::core::ffi::c_float = @@ -130,7 +127,8 @@ static mut global_static_const_builtin: ::core::ffi::c_int = static mut global_static_const_ref_indexing: *const ::core::ffi::c_char = ::core::ptr::null::<::core::ffi::c_char>(); static mut global_static_const_ref_struct: *const S = REF_LITERAL; -static mut global_static_const_ref_struct_mut: *mut S = REF_LITERAL_MUT; +static mut c2rust_lvalue: S = S { i: 5 }; +static mut global_static_const_ref_struct_mut: *mut S = &raw const c2rust_lvalue as *mut S; static mut global_static_const_ternary: ::core::ffi::c_int = 0; static mut global_static_const_member: ::core::ffi::c_int = 0; static mut global_static_const_member_mut: ::core::ffi::c_int = 0; @@ -154,7 +152,7 @@ pub static mut global_const_literal_array: [::core::ffi::c_int; 3] = LITERAL_ARR #[unsafe(no_mangle)] pub static mut global_const_literal_struct: S = LITERAL_STRUCT; #[unsafe(no_mangle)] -pub static mut global_const_literal_struct_mut: S = LITERAL_STRUCT_MUT; +pub static mut global_const_literal_struct_mut: S = S { i: 5 }; #[unsafe(no_mangle)] pub static mut global_const_nested_int: ::core::ffi::c_int = NESTED_INT; #[unsafe(no_mangle)] @@ -205,8 +203,9 @@ pub static mut global_const_ref_indexing: *const ::core::ffi::c_char = ::core::ptr::null::<::core::ffi::c_char>(); #[unsafe(no_mangle)] pub static mut global_const_ref_struct: *const S = REF_LITERAL; +static mut c2rust_lvalue_0: S = S { i: 5 }; #[unsafe(no_mangle)] -pub static mut global_const_ref_struct_mut: *mut S = REF_LITERAL_MUT; +pub static mut global_const_ref_struct_mut: *mut S = &raw const c2rust_lvalue_0 as *mut S; #[unsafe(no_mangle)] pub static mut global_const_ternary: ::core::ffi::c_int = 0; #[unsafe(no_mangle)] @@ -223,7 +222,9 @@ pub unsafe extern "C" fn local_muts() { let mut literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; let mut literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; let mut literal_struct: S = LITERAL_STRUCT; - let mut literal_struct_mut: S = LITERAL_STRUCT_MUT; + let mut literal_struct_mut: S = S { + i: 5 as ::core::ffi::c_int, + }; let mut nested_int: ::core::ffi::c_int = NESTED_INT; let mut nested_bool: bool = NESTED_BOOL != 0; let mut nested_float: ::core::ffi::c_float = NESTED_FLOAT as ::core::ffi::c_float; @@ -247,10 +248,16 @@ pub unsafe extern "C" fn local_muts() { (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; let mut ref_indexing: *const ::core::ffi::c_char = REF_MACRO; let mut ref_struct: *const S = REF_LITERAL; - let mut ref_struct_mut: *mut S = REF_LITERAL_MUT; + let mut c2rust_lvalue_1: S = S { + i: 5 as ::core::ffi::c_int, + }; + let mut ref_struct_mut: *mut S = &raw mut c2rust_lvalue_1; let mut ternary: ::core::ffi::c_int = TERNARY; let mut member: ::core::ffi::c_int = MEMBER; - let mut member_mut: ::core::ffi::c_int = MEMBER_MUT; + let mut member_mut: ::core::ffi::c_int = S { + i: 5 as ::core::ffi::c_int, + } + .i; let mut stmt_expr: ::core::ffi::c_float = ({ let mut builtin_0: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; @@ -274,7 +281,9 @@ pub unsafe extern "C" fn local_consts() { let literal_str: [::core::ffi::c_char; 6] = LITERAL_STR; let literal_array: [::core::ffi::c_int; 3] = LITERAL_ARRAY; let literal_struct: S = LITERAL_STRUCT; - let literal_struct_mut: S = LITERAL_STRUCT_MUT; + let literal_struct_mut: S = S { + i: 5 as ::core::ffi::c_int, + }; let nested_int: ::core::ffi::c_int = NESTED_INT; let nested_bool: bool = NESTED_BOOL != 0; let nested_float: ::core::ffi::c_float = NESTED_FLOAT as ::core::ffi::c_float; @@ -297,10 +306,16 @@ pub unsafe extern "C" fn local_consts() { let builtin: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; let ref_indexing: *const ::core::ffi::c_char = REF_MACRO; let ref_struct: *const S = REF_LITERAL; - let ref_struct_mut: *mut S = REF_LITERAL_MUT; + let mut c2rust_lvalue_1: S = S { + i: 5 as ::core::ffi::c_int, + }; + let ref_struct_mut: *mut S = &raw mut c2rust_lvalue_1; let ternary: ::core::ffi::c_int = TERNARY; let member: ::core::ffi::c_int = MEMBER; - let member_mut: ::core::ffi::c_int = MEMBER_MUT; + let member_mut: ::core::ffi::c_int = S { + i: 5 as ::core::ffi::c_int, + } + .i; let stmt_expr: ::core::ffi::c_float = ({ let mut builtin_0: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; @@ -419,13 +434,13 @@ unsafe extern "C" fn c2rust_run_static_initializers() { global_static_const_ref_indexing = REF_MACRO; global_static_const_ternary = TERNARY; global_static_const_member = MEMBER; - global_static_const_member_mut = MEMBER_MUT; + global_static_const_member_mut = S { i: 5 }.i; global_const_ptr_arithmetic = PTR_ARITHMETIC; global_const_indexing = INDEXING; global_const_ref_indexing = REF_MACRO; global_const_ternary = TERNARY; global_const_member = MEMBER; - global_const_member_mut = MEMBER_MUT; + global_const_member_mut = S { i: 5 }.i; } #[used] #[cfg_attr(target_os = "linux", unsafe(link_section = ".init_array"))] From e78645dd2f5e9ff2b829db6b7a33a5c25f640bd1 Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 3 Sep 2026 19:14:27 +0200 Subject: [PATCH 05/12] transpile: Assert that address-of string literal or macro is immutable --- c2rust-transpile/src/translator/pointers.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index 0723aa0959..2d5c9a5fbe 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -139,14 +139,14 @@ impl<'c> Translation<'c> { // Values that translate into const temporaries can't be raw-borrowed in Rust. // They must be regular-borrowed first, which will extend the lifetime to static. else if arg_is_macro || matches!(arg_expr_kind, Some(CExprKind::Literal(..))) { + if !pointee_cty.qualifiers.is_const { + return Err("taking mutable address of string literal or macro".into()); + } + let arg_cty_kind = &self.ast_context.resolve_type(arg_cty.ctype).kind; if is_array_decay { - let method = match mutbl { - Mutability::Mutable => "as_mut_ptr", - Mutability::Immutable => "as_ptr", - }; - val = val.map(|val| mk().method_call_expr(val, method, vec![])); + val = val.map(|val| mk().method_call_expr(val, "as_ptr", vec![])); // If the target pointee type is different from the source element type, // then we need to cast the ptr type as well. @@ -156,7 +156,7 @@ impl<'c> Translation<'c> { needs_cast = true; } } else { - val = val.map(|val| mk().set_mutbl(mutbl).borrow_expr(val)); + val = val.map(|val| mk().borrow_expr(val)); // Add an intermediate reference-to-pointer cast if the context needs // reference-to-pointer decay, or if another cast follows. From f0741ea77ce2e661e686d4bb5789290b07e01e6a Mon Sep 17 00:00:00 2001 From: Rua Date: Mon, 24 Aug 2026 14:59:40 +0200 Subject: [PATCH 06/12] transpile: Use `ptr::from_ref` to cast references to pointers --- c2rust-transpile/src/translator/mod.rs | 2 +- c2rust-transpile/src/translator/pointers.rs | 29 +++++++------------ ...hots__transpile@arrays.c.2021.clang15.snap | 6 ++-- ...hots__transpile@arrays.c.2024.clang15.snap | 4 +-- ...hots__transpile@macros.c.2021.clang15.snap | 4 +-- ...hots__transpile@macros.c.2024.clang15.snap | 2 +- 6 files changed, 19 insertions(+), 28 deletions(-) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index f1aab97179..0061c80f25 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -1735,7 +1735,7 @@ impl<'c> Translation<'c> { pub fn use_feature(&self, feature: &'static str) { if matches!( feature, - "asm" | "inline_const" | "label_break_value" | "raw_ref_op" + "asm" | "inline_const" | "label_break_value" | "ptr_from_ref" | "raw_ref_op" ) && self.tcfg.edition >= Edition2024 { return; diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index 2d5c9a5fbe..682c119326 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -109,7 +109,6 @@ impl<'c> Translation<'c> { let arg_is_macro = arg.map_or(false, |arg| self.expr_is_expanded_macro(ctx, arg, None)); let mut needs_cast = false; - let mut ref_cast_pointee_ty = None; let mutbl = if ctx.is_const && !pointee_cty.qualifiers.is_const { // const contexts aren't able to use &mut, so we work around that // by using & and an extra cast through & to *const to *mut @@ -122,17 +121,17 @@ impl<'c> Translation<'c> { // Narrow string literals are translated directly as `[u8; N]` literals when their address // is taken, without the transmute. String/byte literals are already references in Rust. - if let ( - Some(&CExprKind::Literal(literal_cty, CLiteral::String(_, element_size @ 1))), - false, - ) = (arg_expr_kind, arg_is_macro) + if matches!( + arg_expr_kind, + Some(&CExprKind::Literal(_, CLiteral::String(_, 1))) + ) && !arg_is_macro { if is_array_decay { val = val.map(|val| mk().method_call_expr(val, "as_ptr", vec![])); } else { - let size = self.ast_context.array_len(literal_cty.ctype) * element_size as usize; - ref_cast_pointee_ty = - Some(mk().array_ty(mk().ident_ty("u8"), mk().lit_expr(size as u128))); + self.use_feature("ptr_from_ref"); + let func = mk().abs_path_expr(vec!["core", "ptr", "from_ref"]); + val = val.map(|val| mk().call_expr(func, vec![val])) } needs_cast = true; } @@ -158,11 +157,9 @@ impl<'c> Translation<'c> { } else { val = val.map(|val| mk().borrow_expr(val)); - // Add an intermediate reference-to-pointer cast if the context needs - // reference-to-pointer decay, or if another cast follows. - if ctx.decay_ref.is_yes() || needs_cast { - ref_cast_pointee_ty = Some(self.convert_pointee_type(arg_cty.ctype)?); - } + self.use_feature("ptr_from_ref"); + let func = mk().abs_path_expr(vec!["core", "ptr", "from_ref"]); + val = val.map(|val| mk().call_expr(func, vec![val])); } } else { self.use_feature("raw_ref_op"); @@ -175,12 +172,6 @@ impl<'c> Translation<'c> { } } - // Perform an intermediate reference-to-pointer cast if needed. - // TODO: Rust 1.76: Use `ptr::from_ref`. - if let Some(pointee_ty) = ref_cast_pointee_ty { - val = val.map(|val| mk().cast_expr(val, mk().set_mutbl(mutbl).ptr_ty(pointee_ty))); - } - // Perform a final cast to the target type if needed. if needs_cast { let pointer_ty = self.convert_type(pointer_cty.ctype)?; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.2021.clang15.snap index d9b2bce721..a827a5af78 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.2021.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.2021.clang15.snap @@ -11,7 +11,7 @@ expression: cat tests/snapshots/arrays.2021.clang15.rs unused_assignments, unused_mut )] -#![feature(raw_ref_op)] +#![feature(ptr_from_ref, raw_ref_op)] extern "C" { static mut my_array: [::core::ffi::c_int; 1]; static mut my_indexes: [::core::ffi::c_int; 1]; @@ -100,10 +100,10 @@ pub unsafe extern "C" fn entry() { let mut const_char_lit_ptr: *const ::core::ffi::c_char = b"abc\0".as_ptr() as *const ::core::ffi::c_char; let mut const_char_lit_array_ptr: *const [::core::ffi::c_char; 4] = - b"abc\0" as *const [u8; 4] as *const [::core::ffi::c_char; 4]; + ::core::ptr::from_ref(b"abc\0") as *const [::core::ffi::c_char; 4]; let mut char_lit_ptr: *mut ::core::ffi::c_char = b"abc\0".as_ptr() as *const ::core::ffi::c_char as *mut ::core::ffi::c_char; - let mut char_lit_array_ptr: *mut [::core::ffi::c_char; 4] = b"abc\0" as *const [u8; 4] + let mut char_lit_array_ptr: *mut [::core::ffi::c_char; 4] = ::core::ptr::from_ref(b"abc\0") as *const [::core::ffi::c_char; 4] as *mut [::core::ffi::c_char; 4]; let mut past_end: *mut ::core::ffi::c_char = (&raw mut static_char_array diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.2024.clang15.snap index 640c1c76dc..18ea69f788 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.2024.clang15.snap @@ -100,10 +100,10 @@ pub unsafe extern "C" fn entry() { let mut const_char_lit_ptr: *const ::core::ffi::c_char = b"abc\0".as_ptr() as *const ::core::ffi::c_char; let mut const_char_lit_array_ptr: *const [::core::ffi::c_char; 4] = - b"abc\0" as *const [u8; 4] as *const [::core::ffi::c_char; 4]; + ::core::ptr::from_ref(b"abc\0") as *const [::core::ffi::c_char; 4]; let mut char_lit_ptr: *mut ::core::ffi::c_char = b"abc\0".as_ptr() as *const ::core::ffi::c_char as *mut ::core::ffi::c_char; - let mut char_lit_array_ptr: *mut [::core::ffi::c_char; 4] = b"abc\0" as *const [u8; 4] + let mut char_lit_array_ptr: *mut [::core::ffi::c_char; 4] = ::core::ptr::from_ref(b"abc\0") as *const [::core::ffi::c_char; 4] as *mut [::core::ffi::c_char; 4]; let mut past_end: *mut ::core::ffi::c_char = (&raw mut static_char_array 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 f87033df87..c7acd36a5e 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 @@ -11,7 +11,7 @@ expression: cat tests/snapshots/macros.2021.clang15.rs unused_assignments, unused_mut )] -#![feature(raw_ref_op)] +#![feature(ptr_from_ref, raw_ref_op)] extern "C" { fn extern_fn() -> ::core::ffi::c_int; } @@ -80,7 +80,7 @@ pub const REF_MACRO: *const ::core::ffi::c_char = unsafe { .as_ptr() .offset(LITERAL_FLOAT as ::core::ffi::c_int as isize) }; -pub const REF_LITERAL: *const S = &LITERAL_STRUCT as *const S; +pub const REF_LITERAL: *const S = ::core::ptr::from_ref(&LITERAL_STRUCT); pub const TERNARY: ::core::ffi::c_int = if LITERAL_BOOL != 0 { 1 as ::core::ffi::c_int } else { 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 1a7531bec4..c2718d6e70 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 @@ -80,7 +80,7 @@ pub const REF_MACRO: *const ::core::ffi::c_char = unsafe { .as_ptr() .offset(LITERAL_FLOAT as ::core::ffi::c_int as isize) }; -pub const REF_LITERAL: *const S = &LITERAL_STRUCT as *const S; +pub const REF_LITERAL: *const S = ::core::ptr::from_ref(&LITERAL_STRUCT); pub const TERNARY: ::core::ffi::c_int = if LITERAL_BOOL != 0 { 1 as ::core::ffi::c_int } else { From 2fb258d6c15cf8da91adeaa64eb29aae2deb31c9 Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 25 Aug 2026 17:05:44 +0200 Subject: [PATCH 07/12] transpile: Remove `decay_ref`, which is now written but never read --- c2rust-transpile/src/translator/functions.rs | 5 +- c2rust-transpile/src/translator/mod.rs | 62 +------------------- c2rust-transpile/src/translator/operators.rs | 27 +-------- c2rust-transpile/src/translator/pointers.rs | 9 +-- docs/source_walkthrough.md | 5 -- 5 files changed, 9 insertions(+), 99 deletions(-) diff --git a/c2rust-transpile/src/translator/functions.rs b/c2rust-transpile/src/translator/functions.rs index e423e49610..c507a2181e 100644 --- a/c2rust-transpile/src/translator/functions.rs +++ b/c2rust-transpile/src/translator/functions.rs @@ -356,7 +356,7 @@ impl<'c> Translation<'c> { pub fn convert_function_call( &self, - mut ctx: ExprContext, + ctx: ExprContext, func: CExprId, args: &[CExprId], call_expr_ty: CQualTypeId, @@ -447,9 +447,6 @@ impl<'c> Translation<'c> { }; let call = func.and_then_try(|func| { - // We want to decay refs only when function is variadic - ctx.decay_ref = DecayRef::from(is_variadic); - let args = self.convert_call_args(ctx.used(), args, arg_tys.as_deref(), is_variadic)?; let call_expr = args.map(|args| mk().call_expr(func, args)); diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 0061c80f25..67245cfaf0 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -74,45 +74,6 @@ struct Import { ident_name: String, } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum DecayRef { - Yes, - Default, - No, -} - -impl DecayRef { - // Here we give intrinsic meaning to default to equate to yes/true - // when actually evaluated - pub fn is_yes(&self) -> bool { - match self { - DecayRef::Yes => true, - DecayRef::Default => true, - DecayRef::No => false, - } - } - - #[inline] - pub fn is_no(&self) -> bool { - !self.is_yes() - } - - pub fn set_default_to_no(&mut self) { - if *self == DecayRef::Default { - *self = DecayRef::No; - } - } -} - -impl From for DecayRef { - fn from(b: bool) -> Self { - match b { - true => DecayRef::Yes, - false => DecayRef::No, - } - } -} - #[derive(Debug, Copy, Clone)] pub enum ReplaceMode { None, @@ -138,7 +99,6 @@ pub struct ExprContext { #[allow(dead_code)] is_static: bool, - decay_ref: DecayRef, is_bitfield_write: bool, /// We will be referring to the expression by address. In this context we @@ -166,12 +126,7 @@ impl ExprContext { pub fn is_unused(&self) -> bool { !self.used } - pub fn decay_ref(self) -> Self { - ExprContext { - decay_ref: DecayRef::Yes, - ..self - } - } + pub fn const_(self) -> Self { ExprContext { is_const: true, @@ -880,7 +835,6 @@ pub fn translate( is_const: false, is_pattern: false, is_static: false, - decay_ref: DecayRef::Default, is_bitfield_write: false, needs_address: false, expanding_macro: None, @@ -2479,7 +2433,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.used(), ptr, None)?; let ptr_type = self .ast_context .index_unwrap_parens(ptr) @@ -2530,11 +2484,7 @@ impl<'c> Translation<'c> { } _ => { - // DecayRef could (and probably should) be Default instead of Yes here; however, as noted - // 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.used(), cond_id, None)?; val.try_map(|e| self.match_bool(ctx, target, ty_id, e)) } } @@ -2976,7 +2926,6 @@ impl<'c> Translation<'c> { }; } - // ref decayed ptrs generally need a type annotation if let Some(CExprKind::Unary(_, CUnOp::AddressOf, _, _)) = initializer_kind { return true; @@ -4105,11 +4054,6 @@ impl<'c> Translation<'c> { } match kind { - // A reference must be decayed if a bitcast is required. Const casts in - // LLVM 8 are now NoOp casts, so we need to include it as well. - CastKind::BitCast | CastKind::PointerToIntegral | CastKind::NoOp => { - ctx.decay_ref = DecayRef::Yes - } CastKind::ArrayToPointerDecay | CastKind::FunctionToPointerDecay | CastKind::BuiltinFnToFnPtr => { diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index 18651aa000..bf2b9f40e9 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -5,7 +5,7 @@ use super::*; impl<'c> Translation<'c> { pub fn convert_binary_expr( &self, - mut ctx: ExprContext, + ctx: ExprContext, expected_type_id: Option, result_type_id: CQualTypeId, op: CBinOp, @@ -53,14 +53,6 @@ impl<'c> Translation<'c> { ), _ => { - // Comparing references to pointers isn't consistently supported by rust - // and so we need to decay references to pointers to do so. See - // https://github.com/rust-lang/rust/issues/53772. This might be removable - // once the above issue is resolved. - if op == CBinOp::EqualEqual || op == CBinOp::NotEqual { - ctx = ctx.decay_ref(); - } - let lhs_kind = &self.ast_context.index_unwrap_parens(lhs).kind; let mut lhs_type_id = lhs_kind.get_qual_type().ok_or_else(|| { format_translation_err!( @@ -119,26 +111,13 @@ impl<'c> Translation<'c> { .and_then_try(|_| self.convert_expr(ctx, rhs, Some(rhs_type_id)))? .map(|_| self.panic_or_err("Binary expression is not supposed to be used"))) } else { - let rhs_ctx = ctx; - - // When we use methods on pointers (ie wrapping_offset_from or offset) - // we must ensure we have an explicit raw ptr for the self param, as - // self references do not decay - if op.is_pointer_arithmetic() { - let ty_kind = &self.ast_context.resolve_type(lhs_type_id.ctype).kind; - - if let CTypeKind::Pointer(_) = ty_kind { - ctx = ctx.decay_ref(); - } - } - // Using `.is_none()` and `.is_some()` for null comparison means we don't // have to rely on `trait PartialEq` as much and it is also more idiomatic. if matches!(op, CBinOp::EqualEqual | CBinOp::NotEqual) { let is_null = op == CBinOp::EqualEqual; if self.ast_context.is_null_expr(lhs) { - let val = self.convert_expr(rhs_ctx, rhs, Some(rhs_type_id))?; + let val = self.convert_expr(ctx, rhs, Some(rhs_type_id))?; let val = val.try_map(|rhs_rs| { self.convert_pointer_is_null( ctx, @@ -163,7 +142,7 @@ impl<'c> Translation<'c> { } let lhs_val = self.convert_expr(ctx, lhs, Some(lhs_type_id))?; - let rhs_val = self.convert_expr(rhs_ctx, rhs, Some(rhs_type_id))?; + let rhs_val = self.convert_expr(ctx, rhs, Some(rhs_type_id))?; lhs_val.zip(rhs_val).and_then_try(|(lhs_val, rhs_val)| { self.convert_binary_operator( diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index 682c119326..571c86c04a 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -18,7 +18,7 @@ use crate::{ impl<'c> Translation<'c> { pub fn convert_address_of( &self, - mut ctx: ExprContext, + ctx: ExprContext, cqual_type: CQualTypeId, arg: CExprId, ) -> TranslationResult>> { @@ -41,10 +41,6 @@ impl<'c> Translation<'c> { false, // don't deref, keep as pointer ); } - // An AddrOf DeclRef/Member is safe to not decay - // if the translator isn't already giving a hard yes to decaying (ie, BitCasts). - // So we only convert default to no decay. - CExprKind::DeclRef(..) | CExprKind::Member(..) => ctx.decay_ref.set_default_to_no(), _ => (), } @@ -339,8 +335,7 @@ 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)?; + let pointer_rs = self.convert_expr(ctx.used().not_needs_address(), 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(), diff --git a/docs/source_walkthrough.md b/docs/source_walkthrough.md index 9b07d228fa..9eae39d6d4 100644 --- a/docs/source_walkthrough.md +++ b/docs/source_walkthrough.md @@ -218,11 +218,6 @@ This allows emitting different code in such situations, to account for the different lifetimes of compound literals for example. `is_static` usually implies `is_const`, but not always. -The `decay_ref` attribute keeps track of whether or not -we're in a context in which Rust will infer that a reference can decay in to a pointer. -This can happen at method calls, variable initializers, and possibly more locations. -This allows the translation to omit some otherwise superfluous casts. - The `va_decl` attribute indicates which, if any, declaration corresponds to the variable-argument list for the current variadic function. This enables us to drop the associated declaration, From df17903a487d5ef09c6ed6b085c87666e1eeaa1b Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 25 Aug 2026 16:35:16 +0200 Subject: [PATCH 08/12] transpile: Refactor `convert_address_of_common` --- c2rust-transpile/src/translator/pointers.rs | 53 +++++++++++---------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index 571c86c04a..b61a85f895 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -115,43 +115,44 @@ impl<'c> Translation<'c> { pointee_cty.mutability() }; - // Narrow string literals are translated directly as `[u8; N]` literals when their address - // is taken, without the transmute. String/byte literals are already references in Rust. - if matches!( - arg_expr_kind, - Some(&CExprKind::Literal(_, CLiteral::String(_, 1))) - ) && !arg_is_macro - { - if is_array_decay { - val = val.map(|val| mk().method_call_expr(val, "as_ptr", vec![])); - } else { - self.use_feature("ptr_from_ref"); - let func = mk().abs_path_expr(vec!["core", "ptr", "from_ref"]); - val = val.map(|val| mk().call_expr(func, vec![val])) - } - needs_cast = true; - } // Values that translate into const temporaries can't be raw-borrowed in Rust. // They must be regular-borrowed first, which will extend the lifetime to static. - else if arg_is_macro || matches!(arg_expr_kind, Some(CExprKind::Literal(..))) { + if arg_is_macro || matches!(arg_expr_kind, Some(CExprKind::Literal(..))) { if !pointee_cty.qualifiers.is_const { return Err("taking mutable address of string literal or macro".into()); } - let arg_cty_kind = &self.ast_context.resolve_type(arg_cty.ctype).kind; + // Narrow string literals are translated directly as `[u8; N]` literals + // when their address is taken, without the transmute. + // String/byte literals are already references in Rust. + let is_byte_string_literal = matches!( + arg_expr_kind, + Some(&CExprKind::Literal(_, CLiteral::String(_, 1))) + ) && !arg_is_macro; if is_array_decay { - val = val.map(|val| mk().method_call_expr(val, "as_ptr", vec![])); - - // If the target pointee type is different from the source element type, - // then we need to cast the ptr type as well. - if arg_cty_kind.element_ty().map_or(false, |arg_element_cty| { - arg_element_cty != pointee_cty.ctype - }) { + if is_byte_string_literal { needs_cast = true; + } else { + let arg_type_kind = &self.ast_context.resolve_type(arg_cty.ctype).kind; + let arg_element_type_id = arg_type_kind.element_ty().ok_or_else(|| { + TranslationError::generic("Array decay should have array argument") + })?; + + // If the target pointee type is different from the source element type, + // then we need to cast the ptr type as well. + if arg_element_type_id != pointee_cty.ctype { + needs_cast = true; + } } + + val = val.map(|val| mk().method_call_expr(val, "as_ptr", vec![])); } else { - val = val.map(|val| mk().borrow_expr(val)); + if is_byte_string_literal { + needs_cast = true; + } else { + val = val.map(|val| mk().borrow_expr(val)); + } self.use_feature("ptr_from_ref"); let func = mk().abs_path_expr(vec!["core", "ptr", "from_ref"]); From 16f1e361d3a40d18f76a7856f7fdfdceb40c2d4a Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 2 Sep 2026 17:38:40 +0200 Subject: [PATCH 09/12] transpile: Only borrow const and cast in 2021 edition --- c2rust-transpile/src/translator/pointers.rs | 5 ++++- ...napshots__transpile@arrays.c.2024.clang15.snap | 2 +- ...ranspile@compound_literals.c.2024.clang15.snap | 15 ++++++--------- ...hots__transpile@empty_init.c.2024.clang15.snap | 2 +- ...napshots__transpile@macros.c.2024.clang15.snap | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index b61a85f895..5b33423232 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -105,7 +105,10 @@ impl<'c> Translation<'c> { let arg_is_macro = arg.map_or(false, |arg| self.expr_is_expanded_macro(ctx, arg, None)); let mut needs_cast = false; - let mutbl = if ctx.is_const && !pointee_cty.qualifiers.is_const { + let mutbl = if self.tcfg.edition < RustEdition::Edition2024 + && ctx.is_const + && !pointee_cty.qualifiers.is_const + { // const contexts aren't able to use &mut, so we work around that // by using & and an extra cast through & to *const to *mut // TODO: Rust 1.83: Allowed, so this can be removed. diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.2024.clang15.snap index 18ea69f788..311b073f8a 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.2024.clang15.snap @@ -52,7 +52,7 @@ pub static mut static_char_ptr: *mut ::core::ffi::c_char = b"mystring\0".as_ptr() as *const ::core::ffi::c_char as *mut ::core::ffi::c_char; #[unsafe(no_mangle)] pub static mut static_void_ptr: *mut ::core::ffi::c_void = - &raw const static_char_array as *mut ::core::ffi::c_char as *mut ::core::ffi::c_void; + &raw mut static_char_array as *mut ::core::ffi::c_char as *mut ::core::ffi::c_void; #[unsafe(no_mangle)] pub unsafe extern "C" fn entry() { let mut int_2d: [[::core::ffi::c_int; 1]; 1] = [[1 as ::core::ffi::c_int]]; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.2024.clang15.snap index 22ce7a4807..10eb302bbe 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.2024.clang15.snap @@ -16,30 +16,27 @@ expression: cat tests/snapshots/compound_literals.2024.clang15.rs pub static mut static_single_int: ::core::ffi::c_int = 42; static mut c2rust_lvalue: ::core::ffi::c_int = 42; #[unsafe(no_mangle)] -pub static mut static_single_int_ptr: *mut ::core::ffi::c_int = - &raw const c2rust_lvalue as *mut ::core::ffi::c_int; +pub static mut static_single_int_ptr: *mut ::core::ffi::c_int = &raw mut c2rust_lvalue; static mut c2rust_lvalue_0: [::core::ffi::c_int; 2] = [42, 9001]; #[unsafe(no_mangle)] pub static mut static_int_ptr_to_array: *mut ::core::ffi::c_int = - &raw const c2rust_lvalue_0 as *mut ::core::ffi::c_int; + &raw mut c2rust_lvalue_0 as *mut ::core::ffi::c_int; static mut c2rust_lvalue_1: [::core::ffi::c_int; 2] = [42, 9001]; #[unsafe(no_mangle)] pub static mut static_volatile_int_ptr_to_array: *mut ::core::ffi::c_int = - &raw const c2rust_lvalue_1 as *mut ::core::ffi::c_int as *mut ::core::ffi::c_int; + &raw mut c2rust_lvalue_1 as *mut ::core::ffi::c_int as *mut ::core::ffi::c_int; static mut c2rust_lvalue_2: [::core::ffi::c_int; 2] = [42, 9001]; #[unsafe(no_mangle)] -pub static mut static_int_array_ptr: *mut [::core::ffi::c_int; 2] = - &raw const c2rust_lvalue_2 as *mut [::core::ffi::c_int; 2]; +pub static mut static_int_array_ptr: *mut [::core::ffi::c_int; 2] = &raw mut c2rust_lvalue_2; static mut c2rust_lvalue_3: [::core::ffi::c_char; 6] = unsafe { ::core::mem::transmute::<[u8; 6], [::core::ffi::c_char; 6]>(*b"hello\0") }; #[unsafe(no_mangle)] pub static mut static_char_ptr_to_array: *mut ::core::ffi::c_char = - &raw const c2rust_lvalue_3 as *mut ::core::ffi::c_char; + &raw mut c2rust_lvalue_3 as *mut ::core::ffi::c_char; static mut c2rust_lvalue_4: [::core::ffi::c_char; 6] = unsafe { ::core::mem::transmute::<[u8; 6], [::core::ffi::c_char; 6]>(*b"hello\0") }; #[unsafe(no_mangle)] -pub static mut static_char_array_ptr: *mut [::core::ffi::c_char; 6] = - &raw const c2rust_lvalue_4 as *mut [::core::ffi::c_char; 6]; +pub static mut static_char_array_ptr: *mut [::core::ffi::c_char; 6] = &raw mut c2rust_lvalue_4; #[unsafe(no_mangle)] pub unsafe extern "C" fn local_compound_literals() { let mut single_int: ::core::ffi::c_int = 42 as ::core::ffi::c_int; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@empty_init.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@empty_init.c.2024.clang15.snap index 4c884273fa..529682bbed 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@empty_init.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@empty_init.c.2024.clang15.snap @@ -27,7 +27,7 @@ static mut c2rust_lvalue: Scope = Scope { next: ::core::ptr::null_mut::(), }; #[unsafe(no_mangle)] -pub static mut scope: *mut Scope = &raw const c2rust_lvalue as *mut Scope; +pub static mut scope: *mut Scope = &raw mut c2rust_lvalue; #[unsafe(no_mangle)] pub unsafe extern "C" fn foo() { let mut f: Foo_t = Foo_t { x: 0 }; 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 c2718d6e70..23eeeb9bf9 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 @@ -128,7 +128,7 @@ static mut global_static_const_ref_indexing: *const ::core::ffi::c_char = ::core::ptr::null::<::core::ffi::c_char>(); static mut global_static_const_ref_struct: *const S = REF_LITERAL; static mut c2rust_lvalue: S = S { i: 5 }; -static mut global_static_const_ref_struct_mut: *mut S = &raw const c2rust_lvalue as *mut S; +static mut global_static_const_ref_struct_mut: *mut S = &raw mut c2rust_lvalue; static mut global_static_const_ternary: ::core::ffi::c_int = 0; static mut global_static_const_member: ::core::ffi::c_int = 0; static mut global_static_const_member_mut: ::core::ffi::c_int = 0; @@ -205,7 +205,7 @@ pub static mut global_const_ref_indexing: *const ::core::ffi::c_char = pub static mut global_const_ref_struct: *const S = REF_LITERAL; static mut c2rust_lvalue_0: S = S { i: 5 }; #[unsafe(no_mangle)] -pub static mut global_const_ref_struct_mut: *mut S = &raw const c2rust_lvalue_0 as *mut S; +pub static mut global_const_ref_struct_mut: *mut S = &raw mut c2rust_lvalue_0; #[unsafe(no_mangle)] pub static mut global_const_ternary: ::core::ffi::c_int = 0; #[unsafe(no_mangle)] From dd62f81787caa5bd541d9ee651fe1d7d95ef3b79 Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 25 Aug 2026 16:53:14 +0200 Subject: [PATCH 10/12] transpile: In `convert_address_of_common`, cast based on type kinds --- c2rust-ast-builder/src/builder.rs | 2 +- c2rust-transpile/src/c_ast/mod.rs | 12 ++++- c2rust-transpile/src/translator/pointers.rs | 55 ++++++++++++--------- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/c2rust-ast-builder/src/builder.rs b/c2rust-ast-builder/src/builder.rs index 89bc092b2c..98c270ebd9 100644 --- a/c2rust-ast-builder/src/builder.rs +++ b/c2rust-ast-builder/src/builder.rs @@ -17,7 +17,7 @@ pub mod properties { fn to_token(&self) -> Option; } - #[derive(Debug, Copy, Clone)] + #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Mutability { Mutable, Immutable, diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index fb1e73a1e3..d6164c52e1 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -3360,9 +3360,17 @@ impl CTypeKind { } /// Return the element type of a pointer or array - pub fn element_ty(&self) -> Option { + pub(crate) fn array_element_type(&self) -> Option { Some(match *self { - Self::Pointer(ty) => ty.ctype, + Self::ConstantArray(ty, _) => ty, + Self::IncompleteArray(ty) => ty, + Self::VariableArray(ty, _) => ty, + _ => return None, + }) + } + + pub(crate) fn array_element_type_mut(&mut self) -> Option<&mut CTypeId> { + Some(match self { Self::ConstantArray(ty, _) => ty, Self::IncompleteArray(ty) => ty, Self::VariableArray(ty, _) => ty, diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index 5b33423232..6adf7b58cb 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -1,10 +1,12 @@ +use std::borrow::Cow; + use c2rust_ast_builder::{mk, properties::Mutability}; use c2rust_ast_exporter::clang_ast::LRValue; use c2rust_rust_tools::RustEdition; use failure::{err_msg, format_err}; use syn::{BinOp, Expr, Type, UnOp}; -use crate::c_ast::CUnOp; +use crate::c_ast::{CUnOp, TypedAstContext}; use crate::{ diagnostics::{TranslationError, TranslationErrorKind, TranslationResult}, format_translation_err, @@ -104,7 +106,6 @@ impl<'c> Translation<'c> { .ok_or_else(|| TranslationError::generic("Address-of should return a pointer"))?; let arg_is_macro = arg.map_or(false, |arg| self.expr_is_expanded_macro(ctx, arg, None)); - let mut needs_cast = false; let mutbl = if self.tcfg.edition < RustEdition::Edition2024 && ctx.is_const && !pointee_cty.qualifiers.is_const @@ -112,12 +113,14 @@ impl<'c> Translation<'c> { // const contexts aren't able to use &mut, so we work around that // by using & and an extra cast through & to *const to *mut // TODO: Rust 1.83: Allowed, so this can be removed. - needs_cast = true; Mutability::Immutable } else { pointee_cty.mutability() }; + let mut cast_source_type_kind = + Cow::Borrowed(&self.ast_context.resolve_type(arg_cty.ctype).kind); + // Values that translate into const temporaries can't be raw-borrowed in Rust. // They must be regular-borrowed first, which will extend the lifetime to static. if arg_is_macro || matches!(arg_expr_kind, Some(CExprKind::Literal(..))) { @@ -135,24 +138,28 @@ impl<'c> Translation<'c> { if is_array_decay { if is_byte_string_literal { - needs_cast = true; + cast_source_type_kind = Cow::Borrowed(&CTypeKind::UInt8); } else { - let arg_type_kind = &self.ast_context.resolve_type(arg_cty.ctype).kind; - let arg_element_type_id = arg_type_kind.element_ty().ok_or_else(|| { - TranslationError::generic("Array decay should have array argument") - })?; - - // If the target pointee type is different from the source element type, - // then we need to cast the ptr type as well. - if arg_element_type_id != pointee_cty.ctype { - needs_cast = true; - } + let Some(element_type_id) = cast_source_type_kind.array_element_type() else { + return Err(TranslationError::generic( + "Argument of array decay should have array type" + )); + }; + cast_source_type_kind = + Cow::Borrowed(&self.ast_context.resolve_type(element_type_id).kind); } val = val.map(|val| mk().method_call_expr(val, "as_ptr", vec![])); } else { if is_byte_string_literal { - needs_cast = true; + let Some(element_type_id) = + cast_source_type_kind.to_mut().array_element_type_mut() + else { + return Err(TranslationError::generic( + "String literal should have array type", + )); + }; + *element_type_id = self.ast_context.type_for_kind(&CTypeKind::UInt8); } else { val = val.map(|val| mk().borrow_expr(val)); } @@ -164,16 +171,18 @@ impl<'c> Translation<'c> { } else { self.use_feature("raw_ref_op"); val = val.map(|val| mk().set_mutbl(mutbl).raw_borrow_expr(val)); - - if is_array_decay { - // TODO: Call `ptr::as_[mut]_ptr` instead once that is available. - // (`array_ptr_get` feature added to nightly in January 2024) - needs_cast = true; - } } - // Perform a final cast to the target type if needed. - if needs_cast { + let pointee_type_kind = &self.ast_context.resolve_type(pointee_cty.ctype).kind; + + // If the target pointee type is different from the source type, + // then we need to cast the pointer type. + if !self.ast_context.type_kinds_eq( + pointee_type_kind, + &cast_source_type_kind, + &TypedAstContext::resolve_type_id, + ) || pointee_cty.mutability() != mutbl + { let pointer_ty = self.convert_type(pointer_cty.ctype)?; val = val.map(|val| mk().cast_expr(val, pointer_ty)); } From c85778d6f2898b13fd1c9f071cce5a535ba875c8 Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 25 Aug 2026 17:16:23 +0200 Subject: [PATCH 11/12] transpile: Merge `convert_array_to_pointer_decay` and `convert_address_of_common` --- c2rust-transpile/src/translator/mod.rs | 2 +- c2rust-transpile/src/translator/pointers.rs | 51 +++++++++------------ 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 67245cfaf0..c5436037f9 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -4305,7 +4305,7 @@ impl<'c> Translation<'c> { } CastKind::ArrayToPointerDecay => { - self.convert_array_to_pointer_decay(ctx, source_cty, target_cty, val, expr) + self.make_address_of(ctx, target_cty, source_cty, expr, val, true) } CastKind::NullToPointer => { diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index 6adf7b58cb..f2e6eda986 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -57,45 +57,36 @@ impl<'c> Translation<'c> { .get_qual_type() .ok_or_else(|| format_err!("bad source type"))?; - self.convert_address_of_common(ctx, Some(arg), arg_cty, cqual_type, val, false) + self.make_address_of(ctx, cqual_type, arg_cty, Some(arg), val, false) } - pub fn convert_array_to_pointer_decay( + pub(crate) fn make_address_of( &self, ctx: ExprContext, - source_cty: CQualTypeId, - target_cty: CQualTypeId, - val: WithStmts>, - expr: Option, + pointer_cty: CQualTypeId, + arg_cty: CQualTypeId, + arg: Option, + mut val: WithStmts>, + is_array_decay: bool, ) -> TranslationResult>> { - // Because va_list is sometimes defined as a single-element - // array in order for it to allocate memory as a local variable - // and to be a pointer as a function argument we would get - // spurious casts when trying to treat it like a VaList which - // has reference semantics. - if self.ast_context.is_va_list(target_cty.ctype) { - return Ok(val); - } + if is_array_decay { + // Because va_list is sometimes defined as a single-element + // array in order for it to allocate memory as a local variable + // and to be a pointer as a function argument we would get + // spurious casts when trying to treat it like a VaList which + // has reference semantics. + if self.ast_context.is_va_list(pointer_cty.ctype) { + return Ok(val); + } - let source_ty_kind = &self.ast_context.resolve_type(source_cty.ctype).kind; + let source_ty_kind = &self.ast_context.resolve_type(arg_cty.ctype).kind; - // Variable length arrays are already represented as pointers. - if let CTypeKind::VariableArray(..) = source_ty_kind { - return Ok(val); + // Variable length arrays are already represented as pointers. + if let CTypeKind::VariableArray(..) = source_ty_kind { + return Ok(val); + } } - self.convert_address_of_common(ctx, expr, source_cty, target_cty, val, true) - } - - fn convert_address_of_common( - &self, - ctx: ExprContext, - arg: Option, - arg_cty: CQualTypeId, - pointer_cty: CQualTypeId, - mut val: WithStmts>, - is_array_decay: bool, - ) -> TranslationResult>> { let arg_expr_kind = arg.map(|arg| { let arg = self.ast_context.unwrap_predefined_ident(arg); &self.ast_context.index_unwrap_parens(arg).kind From e7525b54febd67e0b936db1956e51c37bf6af933 Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 25 Aug 2026 18:30:46 +0200 Subject: [PATCH 12/12] transpile: Move `is_function_pointer` check to `make_address_of` --- c2rust-transpile/src/translator/pointers.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index f2e6eda986..a91a055dad 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -48,11 +48,6 @@ impl<'c> Translation<'c> { let val = self.convert_expr(ctx.used().needs_address(), arg, None)?; - // & becomes a no-op when applied to a function. - if self.ast_context.is_function_pointer(cqual_type.ctype) { - return Ok(val.map(|x| mk().call_expr(mk().ident_expr("Some"), vec![x]))); - } - let arg_cty = arg_kind .get_qual_type() .ok_or_else(|| format_err!("bad source type"))?; @@ -85,6 +80,11 @@ impl<'c> Translation<'c> { if let CTypeKind::VariableArray(..) = source_ty_kind { return Ok(val); } + } else { + // & becomes a no-op when applied to a function. + if self.ast_context.is_function_pointer(pointer_cty.ctype) { + return Ok(val.map(|x| mk().call_expr(mk().ident_expr("Some"), vec![x]))); + } } let arg_expr_kind = arg.map(|arg| {