From d027ef3e929c093c393dda567c524a0000eaaaaa Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 18 Dec 2025 17:18:03 +0100 Subject: [PATCH 1/4] transpile: Split off new functions for pointer casts --- c2rust-transpile/src/translator/mod.rs | 104 ++-------- c2rust-transpile/src/translator/pointers.rs | 200 +++++++++++++++++++- 2 files changed, 215 insertions(+), 89 deletions(-) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index c014935ccf..2adf4c9468 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -4526,79 +4526,31 @@ impl<'c> Translation<'c> { match kind { CastKind::BitCast | CastKind::NoOp => { - if self.ast_context.is_function_pointer(target_cty.ctype) - || self.ast_context.is_function_pointer(source_cty.ctype) - { - let source_ty = self - .type_converter - .borrow_mut() - .convert(&self.ast_context, source_cty.ctype)?; - let target_ty = self - .type_converter - .borrow_mut() - .convert(&self.ast_context, target_cty.ctype)?; - - if source_ty == target_ty { - return Ok(val); - } - - self.import_type(source_cty.ctype); - self.import_type(target_cty.ctype); + self.convert_pointer_to_pointer_cast(source_cty, target_cty, val) + } - val.and_then(|val| { - Ok(WithStmts::new_unsafe_val(transmute_expr( - source_ty, target_ty, val, - ))) - }) - } else { - // Normal case - let target_ty = self.convert_type(target_cty.ctype)?; - Ok(val.map(|val| mk().cast_expr(val, target_ty))) - } + CastKind::IntegralToPointer => { + self.convert_integral_to_pointer_cast(ctx, source_cty, target_cty, expr, val) } - CastKind::IntegralToPointer - if self.ast_context.is_function_pointer(target_cty.ctype) => - { - let target_ty = self.convert_type(target_cty.ctype)?; - val.and_then(|x| { - self.use_crate(ExternCrate::Libc); - let intptr_t = mk().abs_path_ty(vec!["libc", "intptr_t"]); - let intptr = mk().cast_expr(x, intptr_t.clone()); - if ctx.is_const { - return Err(format_translation_err!( - None, - "cannot transmute integers to Option in `const` context", - )); - } - Ok(WithStmts::new_unsafe_val(transmute_expr( - intptr_t, target_ty, intptr, - ))) - }) + CastKind::PointerToIntegral => { + self.convert_pointer_to_integral_cast(ctx, source_cty, target_cty, val, expr) } - CastKind::IntegralToPointer - | CastKind::PointerToIntegral - | CastKind::IntegralCast + CastKind::IntegralCast | CastKind::FloatingCast | CastKind::FloatingToIntegral | CastKind::IntegralToFloating | CastKind::BooleanToSignedIntegral => { - if kind == CastKind::PointerToIntegral && ctx.is_const { - return Err(format_translation_err!( - None, - "cannot observe pointer values in `const` context", - )); - } let target_ty = self.convert_type(target_cty.ctype)?; - let source_ty = self.convert_type(source_cty.ctype)?; if let CTypeKind::LongDouble = target_ty_kind { if ctx.is_const { return Err(format_translation_err!( - None, - "f128 cannot be used in constants because `f128::f128::new` is not `const`", - )); + None, + "f128 cannot be used in constants because \ + `f128::f128::new` is not `const`", + )); } self.use_crate(ExternCrate::F128); @@ -4621,35 +4573,13 @@ impl<'c> Translation<'c> { ) }) } else if target_ty_kind.is_floating_type() && source_ty_kind.is_bool() { - val.and_then(|x| { - Ok(WithStmts::new_val(mk().cast_expr( - mk().cast_expr(x, mk().path_ty(vec!["u8"])), - target_ty, - ))) - }) - } else if target_ty_kind.is_pointer() && source_ty_kind.is_bool() { - val.and_then(|x| { - self.use_crate(ExternCrate::Libc); - Ok(WithStmts::new_val(mk().cast_expr( - mk().cast_expr(x, mk().abs_path_ty(vec!["libc", "size_t"])), - target_ty, - ))) - }) + Ok(val.map(|val| { + mk().cast_expr(mk().cast_expr(val, mk().path_ty(vec!["u8"])), target_ty) + })) + } else if let &CTypeKind::Enum(..) = source_ty_kind { + val.result_map(|val| self.convert_cast_from_enum(target_cty.ctype, val)) } else { - // Other numeric casts translate to Rust `as` casts, - // unless the cast is to a function pointer then use `transmute`. - val.and_then(|x| { - if self.ast_context.is_function_pointer(source_cty.ctype) { - Ok(WithStmts::new_unsafe_val(transmute_expr( - source_ty, target_ty, x, - ))) - } else if let &CTypeKind::Enum(..) = source_ty_kind { - self.convert_cast_from_enum(target_cty.ctype, x) - .map(WithStmts::new_val) - } else { - Ok(WithStmts::new_val(mk().cast_expr(x, target_ty))) - } - }) + Ok(val.map(|val| mk().cast_expr(val, target_ty))) } } diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index 763aefe6e2..d0e9f58afc 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -8,9 +8,10 @@ use syn::{BinOp, Expr, Type, UnOp}; use crate::{ c_ast, diagnostics::{TranslationError, TranslationErrorKind, TranslationResult}, - translator::{cast_int, unwrap_function_pointer, ExprContext, Translation}, + format_translation_err, + translator::{cast_int, transmute_expr, unwrap_function_pointer, ExprContext, Translation}, with_stmts::WithStmts, - CExprId, CExprKind, CLiteral, CQualTypeId, CTypeId, CTypeKind, CastKind, + CExprId, CExprKind, CLiteral, CQualTypeId, CTypeId, CTypeKind, CastKind, ExternCrate, }; impl<'c> Translation<'c> { @@ -449,4 +450,199 @@ impl<'c> Translation<'c> { .borrow_mut() .convert_pointee(&self.ast_context, type_id) } + + pub fn convert_pointer_to_pointer_cast( + &self, + source_cty: CQualTypeId, + target_cty: CQualTypeId, + val: WithStmts>, + ) -> TranslationResult>> { + if self.ast_context.is_function_pointer(target_cty.ctype) + || self.ast_context.is_function_pointer(source_cty.ctype) + { + let source_ty = self + .type_converter + .borrow_mut() + .convert(&self.ast_context, source_cty.ctype)?; + let target_ty = self + .type_converter + .borrow_mut() + .convert(&self.ast_context, target_cty.ctype)?; + + if source_ty == target_ty { + return Ok(val); + } + + self.import_type(source_cty.ctype); + self.import_type(target_cty.ctype); + + val.and_then(|val| { + Ok(WithStmts::new_unsafe_val(transmute_expr( + source_ty, target_ty, val, + ))) + }) + } else { + // Normal case + let target_ty = self.convert_type(target_cty.ctype)?; + Ok(val.map(|val| mk().cast_expr(val, target_ty))) + } + } + + pub fn convert_integral_to_pointer_cast( + &self, + ctx: ExprContext, + source_cty: CQualTypeId, + target_cty: CQualTypeId, + expr: Option, + val: WithStmts>, + ) -> TranslationResult>> { + let source_ty_kind = &self.ast_context.resolve_type(source_cty.ctype).kind; + let target_ty_kind = &self.ast_context.resolve_type(target_cty.ctype).kind; + + if self.ast_context.is_function_pointer(target_cty.ctype) { + let target_ty = self.convert_type(target_cty.ctype)?; + val.and_then(|x| { + self.use_crate(ExternCrate::Libc); + let intptr_t = mk().abs_path_ty(vec!["libc", "intptr_t"]); + let intptr = mk().cast_expr(x, intptr_t.clone()); + if ctx.is_const { + return Err(format_translation_err!( + None, + "cannot transmute integers to Option in `const` context", + )); + } + Ok(WithStmts::new_unsafe_val(transmute_expr( + intptr_t, target_ty, intptr, + ))) + }) + } else { + let target_ty = self.convert_type(target_cty.ctype)?; + let source_ty = self.convert_type(source_cty.ctype)?; + + if let CTypeKind::LongDouble = target_ty_kind { + if ctx.is_const { + return Err(format_translation_err!( + None, + "f128 cannot be used in constants because `f128::f128::new` is not `const`", + )); + } + + self.use_crate(ExternCrate::F128); + + let fn_path = mk().abs_path_expr(vec!["f128", "f128", "new"]); + Ok(val.map(|val| mk().call_expr(fn_path, vec![val]))) + } else if let CTypeKind::LongDouble = self.ast_context[source_cty.ctype].kind { + self.f128_cast_to(val, target_ty_kind) + } else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind { + // Casts targeting `enum` types... + let expr = expr.ok_or_else(|| format_err!("Casts to enums require a C ExprId"))?; + val.result_map(|val| { + self.convert_cast_to_enum(ctx, target_cty.ctype, enum_decl_id, Some(expr), val) + }) + } else if target_ty_kind.is_floating_type() && source_ty_kind.is_bool() { + val.and_then(|x| { + Ok(WithStmts::new_val(mk().cast_expr( + mk().cast_expr(x, mk().path_ty(vec!["u8"])), + target_ty, + ))) + }) + } else if target_ty_kind.is_pointer() && source_ty_kind.is_bool() { + val.and_then(|x| { + self.use_crate(ExternCrate::Libc); + Ok(WithStmts::new_val(mk().cast_expr( + mk().cast_expr(x, mk().abs_path_ty(vec!["libc", "size_t"])), + target_ty, + ))) + }) + } else { + // Other numeric casts translate to Rust `as` casts, + // unless the cast is to a function pointer then use `transmute`. + val.and_then(|x| { + if self.ast_context.is_function_pointer(source_cty.ctype) { + Ok(WithStmts::new_unsafe_val(transmute_expr( + source_ty, target_ty, x, + ))) + } else if let &CTypeKind::Enum(..) = source_ty_kind { + self.convert_cast_from_enum(target_cty.ctype, x) + .map(WithStmts::new_val) + } else { + Ok(WithStmts::new_val(mk().cast_expr(x, target_ty))) + } + }) + } + } + } + + pub fn convert_pointer_to_integral_cast( + &self, + ctx: ExprContext, + source_cty: CQualTypeId, + target_cty: CQualTypeId, + val: WithStmts>, + expr: Option, + ) -> TranslationResult>> { + let source_ty_kind = &self.ast_context.resolve_type(source_cty.ctype).kind; + let target_ty_kind = &self.ast_context.resolve_type(target_cty.ctype).kind; + + if ctx.is_const { + return Err(format_translation_err!( + None, + "cannot observe pointer values in `const` context", + )); + } + let target_ty = self.convert_type(target_cty.ctype)?; + let source_ty = self.convert_type(source_cty.ctype)?; + + if let CTypeKind::LongDouble = target_ty_kind { + if ctx.is_const { + return Err(format_translation_err!( + None, + "f128 cannot be used in constants because `f128::f128::new` is not `const`", + )); + } + + self.use_crate(ExternCrate::F128); + + let fn_path = mk().abs_path_expr(vec!["f128", "f128", "new"]); + Ok(val.map(|val| mk().call_expr(fn_path, vec![val]))) + } else if let CTypeKind::LongDouble = self.ast_context[source_cty.ctype].kind { + self.f128_cast_to(val, target_ty_kind) + } else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind { + // Casts targeting `enum` types... + let expr = expr.ok_or_else(|| format_err!("Casts to enums require a C ExprId"))?; + val.result_map(|val| { + self.convert_cast_to_enum(ctx, target_cty.ctype, enum_decl_id, Some(expr), val) + }) + } else if target_ty_kind.is_floating_type() && source_ty_kind.is_bool() { + val.and_then(|x| { + Ok(WithStmts::new_val(mk().cast_expr( + mk().cast_expr(x, mk().path_ty(vec!["u8"])), + target_ty, + ))) + }) + } else if target_ty_kind.is_pointer() && source_ty_kind.is_bool() { + val.and_then(|x| { + self.use_crate(ExternCrate::Libc); + Ok(WithStmts::new_val(mk().cast_expr( + mk().cast_expr(x, mk().abs_path_ty(vec!["libc", "size_t"])), + target_ty, + ))) + }) + } else { + // Other numeric casts translate to Rust `as` casts, + // unless the cast is to a function pointer then use `transmute`. + val.and_then(|x| { + if self.ast_context.is_function_pointer(source_cty.ctype) { + Ok(WithStmts::new_unsafe_val(transmute_expr( + source_ty, target_ty, x, + ))) + } else if let &CTypeKind::Enum(..) = source_ty_kind { + self.convert_cast_from_enum(target_cty.ctype, x) + .map(WithStmts::new_val) + } else { + Ok(WithStmts::new_val(mk().cast_expr(x, target_ty))) + } + }) + } + } } From 9d65d79674e0b60120363e7ee345a50dcd65df76 Mon Sep 17 00:00:00 2001 From: Rua Date: Mon, 19 Jan 2026 19:33:36 +0100 Subject: [PATCH 2/4] transpile: Simplify and refactor pointer cast functions --- c2rust-transpile/src/translator/mod.rs | 14 +- c2rust-transpile/src/translator/pointers.rs | 181 ++++++-------------- 2 files changed, 59 insertions(+), 136 deletions(-) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 2adf4c9468..68ad9b7f48 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -4526,16 +4526,20 @@ impl<'c> Translation<'c> { match kind { CastKind::BitCast | CastKind::NoOp => { - self.convert_pointer_to_pointer_cast(source_cty, target_cty, val) + self.convert_pointer_to_pointer_cast(source_cty.ctype, target_cty.ctype, val) } CastKind::IntegralToPointer => { - self.convert_integral_to_pointer_cast(ctx, source_cty, target_cty, expr, val) + self.convert_integral_to_pointer_cast(ctx, source_cty.ctype, target_cty.ctype, val) } - CastKind::PointerToIntegral => { - self.convert_pointer_to_integral_cast(ctx, source_cty, target_cty, val, expr) - } + CastKind::PointerToIntegral => self.convert_pointer_to_integral_cast( + ctx, + source_cty.ctype, + target_cty.ctype, + val, + expr, + ), CastKind::IntegralCast | CastKind::FloatingCast diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index d0e9f58afc..f85b359983 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -453,28 +453,28 @@ impl<'c> Translation<'c> { pub fn convert_pointer_to_pointer_cast( &self, - source_cty: CQualTypeId, - target_cty: CQualTypeId, + source_cty: CTypeId, + target_cty: CTypeId, val: WithStmts>, ) -> TranslationResult>> { - if self.ast_context.is_function_pointer(target_cty.ctype) - || self.ast_context.is_function_pointer(source_cty.ctype) + if self.ast_context.is_function_pointer(target_cty) + || self.ast_context.is_function_pointer(source_cty) { let source_ty = self .type_converter .borrow_mut() - .convert(&self.ast_context, source_cty.ctype)?; + .convert(&self.ast_context, source_cty)?; let target_ty = self .type_converter .borrow_mut() - .convert(&self.ast_context, target_cty.ctype)?; + .convert(&self.ast_context, target_cty)?; if source_ty == target_ty { return Ok(val); } - self.import_type(source_cty.ctype); - self.import_type(target_cty.ctype); + self.import_type(source_cty); + self.import_type(target_cty); val.and_then(|val| { Ok(WithStmts::new_unsafe_val(transmute_expr( @@ -483,7 +483,7 @@ impl<'c> Translation<'c> { }) } else { // Normal case - let target_ty = self.convert_type(target_cty.ctype)?; + let target_ty = self.convert_type(target_cty)?; Ok(val.map(|val| mk().cast_expr(val, target_ty))) } } @@ -491,158 +491,77 @@ impl<'c> Translation<'c> { pub fn convert_integral_to_pointer_cast( &self, ctx: ExprContext, - source_cty: CQualTypeId, - target_cty: CQualTypeId, - expr: Option, + source_cty: CTypeId, + target_cty: CTypeId, val: WithStmts>, ) -> TranslationResult>> { - let source_ty_kind = &self.ast_context.resolve_type(source_cty.ctype).kind; - let target_ty_kind = &self.ast_context.resolve_type(target_cty.ctype).kind; + let source_ty_kind = &self.ast_context.resolve_type(source_cty).kind; + let target_ty = self.convert_type(target_cty)?; + + if self.ast_context.is_function_pointer(target_cty) { + if ctx.is_const { + return Err(format_translation_err!( + None, + "cannot transmute integers to Option in `const` context", + )); + } - if self.ast_context.is_function_pointer(target_cty.ctype) { - let target_ty = self.convert_type(target_cty.ctype)?; - val.and_then(|x| { - self.use_crate(ExternCrate::Libc); + self.use_crate(ExternCrate::Libc); + val.and_then(|mut val| { + // First cast the integer to pointer size let intptr_t = mk().abs_path_ty(vec!["libc", "intptr_t"]); - let intptr = mk().cast_expr(x, intptr_t.clone()); - if ctx.is_const { - return Err(format_translation_err!( - None, - "cannot transmute integers to Option in `const` context", - )); - } + val = mk().cast_expr(val, intptr_t.clone()); + Ok(WithStmts::new_unsafe_val(transmute_expr( - intptr_t, target_ty, intptr, + intptr_t, target_ty, val, ))) }) + } else if source_ty_kind.is_bool() { + self.use_crate(ExternCrate::Libc); + Ok(val.map(|mut val| { + // First cast the boolean to pointer size + val = mk().cast_expr(val, mk().abs_path_ty(vec!["libc", "size_t"])); + mk().cast_expr(val, target_ty) + })) + } else if let &CTypeKind::Enum(..) = source_ty_kind { + val.result_map(|val| self.convert_cast_from_enum(target_cty, val)) } else { - let target_ty = self.convert_type(target_cty.ctype)?; - let source_ty = self.convert_type(source_cty.ctype)?; - - if let CTypeKind::LongDouble = target_ty_kind { - if ctx.is_const { - return Err(format_translation_err!( - None, - "f128 cannot be used in constants because `f128::f128::new` is not `const`", - )); - } - - self.use_crate(ExternCrate::F128); - - let fn_path = mk().abs_path_expr(vec!["f128", "f128", "new"]); - Ok(val.map(|val| mk().call_expr(fn_path, vec![val]))) - } else if let CTypeKind::LongDouble = self.ast_context[source_cty.ctype].kind { - self.f128_cast_to(val, target_ty_kind) - } else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind { - // Casts targeting `enum` types... - let expr = expr.ok_or_else(|| format_err!("Casts to enums require a C ExprId"))?; - val.result_map(|val| { - self.convert_cast_to_enum(ctx, target_cty.ctype, enum_decl_id, Some(expr), val) - }) - } else if target_ty_kind.is_floating_type() && source_ty_kind.is_bool() { - val.and_then(|x| { - Ok(WithStmts::new_val(mk().cast_expr( - mk().cast_expr(x, mk().path_ty(vec!["u8"])), - target_ty, - ))) - }) - } else if target_ty_kind.is_pointer() && source_ty_kind.is_bool() { - val.and_then(|x| { - self.use_crate(ExternCrate::Libc); - Ok(WithStmts::new_val(mk().cast_expr( - mk().cast_expr(x, mk().abs_path_ty(vec!["libc", "size_t"])), - target_ty, - ))) - }) - } else { - // Other numeric casts translate to Rust `as` casts, - // unless the cast is to a function pointer then use `transmute`. - val.and_then(|x| { - if self.ast_context.is_function_pointer(source_cty.ctype) { - Ok(WithStmts::new_unsafe_val(transmute_expr( - source_ty, target_ty, x, - ))) - } else if let &CTypeKind::Enum(..) = source_ty_kind { - self.convert_cast_from_enum(target_cty.ctype, x) - .map(WithStmts::new_val) - } else { - Ok(WithStmts::new_val(mk().cast_expr(x, target_ty))) - } - }) - } + Ok(val.map(|val| mk().cast_expr(val, target_ty))) } } pub fn convert_pointer_to_integral_cast( &self, ctx: ExprContext, - source_cty: CQualTypeId, - target_cty: CQualTypeId, + source_cty: CTypeId, + target_cty: CTypeId, val: WithStmts>, expr: Option, ) -> TranslationResult>> { - let source_ty_kind = &self.ast_context.resolve_type(source_cty.ctype).kind; - let target_ty_kind = &self.ast_context.resolve_type(target_cty.ctype).kind; - if ctx.is_const { return Err(format_translation_err!( None, "cannot observe pointer values in `const` context", )); } - let target_ty = self.convert_type(target_cty.ctype)?; - let source_ty = self.convert_type(source_cty.ctype)?; - - if let CTypeKind::LongDouble = target_ty_kind { - if ctx.is_const { - return Err(format_translation_err!( - None, - "f128 cannot be used in constants because `f128::f128::new` is not `const`", - )); - } - self.use_crate(ExternCrate::F128); + let target_ty = self.convert_type(target_cty)?; + let source_ty = self.convert_type(source_cty)?; + let target_ty_kind = &self.ast_context.resolve_type(target_cty).kind; - let fn_path = mk().abs_path_expr(vec!["f128", "f128", "new"]); - Ok(val.map(|val| mk().call_expr(fn_path, vec![val]))) - } else if let CTypeKind::LongDouble = self.ast_context[source_cty.ctype].kind { - self.f128_cast_to(val, target_ty_kind) + if self.ast_context.is_function_pointer(source_cty) { + val.and_then(|val| { + Ok(WithStmts::new_unsafe_val(transmute_expr( + source_ty, target_ty, val, + ))) + }) } else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind { - // Casts targeting `enum` types... let expr = expr.ok_or_else(|| format_err!("Casts to enums require a C ExprId"))?; val.result_map(|val| { - self.convert_cast_to_enum(ctx, target_cty.ctype, enum_decl_id, Some(expr), val) - }) - } else if target_ty_kind.is_floating_type() && source_ty_kind.is_bool() { - val.and_then(|x| { - Ok(WithStmts::new_val(mk().cast_expr( - mk().cast_expr(x, mk().path_ty(vec!["u8"])), - target_ty, - ))) - }) - } else if target_ty_kind.is_pointer() && source_ty_kind.is_bool() { - val.and_then(|x| { - self.use_crate(ExternCrate::Libc); - Ok(WithStmts::new_val(mk().cast_expr( - mk().cast_expr(x, mk().abs_path_ty(vec!["libc", "size_t"])), - target_ty, - ))) + self.convert_cast_to_enum(ctx, target_cty, enum_decl_id, Some(expr), val) }) } else { - // Other numeric casts translate to Rust `as` casts, - // unless the cast is to a function pointer then use `transmute`. - val.and_then(|x| { - if self.ast_context.is_function_pointer(source_cty.ctype) { - Ok(WithStmts::new_unsafe_val(transmute_expr( - source_ty, target_ty, x, - ))) - } else if let &CTypeKind::Enum(..) = source_ty_kind { - self.convert_cast_from_enum(target_cty.ctype, x) - .map(WithStmts::new_val) - } else { - Ok(WithStmts::new_val(mk().cast_expr(x, target_ty))) - } - }) + Ok(val.map(|val| mk().cast_expr(val, target_ty))) } } } From 5e06039b4f0e235992cd29c2a1f2aeecf2883024 Mon Sep 17 00:00:00 2001 From: Rua Date: Mon, 19 Jan 2026 19:42:12 +0100 Subject: [PATCH 3/4] transpile: Factor out `convert_pointer_is_null` --- c2rust-transpile/src/translator/mod.rs | 48 ++--------------- c2rust-transpile/src/translator/operators.rs | 54 +++++++------------- c2rust-transpile/src/translator/pointers.rs | 30 +++++++++++ 3 files changed, 52 insertions(+), 80 deletions(-) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 68ad9b7f48..5d27680a18 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -2729,31 +2729,8 @@ impl<'c> Translation<'c> { .kind .get_type() .ok_or_else(|| format_err!("bad pointer type for condition"))?; - val.and_then(|e| { - Ok(WithStmts::new_val( - if self.ast_context.is_function_pointer(ptr_type) { - if negated { - mk().method_call_expr(e, "is_some", vec![]) - } else { - mk().method_call_expr(e, "is_none", vec![]) - } - } else { - // TODO: `pointer::is_null` becomes stably const in Rust 1.84. - if ctx.is_const { - return Err(format_translation_err!( - None, - "cannot check nullity of pointer in `const` context", - )); - } - let is_null = mk().method_call_expr(e, "is_null", vec![]); - if negated { - mk().unary_expr(UnOp::Not(Default::default()), is_null) - } else { - is_null - } - }, - )) - }) + + val.result_map(|val| self.convert_pointer_is_null(ctx, ptr_type, val, negated)) }; match self.ast_context[cond_id].kind { @@ -4915,25 +4892,8 @@ impl<'c> Translation<'c> { ) -> TranslationResult> { let ty = &self.ast_context.resolve_type(ty_id).kind; - Ok(if self.ast_context.is_function_pointer(ty_id) { - if target { - mk().method_call_expr(val, "is_some", vec![]) - } else { - mk().method_call_expr(val, "is_none", vec![]) - } - } else if ty.is_pointer() { - // TODO: `pointer::is_null` becomes stably const in Rust 1.84. - if ctx.is_const { - return Err(format_translation_err!( - None, - "cannot check nullity of pointer in `const` context", - )); - } - let mut res = mk().method_call_expr(val, "is_null", vec![]); - if target { - res = mk().unary_expr(UnOp::Not(Default::default()), res) - } - res + Ok(if ty.is_pointer() { + self.convert_pointer_is_null(ctx, ty_id, val, !target)? } else if ty.is_bool() { if target { val diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index a8f0a612e4..c63dfdc900 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -181,6 +181,7 @@ impl<'c> Translation<'c> { .and_then(|rhs_val| { let expr_ids = Some((lhs, rhs)); self.convert_binary_operator( + ctx, op, ty, expr_type_id.ctype, @@ -235,6 +236,7 @@ impl<'c> Translation<'c> { }; let ty = self.convert_type(compute_res_type_id.ctype)?; let mut val = self.convert_binary_operator( + ctx, bin_op, ty, compute_res_type_id.ctype, @@ -456,6 +458,7 @@ impl<'c> Translation<'c> { let val = if expr_or_comp_type_id.ctype == initial_lhs_type_id.ctype { self.convert_binary_operator( + ctx, op, ty, expr_type_id.ctype, @@ -472,6 +475,7 @@ impl<'c> Translation<'c> { let lhs = mk().cast_expr(read.clone(), lhs_type); let ty = self.convert_type(result_type_id.ctype)?; let mut val = self.convert_binary_operator( + ctx, op, ty, result_type_id.ctype, @@ -570,6 +574,7 @@ impl<'c> Translation<'c> { /// arguments be usable as rvalues. fn convert_binary_operator( &self, + ctx: ExprContext, op: c_ast::BinOp, ty: Box, ctype: CTypeId, @@ -611,46 +616,23 @@ impl<'c> Translation<'c> { c_ast::BinOp::ShiftRight => mk().binary_expr(BinOp::Shr(Default::default()), lhs, rhs), c_ast::BinOp::ShiftLeft => mk().binary_expr(BinOp::Shl(Default::default()), lhs, rhs), - c_ast::BinOp::EqualEqual => { - // Using is_none method for null comparison means we don't have to - // rely on the PartialEq trait as much and is also more idiomatic - let expr = if let Some((lhs_expr_id, rhs_expr_id)) = lhs_rhs_ids { - let fn_eq_null = self.ast_context.is_function_pointer(lhs_type.ctype) - && self.ast_context.is_null_expr(rhs_expr_id); - let null_eq_fn = self.ast_context.is_function_pointer(rhs_type.ctype) - && self.ast_context.is_null_expr(lhs_expr_id); - - if fn_eq_null { - mk().method_call_expr(lhs, "is_none", vec![]) - } else if null_eq_fn { - mk().method_call_expr(rhs, "is_none", vec![]) - } else { - mk().binary_expr(BinOp::Eq(Default::default()), lhs, rhs) - } + // Using is_none method for null comparison means we don't have to + // rely on the PartialEq trait as much and is also more idiomatic + c_ast::BinOp::EqualEqual | c_ast::BinOp::NotEqual => { + let (negated, bin_op) = if let c_ast::BinOp::EqualEqual = op { + (true, BinOp::Eq(Default::default())) } else { - mk().binary_expr(BinOp::Eq(Default::default()), lhs, rhs) + (false, BinOp::Ne(Default::default())) }; - bool_to_int(expr) - } - c_ast::BinOp::NotEqual => { - // Using is_some method for null comparison means we don't have to - // rely on the PartialEq trait as much and is also more idiomatic - let expr = if let Some((lhs_expr_id, rhs_expr_id)) = lhs_rhs_ids { - let fn_eq_null = self.ast_context.is_function_pointer(lhs_type.ctype) - && self.ast_context.is_null_expr(rhs_expr_id); - let null_eq_fn = self.ast_context.is_function_pointer(rhs_type.ctype) - && self.ast_context.is_null_expr(lhs_expr_id); - - if fn_eq_null { - mk().method_call_expr(lhs, "is_some", vec![]) - } else if null_eq_fn { - mk().method_call_expr(rhs, "is_some", vec![]) - } else { - mk().binary_expr(BinOp::Ne(Default::default()), lhs, rhs) + let expr = match lhs_rhs_ids { + Some((lhs_expr_id, _)) if self.ast_context.is_null_expr(lhs_expr_id) => { + self.convert_pointer_is_null(ctx, rhs_type.ctype, rhs, negated)? } - } else { - mk().binary_expr(BinOp::Ne(Default::default()), lhs, rhs) + Some((_, rhs_expr_id)) if self.ast_context.is_null_expr(rhs_expr_id) => { + self.convert_pointer_is_null(ctx, lhs_type.ctype, lhs, negated)? + } + _ => mk().binary_expr(bin_op, lhs, rhs), }; bool_to_int(expr) diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index f85b359983..1b90276b2c 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -564,4 +564,34 @@ impl<'c> Translation<'c> { Ok(val.map(|val| mk().cast_expr(val, target_ty))) } } + + pub fn convert_pointer_is_null( + &self, + ctx: ExprContext, + ptr_type: CTypeId, + e: Box, + negated: bool, + ) -> TranslationResult> { + Ok(if self.ast_context.is_function_pointer(ptr_type) { + if negated { + mk().method_call_expr(e, "is_some", vec![]) + } else { + mk().method_call_expr(e, "is_none", vec![]) + } + } else { + // TODO: `pointer::is_null` becomes stably const in Rust 1.84. + if ctx.is_const { + return Err(format_translation_err!( + None, + "cannot check nullity of pointer in `const` context", + )); + } + let is_null = mk().method_call_expr(e, "is_null", vec![]); + if negated { + mk().unary_expr(UnOp::Not(Default::default()), is_null) + } else { + is_null + } + }) + } } From 750fb81624bd997c1579bc9f7941c00fb21f1e21 Mon Sep 17 00:00:00 2001 From: Rua Date: Fri, 6 Mar 2026 14:39:46 +0100 Subject: [PATCH 4/4] transpile: Refactor `convert_pointer_is_null` --- c2rust-transpile/src/translator/mod.rs | 13 +++++----- c2rust-transpile/src/translator/pointers.rs | 27 ++++++++++----------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 5d27680a18..d99e94f4ef 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -2723,45 +2723,44 @@ impl<'c> Translation<'c> { .ok_or_else(|| format_err!("bad condition type"))?; let null_pointer_case = - |negated: bool, ptr: CExprId| -> TranslationResult>> { + |ptr: CExprId, is_null: bool| -> TranslationResult>> { let val = self.convert_expr(ctx.used().decay_ref(), ptr, None)?; let ptr_type = self.ast_context[ptr] .kind .get_type() .ok_or_else(|| format_err!("bad pointer type for condition"))?; - val.result_map(|val| self.convert_pointer_is_null(ctx, ptr_type, val, negated)) + val.result_map(|val| self.convert_pointer_is_null(ctx, ptr_type, val, is_null)) }; match self.ast_context[cond_id].kind { CExprKind::Binary(_, c_ast::BinOp::EqualEqual, null_expr, ptr, _, _) if self.ast_context.is_null_expr(null_expr) => { - null_pointer_case(!target, ptr) + null_pointer_case(ptr, target) } CExprKind::Binary(_, c_ast::BinOp::EqualEqual, ptr, null_expr, _, _) if self.ast_context.is_null_expr(null_expr) => { - null_pointer_case(!target, ptr) + null_pointer_case(ptr, target) } CExprKind::Binary(_, c_ast::BinOp::NotEqual, null_expr, ptr, _, _) if self.ast_context.is_null_expr(null_expr) => { - null_pointer_case(target, ptr) + null_pointer_case(ptr, !target) } CExprKind::Binary(_, c_ast::BinOp::NotEqual, ptr, null_expr, _, _) if self.ast_context.is_null_expr(null_expr) => { - null_pointer_case(target, ptr) + null_pointer_case(ptr, !target) } CExprKind::Unary(_, c_ast::UnOp::Not, subexpr_id, _) => { self.convert_condition(ctx, !target, subexpr_id) } - _ => { // DecayRef could (and probably should) be Default instead of Yes here; however, as noted // in https://github.com/rust-lang/rust/issues/53772, you cant compare a reference (lhs) to diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index 1b90276b2c..191d6da92d 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -569,15 +569,12 @@ impl<'c> Translation<'c> { &self, ctx: ExprContext, ptr_type: CTypeId, - e: Box, - negated: bool, + mut val: Box, + is_null: bool, ) -> TranslationResult> { - Ok(if self.ast_context.is_function_pointer(ptr_type) { - if negated { - mk().method_call_expr(e, "is_some", vec![]) - } else { - mk().method_call_expr(e, "is_none", vec![]) - } + if self.ast_context.is_function_pointer(ptr_type) { + let method = if is_null { "is_none" } else { "is_some" }; + val = mk().method_call_expr(val, method, vec![]); } else { // TODO: `pointer::is_null` becomes stably const in Rust 1.84. if ctx.is_const { @@ -586,12 +583,14 @@ impl<'c> Translation<'c> { "cannot check nullity of pointer in `const` context", )); } - let is_null = mk().method_call_expr(e, "is_null", vec![]); - if negated { - mk().unary_expr(UnOp::Not(Default::default()), is_null) - } else { - is_null + + val = mk().method_call_expr(val, "is_null", vec![]); + + if !is_null { + val = mk().unary_expr(UnOp::Not(Default::default()), val); } - }) + } + + Ok(val) } }