diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2024-02-27 22:06:11 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2024-02-28 13:21:05 -0700 |
| commit | 240d0b68f62c4fd3562649ec7fc29a51be3698d7 (patch) | |
| tree | e0c2a736cff252ccc9c7722754efa686cd1ba315 /src | |
| parent | 9410b11ca663231e367e3adfd668979c4b870a41 (diff) | |
| download | zig-240d0b68f62c4fd3562649ec7fc29a51be3698d7.tar.gz zig-240d0b68f62c4fd3562649ec7fc29a51be3698d7.zip | |
make aro-based translate-c lazily built from source
Part of #19063.
Primarily, this moves Aro from deps/ to lib/compiler/ so that it can be
lazily compiled from source. src/aro_translate_c.zig is moved to
lib/compiler/aro_translate_c.zig and some of Zig CLI logic moved to a
main() function there.
aro_translate_c.zig becomes the "common" import for clang-based
translate-c.
Not all of the compiler was able to be detangled from Aro, however, so
it still, for now, remains being compiled with the main compiler
sources due to the clang-based translate-c depending on it. Once
aro-based translate-c achieves feature parity with the clang-based
translate-c implementation, the clang-based one can be removed from Zig.
Aro made it unnecessarily difficult to depend on with these .def files
and all these Zig module requirements. I looked at the .def files and
made these observations:
- The canonical source is llvm .def files.
- Therefore there is an update process to sync with llvm that involves
regenerating the .def files in Aro.
- Therefore you might as well just regenerate the .zig files directly
and check those into Aro.
- Also with a small amount of tinkering, the file size on disk of these
generated .zig files can be made many times smaller, without
compromising type safety in the usage of the data.
This would make things much easier on Zig as downstream project,
particularly we could remove those pesky stubs when bootstrapping.
I have gone ahead with these changes since they unblock me and I will
have a chat with Vexu to see what he thinks.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Compilation.zig | 2 | ||||
| -rw-r--r-- | src/aro_translate_c.zig | 678 | ||||
| -rw-r--r-- | src/main.zig | 121 | ||||
| -rw-r--r-- | src/stubs/aro_builtins.zig | 35 | ||||
| -rw-r--r-- | src/stubs/aro_messages.zig | 508 | ||||
| -rw-r--r-- | src/stubs/aro_names.zig | 10 | ||||
| -rw-r--r-- | src/stubs/aro_options.zig | 1 | ||||
| -rw-r--r-- | src/translate_c.zig | 293 | ||||
| -rw-r--r-- | src/translate_c/ast.zig | 2942 | ||||
| -rw-r--r-- | src/translate_c/common.zig | 322 |
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.?; - } - } - }; -} |
