authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-08-26 21:50:14+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-08-27 06:43:40+01:00
log93cb44c80582dd02b63b02e7bb7e54d7ad8a4ebc
treeae7e7763301389f86890d011c2818000125ed1ac
parentd3c6f7179c7a6086ab9cdbaed231da9a1f0b4dee

translate-c: support GCC/Clang pointer subtraction extension

Pointer subtraction on `void *` or function pointers is UB by the C spec, but is permitted by GCC and Clang as an extension. So, avoid crashing translate-c in such cases, and follow the extension behavior -- there's nothing else that could really be intended.

2 files changed, 33 insertions(+), 11 deletions(-)

src/translate_c.zig+17-11
......@@ -1596,6 +1596,11 @@ fn transBinaryOperator(
15961596 // @divExact(@bitCast(<platform-ptrdiff_t>, @intFromPtr(lhs) -% @intFromPtr(rhs)), @sizeOf(<lhs target type>))
15971597 const ptrdiff_type = try transQualTypeIntWidthOf(c, qt, true);
15981598
1599 const bitcast = try Tag.as.create(c.arena, .{
1600 .lhs = ptrdiff_type,
1601 .rhs = try Tag.bit_cast.create(c.arena, infixOpNode),
1602 });
1603
15991604 // C standard requires that pointer subtraction operands are of the same type,
16001605 // otherwise it is undefined behavior. So we can assume the left and right
16011606 // sides are the same QualType and arbitrarily choose left.
......@@ -1603,18 +1608,19 @@ fn transBinaryOperator(
16031608 const lhs_qt = getExprQualType(c, lhs_expr);
16041609 const lhs_qt_translated = try transQualType(c, scope, lhs_qt, lhs_expr.getBeginLoc());
16051610 const c_pointer = getContainer(c, lhs_qt_translated).?;
1606 const elem_type = c_pointer.castTag(.c_pointer).?.data.elem_type;
1607 const sizeof = try Tag.sizeof.create(c.arena, elem_type);
1608
1609 const bitcast = try Tag.as.create(c.arena, .{
1610 .lhs = ptrdiff_type,
1611 .rhs = try Tag.bit_cast.create(c.arena, infixOpNode),
1612 });
16131611
1614 return Tag.div_exact.create(c.arena, .{
1615 .lhs = bitcast,
1616 .rhs = sizeof,
1617 });
1612 if (c_pointer.castTag(.c_pointer)) |c_pointer_payload| {
1613 const sizeof = try Tag.sizeof.create(c.arena, c_pointer_payload.data.elem_type);
1614 return Tag.div_exact.create(c.arena, .{
1615 .lhs = bitcast,
1616 .rhs = sizeof,
1617 });
1618 } else {
1619 // This is an opaque/incomplete type. This subtraction exhibits Undefined Behavior by the C99 spec.
1620 // However, allowing subtraction on `void *` and function pointers is a commonly used extension.
1621 // So, just return the value in byte units, mirroring the behavior of this language extension as implemented by GCC and Clang.
1622 return bitcast;
1623 }
16181624 }
16191625 return infixOpNode;
16201626}
test/cases/translate_c/void_pointer_subtraction.c created+16
......@@ -0,0 +1,16 @@
1#include <stddef.h>
2ptrdiff_t sub_ptr(void *a, void *b) {
3 return a - b;
4}
5
6// translate-c
7// c_frontend=clang
8// target=x86_64-linux
9//
10// pub export fn sub_ptr(arg_a: ?*anyopaque, arg_b: ?*anyopaque) ptrdiff_t {
11// var a = arg_a;
12// _ = &a;
13// var b = arg_b;
14// _ = &b;
15// return @as(c_long, @bitCast(@intFromPtr(a) -% @intFromPtr(b)));
16// }