Commit 809deb6ec0

Vexu <git@vexu.eu>
2019-12-19 08:39:39
translate-c-2 unary operators common case
1 parent e4c47e8
Changed files (3)
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();
+        \\}
+    });
 }