From 5684416ca5b325e2ead882b58a9ea6e2664a25fd Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 18 Jun 2026 13:19:45 +0200 Subject: [PATCH 1/6] transpile: Ensure that there are always types for primitive type kinds --- c2rust-transpile/src/c_ast/conversion.rs | 22 ++++++++++++++++++++++ c2rust-transpile/src/c_ast/mod.rs | 17 ++++++++++++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/c2rust-transpile/src/c_ast/conversion.rs b/c2rust-transpile/src/c_ast/conversion.rs index 4a3acb0725..a36ce8fe26 100644 --- a/c2rust-transpile/src/c_ast/conversion.rs +++ b/c2rust-transpile/src/c_ast/conversion.rs @@ -503,6 +503,28 @@ impl ConversionContext { self.visit_node(untyped_context, node_id, new_id, expected_ty) } + // Check what primitive kinds were emitted by the compiler. + let mut found_kinds: HashMap<_, _> = CTypeKind::PRIMITIVE_KINDS + .into_iter() + .map(|kind| (kind, false)) + .collect(); + + for Located { kind, .. } in self.typed_context.c_types.values() { + if let Some(is_found) = found_kinds.get_mut(kind) { + *is_found = true; + } + } + + // If any primitives are missing, add them ourselves. + for (kind, is_found) in found_kinds { + if !is_found { + let new_id = self.id_mapper.fresh_id(); + self.add_type(new_id, not_located(kind)); + self.processed_nodes + .insert(new_id, self::node_types::OTHER_TYPE); + } + } + // Function declarations' types look through typedefs, but we want to use the types with // typedefs intact in some cases during translation. To ensure that these types exist in the // `TypedAstContext`, iterate over all function decls, compute their adjusted type using diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index 2b8a76e608..34946d14aa 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -2483,7 +2483,7 @@ pub struct AsmOperand { } /// Type qualifiers (6.7.3) -#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash)] pub struct Qualifiers { /// The `const` qualifier, which marks lvalues as non-assignable. /// @@ -2519,7 +2519,7 @@ impl Qualifiers { } /// Qualified type -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct CQualTypeId { pub qualifiers: Qualifiers, pub ctype: CTypeId, @@ -2546,7 +2546,7 @@ impl CQualTypeId { /// Represents a type in C (6.2.5 Types) /// /// Reflects the types in -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum CTypeKind { Void, @@ -2670,6 +2670,17 @@ pub enum CTypeKind { } impl CTypeKind { + /// Kinds for C primitive types. These are emitted by the compiler, but possibly only if + /// they are actually used in the code. + pub const PRIMITIVE_KINDS: [CTypeKind; 16] = { + use CTypeKind::*; + [ + Void, Bool, Char, SChar, Short, Int, Long, LongLong, UChar, UShort, UInt, ULong, + ULongLong, Float, Double, LongDouble, + ] + }; + + /// Kinds for Rust types that are pulled back into C, for more fine-grained translation. pub const PULLBACK_KINDS: [CTypeKind; 16] = { use CTypeKind::*; [ From 063e0b4fee976b4f8467b33644748d110424a230 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 17 Jun 2026 12:53:31 +0200 Subject: [PATCH 2/6] transpile: Minor refactor to `convert_builtin` --- c2rust-transpile/src/translator/builtins.rs | 240 +++++++++++--------- 1 file changed, 132 insertions(+), 108 deletions(-) diff --git a/c2rust-transpile/src/translator/builtins.rs b/c2rust-transpile/src/translator/builtins.rs index ae05d76ab4..ad0856d46b 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -113,30 +113,37 @@ impl<'c> Translation<'c> { )) } "__builtin_signbit" | "__builtin_signbitf" | "__builtin_signbitl" => { + let val = self.convert_expr(ctx.used(), args[0], None)?; self.import_num_traits(args[0])?; - let val = self.convert_expr(ctx.used(), args[0], None)?; Ok(val.map(|v| { let val = mk().method_call_expr(v, "is_sign_negative", vec![]); - mk().cast_expr(val, mk().abs_path_ty(vec!["core", "ffi", "c_int"])) })) } "__builtin_ffs" | "__builtin_ffsl" | "__builtin_ffsll" => { let val = self.convert_expr(ctx.used(), args[0], None)?; - Ok(val.map(|x| { - let add = BinOp::Add(Default::default()); - let zero = mk().lit_expr(mk().int_lit(0, "")); - let one = mk().lit_expr(mk().int_lit(1, "")); - let cmp = BinOp::Eq(Default::default()); - let zeros = mk().method_call_expr(x.clone(), "trailing_zeros", vec![]); - let zeros_cast = mk().cast_expr(zeros, mk().path_ty(vec!["i32"])); - let zeros_plus1 = mk().binary_expr(add, zeros_cast, one); - let block = mk().block(vec![mk().expr_stmt(zero.clone())]); - let cond = mk().binary_expr(cmp, x, zero); - - mk().ifte_expr(cond, block, Some(zeros_plus1)) + let zero = mk().lit_expr(mk().int_unsuffixed_lit(0)); + let one = mk().lit_expr(mk().int_unsuffixed_lit(1)); + + Ok(val.map(|val| { + let cond = + mk().binary_expr(BinOp::Eq(Default::default()), val.clone(), zero.clone()); + let zeros_plus1 = mk().binary_expr( + BinOp::Add(Default::default()), + mk().cast_expr( + mk().method_call_expr(val, "trailing_zeros", vec![]), + mk().path_ty(vec!["i32"]), + ), + one, + ); + + mk().ifte_expr( + cond, + mk().block(vec![mk().expr_stmt(zero)]), + Some(zeros_plus1), + ) })) } "__builtin_clz" | "__builtin_clzl" | "__builtin_clzll" => { @@ -158,41 +165,48 @@ impl<'c> Translation<'c> { Ok(val.map(|x| mk().method_call_expr(x, "swap_bytes", vec![]))) } "__builtin_fabs" | "__builtin_fabsf" | "__builtin_fabsl" => { - self.import_num_traits(args[0])?; - let val = self.convert_expr(ctx.used(), args[0], None)?; + self.import_num_traits(args[0])?; Ok(val.map(|x| mk().method_call_expr(x, "abs", vec![]))) } "__builtin_isfinite" | "__builtin_isnan" => { - self.import_num_traits(args[0])?; - let val = self.convert_expr(ctx.used(), args[0], None)?; + self.import_num_traits(args[0])?; let seg = match builtin_name { "__builtin_isfinite" => "is_finite", "__builtin_isnan" => "is_nan", _ => panic!(), }; + Ok(val.map(|x| { let call = mk().method_call_expr(x, seg, vec![]); mk().cast_expr(call, mk().path_ty(vec!["i32"])) })) } "__builtin_isinf_sign" => { + let val = self.convert_expr(ctx.used(), args[0], None)?; + + let zero = mk().lit_expr(mk().int_unsuffixed_lit(0)); + let one = mk().lit_expr(mk().int_unsuffixed_lit(1)); + let minus_one = neg_expr(mk().lit_expr(mk().int_unsuffixed_lit(1))); self.import_num_traits(args[0])?; - // isinf_sign(x) -> fabs(x) == infinity ? (signbit(x) ? -1 : 1) : 0 - let val = self.convert_expr(ctx.used(), args[0], None)?; - Ok(val.map(|x| { - let inner_cond = mk().method_call_expr(x.clone(), "is_sign_positive", vec![]); - let one = mk().lit_expr(mk().int_lit(1, "")); - let minus_one = neg_expr(mk().lit_expr(mk().int_lit(1, ""))); - let one_block = mk().block(vec![mk().expr_stmt(one)]); - let inner_ifte = mk().ifte_expr(inner_cond, one_block, Some(minus_one)); - let zero = mk().lit_expr(mk().int_lit(0, "")); - let outer_cond = mk().method_call_expr(x, "is_infinite", vec![]); - let inner_ifte_block = mk().block(vec![mk().expr_stmt(inner_ifte)]); - mk().ifte_expr(outer_cond, inner_ifte_block, Some(zero)) + Ok(val.map(|val| { + let outer_cond = mk().method_call_expr(val.clone(), "is_infinite", vec![]); + let inner_cond = mk().method_call_expr(val, "is_sign_positive", vec![]); + let inner_ifte = mk().ifte_expr( + inner_cond, + mk().block(vec![mk().expr_stmt(one)]), + Some(minus_one), + ); + + // isinf_sign(x) -> fabs(x) == infinity ? (signbit(x) ? -1 : 1) : 0 + mk().ifte_expr( + outer_cond, + mk().block(vec![mk().expr_stmt(inner_ifte)]), + Some(zero), + ) })) } "__builtin_flt_rounds" => { @@ -295,7 +309,9 @@ impl<'c> Translation<'c> { // Should be safe to always return 0 here. "A return of 0 does not indicate that the // value is *not* a constant, but merely that GCC cannot prove it is a constant with // the specified value of the -O option. " - "__builtin_constant_p" => Ok(WithStmts::new_val(mk().lit_expr(mk().int_lit(0, "")))), + "__builtin_constant_p" => Ok(WithStmts::new_val( + mk().lit_expr(mk().int_unsuffixed_lit(0)), + )), "__builtin_object_size" => { // We can't convert this to Rust, but it should be safe to always return -1/0 @@ -303,112 +319,120 @@ impl<'c> Translation<'c> { // `(if (type & 2) == 0 { -1isize } else { 0isize }) as libc::size_t` let ptr_arg = self.convert_expr(ctx.unused(), args[0], None)?; let type_arg = self.convert_expr(ctx.used(), args[1], None)?; - Ok(ptr_arg.and_then(|_| { - type_arg.map(|type_arg| { - let type_and_2 = mk().binary_expr( - BinOp::BitAnd(Default::default()), - type_arg, - mk().lit_expr(mk().int_lit(2, "")), - ); - let if_cond = mk().binary_expr( - BinOp::Eq(Default::default()), - type_and_2, - mk().lit_expr(mk().int_lit(0, "")), - ); - let minus_one = neg_expr(mk().lit_expr(mk().int_lit(1, "isize"))); - let if_expr = mk().ifte_expr( - if_cond, - mk().block(vec![mk().expr_stmt(minus_one)]), - Some(mk().lit_expr(mk().int_lit(0, "isize"))), - ); - self.use_crate(ExternCrate::Libc); - let size_t = mk().abs_path_ty(vec!["libc", "size_t"]); - mk().cast_expr(if_expr, size_t) - }) + + let zero = mk().lit_expr(mk().int_lit(0, "isize")); + let minus_one = neg_expr(mk().lit_expr(mk().int_lit(1, "isize"))); + + Ok(ptr_arg.zip(type_arg).map(|(_ptr_arg, type_arg)| { + let type_and_2 = mk().binary_expr( + BinOp::BitAnd(Default::default()), + type_arg, + mk().lit_expr(mk().int_unsuffixed_lit(2)), + ); + let if_cond = mk().binary_expr( + BinOp::Eq(Default::default()), + type_and_2, + mk().lit_expr(mk().int_unsuffixed_lit(0)), + ); + let if_expr = mk().ifte_expr( + if_cond, + mk().block(vec![mk().expr_stmt(minus_one)]), + Some(zero), + ); + self.use_crate(ExternCrate::Libc); + let size_t = mk().abs_path_ty(vec!["libc", "size_t"]); + mk().cast_expr(if_expr, size_t) })) } "__builtin_va_start" => { - if ctx.is_unused() && args.len() == 2 { - if let Some(va_id) = self.match_vastart(args[0]) { - if self.ast_context.get_decl(&va_id).is_some() { - let dst = self.convert_expr(ctx.used(), args[0], None)?; - let fn_ctx = self.function_context.borrow(); - let src = fn_ctx.get_va_list_arg_name(); - - let call_expr = - mk().method_call_expr(mk().ident_expr(src), "clone", vec![]); - let assign_expr = mk().assign_expr(dst.to_expr(), call_expr); - let stmt = mk().semi_stmt(assign_expr); - - return Ok(WithStmts::new( - vec![stmt], - self.panic_or_err("va_start stub"), - )); - } - } + if ctx.is_used() + || args.len() != 2 + || self + .match_vastart(args[0]) + .map_or(true, |va_id| self.ast_context.get_decl(&va_id).is_none()) + { + return Err(TranslationError::generic("Unsupported va_start")); } - Err(TranslationError::generic("Unsupported va_start")) + + let dst = self.convert_expr(ctx.used(), args[0], None)?; + let fn_ctx = self.function_context.borrow(); + let src = fn_ctx.get_va_list_arg_name(); + + let call_expr = mk().method_call_expr(mk().ident_expr(src), "clone", vec![]); + let assign_expr = mk().assign_expr(dst.to_expr(), call_expr); + let stmt = mk().semi_stmt(assign_expr); + + Ok(WithStmts::new( + vec![stmt], + self.panic_or_err("va_start stub"), + )) } "__builtin_va_copy" => { - if ctx.is_unused() && args.len() == 2 { - if let Some((_dst_va_id, _src_va_id)) = self.match_vacopy(args[0], args[1]) { - let dst = self.convert_expr(ctx.used(), args[0], None)?; - let src = self.convert_expr(ctx.used(), args[1], None)?; - - let call_expr = mk().method_call_expr(src.to_expr(), "clone", vec![]); - let assign_expr = mk().assign_expr(dst.to_expr(), call_expr); - let stmt = mk().semi_stmt(assign_expr); - - return Ok(WithStmts::new( - vec![stmt], - self.panic_or_err("va_copy stub"), - )); - } + if ctx.is_used() || args.len() != 2 || self.match_vacopy(args[0], args[1]).is_none() + { + return Err(TranslationError::generic("Unsupported va_copy")); } - Err(TranslationError::generic("Unsupported va_copy")) + + let dst = self.convert_expr(ctx.used(), args[0], None)?; + let src = self.convert_expr(ctx.used(), args[1], None)?; + + let call_expr = mk().method_call_expr(src.to_expr(), "clone", vec![]); + let assign_expr = mk().assign_expr(dst.to_expr(), call_expr); + let stmt = mk().semi_stmt(assign_expr); + + Ok(WithStmts::new( + vec![stmt], + self.panic_or_err("va_copy stub"), + )) } "__builtin_va_end" => { - if ctx.is_unused() && args.len() == 1 { - if let Some(_va_id) = self.match_vaend(args[0]) { - // nothing to do since the translated Rust `va_list` values get `Drop`'ed. - return Ok(WithStmts::new_val(self.panic("va_end stub"))); - } + if ctx.is_used() || args.len() != 1 || self.match_vaend(args[0]).is_none() { + return Err(TranslationError::generic("Unsupported va_end")); } - Err(TranslationError::generic("Unsupported va_end")) + + // nothing to do since the translated Rust `va_list` values get `Drop`'ed. + Ok(WithStmts::new_val(self.panic("va_end stub"))) } "__builtin_alloca" => { let count = self.convert_expr(ctx.used(), args[0], None)?; - Ok(count.and_then(|count| { - // Get `alloca` allocation storage. - let mut fn_ctx = self.function_context.borrow_mut(); + + // Get `alloca` allocation storage. + let mut fn_ctx = self.function_context.borrow_mut(); + let alloca_allocations_ident = { let alloca_allocations_name = - &*fn_ctx.alloca_allocations_name.get_or_insert_with(|| { + fn_ctx.alloca_allocations_name.get_or_insert_with(|| { self.renamer .borrow_mut() .pick_name("c2rust_alloca_allocations") }); + mk().ident_expr(alloca_allocations_name.as_str()) + }; + + // c2rust_alloca_allocations.last_mut().unwrap().as_mut_ptr() as *mut ::core::ffi::c_void + let expr = mk().method_chain_expr( + alloca_allocations_ident.clone(), + vec![ + (mk().path_segment("last_mut"), vec![]), + (mk().path_segment("unwrap"), vec![]), + (mk().path_segment("as_mut_ptr"), vec![]), + ], + ); + let pointee_ty = mk().abs_path_ty(vec!["core", "ffi", "c_void"]); + let expr = mk().cast_expr(expr, mk().mutbl().ptr_ty(pointee_ty)); + Ok(count.and_then(|count| { // c2rust_alloca_allocations.push(std::vec::from_elem(0, count)); let init_expr = vec_expr( mk().lit_expr(mk().int_unsuffixed_lit(0)), cast_int(count, "usize", false), ); let push_stmt = mk().semi_stmt(mk().method_call_expr( - mk().ident_expr(alloca_allocations_name), + alloca_allocations_ident.clone(), "push", vec![init_expr], )); - - // c2rust_alloca_allocations.last_mut().unwrap().as_mut_ptr() as *mut ::core::ffi::c_void - let expr = mk().ident_expr(alloca_allocations_name); - let expr = mk().method_call_expr(expr, "last_mut", vec![]); - let expr = mk().method_call_expr(expr, "unwrap", vec![]); - let expr = mk().method_call_expr(expr, "as_mut_ptr", vec![]); - let pointee_ty = mk().abs_path_ty(vec!["core", "ffi", "c_void"]); - let expr = mk().cast_expr(expr, mk().mutbl().ptr_ty(pointee_ty)); - WithStmts::new(vec![push_stmt], expr) })) } @@ -553,7 +577,7 @@ impl<'c> Translation<'c> { let atomic_func = self.atomic_intrinsic_expr("store", &[Release]); let arg0 = self.convert_expr(ctx.used(), args[0], None)?; arg0.and_then_try(|arg0| { - let zero = mk().lit_expr(mk().int_lit(0, "")); + let zero = mk().lit_expr(mk().int_unsuffixed_lit(0)); let call_expr = mk().call_expr(atomic_func, vec![arg0, zero]); self.convert_side_effects_expr( ctx, From 086ea42176d83b33409a158f096f77306eea2d07 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 17 Jun 2026 18:09:54 +0200 Subject: [PATCH 3/6] transpile: Override `__builtin_object_size` call result type to `Size` --- c2rust-transpile/src/c_ast/mod.rs | 27 +++++++++++++++++++++ c2rust-transpile/src/translator/builtins.rs | 9 +++---- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index 34946d14aa..5ae6ec1d5d 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1375,6 +1375,33 @@ impl TypedAstContext { self.ast_context, self.ast_context.c_exprs[&e].kind.get_qual_type().unwrap(), ), + CExprKind::Call(result_type_id, callee_id, _) => { + let CExprKind::ImplicitCast(_, callee_id, CastKind::BuiltinFnToFnPtr, _, _) = + self.ast_context.index_unwrap_parens(callee_id).kind + else { + return; + }; + let CExprKind::DeclRef(_, decl_id, _) = + self.ast_context.index_unwrap_parens(callee_id).kind + else { + return; + }; + let CDeclKind::Function { ref name, .. } = self.ast_context[decl_id].kind + else { + return; + }; + + match name.as_str() { + "__builtin_object_size" => { + let type_id = self + .ast_context + .type_for_kind(&CTypeKind::Size) + .expect("CTypeKind::Size should be size_t"); + Some(result_type_id.with_ctype(type_id)) + } + _ => None, + } + } CExprKind::Paren(_ty, e) => self.ast_context.c_exprs[&e].kind.get_qual_type(), CExprKind::UnaryType(_, op, _, _) => { // All of these `CUnTypeOp`s should return `size_t`. diff --git a/c2rust-transpile/src/translator/builtins.rs b/c2rust-transpile/src/translator/builtins.rs index ad0856d46b..6dbb08e1fc 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -316,7 +316,7 @@ impl<'c> Translation<'c> { "__builtin_object_size" => { // We can't convert this to Rust, but it should be safe to always return -1/0 // (depending on the value of `type`), so we emit the following: - // `(if (type & 2) == 0 { -1isize } else { 0isize }) as libc::size_t` + // `(if (type & 2) == 0 { -1isize } else { 0isize })` let ptr_arg = self.convert_expr(ctx.unused(), args[0], None)?; let type_arg = self.convert_expr(ctx.used(), args[1], None)?; @@ -334,14 +334,11 @@ impl<'c> Translation<'c> { type_and_2, mk().lit_expr(mk().int_unsuffixed_lit(0)), ); - let if_expr = mk().ifte_expr( + mk().ifte_expr( if_cond, mk().block(vec![mk().expr_stmt(minus_one)]), Some(zero), - ); - self.use_crate(ExternCrate::Libc); - let size_t = mk().abs_path_ty(vec!["libc", "size_t"]); - mk().cast_expr(if_expr, size_t) + ) })) } From 2ac8c7cff336d99f31085570f2f8705d2fcc0783 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 17 Jun 2026 14:24:07 +0200 Subject: [PATCH 4/6] transpile: Pass result type to `convert_builtin` --- c2rust-transpile/src/translator/builtins.rs | 47 ++++++++++++------- c2rust-transpile/src/translator/functions.rs | 2 +- ...__transpile@f128.c.2021.linux.clang15.snap | 12 ++--- ...__transpile@f128.c.2024.linux.clang15.snap | 16 +++++-- ...hots__transpile@macros.c.2021.clang15.snap | 13 ++--- ...hots__transpile@macros.c.2024.clang15.snap | 13 ++--- 6 files changed, 62 insertions(+), 41 deletions(-) diff --git a/c2rust-transpile/src/translator/builtins.rs b/c2rust-transpile/src/translator/builtins.rs index 6dbb08e1fc..76a6d863d7 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -49,6 +49,7 @@ impl<'c> Translation<'c> { pub fn convert_builtin( &self, ctx: ExprContext, + result_type_id: CQualTypeId, fexp: CExprId, args: &[CExprId], ) -> TranslationResult>> { @@ -114,15 +115,17 @@ impl<'c> Translation<'c> { } "__builtin_signbit" | "__builtin_signbitf" | "__builtin_signbitl" => { let val = self.convert_expr(ctx.used(), args[0], None)?; + let result_type_rs = self.convert_type(result_type_id.ctype)?; self.import_num_traits(args[0])?; Ok(val.map(|v| { let val = mk().method_call_expr(v, "is_sign_negative", vec![]); - mk().cast_expr(val, mk().abs_path_ty(vec!["core", "ffi", "c_int"])) + mk().cast_expr(val, result_type_rs) })) } "__builtin_ffs" | "__builtin_ffsl" | "__builtin_ffsll" => { let val = self.convert_expr(ctx.used(), args[0], None)?; + let result_type_rs = self.convert_type(result_type_id.ctype)?; let zero = mk().lit_expr(mk().int_unsuffixed_lit(0)); let one = mk().lit_expr(mk().int_unsuffixed_lit(1)); @@ -134,7 +137,7 @@ impl<'c> Translation<'c> { BinOp::Add(Default::default()), mk().cast_expr( mk().method_call_expr(val, "trailing_zeros", vec![]), - mk().path_ty(vec!["i32"]), + result_type_rs, ), one, ); @@ -148,16 +151,20 @@ impl<'c> Translation<'c> { } "__builtin_clz" | "__builtin_clzl" | "__builtin_clzll" => { let val = self.convert_expr(ctx.used(), args[0], None)?; + let result_type_rs = self.convert_type(result_type_id.ctype)?; + Ok(val.map(|x| { let zeros = mk().method_call_expr(x, "leading_zeros", vec![]); - mk().cast_expr(zeros, mk().path_ty(vec!["i32"])) + mk().cast_expr(zeros, result_type_rs) })) } "__builtin_ctz" | "__builtin_ctzl" | "__builtin_ctzll" => { let val = self.convert_expr(ctx.used(), args[0], None)?; + let result_type_rs = self.convert_type(result_type_id.ctype)?; + Ok(val.map(|x| { let zeros = mk().method_call_expr(x, "trailing_zeros", vec![]); - mk().cast_expr(zeros, mk().path_ty(vec!["i32"])) + mk().cast_expr(zeros, result_type_rs) })) } "__builtin_bswap16" | "__builtin_bswap32" | "__builtin_bswap64" => { @@ -178,18 +185,19 @@ impl<'c> Translation<'c> { "__builtin_isnan" => "is_nan", _ => panic!(), }; + let result_type_rs = self.convert_type(result_type_id.ctype)?; Ok(val.map(|x| { let call = mk().method_call_expr(x, seg, vec![]); - mk().cast_expr(call, mk().path_ty(vec!["i32"])) + mk().cast_expr(call, result_type_rs) })) } "__builtin_isinf_sign" => { let val = self.convert_expr(ctx.used(), args[0], None)?; - let zero = mk().lit_expr(mk().int_unsuffixed_lit(0)); - let one = mk().lit_expr(mk().int_unsuffixed_lit(1)); - let minus_one = neg_expr(mk().lit_expr(mk().int_unsuffixed_lit(1))); + let zero = self.mk_int_lit(result_type_id, 0, IntBase::Dec, false)?; + let one = self.mk_int_lit(result_type_id, 1, IntBase::Dec, false)?; + let minus_one = self.mk_int_lit(result_type_id, 1, IntBase::Dec, true)?; self.import_num_traits(args[0])?; Ok(val.map(|val| { @@ -213,15 +221,18 @@ impl<'c> Translation<'c> { // LLVM simply lowers this to the constant one which means // that floats are rounded to the nearest number. // https://github.com/llvm-mirror/llvm/blob/master/lib/CodeGen/IntrinsicLowering.cpp#L470 - Ok(WithStmts::new_val(mk().lit_expr(mk().int_lit(1, "i32")))) + self.mk_int_lit(result_type_id, 1, IntBase::Dec, false) + .map(WithStmts::new_val) } "__builtin_expect" => self.convert_expr(ctx.used(), args[0], None), "__builtin_popcount" | "__builtin_popcountl" | "__builtin_popcountll" => { let val = self.convert_expr(ctx.used(), args[0], None)?; + let result_type_rs = self.convert_type(result_type_id.ctype)?; + Ok(val.map(|x| { let zeros = mk().method_call_expr(x, "count_ones", vec![]); - mk().cast_expr(zeros, mk().path_ty(vec!["i32"])) + mk().cast_expr(zeros, result_type_rs) })) } "__builtin_bzero" => { @@ -309,19 +320,19 @@ impl<'c> Translation<'c> { // Should be safe to always return 0 here. "A return of 0 does not indicate that the // value is *not* a constant, but merely that GCC cannot prove it is a constant with // the specified value of the -O option. " - "__builtin_constant_p" => Ok(WithStmts::new_val( - mk().lit_expr(mk().int_unsuffixed_lit(0)), - )), + "__builtin_constant_p" => self + .mk_int_lit(result_type_id, 0, IntBase::Dec, false) + .map(WithStmts::new_val), "__builtin_object_size" => { // We can't convert this to Rust, but it should be safe to always return -1/0 // (depending on the value of `type`), so we emit the following: - // `(if (type & 2) == 0 { -1isize } else { 0isize })` + // `(if (type & 2) == 0 { -1 as ? } else { 0 as ? })` let ptr_arg = self.convert_expr(ctx.unused(), args[0], None)?; let type_arg = self.convert_expr(ctx.used(), args[1], None)?; - let zero = mk().lit_expr(mk().int_lit(0, "isize")); - let minus_one = neg_expr(mk().lit_expr(mk().int_lit(1, "isize"))); + let zero = self.mk_int_lit(result_type_id, 0, IntBase::Dec, false)?; + let minus_one = self.mk_int_lit(result_type_id, 1, IntBase::Dec, true)?; Ok(ptr_arg.zip(type_arg).map(|(_ptr_arg, type_arg)| { let type_and_2 = mk().binary_expr( @@ -416,8 +427,8 @@ impl<'c> Translation<'c> { (mk().path_segment("as_mut_ptr"), vec![]), ], ); - let pointee_ty = mk().abs_path_ty(vec!["core", "ffi", "c_void"]); - let expr = mk().cast_expr(expr, mk().mutbl().ptr_ty(pointee_ty)); + let result_type_rs = self.convert_type(result_type_id.ctype)?; + let expr = mk().cast_expr(expr, result_type_rs); Ok(count.and_then(|count| { // c2rust_alloca_allocations.push(std::vec::from_elem(0, count)); diff --git a/c2rust-transpile/src/translator/functions.rs b/c2rust-transpile/src/translator/functions.rs index bedff5e479..e8928b6836 100644 --- a/c2rust-transpile/src/translator/functions.rs +++ b/c2rust-transpile/src/translator/functions.rs @@ -397,7 +397,7 @@ impl<'c> Translation<'c> { // Builtin function call CExprKind::ImplicitCast(_, fexp, CastKind::BuiltinFnToFnPtr, _, _) => { - return self.convert_builtin(ctx, fexp, args); + return self.convert_builtin(ctx, call_expr_ty, fexp, args); } // Function pointer call diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2021.linux.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2021.linux.clang15.snap index ebc831a1b0..71694fdd05 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2021.linux.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2021.linux.clang15.snap @@ -24,12 +24,12 @@ pub unsafe extern "C" fn long_double_test() { let mut cast_from_float: ::f128::f128 = ::f128::f128::new(f); let mut is_inf: bool = if huge.is_infinite() { if huge.is_sign_positive() { - 1 + 1 as ::core::ffi::c_int } else { - -1 + -1 as ::core::ffi::c_int } } else { - 0 + 0 as ::core::ffi::c_int } != 0; if one != ::f128::f128::ZERO { let mut dummy: ::core::ffi::c_int = 0; @@ -48,12 +48,12 @@ pub unsafe extern "C" fn float128_test() { let mut ld_from_float128: ::f128::f128 = one; let mut is_inf: bool = if huge.is_infinite() { if huge.is_sign_positive() { - 1 + 1 as ::core::ffi::c_int } else { - -1 + -1 as ::core::ffi::c_int } } else { - 0 + 0 as ::core::ffi::c_int } != 0; if one != ::f128::f128::ZERO { let mut dummy: ::core::ffi::c_int = 0; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2024.linux.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2024.linux.clang15.snap index fb3a4deb80..3f3f9a7c56 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2024.linux.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@f128.c.2024.linux.clang15.snap @@ -24,9 +24,13 @@ pub unsafe extern "C" fn long_double_test() { let mut cast_from_int: ::f128::f128 = ::f128::f128::new(i); let mut cast_from_float: ::f128::f128 = ::f128::f128::new(f); let mut is_inf: bool = if huge.is_infinite() { - if huge.is_sign_positive() { 1 } else { -1 } + if huge.is_sign_positive() { + 1 as ::core::ffi::c_int + } else { + -1 as ::core::ffi::c_int + } } else { - 0 + 0 as ::core::ffi::c_int } != 0; if one != ::f128::f128::ZERO { let mut dummy: ::core::ffi::c_int = 0; @@ -44,9 +48,13 @@ pub unsafe extern "C" fn float128_test() { let mut cast_from_float: ::f128::f128 = ::f128::f128::new(f); let mut ld_from_float128: ::f128::f128 = one; let mut is_inf: bool = if huge.is_infinite() { - if huge.is_sign_positive() { 1 } else { -1 } + if huge.is_sign_positive() { + 1 as ::core::ffi::c_int + } else { + -1 as ::core::ffi::c_int + } } else { - 0 + 0 as ::core::ffi::c_int } != 0; if one != ::f128::f128::ZERO { let mut dummy: ::core::ffi::c_int = 0; 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 b059b1a47f..069c7dfaa9 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 @@ -103,7 +103,7 @@ pub unsafe extern "C" fn local_muts() { let mut str_concatenation: [::core::ffi::c_char; 18] = ::core::mem::transmute::<[u8; 18], [::core::ffi::c_char; 18]>(*b"hello hello world\0"); let mut builtin: ::core::ffi::c_int = - (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as ::core::ffi::c_int; let mut ref_indexing: *const ::core::ffi::c_char = NESTED_STR .as_ptr() .offset(LITERAL_FLOAT as ::core::ffi::c_int as isize) @@ -117,7 +117,7 @@ pub unsafe extern "C" fn local_muts() { let mut member: ::core::ffi::c_int = LITERAL_STRUCT.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; + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as ::core::ffi::c_int; let mut indexing_0: ::core::ffi::c_char = NESTED_STR[LITERAL_FLOAT as ::core::ffi::c_int as usize]; let mut mixed: ::core::ffi::c_float = (LITERAL_INT as ::core::ffi::c_double @@ -166,7 +166,8 @@ pub unsafe extern "C" fn local_consts() { b"hello hello world\0".as_ptr() as *const ::core::ffi::c_char; let str_concatenation: [::core::ffi::c_char; 18] = ::core::mem::transmute::<[u8; 18], [::core::ffi::c_char; 18]>(*b"hello hello world\0"); - let builtin: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + let builtin: ::core::ffi::c_int = + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as ::core::ffi::c_int; let ref_indexing: *const ::core::ffi::c_char = NESTED_STR .as_ptr() .offset(LITERAL_FLOAT as ::core::ffi::c_int as isize) @@ -180,7 +181,7 @@ pub unsafe extern "C" fn local_consts() { let member: ::core::ffi::c_int = LITERAL_STRUCT.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; + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as ::core::ffi::c_int; let mut indexing_0: ::core::ffi::c_char = NESTED_STR[LITERAL_FLOAT as ::core::ffi::c_int as usize]; let mut mixed: ::core::ffi::c_float = (LITERAL_INT as ::core::ffi::c_double @@ -235,7 +236,7 @@ static mut global_static_const_str_concatenation: [::core::ffi::c_char; 18] = un ::core::mem::transmute::<[u8; 18], [::core::ffi::c_char; 18]>(*b"hello hello world\0") }; static mut global_static_const_builtin: ::core::ffi::c_int = - (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as ::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 = &LITERAL_STRUCT as *const S as *mut S; @@ -309,7 +310,7 @@ pub static mut global_const_str_concatenation: [::core::ffi::c_char; 18] = unsaf }; #[no_mangle] pub static mut global_const_builtin: ::core::ffi::c_int = - (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as ::core::ffi::c_int; #[no_mangle] pub static mut global_const_ref_indexing: *const ::core::ffi::c_char = ::core::ptr::null::<::core::ffi::c_char>(); 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 1541727a10..2e64c8b830 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 @@ -103,7 +103,7 @@ pub unsafe extern "C" fn local_muts() { let mut str_concatenation: [::core::ffi::c_char; 18] = ::core::mem::transmute::<[u8; 18], [::core::ffi::c_char; 18]>(*b"hello hello world\0"); let mut builtin: ::core::ffi::c_int = - (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as ::core::ffi::c_int; let mut ref_indexing: *const ::core::ffi::c_char = NESTED_STR .as_ptr() .offset(LITERAL_FLOAT as ::core::ffi::c_int as isize) @@ -117,7 +117,7 @@ pub unsafe extern "C" fn local_muts() { let mut member: ::core::ffi::c_int = LITERAL_STRUCT.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; + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as ::core::ffi::c_int; let mut indexing_0: ::core::ffi::c_char = NESTED_STR[LITERAL_FLOAT as ::core::ffi::c_int as usize]; let mut mixed: ::core::ffi::c_float = (LITERAL_INT as ::core::ffi::c_double @@ -166,7 +166,8 @@ pub unsafe extern "C" fn local_consts() { b"hello hello world\0".as_ptr() as *const ::core::ffi::c_char; let str_concatenation: [::core::ffi::c_char; 18] = ::core::mem::transmute::<[u8; 18], [::core::ffi::c_char; 18]>(*b"hello hello world\0"); - let builtin: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + let builtin: ::core::ffi::c_int = + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as ::core::ffi::c_int; let ref_indexing: *const ::core::ffi::c_char = NESTED_STR .as_ptr() .offset(LITERAL_FLOAT as ::core::ffi::c_int as isize) @@ -180,7 +181,7 @@ pub unsafe extern "C" fn local_consts() { let member: ::core::ffi::c_int = LITERAL_STRUCT.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; + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as ::core::ffi::c_int; let mut indexing_0: ::core::ffi::c_char = NESTED_STR[LITERAL_FLOAT as ::core::ffi::c_int as usize]; let mut mixed: ::core::ffi::c_float = (LITERAL_INT as ::core::ffi::c_double @@ -235,7 +236,7 @@ static mut global_static_const_str_concatenation: [::core::ffi::c_char; 18] = un ::core::mem::transmute::<[u8; 18], [::core::ffi::c_char; 18]>(*b"hello hello world\0") }; static mut global_static_const_builtin: ::core::ffi::c_int = - (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as ::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 = &LITERAL_STRUCT as *const S as *mut S; @@ -309,7 +310,7 @@ pub static mut global_const_str_concatenation: [::core::ffi::c_char; 18] = unsaf }; #[unsafe(no_mangle)] pub static mut global_const_builtin: ::core::ffi::c_int = - (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as ::core::ffi::c_int; #[unsafe(no_mangle)] pub static mut global_const_ref_indexing: *const ::core::ffi::c_char = ::core::ptr::null::<::core::ffi::c_char>(); From 5e8aef2609674a66e5ec592b274fe5ee42dea4c4 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 17 Jun 2026 14:55:04 +0200 Subject: [PATCH 5/6] transpile: Lift `Ok` out of `match` in `convert_builtin` --- c2rust-transpile/src/translator/builtins.rs | 177 +++++++++----------- 1 file changed, 82 insertions(+), 95 deletions(-) diff --git a/c2rust-transpile/src/translator/builtins.rs b/c2rust-transpile/src/translator/builtins.rs index 76a6d863d7..54975f10d2 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -73,55 +73,45 @@ impl<'c> Translation<'c> { } }; - match builtin_name { - "__builtin_huge_valf" => Ok(WithStmts::new_val( - mk().abs_path_expr(vec!["core", "f32", "INFINITY"]), - )), - "__builtin_huge_val" => Ok(WithStmts::new_val( - mk().abs_path_expr(vec!["core", "f64", "INFINITY"]), - )), + let val = match builtin_name { + "__builtin_huge_valf" => { + WithStmts::new_val(mk().abs_path_expr(vec!["core", "f32", "INFINITY"])) + } + "__builtin_huge_val" => { + WithStmts::new_val(mk().abs_path_expr(vec!["core", "f64", "INFINITY"])) + } "__builtin_huge_vall" => { self.use_crate(ExternCrate::F128); - Ok(WithStmts::new_val( - mk().abs_path_expr(vec!["f128", "f128", "INFINITY"]), - )) + WithStmts::new_val(mk().abs_path_expr(vec!["f128", "f128", "INFINITY"])) + } + "__builtin_inff" => { + WithStmts::new_val(mk().abs_path_expr(vec!["core", "f32", "INFINITY"])) + } + "__builtin_inf" => { + WithStmts::new_val(mk().abs_path_expr(vec!["core", "f64", "INFINITY"])) } - "__builtin_inff" => Ok(WithStmts::new_val( - mk().abs_path_expr(vec!["core", "f32", "INFINITY"]), - )), - "__builtin_inf" => Ok(WithStmts::new_val( - mk().abs_path_expr(vec!["core", "f64", "INFINITY"]), - )), "__builtin_infl" => { self.use_crate(ExternCrate::F128); - Ok(WithStmts::new_val( - mk().abs_path_expr(vec!["f128", "f128", "INFINITY"]), - )) + WithStmts::new_val(mk().abs_path_expr(vec!["f128", "f128", "INFINITY"])) } - "__builtin_nanf" => Ok(WithStmts::new_val( - mk().abs_path_expr(vec!["core", "f32", "NAN"]), - )), - "__builtin_nan" => Ok(WithStmts::new_val( - mk().abs_path_expr(vec!["core", "f64", "NAN"]), - )), + "__builtin_nanf" => WithStmts::new_val(mk().abs_path_expr(vec!["core", "f32", "NAN"])), + "__builtin_nan" => WithStmts::new_val(mk().abs_path_expr(vec!["core", "f64", "NAN"])), "__builtin_nanl" => { self.use_crate(ExternCrate::F128); - Ok(WithStmts::new_val( - mk().abs_path_expr(vec!["f128", "f128", "NAN"]), - )) + WithStmts::new_val(mk().abs_path_expr(vec!["f128", "f128", "NAN"])) } "__builtin_signbit" | "__builtin_signbitf" | "__builtin_signbitl" => { let val = self.convert_expr(ctx.used(), args[0], None)?; let result_type_rs = self.convert_type(result_type_id.ctype)?; self.import_num_traits(args[0])?; - Ok(val.map(|v| { + val.map(|v| { let val = mk().method_call_expr(v, "is_sign_negative", vec![]); mk().cast_expr(val, result_type_rs) - })) + }) } "__builtin_ffs" | "__builtin_ffsl" | "__builtin_ffsll" => { let val = self.convert_expr(ctx.used(), args[0], None)?; @@ -130,7 +120,7 @@ impl<'c> Translation<'c> { let zero = mk().lit_expr(mk().int_unsuffixed_lit(0)); let one = mk().lit_expr(mk().int_unsuffixed_lit(1)); - Ok(val.map(|val| { + val.map(|val| { let cond = mk().binary_expr(BinOp::Eq(Default::default()), val.clone(), zero.clone()); let zeros_plus1 = mk().binary_expr( @@ -147,34 +137,34 @@ impl<'c> Translation<'c> { mk().block(vec![mk().expr_stmt(zero)]), Some(zeros_plus1), ) - })) + }) } "__builtin_clz" | "__builtin_clzl" | "__builtin_clzll" => { let val = self.convert_expr(ctx.used(), args[0], None)?; let result_type_rs = self.convert_type(result_type_id.ctype)?; - Ok(val.map(|x| { + val.map(|x| { let zeros = mk().method_call_expr(x, "leading_zeros", vec![]); mk().cast_expr(zeros, result_type_rs) - })) + }) } "__builtin_ctz" | "__builtin_ctzl" | "__builtin_ctzll" => { let val = self.convert_expr(ctx.used(), args[0], None)?; let result_type_rs = self.convert_type(result_type_id.ctype)?; - Ok(val.map(|x| { + val.map(|x| { let zeros = mk().method_call_expr(x, "trailing_zeros", vec![]); mk().cast_expr(zeros, result_type_rs) - })) + }) } "__builtin_bswap16" | "__builtin_bswap32" | "__builtin_bswap64" => { let val = self.convert_expr(ctx.used(), args[0], None)?; - Ok(val.map(|x| mk().method_call_expr(x, "swap_bytes", vec![]))) + val.map(|x| mk().method_call_expr(x, "swap_bytes", vec![])) } "__builtin_fabs" | "__builtin_fabsf" | "__builtin_fabsl" => { let val = self.convert_expr(ctx.used(), args[0], None)?; self.import_num_traits(args[0])?; - Ok(val.map(|x| mk().method_call_expr(x, "abs", vec![]))) + val.map(|x| mk().method_call_expr(x, "abs", vec![])) } "__builtin_isfinite" | "__builtin_isnan" => { let val = self.convert_expr(ctx.used(), args[0], None)?; @@ -187,10 +177,10 @@ impl<'c> Translation<'c> { }; let result_type_rs = self.convert_type(result_type_id.ctype)?; - Ok(val.map(|x| { + val.map(|x| { let call = mk().method_call_expr(x, seg, vec![]); mk().cast_expr(call, result_type_rs) - })) + }) } "__builtin_isinf_sign" => { let val = self.convert_expr(ctx.used(), args[0], None)?; @@ -200,7 +190,7 @@ impl<'c> Translation<'c> { let minus_one = self.mk_int_lit(result_type_id, 1, IntBase::Dec, true)?; self.import_num_traits(args[0])?; - Ok(val.map(|val| { + val.map(|val| { let outer_cond = mk().method_call_expr(val.clone(), "is_infinite", vec![]); let inner_cond = mk().method_call_expr(val, "is_sign_positive", vec![]); let inner_ifte = mk().ifte_expr( @@ -215,40 +205,39 @@ impl<'c> Translation<'c> { mk().block(vec![mk().expr_stmt(inner_ifte)]), Some(zero), ) - })) + }) } "__builtin_flt_rounds" => { // LLVM simply lowers this to the constant one which means // that floats are rounded to the nearest number. // https://github.com/llvm-mirror/llvm/blob/master/lib/CodeGen/IntrinsicLowering.cpp#L470 - self.mk_int_lit(result_type_id, 1, IntBase::Dec, false) - .map(WithStmts::new_val) + WithStmts::new_val(self.mk_int_lit(result_type_id, 1, IntBase::Dec, false)?) } - "__builtin_expect" => self.convert_expr(ctx.used(), args[0], None), + "__builtin_expect" => self.convert_expr(ctx.used(), args[0], None)?, "__builtin_popcount" | "__builtin_popcountl" | "__builtin_popcountll" => { let val = self.convert_expr(ctx.used(), args[0], None)?; let result_type_rs = self.convert_type(result_type_id.ctype)?; - Ok(val.map(|x| { + val.map(|x| { let zeros = mk().method_call_expr(x, "count_ones", vec![]); mk().cast_expr(zeros, result_type_rs) - })) + }) } "__builtin_bzero" => { let ptr_stmts = self.convert_expr(ctx.used(), args[0], None)?; let n_stmts = self.convert_expr(ctx.used(), args[1], None)?; let write_bytes = mk().abs_path_expr(vec!["core", "ptr", "write_bytes"]); let zero = mk().lit_expr(mk().int_lit(0, "u8")); - Ok(ptr_stmts.and_then(|ptr| { + ptr_stmts.and_then(|ptr| { n_stmts.map(|n| mk().call_expr(write_bytes, vec![ptr, zero, n])) - })) + }) } // If the target does not support data prefetch, the address expression is evaluated if // it includes side effects but no other code is generated and GCC does not issue a warning. // void __builtin_prefetch (const void *addr, ...); - "__builtin_prefetch" => self.convert_expr(ctx.unused(), args[0], None), + "__builtin_prefetch" => self.convert_expr(ctx.unused(), args[0], None)?, "__builtin_memcpy" | "__builtin_memcmp" | "__builtin_memmove" | "__builtin_strncmp" | "__builtin_strncpy" | "__builtin_strncat" => self.convert_libc_fns( @@ -256,27 +245,27 @@ impl<'c> Translation<'c> { ctx, args, &[LibcFnArgType::Mem, LibcFnArgType::Mem, LibcFnArgType::Size], - ), + )?, "__builtin_memchr" | "__builtin_memset" => self.convert_libc_fns( builtin_name, ctx, args, &[LibcFnArgType::Mem, LibcFnArgType::Int, LibcFnArgType::Size], - ), + )?, "__builtin_strchr" | "__builtin_strrchr" => self.convert_libc_fns( builtin_name, ctx, args, &[LibcFnArgType::Mem, LibcFnArgType::Int], - ), + )?, "__builtin_strndup" | "__builtin_strnlen" => self.convert_libc_fns( builtin_name, ctx, args, &[LibcFnArgType::Mem, LibcFnArgType::Size], - ), + )?, "__builtin_strdup" | "__builtin_strlen" => { - self.convert_libc_fns(builtin_name, ctx, args, &[LibcFnArgType::Mem]) + self.convert_libc_fns(builtin_name, ctx, args, &[LibcFnArgType::Mem])? } "__builtin_strcmp" | "__builtin_strcat" | "__builtin_strcpy" | "__builtin_strcspn" | "__builtin_strpbrk" | "__builtin_strspn" | "__builtin_strstr" => self @@ -285,7 +274,7 @@ impl<'c> Translation<'c> { ctx, args, &[LibcFnArgType::Mem, LibcFnArgType::Mem], - ), + )?, "__builtin_add_overflow" | "__builtin_sadd_overflow" @@ -294,7 +283,7 @@ impl<'c> Translation<'c> { | "__builtin_uadd_overflow" | "__builtin_uaddl_overflow" | "__builtin_uaddll_overflow" => { - self.convert_overflow_arith(ctx, "overflowing_add", args) + self.convert_overflow_arith(ctx, "overflowing_add", args)? } "__builtin_sub_overflow" @@ -304,7 +293,7 @@ impl<'c> Translation<'c> { | "__builtin_usub_overflow" | "__builtin_usubl_overflow" | "__builtin_usubll_overflow" => { - self.convert_overflow_arith(ctx, "overflowing_sub", args) + self.convert_overflow_arith(ctx, "overflowing_sub", args)? } "__builtin_mul_overflow" @@ -314,15 +303,15 @@ impl<'c> Translation<'c> { | "__builtin_umul_overflow" | "__builtin_umull_overflow" | "__builtin_umulll_overflow" => { - self.convert_overflow_arith(ctx, "overflowing_mul", args) + self.convert_overflow_arith(ctx, "overflowing_mul", args)? } // Should be safe to always return 0 here. "A return of 0 does not indicate that the // value is *not* a constant, but merely that GCC cannot prove it is a constant with // the specified value of the -O option. " - "__builtin_constant_p" => self - .mk_int_lit(result_type_id, 0, IntBase::Dec, false) - .map(WithStmts::new_val), + "__builtin_constant_p" => { + WithStmts::new_val(self.mk_int_lit(result_type_id, 0, IntBase::Dec, false)?) + } "__builtin_object_size" => { // We can't convert this to Rust, but it should be safe to always return -1/0 @@ -334,7 +323,7 @@ impl<'c> Translation<'c> { let zero = self.mk_int_lit(result_type_id, 0, IntBase::Dec, false)?; let minus_one = self.mk_int_lit(result_type_id, 1, IntBase::Dec, true)?; - Ok(ptr_arg.zip(type_arg).map(|(_ptr_arg, type_arg)| { + ptr_arg.zip(type_arg).map(|(_ptr_arg, type_arg)| { let type_and_2 = mk().binary_expr( BinOp::BitAnd(Default::default()), type_arg, @@ -350,7 +339,7 @@ impl<'c> Translation<'c> { mk().block(vec![mk().expr_stmt(minus_one)]), Some(zero), ) - })) + }) } "__builtin_va_start" => { @@ -371,10 +360,7 @@ impl<'c> Translation<'c> { let assign_expr = mk().assign_expr(dst.to_expr(), call_expr); let stmt = mk().semi_stmt(assign_expr); - Ok(WithStmts::new( - vec![stmt], - self.panic_or_err("va_start stub"), - )) + WithStmts::new(vec![stmt], self.panic_or_err("va_start stub")) } "__builtin_va_copy" => { if ctx.is_used() || args.len() != 2 || self.match_vacopy(args[0], args[1]).is_none() @@ -389,10 +375,7 @@ impl<'c> Translation<'c> { let assign_expr = mk().assign_expr(dst.to_expr(), call_expr); let stmt = mk().semi_stmt(assign_expr); - Ok(WithStmts::new( - vec![stmt], - self.panic_or_err("va_copy stub"), - )) + WithStmts::new(vec![stmt], self.panic_or_err("va_copy stub")) } "__builtin_va_end" => { if ctx.is_used() || args.len() != 1 || self.match_vaend(args[0]).is_none() { @@ -400,7 +383,7 @@ impl<'c> Translation<'c> { } // nothing to do since the translated Rust `va_list` values get `Drop`'ed. - Ok(WithStmts::new_val(self.panic("va_end stub"))) + WithStmts::new_val(self.panic("va_end stub")) } "__builtin_alloca" => { @@ -430,7 +413,7 @@ impl<'c> Translation<'c> { let result_type_rs = self.convert_type(result_type_id.ctype)?; let expr = mk().cast_expr(expr, result_type_rs); - Ok(count.and_then(|count| { + count.and_then(|count| { // c2rust_alloca_allocations.push(std::vec::from_elem(0, count)); let init_expr = vec_expr( mk().lit_expr(mk().int_unsuffixed_lit(0)), @@ -442,7 +425,7 @@ impl<'c> Translation<'c> { vec![init_expr], )); WithStmts::new(vec![push_stmt], expr) - })) + }) } "__builtin_return_address" | "__builtin_frame_address" => { @@ -459,7 +442,7 @@ impl<'c> Translation<'c> { warn!("{builtin_name} has no Rust equivalent; emitting null pointer"); } let level = self.convert_expr(ctx.unused(), args[0], None)?; - Ok(level.and_then(|_| { + level.and_then(|_| { let void_ty = mk().abs_path_ty(vec!["core", "ffi", "c_void"]); let type_args = mk().angle_bracketed_args(vec![void_ty]); let null_expr = mk().call_expr( @@ -471,7 +454,7 @@ impl<'c> Translation<'c> { vec![], ); WithStmts::new_val(null_expr) - })) + }) } "__builtin_extract_return_addr" | "__builtin_frob_return_addr" => { @@ -479,7 +462,7 @@ impl<'c> Translation<'c> { // architectures (only used to mask/unmask hardware-specific // bits like the ARM Thumb mode bit). Pass the argument // through unchanged. - self.convert_expr(ctx, args[0], None) + self.convert_expr(ctx, args[0], None)? } "__builtin_ia32_pause" => { @@ -491,7 +474,7 @@ impl<'c> Translation<'c> { ctx, WithStmts::new_val(call), "Builtin is not supposed to be used", - ) + )? } "__builtin_arm_yield" => { @@ -514,7 +497,7 @@ impl<'c> Translation<'c> { ctx, WithStmts::new_val(call), "Builtin is not supposed to be used", - ) + )? } "__sync_val_compare_and_swap_1" @@ -544,7 +527,7 @@ impl<'c> Translation<'c> { arg2, returns_val, ) - }) + })? } "__sync_synchronize" => { @@ -554,7 +537,7 @@ impl<'c> Translation<'c> { ctx, WithStmts::new_val(call_expr), "Builtin is not supposed to be used", - ) + )? } "__sync_lock_test_and_set_1" @@ -573,7 +556,7 @@ impl<'c> Translation<'c> { WithStmts::new_val(call_expr), "Builtin is not supposed to be used", ) - }) + })? } "__sync_lock_release_1" @@ -592,31 +575,33 @@ impl<'c> Translation<'c> { WithStmts::new_val(call_expr), "Builtin is not supposed to be used", ) - }) + })? } // There's currently no way to replicate this functionality in Rust, so we just // pass the ptr input param in its place. - "__builtin_assume_aligned" => Ok(self.convert_expr(ctx.used(), args[0], None)?), + "__builtin_assume_aligned" => self.convert_expr(ctx.used(), args[0], None)?, // Skip over, there's no way to implement it in Rust - "__builtin_unwind_init" => Ok(WithStmts::new_val(self.panic_or_err("no value"))), - "__builtin_unreachable" => Ok(WithStmts::new( + "__builtin_unwind_init" => WithStmts::new_val(self.panic_or_err("no value")), + "__builtin_unreachable" => WithStmts::new( vec![mk().semi_stmt(mk().mac_expr(mk().mac::>( mk().path(vec!["unreachable"]), vec![], MacroDelimiter::Paren(Default::default()), )))], self.panic_or_err("unreachable stub"), - )), + ), "__builtin_rotateleft8" | "__builtin_rotateleft16" | "__builtin_rotateleft32" - | "__builtin_rotateleft64" => self.convert_builtin_rotate(ctx, args, "rotate_left"), + | "__builtin_rotateleft64" => self.convert_builtin_rotate(ctx, args, "rotate_left")?, "__builtin_rotateright8" | "__builtin_rotateright16" | "__builtin_rotateright32" - | "__builtin_rotateright64" => self.convert_builtin_rotate(ctx, args, "rotate_right"), + | "__builtin_rotateright64" => { + self.convert_builtin_rotate(ctx, args, "rotate_right")? + } _ => { if let Some(atomic_op) = CAtomicBinOp::from_sync_builtin_fn(builtin_name) { @@ -630,18 +615,20 @@ impl<'c> Translation<'c> { .ok_or_else(|| format_err!("bad arg1 type"))?; arg0.zip(arg1).and_then_try(|(arg0, arg1)| { self.convert_atomic_op(ctx, atomic_op, SeqCst, arg0, arg1, arg1_type_id) - }) + })? } else if let Some(fn_name) = simd_fn_from_builtin_fn(builtin_name) { - self.convert_simd_builtin(ctx, fn_name, args) + self.convert_simd_builtin(ctx, fn_name, args)? } else { - Err(format_translation_err!( + return Err(format_translation_err!( self.ast_context.display_loc(src_loc), "Unimplemented builtin {}", builtin_name - )) + )); } } - } + }; + + Ok(val) } fn import_num_traits(&self, arg_id: CExprId) -> TranslationResult<()> { From 7a907b6be5cc2fdb86bf214b59377f01fcde78bf Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 18 Jun 2026 14:07:04 +0200 Subject: [PATCH 6/6] transpile: Cast to expected type in `convert_builtin` --- c2rust-transpile/src/translator/builtins.rs | 85 ++++++++++---------- c2rust-transpile/src/translator/functions.rs | 2 +- 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/c2rust-transpile/src/translator/builtins.rs b/c2rust-transpile/src/translator/builtins.rs index 54975f10d2..abaa04ed24 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -49,6 +49,7 @@ impl<'c> Translation<'c> { pub fn convert_builtin( &self, ctx: ExprContext, + expected_type_id: Option, result_type_id: CQualTypeId, fexp: CExprId, args: &[CExprId], @@ -73,6 +74,8 @@ impl<'c> Translation<'c> { } }; + let mut source_type_id = result_type_id; + let target_type_id = expected_type_id.unwrap_or(result_type_id); let val = match builtin_name { "__builtin_huge_valf" => { WithStmts::new_val(mk().abs_path_expr(vec!["core", "f32", "INFINITY"])) @@ -105,17 +108,14 @@ impl<'c> Translation<'c> { } "__builtin_signbit" | "__builtin_signbitf" | "__builtin_signbitl" => { let val = self.convert_expr(ctx.used(), args[0], None)?; - let result_type_rs = self.convert_type(result_type_id.ctype)?; + source_type_id.ctype = self.ast_context.type_for_kind(&CTypeKind::Bool).unwrap(); self.import_num_traits(args[0])?; - val.map(|v| { - let val = mk().method_call_expr(v, "is_sign_negative", vec![]); - mk().cast_expr(val, result_type_rs) - }) + val.map(|v| mk().method_call_expr(v, "is_sign_negative", vec![])) } "__builtin_ffs" | "__builtin_ffsl" | "__builtin_ffsll" => { let val = self.convert_expr(ctx.used(), args[0], None)?; - let result_type_rs = self.convert_type(result_type_id.ctype)?; + source_type_id.ctype = self.ast_context.type_for_kind(&CTypeKind::UInt32).unwrap(); let zero = mk().lit_expr(mk().int_unsuffixed_lit(0)); let one = mk().lit_expr(mk().int_unsuffixed_lit(1)); @@ -125,10 +125,7 @@ impl<'c> Translation<'c> { mk().binary_expr(BinOp::Eq(Default::default()), val.clone(), zero.clone()); let zeros_plus1 = mk().binary_expr( BinOp::Add(Default::default()), - mk().cast_expr( - mk().method_call_expr(val, "trailing_zeros", vec![]), - result_type_rs, - ), + mk().method_call_expr(val, "trailing_zeros", vec![]), one, ); @@ -141,21 +138,15 @@ impl<'c> Translation<'c> { } "__builtin_clz" | "__builtin_clzl" | "__builtin_clzll" => { let val = self.convert_expr(ctx.used(), args[0], None)?; - let result_type_rs = self.convert_type(result_type_id.ctype)?; + source_type_id.ctype = self.ast_context.type_for_kind(&CTypeKind::UInt32).unwrap(); - val.map(|x| { - let zeros = mk().method_call_expr(x, "leading_zeros", vec![]); - mk().cast_expr(zeros, result_type_rs) - }) + val.map(|x| mk().method_call_expr(x, "leading_zeros", vec![])) } "__builtin_ctz" | "__builtin_ctzl" | "__builtin_ctzll" => { let val = self.convert_expr(ctx.used(), args[0], None)?; - let result_type_rs = self.convert_type(result_type_id.ctype)?; + source_type_id.ctype = self.ast_context.type_for_kind(&CTypeKind::UInt32).unwrap(); - val.map(|x| { - let zeros = mk().method_call_expr(x, "trailing_zeros", vec![]); - mk().cast_expr(zeros, result_type_rs) - }) + val.map(|x| mk().method_call_expr(x, "trailing_zeros", vec![])) } "__builtin_bswap16" | "__builtin_bswap32" | "__builtin_bswap64" => { let val = self.convert_expr(ctx.used(), args[0], None)?; @@ -175,19 +166,17 @@ impl<'c> Translation<'c> { "__builtin_isnan" => "is_nan", _ => panic!(), }; - let result_type_rs = self.convert_type(result_type_id.ctype)?; + source_type_id.ctype = self.ast_context.type_for_kind(&CTypeKind::Bool).unwrap(); - val.map(|x| { - let call = mk().method_call_expr(x, seg, vec![]); - mk().cast_expr(call, result_type_rs) - }) + val.map(|x| mk().method_call_expr(x, seg, vec![])) } "__builtin_isinf_sign" => { let val = self.convert_expr(ctx.used(), args[0], None)?; - let zero = self.mk_int_lit(result_type_id, 0, IntBase::Dec, false)?; - let one = self.mk_int_lit(result_type_id, 1, IntBase::Dec, false)?; - let minus_one = self.mk_int_lit(result_type_id, 1, IntBase::Dec, true)?; + source_type_id = target_type_id; + let zero = self.mk_int_lit(target_type_id, 0, IntBase::Dec, false)?; + let one = self.mk_int_lit(target_type_id, 1, IntBase::Dec, false)?; + let minus_one = self.mk_int_lit(target_type_id, 1, IntBase::Dec, true)?; self.import_num_traits(args[0])?; val.map(|val| { @@ -211,18 +200,16 @@ impl<'c> Translation<'c> { // LLVM simply lowers this to the constant one which means // that floats are rounded to the nearest number. // https://github.com/llvm-mirror/llvm/blob/master/lib/CodeGen/IntrinsicLowering.cpp#L470 - WithStmts::new_val(self.mk_int_lit(result_type_id, 1, IntBase::Dec, false)?) + source_type_id = target_type_id; + WithStmts::new_val(self.mk_int_lit(target_type_id, 1, IntBase::Dec, false)?) } "__builtin_expect" => self.convert_expr(ctx.used(), args[0], None)?, "__builtin_popcount" | "__builtin_popcountl" | "__builtin_popcountll" => { let val = self.convert_expr(ctx.used(), args[0], None)?; - let result_type_rs = self.convert_type(result_type_id.ctype)?; + source_type_id.ctype = self.ast_context.type_for_kind(&CTypeKind::UInt32).unwrap(); - val.map(|x| { - let zeros = mk().method_call_expr(x, "count_ones", vec![]); - mk().cast_expr(zeros, result_type_rs) - }) + val.map(|x| mk().method_call_expr(x, "count_ones", vec![])) } "__builtin_bzero" => { let ptr_stmts = self.convert_expr(ctx.used(), args[0], None)?; @@ -310,7 +297,8 @@ impl<'c> Translation<'c> { // value is *not* a constant, but merely that GCC cannot prove it is a constant with // the specified value of the -O option. " "__builtin_constant_p" => { - WithStmts::new_val(self.mk_int_lit(result_type_id, 0, IntBase::Dec, false)?) + source_type_id = target_type_id; + WithStmts::new_val(self.mk_int_lit(target_type_id, 0, IntBase::Dec, false)?) } "__builtin_object_size" => { @@ -320,8 +308,9 @@ impl<'c> Translation<'c> { let ptr_arg = self.convert_expr(ctx.unused(), args[0], None)?; let type_arg = self.convert_expr(ctx.used(), args[1], None)?; - let zero = self.mk_int_lit(result_type_id, 0, IntBase::Dec, false)?; - let minus_one = self.mk_int_lit(result_type_id, 1, IntBase::Dec, true)?; + source_type_id = target_type_id; + let zero = self.mk_int_lit(target_type_id, 0, IntBase::Dec, false)?; + let minus_one = self.mk_int_lit(target_type_id, 1, IntBase::Dec, true)?; ptr_arg.zip(type_arg).map(|(_ptr_arg, type_arg)| { let type_and_2 = mk().binary_expr( @@ -402,7 +391,7 @@ impl<'c> Translation<'c> { }; // c2rust_alloca_allocations.last_mut().unwrap().as_mut_ptr() as *mut ::core::ffi::c_void - let expr = mk().method_chain_expr( + let mut expr = mk().method_chain_expr( alloca_allocations_ident.clone(), vec![ (mk().path_segment("last_mut"), vec![]), @@ -410,8 +399,22 @@ impl<'c> Translation<'c> { (mk().path_segment("as_mut_ptr"), vec![]), ], ); - let result_type_rs = self.convert_type(result_type_id.ctype)?; - let expr = mk().cast_expr(expr, result_type_rs); + + source_type_id = target_type_id; + let target_type_kind = &self.ast_context.resolve_type(target_type_id.ctype).kind; + let needs_cast = match target_type_kind { + &CTypeKind::Pointer(pointee_type_id) => { + let pointee_type_kind = + &self.ast_context.resolve_type(pointee_type_id.ctype).kind; + !matches!(pointee_type_kind, CTypeKind::UInt8) + } + _ => true, + }; + + if needs_cast { + let target_type_rs = self.convert_type(target_type_id.ctype)?; + expr = mk().cast_expr(expr, target_type_rs); + } count.and_then(|count| { // c2rust_alloca_allocations.push(std::vec::from_elem(0, count)); @@ -628,7 +631,7 @@ impl<'c> Translation<'c> { } }; - Ok(val) + self.convert_cast(ctx, source_type_id, target_type_id, val, None, None, None) } fn import_num_traits(&self, arg_id: CExprId) -> TranslationResult<()> { diff --git a/c2rust-transpile/src/translator/functions.rs b/c2rust-transpile/src/translator/functions.rs index e8928b6836..41812acaf0 100644 --- a/c2rust-transpile/src/translator/functions.rs +++ b/c2rust-transpile/src/translator/functions.rs @@ -397,7 +397,7 @@ impl<'c> Translation<'c> { // Builtin function call CExprKind::ImplicitCast(_, fexp, CastKind::BuiltinFnToFnPtr, _, _) => { - return self.convert_builtin(ctx, call_expr_ty, fexp, args); + return self.convert_builtin(ctx, override_ty, call_expr_ty, fexp, args); } // Function pointer call