From 56d3fb28b0b3aabb6033092f4dc4518ca8346d79 Mon Sep 17 00:00:00 2001 From: Rua Date: Mon, 8 Jun 2026 21:50:45 +0200 Subject: [PATCH 1/4] transpile: Add `pointer_arithmetic` test to `exprs.c` --- c2rust-transpile/tests/snapshots/exprs.c | 11 +++++++++++ .../snapshots__transpile@exprs.c.2021.clang15.snap | 11 +++++++++++ .../snapshots__transpile@exprs.c.2024.clang15.snap | 11 +++++++++++ 3 files changed, 33 insertions(+) diff --git a/c2rust-transpile/tests/snapshots/exprs.c b/c2rust-transpile/tests/snapshots/exprs.c index 633e854f9f..e19ec52801 100644 --- a/c2rust-transpile/tests/snapshots/exprs.c +++ b/c2rust-transpile/tests/snapshots/exprs.c @@ -1,3 +1,5 @@ +#include + int puts(const char *str); static int side_effect(){ @@ -66,3 +68,12 @@ void statement_expr() { puts("should be unreachable!"); } + +void pointer_arithmetic(void) { + int i1[] = { 0, 1 }; + int i2[] = { 10, 11 }; + int *p1 = i1; + int *p2 = i2; + ptrdiff_t diff = p1 - p2; + int diff_int = p1 - p2; +} 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 56ee5f78bf..8b3f4d7776 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 @@ -15,6 +15,7 @@ expression: cat tests/snapshots/exprs.2021.clang15.rs extern "C" { fn puts(str: *const ::core::ffi::c_char) -> ::core::ffi::c_int; } +pub type ptrdiff_t = isize; pub type E = ::core::ffi::c_uint; pub const EA: E = 0; pub type int_t = ::core::ffi::c_int; @@ -82,3 +83,13 @@ pub unsafe extern "C" fn statement_expr() { }; puts(b"should be unreachable!\0".as_ptr() as *const ::core::ffi::c_char); } +#[no_mangle] +pub unsafe extern "C" fn pointer_arithmetic() { + let mut i1: [::core::ffi::c_int; 2] = [0 as ::core::ffi::c_int, 1 as ::core::ffi::c_int]; + let mut i2: [::core::ffi::c_int; 2] = [10 as ::core::ffi::c_int, 11 as ::core::ffi::c_int]; + let mut p1: *mut ::core::ffi::c_int = &raw mut i1 as *mut ::core::ffi::c_int; + let mut p2: *mut ::core::ffi::c_int = &raw mut i2 as *mut ::core::ffi::c_int; + let mut diff: ptrdiff_t = p1.offset_from(p2) as ptrdiff_t; + let mut diff_int: ::core::ffi::c_int = + p1.offset_from(p2) as ::core::ffi::c_long as ::core::ffi::c_int; +} 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 04e6019c9c..c8d71c429a 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 @@ -15,6 +15,7 @@ expression: cat tests/snapshots/exprs.2024.clang15.rs unsafe extern "C" { unsafe fn puts(str: *const ::core::ffi::c_char) -> ::core::ffi::c_int; } +pub type ptrdiff_t = isize; pub type E = ::core::ffi::c_uint; pub const EA: E = 0; pub type int_t = ::core::ffi::c_int; @@ -82,3 +83,13 @@ pub unsafe extern "C" fn statement_expr() { }; puts(b"should be unreachable!\0".as_ptr() as *const ::core::ffi::c_char); } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pointer_arithmetic() { + let mut i1: [::core::ffi::c_int; 2] = [0 as ::core::ffi::c_int, 1 as ::core::ffi::c_int]; + let mut i2: [::core::ffi::c_int; 2] = [10 as ::core::ffi::c_int, 11 as ::core::ffi::c_int]; + let mut p1: *mut ::core::ffi::c_int = &raw mut i1 as *mut ::core::ffi::c_int; + let mut p2: *mut ::core::ffi::c_int = &raw mut i2 as *mut ::core::ffi::c_int; + let mut diff: ptrdiff_t = p1.offset_from(p2) as ptrdiff_t; + let mut diff_int: ::core::ffi::c_int = + p1.offset_from(p2) as ::core::ffi::c_long as ::core::ffi::c_int; +} From 875ef75be342f658e159e1e15c52afc1bf83402f Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 13 Jun 2026 16:16:35 +0200 Subject: [PATCH 2/4] transpile: Split off `make_pointer_difference` --- c2rust-transpile/src/translator/operators.rs | 10 ++-------- c2rust-transpile/src/translator/pointers.rs | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index a61eac8cdd..f937d0b179 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -576,14 +576,8 @@ impl<'c> Translation<'c> { let rhs_type = &self.ast_context.resolve_type(rhs_type_id.ctype).kind; if let &CTypeKind::Pointer(pointee) = rhs_type { - let mut offset = mk().method_call_expr(lhs, "offset_from", vec![rhs]); - - if let Some(sz) = self.compute_size_of_expr(pointee.ctype) { - let div = cast_int(sz, "isize", false); - offset = mk().binary_expr(BinOp::Div(Default::default()), offset, div); - } - - Ok(WithStmts::new_val(mk().cast_expr(offset, ty)).set_unsafe()) + let val = self.make_pointer_difference(lhs, rhs, pointee.ctype); + Ok(val.map(|val| mk().cast_expr(val, ty))) } else if let &CTypeKind::Pointer(pointee) = lhs_type { Ok(self.convert_pointer_offset(lhs, rhs, pointee.ctype, true, false)) } else if lhs_type.is_unsigned_integral_type() { diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index 5428ce9e6b..a7bb615ffc 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -385,6 +385,23 @@ impl<'c> Translation<'c> { WithStmts::new_val(res).set_unsafe() } + /// Creates a pointer difference expression. Returns an expression of type `isize`. + pub fn make_pointer_difference( + &self, + lhs_rs: Box, + rhs_rs: Box, + pointee_type_id: CTypeId, + ) -> WithStmts> { + let mut expr_rs = mk().method_call_expr(lhs_rs, "offset_from", vec![rhs_rs]); + + if let Some(sz) = self.compute_size_of_expr(pointee_type_id) { + let div_rs = cast_int(sz, "isize", false); + expr_rs = mk().binary_expr(BinOp::Div(Default::default()), expr_rs, div_rs); + } + + WithStmts::new_val(expr_rs).set_unsafe() + } + /// Construct an expression for a NULL at any type, including forward declarations, /// function pointers, and normal pointers. pub fn null_ptr(&self, type_id: CTypeId) -> TranslationResult> { From ab16ca4d7334e79bc6e85d2cbcda22343ab91634 Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 9 Jun 2026 17:19:02 +0200 Subject: [PATCH 3/4] transpile: In `convert_subtraction`, only cast if types differ --- c2rust-transpile/src/translator/operators.rs | 32 +++++++++---------- ...shots__transpile@exprs.c.2021.clang15.snap | 2 +- ...shots__transpile@exprs.c.2024.clang15.snap | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index f937d0b179..68c2ce55ee 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -66,8 +66,6 @@ impl<'c> Translation<'c> { ctx = ctx.decay_ref(); } - let ty = self.convert_type(expr_type_id.ctype)?; - let lhs_kind = &self.ast_context.index_unwrap_parens(lhs).kind; let mut lhs_type_id = lhs_kind.get_qual_type().ok_or_else(|| { format_translation_err!( @@ -174,9 +172,9 @@ impl<'c> Translation<'c> { lhs_val.zip(rhs_val).and_then_try(|(lhs_val, rhs_val)| { self.convert_binary_operator( + ctx, + expr_type_id, op, - ty, - expr_type_id.ctype, lhs_type_id, rhs_type_id, lhs_val, @@ -218,12 +216,11 @@ impl<'c> Translation<'c> { WithStmts::new_val(read.clone()), )?; - let ty = self.convert_type(compute_res_type_id.ctype)?; let val = lhs.and_then_try(|lhs| { self.convert_binary_operator( + ctx, + compute_res_type_id, bin_op, - ty, - compute_res_type_id.ctype, compute_lhs_type_id, rhs_type_id, lhs, @@ -426,12 +423,11 @@ impl<'c> Translation<'c> { WithStmts::new_val(read.clone()), )?; - let ty = self.convert_type(result_type_id.ctype)?; let val = lhs.and_then_try(|lhs| self.convert_binary_operator( + ctx, + result_type_id, op, - ty, - result_type_id.ctype, expr_or_comp_type_id, rhs_type_id, lhs, @@ -503,9 +499,9 @@ impl<'c> Translation<'c> { /// arguments be usable as rvalues. fn convert_binary_operator( &self, + ctx: ExprContext, + expr_type_id: CQualTypeId, op: CBinOp, - ty: Box, - ctype: CTypeId, lhs_type: CQualTypeId, rhs_type: CQualTypeId, lhs: Box, @@ -513,13 +509,15 @@ impl<'c> Translation<'c> { ) -> TranslationResult>> { let is_unsigned_integral_type = self .ast_context - .resolve_type(ctype) + .resolve_type(expr_type_id.ctype) .kind .is_unsigned_integral_type(); Ok(WithStmts::new_val(match op { CBinOp::Add => return self.convert_addition(lhs_type, rhs_type, lhs, rhs), - CBinOp::Subtract => return self.convert_subtraction(ty, lhs_type, rhs_type, lhs, rhs), + CBinOp::Subtract => { + return self.convert_subtraction(ctx, expr_type_id, lhs_type, rhs_type, lhs, rhs) + } op if op.is_arithmetic() && is_unsigned_integral_type => { mk().method_call_expr(lhs, op.wrapping_method(), vec![rhs]) @@ -566,7 +564,8 @@ impl<'c> Translation<'c> { fn convert_subtraction( &self, - ty: Box, + ctx: ExprContext, + expr_type_id: CQualTypeId, lhs_type_id: CQualTypeId, rhs_type_id: CQualTypeId, lhs: Box, @@ -577,7 +576,8 @@ impl<'c> Translation<'c> { if let &CTypeKind::Pointer(pointee) = rhs_type { let val = self.make_pointer_difference(lhs, rhs, pointee.ctype); - Ok(val.map(|val| mk().cast_expr(val, ty))) + let source_type_id = self.ast_context.type_for_kind(&CTypeKind::PtrDiff).unwrap(); + self.make_cast(ctx, CQualTypeId::new(source_type_id), expr_type_id, val) } else if let &CTypeKind::Pointer(pointee) = lhs_type { Ok(self.convert_pointer_offset(lhs, rhs, pointee.ctype, true, false)) } else if lhs_type.is_unsigned_integral_type() { 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 8b3f4d7776..906e9a1865 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 @@ -89,7 +89,7 @@ pub unsafe extern "C" fn pointer_arithmetic() { let mut i2: [::core::ffi::c_int; 2] = [10 as ::core::ffi::c_int, 11 as ::core::ffi::c_int]; let mut p1: *mut ::core::ffi::c_int = &raw mut i1 as *mut ::core::ffi::c_int; let mut p2: *mut ::core::ffi::c_int = &raw mut i2 as *mut ::core::ffi::c_int; - let mut diff: ptrdiff_t = p1.offset_from(p2) as ptrdiff_t; + let mut diff: ptrdiff_t = p1.offset_from(p2); let mut diff_int: ::core::ffi::c_int = p1.offset_from(p2) as ::core::ffi::c_long as ::core::ffi::c_int; } 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 c8d71c429a..16fb537e08 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 @@ -89,7 +89,7 @@ pub unsafe extern "C" fn pointer_arithmetic() { let mut i2: [::core::ffi::c_int; 2] = [10 as ::core::ffi::c_int, 11 as ::core::ffi::c_int]; let mut p1: *mut ::core::ffi::c_int = &raw mut i1 as *mut ::core::ffi::c_int; let mut p2: *mut ::core::ffi::c_int = &raw mut i2 as *mut ::core::ffi::c_int; - let mut diff: ptrdiff_t = p1.offset_from(p2) as ptrdiff_t; + let mut diff: ptrdiff_t = p1.offset_from(p2); let mut diff_int: ::core::ffi::c_int = p1.offset_from(p2) as ::core::ffi::c_long as ::core::ffi::c_int; } From dcf4b5616c9910bbfce2c8488c5cae76bb9cc26a Mon Sep 17 00:00:00 2001 From: Rua Date: Sun, 14 Jun 2026 18:38:06 +0200 Subject: [PATCH 4/4] transpile: Override pointer difference expression type to `PtrDiff` --- c2rust-transpile/src/c_ast/mod.rs | 30 ++++++++++++------- ...shots__transpile@exprs.c.2021.clang15.snap | 3 +- ...shots__transpile@exprs.c.2024.clang15.snap | 3 +- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index 2b8a76e608..1d4fc80d1a 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1356,19 +1356,29 @@ impl TypedAstContext { 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 neither_ptr = !lhs_resolved_ty.kind.is_pointer() - && !rhs_resolved_ty.kind.is_pointer(); - - if op.all_types_same() && neither_ptr { - if CTypeKind::PULLBACK_KINDS.contains(&lhs_resolved_ty.kind) { + if op == CBinOp::Subtract + && lhs_resolved_ty.kind.is_pointer() + && rhs_resolved_ty.kind.is_pointer() + { + // Pointer difference operator should return `ptrdiff_t`. + let new_type_id = + self.ast_context.type_for_kind(&CTypeKind::PtrDiff).unwrap(); + Some(CQualTypeId::new(new_type_id)) + } else { + let neither_ptr = !lhs_resolved_ty.kind.is_pointer() + && !rhs_resolved_ty.kind.is_pointer(); + + if op.all_types_same() && neither_ptr { + if CTypeKind::PULLBACK_KINDS.contains(&lhs_resolved_ty.kind) { + Some(lhs_type_id) + } else { + Some(rhs_type_id) + } + } else if op.is_bitshift() { Some(lhs_type_id) } else { - Some(rhs_type_id) + return; } - } else if op.is_bitshift() { - Some(lhs_type_id) - } else { - return; } } CExprKind::Unary(_ty, op, e, _idk) => op.expected_result_type( 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 906e9a1865..9dee962ce1 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 @@ -90,6 +90,5 @@ pub unsafe extern "C" fn pointer_arithmetic() { let mut p1: *mut ::core::ffi::c_int = &raw mut i1 as *mut ::core::ffi::c_int; let mut p2: *mut ::core::ffi::c_int = &raw mut i2 as *mut ::core::ffi::c_int; let mut diff: ptrdiff_t = p1.offset_from(p2); - let mut diff_int: ::core::ffi::c_int = - p1.offset_from(p2) as ::core::ffi::c_long as ::core::ffi::c_int; + let mut diff_int: ::core::ffi::c_int = p1.offset_from(p2) as ::core::ffi::c_int; } 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 16fb537e08..c0b7086b9b 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 @@ -90,6 +90,5 @@ pub unsafe extern "C" fn pointer_arithmetic() { let mut p1: *mut ::core::ffi::c_int = &raw mut i1 as *mut ::core::ffi::c_int; let mut p2: *mut ::core::ffi::c_int = &raw mut i2 as *mut ::core::ffi::c_int; let mut diff: ptrdiff_t = p1.offset_from(p2); - let mut diff_int: ::core::ffi::c_int = - p1.offset_from(p2) as ::core::ffi::c_long as ::core::ffi::c_int; + let mut diff_int: ::core::ffi::c_int = p1.offset_from(p2) as ::core::ffi::c_int; }