From bf973252cf44c892342694416c3e0f75f2e924a5 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 26 Nov 2025 18:32:29 +0100 Subject: [PATCH 1/4] transpile: Split `convert_pointer` into two functions for easier reuse --- c2rust-transpile/src/convert_type.rs | 49 +++++++++++--------- c2rust-transpile/src/translator/mod.rs | 17 ++++--- c2rust-transpile/src/translator/operators.rs | 9 +--- 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/c2rust-transpile/src/convert_type.rs b/c2rust-transpile/src/convert_type.rs index ec11b14059..970e92ab42 100644 --- a/c2rust-transpile/src/convert_type.rs +++ b/c2rust-transpile/src/convert_type.rs @@ -268,44 +268,49 @@ impl TypeConverter { Ok(mk().unsafe_().extern_("C").barefn_ty(fn_ty)) } + /// Converts the qualified type of a pointer. pub fn convert_pointer( &mut self, ctxt: &TypedAstContext, qtype: CQualTypeId, ) -> TranslationResult> { - let mutbl = if qtype.qualifiers.is_const { - Mutability::Immutable + let pointee_ty = self.convert_pointee(ctxt, qtype.ctype)?; + + if let CTypeKind::Function(..) = ctxt.resolve_type(qtype.ctype).kind { + // Function pointers are translated to Option applied to the function type + // in order to support NULL function pointers natively + let param = mk().angle_bracketed_args(vec![pointee_ty]); + Ok(mk().path_ty(vec![mk().path_segment_with_args("Option", param)])) } else { - Mutability::Mutable - }; + let mutbl = if qtype.qualifiers.is_const { + Mutability::Immutable + } else { + Mutability::Mutable + }; - match ctxt.resolve_type(qtype.ctype).kind { + Ok(mk().set_mutbl(mutbl).ptr_ty(pointee_ty)) + } + } + + /// Converts the pointee type of a pointer. + pub fn convert_pointee( + &mut self, + ctxt: &TypedAstContext, + ctype: CTypeId, + ) -> TranslationResult> { + match ctxt.resolve_type(ctype).kind { // While void converts to () in function returns, it converts to c_void // in the case of pointers. - CTypeKind::Void => Ok(mk() - .set_mutbl(mutbl) - .ptr_ty(mk().abs_path_ty(vec!["core", "ffi", "c_void"]))), + CTypeKind::Void => Ok(mk().abs_path_ty(vec!["core", "ffi", "c_void"])), CTypeKind::VariableArray(mut elt, _len) => { while let CTypeKind::VariableArray(elt_, _) = ctxt.resolve_type(elt).kind { elt = elt_ } - let child_ty = self.convert(ctxt, elt)?; - Ok(mk().set_mutbl(mutbl).ptr_ty(child_ty)) - } - - // Function pointers are translated to Option applied to the function type - // in order to support NULL function pointers natively - CTypeKind::Function(..) => { - let fn_ty = self.convert(ctxt, qtype.ctype)?; - let param = mk().angle_bracketed_args(vec![fn_ty]); - Ok(mk().path_ty(vec![mk().path_segment_with_args("Option", param)])) + self.convert(ctxt, elt) } - _ => { - let child_ty = self.convert(ctxt, qtype.ctype)?; - Ok(mk().set_mutbl(mutbl).ptr_ty(child_ty)) - } + _ => self.convert(ctxt, ctype), } } diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 1ffbb889ff..9054b08681 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -3071,6 +3071,14 @@ impl<'c> Translation<'c> { .convert(&self.ast_context, type_id) } + fn convert_pointee_type(&self, type_id: CTypeId) -> TranslationResult> { + self.import_type(type_id); + + self.type_converter + .borrow_mut() + .convert_pointee(&self.ast_context, type_id) + } + /// Construct an expression for a NULL at any type, including forward declarations, /// function pointers, and normal pointers. fn null_ptr(&self, type_id: CTypeId, is_static: bool) -> TranslationResult> { @@ -3085,13 +3093,8 @@ impl<'c> Translation<'c> { let ty = self.convert_type(type_id)?; let mut zero = mk().lit_expr(mk().int_unsuffixed_lit(0)); if is_static && !pointee.qualifiers.is_const { - let mut qtype = pointee; - qtype.qualifiers.is_const = true; - let ty_ = self - .type_converter - .borrow_mut() - .convert_pointer(&self.ast_context, qtype)?; - zero = mk().cast_expr(zero, ty_); + let ty_ = self.convert_pointee_type(pointee.ctype)?; + zero = mk().cast_expr(zero, mk().ptr_ty(ty_)); } Ok(mk().cast_expr(zero, ty)) } diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index 180baf5017..9afc31c400 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -1009,13 +1009,8 @@ impl<'c> Translation<'c> { // through & to *const to *mut addr_of_arg = mk().addr_of_expr(a); if let Mutability::Mutable = mutbl { - let mut qtype = pointee_ty; - qtype.qualifiers.is_const = true; - let ty_ = self - .type_converter - .borrow_mut() - .convert_pointer(&self.ast_context, qtype)?; - addr_of_arg = mk().cast_expr(addr_of_arg, ty_); + let ty_ = self.convert_pointee_type(pointee_ty.ctype)?; + addr_of_arg = mk().cast_expr(addr_of_arg, mk().ptr_ty(ty_)); } } else { // Normal case is allowed to use &mut if needed From cf1c95a34a2a58e960cde84f96a7583512799e6e Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 4 Nov 2025 11:20:55 +0100 Subject: [PATCH 2/4] transpile: Add tests for #1238 and #1240 --- c2rust-transpile/tests/snapshots/arrays.c | 11 ++++++----- .../tests/snapshots/compound_literals.c | 5 +++-- .../snapshots__transpile@arrays.c.snap | 17 ++++++++++++----- ...napshots__transpile@compound_literals.c.snap | 3 +++ 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/c2rust-transpile/tests/snapshots/arrays.c b/c2rust-transpile/tests/snapshots/arrays.c index da524373cd..2b74929394 100644 --- a/c2rust-transpile/tests/snapshots/arrays.c +++ b/c2rust-transpile/tests/snapshots/arrays.c @@ -1,7 +1,8 @@ #include -static char simple[] = "mystring"; -static char *foo = "mystring"; +char static_char_array[] = "mystring"; +char *static_char_ptr = "mystring"; +void *static_void_ptr = (void*) static_char_array; void entry(void) { int int_2d[1][1] = { 1 }; @@ -27,7 +28,7 @@ void entry(void) { int *int_var_ptr = int_empty_init; int (*int_var_array_ptr)[16] = &int_empty_init; - char *char_var_ptr_var = char_with_string; + char *char_var_ptr = char_with_string; char (*char_var_array_ptr)[4] = &char_with_string; const char *const_char_lit_ptr = "abc"; const char (*const_char_lit_array_ptr)[4] = &"abc"; @@ -44,8 +45,8 @@ void entry(void) { #endif // Test that we can get the address of the element past the end of the array - char *past_end = &simple[sizeof(simple)]; - past_end = &foo[8]; + char *past_end = &static_char_array[sizeof(static_char_array)]; + past_end = &static_char_ptr[8]; } void short_initializer() { diff --git a/c2rust-transpile/tests/snapshots/compound_literals.c b/c2rust-transpile/tests/snapshots/compound_literals.c index df82aee4dc..840b7ad017 100644 --- a/c2rust-transpile/tests/snapshots/compound_literals.c +++ b/c2rust-transpile/tests/snapshots/compound_literals.c @@ -1,11 +1,12 @@ int static_single_int = (int) { 42 }; int *static_single_int_ptr = &((int) { 42 }); int *static_int_ptr_to_array = (int[]) { 42, 9001 }; +volatile int *static_volatile_int_ptr_to_array = (int[]) { 42, 9001 }; +int (*static_int_array_ptr)[2] = &(int[]) { 42, 9001 }; + // Currently generates broken Rust code, see // https://github.com/immunant/c2rust/issues/1410 //char *static_char_ptr_to_array = (char[]) { "hello" }; -int (*static_int_array_ptr)[2] = &(int[]) { 42, 9001 }; -// As above //char (*static_char_array_ptr)[6] = &(char[]) { "hello" }; #define SINGLE_INT ((int) { 42 }) diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.snap index b8eb61d0cc..7042223163 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.snap @@ -34,11 +34,17 @@ pub struct C2RustUnnamed_2 { pub x: ::core::ffi::c_short, pub y: ::core::ffi::c_int, } -static mut simple: [::core::ffi::c_char; 9] = +#[no_mangle] +pub static mut static_char_array: [::core::ffi::c_char; 9] = unsafe { ::core::mem::transmute::<[u8; 9], [::core::ffi::c_char; 9]>(*b"mystring\0") }; -static mut foo: *mut ::core::ffi::c_char = +#[no_mangle] +pub static mut static_char_ptr: *mut ::core::ffi::c_char = b"mystring\0" as *const u8 as *const ::core::ffi::c_char as *mut ::core::ffi::c_char; #[no_mangle] +pub static mut static_void_ptr: *mut ::core::ffi::c_void = unsafe { + static_char_array.as_ptr() as *mut _ as *mut ::core::ffi::c_void +}; +#[no_mangle] pub unsafe extern "C" fn entry() { let mut int_2d: [[::core::ffi::c_int; 1]; 1] = [[1 as ::core::ffi::c_int]]; int_2d[0 as ::core::ffi::c_int as usize][0 as ::core::ffi::c_int as usize] += @@ -81,7 +87,7 @@ pub unsafe extern "C" fn entry() { ); let mut int_var_ptr: *mut ::core::ffi::c_int = int_empty_init.as_mut_ptr(); let mut int_var_array_ptr: *mut [::core::ffi::c_int; 16] = &mut int_empty_init; - let mut char_var_ptr_var: *mut ::core::ffi::c_char = char_with_string.as_mut_ptr(); + let mut char_var_ptr: *mut ::core::ffi::c_char = char_with_string.as_mut_ptr(); let mut char_var_array_ptr: *mut [::core::ffi::c_char; 4] = &mut char_with_string; let mut const_char_lit_ptr: *const ::core::ffi::c_char = b"abc\0" as *const u8 as *const ::core::ffi::c_char; @@ -92,11 +98,12 @@ pub unsafe extern "C" fn entry() { let mut char_lit_array_ptr: *mut [::core::ffi::c_char; 4] = b"abc\0" as *const [u8; 4] as *const [::core::ffi::c_char; 4] as *mut [::core::ffi::c_char; 4]; - let mut past_end: *mut ::core::ffi::c_char = &mut *simple + let mut past_end: *mut ::core::ffi::c_char = &mut *static_char_array .as_mut_ptr() .offset(::core::mem::size_of::<[::core::ffi::c_char; 9]>() as isize) as *mut ::core::ffi::c_char; - past_end = &mut *foo.offset(8 as ::core::ffi::c_int as isize) as *mut ::core::ffi::c_char; + past_end = + &mut *static_char_ptr.offset(8 as ::core::ffi::c_int as isize) as *mut ::core::ffi::c_char; } #[no_mangle] pub unsafe extern "C" fn short_initializer() { diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.snap index b1329d0909..1e5698fc6c 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.snap @@ -19,6 +19,9 @@ pub static mut static_single_int_ptr: *mut ::core::ffi::c_int = #[no_mangle] pub static mut static_int_ptr_to_array: *mut ::core::ffi::c_int = [42, 9001].as_ptr() as *mut _; #[no_mangle] +pub static mut static_volatile_int_ptr_to_array: *mut ::core::ffi::c_int = + [42, 9001].as_ptr() as *mut _ as *mut ::core::ffi::c_int; +#[no_mangle] pub static mut static_int_array_ptr: *mut [::core::ffi::c_int; 2] = &[42, 9001] as *const [::core::ffi::c_int; 2] as *mut [::core::ffi::c_int; 2]; pub const SINGLE_INT: ::core::ffi::c_int = 42 as ::core::ffi::c_int; From d7adfc49d868e2d9430c36fffeba3b2dcc62c0c3 Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 25 Oct 2025 15:14:18 +0200 Subject: [PATCH 3/4] transpile: Factor out function for getting bytes of a literal --- c2rust-transpile/src/translator/literals.rs | 47 ++++++++++++-------- c2rust-transpile/src/translator/mod.rs | 20 +++++---- c2rust-transpile/src/translator/operators.rs | 20 +++------ 3 files changed, 46 insertions(+), 41 deletions(-) diff --git a/c2rust-transpile/src/translator/literals.rs b/c2rust-transpile/src/translator/literals.rs index 200b27bb66..cf82833c5c 100644 --- a/c2rust-transpile/src/translator/literals.rs +++ b/c2rust-transpile/src/translator/literals.rs @@ -151,31 +151,42 @@ impl<'c> Translation<'c> { Ok(WithStmts::new_val(val)) } - CLiteral::String(ref val, width) => { - let mut val = val.to_owned(); - let num_elems = match self.ast_context.resolve_type(ty.ctype).kind { - CTypeKind::ConstantArray(_elem_ty, num_elems) => num_elems, - ref kind => { - panic!("String literal with unknown size: {val:?}, kind = {kind:?}") - } - }; - - // Match the literal size to the expected size padding with zeros as needed - let size = num_elems * (width as usize); - val.resize(size, 0); + CLiteral::String(ref bytes, element_size) => { + let bytes_padded = self.string_literal_bytes(ty.ctype, bytes, element_size); // std::mem::transmute::<[u8; size], ctype>(*b"xxxx") - let u8_ty = mk().path_ty(vec!["u8"]); - let width_lit = mk().lit_expr(mk().int_unsuffixed_lit(val.len() as u128)); - Ok(WithStmts::new_unsafe_val(transmute_expr( - mk().array_ty(u8_ty, width_lit), + let array_ty = mk().array_ty( + mk().ident_ty("u8"), + mk().lit_expr(bytes_padded.len() as u128), + ); + let val = transmute_expr( + array_ty, self.convert_type(ty.ctype)?, - mk().unary_expr(UnOp::Deref(Default::default()), mk().lit_expr(val)), - ))) + mk().unary_expr(UnOp::Deref(Default::default()), mk().lit_expr(bytes_padded)), + ); + Ok(WithStmts::new_unsafe_val(val)) } } } + /// Returns the bytes of a string literal, including any additional zero bytes to pad the + /// literal to the expected size. + pub fn string_literal_bytes(&self, ctype: CTypeId, bytes: &[u8], element_size: u8) -> Vec { + let num_elems = match self.ast_context.resolve_type(ctype).kind { + CTypeKind::ConstantArray(_, num_elems) => num_elems, + ref kind => { + panic!("String literal with unknown size: {bytes:?}, kind = {kind:?}") + } + }; + + let size = num_elems * (element_size as usize); + let mut bytes_padded = Vec::with_capacity(size); + bytes_padded.extend(bytes); + bytes_padded.resize(size, 0); + + bytes_padded + } + /// Convert an initialization list into an expression. These initialization lists can be /// used as array literals, struct literals, and union literals in code. pub fn convert_init_list( diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 9054b08681..5af7cf127a 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -4729,16 +4729,20 @@ impl<'c> Translation<'c> { }) .unwrap_or(false); match expr_kind { - Some(&CExprKind::Literal(_, CLiteral::String(ref bytes, 1))) - if is_const && !translate_as_macro => - { + Some(&CExprKind::Literal( + literal_cqual_type, + CLiteral::String(ref bytes, element_size @ 1), + )) if is_const && !translate_as_macro => { let target_ty = self.convert_type(target_cty.ctype)?; - let mut bytes = bytes.to_owned(); - bytes.push(0); - let byte_literal = mk().lit_expr(bytes); - let val = - mk().cast_expr(byte_literal, mk().ptr_ty(mk().path_ty(vec!["u8"]))); + let bytes_padded = self.string_literal_bytes( + literal_cqual_type.ctype, + bytes, + element_size, + ); + let element_ty = mk().ident_ty("u8"); + let bytes_literal = mk().lit_expr(bytes_padded); + let val = mk().cast_expr(bytes_literal, mk().ptr_ty(element_ty)); let val = mk().cast_expr(val, target_ty); Ok(WithStmts::new_val(val)) } diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index 9afc31c400..e8197edd39 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -968,21 +968,11 @@ impl<'c> Translation<'c> { false, ) = (expr_kind, translate_as_macro) { - let num_elems = - match self.ast_context.resolve_type(literal_cqual_type.ctype).kind { - CTypeKind::ConstantArray(_, num_elems) => num_elems, - ref kind => { - panic!( - "String literal with unknown size: {bytes:?}, kind = {kind:?}" - ) - } - }; - - // Match the literal size to the expected size padding with zeros as needed - let size = num_elems * (element_size as usize); - let mut bytes_padded = Vec::with_capacity(size); - bytes_padded.extend(bytes); - bytes_padded.resize(size, 0); + let bytes_padded = self.string_literal_bytes( + literal_cqual_type.ctype, + bytes, + element_size, + ); let array_ty = mk().array_ty( mk().ident_ty("u8"), From 5a55ba952ee03e18a01f4ec86477864de5241f82 Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 6 Nov 2025 10:39:15 +0100 Subject: [PATCH 4/4] transpile: Factor out common function for address-of and array-decay --- c2rust-transpile/src/translator/mod.rs | 183 ++++++++++-------- c2rust-transpile/src/translator/operators.rs | 92 ++------- .../snapshots__transpile@arrays.c.snap | 5 +- ...pshots__transpile@compound_literals.c.snap | 5 +- .../snapshots__transpile@macros.c.snap | 2 +- 5 files changed, 124 insertions(+), 163 deletions(-) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 5af7cf127a..a2cbd4e0bb 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -4708,84 +4708,12 @@ impl<'c> Translation<'c> { return Ok(val); } - let pointee = self - .ast_context - .get_pointee_qual_type(target_cty.ctype) - .unwrap_or_else(|| panic!("dereferencing a non-pointer")); - - let is_const = pointee.qualifiers.is_const; - - // Handle literals by looking at the next level of expr nesting. Avoid doing this - // for expressions that will be translated as const macros, because emitting the - // name of the const macro only occurs if we process the expr_id with a direct call - // to `convert_expr`. - let expr_kind = expr.map(|e| &self.ast_context.index(e).kind); - let translate_as_macro = expr - .map(|e| { - self.convert_const_macro_expansion(ctx, e, None) - .ok() - .flatten() - .is_some() - }) - .unwrap_or(false); - match expr_kind { - Some(&CExprKind::Literal( - literal_cqual_type, - CLiteral::String(ref bytes, element_size @ 1), - )) if is_const && !translate_as_macro => { - let target_ty = self.convert_type(target_cty.ctype)?; - - let bytes_padded = self.string_literal_bytes( - literal_cqual_type.ctype, - bytes, - element_size, - ); - let element_ty = mk().ident_ty("u8"); - let bytes_literal = mk().lit_expr(bytes_padded); - let val = mk().cast_expr(bytes_literal, mk().ptr_ty(element_ty)); - let val = mk().cast_expr(val, target_ty); - Ok(WithStmts::new_val(val)) - } - _ => { - // Variable length arrays are already represented as pointers. - if let CTypeKind::VariableArray(..) = source_ty_kind { - Ok(val) - } else { - let method = if is_const || ctx.is_static { - "as_ptr" - } else { - "as_mut_ptr" - }; - - let call = val.map(|x| mk().method_call_expr(x, method, vec![])); - - // If the target pointee type is different from the source element type, - // then we need to cast the ptr type as well. - let call = match source_ty_kind.element_ty() { - None => call, - Some(source_element_ty) if source_element_ty == pointee.ctype => { - call - } - Some(_) => { - let target_ty = self.convert_type(target_cty.ctype)?; - call.map(|ptr| mk().cast_expr(ptr, target_ty)) - } - }; - - // Static arrays can now use as_ptr. Can also cast that const ptr to a - // mutable pointer as we do here: - if ctx.is_static && !is_const { - return Ok(call.map(|val| { - let inferred_type = mk().infer_ty(); - let ptr_type = mk().mutbl().ptr_ty(inferred_type); - mk().cast_expr(val, ptr_type) - })); - } - - Ok(call) - } - } + // Variable length arrays are already represented as pointers. + if let CTypeKind::VariableArray(..) = source_ty_kind { + return Ok(val); } + + self.convert_address_of(ctx, expr, source_cty, target_cty, val, true) } CastKind::NullToPointer => { @@ -4945,6 +4873,107 @@ impl<'c> Translation<'c> { val.map(|x| mk().cast_expr(x, target_ty)) } + pub fn convert_address_of( + &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| &self.ast_context.index(arg).kind); + let pointee_cty = self + .ast_context + .get_pointee_qual_type(pointer_cty.ctype) + .ok_or_else(|| TranslationError::generic("Address-of should return a pointer"))?; + let arg_is_macro = arg.map_or(false, |arg| { + matches!( + self.convert_const_macro_expansion(ctx, arg, None), + Ok(Some(_)) + ) + }); + + let mut needs_cast = false; + let mut ref_cast_pointee_ty = None; + let mutbl = if pointee_cty.qualifiers.is_const { + Mutability::Immutable + } else if ctx.is_static { + // static variable initializers 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 { + Mutability::Mutable + }; + + // String literals are translated with a transmute, which produces a temporary. + // Taking the address of a temporary leaves a dangling pointer. So instead, + // cast the string literal directly so that its 'static lifetime is preserved. + if let ( + Some(&CExprKind::Literal(literal_cty, CLiteral::String(ref bytes, element_size @ 1))), + false, + ) = (arg_expr_kind, arg_is_macro) + { + let bytes_padded = self.string_literal_bytes(literal_cty.ctype, bytes, element_size); + let len = bytes_padded.len(); + val = WithStmts::new_val(mk().lit_expr(bytes_padded)); + + if is_array_decay { + ref_cast_pointee_ty = Some(mk().ident_ty("u8")); + } else { + ref_cast_pointee_ty = + Some(mk().array_ty(mk().ident_ty("u8"), mk().lit_expr(len as u128))); + } + needs_cast = true; + } else { + 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![])); + + // 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 + }) { + needs_cast = true; + } + } else { + val = val.map(|val| mk().set_mutbl(mutbl).addr_of_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.type_converter + .borrow_mut() + .convert_pointee(&self.ast_context, arg_cty.ctype)?, + ); + } + } + } + + // 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)?; + val = val.map(|val| mk().cast_expr(val, pointer_ty)); + } + + Ok(val) + } + pub fn implicit_default_expr( &self, ty_id: CTypeId, diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index e8197edd39..62da81c9f1 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -915,7 +915,6 @@ impl<'c> Translation<'c> { lrvalue: LRValue, ) -> TranslationResult>> { let CQualTypeId { ctype, .. } = cqual_type; - let ty = self.convert_type(ctype)?; let resolved_ctype = self.ast_context.resolve_type(ctype); let mut unary = match name { @@ -927,94 +926,27 @@ impl<'c> Translation<'c> { CExprKind::Unary(_, c_ast::UnOp::Deref, target, _) => { return self.convert_expr(ctx, *target, None) } - // 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. + // 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() } _ => (), - }; - - // In this translation, there are only pointers to functions and - // & becomes a no-op when applied to a function. + } let val = self.convert_expr(ctx.used().set_needs_address(true), arg, None)?; - if self.ast_context.is_function_pointer(ctype) { - Ok(val.map(|x| mk().call_expr(mk().ident_expr("Some"), vec![x]))) - } else { - let pointee_ty = - self.ast_context - .get_pointee_qual_type(ctype) - .ok_or_else(|| { - TranslationError::generic("Address-of should return a pointer") - })?; - - let expr_kind = &self.ast_context.index(arg).kind; - let translate_as_macro = self - .convert_const_macro_expansion(ctx, arg, None) - .ok() - .flatten() - .is_some(); - - // String literals are translated with a transmute, which produces a temporary. - // Taking the address of a temporary leaves a dangling pointer. So instead, - // cast the string literal directly so that its 'static lifetime is preserved. - if let ( - &CExprKind::Literal( - literal_cqual_type, - CLiteral::String(ref bytes, element_size @ 1), - ), - false, - ) = (expr_kind, translate_as_macro) - { - let bytes_padded = self.string_literal_bytes( - literal_cqual_type.ctype, - bytes, - element_size, - ); - - let array_ty = mk().array_ty( - mk().ident_ty("u8"), - mk().lit_expr(bytes_padded.len() as u128), - ); - let bytes_literal = mk().lit_expr(bytes_padded); - let val = mk().cast_expr(bytes_literal, mk().ptr_ty(array_ty)); - let val = mk().cast_expr(val, ty); - return Ok(WithStmts::new_val(val)); - } - - let mutbl = if pointee_ty.qualifiers.is_const { - Mutability::Immutable - } else { - Mutability::Mutable - }; - - val.result_map(|a| { - let mut addr_of_arg: Box; - - if ctx.is_static { - // static variable initializers aren't able to use &mut, - // so we work around that by using & and an extra cast - // through & to *const to *mut - addr_of_arg = mk().addr_of_expr(a); - if let Mutability::Mutable = mutbl { - let ty_ = self.convert_pointee_type(pointee_ty.ctype)?; - addr_of_arg = mk().cast_expr(addr_of_arg, mk().ptr_ty(ty_)); - } - } else { - // Normal case is allowed to use &mut if needed - addr_of_arg = mk().set_mutbl(mutbl).addr_of_expr(a); + // & 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]))); + } - // Avoid unnecessary reference to pointer decay in fn call args: - if ctx.decay_ref.is_no() { - return Ok(addr_of_arg); - } - } + let arg_cty = arg_kind + .get_qual_type() + .ok_or_else(|| format_err!("bad source type"))?; - Ok(mk().cast_expr(addr_of_arg, ty)) - }) - } + self.convert_address_of(ctx, Some(arg), arg_cty, cqual_type, val, false) } c_ast::UnOp::PreIncrement => self.convert_pre_increment(ctx, cqual_type, true, arg), c_ast::UnOp::PreDecrement => self.convert_pre_increment(ctx, cqual_type, false, arg), diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.snap index 7042223163..1769946766 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.snap @@ -41,9 +41,8 @@ pub static mut static_char_array: [::core::ffi::c_char; 9] = pub static mut static_char_ptr: *mut ::core::ffi::c_char = b"mystring\0" as *const u8 as *const ::core::ffi::c_char as *mut ::core::ffi::c_char; #[no_mangle] -pub static mut static_void_ptr: *mut ::core::ffi::c_void = unsafe { - static_char_array.as_ptr() as *mut _ as *mut ::core::ffi::c_void -}; +pub static mut static_void_ptr: *mut ::core::ffi::c_void = + unsafe { static_char_array.as_ptr() as *mut ::core::ffi::c_char as *mut ::core::ffi::c_void }; #[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.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.snap index 1e5698fc6c..a6f885b418 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@compound_literals.c.snap @@ -17,10 +17,11 @@ pub static mut static_single_int: ::core::ffi::c_int = 42; pub static mut static_single_int_ptr: *mut ::core::ffi::c_int = &42 as *const ::core::ffi::c_int as *mut ::core::ffi::c_int; #[no_mangle] -pub static mut static_int_ptr_to_array: *mut ::core::ffi::c_int = [42, 9001].as_ptr() as *mut _; +pub static mut static_int_ptr_to_array: *mut ::core::ffi::c_int = + [42, 9001].as_ptr() as *mut ::core::ffi::c_int; #[no_mangle] pub static mut static_volatile_int_ptr_to_array: *mut ::core::ffi::c_int = - [42, 9001].as_ptr() as *mut _ as *mut ::core::ffi::c_int; + [42, 9001].as_ptr() as *mut ::core::ffi::c_int as *mut ::core::ffi::c_int; #[no_mangle] pub static mut static_int_array_ptr: *mut [::core::ffi::c_int; 2] = &[42, 9001] as *const [::core::ffi::c_int; 2] as *mut [::core::ffi::c_int; 2]; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@macros.c.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@macros.c.snap index fc5349c45b..cfe7a84b97 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@macros.c.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@macros.c.snap @@ -370,7 +370,7 @@ pub static mut fns: fn_ptrs = { init }; #[no_mangle] -pub static mut p: *const fn_ptrs = unsafe { &fns as *const fn_ptrs }; +pub static mut p: *const fn_ptrs = unsafe { &fns }; pub const ZSTD_WINDOWLOG_MAX_32: ::core::ffi::c_int = 30 as ::core::ffi::c_int; pub const ZSTD_WINDOWLOG_MAX_64: ::core::ffi::c_int = 31 as ::core::ffi::c_int; #[no_mangle]