From c8c89648b0d98af07aad88a813a74ff650cf3bbe Mon Sep 17 00:00:00 2001 From: Merlyn Morgan-Graham Date: Sat, 14 Dec 2019 22:04:07 -0800 Subject: [PATCH 01/37] 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 02/37] 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 03/37] 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 04/37] 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; From d9527edfe0f017cd397de7ed947e47600092f929 Mon Sep 17 00:00:00 2001 From: Vexu Date: Mon, 16 Dec 2019 15:22:37 +0200 Subject: [PATCH 05/37] translate-c-2 comma operator --- src-self-hosted/translate_c.zig | 75 ++++++++++++++++++++++++++------- test/translate_c.zig | 15 +++++++ 2 files changed, 75 insertions(+), 15 deletions(-) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 54ef53353d..582aca6b88 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -47,7 +47,7 @@ const Scope = struct { Switch, Block, Root, - While, + Condition, FnDef, Ref, }; @@ -65,9 +65,13 @@ const Scope = struct { base: Scope, block_node: *ast.Node.Block, variables: AliasList, + label: ?[]const u8, /// Don't forget to set rbrace token later - fn init(c: *Context, parent: *Scope, block_node: *ast.Node.Block) !*Block { + fn init(c: *Context, parent: *Scope, want_label: bool) !*Block { + // TODO removing `?[]const u8` here causes LLVM error + const label: ?[]const u8 = if (want_label) try std.fmt.allocPrint(c.a(), "blk_{}", .{c.getMangle()}) else null; + const block_node = try transCreateNodeBlock(c, label); const block = try c.a().create(Block); block.* = .{ .base = .{ @@ -76,6 +80,7 @@ const Scope = struct { }, .block_node = block_node, .variables = AliasList.init(c.a()), + .label = label, }; return block; } @@ -120,7 +125,8 @@ const Scope = struct { } }; - const While = struct { + const Condition = struct { + expr: *ast.Node, base: Scope, }; @@ -157,10 +163,19 @@ const Scope = struct { } }; - fn findBlockScope(inner: *Scope) *Scope.Block { + fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block { var scope = inner; - while (true) : (scope = scope.parent orelse unreachable) { - if (scope.id == .Block) return @fieldParentPtr(Scope.Block, "base", scope); + while (true) { + switch (scope.id) { + .Root => unreachable, + .Block => return @fieldParentPtr(Block, "base", scope), + .Condition => { + const cond = @fieldParentPtr(Condition, "base", scope); + // comma operator used + return try Block.init(c, scope, true); + }, + else => scope = inner, + } } } @@ -751,7 +766,6 @@ fn transBinaryOperator( .Or, .LAnd, .LOr, - .Comma, => return revertAndWarn( rp, error.UnsupportedTranslation, @@ -759,6 +773,23 @@ fn transBinaryOperator( "TODO: handle more C binary operators: {}", .{op}, ), + .Comma => { + const block_scope = try scope.findBlockScope(rp.c); + + const lhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getLHS(stmt), .unused, .r_value); + try block_scope.block_node.statements.push(lhs); + + const rhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); + if (block_scope.base.parent == scope) { + const break_node = try transCreateNodeBreak(rp.c, block_scope.label); + _ = try appendToken(rp.c, .Semicolon, ";"); + try block_scope.block_node.statements.push(&break_node.base); + block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + return maybeSuppressResult(rp, scope, result_used, &block_scope.block_node.base); + } else { + return maybeSuppressResult(rp, scope, result_used, rhs); + } + }, .MulAssign, .DivAssign, .RemAssign, @@ -790,11 +821,10 @@ fn transCompoundStmtInline( } fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) TransError!*ast.Node { - const block_node = try transCreateNodeBlock(rp.c, null); - const block_scope = try Scope.Block.init(rp.c, scope, block_node); - try transCompoundStmtInline(rp, &block_scope.base, stmt, block_node); - block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); - return &block_node.base; + const block_scope = try Scope.Block.init(rp.c, scope, false); + try transCompoundStmtInline(rp, &block_scope.base, stmt, block_scope.block_node); + block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + return &block_scope.block_node.base; } fn transCStyleCastExprClass( @@ -818,7 +848,7 @@ fn transCStyleCastExprClass( fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt) TransError!*ast.Node { const c = rp.c; - const block_scope = scope.findBlockScope(); + const block_scope = try scope.findBlockScope(c); var it = ZigClangDeclStmt_decl_begin(stmt); const end_it = ZigClangDeclStmt_decl_end(stmt); @@ -927,7 +957,7 @@ fn transImplicitCastExpr( return transExpr(rp, scope, sub_expr, .used, .r_value); }, .NullToPointer => { - return transCreateNodeNullLiteral(rp.c); + return try transCreateNodeNullLiteral(rp.c); }, else => |kind| return revertAndWarn( rp, @@ -1185,7 +1215,7 @@ fn transImplicitValueInitExpr( .Builtin => blk: { const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty); switch (ZigClangBuiltinType_getKind(builtin_ty)) { - .Bool => return transCreateNodeBoolLiteral(rp.c, false), + .Bool => return try transCreateNodeBoolLiteral(rp.c, false), .Char_U, .UChar, .Char_S, @@ -2046,6 +2076,21 @@ fn transCreateNodeBlock(c: *Context, label: ?[]const u8) !*ast.Node.Block { return block_node; } +fn transCreateNodeBreak(c: *Context, label: ?[]const u8) !*ast.Node.ControlFlowExpression { + const ltoken = try appendToken(c, .Keyword_break, "break"); + const label_node = if (label) |l| blk: { + _ = try appendToken(c, .Colon, ":"); + break :blk try transCreateNodeIdentifier(c, l); + } else null; + const node = try c.a().create(ast.Node.ControlFlowExpression); + node.* = ast.Node.ControlFlowExpression{ + .ltoken = ltoken, + .kind = .{ .Break = label_node }, + .rhs = null, + }; + return node; +} + const RestorePoint = struct { c: *Context, token_index: ast.TokenIndex, diff --git a/test/translate_c.zig b/test/translate_c.zig index 9a4c364d1d..c856517ac5 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -699,6 +699,21 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("comma operator", + \\int foo(char c) { + \\ 2, 4; + \\ return 2, 4, 6; + \\} + , &[_][]const u8{ + \\pub export fn foo(c: u8) c_int { + \\ _ = 2; + \\ _ = 4; + \\ _ = 2; + \\ _ = 4; + \\ return 6; + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// if (builtin.os != builtin.Os.windows) { From 626562555e3ffd9ae259aba967efb23ce293e338 Mon Sep 17 00:00:00 2001 From: Vexu Date: Mon, 16 Dec 2019 15:59:35 +0200 Subject: [PATCH 06/37] translate-c-2 wors-case assign --- src-self-hosted/translate_c.zig | 273 +++++++++++--------------------- test/translate_c.zig | 18 +++ 2 files changed, 111 insertions(+), 180 deletions(-) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 582aca6b88..fd7db400a1 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -524,35 +524,18 @@ fn resolveTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl) Err const typedef_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_decl))); _ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), typedef_name); - const name_tok = try appendIdentifier(c, typedef_name); - const eq_tok = try appendToken(c, .Equal, "="); + const node = try transCreateNodeVarDecl(c, true, true, typedef_name); + node.eq_token = try appendToken(c, .Equal, "="); const child_qt = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl); const typedef_loc = ZigClangTypedefNameDecl_getLocation(typedef_decl); - const type_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) { + node.init_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) { error.UnsupportedType => { return failDecl(c, typedef_loc, typedef_name, "unable to resolve typedef child type", .{}); }, error.OutOfMemory => |e| return e, }; - - const node = try c.a().create(ast.Node.VarDecl); - node.* = ast.Node.VarDecl{ - .doc_comments = null, - .visib_token = visib_tok, - .thread_local_token = null, - .name_token = name_tok, - .eq_token = eq_tok, - .mut_token = const_tok, - .comptime_token = null, - .extern_export_token = null, - .lib_name = null, - .type_node = null, - .align_node = null, - .section_node = null, - .init_node = type_node, - .semicolon_token = try appendToken(c, .Semicolon, ";"), - }; + node.semicolon_token = try appendToken(c, .Semicolon, ";"); try addTopLevelDecl(c, typedef_name, &node.base); } @@ -572,69 +555,29 @@ fn resolveRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error! if (ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl) or bare_name.len == 0) return; - const visib_tok = try appendToken(c, .Keyword_pub, "pub"); - const const_tok = try appendToken(c, .Keyword_const, "const"); - const name = try std.fmt.allocPrint(c.a(), "{}_{}", .{ container_kind_name, bare_name }); _ = try c.decl_table.put(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)), name); - const name_tok = try appendIdentifier(c, name); - const eq_tok = try appendToken(c, .Equal, "="); - const init_node = transRecordDecl(c, record_decl) catch |err| switch (err) { + const node = try transCreateNodeVarDecl(c, true, true, name); + + node.eq_token = try appendToken(c, .Equal, "="); + node.init_node = transRecordDecl(c, record_decl) catch |err| switch (err) { error.UnsupportedType => { return failDecl(c, ZigClangRecordDecl_getLocation(record_decl), name, "unable to resolve record type", .{}); }, error.OutOfMemory => |e| return e, }; - const semicolon_token = try appendToken(c, .Semicolon, ";"); - - const node = try c.a().create(ast.Node.VarDecl); - node.* = ast.Node.VarDecl{ - .doc_comments = null, - .visib_token = visib_tok, - .thread_local_token = null, - .name_token = name_tok, - .eq_token = eq_tok, - .mut_token = const_tok, - .comptime_token = null, - .extern_export_token = null, - .lib_name = null, - .type_node = null, - .align_node = null, - .section_node = null, - .init_node = init_node, - .semicolon_token = semicolon_token, - }; + node.semicolon_token = try appendToken(c, .Semicolon, ";"); try addTopLevelDecl(c, name, &node.base); try c.alias_list.push(.{ .alias = bare_name, .name = name }); } fn createAlias(c: *Context, alias: var) !void { - const visib_tok = try appendToken(c, .Keyword_pub, "pub"); - const mut_tok = try appendToken(c, .Keyword_const, "const"); - const name_tok = try appendIdentifier(c, alias.alias); - - const eq_tok = try appendToken(c, .Equal, "="); - const init_node = try transCreateNodeIdentifier(c, alias.name); - - const node = try c.a().create(ast.Node.VarDecl); - node.* = ast.Node.VarDecl{ - .doc_comments = null, - .visib_token = visib_tok, - .thread_local_token = null, - .name_token = name_tok, - .eq_token = eq_tok, - .mut_token = mut_tok, - .comptime_token = null, - .extern_export_token = null, - .lib_name = null, - .type_node = null, - .align_node = null, - .section_node = null, - .init_node = init_node, - .semicolon_token = try appendToken(c, .Semicolon, ";"), - }; + const node = try transCreateNodeVarDecl(c, true, true, alias.alias); + node.eq_token = try appendToken(c, .Equal, "="); + node.init_node = try transCreateNodeIdentifier(c, alias.name); + node.semicolon_token = try appendToken(c, .Semicolon, ";"); return addTopLevelDecl(c, alias.alias, &node.base); } @@ -697,7 +640,7 @@ fn transBinaryOperator( "TODO: handle more C binary operators: {}", .{op}, ), - .Assign => return &(try transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt))).base, + .Assign => return try transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt)), .Add => { const node = if (cIsUnsignedInteger(qt)) try transCreateNodeInfixOp(rp, scope, stmt, .AddWrap, .PlusPercent, "+%", true) @@ -782,6 +725,7 @@ fn transBinaryOperator( const rhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); if (block_scope.base.parent == scope) { const break_node = try transCreateNodeBreak(rp.c, block_scope.label); + break_node.rhs = rhs; _ = try appendToken(rp.c, .Semicolon, ";"); try block_scope.block_node.statements.push(&break_node.base); block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); @@ -862,10 +806,6 @@ fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt) else try appendToken(c, .Keyword_threadlocal, "threadlocal"); const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl); - const mut_token = if (ZigClangQualType_isConstQualified(qual_type)) - try appendToken(c, .Keyword_const, "const") - else - try appendToken(c, .Keyword_var, "var"); const name = try c.str(ZigClangDecl_getName_bytes_begin( @ptrCast(*const ZigClangDecl, var_decl), )); @@ -873,36 +813,18 @@ fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt) try block_scope.variables.push(.{ .name = name, .alias = a }); break :blk a; } else name; - const name_token = try appendIdentifier(c, checked_name); + const node = try transCreateNodeVarDecl(c, false, ZigClangQualType_isConstQualified(qual_type), checked_name); - const colon_token = try appendToken(c, .Colon, ":"); + _ = try appendToken(c, .Colon, ":"); const loc = ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)); - const type_node = try transQualType(rp, qual_type, loc); + node.type_node = try transQualType(rp, qual_type, loc); - const eq_token = try appendToken(c, .Equal, "="); - const init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| + node.eq_token = try appendToken(c, .Equal, "="); + node.init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| try transExpr(rp, scope, expr, .used, .r_value) else try transCreateNodeUndefinedLiteral(c); - const semicolon_token = try appendToken(c, .Semicolon, ";"); - - const node = try c.a().create(ast.Node.VarDecl); - node.* = ast.Node.VarDecl{ - .doc_comments = null, - .visib_token = null, - .thread_local_token = thread_local_token, - .name_token = name_token, - .eq_token = eq_token, - .mut_token = mut_token, - .comptime_token = null, - .extern_export_token = null, - .lib_name = null, - .type_node = type_node, - .align_node = null, // TODO ?*Node, - .section_node = null, - .init_node = init_node, - .semicolon_token = semicolon_token, - }; + node.semicolon_token = try appendToken(c, .Semicolon, ";"); try block_scope.block_node.statements.push(&node.base); }, else => |kind| return revertAndWarn( @@ -1395,9 +1317,6 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No const rp = makeRestorePoint(c); const enum_loc = ZigClangEnumDecl_getLocation(enum_decl); - const visib_tok = try appendToken(c, .Keyword_pub, "pub"); - const const_tok = try appendToken(c, .Keyword_const, "const"); - var bare_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, enum_decl))); var is_unnamed = false; if (bare_name.len == 0) { @@ -1407,10 +1326,10 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No const name = try std.fmt.allocPrint(c.a(), "enum_{}", .{bare_name}); _ = try c.decl_table.put(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)), name); - const name_tok = try appendIdentifier(c, name); - const eq_tok = try appendToken(c, .Equal, "="); + const node = try transCreateNodeVarDecl(c, true, true, name); + node.eq_token = try appendToken(c, .Equal, "="); - const init_node = if (ZigClangEnumDecl_getDefinition(enum_decl)) |enum_def| blk: { + node.init_node = if (ZigClangEnumDecl_getDefinition(enum_decl)) |enum_def| blk: { var pure_enum = true; var it = ZigClangEnumDecl_enumerator_begin(enum_def); var end_it = ZigClangEnumDecl_enumerator_end(enum_def); @@ -1504,25 +1423,7 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No } else try transCreateNodeOpaqueType(c); - const semicolon_token = try appendToken(c, .Semicolon, ";"); - - const node = try c.a().create(ast.Node.VarDecl); - node.* = ast.Node.VarDecl{ - .visib_token = visib_tok, - .mut_token = const_tok, - .name_token = name_tok, - .eq_token = eq_tok, - .init_node = init_node, - .semicolon_token = semicolon_token, - .doc_comments = null, - .comptime_token = null, - .extern_export_token = null, - .thread_local_token = null, - .lib_name = null, - .type_node = null, - .align_node = null, - .section_node = null, - }; + node.semicolon_token = try appendToken(c, .Semicolon, ";"); try addTopLevelDecl(c, name, &node.base); if (!is_unnamed) @@ -1531,14 +1432,12 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No } fn addEnumTopLevel(c: *Context, enum_name: []const u8, field_name: []const u8, enum_val_name: []const u8) !void { - const visib_tok = try appendToken(c, .Keyword_pub, "pub"); - const const_tok = try appendToken(c, .Keyword_const, "const"); - const name_tok = try appendIdentifier(c, enum_val_name); - const eq_tok = try appendToken(c, .Equal, "="); - + const node = try transCreateNodeVarDecl(c, true, true, enum_val_name); + node.eq_token = try appendToken(c, .Equal, "="); const enum_ident = try transCreateNodeIdentifier(c, enum_name); const period_tok = try appendToken(c, .Period, "."); const field_ident = try transCreateNodeIdentifier(c, field_name); + node.semicolon_token = try appendToken(c, .Semicolon, ";"); const field_access_node = try c.a().create(ast.Node.InfixOp); field_access_node.* = .{ @@ -1547,26 +1446,7 @@ fn addEnumTopLevel(c: *Context, enum_name: []const u8, field_name: []const u8, e .op = .Period, .rhs = field_ident, }; - const semicolon_token = try appendToken(c, .Semicolon, ";"); - - const node = try c.a().create(ast.Node.VarDecl); - node.* = ast.Node.VarDecl{ - .visib_token = visib_tok, - .mut_token = const_tok, - .name_token = name_tok, - .eq_token = eq_tok, - .init_node = &field_access_node.base, - .semicolon_token = semicolon_token, - .thread_local_token = null, - .doc_comments = null, - .comptime_token = null, - .extern_export_token = null, - .lib_name = null, - .type_node = null, - .align_node = null, - .section_node = null, - }; - + node.init_node = &field_access_node.base; try addTopLevelDecl(c, field_name, &node.base); } @@ -1693,7 +1573,7 @@ fn transCreateNodeAssign( result_used: ResultUsed, lhs: *const ZigClangExpr, rhs: *const ZigClangExpr, -) !*ast.Node.InfixOp { +) !*ast.Node { // common case // c: lhs = rhs // zig: lhs = rhs @@ -1710,7 +1590,7 @@ fn transCreateNodeAssign( .op = .Assign, .rhs = rhs_node, }; - return node; + return &node.base; } // worst case @@ -1720,13 +1600,39 @@ fn transCreateNodeAssign( // zig: lhs = _tmp; // zig: break :x _tmp // zig: }) - return revertAndWarn( - rp, - error.UnsupportedTranslation, - ZigClangExpr_getBeginLoc(lhs), - "TODO: worst case assign op expr", - .{}, - ); + _ = try appendToken(rp.c, .LParen, "("); + const block_scope = try Scope.Block.init(rp.c, scope, true); + const tmp = try std.fmt.allocPrint(rp.c.a(), "_tmp_{}", .{rp.c.getMangle()}); + + const node = try transCreateNodeVarDecl(rp.c, false, true, tmp); + node.eq_token = try appendToken(rp.c, .Equal, "="); + node.init_node = try transExpr(rp, scope, rhs, .used, .r_value); + node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); + try block_scope.block_node.statements.push(&node.base); + + const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value); + const eq_token = try appendToken(rp.c, .Equal, "="); + const ident = try transCreateNodeIdentifier(rp.c, tmp); + _ = try appendToken(rp.c, .Semicolon, ";"); + + const assign = try rp.c.a().create(ast.Node.InfixOp); + assign.* = .{ + .op_token = eq_token, + .lhs = lhs_node, + .op = .Assign, + .rhs = ident, + }; + try block_scope.block_node.statements.push(&assign.base); + + const break_node = try transCreateNodeBreak(rp.c, block_scope.label); + break_node.rhs = try transCreateNodeIdentifier(rp.c, tmp); + _ = try appendToken(rp.c, .Semicolon, ";"); + try block_scope.block_node.statements.push(&break_node.base); + block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + // semicolon must immediately follow rbrace because it is the last token in a block + _ = try appendToken(rp.c, .Semicolon, ";"); + _ = try appendToken(rp.c, .RParen, ")"); + return &block_scope.block_node.base; } fn transCreateNodeBuiltinFnCall(c: *Context, name: []const u8) !*ast.Node.BuiltinCall { @@ -2091,6 +1997,31 @@ fn transCreateNodeBreak(c: *Context, label: ?[]const u8) !*ast.Node.ControlFlowE return node; } +fn transCreateNodeVarDecl(c: *Context, is_pub: bool, is_const: bool, name: []const u8) !*ast.Node.VarDecl { + const visib_tok = if (is_pub) try appendToken(c, .Keyword_pub, "pub") else null; + const mut_tok = if (is_const) try appendToken(c, .Keyword_const, "const") else try appendToken(c, .Keyword_var, "var"); + const name_tok = try appendIdentifier(c, name); + + const node = try c.a().create(ast.Node.VarDecl); + node.* = ast.Node.VarDecl{ + .doc_comments = null, + .visib_token = visib_tok, + .thread_local_token = null, + .name_token = name_tok, + .eq_token = undefined, + .mut_token = mut_tok, + .comptime_token = null, + .extern_export_token = null, + .lib_name = null, + .type_node = null, + .align_node = null, + .section_node = null, + .init_node = null, + .semicolon_token = undefined, + }; + return node; +} + const RestorePoint = struct { c: *Context, token_index: ast.TokenIndex, @@ -2673,30 +2604,12 @@ fn transMacroDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, const rp = makeRestorePoint(c); const scope = &c.global_scope.base; - const visib_tok = try appendToken(c, .Keyword_pub, "pub"); - const mut_tok = try appendToken(c, .Keyword_const, "const"); - const name_tok = try appendIdentifier(c, name); - const eq_tok = try appendToken(c, .Equal, "="); + const node = try transCreateNodeVarDecl(c, true, true, name); + node.eq_token = try appendToken(c, .Equal, "="); - const init_node = try parseCExpr(rp, it, source_loc, scope); + node.init_node = try parseCExpr(rp, it, source_loc, scope); - const node = try c.a().create(ast.Node.VarDecl); - node.* = ast.Node.VarDecl{ - .doc_comments = null, - .visib_token = visib_tok, - .thread_local_token = null, - .name_token = name_tok, - .eq_token = eq_tok, - .mut_token = mut_tok, - .comptime_token = null, - .extern_export_token = null, - .lib_name = null, - .type_node = null, - .align_node = null, - .section_node = null, - .init_node = init_node, - .semicolon_token = try appendToken(c, .Semicolon, ";"), - }; + node.semicolon_token = try appendToken(c, .Semicolon, ";"); _ = try c.global_scope.macro_table.put(name, &node.base); } diff --git a/test/translate_c.zig b/test/translate_c.zig index c856517ac5..7a9479034d 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -714,6 +714,24 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("wors-case assign", + \\int foo(char c) { + \\ int a; + \\ int b; + \\ a = b = 2; + \\} + , &[_][]const u8{ + \\pub export fn foo(c: u8) c_int { + \\ var a: c_int = undefined; + \\ var b: c_int = undefined; + \\ a = blk_1: { + \\ const _tmp_2 = 2; + \\ b = _tmp_2; + \\ break :blk_1 _tmp_2; + \\ }; + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// if (builtin.os != builtin.Os.windows) { From e3f1bfe48388b3c585792367a898a25c58324000 Mon Sep 17 00:00:00 2001 From: Vexu Date: Mon, 16 Dec 2019 17:55:51 +0200 Subject: [PATCH 07/37] translate-c-2 if statements --- src-self-hosted/clang.zig | 4 +++ src-self-hosted/translate_c.zig | 59 ++++++++++++++++++++++++++++----- test/translate_c.zig | 23 +++++++++++++ 3 files changed, 78 insertions(+), 8 deletions(-) diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index b9435f96f4..381673e704 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -1036,3 +1036,7 @@ pub extern fn ZigClangPreprocessedEntity_getKind(*const ZigClangPreprocessedEnti pub extern fn ZigClangMacroDefinitionRecord_getName_getNameStart(*const ZigClangMacroDefinitionRecord) [*:0]const u8; pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getBegin(*const ZigClangMacroDefinitionRecord) ZigClangSourceLocation; pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getEnd(*const ZigClangMacroDefinitionRecord) ZigClangSourceLocation; + +pub extern fn ZigClangIfStmt_getThen(*const ZigClangIfStmt) *const ZigClangStmt; +pub extern fn ZigClangIfStmt_getElse(*const ZigClangIfStmt) ?*const ZigClangStmt; +pub extern fn ZigClangIfStmt_getCond(*const ZigClangIfStmt) *const ZigClangStmt; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index fd7db400a1..d8fc5ece9c 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -67,18 +67,17 @@ const Scope = struct { variables: AliasList, label: ?[]const u8, - /// Don't forget to set rbrace token later + /// Don't forget to set rbrace token and block_node later fn init(c: *Context, parent: *Scope, want_label: bool) !*Block { // TODO removing `?[]const u8` here causes LLVM error const label: ?[]const u8 = if (want_label) try std.fmt.allocPrint(c.a(), "blk_{}", .{c.getMangle()}) else null; - const block_node = try transCreateNodeBlock(c, label); const block = try c.a().create(Block); block.* = .{ .base = .{ .id = .Block, .parent = parent, }, - .block_node = block_node, + .block_node = undefined, .variables = AliasList.init(c.a()), .label = label, }; @@ -126,7 +125,6 @@ const Scope = struct { }; const Condition = struct { - expr: *ast.Node, base: Scope, }; @@ -612,6 +610,7 @@ fn transStmt( .ParenExprClass => return transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), result_used, lrvalue), .InitListExprClass => return transInitListExpr(rp, scope, @ptrCast(*const ZigClangInitListExpr, stmt), result_used), .ImplicitValueInitExprClass => return transImplicitValueInitExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used), + .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const ZigClangIfStmt, stmt), result_used), else => { return revertAndWarn( rp, @@ -718,18 +717,31 @@ fn transBinaryOperator( ), .Comma => { const block_scope = try scope.findBlockScope(rp.c); + const expr = block_scope.base.parent == scope; + const lparen = if (expr) blk: { + const l = try appendToken(rp.c, .LParen, "("); + block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label); + break :blk l; + } else undefined; const lhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getLHS(stmt), .unused, .r_value); try block_scope.block_node.statements.push(lhs); const rhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); - if (block_scope.base.parent == scope) { + if (expr) { + _ = try appendToken(rp.c, .Semicolon, ";"); const break_node = try transCreateNodeBreak(rp.c, block_scope.label); break_node.rhs = rhs; - _ = try appendToken(rp.c, .Semicolon, ";"); try block_scope.block_node.statements.push(&break_node.base); block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); - return maybeSuppressResult(rp, scope, result_used, &block_scope.block_node.base); + const rparen = try appendToken(rp.c, .RParen, ")"); + const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); + grouped_expr.* = .{ + .lparen = lparen, + .expr = &block_scope.block_node.base, + .rparen = rparen, + }; + return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base); } else { return maybeSuppressResult(rp, scope, result_used, rhs); } @@ -766,6 +778,7 @@ fn transCompoundStmtInline( fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) TransError!*ast.Node { const block_scope = try Scope.Block.init(rp.c, scope, false); + block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label); try transCompoundStmtInline(rp, &block_scope.base, stmt, block_scope.block_node); block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); return &block_scope.block_node.base; @@ -792,7 +805,7 @@ fn transCStyleCastExprClass( fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt) TransError!*ast.Node { const c = rp.c; - const block_scope = try scope.findBlockScope(c); + const block_scope = scope.findBlockScope(c) catch unreachable; var it = ZigClangDeclStmt_decl_begin(stmt); const end_it = ZigClangDeclStmt_decl_end(stmt); @@ -1167,6 +1180,35 @@ fn transImplicitValueInitExpr( }; } +fn transIfStmt( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangIfStmt, + used: ResultUsed, +) TransError!*ast.Node { + // if (c) t + // if (c) t else e + const if_node = try transCreateNodeIf(rp.c); + + var cond_scope = Scope.Condition{ + .base = .{ + .parent = scope, + .id = .Condition, + }, + }; + if_node.condition = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt)), .used, .r_value); + _ = try appendToken(rp.c, .RParen, ")"); + + if_node.body = try transStmt(rp, scope, ZigClangIfStmt_getThen(stmt), .used, .r_value); + + if (ZigClangIfStmt_getElse(stmt)) |expr| { + if_node.@"else" = try transCreateNodeElse(rp.c); + if_node.@"else".?.body = try transStmt(rp, scope, expr, .used, .r_value); + } + _ = try appendToken(rp.c, .Semicolon, ";"); + return &if_node.base; +} + fn transCPtrCast( rp: RestorePoint, loc: ZigClangSourceLocation, @@ -1602,6 +1644,7 @@ fn transCreateNodeAssign( // zig: }) _ = try appendToken(rp.c, .LParen, "("); const block_scope = try Scope.Block.init(rp.c, scope, true); + block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label); const tmp = try std.fmt.allocPrint(rp.c.a(), "_tmp_{}", .{rp.c.getMangle()}); const node = try transCreateNodeVarDecl(rp.c, false, true, tmp); diff --git a/test/translate_c.zig b/test/translate_c.zig index 7a9479034d..c97ccfa6ab 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -732,6 +732,29 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("if statements", + \\int foo(char c) { + \\ if (2) { + \\ int a = 2; + \\ } + \\ if (2, 5) { + \\ int a = 2; + \\ } + \\} + , &[_][]const u8{ + \\pub export fn foo(c: u8) c_int { + \\ if (2 != 0) { + \\ var a: c_int = 2; + \\ } + \\ if ((blk_1: { + \\ _ = 2; + \\ break :blk_1 5; + \\ }) != 0) { + \\ var a: c_int = 2; + \\ } + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// if (builtin.os != builtin.Os.windows) { From 6a3d48353b14ff234484887258cfab5ef51fdea0 Mon Sep 17 00:00:00 2001 From: Vexu Date: Mon, 16 Dec 2019 21:45:38 +0200 Subject: [PATCH 08/37] translate-c-2 while loops --- src-self-hosted/clang.zig | 9 +++ src-self-hosted/translate_c.zig | 134 ++++++++++++++++++++++++++++---- test/translate_c.zig | 40 ++++++++++ 3 files changed, 166 insertions(+), 17 deletions(-) diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index 381673e704..a3db8f7c2b 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -1040,3 +1040,12 @@ pub extern fn ZigClangMacroDefinitionRecord_getSourceRange_getEnd(*const ZigClan pub extern fn ZigClangIfStmt_getThen(*const ZigClangIfStmt) *const ZigClangStmt; pub extern fn ZigClangIfStmt_getElse(*const ZigClangIfStmt) ?*const ZigClangStmt; pub extern fn ZigClangIfStmt_getCond(*const ZigClangIfStmt) *const ZigClangStmt; + +pub extern fn ZigClangWhileStmt_getCond(*const ZigClangWhileStmt) *const ZigClangExpr; +pub extern fn ZigClangWhileStmt_getBody(*const ZigClangWhileStmt) *const ZigClangStmt; + +pub extern fn ZigClangDoStmt_getCond(*const ZigClangDoStmt) *const ZigClangExpr; +pub extern fn ZigClangDoStmt_getBody(*const ZigClangDoStmt) *const ZigClangStmt; + +pub extern fn ZigClangDoStmt_getCond(*const ZigClangDoStmt) *const ZigClangExpr; +pub extern fn ZigClangDoStmt_getBody(*const ZigClangDoStmt) *const ZigClangStmt; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index d8fc5ece9c..accbef9214 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -172,7 +172,7 @@ const Scope = struct { // comma operator used return try Block.init(c, scope, true); }, - else => scope = inner, + else => scope = scope.parent.?, } } } @@ -610,7 +610,9 @@ fn transStmt( .ParenExprClass => return transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), result_used, lrvalue), .InitListExprClass => return transInitListExpr(rp, scope, @ptrCast(*const ZigClangInitListExpr, stmt), result_used), .ImplicitValueInitExprClass => return transImplicitValueInitExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used), - .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const ZigClangIfStmt, stmt), result_used), + .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const ZigClangIfStmt, stmt)), + .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const ZigClangWhileStmt, stmt)), + .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const ZigClangDoStmt, stmt)), else => { return revertAndWarn( rp, @@ -1184,7 +1186,6 @@ fn transIfStmt( rp: RestorePoint, scope: *Scope, stmt: *const ZigClangIfStmt, - used: ResultUsed, ) TransError!*ast.Node { // if (c) t // if (c) t else e @@ -1196,7 +1197,7 @@ fn transIfStmt( .id = .Condition, }, }; - if_node.condition = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt)), .used, .r_value); + if_node.condition = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt)), .used, .r_value, false); _ = try appendToken(rp.c, .RParen, ")"); if_node.body = try transStmt(rp, scope, ZigClangIfStmt_getThen(stmt), .used, .r_value); @@ -1209,6 +1210,87 @@ fn transIfStmt( return &if_node.base; } +fn transWhileLoop( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangWhileStmt, +) TransError!*ast.Node { + const while_node = try transCreateNodeWhile(rp.c); + + var cond_scope = Scope.Condition{ + .base = .{ + .parent = scope, + .id = .Condition, + }, + }; + while_node.condition = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangWhileStmt_getCond(stmt)), .used, .r_value, false); + _ = try appendToken(rp.c, .RParen, ")"); + + while_node.body = try transStmt(rp, scope, ZigClangWhileStmt_getBody(stmt), .unused, .r_value); + return &while_node.base; +} + +fn transDoWhileLoop( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangDoStmt, +) TransError!*ast.Node { + const while_node = try transCreateNodeWhile(rp.c); + + while_node.condition = try transCreateNodeBoolLiteral(rp.c, true); + _ = try appendToken(rp.c, .RParen, ")"); + var new = false; + + const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) + // there's already a block in C, so we'll append our condition to it. + // c: do { + // c: a; + // c: b; + // c: } while(c); + // zig: while (true) { + // zig: a; + // zig: b; + // zig: if (!cond) break; + // zig: } + (try transStmt(rp, scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).? + else blk: { + // the C statement is without a block, so we need to create a block to contain it. + // c: do + // c: a; + // c: while(c); + // zig: while (true) { + // zig: a; + // zig: if (!cond) break; + // zig: } + + new = true; + const block = try transCreateNodeBlock(rp.c, null); + try block.statements.push(try transStmt(rp, scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)); + break :blk block; + }; + + // if (!cond) break; + const if_node = try transCreateNodeIf(rp.c); + var cond_scope = Scope.Condition{ + .base = .{ + .parent = scope, + .id = .Condition, + }, + }; + const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!"); + prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, false); + _ = try appendToken(rp.c, .RParen, ")"); + if_node.condition = &prefix_op.base; + if_node.body = &(try transCreateNodeBreak(rp.c, null)).base; + _ = try appendToken(rp.c, .Semicolon, ";"); + + try body_node.statements.push(&if_node.base); + if (new) + body_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + while_node.body = &body_node.base; + return &while_node.base; +} + fn transCPtrCast( rp: RestorePoint, loc: ZigClangSourceLocation, @@ -1682,7 +1764,7 @@ fn transCreateNodeBuiltinFnCall(c: *Context, name: []const u8) !*ast.Node.Builti const builtin_token = try appendToken(c, .Builtin, name); _ = try appendToken(c, .LParen, "("); const node = try c.a().create(ast.Node.BuiltinCall); - node.* = ast.Node.BuiltinCall{ + node.* = .{ .builtin_token = builtin_token, .params = ast.Node.BuiltinCall.ParamList.init(c.a()), .rparen_token = undefined, // set after appending args @@ -1693,10 +1775,10 @@ fn transCreateNodeBuiltinFnCall(c: *Context, name: []const u8) !*ast.Node.Builti 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{ + node.* = .{ .lhs = .{ .node = fn_expr }, - .op = ast.Node.SuffixOp.Op{ - .Call = ast.Node.SuffixOp.Op.Call{ + .op = .{ + .Call = .{ .params = ast.Node.SuffixOp.Op.Call.ParamList.init(c.a()), .async_token = null, }, @@ -1744,7 +1826,7 @@ fn transCreateNodeInfixOp( if (!grouped) return &node.base; const rparen = try appendToken(rp.c, .RParen, ")"); const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); - grouped_expr.* = ast.Node.GroupedExpression{ + grouped_expr.* = .{ .lparen = lparen, .expr = &node.base, .rparen = rparen, @@ -1776,9 +1858,9 @@ fn transCreateNodePtrType( .Asterisk => try appendToken(c, .Asterisk, "*"), else => unreachable, }; - node.* = ast.Node.PrefixOp{ + node.* = .{ .op_token = op_token, - .op = ast.Node.PrefixOp.Op{ + .op = .{ .PtrType = .{ .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null, .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null, @@ -1811,7 +1893,7 @@ fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*ast.Node { fn transCreateNodeReturnExpr(c: *Context) !*ast.Node.ControlFlowExpression { const ltoken = try appendToken(c, .Keyword_return, "return"); const node = try c.a().create(ast.Node.ControlFlowExpression); - node.* = ast.Node.ControlFlowExpression{ + node.* = .{ .ltoken = ltoken, .kind = .Return, .rhs = null, @@ -1822,7 +1904,7 @@ fn transCreateNodeReturnExpr(c: *Context) !*ast.Node.ControlFlowExpression { fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node { const token = try appendToken(c, .Keyword_undefined, "undefined"); const node = try c.a().create(ast.Node.UndefinedLiteral); - node.* = ast.Node.UndefinedLiteral{ + node.* = .{ .token = token, }; return &node.base; @@ -1831,7 +1913,7 @@ fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node { fn transCreateNodeNullLiteral(c: *Context) !*ast.Node { const token = try appendToken(c, .Keyword_null, "null"); const node = try c.a().create(ast.Node.NullLiteral); - node.* = ast.Node.NullLiteral{ + node.* = .{ .token = token, }; return &node.base; @@ -1843,7 +1925,7 @@ fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node { else try appendToken(c, .Keyword_false, "false"); const node = try c.a().create(ast.Node.BoolLiteral); - node.* = ast.Node.BoolLiteral{ + node.* = .{ .token = token, }; return &node.base; @@ -2032,7 +2114,7 @@ fn transCreateNodeBreak(c: *Context, label: ?[]const u8) !*ast.Node.ControlFlowE break :blk try transCreateNodeIdentifier(c, l); } else null; const node = try c.a().create(ast.Node.ControlFlowExpression); - node.* = ast.Node.ControlFlowExpression{ + node.* = .{ .ltoken = ltoken, .kind = .{ .Break = label_node }, .rhs = null, @@ -2046,7 +2128,7 @@ fn transCreateNodeVarDecl(c: *Context, is_pub: bool, is_const: bool, name: []con const name_tok = try appendIdentifier(c, name); const node = try c.a().create(ast.Node.VarDecl); - node.* = ast.Node.VarDecl{ + node.* = .{ .doc_comments = null, .visib_token = visib_tok, .thread_local_token = null, @@ -2065,6 +2147,24 @@ fn transCreateNodeVarDecl(c: *Context, is_pub: bool, is_const: bool, name: []con return node; } +fn transCreateNodeWhile(c: *Context) !*ast.Node.While { + const while_tok = try appendToken(c, .Keyword_while, "while"); + _ = try appendToken(c, .LParen, "("); + + const node = try c.a().create(ast.Node.While); + node.* = .{ + .label = null, + .inline_token = null, + .while_token = while_tok, + .condition = undefined, + .payload = null, + .continue_expr = null, + .body = undefined, + .@"else" = null, + }; + return node; +} + const RestorePoint = struct { c: *Context, token_index: ast.TokenIndex, diff --git a/test/translate_c.zig b/test/translate_c.zig index c97ccfa6ab..6b2014db9b 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -755,6 +755,46 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("while loops", + \\int foo() { + \\ int a = 5; + \\ while (2) + \\ a = 2; + \\ while (4) { + \\ int a = 4; + \\ a = 9; + \\ return 6, a; + \\ } + \\ do { + \\ int a = 2; + \\ a = 12; + \\ } while (4); + \\ do + \\ a = 7; + \\ while (4); + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ var a: c_int = 5; + \\ while (2 != 0) a = 2; + \\ while (4 != 0) { + \\ var a: c_int = 4; + \\ a = 9; + \\ _ = 6; + \\ return a; + \\ } + \\ while (true) { + \\ var a: c_int = 2; + \\ a = 12; + \\ if (!4 != 0) break; + \\ } + \\ while (true) { + \\ a = 7; + \\ if (!4 != 0) break; + \\ } + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// if (builtin.os != builtin.Os.windows) { From d54bcb2b62fe6cc7212bf370bcd257aaf1dfff8b Mon Sep 17 00:00:00 2001 From: Vexu Date: Mon, 16 Dec 2019 23:54:16 +0200 Subject: [PATCH 09/37] translate-c-2 break and continue --- src-self-hosted/translate_c.zig | 43 ++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index accbef9214..91c96756e0 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -54,6 +54,7 @@ const Scope = struct { const Switch = struct { base: Scope, + label: []const u8, }; /// used when getting a member `a.b` @@ -190,6 +191,7 @@ const Scope = struct { .Ref => null, .FnDef => @fieldParentPtr(FnDef, "base", scope).getAlias(name), .Block => @fieldParentPtr(Block, "base", scope).getAlias(name), + .Condition => scope.parent.?.getAlias(name), else => @panic("TODO Scope.getAlias"), }; } @@ -200,9 +202,15 @@ const Scope = struct { .Root => @fieldParentPtr(Root, "base", scope).contains(name), .FnDef => @fieldParentPtr(FnDef, "base", scope).contains(name), .Block => @fieldParentPtr(Block, "base", scope).contains(name), + .Condition => scope.parent.?.contains(name), else => @panic("TODO Scope.contains"), }; } + + fn getBreakableScope(scope: *Scope) *Scope { + var scope = inner; + while (scope.id != .Switch and scope.id != .Root) : (scope = scope.parent.?) {} + } }; const Context = struct { @@ -613,6 +621,13 @@ fn transStmt( .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const ZigClangIfStmt, stmt)), .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const ZigClangWhileStmt, stmt)), .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const ZigClangDoStmt, stmt)), + .NullStmtClass => { + const block = try transCreateNodeBlock(rp.c, null); + block.rbrace = try appendToken(rp.c, .RBrace, "}"); + return &block.base; + }, + .ContinueStmtClass => return transCreateNodeContinue(rp.c), + .BreakStmtClass => return transBreak(rp, scope), else => { return revertAndWarn( rp, @@ -1241,7 +1256,7 @@ fn transDoWhileLoop( _ = try appendToken(rp.c, .RParen, ")"); var new = false; - const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) + const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) blk: { // there's already a block in C, so we'll append our condition to it. // c: do { // c: a; @@ -1252,8 +1267,8 @@ fn transDoWhileLoop( // zig: b; // zig: if (!cond) break; // zig: } - (try transStmt(rp, scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).? - else blk: { + break :blk (try transStmt(rp, scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).? + } else blk: { // the C statement is without a block, so we need to create a block to contain it. // c: do // c: a; @@ -1262,7 +1277,6 @@ fn transDoWhileLoop( // zig: a; // zig: if (!cond) break; // zig: } - new = true; const block = try transCreateNodeBlock(rp.c, null); try block.statements.push(try transStmt(rp, scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)); @@ -1328,6 +1342,15 @@ fn transCPtrCast( return &ptrcast_node.base; } +fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node { + const break_scope = scope.getBreakableScope(); + const br = try transCreateNodeBreak(rp.c, if (break_scope.id == .Switch) + @fieldParentPtr(Scope.Switch, "base", break_scope).label + else + null); + return &br.base; +} + fn maybeSuppressResult( rp: RestorePoint, scope: *Scope, @@ -2165,6 +2188,18 @@ fn transCreateNodeWhile(c: *Context) !*ast.Node.While { return node; } +fn transCreateNodeContinue(c: *Context) !*ast.Node { + const ltoken = try appendToken(c, .Keyword_continue, "continue"); + const node = try c.a().create(ast.Node.ControlFlowExpression); + node.* = .{ + .ltoken = ltoken, + .kind = .{ .Continue = null }, + .rhs = null, + }; + _ = try appendToken(rp.c, .Semicolon, ";"); + return &node.base; +} + const RestorePoint = struct { c: *Context, token_index: ast.TokenIndex, From ab6fe57462374d59b20ac46eeb4eefbf2f6938a0 Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 17 Dec 2019 01:08:08 +0200 Subject: [PATCH 10/37] translate-c-2 for loops --- src-self-hosted/clang.zig | 6 ++- src-self-hosted/translate_c.zig | 73 +++++++++++++++++++++++++++++---- test/translate_c.zig | 24 +++++++++++ 3 files changed, 92 insertions(+), 11 deletions(-) diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index a3db8f7c2b..80d8cd2c06 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -1047,5 +1047,7 @@ pub extern fn ZigClangWhileStmt_getBody(*const ZigClangWhileStmt) *const ZigClan pub extern fn ZigClangDoStmt_getCond(*const ZigClangDoStmt) *const ZigClangExpr; pub extern fn ZigClangDoStmt_getBody(*const ZigClangDoStmt) *const ZigClangStmt; -pub extern fn ZigClangDoStmt_getCond(*const ZigClangDoStmt) *const ZigClangExpr; -pub extern fn ZigClangDoStmt_getBody(*const ZigClangDoStmt) *const ZigClangStmt; +pub extern fn ZigClangForStmt_getInit(*const ZigClangForStmt) ?*const ZigClangStmt; +pub extern fn ZigClangForStmt_getCond(*const ZigClangForStmt) ?*const ZigClangExpr; +pub extern fn ZigClangForStmt_getInc(*const ZigClangForStmt) ?*const ZigClangExpr; +pub extern fn ZigClangForStmt_getBody(*const ZigClangForStmt) *const ZigClangStmt; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 91c96756e0..9f74ce72e1 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -207,9 +207,10 @@ const Scope = struct { }; } - fn getBreakableScope(scope: *Scope) *Scope { + fn getBreakableScope(inner: *Scope) *Scope { var scope = inner; while (scope.id != .Switch and scope.id != .Root) : (scope = scope.parent.?) {} + return scope; } }; @@ -626,8 +627,9 @@ fn transStmt( block.rbrace = try appendToken(rp.c, .RBrace, "}"); return &block.base; }, - .ContinueStmtClass => return transCreateNodeContinue(rp.c), + .ContinueStmtClass => return try transCreateNodeContinue(rp.c), .BreakStmtClass => return transBreak(rp, scope), + .ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const ZigClangForStmt, stmt)), else => { return revertAndWarn( rp, @@ -795,7 +797,7 @@ fn transCompoundStmtInline( fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) TransError!*ast.Node { const block_scope = try Scope.Block.init(rp.c, scope, false); - block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label); + block_scope.block_node = try transCreateNodeBlock(rp.c, null); try transCompoundStmtInline(rp, &block_scope.base, stmt, block_scope.block_node); block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); return &block_scope.block_node.base; @@ -1267,7 +1269,7 @@ fn transDoWhileLoop( // zig: b; // zig: if (!cond) break; // zig: } - break :blk (try transStmt(rp, scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).? + break :blk (try transStmt(rp, scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).?; } else blk: { // the C statement is without a block, so we need to create a block to contain it. // c: do @@ -1305,6 +1307,50 @@ fn transDoWhileLoop( return &while_node.base; } +fn transForLoop( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangForStmt, +) TransError!*ast.Node { + var inner = scope; + var block = false; + var block_scope: ?*Scope.Block = null; + if (ZigClangForStmt_getInit(stmt)) |init| { + block_scope = try Scope.Block.init(rp.c, scope, false); + block_scope.?.block_node = try transCreateNodeBlock(rp.c, null); + inner = &block_scope.?.base; + _ = try transStmt(rp, inner, init, .unused, .r_value); + } + var cond_scope = Scope.Condition{ + .base = .{ + .parent = inner, + .id = .Condition, + }, + }; + + const while_node = try transCreateNodeWhile(rp.c); + while_node.condition = if (ZigClangForStmt_getCond(stmt)) |cond| + try transBoolExpr(rp, &cond_scope.base, cond, .used, .r_value, false) + else + try transCreateNodeBoolLiteral(rp.c, true); + _ = try appendToken(rp.c, .RParen, ")"); + + if (ZigClangForStmt_getInc(stmt)) |incr| { + _ = try appendToken(rp.c, .Colon, ":"); + _ = try appendToken(rp.c, .LParen, "("); + while_node.continue_expr = try transExpr(rp, &cond_scope.base, incr, .unused, .r_value); + _ = try appendToken(rp.c, .RParen, ")"); + } + + while_node.body = try transStmt(rp, inner, ZigClangForStmt_getBody(stmt), .unused, .r_value); + if (block_scope != null) { + try block_scope.?.block_node.statements.push(&while_node.base); + block_scope.?.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + return &block_scope.?.block_node.base; + } else + return &while_node.base; +} + fn transCPtrCast( rp: RestorePoint, loc: ZigClangSourceLocation, @@ -1358,12 +1404,20 @@ fn maybeSuppressResult( result: *ast.Node, ) TransError!*ast.Node { if (used == .used) return result; - // NOTE: This is backwards, but the semicolon must immediately follow the node. - _ = try appendToken(rp.c, .Semicolon, ";"); + if (scope.id != .Condition) { + // NOTE: This is backwards, but the semicolon must immediately follow the node. + _ = try appendToken(rp.c, .Semicolon, ";"); + } else { // TODO is there a way to avoid this hack? + // this parenthesis must come immediately following the node + _ = try appendToken(rp.c, .RParen, ")"); + // these need to come before _ + _ = try appendToken(rp.c, .Colon, ":"); + _ = try appendToken(rp.c, .LParen, "("); + } const lhs = try transCreateNodeIdentifier(rp.c, "_"); const op_token = try appendToken(rp.c, .Equal, "="); const op_node = try rp.c.a().create(ast.Node.InfixOp); - op_node.* = ast.Node.InfixOp{ + op_node.* = .{ .op_token = op_token, .lhs = lhs, .op = .Assign, @@ -1728,7 +1782,8 @@ fn transCreateNodeAssign( const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value); const eq_token = try appendToken(rp.c, .Equal, "="); const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); - _ = try appendToken(rp.c, .Semicolon, ";"); + if (scope.id != .Condition) + _ = try appendToken(rp.c, .Semicolon, ";"); const node = try rp.c.a().create(ast.Node.InfixOp); node.* = .{ @@ -2196,7 +2251,7 @@ fn transCreateNodeContinue(c: *Context) !*ast.Node { .kind = .{ .Continue = null }, .rhs = null, }; - _ = try appendToken(rp.c, .Semicolon, ";"); + _ = try appendToken(c, .Semicolon, ";"); return &node.base; } diff --git a/test/translate_c.zig b/test/translate_c.zig index 6b2014db9b..b4164297ea 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -795,6 +795,30 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("for loops", + \\int foo() { + \\ for (int i = 2, b = 4; i + 2; i = 2) { + \\ int a = 2; + \\ a = 6, 5, 7; + \\ } + \\ char i = 2; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ { + \\ var i: c_int = 2; + \\ var b: c_int = 4; + \\ while ((i + 2) != 0) : (i = 2) { + \\ var a: c_int = 2; + \\ a = 6; + \\ _ = 5; + \\ _ = 7; + \\ } + \\ } + \\ var i: u8 = @as(u8, 2); + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// if (builtin.os != builtin.Os.windows) { From 9cda93a24e3f4eaae63f3a7a8da99e91f47222fa Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 17 Dec 2019 10:20:02 +0200 Subject: [PATCH 11/37] translate-c-2 don't shadow primitive types --- src-self-hosted/translate_c.zig | 75 ++++++++++++++++++++++++--------- test/translate_c.zig | 18 +++++--- 2 files changed, 66 insertions(+), 27 deletions(-) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 9f74ce72e1..cbb7639c42 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -69,9 +69,7 @@ const Scope = struct { label: ?[]const u8, /// Don't forget to set rbrace token and block_node later - fn init(c: *Context, parent: *Scope, want_label: bool) !*Block { - // TODO removing `?[]const u8` here causes LLVM error - const label: ?[]const u8 = if (want_label) try std.fmt.allocPrint(c.a(), "blk_{}", .{c.getMangle()}) else null; + fn init(c: *Context, parent: *Scope, label: ?[]const u8) !*Block { const block = try c.a().create(Block); block.* = .{ .base = .{ @@ -171,7 +169,7 @@ const Scope = struct { .Condition => { const cond = @fieldParentPtr(Condition, "base", scope); // comma operator used - return try Block.init(c, scope, true); + return try Block.init(c, scope, "blk"); }, else => scope = scope.parent.?, } @@ -179,7 +177,7 @@ const Scope = struct { } fn createAlias(scope: *Scope, c: *Context, name: []const u8) !?[]const u8 { - if (scope.contains(name)) { + if (isZigPrimitiveType(name) or scope.contains(name)) { return try std.fmt.allocPrint(c.a(), "{}_{}", .{ name, c.getMangle() }); } return null; @@ -452,7 +450,11 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { const scope = &c.global_scope.base; const var_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, var_decl))); - _ = try c.decl_table.put(@ptrToInt(var_decl), var_name); + + // TODO https://github.com/ziglang/zig/issues/3756 + // TODO https://github.com/ziglang/zig/issues/1802 + const checked_name = if (isZigPrimitiveType(var_name)) try std.fmt.allocPrint(c.a(), "_{}", .{var_name}) else var_name; + _ = try c.decl_table.put(@ptrToInt(var_decl), checked_name); const var_decl_loc = ZigClangVarDecl_getLocation(var_decl); const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl); @@ -471,12 +473,12 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { else try appendToken(c, .Keyword_var, "var"); - const name_tok = try appendIdentifier(c, var_name); + const name_tok = try appendIdentifier(c, checked_name); _ = try appendToken(c, .Colon, ":"); const type_node = transQualType(rp, qual_type, var_decl_loc) catch |err| switch (err) { error.UnsupportedType => { - return failDecl(c, var_decl_loc, var_name, "unable to resolve variable type", .{}); + return failDecl(c, var_decl_loc, checked_name, "unable to resolve variable type", .{}); }, error.OutOfMemory => |e| return e, }; @@ -491,14 +493,14 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { error.UnsupportedTranslation, error.UnsupportedType, => { - return failDecl(c, var_decl_loc, var_name, "unable to translate initializer", .{}); + return failDecl(c, var_decl_loc, checked_name, "unable to translate initializer", .{}); }, error.OutOfMemory => |e| return e, } else try transCreateNodeUndefinedLiteral(c); } else if (storage_class != .Extern) { - return failDecl(c, var_decl_loc, var_name, "non-extern variable has no initializer", .{}); + return failDecl(c, var_decl_loc, checked_name, "non-extern variable has no initializer", .{}); } const node = try c.a().create(ast.Node.VarDecl); @@ -518,7 +520,7 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { .init_node = init_node, .semicolon_token = try appendToken(c, .Semicolon, ";"), }; - return addTopLevelDecl(c, var_name, &node.base); + return addTopLevelDecl(c, checked_name, &node.base); } fn resolveTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl) Error!void { @@ -796,7 +798,7 @@ fn transCompoundStmtInline( } fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundStmt) TransError!*ast.Node { - const block_scope = try Scope.Block.init(rp.c, scope, false); + const block_scope = try Scope.Block.init(rp.c, scope, null); block_scope.block_node = try transCreateNodeBlock(rp.c, null); try transCompoundStmtInline(rp, &block_scope.base, stmt, block_scope.block_node); block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); @@ -1316,7 +1318,7 @@ fn transForLoop( var block = false; var block_scope: ?*Scope.Block = null; if (ZigClangForStmt_getInit(stmt)) |init| { - block_scope = try Scope.Block.init(rp.c, scope, false); + block_scope = try Scope.Block.init(rp.c, scope, null); block_scope.?.block_node = try transCreateNodeBlock(rp.c, null); inner = &block_scope.?.base; _ = try transStmt(rp, inner, init, .unused, .r_value); @@ -1803,7 +1805,7 @@ fn transCreateNodeAssign( // zig: break :x _tmp // zig: }) _ = try appendToken(rp.c, .LParen, "("); - const block_scope = try Scope.Block.init(rp.c, scope, true); + const block_scope = try Scope.Block.init(rp.c, scope, "blk"); block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label); const tmp = try std.fmt.allocPrint(rp.c.a(), "_tmp_{}", .{rp.c.getMangle()}); @@ -2732,6 +2734,33 @@ fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, return token_index; } +// TODO hook up with codegen +fn isZigPrimitiveType(name: []const u8) bool { + if (name.len > 1 and std.mem.startsWith(u8, name, "u") or std.mem.startsWith(u8, name, "u")) { + for (name[1..]) |c| { + switch (c) { + '0'...'9' => {}, + else => return false, + } + } + return true; + } + // void is invalid in c so it doesn't need to be checked. + return std.mem.eql(u8, name, "comptime_float") or + std.mem.eql(u8, name, "comptime_int") or + std.mem.eql(u8, name, "bool") or + std.mem.eql(u8, name, "isize") or + std.mem.eql(u8, name, "usize") or + std.mem.eql(u8, name, "f16") or + std.mem.eql(u8, name, "f32") or + std.mem.eql(u8, name, "f64") or + std.mem.eql(u8, name, "f128") or + std.mem.eql(u8, name, "c_longdouble") or + std.mem.eql(u8, name, "noreturn") or + std.mem.eql(u8, name, "type") or + std.mem.eql(u8, name, "anyerror"); +} + fn isValidZigIdentifier(name: []const u8) bool { for (name) |c, i| { switch (c) { @@ -2782,27 +2811,31 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void { const begin_loc = ZigClangMacroDefinitionRecord_getSourceRange_getBegin(macro); const name = try c.str(raw_name); - if (scope.contains(name)) { + + // TODO https://github.com/ziglang/zig/issues/3756 + // TODO https://github.com/ziglang/zig/issues/1802 + const checked_name = if (isZigPrimitiveType(name)) try std.fmt.allocPrint(c.a(), "_{}", .{name}) else name; + if (scope.contains(checked_name)) { continue; } const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc); ctok.tokenizeCMacro(&tok_list, begin_c) catch |err| switch (err) { error.OutOfMemory => |e| return e, else => { - try failDecl(c, begin_loc, name, "unable to tokenize macro definition", .{}); + try failDecl(c, begin_loc, checked_name, "unable to tokenize macro definition", .{}); continue; }, }; var tok_it = tok_list.iterator(0); const first_tok = tok_it.next().?; - assert(first_tok.id == .Identifier and std.mem.eql(u8, first_tok.bytes, name)); + assert(first_tok.id == .Identifier and std.mem.eql(u8, first_tok.bytes, checked_name)); const next = tok_it.peek().?; switch (next.id) { .Identifier => { // if it equals itself, ignore. for example, from stdio.h: // #define stdin stdin - if (std.mem.eql(u8, name, next.bytes)) { + if (std.mem.eql(u8, checked_name, next.bytes)) { continue; } }, @@ -2819,12 +2852,12 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void { } else false; (if (macro_fn) - transMacroFnDefine(c, &tok_it, name, begin_loc) + transMacroFnDefine(c, &tok_it, checked_name, begin_loc) else - transMacroDefine(c, &tok_it, name, begin_loc)) catch |err| switch (err) { + transMacroDefine(c, &tok_it, checked_name, begin_loc)) catch |err| switch (err) { error.UnsupportedTranslation, error.ParseError, - => try failDecl(c, begin_loc, name, "unable to translate macro", .{}), + => try failDecl(c, begin_loc, checked_name, "unable to translate macro", .{}), error.OutOfMemory => |e| return e, }; }, diff --git a/test/translate_c.zig b/test/translate_c.zig index b4164297ea..93535987d5 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -724,10 +724,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub export fn foo(c: u8) c_int { \\ var a: c_int = undefined; \\ var b: c_int = undefined; - \\ a = blk_1: { - \\ const _tmp_2 = 2; - \\ b = _tmp_2; - \\ break :blk_1 _tmp_2; + \\ a = blk: { + \\ const _tmp_1 = 2; + \\ b = _tmp_1; + \\ break :blk _tmp_1; \\ }; \\} }); @@ -746,9 +746,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ if (2 != 0) { \\ var a: c_int = 2; \\ } - \\ if ((blk_1: { + \\ if ((blk: { \\ _ = 2; - \\ break :blk_1 5; + \\ break :blk 5; \\ }) != 0) { \\ var a: c_int = 2; \\ } @@ -819,6 +819,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("shadowing primitive types", + \\unsigned anyerror = 2; + , &[_][]const u8{ + \\pub export var _anyerror: c_uint = @as(c_uint, 2); + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// if (builtin.os != builtin.Os.windows) { From daa22d42b0cdb8ccf04625ea921f3e71b647b68c Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 17 Dec 2019 11:15:41 +0200 Subject: [PATCH 12/37] translate-c-2 floats --- src-self-hosted/clang.zig | 4 +++ src-self-hosted/translate_c.zig | 60 ++++++++++++++++++++++++++++----- src/zig_clang.cpp | 5 +++ src/zig_clang.h | 1 + test/translate_c.zig | 12 +++++++ 5 files changed, 74 insertions(+), 8 deletions(-) diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index 80d8cd2c06..559a4ad0e5 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -76,6 +76,7 @@ pub const struct_ZigClangFunctionType = @OpaqueType(); pub const struct_ZigClangPredefinedExpr = @OpaqueType(); pub const struct_ZigClangInitListExpr = @OpaqueType(); pub const ZigClangPreprocessingRecord = @OpaqueType(); +pub const ZigClangFloatingLiteral = @OpaqueType(); pub const ZigClangBO = extern enum { PtrMemD, @@ -1051,3 +1052,6 @@ pub extern fn ZigClangForStmt_getInit(*const ZigClangForStmt) ?*const ZigClangSt pub extern fn ZigClangForStmt_getCond(*const ZigClangForStmt) ?*const ZigClangExpr; pub extern fn ZigClangForStmt_getInc(*const ZigClangForStmt) ?*const ZigClangExpr; pub extern fn ZigClangForStmt_getBody(*const ZigClangForStmt) *const ZigClangStmt; + +pub extern fn ZigClangAPFloat_toString(self: *const ZigClangAPFloat, precision: c_uint, maxPadding: c_uint, truncateZero: bool) [*:0]const u8; +pub extern fn ZigClangAPFloat_getValueAsApproximateDouble(*const ZigClangFloatingLiteral) f64; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index cbb7639c42..4cdef9f805 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -632,6 +632,7 @@ fn transStmt( .ContinueStmtClass => return try transCreateNodeContinue(rp.c), .BreakStmtClass => return transBreak(rp, scope), .ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const ZigClangForStmt, stmt)), + .FloatingLiteralClass => return transFloatingLiteral(rp, scope, @ptrCast(*const ZigClangFloatingLiteral, stmt), result_used), else => { return revertAndWarn( rp, @@ -896,16 +897,11 @@ fn transImplicitCastExpr( const sub_expr = ZigClangImplicitCastExpr_getSubExpr(expr); const sub_expr_node = try transExpr(rp, scope, @ptrCast(*const ZigClangExpr, sub_expr), .used, .r_value); switch (ZigClangImplicitCastExpr_getCastKind(expr)) { - .BitCast => { + .BitCast, .FloatingCast, .FloatingToIntegral, .IntegralToFloating, .IntegralCast => { const dest_type = getExprQualType(c, @ptrCast(*const ZigClangExpr, expr)); const src_type = getExprQualType(c, sub_expr); return transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node); }, - .IntegralCast => { - const dest_type = ZigClangExpr_getType(@ptrCast(*const ZigClangExpr, expr)); - const src_type = ZigClangExpr_getType(sub_expr); - return transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node); - }, .FunctionToPointerDecay, .ArrayToPointerDecay => { return maybeSuppressResult(rp, scope, result_used, sub_expr_node); }, @@ -1057,6 +1053,30 @@ fn transCCast( builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); return &builtin_node.base; } + if (cIsFloating(src_type) and cIsFloating(dst_type)) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@floatCast"); + try builtin_node.params.push(try transQualType(rp, dst_type, loc)); + _ = try appendToken(rp.c, .Comma, ","); + try builtin_node.params.push(expr); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return &builtin_node.base; + } + if (cIsFloating(src_type) and !cIsFloating(dst_type)) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@floatToInt"); + try builtin_node.params.push(try transQualType(rp, dst_type, loc)); + _ = try appendToken(rp.c, .Comma, ","); + try builtin_node.params.push(expr); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return &builtin_node.base; + } + if (!cIsFloating(src_type) and cIsFloating(dst_type)) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@intToFloat"); + try builtin_node.params.push(try transQualType(rp, dst_type, loc)); + _ = try appendToken(rp.c, .Comma, ","); + try builtin_node.params.push(expr); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return &builtin_node.base; + } // TODO: maybe widen to increase size // TODO: maybe bitcast to change sign // TODO: maybe truncate to reduce size @@ -1399,6 +1419,16 @@ fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node { return &br.base; } +fn transFloatingLiteral(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangFloatingLiteral, used: ResultUsed) TransError!*ast.Node { + // TODO use something more accurate + const dbl = ZigClangAPFloat_getValueAsApproximateDouble(stmt); + const node = try rp.c.a().create(ast.Node.FloatLiteral); + node.* = .{ + .token = try appendTokenFmt(rp.c, .FloatLiteral, "{d}", .{dbl}), + }; + return maybeSuppressResult(rp, scope, used, &node.base); +} + fn maybeSuppressResult( rp: RestorePoint, scope: *Scope, @@ -1770,6 +1800,20 @@ fn cIsUnsignedInteger(qt: ZigClangQualType) bool { }; } +fn cIsFloating(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)) { + .Float, + .Double, + .Float128, + .LongDouble, + => true, + else => false, + }; +} + fn transCreateNodeAssign( rp: RestorePoint, scope: *Scope, @@ -1964,7 +2008,7 @@ fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*ast.Node { }; const token = try appendToken(c, .IntegerLiteral, str); const node = try c.a().create(ast.Node.IntegerLiteral); - node.* = ast.Node.IntegerLiteral{ + node.* = .{ .token = token, }; return &node.base; @@ -2027,7 +2071,7 @@ fn transCreateNodeArrayInitializer(c: *Context, dot_tok: ast.TokenIndex) !*ast.N fn transCreateNodeInt(c: *Context, int: var) !*ast.Node { const token = try appendTokenFmt(c, .IntegerLiteral, "{}", .{int}); const node = try c.a().create(ast.Node.IntegerLiteral); - node.* = ast.Node.IntegerLiteral{ + node.* = .{ .token = token, }; return &node.base; diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index 86ef17ffbe..d87b769f5e 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -2161,6 +2161,11 @@ unsigned ZigClangAPFloat_convertToHexString(const ZigClangAPFloat *self, char *D return casted->convertToHexString(DST, HexDigits, UpperCase, (llvm::APFloat::roundingMode)RM); } +double ZigClangAPFloat_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self) { + auto casted = reinterpret_cast(self); + return casted->getValueAsApproximateDouble(); +} + enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self) { auto casted = reinterpret_cast(self); return (ZigClangStringLiteral_StringKind)casted->getKind(); diff --git a/src/zig_clang.h b/src/zig_clang.h index cf60fdfca9..8dd0b9b3d5 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -985,6 +985,7 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDeclStmt_getBeginLoc(const st ZIG_EXTERN_C unsigned ZigClangAPFloat_convertToHexString(const struct ZigClangAPFloat *self, char *DST, unsigned HexDigits, bool UpperCase, enum ZigClangAPFloat_roundingMode RM); +ZIG_EXTERN_C double ZigClangAPFloat_getValueAsApproximateDouble(const ZigClangFloatingLiteral *self); ZIG_EXTERN_C enum ZigClangStringLiteral_StringKind ZigClangStringLiteral_getKind(const struct ZigClangStringLiteral *self); ZIG_EXTERN_C const char *ZigClangStringLiteral_getString_bytes_begin_size(const struct ZigClangStringLiteral *self, diff --git a/test/translate_c.zig b/test/translate_c.zig index 93535987d5..02ba62a471 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -825,6 +825,18 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub export var _anyerror: c_uint = @as(c_uint, 2); }); + cases.add_2("floats", + \\float a = 3.1415; + \\double b = 3.1415; + \\int c = 3.1415; + \\double d = 3; + , &[_][]const u8{ + \\pub export var a: f32 = @floatCast(f32, 3.1415); + \\pub export var b: f64 = 3.1415; + \\pub export var c: c_int = @floatToInt(c_int, 3.1415); + \\pub export var d: f64 = @intToFloat(f64, 3); + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// if (builtin.os != builtin.Os.windows) { From 0283ab8a1a27c9d3a7a6d88960418485a7125d1d Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 17 Dec 2019 12:06:28 +0200 Subject: [PATCH 13/37] translate-c-2 conditional operator --- src-self-hosted/clang.zig | 4 ++++ src-self-hosted/translate_c.zig | 42 +++++++++++++++++++++++++++++++-- test/translate_c.zig | 12 ++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index 559a4ad0e5..dcb830df69 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -1055,3 +1055,7 @@ pub extern fn ZigClangForStmt_getBody(*const ZigClangForStmt) *const ZigClangStm pub extern fn ZigClangAPFloat_toString(self: *const ZigClangAPFloat, precision: c_uint, maxPadding: c_uint, truncateZero: bool) [*:0]const u8; pub extern fn ZigClangAPFloat_getValueAsApproximateDouble(*const ZigClangFloatingLiteral) f64; + +pub extern fn ZigClangConditionalOperator_getCond(*const ZigClangConditionalOperator) *const ZigClangExpr; +pub extern fn ZigClangConditionalOperator_getTrueExpr(*const ZigClangConditionalOperator) *const ZigClangExpr; +pub extern fn ZigClangConditionalOperator_getFalseExpr(*const ZigClangConditionalOperator) *const ZigClangExpr; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 4cdef9f805..79b32c2fa2 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -633,6 +633,7 @@ fn transStmt( .BreakStmtClass => return transBreak(rp, scope), .ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const ZigClangForStmt, stmt)), .FloatingLiteralClass => return transFloatingLiteral(rp, scope, @ptrCast(*const ZigClangFloatingLiteral, stmt), result_used), + .ConditionalOperatorClass => return transConditionalOperator(rp, scope, @ptrCast(*const ZigClangConditionalOperator, stmt), result_used), else => { return revertAndWarn( rp, @@ -1239,11 +1240,11 @@ fn transIfStmt( if_node.condition = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt)), .used, .r_value, false); _ = try appendToken(rp.c, .RParen, ")"); - if_node.body = try transStmt(rp, scope, ZigClangIfStmt_getThen(stmt), .used, .r_value); + if_node.body = try transStmt(rp, scope, ZigClangIfStmt_getThen(stmt), .unused, .r_value); if (ZigClangIfStmt_getElse(stmt)) |expr| { if_node.@"else" = try transCreateNodeElse(rp.c); - if_node.@"else".?.body = try transStmt(rp, scope, expr, .used, .r_value); + if_node.@"else".?.body = try transStmt(rp, scope, expr, .unused, .r_value); } _ = try appendToken(rp.c, .Semicolon, ";"); return &if_node.base; @@ -1429,6 +1430,43 @@ fn transFloatingLiteral(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangFl return maybeSuppressResult(rp, scope, used, &node.base); } +fn transConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangConditionalOperator, used: ResultUsed) TransError!*ast.Node { + const gropued = scope.id == .Condition; + const lparen = if (gropued) try appendToken(rp.c, .LParen, "(") else undefined; + const if_node = try transCreateNodeIf(rp.c); + var cond_scope = Scope.Condition{ + .base = .{ + .parent = scope, + .id = .Condition, + }, + }; + + const cond_expr = ZigClangConditionalOperator_getCond(stmt); + const true_expr = ZigClangConditionalOperator_getTrueExpr(stmt); + const false_expr = ZigClangConditionalOperator_getFalseExpr(stmt); + + if_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false); + _ = try appendToken(rp.c, .RParen, ")"); + + if_node.body = try transExpr(rp, scope, true_expr, .used, .r_value); + + if_node.@"else" = try transCreateNodeElse(rp.c); + if_node.@"else".?.body = try transExpr(rp, scope, false_expr, .used, .r_value); + + if (gropued) { + const rparen = try appendToken(rp.c, .RParen, ")"); + const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); + grouped_expr.* = .{ + .lparen = lparen, + .expr = &if_node.base, + .rparen = rparen, + }; + return maybeSuppressResult(rp, scope, used, &grouped_expr.base); + } else { + return maybeSuppressResult(rp, scope, used, &if_node.base); + } +} + fn maybeSuppressResult( rp: RestorePoint, scope: *Scope, diff --git a/test/translate_c.zig b/test/translate_c.zig index 02ba62a471..c4fb63799c 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -837,6 +837,18 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub export var d: f64 = @intToFloat(f64, 3); }); + cases.add_2("conditional operator", + \\int bar(void) { + \\ if (2 ? 5 : 5 ? 4 : 6) 2; + \\ return 2 ? 5 : 5 ? 4 : 6; + \\} + , &[_][]const u8{ + \\pub export fn bar() c_int { + \\ if ((if (2 != 0) 5 else (if (5 != 0) 4 else 6)) != 0) _ = 2; + \\ return if (2 != 0) 5 else if (5 != 0) 4 else 6; + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// if (builtin.os != builtin.Os.windows) { From 65531c73a909386824697bb16025c363684a3a66 Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 17 Dec 2019 17:19:28 +0200 Subject: [PATCH 14/37] translate-c-2 switch --- src-self-hosted/clang.zig | 15 +++ src-self-hosted/translate_c.zig | 230 ++++++++++++++++++++++++++++++-- test/translate_c.zig | 126 +++++++++++------ 3 files changed, 321 insertions(+), 50 deletions(-) diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index dcb830df69..3fa937db9c 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -77,6 +77,7 @@ pub const struct_ZigClangPredefinedExpr = @OpaqueType(); pub const struct_ZigClangInitListExpr = @OpaqueType(); pub const ZigClangPreprocessingRecord = @OpaqueType(); pub const ZigClangFloatingLiteral = @OpaqueType(); +pub const ZigClangConstantExpr = @OpaqueType(); pub const ZigClangBO = extern enum { PtrMemD, @@ -1059,3 +1060,17 @@ pub extern fn ZigClangAPFloat_getValueAsApproximateDouble(*const ZigClangFloatin pub extern fn ZigClangConditionalOperator_getCond(*const ZigClangConditionalOperator) *const ZigClangExpr; pub extern fn ZigClangConditionalOperator_getTrueExpr(*const ZigClangConditionalOperator) *const ZigClangExpr; pub extern fn ZigClangConditionalOperator_getFalseExpr(*const ZigClangConditionalOperator) *const ZigClangExpr; + +pub extern fn ZigClangSwitchStmt_getConditionVariableDeclStmt(*const ZigClangSwitchStmt) ?*const ZigClangDeclStmt; +pub extern fn ZigClangSwitchStmt_getCond(*const ZigClangSwitchStmt) *const ZigClangExpr; +pub extern fn ZigClangSwitchStmt_getBody(*const ZigClangSwitchStmt) *const ZigClangStmt; +pub extern fn ZigClangSwitchStmt_isAllEnumCasesCovered(*const ZigClangSwitchStmt) bool; + +pub extern fn ZigClangCaseStmt_getLHS(*const ZigClangCaseStmt) *const ZigClangExpr; +pub extern fn ZigClangCaseStmt_getRHS(*const ZigClangCaseStmt) ?*const ZigClangExpr; +pub extern fn ZigClangCaseStmt_getBeginLoc(*const ZigClangCaseStmt) ZigClangSourceLocation; +pub extern fn ZigClangCaseStmt_getSubStmt(*const ZigClangCaseStmt) *const ZigClangStmt; + +pub extern fn ZigClangDefaultStmt_getSubStmt(*const ZigClangDefaultStmt) *const ZigClangStmt; + +pub extern fn ZigClangExpr_EvaluateAsConstantExpr(*const ZigClangExpr, *ZigClangExprEvalResult, ZigClangExpr_ConstExprUsage, *const ZigClangASTContext) bool; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 79b32c2fa2..a3ed6a9999 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -54,7 +54,9 @@ const Scope = struct { const Switch = struct { base: Scope, - label: []const u8, + pending_block: *ast.Node.Block, + cases: *ast.Node.Switch.CaseList, + has_default: bool = false, }; /// used when getting a member `a.b` @@ -189,8 +191,8 @@ const Scope = struct { .Ref => null, .FnDef => @fieldParentPtr(FnDef, "base", scope).getAlias(name), .Block => @fieldParentPtr(Block, "base", scope).getAlias(name), + .Switch, .Condition => scope.parent.?.getAlias(name), - else => @panic("TODO Scope.getAlias"), }; } @@ -200,15 +202,31 @@ const Scope = struct { .Root => @fieldParentPtr(Root, "base", scope).contains(name), .FnDef => @fieldParentPtr(FnDef, "base", scope).contains(name), .Block => @fieldParentPtr(Block, "base", scope).contains(name), + .Switch, .Condition => scope.parent.?.contains(name), - else => @panic("TODO Scope.contains"), }; } fn getBreakableScope(inner: *Scope) *Scope { var scope = inner; - while (scope.id != .Switch and scope.id != .Root) : (scope = scope.parent.?) {} - return scope; + while (true) { + switch (scope.id) { + .FnDef => unreachable, + .Switch => return scope, + else => scope = scope.parent.?, + } + } + } + + fn getSwitch(inner: *Scope) *Scope.Switch { + var scope = inner; + while (true) { + switch (scope.id) { + .FnDef => unreachable, + .Switch => return @fieldParentPtr(Switch, "base", scope), + else => scope = scope.parent.?, + } + } } }; @@ -634,6 +652,10 @@ fn transStmt( .ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const ZigClangForStmt, stmt)), .FloatingLiteralClass => return transFloatingLiteral(rp, scope, @ptrCast(*const ZigClangFloatingLiteral, stmt), result_used), .ConditionalOperatorClass => return transConditionalOperator(rp, scope, @ptrCast(*const ZigClangConditionalOperator, stmt), result_used), + .SwitchStmtClass => return transSwitch(rp, scope, @ptrCast(*const ZigClangSwitchStmt, stmt)), + .CaseStmtClass => return transCase(rp, scope, @ptrCast(*const ZigClangCaseStmt, stmt)), + .DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const ZigClangDefaultStmt, stmt)), + .ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used), else => { return revertAndWarn( rp, @@ -1374,6 +1396,154 @@ fn transForLoop( return &while_node.base; } +fn transSwitch( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangSwitchStmt, +) TransError!*ast.Node { + const switch_node = try transCreateNodeSwitch(rp.c); + var switch_scope = Scope.Switch{ + .base = .{ + .id = .Switch, + .parent = scope, + }, + .cases = &switch_node.cases, + .pending_block = undefined, + }; + + var cond_scope = Scope.Condition{ + .base = .{ + .parent = scope, + .id = .Condition, + }, + }; + switch_node.expr = try transExpr(rp, &cond_scope.base, ZigClangSwitchStmt_getCond(stmt), .used, .r_value); + _ = try appendToken(rp.c, .RParen, ")"); + _ = try appendToken(rp.c, .LBrace, "{"); + switch_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + + const block_scope = try Scope.Block.init(rp.c, &switch_scope.base, null); + // tmp block that all statements will go before being picked up by a case or default + const block = try transCreateNodeBlock(rp.c, null); + block_scope.block_node = block; + + const switch_block = try transCreateNodeBlock(rp.c, null); + try switch_block.statements.push(&switch_node.base); + switch_scope.pending_block = switch_block; + + + const last = try transStmt(rp, &block_scope.base, ZigClangSwitchStmt_getBody(stmt), .unused, .r_value); + _ = try appendToken(rp.c, .Semicolon, ";"); + + // take all pending statements + var it = last.cast(ast.Node.Block).?.statements.iterator(0); + while (it.next()) |n| { + try switch_scope.pending_block.statements.push(n.*); + } + + switch_scope.pending_block.label = try appendIdentifier(rp.c, "__switch"); + _ = try appendToken(rp.c, .Colon, ":"); + if (!switch_scope.has_default) { + const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c)); + else_prong.expr = &(try transCreateNodeBreak(rp.c, "__switch")).base; + _ = try appendToken(rp.c, .Comma, ","); + try switch_node.cases.push(&else_prong.base); + } + switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}"); + return &switch_scope.pending_block.base; +} + + +fn transCase( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangCaseStmt, +) TransError!*ast.Node { + const block_scope = scope.findBlockScope(rp.c) catch unreachable; + const switch_scope = scope.getSwitch(); + const label = try std.fmt.allocPrint(rp.c.a(), "__case_{}", .{switch_scope.cases.len - @boolToInt(switch_scope.has_default)}); + _ = try appendToken(rp.c, .Semicolon, ";"); + + const expr = if (ZigClangCaseStmt_getRHS(stmt)) |rhs| blk: { + const lhs_node = try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value); + const ellips = try appendToken(rp.c, .Ellipsis3, "..."); + const rhs_node = try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value); + + const node = try rp.c.a().create(ast.Node.InfixOp); + node.* = .{ + .op_token = ellips, + .lhs = lhs_node, + .op = .Range, + .rhs = rhs_node, + }; + break :blk &node.base; + } else + try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value); + + + const switch_prong = try transCreateNodeSwitchCase(rp.c, expr); + switch_prong.expr = &(try transCreateNodeBreak(rp.c, label)).base; + _ = try appendToken(rp.c, .Comma, ","); + try switch_scope.cases.push(&switch_prong.base); + + const block = try transCreateNodeBlock(rp.c, null); + switch_scope.pending_block.label = try appendIdentifier(rp.c, label); + _ = try appendToken(rp.c, .Colon, ":"); + switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}"); + try block.statements.push(&switch_scope.pending_block.base); + + // take all pending statements + var it = block_scope.block_node.statements.iterator(0); + while (it.next()) |n| { + try switch_scope.pending_block.statements.push(n.*); + } + block_scope.block_node.statements.shrink(0); + + switch_scope.pending_block = block; + + return transStmt(rp, scope, ZigClangCaseStmt_getSubStmt(stmt), .unused, .r_value); +} + +fn transDefault( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangDefaultStmt, +) TransError!*ast.Node { + const block_scope = scope.findBlockScope(rp.c) catch unreachable; + const switch_scope = scope.getSwitch(); + const label = "__default"; + switch_scope.has_default = true; + _ = try appendToken(rp.c, .Semicolon, ";"); + + const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c)); + else_prong.expr = &(try transCreateNodeBreak(rp.c, label)).base; + _ = try appendToken(rp.c, .Comma, ","); + try switch_scope.cases.push(&else_prong.base); + + const block = try transCreateNodeBlock(rp.c, null); + switch_scope.pending_block.label = try appendIdentifier(rp.c, label); + _ = try appendToken(rp.c, .Colon, ":"); + switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}"); + try block.statements.push(&switch_scope.pending_block.base); + + // take all pending statements + var it = block_scope.block_node.statements.iterator(0); + while (it.next()) |n| { + try switch_scope.pending_block.statements.push(n.*); + } + block_scope.block_node.statements.shrink(0); + + switch_scope.pending_block = block; + return transStmt(rp, scope, ZigClangDefaultStmt_getSubStmt(stmt), .unused, .r_value); +} + +fn transConstantExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangExpr, used: ResultUsed) TransError!*ast.Node { + var result: ZigClangExprEvalResult = undefined; + if (!ZigClangExpr_EvaluateAsConstantExpr(expr, &result, .EvaluateForCodeGen, rp.c.clang_context)) + return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangExpr_getBeginLoc(expr), "invalid constant expression", .{}); + return maybeSuppressResult(rp, scope, used, try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&result.Val))); +} + fn transCPtrCast( rp: RestorePoint, loc: ZigClangSourceLocation, @@ -1414,7 +1584,7 @@ fn transCPtrCast( fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node { const break_scope = scope.getBreakableScope(); const br = try transCreateNodeBreak(rp.c, if (break_scope.id == .Switch) - @fieldParentPtr(Scope.Switch, "base", break_scope).label + "__switch" else null); return &br.base; @@ -2339,6 +2509,42 @@ fn transCreateNodeContinue(c: *Context) !*ast.Node { return &node.base; } +fn transCreateNodeSwitch(c: *Context) !*ast.Node.Switch { + const switch_tok = try appendToken(c, .Keyword_switch, "switch"); + _ = try appendToken(c, .LParen, "("); + + const node = try c.a().create(ast.Node.Switch); + node.* = .{ + .switch_token = switch_tok, + .expr = undefined, + .cases = ast.Node.Switch.CaseList.init(c.a()), + .rbrace = undefined, + }; + return node; +} + +fn transCreateNodeSwitchCase(c: *Context, lhs: *ast.Node) !*ast.Node.SwitchCase { + const arrow_tok = try appendToken(c, .EqualAngleBracketRight, "=>"); + + const node = try c.a().create(ast.Node.SwitchCase); + node.* = .{ + .items = ast.Node.SwitchCase.ItemList.init(c.a()), + .arrow_token = arrow_tok, + .payload = null, + .expr = undefined, + }; + try node.items.push(lhs); + return node; +} + +fn transCreateNodeSwitchElse(c: *Context) !*ast.Node { + const node = try c.a().create(ast.Node.SwitchElse); + node.* = .{ + .token = try appendToken(c, .Keyword_else, "else"), + }; + return &node.base; +} + const RestorePoint = struct { c: *Context, token_index: ast.TokenIndex, @@ -2818,7 +3024,7 @@ fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, // TODO hook up with codegen fn isZigPrimitiveType(name: []const u8) bool { - if (name.len > 1 and std.mem.startsWith(u8, name, "u") or std.mem.startsWith(u8, name, "u")) { + if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) { for (name[1..]) |c| { switch (c) { '0'...'9' => {}, @@ -2840,7 +3046,15 @@ fn isZigPrimitiveType(name: []const u8) bool { std.mem.eql(u8, name, "c_longdouble") or std.mem.eql(u8, name, "noreturn") or std.mem.eql(u8, name, "type") or - std.mem.eql(u8, name, "anyerror"); + std.mem.eql(u8, name, "anyerror") or + std.mem.eql(u8, name, "c_short") or + std.mem.eql(u8, name, "c_ushort") or + std.mem.eql(u8, name, "c_int") or + std.mem.eql(u8, name, "c_uint") or + std.mem.eql(u8, name, "c_long") or + std.mem.eql(u8, name, "c_ulong") or + std.mem.eql(u8, name, "c_longlong") or + std.mem.eql(u8, name, "c_ulonglong"); } fn isValidZigIdentifier(name: []const u8) bool { diff --git a/test/translate_c.zig b/test/translate_c.zig index c4fb63799c..0935f2cf65 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -849,6 +849,48 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("switch on int", + \\int switch_fn(int i) { + \\ int res = 0; + \\ switch (i) { + \\ case 0: + \\ res = 1; + \\ case 1: + \\ res = 2; + \\ default: + \\ res = 3 * i; + \\ break; + \\ case 2: + \\ res = 5; + \\ } + \\} + , &[_][]const u8{ + \\pub export fn switch_fn(i: c_int) c_int { + \\ var res: c_int = 0; + \\ __switch: { + \\ __case_2: { + \\ __default: { + \\ __case_1: { + \\ __case_0: { + \\ switch (i) { + \\ 0 => break :__case_0, + \\ 1 => break :__case_1, + \\ else => break :__default, + \\ 2 => break :__case_2, + \\ } + \\ } + \\ res = 1; + \\ } + \\ res = 2; + \\ } + \\ res = (3 * i); + \\ break :__switch; + \\ } + \\ res = 5; + \\ } + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// if (builtin.os != builtin.Os.windows) { @@ -1938,48 +1980,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("switch on int", - \\int switch_fn(int i) { - \\ int res = 0; - \\ switch (i) { - \\ case 0: - \\ res = 1; - \\ case 1: - \\ res = 2; - \\ default: - \\ res = 3 * i; - \\ break; - \\ case 2: - \\ res = 5; - \\ } - \\} - , &[_][]const u8{ - \\pub fn switch_fn(i: c_int) c_int { - \\ var res: c_int = 0; - \\ __switch: { - \\ __case_2: { - \\ __default: { - \\ __case_1: { - \\ __case_0: { - \\ switch (i) { - \\ 0 => break :__case_0, - \\ 1 => break :__case_1, - \\ else => break :__default, - \\ 2 => break :__case_2, - \\ } - \\ } - \\ res = 1; - \\ } - \\ res = 2; - \\ } - \\ res = (3 * i); - \\ break :__switch; - \\ } - \\ res = 5; - \\ } - \\} - }); - cases.addC("implicit casts", \\#include \\ @@ -2237,4 +2237,46 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub const NRF_GPIO = if (@typeId(@TypeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@TypeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else @as([*c]NRF_GPIO_Type, NRF_GPIO_BASE); }); + + cases.add("switch on int", + \\int switch_fn(int i) { + \\ int res = 0; + \\ switch (i) { + \\ case 0: + \\ res = 1; + \\ case 1: + \\ res = 2; + \\ default: + \\ res = 3 * i; + \\ break; + \\ case 2: + \\ res = 5; + \\ } + \\} + , &[_][]const u8{ + \\pub fn switch_fn(i: c_int) c_int { + \\ var res: c_int = 0; + \\ __switch: { + \\ __case_2: { + \\ __default: { + \\ __case_1: { + \\ __case_0: { + \\ switch (i) { + \\ 0 => break :__case_0, + \\ 1 => break :__case_1, + \\ else => break :__default, + \\ 2 => break :__case_2, + \\ } + \\ } + \\ res = 1; + \\ } + \\ res = 2; + \\ } + \\ res = (3 * i); + \\ break :__switch; + \\ } + \\ res = 5; + \\ } + \\} + }); } From a6960b89ed9b1a5951d08b1f7d8e215c86bb9e7e Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 17 Dec 2019 22:05:07 +0200 Subject: [PATCH 15/37] translate-c-2 fix container type resolution --- src-self-hosted/translate_c.zig | 518 +++++++++++++++----------------- test/translate_c.zig | 29 +- 2 files changed, 263 insertions(+), 284 deletions(-) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index a3ed6a9999..0f7f1b99bc 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -371,13 +371,13 @@ fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void { return visitFnDecl(c, @ptrCast(*const ZigClangFunctionDecl, decl)); }, .Typedef => { - return resolveTypeDef(c, @ptrCast(*const ZigClangTypedefNameDecl, decl)); + _ = try transTypeDef(c, @ptrCast(*const ZigClangTypedefNameDecl, decl)); }, .Enum => { _ = try transEnumDecl(c, @ptrCast(*const ZigClangEnumDecl, decl)); }, .Record => { - return resolveRecordDecl(c, @ptrCast(*const ZigClangRecordDecl, decl)); + _ = try transRecordDecl(c, @ptrCast(*const ZigClangRecordDecl, decl)); }, .Var => { return visitVarDecl(c, @ptrCast(*const ZigClangVarDecl, decl)); @@ -541,10 +541,9 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { return addTopLevelDecl(c, checked_name, &node.base); } -fn resolveTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl) Error!void { - if (c.decl_table.contains( - @ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), - )) return; // Avoid processing this decl twice +fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl) Error!?*ast.Node { + if (c.decl_table.get(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)))) |kv| + return try transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice const rp = makeRestorePoint(c); const visib_tok = try appendToken(c, .Keyword_pub, "pub"); const const_tok = try appendToken(c, .Keyword_const, "const"); @@ -558,29 +557,40 @@ fn resolveTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl) Err const typedef_loc = ZigClangTypedefNameDecl_getLocation(typedef_decl); node.init_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) { error.UnsupportedType => { - return failDecl(c, typedef_loc, typedef_name, "unable to resolve typedef child type", .{}); + try failDecl(c, typedef_loc, typedef_name, "unable to resolve typedef child type", .{}); + return null; }, error.OutOfMemory => |e| return e, }; node.semicolon_token = try appendToken(c, .Semicolon, ";"); try addTopLevelDecl(c, typedef_name, &node.base); + return transCreateNodeIdentifier(c, typedef_name); } -fn resolveRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!void { - if (c.decl_table.contains(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) return; // Avoid processing this decl twice - const rp = makeRestorePoint(c); +fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*ast.Node { + if (c.decl_table.get(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) |kv| + return try transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice + const record_loc = ZigClangRecordDecl_getLocation(record_decl); - const bare_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, record_decl))); + var bare_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, record_decl))); + var is_unnamed = false; + if (ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl) or bare_name.len == 0) { + bare_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); + is_unnamed = true; + } - const container_kind_name = if (ZigClangRecordDecl_isUnion(record_decl)) - "union" - else if (ZigClangRecordDecl_isStruct(record_decl)) - "struct" - else - return emitWarning(c, ZigClangRecordDecl_getLocation(record_decl), "record {} is not a struct or union", .{bare_name}); - - if (ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl) or bare_name.len == 0) - return; + var container_kind_name: []const u8 = undefined; + var container_kind: std.zig.Token.Id = undefined; + if (ZigClangRecordDecl_isUnion(record_decl)) { + container_kind_name = "union"; + container_kind = .Keyword_union; + } else if (ZigClangRecordDecl_isStruct(record_decl)) { + container_kind_name = "struct"; + container_kind = .Keyword_struct; + } else { + try emitWarning(c, record_loc, "record {} is not a struct or union", .{bare_name}); + return null; + } const name = try std.fmt.allocPrint(c.a(), "{}_{}", .{ container_kind_name, bare_name }); _ = try c.decl_table.put(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)), name); @@ -588,16 +598,211 @@ fn resolveRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error! const node = try transCreateNodeVarDecl(c, true, true, name); node.eq_token = try appendToken(c, .Equal, "="); - node.init_node = transRecordDecl(c, record_decl) catch |err| switch (err) { - error.UnsupportedType => { - return failDecl(c, ZigClangRecordDecl_getLocation(record_decl), name, "unable to resolve record type", .{}); - }, - error.OutOfMemory => |e| return e, + + var semicolon: ast.TokenIndex = undefined; + node.init_node = blk: { + const rp = makeRestorePoint(c); + const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse { + const opaque = try transCreateNodeOpaqueType(c); + semicolon = try appendToken(c, .Semicolon, ";"); + break :blk opaque; + }; + + const extern_tok = try appendToken(c, .Keyword_extern, "extern"); + const container_tok = try appendToken(c, container_kind, container_kind_name); + const lbrace_token = try appendToken(c, .LBrace, "{"); + + const container_node = try c.a().create(ast.Node.ContainerDecl); + container_node.* = .{ + .layout_token = extern_tok, + .kind_token = container_tok, + .init_arg_expr = .None, + .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(c.a()), + .lbrace_token = lbrace_token, + .rbrace_token = undefined, + }; + + var it = ZigClangRecordDecl_field_begin(record_def); + const end_it = ZigClangRecordDecl_field_end(record_def); + while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) { + const field_decl = ZigClangRecordDecl_field_iterator_deref(it); + const field_loc = ZigClangFieldDecl_getLocation(field_decl); + + if (ZigClangFieldDecl_isBitField(field_decl)) { + const opaque = try transCreateNodeOpaqueType(c); + semicolon = try appendToken(c, .Semicolon, ";"); + try emitWarning(c, field_loc, "{} demoted to opaque type - has bitfield", .{container_kind_name}); + break :blk opaque; + } + + const field_name = try appendIdentifier(c, try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, field_decl)))); + _ = try appendToken(c, .Colon, ":"); + const field_type = transQualType(rp, ZigClangFieldDecl_getType(field_decl), field_loc) catch |err| switch (err) { + error.UnsupportedType => { + try failDecl(c, record_loc, name, "unable to translate {} member type", .{container_kind_name}); + return null; + }, + else => |e| return e, + }; + + const field_node = try c.a().create(ast.Node.ContainerField); + field_node.* = .{ + .doc_comments = null, + .comptime_token = null, + .name_token = field_name, + .type_expr = field_type, + .value_expr = null, + .align_expr = null, + }; + + try container_node.fields_and_decls.push(&field_node.base); + _ = try appendToken(c, .Comma, ","); + } + container_node.rbrace_token = try appendToken(c, .RBrace, "}"); + semicolon = try appendToken(c, .Semicolon, ";"); + break :blk &container_node.base; }; + node.semicolon_token = semicolon; + + try addTopLevelDecl(c, name, &node.base); + if (!is_unnamed) + try c.alias_list.push(.{ .alias = bare_name, .name = name }); + return transCreateNodeIdentifier(c, name); +} + +fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.Node { + if (c.decl_table.get(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)))) |name| + return try transCreateNodeIdentifier(c, name.value); // Avoid processing this decl twice + const rp = makeRestorePoint(c); + const enum_loc = ZigClangEnumDecl_getLocation(enum_decl); + + var bare_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, enum_decl))); + var is_unnamed = false; + if (bare_name.len == 0) { + bare_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); + is_unnamed = true; + } + + const name = try std.fmt.allocPrint(c.a(), "enum_{}", .{bare_name}); + _ = try c.decl_table.put(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)), name); + const node = try transCreateNodeVarDecl(c, true, true, name); + node.eq_token = try appendToken(c, .Equal, "="); + + node.init_node = if (ZigClangEnumDecl_getDefinition(enum_decl)) |enum_def| blk: { + var pure_enum = true; + var it = ZigClangEnumDecl_enumerator_begin(enum_def); + var end_it = ZigClangEnumDecl_enumerator_end(enum_def); + while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) { + const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it); + if (ZigClangEnumConstantDecl_getInitExpr(enum_const)) |_| { + pure_enum = false; + break; + } + } + + const extern_tok = try appendToken(c, .Keyword_extern, "extern"); + const container_tok = try appendToken(c, .Keyword_enum, "enum"); + + const container_node = try c.a().create(ast.Node.ContainerDecl); + container_node.* = .{ + .layout_token = extern_tok, + .kind_token = container_tok, + .init_arg_expr = .None, + .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(c.a()), + .lbrace_token = undefined, + .rbrace_token = undefined, + }; + + const int_type = ZigClangEnumDecl_getIntegerType(enum_decl); + + // TODO only emit this tag type if the enum tag type is not the default. + // I don't know what the default is, need to figure out how clang is deciding. + // it appears to at least be different across gcc/msvc + if (!isCBuiltinType(int_type, .UInt) and + !isCBuiltinType(int_type, .Int)) + { + _ = try appendToken(c, .LParen, "("); + container_node.init_arg_expr = .{ + .Type = transQualType(rp, int_type, enum_loc) catch |err| switch (err) { + error.UnsupportedType => { + try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{}); + return null; + }, + else => |e| return e, + }, + }; + _ = try appendToken(c, .RParen, ")"); + } + + container_node.lbrace_token = try appendToken(c, .LBrace, "{"); + + it = ZigClangEnumDecl_enumerator_begin(enum_def); + end_it = ZigClangEnumDecl_enumerator_end(enum_def); + while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) { + const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it); + + const enum_val_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, enum_const))); + + const field_name = if (!is_unnamed and std.mem.startsWith(u8, enum_val_name, bare_name)) + enum_val_name[bare_name.len..] + else + enum_val_name; + + const field_name_tok = try appendIdentifier(c, field_name); + + const int_node = if (!pure_enum) blk: { + _ = try appendToken(c, .Colon, "="); + break :blk try transCreateNodeAPInt(c, ZigClangEnumConstantDecl_getInitVal(enum_const)); + } else + null; + + const field_node = try c.a().create(ast.Node.ContainerField); + field_node.* = .{ + .doc_comments = null, + .comptime_token = null, + .name_token = field_name_tok, + .type_expr = null, + .value_expr = int_node, + .align_expr = null, + }; + + try container_node.fields_and_decls.push(&field_node.base); + _ = try appendToken(c, .Comma, ","); + // In C each enum value is in the global namespace. So we put them there too. + // At this point we can rely on the enum emitting successfully. + try addEnumTopLevel(c, name, field_name, enum_val_name); + } + container_node.rbrace_token = try appendToken(c, .RBrace, "}"); + + break :blk &container_node.base; + } else + try transCreateNodeOpaqueType(c); + node.semicolon_token = try appendToken(c, .Semicolon, ";"); try addTopLevelDecl(c, name, &node.base); - try c.alias_list.push(.{ .alias = bare_name, .name = name }); + if (!is_unnamed) + try c.alias_list.push(.{ .alias = bare_name, .name = name }); + return transCreateNodeIdentifier(c, name); +} + +fn addEnumTopLevel(c: *Context, enum_name: []const u8, field_name: []const u8, enum_val_name: []const u8) !void { + const node = try transCreateNodeVarDecl(c, true, true, enum_val_name); + node.eq_token = try appendToken(c, .Equal, "="); + const enum_ident = try transCreateNodeIdentifier(c, enum_name); + const period_tok = try appendToken(c, .Period, "."); + const field_ident = try transCreateNodeIdentifier(c, field_name); + node.semicolon_token = try appendToken(c, .Semicolon, ";"); + + const field_access_node = try c.a().create(ast.Node.InfixOp); + field_access_node.* = .{ + .op_token = period_tok, + .lhs = enum_ident, + .op = .Period, + .rhs = field_ident, + }; + node.init_node = &field_access_node.base; + try addTopLevelDecl(c, field_name, &node.base); } fn createAlias(c: *Context, alias: var) !void { @@ -1452,7 +1657,6 @@ fn transSwitch( switch_scope.pending_block.rbrace = try appendToken(rp.c, .RBrace, "}"); return &switch_scope.pending_block.base; } - fn transCase( rp: RestorePoint, @@ -1675,222 +1879,6 @@ fn transQualType(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSou return transType(rp, ZigClangQualType_getTypePtr(qt), source_loc); } -fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) TypeError!*ast.Node { - const rp = makeRestorePoint(c); - - const record_loc = ZigClangRecordDecl_getLocation(record_decl); - - var container_kind_name: []const u8 = undefined; - var container_kind: std.zig.Token.Id = undefined; - - if (ZigClangRecordDecl_isUnion(record_decl)) { - container_kind_name = "union"; - container_kind = .Keyword_union; - } else if (ZigClangRecordDecl_isStruct(record_decl)) { - container_kind_name = "struct"; - container_kind = .Keyword_struct; - } else { - return revertAndWarn( - rp, - error.UnsupportedType, - record_loc, - "unsupported record type", - .{}, - ); - } - - const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse { - return transCreateNodeOpaqueType(c); - }; - - const extern_tok = try appendToken(c, .Keyword_extern, "extern"); - const container_tok = try appendToken(c, container_kind, container_kind_name); - const lbrace_token = try appendToken(c, .LBrace, "{"); - - const container_node = try c.a().create(ast.Node.ContainerDecl); - container_node.* = .{ - .layout_token = extern_tok, - .kind_token = container_tok, - .init_arg_expr = .None, - .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(c.a()), - .lbrace_token = lbrace_token, - .rbrace_token = undefined, - }; - - var it = ZigClangRecordDecl_field_begin(record_def); - const end_it = ZigClangRecordDecl_field_end(record_def); - while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) { - const field_decl = ZigClangRecordDecl_field_iterator_deref(it); - const field_loc = ZigClangFieldDecl_getLocation(field_decl); - - if (ZigClangFieldDecl_isBitField(field_decl)) { - rp.activate(); - const node = try transCreateNodeOpaqueType(c); - try emitWarning(c, field_loc, "{} demoted to opaque type - has bitfield", .{container_kind_name}); - return node; - } - - const field_name = try appendIdentifier(c, try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, field_decl)))); - _ = try appendToken(c, .Colon, ":"); - const field_type = try transQualType(rp, ZigClangFieldDecl_getType(field_decl), field_loc); - - const field_node = try c.a().create(ast.Node.ContainerField); - field_node.* = .{ - .doc_comments = null, - .comptime_token = null, - .name_token = field_name, - .type_expr = field_type, - .value_expr = null, - .align_expr = null, - }; - - try container_node.fields_and_decls.push(&field_node.base); - _ = try appendToken(c, .Comma, ","); - } - - container_node.rbrace_token = try appendToken(c, .RBrace, "}"); - return &container_node.base; -} - -fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.Node { - if (c.decl_table.get(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)))) |name| - return try transCreateNodeIdentifier(c, name.value); // Avoid processing this decl twice - const rp = makeRestorePoint(c); - const enum_loc = ZigClangEnumDecl_getLocation(enum_decl); - - var bare_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, enum_decl))); - var is_unnamed = false; - if (bare_name.len == 0) { - bare_name = try std.fmt.allocPrint(c.a(), "unnamed_{}", .{c.getMangle()}); - is_unnamed = true; - } - - const name = try std.fmt.allocPrint(c.a(), "enum_{}", .{bare_name}); - _ = try c.decl_table.put(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)), name); - const node = try transCreateNodeVarDecl(c, true, true, name); - node.eq_token = try appendToken(c, .Equal, "="); - - node.init_node = if (ZigClangEnumDecl_getDefinition(enum_decl)) |enum_def| blk: { - var pure_enum = true; - var it = ZigClangEnumDecl_enumerator_begin(enum_def); - var end_it = ZigClangEnumDecl_enumerator_end(enum_def); - while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) { - const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it); - if (ZigClangEnumConstantDecl_getInitExpr(enum_const)) |_| { - pure_enum = false; - break; - } - } - - const extern_tok = try appendToken(c, .Keyword_extern, "extern"); - const container_tok = try appendToken(c, .Keyword_enum, "enum"); - - const container_node = try c.a().create(ast.Node.ContainerDecl); - container_node.* = .{ - .layout_token = extern_tok, - .kind_token = container_tok, - .init_arg_expr = .None, - .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(c.a()), - .lbrace_token = undefined, - .rbrace_token = undefined, - }; - - const int_type = ZigClangEnumDecl_getIntegerType(enum_decl); - - // TODO only emit this tag type if the enum tag type is not the default. - // I don't know what the default is, need to figure out how clang is deciding. - // it appears to at least be different across gcc/msvc - if (!isCBuiltinType(int_type, .UInt) and - !isCBuiltinType(int_type, .Int)) - { - _ = try appendToken(c, .LParen, "("); - container_node.init_arg_expr = .{ - .Type = transQualType(rp, int_type, enum_loc) catch |err| switch (err) { - error.UnsupportedType => { - if (is_unnamed) { - try emitWarning(c, enum_loc, "unable to translate enum tag type", .{}); - } else { - try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{}); - } - return null; - }, - else => |e| return e, - }, - }; - _ = try appendToken(c, .RParen, ")"); - } - - container_node.lbrace_token = try appendToken(c, .LBrace, "{"); - - it = ZigClangEnumDecl_enumerator_begin(enum_def); - end_it = ZigClangEnumDecl_enumerator_end(enum_def); - while (ZigClangEnumDecl_enumerator_iterator_neq(it, end_it)) : (it = ZigClangEnumDecl_enumerator_iterator_next(it)) { - const enum_const = ZigClangEnumDecl_enumerator_iterator_deref(it); - - const enum_val_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, enum_const))); - - const field_name = if (!is_unnamed and std.mem.startsWith(u8, enum_val_name, bare_name)) - enum_val_name[bare_name.len..] - else - enum_val_name; - - const field_name_tok = try appendIdentifier(c, field_name); - - const int_node = if (!pure_enum) blk: { - _ = try appendToken(c, .Colon, "="); - break :blk try transCreateNodeAPInt(c, ZigClangEnumConstantDecl_getInitVal(enum_const)); - } else - null; - - const field_node = try c.a().create(ast.Node.ContainerField); - field_node.* = .{ - .doc_comments = null, - .comptime_token = null, - .name_token = field_name_tok, - .type_expr = null, - .value_expr = int_node, - .align_expr = null, - }; - - try container_node.fields_and_decls.push(&field_node.base); - _ = try appendToken(c, .Comma, ","); - // In C each enum value is in the global namespace. So we put them there too. - // At this point we can rely on the enum emitting successfully. - try addEnumTopLevel(c, name, field_name, enum_val_name); - } - container_node.rbrace_token = try appendToken(c, .RBrace, "}"); - - break :blk &container_node.base; - } else - try transCreateNodeOpaqueType(c); - - node.semicolon_token = try appendToken(c, .Semicolon, ";"); - - try addTopLevelDecl(c, name, &node.base); - if (!is_unnamed) - try c.alias_list.push(.{ .alias = bare_name, .name = name }); - return transCreateNodeIdentifier(c, name); -} - -fn addEnumTopLevel(c: *Context, enum_name: []const u8, field_name: []const u8, enum_val_name: []const u8) !void { - const node = try transCreateNodeVarDecl(c, true, true, enum_val_name); - node.eq_token = try appendToken(c, .Equal, "="); - const enum_ident = try transCreateNodeIdentifier(c, enum_name); - const period_tok = try appendToken(c, .Period, "."); - const field_ident = try transCreateNodeIdentifier(c, field_name); - node.semicolon_token = try appendToken(c, .Semicolon, ";"); - - const field_access_node = try c.a().create(ast.Node.InfixOp); - field_access_node.* = .{ - .op_token = period_tok, - .lhs = enum_ident, - .op = .Period, - .rhs = field_ident, - }; - node.init_node = &field_access_node.base; - try addTopLevelDecl(c, field_name, &node.base); -} - fn isCBuiltinType(qt: ZigClangQualType, kind: ZigClangBuiltinTypeKind) bool { const c_type = qualTypeCanon(qt); if (ZigClangType_getTypeClass(c_type) != .Builtin) @@ -2591,7 +2579,7 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), }); }, - .FunctionProto => { + .FunctionProto, .FunctionNoProto => { const fn_proto_ty = @ptrCast(*const ZigClangFunctionProtoType, ty); const fn_proto = try transFnProto(rp, null, fn_proto_ty, source_loc, null, false); return &fn_proto.base; @@ -2666,24 +2654,15 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty); const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty); - const typedef_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_decl))); - return transCreateNodeIdentifier(rp.c, typedef_name); + return (try transTypeDef(rp.c, typedef_decl)) orelse + revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate typedef declaration", .{}); }, .Record => { const record_ty = @ptrCast(*const ZigClangRecordType, ty); - // TODO this sould get the name from decl_table - // struct Foo { - // struct Bar{ - // int b; - // }; - // struct Bar c; - // }; const record_decl = ZigClangRecordType_getDecl(record_ty); - if (try getContainerName(rp, record_decl)) |name| - return transCreateNodeIdentifier(rp.c, name) - else - return transRecordDecl(rp.c, record_decl); + return (try transRecordDecl(rp.c, record_decl)) orelse + revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to resolve record declaration", .{}); }, .Enum => { const enum_ty = @ptrCast(*const ZigClangEnumType, ty); @@ -2704,6 +2683,10 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour const attributed_ty = @ptrCast(*const ZigClangAttributedType, ty); return transQualType(rp, ZigClangAttributedType_getEquivalentType(attributed_ty), source_loc); }, + .MacroQualified => { + const macroqualified_ty = @ptrCast(*const ZigClangMacroQualifiedType, ty); + return transQualType(rp, ZigClangMacroQualifiedType_getModifiedType(macroqualified_ty), source_loc); + }, else => { const type_name = rp.c.str(ZigClangType_getTypeClassName(ty)); return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{}'", .{type_name}); @@ -2711,22 +2694,6 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour } } -fn getContainerName(rp: RestorePoint, record_decl: *const ZigClangRecordDecl) !?[]const u8 { - const bare_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, record_decl))); - - const container_kind_name = if (ZigClangRecordDecl_isUnion(record_decl)) - "union" - else if (ZigClangRecordDecl_isStruct(record_decl)) - "struct" - else - return revertAndWarn(rp, error.UnsupportedType, ZigClangRecordDecl_getLocation(record_decl), "record {} is not a struct or union", .{bare_name}); - - if (ZigClangRecordDecl_isAnonymousStructOrUnion(record_decl) or bare_name.len == 0) - return null; - - return try std.fmt.allocPrint(rp.c.a(), "{}_{}", .{ container_kind_name, bare_name }); -} - fn isCVoid(qt: ZigClangQualType) bool { const ty = ZigClangQualType_getTypePtr(qt); if (ZigClangType_getTypeClass(ty) == .Builtin) { @@ -2909,14 +2876,13 @@ fn finishTransFnProto( }; const fn_proto = try rp.c.a().create(ast.Node.FnProto); - fn_proto.* = ast.Node.FnProto{ - .base = ast.Node{ .id = ast.Node.Id.FnProto }, + fn_proto.* = .{ .doc_comments = null, .visib_token = pub_tok, .fn_token = fn_tok, .name_token = name_tok, .params = fn_params, - .return_type = ast.Node.FnProto.ReturnType{ .Explicit = return_type_node }, + .return_type = .{ .Explicit = return_type_node }, .var_args_token = null, // TODO this field is broken in the AST data model .extern_export_inline_token = extern_export_inline_tok, .cc_token = cc_tok, diff --git a/test/translate_c.zig b/test/translate_c.zig index 0935f2cf65..be31ee3cbb 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -228,7 +228,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ struct Foo *foo; \\}; , &[_][]const u8{ - \\pub const struct_Foo = @OpaqueType() + \\pub const struct_Foo = @OpaqueType(); , \\pub const struct_Bar = extern struct { \\ foo: ?*struct_Foo, @@ -622,18 +622,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\#define glClearPFN PFNGLCLEARPROC , &[_][]const u8{ \\pub const GLbitfield = c_uint; - , \\pub const PFNGLCLEARPROC = ?extern fn (GLbitfield) void; - , \\pub const OpenGLProc = ?extern fn () void; - , + \\pub const struct_unnamed_1 = extern struct { + \\ Clear: PFNGLCLEARPROC, + \\}; \\pub const union_OpenGLProcs = extern union { \\ ptr: [1]OpenGLProc, - \\ gl: extern struct { - \\ Clear: PFNGLCLEARPROC, - \\ }, + \\ gl: struct_unnamed_1, \\}; - , \\pub extern var glProcs: union_OpenGLProcs; , \\pub const glClearPFN = PFNGLCLEARPROC; @@ -891,6 +888,22 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("type referenced struct", + \\struct Foo { + \\ struct Bar{ + \\ int b; + \\ }; + \\ struct Bar c; + \\}; + , &[_][]const u8{ + \\pub const struct_Bar = extern struct { + \\ b: c_int, + \\}; + \\pub const struct_Foo = extern struct { + \\ c: struct_Bar, + \\}; + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// if (builtin.os != builtin.Os.windows) { From 6d7025d0c5c619773f0f5cc49edbd30713a7328d Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 17 Dec 2019 23:28:13 +0200 Subject: [PATCH 16/37] translate-c-2 various fixes to get more tests passing --- src-self-hosted/clang.zig | 17 +- src-self-hosted/translate_c.zig | 21 +- test/translate_c.zig | 698 ++++++++++++++++++-------------- 3 files changed, 423 insertions(+), 313 deletions(-) diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index 3fa937db9c..b44f3a2389 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -793,16 +793,16 @@ pub extern fn ZigClangInitListExpr_getInit(self: ?*const struct_ZigClangInitList pub extern fn ZigClangInitListExpr_getArrayFiller(self: ?*const struct_ZigClangInitListExpr) *const ZigClangExpr; pub extern fn ZigClangInitListExpr_getNumInits(self: ?*const struct_ZigClangInitListExpr) c_uint; pub extern fn ZigClangAPValue_getKind(self: ?*const struct_ZigClangAPValue) ZigClangAPValueKind; -pub extern fn ZigClangAPValue_getInt(self: ?*const struct_ZigClangAPValue) ?*const struct_ZigClangAPSInt; +pub extern fn ZigClangAPValue_getInt(self: ?*const struct_ZigClangAPValue) *const struct_ZigClangAPSInt; pub extern fn ZigClangAPValue_getArrayInitializedElts(self: ?*const struct_ZigClangAPValue) c_uint; pub extern fn ZigClangAPValue_getArraySize(self: ?*const struct_ZigClangAPValue) c_uint; pub extern fn ZigClangAPValue_getLValueBase(self: ?*const struct_ZigClangAPValue) struct_ZigClangAPValueLValueBase; -pub extern fn ZigClangAPSInt_isSigned(self: ?*const struct_ZigClangAPSInt) bool; -pub extern fn ZigClangAPSInt_isNegative(self: ?*const struct_ZigClangAPSInt) bool; -pub extern fn ZigClangAPSInt_negate(self: ?*const struct_ZigClangAPSInt) ?*const struct_ZigClangAPSInt; -pub extern fn ZigClangAPSInt_free(self: ?*const struct_ZigClangAPSInt) void; -pub extern fn ZigClangAPSInt_getRawData(self: ?*const struct_ZigClangAPSInt) [*:0]const u64; -pub extern fn ZigClangAPSInt_getNumWords(self: ?*const struct_ZigClangAPSInt) c_uint; +pub extern fn ZigClangAPSInt_isSigned(self: *const struct_ZigClangAPSInt) bool; +pub extern fn ZigClangAPSInt_isNegative(self: *const struct_ZigClangAPSInt) bool; +pub extern fn ZigClangAPSInt_negate(self: *const struct_ZigClangAPSInt) *const struct_ZigClangAPSInt; +pub extern fn ZigClangAPSInt_free(self: *const struct_ZigClangAPSInt) void; +pub extern fn ZigClangAPSInt_getRawData(self: *const struct_ZigClangAPSInt) [*:0]const u64; +pub extern fn ZigClangAPSInt_getNumWords(self: *const struct_ZigClangAPSInt) c_uint; pub extern fn ZigClangAPInt_getLimitedValue(self: *const struct_ZigClangAPInt, limit: u64) u64; pub extern fn ZigClangAPValueLValueBase_dyn_cast_Expr(self: struct_ZigClangAPValueLValueBase) ?*const struct_ZigClangExpr; @@ -1074,3 +1074,6 @@ pub extern fn ZigClangCaseStmt_getSubStmt(*const ZigClangCaseStmt) *const ZigCla pub extern fn ZigClangDefaultStmt_getSubStmt(*const ZigClangDefaultStmt) *const ZigClangStmt; pub extern fn ZigClangExpr_EvaluateAsConstantExpr(*const ZigClangExpr, *ZigClangExprEvalResult, ZigClangExpr_ConstExprUsage, *const ZigClangASTContext) bool; + +pub extern fn ZigClangPredefinedExpr_getFunctionName(*const ZigClangPredefinedExpr) *const ZigClangStringLiteral; + diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 0f7f1b99bc..0933b7aa20 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -861,6 +861,7 @@ fn transStmt( .CaseStmtClass => return transCase(rp, scope, @ptrCast(*const ZigClangCaseStmt, stmt)), .DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const ZigClangDefaultStmt, stmt)), .ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used), + .PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const ZigClangPredefinedExpr, stmt), result_used), else => { return revertAndWarn( rp, @@ -1748,6 +1749,10 @@ fn transConstantExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangExpr, return maybeSuppressResult(rp, scope, used, try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&result.Val))); } +fn transPredefinedExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangPredefinedExpr, used: ResultUsed) TransError!*ast.Node { + return transStringLiteral(rp, scope, ZigClangPredefinedExpr_getFunctionName(expr), used); +} + fn transCPtrCast( rp: RestorePoint, loc: ZigClangSourceLocation, @@ -2191,11 +2196,17 @@ fn transCreateNodePtrType( return node; } -fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*ast.Node { - const num_limbs = ZigClangAPSInt_getNumWords(int.?); +fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node { + const num_limbs = ZigClangAPSInt_getNumWords(int); + var aps_int = int; + const is_negative = ZigClangAPSInt_isSigned(int) and ZigClangAPSInt_isNegative(int); + if (is_negative) + aps_int = ZigClangAPSInt_negate(aps_int); var big = try std.math.big.Int.initCapacity(c.a(), num_limbs); + if (is_negative) + big.negate(); defer big.deinit(); - const data = ZigClangAPSInt_getRawData(int.?); + const data = ZigClangAPSInt_getRawData(aps_int); var i: @TypeOf(num_limbs) = 0; while (i < num_limbs) : (i += 1) big.limbs[i] = data[i]; const str = big.toString(c.a(), 10) catch |err| switch (err) { @@ -2207,6 +2218,8 @@ fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*ast.Node { node.* = .{ .token = token, }; + if (is_negative) + ZigClangAPSInt_free(aps_int); return &node.base; } @@ -2803,7 +2816,7 @@ fn finishTransFnProto( const param = ZigClangFunctionDecl_getParamDecl(fn_decl.?, @intCast(c_uint, i)); var param_name: []const u8 = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, param))); if (param_name.len < 1) - param_name = "arg"[0..]; + param_name = try std.fmt.allocPrint(rp.c.a(), "arg_{}", .{rp.c.getMangle()}); const checked_param_name = if (try scope.createAlias(rp.c, param_name)) |a| blk: { try fndef_scope.params.push(.{ .name = param_name, .alias = a }); break :blk a; diff --git a/test/translate_c.zig b/test/translate_c.zig index be31ee3cbb..aebeddc856 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -426,6 +426,180 @@ pub fn addCases(cases: *tests.TranslateCContext) void { }, ); + cases.addC_both("null statements", + \\void foo(void) { + \\ ;;;;; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ {} + \\ {} + \\ {} + \\ {} + \\ {} + \\} + }); + + if (builtin.os != builtin.Os.windows) { + // Windows treats this as an enum with type c_int + cases.add_both("big negative enum init values when C ABI supports long long enums", + \\enum EnumWithInits { + \\ VAL01 = 0, + \\ VAL02 = 1, + \\ VAL03 = 2, + \\ VAL04 = 3, + \\ VAL05 = -1, + \\ VAL06 = -2, + \\ VAL07 = -3, + \\ VAL08 = -4, + \\ VAL09 = VAL02 + VAL08, + \\ VAL10 = -1000012000, + \\ VAL11 = -1000161000, + \\ VAL12 = -1000174001, + \\ VAL13 = VAL09, + \\ VAL14 = VAL10, + \\ VAL15 = VAL11, + \\ VAL16 = VAL13, + \\ VAL17 = (VAL16 - VAL10 + 1), + \\ VAL18 = 0x1000000000000000L, + \\ VAL19 = VAL18 + VAL18 + VAL18 - 1, + \\ VAL20 = VAL19 + VAL19, + \\ VAL21 = VAL20 + 0xFFFFFFFFFFFFFFFF, + \\ VAL22 = 0xFFFFFFFFFFFFFFFF + 1, + \\ VAL23 = 0xFFFFFFFFFFFFFFFF, + \\}; + , &[_][]const u8{ + \\pub const enum_EnumWithInits = extern enum(c_longlong) { + \\ VAL01 = 0, + \\ VAL02 = 1, + \\ VAL03 = 2, + \\ VAL04 = 3, + \\ VAL05 = -1, + \\ VAL06 = -2, + \\ VAL07 = -3, + \\ VAL08 = -4, + \\ VAL09 = -3, + \\ VAL10 = -1000012000, + \\ VAL11 = -1000161000, + \\ VAL12 = -1000174001, + \\ VAL13 = -3, + \\ VAL14 = -1000012000, + \\ VAL15 = -1000161000, + \\ VAL16 = -3, + \\ VAL17 = 1000011998, + \\ VAL18 = 1152921504606846976, + \\ VAL19 = 3458764513820540927, + \\ VAL20 = 6917529027641081854, + \\ VAL21 = 6917529027641081853, + \\ VAL22 = 0, + \\ VAL23 = -1, + \\}; + }); + } + + cases.addC_both("predefined expressions", + \\void foo(void) { + \\ __func__; + \\ __FUNCTION__; + \\ __PRETTY_FUNCTION__; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ _ = "foo"; + \\ _ = "foo"; + \\ _ = "void foo(void)"; + \\} + }); + + cases.addC_both("ignore result, no function arguments", + \\void foo() { + \\ int a; + \\ 1; + \\ "hey"; + \\ 1 + 1; + \\ 1 - 1; + \\ a = 1; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var a: c_int = undefined; + \\ _ = 1; + \\ _ = "hey"; + \\ _ = (1 + 1); + \\ _ = (1 - 1); + \\ a = 1; + \\} + }); + + cases.add_2("qualified struct and enum", + \\struct Foo { + \\ int x; + \\ int y; + \\}; + \\enum Bar { + \\ BarA, + \\ BarB, + \\}; + \\void func(struct Foo *a, enum Bar **b); + , &[_][]const u8{ + \\pub const struct_Foo = extern struct { + \\ x: c_int, + \\ y: c_int, + \\}; + \\pub const BarA = enum_Bar.A; + \\pub const BarB = enum_Bar.B; + \\pub const enum_Bar = extern enum { + \\ A, + \\ B, + \\}; + \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void; + , + \\pub const Foo = struct_Foo; + \\pub const Bar = enum_Bar; + }); + + cases.add_both("constant size array", + \\void func(int array[20]); + , &[_][]const u8{ + \\pub extern fn func(array: [*c]c_int) void; + }); + + cases.add_both("__cdecl doesn't mess up function pointers", + \\void foo(void (__cdecl *fn_ptr)(void)); + , &[_][]const u8{ + \\pub extern fn foo(fn_ptr: ?extern fn () void) void; + }); + + cases.addC_both("void cast", + \\void foo(int a) { + \\ (void) a; + \\} + , &[_][]const u8{ + \\pub export fn foo(a: c_int) void { + \\ _ = a; + \\} + }); + + cases.addC_both("implicit cast to void *", + \\void *foo(unsigned short *x) { + \\ return x; + \\} + , &[_][]const u8{ + \\pub export fn foo(x: [*c]c_ushort) ?*c_void { + \\ return @ptrCast(?*c_void, x); + \\} + }); + + cases.addC_both("null pointer implicit cast", + \\int* foo(void) { + \\ return 0; + \\} + , &[_][]const u8{ + \\pub export fn foo() [*c]c_int { + \\ return null; + \\} + }); + /////////////// Cases that pass for only stage2 //////////////// cases.add_2("Parameterless function prototypes", @@ -904,145 +1078,75 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; }); + cases.add_2("undefined array global", + \\int array[100] = {}; + , &[_][]const u8{ + \\pub export var array: [100]c_int = .{0} ** 100; + }); + + cases.add_2("restrict -> noalias", + \\void foo(void *restrict bar, void *restrict); + , &[_][]const u8{ + \\pub extern fn foo(noalias bar: ?*c_void, noalias arg_1: ?*c_void) void; + }); + + cases.add_2("assign", + \\int max(int a) { + \\ int tmp; + \\ tmp = a; + \\ a = tmp; + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int) c_int { + \\ var tmp: c_int = undefined; + \\ tmp = a; + \\ a = tmp; + \\} + }); + + cases.add_2("chaining assign", + \\void max(int a) { + \\ int b, c; + \\ c = b = a; + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int) void { + \\ var b: c_int = undefined; + \\ var c: c_int = undefined; + \\ c = blk: { + \\ const _tmp_1 = a; + \\ b = _tmp_1; + \\ break :blk _tmp_1; + \\ }; + \\} + }); + + cases.add_2("anonymous enum", + \\enum { + \\ One, + \\ Two, + \\}; + , &[_][]const u8{ + \\pub const One = enum_unnamed_1.One; + \\pub const Two = enum_unnamed_1.Two; + \\pub const enum_unnamed_1 = extern enum { + \\ One, + \\ Two, + \\}; + }); + + cases.add_2("c style cast", + \\int float_to_int(float a) { + \\ return (int)a; + \\} + , &[_][]const u8{ + \\pub export fn float_to_int(a: f32) c_int { + \\ return @floatToInt(c_int, a); + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// - if (builtin.os != builtin.Os.windows) { - // Windows treats this as an enum with type c_int - cases.add("big negative enum init values when C ABI supports long long enums", - \\enum EnumWithInits { - \\ VAL01 = 0, - \\ VAL02 = 1, - \\ VAL03 = 2, - \\ VAL04 = 3, - \\ VAL05 = -1, - \\ VAL06 = -2, - \\ VAL07 = -3, - \\ VAL08 = -4, - \\ VAL09 = VAL02 + VAL08, - \\ VAL10 = -1000012000, - \\ VAL11 = -1000161000, - \\ VAL12 = -1000174001, - \\ VAL13 = VAL09, - \\ VAL14 = VAL10, - \\ VAL15 = VAL11, - \\ VAL16 = VAL13, - \\ VAL17 = (VAL16 - VAL10 + 1), - \\ VAL18 = 0x1000000000000000L, - \\ VAL19 = VAL18 + VAL18 + VAL18 - 1, - \\ VAL20 = VAL19 + VAL19, - \\ VAL21 = VAL20 + 0xFFFFFFFFFFFFFFFF, - \\ VAL22 = 0xFFFFFFFFFFFFFFFF + 1, - \\ VAL23 = 0xFFFFFFFFFFFFFFFF, - \\}; - , &[_][]const u8{ - \\pub const enum_EnumWithInits = extern enum(c_longlong) { - \\ VAL01 = 0, - \\ VAL02 = 1, - \\ VAL03 = 2, - \\ VAL04 = 3, - \\ VAL05 = -1, - \\ VAL06 = -2, - \\ VAL07 = -3, - \\ VAL08 = -4, - \\ VAL09 = -3, - \\ VAL10 = -1000012000, - \\ VAL11 = -1000161000, - \\ VAL12 = -1000174001, - \\ VAL13 = -3, - \\ VAL14 = -1000012000, - \\ VAL15 = -1000161000, - \\ VAL16 = -3, - \\ VAL17 = 1000011998, - \\ VAL18 = 1152921504606846976, - \\ VAL19 = 3458764513820540927, - \\ VAL20 = 6917529027641081854, - \\ VAL21 = 6917529027641081853, - \\ VAL22 = 0, - \\ VAL23 = -1, - \\}; - }); - } - - cases.add("predefined expressions", - \\void foo(void) { - \\ __func__; - \\ __FUNCTION__; - \\ __PRETTY_FUNCTION__; - \\} - , &[_][]const u8{ - \\pub fn foo() void { - \\ _ = "foo"; - \\ _ = "foo"; - \\ _ = "void foo(void)"; - \\} - }); - - cases.add("ignore result, no function arguments", - \\void foo() { - \\ int a; - \\ 1; - \\ "hey"; - \\ 1 + 1; - \\ 1 - 1; - \\ a = 1; - \\} - , &[_][]const u8{ - \\pub fn foo() void { - \\ var a: c_int = undefined; - \\ _ = 1; - \\ _ = "hey"; - \\ _ = (1 + 1); - \\ _ = (1 - 1); - \\ a = 1; - \\} - }); - - cases.add("for loop with var init but empty body", - \\void foo(void) { - \\ for (int x = 0; x < 10; x++); - \\} - , &[_][]const u8{ - \\pub fn foo() void { - \\ { - \\ var x: c_int = 0; - \\ while (x < 10) : (x += 1) {} - \\ } - \\} - }); - - cases.add("do while with empty body", - \\void foo(void) { - \\ do ; while (1); - \\} - , &[_][]const u8{ // TODO this should be if (1 != 0) break - \\pub fn foo() void { - \\ while (true) { - \\ {} - \\ if (!1) break; - \\ } - \\} - }); - - cases.add("for with empty body", - \\void foo(void) { - \\ for (;;); - \\} - , &[_][]const u8{ - \\pub fn foo() void { - \\ while (true) {} - \\} - }); - - cases.add("while with empty body", - \\void foo(void) { - \\ while (1); - \\} - , &[_][]const u8{ - \\pub fn foo() void { - \\ while (1 != 0) {} - \\} - }); - cases.addAllowWarnings("simple data types", \\#include \\int foo(char a, unsigned char b, signed char c); @@ -1067,56 +1171,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("restrict -> noalias", - \\void foo(void *restrict bar, void *restrict); - , &[_][]const u8{ - \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void; - }); - - cases.add("qualified struct and enum", - \\struct Foo { - \\ int x; - \\ int y; - \\}; - \\enum Bar { - \\ BarA, - \\ BarB, - \\}; - \\void func(struct Foo *a, enum Bar **b); - , &[_][]const u8{ - \\pub const struct_Foo = extern struct { - \\ x: c_int, - \\ y: c_int, - \\}; - , - \\pub const enum_Bar = extern enum { - \\ A, - \\ B, - \\}; - , - \\pub const BarA = enum_Bar.A; - , - \\pub const BarB = enum_Bar.B; - , - \\pub extern fn func(a: [*c]struct_Foo, b: [*c]([*c]enum_Bar)) void; - , - \\pub const Foo = struct_Foo; - , - \\pub const Bar = enum_Bar; - }); - - cases.add("constant size array", - \\void func(int array[20]); - , &[_][]const u8{ - \\pub extern fn func(array: [*c]c_int) void; - }); - - cases.add("__cdecl doesn't mess up function pointers", - \\void foo(void (__cdecl *fn_ptr)(void)); - , &[_][]const u8{ - \\pub extern fn foo(fn_ptr: ?extern fn () void) void; - }); - cases.add("macro defines string literal with hex", \\#define FOO "aoeu\xab derp" \\#define FOO2 "aoeu\x0007a derp" @@ -1266,38 +1320,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("assign", - \\int max(int a) { - \\ int tmp; - \\ tmp = a; - \\ a = tmp; - \\} - , &[_][]const u8{ - \\pub export fn max(_arg_a: c_int) c_int { - \\ var a = _arg_a; - \\ var tmp: c_int = undefined; - \\ tmp = a; - \\ a = tmp; - \\} - }); - - cases.addC("chaining assign", - \\void max(int a) { - \\ int b, c; - \\ c = b = a; - \\} - , &[_][]const u8{ - \\pub export fn max(a: c_int) void { - \\ var b: c_int = undefined; - \\ var c: c_int = undefined; - \\ c = (x: { - \\ const _tmp = a; - \\ b = _tmp; - \\ break :x _tmp; - \\ }); - \\} - }); - cases.addC("shift right assign with a fixed size type", \\#include \\int log2(uint32_t a) { @@ -1318,16 +1340,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("anonymous enum", - \\enum { - \\ One, - \\ Two, - \\}; - , &[_][]const u8{ - \\pub const One = 0; - \\pub const Two = 1; - }); - cases.addC("function call", \\static void bar(void) { } \\static int baz(void) { return 0; } @@ -1362,26 +1374,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("null statements", - \\void foo(void) { - \\ ;;;;; - \\} - , &[_][]const u8{ - \\pub export fn foo() void { - \\ {} - \\ {} - \\ {} - \\ {} - \\ {} - \\} - }); - - cases.add("undefined array global", - \\int array[100]; - , &[_][]const u8{ - \\pub var array: [100]c_int = undefined; - }); - cases.addC("array access", \\int array[100]; \\int foo(int index) { @@ -1394,36 +1386,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("c style cast", - \\int float_to_int(float a) { - \\ return (int)a; - \\} - , &[_][]const u8{ - \\pub export fn float_to_int(a: f32) c_int { - \\ return @as(c_int, a); - \\} - }); - - cases.addC("void cast", - \\void foo(int a) { - \\ (void) a; - \\} - , &[_][]const u8{ - \\pub export fn foo(a: c_int) void { - \\ _ = a; - \\} - }); - - cases.addC("implicit cast to void *", - \\void *foo(unsigned short *x) { - \\ return x; - \\} - , &[_][]const u8{ - \\pub export fn foo(x: [*c]c_ushort) ?*c_void { - \\ return @ptrCast(?*c_void, x); - \\} - }); - cases.addC("sizeof", \\#include \\size_t size_of(void) { @@ -1435,29 +1397,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("null pointer implicit cast", - \\int* foo(void) { - \\ return 0; - \\} - , &[_][]const u8{ - \\pub export fn foo() [*c]c_int { - \\ return null; - \\} - }); - - cases.addC("comma operator", - \\int foo(void) { - \\ return 1, 2; - \\} - , &[_][]const u8{ - \\pub export fn foo() c_int { - \\ return x: { - \\ _ = 1; - \\ break :x 2; - \\ }; - \\} - }); - cases.addC("statement expression", \\int foo(void) { \\ return ({ @@ -2292,4 +2231,159 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ } \\} }); + + cases.add("for loop with var init but empty body", + \\void foo(void) { + \\ for (int x = 0; x < 10; x++); + \\} + , &[_][]const u8{ + \\pub fn foo() void { + \\ { + \\ var x: c_int = 0; + \\ while (x < 10) : (x += 1) {} + \\ } + \\} + }); + + cases.add("do while with empty body", + \\void foo(void) { + \\ do ; while (1); + \\} + , &[_][]const u8{ // TODO this should be if (1 != 0) break + \\pub fn foo() void { + \\ while (true) { + \\ {} + \\ if (!1) break; + \\ } + \\} + }); + + cases.add("for with empty body", + \\void foo(void) { + \\ for (;;); + \\} + , &[_][]const u8{ + \\pub fn foo() void { + \\ while (true) {} + \\} + }); + + cases.add("while with empty body", + \\void foo(void) { + \\ while (1); + \\} + , &[_][]const u8{ + \\pub fn foo() void { + \\ while (1 != 0) {} + \\} + }); + + cases.add("undefined array global", + \\int array[100]; + , &[_][]const u8{ + \\pub var array: [100]c_int = undefined; + }); + + cases.add("qualified struct and enum", + \\struct Foo { + \\ int x; + \\ int y; + \\}; + \\enum Bar { + \\ BarA, + \\ BarB, + \\}; + \\void func(struct Foo *a, enum Bar **b); + , &[_][]const u8{ + \\pub const struct_Foo = extern struct { + \\ x: c_int, + \\ y: c_int, + \\}; + , + \\pub const enum_Bar = extern enum { + \\ A, + \\ B, + \\}; + , + \\pub const BarA = enum_Bar.A; + , + \\pub const BarB = enum_Bar.B; + , + \\pub extern fn func(a: [*c]struct_Foo, b: [*c]([*c]enum_Bar)) void; + , + \\pub const Foo = struct_Foo; + , + \\pub const Bar = enum_Bar; + }); + + cases.add("restrict -> noalias", + \\void foo(void *restrict bar, void *restrict); + , &[_][]const u8{ + \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void; + }); + + cases.addC("assign", + \\int max(int a) { + \\ int tmp; + \\ tmp = a; + \\ a = tmp; + \\} + , &[_][]const u8{ + \\pub export fn max(_arg_a: c_int) c_int { + \\ var a = _arg_a; + \\ var tmp: c_int = undefined; + \\ tmp = a; + \\ a = tmp; + \\} + }); + + cases.addC("chaining assign", + \\void max(int a) { + \\ int b, c; + \\ c = b = a; + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int) void { + \\ var b: c_int = undefined; + \\ var c: c_int = undefined; + \\ c = (x: { + \\ const _tmp = a; + \\ b = _tmp; + \\ break :x _tmp; + \\ }); + \\} + }); + + cases.add("anonymous enum", + \\enum { + \\ One, + \\ Two, + \\}; + , &[_][]const u8{ + \\pub const One = 0; + \\pub const Two = 1; + }); + + cases.addC("c style cast", + \\int float_to_int(float a) { + \\ return (int)a; + \\} + , &[_][]const u8{ + \\pub export fn float_to_int(a: f32) c_int { + \\ return @as(c_int, a); + \\} + }); + + cases.addC("comma operator", + \\int foo(void) { + \\ return 1, 2; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ return x: { + \\ _ = 1; + \\ break :x 2; + \\ }; + \\} + }); } From 21bc3353b8812ea097afb52fef411a4fb32bfea9 Mon Sep 17 00:00:00 2001 From: Vexu Date: Wed, 18 Dec 2019 01:04:01 +0200 Subject: [PATCH 17/37] translate-c-2 character literals and more test fixes --- lib/std/fmt.zig | 4 +- src-self-hosted/clang.zig | 13 + src-self-hosted/translate_c.zig | 165 +++++++----- test/translate_c.zig | 444 ++++++++++++++++++-------------- 4 files changed, 365 insertions(+), 261 deletions(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 2eef92a201..f1249afa97 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -582,7 +582,9 @@ pub fn formatAsciiChar( comptime Errors: type, output: fn (@TypeOf(context), []const u8) Errors!void, ) Errors!void { - return output(context, @as(*const [1]u8, &c)[0..]); + if (std.ascii.isPrint(c)) + return output(context, @as(*const [1]u8, &c)[0..]); + return format(context, Errors, output, "\\x{x:0<2}", .{c}); } pub fn formatBuf( diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index b44f3a2389..efb3eb611f 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -78,6 +78,7 @@ pub const struct_ZigClangInitListExpr = @OpaqueType(); pub const ZigClangPreprocessingRecord = @OpaqueType(); pub const ZigClangFloatingLiteral = @OpaqueType(); pub const ZigClangConstantExpr = @OpaqueType(); +pub const ZigClangCharacterLiteral = @OpaqueType(); pub const ZigClangBO = extern enum { PtrMemD, @@ -712,6 +713,14 @@ pub const ZigClangStringLiteral_StringKind = extern enum { UTF32, }; +pub const ZigClangCharacterLiteral_CharacterKind = extern enum { + Ascii, + Wide, + UTF8, + UTF16, + UTF32, +}; + pub const ZigClangRecordDecl_field_iterator = extern struct { opaque: *c_void, }; @@ -1077,3 +1086,7 @@ pub extern fn ZigClangExpr_EvaluateAsConstantExpr(*const ZigClangExpr, *ZigClang pub extern fn ZigClangPredefinedExpr_getFunctionName(*const ZigClangPredefinedExpr) *const ZigClangStringLiteral; +pub extern fn ZigClangCharacterLiteral_getBeginLoc(*const ZigClangCharacterLiteral) ZigClangSourceLocation; +pub extern fn ZigClangCharacterLiteral_getKind(*const ZigClangCharacterLiteral) ZigClangCharacterLiteral_CharacterKind; +pub extern fn ZigClangCharacterLiteral_getValue(*const ZigClangCharacterLiteral) c_uint; + diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 0933b7aa20..b3e32e4c45 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -49,7 +49,10 @@ const Scope = struct { Root, Condition, FnDef, + + /// used when getting a member `a.b` Ref, + Loop, }; const Switch = struct { @@ -59,11 +62,6 @@ const Scope = struct { has_default: bool = false, }; - /// used when getting a member `a.b` - const Ref = struct { - base: Scope, - }; - const Block = struct { base: Scope, block_node: *ast.Node.Block, @@ -125,10 +123,6 @@ const Scope = struct { } }; - const Condition = struct { - base: Scope, - }; - const FnDef = struct { base: Scope, params: AliasList, @@ -169,7 +163,6 @@ const Scope = struct { .Root => unreachable, .Block => return @fieldParentPtr(Block, "base", scope), .Condition => { - const cond = @fieldParentPtr(Condition, "base", scope); // comma operator used return try Block.init(c, scope, "blk"); }, @@ -192,6 +185,7 @@ const Scope = struct { .FnDef => @fieldParentPtr(FnDef, "base", scope).getAlias(name), .Block => @fieldParentPtr(Block, "base", scope).getAlias(name), .Switch, + .Loop, .Condition => scope.parent.?.getAlias(name), }; } @@ -203,6 +197,7 @@ const Scope = struct { .FnDef => @fieldParentPtr(FnDef, "base", scope).contains(name), .Block => @fieldParentPtr(Block, "base", scope).contains(name), .Switch, + .Loop, .Condition => scope.parent.?.contains(name), }; } @@ -213,6 +208,7 @@ const Scope = struct { switch (scope.id) { .FnDef => unreachable, .Switch => return scope, + .Loop => return scope, else => scope = scope.parent.?, } } @@ -434,7 +430,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { error.OutOfMemory => |e| return e, }; }, - else => unreachable, + else => return failDecl(c, fn_decl_loc, fn_name, "unable to resolve function type {}", .{ZigClangType_getTypeClass(fn_type)}), }; if (!decl_ctx.has_body) { @@ -862,6 +858,7 @@ fn transStmt( .DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const ZigClangDefaultStmt, stmt)), .ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used), .PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const ZigClangPredefinedExpr, stmt), result_used), + .CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, stmt), result_used), else => { return revertAndWarn( rp, @@ -1202,7 +1199,7 @@ fn transStringLiteral( const token = try appendToken(rp.c, .StringLiteral, buf); const node = try rp.c.a().create(ast.Node.StringLiteral); - node.* = ast.Node.StringLiteral{ + node.* = .{ .token = token, }; return maybeSuppressResult(rp, scope, result_used, &node.base); @@ -1237,18 +1234,15 @@ fn writeEscapedString(buf: []u8, s: []const u8) void { // Returns either a string literal or a slice of `buf`. fn escapeChar(c: u8, char_buf: *[4]u8) []const u8 { - // TODO: https://github.com/ziglang/zig/issues/2749 - const escaped = switch (c) { - // Printable ASCII except for ' " \ - ' ', '!', '#'...'&', '('...'[', ']'...'~' => ([_]u8{c})[0..], - '\'', '\"', '\\' => ([_]u8{ '\\', c })[0..], - '\n' => return "\\n"[0..], - '\r' => return "\\r"[0..], - '\t' => return "\\t"[0..], - else => return std.fmt.bufPrint(char_buf[0..], "\\x{x:2}", .{c}) catch unreachable, + return switch (c) { + '\"' => "\\\""[0..], + '\'' => "\\'"[0..], + '\\' => "\\\\"[0..], + '\n' => "\\n"[0..], + '\r' => "\\r"[0..], + '\t' => "\\t"[0..], + else => std.fmt.bufPrint(char_buf[0..], "{c}", .{c}) catch unreachable, }; - std.mem.copy(u8, char_buf, escaped); - return char_buf[0..escaped.len]; } fn transCCast( @@ -1459,13 +1453,11 @@ fn transIfStmt( // if (c) t else e const if_node = try transCreateNodeIf(rp.c); - var cond_scope = Scope.Condition{ - .base = .{ - .parent = scope, - .id = .Condition, - }, + var cond_scope = Scope{ + .parent = scope, + .id = .Condition, }; - if_node.condition = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt)), .used, .r_value, false); + if_node.condition = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangIfStmt_getCond(stmt)), .used, .r_value, false); _ = try appendToken(rp.c, .RParen, ")"); if_node.body = try transStmt(rp, scope, ZigClangIfStmt_getThen(stmt), .unused, .r_value); @@ -1485,16 +1477,18 @@ fn transWhileLoop( ) TransError!*ast.Node { const while_node = try transCreateNodeWhile(rp.c); - var cond_scope = Scope.Condition{ - .base = .{ - .parent = scope, - .id = .Condition, - }, + var cond_scope = Scope{ + .parent = scope, + .id = .Condition, }; - while_node.condition = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangWhileStmt_getCond(stmt)), .used, .r_value, false); + while_node.condition = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangWhileStmt_getCond(stmt)), .used, .r_value, false); _ = try appendToken(rp.c, .RParen, ")"); - while_node.body = try transStmt(rp, scope, ZigClangWhileStmt_getBody(stmt), .unused, .r_value); + var loop_scope = Scope{ + .parent = scope, + .id = .Loop, + }; + while_node.body = try transStmt(rp, &loop_scope, ZigClangWhileStmt_getBody(stmt), .unused, .r_value); return &while_node.base; } @@ -1508,6 +1502,10 @@ fn transDoWhileLoop( while_node.condition = try transCreateNodeBoolLiteral(rp.c, true); _ = try appendToken(rp.c, .RParen, ")"); var new = false; + var loop_scope = Scope{ + .parent = scope, + .id = .Loop, + }; const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) blk: { // there's already a block in C, so we'll append our condition to it. @@ -1520,7 +1518,7 @@ fn transDoWhileLoop( // zig: b; // zig: if (!cond) break; // zig: } - break :blk (try transStmt(rp, scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).?; + break :blk (try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)).cast(ast.Node.Block).?; } else blk: { // the C statement is without a block, so we need to create a block to contain it. // c: do @@ -1532,20 +1530,18 @@ fn transDoWhileLoop( // zig: } new = true; const block = try transCreateNodeBlock(rp.c, null); - try block.statements.push(try transStmt(rp, scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)); + try block.statements.push(try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value)); break :blk block; }; // if (!cond) break; const if_node = try transCreateNodeIf(rp.c); - var cond_scope = Scope.Condition{ - .base = .{ - .parent = scope, - .id = .Condition, - }, + var cond_scope = Scope{ + .parent = scope, + .id = .Condition, }; const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!"); - prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, false); + prefix_op.rhs = try transBoolExpr(rp, &cond_scope, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true); _ = try appendToken(rp.c, .RParen, ")"); if_node.condition = &prefix_op.base; if_node.body = &(try transCreateNodeBreak(rp.c, null)).base; @@ -1563,25 +1559,26 @@ fn transForLoop( scope: *Scope, stmt: *const ZigClangForStmt, ) TransError!*ast.Node { - var inner = scope; + var loop_scope = Scope{ + .parent = scope, + .id = .Loop, + }; var block = false; var block_scope: ?*Scope.Block = null; if (ZigClangForStmt_getInit(stmt)) |init| { block_scope = try Scope.Block.init(rp.c, scope, null); block_scope.?.block_node = try transCreateNodeBlock(rp.c, null); - inner = &block_scope.?.base; - _ = try transStmt(rp, inner, init, .unused, .r_value); + loop_scope.parent = &block_scope.?.base; + _ = try transStmt(rp, &loop_scope, init, .unused, .r_value); } - var cond_scope = Scope.Condition{ - .base = .{ - .parent = inner, - .id = .Condition, - }, + var cond_scope = Scope{ + .parent = scope, + .id = .Condition, }; const while_node = try transCreateNodeWhile(rp.c); while_node.condition = if (ZigClangForStmt_getCond(stmt)) |cond| - try transBoolExpr(rp, &cond_scope.base, cond, .used, .r_value, false) + try transBoolExpr(rp, &cond_scope, cond, .used, .r_value, false) else try transCreateNodeBoolLiteral(rp.c, true); _ = try appendToken(rp.c, .RParen, ")"); @@ -1589,11 +1586,11 @@ fn transForLoop( if (ZigClangForStmt_getInc(stmt)) |incr| { _ = try appendToken(rp.c, .Colon, ":"); _ = try appendToken(rp.c, .LParen, "("); - while_node.continue_expr = try transExpr(rp, &cond_scope.base, incr, .unused, .r_value); + while_node.continue_expr = try transExpr(rp, &cond_scope, incr, .unused, .r_value); _ = try appendToken(rp.c, .RParen, ")"); } - while_node.body = try transStmt(rp, inner, ZigClangForStmt_getBody(stmt), .unused, .r_value); + while_node.body = try transStmt(rp, &loop_scope, ZigClangForStmt_getBody(stmt), .unused, .r_value); if (block_scope != null) { try block_scope.?.block_node.statements.push(&while_node.base); block_scope.?.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); @@ -1617,13 +1614,11 @@ fn transSwitch( .pending_block = undefined, }; - var cond_scope = Scope.Condition{ - .base = .{ - .parent = scope, - .id = .Condition, - }, + var cond_scope = Scope{ + .parent = scope, + .id = .Condition, }; - switch_node.expr = try transExpr(rp, &cond_scope.base, ZigClangSwitchStmt_getCond(stmt), .used, .r_value); + switch_node.expr = try transExpr(rp, &cond_scope, ZigClangSwitchStmt_getCond(stmt), .used, .r_value); _ = try appendToken(rp.c, .RParen, ")"); _ = try appendToken(rp.c, .LBrace, "{"); switch_node.rbrace = try appendToken(rp.c, .RBrace, "}"); @@ -1753,6 +1748,41 @@ fn transPredefinedExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangPre return transStringLiteral(rp, scope, ZigClangPredefinedExpr_getFunctionName(expr), used); } +fn transCharLiteral( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangCharacterLiteral, + result_used: ResultUsed, +) TransError!*ast.Node { + const kind = ZigClangCharacterLiteral_getKind(stmt); + switch (kind) { + .Ascii, .UTF8 => { + const val = ZigClangCharacterLiteral_getValue(stmt); + if (kind == .Ascii) { + // C has a somewhat obscure feature called multi-character character + // constant + if (val > 255) + return transCreateNodeInt(rp.c, val); + } + var char_buf: [4]u8 = undefined; + const token = try appendTokenFmt(rp.c, .CharLiteral, "'{}'", .{escapeChar(@intCast(u8, val), &char_buf)}); + const node = try rp.c.a().create(ast.Node.CharLiteral); + node.* = .{ + .token = token, + }; + return maybeSuppressResult(rp, scope, result_used, &node.base); + }, + .UTF16, .UTF32, .Wide => return revertAndWarn( + rp, + error.UnsupportedTranslation, + ZigClangStmt_getBeginLoc(@ptrCast(*const ZigClangStmt, stmt)), + "TODO: support character literal kind {}", + .{kind}, + ), + else => unreachable, + } +} + fn transCPtrCast( rp: RestorePoint, loc: ZigClangSourceLocation, @@ -1796,6 +1826,7 @@ fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node { "__switch" else null); + _ = try appendToken(rp.c, .Semicolon, ";"); return &br.base; } @@ -1813,18 +1844,16 @@ fn transConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigCla const gropued = scope.id == .Condition; const lparen = if (gropued) try appendToken(rp.c, .LParen, "(") else undefined; const if_node = try transCreateNodeIf(rp.c); - var cond_scope = Scope.Condition{ - .base = .{ - .parent = scope, - .id = .Condition, - }, + var cond_scope = Scope{ + .parent = scope, + .id = .Condition, }; const cond_expr = ZigClangConditionalOperator_getCond(stmt); const true_expr = ZigClangConditionalOperator_getTrueExpr(stmt); const false_expr = ZigClangConditionalOperator_getFalseExpr(stmt); - if_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false); + if_node.condition = try transBoolExpr(rp, &cond_scope, cond_expr, .used, .r_value, false); _ = try appendToken(rp.c, .RParen, ")"); if_node.body = try transExpr(rp, scope, true_expr, .used, .r_value); @@ -3104,7 +3133,7 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void { var tok_it = tok_list.iterator(0); const first_tok = tok_it.next().?; - assert(first_tok.id == .Identifier and std.mem.eql(u8, first_tok.bytes, checked_name)); + assert(first_tok.id == .Identifier and std.mem.eql(u8, first_tok.bytes, name)); const next = tok_it.peek().?; switch (next.id) { .Identifier => { diff --git a/test/translate_c.zig b/test/translate_c.zig index aebeddc856..6191dabe02 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -600,6 +600,135 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_both("simple union", + \\union Foo { + \\ int x; + \\ double y; + \\}; + , &[_][]const u8{ + \\pub const union_Foo = extern union { + \\ x: c_int, + \\ y: f64, + \\}; + , + \\pub const Foo = union_Foo; + }); + + cases.addC_both("string literal", + \\const char *foo(void) { + \\ return "bar"; + \\} + , &[_][]const u8{ + \\pub export fn foo() [*c]const u8 { + \\ return "bar"; + \\} + }); + + cases.addC_both("return void", + \\void foo(void) { + \\ return; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ return; + \\} + }); + + cases.addC_both("for loop", + \\void foo(void) { + \\ for (int i = 0; i; i = i + 1) { } + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ { + \\ var i: c_int = 0; + \\ while (i != 0) : (i = (i + 1)) {} + \\ } + \\} + }); + + cases.addC_both("empty for loop", + \\void foo(void) { + \\ for (;;) { } + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ while (true) {} + \\} + }); + + cases.addC_both("break statement", + \\void foo(void) { + \\ for (;;) { + \\ break; + \\ } + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ while (true) { + \\ break; + \\ } + \\} + }); + + cases.addC_both("continue statement", + \\void foo(void) { + \\ for (;;) { + \\ continue; + \\ } + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ while (true) { + \\ continue; + \\ } + \\} + }); + + cases.addC_both("pointer casting", + \\float *ptrcast(int *a) { + \\ return (float *)a; + \\} + , &[_][]const u8{ + \\pub export fn ptrcast(a: [*c]c_int) [*c]f32 { + \\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a)); + \\} + }); + + cases.addC_both("pointer conversion with different alignment", + \\void test_ptr_cast() { + \\ void *p; + \\ { + \\ char *to_char = (char *)p; + \\ short *to_short = (short *)p; + \\ int *to_int = (int *)p; + \\ long long *to_longlong = (long long *)p; + \\ } + \\ { + \\ char *to_char = p; + \\ short *to_short = p; + \\ int *to_int = p; + \\ long long *to_longlong = p; + \\ } + \\} + , &[_][]const u8{ + \\pub export fn test_ptr_cast() void { + \\ var p: ?*c_void = undefined; + \\ { + \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p)); + \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p)); + \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p)); + \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p)); + \\ } + \\ { + \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p)); + \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p)); + \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p)); + \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p)); + \\ } + \\} + }); + /////////////// Cases that pass for only stage2 //////////////// cases.add_2("Parameterless function prototypes", @@ -957,11 +1086,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ while (true) { \\ var a: c_int = 2; \\ a = 12; - \\ if (!4 != 0) break; + \\ if (!(4 != 0)) break; \\ } \\ while (true) { \\ a = 7; - \\ if (!4 != 0) break; + \\ if (!(4 != 0)) break; \\ } \\} }); @@ -1145,6 +1274,66 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("escape sequences", + \\const char *escapes() { + \\char a = '\'', + \\ b = '\\', + \\ c = '\a', + \\ d = '\b', + \\ e = '\f', + \\ f = '\n', + \\ g = '\r', + \\ h = '\t', + \\ i = '\v', + \\ j = '\0', + \\ k = '\"'; + \\ return "\'\\\a\b\f\n\r\t\v\0\""; + \\} + \\ + , &[_][]const u8{ + \\pub export fn escapes() [*c]const u8 { + \\ var a: u8 = @as(u8, '\''); + \\ var b: u8 = @as(u8, '\\'); + \\ var c: u8 = @as(u8, '\x07'); + \\ var d: u8 = @as(u8, '\x08'); + \\ var e: u8 = @as(u8, '\x0c'); + \\ var f: u8 = @as(u8, '\n'); + \\ var g: u8 = @as(u8, '\r'); + \\ var h: u8 = @as(u8, '\t'); + \\ var i: u8 = @as(u8, '\x0b'); + \\ var j: u8 = @as(u8, '\x00'); + \\ var k: u8 = @as(u8, '\"'); + \\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\""; + \\} + }); + + cases.add_2("do loop", + \\void foo(void) { + \\ int a = 2; + \\ do { + \\ a = a - 1; + \\ } while (a); + \\ + \\ int b = 2; + \\ do + \\ b = b -1; + \\ while (b); + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var a: c_int = 2; + \\ while (true) { + \\ a = (a - 1); + \\ if (!(a != 0)) break; + \\ } + \\ var b: c_int = 2; + \\ while (true) { + \\ b = (b - 1); + \\ if (!(b != 0)) break; + \\ } + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// cases.addAllowWarnings("simple data types", @@ -1641,33 +1830,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("do loop", - \\void foo(void) { - \\ int a = 2; - \\ do { - \\ a--; - \\ } while (a != 0); - \\ - \\ int b = 2; - \\ do - \\ b--; - \\ while (b != 0); - \\} - , &[_][]const u8{ - \\pub export fn foo() void { - \\ var a: c_int = 2; - \\ while (true) { - \\ a -= 1; - \\ if (!(a != 0)) break; - \\ } - \\ var b: c_int = 2; - \\ while (true) { - \\ b -= 1; - \\ if (!(b != 0)) break; - \\ } - \\} - }); - cases.addC("deref function pointer", \\void foo(void) {} \\int baz(void) { return 0; } @@ -1708,20 +1870,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("simple union", - \\union Foo { - \\ int x; - \\ double y; - \\}; - , &[_][]const u8{ - \\pub const union_Foo = extern union { - \\ x: c_int, - \\ y: f64, - \\}; - , - \\pub const Foo = union_Foo; - }); - cases.add("address of operator", \\int foo(void) { \\ int x = 1234; @@ -1736,77 +1884,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("string literal", - \\const char *foo(void) { - \\ return "bar"; - \\} - , &[_][]const u8{ - \\pub fn foo() [*c]const u8 { - \\ return "bar"; - \\} - }); - - cases.add("return void", - \\void foo(void) { - \\ return; - \\} - , &[_][]const u8{ - \\pub fn foo() void { - \\ return; - \\} - }); - - cases.add("for loop", - \\void foo(void) { - \\ for (int i = 0; i < 10; i += 1) { } - \\} - , &[_][]const u8{ - \\pub fn foo() void { - \\ { - \\ var i: c_int = 0; - \\ while (i < 10) : (i += 1) {} - \\ } - \\} - }); - - cases.add("empty for loop", - \\void foo(void) { - \\ for (;;) { } - \\} - , &[_][]const u8{ - \\pub fn foo() void { - \\ while (true) {} - \\} - }); - - cases.add("break statement", - \\void foo(void) { - \\ for (;;) { - \\ break; - \\ } - \\} - , &[_][]const u8{ - \\pub fn foo() void { - \\ while (true) { - \\ break; - \\ } - \\} - }); - - cases.add("continue statement", - \\void foo(void) { - \\ for (;;) { - \\ continue; - \\ } - \\} - , &[_][]const u8{ - \\pub fn foo() void { - \\ while (true) { - \\ continue; - \\ } - \\} - }); - cases.add("variable name shadowing", \\int foo(void) { \\ int x = 1; @@ -1827,16 +1904,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("pointer casting", - \\float *ptrcast(int *a) { - \\ return (float *)a; - \\} - , &[_][]const u8{ - \\fn ptrcast(a: [*c]c_int) [*c]f32 { - \\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a)); - \\} - }); - cases.add("bin not", \\int foo(int x) { \\ return ~x; @@ -1987,74 +2054,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("pointer conversion with different alignment", - \\void test_ptr_cast() { - \\ void *p; - \\ { - \\ char *to_char = (char *)p; - \\ short *to_short = (short *)p; - \\ int *to_int = (int *)p; - \\ long long *to_longlong = (long long *)p; - \\ } - \\ { - \\ char *to_char = p; - \\ short *to_short = p; - \\ int *to_int = p; - \\ long long *to_longlong = p; - \\ } - \\} - , &[_][]const u8{ - \\pub export fn test_ptr_cast() void { - \\ var p: ?*c_void = undefined; - \\ { - \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p)); - \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p)); - \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p)); - \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p)); - \\ } - \\ { - \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p)); - \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p)); - \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p)); - \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p)); - \\ } - \\} - }); - - cases.addC("escape sequences", - \\const char *escapes() { - \\char a = '\'', - \\ b = '\\', - \\ c = '\a', - \\ d = '\b', - \\ e = '\f', - \\ f = '\n', - \\ g = '\r', - \\ h = '\t', - \\ i = '\v', - \\ j = '\0', - \\ k = '\"'; - \\ return "\'\\\a\b\f\n\r\t\v\0\""; - \\} - \\ - , &[_][]const u8{ - \\pub export fn escapes() [*c]const u8 { - \\ var a: u8 = @as(u8, '\''); - \\ var b: u8 = @as(u8, '\\'); - \\ var c: u8 = @as(u8, '\x07'); - \\ var d: u8 = @as(u8, '\x08'); - \\ var e: u8 = @as(u8, '\x0c'); - \\ var f: u8 = @as(u8, '\n'); - \\ var g: u8 = @as(u8, '\r'); - \\ var h: u8 = @as(u8, '\t'); - \\ var i: u8 = @as(u8, '\x0b'); - \\ var j: u8 = @as(u8, '\x00'); - \\ var k: u8 = @as(u8, '\"'); - \\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\""; - \\} - \\ - }); - if (builtin.os != builtin.Os.windows) { // sysv_abi not currently supported on windows cases.add("Macro qualified functions", @@ -2386,4 +2385,65 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ }; \\} }); + + cases.addC("escape sequences", + \\const char *escapes() { + \\char a = '\'', + \\ b = '\\', + \\ c = '\a', + \\ d = '\b', + \\ e = '\f', + \\ f = '\n', + \\ g = '\r', + \\ h = '\t', + \\ i = '\v', + \\ j = '\0', + \\ k = '\"'; + \\ return "\'\\\a\b\f\n\r\t\v\0\""; + \\} + \\ + , &[_][]const u8{ + \\pub export fn escapes() [*c]const u8 { + \\ var a: u8 = @as(u8, '\''); + \\ var b: u8 = @as(u8, '\\'); + \\ var c: u8 = @as(u8, '\x07'); + \\ var d: u8 = @as(u8, '\x08'); + \\ var e: u8 = @as(u8, '\x0c'); + \\ var f: u8 = @as(u8, '\n'); + \\ var g: u8 = @as(u8, '\r'); + \\ var h: u8 = @as(u8, '\t'); + \\ var i: u8 = @as(u8, '\x0b'); + \\ var j: u8 = @as(u8, '\x00'); + \\ var k: u8 = @as(u8, '\"'); + \\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\""; + \\} + \\ + }); + + cases.addC("do loop", + \\void foo(void) { + \\ int a = 2; + \\ do { + \\ a--; + \\ } while (a != 0); + \\ + \\ int b = 2; + \\ do + \\ b--; + \\ while (b != 0); + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var a: c_int = 2; + \\ while (true) { + \\ a -= 1; + \\ if (!(a != 0)) break; + \\ } + \\ var b: c_int = 2; + \\ while (true) { + \\ b -= 1; + \\ if (!(b != 0)) break; + \\ } + \\} + }); } From f54e7d6c99b6fbeedcaf91e643e4e3a02e8f1d81 Mon Sep 17 00:00:00 2001 From: Vexu Date: Wed, 18 Dec 2019 12:47:35 +0200 Subject: [PATCH 18/37] translate-c-2 update @kavika13's work to removal of TransResult --- src-self-hosted/clang.zig | 5 + src-self-hosted/translate_c.zig | 756 +++++++++++--------------------- test/translate_c.zig | 196 ++++----- 3 files changed, 350 insertions(+), 607 deletions(-) diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index efb3eb611f..94d1928a4c 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -741,6 +741,11 @@ pub const ZigClangPreprocessedEntity_EntityKind = extern enum { InclusionDirectiveKind, }; +pub const ZigClangExpr_ConstExprUsage = extern enum { + EvaluateForCodeGen, + EvaluateForMangling, +}; + pub extern fn ZigClangSourceManager_getSpellingLoc(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) struct_ZigClangSourceLocation; pub extern fn ZigClangSourceManager_getFilename(self: *const struct_ZigClangSourceManager, SpellingLoc: struct_ZigClangSourceLocation) ?[*:0]const u8; pub extern fn ZigClangSourceManager_getSpellingLineNumber(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index e3b16c6f77..438feed7a1 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -184,9 +184,7 @@ const Scope = struct { .Ref => null, .FnDef => @fieldParentPtr(FnDef, "base", scope).getAlias(name), .Block => @fieldParentPtr(Block, "base", scope).getAlias(name), - .Switch, - .Loop, - .Condition => scope.parent.?.getAlias(name), + .Switch, .Loop, .Condition => scope.parent.?.getAlias(name), }; } @@ -196,9 +194,7 @@ const Scope = struct { .Root => @fieldParentPtr(Root, "base", scope).contains(name), .FnDef => @fieldParentPtr(FnDef, "base", scope).contains(name), .Block => @fieldParentPtr(Block, "base", scope).contains(name), - .Switch, - .Loop, - .Condition => scope.parent.?.contains(name), + .Switch, .Loop, .Condition => scope.parent.?.contains(name), }; } @@ -594,7 +590,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?* const node = try transCreateNodeVarDecl(c, true, true, name); node.eq_token = try appendToken(c, .Equal, "="); - + var semicolon: ast.TokenIndex = undefined; node.init_node = blk: { const rp = makeRestorePoint(c); @@ -634,12 +630,12 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?* const field_name = try appendIdentifier(c, try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, field_decl)))); _ = try appendToken(c, .Colon, ":"); const field_type = transQualType(rp, ZigClangFieldDecl_getType(field_decl), field_loc) catch |err| switch (err) { - error.UnsupportedType => { - try failDecl(c, record_loc, name, "unable to translate {} member type", .{container_kind_name}); - return null; - }, - else => |e| return e, - }; + error.UnsupportedType => { + try failDecl(c, record_loc, name, "unable to translate {} member type", .{container_kind_name}); + return null; + }, + else => |e| return e, + }; const field_node = try c.a().create(ast.Node.ContainerField); field_node.* = .{ @@ -879,174 +875,10 @@ fn transBinaryOperator( ) TransError!*ast.Node { const op = ZigClangBinaryOperator_getOpcode(stmt); const qt = ZigClangBinaryOperator_getType(stmt); + var op_token: ast.TokenIndex = undefined; + var op_id: ast.Node.InfixOp.Op = undefined; switch (op) { - .PtrMemD, .PtrMemI, .Cmp => return revertAndWarn( - rp, - error.UnsupportedTranslation, - ZigClangBinaryOperator_getBeginLoc(stmt), - "TODO: handle more C binary operators: {}", - .{op}, - ), - .Assign => return try transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt)), - .Add => { - const node = if (cIsUnsignedInteger(qt)) - try transCreateNodeInfixOp(rp, scope, stmt, .AddWrap, .PlusPercent, "+%", true) - else - try transCreateNodeInfixOp(rp, scope, stmt, .Add, .Plus, "+", true); - return maybeSuppressResult(rp, scope, result_used, node); - }, - .Sub => { - const node = if (cIsUnsignedInteger(qt)) - try transCreateNodeInfixOp(rp, scope, stmt, .SubWrap, .MinusPercent, "-%", true) - else - try transCreateNodeInfixOp(rp, scope, stmt, .Sub, .Minus, "-", true); - return maybeSuppressResult(rp, scope, result_used, node); - }, - .Mul => { - const node = if (cIsUnsignedInteger(qt)) - try transCreateNodeInfixOp(rp, scope, stmt, .MultWrap, .AsteriskPercent, "*%", true) - else - try transCreateNodeInfixOp(rp, scope, stmt, .Mult, .Asterisk, "*", true); - return maybeSuppressResult(rp, scope, result_used, node); - }, - .Div => { - 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); - try div_trunc_node.params.push(lhs); - _ = try appendToken(rp.c, .Comma, ","); - const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); - try div_trunc_node.params.push(rhs); - div_trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return maybeSuppressResult(rp, scope, result_used, &div_trunc_node.base); - } else { - // unsigned/float division uses the operator - const node = try transCreateNodeInfixOp(rp, scope, stmt, .Div, .Slash, "/", true); - return maybeSuppressResult(rp, scope, result_used, node); - } - }, - .Rem => { - 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); - try rem_node.params.push(lhs); - _ = try appendToken(rp.c, .Comma, ","); - const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); - try rem_node.params.push(rhs); - rem_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return maybeSuppressResult(rp, scope, result_used, &rem_node.base); - } else { - // unsigned/float division uses the operator - const node = try transCreateNodeInfixOp(rp, scope, stmt, .Mod, .Percent, "%", true); - return maybeSuppressResult(rp, scope, result_used, node); - } - }, - .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{ - .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 => { - 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, - }); - }, + .Assign => return transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt)), .Comma => { const block_scope = try scope.findBlockScope(rp.c); const expr = block_scope.base.parent == scope; @@ -1078,6 +910,131 @@ fn transBinaryOperator( return maybeSuppressResult(rp, scope, result_used, rhs); } }, + .Div => { + if (!cIsUnsignedInteger(qt)) { + // signed integer division uses @divTrunc + const div_trunc_node = try transCreateNodeBuiltinFnCall(rp.c, "@divTrunc"); + try div_trunc_node.params.push(try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value)); + _ = try appendToken(rp.c, .Comma, ","); + const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); + try div_trunc_node.params.push(rhs); + div_trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return maybeSuppressResult(rp, scope, result_used, &div_trunc_node.base); + } + }, + .Rem => { + if (!cIsUnsignedInteger(qt)) { + // signed integer division uses @rem + const rem_node = try transCreateNodeBuiltinFnCall(rp.c, "@rem"); + try rem_node.params.push(try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value)); + _ = try appendToken(rp.c, .Comma, ","); + const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); + try rem_node.params.push(rhs); + rem_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return maybeSuppressResult(rp, scope, result_used, &rem_node.base); + } + }, + .Shl => { + const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<"); + return maybeSuppressResult(rp, scope, result_used, node); + }, + .Shr => { + const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftRight, .AngleBracketAngleBracketRight, ">>"); + return maybeSuppressResult(rp, scope, result_used, node); + }, + .LAnd => { + const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolAnd, result_used, true); + return maybeSuppressResult(rp, scope, result_used, node); + }, + .LOr => { + const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolOr, result_used, true); + return maybeSuppressResult(rp, scope, result_used, node); + }, + else => {}, + } + const lhs_node = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value); + switch (op) { + .PtrMemD, .PtrMemI, .Cmp => return revertAndWarn( + rp, + error.UnsupportedTranslation, + ZigClangBinaryOperator_getBeginLoc(stmt), + "TODO: handle more C binary operators: {}", + .{op}, + ), + .Add => { + if (cIsUnsignedInteger(qt)) { + op_token = try appendToken(rp.c, .PlusPercent, "+%"); + op_id = .AddWrap; + } else { + op_token = try appendToken(rp.c, .Plus, "+"); + op_id = .Add; + } + }, + .Sub => { + if (cIsUnsignedInteger(qt)) { + op_token = try appendToken(rp.c, .MinusPercent, "-%"); + op_id = .SubWrap; + } else { + op_token = try appendToken(rp.c, .Minus, "-"); + op_id = .Sub; + } + }, + .Mul => { + if (cIsUnsignedInteger(qt)) { + op_token = try appendToken(rp.c, .AsteriskPercent, "*%"); + op_id = .MultWrap; + } else { + op_token = try appendToken(rp.c, .Asterisk, "*"); + op_id = .Mult; + } + }, + .Div => { + // unsigned/float division uses the operator + op_id = .Div; + op_token = try appendToken(rp.c, .Slash, "/"); + }, + .Rem => { + // unsigned/float division uses the operator + op_id = .Mod; + op_token = try appendToken(rp.c, .Percent, "%"); + }, + .LT => { + op_id = .LessThan; + op_token = try appendToken(rp.c, .AngleBracketLeft, "<"); + }, + .GT => { + op_id = .GreaterThan; + op_token = try appendToken(rp.c, .AngleBracketRight, ">"); + }, + .LE => { + op_id = .LessOrEqual; + op_token = try appendToken(rp.c, .AngleBracketLeftEqual, "<="); + }, + .GE => { + op_id = .GreaterOrEqual; + op_token = try appendToken(rp.c, .AngleBracketRightEqual, ">="); + }, + .EQ => { + op_id = .EqualEqual; + op_token = try appendToken(rp.c, .EqualEqual, "=="); + }, + .NE => { + op_id = .BangEqual; + op_token = try appendToken(rp.c, .BangEqual, "!="); + }, + .And => { + op_id = .BitAnd; + op_token = try appendToken(rp.c, .Ampersand, "&"); + }, + .Xor => { + op_id = .BitXor; + op_token = try appendToken(rp.c, .Caret, "^"); + }, + .Or => { + op_id = .BitOr; + op_token = try appendToken(rp.c, .Pipe, "|"); + }, + .Assign, .MulAssign, .DivAssign, .RemAssign, @@ -1091,6 +1048,9 @@ fn transBinaryOperator( => unreachable, else => unreachable, } + + const rhs_node = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value); + return transCreateNodeInfixOp(rp, scope, lhs_node, op_id, op_token, rhs_node, result_used, true); } fn transCompoundStmtInline( @@ -1231,56 +1191,22 @@ 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 { + grouped: bool, +) TransError!*ast.Node { + const lparen = if (grouped) + try appendToken(rp.c, .LParen, "(") + else + undefined; var res = try transExpr(rp, scope, expr, used, lrvalue); - switch (res.node.id) { - .InfixOp => switch (@ptrCast(*const ast.Node.InfixOp, &res.node).op) { + switch (res.id) { + .InfixOp => switch (@fieldParentPtr(ast.Node.InfixOp, "base", res).op) { .BoolOr, .BoolAnd, .EqualEqual, @@ -1289,24 +1215,34 @@ fn transBoolExpr( .GreaterThan, .LessOrEqual, .GreaterOrEqual, - => return res.node, + => return res, else => {}, }, - .PrefixOp => switch (@ptrCast(*const ast.Node.PrefixOp, &res.node).op) { - .BoolNot => return res.node, + .PrefixOp => switch (@fieldParentPtr(ast.Node.PrefixOp, "base", res).op) { + .BoolNot => return res, else => {}, }, - .BoolLiteral => return res.node, + .BoolLiteral => return res, else => {}, } - const ty = ZigClangQualType_getTypePtr(getExprQualTypeBeforeImplicitCast(rp.c, expr)); + return finishBoolExpr(rp, scope, ZigClangExpr_getBeginLoc(expr), ty, res, used, grouped); +} +fn finishBoolExpr( + rp: RestorePoint, + scope: *Scope, + loc: ZigClangSourceLocation, + ty: *const ZigClangType, + node: *ast.Node, + used: ResultUsed, + grouped: bool, +) TransError!*ast.Node { switch (ZigClangType_getTypeClass(ty)) { .Builtin => { const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty); @@ -1337,219 +1273,48 @@ fn transBoolExpr( .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, - + => { + const op_token = try appendToken(rp.c, .BangEqual, "!="); + const rhs_node = try transCreateNodeInt(rp.c, 0); + return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, grouped); + }, + .NullPtr => { + const op_token = try appendToken(rp.c, .EqualEqual, "=="); + const rhs_node = try transCreateNodeNullLiteral(rp.c); + return transCreateNodeInfixOp(rp, scope, node, .EqualEqual, op_token, rhs_node, used, grouped); + }, else => {}, } }, - .Pointer => return transCreateNodeNotEqual(rp, scope, res.node, try transCreateNodeNullLiteral(rp.c)), - + .Pointer => { + const op_token = try appendToken(rp.c, .BangEqual, "!="); + const rhs_node = try transCreateNodeNullLiteral(rp.c); + return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, grouped); + }, .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); + const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty); + const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty); + const underlying_type = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl); + return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(underlying_type), node, used, grouped); }, - .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; - } - }; + const enum_ty = @ptrCast(*const ZigClangEnumType, ty); + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@enumToInt"); + try builtin_node.params.push(node); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return toEnumZeroCmp(rp, scope, res.node, gen_enum_decl_node.generate_node, ty, ZigClangExpr_getBeginLoc(expr)); + const op_token = try appendToken(rp.c, .BangEqual, "!="); + const rhs_node = try transCreateNodeInt(rp.c, 0); + return transCreateNodeInfixOp(rp, scope, &builtin_node.base, .BangEqual, op_token, rhs_node, used, grouped); }, - .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, - - else => {}, - } + const named_type = ZigClangElaboratedType_getNamedType(elaborated_ty); + return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(named_type), node, used, grouped); }, - - .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, - - else => unreachable, + else => {}, } - - unreachable; + return revertAndWarn(rp, error.UnsupportedType, loc, "unsupported bool expression type", .{}); } fn transIntegerLiteral( @@ -2036,7 +1801,6 @@ fn transSwitch( const switch_block = try transCreateNodeBlock(rp.c, null); try switch_block.statements.push(&switch_node.base); switch_scope.pending_block = switch_block; - const last = try transStmt(rp, &block_scope.base, ZigClangSwitchStmt_getBody(stmt), .unused, .r_value); _ = try appendToken(rp.c, .Semicolon, ";"); @@ -2085,7 +1849,6 @@ fn transCase( } else try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value); - const switch_prong = try transCreateNodeSwitchCase(rp.c, expr); switch_prong.expr = &(try transCreateNodeBreak(rp.c, label)).base; _ = try appendToken(rp.c, .Comma, ","); @@ -2271,7 +2034,7 @@ fn transConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigCla const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); grouped_expr.* = .{ .lparen = lparen, - .expr = &if_node.base, + .expr = &if_node.base, .rparen = rparen, }; return maybeSuppressResult(rp, scope, used, &grouped_expr.base); @@ -2670,87 +2433,64 @@ fn transCreateNodePrefixOp( return node; } -fn transCreateNodeInfixOpImpl( +fn transCreateNodeInfixOp( rp: RestorePoint, scope: *Scope, lhs_node: *ast.Node, - rhs_node: *ast.Node, op: ast.Node.InfixOp.Op, - op_tok_id: std.zig.Token.Id, - bytes: []const u8, + op_token: ast.TokenIndex, + rhs_node: *ast.Node, + used: ResultUsed, grouped: bool, ) !*ast.Node { - const lparen = if (grouped) try appendToken(rp.c, .LParen, "(") else undefined; - const op_token = try appendToken(rp.c, op_tok_id, bytes); + var lparen = if (grouped) + try appendToken(rp.c, .LParen, "(") + else + null; const node = try rp.c.a().create(ast.Node.InfixOp); - node.* = ast.Node.InfixOp{ + node.* = .{ .op_token = op_token, .lhs = lhs_node, .op = op, .rhs = rhs_node, }; - if (!grouped) return &node.base; + if (!grouped) return maybeSuppressResult(rp, scope, used, &node.base); const rparen = try appendToken(rp.c, .RParen, ")"); const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); grouped_expr.* = .{ - .lparen = lparen, + .lparen = lparen.?, .expr = &node.base, .rparen = rparen, }; - 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); + return maybeSuppressResult(rp, scope, used, &grouped_expr.base); } 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, + op: ast.Node.InfixOp.Op, + used: ResultUsed, + grouped: bool, ) !*ast.Node { - if (!(op == .BoolAnd or op == .BoolOr)) { - @compileError("op must be either .BoolAnd or .BoolOr"); - } + std.debug.assert(op == .BoolAnd or op == .BoolOr); - return transCreateNodeInfixOpImpl( + const lhs_hode = try transBoolExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value, true); + const op_token = if (op == .BoolAnd) + try appendToken(rp.c, .Keyword_and, "and") + else + try appendToken(rp.c, .Keyword_or, "or"); + const rhs = try transBoolExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value, true); + + return transCreateNodeInfixOp( rp, scope, - try transBoolExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .r_value), - try transBoolExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value), + lhs_hode, op, - op_tok_id, - bytes, - true, + op_token, + rhs, + used, + grouped, ); } @@ -3149,16 +2889,14 @@ fn transCreateNodeShiftOp( 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"); - } + std.debug.assert(op == .BitShiftLeft or op == .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, .r_value); + 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"); @@ -3166,13 +2904,13 @@ fn transCreateNodeShiftOp( try as_node.params.push(rhs_type); _ = try appendToken(rp.c, .Comma, ","); const rhs = try transExpr(rp, scope, rhs_expr, .used, .r_value); - try as_node.params.push(rhs.node); + try as_node.params.push(rhs); 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, + .lhs = lhs, .op = op, .rhs = &as_node.base, }; @@ -3648,26 +3386,26 @@ fn isZigPrimitiveType(name: []const u8) bool { } // void is invalid in c so it doesn't need to be checked. return std.mem.eql(u8, name, "comptime_float") or - std.mem.eql(u8, name, "comptime_int") or - std.mem.eql(u8, name, "bool") or - std.mem.eql(u8, name, "isize") or - std.mem.eql(u8, name, "usize") or - std.mem.eql(u8, name, "f16") or - std.mem.eql(u8, name, "f32") or - std.mem.eql(u8, name, "f64") or - std.mem.eql(u8, name, "f128") or - std.mem.eql(u8, name, "c_longdouble") or - std.mem.eql(u8, name, "noreturn") or - std.mem.eql(u8, name, "type") or - std.mem.eql(u8, name, "anyerror") or - std.mem.eql(u8, name, "c_short") or - std.mem.eql(u8, name, "c_ushort") or - std.mem.eql(u8, name, "c_int") or - std.mem.eql(u8, name, "c_uint") or - std.mem.eql(u8, name, "c_long") or - std.mem.eql(u8, name, "c_ulong") or - std.mem.eql(u8, name, "c_longlong") or - std.mem.eql(u8, name, "c_ulonglong"); + std.mem.eql(u8, name, "comptime_int") or + std.mem.eql(u8, name, "bool") or + std.mem.eql(u8, name, "isize") or + std.mem.eql(u8, name, "usize") or + std.mem.eql(u8, name, "f16") or + std.mem.eql(u8, name, "f32") or + std.mem.eql(u8, name, "f64") or + std.mem.eql(u8, name, "f128") or + std.mem.eql(u8, name, "c_longdouble") or + std.mem.eql(u8, name, "noreturn") or + std.mem.eql(u8, name, "type") or + std.mem.eql(u8, name, "anyerror") or + std.mem.eql(u8, name, "c_short") or + std.mem.eql(u8, name, "c_ushort") or + std.mem.eql(u8, name, "c_int") or + std.mem.eql(u8, name, "c_uint") or + std.mem.eql(u8, name, "c_long") or + std.mem.eql(u8, name, "c_ulong") or + std.mem.eql(u8, name, "c_longlong") or + std.mem.eql(u8, name, "c_ulonglong"); } fn isValidZigIdentifier(name: []const u8) bool { diff --git a/test/translate_c.zig b/test/translate_c.zig index 18e806c280..3da0660acc 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -531,33 +531,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add_2("qualified struct and enum", - \\struct Foo { - \\ int x; - \\ int y; - \\}; - \\enum Bar { - \\ BarA, - \\ BarB, - \\}; - \\void func(struct Foo *a, enum Bar **b); - , &[_][]const u8{ - \\pub const struct_Foo = extern struct { - \\ x: c_int, - \\ y: c_int, - \\}; - \\pub const BarA = enum_Bar.A; - \\pub const BarB = enum_Bar.B; - \\pub const enum_Bar = extern enum { - \\ A, - \\ B, - \\}; - \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void; - , - \\pub const Foo = struct_Foo; - \\pub const Bar = enum_Bar; - }); - cases.add_both("constant size array", \\void func(int array[20]); , &[_][]const u8{ @@ -1388,17 +1361,109 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ 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 k: c_int = ((a != 0) or (@enumToInt(@as(c_uint, d)) != 0)); + \\ var l: c_int = ((@enumToInt(@as(c_uint, d)) != 0) and (b != 0)); + \\ var m: c_int = ((c != null) or (@enumToInt(@as(c_uint, d)) != 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.add_2("qualified struct and enum", + \\struct Foo { + \\ int x; + \\ int y; + \\}; + \\enum Bar { + \\ BarA, + \\ BarB, + \\}; + \\void func(struct Foo *a, enum Bar **b); + , &[_][]const u8{ + \\pub const struct_Foo = extern struct { + \\ x: c_int, + \\ y: c_int, + \\}; + \\pub const BarA = enum_Bar.A; + \\pub const BarB = enum_Bar.B; + \\pub const enum_Bar = extern enum { + \\ A, + \\ B, + \\}; + \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void; + , + \\pub const Foo = struct_Foo; + \\pub const Bar = enum_Bar; + }); + + 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.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.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.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 for only stage1 which are TODO items for stage2 //////////////// cases.addAllowWarnings("simple data types", @@ -1525,21 +1590,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return a; \\} }); - - 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); @@ -1550,20 +1600,6 @@ 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) @@ -1580,30 +1616,6 @@ 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 non-bool values", // Note this gets cut off by extra C symbols being injected in middle: `pub const Foo = enum_Foo;` \\enum Foo { \\ FooA, @@ -1640,9 +1652,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ 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 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; \\} }); @@ -1760,18 +1772,6 @@ 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 62bfff5e8774413856d0c548f0fd73a4b5134f7f Mon Sep 17 00:00:00 2001 From: Vexu Date: Wed, 18 Dec 2019 13:30:34 +0200 Subject: [PATCH 19/37] translate-c-2 fix expression grouping bugs --- lib/std/zig/render.zig | 2 +- src-self-hosted/translate_c.zig | 61 ++++++++++++---------- test/translate_c.zig | 91 +++++++++++++++++++-------------- 3 files changed, 88 insertions(+), 66 deletions(-) diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 41b8c48b10..b7d64eda68 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -1629,7 +1629,7 @@ fn renderExpression( .If => { const if_node = @fieldParentPtr(ast.Node.If, "base", base); - const lparen = tree.prevToken(if_node.condition.firstToken()); + const lparen = tree.nextToken(if_node.if_token); const rparen = tree.nextToken(if_node.condition.lastToken()); try renderToken(tree, stream, if_node.if_token, indent, start_col, Space.Space); // if diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 438feed7a1..4f8aa97bda 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -954,13 +954,6 @@ fn transBinaryOperator( } const lhs_node = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value); switch (op) { - .PtrMemD, .PtrMemI, .Cmp => return revertAndWarn( - rp, - error.UnsupportedTranslation, - ZigClangBinaryOperator_getBeginLoc(stmt), - "TODO: handle more C binary operators: {}", - .{op}, - ), .Add => { if (cIsUnsignedInteger(qt)) { op_token = try appendToken(rp.c, .PlusPercent, "+%"); @@ -1205,6 +1198,26 @@ fn transBoolExpr( undefined; var res = try transExpr(rp, scope, expr, used, lrvalue); + if (isBoolRes(res)) + return res; + const ty = ZigClangQualType_getTypePtr(getExprQualTypeBeforeImplicitCast(rp.c, expr)); + const node = try finishBoolExpr(rp, scope, ZigClangExpr_getBeginLoc(expr), ty, res, used); + + if (grouped) { + const rparen = try appendToken(rp.c, .RParen, ")"); + const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); + grouped_expr.* = .{ + .lparen = lparen, + .expr = node, + .rparen = rparen, + }; + return maybeSuppressResult(rp, scope, used, &grouped_expr.base); + } else { + return maybeSuppressResult(rp, scope, used, node); + } +} + +fn isBoolRes(res: *ast.Node) bool { switch (res.id) { .InfixOp => switch (@fieldParentPtr(ast.Node.InfixOp, "base", res).op) { .BoolOr, @@ -1215,23 +1228,20 @@ fn transBoolExpr( .GreaterThan, .LessOrEqual, .GreaterOrEqual, - => return res, + => return true, else => {}, }, - .PrefixOp => switch (@fieldParentPtr(ast.Node.PrefixOp, "base", res).op) { - .BoolNot => return res, + .BoolNot => return true, else => {}, }, - - .BoolLiteral => return res, - + .BoolLiteral => return true, + .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr), else => {}, } - const ty = ZigClangQualType_getTypePtr(getExprQualTypeBeforeImplicitCast(rp.c, expr)); - return finishBoolExpr(rp, scope, ZigClangExpr_getBeginLoc(expr), ty, res, used, grouped); + return false; } fn finishBoolExpr( @@ -1241,14 +1251,13 @@ fn finishBoolExpr( ty: *const ZigClangType, node: *ast.Node, used: ResultUsed, - grouped: bool, ) TransError!*ast.Node { switch (ZigClangType_getTypeClass(ty)) { .Builtin => { const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty); switch (ZigClangBuiltinType_getKind(builtin_ty)) { - .Bool, + .Bool => return node, .Char_U, .UChar, .Char_S, @@ -1276,12 +1285,12 @@ fn finishBoolExpr( => { const op_token = try appendToken(rp.c, .BangEqual, "!="); const rhs_node = try transCreateNodeInt(rp.c, 0); - return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, grouped); + return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false); }, .NullPtr => { const op_token = try appendToken(rp.c, .EqualEqual, "=="); const rhs_node = try transCreateNodeNullLiteral(rp.c); - return transCreateNodeInfixOp(rp, scope, node, .EqualEqual, op_token, rhs_node, used, grouped); + return transCreateNodeInfixOp(rp, scope, node, .EqualEqual, op_token, rhs_node, used, false); }, else => {}, } @@ -1289,13 +1298,13 @@ fn finishBoolExpr( .Pointer => { const op_token = try appendToken(rp.c, .BangEqual, "!="); const rhs_node = try transCreateNodeNullLiteral(rp.c); - return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, grouped); + return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false); }, .Typedef => { const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty); const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty); const underlying_type = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl); - return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(underlying_type), node, used, grouped); + return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(underlying_type), node, used); }, .Enum => { const enum_ty = @ptrCast(*const ZigClangEnumType, ty); @@ -1305,12 +1314,12 @@ fn finishBoolExpr( const op_token = try appendToken(rp.c, .BangEqual, "!="); const rhs_node = try transCreateNodeInt(rp.c, 0); - return transCreateNodeInfixOp(rp, scope, &builtin_node.base, .BangEqual, op_token, rhs_node, used, grouped); + return transCreateNodeInfixOp(rp, scope, &builtin_node.base, .BangEqual, op_token, rhs_node, used, false); }, .Elaborated => { const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty); const named_type = ZigClangElaboratedType_getNamedType(elaborated_ty); - return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(named_type), node, used, grouped); + return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(named_type), node, used); }, else => {}, } @@ -2009,8 +2018,8 @@ fn transFloatingLiteral(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangFl } fn transConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangConditionalOperator, used: ResultUsed) TransError!*ast.Node { - const gropued = scope.id == .Condition; - const lparen = if (gropued) try appendToken(rp.c, .LParen, "(") else undefined; + const grouped = scope.id == .Condition; + const lparen = if (grouped) try appendToken(rp.c, .LParen, "(") else undefined; const if_node = try transCreateNodeIf(rp.c); var cond_scope = Scope{ .parent = scope, @@ -2029,7 +2038,7 @@ fn transConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigCla if_node.@"else" = try transCreateNodeElse(rp.c); if_node.@"else".?.body = try transExpr(rp, scope, false_expr, .used, .r_value); - if (gropued) { + if (grouped) { const rparen = try appendToken(rp.c, .RParen, ")"); const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); grouped_expr.* = .{ diff --git a/test/translate_c.zig b/test/translate_c.zig index 3da0660acc..e925e384fa 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1400,17 +1400,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const Bar = enum_Bar; }); - 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 + cases.add_2("bitwise binary operators, simpler parens", \\int max(int a, int b) { - \\ int c = (a & b); - \\ int d = (a | b); - \\ return (c ^ d); + \\ return (a & b) ^ (a | b); \\} , &[_][]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); + \\ return ((a & b) ^ (a | b)); \\} }); @@ -1438,17 +1434,19 @@ 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 + cases.add_2("==, !=", \\int max(int a, int b) { - \\ int c = (a == b); - \\ int d = (a != b); - \\ return (c != d); + \\ if (a == b) + \\ return a; + \\ if (a != b) + \\ return b; + \\ return a; \\} , &[_][]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); + \\ if ((a == b)) return a; + \\ if ((a != b)) return b; + \\ return a; \\} }); @@ -1464,6 +1462,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("typedeffed bool expression", + \\typedef char* yes; + \\void foo(void) { + \\ yes a; + \\ if (a) 2; + \\} + , &[_][]const u8{ + \\pub const yes = [*c]u8; + \\pub export fn foo() void { + \\ var a: yes = undefined; + \\ if (a != null) _ = 2; + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// cases.addAllowWarnings("simple data types", @@ -1575,31 +1587,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("==, !=", - \\int max(int a, int b) { - \\ if (a == b) - \\ return a; - \\ if (a != b) - \\ return b; - \\ return a; - \\} - , &[_][]const u8{ - \\pub export fn max(a: c_int, b: c_int) c_int { - \\ if (a == b) return a; - \\ if (a != b) return b; - \\ return a; - \\} - }); - cases.addC("bitwise binary operators", - \\int max(int a, int b) { - \\ return (a & b) ^ (a | b); - \\} - , &[_][]const u8{ - \\pub export fn max(a: c_int, b: c_int) c_int { - \\ return (a & b) ^ (a | b); - \\} - }); - cases.addC("logical and, logical or", \\int max(int a, int b) { \\ if (a < b || a == b) @@ -2596,4 +2583,30 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ } \\} }); + + cases.addC("==, !=", + \\int max(int a, int b) { + \\ if (a == b) + \\ return a; + \\ if (a != b) + \\ return b; + \\ return a; + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int, b: c_int) c_int { + \\ if (a == b) return a; + \\ if (a != b) return b; + \\ return a; + \\} + }); + + cases.addC("bitwise binary operators", + \\int max(int a, int b) { + \\ return (a & b) ^ (a | b); + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int, b: c_int) c_int { + \\ return (a & b) ^ (a | b); + \\} + }); } From e65b9e8f7bcc38943f48ed44560a22c62ca222ce Mon Sep 17 00:00:00 2001 From: Vexu Date: Wed, 18 Dec 2019 21:20:38 +0200 Subject: [PATCH 20/37] translate-c-2 stmt expr --- src-self-hosted/clang.zig | 3 ++ src-self-hosted/translate_c.zig | 38 ++++++++++++++++++++++--- test/translate_c.zig | 50 ++++++++++++++++++++++----------- 3 files changed, 71 insertions(+), 20 deletions(-) diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index 94d1928a4c..19b6146109 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -79,6 +79,7 @@ pub const ZigClangPreprocessingRecord = @OpaqueType(); pub const ZigClangFloatingLiteral = @OpaqueType(); pub const ZigClangConstantExpr = @OpaqueType(); pub const ZigClangCharacterLiteral = @OpaqueType(); +pub const ZigClangStmtExpr = @OpaqueType(); pub const ZigClangBO = extern enum { PtrMemD, @@ -1095,3 +1096,5 @@ pub extern fn ZigClangCharacterLiteral_getBeginLoc(*const ZigClangCharacterLiter pub extern fn ZigClangCharacterLiteral_getKind(*const ZigClangCharacterLiteral) ZigClangCharacterLiteral_CharacterKind; pub extern fn ZigClangCharacterLiteral_getValue(*const ZigClangCharacterLiteral) c_uint; +pub extern fn ZigClangStmtExpr_getSubStmt( *const ZigClangStmtExpr) *const ZigClangCompoundStmt; + diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 4f8aa97bda..60eafc3596 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -855,6 +855,7 @@ fn transStmt( .ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used), .PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const ZigClangPredefinedExpr, stmt), result_used), .CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, stmt), result_used), + .StmtExprClass => return transStmtExpr(rp, scope, @ptrCast(*const ZigClangStmtExpr, stmt), result_used), else => { return revertAndWarn( rp, @@ -1165,12 +1166,9 @@ fn transImplicitCastExpr( const src_type = getExprQualType(c, sub_expr); return transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node); }, - .FunctionToPointerDecay, .ArrayToPointerDecay => { + .LValueToRValue, .NoOp, .FunctionToPointerDecay, .ArrayToPointerDecay => { return maybeSuppressResult(rp, scope, result_used, sub_expr_node); }, - .LValueToRValue, .NoOp => { - return transExpr(rp, scope, sub_expr, .used, .r_value); - }, .NullToPointer => { return try transCreateNodeNullLiteral(rp.c); }, @@ -1960,6 +1958,38 @@ fn transCharLiteral( } } +fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr, used: ResultUsed) TransError!*ast.Node { + const comp = ZigClangStmtExpr_getSubStmt(stmt); + if (used == .unused) { + return transCompoundStmt(rp, scope, comp); + } + const lparen = try appendToken(rp.c, .LParen, "("); + const block_scope = try Scope.Block.init(rp.c, scope, "blk"); + const block = try transCreateNodeBlock(rp.c, "blk"); + block_scope.block_node = block; + + var it = ZigClangCompoundStmt_body_begin(comp); + const end_it = ZigClangCompoundStmt_body_end(comp); + while (it != end_it - 1) : (it += 1) { + const result = try transStmt(rp, &block_scope.base, it[0], .unused, .r_value); + if (result != &block.base) + try block.statements.push(result); + } + const break_node = try transCreateNodeBreak(rp.c, "blk"); + break_node.rhs = try transStmt(rp, &block_scope.base, it[0], .used, .r_value); + _ = try appendToken(rp.c, .Semicolon, ";"); + try block.statements.push(&break_node.base); + block.rbrace = try appendToken(rp.c, .RBrace, "}"); + const rparen = try appendToken(rp.c, .RParen, ")"); + const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); + grouped_expr.* = .{ + .lparen = lparen, + .expr = &block.base, + .rparen = rparen, + }; + return maybeSuppressResult(rp, scope, used, &grouped_expr.base); +} + fn transCPtrCast( rp: RestorePoint, loc: ZigClangSourceLocation, diff --git a/test/translate_c.zig b/test/translate_c.zig index e925e384fa..475e066845 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1476,6 +1476,24 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("statement expression", + \\int foo(void) { + \\ return ({ + \\ int a = 1; + \\ a; + \\ a; + \\ }); + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ return (blk: { + \\ var a: c_int = 1; + \\ _ = a; + \\ break :blk a; + \\ }); + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// cases.addAllowWarnings("simple data types", @@ -1723,22 +1741,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("statement expression", - \\int foo(void) { - \\ return ({ - \\ int a = 1; - \\ a; - \\ }); - \\} - , &[_][]const u8{ - \\pub export fn foo() c_int { - \\ return x: { - \\ var a: c_int = 1; - \\ break :x a; - \\ }; - \\} - }); - cases.addC("__extension__ cast", \\int foo(void) { \\ return __extension__ 1; @@ -2609,4 +2611,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return (a & b) ^ (a | b); \\} }); + + cases.addC("statement expression", + \\int foo(void) { + \\ return ({ + \\ int a = 1; + \\ a; + \\ }); + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ return x: { + \\ var a: c_int = 1; + \\ break :x a; + \\ }; + \\} + }); } From cf7a5b7a4a77181a4c90fc9a92df496cfb48c3cd Mon Sep 17 00:00:00 2001 From: Vexu Date: Wed, 18 Dec 2019 22:29:42 +0200 Subject: [PATCH 21/37] translate-c-2 member access --- src-self-hosted/c_tokenizer.zig | 108 ++++++++++++++++---------------- src-self-hosted/clang.zig | 6 +- src-self-hosted/translate_c.zig | 84 ++++++++++++++----------- test/translate_c.zig | 61 +++++++++++++----- 4 files changed, 154 insertions(+), 105 deletions(-) diff --git a/src-self-hosted/c_tokenizer.zig b/src-self-hosted/c_tokenizer.zig index 9715661cd3..29a2d34773 100644 --- a/src-self-hosted/c_tokenizer.zig +++ b/src-self-hosted/c_tokenizer.zig @@ -27,6 +27,7 @@ pub const CToken = struct { Lt, Comma, Fn, + Arrow, }; pub const NumLitSuffix = enum { @@ -164,6 +165,8 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { NumLitIntSuffixL, NumLitIntSuffixLL, NumLitIntSuffixUL, + Minus, + Done, } = .Start; var result = CToken{ @@ -178,9 +181,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { const c = chars[i.*]; if (c == 0) { switch (state) { - .Start => { - return result; - }, .Identifier, .Decimal, .Hex, @@ -193,6 +193,9 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { result.bytes = chars[begin_index..i.*]; return result; }, + .Start, + .Minus, + .Done, .NumLitIntSuffixU, .NumLitIntSuffixL, .NumLitIntSuffixUL, @@ -212,7 +215,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { => return error.TokenizingFailed, } } - i.* += 1; switch (state) { .Start => { switch (c) { @@ -220,12 +222,12 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { '\'' => { state = .CharLit; result.id = .CharLit; - begin_index = i.* - 1; + begin_index = i.*; }, '\"' => { state = .String; result.id = .StrLit; - begin_index = i.* - 1; + begin_index = i.*; }, '/' => { state = .OpenComment; @@ -239,21 +241,21 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { 'a'...'z', 'A'...'Z', '_' => { state = .Identifier; result.id = .Identifier; - begin_index = i.* - 1; + begin_index = i.*; }, '1'...'9' => { state = .Decimal; result.id = .NumLitInt; - begin_index = i.* - 1; + begin_index = i.*; }, '0' => { state = .GotZero; result.id = .NumLitInt; - begin_index = i.* - 1; + begin_index = i.*; }, '.' => { result.id = .Dot; - return result; + state = .Done; }, '<' => { result.id = .Lt; @@ -261,40 +263,52 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { }, '(' => { result.id = .LParen; - return result; + state = .Done; }, ')' => { result.id = .RParen; - return result; + state = .Done; }, '*' => { result.id = .Asterisk; - return result; + state = .Done; }, '-' => { + state = .Minus; result.id = .Minus; - return result; }, '!' => { result.id = .Bang; - return result; + state = .Done; }, '~' => { result.id = .Tilde; - return result; + state = .Done; }, ',' => { result.id = .Comma; - return result; + state = .Done; }, else => return error.TokenizingFailed, } }, + .Done => return result, + .Minus => { + switch (c) { + '>' => { + result.id = .Arrow; + state = .Done; + }, + else => { + return result; + }, + } + }, .GotLt => { switch (c) { '<' => { result.id = .Shl; - return result; + state = .Done; }, else => { return result; @@ -310,19 +324,16 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { 'f', 'F', => { - i.* -= 1; result.num_lit_suffix = .F; result.bytes = chars[begin_index..i.*]; - return result; + state = .Done; }, 'l', 'L' => { - i.* -= 1; result.num_lit_suffix = .L; result.bytes = chars[begin_index..i.*]; - return result; + state = .Done; }, else => { - i.* -= 1; result.bytes = chars[begin_index..i.*]; return result; }, @@ -352,16 +363,15 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { '0'...'9' => {}, 'f', 'F' => { result.num_lit_suffix = .F; - result.bytes = chars[begin_index .. i.* - 1]; - return result; + result.bytes = chars[begin_index..i.*]; + state = .Done; }, 'l', 'L' => { result.num_lit_suffix = .L; - result.bytes = chars[begin_index .. i.* - 1]; - return result; + result.bytes = chars[begin_index..i.*]; + state = .Done; }, else => { - i.* -= 1; result.bytes = chars[begin_index..i.*]; return result; }, @@ -374,19 +384,18 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { 'u', 'U' => { state = .NumLitIntSuffixU; result.num_lit_suffix = .U; - result.bytes = chars[begin_index .. i.* - 1]; + result.bytes = chars[begin_index..i.*]; }, 'l', 'L' => { state = .NumLitIntSuffixL; result.num_lit_suffix = .L; - result.bytes = chars[begin_index .. i.* - 1]; + result.bytes = chars[begin_index..i.*]; }, '.' => { result.id = .NumLitFloat; state = .Float; }, else => { - i.* -= 1; result.bytes = chars[begin_index..i.*]; return result; }, @@ -407,12 +416,12 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { 'u', 'U' => { state = .NumLitIntSuffixU; result.num_lit_suffix = .U; - result.bytes = chars[begin_index .. i.* - 1]; + result.bytes = chars[begin_index..i.*]; }, 'l', 'L' => { state = .NumLitIntSuffixL; result.num_lit_suffix = .L; - result.bytes = chars[begin_index .. i.* - 1]; + result.bytes = chars[begin_index..i.*]; }, else => { i.* -= 1; @@ -425,7 +434,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { '0'...'7' => {}, '8', '9' => return error.TokenizingFailed, else => { - i.* -= 1; result.bytes = chars[begin_index..i.*]; return result; }, @@ -438,16 +446,15 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { // marks the number literal as unsigned state = .NumLitIntSuffixU; result.num_lit_suffix = .U; - result.bytes = chars[begin_index .. i.* - 1]; + result.bytes = chars[begin_index..i.*]; }, 'l', 'L' => { // marks the number literal as long state = .NumLitIntSuffixL; result.num_lit_suffix = .L; - result.bytes = chars[begin_index .. i.* - 1]; + result.bytes = chars[begin_index..i.*]; }, else => { - i.* -= 1; result.bytes = chars[begin_index..i.*]; return result; }, @@ -461,16 +468,15 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { // marks the number literal as unsigned state = .NumLitIntSuffixU; result.num_lit_suffix = .U; - result.bytes = chars[begin_index .. i.* - 1]; + result.bytes = chars[begin_index..i.*]; }, 'l', 'L' => { // marks the number literal as long state = .NumLitIntSuffixL; result.num_lit_suffix = .L; - result.bytes = chars[begin_index .. i.* - 1]; + result.bytes = chars[begin_index..i.*]; }, else => { - i.* -= 1; result.bytes = chars[begin_index..i.*]; return result; }, @@ -483,7 +489,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { state = .NumLitIntSuffixUL; }, else => { - i.* -= 1; return result; }, } @@ -496,10 +501,9 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { }, 'u', 'U' => { result.num_lit_suffix = .LU; - return result; + state = .Done; }, else => { - i.* -= 1; return result; }, } @@ -508,10 +512,9 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { switch (c) { 'u', 'U' => { result.num_lit_suffix = .LLU; - return result; + state = .Done; }, else => { - i.* -= 1; return result; }, } @@ -523,7 +526,6 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { return result; }, else => { - i.* -= 1; return result; }, } @@ -532,17 +534,16 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { switch (c) { '_', 'a'...'z', 'A'...'Z', '0'...'9' => {}, else => { - i.* -= 1; result.bytes = chars[begin_index..i.*]; return result; }, } }, - .String => { // TODO char escapes + .String => { switch (c) { '\"' => { - result.bytes = chars[begin_index..i.*]; - return result; + result.bytes = chars[begin_index .. i.* + 1]; + state = .Done; }, else => {}, } @@ -550,8 +551,8 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { .CharLit => { switch (c) { '\'' => { - result.bytes = chars[begin_index..i.*]; - return result; + result.bytes = chars[begin_index .. i.* + 1]; + state = .Done; }, else => {}, } @@ -566,7 +567,7 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { }, else => { result.id = .Slash; - return result; + state = .Done; }, } }, @@ -598,6 +599,7 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { } }, } + i.* += 1; } unreachable; } diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index 19b6146109..9617802835 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -1096,5 +1096,9 @@ pub extern fn ZigClangCharacterLiteral_getBeginLoc(*const ZigClangCharacterLiter pub extern fn ZigClangCharacterLiteral_getKind(*const ZigClangCharacterLiteral) ZigClangCharacterLiteral_CharacterKind; pub extern fn ZigClangCharacterLiteral_getValue(*const ZigClangCharacterLiteral) c_uint; -pub extern fn ZigClangStmtExpr_getSubStmt( *const ZigClangStmtExpr) *const ZigClangCompoundStmt; +pub extern fn ZigClangStmtExpr_getSubStmt(*const ZigClangStmtExpr) *const ZigClangCompoundStmt; + +pub extern fn ZigClangMemberExpr_getBase(*const ZigClangMemberExpr) *const ZigClangExpr; +pub extern fn ZigClangMemberExpr_isArrow(*const ZigClangMemberExpr) bool; +pub extern fn ZigClangMemberExpr_getMemberDecl(*const ZigClangMemberExpr) *const ZigClangValueDecl; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 60eafc3596..8435629a7d 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -49,9 +49,6 @@ const Scope = struct { Root, Condition, FnDef, - - /// used when getting a member `a.b` - Ref, Loop, }; @@ -181,7 +178,6 @@ const Scope = struct { fn getAlias(scope: *Scope, name: []const u8) ?[]const u8 { return switch (scope.id) { .Root => null, - .Ref => null, .FnDef => @fieldParentPtr(FnDef, "base", scope).getAlias(name), .Block => @fieldParentPtr(Block, "base", scope).getAlias(name), .Switch, .Loop, .Condition => scope.parent.?.getAlias(name), @@ -190,7 +186,6 @@ const Scope = struct { fn contains(scope: *Scope, name: []const u8) bool { return switch (scope.id) { - .Ref => false, .Root => @fieldParentPtr(Root, "base", scope).contains(name), .FnDef => @fieldParentPtr(FnDef, "base", scope).contains(name), .Block => @fieldParentPtr(Block, "base", scope).contains(name), @@ -230,7 +225,6 @@ const Context = struct { decl_table: DeclTable, alias_list: AliasList, global_scope: *Scope.Root, - ptr_params: std.BufSet, clang_context: *ZigClangASTContext, mangle_count: u64 = 0, @@ -316,7 +310,6 @@ pub fn translate( .decl_table = DeclTable.init(arena), .alias_list = AliasList.init(arena), .global_scope = try arena.create(Scope.Root), - .ptr_params = std.BufSet.init(arena), .clang_context = ZigClangASTUnit_getASTContext(ast_unit).?, }; context.global_scope.* = Scope.Root.init(&context); @@ -856,6 +849,7 @@ fn transStmt( .PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const ZigClangPredefinedExpr, stmt), result_used), .CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, stmt), result_used), .StmtExprClass => return transStmtExpr(rp, scope, @ptrCast(*const ZigClangStmtExpr, stmt), result_used), + .MemberExprClass => return transMemberExpr(rp, scope, @ptrCast(*const ZigClangMemberExpr, stmt), result_used), else => { return revertAndWarn( rp, @@ -1147,7 +1141,6 @@ fn transDeclRefExpr( const value_decl = ZigClangDeclRefExpr_getDecl(expr); const name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, value_decl))); const checked_name = if (scope.getAlias(name)) |a| a else name; - if (lrvalue == .l_value) try rp.c.ptr_params.put(checked_name); return transCreateNodeIdentifier(rp.c, checked_name); } @@ -1990,6 +1983,18 @@ fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr, return maybeSuppressResult(rp, scope, used, &grouped_expr.base); } +fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangMemberExpr, result_used: ResultUsed) TransError!*ast.Node { + var container_node = try transExpr(rp, scope, ZigClangMemberExpr_getBase(stmt), .used, .r_value); + + if (ZigClangMemberExpr_isArrow(stmt)) { + container_node = try transCreateNodePtrDeref(rp.c, container_node); + } + + const name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, ZigClangMemberExpr_getMemberDecl(stmt)))); + const node = try transCreateNodeFieldAccess(rp.c, container_node, name); + return maybeSuppressResult(rp, scope, result_used, node); +} + fn transCPtrCast( rp: RestorePoint, loc: ZigClangSourceLocation, @@ -2213,8 +2218,8 @@ fn qualTypeToLog2IntRef(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigC 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); + const outer_field_access = try transCreateNodeFieldAccess(rp.c, inner_field_access, "Log2Int"); + const log2int_fn_call = try transCreateNodeFnCall(rp.c, outer_field_access); 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, ")"); @@ -2446,7 +2451,7 @@ 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 { +fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node { const field_access_node = try c.a().create(ast.Node.InfixOp); field_access_node.* = .{ .op_token = try appendToken(c, .Period, "."), @@ -2454,7 +2459,7 @@ fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []c .op = .Period, .rhs = try transCreateNodeIdentifier(c, field_name), }; - return field_access_node; + return &field_access_node.base; } fn transCreateNodePrefixOp( @@ -2924,9 +2929,9 @@ 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, + op: ast.Node.InfixOp.Op, + op_tok_id: std.zig.Token.Id, + bytes: []const u8, ) !*ast.Node { std.debug.assert(op == .BitShiftLeft or op == .BitShiftRight); @@ -2957,6 +2962,16 @@ fn transCreateNodeShiftOp( return &node.base; } +fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node { + const node = try c.a().create(ast.Node.SuffixOp); + node.* = .{ + .lhs = .{ .node = lhs }, + .op = .Deref, + .rtoken = try appendToken(c, .PeriodAsterisk, ".*"), + }; + return &node.base; +} + const RestorePoint = struct { c: *Context, token_index: ast.TokenIndex, @@ -3862,16 +3877,21 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc .{}, ); - const op_token = try appendToken(rp.c, .Period, "."); - const rhs = try transCreateNodeIdentifier(rp.c, name_tok.bytes); - const access_node = try rp.c.a().create(ast.Node.InfixOp); - access_node.* = .{ - .op_token = op_token, - .lhs = node, - .op = .Period, - .rhs = rhs, - }; - node = &access_node.base; + node = try transCreateNodeFieldAccess(rp.c, node, name_tok.bytes); + }, + .Arrow => { + const name_tok = it.next().?; + if (name_tok.id != .Identifier) + return revertAndWarn( + rp, + error.ParseError, + source_loc, + "unable to translate C expr", + .{}, + ); + + const deref = try transCreateNodePtrDeref(rp.c, node); + node = try transCreateNodeFieldAccess(rp.c, deref, name_tok.bytes); }, .Asterisk => { if (it.peek().?.id == .RParen) { @@ -3887,14 +3907,14 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc // expr * expr const op_token = try appendToken(rp.c, .Asterisk, "*"); const rhs = try parseCPrimaryExpr(rp, it, source_loc, scope); - const bitshift_node = try rp.c.a().create(ast.Node.InfixOp); - bitshift_node.* = .{ + const mul_node = try rp.c.a().create(ast.Node.InfixOp); + mul_node.* = .{ .op_token = op_token, .lhs = node, .op = .BitShiftLeft, .rhs = rhs, }; - node = &bitshift_node.base; + node = &mul_node.base; } }, .Shl => { @@ -3938,13 +3958,7 @@ fn parseCPrefixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc }, .Asterisk => { const prefix_op_expr = try parseCPrefixOpExpr(rp, it, source_loc, scope); - const node = try rp.c.a().create(ast.Node.SuffixOp); - node.* = .{ - .lhs = .{ .node = prefix_op_expr }, - .op = .Deref, - .rtoken = try appendToken(rp.c, .PeriodAsterisk, ".*"), - }; - return &node.base; + return try transCreateNodePtrDeref(rp.c, prefix_op_expr); }, else => { _ = it.prev(); diff --git a/test/translate_c.zig b/test/translate_c.zig index 475e066845..05f3f1960f 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1494,6 +1494,35 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("field access expression", + \\#define ARROW a->b + \\#define DOT a.b + \\extern struct Foo { + \\ int b; + \\}a; + \\float b = 2.0f; + \\int foo(void) { + \\ struct Foo *c; + \\ a.b; + \\ c->b; + \\} + , &[_][]const u8{ + \\pub const struct_Foo = extern struct { + \\ b: c_int, + \\}; + \\pub extern var a: struct_Foo; + \\pub export var b: f32 = 2; + \\pub export fn foo() c_int { + \\ var c: [*c]struct_Foo = undefined; + \\ _ = a.b; + \\ _ = c.*.b; + \\} + , + \\pub const DOT = a.b; + , + \\pub const ARROW = a.*.b; + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// cases.addAllowWarnings("simple data types", @@ -1702,22 +1731,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("field access expression", - \\struct Foo { - \\ int field; - \\}; - \\int read_field(struct Foo *foo) { - \\ return foo->field; - \\} - , &[_][]const u8{ - \\pub const struct_Foo = extern struct { - \\ field: c_int, - \\}; - \\pub export fn read_field(foo: [*c]struct_Foo) c_int { - \\ return foo.*.field; - \\} - }); - cases.addC("array access", \\int array[100]; \\int foo(int index) { @@ -2627,4 +2640,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ }; \\} }); + + cases.addC("field access expression", + \\struct Foo { + \\ int field; + \\}; + \\int read_field(struct Foo *foo) { + \\ return foo->field; + \\} + , &[_][]const u8{ + \\pub const struct_Foo = extern struct { + \\ field: c_int, + \\}; + \\pub export fn read_field(foo: [*c]struct_Foo) c_int { + \\ return foo.*.field; + \\} + }); } From c2666c48a4cf5c0576cf846171744b53f39acb80 Mon Sep 17 00:00:00 2001 From: Vexu Date: Wed, 18 Dec 2019 22:57:53 +0200 Subject: [PATCH 22/37] translate-c-2 array access --- src-self-hosted/c_tokenizer.zig | 10 +++++++ src-self-hosted/clang.zig | 3 ++ src-self-hosted/translate_c.zig | 52 ++++++++++++++++++++++----------- test/translate_c.zig | 39 +++++++++++++++++-------- 4 files changed, 75 insertions(+), 29 deletions(-) diff --git a/src-self-hosted/c_tokenizer.zig b/src-self-hosted/c_tokenizer.zig index 29a2d34773..7685cdc537 100644 --- a/src-self-hosted/c_tokenizer.zig +++ b/src-self-hosted/c_tokenizer.zig @@ -28,6 +28,8 @@ pub const CToken = struct { Comma, Fn, Arrow, + LBrace, + RBrace, }; pub const NumLitSuffix = enum { @@ -289,6 +291,14 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { result.id = .Comma; state = .Done; }, + '[' => { + result.id = .LBrace; + state = .Done; + }, + ']' => { + result.id = .RBrace; + state = .Done; + }, else => return error.TokenizingFailed, } }, diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index 9617802835..a3ad42225a 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -1102,3 +1102,6 @@ pub extern fn ZigClangMemberExpr_getBase(*const ZigClangMemberExpr) *const ZigCl pub extern fn ZigClangMemberExpr_isArrow(*const ZigClangMemberExpr) bool; pub extern fn ZigClangMemberExpr_getMemberDecl(*const ZigClangMemberExpr) *const ZigClangValueDecl; +pub extern fn ZigClangArraySubscriptExpr_getBase(*const ZigClangArraySubscriptExpr) *const ZigClangExpr; +pub extern fn ZigClangArraySubscriptExpr_getIdx(*const ZigClangArraySubscriptExpr) *const ZigClangExpr; + diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 8435629a7d..e13e28608b 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -850,6 +850,7 @@ fn transStmt( .CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const ZigClangCharacterLiteral, stmt), result_used), .StmtExprClass => return transStmtExpr(rp, scope, @ptrCast(*const ZigClangStmtExpr, stmt), result_used), .MemberExprClass => return transMemberExpr(rp, scope, @ptrCast(*const ZigClangMemberExpr, stmt), result_used), + .ArraySubscriptExprClass => return transArrayAccess(rp, scope, @ptrCast(*const ZigClangArraySubscriptExpr, stmt), result_used), else => { return revertAndWarn( rp, @@ -1523,7 +1524,7 @@ fn transInitListExpr( var cat_tok: ast.TokenIndex = undefined; if (init_count != 0) { const dot_tok = try appendToken(rp.c, .Period, "."); - init_node = try transCreateNodeArrayInitializer(rp.c, dot_tok); + init_node = try transCreateNodeContainerInitializer(rp.c, dot_tok); var i: c_uint = 0; while (i < init_count) : (i += 1) { const elem_expr = ZigClangInitListExpr_getInit(expr, i); @@ -1538,7 +1539,7 @@ fn transInitListExpr( } const dot_tok = try appendToken(rp.c, .Period, "."); - var filler_init_node = try transCreateNodeArrayInitializer(rp.c, dot_tok); + var filler_init_node = try transCreateNodeContainerInitializer(rp.c, dot_tok); const filler_val_expr = ZigClangInitListExpr_getArrayFiller(expr); try filler_init_node.op.ArrayInitializer.push(try transExpr(rp, scope, filler_val_expr, .used, .r_value)); filler_init_node.rtoken = try appendToken(rp.c, .RBrace, "}"); @@ -1995,6 +1996,14 @@ fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangMemberE return maybeSuppressResult(rp, scope, result_used, node); } +fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangArraySubscriptExpr, result_used: ResultUsed) TransError!*ast.Node { + const container_node = try transExpr(rp, scope, ZigClangArraySubscriptExpr_getBase(stmt), .used, .r_value); + const node = try transCreateNodeArrayAccess(rp.c, container_node); + node.op.ArrayAccess = try transExpr(rp, scope, ZigClangArraySubscriptExpr_getIdx(stmt), .used, .r_value); + node.rtoken = try appendToken(rp.c, .RBrace, "]"); + return maybeSuppressResult(rp, scope, result_used, &node.base); +} + fn transCPtrCast( rp: RestorePoint, loc: ZigClangSourceLocation, @@ -2643,7 +2652,7 @@ fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node { return &node.base; } -fn transCreateNodeArrayInitializer(c: *Context, dot_tok: ast.TokenIndex) !*ast.Node.SuffixOp { +fn transCreateNodeContainerInitializer(c: *Context, dot_tok: ast.TokenIndex) !*ast.Node.SuffixOp { _ = try appendToken(c, .LBrace, "{"); const node = try c.a().create(ast.Node.SuffixOp); node.* = ast.Node.SuffixOp{ @@ -2972,6 +2981,19 @@ fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node { return &node.base; } +fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.SuffixOp { + _ = try appendToken(c, .LBrace, "["); + const node = try c.a().create(ast.Node.SuffixOp); + node.* = .{ + .lhs = .{ .node = lhs }, + .op = .{ + .ArrayAccess = undefined, + }, + .rtoken = undefined, + }; + return node; +} + const RestorePoint = struct { c: *Context, token_index: ast.TokenIndex, @@ -3869,26 +3891,14 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc .Dot => { const name_tok = it.next().?; if (name_tok.id != .Identifier) - return revertAndWarn( - rp, - error.ParseError, - source_loc, - "unable to translate C expr", - .{}, - ); + return error.ParseError; node = try transCreateNodeFieldAccess(rp.c, node, name_tok.bytes); }, .Arrow => { const name_tok = it.next().?; if (name_tok.id != .Identifier) - return revertAndWarn( - rp, - error.ParseError, - source_loc, - "unable to translate C expr", - .{}, - ); + return error.ParseError; const deref = try transCreateNodePtrDeref(rp.c, node); node = try transCreateNodeFieldAccess(rp.c, deref, name_tok.bytes); @@ -3929,6 +3939,14 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc }; node = &bitshift_node.base; }, + .LBrace => { + const arr_node = try transCreateNodeArrayAccess(rp.c, node); + arr_node.op.ArrayAccess = try parseCPrimaryExpr(rp, it, source_loc, scope); + arr_node.rtoken = try appendToken(rp.c, .RBrace, "]"); + node = &arr_node.base; + if (it.next().?.id != .RBrace) + return error.ParseError; + }, else => { _ = it.prev(); return node; diff --git a/test/translate_c.zig b/test/translate_c.zig index 05f3f1960f..d34bfd9e8a 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1523,6 +1523,21 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const ARROW = a.*.b; }); + cases.add_2("array access", + \\#define ACCESS array[2] + \\int array[100] = {}; + \\int foo(int index) { + \\ return array[index]; + \\} + , &[_][]const u8{ + \\pub export var array: [100]c_int = .{0} ** 100; + \\pub export fn foo(index: c_int) c_int { + \\ return array[index]; + \\} + , + \\pub const ACCESS = array[2]; + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// cases.addAllowWarnings("simple data types", @@ -1731,18 +1746,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("array access", - \\int array[100]; - \\int foo(int index) { - \\ return array[index]; - \\} - , &[_][]const u8{ - \\pub var array: [100]c_int = undefined; - \\pub export fn foo(index: c_int) c_int { - \\ return array[index]; - \\} - }); - cases.addC("sizeof", \\#include \\size_t size_of(void) { @@ -2656,4 +2659,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return foo.*.field; \\} }); + + cases.addC("array access", + \\int array[100]; + \\int foo(int index) { + \\ return array[index]; + \\} + , &[_][]const u8{ + \\pub var array: [100]c_int = undefined; + \\pub export fn foo(index: c_int) c_int { + \\ return array[index]; + \\} + }); } From d54c288bd351adb84dd98b26848687d66cb725dc Mon Sep 17 00:00:00 2001 From: Vexu Date: Wed, 18 Dec 2019 23:56:39 +0200 Subject: [PATCH 23/37] translate-c-2 function calls --- src-self-hosted/clang.zig | 6 +++ src-self-hosted/translate_c.zig | 84 +++++++++++++++++++++++++++++++++ test/translate_c.zig | 40 +++++++++------- 3 files changed, 112 insertions(+), 18 deletions(-) diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index a3ad42225a..bf7adce1e0 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -839,6 +839,7 @@ pub extern fn ZigClangFunctionType_getReturnType(self: *const ZigClangFunctionTy pub extern fn ZigClangFunctionProtoType_isVariadic(self: *const struct_ZigClangFunctionProtoType) bool; pub extern fn ZigClangFunctionProtoType_getNumParams(self: *const struct_ZigClangFunctionProtoType) c_uint; pub extern fn ZigClangFunctionProtoType_getParamType(self: *const struct_ZigClangFunctionProtoType, i: c_uint) ZigClangQualType; +pub extern fn ZigClangFunctionProtoType_getReturnType(self: *const ZigClangFunctionProtoType) ZigClangQualType; pub const ZigClangSourceLocation = struct_ZigClangSourceLocation; pub const ZigClangQualType = struct_ZigClangQualType; @@ -993,6 +994,7 @@ pub extern fn ZigClangIncompleteArrayType_getElementType(*const ZigClangIncomple pub extern fn ZigClangConstantArrayType_getElementType(self: *const struct_ZigClangConstantArrayType) ZigClangQualType; pub extern fn ZigClangConstantArrayType_getSize(self: *const struct_ZigClangConstantArrayType) *const struct_ZigClangAPInt; pub extern fn ZigClangDeclRefExpr_getDecl(*const ZigClangDeclRefExpr) *const ZigClangValueDecl; +pub extern fn ZigClangDeclRefExpr_getFoundDecl(*const ZigClangDeclRefExpr) *const ZigClangNamedDecl; pub extern fn ZigClangParenType_getInnerType(*const ZigClangParenType) ZigClangQualType; @@ -1105,3 +1107,7 @@ pub extern fn ZigClangMemberExpr_getMemberDecl(*const ZigClangMemberExpr) *const pub extern fn ZigClangArraySubscriptExpr_getBase(*const ZigClangArraySubscriptExpr) *const ZigClangExpr; pub extern fn ZigClangArraySubscriptExpr_getIdx(*const ZigClangArraySubscriptExpr) *const ZigClangExpr; +pub extern fn ZigClangCallExpr_getCallee(*const ZigClangCallExpr) *const ZigClangExpr; +pub extern fn ZigClangCallExpr_getNumArgs(*const ZigClangCallExpr) c_uint; +pub extern fn ZigClangCallExpr_getArgs(*const ZigClangCallExpr) [*]const *const ZigClangExpr; + diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index e13e28608b..2247c4dc91 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -851,6 +851,7 @@ fn transStmt( .StmtExprClass => return transStmtExpr(rp, scope, @ptrCast(*const ZigClangStmtExpr, stmt), result_used), .MemberExprClass => return transMemberExpr(rp, scope, @ptrCast(*const ZigClangMemberExpr, stmt), result_used), .ArraySubscriptExprClass => return transArrayAccess(rp, scope, @ptrCast(*const ZigClangArraySubscriptExpr, stmt), result_used), + .CallExprClass => return transCallExpr(rp, scope, @ptrCast(*const ZigClangCallExpr, stmt), result_used), else => { return revertAndWarn( rp, @@ -2004,6 +2005,73 @@ fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangArrayS return maybeSuppressResult(rp, scope, result_used, &node.base); } +fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCallExpr, result_used: ResultUsed) TransError!*ast.Node { + const callee = ZigClangCallExpr_getCallee(stmt); + var raw_fn_expr = try transExpr(rp, scope, callee, .used, .r_value); + + var is_ptr = false; + const fn_ty = qualTypeGetFnProto(ZigClangExpr_getType(callee), &is_ptr); + + const fn_expr = if (is_ptr and fn_ty != null) blk: { + if (ZigClangExpr_getStmtClass(callee) == .ImplicitCastExprClass) { + const implicit_cast = @ptrCast(*const ZigClangImplicitCastExpr, callee); + + if (ZigClangImplicitCastExpr_getCastKind(implicit_cast) == .FunctionToPointerDecay) { + const subexpr = ZigClangImplicitCastExpr_getSubExpr(implicit_cast); + if (ZigClangExpr_getStmtClass(subexpr) == .DeclRefExprClass) { + const decl_ref = @ptrCast(*const ZigClangDeclRefExpr, subexpr); + const named_decl = ZigClangDeclRefExpr_getFoundDecl(decl_ref); + if (ZigClangDecl_getKind(@ptrCast(*const ZigClangDecl, named_decl)) == .Function) { + break :blk raw_fn_expr; + } + } + } + } + break :blk try transCreateNodeUnwrapNull(rp.c, raw_fn_expr); + } else + raw_fn_expr; + const node = try transCreateNodeFnCall(rp.c, fn_expr); + + const num_args = ZigClangCallExpr_getNumArgs(stmt); + const args = ZigClangCallExpr_getArgs(stmt); + var i: usize = 0; + while (i < num_args) : (i+=1) { + if (i != 0) { + _ = try appendToken(rp.c, .Comma, ","); + } + const arg = try transExpr(rp, scope, args[i], .used, .r_value); + try node.op.Call.params.push(arg); + } + node.rtoken = try appendToken(rp.c, .RParen, ")"); + + if (fn_ty) |ty| { + const canon = ZigClangQualType_getCanonicalType(ZigClangFunctionProtoType_getReturnType(ty)); + const ret_ty = ZigClangQualType_getTypePtr(canon); + if (ZigClangType_isVoidType(ret_ty)) { + _ = try appendToken(rp.c, .Semicolon, ";"); + return &node.base; + } + } + + return maybeSuppressResult(rp, scope, result_used, &node.base); +} + +fn qualTypeGetFnProto(qt: ZigClangQualType, is_ptr: *bool) ?*const ZigClangFunctionProtoType { + const canon = ZigClangQualType_getCanonicalType(qt); + var ty = ZigClangQualType_getTypePtr(canon); + is_ptr.* = false; + + if (ZigClangType_getTypeClass(ty) == .Pointer) { + is_ptr.* = true; + const child_qt = ZigClangType_getPointeeType(ty); + ty = ZigClangQualType_getTypePtr(child_qt); + } + if (ZigClangType_getTypeClass(ty) == .FunctionProto) { + return @ptrCast(*const ZigClangFunctionProtoType, ty); + } + return null; +} + fn transCPtrCast( rp: RestorePoint, loc: ZigClangSourceLocation, @@ -3947,6 +4015,22 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc if (it.next().?.id != .RBrace) return error.ParseError; }, + .LParen => { + const call_node = try transCreateNodeFnCall(rp.c, node); + while (true) { + const arg = try parseCExpr(rp, it, source_loc, scope); + try call_node.op.Call.params.push(arg); + const next = it.next().?; + if (next.id == .Comma) + _ = try appendToken(rp.c, .Comma, ",") + else if (next.id == .RParen) + break + else + return error.ParseError; + } + call_node.rtoken = try appendToken(rp.c, .RParen, ")"); + node = &call_node.base; + }, else => { _ = it.prev(); return node; diff --git a/test/translate_c.zig b/test/translate_c.zig index d34bfd9e8a..7f349c54c4 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -702,6 +702,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.addC_both("function call", + \\static void bar(void) { } + \\void foo(int *(baz)(void)) { + \\ bar(); + \\ baz(); + \\} + , &[_][]const u8{ + \\pub fn bar() void {} + \\pub export fn foo(baz: ?extern fn () [*c]c_int) void { + \\ bar(); + \\ _ = baz.?(); + \\} + }); + /////////////// Cases that pass for only stage2 //////////////// cases.add_2("Parameterless function prototypes", @@ -1538,6 +1552,14 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const ACCESS = array[2]; }); + cases.add_2("macro call", + \\#define CALL(arg) bar(arg) + , &[_][]const u8{ + \\pub inline fn CALL(arg: var) @TypeOf(bar(arg)) { + \\ return bar(arg); + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// cases.addAllowWarnings("simple data types", @@ -1728,24 +1750,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("function call", - \\static void bar(void) { } - \\static int baz(void) { return 0; } - \\void foo(void) { - \\ bar(); - \\ baz(); - \\} - , &[_][]const u8{ - \\pub fn bar() void {} - \\pub fn baz() c_int { - \\ return 0; - \\} - \\pub export fn foo() void { - \\ bar(); - \\ _ = baz(); - \\} - }); - cases.addC("sizeof", \\#include \\size_t size_of(void) { From 122a9bad3973bb9f7f48c91eb45cbf0513cf19c3 Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 19 Dec 2019 01:38:42 +0200 Subject: [PATCH 24/37] translate-c-2 fix some casts --- src-self-hosted/clang.zig | 2 + src-self-hosted/translate_c.zig | 64 ++++- src/zig_clang.cpp | 10 + src/zig_clang.h | 2 + test/translate_c.zig | 415 ++++++++++++++++++-------------- 5 files changed, 306 insertions(+), 187 deletions(-) diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index bf7adce1e0..10b46bc9a9 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -761,6 +761,8 @@ pub extern fn ZigClangEnumType_getDecl(record_ty: ?*const struct_ZigClangEnumTyp pub extern fn ZigClangRecordDecl_getCanonicalDecl(record_decl: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangTagDecl; pub extern fn ZigClangEnumDecl_getCanonicalDecl(self: ?*const struct_ZigClangEnumDecl) ?*const struct_ZigClangTagDecl; pub extern fn ZigClangTypedefNameDecl_getCanonicalDecl(self: ?*const struct_ZigClangTypedefNameDecl) ?*const struct_ZigClangTypedefNameDecl; +pub extern fn ZigClangFunctionDecl_getCanonicalDecl(self: ?*const struct_ZigClangFunctionDecl) ?*const struct_ZigClangFunctionDecl; +pub extern fn ZigClangVarDecl_getCanonicalDecl(self: ?*const struct_ZigClangVarDecl) ?*const struct_ZigClangVarDecl; pub extern fn ZigClangRecordDecl_getDefinition(self: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangRecordDecl; pub extern fn ZigClangEnumDecl_getDefinition(self: ?*const struct_ZigClangEnumDecl) ?*const struct_ZigClangEnumDecl; pub extern fn ZigClangRecordDecl_getLocation(self: ?*const struct_ZigClangRecordDecl) struct_ZigClangSourceLocation; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 2247c4dc91..0c34cd56e1 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -375,7 +375,8 @@ fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void { } fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { - if (c.decl_table.contains(@ptrToInt(fn_decl))) return; // Avoid processing this decl twice + if (c.decl_table.contains(@ptrToInt(ZigClangFunctionDecl_getCanonicalDecl(fn_decl)))) + return; // Avoid processing this decl twice const rp = makeRestorePoint(c); const fn_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, fn_decl))); _ = try c.decl_table.put(@ptrToInt(fn_decl), fn_name); @@ -442,7 +443,8 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { } fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { - if (c.decl_table.contains(@ptrToInt(var_decl))) return; // Avoid processing this decl twice + if (c.decl_table.contains(@ptrToInt(ZigClangVarDecl_getCanonicalDecl(var_decl)))) + return; // Avoid processing this decl twice const rp = makeRestorePoint(c); const visib_tok = try appendToken(c, .Keyword_pub, "pub"); @@ -1115,10 +1117,17 @@ fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt) node.type_node = try transQualType(rp, qual_type, loc); node.eq_token = try appendToken(c, .Equal, "="); - node.init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| + var init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| try transExpr(rp, scope, expr, .used, .r_value) else try transCreateNodeUndefinedLiteral(c); + if (isBoolRes(init_node)) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt"); + try builtin_node.params.push(init_node); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + init_node = &builtin_node.base; + } + node.init_node = init_node; node.semicolon_token = try appendToken(c, .Semicolon, ";"); try block_scope.block_node.statements.push(&node.base); }, @@ -1300,14 +1309,9 @@ fn finishBoolExpr( return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(underlying_type), node, used); }, .Enum => { - const enum_ty = @ptrCast(*const ZigClangEnumType, ty); - const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@enumToInt"); - try builtin_node.params.push(node); - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - const op_token = try appendToken(rp.c, .BangEqual, "!="); const rhs_node = try transCreateNodeInt(rp.c, 0); - return transCreateNodeInfixOp(rp, scope, &builtin_node.base, .BangEqual, op_token, rhs_node, used, false); + return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false); }, .Elaborated => { const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty); @@ -1472,6 +1476,31 @@ fn transCCast( builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); return &builtin_node.base; } + if (ZigClangQualType_getTypeClass(src_type) == .Elaborated) { + const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ZigClangQualType_getTypePtr(src_type)); + return transCCast(rp, scope, loc, dst_type, ZigClangElaboratedType_getNamedType(elaborated_ty), expr); + } + if (ZigClangQualType_getTypeClass(dst_type) == .Elaborated) { + const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ZigClangQualType_getTypePtr(dst_type)); + return transCCast(rp, scope, loc, ZigClangElaboratedType_getNamedType(elaborated_ty), src_type, expr); + } + if (ZigClangQualType_getTypeClass(src_type) == .Enum and + ZigClangQualType_getTypeClass(dst_type) != .Enum) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@enumToInt"); + try builtin_node.params.push(expr); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return &builtin_node.base; + } + // TODO + // if (ZigClangQualType_getTypeClass(dst_type) == .Enum and + // ZigClangQualType_getTypeClass(src_type) != .Enum) { + // const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@intToEnum"); + // try builtin_node.params.push(try transQualType(rp, dst_type, loc)); + // _ = try appendToken(rp.c, .Comma, ","); + // try builtin_node.params.push(expr); + // builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + // return &builtin_node.base; + // } // TODO: maybe widen to increase size // TODO: maybe bitcast to change sign // TODO: maybe truncate to reduce size @@ -2443,7 +2472,13 @@ fn transCreateNodeAssign( if (result_used == .unused) { const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value); const eq_token = try appendToken(rp.c, .Equal, "="); - const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); + var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); + if (isBoolRes(rhs_node)) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt"); + try builtin_node.params.push(rhs_node); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + rhs_node = &builtin_node.base; + } if (scope.id != .Condition) _ = try appendToken(rp.c, .Semicolon, ";"); @@ -2471,7 +2506,14 @@ fn transCreateNodeAssign( const node = try transCreateNodeVarDecl(rp.c, false, true, tmp); node.eq_token = try appendToken(rp.c, .Equal, "="); - node.init_node = try transExpr(rp, scope, rhs, .used, .r_value); + var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); + if (isBoolRes(rhs_node)) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt"); + try builtin_node.params.push(rhs_node); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + rhs_node = &builtin_node.base; + } + node.init_node = rhs_node; node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); try block_scope.block_node.statements.push(&node.base); diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index d87b769f5e..93646de97b 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -1571,6 +1571,16 @@ const ZigClangTypedefNameDecl *ZigClangTypedefNameDecl_getCanonicalDecl(const Zi return reinterpret_cast(decl); } +const ZigClangFunctionDecl *ZigClangFunctionDecl_getCanonicalDecl(const ZigClangFunctionDecl *self) { + const clang::FunctionDecl *decl = reinterpret_cast(self)->getCanonicalDecl(); + return reinterpret_cast(decl); +} + +const ZigClangVarDecl *ZigClangVarDecl_getCanonicalDecl(const ZigClangVarDecl *self) { + const clang::VarDecl *decl = reinterpret_cast(self)->getCanonicalDecl(); + return reinterpret_cast(decl); +} + const ZigClangRecordDecl *ZigClangRecordDecl_getDefinition(const ZigClangRecordDecl *zig_record_decl) { const clang::RecordDecl *record_decl = reinterpret_cast(zig_record_decl); const clang::RecordDecl *definition = record_decl->getDefinition(); diff --git a/src/zig_clang.h b/src/zig_clang.h index 8dd0b9b3d5..ce71612468 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -856,6 +856,8 @@ ZIG_EXTERN_C const struct ZigClangEnumDecl *ZigClangEnumType_getDecl(const struc ZIG_EXTERN_C const struct ZigClangTagDecl *ZigClangRecordDecl_getCanonicalDecl(const struct ZigClangRecordDecl *record_decl); ZIG_EXTERN_C const struct ZigClangTagDecl *ZigClangEnumDecl_getCanonicalDecl(const struct ZigClangEnumDecl *); ZIG_EXTERN_C const struct ZigClangTypedefNameDecl *ZigClangTypedefNameDecl_getCanonicalDecl(const struct ZigClangTypedefNameDecl *); +ZIG_EXTERN_C const struct ZigClangFunctionDecl *ZigClangFunctionDecl_getCanonicalDecl(const ZigClangFunctionDecl *self); +ZIG_EXTERN_C const struct ZigClangVarDecl *ZigClangVarDecl_getCanonicalDecl(const ZigClangVarDecl *self); ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangRecordDecl_getDefinition(const struct ZigClangRecordDecl *); ZIG_EXTERN_C const struct ZigClangEnumDecl *ZigClangEnumDecl_getDefinition(const struct ZigClangEnumDecl *); diff --git a/test/translate_c.zig b/test/translate_c.zig index 7f349c54c4..0ecbc56e7c 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -716,6 +716,38 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.addC_both("while on non-bool", + \\int while_none_bool(int a, float b, void *c) { + \\ while (a) return 0; + \\ while (b) return 1; + \\ while (c) return 2; + \\ return 3; + \\} + , &[_][]const u8{ + \\pub export fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\ while (a != 0) return 0; + \\ while (b != 0) return 1; + \\ while (c != null) return 2; + \\ return 3; + \\} + }); + + cases.addC_both("for on non-bool", + \\int for_none_bool(int a, float b, void *c) { + \\ for (;a;) return 0; + \\ for (;b;) return 1; + \\ for (;c;) return 2; + \\ return 3; + \\} + , &[_][]const u8{ + \\pub export fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\ while (a != 0) return 0; + \\ while (b != 0) return 1; + \\ while (c != null) return 2; + \\ return 3; + \\} + }); + /////////////// Cases that pass for only stage2 //////////////// cases.add_2("Parameterless function prototypes", @@ -1369,18 +1401,18 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\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 (@enumToInt(@as(c_uint, d)) != 0)); - \\ var l: c_int = ((@enumToInt(@as(c_uint, d)) != 0) and (b != 0)); - \\ var m: c_int = ((c != null) or (@enumToInt(@as(c_uint, d)) != 0)); + \\ var e: c_int = @boolToInt(((a != 0) and (b != 0))); + \\ var f: c_int = @boolToInt(((b != 0) and (c != null))); + \\ var g: c_int = @boolToInt(((a != 0) and (c != null))); + \\ var h: c_int = @boolToInt(((a != 0) or (b != 0))); + \\ var i: c_int = @boolToInt(((b != 0) or (c != null))); + \\ var j: c_int = @boolToInt(((a != 0) or (c != null))); + \\ var k: c_int = @boolToInt(((a != 0) or (@enumToInt(d) != 0))); + \\ var l: c_int = @boolToInt(((@enumToInt(d) != 0) and (b != 0))); + \\ var m: c_int = @boolToInt(((c != null) or (@enumToInt(d) != 0))); \\ var td: SomeTypedef = 44; - \\ var o: c_int = ((td != 0) or (b != 0)); - \\ var p: c_int = ((c != null) and (td != 0)); + \\ var o: c_int = @boolToInt(((td != 0) or (b != 0))); + \\ var p: c_int = @boolToInt(((c != null) and (td != 0))); \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p); \\} , @@ -1437,13 +1469,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} , &[_][]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); + \\ var c: c_int = @boolToInt((a < b)); + \\ var d: c_int = @boolToInt((a > b)); + \\ var e: c_int = @boolToInt((a <= b)); + \\ var f: c_int = @boolToInt((a >= b)); + \\ var g: c_int = @boolToInt((c < d)); + \\ var h: c_int = @boolToInt((e < f)); + \\ var i: c_int = @boolToInt((g < h)); \\ return i; \\} }); @@ -1560,6 +1592,69 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("logical and, logical or", + \\int max(int a, int b) { + \\ if (a < b || a == b) + \\ return b; + \\ if (a >= b && a == b) + \\ return a; + \\ return a; + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int, b: c_int) c_int { + \\ if (((a < b) or (a == b))) return b; + \\ if (((a >= b) and (a == b))) return a; + \\ return a; + \\} + }); + + cases.add_2("if statement", + \\int max(int a, int b) { + \\ if (a < b) + \\ return b; + \\ + \\ if (a < b) + \\ return b; + \\ else + \\ return a; + \\ + \\ if (a < b) ; else ; + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int, b: c_int) c_int { + \\ if ((a < b)) return b; + \\ if ((a < b)) return b else return a; + \\ if ((a < b)) {} else {} + \\} + }); + + cases.add_2("if on non-bool", + \\enum SomeEnum { A, B, C }; + \\int if_none_bool(int a, float b, void *c, enum SomeEnum d) { + \\ if (a) return 0; + \\ if (b) return 1; + \\ if (c) return 2; + \\ if (d) return 3; + \\ return 4; + \\} + , &[_][]const u8{ + \\pub const A = enum_SomeEnum.A; + \\pub const B = enum_SomeEnum.B; + \\pub const C = enum_SomeEnum.C; + \\pub const enum_SomeEnum = extern enum { + \\ A, + \\ B, + \\ C, + \\}; + \\pub export fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int { + \\ if (a != 0) return 0; + \\ if (b != 0) return 1; + \\ if (c != null) return 2; + \\ if (d != 0) return 3; + \\ return 4; + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// cases.addAllowWarnings("simple data types", @@ -1651,85 +1746,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("if statement", - \\int max(int a, int b) { - \\ if (a < b) - \\ return b; - \\ - \\ if (a < b) - \\ return b; - \\ else - \\ return a; - \\ - \\ if (a < b) ; else ; - \\} - , &[_][]const u8{ - \\pub export fn max(a: c_int, b: c_int) c_int { - \\ if (a < b) return b; - \\ if (a < b) return b else return a; - \\ if (a < b) {} else {} - \\} - }); - - cases.addC("logical and, logical or", - \\int max(int a, int b) { - \\ if (a < b || a == b) - \\ return b; - \\ if (a >= b && a == b) - \\ return a; - \\ return a; - \\} - , &[_][]const u8{ - \\pub export fn max(a: c_int, b: c_int) c_int { - \\ if ((a < b) or (a == b)) return b; - \\ if ((a >= b) and (a == b)) return a; - \\ return a; - \\} - }); - - 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.addC("shift right assign with a fixed size type", \\#include \\int log2(uint32_t a) { @@ -2043,26 +2059,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("variable name shadowing", - \\int foo(void) { - \\ int x = 1; - \\ { - \\ int x = 2; - \\ x += 1; - \\ } - \\ return x; - \\} - , &[_][]const u8{ - \\pub fn foo() c_int { - \\ var x: c_int = 1; - \\ { - \\ var x_0: c_int = 2; - \\ x_0 += 1; - \\ } - \\ return x; - \\} - }); - cases.add("bin not", \\int foo(int x) { \\ return ~x; @@ -2099,65 +2095,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("if on non-bool", - \\enum SomeEnum { A, B, C }; - \\int if_none_bool(int a, float b, void *c, enum SomeEnum d) { - \\ if (a) return 0; - \\ if (b) return 1; - \\ if (c) return 2; - \\ if (d) return 3; - \\ return 4; - \\} - , &[_][]const u8{ - \\pub const A = enum_SomeEnum.A; - \\pub const B = enum_SomeEnum.B; - \\pub const C = enum_SomeEnum.C; - \\pub const enum_SomeEnum = extern enum { - \\ A, - \\ B, - \\ C, - \\}; - \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int { - \\ if (a != 0) return 0; - \\ if (b != 0) return 1; - \\ if (c != null) return 2; - \\ if (d != @bitCast(enum_SomeEnum, @as(@TagType(enum_SomeEnum), 0))) return 3; - \\ return 4; - \\} - }); - - cases.add("while on non-bool", - \\int while_none_bool(int a, float b, void *c) { - \\ while (a) return 0; - \\ while (b) return 1; - \\ while (c) return 2; - \\ return 3; - \\} - , &[_][]const u8{ - \\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { - \\ while (a != 0) return 0; - \\ while (b != 0) return 1; - \\ while (c != null) return 2; - \\ return 3; - \\} - }); - - cases.add("for on non-bool", - \\int for_none_bool(int a, float b, void *c) { - \\ for (;a;) return 0; - \\ for (;b;) return 1; - \\ for (;c;) return 2; - \\ return 3; - \\} - , &[_][]const u8{ - \\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { - \\ while (a != 0) return 0; - \\ while (b != 0) return 1; - \\ while (c != null) return 2; - \\ return 3; - \\} - }); - cases.addC("implicit casts", \\#include \\ @@ -2675,4 +2612,130 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return array[index]; \\} }); + + cases.addC("logical and, logical or", + \\int max(int a, int b) { + \\ if (a < b || a == b) + \\ return b; + \\ if (a >= b && a == b) + \\ return a; + \\ return a; + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int, b: c_int) c_int { + \\ if ((a < b) or (a == b)) return b; + \\ if ((a >= b) and (a == b)) return a; + \\ return a; + \\} + }); + + cases.addC("if statement", + \\int max(int a, int b) { + \\ if (a < b) + \\ return b; + \\ + \\ if (a < b) + \\ return b; + \\ else + \\ return a; + \\ + \\ if (a < b) ; else ; + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int, b: c_int) c_int { + \\ if (a < b) return b; + \\ if (a < b) return b else return a; + \\ if (a < b) {} else {} + \\} + }); + + 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; + \\ { + \\ int x = 2; + \\ x += 1; + \\ } + \\ return x; + \\} + , &[_][]const u8{ + \\pub fn foo() c_int { + \\ var x: c_int = 1; + \\ { + \\ var x_0: c_int = 2; + \\ x_0 += 1; + \\ } + \\ return x; + \\} + }); + + cases.add("if on non-bool", + \\enum SomeEnum { A, B, C }; + \\int if_none_bool(int a, float b, void *c, enum SomeEnum d) { + \\ if (a) return 0; + \\ if (b) return 1; + \\ if (c) return 2; + \\ if (d) return 3; + \\ return 4; + \\} + , &[_][]const u8{ + \\pub const A = enum_SomeEnum.A; + \\pub const B = enum_SomeEnum.B; + \\pub const C = enum_SomeEnum.C; + \\pub const enum_SomeEnum = extern enum { + \\ A, + \\ B, + \\ C, + \\}; + \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int { + \\ if (a != 0) return 0; + \\ if (b != 0) return 1; + \\ if (c != null) return 2; + \\ if (d != @bitCast(enum_SomeEnum, @as(@TagType(enum_SomeEnum), 0))) return 3; + \\ return 4; + \\} + }); } From e4c47e80b43eb3096093b38e8e984d9042e3742e Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 19 Dec 2019 08:27:35 +0200 Subject: [PATCH 25/37] translate-c-2 unaryexprortypetrait + fixes --- src-self-hosted/clang.zig | 3 ++ src-self-hosted/translate_c.zig | 84 ++++++++++++++++++++++++++++----- test/translate_c.zig | 60 ++++++++++------------- 3 files changed, 100 insertions(+), 47 deletions(-) diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index 10b46bc9a9..5c51e3d356 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -1113,3 +1113,6 @@ pub extern fn ZigClangCallExpr_getCallee(*const ZigClangCallExpr) *const ZigClan pub extern fn ZigClangCallExpr_getNumArgs(*const ZigClangCallExpr) c_uint; pub extern fn ZigClangCallExpr_getArgs(*const ZigClangCallExpr) [*]const *const ZigClangExpr; +pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangQualType; +pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangSourceLocation; + diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 0c34cd56e1..d671b5693f 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -375,7 +375,7 @@ fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void { } fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { - if (c.decl_table.contains(@ptrToInt(ZigClangFunctionDecl_getCanonicalDecl(fn_decl)))) + if (c.decl_table.contains(@ptrToInt(ZigClangFunctionDecl_getCanonicalDecl(fn_decl)))) return; // Avoid processing this decl twice const rp = makeRestorePoint(c); const fn_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, fn_decl))); @@ -443,7 +443,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { } fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { - if (c.decl_table.contains(@ptrToInt(ZigClangVarDecl_getCanonicalDecl(var_decl)))) + if (c.decl_table.contains(@ptrToInt(ZigClangVarDecl_getCanonicalDecl(var_decl)))) return; // Avoid processing this decl twice const rp = makeRestorePoint(c); const visib_tok = try appendToken(c, .Keyword_pub, "pub"); @@ -528,15 +528,46 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { return addTopLevelDecl(c, checked_name, &node.base); } +fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, builtin_name: []const u8) !*ast.Node { + _ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), builtin_name); + return transCreateNodeIdentifier(c, builtin_name); +} + fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl) Error!?*ast.Node { if (c.decl_table.get(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)))) |kv| return try transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice const rp = makeRestorePoint(c); - const visib_tok = try appendToken(c, .Keyword_pub, "pub"); - const const_tok = try appendToken(c, .Keyword_const, "const"); const typedef_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_decl))); + + if (std.mem.eql(u8, typedef_name, "uint8_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "u8") + else if (std.mem.eql(u8, typedef_name, "int8_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "i8") + else if (std.mem.eql(u8, typedef_name, "uint16_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "u16") + else if (std.mem.eql(u8, typedef_name, "int16_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "i16") + else if (std.mem.eql(u8, typedef_name, "uint32_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "u32") + else if (std.mem.eql(u8, typedef_name, "int32_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "i32") + else if (std.mem.eql(u8, typedef_name, "uint64_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "u64") + else if (std.mem.eql(u8, typedef_name, "int64_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "i64") + else if (std.mem.eql(u8, typedef_name, "intptr_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "isize") + else if (std.mem.eql(u8, typedef_name, "uintptr_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "usize") + else if (std.mem.eql(u8, typedef_name, "ssize_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "isize") + else if (std.mem.eql(u8, typedef_name, "size_t")) + return transTypeDefAsBuiltin(c, typedef_decl, "usize"); + _ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), typedef_name); + const visib_tok = try appendToken(c, .Keyword_pub, "pub"); + const const_tok = try appendToken(c, .Keyword_const, "const"); const node = try transCreateNodeVarDecl(c, true, true, typedef_name); node.eq_token = try appendToken(c, .Equal, "="); @@ -621,8 +652,9 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?* try emitWarning(c, field_loc, "{} demoted to opaque type - has bitfield", .{container_kind_name}); break :blk opaque; } - - const field_name = try appendIdentifier(c, try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, field_decl)))); + const raw_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, field_decl))); + if (raw_name.len < 1) continue; // fix weird windows bug? + const field_name = try appendIdentifier(c, raw_name); _ = try appendToken(c, .Colon, ":"); const field_type = transQualType(rp, ZigClangFieldDecl_getType(field_decl), field_loc) catch |err| switch (err) { error.UnsupportedType => { @@ -828,7 +860,17 @@ fn transStmt( .IntegerLiteralClass => return transIntegerLiteral(rp, scope, @ptrCast(*const ZigClangIntegerLiteral, stmt), result_used), .ReturnStmtClass => return transReturnStmt(rp, scope, @ptrCast(*const ZigClangReturnStmt, stmt)), .StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const ZigClangStringLiteral, stmt), result_used), - .ParenExprClass => return transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), result_used, lrvalue), + .ParenExprClass => { + const expr = try transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), result_used, lrvalue); + if (expr.id == .GroupedExpression) return expr; + const node = try rp.c.a().create(ast.Node.GroupedExpression); + node.* = .{ + .lparen = try appendToken(rp.c, .LParen, "("), + .expr = expr, + .rparen = try appendToken(rp.c, .RParen, ")"), + }; + return &node.base; + }, .InitListExprClass => return transInitListExpr(rp, scope, @ptrCast(*const ZigClangInitListExpr, stmt), result_used), .ImplicitValueInitExprClass => return transImplicitValueInitExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used), .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const ZigClangIfStmt, stmt)), @@ -854,6 +896,7 @@ fn transStmt( .MemberExprClass => return transMemberExpr(rp, scope, @ptrCast(*const ZigClangMemberExpr, stmt), result_used), .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), else => { return revertAndWarn( rp, @@ -1485,7 +1528,8 @@ fn transCCast( return transCCast(rp, scope, loc, ZigClangElaboratedType_getNamedType(elaborated_ty), src_type, expr); } if (ZigClangQualType_getTypeClass(src_type) == .Enum and - ZigClangQualType_getTypeClass(dst_type) != .Enum) { + ZigClangQualType_getTypeClass(dst_type) != .Enum) + { const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@enumToInt"); try builtin_node.params.push(expr); builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); @@ -2064,7 +2108,7 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCallExpr, const num_args = ZigClangCallExpr_getNumArgs(stmt); const args = ZigClangCallExpr_getArgs(stmt); var i: usize = 0; - while (i < num_args) : (i+=1) { + while (i < num_args) : (i += 1) { if (i != 0) { _ = try appendToken(rp.c, .Comma, ","); } @@ -2081,7 +2125,7 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCallExpr, return &node.base; } } - + return maybeSuppressResult(rp, scope, result_used, &node.base); } @@ -2101,6 +2145,24 @@ fn qualTypeGetFnProto(qt: ZigClangQualType, is_ptr: *bool) ?*const ZigClangFunct return null; } +fn transUnaryExprOrTypeTraitExpr( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangUnaryExprOrTypeTraitExpr, + result_used: ResultUsed, +) TransError!*ast.Node { + const type_node = try transQualType( + rp, + ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(stmt), + ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(stmt), + ); + + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@sizeOf"); + try builtin_node.params.push(type_node); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return maybeSuppressResult(rp, scope, result_used, &builtin_node.base); +} + fn transCPtrCast( rp: RestorePoint, loc: ZigClangSourceLocation, @@ -3092,7 +3154,7 @@ fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node { } fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.SuffixOp { - _ = try appendToken(c, .LBrace, "["); + _ = try appendToken(c, .LBrace, "["); const node = try c.a().create(ast.Node.SuffixOp); node.* = .{ .lhs = .{ .node = lhs }, diff --git a/test/translate_c.zig b/test/translate_c.zig index 0ecbc56e7c..c246c708f0 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -748,6 +748,27 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.addC_both("bitshift", + \\int foo(void) { + \\ return (1 << 2) >> 1; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ return (1 << @as(@import("std").math.Log2Int(c_int), 2)) >> @as(@import("std").math.Log2Int(c_int), 1); + \\} + }); + + cases.addC_both("sizeof", + \\#include + \\size_t size_of(void) { + \\ return sizeof(int); + \\} + , &[_][]const u8{ + \\pub export fn size_of() usize { + \\ return @sizeOf(c_int); + \\} + }); + /////////////// Cases that pass for only stage2 //////////////// cases.add_2("Parameterless function prototypes", @@ -1353,15 +1374,15 @@ 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 + 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 `>>=` is 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); + \\pub export fn some_func(a: u32) u32 { + \\ var b: u32 = a >> @as(u5, 1); \\ return b; \\} }); @@ -1496,18 +1517,6 @@ 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.add_2("typedeffed bool expression", \\typedef char* yes; \\void foo(void) { @@ -1766,17 +1775,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("sizeof", - \\#include - \\size_t size_of(void) { - \\ return sizeof(int); - \\} - , &[_][]const u8{ - \\pub export fn size_of() usize { - \\ return @sizeOf(c_int); - \\} - }); - cases.addC("__extension__ cast", \\int foo(void) { \\ return __extension__ 1; @@ -1787,16 +1785,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("bitshift", - \\int foo(void) { - \\ return (1 << 2) >> 1; - \\} - , &[_][]const u8{ - \\pub export fn foo() c_int { - \\ return (1 << @as(@import("std").math.Log2Int(c_int), 2)) >> @as(@import("std").math.Log2Int(c_int), 1); - \\} - }); - cases.addC("compound assignment operators", \\void foo(void) { \\ int a = 0; From 809deb6ec0bf9ea5649d3ba4ca80485e2148a538 Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 19 Dec 2019 09:39:39 +0200 Subject: [PATCH 26/37] translate-c-2 unary operators common case --- src-self-hosted/clang.zig | 4 + src-self-hosted/translate_c.zig | 151 ++++++++++++++ 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 \\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 \\ @@ -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 + \\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(); + \\} + }); } From 6cd402f1b44247c2e990aab7c7a1ce32f0f8d53a Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 19 Dec 2019 10:48:32 +0200 Subject: [PATCH 27/37] translate-c-2 increments worst-case --- src-self-hosted/translate_c.zig | 97 +++++++++++++++++------ test/translate_c.zig | 132 +++++++++++++++++++++----------- 2 files changed, 160 insertions(+), 69 deletions(-) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 859d10a441..26dbb2d424 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -2261,9 +2261,37 @@ fn transCreatePreCrement( // c: ++expr // zig: (blk: { // zig: const _ref = &expr; - // zig: *_ref += 1; - // zig: break :blk *_ref + // zig: _ref.* += 1; + // zig: break :blk _ref.* // zig: }) + const block_scope = try Scope.Block.init(rp.c, scope, "blk"); + block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label); + const ref = try std.fmt.allocPrint(rp.c.a(), "_ref_{}", .{rp.c.getMangle()}); + + const node = try transCreateNodeVarDecl(rp.c, false, true, ref); + node.eq_token = try appendToken(rp.c, .Equal, "="); + const rhs_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); + rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); + node.init_node = &rhs_node.base; + node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); + try block_scope.block_node.statements.push(&node.base); + + const lhs_node = try transCreateNodeIdentifier(rp.c, ref); + const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node); + _ = try appendToken(rp.c, .Semicolon, ";"); + const token = try appendToken(rp.c, op_tok_id, bytes); + const one = try transCreateNodeInt(rp.c, 1); + _ = try appendToken(rp.c, .Semicolon, ";"); + const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false); + try block_scope.block_node.statements.push(assign); + + const break_node = try transCreateNodeBreak(rp.c, block_scope.label); + break_node.rhs = ref_node; + try block_scope.block_node.statements.push(&break_node.base); + block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + // semicolon must immediately follow rbrace because it is the last token in a block + _ = try appendToken(rp.c, .Semicolon, ";"); + return &block_scope.block_node.base; } fn transCreatePostCrement( @@ -2291,10 +2319,45 @@ fn transCreatePostCrement( // c: expr++ // zig: (blk: { // zig: const _ref = &expr; - // zig: const _tmp = *_ref; - // zig: *_ref += 1; + // zig: const _tmp = _ref.*; + // zig: _ref.* += 1; // zig: break :blk _tmp // zig: }) + const block_scope = try Scope.Block.init(rp.c, scope, "blk"); + block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label); + const ref = try std.fmt.allocPrint(rp.c.a(), "_ref_{}", .{rp.c.getMangle()}); + + const node = try transCreateNodeVarDecl(rp.c, false, true, ref); + node.eq_token = try appendToken(rp.c, .Equal, "="); + const rhs_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); + rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); + node.init_node = &rhs_node.base; + node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); + try block_scope.block_node.statements.push(&node.base); + + const lhs_node = try transCreateNodeIdentifier(rp.c, ref); + const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node); + _ = try appendToken(rp.c, .Semicolon, ";"); + + const tmp = try std.fmt.allocPrint(rp.c.a(), "_tmp_{}", .{rp.c.getMangle()}); + const tmp_node = try transCreateNodeVarDecl(rp.c, false, true, tmp); + tmp_node.eq_token = try appendToken(rp.c, .Equal, "="); + tmp_node.init_node = ref_node; + tmp_node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); + try block_scope.block_node.statements.push(&tmp_node.base); + + const token = try appendToken(rp.c, op_tok_id, bytes); + const one = try transCreateNodeInt(rp.c, 1); + _ = try appendToken(rp.c, .Semicolon, ";"); + const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false); + try block_scope.block_node.statements.push(assign); + + const break_node = try transCreateNodeBreak(rp.c, block_scope.label); + break_node.rhs = try transCreateNodeIdentifier(rp.c,tmp); + try block_scope.block_node.statements.push(&break_node.base); + _ = try appendToken(rp.c, .Semicolon, ";"); + block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + return &block_scope.block_node.base; } fn transCPtrCast( @@ -2694,25 +2757,16 @@ fn transCreateNodeAssign( } if (scope.id != .Condition) _ = try appendToken(rp.c, .Semicolon, ";"); - - const node = try rp.c.a().create(ast.Node.InfixOp); - node.* = .{ - .op_token = eq_token, - .lhs = lhs_node, - .op = .Assign, - .rhs = rhs_node, - }; - return &node.base; + return transCreateNodeInfixOp(rp, scope, lhs_node, .Assign, eq_token, rhs_node, .used, false); } // worst case // c: lhs = rhs - // zig: (x: { + // zig: (blk: { // zig: const _tmp = rhs; // zig: lhs = _tmp; - // zig: break :x _tmp + // zig: break :blk _tmp // zig: }) - _ = try appendToken(rp.c, .LParen, "("); const block_scope = try Scope.Block.init(rp.c, scope, "blk"); block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label); const tmp = try std.fmt.allocPrint(rp.c.a(), "_tmp_{}", .{rp.c.getMangle()}); @@ -2735,14 +2789,8 @@ fn transCreateNodeAssign( const ident = try transCreateNodeIdentifier(rp.c, tmp); _ = try appendToken(rp.c, .Semicolon, ";"); - const assign = try rp.c.a().create(ast.Node.InfixOp); - assign.* = .{ - .op_token = eq_token, - .lhs = lhs_node, - .op = .Assign, - .rhs = ident, - }; - try block_scope.block_node.statements.push(&assign.base); + const assign = try transCreateNodeInfixOp(rp, scope, lhs_node, .Assign, eq_token, ident, .used, false); + try block_scope.block_node.statements.push(assign); const break_node = try transCreateNodeBreak(rp.c, block_scope.label); break_node.rhs = try transCreateNodeIdentifier(rp.c, tmp); @@ -2751,7 +2799,6 @@ fn transCreateNodeAssign( block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); // semicolon must immediately follow rbrace because it is the last token in a block _ = try appendToken(rp.c, .Semicolon, ";"); - _ = try appendToken(rp.c, .RParen, ")"); return &block_scope.block_node.base; } diff --git a/test/translate_c.zig b/test/translate_c.zig index 8bfa2096e7..8d1fc90833 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1786,6 +1786,50 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("pre increment/decrement", + \\void foo(void) { + \\ int i = 0; + \\ unsigned u = 0; + \\ ++i; + \\ --i; + \\ ++u; + \\ --u; + \\ i = ++i; + \\ i = --i; + \\ u = ++u; + \\ u = --u; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var i: c_int = 0; + \\ var u: c_uint = @as(c_uint, 0); + \\ i += 1; + \\ i -= 1; + \\ u +%= 1; + \\ u -%= 1; + \\ i = blk: { + \\ const _ref_1 = &i; + \\ _ref_1.* += 1; + \\ break :blk _ref_1.*; + \\ }; + \\ i = blk: { + \\ const _ref_2 = &i; + \\ _ref_2.* -= 1; + \\ break :blk _ref_2.*; + \\ }; + \\ u = blk: { + \\ const _ref_3 = &u; + \\ _ref_3.* +%= 1; + \\ break :blk _ref_3.*; + \\ }; + \\ u = blk: { + \\ const _ref_4 = &u; + \\ _ref_4.* -%= 1; + \\ break :blk _ref_4.*; + \\ }; + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// cases.add("macro defines string literal with hex", @@ -2025,50 +2069,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("pre increment/decrement", - \\void foo(void) { - \\ int i = 0; - \\ unsigned u = 0; - \\ ++i; - \\ --i; - \\ ++u; - \\ --u; - \\ i = ++i; - \\ i = --i; - \\ u = ++u; - \\ u = --u; - \\} - , &[_][]const u8{ - \\pub export fn foo() void { - \\ var i: c_int = 0; - \\ var u: c_uint = @as(c_uint, 0); - \\ i += 1; - \\ i -= 1; - \\ u +%= 1; - \\ u -%= 1; - \\ i = (x: { - \\ const _ref = &i; - \\ _ref.* += 1; - \\ break :x _ref.*; - \\ }); - \\ i = (x: { - \\ const _ref = &i; - \\ _ref.* -= 1; - \\ break :x _ref.*; - \\ }); - \\ u = (x: { - \\ const _ref = &u; - \\ _ref.* +%= 1; - \\ break :x _ref.*; - \\ }); - \\ u = (x: { - \\ const _ref = &u; - \\ _ref.* -%= 1; - \\ break :x _ref.*; - \\ }); - \\} - }); - cases.addC("implicit casts", \\#include \\ @@ -2745,4 +2745,48 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ _ = baz(); \\} }); + + cases.addC("pre increment/decrement", + \\void foo(void) { + \\ int i = 0; + \\ unsigned u = 0; + \\ ++i; + \\ --i; + \\ ++u; + \\ --u; + \\ i = ++i; + \\ i = --i; + \\ u = ++u; + \\ u = --u; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var i: c_int = 0; + \\ var u: c_uint = @as(c_uint, 0); + \\ i += 1; + \\ i -= 1; + \\ u +%= 1; + \\ u -%= 1; + \\ i = (x: { + \\ const _ref = &i; + \\ _ref.* += 1; + \\ break :x _ref.*; + \\ }); + \\ i = (x: { + \\ const _ref = &i; + \\ _ref.* -= 1; + \\ break :x _ref.*; + \\ }); + \\ u = (x: { + \\ const _ref = &u; + \\ _ref.* +%= 1; + \\ break :x _ref.*; + \\ }); + \\ u = (x: { + \\ const _ref = &u; + \\ _ref.* -%= 1; + \\ break :x _ref.*; + \\ }); + \\} + }); } From f837c7c9cd8f355aeea1dea1e9b328820802ee34 Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 19 Dec 2019 16:07:33 +0200 Subject: [PATCH 28/37] translate-c-2 compound assign --- lib/std/zig/ast.zig | 24 +- lib/std/zig/parse.zig | 12 +- src-self-hosted/clang.zig | 8 + src-self-hosted/translate_c.zig | 141 +++++++- test/translate_c.zig | 607 +++++++++++++++++++++----------- 5 files changed, 559 insertions(+), 233 deletions(-) diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 47933917b1..b5973786a3 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -1431,13 +1431,13 @@ pub const Node = struct { AssignBitShiftRight, AssignBitXor, AssignDiv, - AssignMinus, - AssignMinusWrap, + AssignSub, + AssignSubWrap, AssignMod, - AssignPlus, - AssignPlusWrap, - AssignTimes, - AssignTimesWarp, + AssignAdd, + AssignAddWrap, + AssignMult, + AssignMultWrap, BangEqual, BitAnd, BitOr, @@ -1490,13 +1490,13 @@ pub const Node = struct { Op.AssignBitShiftRight, Op.AssignBitXor, Op.AssignDiv, - Op.AssignMinus, - Op.AssignMinusWrap, + Op.AssignSub, + Op.AssignSubWrap, Op.AssignMod, - Op.AssignPlus, - Op.AssignPlusWrap, - Op.AssignTimes, - Op.AssignTimesWarp, + Op.AssignAdd, + Op.AssignAddWrap, + Op.AssignMult, + Op.AssignMultWrap, Op.BangEqual, Op.BitAnd, Op.BitOr, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index bcf0ea01da..f347070eef 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -1981,19 +1981,19 @@ fn parseAssignOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = nextToken(it); const op = switch (token.ptr.id) { - .AsteriskEqual => Op{ .AssignTimes = {} }, + .AsteriskEqual => Op{ .AssignMult = {} }, .SlashEqual => Op{ .AssignDiv = {} }, .PercentEqual => Op{ .AssignMod = {} }, - .PlusEqual => Op{ .AssignPlus = {} }, - .MinusEqual => Op{ .AssignMinus = {} }, + .PlusEqual => Op{ .AssignAdd = {} }, + .MinusEqual => Op{ .AssignSub = {} }, .AngleBracketAngleBracketLeftEqual => Op{ .AssignBitShiftLeft = {} }, .AngleBracketAngleBracketRightEqual => Op{ .AssignBitShiftRight = {} }, .AmpersandEqual => Op{ .AssignBitAnd = {} }, .CaretEqual => Op{ .AssignBitXor = {} }, .PipeEqual => Op{ .AssignBitOr = {} }, - .AsteriskPercentEqual => Op{ .AssignTimesWarp = {} }, - .PlusPercentEqual => Op{ .AssignPlusWrap = {} }, - .MinusPercentEqual => Op{ .AssignMinusWrap = {} }, + .AsteriskPercentEqual => Op{ .AssignMultWrap = {} }, + .PlusPercentEqual => Op{ .AssignAddWrap = {} }, + .MinusPercentEqual => Op{ .AssignSubWrap = {} }, .Equal => Op{ .Assign = {} }, else => { putBackToken(it, token.index); diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index a967362a8e..87970168e8 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -1120,3 +1120,11 @@ pub extern fn ZigClangUnaryOperator_getOpcode(*const ZigClangUnaryOperator) ZigC 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; + +pub extern fn ZigClangCompoundAssignOperator_getType(*const ZigClangCompoundAssignOperator) ZigClangQualType; +pub extern fn ZigClangCompoundAssignOperator_getComputationLHSType(*const ZigClangCompoundAssignOperator) ZigClangQualType; +pub extern fn ZigClangCompoundAssignOperator_getComputationResultType(*const ZigClangCompoundAssignOperator) ZigClangQualType; +pub extern fn ZigClangCompoundAssignOperator_getBeginLoc(*const ZigClangCompoundAssignOperator) ZigClangSourceLocation; +pub extern fn ZigClangCompoundAssignOperator_getOpcode(*const ZigClangCompoundAssignOperator) ZigClangBO; +pub extern fn ZigClangCompoundAssignOperator_getLHS(*const ZigClangCompoundAssignOperator) *const ZigClangExpr; +pub extern fn ZigClangCompoundAssignOperator_getRHS(*const ZigClangCompoundAssignOperator) *const ZigClangExpr; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 26dbb2d424..faf7958793 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -898,6 +898,7 @@ fn transStmt( .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), + .CompoundAssignOperatorClass => return transCompoundAssignOperator(rp, scope, @ptrCast(*const ZigClangCompoundAssignOperator, stmt), result_used), else => { return revertAndWarn( rp, @@ -2178,21 +2179,21 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar const op_expr = ZigClangUnaryOperator_getSubExpr(stmt); switch (ZigClangUnaryOperator_getOpcode(stmt)) { .PostInc => if (qualTypeHaswrappingOverflow(ZigClangUnaryOperator_getType(stmt))) - return transCreatePostCrement(rp, scope, stmt, .AssignPlusWrap, .PlusPercentEqual, "+%=", used) + return transCreatePostCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used) else - return transCreatePostCrement(rp, scope, stmt, .AssignPlus, .PlusEqual, "+=", used), + return transCreatePostCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used), .PostDec => if (qualTypeHaswrappingOverflow(ZigClangUnaryOperator_getType(stmt))) - return transCreatePostCrement(rp, scope, stmt, .AssignMinusWrap, .MinusPercentEqual, "-%=", used) + return transCreatePostCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used) else - return transCreatePostCrement(rp, scope, stmt, .AssignMinus, .MinusEqual, "-=", used), + return transCreatePostCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used), .PreInc => if (qualTypeHaswrappingOverflow(ZigClangUnaryOperator_getType(stmt))) - return transCreatePreCrement(rp, scope, stmt, .AssignPlusWrap, .PlusPercentEqual, "+%=", used) + return transCreatePreCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used) else - return transCreatePreCrement(rp, scope, stmt, .AssignPlus, .PlusEqual, "+=", used), + return transCreatePreCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used), .PreDec => if (qualTypeHaswrappingOverflow(ZigClangUnaryOperator_getType(stmt))) - return transCreatePreCrement(rp, scope, stmt, .AssignMinusWrap, .MinusPercentEqual, "-%=", used) + return transCreatePreCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used) else - return transCreatePreCrement(rp, scope, stmt, .AssignMinus, .MinusEqual, "-=", used), + return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used), .AddrOf => { const op_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value); @@ -2232,7 +2233,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar 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", .{}), + else => return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "unsupported C translation {}", .{ZigClangUnaryOperator_getOpcode(stmt)}), } } @@ -2353,13 +2354,129 @@ fn transCreatePostCrement( try block_scope.block_node.statements.push(assign); const break_node = try transCreateNodeBreak(rp.c, block_scope.label); - break_node.rhs = try transCreateNodeIdentifier(rp.c,tmp); + break_node.rhs = try transCreateNodeIdentifier(rp.c, tmp); try block_scope.block_node.statements.push(&break_node.base); _ = try appendToken(rp.c, .Semicolon, ";"); block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); return &block_scope.block_node.base; } +fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundAssignOperator, used: ResultUsed) TransError!*ast.Node { + switch (ZigClangCompoundAssignOperator_getOpcode(stmt)) { + .MulAssign => if (qualTypeHaswrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt))) + return transCreateCompoundAssign(rp, scope, stmt, .AssignMultWrap, .AsteriskPercentEqual, "*%=", .MultWrap, .AsteriskPercent, "*%", used) + else + return transCreateCompoundAssign(rp, scope, stmt, .AssignMult, .AsteriskEqual, "*=", .Mult, .Asterisk, "*", used), + .AddAssign => if (qualTypeHaswrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt))) + return transCreateCompoundAssign(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", .AddWrap, .PlusPercent, "+%", used) + else + return transCreateCompoundAssign(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", .Add, .Plus, "+", used), + .SubAssign => if (qualTypeHaswrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt))) + return transCreateCompoundAssign(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", .SubWrap, .MinusPercent, "-%", used) + else + return transCreateCompoundAssign(rp, scope, stmt, .AssignSub, .MinusPercentEqual, "-=", .Sub, .Minus, "-", used), + .ShlAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftLeft, .AngleBracketAngleBracketLeftEqual, "<<=", .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<", used), + .ShrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftRight, .AngleBracketAngleBracketRightEqual, ">>=", .BitShiftRight, .AngleBracketAngleBracketRight, ">>", used), + .AndAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitAnd, .AmpersandEqual, "&=", .BitAnd, .Ampersand, "&", used), + .XorAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitXor, .CaretEqual, "^=", .BitXor, .Caret, "^", used), + .OrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitOr, .PipeEqual, "|=", .BitOr, .Pipe, "|", used), + else => return revertAndWarn( + rp, + error.UnsupportedTranslation, + ZigClangCompoundAssignOperator_getBeginLoc(stmt), + "unsupported C translation {}", + .{ZigClangCompoundAssignOperator_getOpcode(stmt)}, + ), + } +} + +fn transCreateCompoundAssign( + rp: RestorePoint, + scope: *Scope, + stmt: *const ZigClangCompoundAssignOperator, + assign_op: ast.Node.InfixOp.Op, + assign_tok_id: std.zig.Token.Id, + assign_bytes: []const u8, + bin_op: ast.Node.InfixOp.Op, + bin_tok_id: std.zig.Token.Id, + bin_bytes: []const u8, + used: ResultUsed, +) TransError!*ast.Node { + const is_shift = bin_op == .BitShiftLeft or bin_op == .BitShiftRight; + const lhs = ZigClangCompoundAssignOperator_getLHS(stmt); + const rhs = ZigClangCompoundAssignOperator_getRHS(stmt); + const loc = ZigClangCompoundAssignOperator_getBeginLoc(stmt); + if (used == .unused) { + // common case + // c: lhs += rhs + // zig: lhs += rhs + const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value); + const eq_token = try appendToken(rp.c, assign_tok_id, assign_bytes); + var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); + + if (is_shift) { + const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); + const rhs_type = try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc); + try as_node.params.push(rhs_type); + _ = try appendToken(rp.c, .Comma, ","); + try as_node.params.push(rhs_node); + as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + rhs_node = &as_node.base; + } + if (scope.id != .Condition) + _ = try appendToken(rp.c, .Semicolon, ";"); + return transCreateNodeInfixOp(rp, scope, lhs_node, assign_op, eq_token, rhs_node, .used, false); + } + // worst case + // c: lhs += rhs + // zig: (blk: { + // zig: const _ref = &lhs; + // zig: _ref.* = _ref.* + rhs; + // zig: break :blk _ref.* + // zig: }) + const block_scope = try Scope.Block.init(rp.c, scope, "blk"); + block_scope.block_node = try transCreateNodeBlock(rp.c, block_scope.label); + const ref = try std.fmt.allocPrint(rp.c.a(), "_ref_{}", .{rp.c.getMangle()}); + + const node = try transCreateNodeVarDecl(rp.c, false, true, ref); + node.eq_token = try appendToken(rp.c, .Equal, "="); + const addr_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); + addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value); + node.init_node = &addr_node.base; + node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); + try block_scope.block_node.statements.push(&node.base); + + const lhs_node = try transCreateNodeIdentifier(rp.c, ref); + const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node); + _ = try appendToken(rp.c, .Semicolon, ";"); + const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes); + var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); + if (is_shift) { + const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as"); + const rhs_type = try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc); + try as_node.params.push(rhs_type); + _ = try appendToken(rp.c, .Comma, ","); + try as_node.params.push(rhs_node); + as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + rhs_node = &as_node.base; + } + const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false); + + _ = try appendToken(rp.c, .Semicolon, ";"); + + const eq_token = try appendToken(rp.c, .Equal, "="); + const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, eq_token, rhs_bin, .used, false); + try block_scope.block_node.statements.push(assign); + + const break_node = try transCreateNodeBreak(rp.c, block_scope.label); + break_node.rhs = ref_node; + try block_scope.block_node.statements.push(&break_node.base); + block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + // semicolon must immediately follow rbrace because it is the last token in a block + _ = try appendToken(rp.c, .Semicolon, ";"); + return &block_scope.block_node.base; +} + fn transCPtrCast( rp: RestorePoint, loc: ZigClangSourceLocation, @@ -2585,7 +2702,7 @@ fn qualTypeToLog2IntRef(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigC const inner_field_access = try transCreateNodeFieldAccess(rp.c, &import_fn_call.base, "math"); const outer_field_access = try transCreateNodeFieldAccess(rp.c, inner_field_access, "Log2Int"); const log2int_fn_call = try transCreateNodeFnCall(rp.c, outer_field_access); - try @ptrCast(*ast.Node.SuffixOp.Op.Call, &log2int_fn_call.op).params.push(zig_type_node); + try @fieldParentPtr(ast.Node.SuffixOp, "base", &log2int_fn_call.base).op.Call.params.push(zig_type_node); log2int_fn_call.rtoken = try appendToken(rp.c, .RParen, ")"); return &log2int_fn_call.base; @@ -3317,7 +3434,7 @@ fn transCreateNodeShiftOp( const lhs_expr = ZigClangBinaryOperator_getLHS(stmt); const rhs_expr = ZigClangBinaryOperator_getRHS(stmt); const rhs_location = ZigClangExpr_getBeginLoc(rhs_expr); - // lhs >> u5(rh) + // lhs >> @as(u5, rh) const lhs = try transExpr(rp, scope, lhs_expr, .used, .l_value); const op_token = try appendToken(rp.c, op_tok_id, bytes); diff --git a/test/translate_c.zig b/test/translate_c.zig index 8d1fc90833..5e5eccb60d 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1830,6 +1830,207 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("shift right assign", + \\int log2(unsigned a) { + \\ int i = 0; + \\ while (a > 0) { + \\ a >>= 1; + \\ } + \\ return i; + \\} + , &[_][]const u8{// TODO function arguments should be copied + \\pub export fn log2(a: c_uint) c_int { + \\ var i: c_int = 0; + \\ while ((a > @as(c_uint, 0))) { + \\ a >>= @as(@import("std").math.Log2Int(c_int), 1); + \\ } + \\ return i; + \\} + }); + + cases.add_2("shift right assign with a fixed size type", + \\#include + \\int log2(uint32_t a) { + \\ int i = 0; + \\ while (a > 0) { + \\ a >>= 1; + \\ } + \\ return i; + \\} + , &[_][]const u8{ + \\pub export fn log2(a: u32) c_int { + \\ var i: c_int = 0; + \\ while ((a > @as(c_uint, 0))) { + \\ a >>= @as(@import("std").math.Log2Int(c_int), 1); + \\ } + \\ return i; + \\} + }); + + cases.add_2("compound assignment operators", + \\void foo(void) { + \\ int a = 0; + \\ a += (a += 1); + \\ a -= (a -= 1); + \\ a *= (a *= 1); + \\ a &= (a &= 1); + \\ a |= (a |= 1); + \\ a ^= (a ^= 1); + \\ a >>= (a >>= 1); + \\ a <<= (a <<= 1); + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var a: c_int = 0; + \\ a += (blk: { + \\ const _ref_1 = &a; + \\ _ref_1.* = _ref_1.* + 1; + \\ break :blk _ref_1.*; + \\ }); + \\ a -= (blk: { + \\ const _ref_2 = &a; + \\ _ref_2.* = _ref_2.* - 1; + \\ break :blk _ref_2.*; + \\ }); + \\ a *= (blk: { + \\ const _ref_3 = &a; + \\ _ref_3.* = _ref_3.* * 1; + \\ break :blk _ref_3.*; + \\ }); + \\ a &= (blk: { + \\ const _ref_4 = &a; + \\ _ref_4.* = _ref_4.* & 1; + \\ break :blk _ref_4.*; + \\ }); + \\ a |= (blk: { + \\ const _ref_5 = &a; + \\ _ref_5.* = _ref_5.* | 1; + \\ break :blk _ref_5.*; + \\ }); + \\ a ^= (blk: { + \\ const _ref_6 = &a; + \\ _ref_6.* = _ref_6.* ^ 1; + \\ break :blk _ref_6.*; + \\ }); + \\ a >>= @as(@import("std").math.Log2Int(c_int), (blk: { + \\ const _ref_7 = &a; + \\ _ref_7.* = _ref_7.* >> @as(@import("std").math.Log2Int(c_int), 1); + \\ break :blk _ref_7.*; + \\ })); + \\ a <<= @as(@import("std").math.Log2Int(c_int), (blk: { + \\ const _ref_8 = &a; + \\ _ref_8.* = _ref_8.* << @as(@import("std").math.Log2Int(c_int), 1); + \\ break :blk _ref_8.*; + \\ })); + \\} + }); + + cases.add_2("compound assignment operators unsigned", + \\void foo(void) { + \\ unsigned a = 0; + \\ a += (a += 1); + \\ a -= (a -= 1); + \\ a *= (a *= 1); + \\ a &= (a &= 1); + \\ a |= (a |= 1); + \\ a ^= (a ^= 1); + \\ a >>= (a >>= 1); + \\ a <<= (a <<= 1); + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var a: c_uint = @as(c_uint, 0); + \\ a +%= (blk: { + \\ const _ref_1 = &a; + \\ _ref_1.* = _ref_1.* +% @as(c_uint, 1); + \\ break :blk _ref_1.*; + \\ }); + \\ a -%= (blk: { + \\ const _ref_2 = &a; + \\ _ref_2.* = _ref_2.* -% @as(c_uint, 1); + \\ break :blk _ref_2.*; + \\ }); + \\ a *%= (blk: { + \\ const _ref_3 = &a; + \\ _ref_3.* = _ref_3.* *% @as(c_uint, 1); + \\ break :blk _ref_3.*; + \\ }); + \\ a &= (blk: { + \\ const _ref_4 = &a; + \\ _ref_4.* = _ref_4.* & @as(c_uint, 1); + \\ break :blk _ref_4.*; + \\ }); + \\ a |= (blk: { + \\ const _ref_5 = &a; + \\ _ref_5.* = _ref_5.* | @as(c_uint, 1); + \\ break :blk _ref_5.*; + \\ }); + \\ a ^= (blk: { + \\ const _ref_6 = &a; + \\ _ref_6.* = _ref_6.* ^ @as(c_uint, 1); + \\ break :blk _ref_6.*; + \\ }); + \\ a >>= @as(@import("std").math.Log2Int(c_uint), (blk: { + \\ const _ref_7 = &a; + \\ _ref_7.* = _ref_7.* >> @as(@import("std").math.Log2Int(c_int), 1); + \\ break :blk _ref_7.*; + \\ })); + \\ a <<= @as(@import("std").math.Log2Int(c_uint), (blk: { + \\ const _ref_8 = &a; + \\ _ref_8.* = _ref_8.* << @as(@import("std").math.Log2Int(c_int), 1); + \\ break :blk _ref_8.*; + \\ })); + \\} + }); + + cases.add_2("post increment/decrement", + \\void foo(void) { + \\ int i = 0; + \\ unsigned u = 0; + \\ i++; + \\ i--; + \\ u++; + \\ u--; + \\ i = i++; + \\ i = i--; + \\ u = u++; + \\ u = u--; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var i: c_int = 0; + \\ var u: c_uint = @as(c_uint, 0); + \\ i += 1; + \\ i -= 1; + \\ u +%= 1; + \\ u -%= 1; + \\ i = blk: { + \\ const _ref_1 = &i; + \\ const _tmp_2 = _ref_1.*; + \\ _ref_1.* += 1; + \\ break :blk _tmp_2; + \\ }; + \\ i = blk: { + \\ const _ref_3 = &i; + \\ const _tmp_4 = _ref_3.*; + \\ _ref_3.* -= 1; + \\ break :blk _tmp_4; + \\ }; + \\ u = blk: { + \\ const _ref_5 = &u; + \\ const _tmp_6 = _ref_5.*; + \\ _ref_5.* +%= 1; + \\ break :blk _tmp_6; + \\ }; + \\ u = blk: { + \\ const _ref_7 = &u; + \\ const _tmp_8 = _ref_7.*; + \\ _ref_7.* -%= 1; + \\ break :blk _tmp_8; + \\ }; + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// cases.add("macro defines string literal with hex", @@ -1856,45 +2057,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const FOO_CHAR = 63; }); - cases.addC("shift right assign", - \\int log2(unsigned a) { - \\ int i = 0; - \\ while (a > 0) { - \\ a >>= 1; - \\ } - \\ return i; - \\} - , &[_][]const u8{ - \\pub export fn log2(_arg_a: c_uint) c_int { - \\ var a = _arg_a; - \\ var i: c_int = 0; - \\ while (a > @as(c_uint, 0)) { - \\ a >>= @as(@import("std").math.Log2Int(c_uint), 1); - \\ } - \\ return i; - \\} - }); - - cases.addC("shift right assign with a fixed size type", - \\#include - \\int log2(uint32_t a) { - \\ int i = 0; - \\ while (a > 0) { - \\ a >>= 1; - \\ } - \\ return i; - \\} - , &[_][]const u8{ - \\pub export fn log2(_arg_a: u32) c_int { - \\ var a = _arg_a; - \\ var i: c_int = 0; - \\ while (a > @as(c_uint, 0)) { - \\ a >>= @as(u5, 1); - \\ } - \\ return i; - \\} - }); - cases.addC("__extension__ cast", \\int foo(void) { \\ return __extension__ 1; @@ -1905,170 +2067,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("compound assignment operators", - \\void foo(void) { - \\ int a = 0; - \\ a += (a += 1); - \\ a -= (a -= 1); - \\ a *= (a *= 1); - \\ a &= (a &= 1); - \\ a |= (a |= 1); - \\ a ^= (a ^= 1); - \\ a >>= (a >>= 1); - \\ a <<= (a <<= 1); - \\} - , &[_][]const u8{ - \\pub export fn foo() void { - \\ var a: c_int = 0; - \\ a += (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* + 1); - \\ break :x _ref.*; - \\ }); - \\ a -= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* - 1); - \\ break :x _ref.*; - \\ }); - \\ a *= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* * 1); - \\ break :x _ref.*; - \\ }); - \\ a &= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* & 1); - \\ break :x _ref.*; - \\ }); - \\ a |= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* | 1); - \\ break :x _ref.*; - \\ }); - \\ a ^= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* ^ 1); - \\ break :x _ref.*; - \\ }); - \\ a >>= @as(@import("std").math.Log2Int(c_int), (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* >> @as(@import("std").math.Log2Int(c_int), 1)); - \\ break :x _ref.*; - \\ })); - \\ a <<= @as(@import("std").math.Log2Int(c_int), (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* << @as(@import("std").math.Log2Int(c_int), 1)); - \\ break :x _ref.*; - \\ })); - \\} - }); - - cases.addC("compound assignment operators unsigned", - \\void foo(void) { - \\ unsigned a = 0; - \\ a += (a += 1); - \\ a -= (a -= 1); - \\ a *= (a *= 1); - \\ a &= (a &= 1); - \\ a |= (a |= 1); - \\ a ^= (a ^= 1); - \\ a >>= (a >>= 1); - \\ a <<= (a <<= 1); - \\} - , &[_][]const u8{ - \\pub export fn foo() void { - \\ var a: c_uint = @as(c_uint, 0); - \\ a +%= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* +% @as(c_uint, 1)); - \\ break :x _ref.*; - \\ }); - \\ a -%= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* -% @as(c_uint, 1)); - \\ break :x _ref.*; - \\ }); - \\ a *%= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* *% @as(c_uint, 1)); - \\ break :x _ref.*; - \\ }); - \\ a &= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* & @as(c_uint, 1)); - \\ break :x _ref.*; - \\ }); - \\ a |= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* | @as(c_uint, 1)); - \\ break :x _ref.*; - \\ }); - \\ a ^= (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* ^ @as(c_uint, 1)); - \\ break :x _ref.*; - \\ }); - \\ a >>= @as(@import("std").math.Log2Int(c_uint), (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* >> @as(@import("std").math.Log2Int(c_uint), 1)); - \\ break :x _ref.*; - \\ })); - \\ a <<= @as(@import("std").math.Log2Int(c_uint), (x: { - \\ const _ref = &a; - \\ _ref.* = (_ref.* << @as(@import("std").math.Log2Int(c_uint), 1)); - \\ break :x _ref.*; - \\ })); - \\} - }); - - cases.addC("post increment/decrement", - \\void foo(void) { - \\ int i = 0; - \\ unsigned u = 0; - \\ i++; - \\ i--; - \\ u++; - \\ u--; - \\ i = i++; - \\ i = i--; - \\ u = u++; - \\ u = u--; - \\} - , &[_][]const u8{ - \\pub export fn foo() void { - \\ var i: c_int = 0; - \\ var u: c_uint = @as(c_uint, 0); - \\ i += 1; - \\ i -= 1; - \\ u +%= 1; - \\ u -%= 1; - \\ i = (x: { - \\ const _ref = &i; - \\ const _tmp = _ref.*; - \\ _ref.* += 1; - \\ break :x _tmp; - \\ }); - \\ i = (x: { - \\ const _ref = &i; - \\ const _tmp = _ref.*; - \\ _ref.* -= 1; - \\ break :x _tmp; - \\ }); - \\ u = (x: { - \\ const _ref = &u; - \\ const _tmp = _ref.*; - \\ _ref.* +%= 1; - \\ break :x _tmp; - \\ }); - \\ u = (x: { - \\ const _ref = &u; - \\ const _tmp = _ref.*; - \\ _ref.* -%= 1; - \\ break :x _tmp; - \\ }); - \\} - }); - cases.addC("implicit casts", \\#include \\ @@ -2789,4 +2787,207 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ }); \\} }); + + cases.addC("shift right assign", + \\int log2(unsigned a) { + \\ int i = 0; + \\ while (a > 0) { + \\ a >>= 1; + \\ } + \\ return i; + \\} + , &[_][]const u8{ + \\pub export fn log2(_arg_a: c_uint) c_int { + \\ var a = _arg_a; + \\ var i: c_int = 0; + \\ while (a > @as(c_uint, 0)) { + \\ a >>= @as(@import("std").math.Log2Int(c_uint), 1); + \\ } + \\ return i; + \\} + }); + + cases.addC("shift right assign with a fixed size type", + \\#include + \\int log2(uint32_t a) { + \\ int i = 0; + \\ while (a > 0) { + \\ a >>= 1; + \\ } + \\ return i; + \\} + , &[_][]const u8{ + \\pub export fn log2(_arg_a: u32) c_int { + \\ var a = _arg_a; + \\ var i: c_int = 0; + \\ while (a > @as(c_uint, 0)) { + \\ a >>= @as(u5, 1); + \\ } + \\ return i; + \\} + }); + + cases.addC("compound assignment operators", + \\void foo(void) { + \\ int a = 0; + \\ a += (a += 1); + \\ a -= (a -= 1); + \\ a *= (a *= 1); + \\ a &= (a &= 1); + \\ a |= (a |= 1); + \\ a ^= (a ^= 1); + \\ a >>= (a >>= 1); + \\ a <<= (a <<= 1); + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var a: c_int = 0; + \\ a += (x: { + \\ const _ref = &a; + \\ _ref.* = (_ref.* + 1); + \\ break :x _ref.*; + \\ }); + \\ a -= (x: { + \\ const _ref = &a; + \\ _ref.* = (_ref.* - 1); + \\ break :x _ref.*; + \\ }); + \\ a *= (x: { + \\ const _ref = &a; + \\ _ref.* = (_ref.* * 1); + \\ break :x _ref.*; + \\ }); + \\ a &= (x: { + \\ const _ref = &a; + \\ _ref.* = (_ref.* & 1); + \\ break :x _ref.*; + \\ }); + \\ a |= (x: { + \\ const _ref = &a; + \\ _ref.* = (_ref.* | 1); + \\ break :x _ref.*; + \\ }); + \\ a ^= (x: { + \\ const _ref = &a; + \\ _ref.* = (_ref.* ^ 1); + \\ break :x _ref.*; + \\ }); + \\ a >>= @as(@import("std").math.Log2Int(c_int), (x: { + \\ const _ref = &a; + \\ _ref.* = (_ref.* >> @as(@import("std").math.Log2Int(c_int), 1)); + \\ break :x _ref.*; + \\ })); + \\ a <<= @as(@import("std").math.Log2Int(c_int), (x: { + \\ const _ref = &a; + \\ _ref.* = (_ref.* << @as(@import("std").math.Log2Int(c_int), 1)); + \\ break :x _ref.*; + \\ })); + \\} + }); + + cases.addC("compound assignment operators unsigned", + \\void foo(void) { + \\ unsigned a = 0; + \\ a += (a += 1); + \\ a -= (a -= 1); + \\ a *= (a *= 1); + \\ a &= (a &= 1); + \\ a |= (a |= 1); + \\ a ^= (a ^= 1); + \\ a >>= (a >>= 1); + \\ a <<= (a <<= 1); + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var a: c_uint = @as(c_uint, 0); + \\ a +%= (x: { + \\ const _ref = &a; + \\ _ref.* = (_ref.* +% @as(c_uint, 1)); + \\ break :x _ref.*; + \\ }); + \\ a -%= (x: { + \\ const _ref = &a; + \\ _ref.* = (_ref.* -% @as(c_uint, 1)); + \\ break :x _ref.*; + \\ }); + \\ a *%= (x: { + \\ const _ref = &a; + \\ _ref.* = (_ref.* *% @as(c_uint, 1)); + \\ break :x _ref.*; + \\ }); + \\ a &= (x: { + \\ const _ref = &a; + \\ _ref.* = (_ref.* & @as(c_uint, 1)); + \\ break :x _ref.*; + \\ }); + \\ a |= (x: { + \\ const _ref = &a; + \\ _ref.* = (_ref.* | @as(c_uint, 1)); + \\ break :x _ref.*; + \\ }); + \\ a ^= (x: { + \\ const _ref = &a; + \\ _ref.* = (_ref.* ^ @as(c_uint, 1)); + \\ break :x _ref.*; + \\ }); + \\ a >>= @as(@import("std").math.Log2Int(c_uint), (x: { + \\ const _ref = &a; + \\ _ref.* = (_ref.* >> @as(@import("std").math.Log2Int(c_uint), 1)); + \\ break :x _ref.*; + \\ })); + \\ a <<= @as(@import("std").math.Log2Int(c_uint), (x: { + \\ const _ref = &a; + \\ _ref.* = (_ref.* << @as(@import("std").math.Log2Int(c_uint), 1)); + \\ break :x _ref.*; + \\ })); + \\} + }); + + cases.addC("post increment/decrement", + \\void foo(void) { + \\ int i = 0; + \\ unsigned u = 0; + \\ i++; + \\ i--; + \\ u++; + \\ u--; + \\ i = i++; + \\ i = i--; + \\ u = u++; + \\ u = u--; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var i: c_int = 0; + \\ var u: c_uint = @as(c_uint, 0); + \\ i += 1; + \\ i -= 1; + \\ u +%= 1; + \\ u -%= 1; + \\ i = (x: { + \\ const _ref = &i; + \\ const _tmp = _ref.*; + \\ _ref.* += 1; + \\ break :x _tmp; + \\ }); + \\ i = (x: { + \\ const _ref = &i; + \\ const _tmp = _ref.*; + \\ _ref.* -= 1; + \\ break :x _tmp; + \\ }); + \\ u = (x: { + \\ const _ref = &u; + \\ const _tmp = _ref.*; + \\ _ref.* +%= 1; + \\ break :x _tmp; + \\ }); + \\ u = (x: { + \\ const _ref = &u; + \\ const _tmp = _ref.*; + \\ _ref.* -%= 1; + \\ break :x _tmp; + \\ }); + \\} + }); } From 61482be15380be22c8333ff4c6557108596efba2 Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 19 Dec 2019 20:54:39 +0200 Subject: [PATCH 29/37] translate-c-2 improve macro fn ptr caller --- lib/std/zig/ast.zig | 8 +- lib/std/zig/parse.zig | 4 +- src-self-hosted/translate_c.zig | 195 +++++++++++++++++++------------- test/translate_c.zig | 16 +-- 4 files changed, 131 insertions(+), 92 deletions(-) diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index b5973786a3..35353f419c 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -1436,8 +1436,8 @@ pub const Node = struct { AssignMod, AssignAdd, AssignAddWrap, - AssignMult, - AssignMultWrap, + AssignMul, + AssignMulWrap, BangEqual, BitAnd, BitOr, @@ -1495,8 +1495,8 @@ pub const Node = struct { Op.AssignMod, Op.AssignAdd, Op.AssignAddWrap, - Op.AssignMult, - Op.AssignMultWrap, + Op.AssignMul, + Op.AssignMulWrap, Op.BangEqual, Op.BitAnd, Op.BitOr, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index f347070eef..4447e96032 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -1981,7 +1981,7 @@ fn parseAssignOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = nextToken(it); const op = switch (token.ptr.id) { - .AsteriskEqual => Op{ .AssignMult = {} }, + .AsteriskEqual => Op{ .AssignMul = {} }, .SlashEqual => Op{ .AssignDiv = {} }, .PercentEqual => Op{ .AssignMod = {} }, .PlusEqual => Op{ .AssignAdd = {} }, @@ -1991,7 +1991,7 @@ fn parseAssignOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { .AmpersandEqual => Op{ .AssignBitAnd = {} }, .CaretEqual => Op{ .AssignBitXor = {} }, .PipeEqual => Op{ .AssignBitOr = {} }, - .AsteriskPercentEqual => Op{ .AssignMultWrap = {} }, + .AsteriskPercentEqual => Op{ .AssignMulWrap = {} }, .PlusPercentEqual => Op{ .AssignAddWrap = {} }, .MinusPercentEqual => Op{ .AssignSubWrap = {} }, .Equal => Op{ .Assign = {} }, diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index faf7958793..aa7fe335ae 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -8,6 +8,7 @@ const Token = std.zig.Token; usingnamespace @import("clang.zig"); const ctok = @import("c_tokenizer.zig"); const CToken = ctok.CToken; +const mem = std.mem; const CallingConvention = std.builtin.TypeInfo.CallingConvention; @@ -83,7 +84,7 @@ const Scope = struct { fn getAlias(scope: *Block, name: []const u8) ?[]const u8 { var it = scope.variables.iterator(0); while (it.next()) |p| { - if (std.mem.eql(u8, p.name, name)) + if (mem.eql(u8, p.name, name)) return p.alias; } return scope.base.parent.?.getAlias(name); @@ -92,7 +93,7 @@ const Scope = struct { fn contains(scope: *Block, name: []const u8) bool { var it = scope.variables.iterator(0); while (it.next()) |p| { - if (std.mem.eql(u8, p.name, name)) + if (mem.eql(u8, p.name, name)) return true; } return scope.base.parent.?.contains(name); @@ -137,7 +138,7 @@ const Scope = struct { fn getAlias(scope: *FnDef, name: []const u8) ?[]const u8 { var it = scope.params.iterator(0); while (it.next()) |p| { - if (std.mem.eql(u8, p.name, name)) + if (mem.eql(u8, p.name, name)) return p.alias; } return scope.base.parent.?.getAlias(name); @@ -146,7 +147,7 @@ const Scope = struct { fn contains(scope: *FnDef, name: []const u8) bool { var it = scope.params.iterator(0); while (it.next()) |p| { - if (std.mem.eql(u8, p.name, name)) + if (mem.eql(u8, p.name, name)) return true; } return scope.base.parent.?.contains(name); @@ -233,13 +234,13 @@ const Context = struct { return c.mangle_count; } - fn a(c: *Context) *std.mem.Allocator { + fn a(c: *Context) *mem.Allocator { return &c.tree.arena_allocator.allocator; } /// Convert a null-terminated C string to a slice allocated in the arena fn str(c: *Context, s: [*:0]const u8) ![]u8 { - return std.mem.dupe(c.a(), u8, std.mem.toSliceConst(u8, s)); + return mem.dupe(c.a(), u8, mem.toSliceConst(u8, s)); } /// Convert a clang source location to a file:line:column string @@ -255,7 +256,7 @@ const Context = struct { }; pub fn translate( - backing_allocator: *std.mem.Allocator, + backing_allocator: *mem.Allocator, args_begin: [*]?[*]const u8, args_end: [*]?[*]const u8, errors: *[]ClangErrMsg, @@ -540,29 +541,29 @@ fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl) Error const typedef_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_decl))); - if (std.mem.eql(u8, typedef_name, "uint8_t")) + if (mem.eql(u8, typedef_name, "uint8_t")) return transTypeDefAsBuiltin(c, typedef_decl, "u8") - else if (std.mem.eql(u8, typedef_name, "int8_t")) + else if (mem.eql(u8, typedef_name, "int8_t")) return transTypeDefAsBuiltin(c, typedef_decl, "i8") - else if (std.mem.eql(u8, typedef_name, "uint16_t")) + else if (mem.eql(u8, typedef_name, "uint16_t")) return transTypeDefAsBuiltin(c, typedef_decl, "u16") - else if (std.mem.eql(u8, typedef_name, "int16_t")) + else if (mem.eql(u8, typedef_name, "int16_t")) return transTypeDefAsBuiltin(c, typedef_decl, "i16") - else if (std.mem.eql(u8, typedef_name, "uint32_t")) + else if (mem.eql(u8, typedef_name, "uint32_t")) return transTypeDefAsBuiltin(c, typedef_decl, "u32") - else if (std.mem.eql(u8, typedef_name, "int32_t")) + else if (mem.eql(u8, typedef_name, "int32_t")) return transTypeDefAsBuiltin(c, typedef_decl, "i32") - else if (std.mem.eql(u8, typedef_name, "uint64_t")) + else if (mem.eql(u8, typedef_name, "uint64_t")) return transTypeDefAsBuiltin(c, typedef_decl, "u64") - else if (std.mem.eql(u8, typedef_name, "int64_t")) + else if (mem.eql(u8, typedef_name, "int64_t")) return transTypeDefAsBuiltin(c, typedef_decl, "i64") - else if (std.mem.eql(u8, typedef_name, "intptr_t")) + else if (mem.eql(u8, typedef_name, "intptr_t")) return transTypeDefAsBuiltin(c, typedef_decl, "isize") - else if (std.mem.eql(u8, typedef_name, "uintptr_t")) + else if (mem.eql(u8, typedef_name, "uintptr_t")) return transTypeDefAsBuiltin(c, typedef_decl, "usize") - else if (std.mem.eql(u8, typedef_name, "ssize_t")) + else if (mem.eql(u8, typedef_name, "ssize_t")) return transTypeDefAsBuiltin(c, typedef_decl, "isize") - else if (std.mem.eql(u8, typedef_name, "size_t")) + else if (mem.eql(u8, typedef_name, "size_t")) return transTypeDefAsBuiltin(c, typedef_decl, "usize"); _ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), typedef_name); @@ -704,7 +705,7 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No const name = try std.fmt.allocPrint(c.a(), "enum_{}", .{bare_name}); _ = try c.decl_table.put(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)), name); - const node = try transCreateNodeVarDecl(c, true, true, name); + const node = try transCreateNodeVarDecl(c, !is_unnamed, true, name); node.eq_token = try appendToken(c, .Equal, "="); node.init_node = if (ZigClangEnumDecl_getDefinition(enum_decl)) |enum_def| blk: { @@ -762,7 +763,7 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No const enum_val_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, enum_const))); - const field_name = if (!is_unnamed and std.mem.startsWith(u8, enum_val_name, bare_name)) + const field_name = if (!is_unnamed and mem.startsWith(u8, enum_val_name, bare_name)) enum_val_name[bare_name.len..] else enum_val_name; @@ -1448,7 +1449,7 @@ fn writeEscapedString(buf: []u8, s: []const u8) void { var i: usize = 0; for (s) |c| { const escaped = escapeChar(c, &char_buf); - std.mem.copy(u8, buf[i..], escaped); + mem.copy(u8, buf[i..], escaped); i += escaped.len; } } @@ -1537,16 +1538,6 @@ fn transCCast( builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); return &builtin_node.base; } - // TODO - // if (ZigClangQualType_getTypeClass(dst_type) == .Enum and - // ZigClangQualType_getTypeClass(src_type) != .Enum) { - // const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@intToEnum"); - // try builtin_node.params.push(try transQualType(rp, dst_type, loc)); - // _ = try appendToken(rp.c, .Comma, ","); - // try builtin_node.params.push(expr); - // builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - // return &builtin_node.base; - // } // TODO: maybe widen to increase size // TODO: maybe bitcast to change sign // TODO: maybe truncate to reduce size @@ -2364,9 +2355,9 @@ fn transCreatePostCrement( fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundAssignOperator, used: ResultUsed) TransError!*ast.Node { switch (ZigClangCompoundAssignOperator_getOpcode(stmt)) { .MulAssign => if (qualTypeHaswrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt))) - return transCreateCompoundAssign(rp, scope, stmt, .AssignMultWrap, .AsteriskPercentEqual, "*%=", .MultWrap, .AsteriskPercent, "*%", used) + return transCreateCompoundAssign(rp, scope, stmt, .AssignMulWrap, .AsteriskPercentEqual, "*%=", .MultWrap, .AsteriskPercent, "*%", used) else - return transCreateCompoundAssign(rp, scope, stmt, .AssignMult, .AsteriskEqual, "*=", .Mult, .Asterisk, "*", used), + return transCreateCompoundAssign(rp, scope, stmt, .AssignMul, .AsteriskEqual, "*=", .Mult, .Asterisk, "*", used), .AddAssign => if (qualTypeHaswrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt))) return transCreateCompoundAssign(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", .AddWrap, .PlusPercent, "+%", used) else @@ -2645,13 +2636,13 @@ fn qualTypeIntBitWidth(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigCl 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")) { + if (mem.eql(u8, type_name, "uint8_t") or 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")) { + } else if (mem.eql(u8, type_name, "uint16_t") or 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")) { + } else if (mem.eql(u8, type_name, "uint32_t") or 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")) { + } else if (mem.eql(u8, type_name, "uint64_t") or mem.eql(u8, type_name, "int64_t")) { return 64; } else { return 0; @@ -3176,7 +3167,7 @@ fn transCreateNodeOpaqueType(c: *Context) !*ast.Node { return &call_node.base; } -fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias_node: *ast.Node) !*ast.Node { +fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias: *ast.Node.FnProto) !*ast.Node { const scope = &c.global_scope.base; const pub_tok = try appendToken(c, .Keyword_pub, "pub"); @@ -3185,8 +3176,6 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a const name_tok = try appendIdentifier(c, name); _ = try appendToken(c, .LParen, "("); - const proto_alias = proto_alias_node.cast(ast.Node.FnProto).?; - var fn_params = ast.Node.FnProto.ParamList.init(c.a()); var it = proto_alias.params.iterator(0); while (it.next()) |pn| { @@ -3948,27 +3937,27 @@ fn isZigPrimitiveType(name: []const u8) bool { return true; } // void is invalid in c so it doesn't need to be checked. - return std.mem.eql(u8, name, "comptime_float") or - std.mem.eql(u8, name, "comptime_int") or - std.mem.eql(u8, name, "bool") or - std.mem.eql(u8, name, "isize") or - std.mem.eql(u8, name, "usize") or - std.mem.eql(u8, name, "f16") or - std.mem.eql(u8, name, "f32") or - std.mem.eql(u8, name, "f64") or - std.mem.eql(u8, name, "f128") or - std.mem.eql(u8, name, "c_longdouble") or - std.mem.eql(u8, name, "noreturn") or - std.mem.eql(u8, name, "type") or - std.mem.eql(u8, name, "anyerror") or - std.mem.eql(u8, name, "c_short") or - std.mem.eql(u8, name, "c_ushort") or - std.mem.eql(u8, name, "c_int") or - std.mem.eql(u8, name, "c_uint") or - std.mem.eql(u8, name, "c_long") or - std.mem.eql(u8, name, "c_ulong") or - std.mem.eql(u8, name, "c_longlong") or - std.mem.eql(u8, name, "c_ulonglong"); + return mem.eql(u8, name, "comptime_float") or + mem.eql(u8, name, "comptime_int") or + mem.eql(u8, name, "bool") or + mem.eql(u8, name, "isize") or + mem.eql(u8, name, "usize") or + mem.eql(u8, name, "f16") or + mem.eql(u8, name, "f32") or + mem.eql(u8, name, "f64") or + mem.eql(u8, name, "f128") or + mem.eql(u8, name, "c_longdouble") or + mem.eql(u8, name, "noreturn") or + mem.eql(u8, name, "type") or + mem.eql(u8, name, "anyerror") or + mem.eql(u8, name, "c_short") or + mem.eql(u8, name, "c_ushort") or + mem.eql(u8, name, "c_int") or + mem.eql(u8, name, "c_uint") or + mem.eql(u8, name, "c_long") or + mem.eql(u8, name, "c_ulong") or + mem.eql(u8, name, "c_longlong") or + mem.eql(u8, name, "c_ulonglong"); } fn isValidZigIdentifier(name: []const u8) bool { @@ -4039,13 +4028,13 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void { var tok_it = tok_list.iterator(0); const first_tok = tok_it.next().?; - assert(first_tok.id == .Identifier and std.mem.eql(u8, first_tok.bytes, name)); + assert(first_tok.id == .Identifier and mem.eql(u8, first_tok.bytes, name)); const next = tok_it.peek().?; switch (next.id) { .Identifier => { // if it equals itself, ignore. for example, from stdio.h: // #define stdin stdin - if (std.mem.eql(u8, checked_name, next.bytes)) { + if (mem.eql(u8, checked_name, next.bytes)) { continue; } }, @@ -4493,19 +4482,27 @@ fn tokenSlice(c: *Context, token: ast.TokenIndex) []const u8 { return c.source_buffer.toSliceConst()[tok.start..tok.end]; } -fn getFnDecl(c: *Context, ref: *ast.Node) ?*ast.Node { - const init = if (ref.cast(ast.Node.VarDecl)) |v| v.init_node.? else return null; - const name = if (init.cast(ast.Node.Identifier)) |id| - tokenSlice(c, id.token) - else - return null; - // TODO a.b.c - if (c.global_scope.sym_table.get(name)) |kv| { - if (kv.value.cast(ast.Node.VarDecl)) |val| { - if (val.type_node) |type_node| { - if (type_node.cast(ast.Node.PrefixOp)) |casted| { - if (casted.rhs.id == .FnProto) { - return casted.rhs; +fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { + if (node.id == .ContainerDecl) { + return node; + } else if (node.id == .PrefixOp) { + return node; + } else if (node.cast(ast.Node.Identifier)) |ident| { + if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |kv| { + if (kv.value.cast(ast.Node.VarDecl)) |var_decl| + return getContainer(c, var_decl.init_node.?); + } + } else if (node.cast(ast.Node.InfixOp)) |infix| { + if (infix.op != .Period) + return null; + if (getContainerTypeOf(c, infix.lhs)) |ty_node| { + if (ty_node.cast(ast.Node.ContainerDecl)) |container| { + var it = container.fields_and_decls.iterator(0); + while (it.next()) |field_ref| { + const field = field_ref.*.cast(ast.Node.ContainerField).?; + const ident = infix.rhs.cast(ast.Node.Identifier).?; + if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) { + return getContainer(c, field.type_expr.?); } } } @@ -4514,10 +4511,52 @@ fn getFnDecl(c: *Context, ref: *ast.Node) ?*ast.Node { return null; } +fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node { + if (ref.cast(ast.Node.Identifier)) |ident| { + if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |kv| { + if (kv.value.cast(ast.Node.VarDecl)) |var_decl| { + if (var_decl.type_node) |ty| + return getContainer(c, ty); + } + } + } else if (ref.cast(ast.Node.InfixOp)) |infix| { + if (infix.op != .Period) + return null; + if (getContainerTypeOf(c, infix.lhs)) |ty_node| { + if (ty_node.cast(ast.Node.ContainerDecl)) |container| { + var it = container.fields_and_decls.iterator(0); + while (it.next()) |field_ref| { + const field = field_ref.*.cast(ast.Node.ContainerField).?; + const ident = infix.rhs.cast(ast.Node.Identifier).?; + if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) { + return getContainer(c, field.type_expr.?); + } + } + } else + return ty_node; + } + } + return null; +} + +fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto { + const init = if (ref.cast(ast.Node.VarDecl)) |v| v.init_node.? else return null; + if (getContainerTypeOf(c, init)) |ty_node| { + if (ty_node.cast(ast.Node.PrefixOp)) |prefix| { + if (prefix.op == .OptionalType) { + if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| { + return fn_proto; + } + } + } + } + return null; +} + fn addMacros(c: *Context) !void { var macro_it = c.global_scope.macro_table.iterator(); while (macro_it.next()) |kv| { - if (getFnDecl(c, kv.value)) |proto_node| { + if (getFnProto(c, kv.value)) |proto_node| { // If a macro aliases a global variable which is a function pointer, we conclude that // the macro is intended to represent a function that assumes the function pointer // variable is non-null and calls it. diff --git a/test/translate_c.zig b/test/translate_c.zig index 5e5eccb60d..517a190756 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -887,7 +887,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const a = enum_unnamed_1.a; \\pub const b = enum_unnamed_1.b; \\pub const c = enum_unnamed_1.c; - \\pub const enum_unnamed_1 = extern enum { + \\const enum_unnamed_1 = extern enum { \\ a, \\ b, \\ c, @@ -896,7 +896,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const e = enum_unnamed_2.e; \\pub const f = enum_unnamed_2.f; \\pub const g = enum_unnamed_2.g; - \\pub const enum_unnamed_2 = extern enum { + \\const enum_unnamed_2 = extern enum { \\ e = 0, \\ f = 4, \\ g = 5, @@ -905,7 +905,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const i = enum_unnamed_3.i; \\pub const j = enum_unnamed_3.j; \\pub const k = enum_unnamed_3.k; - \\pub const enum_unnamed_3 = extern enum { + \\const enum_unnamed_3 = extern enum { \\ i, \\ j, \\ k, @@ -1027,10 +1027,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub extern var glProcs: union_OpenGLProcs; , \\pub const glClearPFN = PFNGLCLEARPROC; - // , // TODO - // \\pub inline fn glClearUnion(arg_1: GLbitfield) void { - // \\ return glProcs.gl.Clear.?(arg_1); - // \\} + , + \\pub inline fn glClearUnion(arg_2: GLbitfield) void { + \\ return glProcs.gl.Clear.?(arg_2); + \\} , \\pub const OpenGLProcs = union_OpenGLProcs; }); @@ -1348,7 +1348,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub const One = enum_unnamed_1.One; \\pub const Two = enum_unnamed_1.Two; - \\pub const enum_unnamed_1 = extern enum { + \\const enum_unnamed_1 = extern enum { \\ One, \\ Two, \\}; From b7f18164f96f7e695d295c12c06fddf25803b08b Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 19 Dec 2019 21:30:51 +0200 Subject: [PATCH 30/37] translate-c-2 add missing casts --- src-self-hosted/translate_c.zig | 48 ++++++++++- test/translate_c.zig | 143 ++++++++++++++++++++++---------- 2 files changed, 145 insertions(+), 46 deletions(-) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index aa7fe335ae..aa6f06db39 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -1210,10 +1210,10 @@ fn transImplicitCastExpr( const c = rp.c; const sub_expr = ZigClangImplicitCastExpr_getSubExpr(expr); const sub_expr_node = try transExpr(rp, scope, @ptrCast(*const ZigClangExpr, sub_expr), .used, .r_value); + const dest_type = getExprQualType(c, @ptrCast(*const ZigClangExpr, expr)); + const src_type = getExprQualType(c, sub_expr); switch (ZigClangImplicitCastExpr_getCastKind(expr)) { .BitCast, .FloatingCast, .FloatingToIntegral, .IntegralToFloating, .IntegralCast => { - const dest_type = getExprQualType(c, @ptrCast(*const ZigClangExpr, expr)); - const src_type = getExprQualType(c, sub_expr); return transCCast(rp, scope, ZigClangImplicitCastExpr_getBeginLoc(expr), dest_type, src_type, sub_expr_node); }, .LValueToRValue, .NoOp, .FunctionToPointerDecay, .ArrayToPointerDecay => { @@ -1222,6 +1222,47 @@ fn transImplicitCastExpr( .NullToPointer => { return try transCreateNodeNullLiteral(rp.c); }, + .PointerToBoolean => { + // @ptrToInt(val) != 0 + const ptr_to_int = try transCreateNodeBuiltinFnCall(rp.c, "@ptrToInt"); + try ptr_to_int.params.push(try transExpr(rp, scope, sub_expr, .used, .r_value)); + ptr_to_int.rparen_token = try appendToken(rp.c, .RParen, ")"); + + const op_token = try appendToken(rp.c, .BangEqual, "!="); + const rhs_node = try transCreateNodeInt(rp.c, 0); + return transCreateNodeInfixOp(rp, scope, &ptr_to_int.base, .BangEqual, op_token, rhs_node, result_used, false); + }, + .IntegralToBoolean => { + // val != 0 + const node = try transExpr(rp, scope, sub_expr, .used, .r_value); + + const op_token = try appendToken(rp.c, .BangEqual, "!="); + const rhs_node = try transCreateNodeInt(rp.c, 0); + return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, result_used, false); + }, + .PointerToIntegral => { + // @intCast(dest_type, @ptrToInt(val)) + const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@intCast"); + try cast_node.params.push(try transQualType(rp, dest_type, ZigClangImplicitCastExpr_getBeginLoc(expr))); + _ = try appendToken(rp.c, .Comma, ","); + + const ptr_to_int = try transCreateNodeBuiltinFnCall(rp.c, "@ptrToInt"); + try ptr_to_int.params.push(try transExpr(rp, scope, sub_expr, .used, .r_value)); + ptr_to_int.rparen_token = try appendToken(rp.c, .RParen, ")"); + try cast_node.params.push(&ptr_to_int.base); + cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return maybeSuppressResult(rp, scope, result_used, &cast_node.base); + }, + .IntegralToPointer => { + // @intToPtr(dest_type, val) + const int_to_ptr = try transCreateNodeBuiltinFnCall(rp.c, "@intToPtr"); + try int_to_ptr.params.push(try transQualType(rp, dest_type, ZigClangImplicitCastExpr_getBeginLoc(expr))); + _ = try appendToken(rp.c, .Comma, ","); + + try int_to_ptr.params.push(try transExpr(rp, scope, sub_expr, .used, .r_value)); + int_to_ptr.rparen_token = try appendToken(rp.c, .RParen, ")"); + return maybeSuppressResult(rp, scope, result_used, &int_to_ptr.base); + }, else => |kind| return revertAndWarn( rp, error.UnsupportedTranslation, @@ -2224,6 +2265,9 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true); return &op_node.base; }, + .Extension => { + return transExpr(rp, scope, ZigClangUnaryOperator_getSubExpr(stmt), used, .l_value); + }, else => return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "unsupported C translation {}", .{ZigClangUnaryOperator_getOpcode(stmt)}), } } diff --git a/test/translate_c.zig b/test/translate_c.zig index 517a190756..9bafe90d2a 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -819,6 +819,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.addC("__extension__ cast", + \\int foo(void) { + \\ return __extension__ 1; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ return 1; + \\} + }); + /////////////// Cases that pass for only stage2 //////////////// cases.add_2("Parameterless function prototypes", @@ -2031,43 +2041,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - /////////////// 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" - \\#define FOO_CHAR '\xfF' - , &[_][]const u8{ - \\pub const FOO = "aoeu\xab derp"; - , - \\pub const FOO2 = "aoeuz derp"; - , - \\pub const FOO_CHAR = 255; - }); - - cases.add("macro defines string literal with octal", - \\#define FOO "aoeu\023 derp" - \\#define FOO2 "aoeu\0234 derp" - \\#define FOO_CHAR '\077' - , &[_][]const u8{ - \\pub const FOO = "aoeu\x13 derp"; - , - \\pub const FOO2 = "aoeu\x134 derp"; - , - \\pub const FOO_CHAR = 63; - }); - - cases.addC("__extension__ cast", - \\int foo(void) { - \\ return __extension__ 1; - \\} - , &[_][]const u8{ - \\pub export fn foo() c_int { - \\ return 1; - \\} - }); - - cases.addC("implicit casts", + cases.add_2("implicit casts", \\#include \\ \\void fn_int(int x); @@ -2103,25 +2077,51 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub extern fn fn_bool(x: bool) void; \\pub extern fn fn_ptr(x: ?*c_void) void; \\pub export fn call(q: c_int) void { - \\ fn_int(@floatToInt(c_int, 3.000000)); - \\ fn_int(@floatToInt(c_int, 3.000000)); - \\ fn_int(@floatToInt(c_int, 3.000000)); + \\ fn_int(@floatToInt(c_int, 3)); + \\ fn_int(@floatToInt(c_int, 3)); + \\ fn_int(@floatToInt(c_int, 3)); \\ fn_int(1094861636); \\ fn_f32(@intToFloat(f32, 3)); \\ fn_f64(@intToFloat(f64, 3)); \\ fn_char(@as(u8, '3')); \\ fn_char(@as(u8, '\x01')); \\ fn_char(@as(u8, 0)); - \\ fn_f32(3.000000); - \\ fn_f64(3.000000); - \\ fn_bool(true); - \\ fn_bool(false); + \\ fn_f32(3); + \\ fn_f64(3); + \\ fn_bool(123 != 0); + \\ fn_bool(0 != 0); \\ fn_bool(@ptrToInt(&fn_int) != 0); \\ fn_int(@intCast(c_int, @ptrToInt(&fn_int))); \\ fn_ptr(@intToPtr(?*c_void, 42)); \\} }); + /////////////// 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" + \\#define FOO_CHAR '\xfF' + , &[_][]const u8{ + \\pub const FOO = "aoeu\xab derp"; + , + \\pub const FOO2 = "aoeuz derp"; + , + \\pub const FOO_CHAR = 255; + }); + + cases.add("macro defines string literal with octal", + \\#define FOO "aoeu\023 derp" + \\#define FOO2 "aoeu\0234 derp" + \\#define FOO_CHAR '\077' + , &[_][]const u8{ + \\pub const FOO = "aoeu\x13 derp"; + , + \\pub const FOO2 = "aoeu\x134 derp"; + , + \\pub const FOO_CHAR = 63; + }); + if (builtin.os != builtin.Os.windows) { // sysv_abi not currently supported on windows cases.add("Macro qualified functions", @@ -2990,4 +2990,59 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ }); \\} }); + + cases.addC("implicit casts", + \\#include + \\ + \\void fn_int(int x); + \\void fn_f32(float x); + \\void fn_f64(double x); + \\void fn_char(char x); + \\void fn_bool(bool x); + \\void fn_ptr(void *x); + \\ + \\void call(int q) { + \\ fn_int(3.0f); + \\ fn_int(3.0); + \\ fn_int(3.0L); + \\ fn_int('ABCD'); + \\ fn_f32(3); + \\ fn_f64(3); + \\ fn_char('3'); + \\ fn_char('\x1'); + \\ fn_char(0); + \\ fn_f32(3.0f); + \\ fn_f64(3.0); + \\ fn_bool(123); + \\ fn_bool(0); + \\ fn_bool(&fn_int); + \\ fn_int(&fn_int); + \\ fn_ptr(42); + \\} + , &[_][]const u8{ + \\pub extern fn fn_int(x: c_int) void; + \\pub extern fn fn_f32(x: f32) void; + \\pub extern fn fn_f64(x: f64) void; + \\pub extern fn fn_char(x: u8) void; + \\pub extern fn fn_bool(x: bool) void; + \\pub extern fn fn_ptr(x: ?*c_void) void; + \\pub export fn call(q: c_int) void { + \\ fn_int(@floatToInt(c_int, 3.000000)); + \\ fn_int(@floatToInt(c_int, 3.000000)); + \\ fn_int(@floatToInt(c_int, 3.000000)); + \\ fn_int(1094861636); + \\ fn_f32(@intToFloat(f32, 3)); + \\ fn_f64(@intToFloat(f64, 3)); + \\ fn_char(@as(u8, '3')); + \\ fn_char(@as(u8, '\x01')); + \\ fn_char(@as(u8, 0)); + \\ fn_f32(3.000000); + \\ fn_f64(3.000000); + \\ fn_bool(true); + \\ fn_bool(false); + \\ fn_bool(@ptrToInt(&fn_int) != 0); + \\ fn_int(@intCast(c_int, @ptrToInt(&fn_int))); + \\ fn_ptr(@intToPtr(?*c_void, 42)); + \\} + }); } From d172a7335c5b943102e2c9e70c1bbdfa20eec955 Mon Sep 17 00:00:00 2001 From: Vexu Date: Fri, 20 Dec 2019 00:10:25 +0200 Subject: [PATCH 31/37] translate-c-2 copy parametrs to stack --- lib/std/zig/ast.zig | 8 +- lib/std/zig/parse.zig | 4 +- src-self-hosted/translate_c.zig | 130 ++++++++-------- test/translate_c.zig | 254 ++++++++++++++++++++------------ 4 files changed, 227 insertions(+), 169 deletions(-) diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 35353f419c..9e891d1af6 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -1456,8 +1456,8 @@ pub const Node = struct { LessThan, MergeErrorSets, Mod, - Mult, - MultWrap, + Mul, + MulWrap, Period, Range, Sub, @@ -1514,8 +1514,8 @@ pub const Node = struct { Op.LessThan, Op.MergeErrorSets, Op.Mod, - Op.Mult, - Op.MultWrap, + Op.Mul, + Op.MulWrap, Op.Period, Op.Range, Op.Sub, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 4447e96032..8782f87598 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -2120,11 +2120,11 @@ fn parseMultiplyOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = nextToken(it); const op = switch (token.ptr.id) { .PipePipe => ops{ .BoolOr = {} }, - .Asterisk => ops{ .Mult = {} }, + .Asterisk => ops{ .Mul = {} }, .Slash => ops{ .Div = {} }, .Percent => ops{ .Mod = {} }, .AsteriskAsterisk => ops{ .ArrayMult = {} }, - .AsteriskPercent => ops{ .MultWrap = {} }, + .AsteriskPercent => ops{ .MulWrap = {} }, else => { putBackToken(it, token.index); return null; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index aa6f06db39..eec5c48e63 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -49,7 +49,6 @@ const Scope = struct { Block, Root, Condition, - FnDef, Loop, }; @@ -121,39 +120,6 @@ const Scope = struct { } }; - const FnDef = struct { - base: Scope, - params: AliasList, - - fn init(c: *Context) FnDef { - return .{ - .base = .{ - .id = .FnDef, - .parent = &c.global_scope.base, - }, - .params = AliasList.init(c.a()), - }; - } - - fn getAlias(scope: *FnDef, name: []const u8) ?[]const u8 { - var it = scope.params.iterator(0); - while (it.next()) |p| { - if (mem.eql(u8, p.name, name)) - return p.alias; - } - return scope.base.parent.?.getAlias(name); - } - - fn contains(scope: *FnDef, name: []const u8) bool { - var it = scope.params.iterator(0); - while (it.next()) |p| { - if (mem.eql(u8, p.name, name)) - return true; - } - return scope.base.parent.?.contains(name); - } - }; - fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block { var scope = inner; while (true) { @@ -179,7 +145,6 @@ const Scope = struct { fn getAlias(scope: *Scope, name: []const u8) ?[]const u8 { return switch (scope.id) { .Root => null, - .FnDef => @fieldParentPtr(FnDef, "base", scope).getAlias(name), .Block => @fieldParentPtr(Block, "base", scope).getAlias(name), .Switch, .Loop, .Condition => scope.parent.?.getAlias(name), }; @@ -188,7 +153,6 @@ const Scope = struct { fn contains(scope: *Scope, name: []const u8) bool { return switch (scope.id) { .Root => @fieldParentPtr(Root, "base", scope).contains(name), - .FnDef => @fieldParentPtr(FnDef, "base", scope).contains(name), .Block => @fieldParentPtr(Block, "base", scope).contains(name), .Switch, .Loop, .Condition => scope.parent.?.contains(name), }; @@ -198,7 +162,7 @@ const Scope = struct { var scope = inner; while (true) { switch (scope.id) { - .FnDef => unreachable, + .Root => unreachable, .Switch => return scope, .Loop => return scope, else => scope = scope.parent.?, @@ -210,7 +174,7 @@ const Scope = struct { var scope = inner; while (true) { switch (scope.id) { - .FnDef => unreachable, + .Root => unreachable, .Switch => return @fieldParentPtr(Switch, "base", scope), else => scope = scope.parent.?, } @@ -382,17 +346,12 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { const fn_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, fn_decl))); _ = try c.decl_table.put(@ptrToInt(fn_decl), fn_name); const fn_decl_loc = ZigClangFunctionDecl_getLocation(fn_decl); - const fn_qt = ZigClangFunctionDecl_getType(fn_decl); - const fn_type = ZigClangQualType_getTypePtr(fn_qt); - var fndef_scope = Scope.FnDef.init(c); - var scope = &fndef_scope.base; const has_body = ZigClangFunctionDecl_hasBody(fn_decl); const storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl); const decl_ctx = FnDeclContext{ .fn_name = fn_name, .has_body = has_body, .storage_class = storage_class, - .scope = &scope, .is_export = switch (storage_class) { .None => has_body, .Extern, .Static => false, @@ -402,6 +361,15 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { else => unreachable, }, }; + + var fn_qt = ZigClangFunctionDecl_getType(fn_decl); + var fn_type = ZigClangQualType_getTypePtr(fn_qt); + if (ZigClangType_getTypeClass(fn_type) == .Attributed) { + const attr_type = @ptrCast(*const ZigClangAttributedType, fn_type); + fn_qt = ZigClangAttributedType_getEquivalentType(attr_type); + fn_type = ZigClangQualType_getTypePtr(fn_qt); + } + const proto_node = switch (ZigClangType_getTypeClass(fn_type)) { .FunctionProto => blk: { const fn_proto_type = @ptrCast(*const ZigClangFunctionProtoType, fn_type); @@ -431,15 +399,39 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { // actual function definition with body const body_stmt = ZigClangFunctionDecl_getBody(fn_decl); - const body_node = transStmt(rp, scope, body_stmt, .unused, .r_value) catch |err| switch (err) { + const block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, null); + var scope = &block_scope.base; + const block_node = try transCreateNodeBlock(rp.c, null); + block_scope.block_node = block_node; + + var it = proto_node.params.iterator(0); + while (it.next()) |p| { + const param = @fieldParentPtr(ast.Node.ParamDecl, "base", p.*); + const param_name = tokenSlice(c, param.name_token.?); + + const checked_param_name = if (try scope.createAlias(rp.c, param_name)) |a| blk: { + try block_scope.variables.push(.{ .name = param_name, .alias = a }); + break :blk a; + } else param_name; + const arg_name = try std.fmt.allocPrint(c.a(), "_arg_{}", .{checked_param_name}); + + const node = try transCreateNodeVarDecl(c, false, false, checked_param_name); + node.eq_token = try appendToken(c, .Equal, "="); + node.init_node = try transCreateNodeIdentifier(c, arg_name); + node.semicolon_token = try appendToken(c, .Semicolon, ";"); + try block_node.statements.push(&node.base); + param.name_token = try appendIdentifier(c, arg_name); + _ = try appendToken(c, .Colon, ":"); + } + + transCompoundStmtInline(rp, &block_scope.base, @ptrCast(*const ZigClangCompoundStmt, body_stmt), block_node) catch |err| switch (err) { error.OutOfMemory => |e| return e, error.UnsupportedTranslation, error.UnsupportedType, => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function", .{}), }; - assert(body_node.id == .Block); - proto_node.body_node = body_node; - + block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); + proto_node.body_node = &block_node.base; return addTopLevelDecl(c, fn_name, &proto_node.base); } @@ -614,7 +606,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?* const name = try std.fmt.allocPrint(c.a(), "{}_{}", .{ container_kind_name, bare_name }); _ = try c.decl_table.put(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)), name); - const node = try transCreateNodeVarDecl(c, true, true, name); + const node = try transCreateNodeVarDecl(c, !is_unnamed, true, name); node.eq_token = try appendToken(c, .Equal, "="); @@ -1020,10 +1012,10 @@ fn transBinaryOperator( .Mul => { if (cIsUnsignedInteger(qt)) { op_token = try appendToken(rp.c, .AsteriskPercent, "*%"); - op_id = .MultWrap; + op_id = .MulWrap; } else { op_token = try appendToken(rp.c, .Asterisk, "*"); - op_id = .Mult; + op_id = .Mul; } }, .Div => { @@ -1287,8 +1279,16 @@ fn transBoolExpr( undefined; var res = try transExpr(rp, scope, expr, used, lrvalue); - if (isBoolRes(res)) + if (isBoolRes(res)) { + if (!grouped and res.id == .GroupedExpression) { + const group = @fieldParentPtr(ast.Node.GroupedExpression, "base", res); + res = group.expr; + // get zig fmt to work properly + tokenSlice(rp.c, group.lparen)[0] = ')'; + } return res; + } + const ty = ZigClangQualType_getTypePtr(getExprQualTypeBeforeImplicitCast(rp.c, expr)); const node = try finishBoolExpr(rp, scope, ZigClangExpr_getBeginLoc(expr), ty, res, used); @@ -2399,9 +2399,9 @@ fn transCreatePostCrement( fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundAssignOperator, used: ResultUsed) TransError!*ast.Node { switch (ZigClangCompoundAssignOperator_getOpcode(stmt)) { .MulAssign => if (qualTypeHaswrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt))) - return transCreateCompoundAssign(rp, scope, stmt, .AssignMulWrap, .AsteriskPercentEqual, "*%=", .MultWrap, .AsteriskPercent, "*%", used) + return transCreateCompoundAssign(rp, scope, stmt, .AssignMulWrap, .AsteriskPercentEqual, "*%=", .MulWrap, .AsteriskPercent, "*%", used) else - return transCreateCompoundAssign(rp, scope, stmt, .AssignMul, .AsteriskEqual, "*=", .Mult, .Asterisk, "*", used), + return transCreateCompoundAssign(rp, scope, stmt, .AssignMul, .AsteriskEqual, "*=", .Mul, .Asterisk, "*", used), .AddAssign => if (qualTypeHaswrappingOverflow(ZigClangCompoundAssignOperator_getType(stmt))) return transCreateCompoundAssign(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", .AddWrap, .PlusPercent, "+%", used) else @@ -3688,7 +3688,6 @@ const FnDeclContext = struct { fn_name: []const u8, has_body: bool, storage_class: ZigClangStorageClass, - scope: **Scope, is_export: bool, }; @@ -3754,9 +3753,6 @@ fn finishTransFnProto( // TODO check for always_inline attribute // TODO check for align attribute - var fndef_scope = Scope.FnDef.init(rp.c); - const scope = &fndef_scope.base; - // pub extern fn name(...) T const pub_tok = if (is_pub) try appendToken(rp.c, .Keyword_pub, "pub") else null; const cc_tok = if (cc == .Stdcall) try appendToken(rp.c, .Keyword_stdcallcc, "stdcallcc") else null; @@ -3782,15 +3778,11 @@ fn finishTransFnProto( const param_name_tok: ?ast.TokenIndex = blk: { if (fn_decl != null) { const param = ZigClangFunctionDecl_getParamDecl(fn_decl.?, @intCast(c_uint, i)); - var param_name: []const u8 = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, param))); + const param_name: []const u8 = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, param))); if (param_name.len < 1) - param_name = try std.fmt.allocPrint(rp.c.a(), "arg_{}", .{rp.c.getMangle()}); - const checked_param_name = if (try scope.createAlias(rp.c, param_name)) |a| blk: { - try fndef_scope.params.push(.{ .name = param_name, .alias = a }); - break :blk a; - } else param_name; + break :blk null; - const result = try appendIdentifier(rp.c, checked_param_name); + const result = try appendIdentifier(rp.c, param_name); _ = try appendToken(rp.c, .Colon, ":"); break :blk result; } @@ -4124,8 +4116,8 @@ fn transMacroDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void { const rp = makeRestorePoint(c); - var fndef_scope = Scope.FnDef.init(c); - const scope = &fndef_scope.base; + const block_scope = try Scope.Block.init(c, &c.global_scope.base, null); + const scope = &block_scope.base; const pub_tok = try appendToken(c, .Keyword_pub, "pub"); const inline_tok = try appendToken(c, .Keyword_inline, "inline"); @@ -4143,7 +4135,7 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u return error.ParseError; const checked_name = if (try scope.createAlias(c, param_tok.bytes)) |alias| blk: { - try fndef_scope.params.push(.{ .name = param_tok.bytes, .alias = alias }); + try block_scope.variables.push(.{ .name = param_tok.bytes, .alias = alias }); break :blk alias; } else param_tok.bytes; @@ -4521,9 +4513,9 @@ fn parseCPrefixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc } } -fn tokenSlice(c: *Context, token: ast.TokenIndex) []const u8 { +fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 { const tok = c.tree.tokens.at(token); - return c.source_buffer.toSliceConst()[tok.start..tok.end]; + return c.source_buffer.toSlice()[tok.start..tok.end]; } fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { diff --git a/test/translate_c.zig b/test/translate_c.zig index 9bafe90d2a..796fc84213 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -127,16 +127,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { }); cases.addC_both("add, sub, mul, div, rem", - \\int s(int a, int b) { - \\ int c; + \\int s() { + \\ int a, b, c; \\ c = a + b; \\ c = a - b; \\ c = a * b; \\ c = a / b; \\ c = a % b; \\} - \\unsigned u(unsigned a, unsigned b) { - \\ unsigned c; + \\unsigned u() { + \\ unsigned a, b, c; \\ c = a + b; \\ c = a - b; \\ c = a * b; @@ -144,7 +144,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ c = a % b; \\} , &[_][]const u8{ - \\pub export fn s(a: c_int, b: c_int) c_int { + \\pub export fn s() c_int { + \\ var a: c_int = undefined; + \\ var b: c_int = undefined; \\ var c: c_int = undefined; \\ c = (a + b); \\ c = (a - b); @@ -152,7 +154,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ c = @divTrunc(a, b); \\ c = @rem(a, b); \\} - \\pub export fn u(a: c_uint, b: c_uint) c_uint { + \\pub export fn u() c_uint { + \\ var a: c_uint = undefined; + \\ var b: c_uint = undefined; \\ var c: c_uint = undefined; \\ c = (a +% b); \\ c = (a -% b); @@ -544,21 +548,25 @@ pub fn addCases(cases: *tests.TranslateCContext) void { }); cases.addC_both("void cast", - \\void foo(int a) { + \\void foo() { + \\ int a; \\ (void) a; \\} , &[_][]const u8{ - \\pub export fn foo(a: c_int) void { + \\pub export fn foo() void { + \\ var a: c_int = undefined; \\ _ = a; \\} }); cases.addC_both("implicit cast to void *", - \\void *foo(unsigned short *x) { + \\void *foo() { + \\ unsigned short *x; \\ return x; \\} , &[_][]const u8{ - \\pub export fn foo(x: [*c]c_ushort) ?*c_void { + \\pub export fn foo() ?*c_void { + \\ var x: [*c]c_ushort = undefined; \\ return @ptrCast(?*c_void, x); \\} }); @@ -659,11 +667,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { }); cases.addC_both("pointer casting", - \\float *ptrcast(int *a) { + \\float *ptrcast() { + \\ int *a; \\ return (float *)a; \\} , &[_][]const u8{ - \\pub export fn ptrcast(a: [*c]c_int) [*c]f32 { + \\pub export fn ptrcast() [*c]f32 { + \\ var a: [*c]c_int = undefined; \\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a)); \\} }); @@ -702,29 +712,21 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC_both("function call", - \\static void bar(void) { } - \\void foo(int *(baz)(void)) { - \\ bar(); - \\ baz(); - \\} - , &[_][]const u8{ - \\pub fn bar() void {} - \\pub export fn foo(baz: ?extern fn () [*c]c_int) void { - \\ bar(); - \\ _ = baz.?(); - \\} - }); - cases.addC_both("while on non-bool", - \\int while_none_bool(int a, float b, void *c) { + \\int while_none_bool() { + \\ int a; + \\ float b; + \\ void *c; \\ while (a) return 0; \\ while (b) return 1; \\ while (c) return 2; \\ return 3; \\} , &[_][]const u8{ - \\pub export fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\pub export fn while_none_bool() c_int { + \\ var a: c_int = undefined; + \\ var b: f32 = undefined; + \\ var c: ?*c_void = undefined; \\ while (a != 0) return 0; \\ while (b != 0) return 1; \\ while (c != null) return 2; @@ -733,14 +735,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void { }); cases.addC_both("for on non-bool", - \\int for_none_bool(int a, float b, void *c) { + \\int for_none_bool() { + \\ int a; + \\ float b; + \\ void *c; \\ for (;a;) return 0; \\ for (;b;) return 1; \\ for (;c;) return 2; \\ return 3; \\} , &[_][]const u8{ - \\pub export fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\pub export fn for_none_bool() c_int { + \\ var a: c_int = undefined; + \\ var b: f32 = undefined; + \\ var c: ?*c_void = undefined; \\ while (a != 0) return 0; \\ while (b != 0) return 1; \\ while (c != null) return 2; @@ -770,11 +778,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { }); cases.addC_both("normal deref", - \\void foo(int *x) { + \\void foo() { + \\ int *x; \\ *x = 1; \\} , &[_][]const u8{ - \\pub export fn foo(x: [*c]c_int) void { + \\pub export fn foo() void { + \\ var x: [*c]c_int = undefined; \\ x.?.* = 1; \\} }); @@ -794,24 +804,32 @@ pub fn addCases(cases: *tests.TranslateCContext) void { }); cases.addC_both("bin not", - \\int foo(int x) { + \\int foo() { + \\ int x; \\ return ~x; \\} , &[_][]const u8{ - \\pub export fn foo(x: c_int) c_int { + \\pub export fn foo() c_int { + \\ var x: c_int = undefined; \\ return ~x; \\} }); cases.addC_both("bool not", - \\int foo(int a, float b, void *c) { + \\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 { + \\pub export fn foo() c_int { + \\ var a: c_int = undefined; + \\ var b: f32 = undefined; + \\ var c: ?*c_void = undefined; \\ return !(a == 0); \\ return !(a != 0); \\ return !(b != 0); @@ -829,6 +847,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + if (builtin.os != builtin.Os.windows) { + // sysv_abi not currently supported on windows + cases.add_both("Macro qualified functions", + \\void __attribute__((sysv_abi)) foo(void); + , &[_][]const u8{ + \\pub extern fn foo() void; + }); + } + /////////////// Cases that pass for only stage2 //////////////// cases.add_2("Parameterless function prototypes", @@ -1027,7 +1054,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const GLbitfield = c_uint; \\pub const PFNGLCLEARPROC = ?extern fn (GLbitfield) void; \\pub const OpenGLProc = ?extern fn () void; - \\pub const struct_unnamed_1 = extern struct { + \\const struct_unnamed_1 = extern struct { \\ Clear: PFNGLCLEARPROC, \\}; \\pub const union_OpenGLProcs = extern union { @@ -1088,7 +1115,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub var a: c_long = @as(c_long, 2); \\pub var b: c_long = @as(c_long, 2); \\pub var c: c_int = 4; - \\pub export fn foo(c_1: u8) void { + \\pub export fn foo(_arg_c_1: u8) void { + \\ var c_1 = _arg_c_1; \\ var a_2: c_int = undefined; \\ var b_3: u8 = @as(u8, 123); \\ b_3 = @as(u8, a_2); @@ -1105,7 +1133,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 2, 4, 6; \\} , &[_][]const u8{ - \\pub export fn foo(c: u8) c_int { + \\pub export fn foo(_arg_c: u8) c_int { + \\ var c = _arg_c; \\ _ = 2; \\ _ = 4; \\ _ = 2; @@ -1121,7 +1150,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ a = b = 2; \\} , &[_][]const u8{ - \\pub export fn foo(c: u8) c_int { + \\pub export fn foo(_arg_c: u8) c_int { + \\ var c = _arg_c; \\ var a: c_int = undefined; \\ var b: c_int = undefined; \\ a = blk: { @@ -1142,7 +1172,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ } \\} , &[_][]const u8{ - \\pub export fn foo(c: u8) c_int { + \\pub export fn foo(_arg_c: u8) c_int { + \\ var c = _arg_c; \\ if (2 != 0) { \\ var a: c_int = 2; \\ } @@ -1265,7 +1296,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ } \\} , &[_][]const u8{ - \\pub export fn switch_fn(i: c_int) c_int { + \\pub export fn switch_fn(_arg_i: c_int) c_int { + \\ var i = _arg_i; \\ var res: c_int = 0; \\ __switch: { \\ __case_2: { @@ -1316,7 +1348,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add_2("restrict -> noalias", \\void foo(void *restrict bar, void *restrict); , &[_][]const u8{ - \\pub extern fn foo(noalias bar: ?*c_void, noalias arg_1: ?*c_void) void; + \\pub extern fn foo(noalias bar: ?*c_void, noalias ?*c_void) void; }); cases.add_2("assign", @@ -1326,7 +1358,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ a = tmp; \\} , &[_][]const u8{ - \\pub export fn max(a: c_int) c_int { + \\pub export fn max(_arg_a: c_int) c_int { + \\ var a = _arg_a; \\ var tmp: c_int = undefined; \\ tmp = a; \\ a = tmp; @@ -1339,7 +1372,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ c = b = a; \\} , &[_][]const u8{ - \\pub export fn max(a: c_int) void { + \\pub export fn max(_arg_a: c_int) void { + \\ var a = _arg_a; \\ var b: c_int = undefined; \\ var c: c_int = undefined; \\ c = blk: { @@ -1369,7 +1403,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return (int)a; \\} , &[_][]const u8{ - \\pub export fn float_to_int(a: f32) c_int { + \\pub export fn float_to_int(_arg_a: f32) c_int { + \\ var a = _arg_a; \\ return @floatToInt(c_int, a); \\} }); @@ -1434,19 +1469,6 @@ 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 `>>=` is 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: u32) u32 { - \\ var b: u32 = a >> @as(u5, 1); - \\ return b; - \\} - }); - cases.add_2("logical and, logical or, on non-bool values, extra parens", \\enum Foo { \\ FooA, @@ -1480,7 +1502,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ C, \\}; \\pub const SomeTypedef = c_int; - \\pub export fn and_or_non_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\pub export fn and_or_non_bool(_arg_a: c_int, _arg_b: f32, _arg_c: ?*c_void) c_int { + \\ var a = _arg_a; + \\ var b = _arg_b; + \\ var c = _arg_c; \\ var d: enum_Foo = @as(enum_Foo, FooA); \\ var e: c_int = @boolToInt(((a != 0) and (b != 0))); \\ var f: c_int = @boolToInt(((b != 0) and (c != null))); @@ -1532,12 +1557,14 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return (a & b) ^ (a | b); \\} , &[_][]const u8{ - \\pub export fn max(a: c_int, b: c_int) c_int { + \\pub export fn max(_arg_a: c_int, _arg_b: c_int) c_int { + \\ var a = _arg_a; + \\ var b = _arg_b; \\ return ((a & b) ^ (a | b)); \\} }); - 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 + cases.add_2("comparison operators (no if)", // TODO Come up with less contrived tests? Make sure to cover all these comparisons. \\int test_comparisons(int a, int b) { \\ int c = (a < b); \\ int d = (a > b); @@ -1549,7 +1576,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return i; \\} , &[_][]const u8{ - \\pub export fn test_comparisons(a: c_int, b: c_int) c_int { + \\pub export fn test_comparisons(_arg_a: c_int, _arg_b: c_int) c_int { + \\ var a = _arg_a; + \\ var b = _arg_b; \\ var c: c_int = @boolToInt((a < b)); \\ var d: c_int = @boolToInt((a > b)); \\ var e: c_int = @boolToInt((a <= b)); @@ -1570,9 +1599,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return a; \\} , &[_][]const u8{ - \\pub export fn max(a: c_int, b: c_int) c_int { - \\ if ((a == b)) return a; - \\ if ((a != b)) return b; + \\pub export fn max(_arg_a: c_int, _arg_b: c_int) c_int { + \\ var a = _arg_a; + \\ var b = _arg_b; + \\ if (a == b) return a; + \\ if (a != b) return b; \\ return a; \\} }); @@ -1646,7 +1677,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} , &[_][]const u8{ \\pub export var array: [100]c_int = .{0} ** 100; - \\pub export fn foo(index: c_int) c_int { + \\pub export fn foo(_arg_index: c_int) c_int { + \\ var index = _arg_index; \\ return array[index]; \\} , @@ -1670,9 +1702,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return a; \\} , &[_][]const u8{ - \\pub export fn max(a: c_int, b: c_int) c_int { - \\ if (((a < b) or (a == b))) return b; - \\ if (((a >= b) and (a == b))) return a; + \\pub export fn max(_arg_a: c_int, _arg_b: c_int) c_int { + \\ var a = _arg_a; + \\ var b = _arg_b; + \\ if ((a < b) or (a == b)) return b; + \\ if ((a >= b) and (a == b)) return a; \\ return a; \\} }); @@ -1690,10 +1724,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ if (a < b) ; else ; \\} , &[_][]const u8{ - \\pub export fn max(a: c_int, b: c_int) c_int { - \\ if ((a < b)) return b; - \\ if ((a < b)) return b else return a; - \\ if ((a < b)) {} else {} + \\pub export fn max(_arg_a: c_int, _arg_b: c_int) c_int { + \\ var a = _arg_a; + \\ var b = _arg_b; + \\ if (a < b) return b; + \\ if (a < b) return b else return a; + \\ if (a < b) {} else {} \\} }); @@ -1715,7 +1751,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ B, \\ C, \\}; - \\pub export fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int { + \\pub export fn if_none_bool(_arg_a: c_int, _arg_b: f32, _arg_c: ?*c_void, _arg_d: enum_SomeEnum) c_int { + \\ var a = _arg_a; + \\ var b = _arg_b; + \\ var c = _arg_c; + \\ var d = _arg_d; \\ if (a != 0) return 0; \\ if (b != 0) return 1; \\ if (c != null) return 2; @@ -1741,8 +1781,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return a < 0 ? -a : a; \\} , &[_][]const u8{ - \\pub export fn abs(a: c_int) c_int { - \\ return if ((a < 0)) -a else a; + \\pub export fn abs(_arg_a: c_int) c_int { + \\ var a = _arg_a; + \\ return if (a < 0) -a else a; \\} }); @@ -1756,11 +1797,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return a; \\} , &[_][]const u8{ - \\pub export fn foo1(a: c_uint) c_uint { + \\pub export fn foo1(_arg_a: c_uint) c_uint { + \\ var a = _arg_a; \\ a +%= 1; \\ return a; \\} - \\pub export fn foo2(a: c_int) c_int { + \\pub export fn foo2(_arg_a: c_int) c_int { + \\ var a = _arg_a; \\ a += 1; \\ return a; \\} @@ -1848,10 +1891,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ } \\ return i; \\} - , &[_][]const u8{// TODO function arguments should be copied - \\pub export fn log2(a: c_uint) c_int { + , &[_][]const u8{ + \\pub export fn log2(_arg_a: c_uint) c_int { + \\ var a = _arg_a; \\ var i: c_int = 0; - \\ while ((a > @as(c_uint, 0))) { + \\ while (a > @as(c_uint, 0)) { \\ a >>= @as(@import("std").math.Log2Int(c_int), 1); \\ } \\ return i; @@ -1868,9 +1912,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return i; \\} , &[_][]const u8{ - \\pub export fn log2(a: u32) c_int { + \\pub export fn log2(_arg_a: u32) c_int { + \\ var a = _arg_a; \\ var i: c_int = 0; - \\ while ((a > @as(c_uint, 0))) { + \\ while (a > @as(c_uint, 0)) { \\ a >>= @as(@import("std").math.Log2Int(c_int), 1); \\ } \\ return i; @@ -2076,7 +2121,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub extern fn fn_char(x: u8) void; \\pub extern fn fn_bool(x: bool) void; \\pub extern fn fn_ptr(x: ?*c_void) void; - \\pub export fn call(q: c_int) void { + \\pub export fn call(_arg_q: c_int) void { + \\ var q = _arg_q; \\ fn_int(@floatToInt(c_int, 3)); \\ fn_int(@floatToInt(c_int, 3)); \\ fn_int(@floatToInt(c_int, 3)); @@ -2096,6 +2142,21 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("function call", + \\static void bar(void) { } + \\void foo(int *(baz)(void)) { + \\ bar(); + \\ baz(); + \\} + , &[_][]const u8{ + \\pub fn bar() void {} + \\pub export fn foo(_arg_baz: ?extern fn () [*c]c_int) void { + \\ var baz = _arg_baz; + \\ bar(); + \\ _ = baz.?(); + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// cases.add("macro defines string literal with hex", @@ -2122,15 +2183,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const FOO_CHAR = 63; }); - if (builtin.os != builtin.Os.windows) { - // sysv_abi not currently supported on windows - cases.add("Macro qualified functions", - \\void __attribute__((sysv_abi)) foo(void); - , &[_][]const u8{ - \\pub extern fn foo() void; - }); - } - /////////////// Cases for only stage1 because stage2 behavior is better //////////////// cases.addC("Parameterless function prototypes", \\void foo() {} @@ -3045,4 +3097,18 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ fn_ptr(@intToPtr(?*c_void, 42)); \\} }); + + cases.addC("function call", + \\static void bar(void) { } + \\void foo(int *(baz)(void)) { + \\ bar(); + \\ baz(); + \\} + , &[_][]const u8{ + \\pub fn bar() void {} + \\pub export fn foo(baz: ?extern fn () [*c]c_int) void { + \\ bar(); + \\ _ = baz.?(); + \\} + }); } From daeb93921049758f69474f8ad5b0d4b7a7d26a18 Mon Sep 17 00:00:00 2001 From: Vexu Date: Fri, 20 Dec 2019 11:35:21 +0200 Subject: [PATCH 32/37] translate-c-2 fix switch range --- src-self-hosted/translate_c.zig | 2 +- test/translate_c.zig | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index eec5c48e63..cd8a21162d 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -1945,7 +1945,7 @@ fn transCase( const expr = if (ZigClangCaseStmt_getRHS(stmt)) |rhs| blk: { const lhs_node = try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value); const ellips = try appendToken(rp.c, .Ellipsis3, "..."); - const rhs_node = try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value); + const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); const node = try rp.c.a().create(ast.Node.InfixOp); node.* = .{ diff --git a/test/translate_c.zig b/test/translate_c.zig index 796fc84213..c3174437c6 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1286,12 +1286,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ switch (i) { \\ case 0: \\ res = 1; - \\ case 1: + \\ case 1 ... 3: \\ res = 2; \\ default: \\ res = 3 * i; \\ break; - \\ case 2: + \\ case 4: \\ res = 5; \\ } \\} @@ -1306,9 +1306,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ __case_0: { \\ switch (i) { \\ 0 => break :__case_0, - \\ 1 => break :__case_1, + \\ 1...3 => break :__case_1, \\ else => break :__default, - \\ 2 => break :__case_2, + \\ 4 => break :__case_2, \\ } \\ } \\ res = 1; From e0046b737eddffe0d6881d202608769fe720ff94 Mon Sep 17 00:00:00 2001 From: Vexu Date: Fri, 20 Dec 2019 13:50:34 +0200 Subject: [PATCH 33/37] translate-c-2 improve macro escape sequences --- src-self-hosted/c_tokenizer.zig | 262 +++++++++++++++++++++++++------- test/translate_c.zig | 53 ++++--- 2 files changed, 236 insertions(+), 79 deletions(-) diff --git a/src-self-hosted/c_tokenizer.zig b/src-self-hosted/c_tokenizer.zig index 7685cdc537..075ea86a0b 100644 --- a/src-self-hosted/c_tokenizer.zig +++ b/src-self-hosted/c_tokenizer.zig @@ -74,69 +74,191 @@ fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken { } } else return tok; var bytes = try allocator.alloc(u8, tok.bytes.len * 2); - var escape = false; + var state: enum { + Start, + Escape, + Hex, + Octal, + HexZero, + OctalZero, + } = .Start; var i: usize = 0; + var count: u8 = 0; + var num: u8 = 0; for (tok.bytes) |c| { - if (escape) { - switch (c) { - 'n', 'r', 't', '\\', '\'', '\"', 'x' => { - bytes[i] = c; - }, - 'a' => { - bytes[i] = 'x'; - i += 1; - bytes[i] = '0'; - i += 1; - bytes[i] = '7'; - }, - 'b' => { - bytes[i] = 'x'; - i += 1; - bytes[i] = '0'; - i += 1; - bytes[i] = '8'; - }, - 'f' => { - bytes[i] = 'x'; - i += 1; - bytes[i] = '0'; - i += 1; - bytes[i] = 'C'; - }, - 'v' => { - bytes[i] = 'x'; - i += 1; - bytes[i] = '0'; - i += 1; - bytes[i] = 'B'; - }, - '?' => { - i -= 1; - bytes[i] = '?'; - }, - 'u', 'U' => { - // TODO unicode escape sequences - return error.TokenizingFailed; - }, - '0'...'7' => { - // TODO octal escape sequences - return error.TokenizingFailed; - }, - else => { - // unknown escape sequence - return error.TokenizingFailed; - }, - } - i += 1; - escape = false; - } else { - if (c == '\\') { - escape = true; - } - bytes[i] = c; - i += 1; + switch (state) { + .Escape => { + switch (c) { + 'n', 'r', 't', '\\', '\'', '\"' => { + bytes[i] = c; + }, + '0' => { + state = .OctalZero; + bytes[i] = 'x'; + }, + '1'...'7' => { + count += 1; + num *= 8; + num += c - '0'; + state = .Octal; + bytes[i] = 'x'; + }, + 'x' => { + state = .HexZero; + bytes[i] = c; + }, + 'a' => { + bytes[i] = 'x'; + i += 1; + bytes[i] = '0'; + i += 1; + bytes[i] = '7'; + }, + 'b' => { + bytes[i] = 'x'; + i += 1; + bytes[i] = '0'; + i += 1; + bytes[i] = '8'; + }, + 'f' => { + bytes[i] = 'x'; + i += 1; + bytes[i] = '0'; + i += 1; + bytes[i] = 'C'; + }, + 'v' => { + bytes[i] = 'x'; + i += 1; + bytes[i] = '0'; + i += 1; + bytes[i] = 'B'; + }, + '?' => { + i -= 1; + bytes[i] = '?'; + }, + 'u', 'U' => { + // TODO unicode escape sequences + return error.TokenizingFailed; + }, + else => { + // unknown escape sequence + return error.TokenizingFailed; + }, + } + i += 1; + if (state == .Escape) + state = .Start; + }, + .Start => { + if (c == '\\') { + state = .Escape; + } + bytes[i] = c; + i += 1; + }, + .HexZero => { + switch (c) { + '0' => { continue; }, + '1'...'9' => { + count += 1; + num *= 16; + num += c - '0'; + }, + 'a'...'f' => { + count += 1; + num *= 16; + num += c - 'a' + 10; + }, + 'A'...'F' => { + count += 1; + num *= 16; + num += c - 'A' + 10; + }, + else => {}, + } + state = .Hex; + }, + .Hex => { + switch (c) { + '0'...'9' => { + count += 1; + num *= 16; + num += c - '0'; + if (count < 2) + continue; + }, + 'a'...'f' => { + count += 1; + num *= 16; + num += c - 'a' + 10; + if (count < 2) + continue; + }, + 'A'...'F' => { + count += 1; + num *= 16; + num += c - 'A' + 10; + if (count < 2) + continue; + }, + else => {}, + } + i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2}); + switch (c) { + '\\' => state = .Escape, + '0'...'9', 'a'...'f','A'...'F' => state = .Start, + else => { + state = .Start; + bytes[i] = c; + i += 1; + }, + } + count = 0; + num = 0; + }, + .OctalZero => { + switch (c) { + '0' => { continue; }, + '1'...'7' => { + count += 1; + num *= 8; + num += c - '0'; + }, + else => {}, + } + state = .Octal; + }, + .Octal => { + switch (c) { + '0'...'7' => { + count += 1; + num *= 8; + num += c - '0'; + if (count < 3) + continue; + }, + else => {}, + } + i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2}); + switch (c) { + '\\' => state = .Escape, + '0'...'7' => state = .Start, + else => { + state = .Start; + bytes[i] = c; + i += 1; + }, + } + count = 0; + num = 0; + }, } } + if (state == .Hex or state == .Octal) + i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2}); return CToken{ .id = tok.id, .bytes = bytes[0..i], @@ -666,3 +788,25 @@ test "tokenize macro" { expect(it.next() == null); tl.shrink(0); } + +test "escape sequences" { + var buf: [1024]u8 = undefined; + var alloc = std.heap.FixedBufferAllocator.init(buf[0..]); + const a = &alloc.allocator; + expect(std.mem.eql(u8, (try zigifyEscapeSequences(a, .{ + .id = .StrLit, + .bytes = "\\x0077", + })).bytes, "\\x77")); + expect(std.mem.eql(u8, (try zigifyEscapeSequences(a, .{ + .id = .StrLit, + .bytes = "\\00245", + })).bytes, "\\xa5")); + expect(std.mem.eql(u8, (try zigifyEscapeSequences(a, .{ + .id = .StrLit, + .bytes = "\\x0077abc", + })).bytes, "\\x77abc")); + expect(std.mem.eql(u8, (try zigifyEscapeSequences(a, .{ + .id = .StrLit, + .bytes = "\\045abc", + })).bytes, "\\x25abc")); +} diff --git a/test/translate_c.zig b/test/translate_c.zig index c3174437c6..9892d66ad9 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1089,13 +1089,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add_2("macro escape sequences", + cases.add_2("macro defines string literal with hex", \\#define FOO "aoeu\xab derp" - \\#define FOO2 "aoeu\a derp" + \\#define FOO2 "aoeu\x0007a derp" + \\#define FOO_CHAR '\xfF' , &[_][]const u8{ \\pub const FOO = "aoeu\xab derp"; , - \\pub const FOO2 = "aoeu\x07 derp"; + \\pub const FOO2 = "aoeu\x7a derp"; + , + \\pub const FOO_CHAR = '\xff'; }); cases.add_2("variable aliasing", @@ -2157,30 +2160,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - /////////////// 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" - \\#define FOO_CHAR '\xfF' - , &[_][]const u8{ - \\pub const FOO = "aoeu\xab derp"; - , - \\pub const FOO2 = "aoeuz derp"; - , - \\pub const FOO_CHAR = 255; - }); - - cases.add("macro defines string literal with octal", + cases.add_2("macro defines string literal with octal", \\#define FOO "aoeu\023 derp" \\#define FOO2 "aoeu\0234 derp" \\#define FOO_CHAR '\077' , &[_][]const u8{ \\pub const FOO = "aoeu\x13 derp"; , - \\pub const FOO2 = "aoeu\x134 derp"; + \\pub const FOO2 = "aoeu\x9c derp"; , - \\pub const FOO_CHAR = 63; + \\pub const FOO_CHAR = '\x3f'; }); /////////////// Cases for only stage1 because stage2 behavior is better //////////////// @@ -3111,4 +3100,28 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ _ = baz.?(); \\} }); + + cases.add("macro defines string literal with hex", + \\#define FOO "aoeu\xab derp" + \\#define FOO2 "aoeu\x0007a derp" + \\#define FOO_CHAR '\xfF' + , &[_][]const u8{ + \\pub const FOO = "aoeu\xab derp"; + , + \\pub const FOO2 = "aoeuz derp"; + , + \\pub const FOO_CHAR = 255; + }); + + cases.add("macro defines string literal with octal", + \\#define FOO "aoeu\023 derp" + \\#define FOO2 "aoeu\0234 derp" + \\#define FOO_CHAR '\077' + , &[_][]const u8{ + \\pub const FOO = "aoeu\x13 derp"; + , + \\pub const FOO2 = "aoeu\x134 derp"; + , + \\pub const FOO_CHAR = 63; + }); } From 9437d99ae25c635239cdb113457053862b3339f8 Mon Sep 17 00:00:00 2001 From: Vexu Date: Fri, 20 Dec 2019 18:51:44 +0200 Subject: [PATCH 34/37] translate-c-2 final small fixes --- src-self-hosted/c_tokenizer.zig | 97 ++++++--------------------------- src-self-hosted/translate_c.zig | 10 ++-- test/translate_c.zig | 2 +- 3 files changed, 23 insertions(+), 86 deletions(-) diff --git a/src-self-hosted/c_tokenizer.zig b/src-self-hosted/c_tokenizer.zig index 075ea86a0b..3999645aee 100644 --- a/src-self-hosted/c_tokenizer.zig +++ b/src-self-hosted/c_tokenizer.zig @@ -79,8 +79,6 @@ fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken { Escape, Hex, Octal, - HexZero, - OctalZero, } = .Start; var i: usize = 0; var count: u8 = 0; @@ -92,20 +90,15 @@ fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken { 'n', 'r', 't', '\\', '\'', '\"' => { bytes[i] = c; }, - '0' => { - state = .OctalZero; - bytes[i] = 'x'; - }, - '1'...'7' => { + '0'...'7' => { count += 1; - num *= 8; num += c - '0'; state = .Octal; bytes[i] = 'x'; }, 'x' => { - state = .HexZero; - bytes[i] = c; + state = .Hex; + bytes[i] = 'x'; }, 'a' => { bytes[i] = 'x'; @@ -159,83 +152,37 @@ fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken { bytes[i] = c; i += 1; }, - .HexZero => { - switch (c) { - '0' => { continue; }, - '1'...'9' => { - count += 1; - num *= 16; - num += c - '0'; - }, - 'a'...'f' => { - count += 1; - num *= 16; - num += c - 'a' + 10; - }, - 'A'...'F' => { - count += 1; - num *= 16; - num += c - 'A' + 10; - }, - else => {}, - } - state = .Hex; - }, .Hex => { switch (c) { '0'...'9' => { - count += 1; - num *= 16; + num = std.math.mul(u8, num, 16) catch return error.TokenizingFailed; num += c - '0'; - if (count < 2) - continue; }, 'a'...'f' => { - count += 1; - num *= 16; + num = std.math.mul(u8, num, 16) catch return error.TokenizingFailed; num += c - 'a' + 10; - if (count < 2) - continue; }, 'A'...'F' => { - count += 1; - num *= 16; + num = std.math.mul(u8, num, 16) catch return error.TokenizingFailed; num += c - 'A' + 10; - if (count < 2) - continue; }, - else => {}, - } - i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2}); - switch (c) { - '\\' => state = .Escape, - '0'...'9', 'a'...'f','A'...'F' => state = .Start, else => { - state = .Start; + i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2}); + num = 0; + if (c == '\\') + state = .Escape + else + state = .Start; bytes[i] = c; i += 1; }, } - count = 0; - num = 0; - }, - .OctalZero => { - switch (c) { - '0' => { continue; }, - '1'...'7' => { - count += 1; - num *= 8; - num += c - '0'; - }, - else => {}, - } - state = .Octal; }, .Octal => { switch (c) { '0'...'7' => { count += 1; - num *= 8; + num = std.math.mul(u8, num, 8) catch return error.TokenizingFailed; num += c - '0'; if (count < 3) continue; @@ -243,15 +190,7 @@ fn zigifyEscapeSequences(allocator: *std.mem.Allocator, tok: CToken) !CToken { else => {}, } i += std.fmt.formatIntBuf(bytes[i..], num, 16, false, std.fmt.FormatOptions{.fill = '0', .width = 2}); - switch (c) { - '\\' => state = .Escape, - '0'...'7' => state = .Start, - else => { - state = .Start; - bytes[i] = c; - i += 1; - }, - } + state = .Start; count = 0; num = 0; }, @@ -799,12 +738,12 @@ test "escape sequences" { })).bytes, "\\x77")); expect(std.mem.eql(u8, (try zigifyEscapeSequences(a, .{ .id = .StrLit, - .bytes = "\\00245", - })).bytes, "\\xa5")); + .bytes = "\\24500", + })).bytes, "\\xa500")); expect(std.mem.eql(u8, (try zigifyEscapeSequences(a, .{ .id = .StrLit, - .bytes = "\\x0077abc", - })).bytes, "\\x77abc")); + .bytes = "\\x0077 abc", + })).bytes, "\\x77 abc")); expect(std.mem.eql(u8, (try zigifyEscapeSequences(a, .{ .id = .StrLit, .bytes = "\\045abc", diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index cd8a21162d..a093c7e705 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -340,11 +340,10 @@ fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void { } fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { - if (c.decl_table.contains(@ptrToInt(ZigClangFunctionDecl_getCanonicalDecl(fn_decl)))) + const fn_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, fn_decl))); + if (c.global_scope.sym_table.contains(fn_name)) return; // Avoid processing this decl twice const rp = makeRestorePoint(c); - const fn_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, fn_decl))); - _ = try c.decl_table.put(@ptrToInt(fn_decl), fn_name); const fn_decl_loc = ZigClangFunctionDecl_getLocation(fn_decl); const has_body = ZigClangFunctionDecl_hasBody(fn_decl); const storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl); @@ -436,7 +435,8 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { } fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { - if (c.decl_table.contains(@ptrToInt(ZigClangVarDecl_getCanonicalDecl(var_decl)))) + const var_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, var_decl))); + if (c.global_scope.sym_table.contains(var_name)) return; // Avoid processing this decl twice const rp = makeRestorePoint(c); const visib_tok = try appendToken(c, .Keyword_pub, "pub"); @@ -447,12 +447,10 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { try appendToken(c, .Keyword_threadlocal, "threadlocal"); const scope = &c.global_scope.base; - const var_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, var_decl))); // TODO https://github.com/ziglang/zig/issues/3756 // TODO https://github.com/ziglang/zig/issues/1802 const checked_name = if (isZigPrimitiveType(var_name)) try std.fmt.allocPrint(c.a(), "_{}", .{var_name}) else var_name; - _ = try c.decl_table.put(@ptrToInt(var_decl), checked_name); const var_decl_loc = ZigClangVarDecl_getLocation(var_decl); const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl); diff --git a/test/translate_c.zig b/test/translate_c.zig index 9892d66ad9..d7b5d7208c 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -2167,7 +2167,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub const FOO = "aoeu\x13 derp"; , - \\pub const FOO2 = "aoeu\x9c derp"; + \\pub const FOO2 = "aoeu\x134 derp"; , \\pub const FOO_CHAR = '\x3f'; }); From 949f2369c1a8958e0cd054978f8045de9693eb72 Mon Sep 17 00:00:00 2001 From: Vexu Date: Fri, 20 Dec 2019 20:22:22 +0200 Subject: [PATCH 35/37] translate-c-2 fix bugs found translating SDL --- src-self-hosted/translate_c.zig | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index a093c7e705..a683a02002 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -406,7 +406,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { var it = proto_node.params.iterator(0); while (it.next()) |p| { const param = @fieldParentPtr(ast.Node.ParamDecl, "base", p.*); - const param_name = tokenSlice(c, param.name_token.?); + const param_name = tokenSlice(c, param.name_token orelse + return failDecl(c, fn_decl_loc, fn_name, "function {} parameter has no name", .{fn_name}) + ); const checked_param_name = if (try scope.createAlias(rp.c, param_name)) |a| blk: { try block_scope.variables.push(.{ .name = param_name, .alias = a }); @@ -4387,8 +4389,8 @@ fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: rp, error.UnsupportedTranslation, source_loc, - "unable to translate C expr", - .{}, + "unable to translate C expr, unexpected token: {}", + .{tok.id}, ), } } @@ -4473,10 +4475,17 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc call_node.rtoken = try appendToken(rp.c, .RParen, ")"); node = &call_node.base; }, - else => { + .Eof => { _ = it.prev(); return node; }, + else => return revertAndWarn( + rp, + error.UnsupportedTranslation, + source_loc, + "unable to translate C expr, unexpected token: {}", + .{tok.id}, + ), } } } From 40f607d195440fe62e819592deb6746cd2695753 Mon Sep 17 00:00:00 2001 From: Vexu Date: Fri, 20 Dec 2019 22:48:40 +0200 Subject: [PATCH 36/37] translate-c-2 fix macro regression --- src-self-hosted/c_tokenizer.zig | 4 ++-- src-self-hosted/translate_c.zig | 30 ++++++++++++++++++++---------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src-self-hosted/c_tokenizer.zig b/src-self-hosted/c_tokenizer.zig index 3999645aee..1d166cafe9 100644 --- a/src-self-hosted/c_tokenizer.zig +++ b/src-self-hosted/c_tokenizer.zig @@ -594,7 +594,7 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { switch (c) { 'l', 'L' => { result.num_lit_suffix = .LLU; - return result; + state = .Done; }, else => { return result; @@ -718,7 +718,7 @@ test "tokenize macro" { expect(it.next() == null); tl.shrink(0); - const src5 = "FOO 0l"; + const src5 = "FOO 0ull"; try tokenizeCMacro(&tl, src5); it = tl.iterator(0); expect(it.next().?.id == .Identifier); diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index a683a02002..489c809e4f 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -407,8 +407,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { while (it.next()) |p| { const param = @fieldParentPtr(ast.Node.ParamDecl, "base", p.*); const param_name = tokenSlice(c, param.name_token orelse - return failDecl(c, fn_decl_loc, fn_name, "function {} parameter has no name", .{fn_name}) - ); + return failDecl(c, fn_decl_loc, fn_name, "function {} parameter has no name", .{fn_name})); const checked_param_name = if (try scope.createAlias(rp.c, param_name)) |a| blk: { try block_scope.variables.push(.{ .name = param_name, .alias = a }); @@ -4109,6 +4108,15 @@ fn transMacroDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u8, node.eq_token = try appendToken(c, .Equal, "="); node.init_node = try parseCExpr(rp, it, source_loc, scope); + const last = it.next().?; + if (last.id != .Eof) + return revertAndWarn( + rp, + error.UnsupportedTranslation, + source_loc, + "unable to translate C expr, unexpected token: {}", + .{last.id}, + ); node.semicolon_token = try appendToken(c, .Semicolon, ";"); _ = try c.global_scope.macro_table.put(name, &node.base); @@ -4196,6 +4204,15 @@ fn transMacroFnDefine(c: *Context, it: *ctok.TokenList.Iterator, name: []const u const return_expr = try transCreateNodeReturnExpr(c); const expr = try parseCExpr(rp, it, source_loc, scope); + const last = it.next().?; + if (last.id != .Eof) + return revertAndWarn( + rp, + error.UnsupportedTranslation, + source_loc, + "unable to translate C expr, unexpected token: {}", + .{last.id}, + ); _ = try appendToken(c, .Semicolon, ";"); try type_of.params.push(expr); return_expr.rhs = expr; @@ -4475,17 +4492,10 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc call_node.rtoken = try appendToken(rp.c, .RParen, ")"); node = &call_node.base; }, - .Eof => { + else => { _ = it.prev(); return node; }, - else => return revertAndWarn( - rp, - error.UnsupportedTranslation, - source_loc, - "unable to translate C expr, unexpected token: {}", - .{tok.id}, - ), } } } From 9d31b65b34a222ff8b11d5bcaefcfc3c22ef4250 Mon Sep 17 00:00:00 2001 From: Vexu Date: Sun, 22 Dec 2019 12:53:57 +0200 Subject: [PATCH 37/37] translate-c-2 various fixes - make non-namespaced enums ints - fix .used compound assignments not being grouped - fix macro calls with casts producing invalid Zig --- src-self-hosted/c_tokenizer.zig | 5 + src-self-hosted/translate_c.zig | 79 ++++++++---- test/translate_c.zig | 213 +++++++++++++++++++------------- 3 files changed, 187 insertions(+), 110 deletions(-) diff --git a/src-self-hosted/c_tokenizer.zig b/src-self-hosted/c_tokenizer.zig index 1d166cafe9..cc862a0e67 100644 --- a/src-self-hosted/c_tokenizer.zig +++ b/src-self-hosted/c_tokenizer.zig @@ -30,6 +30,7 @@ pub const CToken = struct { Arrow, LBrace, RBrace, + Pipe, }; pub const NumLitSuffix = enum { @@ -360,6 +361,10 @@ fn next(chars: [*:0]const u8, i: *usize) !CToken { result.id = .RBrace; state = .Done; }, + '|' => { + result.id = .Pipe; + state = .Done; + }, else => return error.TokenizingFailed, } }, diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 489c809e4f..478232b20a 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -781,7 +781,11 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No _ = try appendToken(c, .Comma, ","); // In C each enum value is in the global namespace. So we put them there too. // At this point we can rely on the enum emitting successfully. - try addEnumTopLevel(c, name, field_name, enum_val_name); + const tld_node = try transCreateNodeVarDecl(c, true, true, enum_val_name); + tld_node.eq_token = try appendToken(c, .Equal, "="); + tld_node.init_node = try transCreateNodeAPInt(c, ZigClangEnumConstantDecl_getInitVal(enum_const)); + tld_node.semicolon_token = try appendToken(c, .Semicolon, ";"); + try addTopLevelDecl(c, field_name, &tld_node.base); } container_node.rbrace_token = try appendToken(c, .RBrace, "}"); @@ -797,25 +801,6 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No return transCreateNodeIdentifier(c, name); } -fn addEnumTopLevel(c: *Context, enum_name: []const u8, field_name: []const u8, enum_val_name: []const u8) !void { - const node = try transCreateNodeVarDecl(c, true, true, enum_val_name); - node.eq_token = try appendToken(c, .Equal, "="); - const enum_ident = try transCreateNodeIdentifier(c, enum_name); - const period_tok = try appendToken(c, .Period, "."); - const field_ident = try transCreateNodeIdentifier(c, field_name); - node.semicolon_token = try appendToken(c, .Semicolon, ";"); - - const field_access_node = try c.a().create(ast.Node.InfixOp); - field_access_node.* = .{ - .op_token = period_tok, - .lhs = enum_ident, - .op = .Period, - .rhs = field_ident, - }; - node.init_node = &field_access_node.base; - try addTopLevelDecl(c, field_name, &node.base); -} - fn createAlias(c: *Context, alias: var) !void { const node = try transCreateNodeVarDecl(c, true, true, alias.alias); node.eq_token = try appendToken(c, .Equal, "="); @@ -1570,6 +1555,15 @@ fn transCCast( const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ZigClangQualType_getTypePtr(dst_type)); return transCCast(rp, scope, loc, ZigClangElaboratedType_getNamedType(elaborated_ty), src_type, expr); } + if (ZigClangQualType_getTypeClass(dst_type) == .Enum) + { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@intToEnum"); + try builtin_node.params.push(try transQualType(rp, dst_type, loc)); + _ = try appendToken(rp.c, .Comma, ","); + try builtin_node.params.push(expr); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return &builtin_node.base; + } if (ZigClangQualType_getTypeClass(src_type) == .Enum and ZigClangQualType_getTypeClass(dst_type) != .Enum) { @@ -2326,7 +2320,13 @@ fn transCreatePreCrement( block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); // semicolon must immediately follow rbrace because it is the last token in a block _ = try appendToken(rp.c, .Semicolon, ";"); - return &block_scope.block_node.base; + const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); + grouped_expr.* = .{ + .lparen = try appendToken(rp.c, .LParen, "("), + .expr = &block_scope.block_node.base, + .rparen = try appendToken(rp.c, .RParen, ")"), + }; + return &grouped_expr.base; } fn transCreatePostCrement( @@ -2392,7 +2392,13 @@ fn transCreatePostCrement( try block_scope.block_node.statements.push(&break_node.base); _ = try appendToken(rp.c, .Semicolon, ";"); block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); - return &block_scope.block_node.base; + const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); + grouped_expr.* = .{ + .lparen = try appendToken(rp.c, .LParen, "("), + .expr = &block_scope.block_node.base, + .rparen = try appendToken(rp.c, .RParen, ")"), + }; + return &grouped_expr.base; } fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompoundAssignOperator, used: ResultUsed) TransError!*ast.Node { @@ -2508,7 +2514,13 @@ fn transCreateCompoundAssign( block_scope.block_node.rbrace = try appendToken(rp.c, .RBrace, "}"); // semicolon must immediately follow rbrace because it is the last token in a block _ = try appendToken(rp.c, .Semicolon, ";"); - return &block_scope.block_node.base; + const grouped_expr = try rp.c.a().create(ast.Node.GroupedExpression); + grouped_expr.* = .{ + .lparen = try appendToken(rp.c, .LParen, "("), + .expr = &block_scope.block_node.base, + .rparen = try appendToken(rp.c, .RParen, ")"), + }; + return &grouped_expr.base; } fn transCPtrCast( @@ -4314,7 +4326,10 @@ fn parseCPrimaryExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc: if (it.peek().?.id == .RParen) { _ = it.next(); - return inner_node; + if (it.peek().?.id != .LParen) { + return inner_node; + } + _ = it.next(); } // hack to get zig fmt to render a comma in builtin calls @@ -4458,7 +4473,7 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc }, .Shl => { const op_token = try appendToken(rp.c, .AngleBracketAngleBracketLeft, "<<"); - const rhs = try parseCPrimaryExpr(rp, it, source_loc, scope); + const rhs = try parseCExpr(rp, it, source_loc, scope); const bitshift_node = try rp.c.a().create(ast.Node.InfixOp); bitshift_node.* = .{ .op_token = op_token, @@ -4468,9 +4483,21 @@ fn parseCSuffixOpExpr(rp: RestorePoint, it: *ctok.TokenList.Iterator, source_loc }; node = &bitshift_node.base; }, + .Pipe => { + const op_token = try appendToken(rp.c, .Pipe, "|"); + const rhs = try parseCExpr(rp, it, source_loc, scope); + const or_node = try rp.c.a().create(ast.Node.InfixOp); + or_node.* = .{ + .op_token = op_token, + .lhs = node, + .op = .BitOr, + .rhs = rhs, + }; + node = &or_node.base; + }, .LBrace => { const arr_node = try transCreateNodeArrayAccess(rp.c, node); - arr_node.op.ArrayAccess = try parseCPrimaryExpr(rp, it, source_loc, scope); + arr_node.op.ArrayAccess = try parseCExpr(rp, it, source_loc, scope); arr_node.rtoken = try appendToken(rp.c, .RBrace, "]"); node = &arr_node.base; if (it.next().?.id != .RBrace) diff --git a/test/translate_c.zig b/test/translate_c.zig index d7b5d7208c..917bd98d09 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -166,50 +166,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add_both("enums", - \\enum Foo { - \\ FooA, - \\ FooB, - \\ Foo1, - \\}; - , &[_][]const u8{ - \\pub const enum_Foo = extern enum { - \\ A, - \\ B, - \\ @"1", - \\}; - , - \\pub const FooA = enum_Foo.A; - , - \\pub const FooB = enum_Foo.B; - , - \\pub const Foo1 = enum_Foo.@"1"; - , - \\pub const Foo = enum_Foo; - }); - - cases.add_both("enums", - \\enum Foo { - \\ FooA = 2, - \\ FooB = 5, - \\ Foo1, - \\}; - , &[_][]const u8{ - \\pub const enum_Foo = extern enum { - \\ A = 2, - \\ B = 5, - \\ @"1" = 6, - \\}; - , - \\pub const FooA = enum_Foo.A; - , - \\pub const FooB = enum_Foo.B; - , - \\pub const Foo1 = enum_Foo.@"1"; - , - \\pub const Foo = enum_Foo; - }); - cases.add_both("typedef of function in struct field", \\typedef void lws_callback_function(void); \\struct Foo { @@ -921,27 +877,27 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ p, \\}; , &[_][]const u8{ - \\pub const a = enum_unnamed_1.a; - \\pub const b = enum_unnamed_1.b; - \\pub const c = enum_unnamed_1.c; + \\pub const a = 0; + \\pub const b = 1; + \\pub const c = 2; \\const enum_unnamed_1 = extern enum { \\ a, \\ b, \\ c, \\}; \\pub const d = enum_unnamed_1; - \\pub const e = enum_unnamed_2.e; - \\pub const f = enum_unnamed_2.f; - \\pub const g = enum_unnamed_2.g; + \\pub const e = 0; + \\pub const f = 4; + \\pub const g = 5; \\const enum_unnamed_2 = extern enum { \\ e = 0, \\ f = 4, \\ g = 5, \\}; - \\pub export var h: enum_unnamed_2 = @as(enum_unnamed_2, e); - \\pub const i = enum_unnamed_3.i; - \\pub const j = enum_unnamed_3.j; - \\pub const k = enum_unnamed_3.k; + \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e); + \\pub const i = 0; + \\pub const j = 1; + \\pub const k = 2; \\const enum_unnamed_3 = extern enum { \\ i, \\ j, @@ -951,9 +907,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ l: enum_unnamed_3, \\ m: d, \\}; - \\pub const n = enum_i.n; - \\pub const o = enum_i.o; - \\pub const p = enum_i.p; + \\pub const n = 0; + \\pub const o = 1; + \\pub const p = 2; \\pub const enum_i = extern enum { \\ n, \\ o, @@ -1393,8 +1349,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ Two, \\}; , &[_][]const u8{ - \\pub const One = enum_unnamed_1.One; - \\pub const Two = enum_unnamed_1.Two; + \\pub const One = 0; + \\pub const Two = 1; \\const enum_unnamed_1 = extern enum { \\ One, \\ Two, @@ -1496,9 +1452,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ 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, @@ -1509,7 +1462,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ var a = _arg_a; \\ var b = _arg_b; \\ var c = _arg_c; - \\ var d: enum_Foo = @as(enum_Foo, FooA); + \\ var d: enum_Foo = @intToEnum(enum_Foo, FooA); \\ var e: c_int = @boolToInt(((a != 0) and (b != 0))); \\ var f: c_int = @boolToInt(((b != 0) and (c != null))); \\ var g: c_int = @boolToInt(((a != 0) and (c != null))); @@ -1543,8 +1496,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ x: c_int, \\ y: c_int, \\}; - \\pub const BarA = enum_Bar.A; - \\pub const BarB = enum_Bar.B; + , \\pub const enum_Bar = extern enum { \\ A, \\ B, @@ -1746,9 +1698,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 4; \\} , &[_][]const u8{ - \\pub const A = enum_SomeEnum.A; - \\pub const B = enum_SomeEnum.B; - \\pub const C = enum_SomeEnum.C; \\pub const enum_SomeEnum = extern enum { \\ A, \\ B, @@ -1863,26 +1812,26 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ i -= 1; \\ u +%= 1; \\ u -%= 1; - \\ i = blk: { + \\ i = (blk: { \\ const _ref_1 = &i; \\ _ref_1.* += 1; \\ break :blk _ref_1.*; - \\ }; - \\ i = blk: { + \\ }); + \\ i = (blk: { \\ const _ref_2 = &i; \\ _ref_2.* -= 1; \\ break :blk _ref_2.*; - \\ }; - \\ u = blk: { + \\ }); + \\ u = (blk: { \\ const _ref_3 = &u; \\ _ref_3.* +%= 1; \\ break :blk _ref_3.*; - \\ }; - \\ u = blk: { + \\ }); + \\ u = (blk: { \\ const _ref_4 = &u; \\ _ref_4.* -%= 1; \\ break :blk _ref_4.*; - \\ }; + \\ }); \\} }); @@ -2062,30 +2011,30 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ i -= 1; \\ u +%= 1; \\ u -%= 1; - \\ i = blk: { + \\ i = (blk: { \\ const _ref_1 = &i; \\ const _tmp_2 = _ref_1.*; \\ _ref_1.* += 1; \\ break :blk _tmp_2; - \\ }; - \\ i = blk: { + \\ }); + \\ i = (blk: { \\ const _ref_3 = &i; \\ const _tmp_4 = _ref_3.*; \\ _ref_3.* -= 1; \\ break :blk _tmp_4; - \\ }; - \\ u = blk: { + \\ }); + \\ u = (blk: { \\ const _ref_5 = &u; \\ const _tmp_6 = _ref_5.*; \\ _ref_5.* +%= 1; \\ break :blk _tmp_6; - \\ }; - \\ u = blk: { + \\ }); + \\ u = (blk: { \\ const _ref_7 = &u; \\ const _tmp_8 = _ref_7.*; \\ _ref_7.* -%= 1; \\ break :blk _tmp_8; - \\ }; + \\ }); \\} }); @@ -2172,6 +2121,58 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const FOO_CHAR = '\x3f'; }); + cases.add_2("enums", + \\enum Foo { + \\ FooA, + \\ FooB, + \\ Foo1, + \\}; + , &[_][]const u8{ + \\pub const enum_Foo = extern enum { + \\ A, + \\ B, + \\ @"1", + \\}; + , + \\pub const FooA = 0; + , + \\pub const FooB = 1; + , + \\pub const Foo1 = 2; + , + \\pub const Foo = enum_Foo; + }); + + cases.add_2("enums", + \\enum Foo { + \\ FooA = 2, + \\ FooB = 5, + \\ Foo1, + \\}; + , &[_][]const u8{ + \\pub const enum_Foo = extern enum { + \\ A = 2, + \\ B = 5, + \\ @"1" = 6, + \\}; + , + \\pub const FooA = 2; + , + \\pub const FooB = 5; + , + \\pub const Foo1 = 6; + , + \\pub const Foo = enum_Foo; + }); + + cases.add_2("macro cast", + \\#define FOO(bar) baz((void *)(baz)) + , &[_][]const u8{ + \\pub inline fn FOO(bar: var) @TypeOf(baz(if (@typeId(@TypeOf(baz)) == .Pointer) @ptrCast([*c]void, baz) else if (@typeId(@TypeOf(baz)) == .Int) @intToPtr([*c]void, baz) else @as([*c]void, baz))) { + \\ return baz(if (@typeId(@TypeOf(baz)) == .Pointer) @ptrCast([*c]void, baz) else if (@typeId(@TypeOf(baz)) == .Int) @intToPtr([*c]void, baz) else @as([*c]void, baz)); + \\} + }); + /////////////// Cases for only stage1 because stage2 behavior is better //////////////// cases.addC("Parameterless function prototypes", \\void foo() {} @@ -3124,4 +3125,48 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const FOO_CHAR = 63; }); + + cases.add("enums", + \\enum Foo { + \\ FooA, + \\ FooB, + \\ Foo1, + \\}; + , &[_][]const u8{ + \\pub const enum_Foo = extern enum { + \\ A, + \\ B, + \\ @"1", + \\}; + , + \\pub const FooA = enum_Foo.A; + , + \\pub const FooB = enum_Foo.B; + , + \\pub const Foo1 = enum_Foo.@"1"; + , + \\pub const Foo = enum_Foo; + }); + + cases.add("enums", + \\enum Foo { + \\ FooA = 2, + \\ FooB = 5, + \\ Foo1, + \\}; + , &[_][]const u8{ + \\pub const enum_Foo = extern enum { + \\ A = 2, + \\ B = 5, + \\ @"1" = 6, + \\}; + , + \\pub const FooA = enum_Foo.A; + , + \\pub const FooB = enum_Foo.B; + , + \\pub const Foo1 = enum_Foo.@"1"; + , + \\pub const Foo = enum_Foo; + }); }