From f317c231ce4f742cce072a660839551cf2a0510f Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 17 Jun 2026 12:53:31 +0200 Subject: [PATCH 1/5] 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 1ee2ec74cd..0da1ec2d9e 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -114,30 +114,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" => { @@ -159,41 +166,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" => { @@ -296,7 +310,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 @@ -304,112 +320,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) })) } @@ -591,7 +615,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 306a79c1231fffbb248652c6470c472c9428f078 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 17 Jun 2026 18:09:54 +0200 Subject: [PATCH 2/5] transpile: Override `__builtin_object_size` call result type to `Size` --- c2rust-transpile/src/c_ast/mod.rs | 39 +++++++++++++++++---- c2rust-transpile/src/translator/builtins.rs | 9 ++--- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index 80976ee2bd..77d004f877 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1306,17 +1306,18 @@ impl TypedAstContext { } } - /// Bubble types of unary and binary operators up from their args into the expression type. - /// - /// In Clang 15 and below, the Clang AST resolves typedefs in the expression type of unary and - /// binary expressions. For example, a BinaryExpr node adding two `size_t` expressions will be - /// given an `unsigned long` type rather than the `size_t` typedef type. This behavior changed - /// in Clang 16. This method adjusts AST node types to match those produced by Clang 16 and - /// newer; on these later Clang versions, it should have no effect. + /// Overrides the result types of certain expressions to one of the `PULLBACK_KINDS` where + /// appropriate. Then bubbles those types up to the parent expressions. /// /// This pass is necessary because we reify some typedef types (such as `size_t`) into their own /// distinct Rust types. As such, we need to make sure we know the exact type to generate when /// we translate an expr, not just its resolved type (looking through typedefs). + /// + /// Additionally, in Clang 15 and below, the Clang AST resolves typedefs in the expression type + /// of unary and binary expressions. For example, a BinaryExpr node adding two `size_t` + /// expressions will be given an `unsigned long` type rather than the `size_t` typedef type. + /// This behavior changed in Clang 16. This method adjusts AST node types to match those + /// produced by Clang 16 and newer. pub fn bubble_expr_types(&mut self) { struct BubbleExprTypes<'a> { ast_context: &'a mut TypedAstContext, @@ -1379,6 +1380,30 @@ 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); + 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 0da1ec2d9e..ac917c1a43 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -317,7 +317,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)?; @@ -335,14 +335,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 c5e2c5b481549d6e400d226f5a78931b15e0150f Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 17 Jun 2026 14:24:07 +0200 Subject: [PATCH 3/5] 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 ac917c1a43..4628230c4d 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -50,6 +50,7 @@ impl<'c> Translation<'c> { pub fn convert_builtin( &self, ctx: ExprContext, + result_type_id: CQualTypeId, fexp: CExprId, args: &[CExprId], ) -> TranslationResult>> { @@ -115,15 +116,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)); @@ -135,7 +138,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, ); @@ -149,16 +152,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" => { @@ -179,18 +186,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| { @@ -214,15 +222,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" => { @@ -310,19 +321,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( @@ -417,8 +428,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 0bb9d8faf9..b67f74856b 100644 --- a/c2rust-transpile/src/translator/functions.rs +++ b/c2rust-transpile/src/translator/functions.rs @@ -393,7 +393,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 fe8cd3c510..045589f7ba 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 @@ -119,14 +119,14 @@ pub unsafe extern "C" fn local_muts() { let mut str_concatenation_ptr: *const ::core::ffi::c_char = STR_CONCATENATION.as_ptr(); let mut str_concatenation: [::core::ffi::c_char; 18] = STR_CONCATENATION; let mut builtin: ::core::ffi::c_int = - (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as ::core::ffi::c_int; let mut ref_indexing: *const ::core::ffi::c_char = REF_MACRO; let mut ref_struct: *const S = REF_LITERAL; let mut ternary: ::core::ffi::c_int = TERNARY; let mut member: ::core::ffi::c_int = MEMBER; 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 = INDEXING; let mut mixed: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; @@ -166,14 +166,15 @@ pub unsafe extern "C" fn local_consts() { let indexing: ::core::ffi::c_char = INDEXING; let str_concatenation_ptr: *const ::core::ffi::c_char = STR_CONCATENATION.as_ptr(); let str_concatenation: [::core::ffi::c_char; 18] = STR_CONCATENATION; - let builtin: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + let 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 = REF_MACRO; let ref_struct: *const S = REF_LITERAL; let ternary: ::core::ffi::c_int = TERNARY; let member: ::core::ffi::c_int = MEMBER; 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 = INDEXING; let mut mixed: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; @@ -219,7 +220,7 @@ static mut global_static_const_str_concatenation_ptr: *const ::core::ffi::c_char STR_CONCATENATION.as_ptr(); static mut global_static_const_str_concatenation: [::core::ffi::c_char; 18] = STR_CONCATENATION; 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 = REF_LITERAL; @@ -288,7 +289,7 @@ pub static mut global_const_str_concatenation_ptr: *const ::core::ffi::c_char = pub static mut global_const_str_concatenation: [::core::ffi::c_char; 18] = STR_CONCATENATION; #[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 148cb164a1..d58f43e0e0 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 @@ -119,14 +119,14 @@ pub unsafe extern "C" fn local_muts() { let mut str_concatenation_ptr: *const ::core::ffi::c_char = STR_CONCATENATION.as_ptr(); let mut str_concatenation: [::core::ffi::c_char; 18] = STR_CONCATENATION; let mut builtin: ::core::ffi::c_int = - (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as ::core::ffi::c_int; let mut ref_indexing: *const ::core::ffi::c_char = REF_MACRO; let mut ref_struct: *const S = REF_LITERAL; let mut ternary: ::core::ffi::c_int = TERNARY; let mut member: ::core::ffi::c_int = MEMBER; 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 = INDEXING; let mut mixed: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; @@ -166,14 +166,15 @@ pub unsafe extern "C" fn local_consts() { let indexing: ::core::ffi::c_char = INDEXING; let str_concatenation_ptr: *const ::core::ffi::c_char = STR_CONCATENATION.as_ptr(); let str_concatenation: [::core::ffi::c_char; 18] = STR_CONCATENATION; - let builtin: ::core::ffi::c_int = (LITERAL_INT as ::core::ffi::c_uint).leading_zeros() as i32; + let 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 = REF_MACRO; let ref_struct: *const S = REF_LITERAL; let ternary: ::core::ffi::c_int = TERNARY; let member: ::core::ffi::c_int = MEMBER; 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 = INDEXING; let mut mixed: ::core::ffi::c_float = MIXED_ARITHMETIC as ::core::ffi::c_float; let mut i: ::core::ffi::c_int = 0 as ::core::ffi::c_int; @@ -219,7 +220,7 @@ static mut global_static_const_str_concatenation_ptr: *const ::core::ffi::c_char STR_CONCATENATION.as_ptr(); static mut global_static_const_str_concatenation: [::core::ffi::c_char; 18] = STR_CONCATENATION; 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 = REF_LITERAL; @@ -288,7 +289,7 @@ pub static mut global_const_str_concatenation_ptr: *const ::core::ffi::c_char = pub static mut global_const_str_concatenation: [::core::ffi::c_char; 18] = STR_CONCATENATION; #[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 c0915dc7f0bca8d0f107858a18f05ab261b10ef8 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 17 Jun 2026 14:55:04 +0200 Subject: [PATCH 4/5] transpile: Lift `Ok` out of `match` in `convert_builtin` --- c2rust-transpile/src/translator/builtins.rs | 179 +++++++++----------- 1 file changed, 83 insertions(+), 96 deletions(-) diff --git a/c2rust-transpile/src/translator/builtins.rs b/c2rust-transpile/src/translator/builtins.rs index 4628230c4d..76bcfb453e 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -74,55 +74,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)?; @@ -131,7 +121,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( @@ -148,34 +138,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)?; @@ -188,10 +178,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)?; @@ -201,7 +191,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( @@ -216,40 +206,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( @@ -257,27 +246,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 @@ -286,7 +275,7 @@ impl<'c> Translation<'c> { ctx, args, &[LibcFnArgType::Mem, LibcFnArgType::Mem], - ), + )?, "__builtin_add_overflow" | "__builtin_sadd_overflow" @@ -295,7 +284,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" @@ -305,7 +294,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" @@ -315,15 +304,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 @@ -335,7 +324,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, @@ -351,7 +340,7 @@ impl<'c> Translation<'c> { mk().block(vec![mk().expr_stmt(minus_one)]), Some(zero), ) - })) + }) } "__builtin_va_start" => { @@ -372,10 +361,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() @@ -390,10 +376,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() { @@ -401,7 +384,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" => { @@ -431,7 +414,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)), @@ -443,7 +426,7 @@ impl<'c> Translation<'c> { vec![init_expr], )); WithStmts::new(vec![push_stmt], expr) - })) + }) } "__builtin_return_address" | "__builtin_frame_address" => { @@ -460,7 +443,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( @@ -472,7 +455,7 @@ impl<'c> Translation<'c> { vec![], ); WithStmts::new_val(null_expr) - })) + }) } "__builtin_extract_return_addr" | "__builtin_frob_return_addr" => { @@ -480,7 +463,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" => { @@ -492,7 +475,7 @@ impl<'c> Translation<'c> { ctx, WithStmts::new_val(call), "Builtin is not supposed to be used", - ) + )? } "__builtin_arm_yield" => { @@ -515,7 +498,7 @@ impl<'c> Translation<'c> { ctx, WithStmts::new_val(call), "Builtin is not supposed to be used", - ) + )? } "__sync_val_compare_and_swap_1" @@ -545,7 +528,7 @@ impl<'c> Translation<'c> { arg2, returns_val, ) - }) + })? } "__sync_synchronize" => { @@ -555,7 +538,7 @@ impl<'c> Translation<'c> { ctx, WithStmts::new_val(call_expr), "Builtin is not supposed to be used", - ) + )? } // `__atomic_thread_fence` is a full fence (`atomic_fence`); @@ -592,7 +575,7 @@ impl<'c> Translation<'c> { ctx, WithStmts::new_val(call_expr), "Builtin is not supposed to be used", - ) + )? } "__sync_lock_test_and_set_1" @@ -611,7 +594,7 @@ impl<'c> Translation<'c> { WithStmts::new_val(call_expr), "Builtin is not supposed to be used", ) - }) + })? } "__sync_lock_release_1" @@ -630,31 +613,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) { @@ -668,18 +653,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 04f75f7ba48ad81ea9aa5d0684d078374397669b Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 18 Jun 2026 14:07:04 +0200 Subject: [PATCH 5/5] 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 76bcfb453e..bc54868aac 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -50,6 +50,7 @@ impl<'c> Translation<'c> { pub fn convert_builtin( &self, ctx: ExprContext, + expected_type_id: Option, result_type_id: CQualTypeId, fexp: CExprId, args: &[CExprId], @@ -74,6 +75,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"])) @@ -106,17 +109,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); 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); let zero = mk().lit_expr(mk().int_unsuffixed_lit(0)); let one = mk().lit_expr(mk().int_unsuffixed_lit(1)); @@ -126,10 +126,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, ); @@ -142,21 +139,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); - 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); - 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)?; @@ -176,19 +167,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); - 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| { @@ -212,18 +201,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); - 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)?; @@ -311,7 +298,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" => { @@ -321,8 +309,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( @@ -403,7 +392,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![]), @@ -411,8 +400,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)); @@ -666,7 +669,7 @@ impl<'c> Translation<'c> { } }; - Ok(val) + self.make_cast(ctx, source_type_id, target_type_id, val) } 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 b67f74856b..3203c6690a 100644 --- a/c2rust-transpile/src/translator/functions.rs +++ b/c2rust-transpile/src/translator/functions.rs @@ -393,7 +393,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