From c8c89648b0d98af07aad88a813a74ff650cf3bbe Mon Sep 17 00:00:00 2001 From: Merlyn Morgan-Graham Date: Sat, 14 Dec 2019 22:04:07 -0800 Subject: [PATCH 1/4] Add comparison and bitwise binary ops in translate-c-2 --- src-self-hosted/translate_c.zig | 88 +++++++++++++++++++++++++++++---- test/translate_c.zig | 52 +++++++++++++++++++ 2 files changed, 131 insertions(+), 9 deletions(-) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 9d011b1255..70ff1daf40 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -681,15 +681,85 @@ fn transBinaryOperator( }, .Shl, .Shr, - .LT, - .GT, - .LE, - .GE, - .EQ, - .NE, - .And, - .Xor, - .Or, + => return revertAndWarn( + rp, + error.UnsupportedTranslation, + ZigClangBinaryOperator_getBeginLoc(stmt), + "TODO: handle more C binary operators: {}", + .{op}, + ), + .LT => { + const node = try transCreateNodeInfixOp(rp, scope, stmt, .LessThan, .AngleBracketLeft, "<", true); + return maybeSuppressResult(rp, scope, result_used, TransResult{ + .node = node, + .child_scope = scope, + .node_scope = scope, + }); + }, + .GT => { + const node = try transCreateNodeInfixOp(rp, scope, stmt, .GreaterThan, .AngleBracketRight, ">", true); + return maybeSuppressResult(rp, scope, result_used, TransResult{ + .node = node, + .child_scope = scope, + .node_scope = scope, + }); + }, + .LE => { + const node = try transCreateNodeInfixOp(rp, scope, stmt, .LessOrEqual, .AngleBracketLeftEqual, "<=", true); + return maybeSuppressResult(rp, scope, result_used, TransResult{ + .node = node, + .child_scope = scope, + .node_scope = scope, + }); + }, + .GE => { + const node = try transCreateNodeInfixOp(rp, scope, stmt, .GreaterOrEqual, .AngleBracketRightEqual, ">=", true); + return maybeSuppressResult(rp, scope, result_used, TransResult{ + .node = node, + .child_scope = scope, + .node_scope = scope, + }); + }, + .EQ => { + const node = try transCreateNodeInfixOp(rp, scope, stmt, .EqualEqual, .EqualEqual, "==", true); + return maybeSuppressResult(rp, scope, result_used, TransResult{ + .node = node, + .child_scope = scope, + .node_scope = scope, + }); + }, + .NE => { + const node = try transCreateNodeInfixOp(rp, scope, stmt, .BangEqual, .BangEqual, "!=", true); + return maybeSuppressResult(rp, scope, result_used, TransResult{ + .node = node, + .child_scope = scope, + .node_scope = scope, + }); + }, + .And => { + const node = try transCreateNodeInfixOp(rp, scope, stmt, .BitAnd, .Ampersand, "&", true); + return maybeSuppressResult(rp, scope, result_used, TransResult{ + .node = node, + .child_scope = scope, + .node_scope = scope, + }); + }, + .Xor => { + const node = try transCreateNodeInfixOp(rp, scope, stmt, .BitXor, .Caret, "^", true); + return maybeSuppressResult(rp, scope, result_used, TransResult{ + .node = node, + .child_scope = scope, + .node_scope = scope, + }); + }, + .Or => { + const node = try transCreateNodeInfixOp(rp, scope, stmt, .BitOr, .Pipe, "|", true); + return maybeSuppressResult(rp, scope, result_used, TransResult{ + .node = node, + .child_scope = scope, + .node_scope = scope, + }); + }, .LAnd, .LOr, .Comma, diff --git a/test/translate_c.zig b/test/translate_c.zig index 97cfc129f7..36cdc23915 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -871,6 +871,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("==, !=, no if", // TODO remove this test after `if` conversion supported, and switch "==, !=" to addC_both + \\int max(int a, int b) { + \\ int c = (a == b); + \\ int d = (a != b); + \\ return (c != d); + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int, b: c_int) c_int { + \\ var c: c_int = (a == b); + \\ var d: c_int = (a != b); + \\ return (c != d); + \\} + }); + cases.addC("bitwise binary operators", \\int max(int a, int b) { \\ return (a & b) ^ (a | b); @@ -881,6 +895,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("bitwise binary operators, simpler parens", // TODO can combine with "bitwise binary operators" when parens are correctly preserved/not added in translate-c-2 + \\int max(int a, int b) { + \\ int c = (a & b); + \\ int d = (a | b); + \\ return (c ^ d); + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int, b: c_int) c_int { + \\ var c: c_int = (a & b); + \\ var d: c_int = (a | b); + \\ return (c ^ d); + \\} + }); + cases.addC("logical and, logical or", \\int max(int a, int b) { \\ if (a < b || a == b) @@ -897,6 +925,30 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("comparison operators (no if)", // TODO Come up with less contrived tests? Make sure to cover all these comparisons. Can use `if` after it is added to translate-c-2 + \\int test_comparisons(int a, int b) { + \\ int c = (a < b); + \\ int d = (a > b); + \\ int e = (a <= b); + \\ int f = (a >= b); + \\ int g = (c < d); + \\ int h = (e < f); + \\ int i = (g < h); + \\ return i; + \\} + , &[_][]const u8{ + \\pub export fn test_comparisons(a: c_int, b: c_int) c_int { + \\ var c: c_int = (a < b); + \\ var d: c_int = (a > b); + \\ var e: c_int = (a <= b); + \\ var f: c_int = (a >= b); + \\ var g: c_int = (c < d); + \\ var h: c_int = (e < f); + \\ var i: c_int = (g < h); + \\ return i; + \\} + }); + cases.addC("logical and, logical or on none bool values", \\int and_or_none_bool(int a, float b, void *c) { \\ if (a && b) return 0; From acff2d407b45519cc6dc24639dfe1289c899addd Mon Sep 17 00:00:00 2001 From: Merlyn Morgan-Graham Date: Sun, 15 Dec 2019 15:45:46 -0800 Subject: [PATCH 2/4] Add bit shift binary ops in translate-c-2 --- src-self-hosted/translate_c.zig | 166 ++++++++++++++++++++++++++++++-- test/translate_c.zig | 25 +++++ 2 files changed, 181 insertions(+), 10 deletions(-) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 70ff1daf40..5f11076abb 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -575,6 +575,45 @@ fn transStmt( } } +fn transCreateNodeShiftOp( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangBinaryOperator, + comptime op: ast.Node.InfixOp.Op, + comptime op_tok_id: std.zig.Token.Id, + comptime bytes: []const u8, +) !*ast.Node { + if (!(op == .BitShiftLeft or op == .BitShiftRight)) { + @compileError("op must be either .BitShiftLeft or .BitShiftRight"); + } + + const lhs_expr = ZigClangBinaryOperator_getLHS(stmt); + const rhs_expr = ZigClangBinaryOperator_getRHS(stmt); + const rhs_location = ZigClangExpr_getBeginLoc(rhs_expr); + // lhs >> u5(rh) + + const lhs = try transExpr(rp, scope, lhs_expr, .used, .l_value); + const op_token = try appendToken(rp.c, op_tok_id, bytes); + + const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); + const rhs_type = try qualTypeToLog2IntRef(rp, ZigClangBinaryOperator_getType(stmt), rhs_location); + try as_node.params.push(rhs_type); + _ = try appendToken(rp.c, .Comma, ","); + const rhs = try transExpr(rp, scope, rhs_expr, .used, .l_value); + try as_node.params.push(rhs.node); + as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + + const node = try rp.c.a().create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ + .op_token = op_token, + .lhs = lhs.node, + .op = op, + .rhs = &as_node.base, + }; + + return &node.base; +} + fn transBinaryOperator( rp: RestorePoint, scope: *Scope, @@ -679,15 +718,22 @@ fn transBinaryOperator( }); } }, - .Shl, - .Shr, - => return revertAndWarn( - rp, - error.UnsupportedTranslation, - ZigClangBinaryOperator_getBeginLoc(stmt), - "TODO: handle more C binary operators: {}", - .{op}, - ), + .Shl => { + const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<"); + return maybeSuppressResult(rp, scope, result_used, TransResult{ + .node = node, + .child_scope = scope, + .node_scope = scope, + }); + }, + .Shr => { + const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftRight, .AngleBracketAngleBracketRight, ">>"); + return maybeSuppressResult(rp, scope, result_used, TransResult{ + .node = node, + .child_scope = scope, + .node_scope = scope, + }); + }, .LT => { const node = try transCreateNodeInfixOp(rp, scope, stmt, .LessThan, .AngleBracketLeft, "<", true); return maybeSuppressResult(rp, scope, result_used, TransResult{ @@ -1664,6 +1710,95 @@ fn qualTypeIsPtr(qt: ZigClangQualType) bool { return ZigClangType_getTypeClass(qualTypeCanon(qt)) == .Pointer; } +fn qualTypeIntBitWidth(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) !u32 { + const ty = ZigClangQualType_getTypePtr(qt); + + switch (ZigClangType_getTypeClass(ty)) { + .Builtin => { + const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty); + + switch (ZigClangBuiltinType_getKind(builtin_ty)) { + .Char_U, + .UChar, + .Char_S, + .SChar, + => return 8, + .UInt128, + .Int128, + => return 128, + else => return 0, + } + + unreachable; + }, + .Typedef => { + const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty); + const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty); + const type_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_decl))); + + if (std.mem.eql(u8, type_name, "uint8_t") or std.mem.eql(u8, type_name, "int8_t")) { + return 8; + } else if (std.mem.eql(u8, type_name, "uint16_t") or std.mem.eql(u8, type_name, "int16_t")) { + return 16; + } else if (std.mem.eql(u8, type_name, "uint32_t") or std.mem.eql(u8, type_name, "int32_t")) { + return 32; + } else if (std.mem.eql(u8, type_name, "uint64_t") or std.mem.eql(u8, type_name, "int64_t")) { + return 64; + } else { + return 0; + } + }, + else => return 0, + } + + unreachable; +} + +fn qualTypeToLog2IntRef(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) !*ast.Node { + const int_bit_width = try qualTypeIntBitWidth(rp, qt, source_loc); + + if (int_bit_width != 0) { + // we can perform the log2 now. + const cast_bit_width = std.math.log2_int(u64, int_bit_width); + const node = try rp.c.a().create(ast.Node.IntegerLiteral); + node.* = ast.Node.IntegerLiteral{ + .token = try appendTokenFmt(rp.c, .Identifier, "u{}", .{cast_bit_width}), + }; + return &node.base; + } + + const zig_type_node = try transQualType(rp, qt, source_loc); + + // @import("std").math.Log2Int(c_long); + // + // FnCall + // FieldAccess + // FieldAccess + // FnCall (.builtin = true) + // Symbol "import" + // StringLiteral "std" + // Symbol "math" + // Symbol "Log2Int" + // Symbol (var from above) + + const import_fn_call = try transCreateNodeBuiltinFnCall(rp.c, "@import"); + const std_token = try appendToken(rp.c, .StringLiteral, "\"std\""); + const std_node = try rp.c.a().create(ast.Node.StringLiteral); + std_node.* = ast.Node.StringLiteral{ + .token = std_token, + }; + try import_fn_call.params.push(&std_node.base); + import_fn_call.rparen_token = try appendToken(rp.c, .RParen, ")"); + + const inner_field_access = try transCreateNodeFieldAccess(rp.c, &import_fn_call.base, "math"); + const outer_field_access = try transCreateNodeFieldAccess(rp.c, &inner_field_access.base, "Log2Int"); + const log2int_fn_call = try transCreateNodeFnCall(rp.c, &outer_field_access.base); + try @ptrCast(*ast.Node.SuffixOp.Op.Call, &log2int_fn_call.op).params.push(zig_type_node); + log2int_fn_call.rtoken = try appendToken(rp.c, .RParen, ")"); + + return &log2int_fn_call.base; +} + fn qualTypeChildIsFnProto(qt: ZigClangQualType) bool { const ty = ZigClangQualType_getTypePtr(qt); @@ -1827,7 +1962,7 @@ fn transCreateNodeFnCall(c: *Context, fn_expr: *ast.Node) !*ast.Node.SuffixOp { _ = try appendToken(c, .LParen, "("); const node = try c.a().create(ast.Node.SuffixOp); node.* = ast.Node.SuffixOp{ - .lhs = fn_expr, + .lhs = .{ .node = fn_expr }, .op = ast.Node.SuffixOp.Op{ .Call = ast.Node.SuffixOp.Op.Call{ .params = ast.Node.SuffixOp.Op.Call.ParamList.init(c.a()), @@ -1839,6 +1974,17 @@ fn transCreateNodeFnCall(c: *Context, fn_expr: *ast.Node) !*ast.Node.SuffixOp { return node; } +fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node.InfixOp { + const field_access_node = try c.a().create(ast.Node.InfixOp); + field_access_node.* = .{ + .op_token = try appendToken(c, .Period, "."), + .lhs = container, + .op = .Period, + .rhs = try transCreateNodeIdentifier(c, field_name), + }; + return field_access_node; +} + fn transCreateNodePrefixOp( c: *Context, op: ast.Node.PrefixOp.Op, diff --git a/test/translate_c.zig b/test/translate_c.zig index 36cdc23915..f09c1832dd 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1023,6 +1023,19 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("shift right with a fixed size type, no while", // TODO can fold this into "shift right assign with a fixed size type" once `while` and `>>=` and `uint32_t` are handled in translate-c-2 + \\#include + \\uint32_t some_func(uint32_t a) { + \\ uint32_t b = a >> 1; + \\ return b; + \\} + , &[_][]const u8{ + \\pub export fn some_func(a: uint32_t) uint32_t { + \\ var b: uint32_t = a >> @as(u5, 1); + \\ return b; + \\} + }); + cases.add("anonymous enum", \\enum { \\ One, @@ -1199,6 +1212,18 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("bitshift, no parens", // TODO can fold this into "bitshift" once parens are preserved correctly in translate-c-2 + \\int foo(void) { + \\ int a = (1 << 2); + \\ return a >> 1; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ var a: c_int = 1 << @as(@import("std").math.Log2Int(c_int), 2); + \\ return a >> @as(@import("std").math.Log2Int(c_int), 1); + \\} + }); + cases.addC("compound assignment operators", \\void foo(void) { \\ int a = 0; From 89ef635b354062bebbe126e2be2f6e4540dc0db7 Mon Sep 17 00:00:00 2001 From: Merlyn Morgan-Graham Date: Mon, 16 Dec 2019 01:33:27 -0800 Subject: [PATCH 3/4] Add boolean and, boolean or binary ops in translate-c-2 --- src-self-hosted/clang.zig | 10 + src-self-hosted/translate_c.zig | 417 ++++++++++++++++++++++++++++++-- test/translate_c.zig | 107 ++++++-- 3 files changed, 503 insertions(+), 31 deletions(-) diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index 4b3aa44fab..ea3d0318a5 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -967,6 +967,16 @@ pub extern fn ZigClangDeclRefExpr_getDecl(*const ZigClangDeclRefExpr) *const Zig pub extern fn ZigClangParenType_getInnerType(*const ZigClangParenType) ZigClangQualType; pub extern fn ZigClangElaboratedType_getNamedType(*const ZigClangElaboratedType) ZigClangQualType; +pub extern fn ZigClangElaboratedType_getKeyword(*const ZigClangElaboratedType) ZigClangElaboratedTypeKeyword; +pub const ZigClangElaboratedTypeKeyword = extern enum { + Struct, + Interface, + Union, + Class, + Enum, + Typename, + None, +}; pub extern fn ZigClangAttributedType_getEquivalentType(*const ZigClangAttributedType) ZigClangQualType; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 5f11076abb..c3d8cb215c 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -592,14 +592,14 @@ fn transCreateNodeShiftOp( const rhs_location = ZigClangExpr_getBeginLoc(rhs_expr); // lhs >> u5(rh) - const lhs = try transExpr(rp, scope, lhs_expr, .used, .l_value); + const lhs = try transExpr(rp, scope, lhs_expr, .used, .r_value); const op_token = try appendToken(rp.c, op_tok_id, bytes); const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); const rhs_type = try qualTypeToLog2IntRef(rp, ZigClangBinaryOperator_getType(stmt), rhs_location); try as_node.params.push(rhs_type); _ = try appendToken(rp.c, .Comma, ","); - const rhs = try transExpr(rp, scope, rhs_expr, .used, .l_value); + const rhs = try transExpr(rp, scope, rhs_expr, .used, .r_value); try as_node.params.push(rhs.node); as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); @@ -672,7 +672,7 @@ fn transBinaryOperator( if (!cIsUnsignedInteger(qt)) { // signed integer division uses @divTrunc const div_trunc_node = try transCreateNodeBuiltinFnCall(rp.c, "@divTrunc"); - const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value); + const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .r_value); try div_trunc_node.params.push(lhs.node); _ = try appendToken(rp.c, .Comma, ","); const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); @@ -697,7 +697,7 @@ fn transBinaryOperator( if (!cIsUnsignedInteger(qt)) { // signed integer division uses @rem const rem_node = try transCreateNodeBuiltinFnCall(rp.c, "@rem"); - const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value); + const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .r_value); try rem_node.params.push(lhs.node); _ = try appendToken(rp.c, .Comma, ","); const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); @@ -806,10 +806,23 @@ fn transBinaryOperator( .node_scope = scope, }); }, - .LAnd, - .LOr, - .Comma, - => return revertAndWarn( + .LAnd => { + const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolAnd, .Keyword_and, "and"); + return maybeSuppressResult(rp, scope, result_used, TransResult{ + .node = node, + .child_scope = scope, + .node_scope = scope, + }); + }, + .LOr => { + const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolOr, .Keyword_or, "or"); + return maybeSuppressResult(rp, scope, result_used, TransResult{ + .node = node, + .child_scope = scope, + .node_scope = scope, + }); + }, + .Comma => return revertAndWarn( rp, error.UnsupportedTranslation, ZigClangBinaryOperator_getBeginLoc(stmt), @@ -1040,6 +1053,321 @@ fn transImplicitCastExpr( } } +fn toEnumZeroCmp( + rp: RestorePoint, + scope: *Scope, + expr: *ast.Node, + generate_enum_node: fn (RestorePoint, *const struct_ZigClangType, source_loc: ZigClangSourceLocation) TransError!*ast.Node, + enum_ty: *const struct_ZigClangType, + enum_source_loc: ZigClangSourceLocation, +) !*ast.Node { + // expr != @bitCast(EnumType, @as(@TagType(EnumType), 0)) + + // @bitCast(Enum, + const bitcast = try transCreateNodeBuiltinFnCall(rp.c, "@bitCast"); + const bitcast_enum_identifier = try generate_enum_node(rp, enum_ty, enum_source_loc); + try bitcast.params.push(bitcast_enum_identifier); + _ = try appendToken(rp.c, .Comma, ","); + + // @as( + const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); + + // @TagType(Enum), + const tag_type = try transCreateNodeBuiltinFnCall(rp.c, "@TagType"); + const tag_type_enum_identifier = try generate_enum_node(rp, enum_ty, enum_source_loc); + try tag_type.params.push(tag_type_enum_identifier); + tag_type.rparen_token = try appendToken(rp.c, .RParen, ")"); + try cast_node.params.push(&tag_type.base); + _ = try appendToken(rp.c, .Comma, ","); + + // 0) + const zero = try transCreateNodeInt(rp.c, 0); + try cast_node.params.push(zero); + cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + + try bitcast.params.push(&cast_node.base); + bitcast.rparen_token = try appendToken(rp.c, .RParen, ")"); + + // expr != @bitCast(EnumType, @as(@TagType(EnumType), 0)) + return transCreateNodeNotEqual(rp, scope, expr, &bitcast.base); +} + +fn transBoolExpr( + rp: RestorePoint, + scope: *Scope, + expr: *const ZigClangExpr, + used: ResultUsed, + lrvalue: LRValue, +) !*ast.Node { + var res = try transExpr(rp, scope, expr, used, lrvalue); + + switch (res.node.id) { + .InfixOp => switch (@ptrCast(*const ast.Node.InfixOp, &res.node).op) { + .BoolOr, + .BoolAnd, + .EqualEqual, + .BangEqual, + .LessThan, + .GreaterThan, + .LessOrEqual, + .GreaterOrEqual, + => return res.node, + + else => {}, + }, + + .PrefixOp => switch (@ptrCast(*const ast.Node.PrefixOp, &res.node).op) { + .BoolNot => return res.node, + + else => {}, + }, + + .BoolLiteral => return res.node, + + else => {}, + } + + const ty = ZigClangQualType_getTypePtr(getExprQualTypeBeforeImplicitCast(rp.c, expr)); + + switch (ZigClangType_getTypeClass(ty)) { + .Builtin => { + const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty); + + switch (ZigClangBuiltinType_getKind(builtin_ty)) { + .Bool, + .Char_U, + .UChar, + .Char_S, + .SChar, + .UShort, + .UInt, + .ULong, + .ULongLong, + .Short, + .Int, + .Long, + .LongLong, + .UInt128, + .Int128, + .Float, + .Double, + .Float128, + .LongDouble, + .WChar_U, + .Char8, + .Char16, + .Char32, + .WChar_S, + .Float16, + => return transCreateNodeNotEqual(rp, scope, res.node, try transCreateNodeInt(rp.c, 0)), + + .NullPtr => return transCreateNodeNotEqual(rp, scope, res.node, try transCreateNodeNullLiteral(rp.c)), + + .Void, + .Half, + .ObjCId, + .ObjCClass, + .ObjCSel, + .OMPArraySection, + .Dependent, + .Overload, + .BoundMember, + .PseudoObject, + .UnknownAny, + .BuiltinFn, + .ARCUnbridgedCast, + .OCLImage1dRO, + .OCLImage1dArrayRO, + .OCLImage1dBufferRO, + .OCLImage2dRO, + .OCLImage2dArrayRO, + .OCLImage2dDepthRO, + .OCLImage2dArrayDepthRO, + .OCLImage2dMSAARO, + .OCLImage2dArrayMSAARO, + .OCLImage2dMSAADepthRO, + .OCLImage2dArrayMSAADepthRO, + .OCLImage3dRO, + .OCLImage1dWO, + .OCLImage1dArrayWO, + .OCLImage1dBufferWO, + .OCLImage2dWO, + .OCLImage2dArrayWO, + .OCLImage2dDepthWO, + .OCLImage2dArrayDepthWO, + .OCLImage2dMSAAWO, + .OCLImage2dArrayMSAAWO, + .OCLImage2dMSAADepthWO, + .OCLImage2dArrayMSAADepthWO, + .OCLImage3dWO, + .OCLImage1dRW, + .OCLImage1dArrayRW, + .OCLImage1dBufferRW, + .OCLImage2dRW, + .OCLImage2dArrayRW, + .OCLImage2dDepthRW, + .OCLImage2dArrayDepthRW, + .OCLImage2dMSAARW, + .OCLImage2dArrayMSAARW, + .OCLImage2dMSAADepthRW, + .OCLImage2dArrayMSAADepthRW, + .OCLImage3dRW, + .OCLSampler, + .OCLEvent, + .OCLClkEvent, + .OCLQueue, + .OCLReserveID, + .ShortAccum, + .Accum, + .LongAccum, + .UShortAccum, + .UAccum, + .ULongAccum, + .ShortFract, + .Fract, + .LongFract, + .UShortFract, + .UFract, + .ULongFract, + .SatShortAccum, + .SatAccum, + .SatLongAccum, + .SatUShortAccum, + .SatUAccum, + .SatULongAccum, + .SatShortFract, + .SatFract, + .SatLongFract, + .SatUShortFract, + .SatUFract, + .SatULongFract, + .OCLIntelSubgroupAVCMcePayload, + .OCLIntelSubgroupAVCImePayload, + .OCLIntelSubgroupAVCRefPayload, + .OCLIntelSubgroupAVCSicPayload, + .OCLIntelSubgroupAVCMceResult, + .OCLIntelSubgroupAVCImeResult, + .OCLIntelSubgroupAVCRefResult, + .OCLIntelSubgroupAVCSicResult, + .OCLIntelSubgroupAVCImeResultSingleRefStreamout, + .OCLIntelSubgroupAVCImeResultDualRefStreamout, + .OCLIntelSubgroupAVCImeSingleRefStreamin, + .OCLIntelSubgroupAVCImeDualRefStreamin, + => return res.node, + } + }, + .Pointer => return transCreateNodeNotEqual(rp, scope, res.node, try transCreateNodeNullLiteral(rp.c)), + + .Typedef => { + return transCreateNodeNotEqual(rp, scope, res.node, try transCreateNodeInt(rp.c, 0)); // TODO currently assuming it is like an int/char/bool builtin type. Coerce the type and recurse? Add a toTypedefZeroCmp function? + + // TODO This is the code that was in translate-c, but it seems like it is giving wrong results! It just prints the typedef name instead of the value + // const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty); + // const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty); + // const typedef_name_decl = ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl); + + // const typedef_name = if (rp.c.decl_table.get(@ptrToInt(typedef_name_decl))) |existing_entry| + // existing_entry.value + // else + // try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_name_decl))); + + // return transCreateNodeIdentifier(rp.c, typedef_name); + }, + + .Enum => { + const gen_enum_decl_node = struct { + // Have to use a callback because node must be generated inline in order to avoid weird AST printing behavior, + // and the code to generate the nodes is a little different for each case + fn generate_node(inner_rp: RestorePoint, enum_ty: *const struct_ZigClangType, source_loc: ZigClangSourceLocation) TransError!*ast.Node { + const actual_enum_ty = @ptrCast(*const ZigClangEnumType, enum_ty); + const enum_decl = ZigClangEnumType_getDecl(actual_enum_ty); + const enum_type = (try transEnumDecl(inner_rp.c, enum_decl)) orelse { + return revertAndWarn(inner_rp, error.UnsupportedType, source_loc, "unable to translate enum declaration", .{}); + }; + return enum_type; + } + }; + + return toEnumZeroCmp(rp, scope, res.node, gen_enum_decl_node.generate_node, ty, ZigClangExpr_getBeginLoc(expr)); + }, + + .Elaborated => { + const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty); + + switch (ZigClangElaboratedType_getKeyword(elaborated_ty)) { + .Enum => { + // Have to use a callback because node must be generated inline in order to avoid weird AST printing behavior, + // and the code to generate the nodes is a little different for each case + const gen_enum_type_node = struct { + fn generate_node(inner_rp: RestorePoint, enum_ty: *const struct_ZigClangType, source_loc: ZigClangSourceLocation) TransError!*ast.Node { + const inner_elaborated_ty = @ptrCast(*const ZigClangElaboratedType, enum_ty); + const enum_type = try transQualType(inner_rp, ZigClangElaboratedType_getNamedType(inner_elaborated_ty), source_loc); + return enum_type; + } + }; + + return toEnumZeroCmp(rp, scope, res.node, gen_enum_type_node.generate_node, ty, ZigClangExpr_getBeginLoc(expr)); + }, + + .Struct, + .Union, + .Interface, + .Class, + .Typename, + .None, + => return res.node, + } + }, + + .FunctionProto, + .Record, + .ConstantArray, + .Paren, + .Decayed, + .Attributed, + .IncompleteArray, + .BlockPointer, + .LValueReference, + .RValueReference, + .MemberPointer, + .VariableArray, + .DependentSizedArray, + .DependentSizedExtVector, + .Vector, + .ExtVector, + .FunctionNoProto, + .UnresolvedUsing, + .Adjusted, + .TypeOfExpr, + .TypeOf, + .Decltype, + .UnaryTransform, + .TemplateTypeParm, + .SubstTemplateTypeParm, + .SubstTemplateTypeParmPack, + .TemplateSpecialization, + .Auto, + .InjectedClassName, + .DependentName, + .DependentTemplateSpecialization, + .PackExpansion, + .ObjCObject, + .ObjCInterface, + .Complex, + .ObjCObjectPointer, + .Atomic, + .Pipe, + .ObjCTypeParam, + .DeducedTemplateSpecialization, + .DependentAddressSpace, + .DependentVector, + .MacroQualified, + => return res.node, + } + + unreachable; +} + fn transIntegerLiteral( rp: RestorePoint, scope: *Scope, @@ -1848,6 +2176,14 @@ fn getExprQualType(c: *Context, expr: *const ZigClangExpr) ZigClangQualType { return ZigClangExpr_getType(expr); } +fn getExprQualTypeBeforeImplicitCast(c: *Context, expr: *const ZigClangExpr) ZigClangQualType { + if (ZigClangExpr_getStmtClass(expr) == .ImplicitCastExprClass) { + const cast_expr = @ptrCast(*const ZigClangImplicitCastExpr, expr); + return getExprQualType(c, ZigClangImplicitCastExpr_getSubExpr(cast_expr)); + } + return ZigClangExpr_getType(expr); +} + fn typeIsOpaque(c: *Context, ty: *const ZigClangType, loc: ZigClangSourceLocation) bool { switch (ZigClangType_getTypeClass(ty)) { .Builtin => { @@ -2000,25 +2336,24 @@ fn transCreateNodePrefixOp( return node; } -fn transCreateNodeInfixOp( +fn transCreateNodeInfixOpImpl( rp: RestorePoint, scope: *Scope, - stmt: *const ZigClangBinaryOperator, + lhs_node: *ast.Node, + rhs_node: *ast.Node, op: ast.Node.InfixOp.Op, op_tok_id: std.zig.Token.Id, bytes: []const u8, grouped: bool, ) !*ast.Node { const lparen = if (grouped) try appendToken(rp.c, .LParen, "(") else undefined; - const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value); const op_token = try appendToken(rp.c, op_tok_id, bytes); - const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); const node = try rp.c.a().create(ast.Node.InfixOp); node.* = ast.Node.InfixOp{ .op_token = op_token, - .lhs = lhs.node, + .lhs = lhs_node, .op = op, - .rhs = rhs.node, + .rhs = rhs_node, }; if (!grouped) return &node.base; const rparen = try appendToken(rp.c, .RParen, ")"); @@ -2031,6 +2366,60 @@ fn transCreateNodeInfixOp( return &grouped_expr.base; } +fn transCreateNodeInfixOp( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangBinaryOperator, + op: ast.Node.InfixOp.Op, + op_tok_id: std.zig.Token.Id, + bytes: []const u8, + grouped: bool, +) !*ast.Node { + return transCreateNodeInfixOpImpl( + rp, + scope, + (try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .r_value)).node, + (try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value)).node, + op, + op_tok_id, + bytes, + grouped, + ); +} + +fn transCreateNodeNotEqual( + rp: RestorePoint, + scope: *Scope, + lhs_node: *ast.Node, + rhs_node: *ast.Node, +) !*ast.Node { + return transCreateNodeInfixOpImpl(rp, scope, lhs_node, rhs_node, .BangEqual, .BangEqual, "!=", true); +} + +fn transCreateNodeBoolInfixOp( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangBinaryOperator, + comptime op: ast.Node.InfixOp.Op, + comptime op_tok_id: std.zig.Token.Id, + comptime bytes: []const u8, +) !*ast.Node { + if (!(op == .BoolAnd or op == .BoolOr)) { + @compileError("op must be either .BoolAnd or .BoolOr"); + } + + return transCreateNodeInfixOpImpl( + rp, + scope, + try transBoolExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .r_value), + try transBoolExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value), + op, + op_tok_id, + bytes, + true, + ); +} + fn transCreateNodePtrType( c: *Context, is_const: bool, diff --git a/test/translate_c.zig b/test/translate_c.zig index f09c1832dd..e1b3c2d788 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -949,28 +949,101 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("logical and, logical or on none bool values", - \\int and_or_none_bool(int a, float b, void *c) { - \\ if (a && b) return 0; - \\ if (b && c) return 1; - \\ if (a && c) return 2; - \\ if (a || b) return 3; - \\ if (b || c) return 4; - \\ if (a || c) return 5; - \\ return 6; + 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 export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { - \\ if ((a != 0) and (b != 0)) return 0; - \\ if ((b != 0) and (c != null)) return 1; - \\ if ((a != 0) and (c != null)) return 2; - \\ if ((a != 0) or (b != 0)) return 3; - \\ if ((b != 0) or (c != null)) return 4; - \\ if ((a != 0) or (c != null)) return 5; - \\ return 6; + \\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_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))); + \\ var l: c_int = (@as(c_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))) and (b != 0); + \\ var m: c_int = (c != null) or (@as(c_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))); + \\ return (((((((e + f) + g) + h) + i) + j) + k) + l) + m; \\} }); + cases.add_2("logical and, logical or, on non-bool values, extra parens", + \\enum Foo { + \\ FooA, + \\ FooB, + \\ FooC, + \\}; + \\typedef int SomeTypedef; + \\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); + \\ SomeTypedef td = 44; + \\ int o = (td || b); + \\ int p = (c && td); + \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p); + \\} + , &[_][]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 const SomeTypedef = c_int; + \\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_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0)))); + \\ var l: c_int = ((@as(c_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))) and (b != 0)); + \\ var m: c_int = ((c != null) or (@as(c_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0)))); + \\ var td: SomeTypedef = 44; + \\ var o: c_int = ((td != 0) or (b != 0)); + \\ var p: c_int = ((c != null) and (td != 0)); + \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p); + \\} + \\pub const Foo = enum_Foo; + }); + cases.addC("assign", \\int max(int a) { \\ int tmp; From 0c03fe48b308ba907e69d1e442d60f556b7bdd8c Mon Sep 17 00:00:00 2001 From: Merlyn Morgan-Graham Date: Mon, 16 Dec 2019 01:37:53 -0800 Subject: [PATCH 4/4] Fix compile errors after rebasing on master (missing switch->else) --- src-self-hosted/translate_c.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index c3d8cb215c..ddb7d2da79 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -1254,6 +1254,8 @@ fn transBoolExpr( .OCLIntelSubgroupAVCImeSingleRefStreamin, .OCLIntelSubgroupAVCImeDualRefStreamin, => return res.node, + + else => {}, } }, .Pointer => return transCreateNodeNotEqual(rp, scope, res.node, try transCreateNodeNullLiteral(rp.c)), @@ -1316,6 +1318,8 @@ fn transBoolExpr( .Typename, .None, => return res.node, + + else => {}, } }, @@ -1363,6 +1367,8 @@ fn transBoolExpr( .DependentVector, .MacroQualified, => return res.node, + + else => unreachable, } unreachable;