From f36849fed24b54476ecadacb52d9a1b55ae14274 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 7 Feb 2021 23:13:40 +0200 Subject: [PATCH] translate-c: convert function translation --- src/translate_c.zig | 378 ++++++++++------------------------------ src/translate_c/ast.zig | 68 +++++++- 2 files changed, 153 insertions(+), 293 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index a8285e3036..2bbfe0a562 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -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 { diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index cc5e8dd7ce..825d36add1 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -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.