| author | |
| committer | |
| log | 4f11a88b9f21a365cf2281e840ed12b0d4e687a6 |
| tree | 2cdfb246283630b20a5ab1753175d4e724f42c3c |
| parent | 53cc63f0c91ce9f6f9c81f87e2673c5adfe1afe7 |
When two pointers are subtracted, both shall point to elements of the
same array object, or one past the last element of the array object;
the result is the difference of the subscripts of the two array elements.
The size of the result is implementation-defined, and its type
(a signed integer type) is ptrdiff_t defined in the <stddef.h> header.
If the result is not representable in an object of that type,
the behavior is undefined.
See C Standard, §6.5.6 [ISO/IEC 9899:2011]
Fixes #72163 files changed, 143 insertions(+), 2 deletions(-)
src/translate_c.zig+39-2| ... | ... | @@ -1083,6 +1083,7 @@ fn transBinaryOperator( |
| 1083 | 1083 | ) TransError!Node { |
| 1084 | 1084 | const op = stmt.getOpcode(); |
| 1085 | 1085 | const qt = stmt.getType(); |
| 1086 | const isPointerDiffExpr = cIsPointerDiffExpr(c, stmt); | |
| 1086 | 1087 | switch (op) { |
| 1087 | 1088 | .Assign => return try transCreateNodeAssign(c, scope, result_used, stmt.getLHS(), stmt.getRHS()), |
| 1088 | 1089 | .Comma => { |
| ... | ... | @@ -1143,7 +1144,7 @@ fn transBinaryOperator( |
| 1143 | 1144 | } |
| 1144 | 1145 | }, |
| 1145 | 1146 | .Sub => { |
| 1146 | if (cIsUnsignedInteger(qt)) { | |
| 1147 | if (cIsUnsignedInteger(qt) or isPointerDiffExpr) { | |
| 1147 | 1148 | op_id = .sub_wrap; |
| 1148 | 1149 | } else { |
| 1149 | 1150 | op_id = .sub; |
| ... | ... | @@ -1199,15 +1200,40 @@ fn transBinaryOperator( |
| 1199 | 1200 | |
| 1200 | 1201 | const lhs = if (isBoolRes(lhs_uncasted)) |
| 1201 | 1202 | try Tag.bool_to_int.create(c.arena, lhs_uncasted) |
| 1203 | else if (isPointerDiffExpr) | |
| 1204 | try Tag.ptr_to_int.create(c.arena, lhs_uncasted) | |
| 1202 | 1205 | else |
| 1203 | 1206 | lhs_uncasted; |
| 1204 | 1207 | |
| 1205 | 1208 | const rhs = if (isBoolRes(rhs_uncasted)) |
| 1206 | 1209 | try Tag.bool_to_int.create(c.arena, rhs_uncasted) |
| 1210 | else if (isPointerDiffExpr) | |
| 1211 | try Tag.ptr_to_int.create(c.arena, rhs_uncasted) | |
| 1207 | 1212 | else |
| 1208 | 1213 | rhs_uncasted; |
| 1209 | 1214 | |
| 1210 | return transCreateNodeInfixOp(c, scope, op_id, lhs, rhs, result_used); | |
| 1215 | const infixOpNode = try transCreateNodeInfixOp(c, scope, op_id, lhs, rhs, result_used); | |
| 1216 | if (isPointerDiffExpr) { | |
| 1217 | // @divExact(@bitCast(<platform-ptrdiff_t>, @ptrToInt(lhs) -% @ptrToInt(rhs)), @sizeOf(<lhs target type>)) | |
| 1218 | const ptrdiff_type = try transQualTypeIntWidthOf(c, qt, true); | |
| 1219 | ||
| 1220 | // C standard requires that pointer subtraction operands are of the same type, | |
| 1221 | // otherwise it is undefined behavior. So we can assume the left and right | |
| 1222 | // sides are the same QualType and arbitrarily choose left. | |
| 1223 | const lhs_expr = stmt.getLHS(); | |
| 1224 | const lhs_qt = getExprQualType(c, lhs_expr); | |
| 1225 | const lhs_qt_translated = try transQualType(c, scope, lhs_qt, lhs_expr.getBeginLoc()); | |
| 1226 | const elem_type = lhs_qt_translated.castTag(.c_pointer).?.data.elem_type; | |
| 1227 | const sizeof = try Tag.sizeof.create(c.arena, elem_type); | |
| 1228 | ||
| 1229 | const bitcast = try Tag.bit_cast.create(c.arena, .{ .lhs = ptrdiff_type, .rhs = infixOpNode }); | |
| 1230 | ||
| 1231 | return Tag.div_exact.create(c.arena, .{ | |
| 1232 | .lhs = bitcast, | |
| 1233 | .rhs = sizeof, | |
| 1234 | }); | |
| 1235 | } | |
| 1236 | return infixOpNode; | |
| 1211 | 1237 | } |
| 1212 | 1238 | |
| 1213 | 1239 | fn transCompoundStmtInline( |
| ... | ... | @@ -1683,6 +1709,17 @@ fn transStringLiteralAsArray( |
| 1683 | 1709 | }); |
| 1684 | 1710 | } |
| 1685 | 1711 | |
| 1712 | /// determine whether `stmt` is a "pointer subtraction expression" - a subtraction where | |
| 1713 | /// both operands resolve to addresses. The C standard requires that both operands | |
| 1714 | /// point to elements of the same array object, but we do not verify that here. | |
| 1715 | fn cIsPointerDiffExpr(c: *Context, stmt: *const clang.BinaryOperator) bool { | |
| 1716 | const lhs = @ptrCast(*const clang.Stmt, stmt.getLHS()); | |
| 1717 | const rhs = @ptrCast(*const clang.Stmt, stmt.getRHS()); | |
| 1718 | return stmt.getOpcode() == .Sub and | |
| 1719 | qualTypeIsPtr(@ptrCast(*const clang.Expr, lhs).getType()) and | |
| 1720 | qualTypeIsPtr(@ptrCast(*const clang.Expr, rhs).getType()); | |
| 1721 | } | |
| 1722 | ||
| 1686 | 1723 | fn cIsEnum(qt: clang.QualType) bool { |
| 1687 | 1724 | return qt.getCanonicalType().getTypeClass() == .Enum; |
| 1688 | 1725 | } |
src/translate_c/ast.zig+8| ... | ... | @@ -146,6 +146,8 @@ pub const Node = extern union { |
| 146 | 146 | align_cast, |
| 147 | 147 | /// @ptrCast(lhs, rhs) |
| 148 | 148 | ptr_cast, |
| 149 | /// @divExact(lhs, rhs) | |
| 150 | div_exact, | |
| 149 | 151 | |
| 150 | 152 | negate, |
| 151 | 153 | negate_wrap, |
| ... | ... | @@ -300,6 +302,7 @@ pub const Node = extern union { |
| 300 | 302 | .array_access, |
| 301 | 303 | .std_mem_zeroinit, |
| 302 | 304 | .ptr_cast, |
| 305 | .div_exact, | |
| 303 | 306 | => Payload.BinOp, |
| 304 | 307 | |
| 305 | 308 | .integer_literal, |
| ... | ... | @@ -1128,6 +1131,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { |
| 1128 | 1131 | const payload = node.castTag(.ptr_cast).?.data; |
| 1129 | 1132 | return renderBuiltinCall(c, "@ptrCast", &.{ payload.lhs, payload.rhs }); |
| 1130 | 1133 | }, |
| 1134 | .div_exact => { | |
| 1135 | const payload = node.castTag(.div_exact).?.data; | |
| 1136 | return renderBuiltinCall(c, "@divExact", &.{ payload.lhs, payload.rhs }); | |
| 1137 | }, | |
| 1131 | 1138 | .sizeof => { |
| 1132 | 1139 | const payload = node.castTag(.sizeof).?.data; |
| 1133 | 1140 | return renderBuiltinCall(c, "@sizeOf", &.{payload}); |
| ... | ... | @@ -1993,6 +2000,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { |
| 1993 | 2000 | .call, |
| 1994 | 2001 | .array_type, |
| 1995 | 2002 | .bool_to_int, |
| 2003 | .div_exact, | |
| 1996 | 2004 | => { |
| 1997 | 2005 | // no grouping needed |
| 1998 | 2006 | return renderNode(c, node); |
test/run_translated_c.zig+96| ... | ... | @@ -958,4 +958,100 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 958 | 958 | \\ return 0; |
| 959 | 959 | \\} |
| 960 | 960 | , ""); |
| 961 | ||
| 962 | cases.add("pointer difference: scalar array w/ size truncation or negative result. Issue #7216", | |
| 963 | \\#include <stdlib.h> | |
| 964 | \\#include <stddef.h> | |
| 965 | \\#define SIZE 10 | |
| 966 | \\int main() { | |
| 967 | \\ int foo[SIZE]; | |
| 968 | \\ int *start = &foo[0]; | |
| 969 | \\ int *one_past_end = start + SIZE; | |
| 970 | \\ ptrdiff_t diff = one_past_end - start; | |
| 971 | \\ char diff_char = one_past_end - start; | |
| 972 | \\ if (diff != SIZE || diff_char != SIZE) abort(); | |
| 973 | \\ diff = start - one_past_end; | |
| 974 | \\ if (diff != -SIZE) abort(); | |
| 975 | \\ if (one_past_end - foo != SIZE) abort(); | |
| 976 | \\ if ((one_past_end - 1) - foo != SIZE - 1) abort(); | |
| 977 | \\ if ((start + 1) - foo != 1) abort(); | |
| 978 | \\ return 0; | |
| 979 | \\} | |
| 980 | , ""); | |
| 981 | ||
| 982 | // C standard: if the expression P points either to an element of an array object or one | |
| 983 | // past the last element of an array object, and the expression Q points to the last | |
| 984 | // element of the same array object, the expression ((Q)+1)-(P) has the same value as | |
| 985 | // ((Q)-(P))+1 and as -((P)-((Q)+1)), and has the value zero if the expression P points | |
| 986 | // one past the last element of the array object, even though the expression (Q)+1 | |
| 987 | // does not point to an element of the array object | |
| 988 | cases.add("pointer difference: C standard edge case", | |
| 989 | \\#include <stdlib.h> | |
| 990 | \\#include <stddef.h> | |
| 991 | \\#define SIZE 10 | |
| 992 | \\int main() { | |
| 993 | \\ int foo[SIZE]; | |
| 994 | \\ int *start = &foo[0]; | |
| 995 | \\ int *P = start + SIZE; | |
| 996 | \\ int *Q = &foo[SIZE - 1]; | |
| 997 | \\ if ((Q + 1) - P != 0) abort(); | |
| 998 | \\ if ((Q + 1) - P != (Q - P) + 1) abort(); | |
| 999 | \\ if ((Q + 1) - P != -(P - (Q + 1))) abort(); | |
| 1000 | \\ return 0; | |
| 1001 | \\} | |
| 1002 | , ""); | |
| 1003 | ||
| 1004 | cases.add("pointer difference: unary operators", | |
| 1005 | \\#include <stdlib.h> | |
| 1006 | \\int main() { | |
| 1007 | \\ int foo[10]; | |
| 1008 | \\ int *x = &foo[1]; | |
| 1009 | \\ const int *y = &foo[5]; | |
| 1010 | \\ if (y - x++ != 4) abort(); | |
| 1011 | \\ if (y - x != 3) abort(); | |
| 1012 | \\ if (y - ++x != 2) abort(); | |
| 1013 | \\ if (y - x-- != 2) abort(); | |
| 1014 | \\ if (y - x != 3) abort(); | |
| 1015 | \\ if (y - --x != 4) abort(); | |
| 1016 | \\ if (y - &foo[0] != 5) abort(); | |
| 1017 | \\ return 0; | |
| 1018 | \\} | |
| 1019 | , ""); | |
| 1020 | ||
| 1021 | cases.add("pointer difference: struct array with padding", | |
| 1022 | \\#include <stdlib.h> | |
| 1023 | \\#include <stddef.h> | |
| 1024 | \\#define SIZE 10 | |
| 1025 | \\typedef struct my_struct { | |
| 1026 | \\ int x; | |
| 1027 | \\ char c; | |
| 1028 | \\ int y; | |
| 1029 | \\} my_struct_t; | |
| 1030 | \\int main() { | |
| 1031 | \\ my_struct_t foo[SIZE]; | |
| 1032 | \\ my_struct_t *start = &foo[0]; | |
| 1033 | \\ my_struct_t *one_past_end = start + SIZE; | |
| 1034 | \\ ptrdiff_t diff = one_past_end - start; | |
| 1035 | \\ int diff_int = one_past_end - start; | |
| 1036 | \\ if (diff != SIZE || diff_int != SIZE) abort(); | |
| 1037 | \\ diff = start - one_past_end; | |
| 1038 | \\ if (diff != -SIZE) abort(); | |
| 1039 | \\ return 0; | |
| 1040 | \\} | |
| 1041 | , ""); | |
| 1042 | ||
| 1043 | cases.add("pointer difference: array of function pointers", | |
| 1044 | \\#include <stdlib.h> | |
| 1045 | \\int a(void) { return 1;} | |
| 1046 | \\int b(void) { return 2;} | |
| 1047 | \\int c(void) { return 3;} | |
| 1048 | \\typedef int (*myfunc)(void); | |
| 1049 | \\int main() { | |
| 1050 | \\ myfunc arr[] = {a, b, c, a, b, c}; | |
| 1051 | \\ myfunc *f1 = &arr[1]; | |
| 1052 | \\ myfunc *f4 = &arr[4]; | |
| 1053 | \\ if (f4 - f1 != 3) abort(); | |
| 1054 | \\ return 0; | |
| 1055 | \\} | |
| 1056 | , ""); | |
| 961 | 1057 | } |