diff --git a/BRANCH_TODO b/BRANCH_TODO index 28a611d809..76b4f7e1aa 100644 --- a/BRANCH_TODO +++ b/BRANCH_TODO @@ -65,3 +65,6 @@ * use ZIR memory for decl names where possible and also for keys - this will require more sophisticated changelist detection which does some pre-emptive deletion of decls from the parent namespace + + * better anonymous Decl naming convention + - avoid the global atomic integer for the number because of contention diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 518a971430..4d9350bbdd 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -263,7 +263,7 @@ pub const TypeInfo = union(enum) { /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. - pub const ContainerLayout = enum { + pub const ContainerLayout = enum(u2) { Auto, Extern, Packed, diff --git a/src/AstGen.zig b/src/AstGen.zig index 7d52fd9cf4..f445f3dd26 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -85,6 +85,7 @@ pub fn generate(gpa: *Allocator, tree: ast.Tree) InnerError!Zir { var gen_scope: GenZir = .{ .force_comptime = true, .parent = null, + .anon_name_strategy = .parent, .decl_node_index = 0, .decl_line = 0, .astgen = &astgen, @@ -105,7 +106,7 @@ pub fn generate(gpa: *Allocator, tree: ast.Tree) InnerError!Zir { &gen_scope.base, 0, container_decl, - .struct_decl, + .Auto, )) |struct_decl_ref| { astgen.extra.items[@enumToInt(Zir.ExtraIndex.main_struct)] = @enumToInt(struct_decl_ref); } else |err| switch (err) { @@ -1959,16 +1960,12 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner .union_init_ptr, .field_type, .field_type_ref, - .struct_decl, - .struct_decl_packed, - .struct_decl_extern, - .union_decl, - .union_decl_packed, - .union_decl_extern, - .enum_decl, - .enum_decl_nonexhaustive, .opaque_decl, + .opaque_decl_anon, + .opaque_decl_func, .error_set_decl, + .error_set_decl_anon, + .error_set_decl_func, .int_to_enum, .enum_to_int, .type_info, @@ -2166,7 +2163,7 @@ fn varDecl( const local_val = s.cast(Scope.LocalVal).?; if (local_val.name == ident_name) { return astgen.failTokNotes(name_token, "redeclaration of '{s}'", .{ - @ptrCast([*:0]const u8, astgen.string_bytes.items.ptr) + ident_name, + astgen.nullTerminatedString(ident_name), }, &[_]u32{ try astgen.errNoteTok( local_val.token_src, @@ -2181,7 +2178,7 @@ fn varDecl( const local_ptr = s.cast(Scope.LocalPtr).?; if (local_ptr.name == ident_name) { return astgen.failTokNotes(name_token, "redeclaration of '{s}'", .{ - @ptrCast([*:0]const u8, astgen.string_bytes.items.ptr) + ident_name, + astgen.nullTerminatedString(ident_name), }, &[_]u32{ try astgen.errNoteTok( local_ptr.token_src, @@ -2941,12 +2938,16 @@ fn globalVarDecl( // of the top level declaration. const block_inst = try gz.addBlock(.block_inline, node); + const name_token = var_decl.ast.mut_token + 1; + const name_str_index = try astgen.identAsString(name_token); + var block_scope: GenZir = .{ .parent = scope, .decl_node_index = node, .decl_line = gz.calcLine(node), .astgen = astgen, .force_comptime = true, + .anon_name_strategy = .parent, }; defer block_scope.instructions.deinit(gpa); @@ -3043,9 +3044,6 @@ fn globalVarDecl( _ = try block_scope.addBreak(.break_inline, block_inst, var_inst); try block_scope.setBlockBody(block_inst); - const name_token = var_decl.ast.mut_token + 1; - const name_str_index = try astgen.identAsString(name_token); - try wip_decls.payload.ensureUnusedCapacity(gpa, 9); { const contents_hash = std.zig.hashSrc(tree.getNodeSource(node)); @@ -3258,14 +3256,18 @@ fn structDeclInner( scope: *Scope, node: ast.Node.Index, container_decl: ast.full.ContainerDecl, - tag: Zir.Inst.Tag, + layout: std.builtin.TypeInfo.ContainerLayout, ) InnerError!Zir.Inst.Ref { if (container_decl.ast.members.len == 0) { - return gz.addPlNode(tag, node, Zir.Inst.StructDecl{ + const decl_inst = try gz.reserveInstructionIndex(); + try gz.setStruct(decl_inst, .{ + .src_node = node, + .layout = layout, .fields_len = 0, .body_len = 0, .decls_len = 0, }); + return gz.indexToRef(decl_inst); } const astgen = gz.astgen; @@ -3437,23 +3439,24 @@ fn structDeclInner( } } - const decl_inst = try gz.addBlock(tag, node); - try gz.instructions.append(gpa, decl_inst); + const decl_inst = try gz.reserveInstructionIndex(); if (block_scope.instructions.items.len != 0) { _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); } - try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.StructDecl).Struct.fields.len + - bit_bag.items.len + @boolToInt(field_index != 0) + fields_data.items.len + - block_scope.instructions.items.len + - wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) + - wip_decls.payload.items.len); - const zir_datas = astgen.instructions.items(.data); - zir_datas[decl_inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.StructDecl{ + try gz.setStruct(decl_inst, .{ + .src_node = node, + .layout = layout, .body_len = @intCast(u32, block_scope.instructions.items.len), .fields_len = @intCast(u32, field_index), .decls_len = @intCast(u32, wip_decls.decl_index), }); + + try astgen.extra.ensureUnusedCapacity(gpa, bit_bag.items.len + + @boolToInt(field_index != 0) + fields_data.items.len + + block_scope.instructions.items.len + + wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) + + wip_decls.payload.items.len); astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty. if (wip_decls.decl_index != 0) { astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag); @@ -3476,7 +3479,7 @@ fn unionDeclInner( scope: *Scope, node: ast.Node.Index, members: []const ast.Node.Index, - tag: Zir.Inst.Tag, + layout: std.builtin.TypeInfo.ContainerLayout, arg_inst: Zir.Inst.Ref, have_auto_enum: bool, ) InnerError!Zir.Inst.Ref { @@ -3611,11 +3614,12 @@ fn unionDeclInner( const have_type = member.ast.type_expr != 0; const have_align = member.ast.align_expr != 0; const have_value = member.ast.value_expr != 0; + const unused = false; cur_bit_bag = (cur_bit_bag >> bits_per_field) | (@as(u32, @boolToInt(have_type)) << 28) | (@as(u32, @boolToInt(have_align)) << 29) | (@as(u32, @boolToInt(have_value)) << 30) | - (@as(u32, @boolToInt(have_auto_enum)) << 31); + (@as(u32, @boolToInt(unused)) << 31); if (have_type) { const field_type = try typeExpr(&block_scope, &block_scope.base, member.ast.type_expr); @@ -3662,24 +3666,26 @@ fn unionDeclInner( } } - const decl_inst = try gz.addBlock(tag, node); - try gz.instructions.append(gpa, decl_inst); + const decl_inst = try gz.reserveInstructionIndex(); if (block_scope.instructions.items.len != 0) { _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); } - try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.UnionDecl).Struct.fields.len + - bit_bag.items.len + 1 + fields_data.items.len + - block_scope.instructions.items.len + - wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) + - wip_decls.payload.items.len); - const zir_datas = astgen.instructions.items(.data); - zir_datas[decl_inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.UnionDecl{ + try gz.setUnion(decl_inst, .{ + .src_node = node, + .layout = layout, .tag_type = arg_inst, .body_len = @intCast(u32, block_scope.instructions.items.len), .fields_len = @intCast(u32, field_index), .decls_len = @intCast(u32, wip_decls.decl_index), + .auto_enum_tag = have_auto_enum, }); + + try astgen.extra.ensureUnusedCapacity(gpa, bit_bag.items.len + + 1 + fields_data.items.len + + block_scope.instructions.items.len + + wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) + + wip_decls.payload.items.len); astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty. if (wip_decls.decl_index != 0) { astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag); @@ -3719,29 +3725,27 @@ fn containerDecl( switch (token_tags[container_decl.ast.main_token]) { .keyword_struct => { - const tag = if (container_decl.layout_token) |t| switch (token_tags[t]) { - .keyword_packed => Zir.Inst.Tag.struct_decl_packed, - .keyword_extern => Zir.Inst.Tag.struct_decl_extern, + const layout = if (container_decl.layout_token) |t| switch (token_tags[t]) { + .keyword_packed => std.builtin.TypeInfo.ContainerLayout.Packed, + .keyword_extern => std.builtin.TypeInfo.ContainerLayout.Extern, else => unreachable, - } else Zir.Inst.Tag.struct_decl; + } else std.builtin.TypeInfo.ContainerLayout.Auto; assert(arg_inst == .none); - const result = try structDeclInner(gz, scope, node, container_decl, tag); + const result = try structDeclInner(gz, scope, node, container_decl, layout); return rvalue(gz, scope, rl, result, node); }, .keyword_union => { - const tag = if (container_decl.layout_token) |t| switch (token_tags[t]) { - .keyword_packed => Zir.Inst.Tag.union_decl_packed, - .keyword_extern => Zir.Inst.Tag.union_decl_extern, + const layout = if (container_decl.layout_token) |t| switch (token_tags[t]) { + .keyword_packed => std.builtin.TypeInfo.ContainerLayout.Packed, + .keyword_extern => std.builtin.TypeInfo.ContainerLayout.Extern, else => unreachable, - } else Zir.Inst.Tag.union_decl; + } else std.builtin.TypeInfo.ContainerLayout.Auto; - // See `Zir.Inst.UnionDecl` doc comments for why this is stored along - // with fields instead of separately. const have_auto_enum = container_decl.ast.enum_token != null; - const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, tag, arg_inst, have_auto_enum); + const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, arg_inst, have_auto_enum); return rvalue(gz, scope, rl, result, node); }, .keyword_enum => { @@ -3832,10 +3836,7 @@ fn containerDecl( } // In this case we must generate ZIR code for the tag values, similar to // how structs are handled above. - const tag: Zir.Inst.Tag = if (counts.nonexhaustive_node == 0) - .enum_decl - else - .enum_decl_nonexhaustive; + const nonexhaustive = counts.nonexhaustive_node != 0; // The enum_decl instruction introduces a scope in which the decls of the enum // are in scope, so that tag values can refer to decls within the enum itself. @@ -3995,24 +3996,25 @@ fn containerDecl( } } - const decl_inst = try gz.addBlock(tag, node); - try gz.instructions.append(gpa, decl_inst); + const decl_inst = try gz.reserveInstructionIndex(); if (block_scope.instructions.items.len != 0) { _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); } - try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.EnumDecl).Struct.fields.len + - bit_bag.items.len + 1 + fields_data.items.len + - block_scope.instructions.items.len + - wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) + - wip_decls.payload.items.len); - const zir_datas = astgen.instructions.items(.data); - zir_datas[decl_inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.EnumDecl{ + try gz.setEnum(decl_inst, .{ + .src_node = node, + .nonexhaustive = nonexhaustive, .tag_type = arg_inst, .body_len = @intCast(u32, block_scope.instructions.items.len), .fields_len = @intCast(u32, field_index), .decls_len = @intCast(u32, wip_decls.decl_index), }); + + try astgen.extra.ensureUnusedCapacity(gpa, bit_bag.items.len + + 1 + fields_data.items.len + + block_scope.instructions.items.len + + wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) + + wip_decls.payload.items.len); astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty. if (wip_decls.decl_index != 0) { astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag); @@ -4118,7 +4120,12 @@ fn containerDecl( wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * WipDecls.bits_per_field); } } - const decl_inst = try gz.addBlock(.opaque_decl, node); + const tag: Zir.Inst.Tag = switch (gz.anon_name_strategy) { + .parent => .opaque_decl, + .anon => .opaque_decl_anon, + .func => .opaque_decl_func, + }; + const decl_inst = try gz.addBlock(tag, node); try gz.instructions.append(gpa, decl_inst); try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.OpaqueDecl).Struct.fields.len + @@ -4173,6 +4180,11 @@ fn errorSetDecl( } } + const tag: Zir.Inst.Tag = switch (gz.anon_name_strategy) { + .parent => .error_set_decl, + .anon => .error_set_decl_anon, + .func => .error_set_decl_func, + }; const result = try gz.addPlNode(.error_set_decl, node, Zir.Inst.ErrorSetDecl{ .fields_len = @intCast(u32, field_names.items.len), }); @@ -5907,7 +5919,6 @@ fn charLiteral(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) { error.InvalidCharacter => { const bad_byte = slice[bad_index]; - const token_starts = tree.tokens.items(.start); return astgen.failOff( main_token, @intCast(u32, bad_index), @@ -7779,6 +7790,8 @@ const GenZir = struct { const base_tag: Scope.Tag = .gen_zir; base: Scope = Scope{ .tag = base_tag }, force_comptime: bool, + /// How decls created in this scope should be named. + anon_name_strategy: Zir.Inst.NameStrategy = .anon, /// The end of special indexes. `Zir.Inst.Ref` subtracts against this number to convert /// to `Zir.Inst.Index`. The default here is correct if there are 0 parameters. ref_start_index: u32 = Zir.Inst.Ref.typed_value_map.len, @@ -8623,18 +8636,176 @@ const GenZir = struct { return new_index; } + fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct { + src_node: ast.Node.Index, + body_len: u32, + fields_len: u32, + decls_len: u32, + layout: std.builtin.TypeInfo.ContainerLayout, + }) !void { + const astgen = gz.astgen; + const gpa = astgen.gpa; + + try astgen.extra.ensureUnusedCapacity(gpa, 4); + const payload_index = @intCast(u32, astgen.extra.items.len); + + if (args.src_node != 0) { + const node_offset = gz.nodeIndexToRelative(args.src_node); + astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset)); + } + if (args.body_len != 0) { + astgen.extra.appendAssumeCapacity(args.body_len); + } + if (args.fields_len != 0) { + astgen.extra.appendAssumeCapacity(args.fields_len); + } + if (args.decls_len != 0) { + astgen.extra.appendAssumeCapacity(args.decls_len); + } + astgen.instructions.set(inst, .{ + .tag = .extended, + .data = .{ .extended = .{ + .opcode = .struct_decl, + .small = @bitCast(u16, Zir.Inst.StructDecl.Small{ + .has_src_node = args.src_node != 0, + .has_body_len = args.body_len != 0, + .has_fields_len = args.fields_len != 0, + .has_decls_len = args.decls_len != 0, + .name_strategy = gz.anon_name_strategy, + .layout = args.layout, + }), + .operand = payload_index, + } }, + }); + } + + fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct { + src_node: ast.Node.Index, + tag_type: Zir.Inst.Ref, + body_len: u32, + fields_len: u32, + decls_len: u32, + layout: std.builtin.TypeInfo.ContainerLayout, + auto_enum_tag: bool, + }) !void { + const astgen = gz.astgen; + const gpa = astgen.gpa; + + try astgen.extra.ensureUnusedCapacity(gpa, 5); + const payload_index = @intCast(u32, astgen.extra.items.len); + + if (args.src_node != 0) { + const node_offset = gz.nodeIndexToRelative(args.src_node); + astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset)); + } + if (args.tag_type != .none) { + astgen.extra.appendAssumeCapacity(@enumToInt(args.tag_type)); + } + if (args.body_len != 0) { + astgen.extra.appendAssumeCapacity(args.body_len); + } + if (args.fields_len != 0) { + astgen.extra.appendAssumeCapacity(args.fields_len); + } + if (args.decls_len != 0) { + astgen.extra.appendAssumeCapacity(args.decls_len); + } + astgen.instructions.set(inst, .{ + .tag = .extended, + .data = .{ .extended = .{ + .opcode = .union_decl, + .small = @bitCast(u16, Zir.Inst.UnionDecl.Small{ + .has_src_node = args.src_node != 0, + .has_tag_type = args.tag_type != .none, + .has_body_len = args.body_len != 0, + .has_fields_len = args.fields_len != 0, + .has_decls_len = args.decls_len != 0, + .name_strategy = gz.anon_name_strategy, + .layout = args.layout, + .auto_enum_tag = args.auto_enum_tag, + }), + .operand = payload_index, + } }, + }); + } + + fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct { + src_node: ast.Node.Index, + tag_type: Zir.Inst.Ref, + body_len: u32, + fields_len: u32, + decls_len: u32, + nonexhaustive: bool, + }) !void { + const astgen = gz.astgen; + const gpa = astgen.gpa; + + try astgen.extra.ensureUnusedCapacity(gpa, 5); + const payload_index = @intCast(u32, astgen.extra.items.len); + + if (args.src_node != 0) { + const node_offset = gz.nodeIndexToRelative(args.src_node); + astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset)); + } + if (args.tag_type != .none) { + astgen.extra.appendAssumeCapacity(@enumToInt(args.tag_type)); + } + if (args.body_len != 0) { + astgen.extra.appendAssumeCapacity(args.body_len); + } + if (args.fields_len != 0) { + astgen.extra.appendAssumeCapacity(args.fields_len); + } + if (args.decls_len != 0) { + astgen.extra.appendAssumeCapacity(args.decls_len); + } + astgen.instructions.set(inst, .{ + .tag = .extended, + .data = .{ .extended = .{ + .opcode = .enum_decl, + .small = @bitCast(u16, Zir.Inst.EnumDecl.Small{ + .has_src_node = args.src_node != 0, + .has_tag_type = args.tag_type != .none, + .has_body_len = args.body_len != 0, + .has_fields_len = args.fields_len != 0, + .has_decls_len = args.decls_len != 0, + .name_strategy = gz.anon_name_strategy, + .nonexhaustive = args.nonexhaustive, + }), + .operand = payload_index, + } }, + }); + } + fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref { return gz.indexToRef(try gz.addAsIndex(inst)); } fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index { const gpa = gz.astgen.gpa; - try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1); - try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1); + try gz.instructions.ensureUnusedCapacity(gpa, 1); + try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); gz.astgen.instructions.appendAssumeCapacity(inst); gz.instructions.appendAssumeCapacity(new_index); return new_index; } + + fn reserveInstructionIndex(gz: *GenZir) !Zir.Inst.Index { + const gpa = gz.astgen.gpa; + try gz.instructions.ensureUnusedCapacity(gpa, 1); + try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); + + const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); + gz.astgen.instructions.len += 1; + gz.instructions.appendAssumeCapacity(new_index); + return new_index; + } }; + +/// This can only be for short-lived references; the memory becomes invalidated +/// when another string is added. +fn nullTerminatedString(astgen: AstGen, index: usize) [*:0]const u8 { + return @ptrCast([*:0]const u8, astgen.string_bytes.items.ptr) + index; +} diff --git a/src/Module.zig b/src/Module.zig index d7124041b0..434e74ca2f 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -75,6 +75,8 @@ failed_files: std.AutoArrayHashMapUnmanaged(*Scope.File, ?*ErrorMsg) = .{}, /// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator. failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{}, +next_anon_name_index: usize = 0, + /// Candidates for deletion. After a semantic analysis update completes, this list /// contains Decls that need to be deleted if they end up having no references to them. deletion_set: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, @@ -884,8 +886,11 @@ pub const Scope = struct { /// Declaration order is preserved via entry order. /// Key memory is owned by `decl.name`. /// TODO save memory with https://github.com/ziglang/zig/issues/8619. + /// Anonymous decls are not stored here; they are kept in `anon_decls` instead. decls: std.StringArrayHashMapUnmanaged(*Decl) = .{}, + anon_decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, + pub fn deinit(ns: *Namespace, mod: *Module) void { ns.clearDecls(mod); ns.* = undefined; @@ -899,15 +904,27 @@ pub const Scope = struct { var decls = ns.decls; ns.decls = .{}; + var anon_decls = ns.anon_decls; + ns.anon_decls = .{}; + for (decls.items()) |entry| { entry.value.destroy(mod); } decls.deinit(gpa); + + for (anon_decls.items()) |entry| { + entry.key.destroy(mod); + } + anon_decls.deinit(gpa); } pub fn removeDecl(ns: *Namespace, child: *Decl) void { - // Preserve declaration order. - _ = ns.decls.orderedRemove(mem.spanZ(child.name)); + if (child.zir_decl_index == 0) { + _ = ns.anon_decls.swapRemove(child); + } else { + // Preserve declaration order. + _ = ns.decls.orderedRemove(mem.spanZ(child.name)); + } } // This renders e.g. "std.fs.Dir.OpenOptions" @@ -2607,10 +2624,14 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void { } if (decl.getInnerNamespace()) |namespace| { - for (namespace.decls.items()) |*entry| { + for (namespace.decls.items()) |entry| { const sub_decl = entry.value; try decl_stack.append(gpa, sub_decl); } + for (namespace.anon_decls.items()) |entry| { + const sub_decl = entry.key; + try decl_stack.append(gpa, sub_decl); + } } } } @@ -3741,31 +3762,17 @@ pub fn constIntBig(mod: *Module, arena: *Allocator, src: LazySrcLoc, ty: Type, b pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) !*Decl { const scope_decl = scope.ownerDecl().?; const namespace = scope_decl.namespace; - try namespace.decls.ensureUnusedCapacity(mod.gpa, 1); + try namespace.anon_decls.ensureUnusedCapacity(mod.gpa, 1); - // Find a unique name for the anon decl. - var name_buf = std.ArrayList(u8).init(mod.gpa); - defer name_buf.deinit(); + const name_index = mod.getNextAnonNameIndex(); + const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{ + scope_decl.name, name_index, + }); + errdefer mod.gpa.free(name); - try name_buf.appendSlice(mem.spanZ(scope_decl.name)); - var name_index: usize = namespace.decls.count(); - - const new_decl = while (true) { - const gop = namespace.decls.getOrPutAssumeCapacity(name_buf.items); - if (!gop.found_existing) { - const name = try name_buf.toOwnedSliceSentinel(0); - const new_decl = try mod.allocateNewDecl(namespace, scope_decl.src_node); - new_decl.name = name; - gop.entry.key = name; - gop.entry.value = new_decl; - break gop.entry.value; - } - - name_buf.clearRetainingCapacity(); - try name_buf.writer().print("{s}__anon_{d}", .{ scope_decl.name, name_index }); - name_index += 1; - } else unreachable; // TODO should not need else unreachable on while(true) + const new_decl = try mod.allocateNewDecl(namespace, scope_decl.src_node); + new_decl.name = name; new_decl.src_line = scope_decl.src_line; new_decl.ty = typed_value.ty; new_decl.val = typed_value.val; @@ -3773,6 +3780,8 @@ pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) new_decl.analysis = .complete; new_decl.generation = mod.generation; + namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {}); + // TODO: This generates the Decl into the machine code file if it is of a // type that is non-zero size. We should be able to further improve the // compiler to omit Decls which are only referenced at compile-time and not runtime. @@ -3784,6 +3793,10 @@ pub fn createAnonymousDecl(mod: *Module, scope: *Scope, typed_value: TypedValue) return new_decl; } +fn getNextAnonNameIndex(mod: *Module) usize { + return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic); +} + /// This looks up a bare identifier in the given scope. This will walk up the tree of namespaces /// in scope and check each one for the identifier. /// TODO emit a compile error if more than one decl would be matched. @@ -4394,18 +4407,38 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) InnerError!void { const gpa = mod.gpa; const zir = struct_obj.owner_decl.namespace.file_scope.zir; - const inst_data = zir.instructions.items(.data)[struct_obj.zir_index].pl_node; - const src = inst_data.src(); - const extra = zir.extraData(Zir.Inst.StructDecl, inst_data.payload_index); - const fields_len = extra.data.fields_len; - const decls_len = extra.data.decls_len; + const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended; + assert(extended.opcode == .struct_decl); + const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); + var extra_index: usize = extended.operand; + + const src: LazySrcLoc = .{ .node_offset = struct_obj.node_offset }; + extra_index += @boolToInt(small.has_src_node); + + const body_len = if (small.has_body_len) blk: { + const body_len = zir.extra[extra_index]; + extra_index += 1; + break :blk body_len; + } else 0; + + const fields_len = if (small.has_fields_len) blk: { + const fields_len = zir.extra[extra_index]; + extra_index += 1; + break :blk fields_len; + } else 0; + + const decls_len = if (small.has_decls_len) decls_len: { + const decls_len = zir.extra[extra_index]; + extra_index += 1; + break :decls_len decls_len; + } else 0; // Skip over decls. - var decls_it = zir.declIterator(struct_obj.zir_index); + var decls_it = zir.declIteratorInner(extra_index, decls_len); while (decls_it.next()) |_| {} - var extra_index = decls_it.extra_index; + extra_index = decls_it.extra_index; - const body = zir.extra[extra_index..][0..extra.data.body_len]; + const body = zir.extra[extra_index..][0..body_len]; if (fields_len == 0) { assert(body.len == 0); return; @@ -4525,18 +4558,44 @@ pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) InnerError!void { const gpa = mod.gpa; const zir = union_obj.owner_decl.namespace.file_scope.zir; - const inst_data = zir.instructions.items(.data)[union_obj.zir_index].pl_node; - const src = inst_data.src(); - const extra = zir.extraData(Zir.Inst.UnionDecl, inst_data.payload_index); - const fields_len = extra.data.fields_len; - const decls_len = extra.data.decls_len; + const extended = zir.instructions.items(.data)[union_obj.zir_index].extended; + assert(extended.opcode == .union_decl); + const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small); + var extra_index: usize = extended.operand; + + const src: LazySrcLoc = .{ .node_offset = union_obj.node_offset }; + extra_index += @boolToInt(small.has_src_node); + + const tag_type_ref = if (small.has_tag_type) blk: { + const tag_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); + extra_index += 1; + break :blk tag_type_ref; + } else .none; + + const body_len = if (small.has_body_len) blk: { + const body_len = zir.extra[extra_index]; + extra_index += 1; + break :blk body_len; + } else 0; + + const fields_len = if (small.has_fields_len) blk: { + const fields_len = zir.extra[extra_index]; + extra_index += 1; + break :blk fields_len; + } else 0; + + const decls_len = if (small.has_decls_len) decls_len: { + const decls_len = zir.extra[extra_index]; + extra_index += 1; + break :decls_len decls_len; + } else 0; // Skip over decls. - var decls_it = zir.declIterator(union_obj.zir_index); + var decls_it = zir.declIteratorInner(extra_index, decls_len); while (decls_it.next()) |_| {} - var extra_index = decls_it.extra_index; + extra_index = decls_it.extra_index; - const body = zir.extra[extra_index..][0..extra.data.body_len]; + const body = zir.extra[extra_index..][0..body_len]; if (fields_len == 0) { assert(body.len == 0); return; @@ -4580,8 +4639,6 @@ pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) InnerError!void { _ = try sema.analyzeBody(&block, body); } - var auto_enum_tag: ?bool = null; - const bits_per_field = 4; const fields_per_u32 = 32 / bits_per_field; const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; @@ -4603,10 +4660,6 @@ pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) InnerError!void { const unused = @truncate(u1, cur_bit_bag) != 0; cur_bit_bag >>= 1; - if (auto_enum_tag == null) { - auto_enum_tag = unused; - } - const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]); extra_index += 1; @@ -4653,7 +4706,7 @@ pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) InnerError!void { } } - // TODO resolve the union tag type + // TODO resolve the union tag_type_ref } /// Called from `performAllTheWork`, after all AstGen workers have finished, diff --git a/src/Sema.zig b/src/Sema.zig index 918b8b5e2a..07fb4b4cd3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -350,16 +350,12 @@ pub fn analyzeBody( .trunc => try sema.zirUnaryMath(block, inst), .round => try sema.zirUnaryMath(block, inst), - .struct_decl => try sema.zirStructDecl(block, inst, .Auto), - .struct_decl_packed => try sema.zirStructDecl(block, inst, .Packed), - .struct_decl_extern => try sema.zirStructDecl(block, inst, .Extern), - .enum_decl => try sema.zirEnumDecl(block, inst, false), - .enum_decl_nonexhaustive => try sema.zirEnumDecl(block, inst, true), - .union_decl => try sema.zirUnionDecl(block, inst, .Auto), - .union_decl_packed => try sema.zirUnionDecl(block, inst, .Packed), - .union_decl_extern => try sema.zirUnionDecl(block, inst, .Extern), - .opaque_decl => try sema.zirOpaqueDecl(block, inst), - .error_set_decl => try sema.zirErrorSetDecl(block, inst), + .opaque_decl => try sema.zirOpaqueDecl(block, inst, .parent), + .opaque_decl_anon => try sema.zirOpaqueDecl(block, inst, .anon), + .opaque_decl_func => try sema.zirOpaqueDecl(block, inst, .func), + .error_set_decl => try sema.zirErrorSetDecl(block, inst, .parent), + .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon), + .error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func), .add => try sema.zirArithmetic(block, inst), .addwrap => try sema.zirArithmetic(block, inst), @@ -515,6 +511,9 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro // zig fmt: off .func => return sema.zirFuncExtended( block, extended, inst), .variable => return sema.zirVarExtended( block, extended), + .struct_decl => return sema.zirStructDecl( block, extended, inst), + .enum_decl => return sema.zirEnumDecl( block, extended), + .union_decl => return sema.zirUnionDecl( block, extended, inst), .ret_ptr => return sema.zirRetPtr( block, extended), .ret_type => return sema.zirRetType( block, extended), .this => return sema.zirThis( block, extended), @@ -686,21 +685,34 @@ pub fn analyzeStructDecl( inst: Zir.Inst.Index, struct_obj: *Module.Struct, ) InnerError!void { - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const extra = sema.code.extraData(Zir.Inst.StructDecl, inst_data.payload_index); - const decls_len = extra.data.decls_len; + const extended = sema.code.instructions.items(.data)[inst].extended; + assert(extended.opcode == .struct_decl); + const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); - _ = try sema.mod.scanNamespace(&struct_obj.namespace, extra.end, decls_len, new_decl); + var extra_index: usize = extended.operand; + extra_index += @boolToInt(small.has_src_node); + extra_index += @boolToInt(small.has_body_len); + extra_index += @boolToInt(small.has_fields_len); + const decls_len = if (small.has_decls_len) blk: { + const decls_len = sema.code.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + + _ = try sema.mod.scanNamespace(&struct_obj.namespace, extra_index, decls_len, new_decl); } fn zirStructDecl( sema: *Sema, block: *Scope.Block, + extended: Zir.Inst.Extended.InstData, inst: Zir.Inst.Index, - layout: std.builtin.TypeInfo.ContainerLayout, ) InnerError!*Inst { - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); + const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); + const src: LazySrcLoc = if (small.has_src_node) blk: { + const node_offset = @bitCast(i32, sema.code.extra[extended.operand]); + break :blk .{ .node_offset = node_offset }; + } else sema.src; var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); @@ -714,9 +726,9 @@ fn zirStructDecl( struct_obj.* = .{ .owner_decl = new_decl, .fields = .{}, - .node_offset = inst_data.src_node, + .node_offset = src.node_offset, .zir_index = inst, - .layout = layout, + .layout = small.layout, .status = .none, .namespace = .{ .parent = sema.owner_decl.namespace, @@ -735,27 +747,53 @@ fn zirStructDecl( fn zirEnumDecl( sema: *Sema, block: *Scope.Block, - inst: Zir.Inst.Index, - nonexhaustive: bool, + extended: Zir.Inst.Extended.InstData, ) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); const gpa = sema.gpa; - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - const extra = sema.code.extraData(Zir.Inst.EnumDecl, inst_data.payload_index); - const fields_len = extra.data.fields_len; - const decls_len = extra.data.decls_len; + const small = @bitCast(Zir.Inst.EnumDecl.Small, extended.small); + var extra_index: usize = extended.operand; + + const src: LazySrcLoc = if (small.has_src_node) blk: { + const node_offset = @bitCast(i32, sema.code.extra[extra_index]); + extra_index += 1; + break :blk .{ .node_offset = node_offset }; + } else sema.src; + + const tag_type_ref = if (small.has_tag_type) blk: { + const tag_type_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); + extra_index += 1; + break :blk tag_type_ref; + } else .none; + + const body_len = if (small.has_body_len) blk: { + const body_len = sema.code.extra[extra_index]; + extra_index += 1; + break :blk body_len; + } else 0; + + const fields_len = if (small.has_fields_len) blk: { + const fields_len = sema.code.extra[extra_index]; + extra_index += 1; + break :blk fields_len; + } else 0; + + const decls_len = if (small.has_decls_len) blk: { + const decls_len = sema.code.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; var new_decl_arena = std.heap.ArenaAllocator.init(gpa); const tag_ty = blk: { - if (extra.data.tag_type != .none) { + if (tag_type_ref != .none) { // TODO better source location // TODO (needs AstGen fix too) move this eval to the block so it gets allocated // in the new decl arena. - break :blk try sema.resolveType(block, src, extra.data.tag_type); + break :blk try sema.resolveType(block, src, tag_type_ref); } const bits = std.math.log2_int_ceil(usize, fields_len); break :blk try Type.Tag.int_unsigned.create(&new_decl_arena.allocator, bits); @@ -764,7 +802,7 @@ fn zirEnumDecl( const enum_obj = try new_decl_arena.allocator.create(Module.EnumFull); const enum_ty_payload = try new_decl_arena.allocator.create(Type.Payload.EnumFull); enum_ty_payload.* = .{ - .base = .{ .tag = if (nonexhaustive) .enum_nonexhaustive else .enum_full }, + .base = .{ .tag = if (small.nonexhaustive) .enum_nonexhaustive else .enum_full }, .data = enum_obj, }; const enum_ty = Type.initPayload(&enum_ty_payload.base); @@ -778,7 +816,7 @@ fn zirEnumDecl( .tag_ty = tag_ty, .fields = .{}, .values = .{}, - .node_offset = inst_data.src_node, + .node_offset = src.node_offset, .namespace = .{ .parent = sema.owner_decl.namespace, .ty = enum_ty, @@ -789,14 +827,9 @@ fn zirEnumDecl( &enum_obj.namespace, new_decl, new_decl.name, }); - var extra_index: usize = try sema.mod.scanNamespace( - &enum_obj.namespace, - extra.end, - decls_len, - new_decl, - ); + extra_index = try sema.mod.scanNamespace(&enum_obj.namespace, extra_index, decls_len, new_decl); - const body = sema.code.extra[extra_index..][0..extra.data.body_len]; + const body = sema.code.extra[extra_index..][0..body_len]; if (fields_len == 0) { assert(body.len == 0); try new_decl.finalizeNewArena(&new_decl_arena); @@ -894,16 +927,30 @@ fn zirEnumDecl( fn zirUnionDecl( sema: *Sema, block: *Scope.Block, + extended: Zir.Inst.Extended.InstData, inst: Zir.Inst.Index, - layout: std.builtin.TypeInfo.ContainerLayout, ) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - const extra = sema.code.extraData(Zir.Inst.UnionDecl, inst_data.payload_index); - const decls_len = extra.data.decls_len; + const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small); + var extra_index: usize = extended.operand; + + const src: LazySrcLoc = if (small.has_src_node) blk: { + const node_offset = @bitCast(i32, sema.code.extra[extra_index]); + extra_index += 1; + break :blk .{ .node_offset = node_offset }; + } else sema.src; + + extra_index += @boolToInt(small.has_tag_type); + extra_index += @boolToInt(small.has_body_len); + extra_index += @boolToInt(small.has_fields_len); + + const decls_len = if (small.has_decls_len) blk: { + const decls_len = sema.code.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); @@ -918,9 +965,9 @@ fn zirUnionDecl( .owner_decl = new_decl, .tag_ty = Type.initTag(.@"null"), .fields = .{}, - .node_offset = inst_data.src_node, + .node_offset = src.node_offset, .zir_index = inst, - .layout = layout, + .layout = small.layout, .status = .none, .namespace = .{ .parent = sema.owner_decl.namespace, @@ -932,13 +979,18 @@ fn zirUnionDecl( &union_obj.namespace, new_decl, new_decl.name, }); - _ = try sema.mod.scanNamespace(&union_obj.namespace, extra.end, decls_len, new_decl); + _ = try sema.mod.scanNamespace(&union_obj.namespace, extra_index, decls_len, new_decl); try new_decl.finalizeNewArena(&new_decl_arena); return sema.analyzeDeclVal(block, src, new_decl); } -fn zirOpaqueDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { +fn zirOpaqueDecl( + sema: *Sema, + block: *Scope.Block, + inst: Zir.Inst.Index, + name_strategy: Zir.Inst.NameStrategy, +) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -949,7 +1001,12 @@ fn zirOpaqueDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr return sema.mod.fail(&block.base, sema.src, "TODO implement zirOpaqueDecl", .{}); } -fn zirErrorSetDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { +fn zirErrorSetDecl( + sema: *Sema, + block: *Scope.Block, + inst: Zir.Inst.Index, + name_strategy: Zir.Inst.NameStrategy, +) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/Zir.zig b/src/Zir.zig index 88a4ddc269..274098fa28 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -296,34 +296,16 @@ pub const Inst = struct { /// only the taken branch is analyzed. The then block and else block must /// terminate with an "inline" variant of a noreturn instruction. condbr_inline, - /// A struct type definition. Contains references to ZIR instructions for - /// the field types, defaults, and alignments. - /// Uses the `pl_node` union field. Payload is `StructDecl`. - struct_decl, - /// Same as `struct_decl`, except has the `packed` layout. - struct_decl_packed, - /// Same as `struct_decl`, except has the `extern` layout. - struct_decl_extern, - /// A union type definition. Contains references to ZIR instructions for - /// the field types and optional type tag expression. - /// Uses the `pl_node` union field. Payload is `UnionDecl`. - union_decl, - /// Same as `union_decl`, except has the `packed` layout. - union_decl_packed, - /// Same as `union_decl`, except has the `extern` layout. - union_decl_extern, - /// An enum type definition. Contains references to ZIR instructions for - /// the field value expressions and optional type tag expression. - /// Uses the `pl_node` union field. Payload is `EnumDecl`. - enum_decl, - /// Same as `enum_decl`, except the enum is non-exhaustive. - enum_decl_nonexhaustive, /// An opaque type definition. Provides an AST node only. /// Uses the `pl_node` union field. Payload is `OpaqueDecl`. opaque_decl, + opaque_decl_anon, + opaque_decl_func, /// An error set type definition. Contains a list of field names. /// Uses the `pl_node` union field. Payload is `ErrorSetDecl`. error_set_decl, + error_set_decl_anon, + error_set_decl_func, /// Declares the beginning of a statement. Used for debug info. /// Uses the `dbg_stmt` union field. The line and column are offset /// from the parent declaration. @@ -1011,16 +993,12 @@ pub const Inst = struct { .cmp_gt, .cmp_neq, .coerce_result_ptr, - .struct_decl, - .struct_decl_packed, - .struct_decl_extern, - .union_decl, - .union_decl_packed, - .union_decl_extern, - .enum_decl, - .enum_decl_nonexhaustive, .opaque_decl, + .opaque_decl_anon, + .opaque_decl_func, .error_set_decl, + .error_set_decl_anon, + .error_set_decl_func, .dbg_stmt, .decl_ref, .decl_val, @@ -1271,16 +1249,12 @@ pub const Inst = struct { .coerce_result_ptr = .bin, .condbr = .pl_node, .condbr_inline = .pl_node, - .struct_decl = .pl_node, - .struct_decl_packed = .pl_node, - .struct_decl_extern = .pl_node, - .union_decl = .pl_node, - .union_decl_packed = .pl_node, - .union_decl_extern = .pl_node, - .enum_decl = .pl_node, - .enum_decl_nonexhaustive = .pl_node, .opaque_decl = .pl_node, + .opaque_decl_anon = .pl_node, + .opaque_decl_func = .pl_node, .error_set_decl = .pl_node, + .error_set_decl_anon = .pl_node, + .error_set_decl_func = .pl_node, .dbg_stmt = .dbg_stmt, .decl_ref = .str_tok, .decl_val = .str_tok, @@ -1507,6 +1481,21 @@ pub const Inst = struct { /// `operand` is payload index to `ExtendedVar`. /// `small` is `ExtendedVar.Small`. variable, + /// A struct type definition. Contains references to ZIR instructions for + /// the field types, defaults, and alignments. + /// `operand` is payload index to `StructDecl`. + /// `small` is `StructDecl.Small`. + struct_decl, + /// An enum type definition. Contains references to ZIR instructions for + /// the field value expressions and optional type tag expression. + /// `operand` is payload index to `EnumDecl`. + /// `small` is `EnumDecl.Small`. + enum_decl, + /// A union type definition. Contains references to ZIR instructions for + /// the field types and optional type tag expression. + /// `operand` is payload index to `UnionDecl`. + /// `small` is `UnionDecl.Small`. + union_decl, /// Obtains a pointer to the return value. /// `operand` is `src_node: i32`. ret_ptr, @@ -2251,9 +2240,9 @@ pub const Inst = struct { body_len: u32, pub const SrcLocs = struct { - /// Absolute line number in the source file. + /// Absolute line index in the source file. lbrace_line: u32, - /// Absolute line number in the source file. + /// Absolute line index in the source file. rbrace_line: u32, /// lbrace_column is least significant bits u16 /// rbrace_column is most significant bits u16 @@ -2414,13 +2403,17 @@ pub const Inst = struct { }; /// Trailing: - /// 0. decl_bits: u32 // for every 8 decls + /// 0. src_node: i32, // if has_src_node + /// 1. body_len: u32, // if has_body_len + /// 2. fields_len: u32, // if has_fields_len + /// 3. decls_len: u32, // if has_decls_len + /// 4. decl_bits: u32 // for every 8 decls /// - sets of 4 bits: /// 0b000X: whether corresponding decl is pub /// 0b00X0: whether corresponding decl is exported /// 0b0X00: whether corresponding decl has an align expression /// 0bX000: whether corresponding decl has a linksection expression - /// 1. decl: { // for every decls_len + /// 5. decl: { // for every decls_len /// src_hash: [4]u32, // hash of source bytes /// line: u32, // line number of decl, relative to parent /// name: u32, // null terminated string index @@ -2433,14 +2426,14 @@ pub const Inst = struct { /// align: Ref, // if corresponding bit is set /// link_section: Ref, // if corresponding bit is set /// } - /// 2. inst: Index // for every body_len - /// 3. flags: u32 // for every 8 fields + /// 6. inst: Index // for every body_len + /// 7. flags: u32 // for every 8 fields /// - sets of 4 bits: /// 0b000X: whether corresponding field has an align expression /// 0b00X0: whether corresponding field has a default expression /// 0b0X00: whether corresponding field is comptime /// 0bX000: unused - /// 4. fields: { // for every fields_len + /// 8. fields: { // for every fields_len /// field_name: u32, /// field_type: Ref, /// - if none, means `anytype`. @@ -2448,19 +2441,42 @@ pub const Inst = struct { /// default_value: Ref, // if corresponding bit is set /// } pub const StructDecl = struct { - body_len: u32, - fields_len: u32, - decls_len: u32, + pub const Small = packed struct { + has_src_node: bool, + has_body_len: bool, + has_fields_len: bool, + has_decls_len: bool, + name_strategy: NameStrategy, + layout: std.builtin.TypeInfo.ContainerLayout, + _: u8 = undefined, + }; + }; + + pub const NameStrategy = enum(u2) { + /// Use the same name as the parent declaration name. + /// e.g. `const Foo = struct {...};`. + parent, + /// Use the name of the currently executing comptime function call, + /// with the current parameters. e.g. `ArrayList(i32)`. + func, + /// Create an anonymous name for this declaration. + /// Like this: "ParentDeclName_struct_69" + anon, }; /// Trailing: - /// 0. decl_bits: u32 // for every 8 decls + /// 0. src_node: i32, // if has_src_node + /// 1. tag_type: Ref, // if has_tag_type + /// 2. body_len: u32, // if has_body_len + /// 3. fields_len: u32, // if has_fields_len + /// 4. decls_len: u32, // if has_decls_len + /// 5. decl_bits: u32 // for every 8 decls /// - sets of 4 bits: /// 0b000X: whether corresponding decl is pub /// 0b00X0: whether corresponding decl is exported /// 0b0X00: whether corresponding decl has an align expression /// 0bX000: whether corresponding decl has a linksection expression - /// 1. decl: { // for every decls_len + /// 6. decl: { // for every decls_len /// src_hash: [4]u32, // hash of source bytes /// line: u32, // line number of decl, relative to parent /// name: u32, // null terminated string index @@ -2473,29 +2489,39 @@ pub const Inst = struct { /// align: Ref, // if corresponding bit is set /// link_section: Ref, // if corresponding bit is set /// } - /// 2. inst: Index // for every body_len - /// 3. has_bits: u32 // for every 32 fields + /// 7. inst: Index // for every body_len + /// 8. has_bits: u32 // for every 32 fields /// - the bit is whether corresponding field has an value expression - /// 4. fields: { // for every fields_len + /// 9. fields: { // for every fields_len /// field_name: u32, /// value: Ref, // if corresponding bit is set /// } pub const EnumDecl = struct { - /// Can be `Ref.none`. - tag_type: Ref, - body_len: u32, - fields_len: u32, - decls_len: u32, + pub const Small = packed struct { + has_src_node: bool, + has_tag_type: bool, + has_body_len: bool, + has_fields_len: bool, + has_decls_len: bool, + name_strategy: NameStrategy, + nonexhaustive: bool, + _: u8 = undefined, + }; }; /// Trailing: - /// 0. decl_bits: u32 // for every 8 decls + /// 0. src_node: i32, // if has_src_node + /// 1. tag_type: Ref, // if has_tag_type + /// 2. body_len: u32, // if has_body_len + /// 3. fields_len: u32, // if has_fields_len + /// 4. decls_len: u32, // if has_decls_len + /// 5. decl_bits: u32 // for every 8 decls /// - sets of 4 bits: /// 0b000X: whether corresponding decl is pub /// 0b00X0: whether corresponding decl is exported /// 0b0X00: whether corresponding decl has an align expression /// 0bX000: whether corresponding decl has a linksection expression - /// 1. decl: { // for every decls_len + /// 6. decl: { // for every decls_len /// src_hash: [4]u32, // hash of source bytes /// line: u32, // line number of decl, relative to parent /// name: u32, // null terminated string index @@ -2508,29 +2534,33 @@ pub const Inst = struct { /// align: Ref, // if corresponding bit is set /// link_section: Ref, // if corresponding bit is set /// } - /// 2. inst: Index // for every body_len - /// 3. has_bits: u32 // for every 8 fields + /// 7. inst: Index // for every body_len + /// 8. has_bits: u32 // for every 8 fields /// - sets of 4 bits: /// 0b000X: whether corresponding field has a type expression /// 0b00X0: whether corresponding field has a align expression /// 0b0X00: whether corresponding field has a tag value expression - /// 0bX000: unused(*) - /// * the first unused bit (the unused bit of the first field) is used - /// to indicate whether auto enum tag is enabled. - /// 0 = union(tag_type) - /// 1 = union(enum(tag_type)) - /// 4. fields: { // for every fields_len + /// 0bX000: unused + /// 9. fields: { // for every fields_len /// field_name: u32, // null terminated string index /// field_type: Ref, // if corresponding bit is set /// align: Ref, // if corresponding bit is set /// tag_value: Ref, // if corresponding bit is set /// } pub const UnionDecl = struct { - /// Can be `Ref.none`. - tag_type: Ref, - body_len: u32, - fields_len: u32, - decls_len: u32, + pub const Small = packed struct { + has_src_node: bool, + has_tag_type: bool, + has_body_len: bool, + has_fields_len: bool, + has_decls_len: bool, + name_strategy: NameStrategy, + layout: std.builtin.TypeInfo.ContainerLayout, + /// false: union(tag_type) + /// true: union(enum(tag_type)) + auto_enum_tag: bool, + _: u6 = undefined, + }; }; /// Trailing: @@ -2901,8 +2931,6 @@ const Writer = struct { .field_type => try self.writeFieldType(stream, inst), .field_type_ref => try self.writeFieldTypeRef(stream, inst), - .error_set_decl => try self.writePlNodeErrorSetDecl(stream, inst), - .add, .addwrap, .array_cat, @@ -2980,21 +3008,13 @@ const Writer = struct { .condbr_inline, => try self.writePlNodeCondBr(stream, inst), - .struct_decl, - .struct_decl_packed, - .struct_decl_extern, - => try self.writeStructDecl(stream, inst), + .opaque_decl => try self.writeOpaqueDecl(stream, inst, .parent), + .opaque_decl_anon => try self.writeOpaqueDecl(stream, inst, .anon), + .opaque_decl_func => try self.writeOpaqueDecl(stream, inst, .func), - .union_decl, - .union_decl_packed, - .union_decl_extern, - => try self.writeUnionDecl(stream, inst), - - .enum_decl, - .enum_decl_nonexhaustive, - => try self.writeEnumDecl(stream, inst), - - .opaque_decl => try self.writeOpaqueDecl(stream, inst), + .error_set_decl => try self.writeErrorSetDecl(stream, inst, .parent), + .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon), + .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func), .switch_block => try self.writePlNodeSwitchBr(stream, inst, .none), .switch_block_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"), @@ -3080,6 +3100,10 @@ const Writer = struct { .shl_with_overflow, => try self.writeOverflowArithmetic(stream, extended), + .struct_decl => try self.writeStructDecl(stream, extended), + .union_decl => try self.writeUnionDecl(stream, extended), + .enum_decl => try self.writeEnumDecl(stream, extended), + .alloc, .builtin_extern, .c_undef, @@ -3315,25 +3339,6 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } - fn writePlNodeErrorSetDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Inst.ErrorSetDecl, inst_data.payload_index); - const fields = self.code.extra[extra.end..][0..extra.data.fields_len]; - - try stream.writeAll("{\n"); - self.indent += 2; - for (fields) |str_index| { - const name = self.code.nullTerminatedString(str_index); - try stream.writeByteNTimes(' ', self.indent); - try stream.print("{},\n", .{std.zig.fmtId(name)}); - } - self.indent -= 2; - try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("}) "); - - try self.writeSrc(stream, inst_data.src()); - } - fn writeNodeMultiOp(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void { const extra = self.code.extraData(Inst.NodeMultiOp, extended.operand); const src: LazySrcLoc = .{ .node_offset = extra.data.src_node }; @@ -3483,33 +3488,56 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } - fn writeStructDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Inst.StructDecl, inst_data.payload_index); - const fields_len = extra.data.fields_len; - const decls_len = extra.data.decls_len; + fn writeStructDecl(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void { + const small = @bitCast(Inst.StructDecl.Small, extended.small); - var extra_index: usize = undefined; + var extra_index: usize = extended.operand; + + const src_node: ?i32 = if (small.has_src_node) blk: { + const src_node = @bitCast(i32, self.code.extra[extra_index]); + extra_index += 1; + break :blk src_node; + } else null; + + const body_len = if (small.has_body_len) blk: { + const body_len = self.code.extra[extra_index]; + extra_index += 1; + break :blk body_len; + } else 0; + + const fields_len = if (small.has_fields_len) blk: { + const fields_len = self.code.extra[extra_index]; + extra_index += 1; + break :blk fields_len; + } else 0; + + const decls_len = if (small.has_decls_len) blk: { + const decls_len = self.code.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + + try stream.print("{s}, {s}, ", .{ + @tagName(small.name_strategy), @tagName(small.layout), + }); if (decls_len == 0) { try stream.writeAll("{}, "); - extra_index = extra.end; } else { try stream.writeAll("{\n"); self.indent += 2; - extra_index = try self.writeDecls(stream, decls_len, extra.end); + extra_index = try self.writeDecls(stream, decls_len, extra_index); self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("}, "); } - const body = self.code.extra[extra_index..][0..extra.data.body_len]; + const body = self.code.extra[extra_index..][0..body_len]; extra_index += body.len; if (fields_len == 0) { assert(body.len == 0); - try stream.writeAll("{}, {}) "); - extra_index = extra.end; + try stream.writeAll("{}, {})"); } else { self.indent += 2; if (body.len == 0) { @@ -3575,41 +3603,70 @@ const Writer = struct { self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("}) "); + try stream.writeAll("})"); } - try self.writeSrc(stream, inst_data.src()); + try self.writeSrcNode(stream, src_node); } - fn writeUnionDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Inst.UnionDecl, inst_data.payload_index); - const fields_len = extra.data.fields_len; - const decls_len = extra.data.decls_len; - const tag_type_ref = extra.data.tag_type; + fn writeUnionDecl(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void { + const small = @bitCast(Inst.UnionDecl.Small, extended.small); - var extra_index: usize = undefined; + var extra_index: usize = extended.operand; + + const src_node: ?i32 = if (small.has_src_node) blk: { + const src_node = @bitCast(i32, self.code.extra[extra_index]); + extra_index += 1; + break :blk src_node; + } else null; + + const tag_type_ref = if (small.has_tag_type) blk: { + const tag_type_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + break :blk tag_type_ref; + } else .none; + + const body_len = if (small.has_body_len) blk: { + const body_len = self.code.extra[extra_index]; + extra_index += 1; + break :blk body_len; + } else 0; + + const fields_len = if (small.has_fields_len) blk: { + const fields_len = self.code.extra[extra_index]; + extra_index += 1; + break :blk fields_len; + } else 0; + + const decls_len = if (small.has_decls_len) blk: { + const decls_len = self.code.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + + try stream.print("{s}, {s}, ", .{ + @tagName(small.name_strategy), @tagName(small.layout), + }); + try self.writeFlag(stream, "autoenum, ", small.auto_enum_tag); if (decls_len == 0) { try stream.writeAll("{}, "); - extra_index = extra.end; } else { try stream.writeAll("{\n"); self.indent += 2; - extra_index = try self.writeDecls(stream, decls_len, extra.end); + extra_index = try self.writeDecls(stream, decls_len, extra_index); self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("}, "); } assert(fields_len != 0); - var first_has_auto_enum: ?bool = null; if (tag_type_ref != .none) { try self.writeInstRef(stream, tag_type_ref); try stream.writeAll(", "); } - const body = self.code.extra[extra_index..][0..extra.data.body_len]; + const body = self.code.extra[extra_index..][0..body_len]; extra_index += body.len; self.indent += 2; @@ -3642,12 +3699,10 @@ const Writer = struct { cur_bit_bag >>= 1; const has_value = @truncate(u1, cur_bit_bag) != 0; cur_bit_bag >>= 1; - const has_auto_enum = @truncate(u1, cur_bit_bag) != 0; + const unused = @truncate(u1, cur_bit_bag) != 0; cur_bit_bag >>= 1; - if (first_has_auto_enum == null) { - first_has_auto_enum = has_auto_enum; - } + _ = unused; const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]); extra_index += 1; @@ -3681,10 +3736,8 @@ const Writer = struct { self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("}"); - try self.writeFlag(stream, ", autoenum", first_has_auto_enum.?); - try stream.writeAll(") "); - try self.writeSrc(stream, inst_data.src()); + try stream.writeAll("})"); + try self.writeSrcNode(stream, src_node); } fn writeDecls(self: *Writer, stream: anytype, decls_len: u32, extra_start: usize) !usize { @@ -3776,22 +3829,49 @@ const Writer = struct { return extra_index; } - fn writeEnumDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[inst].pl_node; - const extra = self.code.extraData(Inst.EnumDecl, inst_data.payload_index); - const fields_len = extra.data.fields_len; - const decls_len = extra.data.decls_len; - const tag_type_ref = extra.data.tag_type; + fn writeEnumDecl(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void { + const small = @bitCast(Inst.EnumDecl.Small, extended.small); + var extra_index: usize = extended.operand; - var extra_index: usize = undefined; + const src_node: ?i32 = if (small.has_src_node) blk: { + const src_node = @bitCast(i32, self.code.extra[extra_index]); + extra_index += 1; + break :blk src_node; + } else null; + + const tag_type_ref = if (small.has_tag_type) blk: { + const tag_type_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); + extra_index += 1; + break :blk tag_type_ref; + } else .none; + + const body_len = if (small.has_body_len) blk: { + const body_len = self.code.extra[extra_index]; + extra_index += 1; + break :blk body_len; + } else 0; + + const fields_len = if (small.has_fields_len) blk: { + const fields_len = self.code.extra[extra_index]; + extra_index += 1; + break :blk fields_len; + } else 0; + + const decls_len = if (small.has_decls_len) blk: { + const decls_len = self.code.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + + try stream.print("{s}, ", .{@tagName(small.name_strategy)}); + try self.writeFlag(stream, "nonexhaustive, ", small.nonexhaustive); if (decls_len == 0) { try stream.writeAll("{}, "); - extra_index = extra.end; } else { try stream.writeAll("{\n"); self.indent += 2; - extra_index = try self.writeDecls(stream, decls_len, extra.end); + extra_index = try self.writeDecls(stream, decls_len, extra_index); self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); try stream.writeAll("}, "); @@ -3802,12 +3882,12 @@ const Writer = struct { try stream.writeAll(", "); } - const body = self.code.extra[extra_index..][0..extra.data.body_len]; + const body = self.code.extra[extra_index..][0..body_len]; extra_index += body.len; if (fields_len == 0) { assert(body.len == 0); - try stream.writeAll("{}, {}) "); + try stream.writeAll("{}, {})"); } else { self.indent += 2; if (body.len == 0) { @@ -3851,16 +3931,23 @@ const Writer = struct { } self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("}) "); + try stream.writeAll("})"); } - try self.writeSrc(stream, inst_data.src()); + try self.writeSrcNode(stream, src_node); } - fn writeOpaqueDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void { + fn writeOpaqueDecl( + self: *Writer, + stream: anytype, + inst: Inst.Index, + name_strategy: Inst.NameStrategy, + ) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Inst.OpaqueDecl, inst_data.payload_index); const decls_len = extra.data.decls_len; + try stream.print("{s}, ", .{@tagName(name_strategy)}); + if (decls_len == 0) { try stream.writeAll("}) "); } else { @@ -3874,6 +3961,32 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } + fn writeErrorSetDecl( + self: *Writer, + stream: anytype, + inst: Inst.Index, + name_strategy: Inst.NameStrategy, + ) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Inst.ErrorSetDecl, inst_data.payload_index); + const fields = self.code.extra[extra.end..][0..extra.data.fields_len]; + + try stream.print("{s}, ", .{@tagName(name_strategy)}); + + try stream.writeAll("{\n"); + self.indent += 2; + for (fields) |str_index| { + const name = self.code.nullTerminatedString(str_index); + try stream.writeByteNTimes(' ', self.indent); + try stream.print("{},\n", .{std.zig.fmtId(name)}); + } + self.indent -= 2; + try stream.writeByteNTimes(' ', self.indent); + try stream.writeAll("}) "); + + try self.writeSrc(stream, inst_data.src()); + } + fn writePlNodeSwitchBr( self: *Writer, stream: anytype, @@ -4337,6 +4450,13 @@ const Writer = struct { }); } + fn writeSrcNode(self: *Writer, stream: anytype, src_node: ?i32) !void { + const node_offset = src_node orelse return; + const src: LazySrcLoc = .{ .node_offset = node_offset }; + try stream.writeAll(" "); + return self.writeSrc(stream, src); + } + fn writeBody(self: *Writer, stream: anytype, body: []const Inst.Index) !void { for (body) |inst| { try stream.writeByteNTimes(' ', self.indent); @@ -4389,75 +4509,86 @@ pub const DeclIterator = struct { pub fn declIterator(zir: Zir, decl_inst: u32) DeclIterator { const tags = zir.instructions.items(.tag); const datas = zir.instructions.items(.data); - const decl_info: struct { - extra_index: usize, - decls_len: u32, - } = switch (tags[decl_inst]) { - .struct_decl, - .struct_decl_packed, - .struct_decl_extern, - => blk: { - const inst_data = datas[decl_inst].pl_node; - const extra = zir.extraData(Inst.StructDecl, inst_data.payload_index); - break :blk .{ - .extra_index = extra.end, - .decls_len = extra.data.decls_len, - }; - }, - - .union_decl, - .union_decl_packed, - .union_decl_extern, - => blk: { - const inst_data = datas[decl_inst].pl_node; - const extra = zir.extraData(Inst.UnionDecl, inst_data.payload_index); - break :blk .{ - .extra_index = extra.end, - .decls_len = extra.data.decls_len, - }; - }, - - .enum_decl, - .enum_decl_nonexhaustive, - => blk: { - const inst_data = datas[decl_inst].pl_node; - const extra = zir.extraData(Inst.EnumDecl, inst_data.payload_index); - break :blk .{ - .extra_index = extra.end, - .decls_len = extra.data.decls_len, - }; - }, - - .opaque_decl => blk: { + switch (tags[decl_inst]) { + .opaque_decl, + .opaque_decl_anon, + .opaque_decl_func, + => { const inst_data = datas[decl_inst].pl_node; const extra = zir.extraData(Inst.OpaqueDecl, inst_data.payload_index); - break :blk .{ - .extra_index = extra.end, - .decls_len = extra.data.decls_len, - }; + return declIteratorInner(zir, extra.end, extra.data.decls_len); }, // Functions are allowed and yield no iterations. + // There is one case matching this in the extended instruction set below. .func, .func_inferred, - .extended, // assume also a function - => .{ - .extra_index = 0, - .decls_len = 0, + => return declIteratorInner(zir, 0, 0), + + .extended => { + const extended = datas[decl_inst].extended; + switch (extended.opcode) { + .func => return declIteratorInner(zir, 0, 0), + .struct_decl => { + const small = @bitCast(Inst.StructDecl.Small, extended.small); + var extra_index: usize = extended.operand; + extra_index += @boolToInt(small.has_src_node); + extra_index += @boolToInt(small.has_body_len); + extra_index += @boolToInt(small.has_fields_len); + const decls_len = if (small.has_decls_len) decls_len: { + const decls_len = zir.extra[extra_index]; + extra_index += 1; + break :decls_len decls_len; + } else 0; + + return declIteratorInner(zir, extra_index, decls_len); + }, + .enum_decl => { + const small = @bitCast(Inst.EnumDecl.Small, extended.small); + var extra_index: usize = extended.operand; + extra_index += @boolToInt(small.has_src_node); + extra_index += @boolToInt(small.has_tag_type); + extra_index += @boolToInt(small.has_body_len); + extra_index += @boolToInt(small.has_fields_len); + const decls_len = if (small.has_decls_len) decls_len: { + const decls_len = zir.extra[extra_index]; + extra_index += 1; + break :decls_len decls_len; + } else 0; + + return declIteratorInner(zir, extra_index, decls_len); + }, + .union_decl => { + const small = @bitCast(Inst.UnionDecl.Small, extended.small); + var extra_index: usize = extended.operand; + extra_index += @boolToInt(small.has_src_node); + extra_index += @boolToInt(small.has_tag_type); + extra_index += @boolToInt(small.has_body_len); + extra_index += @boolToInt(small.has_fields_len); + const decls_len = if (small.has_decls_len) decls_len: { + const decls_len = zir.extra[extra_index]; + extra_index += 1; + break :decls_len decls_len; + } else 0; + + return declIteratorInner(zir, extra_index, decls_len); + }, + else => unreachable, + } }, - else => unreachable, - }; - - const bit_bags_count = std.math.divCeil(usize, decl_info.decls_len, 8) catch unreachable; + } +} +pub fn declIteratorInner(zir: Zir, extra_index: usize, decls_len: u32) DeclIterator { + const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable; return .{ .zir = zir, - .extra_index = decl_info.extra_index + bit_bags_count, - .bit_bag_index = decl_info.extra_index, + .extra_index = extra_index + bit_bags_count, + .bit_bag_index = extra_index, .cur_bit_bag = undefined, .decl_i = 0, - .decls_len = decl_info.decls_len, + .decls_len = decls_len, }; } @@ -4480,15 +4611,10 @@ fn findDeclsInner( switch (tags[inst]) { // Decl instructions are interesting but have no body. - .struct_decl, - .struct_decl_packed, - .struct_decl_extern, - .union_decl, - .union_decl_packed, - .union_decl_extern, - .enum_decl, - .enum_decl_nonexhaustive, + // TODO yes they do have a body actually. recurse over them just like block instructions. .opaque_decl, + .opaque_decl_anon, + .opaque_decl_func, => return list.append(inst), // Functions instructions are interesting and have a body. @@ -4505,19 +4631,28 @@ fn findDeclsInner( }, .extended => { const extended = datas[inst].extended; - if (extended.opcode != .func) return; + switch (extended.opcode) { + .func => { + try list.append(inst); - try list.append(inst); + const extra = zir.extraData(Inst.ExtendedFunc, extended.operand); + const small = @bitCast(Inst.ExtendedFunc.Small, extended.small); + var extra_index: usize = extra.end; + extra_index += @boolToInt(small.has_lib_name); + extra_index += @boolToInt(small.has_cc); + extra_index += @boolToInt(small.has_align); + extra_index += extra.data.param_types_len; + const body = zir.extra[extra_index..][0..extra.data.body_len]; + return zir.findDeclsBody(list, body); + }, - const extra = zir.extraData(Inst.ExtendedFunc, extended.operand); - const small = @bitCast(Inst.ExtendedFunc.Small, extended.small); - var extra_index: usize = extra.end; - extra_index += @boolToInt(small.has_lib_name); - extra_index += @boolToInt(small.has_cc); - extra_index += @boolToInt(small.has_align); - extra_index += extra.data.param_types_len; - const body = zir.extra[extra_index..][0..extra.data.body_len]; - return zir.findDeclsBody(list, body); + .struct_decl, + .union_decl, + .enum_decl, + => return list.append(inst), + + else => return, + } }, // Block instructions, recurse over the bodies.