mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
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.
This commit is contained in:
parent
d3c6f7179c
commit
93cb44c805
2 changed files with 33 additions and 11 deletions
|
|
@ -1596,6 +1596,11 @@ fn transBinaryOperator(
|
||||||
// @divExact(@bitCast(<platform-ptrdiff_t>, @intFromPtr(lhs) -% @intFromPtr(rhs)), @sizeOf(<lhs target type>))
|
// @divExact(@bitCast(<platform-ptrdiff_t>, @intFromPtr(lhs) -% @intFromPtr(rhs)), @sizeOf(<lhs target type>))
|
||||||
const ptrdiff_type = try transQualTypeIntWidthOf(c, qt, true);
|
const ptrdiff_type = try transQualTypeIntWidthOf(c, qt, true);
|
||||||
|
|
||||||
|
const bitcast = try Tag.as.create(c.arena, .{
|
||||||
|
.lhs = ptrdiff_type,
|
||||||
|
.rhs = try Tag.bit_cast.create(c.arena, infixOpNode),
|
||||||
|
});
|
||||||
|
|
||||||
// C standard requires that pointer subtraction operands are of the same type,
|
// 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
|
// otherwise it is undefined behavior. So we can assume the left and right
|
||||||
// sides are the same QualType and arbitrarily choose left.
|
// sides are the same QualType and arbitrarily choose left.
|
||||||
|
|
@ -1603,18 +1608,19 @@ fn transBinaryOperator(
|
||||||
const lhs_qt = getExprQualType(c, lhs_expr);
|
const lhs_qt = getExprQualType(c, lhs_expr);
|
||||||
const lhs_qt_translated = try transQualType(c, scope, lhs_qt, lhs_expr.getBeginLoc());
|
const lhs_qt_translated = try transQualType(c, scope, lhs_qt, lhs_expr.getBeginLoc());
|
||||||
const c_pointer = getContainer(c, lhs_qt_translated).?;
|
const c_pointer = getContainer(c, lhs_qt_translated).?;
|
||||||
const elem_type = c_pointer.castTag(.c_pointer).?.data.elem_type;
|
|
||||||
const sizeof = try Tag.sizeof.create(c.arena, elem_type);
|
|
||||||
|
|
||||||
const bitcast = try Tag.as.create(c.arena, .{
|
|
||||||
.lhs = ptrdiff_type,
|
|
||||||
.rhs = try Tag.bit_cast.create(c.arena, infixOpNode),
|
|
||||||
});
|
|
||||||
|
|
||||||
|
if (c_pointer.castTag(.c_pointer)) |c_pointer_payload| {
|
||||||
|
const sizeof = try Tag.sizeof.create(c.arena, c_pointer_payload.data.elem_type);
|
||||||
return Tag.div_exact.create(c.arena, .{
|
return Tag.div_exact.create(c.arena, .{
|
||||||
.lhs = bitcast,
|
.lhs = bitcast,
|
||||||
.rhs = sizeof,
|
.rhs = sizeof,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// This is an opaque/incomplete type. This subtraction exhibits Undefined Behavior by the C99 spec.
|
||||||
|
// However, allowing subtraction on `void *` and function pointers is a commonly used extension.
|
||||||
|
// So, just return the value in byte units, mirroring the behavior of this language extension as implemented by GCC and Clang.
|
||||||
|
return bitcast;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return infixOpNode;
|
return infixOpNode;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
test/cases/translate_c/void_pointer_subtraction.c
Normal file
16
test/cases/translate_c/void_pointer_subtraction.c
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#include <stddef.h>
|
||||||
|
ptrdiff_t sub_ptr(void *a, void *b) {
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// translate-c
|
||||||
|
// c_frontend=clang
|
||||||
|
// target=x86_64-linux
|
||||||
|
//
|
||||||
|
// pub export fn sub_ptr(arg_a: ?*anyopaque, arg_b: ?*anyopaque) ptrdiff_t {
|
||||||
|
// var a = arg_a;
|
||||||
|
// _ = &a;
|
||||||
|
// var b = arg_b;
|
||||||
|
// _ = &b;
|
||||||
|
// return @as(c_long, @bitCast(@intFromPtr(a) -% @intFromPtr(b)));
|
||||||
|
// }
|
||||||
Loading…
Add table
Reference in a new issue