Commit 809deb6ec0
Changed files (3)
src-self-hosted
test
src-self-hosted/clang.zig
@@ -1116,3 +1116,7 @@ pub extern fn ZigClangCallExpr_getArgs(*const ZigClangCallExpr) [*]const *const
pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangQualType;
pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangSourceLocation;
+pub extern fn ZigClangUnaryOperator_getOpcode(*const ZigClangUnaryOperator) ZigClangUO;
+pub extern fn ZigClangUnaryOperator_getType(*const ZigClangUnaryOperator) ZigClangQualType;
+pub extern fn ZigClangUnaryOperator_getSubExpr(*const ZigClangUnaryOperator) *const ZigClangExpr;
+pub extern fn ZigClangUnaryOperator_getBeginLoc(*const ZigClangUnaryOperator) ZigClangSourceLocation;
src-self-hosted/translate_c.zig
@@ -897,6 +897,7 @@ fn transStmt(
.ArraySubscriptExprClass => return transArrayAccess(rp, scope, @ptrCast(*const ZigClangArraySubscriptExpr, stmt), result_used),
.CallExprClass => return transCallExpr(rp, scope, @ptrCast(*const ZigClangCallExpr, stmt), result_used),
.UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(rp, scope, @ptrCast(*const ZigClangUnaryExprOrTypeTraitExpr, stmt), result_used),
+ .UnaryOperatorClass => return transUnaryOperator(rp, scope, @ptrCast(*const ZigClangUnaryOperator, stmt), result_used),
else => {
return revertAndWarn(
rp,
@@ -2163,6 +2164,139 @@ fn transUnaryExprOrTypeTraitExpr(
return maybeSuppressResult(rp, scope, result_used, &builtin_node.base);
}
+fn qualTypeHaswrappingOverflow(qt: ZigClangQualType) bool {
+ if (cIsSignedInteger(qt) or cIsFloating(qt)) {
+ // float and signed integer overflow is undefined behavior.
+ return false;
+ } else {
+ // unsigned integer overflow wraps around.
+ return true;
+ }
+}
+
+fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnaryOperator, used: ResultUsed) TransError!*ast.Node {
+ const op_expr = ZigClangUnaryOperator_getSubExpr(stmt);
+ switch (ZigClangUnaryOperator_getOpcode(stmt)) {
+ .PostInc => if (qualTypeHaswrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
+ return transCreatePostCrement(rp, scope, stmt, .AssignPlusWrap, .PlusPercentEqual, "+%=", used)
+ else
+ return transCreatePostCrement(rp, scope, stmt, .AssignPlus, .PlusEqual, "+=", used),
+ .PostDec => if (qualTypeHaswrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
+ return transCreatePostCrement(rp, scope, stmt, .AssignMinusWrap, .MinusPercentEqual, "-%=", used)
+ else
+ return transCreatePostCrement(rp, scope, stmt, .AssignMinus, .MinusEqual, "-=", used),
+ .PreInc => if (qualTypeHaswrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
+ return transCreatePreCrement(rp, scope, stmt, .AssignPlusWrap, .PlusPercentEqual, "+%=", used)
+ else
+ return transCreatePreCrement(rp, scope, stmt, .AssignPlus, .PlusEqual, "+=", used),
+ .PreDec => if (qualTypeHaswrappingOverflow(ZigClangUnaryOperator_getType(stmt)))
+ return transCreatePreCrement(rp, scope, stmt, .AssignMinusWrap, .MinusPercentEqual, "-%=", used)
+ else
+ return transCreatePreCrement(rp, scope, stmt, .AssignMinus, .MinusEqual, "-=", used),
+ .AddrOf => {
+ const op_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
+ op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value);
+ return &op_node.base;
+ },
+ .Deref => {
+ const value_node = try transExpr(rp, scope, op_expr, used, .r_value);
+ var is_ptr = false;
+ const fn_ty = qualTypeGetFnProto(ZigClangExpr_getType(op_expr), &is_ptr);
+ if (fn_ty != null and is_ptr)
+ return value_node;
+ const unwrapped = try transCreateNodeUnwrapNull(rp.c, value_node);
+ return transCreateNodePtrDeref(rp.c, unwrapped);
+ },
+ .Plus => return transExpr(rp, scope, op_expr, used, .r_value),
+ .Minus => {
+ if (!qualTypeHaswrappingOverflow(ZigClangExpr_getType(op_expr))) {
+ const op_node = try transCreateNodePrefixOp(rp.c, .Negation, .Minus, "-");
+ op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
+ return &op_node.base;
+ } else if (cIsUnsignedInteger(ZigClangExpr_getType(op_expr))) {
+ // we gotta emit 0 -% x
+ const zero = try transCreateNodeInt(rp.c, 0);
+ const token = try appendToken(rp.c, .MinusPercent, "-%");
+ const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
+ return transCreateNodeInfixOp(rp, scope, zero, .SubWrap, token, expr, used, true);
+ } else
+ return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "C negation with non float non integer", .{});
+ },
+ .Not => {
+ const op_node = try transCreateNodePrefixOp(rp.c, .BitNot, .Tilde, "~");
+ op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
+ return &op_node.base;
+ },
+ .LNot => {
+ const op_node = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");
+ op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true);
+ return &op_node.base;
+ },
+ else => return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "TODO handle C translation UO_Real", .{}),
+ }
+}
+
+fn transCreatePreCrement(
+ rp: RestorePoint,
+ scope: *Scope,
+ stmt: *const ZigClangUnaryOperator,
+ op: ast.Node.InfixOp.Op,
+ op_tok_id: std.zig.Token.Id,
+ bytes: []const u8,
+ used: ResultUsed,
+) TransError!*ast.Node {
+ const op_expr = ZigClangUnaryOperator_getSubExpr(stmt);
+
+ if (used == .unused) {
+ // common case
+ // c: ++expr
+ // zig: expr += 1
+ const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
+ const token = try appendToken(rp.c, op_tok_id, bytes);
+ const one = try transCreateNodeInt(rp.c, 1);
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false);
+ }
+ // worst case
+ // c: ++expr
+ // zig: (blk: {
+ // zig: const _ref = &expr;
+ // zig: *_ref += 1;
+ // zig: break :blk *_ref
+ // zig: })
+}
+
+fn transCreatePostCrement(
+ rp: RestorePoint,
+ scope: *Scope,
+ stmt: *const ZigClangUnaryOperator,
+ op: ast.Node.InfixOp.Op,
+ op_tok_id: std.zig.Token.Id,
+ bytes: []const u8,
+ used: ResultUsed,
+) TransError!*ast.Node {
+ const op_expr = ZigClangUnaryOperator_getSubExpr(stmt);
+
+ if (used == .unused) {
+ // common case
+ // c: ++expr
+ // zig: expr += 1
+ const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
+ const token = try appendToken(rp.c, op_tok_id, bytes);
+ const one = try transCreateNodeInt(rp.c, 1);
+ _ = try appendToken(rp.c, .Semicolon, ";");
+ return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false);
+ }
+ // worst case
+ // c: expr++
+ // zig: (blk: {
+ // zig: const _ref = &expr;
+ // zig: const _tmp = *_ref;
+ // zig: *_ref += 1;
+ // zig: break :blk _tmp
+ // zig: })
+}
+
fn transCPtrCast(
rp: RestorePoint,
loc: ZigClangSourceLocation,
@@ -2507,6 +2641,23 @@ fn cIsUnsignedInteger(qt: ZigClangQualType) bool {
};
}
+fn cIsSignedInteger(qt: ZigClangQualType) bool {
+ const c_type = qualTypeCanon(qt);
+ if (ZigClangType_getTypeClass(c_type) != .Builtin) return false;
+ const builtin_ty = @ptrCast(*const ZigClangBuiltinType, c_type);
+ return switch (ZigClangBuiltinType_getKind(builtin_ty)) {
+ .SChar,
+ .Short,
+ .Int,
+ .Long,
+ .LongLong,
+ .Int128,
+ .WChar_S,
+ => true,
+ else => false,
+ };
+}
+
fn cIsFloating(qt: ZigClangQualType) bool {
const c_type = qualTypeCanon(qt);
if (ZigClangType_getTypeClass(c_type) != .Builtin) return false;
test/translate_c.zig
@@ -769,6 +769,56 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
+ cases.addC_both("normal deref",
+ \\void foo(int *x) {
+ \\ *x = 1;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo(x: [*c]c_int) void {
+ \\ x.?.* = 1;
+ \\}
+ });
+
+ cases.addC_both("address of operator",
+ \\int foo(void) {
+ \\ int x = 1234;
+ \\ int *ptr = &x;
+ \\ return *ptr;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo() c_int {
+ \\ var x: c_int = 1234;
+ \\ var ptr: [*c]c_int = &x;
+ \\ return ptr.?.*;
+ \\}
+ });
+
+ cases.addC_both("bin not",
+ \\int foo(int x) {
+ \\ return ~x;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo(x: c_int) c_int {
+ \\ return ~x;
+ \\}
+ });
+
+ cases.addC_both("bool not",
+ \\int foo(int a, float b, void *c) {
+ \\ return !(a == 0);
+ \\ return !a;
+ \\ return !b;
+ \\ return !c;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo(a: c_int, b: f32, c: ?*c_void) c_int {
+ \\ return !(a == 0);
+ \\ return !(a != 0);
+ \\ return !(b != 0);
+ \\ return !(c != null);
+ \\}
+ });
+
/////////////// Cases that pass for only stage2 ////////////////
cases.add_2("Parameterless function prototypes",
@@ -1664,9 +1714,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
- /////////////// Cases for only stage1 which are TODO items for stage2 ////////////////
-
- cases.addAllowWarnings("simple data types",
+ cases.add_2("simple data types",
\\#include <stdint.h>
\\int foo(char a, unsigned char b, signed char c);
\\int foo(char a, unsigned char b, signed char c); // test a duplicate prototype
@@ -1674,22 +1722,72 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\void baz(int8_t a, int16_t b, int32_t c, int64_t d);
, &[_][]const u8{
\\pub extern fn foo(a: u8, b: u8, c: i8) c_int;
- ,
\\pub extern fn bar(a: u8, b: u16, c: u32, d: u64) void;
- ,
\\pub extern fn baz(a: i8, b: i16, c: i32, d: i64) void;
});
- cases.addC("simple function",
+ cases.add_2("simple function",
\\int abs(int a) {
\\ return a < 0 ? -a : a;
\\}
, &[_][]const u8{
- \\export fn abs(a: c_int) c_int {
- \\ return if (a < 0) -a else a;
+ \\pub export fn abs(a: c_int) c_int {
+ \\ return if ((a < 0)) -a else a;
+ \\}
+ });
+
+ cases.add_2("post increment",
+ \\unsigned foo1(unsigned a) {
+ \\ a++;
+ \\ return a;
+ \\}
+ \\int foo2(int a) {
+ \\ a++;
+ \\ return a;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo1(a: c_uint) c_uint {
+ \\ a +%= 1;
+ \\ return a;
+ \\}
+ \\pub export fn foo2(a: c_int) c_int {
+ \\ a += 1;
+ \\ return a;
+ \\}
+ });
+
+ cases.add_2("deref function pointer",
+ \\void foo(void) {}
+ \\int baz(void) { return 0; }
+ \\void bar(void) {
+ \\ void(*f)(void) = foo;
+ \\ int(*b)(void) = baz;
+ \\ f();
+ \\ (*(f))();
+ \\ foo();
+ \\ b();
+ \\ (*(b))();
+ \\ baz();
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo() void {}
+ \\pub export fn baz() c_int {
+ \\ return 0;
+ \\}
+ \\pub export fn bar() void {
+ \\ var f: ?extern fn () void = foo;
+ \\ var b: ?extern fn () c_int = baz;
+ \\ f.?();
+ \\ (f).?();
+ \\ foo();
+ \\ _ = b.?();
+ \\ _ = (b).?();
+ \\ _ = baz();
\\}
});
+ /////////////// Cases for only stage1 which are TODO items for stage2 ////////////////
+
cases.add("macro defines string literal with hex",
\\#define FOO "aoeu\xab derp"
\\#define FOO2 "aoeu\x0007a derp"
@@ -1714,28 +1812,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub const FOO_CHAR = 63;
});
- cases.addC("post increment",
- \\unsigned foo1(unsigned a) {
- \\ a++;
- \\ return a;
- \\}
- \\int foo2(int a) {
- \\ a++;
- \\ return a;
- \\}
- , &[_][]const u8{
- \\pub export fn foo1(_arg_a: c_uint) c_uint {
- \\ var a = _arg_a;
- \\ a +%= 1;
- \\ return a;
- \\}
- \\pub export fn foo2(_arg_a: c_int) c_int {
- \\ var a = _arg_a;
- \\ a += 1;
- \\ return a;
- \\}
- });
-
cases.addC("shift right assign",
\\int log2(unsigned a) {
\\ int i = 0;
@@ -1993,96 +2069,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
- cases.addC("deref function pointer",
- \\void foo(void) {}
- \\int baz(void) { return 0; }
- \\void bar(void) {
- \\ void(*f)(void) = foo;
- \\ int(*b)(void) = baz;
- \\ f();
- \\ (*(f))();
- \\ foo();
- \\ b();
- \\ (*(b))();
- \\ baz();
- \\}
- , &[_][]const u8{
- \\pub export fn foo() void {}
- \\pub export fn baz() c_int {
- \\ return 0;
- \\}
- \\pub export fn bar() void {
- \\ var f: ?extern fn () void = foo;
- \\ var b: ?extern fn () c_int = baz;
- \\ f.?();
- \\ f.?();
- \\ foo();
- \\ _ = b.?();
- \\ _ = b.?();
- \\ _ = baz();
- \\}
- });
-
- cases.addC("normal deref",
- \\void foo(int *x) {
- \\ *x = 1;
- \\}
- , &[_][]const u8{
- \\pub export fn foo(x: [*c]c_int) void {
- \\ x.?.* = 1;
- \\}
- });
-
- cases.add("address of operator",
- \\int foo(void) {
- \\ int x = 1234;
- \\ int *ptr = &x;
- \\ return *ptr;
- \\}
- , &[_][]const u8{
- \\pub fn foo() c_int {
- \\ var x: c_int = 1234;
- \\ var ptr: [*c]c_int = &x;
- \\ return ptr.?.*;
- \\}
- });
-
- cases.add("bin not",
- \\int foo(int x) {
- \\ return ~x;
- \\}
- , &[_][]const u8{
- \\pub fn foo(x: c_int) c_int {
- \\ return ~x;
- \\}
- });
-
- cases.add("bool not",
- \\int foo(int a, float b, void *c) {
- \\ return !(a == 0);
- \\ return !a;
- \\ return !b;
- \\ return !c;
- \\}
- , &[_][]const u8{
- \\pub fn foo(a: c_int, b: f32, c: ?*c_void) c_int {
- \\ return !(a == 0);
- \\ return !(a != 0);
- \\ return !(b != 0);
- \\ return !(c != null);
- \\}
- });
-
- cases.add("primitive types included in defined symbols",
- \\int foo(int u32) {
- \\ return u32;
- \\}
- , &[_][]const u8{
- \\pub fn foo(u32_0: c_int) c_int {
- \\ return u32_0;
- \\}
- });
-
cases.addC("implicit casts",
\\#include <stdbool.h>
\\
@@ -2637,49 +2623,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
- cases.addC("logical and, logical or, on non-bool values", // Note this gets cut off by extra C symbols being injected in middle: `pub const Foo = enum_Foo;`
- \\enum Foo {
- \\ FooA,
- \\ FooB,
- \\ FooC,
- \\};
- \\int and_or_non_bool(int a, float b, void *c) {
- \\ enum Foo d = FooA;
- \\ int e = (a && b);
- \\ int f = (b && c);
- \\ int g = (a && c);
- \\ int h = (a || b);
- \\ int i = (b || c);
- \\ int j = (a || c);
- \\ int k = (a || d);
- \\ int l = (d && b);
- \\ int m = (c || d);
- \\ return (((((((e + f) + g) + h) + i) + j) + k) + l) + m;
- \\}
- , &[_][]const u8{
- \\pub const FooA = enum_Foo.A;
- \\pub const FooB = enum_Foo.B;
- \\pub const FooC = enum_Foo.C;
- \\pub const enum_Foo = extern enum {
- \\ A,
- \\ B,
- \\ C,
- \\};
- \\pub export fn and_or_non_bool(a: c_int, b: f32, c: ?*c_void) c_int {
- \\ var d: enum_Foo = @as(enum_Foo, FooA);
- \\ var e: c_int = (a != 0) and (b != 0);
- \\ var f: c_int = (b != 0) and (c != null);
- \\ var g: c_int = (a != 0) and (c != null);
- \\ var h: c_int = (a != 0) or (b != 0);
- \\ var i: c_int = (b != 0) or (c != null);
- \\ var j: c_int = (a != 0) or (c != null);
- \\ var k: c_int = (a != 0) or (@as(c_uint, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0)));
- \\ var l: c_int = (@as(c_uint, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))) and (b != 0);
- \\ var m: c_int = (c != null) or (@as(c_uint, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0)));
- \\ return (((((((e + f) + g) + h) + i) + j) + k) + l) + m;
- \\}
- });
-
cases.add("variable name shadowing",
\\int foo(void) {
\\ int x = 1;
@@ -2726,4 +2669,80 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return 4;
\\}
});
+
+ cases.addAllowWarnings("simple data types",
+ \\#include <stdint.h>
+ \\int foo(char a, unsigned char b, signed char c);
+ \\int foo(char a, unsigned char b, signed char c); // test a duplicate prototype
+ \\void bar(uint8_t a, uint16_t b, uint32_t c, uint64_t d);
+ \\void baz(int8_t a, int16_t b, int32_t c, int64_t d);
+ , &[_][]const u8{
+ \\pub extern fn foo(a: u8, b: u8, c: i8) c_int;
+ ,
+ \\pub extern fn bar(a: u8, b: u16, c: u32, d: u64) void;
+ ,
+ \\pub extern fn baz(a: i8, b: i16, c: i32, d: i64) void;
+ });
+
+ cases.addC("simple function",
+ \\int abs(int a) {
+ \\ return a < 0 ? -a : a;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn abs(a: c_int) c_int {
+ \\ return if (a < 0) -a else a;
+ \\}
+ });
+
+ cases.addC("post increment",
+ \\unsigned foo1(unsigned a) {
+ \\ a++;
+ \\ return a;
+ \\}
+ \\int foo2(int a) {
+ \\ a++;
+ \\ return a;
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo1(_arg_a: c_uint) c_uint {
+ \\ var a = _arg_a;
+ \\ a +%= 1;
+ \\ return a;
+ \\}
+ \\pub export fn foo2(_arg_a: c_int) c_int {
+ \\ var a = _arg_a;
+ \\ a += 1;
+ \\ return a;
+ \\}
+ });
+
+ cases.addC("deref function pointer",
+ \\void foo(void) {}
+ \\int baz(void) { return 0; }
+ \\void bar(void) {
+ \\ void(*f)(void) = foo;
+ \\ int(*b)(void) = baz;
+ \\ f();
+ \\ (*(f))();
+ \\ foo();
+ \\ b();
+ \\ (*(b))();
+ \\ baz();
+ \\}
+ , &[_][]const u8{
+ \\pub export fn foo() void {}
+ \\pub export fn baz() c_int {
+ \\ return 0;
+ \\}
+ \\pub export fn bar() void {
+ \\ var f: ?extern fn () void = foo;
+ \\ var b: ?extern fn () c_int = baz;
+ \\ f.?();
+ \\ f.?();
+ \\ foo();
+ \\ _ = b.?();
+ \\ _ = b.?();
+ \\ _ = baz();
+ \\}
+ });
}