From bf973252cf44c892342694416c3e0f75f2e924a5 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 26 Nov 2025 18:32:29 +0100 Subject: [PATCH 1/2] 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 4ae5c83087b019b77c6f374543579a10f91e7c88 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 26 Nov 2025 17:45:25 +0100 Subject: [PATCH 2/2] transpile: Translate null pointers with `ptr::null[_mut]` --- c2rust-transpile/src/translator/mod.rs | 36 ++++++++++++++----- ...snapshots__transpile-aarch64@vm_x86.c.snap | 6 ++-- .../snapshots__transpile-x86_64@vm_x86.c.snap | 6 ++-- .../snapshots__transpile@alloca.c.snap | 4 +-- .../snapshots__transpile@arrays.c.snap | 2 +- .../snapshots__transpile@empty_init.c.snap | 2 +- .../snapshots__transpile@exprs.c.snap | 2 +- .../snapshots__transpile@macros.c.snap | 10 +++--- .../snapshots__transpile@str_init.c.snap | 4 +-- 9 files changed, 46 insertions(+), 26 deletions(-) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 9054b08681..b0e4d28133 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -2990,7 +2990,7 @@ impl<'c> Translation<'c> { } _ => { // Non function null ptrs provide enough information to skip - // type annotations; ie `= 0 as *const MyStruct;` + // type annotations; ie `= ::core::ptr::null::();` if initializer.is_none() { return false; } @@ -3086,17 +3086,37 @@ impl<'c> Translation<'c> { return Ok(mk().path_expr(vec!["None"])); } - let pointee = self + let pointer_qty = self .ast_context .get_pointee_qual_type(type_id) .ok_or_else(|| TranslationError::generic("null_ptr requires a pointer"))?; - 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 ty_ = self.convert_pointee_type(pointee.ctype)?; - zero = mk().cast_expr(zero, mk().ptr_ty(ty_)); + + let func = if pointer_qty.qualifiers.is_const + // static variable initializers aren't able to use null_mut + // TODO: Rust 1.83: Allowed, so this can be removed. + || is_static + { + "null" + } else { + "null_mut" + }; + let pointee_ty = self.convert_pointee_type(pointer_qty.ctype)?; + let type_args = mk().angle_bracketed_args(vec![pointee_ty.clone()]); + let mut val = mk().call_expr( + mk().abs_path_expr(vec![ + mk().path_segment("core"), + mk().path_segment("ptr"), + mk().path_segment_with_args(func, type_args), + ]), + vec![], + ); + + // TODO: Rust 1.83: Remove. + if is_static && !pointer_qty.qualifiers.is_const { + val = mk().cast_expr(val, mk().mutbl().ptr_ty(pointee_ty)); } - Ok(mk().cast_expr(zero, ty)) + + Ok(val) } fn addr_lhs( diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile-aarch64@vm_x86.c.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile-aarch64@vm_x86.c.snap index 8e15e172ef..720d38b207 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile-aarch64@vm_x86.c.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile-aarch64@vm_x86.c.snap @@ -28,11 +28,11 @@ pub unsafe extern "C" fn VM_CallCompiled( mut args: *mut ::core::ffi::c_int, ) -> ::core::ffi::c_int { let mut stack: [byte; 271] = [0; 271]; - let mut entryPoint: *mut ::core::ffi::c_void = 0 as *mut ::core::ffi::c_void; + let mut entryPoint: *mut ::core::ffi::c_void = ::core::ptr::null_mut::<::core::ffi::c_void>(); let mut programStack: ::core::ffi::c_int = 0; let mut stackOnEntry: ::core::ffi::c_int = 0; - let mut image: *mut byte = 0 as *mut byte; - let mut opStack: *mut ::core::ffi::c_int = 0 as *mut ::core::ffi::c_int; + let mut image: *mut byte = ::core::ptr::null_mut::(); + let mut opStack: *mut ::core::ffi::c_int = ::core::ptr::null_mut::<::core::ffi::c_int>(); let mut opStackOfs: ::core::ffi::c_int = 0; let mut arg: ::core::ffi::c_int = 0; let mut currentVM: *mut vm_t = vm; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile-x86_64@vm_x86.c.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile-x86_64@vm_x86.c.snap index 3893b0277f..9b967f6d04 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile-x86_64@vm_x86.c.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile-x86_64@vm_x86.c.snap @@ -30,11 +30,11 @@ pub unsafe extern "C" fn VM_CallCompiled( mut args: *mut ::core::ffi::c_int, ) -> ::core::ffi::c_int { let mut stack: [byte; 271] = [0; 271]; - let mut entryPoint: *mut ::core::ffi::c_void = 0 as *mut ::core::ffi::c_void; + let mut entryPoint: *mut ::core::ffi::c_void = ::core::ptr::null_mut::<::core::ffi::c_void>(); let mut programStack: ::core::ffi::c_int = 0; let mut stackOnEntry: ::core::ffi::c_int = 0; - let mut image: *mut byte = 0 as *mut byte; - let mut opStack: *mut ::core::ffi::c_int = 0 as *mut ::core::ffi::c_int; + let mut image: *mut byte = ::core::ptr::null_mut::(); + let mut opStack: *mut ::core::ffi::c_int = ::core::ptr::null_mut::<::core::ffi::c_int>(); let mut opStackOfs: ::core::ffi::c_int = 0; let mut arg: ::core::ffi::c_int = 0; let mut currentVM: *mut vm_t = vm; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@alloca.c.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@alloca.c.snap index 126703a4d3..cc09026f03 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@alloca.c.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@alloca.c.snap @@ -19,8 +19,8 @@ pub unsafe extern "C" fn alloca_sum( mut val2: ::core::ffi::c_int, ) -> ::core::ffi::c_int { let mut alloca_allocations: Vec> = Vec::new(); - let mut alloca1: *mut ::core::ffi::c_int = 0 as *mut ::core::ffi::c_int; - let mut alloca2: *mut ::core::ffi::c_int = 0 as *mut ::core::ffi::c_int; + let mut alloca1: *mut ::core::ffi::c_int = ::core::ptr::null_mut::<::core::ffi::c_int>(); + let mut alloca2: *mut ::core::ffi::c_int = ::core::ptr::null_mut::<::core::ffi::c_int>(); if TRUE != 0 { alloca_allocations.push(::std::vec::from_elem( 0, diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.snap index b8eb61d0cc..032271ecd5 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@arrays.c.snap @@ -51,7 +51,7 @@ pub unsafe extern "C" fn entry() { let mut int_too_short: [::core::ffi::c_int; 16] = [0 as ::core::ffi::c_int; 16]; int_too_short[15 as ::core::ffi::c_int as usize] += 9 as ::core::ffi::c_int; let mut struct_init_too_short: [C2RustUnnamed_0; 1] = [C2RustUnnamed_0 { - x: 0 as *mut ::core::ffi::c_char, + x: ::core::ptr::null_mut::<::core::ffi::c_char>(), y: 0, }; 1]; struct_init_too_short[0 as ::core::ffi::c_int as usize].y += 9 as ::core::ffi::c_int; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@empty_init.c.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@empty_init.c.snap index c4251bcb09..db8be5bc49 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@empty_init.c.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@empty_init.c.snap @@ -19,7 +19,7 @@ pub struct Scope { #[no_mangle] pub static mut scope: *mut Scope = &{ let mut init = Scope { - next: 0 as *const Scope as *mut Scope, + next: ::core::ptr::null::() as *mut Scope, }; init } as *const Scope as *mut Scope; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.snap index 2125fc3142..4941db5813 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.snap @@ -38,7 +38,7 @@ pub unsafe extern "C" fn unary_without_side_effect() { } #[no_mangle] pub unsafe extern "C" fn unary_with_side_effect() { - let mut arr: [*mut ::core::ffi::c_char; 1] = [0 as *mut ::core::ffi::c_char]; + let mut arr: [*mut ::core::ffi::c_char; 1] = [::core::ptr::null_mut::<::core::ffi::c_char>()]; -side_effect(); side_effect(); !side_effect(); diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@macros.c.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@macros.c.snap index fc5349c45b..64e751aafa 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@macros.c.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@macros.c.snap @@ -240,7 +240,7 @@ static mut global_static_const_mixed_arithmetic: ::core::ffi::c_float = - true_0 as ::core::ffi::c_double) as ::core::ffi::c_float; static mut global_static_const_parens: ::core::ffi::c_int = PARENS; static mut global_static_const_ptr_arithmetic: *const ::core::ffi::c_char = - 0 as *const ::core::ffi::c_char; + ::core::ptr::null::<::core::ffi::c_char>(); static mut global_static_const_widening_cast: ::core::ffi::c_ulonglong = WIDENING_CAST; static mut global_static_const_narrowing_cast: ::core::ffi::c_char = LITERAL_INT as ::core::ffi::c_char; @@ -254,7 +254,7 @@ static mut global_static_const_str_concatenation: [::core::ffi::c_char; 18] = un static mut global_static_const_builtin: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; static mut global_static_const_ref_indexing: *const ::core::ffi::c_char = - 0 as *const ::core::ffi::c_char; + ::core::ptr::null::<::core::ffi::c_char>(); static mut global_static_const_ref_struct: *const S = &LITERAL_STRUCT as *const S 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; @@ -313,7 +313,7 @@ pub static mut global_const_mixed_arithmetic: ::core::ffi::c_float = pub static mut global_const_parens: ::core::ffi::c_int = PARENS; #[no_mangle] pub static mut global_const_ptr_arithmetic: *const ::core::ffi::c_char = - 0 as *const ::core::ffi::c_char; + ::core::ptr::null::<::core::ffi::c_char>(); #[no_mangle] pub static mut global_const_widening_cast: ::core::ffi::c_ulonglong = WIDENING_CAST; #[no_mangle] @@ -335,7 +335,7 @@ pub static mut global_const_builtin: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; #[no_mangle] pub static mut global_const_ref_indexing: *const ::core::ffi::c_char = - 0 as *const ::core::ffi::c_char; + ::core::ptr::null::<::core::ffi::c_char>(); #[no_mangle] pub static mut global_const_ref_struct: *const S = &LITERAL_STRUCT as *const S as *mut S; #[no_mangle] @@ -363,7 +363,7 @@ pub unsafe extern "C" fn reference_define() -> ::core::ffi::c_int { #[no_mangle] pub static mut fns: fn_ptrs = { let mut init = fn_ptrs { - v: 0 as *const ::core::ffi::c_void as *mut ::core::ffi::c_void, + v: ::core::ptr::null::<::core::ffi::c_void>() as *mut ::core::ffi::c_void, fn1: None, fn2: None, }; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@str_init.c.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@str_init.c.snap index c72a2d4bc7..2a7959eec5 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@str_init.c.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@str_init.c.snap @@ -68,8 +68,8 @@ pub unsafe extern "C" fn array_extra_braces() { pub unsafe extern "C" fn array_of_ptrs() { let mut _s: [*const ::core::ffi::c_char; 3] = [ b"hello\0" as *const u8 as *const ::core::ffi::c_char, - 0 as *const ::core::ffi::c_char, - 0 as *const ::core::ffi::c_char, + ::core::ptr::null::<::core::ffi::c_char>(), + ::core::ptr::null::<::core::ffi::c_char>(), ]; } #[no_mangle]