Commit 4f11a88b9f
Changed files (3)
src
translate_c
src/translate_c/ast.zig
@@ -146,6 +146,8 @@ pub const Node = extern union {
align_cast,
/// @ptrCast(lhs, rhs)
ptr_cast,
+ /// @divExact(lhs, rhs)
+ div_exact,
negate,
negate_wrap,
@@ -300,6 +302,7 @@ pub const Node = extern union {
.array_access,
.std_mem_zeroinit,
.ptr_cast,
+ .div_exact,
=> Payload.BinOp,
.integer_literal,
@@ -1128,6 +1131,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
const payload = node.castTag(.ptr_cast).?.data;
return renderBuiltinCall(c, "@ptrCast", &.{ payload.lhs, payload.rhs });
},
+ .div_exact => {
+ const payload = node.castTag(.div_exact).?.data;
+ return renderBuiltinCall(c, "@divExact", &.{ payload.lhs, payload.rhs });
+ },
.sizeof => {
const payload = node.castTag(.sizeof).?.data;
return renderBuiltinCall(c, "@sizeOf", &.{payload});
@@ -1993,6 +2000,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
.call,
.array_type,
.bool_to_int,
+ .div_exact,
=> {
// no grouping needed
return renderNode(c, node);
src/translate_c.zig
@@ -1083,6 +1083,7 @@ fn transBinaryOperator(
) TransError!Node {
const op = stmt.getOpcode();
const qt = stmt.getType();
+ const isPointerDiffExpr = cIsPointerDiffExpr(c, stmt);
switch (op) {
.Assign => return try transCreateNodeAssign(c, scope, result_used, stmt.getLHS(), stmt.getRHS()),
.Comma => {
@@ -1143,7 +1144,7 @@ fn transBinaryOperator(
}
},
.Sub => {
- if (cIsUnsignedInteger(qt)) {
+ if (cIsUnsignedInteger(qt) or isPointerDiffExpr) {
op_id = .sub_wrap;
} else {
op_id = .sub;
@@ -1199,15 +1200,40 @@ fn transBinaryOperator(
const lhs = if (isBoolRes(lhs_uncasted))
try Tag.bool_to_int.create(c.arena, lhs_uncasted)
+ else if (isPointerDiffExpr)
+ try Tag.ptr_to_int.create(c.arena, lhs_uncasted)
else
lhs_uncasted;
const rhs = if (isBoolRes(rhs_uncasted))
try Tag.bool_to_int.create(c.arena, rhs_uncasted)
+ else if (isPointerDiffExpr)
+ try Tag.ptr_to_int.create(c.arena, rhs_uncasted)
else
rhs_uncasted;
- return transCreateNodeInfixOp(c, scope, op_id, lhs, rhs, result_used);
+ const infixOpNode = try transCreateNodeInfixOp(c, scope, op_id, lhs, rhs, result_used);
+ if (isPointerDiffExpr) {
+ // @divExact(@bitCast(<platform-ptrdiff_t>, @ptrToInt(lhs) -% @ptrToInt(rhs)), @sizeOf(<lhs target type>))
+ const ptrdiff_type = try transQualTypeIntWidthOf(c, qt, true);
+
+ // C standard requires that pointer subtraction operands are of the same type,
+ // otherwise it is undefined behavior. So we can assume the left and right
+ // sides are the same QualType and arbitrarily choose left.
+ const lhs_expr = stmt.getLHS();
+ const lhs_qt = getExprQualType(c, lhs_expr);
+ const lhs_qt_translated = try transQualType(c, scope, lhs_qt, lhs_expr.getBeginLoc());
+ const elem_type = lhs_qt_translated.castTag(.c_pointer).?.data.elem_type;
+ const sizeof = try Tag.sizeof.create(c.arena, elem_type);
+
+ const bitcast = try Tag.bit_cast.create(c.arena, .{ .lhs = ptrdiff_type, .rhs = infixOpNode });
+
+ return Tag.div_exact.create(c.arena, .{
+ .lhs = bitcast,
+ .rhs = sizeof,
+ });
+ }
+ return infixOpNode;
}
fn transCompoundStmtInline(
@@ -1683,6 +1709,17 @@ fn transStringLiteralAsArray(
});
}
+/// determine whether `stmt` is a "pointer subtraction expression" - a subtraction where
+/// both operands resolve to addresses. The C standard requires that both operands
+/// point to elements of the same array object, but we do not verify that here.
+fn cIsPointerDiffExpr(c: *Context, stmt: *const clang.BinaryOperator) bool {
+ const lhs = @ptrCast(*const clang.Stmt, stmt.getLHS());
+ const rhs = @ptrCast(*const clang.Stmt, stmt.getRHS());
+ return stmt.getOpcode() == .Sub and
+ qualTypeIsPtr(@ptrCast(*const clang.Expr, lhs).getType()) and
+ qualTypeIsPtr(@ptrCast(*const clang.Expr, rhs).getType());
+}
+
fn cIsEnum(qt: clang.QualType) bool {
return qt.getCanonicalType().getTypeClass() == .Enum;
}
test/run_translated_c.zig
@@ -958,4 +958,100 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
\\ return 0;
\\}
, "");
+
+ cases.add("pointer difference: scalar array w/ size truncation or negative result. Issue #7216",
+ \\#include <stdlib.h>
+ \\#include <stddef.h>
+ \\#define SIZE 10
+ \\int main() {
+ \\ int foo[SIZE];
+ \\ int *start = &foo[0];
+ \\ int *one_past_end = start + SIZE;
+ \\ ptrdiff_t diff = one_past_end - start;
+ \\ char diff_char = one_past_end - start;
+ \\ if (diff != SIZE || diff_char != SIZE) abort();
+ \\ diff = start - one_past_end;
+ \\ if (diff != -SIZE) abort();
+ \\ if (one_past_end - foo != SIZE) abort();
+ \\ if ((one_past_end - 1) - foo != SIZE - 1) abort();
+ \\ if ((start + 1) - foo != 1) abort();
+ \\ return 0;
+ \\}
+ , "");
+
+ // C standard: if the expression P points either to an element of an array object or one
+ // past the last element of an array object, and the expression Q points to the last
+ // element of the same array object, the expression ((Q)+1)-(P) has the same value as
+ // ((Q)-(P))+1 and as -((P)-((Q)+1)), and has the value zero if the expression P points
+ // one past the last element of the array object, even though the expression (Q)+1
+ // does not point to an element of the array object
+ cases.add("pointer difference: C standard edge case",
+ \\#include <stdlib.h>
+ \\#include <stddef.h>
+ \\#define SIZE 10
+ \\int main() {
+ \\ int foo[SIZE];
+ \\ int *start = &foo[0];
+ \\ int *P = start + SIZE;
+ \\ int *Q = &foo[SIZE - 1];
+ \\ if ((Q + 1) - P != 0) abort();
+ \\ if ((Q + 1) - P != (Q - P) + 1) abort();
+ \\ if ((Q + 1) - P != -(P - (Q + 1))) abort();
+ \\ return 0;
+ \\}
+ , "");
+
+ cases.add("pointer difference: unary operators",
+ \\#include <stdlib.h>
+ \\int main() {
+ \\ int foo[10];
+ \\ int *x = &foo[1];
+ \\ const int *y = &foo[5];
+ \\ if (y - x++ != 4) abort();
+ \\ if (y - x != 3) abort();
+ \\ if (y - ++x != 2) abort();
+ \\ if (y - x-- != 2) abort();
+ \\ if (y - x != 3) abort();
+ \\ if (y - --x != 4) abort();
+ \\ if (y - &foo[0] != 5) abort();
+ \\ return 0;
+ \\}
+ , "");
+
+ cases.add("pointer difference: struct array with padding",
+ \\#include <stdlib.h>
+ \\#include <stddef.h>
+ \\#define SIZE 10
+ \\typedef struct my_struct {
+ \\ int x;
+ \\ char c;
+ \\ int y;
+ \\} my_struct_t;
+ \\int main() {
+ \\ my_struct_t foo[SIZE];
+ \\ my_struct_t *start = &foo[0];
+ \\ my_struct_t *one_past_end = start + SIZE;
+ \\ ptrdiff_t diff = one_past_end - start;
+ \\ int diff_int = one_past_end - start;
+ \\ if (diff != SIZE || diff_int != SIZE) abort();
+ \\ diff = start - one_past_end;
+ \\ if (diff != -SIZE) abort();
+ \\ return 0;
+ \\}
+ , "");
+
+ cases.add("pointer difference: array of function pointers",
+ \\#include <stdlib.h>
+ \\int a(void) { return 1;}
+ \\int b(void) { return 2;}
+ \\int c(void) { return 3;}
+ \\typedef int (*myfunc)(void);
+ \\int main() {
+ \\ myfunc arr[] = {a, b, c, a, b, c};
+ \\ myfunc *f1 = &arr[1];
+ \\ myfunc *f4 = &arr[4];
+ \\ if (f4 - f1 != 3) abort();
+ \\ return 0;
+ \\}
+ , "");
}