Skip to content

Commit 93cb44c

Browse files
committed
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.
1 parent d3c6f71 commit 93cb44c

File tree

2 files changed

+33
-11
lines changed

2 files changed

+33
-11
lines changed

src/translate_c.zig

+17-11
Original file line numberDiff line numberDiff line change
@@ -1596,25 +1596,31 @@ 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.
16021607
const lhs_expr = stmt.getLHS();
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
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <stddef.h>
2+
ptrdiff_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+
// }

0 commit comments

Comments
 (0)