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 09bef7132d..3a2a44dd95 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -401,103 +401,151 @@ 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 = 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) + .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` @@ -600,166 +648,6 @@ impl<'c> Translation<'c> { } } - fn convert_pre_increment( - &self, - ctx: ExprContext, - expected_type_id: Option, - result_type_id: CQualTypeId, - op: CBinOp, - arg: CExprId, - ) -> TranslationResult>> { - let arg_type = self - .ast_context - .index_unwrap_parens(arg) - .kind - .get_qual_type() - .ok_or_else(|| format_err!("bad arg type"))?; - - 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.")), - 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 one_type_id = arg_type; - let mut compute_lhs_type_id = arg_type; - let mut compute_res_type_id = result_type_id; - - match self.ast_context.resolve_type(arg_type.ctype).kind { - CTypeKind::Pointer(..) => { - one_type_id = CQualTypeId::new(self.ast_context.type_for_kind(&CTypeKind::Int)); - } - CTypeKind::Enum(enum_id) => { - one_type_id = self.enum_integral_type(enum_id); - compute_lhs_type_id = one_type_id; - compute_res_type_id = one_type_id; - } - _ => {} - }; - - 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 op = op - .underlying_assignment() - .expect("not an valid assignment operator"); - let ty = 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[ty.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(ty.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 ty.qualifiers.is_volatile { - is_unsafe = true; - self.volatile_write(write, ty, 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) - }, - ) - } - pub fn convert_unary_operator( &self, ctx: ExprContext, @@ -771,34 +659,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 @@ -842,6 +710,100 @@ impl<'c> Translation<'c> { Ok(unary) } + fn convert_indecrement_operator( + &self, + ctx: ExprContext, + expected_type_id: Option, + result_type_id: CQualTypeId, + op: CUnOp, + arg: CExprId, + ) -> TranslationResult>> { + let arg_type = self + .ast_context + .index_unwrap_parens(arg) + .kind + .get_qual_type() + .ok_or_else(|| format_err!("bad arg type"))?; + + 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.")), + 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 one_type_id = arg_type; + let mut compute_lhs_type_id = arg_type; + let mut compute_res_type_id = result_type_id; + + match self.ast_context.resolve_type(arg_type.ctype).kind { + CTypeKind::Pointer(..) => { + one_type_id = CQualTypeId::new(self.ast_context.type_for_kind(&CTypeKind::Int)); + } + CTypeKind::Enum(enum_id) => { + one_type_id = self.enum_integral_type(enum_id); + compute_lhs_type_id = one_type_id; + compute_res_type_id = one_type_id; + } + _ => {} + }; + + // If we aren't going to be using the result, may as well do a simple pre-increment + let dont_yield_old_value = op.is_prefix() || ctx.is_unused(); + let op = op.underlying_compound_assignment().unwrap(); + + 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 { + 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(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, + )?; + + // Replace the assignment result with the old value + Ok(val.map(|_| old_val_expr)) + }) + } + } + fn convert_negate_operator( &self, ctx: ExprContext, 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)]