translate-c-2 unary operators common case

This commit is contained in:
Vexu 2019-12-19 09:39:39 +02:00
parent e4c47e80b4
commit 809deb6ec0
No known key found for this signature in database
GPG Key ID: 59AEB8936E16A6AC
3 changed files with 337 additions and 163 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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();
\\}
});
}