aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2024-02-28 18:21:30 -0800
committerGitHub <noreply@github.com>2024-02-28 18:21:30 -0800
commitf5aad472873a37eba2f15d0ece9d1e813fa93d06 (patch)
treee66a3364a6ce5c380ce1c0cbcdf4de9b5ca656ba /src
parent69df00f657c567339d0d39c50381eaec4fab32a4 (diff)
parent49437d34e65271a9e9211043b46c27e9c8196a6a (diff)
downloadzig-f5aad472873a37eba2f15d0ece9d1e813fa93d06.tar.gz
zig-f5aad472873a37eba2f15d0ece9d1e813fa93d06.zip
Merge pull request #19122 from ziglang/lazy-aro
make aro-based translate-c lazily built from source
Diffstat (limited to 'src')
-rw-r--r--src/Compilation.zig2
-rw-r--r--src/aro_translate_c.zig678
-rw-r--r--src/main.zig121
-rw-r--r--src/stubs/aro_builtins.zig35
-rw-r--r--src/stubs/aro_messages.zig508
-rw-r--r--src/stubs/aro_names.zig10
-rw-r--r--src/stubs/aro_options.zig1
-rw-r--r--src/translate_c.zig293
-rw-r--r--src/translate_c/ast.zig2942
-rw-r--r--src/translate_c/common.zig322
10 files changed, 90 insertions, 4822 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 58413f7c9e..78330b6806 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -4007,8 +4007,6 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module
}
var tree = switch (comp.config.c_frontend) {
.aro => tree: {
- const translate_c = @import("aro_translate_c.zig");
- _ = translate_c;
if (true) @panic("TODO");
break :tree undefined;
},
diff --git a/src/aro_translate_c.zig b/src/aro_translate_c.zig
deleted file mode 100644
index 4b29ec1edd..0000000000
--- a/src/aro_translate_c.zig
+++ /dev/null
@@ -1,678 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-const assert = std.debug.assert;
-const CallingConvention = std.builtin.CallingConvention;
-const translate_c = @import("translate_c.zig");
-const aro = @import("aro");
-const Tree = aro.Tree;
-const NodeIndex = Tree.NodeIndex;
-const TokenIndex = Tree.TokenIndex;
-const Type = aro.Type;
-const ast = @import("translate_c/ast.zig");
-const ZigNode = ast.Node;
-const ZigTag = ZigNode.Tag;
-const common = @import("translate_c/common.zig");
-const Error = common.Error;
-const MacroProcessingError = common.MacroProcessingError;
-const TypeError = common.TypeError;
-const TransError = common.TransError;
-const SymbolTable = common.SymbolTable;
-const AliasList = common.AliasList;
-const ResultUsed = common.ResultUsed;
-const Scope = common.ScopeExtra(Context, Type);
-
-const Context = struct {
- gpa: mem.Allocator,
- arena: mem.Allocator,
- decl_table: std.AutoArrayHashMapUnmanaged(usize, []const u8) = .{},
- alias_list: AliasList,
- global_scope: *Scope.Root,
- mangle_count: u32 = 0,
- /// Table of record decls that have been demoted to opaques.
- opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{},
- /// Table of unnamed enums and records that are child types of typedefs.
- unnamed_typedefs: std.AutoHashMapUnmanaged(usize, []const u8) = .{},
- /// Needed to decide if we are parsing a typename
- typedefs: std.StringArrayHashMapUnmanaged(void) = .{},
-
- /// This one is different than the root scope's name table. This contains
- /// a list of names that we found by visiting all the top level decls without
- /// translating them. The other maps are updated as we translate; this one is updated
- /// up front in a pre-processing step.
- global_names: std.StringArrayHashMapUnmanaged(void) = .{},
-
- /// This is similar to `global_names`, but contains names which we would
- /// *like* to use, but do not strictly *have* to if they are unavailable.
- /// These are relevant to types, which ideally we would name like
- /// 'struct_foo' with an alias 'foo', but if either of those names is taken,
- /// may be mangled.
- /// This is distinct from `global_names` so we can detect at a type
- /// declaration whether or not the name is available.
- weak_global_names: std.StringArrayHashMapUnmanaged(void) = .{},
-
- pattern_list: translate_c.PatternList,
- tree: Tree,
- comp: *aro.Compilation,
- mapper: aro.TypeMapper,
-
- fn getMangle(c: *Context) u32 {
- c.mangle_count += 1;
- return c.mangle_count;
- }
-
- /// Convert a clang source location to a file:line:column string
- fn locStr(c: *Context, loc: TokenIndex) ![]const u8 {
- _ = c;
- _ = loc;
- // const spelling_loc = c.source_manager.getSpellingLoc(loc);
- // const filename_c = c.source_manager.getFilename(spelling_loc);
- // const filename = if (filename_c) |s| try c.str(s) else @as([]const u8, "(no file)");
-
- // const line = c.source_manager.getSpellingLineNumber(spelling_loc);
- // const column = c.source_manager.getSpellingColumnNumber(spelling_loc);
- // return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, column });
- return "somewhere";
- }
-};
-
-fn maybeSuppressResult(c: *Context, used: ResultUsed, result: ZigNode) TransError!ZigNode {
- if (used == .used) return result;
- return ZigTag.discard.create(c.arena, .{ .should_skip = false, .value = result });
-}
-
-fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: ZigNode) !void {
- const gop = try c.global_scope.sym_table.getOrPut(name);
- if (!gop.found_existing) {
- gop.value_ptr.* = decl_node;
- try c.global_scope.nodes.append(decl_node);
- }
-}
-
-fn failDecl(c: *Context, loc: TokenIndex, name: []const u8, comptime format: []const u8, args: anytype) Error!void {
- // location
- // pub const name = @compileError(msg);
- const fail_msg = try std.fmt.allocPrint(c.arena, format, args);
- try addTopLevelDecl(c, name, try ZigTag.fail_decl.create(c.arena, .{ .actual = name, .mangled = fail_msg }));
- const str = try c.locStr(loc);
- const location_comment = try std.fmt.allocPrint(c.arena, "// {s}", .{str});
- try c.global_scope.nodes.append(try ZigTag.warning.create(c.arena, location_comment));
-}
-
-fn warn(c: *Context, scope: *Scope, loc: TokenIndex, comptime format: []const u8, args: anytype) !void {
- const str = try c.locStr(loc);
- const value = try std.fmt.allocPrint(c.arena, "// {s}: warning: " ++ format, .{str} ++ args);
- try scope.appendNode(try ZigTag.warning.create(c.arena, value));
-}
-
-pub fn translate(
- gpa: mem.Allocator,
- comp: *aro.Compilation,
- args: []const []const u8,
-) !std.zig.Ast {
- try comp.addDefaultPragmaHandlers();
- comp.langopts.setEmulatedCompiler(aro.target_util.systemCompiler(comp.target));
-
- var driver: aro.Driver = .{ .comp = comp };
- defer driver.deinit();
-
- var macro_buf = std.ArrayList(u8).init(gpa);
- defer macro_buf.deinit();
-
- assert(!try driver.parseArgs(std.io.null_writer, macro_buf.writer(), args));
- assert(driver.inputs.items.len == 1);
- const source = driver.inputs.items[0];
-
- const builtin_macros = try comp.generateBuiltinMacros(.include_system_defines);
- const user_macros = try comp.addSourceFromBuffer("<command line>", macro_buf.items);
-
- var pp = try aro.Preprocessor.initDefault(comp);
- defer pp.deinit();
-
- try pp.preprocessSources(&.{ source, builtin_macros, user_macros });
-
- var tree = try pp.parse();
- defer tree.deinit();
-
- if (driver.comp.diagnostics.errors != 0) {
- return error.SemanticAnalyzeFail;
- }
-
- const mapper = tree.comp.string_interner.getFastTypeMapper(tree.comp.gpa) catch tree.comp.string_interner.getSlowTypeMapper();
- defer mapper.deinit(tree.comp.gpa);
-
- var arena_allocator = std.heap.ArenaAllocator.init(gpa);
- defer arena_allocator.deinit();
- const arena = arena_allocator.allocator();
-
- var context = Context{
- .gpa = gpa,
- .arena = arena,
- .alias_list = AliasList.init(gpa),
- .global_scope = try arena.create(Scope.Root),
- .pattern_list = try translate_c.PatternList.init(gpa),
- .comp = comp,
- .mapper = mapper,
- .tree = tree,
- };
- context.global_scope.* = Scope.Root.init(&context);
- defer {
- context.decl_table.deinit(gpa);
- context.alias_list.deinit();
- context.global_names.deinit(gpa);
- context.opaque_demotes.deinit(gpa);
- context.unnamed_typedefs.deinit(gpa);
- context.typedefs.deinit(gpa);
- context.global_scope.deinit();
- context.pattern_list.deinit(gpa);
- }
-
- inline for (@typeInfo(std.zig.c_builtins).Struct.decls) |decl| {
- const builtin_fn = try ZigTag.pub_var_simple.create(arena, .{
- .name = decl.name,
- .init = try ZigTag.import_c_builtin.create(arena, decl.name),
- });
- try addTopLevelDecl(&context, decl.name, builtin_fn);
- }
-
- try prepopulateGlobalNameTable(&context);
- try transTopLevelDecls(&context);
-
- for (context.alias_list.items) |alias| {
- if (!context.global_scope.sym_table.contains(alias.alias)) {
- const node = try ZigTag.alias.create(arena, .{ .actual = alias.alias, .mangled = alias.name });
- try addTopLevelDecl(&context, alias.alias, node);
- }
- }
-
- return ast.render(gpa, context.global_scope.nodes.items);
-}
-
-fn prepopulateGlobalNameTable(c: *Context) !void {
- const node_tags = c.tree.nodes.items(.tag);
- const node_types = c.tree.nodes.items(.ty);
- const node_data = c.tree.nodes.items(.data);
- for (c.tree.root_decls) |node| {
- const data = node_data[@intFromEnum(node)];
- const decl_name = switch (node_tags[@intFromEnum(node)]) {
- .typedef => @panic("TODO"),
-
- .static_assert,
- .struct_decl_two,
- .union_decl_two,
- .struct_decl,
- .union_decl,
- => blk: {
- const ty = node_types[@intFromEnum(node)];
- const name_id = ty.data.record.name;
- break :blk c.mapper.lookup(name_id);
- },
-
- .enum_decl_two,
- .enum_decl,
- => blk: {
- const ty = node_types[@intFromEnum(node)];
- const name_id = ty.data.@"enum".name;
- break :blk c.mapper.lookup(name_id);
- },
-
- .fn_proto,
- .static_fn_proto,
- .inline_fn_proto,
- .inline_static_fn_proto,
- .fn_def,
- .static_fn_def,
- .inline_fn_def,
- .inline_static_fn_def,
- .@"var",
- .static_var,
- .threadlocal_var,
- .threadlocal_static_var,
- .extern_var,
- .threadlocal_extern_var,
- => c.tree.tokSlice(data.decl.name),
- else => unreachable,
- };
- try c.global_names.put(c.gpa, decl_name, {});
- }
-}
-
-fn transTopLevelDecls(c: *Context) !void {
- const node_tags = c.tree.nodes.items(.tag);
- const node_data = c.tree.nodes.items(.data);
- for (c.tree.root_decls) |node| {
- const data = node_data[@intFromEnum(node)];
- switch (node_tags[@intFromEnum(node)]) {
- .typedef => {
- try transTypeDef(c, &c.global_scope.base, node);
- },
-
- .static_assert,
- .struct_decl_two,
- .union_decl_two,
- .struct_decl,
- .union_decl,
- => {
- try transRecordDecl(c, &c.global_scope.base, node);
- },
-
- .enum_decl_two => {
- var fields = [2]NodeIndex{ data.bin.lhs, data.bin.rhs };
- var field_count: u8 = 0;
- if (fields[0] != .none) field_count += 1;
- if (fields[1] != .none) field_count += 1;
- try transEnumDecl(c, &c.global_scope.base, node, fields[0..field_count]);
- },
- .enum_decl => {
- const fields = c.tree.data[data.range.start..data.range.end];
- try transEnumDecl(c, &c.global_scope.base, node, fields);
- },
-
- .fn_proto,
- .static_fn_proto,
- .inline_fn_proto,
- .inline_static_fn_proto,
- .fn_def,
- .static_fn_def,
- .inline_fn_def,
- .inline_static_fn_def,
- => {
- try transFnDecl(c, node);
- },
-
- .@"var",
- .static_var,
- .threadlocal_var,
- .threadlocal_static_var,
- .extern_var,
- .threadlocal_extern_var,
- => {
- try transVarDecl(c, node, null);
- },
- else => unreachable,
- }
- }
-}
-
-fn transTypeDef(_: *Context, _: *Scope, _: NodeIndex) Error!void {
- @panic("TODO");
-}
-fn transRecordDecl(_: *Context, _: *Scope, _: NodeIndex) Error!void {
- @panic("TODO");
-}
-
-fn transFnDecl(c: *Context, fn_decl: NodeIndex) Error!void {
- const raw_ty = c.tree.nodes.items(.ty)[@intFromEnum(fn_decl)];
- const fn_ty = raw_ty.canonicalize(.standard);
- const node_data = c.tree.nodes.items(.data)[@intFromEnum(fn_decl)];
- if (c.decl_table.get(@intFromPtr(fn_ty.data.func))) |_|
- return; // Avoid processing this decl twice
-
- const fn_name = c.tree.tokSlice(node_data.decl.name);
- if (c.global_scope.sym_table.contains(fn_name))
- return; // Avoid processing this decl twice
-
- const fn_decl_loc = 0; // TODO
- const has_body = node_data.decl.node != .none;
- const is_always_inline = has_body and raw_ty.getAttribute(.always_inline) != null;
- const proto_ctx = FnProtoContext{
- .fn_name = fn_name,
- .is_inline = is_always_inline,
- .is_extern = !has_body,
- .is_export = switch (c.tree.nodes.items(.tag)[@intFromEnum(fn_decl)]) {
- .fn_proto, .fn_def => has_body and !is_always_inline,
-
- .inline_fn_proto, .inline_fn_def, .inline_static_fn_proto, .inline_static_fn_def, .static_fn_proto, .static_fn_def => false,
-
- else => unreachable,
- },
- };
-
- const proto_node = transFnType(c, &c.global_scope.base, raw_ty, fn_ty, fn_decl_loc, proto_ctx) catch |err| switch (err) {
- error.UnsupportedType => {
- return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
- },
- error.OutOfMemory => |e| return e,
- };
-
- if (!has_body) {
- return addTopLevelDecl(c, fn_name, proto_node);
- }
- const proto_payload = proto_node.castTag(.func).?;
-
- // actual function definition with body
- const body_stmt = node_data.decl.node;
- var block_scope = try Scope.Block.init(c, &c.global_scope.base, false);
- block_scope.return_type = fn_ty.data.func.return_type;
- defer block_scope.deinit();
-
- var scope = &block_scope.base;
- _ = &scope;
-
- var param_id: c_uint = 0;
- for (proto_payload.data.params, fn_ty.data.func.params) |*param, param_info| {
- const param_name = param.name orelse {
- proto_payload.data.is_extern = true;
- proto_payload.data.is_export = false;
- proto_payload.data.is_inline = false;
- try warn(c, &c.global_scope.base, fn_decl_loc, "function {s} parameter has no name, demoted to extern", .{fn_name});
- return addTopLevelDecl(c, fn_name, proto_node);
- };
-
- const is_const = param_info.ty.qual.@"const";
-
- 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 redecl_node = try ZigTag.arg_redecl.create(c.arena, .{ .actual = mangled_param_name, .mangled = arg_name });
- try block_scope.statements.append(redecl_node);
- }
- try block_scope.discardVariable(c, mangled_param_name);
-
- param_id += 1;
- }
-
- transCompoundStmtInline(c, body_stmt, &block_scope) catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- error.UnsupportedTranslation,
- error.UnsupportedType,
- => {
- proto_payload.data.is_extern = true;
- proto_payload.data.is_export = false;
- proto_payload.data.is_inline = false;
- try warn(c, &c.global_scope.base, fn_decl_loc, "unable to translate function, demoted to extern", .{});
- return addTopLevelDecl(c, fn_name, proto_node);
- },
- };
-
- proto_payload.data.body = try block_scope.complete(c);
- return addTopLevelDecl(c, fn_name, proto_node);
-}
-
-fn transVarDecl(_: *Context, _: NodeIndex, _: ?usize) Error!void {
- @panic("TODO");
-}
-
-fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: NodeIndex, field_nodes: []const NodeIndex) Error!void {
- const node_types = c.tree.nodes.items(.ty);
- const ty = node_types[@intFromEnum(enum_decl)];
- if (c.decl_table.get(@intFromPtr(ty.data.@"enum"))) |_|
- return; // Avoid processing this decl twice
- const toplevel = scope.id == .root;
- const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
-
- var is_unnamed = false;
- var bare_name: []const u8 = c.mapper.lookup(ty.data.@"enum".name);
- var name = bare_name;
- if (c.unnamed_typedefs.get(@intFromPtr(ty.data.@"enum"))) |typedef_name| {
- bare_name = typedef_name;
- name = typedef_name;
- } else {
- if (bare_name.len == 0) {
- bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});
- is_unnamed = true;
- }
- name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});
- }
- if (!toplevel) name = try bs.makeMangledName(c, name);
- try c.decl_table.putNoClobber(c.gpa, @intFromPtr(ty.data.@"enum"), name);
-
- const enum_type_node = if (!ty.data.@"enum".isIncomplete()) blk: {
- for (ty.data.@"enum".fields, field_nodes) |field, field_node| {
- var enum_val_name: []const u8 = c.mapper.lookup(field.name);
- if (!toplevel) {
- enum_val_name = try bs.makeMangledName(c, enum_val_name);
- }
-
- const enum_const_type_node: ?ZigNode = transType(c, scope, field.ty, field.name_tok) catch |err| switch (err) {
- error.UnsupportedType => null,
- else => |e| return e,
- };
-
- const val = c.tree.value_map.get(field_node).?;
- const enum_const_def = try ZigTag.enum_constant.create(c.arena, .{
- .name = enum_val_name,
- .is_public = toplevel,
- .type = enum_const_type_node,
- .value = try transCreateNodeAPInt(c, val),
- });
- if (toplevel)
- try addTopLevelDecl(c, enum_val_name, enum_const_def)
- else {
- try scope.appendNode(enum_const_def);
- try bs.discardVariable(c, enum_val_name);
- }
- }
-
- break :blk transType(c, scope, ty.data.@"enum".tag_ty, 0) catch |err| switch (err) {
- error.UnsupportedType => {
- return failDecl(c, 0, name, "unable to translate enum integer type", .{});
- },
- else => |e| return e,
- };
- } else blk: {
- try c.opaque_demotes.put(c.gpa, @intFromPtr(ty.data.@"enum"), {});
- break :blk ZigTag.opaque_literal.init();
- };
-
- const is_pub = toplevel and !is_unnamed;
- const payload = try c.arena.create(ast.Payload.SimpleVarDecl);
- payload.* = .{
- .base = .{ .tag = ([2]ZigTag{ .var_simple, .pub_var_simple })[@intFromBool(is_pub)] },
- .data = .{
- .init = enum_type_node,
- .name = name,
- },
- };
- const node = ZigNode.initPayload(&payload.base);
- if (toplevel) {
- try addTopLevelDecl(c, name, node);
- if (!is_unnamed)
- try c.alias_list.append(.{ .alias = bare_name, .name = name });
- } else {
- try scope.appendNode(node);
- if (node.tag() != .pub_var_simple) {
- try bs.discardVariable(c, name);
- }
- }
-}
-
-fn transType(c: *Context, scope: *Scope, raw_ty: Type, source_loc: TokenIndex) TypeError!ZigNode {
- const ty = raw_ty.canonicalize(.standard);
- switch (ty.specifier) {
- .void => return ZigTag.type.create(c.arena, "anyopaque"),
- .bool => return ZigTag.type.create(c.arena, "bool"),
- .char => return ZigTag.type.create(c.arena, "c_char"),
- .schar => return ZigTag.type.create(c.arena, "i8"),
- .uchar => return ZigTag.type.create(c.arena, "u8"),
- .short => return ZigTag.type.create(c.arena, "c_short"),
- .ushort => return ZigTag.type.create(c.arena, "c_ushort"),
- .int => return ZigTag.type.create(c.arena, "c_int"),
- .uint => return ZigTag.type.create(c.arena, "c_uint"),
- .long => return ZigTag.type.create(c.arena, "c_long"),
- .ulong => return ZigTag.type.create(c.arena, "c_ulong"),
- .long_long => return ZigTag.type.create(c.arena, "c_longlong"),
- .ulong_long => return ZigTag.type.create(c.arena, "c_ulonglong"),
- .int128 => return ZigTag.type.create(c.arena, "i128"),
- .uint128 => return ZigTag.type.create(c.arena, "u128"),
- .fp16, .float16 => return ZigTag.type.create(c.arena, "f16"),
- .float => return ZigTag.type.create(c.arena, "f32"),
- .double => return ZigTag.type.create(c.arena, "f64"),
- .long_double => return ZigTag.type.create(c.arena, "c_longdouble"),
- .float80 => return ZigTag.type.create(c.arena, "f80"),
- .float128 => return ZigTag.type.create(c.arena, "f128"),
- .func,
- .var_args_func,
- .old_style_func,
- => return transFnType(c, scope, raw_ty, ty, source_loc, .{}),
- else => return error.UnsupportedType,
- }
-}
-
-fn zigAlignment(bit_alignment: u29) u32 {
- return bit_alignment / 8;
-}
-
-const FnProtoContext = struct {
- is_pub: bool = false,
- is_export: bool = false,
- is_extern: bool = false,
- is_inline: bool = false,
- fn_name: ?[]const u8 = null,
-};
-
-fn transFnType(
- c: *Context,
- scope: *Scope,
- raw_ty: Type,
- fn_ty: Type,
- source_loc: TokenIndex,
- ctx: FnProtoContext,
-) !ZigNode {
- const param_count: usize = fn_ty.data.func.params.len;
- const fn_params = try c.arena.alloc(ast.Payload.Param, param_count);
-
- for (fn_ty.data.func.params, fn_params) |param_info, *param_node| {
- const param_ty = param_info.ty;
- const is_noalias = param_ty.qual.restrict;
-
- const param_name: ?[]const u8 = if (param_info.name == .empty)
- null
- else
- c.mapper.lookup(param_info.name);
-
- const type_node = try transType(c, scope, param_ty, param_info.name_tok);
- param_node.* = .{
- .is_noalias = is_noalias,
- .name = param_name,
- .type = type_node,
- };
- }
-
- const linksection_string = blk: {
- if (raw_ty.getAttribute(.section)) |section| {
- break :blk c.comp.interner.get(section.name.ref()).bytes;
- }
- break :blk null;
- };
-
- const alignment = if (raw_ty.requestedAlignment(c.comp)) |alignment| zigAlignment(alignment) else null;
-
- const explicit_callconv = null;
- // const explicit_callconv = if ((ctx.is_inline or ctx.is_export or ctx.is_extern) and ctx.cc == .C) null else ctx.cc;
-
- const return_type_node = blk: {
- if (raw_ty.getAttribute(.noreturn) != null) {
- break :blk ZigTag.noreturn_type.init();
- } else {
- const return_ty = fn_ty.data.func.return_type;
- if (return_ty.is(.void)) {
- // convert primitive anyopaque to actual void (only for return type)
- break :blk ZigTag.void_type.init();
- } else {
- break :blk transType(c, scope, return_ty, source_loc) catch |err| switch (err) {
- error.UnsupportedType => {
- try warn(c, scope, source_loc, "unsupported function proto return type", .{});
- return err;
- },
- error.OutOfMemory => |e| return e,
- };
- }
- }
- };
-
- const payload = try c.arena.create(ast.Payload.Func);
- payload.* = .{
- .base = .{ .tag = .func },
- .data = .{
- .is_pub = ctx.is_pub,
- .is_extern = ctx.is_extern,
- .is_export = ctx.is_export,
- .is_inline = ctx.is_inline,
- .is_var_args = switch (fn_ty.specifier) {
- .func => false,
- .var_args_func => true,
- .old_style_func => !ctx.is_export and !ctx.is_inline,
- else => unreachable,
- },
- .name = ctx.fn_name,
- .linksection_string = linksection_string,
- .explicit_callconv = explicit_callconv,
- .params = fn_params,
- .return_type = return_type_node,
- .body = null,
- .alignment = alignment,
- },
- };
- return ZigNode.initPayload(&payload.base);
-}
-
-fn transStmt(c: *Context, node: NodeIndex) TransError!ZigNode {
- return transExpr(c, node, .unused);
-}
-
-fn transCompoundStmtInline(c: *Context, compound: NodeIndex, block: *Scope.Block) TransError!void {
- const data = c.tree.nodes.items(.data)[@intFromEnum(compound)];
- var buf: [2]NodeIndex = undefined;
- // TODO move these helpers to Aro
- const stmts = switch (c.tree.nodes.items(.tag)[@intFromEnum(compound)]) {
- .compound_stmt_two => blk: {
- if (data.bin.lhs != .none) buf[0] = data.bin.lhs;
- if (data.bin.rhs != .none) buf[1] = data.bin.rhs;
- break :blk buf[0 .. @as(u32, @intFromBool(data.bin.lhs != .none)) + @intFromBool(data.bin.rhs != .none)];
- },
- .compound_stmt => c.tree.data[data.range.start..data.range.end],
- else => unreachable,
- };
- for (stmts) |stmt| {
- const result = try transStmt(c, stmt);
- switch (result.tag()) {
- .declaration, .empty_block => {},
- else => try block.statements.append(result),
- }
- }
-}
-
-fn transCompoundStmt(c: *Context, scope: *Scope, compound: NodeIndex) TransError!ZigNode {
- var block_scope = try Scope.Block.init(c, scope, false);
- defer block_scope.deinit();
- try transCompoundStmtInline(c, compound, &block_scope);
- return try block_scope.complete(c);
-}
-
-fn transExpr(c: *Context, node: NodeIndex, result_used: ResultUsed) TransError!ZigNode {
- std.debug.assert(node != .none);
- const ty = c.tree.nodes.items(.ty)[@intFromEnum(node)];
- if (c.tree.value_map.get(node)) |val| {
- // TODO handle other values
- const int = try transCreateNodeAPInt(c, val);
- const as_node = try ZigTag.as.create(c.arena, .{
- .lhs = try transType(c, undefined, ty, undefined),
- .rhs = int,
- });
- return maybeSuppressResult(c, result_used, as_node);
- }
- const node_tags = c.tree.nodes.items(.tag);
- switch (node_tags[@intFromEnum(node)]) {
- else => unreachable, // Not an expression.
- }
- return .none;
-}
-
-fn transCreateNodeAPInt(c: *Context, int: aro.Value) !ZigNode {
- var space: aro.Interner.Tag.Int.BigIntSpace = undefined;
- var big = int.toBigInt(&space, c.comp);
- const is_negative = !big.positive;
- big.positive = true;
-
- const str = big.toStringAlloc(c.arena, 10, .lower) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- };
- const res = try ZigTag.integer_literal.create(c.arena, str);
- if (is_negative) return ZigTag.negate.create(c.arena, res);
- return res;
-}
diff --git a/src/main.zig b/src/main.zig
index 9da48ba69c..e08eeb20d1 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -294,13 +294,20 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
} else if (mem.eql(u8, cmd, "rc")) {
return cmdRc(gpa, arena, args[1..]);
} else if (mem.eql(u8, cmd, "fmt")) {
- return jitCmd(gpa, arena, cmd_args, "fmt", "fmt.zig", false);
+ return jitCmd(gpa, arena, cmd_args, .{
+ .cmd_name = "fmt",
+ .root_src_path = "fmt.zig",
+ });
} else if (mem.eql(u8, cmd, "objcopy")) {
return @import("objcopy.zig").cmdObjCopy(gpa, arena, cmd_args);
} else if (mem.eql(u8, cmd, "fetch")) {
return cmdFetch(gpa, arena, cmd_args);
} else if (mem.eql(u8, cmd, "libc")) {
- return jitCmd(gpa, arena, cmd_args, "libc", "libc.zig", true);
+ return jitCmd(gpa, arena, cmd_args, .{
+ .cmd_name = "libc",
+ .root_src_path = "libc.zig",
+ .prepend_zig_lib_dir_path = true,
+ });
} else if (mem.eql(u8, cmd, "init")) {
return cmdInit(gpa, arena, cmd_args);
} else if (mem.eql(u8, cmd, "targets")) {
@@ -317,7 +324,10 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
verifyLibcxxCorrectlyLinked();
return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer());
} else if (mem.eql(u8, cmd, "reduce")) {
- return jitCmd(gpa, arena, cmd_args, "reduce", "reduce.zig", false);
+ return jitCmd(gpa, arena, cmd_args, .{
+ .cmd_name = "reduce",
+ .root_src_path = "reduce.zig",
+ });
} else if (mem.eql(u8, cmd, "zen")) {
return io.getStdOut().writeAll(info_zen);
} else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) {
@@ -4459,7 +4469,13 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati
const digest = if (try man.hit()) man.final() else digest: {
if (fancy_output) |p| p.cache_hit = false;
var argv = std.ArrayList([]const u8).init(arena);
- try argv.append(@tagName(comp.config.c_frontend)); // argv[0] is program name, actual args start at [1]
+ switch (comp.config.c_frontend) {
+ .aro => {},
+ .clang => {
+ // argv[0] is program name, actual args start at [1]
+ try argv.append(@tagName(comp.config.c_frontend));
+ },
+ }
var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});
defer zig_cache_tmp_dir.close();
@@ -4484,24 +4500,18 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati
Compilation.dump_argv(argv.items);
}
- var tree = switch (comp.config.c_frontend) {
- .aro => tree: {
- const aro = @import("aro");
- const translate_c = @import("aro_translate_c.zig");
- var aro_comp = aro.Compilation.init(comp.gpa);
- defer aro_comp.deinit();
-
- break :tree translate_c.translate(comp.gpa, &aro_comp, argv.items) catch |err| switch (err) {
- error.SemanticAnalyzeFail, error.FatalError => {
- // TODO convert these to zig errors
- aro.Diagnostics.render(&aro_comp, std.io.tty.detectConfig(std.io.getStdErr()));
- process.exit(1);
- },
- error.OutOfMemory => return error.OutOfMemory,
- error.StreamTooLong => fatal("StreamTooLong?", .{}),
- };
+ const formatted = switch (comp.config.c_frontend) {
+ .aro => f: {
+ var stdout: []u8 = undefined;
+ try jitCmd(comp.gpa, arena, argv.items, .{
+ .cmd_name = "aro_translate_c",
+ .root_src_path = "aro_translate_c.zig",
+ .depend_on_aro = true,
+ .capture = &stdout,
+ });
+ break :f stdout;
},
- .clang => tree: {
+ .clang => f: {
if (!build_options.have_llvm) unreachable;
const translate_c = @import("translate_c.zig");
@@ -4519,7 +4529,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati
const c_headers_dir_path_z = try comp.zig_lib_directory.joinZ(arena, &[_][]const u8{"include"});
var errors = std.zig.ErrorBundle.empty;
- break :tree translate_c.translate(
+ var tree = translate_c.translate(
comp.gpa,
new_argv.ptr,
new_argv.ptr + new_argv.len,
@@ -4537,9 +4547,10 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati
}
},
};
+ defer tree.deinit(comp.gpa);
+ break :f try tree.render(arena);
},
};
- defer tree.deinit(comp.gpa);
if (out_dep_path) |dep_file_path| {
const dep_basename = fs.path.basename(dep_file_path);
@@ -4560,9 +4571,6 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati
var zig_file = try o_dir.createFile(translated_zig_basename, .{});
defer zig_file.close();
- const formatted = try tree.render(comp.gpa);
- defer comp.gpa.free(formatted);
-
try zig_file.writeAll(formatted);
man.writeManifest() catch |err| warn("failed to write cache manifest: {s}", .{
@@ -5522,13 +5530,19 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
}
}
+const JitCmdOptions = struct {
+ cmd_name: []const u8,
+ root_src_path: []const u8,
+ prepend_zig_lib_dir_path: bool = false,
+ depend_on_aro: bool = false,
+ capture: ?*[]u8 = null,
+};
+
fn jitCmd(
gpa: Allocator,
arena: Allocator,
args: []const []const u8,
- cmd_name: []const u8,
- root_src_path: []const u8,
- prepend_zig_lib_dir_path: bool,
+ options: JitCmdOptions,
) !void {
const color: Color = .auto;
@@ -5540,7 +5554,7 @@ fn jitCmd(
};
const exe_basename = try std.zig.binNameAlloc(arena, .{
- .root_name = cmd_name,
+ .root_name = options.cmd_name,
.target = resolved_target.result,
.output_mode = .Exe,
});
@@ -5595,7 +5609,7 @@ fn jitCmd(
.root_dir = zig_lib_directory,
.sub_path = "compiler",
},
- .root_src_path = root_src_path,
+ .root_src_path = options.root_src_path,
};
const config = try Compilation.Config.resolve(.{
@@ -5623,11 +5637,35 @@ fn jitCmd(
.builtin_mod = null,
});
+ if (options.depend_on_aro) {
+ const aro_mod = try Package.Module.create(arena, .{
+ .global_cache_directory = global_cache_directory,
+ .paths = .{
+ .root = .{
+ .root_dir = zig_lib_directory,
+ .sub_path = "compiler/aro",
+ },
+ .root_src_path = "aro.zig",
+ },
+ .fully_qualified_name = "aro",
+ .cc_argv = &.{},
+ .inherited = .{
+ .resolved_target = resolved_target,
+ .optimize_mode = optimize_mode,
+ .strip = strip,
+ },
+ .global = config,
+ .parent = null,
+ .builtin_mod = root_mod.getBuiltinDependency(),
+ });
+ try root_mod.deps.put(arena, "aro", aro_mod);
+ }
+
const comp = Compilation.create(gpa, arena, .{
.zig_lib_directory = zig_lib_directory,
.local_cache_directory = global_cache_directory,
.global_cache_directory = global_cache_directory,
- .root_name = cmd_name,
+ .root_name = options.cmd_name,
.config = config,
.root_mod = root_mod,
.main_mod = root_mod,
@@ -5650,12 +5688,12 @@ fn jitCmd(
child_argv.appendAssumeCapacity(exe_path);
}
- if (prepend_zig_lib_dir_path)
+ if (options.prepend_zig_lib_dir_path)
child_argv.appendAssumeCapacity(zig_lib_directory.path.?);
child_argv.appendSliceAssumeCapacity(args);
- if (process.can_execv) {
+ if (process.can_execv and options.capture == null) {
const err = process.execv(gpa, child_argv.items);
const cmd = try std.mem.join(arena, " ", child_argv.items);
fatal("the following command failed to execve with '{s}':\n{s}", .{
@@ -5673,13 +5711,22 @@ fn jitCmd(
var child = std.ChildProcess.init(child_argv.items, gpa);
child.stdin_behavior = .Inherit;
- child.stdout_behavior = .Inherit;
+ child.stdout_behavior = if (options.capture == null) .Inherit else .Pipe;
child.stderr_behavior = .Inherit;
- const term = try child.spawnAndWait();
+ try child.spawn();
+
+ if (options.capture) |ptr| {
+ ptr.* = try child.stdout.?.readToEndAlloc(arena, std.math.maxInt(u32));
+ }
+
+ const term = try child.wait();
switch (term) {
.Exited => |code| {
- if (code == 0) return cleanExit();
+ if (code == 0) {
+ if (options.capture != null) return;
+ return cleanExit();
+ }
const cmd = try std.mem.join(arena, " ", child_argv.items);
fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
},
diff --git a/src/stubs/aro_builtins.zig b/src/stubs/aro_builtins.zig
deleted file mode 100644
index 8e643b8307..0000000000
--- a/src/stubs/aro_builtins.zig
+++ /dev/null
@@ -1,35 +0,0 @@
-//! Stub implementation only used when bootstrapping stage2
-//! Keep in sync with deps/aro/build/GenerateDef.zig
-
-pub fn with(comptime Properties: type) type {
- return struct {
- tag: Tag = @enumFromInt(0),
- properties: Properties = undefined,
- pub const max_param_count = 1;
- pub const longest_name = 0;
- pub const data = [_]@This(){.{}};
- pub inline fn fromName(_: []const u8) ?@This() {
- return .{};
- }
- pub fn nameFromUniqueIndex(_: u16, _: []u8) []u8 {
- return "";
- }
- pub fn uniqueIndex(_: []const u8) ?u16 {
- return null;
- }
- pub const Tag = enum(u16) { _ };
- pub fn nameFromTag(_: Tag) NameBuf {
- return .{};
- }
- pub fn tagFromName(name: []const u8) ?Tag {
- var res: u16 = 0;
- for (name) |c| res +%= c;
- return @enumFromInt(res);
- }
- pub const NameBuf = struct {
- pub fn span(_: *const NameBuf) []const u8 {
- return "";
- }
- };
- };
-}
diff --git a/src/stubs/aro_messages.zig b/src/stubs/aro_messages.zig
deleted file mode 100644
index abbaf93eec..0000000000
--- a/src/stubs/aro_messages.zig
+++ /dev/null
@@ -1,508 +0,0 @@
-//! Stub implementation only used when bootstrapping stage2
-//! Keep in sync with deps/aro/build/GenerateDef.zig
-
-pub fn with(comptime Properties: type) type {
- return struct {
- pub const Tag = enum {
- todo,
- error_directive,
- warning_directive,
- elif_without_if,
- elif_after_else,
- elifdef_without_if,
- elifdef_after_else,
- elifndef_without_if,
- elifndef_after_else,
- else_without_if,
- else_after_else,
- endif_without_if,
- unknown_pragma,
- line_simple_digit,
- line_invalid_filename,
- unterminated_conditional_directive,
- invalid_preprocessing_directive,
- macro_name_missing,
- extra_tokens_directive_end,
- expected_value_in_expr,
- closing_paren,
- to_match_paren,
- to_match_brace,
- to_match_bracket,
- header_str_closing,
- header_str_match,
- string_literal_in_pp_expr,
- float_literal_in_pp_expr,
- defined_as_macro_name,
- macro_name_must_be_identifier,
- whitespace_after_macro_name,
- hash_hash_at_start,
- hash_hash_at_end,
- pasting_formed_invalid,
- missing_paren_param_list,
- unterminated_macro_param_list,
- invalid_token_param_list,
- expected_comma_param_list,
- hash_not_followed_param,
- expected_filename,
- empty_filename,
- expected_invalid,
- expected_eof,
- expected_token,
- expected_expr,
- expected_integer_constant_expr,
- missing_type_specifier,
- missing_type_specifier_c23,
- multiple_storage_class,
- static_assert_failure,
- static_assert_failure_message,
- expected_type,
- cannot_combine_spec,
- duplicate_decl_spec,
- restrict_non_pointer,
- expected_external_decl,
- expected_ident_or_l_paren,
- missing_declaration,
- func_not_in_root,
- illegal_initializer,
- extern_initializer,
- spec_from_typedef,
- param_before_var_args,
- void_only_param,
- void_param_qualified,
- void_must_be_first_param,
- invalid_storage_on_param,
- threadlocal_non_var,
- func_spec_non_func,
- illegal_storage_on_func,
- illegal_storage_on_global,
- expected_stmt,
- func_cannot_return_func,
- func_cannot_return_array,
- undeclared_identifier,
- not_callable,
- unsupported_str_cat,
- static_func_not_global,
- implicit_func_decl,
- unknown_builtin,
- implicit_builtin,
- implicit_builtin_header_note,
- expected_param_decl,
- invalid_old_style_params,
- expected_fn_body,
- invalid_void_param,
- unused_value,
- continue_not_in_loop,
- break_not_in_loop_or_switch,
- unreachable_code,
- duplicate_label,
- previous_label,
- undeclared_label,
- case_not_in_switch,
- duplicate_switch_case,
- multiple_default,
- previous_case,
- expected_arguments,
- expected_arguments_old,
- expected_at_least_arguments,
- invalid_static_star,
- static_non_param,
- array_qualifiers,
- star_non_param,
- variable_len_array_file_scope,
- useless_static,
- negative_array_size,
- array_incomplete_elem,
- array_func_elem,
- static_non_outermost_array,
- qualifier_non_outermost_array,
- unterminated_macro_arg_list,
- unknown_warning,
- overflow,
- int_literal_too_big,
- indirection_ptr,
- addr_of_rvalue,
- addr_of_bitfield,
- not_assignable,
- ident_or_l_brace,
- empty_enum,
- redefinition,
- previous_definition,
- expected_identifier,
- expected_str_literal,
- expected_str_literal_in,
- parameter_missing,
- empty_record,
- empty_record_size,
- wrong_tag,
- expected_parens_around_typename,
- alignof_expr,
- invalid_alignof,
- invalid_sizeof,
- macro_redefined,
- generic_qual_type,
- generic_array_type,
- generic_func_type,
- generic_duplicate,
- generic_duplicate_here,
- generic_duplicate_default,
- generic_no_match,
- escape_sequence_overflow,
- invalid_universal_character,
- incomplete_universal_character,
- multichar_literal_warning,
- invalid_multichar_literal,
- wide_multichar_literal,
- char_lit_too_wide,
- char_too_large,
- must_use_struct,
- must_use_union,
- must_use_enum,
- redefinition_different_sym,
- redefinition_incompatible,
- redefinition_of_parameter,
- invalid_bin_types,
- comparison_ptr_int,
- comparison_distinct_ptr,
- incompatible_pointers,
- invalid_argument_un,
- incompatible_assign,
- implicit_ptr_to_int,
- invalid_cast_to_float,
- invalid_cast_to_pointer,
- invalid_cast_type,
- qual_cast,
- invalid_index,
- invalid_subscript,
- array_after,
- array_before,
- statement_int,
- statement_scalar,
- func_should_return,
- incompatible_return,
- incompatible_return_sign,
- implicit_int_to_ptr,
- func_does_not_return,
- void_func_returns_value,
- incompatible_arg,
- incompatible_ptr_arg,
- incompatible_ptr_arg_sign,
- parameter_here,
- atomic_array,
- atomic_func,
- atomic_incomplete,
- addr_of_register,
- variable_incomplete_ty,
- parameter_incomplete_ty,
- tentative_array,
- deref_incomplete_ty_ptr,
- alignas_on_func,
- alignas_on_param,
- minimum_alignment,
- maximum_alignment,
- negative_alignment,
- align_ignored,
- zero_align_ignored,
- non_pow2_align,
- pointer_mismatch,
- static_assert_not_constant,
- static_assert_missing_message,
- pre_c23_compat,
- unbound_vla,
- array_too_large,
- incompatible_ptr_init,
- incompatible_ptr_init_sign,
- incompatible_ptr_assign,
- incompatible_ptr_assign_sign,
- vla_init,
- func_init,
- incompatible_init,
- empty_scalar_init,
- excess_scalar_init,
- excess_str_init,
- excess_struct_init,
- excess_array_init,
- str_init_too_long,
- arr_init_too_long,
- invalid_typeof,
- division_by_zero,
- division_by_zero_macro,
- builtin_choose_cond,
- alignas_unavailable,
- case_val_unavailable,
- enum_val_unavailable,
- incompatible_array_init,
- array_init_str,
- initializer_overrides,
- previous_initializer,
- invalid_array_designator,
- negative_array_designator,
- oob_array_designator,
- invalid_field_designator,
- no_such_field_designator,
- empty_aggregate_init_braces,
- ptr_init_discards_quals,
- ptr_assign_discards_quals,
- ptr_ret_discards_quals,
- ptr_arg_discards_quals,
- unknown_attribute,
- ignored_attribute,
- invalid_fallthrough,
- cannot_apply_attribute_to_statement,
- builtin_macro_redefined,
- feature_check_requires_identifier,
- missing_tok_builtin,
- gnu_label_as_value,
- expected_record_ty,
- member_expr_not_ptr,
- member_expr_ptr,
- no_such_member,
- malformed_warning_check,
- invalid_computed_goto,
- pragma_warning_message,
- pragma_error_message,
- pragma_message,
- pragma_requires_string_literal,
- poisoned_identifier,
- pragma_poison_identifier,
- pragma_poison_macro,
- newline_eof,
- empty_translation_unit,
- omitting_parameter_name,
- non_int_bitfield,
- negative_bitwidth,
- zero_width_named_field,
- bitfield_too_big,
- invalid_utf8,
- implicitly_unsigned_literal,
- invalid_preproc_operator,
- invalid_preproc_expr_start,
- c99_compat,
- unexpected_character,
- invalid_identifier_start_char,
- unicode_zero_width,
- unicode_homoglyph,
- meaningless_asm_qual,
- duplicate_asm_qual,
- invalid_asm_str,
- dollar_in_identifier_extension,
- dollars_in_identifiers,
- expanded_from_here,
- skipping_macro_backtrace,
- pragma_operator_string_literal,
- unknown_gcc_pragma,
- unknown_gcc_pragma_directive,
- predefined_top_level,
- incompatible_va_arg,
- too_many_scalar_init_braces,
- uninitialized_in_own_init,
- gnu_statement_expression,
- stmt_expr_not_allowed_file_scope,
- gnu_imaginary_constant,
- plain_complex,
- complex_int,
- qual_on_ret_type,
- cli_invalid_standard,
- cli_invalid_target,
- cli_invalid_emulate,
- cli_unknown_arg,
- cli_error,
- cli_unused_link_object,
- cli_unknown_linker,
- extra_semi,
- func_field,
- vla_field,
- field_incomplete_ty,
- flexible_in_union,
- flexible_non_final,
- flexible_in_empty,
- duplicate_member,
- binary_integer_literal,
- gnu_va_macro,
- builtin_must_be_called,
- va_start_not_in_func,
- va_start_fixed_args,
- va_start_not_last_param,
- attribute_not_enough_args,
- attribute_too_many_args,
- attribute_arg_invalid,
- unknown_attr_enum,
- attribute_requires_identifier,
- declspec_not_enabled,
- declspec_attr_not_supported,
- deprecated_declarations,
- deprecated_note,
- unavailable,
- unavailable_note,
- warning_attribute,
- error_attribute,
- ignored_record_attr,
- backslash_newline_escape,
- array_size_non_int,
- cast_to_smaller_int,
- gnu_switch_range,
- empty_case_range,
- non_standard_escape_char,
- invalid_pp_stringify_escape,
- vla,
- float_overflow_conversion,
- float_out_of_range,
- float_zero_conversion,
- float_value_changed,
- float_to_int,
- const_decl_folded,
- const_decl_folded_vla,
- redefinition_of_typedef,
- undefined_macro,
- fn_macro_undefined,
- preprocessing_directive_only,
- missing_lparen_after_builtin,
- offsetof_ty,
- offsetof_incomplete,
- offsetof_array,
- pragma_pack_lparen,
- pragma_pack_rparen,
- pragma_pack_unknown_action,
- pragma_pack_show,
- pragma_pack_int,
- pragma_pack_int_ident,
- pragma_pack_undefined_pop,
- pragma_pack_empty_stack,
- cond_expr_type,
- too_many_includes,
- enumerator_too_small,
- enumerator_too_large,
- include_next,
- include_next_outside_header,
- enumerator_overflow,
- enum_not_representable,
- enum_too_large,
- enum_fixed,
- enum_prev_nonfixed,
- enum_prev_fixed,
- enum_different_explicit_ty,
- enum_not_representable_fixed,
- transparent_union_wrong_type,
- transparent_union_one_field,
- transparent_union_size,
- transparent_union_size_note,
- designated_init_invalid,
- designated_init_needed,
- ignore_common,
- ignore_nocommon,
- non_string_ignored,
- local_variable_attribute,
- ignore_cold,
- ignore_hot,
- ignore_noinline,
- ignore_always_inline,
- invalid_noreturn,
- nodiscard_unused,
- warn_unused_result,
- invalid_vec_elem_ty,
- vec_size_not_multiple,
- invalid_imag,
- invalid_real,
- zero_length_array,
- old_style_flexible_struct,
- comma_deletion_va_args,
- main_return_type,
- expansion_to_defined,
- invalid_int_suffix,
- invalid_float_suffix,
- invalid_octal_digit,
- invalid_binary_digit,
- exponent_has_no_digits,
- hex_floating_constant_requires_exponent,
- sizeof_returns_zero,
- declspec_not_allowed_after_declarator,
- declarator_name_tok,
- type_not_supported_on_target,
- bit_int,
- unsigned_bit_int_too_small,
- signed_bit_int_too_small,
- bit_int_too_big,
- keyword_macro,
- ptr_arithmetic_incomplete,
- callconv_not_supported,
- pointer_arith_void,
- sizeof_array_arg,
- array_address_to_bool,
- string_literal_to_bool,
- constant_expression_conversion_not_allowed,
- invalid_object_cast,
- cli_invalid_fp_eval_method,
- suggest_pointer_for_invalid_fp16,
- bitint_suffix,
- auto_type_extension,
- auto_type_not_allowed,
- auto_type_requires_initializer,
- auto_type_requires_single_declarator,
- auto_type_requires_plain_declarator,
- invalid_cast_to_auto_type,
- auto_type_from_bitfield,
- array_of_auto_type,
- auto_type_with_init_list,
- missing_semicolon,
- tentative_definition_incomplete,
- forward_declaration_here,
- gnu_union_cast,
- invalid_union_cast,
- cast_to_incomplete_type,
- invalid_source_epoch,
- fuse_ld_path,
- invalid_rtlib,
- unsupported_rtlib_gcc,
- invalid_unwindlib,
- incompatible_unwindlib,
- gnu_asm_disabled,
- extension_token_used,
- complex_component_init,
- complex_prefix_postfix_op,
- not_floating_type,
- argument_types_differ,
- ms_search_rule,
- ctrl_z_eof,
- illegal_char_encoding_warning,
- illegal_char_encoding_error,
- ucn_basic_char_error,
- ucn_basic_char_warning,
- ucn_control_char_error,
- ucn_control_char_warning,
- c89_ucn_in_literal,
- four_char_char_literal,
- multi_char_char_literal,
- missing_hex_escape,
- unknown_escape_sequence,
- attribute_requires_string,
- unterminated_string_literal_warning,
- unterminated_string_literal_error,
- empty_char_literal_warning,
- empty_char_literal_error,
- unterminated_char_literal_warning,
- unterminated_char_literal_error,
- unterminated_comment,
- def_no_proto_deprecated,
- passing_args_to_kr,
- unknown_type_name,
- label_compound_end,
- u8_char_lit,
- malformed_embed_param,
- malformed_embed_limit,
- duplicate_embed_param,
- unsupported_embed_param,
- invalid_compound_literal_storage_class,
- va_opt_lparen,
- va_opt_rparen,
- attribute_int_out_of_range,
- identifier_not_normalized,
- c23_auto_plain_declarator,
- c23_auto_single_declarator,
- c32_auto_requires_initializer,
- c23_auto_scalar_init,
-
- pub fn property(_: Tag) Properties {
- return undefined;
- }
- };
- };
-}
diff --git a/src/stubs/aro_names.zig b/src/stubs/aro_names.zig
deleted file mode 100644
index 3dee3c12e4..0000000000
--- a/src/stubs/aro_names.zig
+++ /dev/null
@@ -1,10 +0,0 @@
-//! Stub implementation only used when bootstrapping stage2
-//! Keep in sync with deps/aro/build/GenerateDef.zig
-
-pub fn with(comptime _: type) type {
- return struct {
- pub inline fn fromName(_: []const u8) ?@This() {
- return null;
- }
- };
-}
diff --git a/src/stubs/aro_options.zig b/src/stubs/aro_options.zig
deleted file mode 100644
index b8f9201ce9..0000000000
--- a/src/stubs/aro_options.zig
+++ /dev/null
@@ -1 +0,0 @@
-pub const version_str: []const u8 = "bootstrap-stub";
diff --git a/src/translate_c.zig b/src/translate_c.zig
index fc2d56ba05..0afd7b7695 100644
--- a/src/translate_c.zig
+++ b/src/translate_c.zig
@@ -8,10 +8,10 @@ const CallingConvention = std.builtin.CallingConvention;
const clang = @import("clang.zig");
const aro = @import("aro");
const CToken = aro.Tokenizer.Token;
-const ast = @import("translate_c/ast.zig");
const Node = ast.Node;
const Tag = Node.Tag;
-const common = @import("translate_c/common.zig");
+const common = @import("aro_translate_c");
+const ast = common.ast;
const Error = common.Error;
const MacroProcessingError = common.MacroProcessingError;
const TypeError = common.TypeError;
@@ -20,10 +20,8 @@ const SymbolTable = common.SymbolTable;
const AliasList = common.AliasList;
const ResultUsed = common.ResultUsed;
const Scope = common.ScopeExtra(Context, clang.QualType);
-
-// Maps macro parameter names to token position, for determining if different
-// identifiers refer to the same positional argument in different macros.
-const ArgsPositionMap = std.StringArrayHashMapUnmanaged(usize);
+const PatternList = common.PatternList;
+const MacroSlicer = common.MacroSlicer;
pub const Context = struct {
gpa: mem.Allocator,
@@ -5093,265 +5091,6 @@ pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, compti
try c.global_scope.nodes.append(try Tag.warning.create(c.arena, location_comment));
}
-pub const PatternList = struct {
- patterns: []Pattern,
-
- /// Templates must be function-like macros
- /// first element is macro source, second element is the name of the function
- /// in std.lib.zig.c_translation.Macros which implements it
- const templates = [_][2][]const u8{
- [2][]const u8{ "f_SUFFIX(X) (X ## f)", "F_SUFFIX" },
- [2][]const u8{ "F_SUFFIX(X) (X ## F)", "F_SUFFIX" },
-
- [2][]const u8{ "u_SUFFIX(X) (X ## u)", "U_SUFFIX" },
- [2][]const u8{ "U_SUFFIX(X) (X ## U)", "U_SUFFIX" },
-
- [2][]const u8{ "l_SUFFIX(X) (X ## l)", "L_SUFFIX" },
- [2][]const u8{ "L_SUFFIX(X) (X ## L)", "L_SUFFIX" },
-
- [2][]const u8{ "ul_SUFFIX(X) (X ## ul)", "UL_SUFFIX" },
- [2][]const u8{ "uL_SUFFIX(X) (X ## uL)", "UL_SUFFIX" },
- [2][]const u8{ "Ul_SUFFIX(X) (X ## Ul)", "UL_SUFFIX" },
- [2][]const u8{ "UL_SUFFIX(X) (X ## UL)", "UL_SUFFIX" },
-
- [2][]const u8{ "ll_SUFFIX(X) (X ## ll)", "LL_SUFFIX" },
- [2][]const u8{ "LL_SUFFIX(X) (X ## LL)", "LL_SUFFIX" },
-
- [2][]const u8{ "ull_SUFFIX(X) (X ## ull)", "ULL_SUFFIX" },
- [2][]const u8{ "uLL_SUFFIX(X) (X ## uLL)", "ULL_SUFFIX" },
- [2][]const u8{ "Ull_SUFFIX(X) (X ## Ull)", "ULL_SUFFIX" },
- [2][]const u8{ "ULL_SUFFIX(X) (X ## ULL)", "ULL_SUFFIX" },
-
- [2][]const u8{ "f_SUFFIX(X) X ## f", "F_SUFFIX" },
- [2][]const u8{ "F_SUFFIX(X) X ## F", "F_SUFFIX" },
-
- [2][]const u8{ "u_SUFFIX(X) X ## u", "U_SUFFIX" },
- [2][]const u8{ "U_SUFFIX(X) X ## U", "U_SUFFIX" },
-
- [2][]const u8{ "l_SUFFIX(X) X ## l", "L_SUFFIX" },
- [2][]const u8{ "L_SUFFIX(X) X ## L", "L_SUFFIX" },
-
- [2][]const u8{ "ul_SUFFIX(X) X ## ul", "UL_SUFFIX" },
- [2][]const u8{ "uL_SUFFIX(X) X ## uL", "UL_SUFFIX" },
- [2][]const u8{ "Ul_SUFFIX(X) X ## Ul", "UL_SUFFIX" },
- [2][]const u8{ "UL_SUFFIX(X) X ## UL", "UL_SUFFIX" },
-
- [2][]const u8{ "ll_SUFFIX(X) X ## ll", "LL_SUFFIX" },
- [2][]const u8{ "LL_SUFFIX(X) X ## LL", "LL_SUFFIX" },
-
- [2][]const u8{ "ull_SUFFIX(X) X ## ull", "ULL_SUFFIX" },
- [2][]const u8{ "uLL_SUFFIX(X) X ## uLL", "ULL_SUFFIX" },
- [2][]const u8{ "Ull_SUFFIX(X) X ## Ull", "ULL_SUFFIX" },
- [2][]const u8{ "ULL_SUFFIX(X) X ## ULL", "ULL_SUFFIX" },
-
- [2][]const u8{ "CAST_OR_CALL(X, Y) (X)(Y)", "CAST_OR_CALL" },
- [2][]const u8{ "CAST_OR_CALL(X, Y) ((X)(Y))", "CAST_OR_CALL" },
-
- [2][]const u8{
- \\wl_container_of(ptr, sample, member) \
- \\(__typeof__(sample))((char *)(ptr) - \
- \\ offsetof(__typeof__(*sample), member))
- ,
- "WL_CONTAINER_OF",
- },
-
- [2][]const u8{ "IGNORE_ME(X) ((void)(X))", "DISCARD" },
- [2][]const u8{ "IGNORE_ME(X) (void)(X)", "DISCARD" },
- [2][]const u8{ "IGNORE_ME(X) ((const void)(X))", "DISCARD" },
- [2][]const u8{ "IGNORE_ME(X) (const void)(X)", "DISCARD" },
- [2][]const u8{ "IGNORE_ME(X) ((volatile void)(X))", "DISCARD" },
- [2][]const u8{ "IGNORE_ME(X) (volatile void)(X)", "DISCARD" },
- [2][]const u8{ "IGNORE_ME(X) ((const volatile void)(X))", "DISCARD" },
- [2][]const u8{ "IGNORE_ME(X) (const volatile void)(X)", "DISCARD" },
- [2][]const u8{ "IGNORE_ME(X) ((volatile const void)(X))", "DISCARD" },
- [2][]const u8{ "IGNORE_ME(X) (volatile const void)(X)", "DISCARD" },
- };
-
- /// Assumes that `ms` represents a tokenized function-like macro.
- fn buildArgsHash(allocator: mem.Allocator, ms: MacroSlicer, hash: *ArgsPositionMap) MacroProcessingError!void {
- assert(ms.tokens.len > 2);
- assert(ms.tokens[0].id == .identifier or ms.tokens[0].id == .extended_identifier);
- assert(ms.tokens[1].id == .l_paren);
-
- var i: usize = 2;
- while (true) : (i += 1) {
- const token = ms.tokens[i];
- switch (token.id) {
- .r_paren => break,
- .comma => continue,
- .identifier, .extended_identifier => {
- const identifier = ms.slice(token);
- try hash.put(allocator, identifier, i);
- },
- else => return error.UnexpectedMacroToken,
- }
- }
- }
-
- const Pattern = struct {
- tokens: []const CToken,
- source: []const u8,
- impl: []const u8,
- args_hash: ArgsPositionMap,
-
- fn init(self: *Pattern, allocator: mem.Allocator, template: [2][]const u8) Error!void {
- const source = template[0];
- const impl = template[1];
-
- var tok_list = std.ArrayList(CToken).init(allocator);
- defer tok_list.deinit();
- try tokenizeMacro(source, &tok_list);
- const tokens = try allocator.dupe(CToken, tok_list.items);
-
- self.* = .{
- .tokens = tokens,
- .source = source,
- .impl = impl,
- .args_hash = .{},
- };
- const ms = MacroSlicer{ .source = source, .tokens = tokens };
- buildArgsHash(allocator, ms, &self.args_hash) catch |err| switch (err) {
- error.UnexpectedMacroToken => unreachable,
- else => |e| return e,
- };
- }
-
- fn deinit(self: *Pattern, allocator: mem.Allocator) void {
- self.args_hash.deinit(allocator);
- allocator.free(self.tokens);
- }
-
- /// This function assumes that `ms` has already been validated to contain a function-like
- /// macro, and that the parsed template macro in `self` also contains a function-like
- /// macro. Please review this logic carefully if changing that assumption. Two
- /// function-like macros are considered equivalent if and only if they contain the same
- /// list of tokens, modulo parameter names.
- pub fn isEquivalent(self: Pattern, ms: MacroSlicer, args_hash: ArgsPositionMap) bool {
- if (self.tokens.len != ms.tokens.len) return false;
- if (args_hash.count() != self.args_hash.count()) return false;
-
- var i: usize = 2;
- while (self.tokens[i].id != .r_paren) : (i += 1) {}
-
- const pattern_slicer = MacroSlicer{ .source = self.source, .tokens = self.tokens };
- while (i < self.tokens.len) : (i += 1) {
- const pattern_token = self.tokens[i];
- const macro_token = ms.tokens[i];
- if (pattern_token.id != macro_token.id) return false;
-
- const pattern_bytes = pattern_slicer.slice(pattern_token);
- const macro_bytes = ms.slice(macro_token);
- switch (pattern_token.id) {
- .identifier, .extended_identifier => {
- const pattern_arg_index = self.args_hash.get(pattern_bytes);
- const macro_arg_index = args_hash.get(macro_bytes);
-
- if (pattern_arg_index == null and macro_arg_index == null) {
- if (!mem.eql(u8, pattern_bytes, macro_bytes)) return false;
- } else if (pattern_arg_index != null and macro_arg_index != null) {
- if (pattern_arg_index.? != macro_arg_index.?) return false;
- } else {
- return false;
- }
- },
- .string_literal, .char_literal, .pp_num => {
- if (!mem.eql(u8, pattern_bytes, macro_bytes)) return false;
- },
- else => {
- // other tags correspond to keywords and operators that do not contain a "payload"
- // that can vary
- },
- }
- }
- return true;
- }
- };
-
- pub fn init(allocator: mem.Allocator) Error!PatternList {
- const patterns = try allocator.alloc(Pattern, templates.len);
- for (templates, 0..) |template, i| {
- try patterns[i].init(allocator, template);
- }
- return PatternList{ .patterns = patterns };
- }
-
- pub fn deinit(self: *PatternList, allocator: mem.Allocator) void {
- for (self.patterns) |*pattern| pattern.deinit(allocator);
- allocator.free(self.patterns);
- }
-
- pub fn match(self: PatternList, allocator: mem.Allocator, ms: MacroSlicer) Error!?Pattern {
- var args_hash: ArgsPositionMap = .{};
- defer args_hash.deinit(allocator);
-
- buildArgsHash(allocator, ms, &args_hash) catch |err| switch (err) {
- error.UnexpectedMacroToken => return null,
- else => |e| return e,
- };
-
- for (self.patterns) |pattern| if (pattern.isEquivalent(ms, args_hash)) return pattern;
- return null;
- }
-};
-
-const MacroSlicer = struct {
- source: []const u8,
- tokens: []const CToken,
- fn slice(self: MacroSlicer, token: CToken) []const u8 {
- return self.source[token.start..token.end];
- }
-};
-
-// Testing here instead of test/translate_c.zig allows us to also test that the
-// mapped function exists in `std.zig.c_translation.Macros`
-test "Macro matching" {
- const helper = struct {
- const MacroFunctions = std.zig.c_translation.Macros;
- fn checkMacro(allocator: mem.Allocator, pattern_list: PatternList, source: []const u8, comptime expected_match: ?[]const u8) !void {
- var tok_list = std.ArrayList(CToken).init(allocator);
- defer tok_list.deinit();
- try tokenizeMacro(source, &tok_list);
- const macro_slicer = MacroSlicer{ .source = source, .tokens = tok_list.items };
- const matched = try pattern_list.match(allocator, macro_slicer);
- if (expected_match) |expected| {
- try testing.expectEqualStrings(expected, matched.?.impl);
- try testing.expect(@hasDecl(MacroFunctions, expected));
- } else {
- try testing.expectEqual(@as(@TypeOf(matched), null), matched);
- }
- }
- };
- const allocator = std.testing.allocator;
- var pattern_list = try PatternList.init(allocator);
- defer pattern_list.deinit(allocator);
-
- try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## F)", "F_SUFFIX");
- try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## U)", "U_SUFFIX");
- try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## L)", "L_SUFFIX");
- try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## LL)", "LL_SUFFIX");
- try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## UL)", "UL_SUFFIX");
- try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## ULL)", "ULL_SUFFIX");
- try helper.checkMacro(allocator, pattern_list,
- \\container_of(a, b, c) \
- \\(__typeof__(b))((char *)(a) - \
- \\ offsetof(__typeof__(*b), c))
- , "WL_CONTAINER_OF");
-
- try helper.checkMacro(allocator, pattern_list, "NO_MATCH(X, Y) (X + Y)", null);
- try helper.checkMacro(allocator, pattern_list, "CAST_OR_CALL(X, Y) (X)(Y)", "CAST_OR_CALL");
- try helper.checkMacro(allocator, pattern_list, "CAST_OR_CALL(X, Y) ((X)(Y))", "CAST_OR_CALL");
- try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (void)(X)", "DISCARD");
- try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((void)(X))", "DISCARD");
- try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (const void)(X)", "DISCARD");
- try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((const void)(X))", "DISCARD");
- try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (volatile void)(X)", "DISCARD");
- try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((volatile void)(X))", "DISCARD");
- try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (const volatile void)(X)", "DISCARD");
- try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((const volatile void)(X))", "DISCARD");
- try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) (volatile const void)(X)", "DISCARD");
- try helper.checkMacro(allocator, pattern_list, "IGNORE_ME(X) ((volatile const void)(X))", "DISCARD");
-}
-
const MacroCtx = struct {
source: []const u8,
list: []const CToken,
@@ -5392,7 +5131,7 @@ const MacroCtx = struct {
}
fn makeSlicer(self: *const MacroCtx) MacroSlicer {
- return MacroSlicer{ .source = self.source, .tokens = self.list };
+ return .{ .source = self.source, .tokens = self.list };
}
const MacroTranslateError = union(enum) {
@@ -5432,26 +5171,6 @@ const MacroCtx = struct {
}
};
-fn tokenizeMacro(source: []const u8, tok_list: *std.ArrayList(CToken)) Error!void {
- var tokenizer: aro.Tokenizer = .{
- .buf = source,
- .source = .unused,
- .langopts = .{},
- };
- while (true) {
- const tok = tokenizer.next();
- switch (tok.id) {
- .whitespace => continue,
- .nl, .eof => {
- try tok_list.append(tok);
- break;
- },
- else => {},
- }
- try tok_list.append(tok);
- }
-}
-
fn getMacroText(unit: *const clang.ASTUnit, c: *const Context, macro: *const clang.MacroDefinitionRecord) ![]const u8 {
const begin_loc = macro.getSourceRange_getBegin();
const end_loc = clang.Lexer.getLocForEndOfToken(macro.getSourceRange_getEnd(), c.source_manager, unit);
@@ -5491,7 +5210,7 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
const source = try getMacroText(unit, c, macro);
- try tokenizeMacro(source, &tok_list);
+ try common.tokenizeMacro(source, &tok_list);
var macro_ctx = MacroCtx{
.source = source,
diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig
deleted file mode 100644
index 9d274d7733..0000000000
--- a/src/translate_c/ast.zig
+++ /dev/null
@@ -1,2942 +0,0 @@
-const std = @import("std");
-const Type = @import("../type.zig").Type;
-const Allocator = std.mem.Allocator;
-
-pub const Node = extern union {
- /// If the tag value is less than Tag.no_payload_count, then no pointer
- /// dereference is needed.
- tag_if_small_enough: usize,
- ptr_otherwise: *Payload,
-
- pub const Tag = enum {
- /// Declarations add themselves to the correct scopes and should not be emitted as this tag.
- declaration,
- null_literal,
- undefined_literal,
- /// opaque {}
- opaque_literal,
- true_literal,
- false_literal,
- empty_block,
- return_void,
- zero_literal,
- one_literal,
- void_type,
- noreturn_type,
- @"anytype",
- @"continue",
- @"break",
- // After this, the tag requires a payload.
-
- integer_literal,
- float_literal,
- string_literal,
- char_literal,
- enum_literal,
- /// "string"[0..end]
- string_slice,
- identifier,
- fn_identifier,
- @"if",
- /// if (!operand) break;
- if_not_break,
- @"while",
- /// while (true) operand
- while_true,
- @"switch",
- /// else => operand,
- switch_else,
- /// items => body,
- switch_prong,
- break_val,
- @"return",
- field_access,
- array_access,
- call,
- var_decl,
- /// const name = struct { init }
- static_local_var,
- /// var name = init.*
- mut_str,
- func,
- warning,
- @"struct",
- @"union",
- @"comptime",
- @"defer",
- array_init,
- tuple,
- container_init,
- container_init_dot,
- helpers_cast,
- /// _ = operand;
- discard,
-
- // a + b
- add,
- // a = b
- add_assign,
- // c = (a = b)
- add_wrap,
- add_wrap_assign,
- sub,
- sub_assign,
- sub_wrap,
- sub_wrap_assign,
- mul,
- mul_assign,
- mul_wrap,
- mul_wrap_assign,
- div,
- div_assign,
- shl,
- shl_assign,
- shr,
- shr_assign,
- mod,
- mod_assign,
- @"and",
- @"or",
- less_than,
- less_than_equal,
- greater_than,
- greater_than_equal,
- equal,
- not_equal,
- bit_and,
- bit_and_assign,
- bit_or,
- bit_or_assign,
- bit_xor,
- bit_xor_assign,
- array_cat,
- ellipsis3,
- assign,
-
- /// @import("std").zig.c_builtins.<name>
- import_c_builtin,
- /// @intCast(operand)
- int_cast,
- /// @constCast(operand)
- const_cast,
- /// @volatileCast(operand)
- volatile_cast,
- /// @import("std").zig.c_translation.promoteIntLiteral(value, type, base)
- helpers_promoteIntLiteral,
- /// @import("std").zig.c_translation.signedRemainder(lhs, rhs)
- signed_remainder,
- /// @divTrunc(lhs, rhs)
- div_trunc,
- /// @intFromBool(operand)
- int_from_bool,
- /// @as(lhs, rhs)
- as,
- /// @truncate(operand)
- truncate,
- /// @bitCast(operand)
- bit_cast,
- /// @floatCast(operand)
- float_cast,
- /// @intFromFloat(operand)
- int_from_float,
- /// @floatFromInt(operand)
- float_from_int,
- /// @ptrFromInt(operand)
- ptr_from_int,
- /// @intFromPtr(operand)
- int_from_ptr,
- /// @alignCast(operand)
- align_cast,
- /// @ptrCast(operand)
- ptr_cast,
- /// @divExact(lhs, rhs)
- div_exact,
- /// @offsetOf(lhs, rhs)
- offset_of,
- /// @splat(operand)
- vector_zero_init,
- /// @shuffle(type, a, b, mask)
- shuffle,
- /// @extern(ty, .{ .name = n })
- builtin_extern,
-
- /// @import("std").zig.c_translation.MacroArithmetic.<op>(lhs, rhs)
- macro_arithmetic,
-
- asm_simple,
-
- negate,
- negate_wrap,
- bit_not,
- not,
- address_of,
- /// .?
- unwrap,
- /// .*
- deref,
-
- block,
- /// { operand }
- block_single,
-
- sizeof,
- alignof,
- typeof,
- typeinfo,
- type,
-
- optional_type,
- c_pointer,
- single_pointer,
- array_type,
- null_sentinel_array_type,
-
- /// @import("std").zig.c_translation.sizeof(operand)
- helpers_sizeof,
- /// @import("std").zig.c_translation.FlexibleArrayType(lhs, rhs)
- helpers_flexible_array_type,
- /// @import("std").zig.c_translation.shuffleVectorIndex(lhs, rhs)
- helpers_shuffle_vector_index,
- /// @import("std").zig.c_translation.Macro.<operand>
- helpers_macro,
- /// @Vector(lhs, rhs)
- vector,
- /// @import("std").mem.zeroes(operand)
- std_mem_zeroes,
- /// @import("std").mem.zeroInit(lhs, rhs)
- std_mem_zeroinit,
- // pub const name = @compileError(msg);
- fail_decl,
- // var actual = mangled;
- arg_redecl,
- /// pub const alias = actual;
- alias,
- /// const name = init;
- var_simple,
- /// pub const name = init;
- pub_var_simple,
- /// pub? const name (: type)? = value
- enum_constant,
-
- /// pub inline fn name(params) return_type body
- pub_inline_fn,
-
- /// [0]type{}
- empty_array,
- /// [1]type{val} ** count
- array_filler,
-
- pub const last_no_payload_tag = Tag.@"break";
- pub const no_payload_count = @intFromEnum(last_no_payload_tag) + 1;
-
- pub fn Type(comptime t: Tag) type {
- return switch (t) {
- .declaration,
- .null_literal,
- .undefined_literal,
- .opaque_literal,
- .true_literal,
- .false_literal,
- .empty_block,
- .return_void,
- .zero_literal,
- .one_literal,
- .void_type,
- .noreturn_type,
- .@"anytype",
- .@"continue",
- .@"break",
- => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
-
- .std_mem_zeroes,
- .@"return",
- .@"comptime",
- .@"defer",
- .asm_simple,
- .negate,
- .negate_wrap,
- .bit_not,
- .not,
- .optional_type,
- .address_of,
- .unwrap,
- .deref,
- .int_from_ptr,
- .empty_array,
- .while_true,
- .if_not_break,
- .switch_else,
- .block_single,
- .helpers_sizeof,
- .int_from_bool,
- .sizeof,
- .alignof,
- .typeof,
- .typeinfo,
- .align_cast,
- .truncate,
- .bit_cast,
- .float_cast,
- .int_from_float,
- .float_from_int,
- .ptr_from_int,
- .ptr_cast,
- .int_cast,
- .const_cast,
- .volatile_cast,
- .vector_zero_init,
- => Payload.UnOp,
-
- .add,
- .add_assign,
- .add_wrap,
- .add_wrap_assign,
- .sub,
- .sub_assign,
- .sub_wrap,
- .sub_wrap_assign,
- .mul,
- .mul_assign,
- .mul_wrap,
- .mul_wrap_assign,
- .div,
- .div_assign,
- .shl,
- .shl_assign,
- .shr,
- .shr_assign,
- .mod,
- .mod_assign,
- .@"and",
- .@"or",
- .less_than,
- .less_than_equal,
- .greater_than,
- .greater_than_equal,
- .equal,
- .not_equal,
- .bit_and,
- .bit_and_assign,
- .bit_or,
- .bit_or_assign,
- .bit_xor,
- .bit_xor_assign,
- .div_trunc,
- .signed_remainder,
- .as,
- .array_cat,
- .ellipsis3,
- .assign,
- .array_access,
- .std_mem_zeroinit,
- .helpers_flexible_array_type,
- .helpers_shuffle_vector_index,
- .vector,
- .div_exact,
- .offset_of,
- .helpers_cast,
- => Payload.BinOp,
-
- .integer_literal,
- .float_literal,
- .string_literal,
- .char_literal,
- .enum_literal,
- .identifier,
- .fn_identifier,
- .warning,
- .type,
- .helpers_macro,
- .import_c_builtin,
- => Payload.Value,
- .discard => Payload.Discard,
- .@"if" => Payload.If,
- .@"while" => Payload.While,
- .@"switch", .array_init, .switch_prong => Payload.Switch,
- .break_val => Payload.BreakVal,
- .call => Payload.Call,
- .var_decl => Payload.VarDecl,
- .func => Payload.Func,
- .@"struct", .@"union" => Payload.Record,
- .tuple => Payload.TupleInit,
- .container_init => Payload.ContainerInit,
- .container_init_dot => Payload.ContainerInitDot,
- .helpers_promoteIntLiteral => Payload.PromoteIntLiteral,
- .block => Payload.Block,
- .c_pointer, .single_pointer => Payload.Pointer,
- .array_type, .null_sentinel_array_type => Payload.Array,
- .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,
- .var_simple, .pub_var_simple, .static_local_var, .mut_str => Payload.SimpleVarDecl,
- .enum_constant => Payload.EnumConstant,
- .array_filler => Payload.ArrayFiller,
- .pub_inline_fn => Payload.PubInlineFn,
- .field_access => Payload.FieldAccess,
- .string_slice => Payload.StringSlice,
- .shuffle => Payload.Shuffle,
- .builtin_extern => Payload.Extern,
- .macro_arithmetic => Payload.MacroArithmetic,
- };
- }
-
- pub fn init(comptime t: Tag) Node {
- comptime std.debug.assert(@intFromEnum(t) < Tag.no_payload_count);
- return .{ .tag_if_small_enough = @intFromEnum(t) };
- }
-
- pub fn create(comptime t: Tag, ally: Allocator, data: Data(t)) error{OutOfMemory}!Node {
- const ptr = try ally.create(t.Type());
- ptr.* = .{
- .base = .{ .tag = t },
- .data = data,
- };
- return Node{ .ptr_otherwise = &ptr.base };
- }
-
- pub fn Data(comptime t: Tag) type {
- return std.meta.fieldInfo(t.Type(), .data).type;
- }
- };
-
- pub fn tag(self: Node) Tag {
- if (self.tag_if_small_enough < Tag.no_payload_count) {
- return @as(Tag, @enumFromInt(@as(std.meta.Tag(Tag), @intCast(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 fn initPayload(payload: *Payload) Node {
- std.debug.assert(@intFromEnum(payload.tag) >= Tag.no_payload_count);
- return .{ .ptr_otherwise = payload };
- }
-
- pub fn isNoreturn(node: Node, break_counts: bool) bool {
- switch (node.tag()) {
- .block => {
- const block_node = node.castTag(.block).?;
- if (block_node.data.stmts.len == 0) return false;
-
- const last = block_node.data.stmts[block_node.data.stmts.len - 1];
- return last.isNoreturn(break_counts);
- },
- .@"switch" => {
- const switch_node = node.castTag(.@"switch").?;
-
- for (switch_node.data.cases) |case| {
- const body = if (case.castTag(.switch_else)) |some|
- some.data
- else if (case.castTag(.switch_prong)) |some|
- some.data.cond
- else
- unreachable;
-
- if (!body.isNoreturn(break_counts)) return false;
- }
- return true;
- },
- .@"return", .return_void => return true,
- .@"break" => if (break_counts) return true,
- else => {},
- }
- return false;
- }
-};
-
-pub const Payload = struct {
- tag: Node.Tag,
-
- pub const Value = struct {
- base: Payload,
- data: []const u8,
- };
-
- pub const UnOp = struct {
- base: Payload,
- data: Node,
- };
-
- pub const BinOp = struct {
- base: Payload,
- data: struct {
- lhs: Node,
- rhs: Node,
- },
- };
-
- pub const Discard = struct {
- base: Payload,
- data: struct {
- should_skip: bool,
- value: Node,
- },
- };
-
- pub const If = struct {
- base: Payload,
- data: struct {
- cond: Node,
- then: Node,
- @"else": ?Node,
- },
- };
-
- pub const While = struct {
- base: Payload,
- data: struct {
- cond: Node,
- body: Node,
- cont_expr: ?Node,
- },
- };
-
- pub const Switch = struct {
- base: Payload,
- data: struct {
- cond: Node,
- cases: []Node,
- },
- };
-
- pub const BreakVal = struct {
- base: Payload,
- data: struct {
- label: ?[]const u8,
- val: Node,
- },
- };
-
- pub const Call = struct {
- base: Payload,
- data: struct {
- lhs: Node,
- args: []Node,
- },
- };
-
- pub const VarDecl = struct {
- base: Payload,
- data: struct {
- is_pub: bool,
- is_const: bool,
- is_extern: bool,
- is_export: bool,
- is_threadlocal: bool,
- alignment: ?c_uint,
- linksection_string: ?[]const u8,
- name: []const u8,
- type: Node,
- init: ?Node,
- },
- };
-
- pub const Func = struct {
- base: Payload,
- data: struct {
- is_pub: bool,
- is_extern: bool,
- is_export: bool,
- is_inline: bool,
- is_var_args: bool,
- name: ?[]const u8,
- linksection_string: ?[]const u8,
- explicit_callconv: ?std.builtin.CallingConvention,
- params: []Param,
- return_type: Node,
- body: ?Node,
- alignment: ?c_uint,
- },
- };
-
- pub const Param = struct {
- is_noalias: bool,
- name: ?[]const u8,
- type: Node,
- };
-
- pub const Record = struct {
- base: Payload,
- data: struct {
- layout: enum { @"packed", @"extern", none },
- fields: []Field,
- functions: []Node,
- variables: []Node,
- },
-
- pub const Field = struct {
- name: []const u8,
- type: Node,
- alignment: ?c_uint,
- default_value: ?Node,
- };
- };
-
- pub const TupleInit = struct {
- base: Payload,
- data: []Node,
- };
-
- pub const ContainerInit = struct {
- base: Payload,
- data: struct {
- lhs: Node,
- inits: []Initializer,
- },
-
- pub const Initializer = struct {
- name: []const u8,
- value: Node,
- };
- };
-
- pub const ContainerInitDot = struct {
- base: Payload,
- data: []Initializer,
-
- pub const Initializer = struct {
- name: []const u8,
- value: Node,
- };
- };
-
- pub const Block = struct {
- base: Payload,
- data: struct {
- label: ?[]const u8,
- stmts: []Node,
- },
- };
-
- pub const Array = struct {
- base: Payload,
- data: ArrayTypeInfo,
-
- pub const ArrayTypeInfo = struct {
- elem_type: Node,
- len: usize,
- };
- };
-
- pub const Pointer = struct {
- base: Payload,
- data: struct {
- elem_type: Node,
- is_const: bool,
- is_volatile: bool,
- },
- };
-
- pub const ArgRedecl = struct {
- base: Payload,
- data: struct {
- actual: []const u8,
- mangled: []const u8,
- },
- };
-
- pub const SimpleVarDecl = struct {
- base: Payload,
- data: struct {
- name: []const u8,
- init: Node,
- },
- };
-
- pub const EnumConstant = struct {
- base: Payload,
- data: struct {
- name: []const u8,
- is_public: bool,
- type: ?Node,
- value: Node,
- },
- };
-
- pub const ArrayFiller = struct {
- base: Payload,
- data: struct {
- type: Node,
- filler: Node,
- count: usize,
- },
- };
-
- pub const PubInlineFn = struct {
- base: Payload,
- data: struct {
- name: []const u8,
- params: []Param,
- return_type: Node,
- body: Node,
- },
- };
-
- pub const FieldAccess = struct {
- base: Payload,
- data: struct {
- lhs: Node,
- field_name: []const u8,
- },
- };
-
- pub const PromoteIntLiteral = struct {
- base: Payload,
- data: struct {
- value: Node,
- type: Node,
- base: Node,
- },
- };
-
- pub const StringSlice = struct {
- base: Payload,
- data: struct {
- string: Node,
- end: usize,
- },
- };
-
- pub const Shuffle = struct {
- base: Payload,
- data: struct {
- element_type: Node,
- a: Node,
- b: Node,
- mask_vector: Node,
- },
- };
-
- pub const Extern = struct {
- base: Payload,
- data: struct {
- type: Node,
- name: Node,
- },
- };
-
- pub const MacroArithmetic = struct {
- base: Payload,
- data: struct {
- op: Operator,
- lhs: Node,
- rhs: Node,
- },
-
- pub const Operator = enum { div, rem };
- };
-};
-
-/// Converts the nodes into a Zig Ast.
-/// Caller must free the source slice.
-pub fn render(gpa: Allocator, nodes: []const Node) !std.zig.Ast {
- var ctx = Context{
- .gpa = gpa,
- .buf = std.ArrayList(u8).init(gpa),
- };
- defer ctx.buf.deinit();
- defer ctx.nodes.deinit(gpa);
- defer ctx.extra_data.deinit(gpa);
- defer ctx.tokens.deinit(gpa);
-
- // Estimate that each top level node has 10 child nodes.
- const estimated_node_count = nodes.len * 10;
- try ctx.nodes.ensureTotalCapacity(gpa, estimated_node_count);
- // Estimate that each each node has 2 tokens.
- const estimated_tokens_count = estimated_node_count * 2;
- try ctx.tokens.ensureTotalCapacity(gpa, estimated_tokens_count);
- // Estimate that each each token is 3 bytes long.
- const estimated_buf_len = estimated_tokens_count * 3;
- try ctx.buf.ensureTotalCapacity(estimated_buf_len);
-
- ctx.nodes.appendAssumeCapacity(.{
- .tag = .root,
- .main_token = 0,
- .data = .{
- .lhs = undefined,
- .rhs = undefined,
- },
- });
-
- const root_members = blk: {
- var result = std.ArrayList(NodeIndex).init(gpa);
- defer result.deinit();
-
- for (nodes) |node| {
- const res = try renderNode(&ctx, node);
- if (node.tag() == .warning) continue;
- try result.append(res);
- }
- break :blk try ctx.listToSpan(result.items);
- };
-
- ctx.nodes.items(.data)[0] = .{
- .lhs = root_members.start,
- .rhs = root_members.end,
- };
-
- try ctx.tokens.append(gpa, .{
- .tag = .eof,
- .start = @as(u32, @intCast(ctx.buf.items.len)),
- });
-
- return std.zig.Ast{
- .source = try ctx.buf.toOwnedSliceSentinel(0),
- .tokens = ctx.tokens.toOwnedSlice(),
- .nodes = ctx.nodes.toOwnedSlice(),
- .extra_data = try ctx.extra_data.toOwnedSlice(gpa),
- .errors = &.{},
- .mode = .zig,
- };
-}
-
-const NodeIndex = std.zig.Ast.Node.Index;
-const NodeSubRange = std.zig.Ast.Node.SubRange;
-const TokenIndex = std.zig.Ast.TokenIndex;
-const TokenTag = std.zig.Token.Tag;
-
-const Context = struct {
- gpa: Allocator,
- buf: std.ArrayList(u8),
- nodes: std.zig.Ast.NodeList = .{},
- extra_data: std.ArrayListUnmanaged(std.zig.Ast.Node.Index) = .{},
- tokens: std.zig.Ast.TokenList = .{},
-
- fn addTokenFmt(c: *Context, tag: TokenTag, comptime format: []const u8, args: anytype) Allocator.Error!TokenIndex {
- const start_index = c.buf.items.len;
- try c.buf.writer().print(format ++ " ", args);
-
- try c.tokens.append(c.gpa, .{
- .tag = tag,
- .start = @as(u32, @intCast(start_index)),
- });
-
- return @as(u32, @intCast(c.tokens.len - 1));
- }
-
- fn addToken(c: *Context, tag: TokenTag, bytes: []const u8) Allocator.Error!TokenIndex {
- return c.addTokenFmt(tag, "{s}", .{bytes});
- }
-
- fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex {
- if (std.zig.primitives.isPrimitive(bytes))
- return c.addTokenFmt(.identifier, "@\"{s}\"", .{bytes});
- return c.addTokenFmt(.identifier, "{s}", .{std.zig.fmtId(bytes)});
- }
-
- fn listToSpan(c: *Context, list: []const NodeIndex) Allocator.Error!NodeSubRange {
- try c.extra_data.appendSlice(c.gpa, list);
- return NodeSubRange{
- .start = @as(NodeIndex, @intCast(c.extra_data.items.len - list.len)),
- .end = @as(NodeIndex, @intCast(c.extra_data.items.len)),
- };
- }
-
- fn addNode(c: *Context, elem: std.zig.Ast.Node) Allocator.Error!NodeIndex {
- const result = @as(NodeIndex, @intCast(c.nodes.len));
- try c.nodes.append(c.gpa, elem);
- return result;
- }
-
- fn addExtra(c: *Context, extra: anytype) Allocator.Error!NodeIndex {
- const fields = std.meta.fields(@TypeOf(extra));
- try c.extra_data.ensureUnusedCapacity(c.gpa, fields.len);
- const result = @as(u32, @intCast(c.extra_data.items.len));
- inline for (fields) |field| {
- comptime std.debug.assert(field.type == NodeIndex);
- c.extra_data.appendAssumeCapacity(@field(extra, field.name));
- }
- return result;
- }
-};
-
-fn renderNodes(c: *Context, nodes: []const Node) Allocator.Error!NodeSubRange {
- var result = std.ArrayList(NodeIndex).init(c.gpa);
- defer result.deinit();
-
- for (nodes) |node| {
- const res = try renderNode(c, node);
- if (node.tag() == .warning) continue;
- try result.append(res);
- }
-
- return try c.listToSpan(result.items);
-}
-
-fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
- switch (node.tag()) {
- .declaration => unreachable,
- .warning => {
- const payload = node.castTag(.warning).?.data;
- try c.buf.appendSlice(payload);
- try c.buf.append('\n');
- return @as(NodeIndex, 0); // error: integer value 0 cannot be coerced to type 'std.mem.Allocator.Error!u32'
- },
- .helpers_cast => {
- const payload = node.castTag(.helpers_cast).?.data;
- const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "cast" });
- return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
- },
- .helpers_promoteIntLiteral => {
- const payload = node.castTag(.helpers_promoteIntLiteral).?.data;
- const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "promoteIntLiteral" });
- return renderCall(c, import_node, &.{ payload.type, payload.value, payload.base });
- },
- .helpers_sizeof => {
- const payload = node.castTag(.helpers_sizeof).?.data;
- const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "sizeof" });
- return renderCall(c, import_node, &.{payload});
- },
- .std_mem_zeroes => {
- const payload = node.castTag(.std_mem_zeroes).?.data;
- const import_node = try renderStdImport(c, &.{ "mem", "zeroes" });
- return renderCall(c, import_node, &.{payload});
- },
- .std_mem_zeroinit => {
- const payload = node.castTag(.std_mem_zeroinit).?.data;
- const import_node = try renderStdImport(c, &.{ "mem", "zeroInit" });
- return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
- },
- .helpers_flexible_array_type => {
- const payload = node.castTag(.helpers_flexible_array_type).?.data;
- const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "FlexibleArrayType" });
- return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
- },
- .helpers_shuffle_vector_index => {
- const payload = node.castTag(.helpers_shuffle_vector_index).?.data;
- const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "shuffleVectorIndex" });
- return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
- },
- .vector => {
- const payload = node.castTag(.vector).?.data;
- return renderBuiltinCall(c, "@Vector", &.{ payload.lhs, payload.rhs });
- },
- .call => {
- const payload = node.castTag(.call).?.data;
- // Cosmetic: avoids an unnecesary address_of on most function calls.
- const lhs = if (payload.lhs.tag() == .fn_identifier)
- try c.addNode(.{
- .tag = .identifier,
- .main_token = try c.addIdentifier(payload.lhs.castTag(.fn_identifier).?.data),
- .data = undefined,
- })
- else
- try renderNodeGrouped(c, payload.lhs);
- return renderCall(c, lhs, payload.args);
- },
- .null_literal => return c.addNode(.{
- .tag = .identifier,
- .main_token = try c.addToken(.identifier, "null"),
- .data = undefined,
- }),
- .undefined_literal => return c.addNode(.{
- .tag = .identifier,
- .main_token = try c.addToken(.identifier, "undefined"),
- .data = undefined,
- }),
- .true_literal => return c.addNode(.{
- .tag = .identifier,
- .main_token = try c.addToken(.identifier, "true"),
- .data = undefined,
- }),
- .false_literal => return c.addNode(.{
- .tag = .identifier,
- .main_token = try c.addToken(.identifier, "false"),
- .data = undefined,
- }),
- .zero_literal => return c.addNode(.{
- .tag = .number_literal,
- .main_token = try c.addToken(.number_literal, "0"),
- .data = undefined,
- }),
- .one_literal => return c.addNode(.{
- .tag = .number_literal,
- .main_token = try c.addToken(.number_literal, "1"),
- .data = undefined,
- }),
- .void_type => return c.addNode(.{
- .tag = .identifier,
- .main_token = try c.addToken(.identifier, "void"),
- .data = undefined,
- }),
- .noreturn_type => return c.addNode(.{
- .tag = .identifier,
- .main_token = try c.addToken(.identifier, "noreturn"),
- .data = undefined,
- }),
- .@"continue" => return c.addNode(.{
- .tag = .@"continue",
- .main_token = try c.addToken(.keyword_continue, "continue"),
- .data = .{
- .lhs = 0,
- .rhs = undefined,
- },
- }),
- .return_void => return c.addNode(.{
- .tag = .@"return",
- .main_token = try c.addToken(.keyword_return, "return"),
- .data = .{
- .lhs = 0,
- .rhs = undefined,
- },
- }),
- .@"break" => return c.addNode(.{
- .tag = .@"break",
- .main_token = try c.addToken(.keyword_break, "break"),
- .data = .{
- .lhs = 0,
- .rhs = 0,
- },
- }),
- .break_val => {
- const payload = node.castTag(.break_val).?.data;
- const tok = try c.addToken(.keyword_break, "break");
- const break_label = if (payload.label) |some| blk: {
- _ = try c.addToken(.colon, ":");
- break :blk try c.addIdentifier(some);
- } else 0;
- return c.addNode(.{
- .tag = .@"break",
- .main_token = tok,
- .data = .{
- .lhs = break_label,
- .rhs = try renderNode(c, payload.val),
- },
- });
- },
- .@"return" => {
- const payload = node.castTag(.@"return").?.data;
- return c.addNode(.{
- .tag = .@"return",
- .main_token = try c.addToken(.keyword_return, "return"),
- .data = .{
- .lhs = try renderNode(c, payload),
- .rhs = undefined,
- },
- });
- },
- .@"comptime" => {
- const payload = node.castTag(.@"comptime").?.data;
- return c.addNode(.{
- .tag = .@"comptime",
- .main_token = try c.addToken(.keyword_comptime, "comptime"),
- .data = .{
- .lhs = try renderNode(c, payload),
- .rhs = undefined,
- },
- });
- },
- .@"defer" => {
- const payload = node.castTag(.@"defer").?.data;
- return c.addNode(.{
- .tag = .@"defer",
- .main_token = try c.addToken(.keyword_defer, "defer"),
- .data = .{
- .lhs = undefined,
- .rhs = try renderNode(c, payload),
- },
- });
- },
- .asm_simple => {
- const payload = node.castTag(.asm_simple).?.data;
- const asm_token = try c.addToken(.keyword_asm, "asm");
- _ = try c.addToken(.l_paren, "(");
- return c.addNode(.{
- .tag = .asm_simple,
- .main_token = asm_token,
- .data = .{
- .lhs = try renderNode(c, payload),
- .rhs = try c.addToken(.r_paren, ")"),
- },
- });
- },
- .type => {
- const payload = node.castTag(.type).?.data;
- return c.addNode(.{
- .tag = .identifier,
- .main_token = try c.addToken(.identifier, payload),
- .data = undefined,
- });
- },
- .identifier => {
- const payload = node.castTag(.identifier).?.data;
- return c.addNode(.{
- .tag = .identifier,
- .main_token = try c.addIdentifier(payload),
- .data = undefined,
- });
- },
- .fn_identifier => {
- // C semantics are that a function identifier has address
- // value (implicit in stage1, explicit in stage2), except in
- // the context of an address_of, which is handled there.
- const payload = node.castTag(.fn_identifier).?.data;
- const tok = try c.addToken(.ampersand, "&");
- const arg = try c.addNode(.{
- .tag = .identifier,
- .main_token = try c.addIdentifier(payload),
- .data = undefined,
- });
- return c.addNode(.{
- .tag = .address_of,
- .main_token = tok,
- .data = .{
- .lhs = arg,
- .rhs = undefined,
- },
- });
- },
- .float_literal => {
- const payload = node.castTag(.float_literal).?.data;
- return c.addNode(.{
- .tag = .number_literal,
- .main_token = try c.addToken(.number_literal, payload),
- .data = undefined,
- });
- },
- .integer_literal => {
- const payload = node.castTag(.integer_literal).?.data;
- return c.addNode(.{
- .tag = .number_literal,
- .main_token = try c.addToken(.number_literal, payload),
- .data = undefined,
- });
- },
- .string_literal => {
- const payload = node.castTag(.string_literal).?.data;
- return c.addNode(.{
- .tag = .string_literal,
- .main_token = try c.addToken(.string_literal, payload),
- .data = undefined,
- });
- },
- .char_literal => {
- const payload = node.castTag(.char_literal).?.data;
- return c.addNode(.{
- .tag = .char_literal,
- .main_token = try c.addToken(.char_literal, payload),
- .data = undefined,
- });
- },
- .enum_literal => {
- const payload = node.castTag(.enum_literal).?.data;
- _ = try c.addToken(.period, ".");
- return c.addNode(.{
- .tag = .enum_literal,
- .main_token = try c.addToken(.identifier, payload),
- .data = undefined,
- });
- },
- .helpers_macro => {
- const payload = node.castTag(.helpers_macro).?.data;
- const chain = [_][]const u8{
- "zig",
- "c_translation",
- "Macros",
- payload,
- };
- return renderStdImport(c, &chain);
- },
- .import_c_builtin => {
- const payload = node.castTag(.import_c_builtin).?.data;
- const chain = [_][]const u8{
- "zig",
- "c_builtins",
- payload,
- };
- return renderStdImport(c, &chain);
- },
- .string_slice => {
- const payload = node.castTag(.string_slice).?.data;
-
- const string = try renderNode(c, payload.string);
- const l_bracket = try c.addToken(.l_bracket, "[");
- const start = try c.addNode(.{
- .tag = .number_literal,
- .main_token = try c.addToken(.number_literal, "0"),
- .data = undefined,
- });
- _ = try c.addToken(.ellipsis2, "..");
- const end = try c.addNode(.{
- .tag = .number_literal,
- .main_token = try c.addTokenFmt(.number_literal, "{d}", .{payload.end}),
- .data = undefined,
- });
- _ = try c.addToken(.r_bracket, "]");
-
- return c.addNode(.{
- .tag = .slice,
- .main_token = l_bracket,
- .data = .{
- .lhs = string,
- .rhs = try c.addExtra(std.zig.Ast.Node.Slice{
- .start = start,
- .end = end,
- }),
- },
- });
- },
- .fail_decl => {
- const payload = node.castTag(.fail_decl).?.data;
- // pub const name = @compileError(msg);
- _ = try c.addToken(.keyword_pub, "pub");
- const const_tok = try c.addToken(.keyword_const, "const");
- _ = try c.addIdentifier(payload.actual);
- _ = try c.addToken(.equal, "=");
-
- const compile_error_tok = try c.addToken(.builtin, "@compileError");
- _ = try c.addToken(.l_paren, "(");
- const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(payload.mangled)});
- const err_msg = try c.addNode(.{
- .tag = .string_literal,
- .main_token = err_msg_tok,
- .data = undefined,
- });
- _ = try c.addToken(.r_paren, ")");
- const compile_error = try c.addNode(.{
- .tag = .builtin_call_two,
- .main_token = compile_error_tok,
- .data = .{
- .lhs = err_msg,
- .rhs = 0,
- },
- });
- _ = try c.addToken(.semicolon, ";");
-
- return c.addNode(.{
- .tag = .simple_var_decl,
- .main_token = const_tok,
- .data = .{
- .lhs = 0,
- .rhs = compile_error,
- },
- });
- },
- .pub_var_simple, .var_simple => {
- const payload = @fieldParentPtr(Payload.SimpleVarDecl, "base", node.ptr_otherwise).data;
- if (node.tag() == .pub_var_simple) _ = try c.addToken(.keyword_pub, "pub");
- const const_tok = try c.addToken(.keyword_const, "const");
- _ = try c.addIdentifier(payload.name);
- _ = try c.addToken(.equal, "=");
-
- const init = try renderNode(c, payload.init);
- _ = try c.addToken(.semicolon, ";");
-
- return c.addNode(.{
- .tag = .simple_var_decl,
- .main_token = const_tok,
- .data = .{
- .lhs = 0,
- .rhs = init,
- },
- });
- },
- .static_local_var => {
- const payload = node.castTag(.static_local_var).?.data;
-
- const const_tok = try c.addToken(.keyword_const, "const");
- _ = try c.addIdentifier(payload.name);
- _ = try c.addToken(.equal, "=");
-
- const kind_tok = try c.addToken(.keyword_struct, "struct");
- _ = try c.addToken(.l_brace, "{");
-
- const container_def = try c.addNode(.{
- .tag = .container_decl_two_trailing,
- .main_token = kind_tok,
- .data = .{
- .lhs = try renderNode(c, payload.init),
- .rhs = 0,
- },
- });
- _ = try c.addToken(.r_brace, "}");
- _ = try c.addToken(.semicolon, ";");
-
- return c.addNode(.{
- .tag = .simple_var_decl,
- .main_token = const_tok,
- .data = .{
- .lhs = 0,
- .rhs = container_def,
- },
- });
- },
- .mut_str => {
- const payload = node.castTag(.mut_str).?.data;
-
- const var_tok = try c.addToken(.keyword_var, "var");
- _ = try c.addIdentifier(payload.name);
- _ = try c.addToken(.equal, "=");
-
- const deref = try c.addNode(.{
- .tag = .deref,
- .data = .{
- .lhs = try renderNodeGrouped(c, payload.init),
- .rhs = undefined,
- },
- .main_token = try c.addToken(.period_asterisk, ".*"),
- });
- _ = try c.addToken(.semicolon, ";");
-
- return c.addNode(.{
- .tag = .simple_var_decl,
- .main_token = var_tok,
- .data = .{ .lhs = 0, .rhs = deref },
- });
- },
- .var_decl => return renderVar(c, node),
- .arg_redecl, .alias => {
- const payload = @fieldParentPtr(Payload.ArgRedecl, "base", node.ptr_otherwise).data;
- if (node.tag() == .alias) _ = try c.addToken(.keyword_pub, "pub");
- const mut_tok = if (node.tag() == .alias)
- try c.addToken(.keyword_const, "const")
- else
- try c.addToken(.keyword_var, "var");
- _ = try c.addIdentifier(payload.actual);
- _ = try c.addToken(.equal, "=");
-
- const init = try c.addNode(.{
- .tag = .identifier,
- .main_token = try c.addIdentifier(payload.mangled),
- .data = undefined,
- });
- _ = try c.addToken(.semicolon, ";");
-
- return c.addNode(.{
- .tag = .simple_var_decl,
- .main_token = mut_tok,
- .data = .{
- .lhs = 0,
- .rhs = init,
- },
- });
- },
- .int_cast => {
- const payload = node.castTag(.int_cast).?.data;
- return renderBuiltinCall(c, "@intCast", &.{payload});
- },
- .const_cast => {
- const payload = node.castTag(.const_cast).?.data;
- return renderBuiltinCall(c, "@constCast", &.{payload});
- },
- .volatile_cast => {
- const payload = node.castTag(.volatile_cast).?.data;
- return renderBuiltinCall(c, "@volatileCast", &.{payload});
- },
- .signed_remainder => {
- const payload = node.castTag(.signed_remainder).?.data;
- const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "signedRemainder" });
- return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
- },
- .div_trunc => {
- const payload = node.castTag(.div_trunc).?.data;
- return renderBuiltinCall(c, "@divTrunc", &.{ payload.lhs, payload.rhs });
- },
- .int_from_bool => {
- const payload = node.castTag(.int_from_bool).?.data;
- return renderBuiltinCall(c, "@intFromBool", &.{payload});
- },
- .as => {
- const payload = node.castTag(.as).?.data;
- return renderBuiltinCall(c, "@as", &.{ payload.lhs, payload.rhs });
- },
- .truncate => {
- const payload = node.castTag(.truncate).?.data;
- return renderBuiltinCall(c, "@truncate", &.{payload});
- },
- .bit_cast => {
- const payload = node.castTag(.bit_cast).?.data;
- return renderBuiltinCall(c, "@bitCast", &.{payload});
- },
- .float_cast => {
- const payload = node.castTag(.float_cast).?.data;
- return renderBuiltinCall(c, "@floatCast", &.{payload});
- },
- .int_from_float => {
- const payload = node.castTag(.int_from_float).?.data;
- return renderBuiltinCall(c, "@intFromFloat", &.{payload});
- },
- .float_from_int => {
- const payload = node.castTag(.float_from_int).?.data;
- return renderBuiltinCall(c, "@floatFromInt", &.{payload});
- },
- .ptr_from_int => {
- const payload = node.castTag(.ptr_from_int).?.data;
- return renderBuiltinCall(c, "@ptrFromInt", &.{payload});
- },
- .int_from_ptr => {
- const payload = node.castTag(.int_from_ptr).?.data;
- return renderBuiltinCall(c, "@intFromPtr", &.{payload});
- },
- .align_cast => {
- const payload = node.castTag(.align_cast).?.data;
- return renderBuiltinCall(c, "@alignCast", &.{payload});
- },
- .ptr_cast => {
- const payload = node.castTag(.ptr_cast).?.data;
- return renderBuiltinCall(c, "@ptrCast", &.{payload});
- },
- .div_exact => {
- const payload = node.castTag(.div_exact).?.data;
- return renderBuiltinCall(c, "@divExact", &.{ payload.lhs, payload.rhs });
- },
- .offset_of => {
- const payload = node.castTag(.offset_of).?.data;
- return renderBuiltinCall(c, "@offsetOf", &.{ payload.lhs, payload.rhs });
- },
- .sizeof => {
- const payload = node.castTag(.sizeof).?.data;
- return renderBuiltinCall(c, "@sizeOf", &.{payload});
- },
- .shuffle => {
- const payload = node.castTag(.shuffle).?.data;
- return renderBuiltinCall(c, "@shuffle", &.{
- payload.element_type,
- payload.a,
- payload.b,
- payload.mask_vector,
- });
- },
- .builtin_extern => {
- const payload = node.castTag(.builtin_extern).?.data;
-
- var info_inits: [1]Payload.ContainerInitDot.Initializer = .{
- .{ .name = "name", .value = payload.name },
- };
- var info_payload: Payload.ContainerInitDot = .{
- .base = .{ .tag = .container_init_dot },
- .data = &info_inits,
- };
-
- return renderBuiltinCall(c, "@extern", &.{
- payload.type,
- .{ .ptr_otherwise = &info_payload.base },
- });
- },
- .macro_arithmetic => {
- const payload = node.castTag(.macro_arithmetic).?.data;
- const op = @tagName(payload.op);
- const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "MacroArithmetic", op });
- return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
- },
- .alignof => {
- const payload = node.castTag(.alignof).?.data;
- return renderBuiltinCall(c, "@alignOf", &.{payload});
- },
- .typeof => {
- const payload = node.castTag(.typeof).?.data;
- return renderBuiltinCall(c, "@TypeOf", &.{payload});
- },
- .typeinfo => {
- const payload = node.castTag(.typeinfo).?.data;
- return renderBuiltinCall(c, "@typeInfo", &.{payload});
- },
- .negate => return renderPrefixOp(c, node, .negation, .minus, "-"),
- .negate_wrap => return renderPrefixOp(c, node, .negation_wrap, .minus_percent, "-%"),
- .bit_not => return renderPrefixOp(c, node, .bit_not, .tilde, "~"),
- .not => return renderPrefixOp(c, node, .bool_not, .bang, "!"),
- .optional_type => return renderPrefixOp(c, node, .optional_type, .question_mark, "?"),
- .address_of => {
- const payload = node.castTag(.address_of).?.data;
-
- const ampersand = try c.addToken(.ampersand, "&");
- const base = if (payload.tag() == .fn_identifier)
- try c.addNode(.{
- .tag = .identifier,
- .main_token = try c.addIdentifier(payload.castTag(.fn_identifier).?.data),
- .data = undefined,
- })
- else
- try renderNodeGrouped(c, payload);
- return c.addNode(.{
- .tag = .address_of,
- .main_token = ampersand,
- .data = .{
- .lhs = base,
- .rhs = undefined,
- },
- });
- },
- .deref => {
- const payload = node.castTag(.deref).?.data;
- const operand = try renderNodeGrouped(c, payload);
- const deref_tok = try c.addToken(.period_asterisk, ".*");
- return c.addNode(.{
- .tag = .deref,
- .main_token = deref_tok,
- .data = .{
- .lhs = operand,
- .rhs = undefined,
- },
- });
- },
- .unwrap => {
- const payload = node.castTag(.unwrap).?.data;
- const operand = try renderNodeGrouped(c, payload);
- const period = try c.addToken(.period, ".");
- const question_mark = try c.addToken(.question_mark, "?");
- return c.addNode(.{
- .tag = .unwrap_optional,
- .main_token = period,
- .data = .{
- .lhs = operand,
- .rhs = question_mark,
- },
- });
- },
- .c_pointer, .single_pointer => {
- const payload = @fieldParentPtr(Payload.Pointer, "base", node.ptr_otherwise).data;
-
- const asterisk = if (node.tag() == .single_pointer)
- try c.addToken(.asterisk, "*")
- else blk: {
- _ = try c.addToken(.l_bracket, "[");
- const res = try c.addToken(.asterisk, "*");
- _ = try c.addIdentifier("c");
- _ = try c.addToken(.r_bracket, "]");
- break :blk res;
- };
- if (payload.is_const) _ = try c.addToken(.keyword_const, "const");
- if (payload.is_volatile) _ = try c.addToken(.keyword_volatile, "volatile");
- const elem_type = try renderNodeGrouped(c, payload.elem_type);
-
- return c.addNode(.{
- .tag = .ptr_type_aligned,
- .main_token = asterisk,
- .data = .{
- .lhs = 0,
- .rhs = elem_type,
- },
- });
- },
- .add => return renderBinOpGrouped(c, node, .add, .plus, "+"),
- .add_assign => return renderBinOp(c, node, .assign_add, .plus_equal, "+="),
- .add_wrap => return renderBinOpGrouped(c, node, .add_wrap, .plus_percent, "+%"),
- .add_wrap_assign => return renderBinOp(c, node, .assign_add_wrap, .plus_percent_equal, "+%="),
- .sub => return renderBinOpGrouped(c, node, .sub, .minus, "-"),
- .sub_assign => return renderBinOp(c, node, .assign_sub, .minus_equal, "-="),
- .sub_wrap => return renderBinOpGrouped(c, node, .sub_wrap, .minus_percent, "-%"),
- .sub_wrap_assign => return renderBinOp(c, node, .assign_sub_wrap, .minus_percent_equal, "-%="),
- .mul => return renderBinOpGrouped(c, node, .mul, .asterisk, "*"),
- .mul_assign => return renderBinOp(c, node, .assign_mul, .asterisk_equal, "*="),
- .mul_wrap => return renderBinOpGrouped(c, node, .mul_wrap, .asterisk_percent, "*%"),
- .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="),
- .div => return renderBinOpGrouped(c, node, .div, .slash, "/"),
- .div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="),
- .shl => return renderBinOpGrouped(c, node, .shl, .angle_bracket_angle_bracket_left, "<<"),
- .shl_assign => return renderBinOp(c, node, .assign_shl, .angle_bracket_angle_bracket_left_equal, "<<="),
- .shr => return renderBinOpGrouped(c, node, .shr, .angle_bracket_angle_bracket_right, ">>"),
- .shr_assign => return renderBinOp(c, node, .assign_shr, .angle_bracket_angle_bracket_right_equal, ">>="),
- .mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"),
- .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="),
- .@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"),
- .@"or" => return renderBinOpGrouped(c, node, .bool_or, .keyword_or, "or"),
- .less_than => return renderBinOpGrouped(c, node, .less_than, .angle_bracket_left, "<"),
- .less_than_equal => return renderBinOpGrouped(c, node, .less_or_equal, .angle_bracket_left_equal, "<="),
- .greater_than => return renderBinOpGrouped(c, node, .greater_than, .angle_bracket_right, ">="),
- .greater_than_equal => return renderBinOpGrouped(c, node, .greater_or_equal, .angle_bracket_right_equal, ">="),
- .equal => return renderBinOpGrouped(c, node, .equal_equal, .equal_equal, "=="),
- .not_equal => return renderBinOpGrouped(c, node, .bang_equal, .bang_equal, "!="),
- .bit_and => return renderBinOpGrouped(c, node, .bit_and, .ampersand, "&"),
- .bit_and_assign => return renderBinOp(c, node, .assign_bit_and, .ampersand_equal, "&="),
- .bit_or => return renderBinOpGrouped(c, node, .bit_or, .pipe, "|"),
- .bit_or_assign => return renderBinOp(c, node, .assign_bit_or, .pipe_equal, "|="),
- .bit_xor => return renderBinOpGrouped(c, node, .bit_xor, .caret, "^"),
- .bit_xor_assign => return renderBinOp(c, node, .assign_bit_xor, .caret_equal, "^="),
- .array_cat => return renderBinOp(c, node, .array_cat, .plus_plus, "++"),
- .ellipsis3 => return renderBinOpGrouped(c, node, .switch_range, .ellipsis3, "..."),
- .assign => return renderBinOp(c, node, .assign, .equal, "="),
- .empty_block => {
- const l_brace = try c.addToken(.l_brace, "{");
- _ = try c.addToken(.r_brace, "}");
- return c.addNode(.{
- .tag = .block_two,
- .main_token = l_brace,
- .data = .{
- .lhs = 0,
- .rhs = 0,
- },
- });
- },
- .block_single => {
- const payload = node.castTag(.block_single).?.data;
- const l_brace = try c.addToken(.l_brace, "{");
-
- const stmt = try renderNode(c, payload);
- try addSemicolonIfNeeded(c, payload);
-
- _ = try c.addToken(.r_brace, "}");
- return c.addNode(.{
- .tag = .block_two_semicolon,
- .main_token = l_brace,
- .data = .{
- .lhs = stmt,
- .rhs = 0,
- },
- });
- },
- .block => {
- const payload = node.castTag(.block).?.data;
- if (payload.label) |some| {
- _ = try c.addIdentifier(some);
- _ = try c.addToken(.colon, ":");
- }
- const l_brace = try c.addToken(.l_brace, "{");
-
- var stmts = std.ArrayList(NodeIndex).init(c.gpa);
- defer stmts.deinit();
- for (payload.stmts) |stmt| {
- const res = try renderNode(c, stmt);
- if (res == 0) continue;
- try addSemicolonIfNeeded(c, stmt);
- try stmts.append(res);
- }
- const span = try c.listToSpan(stmts.items);
- _ = try c.addToken(.r_brace, "}");
-
- const semicolon = c.tokens.items(.tag)[c.tokens.len - 2] == .semicolon;
- return c.addNode(.{
- .tag = if (semicolon) .block_semicolon else .block,
- .main_token = l_brace,
- .data = .{
- .lhs = span.start,
- .rhs = span.end,
- },
- });
- },
- .func => return renderFunc(c, node),
- .pub_inline_fn => return renderMacroFunc(c, node),
- .discard => {
- const payload = node.castTag(.discard).?.data;
- if (payload.should_skip) return @as(NodeIndex, 0);
-
- const lhs = try c.addNode(.{
- .tag = .identifier,
- .main_token = try c.addToken(.identifier, "_"),
- .data = undefined,
- });
- const main_token = try c.addToken(.equal, "=");
- if (payload.value.tag() == .identifier) {
- // Render as `_ = &foo;` to avoid tripping "pointless discard" and "local variable never mutated" errors.
- var addr_of_pl: Payload.UnOp = .{
- .base = .{ .tag = .address_of },
- .data = payload.value,
- };
- const addr_of: Node = .{ .ptr_otherwise = &addr_of_pl.base };
- return c.addNode(.{
- .tag = .assign,
- .main_token = main_token,
- .data = .{
- .lhs = lhs,
- .rhs = try renderNode(c, addr_of),
- },
- });
- } else {
- return c.addNode(.{
- .tag = .assign,
- .main_token = main_token,
- .data = .{
- .lhs = lhs,
- .rhs = try renderNode(c, payload.value),
- },
- });
- }
- },
- .@"while" => {
- const payload = node.castTag(.@"while").?.data;
- const while_tok = try c.addToken(.keyword_while, "while");
- _ = try c.addToken(.l_paren, "(");
- const cond = try renderNode(c, payload.cond);
- _ = try c.addToken(.r_paren, ")");
-
- const cont_expr = if (payload.cont_expr) |some| blk: {
- _ = try c.addToken(.colon, ":");
- _ = try c.addToken(.l_paren, "(");
- const res = try renderNode(c, some);
- _ = try c.addToken(.r_paren, ")");
- break :blk res;
- } else 0;
- const body = try renderNode(c, payload.body);
-
- if (cont_expr == 0) {
- return c.addNode(.{
- .tag = .while_simple,
- .main_token = while_tok,
- .data = .{
- .lhs = cond,
- .rhs = body,
- },
- });
- } else {
- return c.addNode(.{
- .tag = .while_cont,
- .main_token = while_tok,
- .data = .{
- .lhs = cond,
- .rhs = try c.addExtra(std.zig.Ast.Node.WhileCont{
- .cont_expr = cont_expr,
- .then_expr = body,
- }),
- },
- });
- }
- },
- .while_true => {
- const payload = node.castTag(.while_true).?.data;
- const while_tok = try c.addToken(.keyword_while, "while");
- _ = try c.addToken(.l_paren, "(");
- const cond = try c.addNode(.{
- .tag = .identifier,
- .main_token = try c.addToken(.identifier, "true"),
- .data = undefined,
- });
- _ = try c.addToken(.r_paren, ")");
- const body = try renderNode(c, payload);
-
- return c.addNode(.{
- .tag = .while_simple,
- .main_token = while_tok,
- .data = .{
- .lhs = cond,
- .rhs = body,
- },
- });
- },
- .@"if" => {
- const payload = node.castTag(.@"if").?.data;
- const if_tok = try c.addToken(.keyword_if, "if");
- _ = try c.addToken(.l_paren, "(");
- const cond = try renderNode(c, payload.cond);
- _ = try c.addToken(.r_paren, ")");
-
- const then_expr = try renderNode(c, payload.then);
- const else_node = payload.@"else" orelse return c.addNode(.{
- .tag = .if_simple,
- .main_token = if_tok,
- .data = .{
- .lhs = cond,
- .rhs = then_expr,
- },
- });
- _ = try c.addToken(.keyword_else, "else");
- const else_expr = try renderNode(c, else_node);
-
- return c.addNode(.{
- .tag = .@"if",
- .main_token = if_tok,
- .data = .{
- .lhs = cond,
- .rhs = try c.addExtra(std.zig.Ast.Node.If{
- .then_expr = then_expr,
- .else_expr = else_expr,
- }),
- },
- });
- },
- .if_not_break => {
- const payload = node.castTag(.if_not_break).?.data;
- const if_tok = try c.addToken(.keyword_if, "if");
- _ = try c.addToken(.l_paren, "(");
- const cond = try c.addNode(.{
- .tag = .bool_not,
- .main_token = try c.addToken(.bang, "!"),
- .data = .{
- .lhs = try renderNodeGrouped(c, payload),
- .rhs = undefined,
- },
- });
- _ = try c.addToken(.r_paren, ")");
- const then_expr = try c.addNode(.{
- .tag = .@"break",
- .main_token = try c.addToken(.keyword_break, "break"),
- .data = .{
- .lhs = 0,
- .rhs = 0,
- },
- });
-
- return c.addNode(.{
- .tag = .if_simple,
- .main_token = if_tok,
- .data = .{
- .lhs = cond,
- .rhs = then_expr,
- },
- });
- },
- .@"switch" => {
- const payload = node.castTag(.@"switch").?.data;
- const switch_tok = try c.addToken(.keyword_switch, "switch");
- _ = try c.addToken(.l_paren, "(");
- const cond = try renderNode(c, payload.cond);
- _ = try c.addToken(.r_paren, ")");
-
- _ = try c.addToken(.l_brace, "{");
- var cases = try c.gpa.alloc(NodeIndex, payload.cases.len);
- defer c.gpa.free(cases);
- for (payload.cases, 0..) |case, i| {
- cases[i] = try renderNode(c, case);
- _ = try c.addToken(.comma, ",");
- }
- const span = try c.listToSpan(cases);
- _ = try c.addToken(.r_brace, "}");
- return c.addNode(.{
- .tag = .switch_comma,
- .main_token = switch_tok,
- .data = .{
- .lhs = cond,
- .rhs = try c.addExtra(NodeSubRange{
- .start = span.start,
- .end = span.end,
- }),
- },
- });
- },
- .switch_else => {
- const payload = node.castTag(.switch_else).?.data;
- _ = try c.addToken(.keyword_else, "else");
- return c.addNode(.{
- .tag = .switch_case_one,
- .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
- .data = .{
- .lhs = 0,
- .rhs = try renderNode(c, payload),
- },
- });
- },
- .switch_prong => {
- const payload = node.castTag(.switch_prong).?.data;
- var items = try c.gpa.alloc(NodeIndex, @max(payload.cases.len, 1));
- defer c.gpa.free(items);
- items[0] = 0;
- for (payload.cases, 0..) |item, i| {
- if (i != 0) _ = try c.addToken(.comma, ",");
- items[i] = try renderNode(c, item);
- }
- _ = try c.addToken(.r_brace, "}");
- if (items.len < 2) {
- return c.addNode(.{
- .tag = .switch_case_one,
- .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
- .data = .{
- .lhs = items[0],
- .rhs = try renderNode(c, payload.cond),
- },
- });
- } else {
- const span = try c.listToSpan(items);
- return c.addNode(.{
- .tag = .switch_case,
- .main_token = try c.addToken(.equal_angle_bracket_right, "=>"),
- .data = .{
- .lhs = try c.addExtra(NodeSubRange{
- .start = span.start,
- .end = span.end,
- }),
- .rhs = try renderNode(c, payload.cond),
- },
- });
- }
- },
- .opaque_literal => {
- const opaque_tok = try c.addToken(.keyword_opaque, "opaque");
- _ = try c.addToken(.l_brace, "{");
- _ = try c.addToken(.r_brace, "}");
-
- return c.addNode(.{
- .tag = .container_decl_two,
- .main_token = opaque_tok,
- .data = .{
- .lhs = 0,
- .rhs = 0,
- },
- });
- },
- .array_access => {
- const payload = node.castTag(.array_access).?.data;
- const lhs = try renderNodeGrouped(c, payload.lhs);
- const l_bracket = try c.addToken(.l_bracket, "[");
- const index_expr = try renderNode(c, payload.rhs);
- _ = try c.addToken(.r_bracket, "]");
- return c.addNode(.{
- .tag = .array_access,
- .main_token = l_bracket,
- .data = .{
- .lhs = lhs,
- .rhs = index_expr,
- },
- });
- },
- .array_type => {
- const payload = node.castTag(.array_type).?.data;
- return renderArrayType(c, payload.len, payload.elem_type);
- },
- .null_sentinel_array_type => {
- const payload = node.castTag(.null_sentinel_array_type).?.data;
- return renderNullSentinelArrayType(c, payload.len, payload.elem_type);
- },
- .array_filler => {
- const payload = node.castTag(.array_filler).?.data;
-
- const type_expr = try renderArrayType(c, 1, payload.type);
- const l_brace = try c.addToken(.l_brace, "{");
- const val = try renderNode(c, payload.filler);
- _ = try c.addToken(.r_brace, "}");
-
- const init = try c.addNode(.{
- .tag = .array_init_one,
- .main_token = l_brace,
- .data = .{
- .lhs = type_expr,
- .rhs = val,
- },
- });
- return c.addNode(.{
- .tag = .array_cat,
- .main_token = try c.addToken(.asterisk_asterisk, "**"),
- .data = .{
- .lhs = init,
- .rhs = try c.addNode(.{
- .tag = .number_literal,
- .main_token = try c.addTokenFmt(.number_literal, "{d}", .{payload.count}),
- .data = undefined,
- }),
- },
- });
- },
- .empty_array => {
- const payload = node.castTag(.empty_array).?.data;
-
- const type_expr = try renderArrayType(c, 0, payload);
- return renderArrayInit(c, type_expr, &.{});
- },
- .array_init => {
- const payload = node.castTag(.array_init).?.data;
- const type_expr = try renderNode(c, payload.cond);
- return renderArrayInit(c, type_expr, payload.cases);
- },
- .vector_zero_init => {
- const payload = node.castTag(.vector_zero_init).?.data;
- return renderBuiltinCall(c, "@splat", &.{payload});
- },
- .field_access => {
- const payload = node.castTag(.field_access).?.data;
- const lhs = try renderNodeGrouped(c, payload.lhs);
- return renderFieldAccess(c, lhs, payload.field_name);
- },
- .@"struct", .@"union" => return renderRecord(c, node),
- .enum_constant => {
- const payload = node.castTag(.enum_constant).?.data;
-
- if (payload.is_public) _ = try c.addToken(.keyword_pub, "pub");
- const const_tok = try c.addToken(.keyword_const, "const");
- _ = try c.addIdentifier(payload.name);
-
- const type_node = if (payload.type) |enum_const_type| blk: {
- _ = try c.addToken(.colon, ":");
- break :blk try renderNode(c, enum_const_type);
- } else 0;
-
- _ = try c.addToken(.equal, "=");
-
- const init_node = try renderNode(c, payload.value);
- _ = try c.addToken(.semicolon, ";");
-
- return c.addNode(.{
- .tag = .simple_var_decl,
- .main_token = const_tok,
- .data = .{
- .lhs = type_node,
- .rhs = init_node,
- },
- });
- },
- .tuple => {
- const payload = node.castTag(.tuple).?.data;
- _ = try c.addToken(.period, ".");
- const l_brace = try c.addToken(.l_brace, "{");
- var inits = try c.gpa.alloc(NodeIndex, @max(payload.len, 2));
- defer c.gpa.free(inits);
- inits[0] = 0;
- inits[1] = 0;
- for (payload, 0..) |init, i| {
- if (i != 0) _ = try c.addToken(.comma, ",");
- inits[i] = try renderNode(c, init);
- }
- _ = try c.addToken(.r_brace, "}");
- if (payload.len < 3) {
- return c.addNode(.{
- .tag = .array_init_dot_two,
- .main_token = l_brace,
- .data = .{
- .lhs = inits[0],
- .rhs = inits[1],
- },
- });
- } else {
- const span = try c.listToSpan(inits);
- return c.addNode(.{
- .tag = .array_init_dot,
- .main_token = l_brace,
- .data = .{
- .lhs = span.start,
- .rhs = span.end,
- },
- });
- }
- },
- .container_init_dot => {
- const payload = node.castTag(.container_init_dot).?.data;
- _ = try c.addToken(.period, ".");
- const l_brace = try c.addToken(.l_brace, "{");
- var inits = try c.gpa.alloc(NodeIndex, @max(payload.len, 2));
- defer c.gpa.free(inits);
- inits[0] = 0;
- inits[1] = 0;
- for (payload, 0..) |init, i| {
- _ = try c.addToken(.period, ".");
- _ = try c.addIdentifier(init.name);
- _ = try c.addToken(.equal, "=");
- inits[i] = try renderNode(c, init.value);
- _ = try c.addToken(.comma, ",");
- }
- _ = try c.addToken(.r_brace, "}");
-
- if (payload.len < 3) {
- return c.addNode(.{
- .tag = .struct_init_dot_two_comma,
- .main_token = l_brace,
- .data = .{
- .lhs = inits[0],
- .rhs = inits[1],
- },
- });
- } else {
- const span = try c.listToSpan(inits);
- return c.addNode(.{
- .tag = .struct_init_dot_comma,
- .main_token = l_brace,
- .data = .{
- .lhs = span.start,
- .rhs = span.end,
- },
- });
- }
- },
- .container_init => {
- const payload = node.castTag(.container_init).?.data;
- const lhs = try renderNode(c, payload.lhs);
-
- const l_brace = try c.addToken(.l_brace, "{");
- var inits = try c.gpa.alloc(NodeIndex, @max(payload.inits.len, 1));
- defer c.gpa.free(inits);
- inits[0] = 0;
- for (payload.inits, 0..) |init, i| {
- _ = try c.addToken(.period, ".");
- _ = try c.addIdentifier(init.name);
- _ = try c.addToken(.equal, "=");
- inits[i] = try renderNode(c, init.value);
- _ = try c.addToken(.comma, ",");
- }
- _ = try c.addToken(.r_brace, "}");
-
- return switch (payload.inits.len) {
- 0 => c.addNode(.{
- .tag = .struct_init_one,
- .main_token = l_brace,
- .data = .{
- .lhs = lhs,
- .rhs = 0,
- },
- }),
- 1 => c.addNode(.{
- .tag = .struct_init_one_comma,
- .main_token = l_brace,
- .data = .{
- .lhs = lhs,
- .rhs = inits[0],
- },
- }),
- else => blk: {
- const span = try c.listToSpan(inits);
- break :blk c.addNode(.{
- .tag = .struct_init_comma,
- .main_token = l_brace,
- .data = .{
- .lhs = lhs,
- .rhs = try c.addExtra(NodeSubRange{
- .start = span.start,
- .end = span.end,
- }),
- },
- });
- },
- };
- },
- .@"anytype" => unreachable, // Handled in renderParams
- }
-}
-
-fn renderRecord(c: *Context, node: Node) !NodeIndex {
- const payload = @fieldParentPtr(Payload.Record, "base", node.ptr_otherwise).data;
- if (payload.layout == .@"packed")
- _ = try c.addToken(.keyword_packed, "packed")
- else if (payload.layout == .@"extern")
- _ = try c.addToken(.keyword_extern, "extern");
- const kind_tok = if (node.tag() == .@"struct")
- try c.addToken(.keyword_struct, "struct")
- else
- try c.addToken(.keyword_union, "union");
-
- _ = try c.addToken(.l_brace, "{");
-
- const num_vars = payload.variables.len;
- const num_funcs = payload.functions.len;
- const total_members = payload.fields.len + num_vars + num_funcs;
- const members = try c.gpa.alloc(NodeIndex, @max(total_members, 2));
- defer c.gpa.free(members);
- members[0] = 0;
- members[1] = 0;
-
- for (payload.fields, 0..) |field, i| {
- const name_tok = try c.addTokenFmt(.identifier, "{s}", .{std.zig.fmtId(field.name)});
- _ = try c.addToken(.colon, ":");
- const type_expr = try renderNode(c, field.type);
-
- const align_expr = if (field.alignment) |alignment| blk: {
- _ = try c.addToken(.keyword_align, "align");
- _ = try c.addToken(.l_paren, "(");
- const align_expr = try c.addNode(.{
- .tag = .number_literal,
- .main_token = try c.addTokenFmt(.number_literal, "{d}", .{alignment}),
- .data = undefined,
- });
- _ = try c.addToken(.r_paren, ")");
- break :blk align_expr;
- } else 0;
-
- const value_expr = if (field.default_value) |value| blk: {
- _ = try c.addToken(.equal, "=");
- break :blk try renderNode(c, value);
- } else 0;
-
- members[i] = try c.addNode(if (align_expr == 0) .{
- .tag = .container_field_init,
- .main_token = name_tok,
- .data = .{
- .lhs = type_expr,
- .rhs = value_expr,
- },
- } else if (value_expr == 0) .{
- .tag = .container_field_align,
- .main_token = name_tok,
- .data = .{
- .lhs = type_expr,
- .rhs = align_expr,
- },
- } else .{
- .tag = .container_field,
- .main_token = name_tok,
- .data = .{
- .lhs = type_expr,
- .rhs = try c.addExtra(std.zig.Ast.Node.ContainerField{
- .align_expr = align_expr,
- .value_expr = value_expr,
- }),
- },
- });
- _ = try c.addToken(.comma, ",");
- }
- for (payload.variables, 0..) |variable, i| {
- members[payload.fields.len + i] = try renderNode(c, variable);
- }
- for (payload.functions, 0..) |function, i| {
- members[payload.fields.len + num_vars + i] = try renderNode(c, function);
- }
- _ = try c.addToken(.r_brace, "}");
-
- if (total_members == 0) {
- return c.addNode(.{
- .tag = .container_decl_two,
- .main_token = kind_tok,
- .data = .{
- .lhs = 0,
- .rhs = 0,
- },
- });
- } else if (total_members <= 2) {
- return c.addNode(.{
- .tag = if (num_funcs == 0) .container_decl_two_trailing else .container_decl_two,
- .main_token = kind_tok,
- .data = .{
- .lhs = members[0],
- .rhs = members[1],
- },
- });
- } else {
- const span = try c.listToSpan(members);
- return c.addNode(.{
- .tag = if (num_funcs == 0) .container_decl_trailing else .container_decl,
- .main_token = kind_tok,
- .data = .{
- .lhs = span.start,
- .rhs = span.end,
- },
- });
- }
-}
-
-fn renderFieldAccess(c: *Context, lhs: NodeIndex, field_name: []const u8) !NodeIndex {
- return c.addNode(.{
- .tag = .field_access,
- .main_token = try c.addToken(.period, "."),
- .data = .{
- .lhs = lhs,
- .rhs = try c.addTokenFmt(.identifier, "{s}", .{std.zig.fmtId(field_name)}),
- },
- });
-}
-
-fn renderArrayInit(c: *Context, lhs: NodeIndex, inits: []const Node) !NodeIndex {
- const l_brace = try c.addToken(.l_brace, "{");
- var rendered = try c.gpa.alloc(NodeIndex, @max(inits.len, 1));
- defer c.gpa.free(rendered);
- rendered[0] = 0;
- for (inits, 0..) |init, i| {
- rendered[i] = try renderNode(c, init);
- _ = try c.addToken(.comma, ",");
- }
- _ = try c.addToken(.r_brace, "}");
- if (inits.len < 2) {
- return c.addNode(.{
- .tag = .array_init_one_comma,
- .main_token = l_brace,
- .data = .{
- .lhs = lhs,
- .rhs = rendered[0],
- },
- });
- } else {
- const span = try c.listToSpan(rendered);
- return c.addNode(.{
- .tag = .array_init_comma,
- .main_token = l_brace,
- .data = .{
- .lhs = lhs,
- .rhs = try c.addExtra(NodeSubRange{
- .start = span.start,
- .end = span.end,
- }),
- },
- });
- }
-}
-
-fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex {
- const l_bracket = try c.addToken(.l_bracket, "[");
- const len_expr = try c.addNode(.{
- .tag = .number_literal,
- .main_token = try c.addTokenFmt(.number_literal, "{d}", .{len}),
- .data = undefined,
- });
- _ = try c.addToken(.r_bracket, "]");
- const elem_type_expr = try renderNode(c, elem_type);
- return c.addNode(.{
- .tag = .array_type,
- .main_token = l_bracket,
- .data = .{
- .lhs = len_expr,
- .rhs = elem_type_expr,
- },
- });
-}
-
-fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex {
- const l_bracket = try c.addToken(.l_bracket, "[");
- const len_expr = try c.addNode(.{
- .tag = .number_literal,
- .main_token = try c.addTokenFmt(.number_literal, "{d}", .{len}),
- .data = undefined,
- });
- _ = try c.addToken(.colon, ":");
-
- const sentinel_expr = try c.addNode(.{
- .tag = .number_literal,
- .main_token = try c.addToken(.number_literal, "0"),
- .data = undefined,
- });
-
- _ = try c.addToken(.r_bracket, "]");
- const elem_type_expr = try renderNode(c, elem_type);
- return c.addNode(.{
- .tag = .array_type_sentinel,
- .main_token = l_bracket,
- .data = .{
- .lhs = len_expr,
- .rhs = try c.addExtra(std.zig.Ast.Node.ArrayTypeSentinel{
- .sentinel = sentinel_expr,
- .elem_type = elem_type_expr,
- }),
- },
- });
-}
-
-fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
- switch (node.tag()) {
- .warning => unreachable,
- .var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch", .static_local_var, .mut_str => {},
- .while_true => {
- const payload = node.castTag(.while_true).?.data;
- return addSemicolonIfNotBlock(c, payload);
- },
- .@"while" => {
- const payload = node.castTag(.@"while").?.data;
- return addSemicolonIfNotBlock(c, payload.body);
- },
- .@"if" => {
- const payload = node.castTag(.@"if").?.data;
- if (payload.@"else") |some|
- return addSemicolonIfNeeded(c, some);
- return addSemicolonIfNotBlock(c, payload.then);
- },
- else => _ = try c.addToken(.semicolon, ";"),
- }
-}
-
-fn addSemicolonIfNotBlock(c: *Context, node: Node) !void {
- switch (node.tag()) {
- .block, .empty_block, .block_single => {},
- else => _ = try c.addToken(.semicolon, ";"),
- }
-}
-
-fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
- switch (node.tag()) {
- .declaration => unreachable,
- .null_literal,
- .undefined_literal,
- .true_literal,
- .false_literal,
- .return_void,
- .zero_literal,
- .one_literal,
- .void_type,
- .noreturn_type,
- .@"anytype",
- .div_trunc,
- .signed_remainder,
- .int_cast,
- .const_cast,
- .volatile_cast,
- .as,
- .truncate,
- .bit_cast,
- .float_cast,
- .int_from_float,
- .float_from_int,
- .ptr_from_int,
- .std_mem_zeroes,
- .int_from_ptr,
- .sizeof,
- .alignof,
- .typeof,
- .typeinfo,
- .vector,
- .helpers_sizeof,
- .helpers_cast,
- .helpers_promoteIntLiteral,
- .helpers_shuffle_vector_index,
- .helpers_flexible_array_type,
- .std_mem_zeroinit,
- .integer_literal,
- .float_literal,
- .string_literal,
- .string_slice,
- .char_literal,
- .enum_literal,
- .identifier,
- .fn_identifier,
- .field_access,
- .ptr_cast,
- .type,
- .array_access,
- .align_cast,
- .optional_type,
- .c_pointer,
- .single_pointer,
- .unwrap,
- .deref,
- .not,
- .negate,
- .negate_wrap,
- .bit_not,
- .func,
- .call,
- .array_type,
- .null_sentinel_array_type,
- .int_from_bool,
- .div_exact,
- .offset_of,
- .shuffle,
- .builtin_extern,
- .static_local_var,
- .mut_str,
- .macro_arithmetic,
- => {
- // no grouping needed
- return renderNode(c, node);
- },
-
- .opaque_literal,
- .empty_array,
- .block_single,
- .add,
- .add_wrap,
- .sub,
- .sub_wrap,
- .mul,
- .mul_wrap,
- .div,
- .shl,
- .shr,
- .mod,
- .@"and",
- .@"or",
- .less_than,
- .less_than_equal,
- .greater_than,
- .greater_than_equal,
- .equal,
- .not_equal,
- .bit_and,
- .bit_or,
- .bit_xor,
- .empty_block,
- .array_cat,
- .array_filler,
- .@"if",
- .@"struct",
- .@"union",
- .array_init,
- .vector_zero_init,
- .tuple,
- .container_init,
- .container_init_dot,
- .block,
- .address_of,
- => return c.addNode(.{
- .tag = .grouped_expression,
- .main_token = try c.addToken(.l_paren, "("),
- .data = .{
- .lhs = try renderNode(c, node),
- .rhs = try c.addToken(.r_paren, ")"),
- },
- }),
- .ellipsis3,
- .switch_prong,
- .warning,
- .var_decl,
- .fail_decl,
- .arg_redecl,
- .alias,
- .var_simple,
- .pub_var_simple,
- .enum_constant,
- .@"while",
- .@"switch",
- .@"break",
- .break_val,
- .pub_inline_fn,
- .discard,
- .@"continue",
- .@"return",
- .@"comptime",
- .@"defer",
- .asm_simple,
- .while_true,
- .if_not_break,
- .switch_else,
- .add_assign,
- .add_wrap_assign,
- .sub_assign,
- .sub_wrap_assign,
- .mul_assign,
- .mul_wrap_assign,
- .div_assign,
- .shl_assign,
- .shr_assign,
- .mod_assign,
- .bit_and_assign,
- .bit_or_assign,
- .bit_xor_assign,
- .assign,
- .helpers_macro,
- .import_c_builtin,
- => {
- // these should never appear in places where grouping might be needed.
- unreachable;
- },
- }
-}
-
-fn renderPrefixOp(c: *Context, node: Node, tag: std.zig.Ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
- const payload = @fieldParentPtr(Payload.UnOp, "base", node.ptr_otherwise).data;
- return c.addNode(.{
- .tag = tag,
- .main_token = try c.addToken(tok_tag, bytes),
- .data = .{
- .lhs = try renderNodeGrouped(c, payload),
- .rhs = undefined,
- },
- });
-}
-
-fn renderBinOpGrouped(c: *Context, node: Node, tag: std.zig.Ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
- const payload = @fieldParentPtr(Payload.BinOp, "base", node.ptr_otherwise).data;
- const lhs = try renderNodeGrouped(c, payload.lhs);
- return c.addNode(.{
- .tag = tag,
- .main_token = try c.addToken(tok_tag, bytes),
- .data = .{
- .lhs = lhs,
- .rhs = try renderNodeGrouped(c, payload.rhs),
- },
- });
-}
-
-fn renderBinOp(c: *Context, node: Node, tag: std.zig.Ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex {
- const payload = @fieldParentPtr(Payload.BinOp, "base", node.ptr_otherwise).data;
- const lhs = try renderNode(c, payload.lhs);
- return c.addNode(.{
- .tag = tag,
- .main_token = try c.addToken(tok_tag, bytes),
- .data = .{
- .lhs = lhs,
- .rhs = try renderNode(c, payload.rhs),
- },
- });
-}
-
-fn renderStdImport(c: *Context, parts: []const []const u8) !NodeIndex {
- const import_tok = try c.addToken(.builtin, "@import");
- _ = try c.addToken(.l_paren, "(");
- const std_tok = try c.addToken(.string_literal, "\"std\"");
- const std_node = try c.addNode(.{
- .tag = .string_literal,
- .main_token = std_tok,
- .data = undefined,
- });
- _ = try c.addToken(.r_paren, ")");
-
- const import_node = try c.addNode(.{
- .tag = .builtin_call_two,
- .main_token = import_tok,
- .data = .{
- .lhs = std_node,
- .rhs = 0,
- },
- });
-
- var access_chain = import_node;
- for (parts) |part| {
- access_chain = try renderFieldAccess(c, access_chain, part);
- }
- return access_chain;
-}
-
-fn renderCall(c: *Context, lhs: NodeIndex, args: []const Node) !NodeIndex {
- const lparen = try c.addToken(.l_paren, "(");
- const res = switch (args.len) {
- 0 => try c.addNode(.{
- .tag = .call_one,
- .main_token = lparen,
- .data = .{
- .lhs = lhs,
- .rhs = 0,
- },
- }),
- 1 => blk: {
- const arg = try renderNode(c, args[0]);
- break :blk try c.addNode(.{
- .tag = .call_one,
- .main_token = lparen,
- .data = .{
- .lhs = lhs,
- .rhs = arg,
- },
- });
- },
- else => blk: {
- var rendered = try c.gpa.alloc(NodeIndex, args.len);
- defer c.gpa.free(rendered);
-
- for (args, 0..) |arg, i| {
- if (i != 0) _ = try c.addToken(.comma, ",");
- rendered[i] = try renderNode(c, arg);
- }
- const span = try c.listToSpan(rendered);
- break :blk try c.addNode(.{
- .tag = .call,
- .main_token = lparen,
- .data = .{
- .lhs = lhs,
- .rhs = try c.addExtra(NodeSubRange{
- .start = span.start,
- .end = span.end,
- }),
- },
- });
- },
- };
- _ = try c.addToken(.r_paren, ")");
- return res;
-}
-
-fn renderBuiltinCall(c: *Context, builtin: []const u8, args: []const Node) !NodeIndex {
- const builtin_tok = try c.addToken(.builtin, builtin);
- _ = try c.addToken(.l_paren, "(");
- var arg_1: NodeIndex = 0;
- var arg_2: NodeIndex = 0;
- var arg_3: NodeIndex = 0;
- var arg_4: NodeIndex = 0;
- switch (args.len) {
- 0 => {},
- 1 => {
- arg_1 = try renderNode(c, args[0]);
- },
- 2 => {
- arg_1 = try renderNode(c, args[0]);
- _ = try c.addToken(.comma, ",");
- arg_2 = try renderNode(c, args[1]);
- },
- 4 => {
- arg_1 = try renderNode(c, args[0]);
- _ = try c.addToken(.comma, ",");
- arg_2 = try renderNode(c, args[1]);
- _ = try c.addToken(.comma, ",");
- arg_3 = try renderNode(c, args[2]);
- _ = try c.addToken(.comma, ",");
- arg_4 = try renderNode(c, args[3]);
- },
- else => unreachable, // expand this function as needed.
- }
-
- _ = try c.addToken(.r_paren, ")");
- if (args.len <= 2) {
- return c.addNode(.{
- .tag = .builtin_call_two,
- .main_token = builtin_tok,
- .data = .{
- .lhs = arg_1,
- .rhs = arg_2,
- },
- });
- } else {
- std.debug.assert(args.len == 4);
-
- const params = try c.listToSpan(&.{ arg_1, arg_2, arg_3, arg_4 });
- return c.addNode(.{
- .tag = .builtin_call,
- .main_token = builtin_tok,
- .data = .{
- .lhs = params.start,
- .rhs = params.end,
- },
- });
- }
-}
-
-fn renderVar(c: *Context, node: Node) !NodeIndex {
- const payload = node.castTag(.var_decl).?.data;
- if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub");
- if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern");
- if (payload.is_export) _ = try c.addToken(.keyword_export, "export");
- if (payload.is_threadlocal) _ = try c.addToken(.keyword_threadlocal, "threadlocal");
- const mut_tok = if (payload.is_const)
- try c.addToken(.keyword_const, "const")
- else
- try c.addToken(.keyword_var, "var");
- _ = try c.addIdentifier(payload.name);
- _ = try c.addToken(.colon, ":");
- const type_node = try renderNode(c, payload.type);
-
- const align_node = if (payload.alignment) |some| blk: {
- _ = try c.addToken(.keyword_align, "align");
- _ = try c.addToken(.l_paren, "(");
- const res = try c.addNode(.{
- .tag = .number_literal,
- .main_token = try c.addTokenFmt(.number_literal, "{d}", .{some}),
- .data = undefined,
- });
- _ = try c.addToken(.r_paren, ")");
- break :blk res;
- } else 0;
-
- const section_node = if (payload.linksection_string) |some| blk: {
- _ = try c.addToken(.keyword_linksection, "linksection");
- _ = try c.addToken(.l_paren, "(");
- const res = try c.addNode(.{
- .tag = .string_literal,
- .main_token = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(some)}),
- .data = undefined,
- });
- _ = try c.addToken(.r_paren, ")");
- break :blk res;
- } else 0;
-
- const init_node = if (payload.init) |some| blk: {
- _ = try c.addToken(.equal, "=");
- break :blk try renderNode(c, some);
- } else 0;
- _ = try c.addToken(.semicolon, ";");
-
- if (section_node == 0) {
- if (align_node == 0) {
- return c.addNode(.{
- .tag = .simple_var_decl,
- .main_token = mut_tok,
- .data = .{
- .lhs = type_node,
- .rhs = init_node,
- },
- });
- } else {
- return c.addNode(.{
- .tag = .local_var_decl,
- .main_token = mut_tok,
- .data = .{
- .lhs = try c.addExtra(std.zig.Ast.Node.LocalVarDecl{
- .type_node = type_node,
- .align_node = align_node,
- }),
- .rhs = init_node,
- },
- });
- }
- } else {
- return c.addNode(.{
- .tag = .global_var_decl,
- .main_token = mut_tok,
- .data = .{
- .lhs = try c.addExtra(std.zig.Ast.Node.GlobalVarDecl{
- .type_node = type_node,
- .align_node = align_node,
- .section_node = section_node,
- .addrspace_node = 0,
- }),
- .rhs = init_node,
- },
- });
- }
-}
-
-fn renderFunc(c: *Context, node: Node) !NodeIndex {
- const payload = node.castTag(.func).?.data;
- if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub");
- if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern");
- if (payload.is_export) _ = try c.addToken(.keyword_export, "export");
- if (payload.is_inline) _ = try c.addToken(.keyword_inline, "inline");
- const fn_token = try c.addToken(.keyword_fn, "fn");
- if (payload.name) |some| _ = try c.addIdentifier(some);
-
- const params = try renderParams(c, payload.params, payload.is_var_args);
- defer params.deinit();
- var span: NodeSubRange = undefined;
- if (params.items.len > 1) span = try c.listToSpan(params.items);
-
- const align_expr = if (payload.alignment) |some| blk: {
- _ = try c.addToken(.keyword_align, "align");
- _ = try c.addToken(.l_paren, "(");
- const res = try c.addNode(.{
- .tag = .number_literal,
- .main_token = try c.addTokenFmt(.number_literal, "{d}", .{some}),
- .data = undefined,
- });
- _ = try c.addToken(.r_paren, ")");
- break :blk res;
- } else 0;
-
- const section_expr = if (payload.linksection_string) |some| blk: {
- _ = try c.addToken(.keyword_linksection, "linksection");
- _ = try c.addToken(.l_paren, "(");
- const res = try c.addNode(.{
- .tag = .string_literal,
- .main_token = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(some)}),
- .data = undefined,
- });
- _ = try c.addToken(.r_paren, ")");
- break :blk res;
- } else 0;
-
- const callconv_expr = if (payload.explicit_callconv) |some| blk: {
- _ = try c.addToken(.keyword_callconv, "callconv");
- _ = try c.addToken(.l_paren, "(");
- _ = try c.addToken(.period, ".");
- const res = try c.addNode(.{
- .tag = .enum_literal,
- .main_token = try c.addTokenFmt(.identifier, "{s}", .{@tagName(some)}),
- .data = undefined,
- });
- _ = try c.addToken(.r_paren, ")");
- break :blk res;
- } else 0;
-
- const return_type_expr = try renderNode(c, payload.return_type);
-
- const fn_proto = try blk: {
- if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) {
- if (params.items.len < 2)
- break :blk c.addNode(.{
- .tag = .fn_proto_simple,
- .main_token = fn_token,
- .data = .{
- .lhs = params.items[0],
- .rhs = return_type_expr,
- },
- })
- else
- break :blk c.addNode(.{
- .tag = .fn_proto_multi,
- .main_token = fn_token,
- .data = .{
- .lhs = try c.addExtra(NodeSubRange{
- .start = span.start,
- .end = span.end,
- }),
- .rhs = return_type_expr,
- },
- });
- }
- if (params.items.len < 2)
- break :blk c.addNode(.{
- .tag = .fn_proto_one,
- .main_token = fn_token,
- .data = .{
- .lhs = try c.addExtra(std.zig.Ast.Node.FnProtoOne{
- .param = params.items[0],
- .align_expr = align_expr,
- .addrspace_expr = 0, // TODO
- .section_expr = section_expr,
- .callconv_expr = callconv_expr,
- }),
- .rhs = return_type_expr,
- },
- })
- else
- break :blk c.addNode(.{
- .tag = .fn_proto,
- .main_token = fn_token,
- .data = .{
- .lhs = try c.addExtra(std.zig.Ast.Node.FnProto{
- .params_start = span.start,
- .params_end = span.end,
- .align_expr = align_expr,
- .addrspace_expr = 0, // TODO
- .section_expr = section_expr,
- .callconv_expr = callconv_expr,
- }),
- .rhs = return_type_expr,
- },
- });
- };
-
- const payload_body = payload.body orelse {
- if (payload.is_extern) {
- _ = try c.addToken(.semicolon, ";");
- }
- return fn_proto;
- };
- const body = try renderNode(c, payload_body);
- return c.addNode(.{
- .tag = .fn_decl,
- .main_token = fn_token,
- .data = .{
- .lhs = fn_proto,
- .rhs = body,
- },
- });
-}
-
-fn renderMacroFunc(c: *Context, node: Node) !NodeIndex {
- const payload = node.castTag(.pub_inline_fn).?.data;
- _ = try c.addToken(.keyword_pub, "pub");
- _ = try c.addToken(.keyword_inline, "inline");
- const fn_token = try c.addToken(.keyword_fn, "fn");
- _ = try c.addIdentifier(payload.name);
-
- const params = try renderParams(c, payload.params, false);
- defer params.deinit();
- var span: NodeSubRange = undefined;
- if (params.items.len > 1) span = try c.listToSpan(params.items);
-
- const return_type_expr = try renderNodeGrouped(c, payload.return_type);
-
- const fn_proto = blk: {
- if (params.items.len < 2) {
- break :blk try c.addNode(.{
- .tag = .fn_proto_simple,
- .main_token = fn_token,
- .data = .{
- .lhs = params.items[0],
- .rhs = return_type_expr,
- },
- });
- } else {
- break :blk try c.addNode(.{
- .tag = .fn_proto_multi,
- .main_token = fn_token,
- .data = .{
- .lhs = try c.addExtra(std.zig.Ast.Node.SubRange{
- .start = span.start,
- .end = span.end,
- }),
- .rhs = return_type_expr,
- },
- });
- }
- };
- return c.addNode(.{
- .tag = .fn_decl,
- .main_token = fn_token,
- .data = .{
- .lhs = fn_proto,
- .rhs = try renderNode(c, payload.body),
- },
- });
-}
-
-fn renderParams(c: *Context, params: []Payload.Param, is_var_args: bool) !std.ArrayList(NodeIndex) {
- _ = try c.addToken(.l_paren, "(");
- var rendered = try std.ArrayList(NodeIndex).initCapacity(c.gpa, @max(params.len, 1));
- errdefer rendered.deinit();
-
- for (params, 0..) |param, i| {
- if (i != 0) _ = try c.addToken(.comma, ",");
- if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias");
- if (param.name) |some| {
- _ = try c.addIdentifier(some);
- _ = try c.addToken(.colon, ":");
- }
- if (param.type.tag() == .@"anytype") {
- _ = try c.addToken(.keyword_anytype, "anytype");
- continue;
- }
- rendered.appendAssumeCapacity(try renderNode(c, param.type));
- }
- if (is_var_args) {
- if (params.len != 0) _ = try c.addToken(.comma, ",");
- _ = try c.addToken(.ellipsis3, "...");
- }
- _ = try c.addToken(.r_paren, ")");
-
- if (rendered.items.len == 0) rendered.appendAssumeCapacity(0);
- return rendered;
-}
diff --git a/src/translate_c/common.zig b/src/translate_c/common.zig
deleted file mode 100644
index afbb63d05e..0000000000
--- a/src/translate_c/common.zig
+++ /dev/null
@@ -1,322 +0,0 @@
-const std = @import("std");
-const ast = @import("ast.zig");
-const Node = ast.Node;
-const Tag = Node.Tag;
-
-const CallingConvention = std.builtin.CallingConvention;
-
-pub const Error = std.mem.Allocator.Error;
-pub const MacroProcessingError = Error || error{UnexpectedMacroToken};
-pub const TypeError = Error || error{UnsupportedType};
-pub const TransError = TypeError || error{UnsupportedTranslation};
-
-pub const SymbolTable = std.StringArrayHashMap(Node);
-pub const AliasList = std.ArrayList(struct {
- alias: []const u8,
- name: []const u8,
-});
-
-pub const ResultUsed = enum {
- used,
- unused,
-};
-
-pub fn ScopeExtra(comptime Context: type, comptime Type: type) type {
- return struct {
- id: Id,
- parent: ?*Scope,
-
- const Scope = @This();
-
- pub const Id = enum {
- block,
- root,
- condition,
- loop,
- do_loop,
- };
-
- /// Used for the scope of condition expressions, for example `if (cond)`.
- /// The block is lazily initialised because it is only needed for rare
- /// cases of comma operators being used.
- pub const Condition = struct {
- base: Scope,
- block: ?Block = null,
-
- pub fn getBlockScope(self: *Condition, c: *Context) !*Block {
- if (self.block) |*b| return b;
- self.block = try Block.init(c, &self.base, true);
- return &self.block.?;
- }
-
- pub fn deinit(self: *Condition) void {
- if (self.block) |*b| b.deinit();
- }
- };
-
- /// Represents an in-progress Node.Block. This struct is stack-allocated.
- /// When it is deinitialized, it produces an Node.Block which is allocated
- /// into the main arena.
- pub const Block = struct {
- base: Scope,
- statements: std.ArrayList(Node),
- variables: AliasList,
- mangle_count: u32 = 0,
- label: ?[]const u8 = null,
-
- /// By default all variables are discarded, since we do not know in advance if they
- /// will be used. This maps the variable's name to the Discard payload, so that if
- /// the variable is subsequently referenced we can indicate that the discard should
- /// be skipped during the intermediate AST -> Zig AST render step.
- variable_discards: std.StringArrayHashMap(*ast.Payload.Discard),
-
- /// When the block corresponds to a function, keep track of the return type
- /// so that the return expression can be cast, if necessary
- return_type: ?Type = null,
-
- /// C static local variables are wrapped in a block-local struct. The struct
- /// is named after the (mangled) variable name, the Zig variable within the
- /// struct itself is given this name.
- pub const static_inner_name = "static";
-
- pub fn init(c: *Context, parent: *Scope, labeled: bool) !Block {
- var blk = Block{
- .base = .{
- .id = .block,
- .parent = parent,
- },
- .statements = std.ArrayList(Node).init(c.gpa),
- .variables = AliasList.init(c.gpa),
- .variable_discards = std.StringArrayHashMap(*ast.Payload.Discard).init(c.gpa),
- };
- if (labeled) {
- blk.label = try blk.makeMangledName(c, "blk");
- }
- return blk;
- }
-
- pub fn deinit(self: *Block) void {
- self.statements.deinit();
- self.variables.deinit();
- self.variable_discards.deinit();
- self.* = undefined;
- }
-
- pub fn complete(self: *Block, c: *Context) !Node {
- if (self.base.parent.?.id == .do_loop) {
- // We reserve 1 extra statement if the parent is a do_loop. This is in case of
- // do while, we want to put `if (cond) break;` at the end.
- const alloc_len = self.statements.items.len + @intFromBool(self.base.parent.?.id == .do_loop);
- var stmts = try c.arena.alloc(Node, alloc_len);
- stmts.len = self.statements.items.len;
- @memcpy(stmts[0..self.statements.items.len], self.statements.items);
- return Tag.block.create(c.arena, .{
- .label = self.label,
- .stmts = stmts,
- });
- }
- if (self.statements.items.len == 0) return Tag.empty_block.init();
- return Tag.block.create(c.arena, .{
- .label = self.label,
- .stmts = try c.arena.dupe(Node, self.statements.items),
- });
- }
-
- /// Given the desired name, return a name that does not shadow anything from outer scopes.
- /// Inserts the returned name into the scope.
- /// The name will not be visible to callers of getAlias.
- pub fn reserveMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
- return scope.createMangledName(c, name, true);
- }
-
- /// Same as reserveMangledName, but enables the alias immediately.
- pub fn makeMangledName(scope: *Block, c: *Context, name: []const u8) ![]const u8 {
- return scope.createMangledName(c, name, false);
- }
-
- pub fn createMangledName(scope: *Block, c: *Context, name: []const u8, reservation: bool) ![]const u8 {
- const name_copy = try c.arena.dupe(u8, name);
- var proposed_name = name_copy;
- while (scope.contains(proposed_name)) {
- scope.mangle_count += 1;
- proposed_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ name, scope.mangle_count });
- }
- const new_mangle = try scope.variables.addOne();
- if (reservation) {
- new_mangle.* = .{ .name = name_copy, .alias = name_copy };
- } else {
- new_mangle.* = .{ .name = name_copy, .alias = proposed_name };
- }
- return proposed_name;
- }
-
- pub fn getAlias(scope: *Block, name: []const u8) []const u8 {
- for (scope.variables.items) |p| {
- if (std.mem.eql(u8, p.name, name))
- return p.alias;
- }
- return scope.base.parent.?.getAlias(name);
- }
-
- pub fn localContains(scope: *Block, name: []const u8) bool {
- for (scope.variables.items) |p| {
- if (std.mem.eql(u8, p.alias, name))
- return true;
- }
- return false;
- }
-
- pub fn contains(scope: *Block, name: []const u8) bool {
- if (scope.localContains(name))
- return true;
- return scope.base.parent.?.contains(name);
- }
-
- pub fn discardVariable(scope: *Block, c: *Context, name: []const u8) Error!void {
- const name_node = try Tag.identifier.create(c.arena, name);
- const discard = try Tag.discard.create(c.arena, .{ .should_skip = false, .value = name_node });
- try scope.statements.append(discard);
- try scope.variable_discards.putNoClobber(name, discard.castTag(.discard).?);
- }
- };
-
- pub const Root = struct {
- base: Scope,
- sym_table: SymbolTable,
- macro_table: SymbolTable,
- blank_macros: std.StringArrayHashMap(void),
- context: *Context,
- nodes: std.ArrayList(Node),
-
- pub fn init(c: *Context) Root {
- return .{
- .base = .{
- .id = .root,
- .parent = null,
- },
- .sym_table = SymbolTable.init(c.gpa),
- .macro_table = SymbolTable.init(c.gpa),
- .blank_macros = std.StringArrayHashMap(void).init(c.gpa),
- .context = c,
- .nodes = std.ArrayList(Node).init(c.gpa),
- };
- }
-
- pub fn deinit(scope: *Root) void {
- scope.sym_table.deinit();
- scope.macro_table.deinit();
- scope.blank_macros.deinit();
- scope.nodes.deinit();
- }
-
- /// Check if the global scope contains this name, without looking into the "future", e.g.
- /// ignore the preprocessed decl and macro names.
- pub fn containsNow(scope: *Root, name: []const u8) bool {
- return scope.sym_table.contains(name) or scope.macro_table.contains(name);
- }
-
- /// Check if the global scope contains the name, includes all decls that haven't been translated yet.
- pub fn contains(scope: *Root, name: []const u8) bool {
- return scope.containsNow(name) or scope.context.global_names.contains(name) or scope.context.weak_global_names.contains(name);
- }
- };
-
- pub fn findBlockScope(inner: *Scope, c: *Context) !*Scope.Block {
- var scope = inner;
- while (true) {
- switch (scope.id) {
- .root => unreachable,
- .block => return @fieldParentPtr(Block, "base", scope),
- .condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c),
- else => scope = scope.parent.?,
- }
- }
- }
-
- pub fn findBlockReturnType(inner: *Scope) Type {
- var scope = inner;
- while (true) {
- switch (scope.id) {
- .root => unreachable,
- .block => {
- const block = @fieldParentPtr(Block, "base", scope);
- if (block.return_type) |ty| return ty;
- scope = scope.parent.?;
- },
- else => scope = scope.parent.?,
- }
- }
- }
-
- pub fn getAlias(scope: *Scope, name: []const u8) []const u8 {
- return switch (scope.id) {
- .root => return name,
- .block => @fieldParentPtr(Block, "base", scope).getAlias(name),
- .loop, .do_loop, .condition => scope.parent.?.getAlias(name),
- };
- }
-
- pub fn contains(scope: *Scope, name: []const u8) bool {
- return switch (scope.id) {
- .root => @fieldParentPtr(Root, "base", scope).contains(name),
- .block => @fieldParentPtr(Block, "base", scope).contains(name),
- .loop, .do_loop, .condition => scope.parent.?.contains(name),
- };
- }
-
- pub fn getBreakableScope(inner: *Scope) *Scope {
- var scope = inner;
- while (true) {
- switch (scope.id) {
- .root => unreachable,
- .loop, .do_loop => return scope,
- else => scope = scope.parent.?,
- }
- }
- }
-
- /// Appends a node to the first block scope if inside a function, or to the root tree if not.
- pub fn appendNode(inner: *Scope, node: Node) !void {
- var scope = inner;
- while (true) {
- switch (scope.id) {
- .root => {
- const root = @fieldParentPtr(Root, "base", scope);
- return root.nodes.append(node);
- },
- .block => {
- const block = @fieldParentPtr(Block, "base", scope);
- return block.statements.append(node);
- },
- else => scope = scope.parent.?,
- }
- }
- }
-
- pub fn skipVariableDiscard(inner: *Scope, name: []const u8) void {
- if (true) {
- // TODO: due to 'local variable is never mutated' errors, we can
- // only skip discards if a variable is used as an lvalue, which
- // we don't currently have detection for in translate-c.
- // Once #17584 is completed, perhaps we can do away with this
- // logic entirely, and instead rely on render to fixup code.
- return;
- }
- var scope = inner;
- while (true) {
- switch (scope.id) {
- .root => return,
- .block => {
- const block = @fieldParentPtr(Block, "base", scope);
- if (block.variable_discards.get(name)) |discard| {
- discard.data.should_skip = true;
- return;
- }
- },
- else => {},
- }
- scope = scope.parent.?;
- }
- }
- };
-}