diff options
| author | Vexu <git@vexu.eu> | 2019-12-19 09:39:39 +0200 |
|---|---|---|
| committer | Vexu <git@vexu.eu> | 2019-12-19 09:56:00 +0200 |
| commit | 809deb6ec0bf9ea5649d3ba4ca80485e2148a538 (patch) | |
| tree | 3e0fce5e13a70ac3757c39e4577b92608d87ef45 | |
| parent | e4c47e80b43eb3096093b38e8e984d9042e3742e (diff) | |
| download | zig-809deb6ec0bf9ea5649d3ba4ca80485e2148a538.tar.gz zig-809deb6ec0bf9ea5649d3ba4ca80485e2148a538.zip | |
translate-c-2 unary operators common case
| -rw-r--r-- | src-self-hosted/clang.zig | 4 | ||||
| -rw-r--r-- | src-self-hosted/translate_c.zig | 151 | ||||
| -rw-r--r-- | test/translate_c.zig | 345 |
3 files changed, 337 insertions, 163 deletions
diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index 5c51e3d356..a967362a8e 100644 --- a/src-self-hosted/clang.zig +++ b/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; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index d671b5693f..859d10a441 100644 --- a/src-self-hosted/translate_c.zig +++ b/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; diff --git a/test/translate_c.zig b/test/translate_c.zig index c246c708f0..8bfa2096e7 100644 --- a/test/translate_c.zig +++ b/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(); + \\} + }); } |
