From 5dac3683c9e8819d6ceefb869577e3537d863c41 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 8 Feb 2021 11:52:21 +0200 Subject: [PATCH] translate-c: convert record and enum decls --- src/translate_c.zig | 296 ++++++++++++---------------------------- src/translate_c/ast.zig | 58 ++++---- 2 files changed, 125 insertions(+), 229 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index bcefc41971..3181f05f78 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -783,7 +783,7 @@ fn transCreateNodeTypedef( const payload = try c.arena.create(ast.Payload.Typedef); payload.* = .{ - .base = .{ .tag = ([2]ast.Node.Tag{ .typedef, .pub_typedef })[toplevel] }, + .base = .{ .tag = ([2]ast.Node.Tag{ .typedef, .pub_typedef })[@boolToInt(toplevel)] }, .data = .{ .name = checked_name, .init = init_node, @@ -792,7 +792,7 @@ fn transCreateNodeTypedef( return Node.initPayload(&payload.base); } -fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*ast.Node { +fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Node { if (c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl()))) |name| return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice const record_loc = record_decl.getLocation(); @@ -807,46 +807,30 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as } var container_kind_name: []const u8 = undefined; - var container_kind: std.zig.Token.Id = undefined; + var is_union = false; if (record_decl.isUnion()) { container_kind_name = "union"; - container_kind = .Keyword_union; + is_union = true; } else if (record_decl.isStruct()) { container_kind_name = "struct"; - container_kind = .Keyword_struct; } else { - try emitWarning(c, record_loc, "record {s} is not a struct or union", .{bare_name}); + try warn(c, record_loc, "record {s} is not a struct or union", .{bare_name}); return null; } const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name }); _ = try c.decl_table.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name); - const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null; - const mut_tok = try appendToken(c, .Keyword_const, "const"); - const name_tok = try appendIdentifier(c, name); - - const eq_token = try appendToken(c, .Equal, "="); - - var semicolon: ast.TokenIndex = undefined; + const is_pub = !is_unnamed; const init_node = blk: { - const rp = makeRestorePoint(c); const record_def = record_decl.getDefinition() orelse { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - const opaque_type = try transCreateNodeOpaqueType(c); - semicolon = try appendToken(c, .Semicolon, ";"); - break :blk opaque_type; + break :blk Node.opaque_literal.init(); }; - const layout_tok = try if (record_decl.getPackedAttribute()) - appendToken(c, .Keyword_packed, "packed") - else - appendToken(c, .Keyword_extern, "extern"); - const container_tok = try appendToken(c, container_kind, container_kind_name); - const lbrace_token = try appendToken(c, .LBrace, "{"); - - var fields_and_decls = std.ArrayList(*ast.Node).init(c.gpa); - defer fields_and_decls.deinit(); + const is_packed = record_decl.getPackedAttribute(); + var fields = std.ArrayList(ast.Payload.Record.Field).init(c.gpa); + defer fields.deinit(); var unnamed_field_count: u32 = 0; var it = record_def.field_begin(); @@ -858,110 +842,82 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as if (field_decl.isBitField()) { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - const opaque_type = try transCreateNodeOpaqueType(c); - semicolon = try appendToken(c, .Semicolon, ";"); - try emitWarning(c, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); - break :blk opaque_type; + try warn(c, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); + break :blk Node.opaque_literal.init(); } if (qualTypeCanon(field_qt).isIncompleteOrZeroLengthArrayType(c.clang_context)) { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - const opaque_type = try transCreateNodeOpaqueType(c); - semicolon = try appendToken(c, .Semicolon, ";"); - try emitWarning(c, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name}); - break :blk opaque_type; + try warn(c, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name}); + break :blk Node.opaque_literal.init(); } var is_anon = false; - var raw_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin()); - if (field_decl.isAnonymousStructOrUnion() or raw_name.len == 0) { + var field_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin()); + if (field_decl.isAnonymousStructOrUnion() or field_name.len == 0) { // Context.getMangle() is not used here because doing so causes unpredictable field names for anonymous fields. - raw_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count}); + field_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count}); unnamed_field_count += 1; is_anon = true; } - const field_name = try appendIdentifier(c, raw_name); - _ = try appendToken(c, .Colon, ":"); - const field_type = transQualType(rp, field_qt, field_loc) catch |err| switch (err) { + const field_type = transQualType(c, field_qt, field_loc) catch |err| switch (err) { error.UnsupportedType => { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - const opaque_type = try transCreateNodeOpaqueType(c); - semicolon = try appendToken(c, .Semicolon, ";"); - try emitWarning(c, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, raw_name }); - break :blk opaque_type; + try warn(c, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, raw_name }); + break :blk Node.opaque_literal.init(); }, else => |e| return e, }; - const align_expr = blk_2: { + const alignment = blk_2: { const alignment = field_decl.getAlignedAttribute(c.clang_context); if (alignment != 0) { - _ = try appendToken(c, .Keyword_align, "align"); - _ = try appendToken(c, .LParen, "("); // Clang reports the alignment in bits - const expr = try transCreateNodeInt(c, alignment / 8); - _ = try appendToken(c, .RParen, ")"); - - break :blk_2 expr; + break :blk_2 alignment / 8; } break :blk_2 null; }; - const field_node = try c.arena.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 = align_expr, - }; - if (is_anon) { - _ = try c.decl_table.put( - c.gpa, - @ptrToInt(field_decl.getCanonicalDecl()), - raw_name, - ); + _ = try c.decl_table.put(c.gpa, @ptrToInt(field_decl.getCanonicalDecl()), field_name); } - try fields_and_decls.append(&field_node.base); - _ = try appendToken(c, .Comma, ","); + try fields.append(.{ + .name = field_name, + .type = field_type, + .alignment = alignment, + }); } - const container_node = try ast.Node.ContainerDecl.alloc(c.arena, fields_and_decls.items.len); + + const payload = try c.arena.create(ast.Payload.Record); container_node.* = .{ - .layout_token = layout_tok, - .kind_token = container_tok, - .init_arg_expr = .None, - .fields_and_decls_len = fields_and_decls.items.len, - .lbrace_token = lbrace_token, - .rbrace_token = try appendToken(c, .RBrace, "}"), + .base = .{ .tag = ([2]ast.Node.Tag{ .@"struct", .@"union" })[@boolToInt(is_union)] }, + .data = .{ + .is_packed = is_packed, + .fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items), + }, }; - mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items); - semicolon = try appendToken(c, .Semicolon, ";"); - break :blk &container_node.base; + break :blk Node.initPayload(&container_node.base); }; - const node = try ast.Node.VarDecl.create(c.arena, .{ - .name_token = name_tok, - .mut_token = mut_tok, - .semicolon_token = semicolon, - }, .{ - .visib_token = visib_tok, - .eq_token = eq_token, - .init_node = init_node, - }); + const payload = try c.arena.create(ast.Payload.SimpleVarDecl); + payload.* = .{ + .base = .{ .tag = ([2]ast.Node.Tag{ .var_simple, .pub_var_simple })[@boolToInt(is_pub)] }, + .data = .{ + .name = name, + .init = init_node, + }, + }; - try addTopLevelDecl(c, name, &node.base); + try addTopLevelDecl(c, name, Node.initPayload(&payload.base)); if (!is_unnamed) try c.alias_list.append(.{ .alias = bare_name, .name = name }); - return transCreateNodeIdentifier(c, name); + return Node.identifier.create(c.arena, name); } -fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node { +fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node { if (c.decl_table.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |name| return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice - const rp = makeRestorePoint(c); const enum_loc = enum_decl.getLocation(); var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()); @@ -974,10 +930,7 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node const name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name}); _ = try c.decl_table.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name); - const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null; - const mut_tok = try appendToken(c, .Keyword_const, "const"); - const name_tok = try appendIdentifier(c, name); - const eq_token = try appendToken(c, .Equal, "="); + const is_pub = !is_unnamed; const init_node = if (enum_decl.getDefinition()) |enum_def| blk: { var pure_enum = true; @@ -991,11 +944,8 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node } } - const extern_tok = try appendToken(c, .Keyword_extern, "extern"); - const container_tok = try appendToken(c, .Keyword_enum, "enum"); - - var fields_and_decls = std.ArrayList(*ast.Node).init(c.gpa); - defer fields_and_decls.deinit(); + var fields = std.ArrayList(ast.Payload.Enum.Field).init(c.gpa); + defer fields.deinit(); const int_type = enum_decl.getIntegerType(); // The underlying type may be null in case of forward-declared enum @@ -1003,30 +953,23 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node // default to the usual integer type used for all the enums. // default to c_int since msvc and gcc default to different types - _ = try appendToken(c, .LParen, "("); - const init_arg_expr = ast.Node.ContainerDecl.InitArg{ - .Type = if (int_type.ptr != null and - !isCBuiltinType(int_type, .UInt) and - !isCBuiltinType(int_type, .Int)) - 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, - } - else - try transCreateNodeIdentifier(c, "c_int"), - }; - _ = try appendToken(c, .RParen, ")"); - - const lbrace_token = try appendToken(c, .LBrace, "{"); + const init_arg_expr = if (int_type.ptr != null and + !isCBuiltinType(int_type, .UInt) and + !isCBuiltinType(int_type, .Int)) + transQualType(c, 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, + } + else + try Node.type.create(c.arena, "c_int"); it = enum_def.enumerator_begin(); end_it = enum_def.enumerator_end(); while (it.neq(end_it)) : (it = it.next()) { const enum_const = it.deref(); - const enum_val_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_const).getName_bytes_begin()); const field_name = if (!is_unnamed and mem.startsWith(u8, enum_val_name, bare_name)) @@ -1034,100 +977,41 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node else enum_val_name; - const field_name_tok = try appendIdentifier(c, field_name); - - const int_node = if (!pure_enum) blk_2: { - _ = try appendToken(c, .Colon, "="); - break :blk_2 try transCreateNodeAPInt(c, enum_const.getInitVal()); - } else + const int_node = if (!pure_enum) + try transCreateNodeAPInt(c, enum_const.getInitVal()) + else null; - const field_node = try c.arena.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 fields_and_decls.append(&field_node.base); - _ = try appendToken(c, .Comma, ","); + try fields_and_decls.append(.{ + .name = field_name, + .value = int_node, + }); // 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. - const tld_visib_tok = try appendToken(c, .Keyword_pub, "pub"); - const tld_mut_tok = try appendToken(c, .Keyword_const, "const"); - const tld_name_tok = try appendIdentifier(c, enum_val_name); - const tld_eq_token = try appendToken(c, .Equal, "="); - const cast_node = try rp.c.createBuiltinCall("@enumToInt", 1); - const enum_ident = try transCreateNodeIdentifier(c, name); - const period_tok = try appendToken(c, .Period, "."); - const field_ident = try transCreateNodeIdentifier(c, field_name); - const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp); - field_access_node.* = .{ - .base = .{ .tag = .Period }, - .op_token = period_tok, - .lhs = enum_ident, - .rhs = field_ident, - }; - cast_node.params()[0] = &field_access_node.base; - cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - const tld_init_node = &cast_node.base; - const tld_semicolon_token = try appendToken(c, .Semicolon, ";"); - const tld_node = try ast.Node.VarDecl.create(c.arena, .{ - .name_token = tld_name_tok, - .mut_token = tld_mut_tok, - .semicolon_token = tld_semicolon_token, - }, .{ - .visib_token = tld_visib_tok, - .eq_token = tld_eq_token, - .init_node = tld_init_node, - }); - try addTopLevelDecl(c, field_name, &tld_node.base); + try addTopLevelDecl(c, field_name, try Node.enum_redecl.create(c.arena, .{ + .enum_val_name = enum_val_name, + .field_name = field_name, + .enum_name = name, + })); } - // make non exhaustive - const field_node = try c.arena.create(ast.Node.ContainerField); - field_node.* = .{ - .doc_comments = null, - .comptime_token = null, - .name_token = try appendIdentifier(c, "_"), - .type_expr = null, - .value_expr = null, - .align_expr = null, - }; - try fields_and_decls.append(&field_node.base); - _ = try appendToken(c, .Comma, ","); - const container_node = try ast.Node.ContainerDecl.alloc(c.arena, fields_and_decls.items.len); - container_node.* = .{ - .layout_token = extern_tok, - .kind_token = container_tok, - .init_arg_expr = init_arg_expr, - .fields_and_decls_len = fields_and_decls.items.len, - .lbrace_token = lbrace_token, - .rbrace_token = try appendToken(c, .RBrace, "}"), - }; - mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items); - break :blk &container_node.base; + break :blk try Node.@"enum".create(c.arena, try c.arena.dupe(ast.Payload.Enum.Field, fields.items)); } else blk: { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), {}); - break :blk try transCreateNodeOpaqueType(c); + break :blk Node.opaque_literal.init(); }; - const semicolon_token = try appendToken(c, .Semicolon, ";"); - const node = try ast.Node.VarDecl.create(c.arena, .{ - .name_token = name_tok, - .mut_token = mut_tok, - .semicolon_token = semicolon_token, - }, .{ - .visib_token = visib_tok, - .eq_token = eq_token, - .init_node = init_node, - }); + const payload = try c.arena.create(ast.Payload.SimpleVarDecl); + payload.* = .{ + .base = .{ .tag = ([2]ast.Node.Tag{ .var_simple, .pub_var_simple })[@boolToInt(is_pub)] }, + .data = .{ + .name = name, + .init = init_node, + }, + }; - try addTopLevelDecl(c, name, &node.base); + try addTopLevelDecl(c, name, Node.initPayload(&payload.base)); if (!is_unnamed) try c.alias_list.append(.{ .alias = bare_name, .name = name }); return transCreateNodeIdentifier(c, name); @@ -1382,22 +1266,22 @@ fn transCompoundStmt(c: *Context, scope: *Scope, stmt: *const clang.CompoundStmt } fn transCStyleCastExprClass( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.CStyleCastExpr, result_used: ResultUsed, lrvalue: LRValue, -) TransError!*ast.Node { +) TransError!Node { const sub_expr = stmt.getSubExpr(); const cast_node = (try transCCast( - rp, + c, scope, stmt.getBeginLoc(), stmt.getType(), sub_expr.getType(), - try transExpr(rp, scope, sub_expr, .used, lrvalue), + try transExpr(c, scope, sub_expr, .used, lrvalue), )); - return maybeSuppressResult(rp, scope, result_used, cast_node); + return maybeSuppressResult(c, scope, result_used, cast_node); } fn transDeclStmtOne( diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 9abfe215e6..b926efdbef 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -10,6 +10,7 @@ pub const Node = extern union { pub const Tag = enum { null_literal, undefined_literal, + /// opaque {} opaque_literal, true_literal, false_literal, @@ -42,6 +43,7 @@ pub const Node = extern union { func, warning, failed_decl, + /// All enums are non-exhaustive @"enum", @"struct", @"union", @@ -145,8 +147,12 @@ pub const Node = extern union { arg_redecl, /// const name = init; typedef, + var_simple, /// pub const name = init; pub_typedef, + pub_var_simple, + /// pub const enum_field_name = @enumToInt(enum_name.field_name); + enum_redecl, pub const last_no_payload_tag = Tag.usingnamespace_builtins; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -266,7 +272,8 @@ pub const Node = extern union { .array_type => Payload.Array, .arg_redecl => Payload.ArgRedecl, .log2_int_type => Payload.Log2IntType, - .typedef, .pub_typedef => Payload.Typedef, + .typedef, .pub_typedef, .pub_var_simple => Payload.SimpleVarDecl, + .enum_redecl => Payload.EnumRedecl, }; } @@ -419,41 +426,37 @@ pub const Payload = struct { return_type: Node, body: ?Node, alignment: ?c_uint, - - pub const Param = struct { - is_noalias: bool, - name: ?[]const u8, - type: Node, - }; }, + + pub const Param = struct { + is_noalias: bool, + name: ?[]const u8, + type: Node, + }; }; pub const Enum = struct { base: Node = .{ .tag = .@"enum" }, - data: struct { - name: ?[]const u8, - fields: []Field, + data: []Field, - pub const Field = struct { - name: []const u8, - value: ?[]const u8, - }; - }, + pub const Field = struct { + name: []const u8, + value: ?Node, + }; }; pub const Record = struct { base: Node, data: struct { - name: ?[]const u8, @"packed": bool, fields: []Field, - - pub const Field = struct { - name: []const u8, - type: Type, - alignment: c_uint, - }; }, + + pub const Field = struct { + name: []const u8, + type: Node, + alignment: ?c_uint, + }; }; pub const ArrayInit = struct { @@ -514,13 +517,22 @@ pub const Payload = struct { data: std.math.Log2Int(u64), }; - pub const Typedef = struct { + pub const SimpleVarDecl = struct { base: Node, data: struct { name: []const u8, init: Node, }, }; + + pub const EnumRedecl = struct { + base: Node, + data: struct { + enum_val_name: []const u8, + field_name: []const u8, + enum_name: []const u8, + }, + }; }; /// Converts the nodes into a Zig ast.