| 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,6 +1083,7 @@ fn transBinaryOperator( |
| 1083 | ) TransError!Node { | 1083 | ) TransError!Node { |
| 1084 | const op = stmt.getOpcode(); | 1084 | const op = stmt.getOpcode(); |
| 1085 | const qt = stmt.getType(); | 1085 | const qt = stmt.getType(); |
| 1086 | const isPointerDiffExpr = cIsPointerDiffExpr(c, stmt); | ||
| 1086 | switch (op) { | 1087 | switch (op) { |
| 1087 | .Assign => return try transCreateNodeAssign(c, scope, result_used, stmt.getLHS(), stmt.getRHS()), | 1088 | .Assign => return try transCreateNodeAssign(c, scope, result_used, stmt.getLHS(), stmt.getRHS()), |
| 1088 | .Comma => { | 1089 | .Comma => { |
| ... | @@ -1143,7 +1144,7 @@ fn transBinaryOperator( | ... | @@ -1143,7 +1144,7 @@ fn transBinaryOperator( |
| 1143 | } | 1144 | } |
| 1144 | }, | 1145 | }, |
| 1145 | .Sub => { | 1146 | .Sub => { |
| 1146 | if (cIsUnsignedInteger(qt)) { | 1147 | if (cIsUnsignedInteger(qt) or isPointerDiffExpr) { |
| 1147 | op_id = .sub_wrap; | 1148 | op_id = .sub_wrap; |
| 1148 | } else { | 1149 | } else { |
| 1149 | op_id = .sub; | 1150 | op_id = .sub; |
| ... | @@ -1199,15 +1200,40 @@ fn transBinaryOperator( | ... | @@ -1199,15 +1200,40 @@ fn transBinaryOperator( |
| 1199 | 1200 | ||
| 1200 | const lhs = if (isBoolRes(lhs_uncasted)) | 1201 | const lhs = if (isBoolRes(lhs_uncasted)) |
| 1201 | try Tag.bool_to_int.create(c.arena, lhs_uncasted) | 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 | else | 1205 | else |
| 1203 | lhs_uncasted; | 1206 | lhs_uncasted; |
| 1204 | 1207 | ||
| 1205 | const rhs = if (isBoolRes(rhs_uncasted)) | 1208 | const rhs = if (isBoolRes(rhs_uncasted)) |
| 1206 | try Tag.bool_to_int.create(c.arena, rhs_uncasted) | 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 | else | 1212 | else |
| 1208 | rhs_uncasted; | 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 | fn transCompoundStmtInline( | 1239 | fn transCompoundStmtInline( |
| ... | @@ -1683,6 +1709,17 @@ fn transStringLiteralAsArray( | ... | @@ -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 | fn cIsEnum(qt: clang.QualType) bool { | 1723 | fn cIsEnum(qt: clang.QualType) bool { |
| 1687 | return qt.getCanonicalType().getTypeClass() == .Enum; | 1724 | return qt.getCanonicalType().getTypeClass() == .Enum; |
| 1688 | } | 1725 | } |
src/translate_c/ast.zig+8| ... | @@ -146,6 +146,8 @@ pub const Node = extern union { | ... | @@ -146,6 +146,8 @@ pub const Node = extern union { |
| 146 | align_cast, | 146 | align_cast, |
| 147 | /// @ptrCast(lhs, rhs) | 147 | /// @ptrCast(lhs, rhs) |
| 148 | ptr_cast, | 148 | ptr_cast, |
| 149 | /// @divExact(lhs, rhs) | ||
| 150 | div_exact, | ||
| 149 | 151 | ||
| 150 | negate, | 152 | negate, |
| 151 | negate_wrap, | 153 | negate_wrap, |
| ... | @@ -300,6 +302,7 @@ pub const Node = extern union { | ... | @@ -300,6 +302,7 @@ pub const Node = extern union { |
| 300 | .array_access, | 302 | .array_access, |
| 301 | .std_mem_zeroinit, | 303 | .std_mem_zeroinit, |
| 302 | .ptr_cast, | 304 | .ptr_cast, |
| 305 | .div_exact, | ||
| 303 | => Payload.BinOp, | 306 | => Payload.BinOp, |
| 304 | 307 | ||
| 305 | .integer_literal, | 308 | .integer_literal, |
| ... | @@ -1128,6 +1131,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { | ... | @@ -1128,6 +1131,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { |
| 1128 | const payload = node.castTag(.ptr_cast).?.data; | 1131 | const payload = node.castTag(.ptr_cast).?.data; |
| 1129 | return renderBuiltinCall(c, "@ptrCast", &.{ payload.lhs, payload.rhs }); | 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 | .sizeof => { | 1138 | .sizeof => { |
| 1132 | const payload = node.castTag(.sizeof).?.data; | 1139 | const payload = node.castTag(.sizeof).?.data; |
| 1133 | return renderBuiltinCall(c, "@sizeOf", &.{payload}); | 1140 | return renderBuiltinCall(c, "@sizeOf", &.{payload}); |
| ... | @@ -1993,6 +2000,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { | ... | @@ -1993,6 +2000,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { |
| 1993 | .call, | 2000 | .call, |
| 1994 | .array_type, | 2001 | .array_type, |
| 1995 | .bool_to_int, | 2002 | .bool_to_int, |
| 2003 | .div_exact, | ||
| 1996 | => { | 2004 | => { |
| 1997 | // no grouping needed | 2005 | // no grouping needed |
| 1998 | return renderNode(c, node); | 2006 | return renderNode(c, node); |
test/run_translated_c.zig+96| ... | @@ -958,4 +958,100 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { | ... | @@ -958,4 +958,100 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 958 | \\ return 0; | 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 | } |