From efc2a00d228cb34505e39315893a1af5ce3cf8ef Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 13 Jun 2026 17:39:48 +0200 Subject: [PATCH 1/2] transpile: Refactor operand type selection in operators.rs and `bubble_expr_types` --- c2rust-transpile/src/c_ast/mod.rs | 25 +++--- c2rust-transpile/src/translator/operators.rs | 80 +++++++++----------- 2 files changed, 48 insertions(+), 57 deletions(-) diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index 2b8a76e608..ddc7d26bb8 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1336,31 +1336,32 @@ impl TypedAstContext { let rhs_type_id = self.ast_context.c_exprs[&rhs].kind.get_qual_type().unwrap(); - let lhs_resolved_ty = self.ast_context.resolve_type(lhs_type_id.ctype); - let rhs_resolved_ty = self.ast_context.resolve_type(rhs_type_id.ctype); + let lhs_type_kind = &self.ast_context.resolve_type(lhs_type_id.ctype).kind; + let rhs_type_kind = &self.ast_context.resolve_type(rhs_type_id.ctype).kind; - if CTypeKind::PULLBACK_KINDS.contains(&lhs_resolved_ty.kind) { + if CTypeKind::PULLBACK_KINDS.contains(lhs_type_kind) { Some(lhs_type_id) - } else if CTypeKind::PULLBACK_KINDS.contains(&rhs_resolved_ty.kind) { + } else if CTypeKind::PULLBACK_KINDS.contains(rhs_type_kind) { Some(rhs_type_id) } else { None } } CExprKind::Binary(_ty, op, lhs, rhs, _, _) => { + let lhs_type_id = + self.ast_context.c_exprs[&lhs].kind.get_qual_type().unwrap(); let rhs_type_id = self.ast_context.c_exprs[&rhs].kind.get_qual_type().unwrap(); - let lhs_kind = &self.ast_context.c_exprs[&lhs].kind; - let lhs_type_id = lhs_kind.get_qual_type().unwrap(); - let lhs_resolved_ty = self.ast_context.resolve_type(lhs_type_id.ctype); - let rhs_resolved_ty = self.ast_context.resolve_type(rhs_type_id.ctype); + let lhs_type_kind = &self.ast_context.resolve_type(lhs_type_id.ctype).kind; + let rhs_type_kind = &self.ast_context.resolve_type(rhs_type_id.ctype).kind; - let neither_ptr = !lhs_resolved_ty.kind.is_pointer() - && !rhs_resolved_ty.kind.is_pointer(); + if lhs_type_kind.is_pointer() || rhs_type_kind.is_pointer() { + return; + } - if op.all_types_same() && neither_ptr { - if CTypeKind::PULLBACK_KINDS.contains(&lhs_resolved_ty.kind) { + if op.all_types_same() { + if CTypeKind::PULLBACK_KINDS.contains(lhs_type_kind) { Some(lhs_type_id) } else { Some(rhs_type_id) diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index a61eac8cdd..45d2fe20fd 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -85,38 +85,34 @@ impl<'c> Translation<'c> { // If this operation will (in Rust) take args of the same type, then propagate our // expected type down to the translation of our argument expressions. - let lhs_resolved_ty = self.ast_context.resolve_type(lhs_type_id.ctype); - let rhs_resolved_ty = self.ast_context.resolve_type(rhs_type_id.ctype); - let expr_ty_kind = &self.ast_context.index(expr_type_id.ctype).kind; + let lhs_type_kind = &self.ast_context.resolve_type(lhs_type_id.ctype).kind; + let rhs_type_kind = &self.ast_context.resolve_type(rhs_type_id.ctype).kind; + // Addition and subtraction can accept one pointer argument for .offset(), in which // case we don't want to homogenize arg types. - if !lhs_resolved_ty.kind.is_pointer() - && !rhs_resolved_ty.kind.is_pointer() - && !expr_ty_kind.is_pointer() + if !(op.is_pointer_arithmetic() + && (lhs_type_kind.is_pointer() || rhs_type_kind.is_pointer())) { if op.all_types_same() { // Ops like division and bitxor accept inputs of their expected result type. - lhs_type_id = expr_type_id; - rhs_type_id = expr_type_id; - } else if op.input_types_same() && lhs_resolved_ty.kind != rhs_resolved_ty.kind - { + lhs_type_id.ctype = expr_type_id.ctype; + rhs_type_id.ctype = expr_type_id.ctype; + } else if op.input_types_same() && lhs_type_kind != rhs_type_kind { // Ops like comparisons require argument types to match, but the result type // doesn't inform us what type to choose. Select a synthetic definition of a // portable rust type (e.g. u64 or usize) if either arg is one. trace!( "Binary op arg types differ: {:?} vs {:?}", - lhs_resolved_ty.kind, - rhs_resolved_ty.kind + lhs_type_kind, + rhs_type_kind ); - let ty = if CTypeKind::PULLBACK_KINDS.contains(&lhs_resolved_ty.kind) { - lhs_type_id + if CTypeKind::PULLBACK_KINDS.contains(&lhs_type_kind) { + rhs_type_id.ctype = lhs_type_id.ctype; } else { - rhs_type_id + lhs_type_id.ctype = rhs_type_id.ctype; }; - lhs_type_id = ty; - rhs_type_id = ty; - } else if matches!(op, ShiftLeft | ShiftRight) { - lhs_type_id = expr_type_id; + } else if op.is_bitshift() { + lhs_type_id.ctype = expr_type_id.ctype; } } @@ -271,31 +267,25 @@ impl<'c> Translation<'c> { .get_qual_type() .ok_or_else(|| format_err!("bad initial lhs type"))?; - // First, translate the rhs. Then, if it must match the lhs but doesn't, add a cast. - let mut rhs_translation = self.convert_expr(ctx.used(), rhs, Some(rhs_type_id))?; - let lhs_rhs_types_must_match = { - let lhs_resolved_ty = &self.ast_context.resolve_type(lhs_type_id.ctype); - let rhs_resolved_ty = &self.ast_context.resolve_type(rhs_type_id.ctype); - // Addition and subtraction can accept one pointer argument for .offset(), in which - // case we don't want to homogenize arg types. - let neither_ptr = - !lhs_resolved_ty.kind.is_pointer() && !rhs_resolved_ty.kind.is_pointer(); - - op.underlying_assignment().map_or(true, |op| { - if op.is_pointer_arithmetic() { - neither_ptr - } else { - op.is_arithmetic() || op.is_bitwise() - } - }) - }; - if lhs_rhs_types_must_match { - // For compound assignment, use the compute type; for regular assignment, use lhs type - let effective_lhs_ty = compute_lhs_type_id.unwrap_or(lhs_type_id); - if effective_lhs_ty.ctype != rhs_type_id.ctype { - let new_rhs_ty = - self.convert_type(compute_lhs_type_id.unwrap_or(lhs_type_id).ctype)?; - rhs_translation = rhs_translation.map(|val| mk().cast_expr(val, new_rhs_ty)); + // First, translate the rhs. + let mut rhs_rs = self.convert_expr(ctx.used(), rhs, Some(rhs_type_id))?; + + let lhs_type_kind = &self.ast_context.resolve_type(lhs_type_id.ctype).kind; + let rhs_type_kind = &self.ast_context.resolve_type(rhs_type_id.ctype).kind; + + // Addition and subtraction can accept one pointer argument for .offset(), in which + // case we don't want to homogenize arg types. + if !(op.is_pointer_arithmetic() + && (lhs_type_kind.is_pointer() || rhs_type_kind.is_pointer())) + { + if op + .underlying_assignment() + .map_or(true, |op| op.is_arithmetic() || op.is_bitwise()) + { + // If the rhs must match the lhs but doesn't, add a cast. + // For compound assignment, use the compute type; for regular assignment, use lhs type. + let target_type_id = compute_lhs_type_id.unwrap_or(lhs_type_id); + rhs_rs = self.make_cast(ctx.used(), rhs_type_id, target_type_id, rhs_rs)?; } } @@ -306,7 +296,7 @@ impl<'c> Translation<'c> { expr_type_id, lhs, rhs_type_id, - rhs_translation, + rhs_rs, compute_lhs_type_id, compute_res_type_id, ) From 85e05aa05f20cf0cb5d328070b5afc4d9c462179 Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 13 Jun 2026 19:16:29 +0200 Subject: [PATCH 2/2] transpile: Special case mixed operand types for complex arithmetic --- c2rust-transpile/src/c_ast/conversion.rs | 6 +++++ c2rust-transpile/src/c_ast/mod.rs | 27 ++++++++++++++++---- c2rust-transpile/src/translator/operators.rs | 23 ++++++++++++++++- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/c2rust-transpile/src/c_ast/conversion.rs b/c2rust-transpile/src/c_ast/conversion.rs index 4a3acb0725..1ac280255e 100644 --- a/c2rust-transpile/src/c_ast/conversion.rs +++ b/c2rust-transpile/src/c_ast/conversion.rs @@ -472,6 +472,12 @@ impl ConversionContext { self.add_type(new_id, not_located(rust_type_kind)); self.processed_nodes .insert(new_id, self::node_types::OTHER_TYPE); + + let complex_kind = CTypeKind::Complex(CTypeId(new_id)); + let new_id = self.id_mapper.fresh_id(); + self.add_type(new_id, not_located(complex_kind)); + self.processed_nodes + .insert(new_id, self::node_types::OTHER_TYPE); } // Continue popping Clang nodes off of the stack of nodes we have promised to visit diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index ddc7d26bb8..382ba67837 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1347,7 +1347,7 @@ impl TypedAstContext { None } } - CExprKind::Binary(_ty, op, lhs, rhs, _, _) => { + CExprKind::Binary(result_type_id, op, lhs, rhs, _, _) => { let lhs_type_id = self.ast_context.c_exprs[&lhs].kind.get_qual_type().unwrap(); let rhs_type_id = @@ -1361,11 +1361,28 @@ impl TypedAstContext { } if op.all_types_same() { - if CTypeKind::PULLBACK_KINDS.contains(lhs_type_kind) { - Some(lhs_type_id) - } else { - Some(rhs_type_id) + let result_type_kind = + &self.ast_context.resolve_type(result_type_id.ctype).kind; + + let mut result_type_id = + if CTypeKind::PULLBACK_KINDS.contains(lhs_type_kind) { + lhs_type_id + } else { + rhs_type_id + }; + + // For complex arithmetic, complex and real values can be mixed. + // See if a `Complex` version of the new result type exists. + if matches!(result_type_kind, CTypeKind::Complex(..)) { + let new_result_type_kind = CTypeKind::Complex(result_type_id.ctype); + let new_type_id = self + .ast_context + .type_for_kind(&new_result_type_kind) + .unwrap(); + result_type_id.ctype = new_type_id; } + + Some(result_type_id) } else if op.is_bitshift() { Some(lhs_type_id) } else { diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index 45d2fe20fd..dc6511399b 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -97,6 +97,19 @@ impl<'c> Translation<'c> { // Ops like division and bitxor accept inputs of their expected result type. lhs_type_id.ctype = expr_type_id.ctype; rhs_type_id.ctype = expr_type_id.ctype; + + // For complex arithmetic, complex and real values can be mixed. + let expr_type_kind = + &self.ast_context.resolve_type(expr_type_id.ctype).kind; + if let &CTypeKind::Complex(result_scalar_type_id) = expr_type_kind { + if !matches!(lhs_type_kind, CTypeKind::Complex(..)) { + lhs_type_id.ctype = result_scalar_type_id; + } + + if !matches!(rhs_type_kind, CTypeKind::Complex(..)) { + rhs_type_id.ctype = result_scalar_type_id; + } + } } else if op.input_types_same() && lhs_type_kind != rhs_type_kind { // Ops like comparisons require argument types to match, but the result type // doesn't inform us what type to choose. Select a synthetic definition of a @@ -284,7 +297,15 @@ impl<'c> Translation<'c> { { // If the rhs must match the lhs but doesn't, add a cast. // For compound assignment, use the compute type; for regular assignment, use lhs type. - let target_type_id = compute_lhs_type_id.unwrap_or(lhs_type_id); + let mut target_type_id = compute_lhs_type_id.unwrap_or(lhs_type_id); + + // For complex arithmetic, complex and real values can be mixed. + if let &CTypeKind::Complex(lhs_scalar_type_id) = lhs_type_kind { + if !matches!(rhs_type_kind, CTypeKind::Complex(..)) { + target_type_id.ctype = lhs_scalar_type_id; + } + } + rhs_rs = self.make_cast(ctx.used(), rhs_type_id, target_type_id, rhs_rs)?; } }