diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2024-02-28 18:21:30 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-28 18:21:30 -0800 |
| commit | f5aad472873a37eba2f15d0ece9d1e813fa93d06 (patch) | |
| tree | e66a3364a6ce5c380ce1c0cbcdf4de9b5ca656ba /src | |
| parent | 69df00f657c567339d0d39c50381eaec4fab32a4 (diff) | |
| parent | 49437d34e65271a9e9211043b46c27e9c8196a6a (diff) | |
| download | zig-f5aad472873a37eba2f15d0ece9d1e813fa93d06.tar.gz zig-f5aad472873a37eba2f15d0ece9d1e813fa93d06.zip | |
Merge pull request #19122 from ziglang/lazy-aro
make aro-based translate-c lazily built from source
Diffstat (limited to 'src')
| -rw-r--r-- | src/Compilation.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.?; - } - } - }; -} |
