authorgravatar for evan@lagerdata.comEvan Haas <evan@lagerdata.com> 2022-07-25 09:53:40-07:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-07-27 14:03:08+03:00
log7ba1f9bfb52a1f6fa776eeafb45790331be4388f
treee893b174fc9b7e88ea15190f75e05753b315e940
parentc8c798685f3a7d6454bc06d5f47531ad6f615eb5

translate-c: take address of functions before passing them to @ptrToInt

Fixes #12194

7 files changed, 66 insertions(+), 5 deletions(-)

lib/std/zig/c_translation.zig+4
...@@ -36,6 +36,9 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {...@@ -36,6 +36,9 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
36 .Int => {36 .Int => {
37 return castInt(DestType, target);37 return castInt(DestType, target);
38 },38 },
39 .Fn => {
40 return castInt(DestType, @ptrToInt(&target));
41 },
39 else => {},42 else => {},
40 }43 }
41 },44 },
...@@ -45,6 +48,7 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {...@@ -45,6 +48,7 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
45 }48 }
46 @compileError("cast to union type '" ++ @typeName(DestType) ++ "' from type '" ++ @typeName(SourceType) ++ "' which is not present in union");49 @compileError("cast to union type '" ++ @typeName(DestType) ++ "' from type '" ++ @typeName(SourceType) ++ "' which is not present in union");
47 },50 },
51 .Bool => return cast(usize, target) != 0,
48 else => {},52 else => {},
49 }53 }
50 return @as(DestType, target);54 return @as(DestType, target);
src/translate_c.zig+14-4
...@@ -1950,7 +1950,10 @@ fn transDeclRefExpr(...@@ -1950,7 +1950,10 @@ fn transDeclRefExpr(
1950 const value_decl = expr.getDecl();1950 const value_decl = expr.getDecl();
1951 const name = try c.str(@ptrCast(*const clang.NamedDecl, value_decl).getName_bytes_begin());1951 const name = try c.str(@ptrCast(*const clang.NamedDecl, value_decl).getName_bytes_begin());
1952 const mangled_name = scope.getAlias(name);1952 const mangled_name = scope.getAlias(name);
1953 var ref_expr = try Tag.identifier.create(c.arena, mangled_name);1953 var ref_expr = if (cIsFunctionDeclRef(@ptrCast(*const clang.Expr, expr)))
1954 try Tag.fn_identifier.create(c.arena, mangled_name)
1955 else
1956 try Tag.identifier.create(c.arena, mangled_name);
19541957
1955 if (@ptrCast(*const clang.Decl, value_decl).getKind() == .Var) {1958 if (@ptrCast(*const clang.Decl, value_decl).getKind() == .Var) {
1956 const var_decl = @ptrCast(*const clang.VarDecl, value_decl);1959 const var_decl = @ptrCast(*const clang.VarDecl, value_decl);
...@@ -1999,7 +2002,11 @@ fn transImplicitCastExpr(...@@ -1999,7 +2002,11 @@ fn transImplicitCastExpr(
1999 },2002 },
2000 .PointerToBoolean => {2003 .PointerToBoolean => {
2001 // @ptrToInt(val) != 02004 // @ptrToInt(val) != 0
2002 const ptr_to_int = try Tag.ptr_to_int.create(c.arena, try transExpr(c, scope, sub_expr, .used));2005 var ptr_node = try transExpr(c, scope, sub_expr, .used);
2006 if (ptr_node.tag() == .fn_identifier) {
2007 ptr_node = try Tag.address_of.create(c.arena, ptr_node);
2008 }
2009 const ptr_to_int = try Tag.ptr_to_int.create(c.arena, ptr_node);
20032010
2004 const ne = try Tag.not_equal.create(c.arena, .{ .lhs = ptr_to_int, .rhs = Tag.zero_literal.init() });2011 const ne = try Tag.not_equal.create(c.arena, .{ .lhs = ptr_to_int, .rhs = Tag.zero_literal.init() });
2005 return maybeSuppressResult(c, scope, result_used, ne);2012 return maybeSuppressResult(c, scope, result_used, ne);
...@@ -2042,7 +2049,7 @@ fn isBuiltinDefined(name: []const u8) bool {...@@ -2042,7 +2049,7 @@ fn isBuiltinDefined(name: []const u8) bool {
20422049
2043fn transBuiltinFnExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node {2050fn transBuiltinFnExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node {
2044 const node = try transExpr(c, scope, expr, used);2051 const node = try transExpr(c, scope, expr, used);
2045 if (node.castTag(.identifier)) |ident| {2052 if (node.castTag(.fn_identifier)) |ident| {
2046 const name = ident.data;2053 const name = ident.data;
2047 if (!isBuiltinDefined(name)) return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO implement function '{s}' in std.zig.c_builtins", .{name});2054 if (!isBuiltinDefined(name)) return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO implement function '{s}' in std.zig.c_builtins", .{name});
2048 }2055 }
...@@ -2447,7 +2454,10 @@ fn transCCast(...@@ -2447,7 +2454,10 @@ fn transCCast(
2447 }2454 }
2448 if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) {2455 if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) {
2449 // @intCast(dest_type, @ptrToInt(val))2456 // @intCast(dest_type, @ptrToInt(val))
2450 const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr);2457 const ptr_to_int = if (expr.tag() == .fn_identifier)
2458 try Tag.ptr_to_int.create(c.arena, try Tag.address_of.create(c.arena, expr))
2459 else
2460 try Tag.ptr_to_int.create(c.arena, expr);
2451 return Tag.int_cast.create(c.arena, .{ .lhs = dst_node, .rhs = ptr_to_int });2461 return Tag.int_cast.create(c.arena, .{ .lhs = dst_node, .rhs = ptr_to_int });
2452 }2462 }
2453 if (cIsInteger(src_type) and qualTypeIsPtr(dst_type)) {2463 if (cIsInteger(src_type) and qualTypeIsPtr(dst_type)) {
src/translate_c/ast.zig+11
...@@ -36,6 +36,7 @@ pub const Node = extern union {...@@ -36,6 +36,7 @@ pub const Node = extern union {
36 /// "string"[0..end]36 /// "string"[0..end]
37 string_slice,37 string_slice,
38 identifier,38 identifier,
39 fn_identifier,
39 @"if",40 @"if",
40 /// if (!operand) break;41 /// if (!operand) break;
41 if_not_break,42 if_not_break,
...@@ -335,6 +336,7 @@ pub const Node = extern union {...@@ -335,6 +336,7 @@ pub const Node = extern union {
335 .char_literal,336 .char_literal,
336 .enum_literal,337 .enum_literal,
337 .identifier,338 .identifier,
339 .fn_identifier,
338 .warning,340 .warning,
339 .type,341 .type,
340 .helpers_macro,342 .helpers_macro,
...@@ -1058,6 +1060,14 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -1058,6 +1060,14 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
1058 .data = undefined,1060 .data = undefined,
1059 });1061 });
1060 },1062 },
1063 .fn_identifier => {
1064 const payload = node.castTag(.fn_identifier).?.data;
1065 return c.addNode(.{
1066 .tag = .identifier,
1067 .main_token = try c.addIdentifier(payload),
1068 .data = undefined,
1069 });
1070 },
1061 .float_literal => {1071 .float_literal => {
1062 const payload = node.castTag(.float_literal).?.data;1072 const payload = node.castTag(.float_literal).?.data;
1063 return c.addNode(.{1073 return c.addNode(.{
...@@ -2234,6 +2244,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2234,6 +2244,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2234 .char_literal,2244 .char_literal,
2235 .enum_literal,2245 .enum_literal,
2236 .identifier,2246 .identifier,
2247 .fn_identifier,
2237 .field_access,2248 .field_access,
2238 .ptr_cast,2249 .ptr_cast,
2239 .type,2250 .type,
test/behavior/translate_c_macros.h+8
...@@ -40,3 +40,11 @@ union U {...@@ -40,3 +40,11 @@ union U {
40#define CAST_OR_CALL_WITH_PARENS(type_or_fn, val) ((type_or_fn)(val))40#define CAST_OR_CALL_WITH_PARENS(type_or_fn, val) ((type_or_fn)(val))
4141
42#define NESTED_COMMA_OPERATOR (1, (2, 3))42#define NESTED_COMMA_OPERATOR (1, (2, 3))
43
44#include <stdint.h>
45#if !defined(__UINTPTR_MAX__)
46typedef _Bool uintptr_t;
47#endif
48
49#define CAST_TO_BOOL(X) (_Bool)(X)
50#define CAST_TO_UINTPTR(X) (uintptr_t)(X)
test/behavior/translate_c_macros.zig+14
...@@ -99,3 +99,17 @@ test "nested comma operator" {...@@ -99,3 +99,17 @@ test "nested comma operator" {
9999
100 try expectEqual(@as(c_int, 3), h.NESTED_COMMA_OPERATOR);100 try expectEqual(@as(c_int, 3), h.NESTED_COMMA_OPERATOR);
101}101}
102
103test "cast functions" {
104 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
105 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
106 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
107 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
108 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
109
110 const S = struct {
111 fn foo() void {}
112 };
113 try expectEqual(true, h.CAST_TO_BOOL(S.foo));
114 try expect(h.CAST_TO_UINTPTR(S.foo) != 0);
115}
test/run_translated_c.zig+14
...@@ -1861,4 +1861,18 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {...@@ -1861,4 +1861,18 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
1861 \\ return 0;1861 \\ return 0;
1862 \\}1862 \\}
1863 , "");1863 , "");
1864
1865 // The C standard does not require function pointers to be convertible to any integer type.
1866 // However, POSIX requires that function pointers have the same representation as `void *`
1867 // so that dlsym() can work
1868 cases.add("Function to integral",
1869 \\#include <stdint.h>
1870 \\int main(void) {
1871 \\#if defined(__UINTPTR_MAX__) && __has_include(<unistd.h>)
1872 \\ uintptr_t x = main;
1873 \\ x = (uintptr_t)main;
1874 \\#endif
1875 \\ return 0;
1876 \\}
1877 , "");
1864}1878}
test/translate_c.zig+1-1
...@@ -3435,7 +3435,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3435,7 +3435,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3435 \\ var x = arg_x;3435 \\ var x = arg_x;
3436 \\ var a: bool = @as(c_int, @boolToInt(x)) != @as(c_int, 1);3436 \\ var a: bool = @as(c_int, @boolToInt(x)) != @as(c_int, 1);
3437 \\ var b: bool = @as(c_int, @boolToInt(a)) != @as(c_int, 0);3437 \\ var b: bool = @as(c_int, @boolToInt(a)) != @as(c_int, 0);
3438 \\ var c: bool = @ptrToInt(foo) != 0;3438 \\ var c: bool = @ptrToInt(&foo) != 0;
3439 \\ return foo(@as(c_int, @boolToInt(c)) != @as(c_int, @boolToInt(b)));3439 \\ return foo(@as(c_int, @boolToInt(c)) != @as(c_int, @boolToInt(b)));
3440 \\}3440 \\}
3441 });3441 });