translate-c: convert record and enum decls

This commit is contained in:
Veikka Tuominen
2021-02-08 11:52:21 +02:00
parent bb867b071a
commit 5dac3683c9
2 changed files with 125 additions and 229 deletions

View File

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

View File

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