From 09a9ce9bfb7f09a059e3e90c00a27e445f53ed7e Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 6 May 2026 14:31:54 +0200 Subject: [PATCH 1/6] transpile: Split off `make_assignment_operator` --- c2rust-transpile/src/translator/operators.rs | 222 +++++++++++-------- 1 file changed, 133 insertions(+), 89 deletions(-) diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index 09bef7132d..315d8db281 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -401,103 +401,147 @@ impl<'c> Translation<'c> { rhs_translation .zip(lhs_translation) .and_then_try(|(rhs, lhs)| { - let NamedReference { - lvalue: write, - rvalue: read, - } = lhs; - - // Assignment expression itself - use CBinOp::*; - let assign_stmt = match op { - // Regular (possibly volatile) assignment - Assign if !is_volatile => WithStmts::new_val(mk().assign_expr(write, rhs)), - Assign => WithStmts::new_val(self.volatile_write(write, lhs_type_id, rhs)?) - .set_unsafe(), - - // Anything volatile needs to be desugared into explicit reads and writes - op if is_volatile || is_unsigned_arith => { - // Cast the lhs to the compute lhs type, do the compute, and then - // cast the compute result to the final lhs type. - - let op = op - .underlying_assignment() - .expect("Cannot convert non-assignment operator"); - - let lhs = self.make_cast( - ctx.used(), - lhs_type_id, - compute_res_type_id, - WithStmts::new_val(read.clone()), - )?; - - let val = lhs.and_then_try(|lhs| { - self.convert_binary_operator( - ctx, - compute_res_type_id, - op, - compute_lhs_type_id, - rhs_type_id, - lhs, - rhs, - ) - })?; + self.make_assignment_operator( + ctx, + expected_type_id, + result_type_id, + op, + lhs, + lhs_type_id, + rhs, + rhs_type_id, + compute_lhs_type_id, + compute_res_type_id, + ) + }) + } - let val = self.make_cast(ctx, compute_res_type_id, lhs_type_id, val)?; + fn make_assignment_operator( + &self, + ctx: ExprContext, + expected_type_id: Option, + result_type_id: CQualTypeId, + op: CBinOp, + lhs: NamedReference>, + lhs_type_id: CQualTypeId, + rhs: Box, + rhs_type_id: CQualTypeId, + compute_lhs_type_id: CQualTypeId, + compute_res_type_id: CQualTypeId, + ) -> TranslationResult>> { + let is_volatile = lhs_type_id.qualifiers.is_volatile; - if is_volatile { - val.try_map(|val| self.volatile_write(write, lhs_type_id, val))? - .set_unsafe() - } else { - val.map(|val| mk().assign_expr(write, val)) - } - } + let lhs_type_kind = &self.ast_context.resolve_type(lhs_type_id.ctype).kind; + let compute_lhs_type_kind = &self + .ast_context + .resolve_type(compute_lhs_type_id.ctype) + .kind; + let is_unsigned_arith = op + .underlying_assignment() + .map_or(false, |op| op.is_arithmetic()) + && compute_lhs_type_kind.is_unsigned_integral_type(); + let pointer_lhs = match lhs_type_kind { + &CTypeKind::Pointer(pointee) => Some(pointee), + _ => None, + }; - // Everything else - AssignAdd | AssignSubtract if pointer_lhs.is_some() => { - let ptr = self.convert_pointer_offset( - write.clone(), - rhs, - pointer_lhs.unwrap().ctype, - op == AssignSubtract, - false, - ); - ptr.map(|ptr| mk().assign_expr(write, ptr)) - } + let NamedReference { + lvalue: write, + rvalue: read, + } = lhs; - _ => { - let bin_op = op - .underlying_assignment() - .expect("Cannot convert non-assignment operator"); - let bin_op_kind = BinOp::from(op); + // Assignment expression itself + use CBinOp::*; + let assign_stmt = match op { + // Regular (possibly volatile) assignment + Assign if !is_volatile => WithStmts::new_val(mk().assign_expr(write, rhs)), + Assign => { + WithStmts::new_val(self.volatile_write(write, lhs_type_id, rhs)?).set_unsafe() + } - self.convert_assignment_operator_aux( - ctx, - bin_op_kind, - bin_op, - read.clone(), - write, - rhs, - lhs_type_id, - compute_lhs_type_id, - compute_res_type_id, - rhs_type_id, - )? - } - }; + // Anything volatile needs to be desugared into explicit reads and writes + op if is_volatile || is_unsigned_arith => { + // Cast the lhs to the compute lhs type, do the compute, and then + // cast the compute result to the final lhs type. - let assign_result = self.make_cast( - ctx, - result_type_id, - expected_type_id.unwrap_or(result_type_id), - WithStmts::new_val(read), + let op = op + .underlying_assignment() + .expect("Cannot convert non-assignment operator"); + + let lhs = self.make_cast( + ctx.used(), + lhs_type_id, + compute_res_type_id, + WithStmts::new_val(read.clone()), )?; - Ok(assign_stmt - .zip(assign_result) - .and_then(|(assign_stmt, assign_result)| { - WithStmts::new(vec![mk().semi_stmt(assign_stmt)], assign_result) - })) - }) + let val = lhs.and_then_try(|lhs| { + self.convert_binary_operator( + ctx, + compute_res_type_id, + op, + compute_lhs_type_id, + rhs_type_id, + lhs, + rhs, + ) + })?; + + let val = self.make_cast(ctx, compute_res_type_id, lhs_type_id, val)?; + + if is_volatile { + val.try_map(|val| self.volatile_write(write, lhs_type_id, val))? + .set_unsafe() + } else { + val.map(|val| mk().assign_expr(write, val)) + } + } + + // Everything else + AssignAdd | AssignSubtract if pointer_lhs.is_some() => { + let ptr = self.convert_pointer_offset( + write.clone(), + rhs, + pointer_lhs.unwrap().ctype, + op == AssignSubtract, + false, + ); + ptr.map(|ptr| mk().assign_expr(write, ptr)) + } + + _ => { + let bin_op = op + .underlying_assignment() + .expect("Cannot convert non-assignment operator"); + let bin_op_kind = BinOp::from(op); + + self.convert_assignment_operator_aux( + ctx, + bin_op_kind, + bin_op, + read.clone(), + write, + rhs, + lhs_type_id, + compute_lhs_type_id, + compute_res_type_id, + rhs_type_id, + )? + } + }; + + let assign_result = self.make_cast( + ctx, + result_type_id, + expected_type_id.unwrap_or(result_type_id), + WithStmts::new_val(read), + )?; + + Ok(assign_stmt + .zip(assign_result) + .and_then(|(assign_stmt, assign_result)| { + WithStmts::new(vec![mk().semi_stmt(assign_stmt)], assign_result) + })) } /// Translate a non-assignment binary operator. It is expected that the `lhs` and `rhs` From 0b36bd6395438ee6ab144494b5c196ba972d2a01 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 6 May 2026 15:10:02 +0200 Subject: [PATCH 2/6] transpile: Account for unused result in `make_assignment_operator` --- c2rust-transpile/src/translator/operators.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index 315d8db281..a6d17cc0cb 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -530,12 +530,16 @@ impl<'c> Translation<'c> { } }; - let assign_result = self.make_cast( - ctx, - result_type_id, - expected_type_id.unwrap_or(result_type_id), - WithStmts::new_val(read), - )?; + let assign_result = if ctx.is_used() { + self.make_cast( + ctx, + result_type_id, + expected_type_id.unwrap_or(result_type_id), + WithStmts::new_val(read), + )? + } else { + WithStmts::new_val(self.panic_or_err("assignment result is not supposed to be used")) + }; Ok(assign_stmt .zip(assign_result) From 2c86612b841061d3ac2327cefd515b2499609cb9 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 6 May 2026 18:17:48 +0200 Subject: [PATCH 3/6] transpile: Make variable names consistent in `convert_pre/post_increment` --- c2rust-transpile/src/translator/operators.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index a6d17cc0cb..1de8918d75 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -723,7 +723,7 @@ impl<'c> Translation<'c> { let op = op .underlying_assignment() .expect("not an valid assignment operator"); - let ty = self + let arg_type = self .ast_context .index_unwrap_parens(arg) .kind @@ -742,7 +742,7 @@ impl<'c> Translation<'c> { Some(read.clone()), ))); - let mut one = match self.ast_context[ty.ctype].kind { + let mut one = match self.ast_context.resolve_type(arg_type.ctype).kind { // TODO: If rust gets f16 support: // CTypeKind::Half | CTypeKind::Float | CTypeKind::Double => { @@ -762,7 +762,7 @@ impl<'c> Translation<'c> { let mut is_unsafe = false; // Track unsafety if we call `pointer::offset`. // *p + 1 - let mut type_kind = &self.ast_context.resolve_type(ty.ctype).kind; + let mut type_kind = &self.ast_context.resolve_type(arg_type.ctype).kind; let val = if let &CTypeKind::Pointer(pointee) = type_kind { if let Some(n) = self.compute_size_of_expr(pointee.ctype) { @@ -790,9 +790,9 @@ impl<'c> Translation<'c> { }; // *p = *p + rhs - let assign_stmt = if ty.qualifiers.is_volatile { + let assign_stmt = if arg_type.qualifiers.is_volatile { is_unsafe = true; - self.volatile_write(write, ty, val)? + self.volatile_write(write, arg_type, val)? } else { mk().assign_expr(write, val) }; From 9efaf4da1c847b8fdb10cef05fa12c4fa076550c Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 6 May 2026 18:32:57 +0200 Subject: [PATCH 4/6] transpile: Merge `convert_pre/post_increment` to `convert_indecrement_operator` --- c2rust-transpile/src/c_ast/mod.rs | 10 +- c2rust-transpile/src/translator/operators.rs | 217 +++++++------------ 2 files changed, 92 insertions(+), 135 deletions(-) diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index 1ebe5409ca..158e09a24e 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -2169,7 +2169,15 @@ impl Display for CUnTypeOp { } impl CUnOp { - /// Check is the operator is rendered before or after is operand. + pub fn underlying_compound_assignment(&self) -> Option { + Some(match self { + CUnOp::PreIncrement | CUnOp::PostIncrement => CBinOp::AssignAdd, + CUnOp::PreDecrement | CUnOp::PostDecrement => CBinOp::AssignSubtract, + _ => return None, + }) + } + + /// Returns whether the operator is placed before its operand. pub fn is_prefix(&self) -> bool { !matches!(*self, CUnOp::PostIncrement | CUnOp::PostDecrement) } diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index 1de8918d75..85441225b9 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -648,12 +648,12 @@ impl<'c> Translation<'c> { } } - fn convert_pre_increment( + fn convert_indecrement_operator( &self, ctx: ExprContext, expected_type_id: Option, result_type_id: CQualTypeId, - op: CBinOp, + op: CUnOp, arg: CExprId, ) -> TranslationResult>> { let arg_type = self @@ -663,7 +663,7 @@ impl<'c> Translation<'c> { .get_qual_type() .ok_or_else(|| format_err!("bad arg type"))?; - let one = match self.ast_context.resolve_type(arg_type.ctype).kind { + let mut one = match self.ast_context.resolve_type(arg_type.ctype).kind { // TODO: If rust gets f16 support: // CTypeKind::Half | CTypeKind::Float | CTypeKind::Double => mk().lit_expr(mk().float_unsuffixed_lit("1.")), @@ -694,118 +694,87 @@ impl<'c> Translation<'c> { _ => {} }; - self.convert_assignment_operator_with_rhs( - ctx, - expected_type_id, - result_type_id, - op, - arg, - one_type_id, - WithStmts::new_val(one), - Some(compute_lhs_type_id), - Some(compute_res_type_id), - ) - } - - fn convert_post_increment( - &self, - ctx: ExprContext, - expected_type_id: Option, - result_type_id: CQualTypeId, - op: CBinOp, - arg: CExprId, - ) -> TranslationResult>> { // If we aren't going to be using the result, may as well do a simple pre-increment - if ctx.is_unused() { - return self.convert_pre_increment(ctx, expected_type_id, result_type_id, op, arg); - } + let dont_yield_old_value = op.is_prefix() || ctx.is_unused(); + let op = op.underlying_compound_assignment().unwrap(); - let op = op - .underlying_assignment() - .expect("not an valid assignment operator"); - let arg_type = self - .ast_context - .index_unwrap_parens(arg) - .kind - .get_qual_type() - .ok_or_else(|| format_err!("bad post inc type"))?; - - self.name_reference_write_read(ctx, arg)?.and_then_try( - |NamedReference { - lvalue: write, - rvalue: read, - }| { - let val_name = self.renamer.borrow_mut().fresh(); - let save_old_val = mk().local_stmt(Box::new(mk().local( - mk().ident_pat(&val_name), - None, - Some(read.clone()), - ))); - - let mut one = match self.ast_context.resolve_type(arg_type.ctype).kind { - // TODO: If rust gets f16 support: - // CTypeKind::Half | - CTypeKind::Float | CTypeKind::Double => { - mk().lit_expr(mk().float_unsuffixed_lit("1.")) - } - CTypeKind::LongDouble | CTypeKind::Float128 => { - self.use_crate(ExternCrate::F128); - - let fn_path = mk().abs_path_expr(vec!["f128", "f128", "new"]); - let args = vec![mk().lit_expr(mk().float_unsuffixed_lit("1."))]; - - mk().call_expr(fn_path, args) - } - _ => mk().lit_expr(mk().int_unsuffixed_lit(1)), - }; - - let mut is_unsafe = false; // Track unsafety if we call `pointer::offset`. - - // *p + 1 - let mut type_kind = &self.ast_context.resolve_type(arg_type.ctype).kind; - - let val = if let &CTypeKind::Pointer(pointee) = type_kind { - if let Some(n) = self.compute_size_of_expr(pointee.ctype) { - one = n - } + if dont_yield_old_value { + self.convert_assignment_operator_with_rhs( + ctx, + expected_type_id, + result_type_id, + op, + arg, + one_type_id, + WithStmts::new_val(one), + Some(compute_lhs_type_id), + Some(compute_res_type_id), + ) + } else { + let op = op + .underlying_assignment() + .expect("not an valid assignment operator"); + + self.name_reference_write_read(ctx, arg)?.and_then_try( + |NamedReference { + lvalue: write, + rvalue: read, + }| { + let val_name = self.renamer.borrow_mut().fresh(); + let save_old_val = mk().local_stmt(Box::new(mk().local( + mk().ident_pat(&val_name), + None, + Some(read.clone()), + ))); + + let mut is_unsafe = false; // Track unsafety if we call `pointer::offset`. + + // *p + 1 + let mut type_kind = &self.ast_context.resolve_type(arg_type.ctype).kind; + + let val = if let &CTypeKind::Pointer(pointee) = type_kind { + if let Some(n) = self.compute_size_of_expr(pointee.ctype) { + one = n + } - let n = if op == CBinOp::Subtract { - neg_expr(one) + let n = if op == CBinOp::Subtract { + neg_expr(one) + } else { + one + }; + is_unsafe = true; + mk().method_call_expr(read, "offset", vec![n]) } else { - one + if let &CTypeKind::Enum(enum_id) = type_kind { + let integral_type = self.enum_integral_type(enum_id); + type_kind = &self.ast_context.resolve_type(integral_type.ctype).kind; + } + + if type_kind.is_unsigned_integral_type() { + mk().method_call_expr(read, op.wrapping_method(), vec![one]) + } else { + mk().binary_expr(BinOp::from(op), read, one) + } }; - is_unsafe = true; - mk().method_call_expr(read, "offset", vec![n]) - } else { - if let &CTypeKind::Enum(enum_id) = type_kind { - let integral_type = self.enum_integral_type(enum_id); - type_kind = &self.ast_context.resolve_type(integral_type.ctype).kind; - } - if type_kind.is_unsigned_integral_type() { - mk().method_call_expr(read, op.wrapping_method(), vec![one]) + // *p = *p + rhs + let assign_stmt = if arg_type.qualifiers.is_volatile { + is_unsafe = true; + self.volatile_write(write, arg_type, val)? } else { - mk().binary_expr(BinOp::from(op), read, one) - } - }; - - // *p = *p + rhs - let assign_stmt = if arg_type.qualifiers.is_volatile { - is_unsafe = true; - self.volatile_write(write, arg_type, val)? - } else { - mk().assign_expr(write, val) - }; + mk().assign_expr(write, val) + }; - let val = WithStmts::new( - vec![save_old_val, mk().expr_stmt(assign_stmt)], - mk().ident_expr(val_name), - ) - .merge_unsafe(is_unsafe); + let val = WithStmts::new( + vec![save_old_val, mk().expr_stmt(assign_stmt)], + mk().ident_expr(val_name), + ) + .merge_unsafe(is_unsafe); - Ok(val) - }, - ) + Ok(val) + }, + ) + } } pub fn convert_unary_operator( @@ -819,34 +788,14 @@ impl<'c> Translation<'c> { let expr_type_id = expected_type_id.unwrap_or(result_type_id); let mut unary = match op { CUnOp::AddressOf => self.convert_address_of(ctx, expr_type_id, arg), - CUnOp::PreIncrement => self.convert_pre_increment( - ctx, - expected_type_id, - result_type_id, - CBinOp::AssignAdd, - arg, - ), - CUnOp::PreDecrement => self.convert_pre_increment( - ctx, - expected_type_id, - result_type_id, - CBinOp::AssignSubtract, - arg, - ), - CUnOp::PostIncrement => self.convert_post_increment( - ctx, - expected_type_id, - result_type_id, - CBinOp::AssignAdd, - arg, - ), - CUnOp::PostDecrement => self.convert_post_increment( - ctx, - expected_type_id, - result_type_id, - CBinOp::AssignSubtract, - arg, - ), + + CUnOp::PreIncrement + | CUnOp::PreDecrement + | CUnOp::PostIncrement + | CUnOp::PostDecrement => { + self.convert_indecrement_operator(ctx, expected_type_id, result_type_id, op, arg) + } + CUnOp::Deref => self.convert_deref(ctx, expr_type_id, arg), CUnOp::Plus => self.convert_expr(ctx.used(), arg, expected_type_id), // promotion is explicit in the clang AST From cbd4f64959713f8dd9137e1159849dee78b96e17 Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 6 May 2026 19:16:17 +0200 Subject: [PATCH 5/6] transpile: Group `convert_indecrement_operator` with `convert_negate_operator` --- c2rust-transpile/src/translator/operators.rs | 124 +++++++++---------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index 85441225b9..4711085a00 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -648,6 +648,68 @@ impl<'c> Translation<'c> { } } + pub fn convert_unary_operator( + &self, + ctx: ExprContext, + expected_type_id: Option, + result_type_id: CQualTypeId, + op: CUnOp, + arg: CExprId, + ) -> TranslationResult>> { + let expr_type_id = expected_type_id.unwrap_or(result_type_id); + let mut unary = match op { + CUnOp::AddressOf => self.convert_address_of(ctx, expr_type_id, arg), + + CUnOp::PreIncrement + | CUnOp::PreDecrement + | CUnOp::PostIncrement + | CUnOp::PostDecrement => { + self.convert_indecrement_operator(ctx, expected_type_id, result_type_id, op, arg) + } + + CUnOp::Deref => self.convert_deref(ctx, expr_type_id, arg), + CUnOp::Plus => self.convert_expr(ctx.used(), arg, expected_type_id), // promotion is explicit in the clang AST + + CUnOp::Negate => self.convert_negate_operator(ctx, expr_type_id, arg), + CUnOp::Complement => Ok(self + .convert_expr(ctx.used(), arg, expected_type_id)? + .map(|a| mk().unary_expr(UnOp::Not(Default::default()), a))), + + CUnOp::Not => { + let val = self.convert_condition(ctx.used(), false, arg)?; + Ok(val.map(|x| mk().cast_expr(x, mk().abs_path_ty(vec!["core", "ffi", "c_int"])))) + } + CUnOp::Extension => { + let arg = self.convert_expr(ctx, arg, expected_type_id)?; + Ok(arg) + } + CUnOp::Real | CUnOp::Imag | CUnOp::Coawait => { + panic!("Unsupported extension operator") + } + }?; + + // Some unused unary operators (`-foo()`) may have side effects, so we need + // to add them to stmts when name is not increment/decrement operator. + // + // `UnOp::Extension` (`__extension__`) is another exception since + // it's a no-op around the inner expression. + if !matches!( + op, + CUnOp::PreDecrement + | CUnOp::PreIncrement + | CUnOp::PostDecrement + | CUnOp::PostIncrement + | CUnOp::Extension + ) { + unary = self.convert_side_effects_expr( + ctx, + unary, + "Unary expression is not supposed to be used", + )?; + } + Ok(unary) + } + fn convert_indecrement_operator( &self, ctx: ExprContext, @@ -777,68 +839,6 @@ impl<'c> Translation<'c> { } } - pub fn convert_unary_operator( - &self, - ctx: ExprContext, - expected_type_id: Option, - result_type_id: CQualTypeId, - op: CUnOp, - arg: CExprId, - ) -> TranslationResult>> { - let expr_type_id = expected_type_id.unwrap_or(result_type_id); - let mut unary = match op { - CUnOp::AddressOf => self.convert_address_of(ctx, expr_type_id, arg), - - CUnOp::PreIncrement - | CUnOp::PreDecrement - | CUnOp::PostIncrement - | CUnOp::PostDecrement => { - self.convert_indecrement_operator(ctx, expected_type_id, result_type_id, op, arg) - } - - CUnOp::Deref => self.convert_deref(ctx, expr_type_id, arg), - CUnOp::Plus => self.convert_expr(ctx.used(), arg, expected_type_id), // promotion is explicit in the clang AST - - CUnOp::Negate => self.convert_negate_operator(ctx, expr_type_id, arg), - CUnOp::Complement => Ok(self - .convert_expr(ctx.used(), arg, expected_type_id)? - .map(|a| mk().unary_expr(UnOp::Not(Default::default()), a))), - - CUnOp::Not => { - let val = self.convert_condition(ctx.used(), false, arg)?; - Ok(val.map(|x| mk().cast_expr(x, mk().abs_path_ty(vec!["core", "ffi", "c_int"])))) - } - CUnOp::Extension => { - let arg = self.convert_expr(ctx, arg, expected_type_id)?; - Ok(arg) - } - CUnOp::Real | CUnOp::Imag | CUnOp::Coawait => { - panic!("Unsupported extension operator") - } - }?; - - // Some unused unary operators (`-foo()`) may have side effects, so we need - // to add them to stmts when name is not increment/decrement operator. - // - // `UnOp::Extension` (`__extension__`) is another exception since - // it's a no-op around the inner expression. - if !matches!( - op, - CUnOp::PreDecrement - | CUnOp::PreIncrement - | CUnOp::PostDecrement - | CUnOp::PostIncrement - | CUnOp::Extension - ) { - unary = self.convert_side_effects_expr( - ctx, - unary, - "Unary expression is not supposed to be used", - )?; - } - Ok(unary) - } - fn convert_negate_operator( &self, ctx: ExprContext, From 11dbc42380771f5737a7d3baba7f88d776fc002c Mon Sep 17 00:00:00 2001 From: Rua Date: Fri, 24 Jul 2026 17:08:45 +0200 Subject: [PATCH 6/6] transpile: Use `make_assignment_operator` in `convert_indecrement_operator` --- c2rust-transpile/src/translator/operators.rs | 81 ++++++------------- ...shots__transpile@enums.c.2021.clang15.snap | 4 +- ...shots__transpile@enums.c.2024.clang15.snap | 4 +- ...shots__transpile@exprs.c.2021.clang15.snap | 8 +- ...shots__transpile@exprs.c.2024.clang15.snap | 8 +- 5 files changed, 35 insertions(+), 70 deletions(-) diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index 4711085a00..3a2a44dd95 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -725,7 +725,7 @@ impl<'c> Translation<'c> { .get_qual_type() .ok_or_else(|| format_err!("bad arg type"))?; - let mut one = match self.ast_context.resolve_type(arg_type.ctype).kind { + let one = match self.ast_context.resolve_type(arg_type.ctype).kind { // TODO: If rust gets f16 support: // CTypeKind::Half | CTypeKind::Float | CTypeKind::Double => mk().lit_expr(mk().float_unsuffixed_lit("1.")), @@ -773,69 +773,34 @@ impl<'c> Translation<'c> { Some(compute_res_type_id), ) } else { - let op = op - .underlying_assignment() - .expect("not an valid assignment operator"); - - self.name_reference_write_read(ctx, arg)?.and_then_try( - |NamedReference { - lvalue: write, - rvalue: read, - }| { + self.name_reference_write_read(ctx, arg)? + .and_then(|lhs| { let val_name = self.renamer.borrow_mut().fresh(); let save_old_val = mk().local_stmt(Box::new(mk().local( mk().ident_pat(&val_name), None, - Some(read.clone()), + Some(lhs.rvalue.clone()), ))); + let old_val_expr = mk().ident_expr(val_name); + WithStmts::new(vec![save_old_val], (lhs, old_val_expr)) + }) + .and_then_try(|(lhs, old_val_expr)| { + let val = self.make_assignment_operator( + ctx.unused(), + expected_type_id, + result_type_id, + op, + lhs, + arg_type, + one, + one_type_id, + compute_lhs_type_id, + compute_res_type_id, + )?; - let mut is_unsafe = false; // Track unsafety if we call `pointer::offset`. - - // *p + 1 - let mut type_kind = &self.ast_context.resolve_type(arg_type.ctype).kind; - - let val = if let &CTypeKind::Pointer(pointee) = type_kind { - if let Some(n) = self.compute_size_of_expr(pointee.ctype) { - one = n - } - - let n = if op == CBinOp::Subtract { - neg_expr(one) - } else { - one - }; - is_unsafe = true; - mk().method_call_expr(read, "offset", vec![n]) - } else { - if let &CTypeKind::Enum(enum_id) = type_kind { - let integral_type = self.enum_integral_type(enum_id); - type_kind = &self.ast_context.resolve_type(integral_type.ctype).kind; - } - - if type_kind.is_unsigned_integral_type() { - mk().method_call_expr(read, op.wrapping_method(), vec![one]) - } else { - mk().binary_expr(BinOp::from(op), read, one) - } - }; - - // *p = *p + rhs - let assign_stmt = if arg_type.qualifiers.is_volatile { - is_unsafe = true; - self.volatile_write(write, arg_type, val)? - } else { - mk().assign_expr(write, val) - }; - - let val = WithStmts::new( - vec![save_old_val, mk().expr_stmt(assign_stmt)], - mk().ident_expr(val_name), - ) - .merge_unsafe(is_unsafe); - - Ok(val) - }, - ) + // Replace the assignment result with the old value + Ok(val.map(|_| old_val_expr)) + }) } } diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@enums.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@enums.c.2021.clang15.snap index 1801a51ebe..d190cd30f3 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@enums.c.2021.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@enums.c.2021.clang15.snap @@ -41,10 +41,10 @@ pub unsafe extern "C" fn test_enums() { foo = (foo as ::core::ffi::c_uint).wrapping_add(1) as Foo; bar = (bar as ::core::ffi::c_int + 1) as Bar; let c2rust_fresh0 = foo; - foo = foo.wrapping_add(1); + foo = (foo as ::core::ffi::c_uint).wrapping_add(1) as Foo; bar = c2rust_fresh0 as Bar; let c2rust_fresh1 = bar; - bar = bar + 1; + bar = (bar as ::core::ffi::c_int + 1) as Bar; foo = c2rust_fresh1 as Foo; let mut e: Foo = Foo1; let mut enum_enum: ::core::ffi::c_int = diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@enums.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@enums.c.2024.clang15.snap index 8aa6150078..2bc2ca40a1 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@enums.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@enums.c.2024.clang15.snap @@ -42,10 +42,10 @@ pub unsafe extern "C" fn test_enums() { foo = (foo as ::core::ffi::c_uint).wrapping_add(1) as Foo; bar = (bar as ::core::ffi::c_int + 1) as Bar; let c2rust_fresh0 = foo; - foo = foo.wrapping_add(1); + foo = (foo as ::core::ffi::c_uint).wrapping_add(1) as Foo; bar = c2rust_fresh0 as Bar; let c2rust_fresh1 = bar; - bar = bar + 1; + bar = (bar as ::core::ffi::c_int + 1) as Bar; foo = c2rust_fresh1 as Foo; let mut e: Foo = Foo1; let mut enum_enum: ::core::ffi::c_int = diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.clang15.snap index c562665716..46968090d8 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.clang15.snap @@ -71,11 +71,11 @@ pub unsafe extern "C" fn inc_decl_with_rvalue_side_effect() { let mut pre_dec: ::core::ffi::c_int = *c2rust_lvalue_ptr_0; let c2rust_lvalue_ptr_1 = &raw mut arr[side_effect() as usize]; let c2rust_fresh0 = *c2rust_lvalue_ptr_1; - *c2rust_lvalue_ptr_1 = *c2rust_lvalue_ptr_1 + 1; + *c2rust_lvalue_ptr_1 += 1; let mut post_inc: ::core::ffi::c_int = c2rust_fresh0; let c2rust_lvalue_ptr_2 = &raw mut arr[side_effect() as usize]; let c2rust_fresh1 = *c2rust_lvalue_ptr_2; - *c2rust_lvalue_ptr_2 = *c2rust_lvalue_ptr_2 - 1; + *c2rust_lvalue_ptr_2 -= 1; let mut post_dec: ::core::ffi::c_int = c2rust_fresh1; } #[no_mangle] @@ -92,11 +92,11 @@ pub unsafe extern "C" fn inc_decl_with_lvalue_side_effect() { let mut pre_dec: ::core::ffi::c_int = *c2rust_lvalue_ptr_0; let c2rust_lvalue_ptr_1 = &raw mut *lvalue_side_effect(); let c2rust_fresh2 = *c2rust_lvalue_ptr_1; - *c2rust_lvalue_ptr_1 = *c2rust_lvalue_ptr_1 + 1; + *c2rust_lvalue_ptr_1 += 1; let mut post_inc: ::core::ffi::c_int = c2rust_fresh2; let c2rust_lvalue_ptr_2 = &raw mut *lvalue_side_effect(); let c2rust_fresh3 = *c2rust_lvalue_ptr_2; - *c2rust_lvalue_ptr_2 = *c2rust_lvalue_ptr_2 - 1; + *c2rust_lvalue_ptr_2 -= 1; let mut post_dec: ::core::ffi::c_int = c2rust_fresh3; } #[no_mangle] diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.clang15.snap index e7b36aa49a..2566035066 100644 --- a/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.clang15.snap +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.clang15.snap @@ -71,11 +71,11 @@ pub unsafe extern "C" fn inc_decl_with_rvalue_side_effect() { let mut pre_dec: ::core::ffi::c_int = *c2rust_lvalue_ptr_0; let c2rust_lvalue_ptr_1 = &raw mut arr[side_effect() as usize]; let c2rust_fresh0 = *c2rust_lvalue_ptr_1; - *c2rust_lvalue_ptr_1 = *c2rust_lvalue_ptr_1 + 1; + *c2rust_lvalue_ptr_1 += 1; let mut post_inc: ::core::ffi::c_int = c2rust_fresh0; let c2rust_lvalue_ptr_2 = &raw mut arr[side_effect() as usize]; let c2rust_fresh1 = *c2rust_lvalue_ptr_2; - *c2rust_lvalue_ptr_2 = *c2rust_lvalue_ptr_2 - 1; + *c2rust_lvalue_ptr_2 -= 1; let mut post_dec: ::core::ffi::c_int = c2rust_fresh1; } #[unsafe(no_mangle)] @@ -92,11 +92,11 @@ pub unsafe extern "C" fn inc_decl_with_lvalue_side_effect() { let mut pre_dec: ::core::ffi::c_int = *c2rust_lvalue_ptr_0; let c2rust_lvalue_ptr_1 = &raw mut *lvalue_side_effect(); let c2rust_fresh2 = *c2rust_lvalue_ptr_1; - *c2rust_lvalue_ptr_1 = *c2rust_lvalue_ptr_1 + 1; + *c2rust_lvalue_ptr_1 += 1; let mut post_inc: ::core::ffi::c_int = c2rust_fresh2; let c2rust_lvalue_ptr_2 = &raw mut *lvalue_side_effect(); let c2rust_fresh3 = *c2rust_lvalue_ptr_2; - *c2rust_lvalue_ptr_2 = *c2rust_lvalue_ptr_2 - 1; + *c2rust_lvalue_ptr_2 -= 1; let mut post_dec: ::core::ffi::c_int = c2rust_fresh3; } #[unsafe(no_mangle)]