translate-c: convert function translation

This commit is contained in:
Veikka Tuominen 2021-02-07 23:13:40 +02:00
parent 7514c0ad0d
commit f36849fed2
No known key found for this signature in database
GPG Key ID: 59AEB8936E16A6AC
2 changed files with 153 additions and 293 deletions

View File

@ -469,7 +469,6 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
return visitFnDecl(c, def);
}
const rp = makeRestorePoint(c);
const fn_decl_loc = fn_decl.getLocation();
const has_body = fn_decl.hasBody();
const storage_class = fn_decl.getStorageClass();
@ -513,9 +512,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
decl_ctx.has_body = false;
decl_ctx.storage_class = .Extern;
decl_ctx.is_export = false;
try emitWarning(c, fn_decl_loc, "TODO unable to translate variadic function, demoted to declaration", .{});
try warn(c, fn_decl_loc, "TODO unable to translate variadic function, demoted to declaration", .{});
}
break :blk transFnProto(rp, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
break :blk transFnProto(c, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
error.UnsupportedType => {
return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
},
@ -524,7 +523,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
},
.FunctionNoProto => blk: {
const fn_no_proto_type = @ptrCast(*const clang.FunctionType, fn_type);
break :blk transFnNoProto(rp, fn_no_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
break :blk transFnNoProto(c, fn_no_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
error.UnsupportedType => {
return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
},
@ -535,13 +534,12 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
};
if (!decl_ctx.has_body) {
const semi_tok = try appendToken(c, .Semicolon, ";");
return addTopLevelDecl(c, fn_name, &proto_node.base);
}
// actual function definition with body
const body_stmt = fn_decl.getBody();
var block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, false);
var block_scope = try Scope.Block.init(c, &c.global_scope.base, false);
block_scope.return_type = return_qt;
defer block_scope.deinit();
@ -559,34 +557,22 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
const is_const = qual_type.isConstQualified();
const mangled_param_name = try block_scope.makeMangledName(c, param_name);
param.name = mangled_param_name;
if (!is_const) {
const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{s}", .{mangled_param_name});
const arg_name = try block_scope.makeMangledName(c, bare_arg_name);
param.name = arg_name;
const mut_tok = try appendToken(c, .Keyword_var, "var");
const name_tok = try appendIdentifier(c, mangled_param_name);
const eq_token = try appendToken(c, .Equal, "=");
const init_node = try transCreateNodeIdentifier(c, arg_name);
const semicolon_token = try appendToken(c, .Semicolon, ";");
const node = try ast.Node.VarDecl.create(c.arena, .{
.mut_token = mut_tok,
.name_token = name_tok,
.semicolon_token = semicolon_token,
}, .{
.eq_token = eq_token,
.init_node = init_node,
});
try block_scope.statements.append(&node.base);
param.name_token = try appendIdentifier(c, arg_name);
_ = try appendToken(c, .Colon, ":");
const redecl_node = try Node.arg_redecl.create(c.arena, .{ .actual = mangled_param_name, .mangled = arg_name });
try block_scope.statements.append(redecl_node);
}
param_id += 1;
}
const casted_body = @ptrCast(*const clang.CompoundStmt, body_stmt);
transCompoundStmtInline(rp, &block_scope.base, casted_body, &block_scope) catch |err| switch (err) {
transCompoundStmtInline(c, &block_scope.base, casted_body, &block_scope) catch |err| switch (err) {
error.OutOfMemory => |e| return e,
error.UnsupportedTranslation,
error.UnsupportedType,
@ -600,37 +586,31 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
if (block_scope.statements.items.len > 0) {
var last = block_scope.statements.items[block_scope.statements.items.len - 1];
while (true) {
switch (last.tag) {
.Block, .LabeledBlock => {
const stmts = last.blockStatements();
if (stmts.len == 0) break;
switch (last.tag()) {
.block => {
const block = last.castTag(.block).?;
if (block.data.stmts.len == 0) break;
last = stmts[stmts.len - 1];
last = block.data.stmts[block.data.stmts.len - 1];
},
// no extra return needed
.Return => break :blk,
.@"return", .return_void => break :blk,
else => break,
}
}
}
const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{
.ltoken = try appendToken(rp.c, .Keyword_return, "return"),
.tag = .Return,
}, .{
.rhs = transZeroInitExpr(rp, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) {
error.OutOfMemory => |e| return e,
error.UnsupportedTranslation,
error.UnsupportedType,
=> return failDecl(c, fn_decl_loc, fn_name, "unable to create a return value for function", .{}),
},
});
_ = try appendToken(rp.c, .Semicolon, ";");
try block_scope.statements.append(&return_expr.base);
const rhs = transZeroInitExpr(c, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) {
error.OutOfMemory => |e| return e,
error.UnsupportedTranslation,
error.UnsupportedType,
=> return failDecl(c, fn_decl_loc, fn_name, "unable to create a return value for function", .{}),
};
const ret = try Node.@"return".create(c.arena, rhs);
try block_scope.statements.append(ret);
}
const body_node = try block_scope.complete(rp.c);
proto_node.setBodyNode(body_node);
proto_node.body = try block_scope.complete(c);
return addTopLevelDecl(c, fn_name, &proto_node.base);
}
@ -2440,16 +2420,16 @@ fn transInitListExpr(
}
fn transZeroInitExpr(
rp: RestorePoint,
c: *Context,
scope: *Scope,
source_loc: clang.SourceLocation,
ty: *const clang.Type,
) TransError!*ast.Node {
) TransError!Node {
switch (ty.getTypeClass()) {
.Builtin => {
const builtin_ty = @ptrCast(*const clang.BuiltinType, ty);
switch (builtin_ty.getKind()) {
.Bool => return try transCreateNodeBoolLiteral(rp.c, false),
.Bool => return Node.false_literal.init(),
.Char_U,
.UChar,
.Char_S,
@ -2470,16 +2450,16 @@ fn transZeroInitExpr(
.Float128,
.Float16,
.LongDouble,
=> return transCreateNodeInt(rp.c, 0),
else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
=> return Node.zero_literal.init(),
else => return fail(c, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
}
},
.Pointer => return transCreateNodeNullLiteral(rp.c),
.Pointer => return Node.null_literal.init(),
.Typedef => {
const typedef_ty = @ptrCast(*const clang.TypedefType, ty);
const typedef_decl = typedef_ty.getDecl();
return transZeroInitExpr(
rp,
c,
scope,
source_loc,
typedef_decl.getUnderlyingType().getTypePtr(),
@ -2488,7 +2468,7 @@ fn transZeroInitExpr(
else => {},
}
return revertAndWarn(rp, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{});
return fail(c, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{});
}
fn transImplicitValueInitExpr(
@ -3985,7 +3965,7 @@ fn qualTypeToLog2IntRef(c: *Context, qt: clang.QualType, source_loc: clang.Sourc
if (int_bit_width != 0) {
// we can perform the log2 now.
const cast_bit_width = math.log2_int(u64, int_bit_width);
return Node.uint_type.create(c.arena, cast_bit_width);
return Node.log2_int_type.create(c.arena, cast_bit_width);
}
const zig_type = try transQualType(c, qt, source_loc);
@ -4886,7 +4866,7 @@ const FnDeclContext = struct {
};
fn transCC(
rp: RestorePoint,
c: *Context,
fn_ty: *const clang.FunctionType,
source_loc: clang.SourceLocation,
) !CallingConvention {
@ -4899,7 +4879,7 @@ fn transCC(
.X86ThisCall => return CallingConvention.Thiscall,
.AAPCS => return CallingConvention.AAPCS,
.AAPCS_VFP => return CallingConvention.AAPCSVFP,
else => return revertAndWarn(
else => return fail(
rp,
error.UnsupportedType,
source_loc,
@ -4910,33 +4890,33 @@ fn transCC(
}
fn transFnProto(
rp: RestorePoint,
c: *Context,
fn_decl: ?*const clang.FunctionDecl,
fn_proto_ty: *const clang.FunctionProtoType,
source_loc: clang.SourceLocation,
fn_decl_context: ?FnDeclContext,
is_pub: bool,
) !*ast.Node.FnProto {
) !Node.FnProto {
const fn_ty = @ptrCast(*const clang.FunctionType, fn_proto_ty);
const cc = try transCC(rp, fn_ty, source_loc);
const cc = try transCC(c, fn_ty, source_loc);
const is_var_args = fn_proto_ty.isVariadic();
return finishTransFnProto(rp, fn_decl, fn_proto_ty, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
return finishTransFnProto(c, fn_decl, fn_proto_ty, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
}
fn transFnNoProto(
rp: RestorePoint,
c: *Context,
fn_ty: *const clang.FunctionType,
source_loc: clang.SourceLocation,
fn_decl_context: ?FnDeclContext,
is_pub: bool,
) !*ast.Node.FnProto {
const cc = try transCC(rp, fn_ty, source_loc);
) !Node.FnProto {
const cc = try transCC(c, fn_ty, source_loc);
const is_var_args = if (fn_decl_context) |ctx| (!ctx.is_export and ctx.storage_class != .Static) else true;
return finishTransFnProto(rp, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
return finishTransFnProto(c, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
}
fn finishTransFnProto(
rp: RestorePoint,
c: *Context,
fn_decl: ?*const clang.FunctionDecl,
fn_proto_ty: ?*const clang.FunctionProtoType,
fn_ty: *const clang.FunctionType,
@ -4945,128 +4925,77 @@ fn finishTransFnProto(
is_var_args: bool,
cc: CallingConvention,
is_pub: bool,
) !*ast.Node.FnProto {
) !*ast.Payload.Func {
const is_export = if (fn_decl_context) |ctx| ctx.is_export else false;
const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else false;
// TODO check for always_inline attribute
// TODO check for align attribute
// pub extern fn name(...) T
const pub_tok = if (is_pub) try appendToken(rp.c, .Keyword_pub, "pub") else null;
const extern_export_inline_tok = if (is_export)
try appendToken(rp.c, .Keyword_export, "export")
else if (is_extern)
try appendToken(rp.c, .Keyword_extern, "extern")
else
null;
const fn_tok = try appendToken(rp.c, .Keyword_fn, "fn");
const name_tok = if (fn_decl_context) |ctx| try appendIdentifier(rp.c, ctx.fn_name) else null;
const lparen_tok = try appendToken(rp.c, .LParen, "(");
var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(rp.c.gpa);
var fn_params = std.ArrayList(ast.Payload.Func.Param).init(c.gpa);
defer fn_params.deinit();
const param_count: usize = if (fn_proto_ty != null) fn_proto_ty.?.getNumParams() else 0;
try fn_params.ensureCapacity(param_count + 1); // +1 for possible var args node
try fn_params.ensureCapacity(param_count);
var i: usize = 0;
while (i < param_count) : (i += 1) {
const param_qt = fn_proto_ty.?.getParamType(@intCast(c_uint, i));
const is_noalias = param_qt.isRestrictQualified();
const noalias_tok = if (param_qt.isRestrictQualified()) try appendToken(rp.c, .Keyword_noalias, "noalias") else null;
const param_name: ?[]const u8 =
if (fn_decl) |decl|
blk: {
const param = decl.getParamDecl(@intCast(c_uint, i));
const param_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, param).getName_bytes_begin());
if (param_name.len < 1)
break :blk null;
const param_name_tok: ?ast.TokenIndex = blk: {
if (fn_decl) |decl| {
const param = decl.getParamDecl(@intCast(c_uint, i));
const param_name: []const u8 = try rp.c.str(@ptrCast(*const clang.NamedDecl, param).getName_bytes_begin());
if (param_name.len < 1)
break :blk null;
const result = try appendIdentifier(rp.c, param_name);
_ = try appendToken(rp.c, .Colon, ":");
break :blk result;
}
break :blk null;
};
const type_node = try transQualType(rp, param_qt, source_loc);
break :blk param_name;
} else null;
const type_node = try transQualType(c, param_qt, source_loc);
fn_params.addOneAssumeCapacity().* = .{
.doc_comments = null,
.comptime_token = null,
.noalias_token = noalias_tok,
.name_token = param_name_tok,
.param_type = .{ .type_expr = type_node },
.is_noalias = is_noalias,
.name = param_name,
.type = type_node,
};
if (i + 1 < param_count) {
_ = try appendToken(rp.c, .Comma, ",");
}
}
const var_args_token: ?ast.TokenIndex = if (is_var_args) blk: {
if (param_count > 0) {
_ = try appendToken(rp.c, .Comma, ",");
}
break :blk try appendToken(rp.c, .Ellipsis3, "...");
} else null;
const rparen_tok = try appendToken(rp.c, .RParen, ")");
const linksection_expr = blk: {
const link_section_string: ?[]const u8 = blk: {
if (fn_decl) |decl| {
var str_len: usize = undefined;
if (decl.getSectionAttribute(&str_len)) |str_ptr| {
_ = try appendToken(rp.c, .Keyword_linksection, "linksection");
_ = try appendToken(rp.c, .LParen, "(");
const expr = try transCreateNodeStringLiteral(
rp.c,
try std.fmt.allocPrint(rp.c.arena, "\"{s}\"", .{str_ptr[0..str_len]}),
);
_ = try appendToken(rp.c, .RParen, ")");
break :blk expr;
break :blk str_ptr[0..str_len];
}
}
break :blk null;
};
const align_expr = blk: {
const alignment: c_uint = blk: {
if (fn_decl) |decl| {
const alignment = decl.getAlignedAttribute(rp.c.clang_context);
const alignment = decl.getAlignedAttribute(c.clang_context);
if (alignment != 0) {
_ = try appendToken(rp.c, .Keyword_align, "align");
_ = try appendToken(rp.c, .LParen, "(");
// Clang reports the alignment in bits
const expr = try transCreateNodeInt(rp.c, alignment / 8);
_ = try appendToken(rp.c, .RParen, ")");
break :blk expr;
break :blk alignment / 8;
}
}
break :blk null;
};
const callconv_expr = if ((is_export or is_extern) and cc == .C) null else blk: {
_ = try appendToken(rp.c, .Keyword_callconv, "callconv");
_ = try appendToken(rp.c, .LParen, "(");
const expr = try transCreateNodeEnumLiteral(rp.c, @tagName(cc));
_ = try appendToken(rp.c, .RParen, ")");
break :blk expr;
};
const explicit_callconv = if ((is_export or is_extern) and cc == .C) null else cc;
const return_type_node = blk: {
if (fn_ty.getNoReturnAttr()) {
break :blk try transCreateNodeIdentifier(rp.c, "noreturn");
break :blk Node.noreturn_type.init();
} else {
const return_qt = fn_ty.getReturnType();
if (isCVoid(return_qt)) {
// convert primitive c_void to actual void (only for return type)
break :blk try transCreateNodeIdentifier(rp.c, "void");
break :blk Node.void_type.init();
} else {
break :blk transQualType(rp, return_qt, source_loc) catch |err| switch (err) {
break :blk transQualType(c, return_qt, source_loc) catch |err| switch (err) {
error.UnsupportedType => {
try emitWarning(rp.c, source_loc, "unsupported function proto return type", .{});
try warn(c, source_loc, "unsupported function proto return type", .{});
return err;
},
error.OutOfMemory => |e| return e,
@ -5075,32 +5004,23 @@ fn finishTransFnProto(
}
};
// We need to reserve an undefined (but non-null) body node to set later.
var body_node: ?*ast.Node = null;
if (fn_decl_context) |ctx| {
if (ctx.has_body) {
// TODO: we should be able to use undefined here but
// it causes a bug. This is undefined without zig language
// being aware of it.
body_node = @intToPtr(*ast.Node, 0x08);
}
}
const fn_proto = try ast.Node.FnProto.create(rp.c.arena, .{
.params_len = fn_params.items.len,
.return_type = .{ .Explicit = return_type_node },
.fn_token = fn_tok,
}, .{
.visib_token = pub_tok,
.name_token = name_tok,
.extern_export_inline_token = extern_export_inline_tok,
.align_expr = align_expr,
.section_expr = linksection_expr,
.callconv_expr = callconv_expr,
.body_node = body_node,
.var_args_token = var_args_token,
});
mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
const fn_proto = try c.arena.create(ast.Payload.Func);
fn_proto.* = .{
.base = .{ .tag = .func },
.data = .{
.is_pub = is_pub,
.is_extern = is_extern,
.is_export = is_export,
.is_var_args = is_var_args,
.name = name,
.link_section_string = link_section_string,
.explicit_callconv = explicit_callconv,
.params = c.arena.dupe(ast.Payload.Func.Param, fn_params.items),
.return_type = return_node,
.body = null,
.alignment = alignment,
},
};
return fn_proto;
}
@ -5122,124 +5042,12 @@ fn fail(
}
pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, comptime format: []const u8, args: anytype) !void {
// location
// pub const name = @compileError(msg);
const pub_tok = try appendToken(c, .Keyword_pub, "pub");
const const_tok = try appendToken(c, .Keyword_const, "const");
const name_tok = try appendIdentifier(c, name);
const eq_tok = try appendToken(c, .Equal, "=");
const builtin_tok = try appendToken(c, .Builtin, "@compileError");
const lparen_tok = try appendToken(c, .LParen, "(");
const msg_tok = try appendTokenFmt(c, .StringLiteral, "\"" ++ format ++ "\"", args);
const rparen_tok = try appendToken(c, .RParen, ")");
const semi_tok = try appendToken(c, .Semicolon, ";");
_ = try appendTokenFmt(c, .LineComment, "// {s}", .{c.locStr(loc)});
const msg_node = try c.arena.create(ast.Node.OneToken);
msg_node.* = .{
.base = .{ .tag = .StringLiteral },
.token = msg_tok,
};
const call_node = try ast.Node.BuiltinCall.alloc(c.arena, 1);
call_node.* = .{
.builtin_token = builtin_tok,
.params_len = 1,
.rparen_token = rparen_tok,
};
call_node.params()[0] = &msg_node.base;
const var_decl_node = try ast.Node.VarDecl.create(c.arena, .{
.name_token = name_tok,
.mut_token = const_tok,
.semicolon_token = semi_tok,
}, .{
.visib_token = pub_tok,
.eq_token = eq_tok,
.init_node = &call_node.base,
});
try addTopLevelDecl(c, name, &var_decl_node.base);
}
fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenIndex {
std.debug.assert(token_id != .Identifier); // use appendIdentifier
return appendTokenFmt(c, token_id, "{s}", .{bytes});
}
fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: anytype) !ast.TokenIndex {
assert(token_id != .Invalid);
try c.token_ids.ensureCapacity(c.gpa, c.token_ids.items.len + 1);
try c.token_locs.ensureCapacity(c.gpa, c.token_locs.items.len + 1);
const start_index = c.source_buffer.items.len;
try c.source_buffer.writer().print(format ++ " ", args);
c.token_ids.appendAssumeCapacity(token_id);
c.token_locs.appendAssumeCapacity(.{
.start = start_index,
.end = c.source_buffer.items.len - 1, // back up before the space
});
return c.token_ids.items.len - 1;
}
// TODO hook up with codegen
fn isZigPrimitiveType(name: []const u8) bool {
if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) {
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 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 appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex {
return appendTokenFmt(c, .Identifier, "{}", .{std.zig.fmtId(name)});
}
fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node {
const token_index = try appendIdentifier(c, name);
const identifier = try c.arena.create(ast.Node.OneToken);
identifier.* = .{
.base = .{ .tag = .Identifier },
.token = token_index,
};
return &identifier.base;
}
fn transCreateNodeIdentifierUnchecked(c: *Context, name: []const u8) !*ast.Node {
const token_index = try appendTokenFmt(c, .Identifier, "{s}", .{name});
const identifier = try c.arena.create(ast.Node.OneToken);
identifier.* = .{
.base = .{ .tag = .Identifier },
.token = token_index,
};
return &identifier.base;
const location_comment = std.fmt.allocPrint(c.arena, "// {s}", .{c.locStr(loc)});
try c.global_scope.nodes.append(try Node.warning.create(c.arena, location_comment));
const fail_msg = std.fmt.allocPrint(c.arena, format, args);
try c.global_scope.nodes.append(try Node.fail_decl.create(c.arena, fail_msg));
}
pub fn freeErrors(errors: []ClangErrMsg) void {

View File

@ -14,6 +14,10 @@ pub const Node = extern union {
true_literal,
false_literal,
empty_block,
return_void,
zero_literal,
void_type,
noreturn_type,
/// pub usingnamespace @import("std").c.builtins;
usingnamespace_builtins,
// After this, the tag requires a payload.
@ -99,6 +103,7 @@ pub const Node = extern union {
bit_or,
bit_xor,
log2_int_type,
/// @import("std").math.Log2Int(operand)
std_math_Log2Int,
/// @intCast(lhs, rhs)
@ -132,7 +137,13 @@ pub const Node = extern union {
single_pointer,
array_type,
pub const last_no_payload_tag = Tag.false_literal;
// pub const name = @compileError(msg);
fail_decl,
// var actual = mangled;
arg_redecl,
pub const last_no_payload_tag = Tag.usingnamespace_builtins;
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
pub fn Type(tag: Tag) ?type {
@ -144,6 +155,10 @@ pub const Node = extern union {
.false_litral,
.empty_block,
.usingnamespace_builtins,
.return_void,
.zero_literal,
.void_type,
.noreturn_type,
=> @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
.array_access,
@ -227,6 +242,7 @@ pub const Node = extern union {
.sizeof,
.alignof,
.type,
.fail_decl,
=> Payload.Value,
.@"if" => Payload.If,
.@"while" => Payload.While,
@ -244,6 +260,8 @@ pub const Node = extern union {
.c_pointer => Payload.Pointer,
.single_pointer => Payload.Pointer,
.array_type => Payload.Array,
.arg_redecl => Payload.ArgRedecl,
.log2_int_type => Payload.Log2IntType,
};
}
@ -265,6 +283,24 @@ pub const Node = extern union {
return std.meta.fieldInfo(t.Type(), .data).field_type;
}
};
pub fn tag(self: Node) Tag {
if (self.tag_if_small_enough < Tag.no_payload_count) {
return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
} else {
return self.ptr_otherwise.tag;
}
}
pub fn castTag(self: Node, comptime t: Tag) ?*t.Type() {
if (self.tag_if_small_enough < Tag.no_payload_count)
return null;
if (self.ptr_otherwise.tag == t)
return @fieldParentPtr(t.Type(), "base", self.ptr_otherwise);
return null;
}
};
pub const Payload = struct {
@ -360,19 +396,22 @@ pub const Payload = struct {
pub const Func = struct {
base: Node = .{.func},
data: struct {
@"pub": bool,
@"extern": bool,
@"export": bool,
is_pub: bool,
is_extern: bool,
is_export: bool,
is_var_args: bool,
name: []const u8,
cc: std.builtin.CallingConvention,
link_section_string: ?[]const u8,
explicit_callconv: ?std.builtin.CallingConvention,
params: []Param,
return_type: Type,
return_type: Node,
body: ?Node,
alignment: c_uint,
pub const Param = struct {
@"noalias": bool,
is_noalias: bool,
name: ?[]const u8,
type: Type,
type: Node,
};
},
};
@ -449,6 +488,19 @@ pub const Payload = struct {
is_volatile: bool,
},
};
pub const ArgRedecl = struct {
base: Node,
data: struct {
actual: []const u8,
mangled: []const u8,
},
};
pub const Log2IntType = struct {
base: Node,
data: std.math.Log2Int(u64),
};
};
/// Converts the nodes into a Zig ast.