Commit ae845a33c0
Changed files (8)
src/AstGen.zig
@@ -86,6 +86,7 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void {
Zir.Inst.Ref,
Zir.Inst.Index,
+ Zir.Inst.Declaration.Name,
Zir.NullTerminatedString,
=> @intFromEnum(@field(extra, field.name)),
@@ -95,6 +96,7 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void {
Zir.Inst.SwitchBlock.Bits,
Zir.Inst.SwitchBlockErrUnion.Bits,
Zir.Inst.FuncFancy.Bits,
+ Zir.Inst.Declaration.Flags,
=> @bitCast(@field(extra, field.name)),
else => @compileError("bad field type"),
@@ -132,8 +134,8 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
};
defer astgen.deinit(gpa);
- // String table indexes 0, 1, 2 are reserved for special meaning.
- try astgen.string_bytes.appendSlice(gpa, &[_]u8{ 0, 0, 0 });
+ // String table index 0 is reserved for `NullTerminatedString.empty`.
+ try astgen.string_bytes.append(gpa, 0);
// We expect at least as many ZIR instructions and extra data items
// as AST nodes.
@@ -355,8 +357,13 @@ const ResultInfo = struct {
};
};
+/// TODO: modify Sema to remove in favour of `coerced_align_ri`
const align_ri: ResultInfo = .{ .rl = .{ .ty = .u29_type } };
const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } };
+/// TODO: modify Sema to remove in favour of `coerced_addrspace_ri`
+const addrspace_ri: ResultInfo = .{ .rl = .{ .ty = .address_space_type } };
+const coerced_addrspace_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .address_space_type } };
+const coerced_linksection_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .slice_const_u8_type } };
const bool_ri: ResultInfo = .{ .rl = .{ .ty = .bool_type } };
const type_ri: ResultInfo = .{ .rl = .{ .ty = .type_type } };
const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } };
@@ -2592,6 +2599,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.block,
.block_comptime,
.block_inline,
+ .declaration,
.suspend_block,
.loop,
.bool_br_and,
@@ -3783,7 +3791,7 @@ fn ptrType(
gz.astgen.source_line = source_line;
gz.astgen.source_column = source_column;
- addrspace_ref = try expr(gz, scope, .{ .rl = .{ .ty = .address_space_type } }, ptr_info.ast.addrspace_node);
+ addrspace_ref = try expr(gz, scope, addrspace_ri, ptr_info.ast.addrspace_node);
trailing_count += 1;
}
if (ptr_info.ast.align_node != 0) {
@@ -3899,8 +3907,6 @@ fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.
const WipMembers = struct {
payload: *ArrayListUnmanaged(u32),
payload_top: usize,
- decls_start: u32,
- decls_end: u32,
field_bits_start: u32,
fields_start: u32,
fields_end: u32,
@@ -3908,43 +3914,27 @@ const WipMembers = struct {
field_index: u32 = 0,
const Self = @This();
- /// struct, union, enum, and opaque decls all use same 4 bits per decl
- const bits_per_decl = 4;
- const decls_per_u32 = 32 / bits_per_decl;
- /// struct, union, enum, and opaque decls all have maximum size of 11 u32 slots
- /// (4 for src_hash + line + name + value + doc_comment + align + link_section + address_space )
- const max_decl_size = 11;
fn init(gpa: Allocator, payload: *ArrayListUnmanaged(u32), decl_count: u32, field_count: u32, comptime bits_per_field: u32, comptime max_field_size: u32) Allocator.Error!Self {
const payload_top: u32 = @intCast(payload.items.len);
- const decls_start = payload_top + (decl_count + decls_per_u32 - 1) / decls_per_u32;
- const field_bits_start = decls_start + decl_count * max_decl_size;
+ const field_bits_start = payload_top + decl_count;
const fields_start = field_bits_start + if (bits_per_field > 0) blk: {
const fields_per_u32 = 32 / bits_per_field;
break :blk (field_count + fields_per_u32 - 1) / fields_per_u32;
} else 0;
const payload_end = fields_start + field_count * max_field_size;
try payload.resize(gpa, payload_end);
- return Self{
+ return .{
.payload = payload,
.payload_top = payload_top,
- .decls_start = decls_start,
.field_bits_start = field_bits_start,
.fields_start = fields_start,
- .decls_end = decls_start,
.fields_end = fields_start,
};
}
- fn nextDecl(self: *Self, is_pub: bool, is_export: bool, has_align: bool, has_section_or_addrspace: bool) void {
- const index = self.payload_top + self.decl_index / decls_per_u32;
- assert(index < self.decls_start);
- const bit_bag: u32 = if (self.decl_index % decls_per_u32 == 0) 0 else self.payload.items[index];
- self.payload.items[index] = (bit_bag >> bits_per_decl) |
- (@as(u32, @intFromBool(is_pub)) << 28) |
- (@as(u32, @intFromBool(is_export)) << 29) |
- (@as(u32, @intFromBool(has_align)) << 30) |
- (@as(u32, @intFromBool(has_section_or_addrspace)) << 31);
+ fn nextDecl(self: *Self, decl_inst: Zir.Inst.Index) void {
+ self.payload.items[self.payload_top + self.decl_index] = @intFromEnum(decl_inst);
self.decl_index += 1;
}
@@ -3962,18 +3952,6 @@ const WipMembers = struct {
self.field_index += 1;
}
- fn appendToDecl(self: *Self, data: u32) void {
- assert(self.decls_end < self.field_bits_start);
- self.payload.items[self.decls_end] = data;
- self.decls_end += 1;
- }
-
- fn appendToDeclSlice(self: *Self, data: []const u32) void {
- assert(self.decls_end + data.len <= self.field_bits_start);
- @memcpy(self.payload.items[self.decls_end..][0..data.len], data);
- self.decls_end += @intCast(data.len);
- }
-
fn appendToField(self: *Self, data: u32) void {
assert(self.fields_end < self.payload.items.len);
self.payload.items[self.fields_end] = data;
@@ -3981,11 +3959,6 @@ const WipMembers = struct {
}
fn finishBits(self: *Self, comptime bits_per_field: u32) void {
- const empty_decl_slots = decls_per_u32 - (self.decl_index % decls_per_u32);
- if (self.decl_index > 0 and empty_decl_slots < decls_per_u32) {
- const index = self.payload_top + self.decl_index / decls_per_u32;
- self.payload.items[index] >>= @intCast(empty_decl_slots * bits_per_decl);
- }
if (bits_per_field > 0) {
const fields_per_u32 = 32 / bits_per_field;
const empty_field_slots = fields_per_u32 - (self.field_index % fields_per_u32);
@@ -3997,7 +3970,7 @@ const WipMembers = struct {
}
fn declsSlice(self: *Self) []u32 {
- return self.payload.items[self.payload_top..self.decls_end];
+ return self.payload.items[self.payload_top..][0..self.decl_index];
}
fn fieldsSlice(self: *Self) []u32 {
@@ -4023,11 +3996,10 @@ fn fnDecl(
// missing function name already happened in scanDecls()
const fn_name_token = fn_proto.name_token orelse return error.AnalysisFail;
- const fn_name_str_index = try astgen.identAsString(fn_name_token);
// We insert this at the beginning so that its instruction index marks the
// start of the top level declaration.
- const block_inst = try gz.makeBlockInst(.block_inline, fn_proto.ast.proto_node);
+ const decl_inst = try gz.makeBlockInst(.declaration, fn_proto.ast.proto_node);
astgen.advanceSourceCursorToNode(decl_node);
var decl_gz: GenZir = .{
@@ -4072,8 +4044,7 @@ fn fnDecl(
const doc_comment_index = try astgen.docCommentAsString(fn_proto.firstToken());
- // align, linksection, and addrspace is passed in the func instruction in this case.
- wip_members.nextDecl(is_pub, is_export, false, false);
+ wip_members.nextDecl(decl_inst);
var noalias_bits: u32 = 0;
var params_scope = &fn_gz.base;
@@ -4213,7 +4184,7 @@ fn fnDecl(
var addrspace_gz = decl_gz.makeSubBlock(params_scope);
defer addrspace_gz.unstack();
const addrspace_ref: Zir.Inst.Ref = if (fn_proto.ast.addrspace_expr == 0) .none else inst: {
- const inst = try expr(&decl_gz, params_scope, .{ .rl = .{ .coerced_ty = .address_space_type } }, fn_proto.ast.addrspace_expr);
+ const inst = try expr(&decl_gz, params_scope, addrspace_ri, fn_proto.ast.addrspace_expr);
if (addrspace_gz.instructionsSlice().len == 0) {
// In this case we will send a len=0 body which can be encoded more efficiently.
break :inst inst;
@@ -4298,7 +4269,7 @@ fn fnDecl(
.section_gz = §ion_gz,
.addrspace_ref = addrspace_ref,
.addrspace_gz = &addrspace_gz,
- .param_block = block_inst,
+ .param_block = decl_inst,
.body_gz = null,
.lib_name = lib_name,
.is_var_args = is_var_args,
@@ -4349,7 +4320,7 @@ fn fnDecl(
.addrspace_gz = &addrspace_gz,
.lbrace_line = lbrace_line,
.lbrace_column = lbrace_column,
- .param_block = block_inst,
+ .param_block = decl_inst,
.body_gz = &fn_gz,
.lib_name = lib_name,
.is_var_args = is_var_args,
@@ -4363,20 +4334,21 @@ fn fnDecl(
// We add this at the end so that its instruction index marks the end range
// of the top level declaration. addFunc already unstacked fn_gz and ret_gz.
- _ = try decl_gz.addBreak(.break_inline, block_inst, func_inst);
- try decl_gz.setBlockBody(block_inst);
-
- {
- const contents_hash align(@alignOf(u32)) = std.zig.hashSrc(tree.getNodeSource(decl_node));
- wip_members.appendToDeclSlice(std.mem.bytesAsSlice(u32, &contents_hash));
- }
- {
- const line_delta = decl_gz.decl_line - gz.decl_line;
- wip_members.appendToDecl(line_delta);
- }
- wip_members.appendToDecl(@intFromEnum(fn_name_str_index));
- wip_members.appendToDecl(@intFromEnum(block_inst));
- wip_members.appendToDecl(@intFromEnum(doc_comment_index));
+ _ = try decl_gz.addBreak(.break_inline, decl_inst, func_inst);
+
+ try setDeclaration(
+ decl_inst,
+ std.zig.hashSrc(tree.getNodeSource(decl_node)),
+ .{ .named = fn_name_token },
+ decl_gz.decl_line - gz.decl_line,
+ is_pub,
+ is_export,
+ doc_comment_index,
+ &decl_gz,
+ // align, linksection, and addrspace are passed in the func instruction in this case.
+ // TODO: move them from the function instruction to the declaration instruction?
+ null,
+ );
}
fn globalVarDecl(
@@ -4393,10 +4365,9 @@ fn globalVarDecl(
const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
// We do this at the beginning so that the instruction index marks the range start
// of the top level declaration.
- const block_inst = try gz.makeBlockInst(.block_inline, node);
+ const decl_inst = try gz.makeBlockInst(.declaration, node);
const name_token = var_decl.ast.mut_token + 1;
- const name_str_index = try astgen.identAsString(name_token);
astgen.advanceSourceCursorToNode(node);
var block_scope: GenZir = .{
@@ -4420,17 +4391,7 @@ fn globalVarDecl(
const maybe_extern_token = var_decl.extern_export_token orelse break :blk false;
break :blk token_tags[maybe_extern_token] == .keyword_extern;
};
- const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node == 0) .none else inst: {
- break :inst try expr(&block_scope, &block_scope.base, align_ri, var_decl.ast.align_node);
- };
- const addrspace_inst: Zir.Inst.Ref = if (var_decl.ast.addrspace_node == 0) .none else inst: {
- break :inst try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = .address_space_type } }, var_decl.ast.addrspace_node);
- };
- const section_inst: Zir.Inst.Ref = if (var_decl.ast.section_node == 0) .none else inst: {
- break :inst try comptimeExpr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = .slice_const_u8_type } }, var_decl.ast.section_node);
- };
- const has_section_or_addrspace = section_inst != .none or addrspace_inst != .none;
- wip_members.nextDecl(is_pub, is_export, align_inst != .none, has_section_or_addrspace);
+ wip_members.nextDecl(decl_inst);
const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
if (!is_mutable) {
@@ -4513,29 +4474,44 @@ fn globalVarDecl(
} else {
return astgen.failNode(node, "unable to infer variable type", .{});
};
+
// We do this at the end so that the instruction index marks the end
// range of a top level declaration.
- _ = try block_scope.addBreakWithSrcNode(.break_inline, block_inst, var_inst, node);
- try block_scope.setBlockBody(block_inst);
+ _ = try block_scope.addBreakWithSrcNode(.break_inline, decl_inst, var_inst, node);
- {
- const contents_hash align(@alignOf(u32)) = std.zig.hashSrc(tree.getNodeSource(node));
- wip_members.appendToDeclSlice(std.mem.bytesAsSlice(u32, &contents_hash));
+ var align_gz = block_scope.makeSubBlock(scope);
+ if (var_decl.ast.align_node != 0) {
+ const align_inst = try expr(&align_gz, &align_gz.base, coerced_align_ri, var_decl.ast.align_node);
+ _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, align_inst, node);
}
- {
- const line_delta = block_scope.decl_line - gz.decl_line;
- wip_members.appendToDecl(line_delta);
- }
- wip_members.appendToDecl(@intFromEnum(name_str_index));
- wip_members.appendToDecl(@intFromEnum(block_inst));
- wip_members.appendToDecl(@intFromEnum(doc_comment_index)); // doc_comment wip
- if (align_inst != .none) {
- wip_members.appendToDecl(@intFromEnum(align_inst));
- }
- if (has_section_or_addrspace) {
- wip_members.appendToDecl(@intFromEnum(section_inst));
- wip_members.appendToDecl(@intFromEnum(addrspace_inst));
+
+ var linksection_gz = align_gz.makeSubBlock(scope);
+ if (var_decl.ast.section_node != 0) {
+ const linksection_inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, var_decl.ast.section_node);
+ _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node);
}
+
+ var addrspace_gz = linksection_gz.makeSubBlock(scope);
+ if (var_decl.ast.addrspace_node != 0) {
+ const addrspace_inst = try expr(&addrspace_gz, &addrspace_gz.base, coerced_addrspace_ri, var_decl.ast.addrspace_node);
+ _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node);
+ }
+
+ try setDeclaration(
+ decl_inst,
+ std.zig.hashSrc(tree.getNodeSource(node)),
+ .{ .named = name_token },
+ block_scope.decl_line - gz.decl_line,
+ is_pub,
+ is_export,
+ doc_comment_index,
+ &block_scope,
+ .{
+ .align_gz = &align_gz,
+ .linksection_gz = &linksection_gz,
+ .addrspace_gz = &addrspace_gz,
+ },
+ );
}
fn comptimeDecl(
@@ -4551,8 +4527,8 @@ fn comptimeDecl(
// Up top so the ZIR instruction index marks the start range of this
// top-level declaration.
- const block_inst = try gz.makeBlockInst(.block_inline, node);
- wip_members.nextDecl(false, false, false, false);
+ const decl_inst = try gz.makeBlockInst(.declaration, node);
+ wip_members.nextDecl(decl_inst);
astgen.advanceSourceCursorToNode(node);
var decl_block: GenZir = .{
@@ -4568,21 +4544,20 @@ fn comptimeDecl(
const block_result = try expr(&decl_block, &decl_block.base, .{ .rl = .none }, body_node);
if (decl_block.isEmpty() or !decl_block.refIsNoReturn(block_result)) {
- _ = try decl_block.addBreak(.break_inline, block_inst, .void_value);
+ _ = try decl_block.addBreak(.break_inline, decl_inst, .void_value);
}
- try decl_block.setBlockBody(block_inst);
- {
- const contents_hash align(@alignOf(u32)) = std.zig.hashSrc(tree.getNodeSource(node));
- wip_members.appendToDeclSlice(std.mem.bytesAsSlice(u32, &contents_hash));
- }
- {
- const line_delta = decl_block.decl_line - gz.decl_line;
- wip_members.appendToDecl(line_delta);
- }
- wip_members.appendToDecl(0);
- wip_members.appendToDecl(@intFromEnum(block_inst));
- wip_members.appendToDecl(0); // no doc comments on comptime decls
+ try setDeclaration(
+ decl_inst,
+ std.zig.hashSrc(tree.getNodeSource(node)),
+ .@"comptime",
+ decl_block.decl_line - gz.decl_line,
+ false,
+ false,
+ .empty,
+ &decl_block,
+ null,
+ );
}
fn usingnamespaceDecl(
@@ -4604,8 +4579,8 @@ fn usingnamespaceDecl(
};
// Up top so the ZIR instruction index marks the start range of this
// top-level declaration.
- const block_inst = try gz.makeBlockInst(.block_inline, node);
- wip_members.nextDecl(is_pub, true, false, false);
+ const decl_inst = try gz.makeBlockInst(.declaration, node);
+ wip_members.nextDecl(decl_inst);
astgen.advanceSourceCursorToNode(node);
var decl_block: GenZir = .{
@@ -4620,20 +4595,19 @@ fn usingnamespaceDecl(
defer decl_block.unstack();
const namespace_inst = try typeExpr(&decl_block, &decl_block.base, type_expr);
- _ = try decl_block.addBreak(.break_inline, block_inst, namespace_inst);
- try decl_block.setBlockBody(block_inst);
-
- {
- const contents_hash align(@alignOf(u32)) = std.zig.hashSrc(tree.getNodeSource(node));
- wip_members.appendToDeclSlice(std.mem.bytesAsSlice(u32, &contents_hash));
- }
- {
- const line_delta = decl_block.decl_line - gz.decl_line;
- wip_members.appendToDecl(line_delta);
- }
- wip_members.appendToDecl(0);
- wip_members.appendToDecl(@intFromEnum(block_inst));
- wip_members.appendToDecl(0); // no doc comments on usingnamespace decls
+ _ = try decl_block.addBreak(.break_inline, decl_inst, namespace_inst);
+
+ try setDeclaration(
+ decl_inst,
+ std.zig.hashSrc(tree.getNodeSource(node)),
+ .@"usingnamespace",
+ decl_block.decl_line - gz.decl_line,
+ is_pub,
+ false,
+ .empty,
+ &decl_block,
+ null,
+ );
}
fn testDecl(
@@ -4649,9 +4623,9 @@ fn testDecl(
// Up top so the ZIR instruction index marks the start range of this
// top-level declaration.
- const block_inst = try gz.makeBlockInst(.block_inline, node);
+ const decl_inst = try gz.makeBlockInst(.declaration, node);
- wip_members.nextDecl(false, false, false, false);
+ wip_members.nextDecl(decl_inst);
astgen.advanceSourceCursorToNode(node);
var decl_block: GenZir = .{
@@ -4669,12 +4643,10 @@ fn testDecl(
const token_tags = tree.tokens.items(.tag);
const test_token = main_tokens[node];
const test_name_token = test_token + 1;
- const test_name_token_tag = token_tags[test_name_token];
- const is_decltest = test_name_token_tag == .identifier;
- const test_name: Zir.NullTerminatedString = blk: {
- if (test_name_token_tag == .string_literal) {
- break :blk try astgen.testNameString(test_name_token);
- } else if (test_name_token_tag == .identifier) {
+ const test_name: DeclarationName = switch (token_tags[test_name_token]) {
+ else => .unnamed_test,
+ .string_literal => .{ .named_test = test_name_token },
+ .identifier => blk: {
const ident_name_raw = tree.tokenSlice(test_name_token);
if (mem.eql(u8, ident_name_raw, "_")) return astgen.failTok(test_name_token, "'_' used as an identifier without @\"_\" syntax", .{});
@@ -4744,10 +4716,8 @@ fn testDecl(
return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});
}
- break :blk name_str_index;
- }
- // String table index 1 has a special meaning here of test decl with no name.
- break :blk .unnamed_test_decl;
+ break :blk .{ .decltest = name_str_index };
+ },
};
var fn_block: GenZir = .{
@@ -4795,7 +4765,7 @@ fn testDecl(
.lbrace_line = lbrace_line,
.lbrace_column = lbrace_column,
- .param_block = block_inst,
+ .param_block = decl_inst,
.body_gz = &fn_block,
.lib_name = .empty,
.is_var_args = false,
@@ -4806,26 +4776,19 @@ fn testDecl(
.noalias_bits = 0,
});
- _ = try decl_block.addBreak(.break_inline, block_inst, func_inst);
- try decl_block.setBlockBody(block_inst);
-
- {
- const contents_hash align(@alignOf(u32)) = std.zig.hashSrc(tree.getNodeSource(node));
- wip_members.appendToDeclSlice(std.mem.bytesAsSlice(u32, &contents_hash));
- }
- {
- const line_delta = decl_block.decl_line - gz.decl_line;
- wip_members.appendToDecl(line_delta);
- }
- if (is_decltest)
- wip_members.appendToDecl(2) // 2 here means that it is a decltest, look at doc comment for name
- else
- wip_members.appendToDecl(@intFromEnum(test_name));
- wip_members.appendToDecl(@intFromEnum(block_inst));
- if (is_decltest)
- wip_members.appendToDecl(@intFromEnum(test_name)) // the doc comment on a decltest represents it's name
- else
- wip_members.appendToDecl(0); // no doc comments on test decls
+ _ = try decl_block.addBreak(.break_inline, decl_inst, func_inst);
+
+ try setDeclaration(
+ decl_inst,
+ std.zig.hashSrc(tree.getNodeSource(node)),
+ test_name,
+ decl_block.decl_line - gz.decl_line,
+ false,
+ false,
+ .empty,
+ &decl_block,
+ null,
+ );
}
fn structDeclInner(
@@ -13524,3 +13487,106 @@ fn lowerAstErrors(astgen: *AstGen) !void {
try tree.renderError(parse_err, msg.writer(gpa));
try astgen.appendErrorTokNotesOff(parse_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
}
+
+const DeclarationName = union(enum) {
+ named: Ast.TokenIndex,
+ named_test: Ast.TokenIndex,
+ unnamed_test,
+ decltest: Zir.NullTerminatedString,
+ @"comptime",
+ @"usingnamespace",
+};
+
+/// Sets all extra data for a `declaration` instruction.
+/// Unstacks `value_gz`, `align_gz`, `linksection_gz`, and `addrspace_gz`.
+fn setDeclaration(
+ decl_inst: Zir.Inst.Index,
+ src_hash: std.zig.SrcHash,
+ name: DeclarationName,
+ line_offset: u32,
+ is_pub: bool,
+ is_export: bool,
+ doc_comment: Zir.NullTerminatedString,
+ value_gz: *GenZir,
+ /// May be `null` if all these blocks would be empty.
+ /// If `null`, then `value_gz` must have nothing stacked on it.
+ extra_gzs: ?struct {
+ /// Must be stacked on `value_gz`.
+ align_gz: *GenZir,
+ /// Must be stacked on `align_gz`.
+ linksection_gz: *GenZir,
+ /// Must be stacked on `linksection_gz`, and have nothing stacked on it.
+ addrspace_gz: *GenZir,
+ },
+) !void {
+ const astgen = value_gz.astgen;
+ const gpa = astgen.gpa;
+
+ const empty_body: []Zir.Inst.Index = &.{};
+ const value_body, const align_body, const linksection_body, const addrspace_body = if (extra_gzs) |e| .{
+ value_gz.instructionsSliceUpto(e.align_gz),
+ e.align_gz.instructionsSliceUpto(e.linksection_gz),
+ e.linksection_gz.instructionsSliceUpto(e.addrspace_gz),
+ e.addrspace_gz.instructionsSlice(),
+ } else .{ value_gz.instructionsSlice(), empty_body, empty_body, empty_body };
+
+ const value_len = astgen.countBodyLenAfterFixups(value_body);
+ const align_len = astgen.countBodyLenAfterFixups(align_body);
+ const linksection_len = astgen.countBodyLenAfterFixups(linksection_body);
+ const addrspace_len = astgen.countBodyLenAfterFixups(addrspace_body);
+
+ const true_doc_comment: Zir.NullTerminatedString = switch (name) {
+ .decltest => |test_name| test_name,
+ else => doc_comment,
+ };
+
+ const src_hash_arr: [4]u32 = @bitCast(src_hash);
+
+ const extra: Zir.Inst.Declaration = .{
+ .src_hash_0 = src_hash_arr[0],
+ .src_hash_1 = src_hash_arr[1],
+ .src_hash_2 = src_hash_arr[2],
+ .src_hash_3 = src_hash_arr[3],
+ .name = switch (name) {
+ .named => |tok| @enumFromInt(@intFromEnum(try astgen.identAsString(tok))),
+ .named_test => |tok| @enumFromInt(@intFromEnum(try astgen.testNameString(tok))),
+ .unnamed_test => .unnamed_test,
+ .decltest => .decltest,
+ .@"comptime" => .@"comptime",
+ .@"usingnamespace" => .@"usingnamespace",
+ },
+ .line_offset = line_offset,
+ .flags = .{
+ .value_body_len = @intCast(value_len),
+ .is_pub = is_pub,
+ .is_export = is_export,
+ .has_doc_comment = true_doc_comment != .empty,
+ .has_align_linksection_addrspace = align_len != 0 or linksection_len != 0 or addrspace_len != 0,
+ },
+ };
+ astgen.instructions.items(.data)[@intFromEnum(decl_inst)].pl_node.payload_index = try astgen.addExtra(extra);
+ if (extra.flags.has_doc_comment) {
+ try astgen.extra.append(gpa, @intFromEnum(true_doc_comment));
+ }
+ if (extra.flags.has_align_linksection_addrspace) {
+ try astgen.extra.appendSlice(gpa, &.{
+ align_len,
+ linksection_len,
+ addrspace_len,
+ });
+ }
+ try astgen.extra.ensureUnusedCapacity(gpa, value_len + align_len + linksection_len + addrspace_len);
+ astgen.appendBodyWithFixups(value_body);
+ if (extra.flags.has_align_linksection_addrspace) {
+ astgen.appendBodyWithFixups(align_body);
+ astgen.appendBodyWithFixups(linksection_body);
+ astgen.appendBodyWithFixups(addrspace_body);
+ }
+
+ if (extra_gzs) |e| {
+ e.addrspace_gz.unstack();
+ e.linksection_gz.unstack();
+ e.align_gz.unstack();
+ }
+ value_gz.unstack();
+}
src/Autodoc.zig
@@ -2846,22 +2846,14 @@ fn walkInstruction(
return res;
},
.block_inline => {
- return self.walkRef(
+ const pl_node = data[@intFromEnum(inst)].pl_node;
+ const extra = file.zir.extraData(Zir.Inst.Block, pl_node.payload_index);
+ return self.walkInlineBody(
file,
parent_scope,
+ try self.srcLocInfo(file, pl_node.src_node, parent_src),
parent_src,
- getBlockInlineBreak(file.zir, inst) orelse {
- const res = DocData.WalkResult{
- .typeRef = .{ .type = @intFromEnum(Ref.type_type) },
- .expr = .{ .comptimeExpr = self.comptime_exprs.items.len },
- };
- const pl_node = data[@intFromEnum(inst)].pl_node;
- const block_inline_expr = try self.getBlockSource(file, parent_src, pl_node.src_node);
- try self.comptime_exprs.append(self.arena, .{
- .code = block_inline_expr,
- });
- return res;
- },
+ file.zir.bodySlice(extra.end, extra.data.body_len),
need_type,
call_ctx,
);
@@ -4084,19 +4076,11 @@ fn analyzeAllDecls(
// First loop to discover decl names
{
var it = original_it;
- while (it.next()) |d| {
- const decl_name_index: Zir.NullTerminatedString = @enumFromInt(file.zir.extra[@intFromEnum(d.sub_index) + 5]);
- switch (decl_name_index) {
- .empty,
- .unnamed_test_decl,
- .decltest,
- => continue,
- _ => if (file.zir.nullTerminatedString(decl_name_index).len == 0) {
- continue;
- },
- }
-
- try scope.insertDeclRef(self.arena, decl_name_index, .Pending);
+ while (it.next()) |zir_index| {
+ const declaration, _ = file.zir.getDeclaration(zir_index);
+ if (declaration.name.isNamedTest(file.zir)) continue;
+ const decl_name = declaration.name.toString(file.zir) orelse continue;
+ try scope.insertDeclRef(self.arena, decl_name, .Pending);
}
}
@@ -4104,147 +4088,114 @@ fn analyzeAllDecls(
{
var it = original_it;
var decl_indexes_slot = first_decl_indexes_slot;
- while (it.next()) |d| : (decl_indexes_slot += 1) {
- const decl_name_index = file.zir.extra[@intFromEnum(d.sub_index) + 5];
- switch (decl_name_index) {
- 0 => {
- const is_exported = @as(u1, @truncate(d.flags >> 1));
- switch (is_exported) {
- 0 => continue, // comptime decl
- 1 => {
- try self.analyzeUsingnamespaceDecl(
- file,
- scope,
- parent_src,
- decl_indexes,
- priv_decl_indexes,
- d,
- call_ctx,
- );
- },
- }
- },
- else => continue,
- }
+ while (it.next()) |zir_index| : (decl_indexes_slot += 1) {
+ const pl_node = file.zir.instructions.items(.data)[@intFromEnum(zir_index)].pl_node;
+ const extra = file.zir.extraData(Zir.Inst.Declaration, pl_node.payload_index);
+ if (extra.data.name != .@"usingnamespace") continue;
+ try self.analyzeUsingnamespaceDecl(
+ file,
+ scope,
+ try self.srcLocInfo(file, pl_node.src_node, parent_src),
+ decl_indexes,
+ priv_decl_indexes,
+ extra.data,
+ @intCast(extra.end),
+ call_ctx,
+ );
}
}
// Third loop to analyze all remaining decls
- var it = original_it;
- while (it.next()) |d| {
- const decl_name_index = file.zir.extra[@intFromEnum(d.sub_index) + 5];
- switch (decl_name_index) {
- 0, 1 => continue, // skip over usingnamespace decls
- 2 => continue, // skip decltests
-
- else => if (file.zir.string_bytes[decl_name_index] == 0) {
- continue;
- },
+ {
+ var it = original_it;
+ while (it.next()) |zir_index| {
+ const pl_node = file.zir.instructions.items(.data)[@intFromEnum(zir_index)].pl_node;
+ const extra = file.zir.extraData(Zir.Inst.Declaration, pl_node.payload_index);
+ switch (extra.data.name) {
+ .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue,
+ _ => if (extra.data.name.isNamedTest(file.zir)) continue,
+ }
+ try self.analyzeDecl(
+ file,
+ scope,
+ try self.srcLocInfo(file, pl_node.src_node, parent_src),
+ decl_indexes,
+ priv_decl_indexes,
+ zir_index,
+ extra.data,
+ @intCast(extra.end),
+ call_ctx,
+ );
}
-
- try self.analyzeDecl(
- file,
- scope,
- parent_src,
- decl_indexes,
- priv_decl_indexes,
- d,
- call_ctx,
- );
}
// Fourth loop to analyze decltests
- it = original_it;
- while (it.next()) |d| {
- const decl_name_index = file.zir.extra[@intFromEnum(d.sub_index) + 5];
- switch (decl_name_index) {
- 0, 1 => continue, // skip over usingnamespace decls
- 2 => {},
- else => continue, // skip tests and normal decls
- }
-
+ var it = original_it;
+ while (it.next()) |zir_index| {
+ const pl_node = file.zir.instructions.items(.data)[@intFromEnum(zir_index)].pl_node;
+ const extra = file.zir.extraData(Zir.Inst.Declaration, pl_node.payload_index);
+ if (extra.data.name != .decltest) continue;
try self.analyzeDecltest(
file,
scope,
- parent_src,
- d,
+ try self.srcLocInfo(file, pl_node.src_node, parent_src),
+ extra.data,
+ @intCast(extra.end),
);
}
return it.extra_index;
}
+fn walkInlineBody(
+ autodoc: *Autodoc,
+ file: *File,
+ scope: *Scope,
+ block_src: SrcLocInfo,
+ parent_src: SrcLocInfo,
+ body: []const Zir.Inst.Index,
+ need_type: bool,
+ call_ctx: ?*const CallContext,
+) AutodocErrors!DocData.WalkResult {
+ const tags = file.zir.instructions.items(.tag);
+ const break_inst = switch (tags[@intFromEnum(body[body.len - 1])]) {
+ .condbr_inline => {
+ // Unresolvable.
+ const res: DocData.WalkResult = .{
+ .typeRef = .{ .type = @intFromEnum(Ref.type_type) },
+ .expr = .{ .comptimeExpr = autodoc.comptime_exprs.items.len },
+ };
+ const source = (try file.getTree(autodoc.zcu.gpa)).getNodeSource(block_src.src_node);
+ try autodoc.comptime_exprs.append(autodoc.arena, .{
+ .code = source,
+ });
+ return res;
+ },
+ .break_inline => body[body.len - 1],
+ else => unreachable,
+ };
+ const break_data = file.zir.instructions.items(.data)[@intFromEnum(break_inst)].@"break";
+ return autodoc.walkRef(file, scope, parent_src, break_data.operand, need_type, call_ctx);
+}
+
// Asserts the given decl is public
fn analyzeDecl(
self: *Autodoc,
file: *File,
scope: *Scope,
- parent_src: SrcLocInfo,
+ decl_src: SrcLocInfo,
decl_indexes: *std.ArrayListUnmanaged(usize),
priv_decl_indexes: *std.ArrayListUnmanaged(usize),
- d: Zir.DeclIterator.Item,
+ decl_inst: Zir.Inst.Index,
+ declaration: Zir.Inst.Declaration,
+ extra_index: u32,
call_ctx: ?*const CallContext,
) AutodocErrors!void {
- const data = file.zir.instructions.items(.data);
- const is_pub = @as(u1, @truncate(d.flags >> 0)) != 0;
- // const is_exported = @truncate(u1, d.flags >> 1) != 0;
- const has_align = @as(u1, @truncate(d.flags >> 2)) != 0;
- const has_section_or_addrspace = @as(u1, @truncate(d.flags >> 3)) != 0;
-
- var extra_index = @intFromEnum(d.sub_index);
- // const hash_u32s = file.zir.extra[extra_index..][0..4];
-
- extra_index += 4;
- // const line = file.zir.extra[extra_index];
-
- extra_index += 1;
- const decl_name_index: Zir.NullTerminatedString = @enumFromInt(file.zir.extra[extra_index]);
-
- extra_index += 1;
- const value_index: Zir.Inst.Index = @enumFromInt(file.zir.extra[extra_index]);
-
- extra_index += 1;
- const doc_comment_index: Zir.NullTerminatedString = @enumFromInt(file.zir.extra[extra_index]);
-
- extra_index += 1;
- const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: {
- const inst: Zir.Inst.Ref = @enumFromInt(file.zir.extra[extra_index]);
- extra_index += 1;
- break :inst inst;
- };
- _ = align_inst;
-
- const section_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
- const inst: Zir.Inst.Ref = @enumFromInt(file.zir.extra[extra_index]);
- extra_index += 1;
- break :inst inst;
- };
- _ = section_inst;
+ const bodies = declaration.getBodies(extra_index, file.zir);
+ const name = file.zir.nullTerminatedString(declaration.name.toString(file.zir).?);
- const addrspace_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
- const inst: Zir.Inst.Ref = @enumFromInt(file.zir.extra[extra_index]);
- extra_index += 1;
- break :inst inst;
- };
- _ = addrspace_inst;
-
- // This is known to work because decl values are always block_inlines
- const value_pl_node = data[@intFromEnum(value_index)].pl_node;
- const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
-
- const name: []const u8 = switch (decl_name_index) {
- .empty, .unnamed_test_decl, .decltest => unreachable,
- _ => blk: {
- if (decl_name_index == .empty) {
- // test decl
- unreachable;
- }
- break :blk file.zir.nullTerminatedString(decl_name_index);
- },
- };
-
- const doc_comment: ?[]const u8 = if (doc_comment_index != .empty)
- file.zir.nullTerminatedString(doc_comment_index)
+ const doc_comment: ?[]const u8 = if (declaration.flags.has_doc_comment)
+ file.zir.nullTerminatedString(@enumFromInt(file.zir.extra[extra_index]))
else
null;
@@ -4261,16 +4212,22 @@ fn analyzeDecl(
break :idx idx;
};
- const walk_result = try self.walkInstruction(
+ const walk_result = try self.walkInlineBody(
file,
scope,
decl_src,
- value_index,
+ decl_src,
+ bodies.value_body,
true,
call_ctx,
);
- const kind: []const u8 = if (try self.declIsVar(file, value_pl_node.src_node, parent_src)) "var" else "const";
+ const tree = try file.getTree(self.zcu.gpa);
+ const kind_token = tree.nodes.items(.main_token)[decl_src.src_node];
+ const kind: []const u8 = switch (tree.tokens.items(.tag)[kind_token]) {
+ .keyword_var => "var",
+ else => "const",
+ };
const decls_slot_index = self.decls.items.len;
try self.decls.append(self.arena, .{
@@ -4281,13 +4238,13 @@ fn analyzeDecl(
.parent_container = scope.enclosing_type,
});
- if (is_pub) {
+ if (declaration.flags.is_pub) {
try decl_indexes.append(self.arena, decls_slot_index);
} else {
try priv_decl_indexes.append(self.arena, decls_slot_index);
}
- const decl_status_ptr = scope.resolveDeclName(decl_name_index, file, .none);
+ const decl_status_ptr = scope.resolveDeclName(declaration.name.toString(file.zir).?, file, .none);
std.debug.assert(decl_status_ptr.* == .Pending);
decl_status_ptr.* = .{ .Analyzed = decls_slot_index };
@@ -4296,7 +4253,7 @@ fn analyzeDecl(
for (paths.items) |resume_info| {
try self.tryResolveRefPath(
resume_info.file,
- value_index,
+ decl_inst,
resume_info.ref_path,
);
}
@@ -4312,24 +4269,17 @@ fn analyzeUsingnamespaceDecl(
self: *Autodoc,
file: *File,
scope: *Scope,
- parent_src: SrcLocInfo,
+ decl_src: SrcLocInfo,
decl_indexes: *std.ArrayListUnmanaged(usize),
priv_decl_indexes: *std.ArrayListUnmanaged(usize),
- d: Zir.DeclIterator.Item,
+ declaration: Zir.Inst.Declaration,
+ extra_index: u32,
call_ctx: ?*const CallContext,
) AutodocErrors!void {
- const data = file.zir.instructions.items(.data);
+ const bodies = declaration.getBodies(extra_index, file.zir);
- const is_pub = @as(u1, @truncate(d.flags)) != 0;
- const value_index: Zir.Inst.Index = @enumFromInt(file.zir.extra[@intFromEnum(d.sub_index) + 6]);
- const doc_comment_index: Zir.NullTerminatedString = @enumFromInt(file.zir.extra[@intFromEnum(d.sub_index) + 7]);
-
- // This is known to work because decl values are always block_inlines
- const value_pl_node = data[@intFromEnum(value_index)].pl_node;
- const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
-
- const doc_comment: ?[]const u8 = if (doc_comment_index != .empty)
- file.zir.nullTerminatedString(doc_comment_index)
+ const doc_comment: ?[]const u8 = if (declaration.flags.has_doc_comment)
+ file.zir.nullTerminatedString(@enumFromInt(file.zir.extra[extra_index]))
else
null;
@@ -4346,11 +4296,12 @@ fn analyzeUsingnamespaceDecl(
break :idx idx;
};
- const walk_result = try self.walkInstruction(
+ const walk_result = try self.walkInlineBody(
file,
scope,
decl_src,
- value_index,
+ decl_src,
+ bodies.value_body,
true,
call_ctx,
);
@@ -4365,7 +4316,7 @@ fn analyzeUsingnamespaceDecl(
.parent_container = scope.enclosing_type,
});
- if (is_pub) {
+ if (declaration.flags.is_pub) {
try decl_indexes.append(self.arena, decl_slot_index);
} else {
try priv_decl_indexes.append(self.arena, decl_slot_index);
@@ -4376,18 +4327,14 @@ fn analyzeDecltest(
self: *Autodoc,
file: *File,
scope: *Scope,
- parent_src: SrcLocInfo,
- d: Zir.DeclIterator.Item,
+ decl_src: SrcLocInfo,
+ declaration: Zir.Inst.Declaration,
+ extra_index: u32,
) AutodocErrors!void {
- const data = file.zir.instructions.items(.data);
-
- const value_index = file.zir.extra[@intFromEnum(d.sub_index) + 6];
- const decl_name_index: Zir.NullTerminatedString = @enumFromInt(file.zir.extra[@intFromEnum(d.sub_index) + 7]);
-
- const value_pl_node = data[value_index].pl_node;
- const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
+ std.debug.assert(declaration.flags.has_doc_comment);
+ const decl_name_index: Zir.NullTerminatedString = @enumFromInt(file.zir.extra[extra_index]);
- const test_source_code = try self.getBlockSource(file, parent_src, value_pl_node.src_node);
+ const test_source_code = (try file.getTree(self.zcu.gpa)).getNodeSource(decl_src.src_node);
const decl_name: ?[]const u8 = if (decl_name_index != .empty)
file.zir.nullTerminatedString(decl_name_index)
@@ -5830,17 +5777,6 @@ fn walkRef(
}
}
-fn getBlockInlineBreak(zir: Zir, inst: Zir.Inst.Index) ?Zir.Inst.Ref {
- const tags = zir.instructions.items(.tag);
- const data = zir.instructions.items(.data);
- const pl_node = data[@intFromEnum(inst)].pl_node;
- const extra = zir.extraData(Zir.Inst.Block, pl_node.payload_index);
- const break_index = zir.extra[extra.end..][extra.data.body_len - 1];
- if (tags[break_index] == .condbr_inline) return null;
- std.debug.assert(tags[break_index] == .break_inline);
- return data[break_index].@"break".operand;
-}
-
fn printWithContext(
file: *File,
inst: Zir.Inst.Index,
src/InternPool.zig
@@ -6186,8 +6186,6 @@ fn finishFuncInstance(
.generation = generation,
.is_pub = fn_owner_decl.is_pub,
.is_exported = fn_owner_decl.is_exported,
- .has_linksection_or_addrspace = fn_owner_decl.has_linksection_or_addrspace,
- .has_align = fn_owner_decl.has_align,
.alive = true,
.kind = .anon,
});
src/main.zig
@@ -6856,10 +6856,7 @@ pub fn cmdChangelist(
var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{};
defer inst_map.deinit(gpa);
- var extra_map: std.AutoHashMapUnmanaged(Zir.ExtraIndex, Zir.ExtraIndex) = .{};
- defer extra_map.deinit(gpa);
-
- try Module.mapOldZirToNew(gpa, old_zir, file.zir, &inst_map, &extra_map);
+ try Module.mapOldZirToNew(gpa, old_zir, file.zir, &inst_map);
var bw = io.bufferedWriter(io.getStdOut().writer());
const stdout = bw.writer();
@@ -6868,16 +6865,8 @@ pub fn cmdChangelist(
var it = inst_map.iterator();
while (it.next()) |entry| {
try stdout.print(" %{d} => %{d}\n", .{
- entry.key_ptr.*, entry.value_ptr.*,
- });
- }
- }
- {
- try stdout.print("Extra mappings:\n", .{});
- var it = extra_map.iterator();
- while (it.next()) |entry| {
- try stdout.print(" {d} => {d}\n", .{
- entry.key_ptr.*, entry.value_ptr.*,
+ @intFromEnum(entry.key_ptr.*),
+ @intFromEnum(entry.value_ptr.*),
});
}
}
src/Module.zig
@@ -386,11 +386,9 @@ pub const Decl = struct {
/// do not need to be loaded into memory in order to compute debug line numbers.
/// This value is absolute.
src_line: u32,
- /// Index to ZIR `extra` array to the entry in the parent's decl structure
- /// (the part that says "for every decls_len"). The first item at this index is
- /// the contents hash, followed by line, name, etc.
- /// For anonymous decls and also the root Decl for a File, this is `none`.
- zir_decl_index: Zir.OptionalExtraIndex,
+ /// Index of the ZIR `declaration` instruction from which this `Decl` was created.
+ /// For the root `Decl` of a `File` and legacy anonymous decls, this is `.none`.
+ zir_decl_index: Zir.Inst.OptionalIndex,
/// Represents the "shallow" analysis status. For example, for decls that are functions,
/// the function type is analyzed with this set to `in_progress`, however, the semantic
@@ -442,10 +440,6 @@ pub const Decl = struct {
is_pub: bool,
/// Whether the corresponding AST decl has a `export` keyword.
is_exported: bool,
- /// Whether the ZIR code provides an align instruction.
- has_align: bool,
- /// Whether the ZIR code provides a linksection and address space instruction.
- has_linksection_or_addrspace: bool,
/// Flag used by garbage collection to mark and sweep.
/// Decls which correspond to an AST node always have this field set to `true`.
/// Anonymous Decls are initialized with this field set to `false` and then it
@@ -471,81 +465,19 @@ pub const Decl = struct {
const Index = InternPool.DeclIndex;
const OptionalIndex = InternPool.OptionalDeclIndex;
- pub const DepsTable = std.AutoArrayHashMapUnmanaged(Decl.Index, DepType);
-
- /// Later types take priority; e.g. if a dependent decl has both `normal`
- /// and `function_body` dependencies on another decl, it will be marked as
- /// having a `function_body` dependency.
- pub const DepType = enum {
- /// The dependent references or uses the dependency's value, so must be
- /// updated whenever it is changed. However, if the dependency is a
- /// function and its type is unchanged, the dependent does not need to
- /// be updated.
- normal,
- /// The dependent performs an inline or comptime call to the dependency,
- /// or is a generic instantiation of it. It must therefore be updated
- /// whenever the dependency is updated, even if the function type
- /// remained the same.
- function_body,
- };
-
- /// This name is relative to the containing namespace of the decl.
- /// The memory is owned by the containing File ZIR.
- pub fn getName(decl: Decl, mod: *Module) ?[:0]const u8 {
- const zir = decl.getFileScope(mod).zir;
- return decl.getNameZir(zir);
+ /// Asserts that `zir_decl_index` is not `.none`.
+ fn getDeclaration(decl: Decl, zir: Zir) Zir.Inst.Declaration {
+ const zir_index = decl.zir_decl_index.unwrap().?;
+ const pl_node = zir.instructions.items(.data)[@intFromEnum(zir_index)].pl_node;
+ return zir.extraData(Zir.Inst.Declaration, pl_node.payload_index).data;
}
- pub fn getNameZir(decl: Decl, zir: Zir) ?[:0]const u8 {
- assert(decl.zir_decl_index != .none);
- const name_index = zir.extra[@intFromEnum(decl.zir_decl_index) + 5];
- if (name_index <= 1) return null;
- return zir.nullTerminatedString(name_index);
- }
-
- pub fn contentsHash(decl: Decl, mod: *Module) std.zig.SrcHash {
- const zir = decl.getFileScope(mod).zir;
- return decl.contentsHashZir(zir);
- }
-
- pub fn contentsHashZir(decl: Decl, zir: Zir) std.zig.SrcHash {
- assert(decl.zir_decl_index != .none);
- const hash_u32s = zir.extra[@intFromEnum(decl.zir_decl_index)..][0..4];
- const contents_hash = @as(std.zig.SrcHash, @bitCast(hash_u32s.*));
- return contents_hash;
- }
-
- pub fn zirBlockIndex(decl: *const Decl, mod: *Module) Zir.Inst.Index {
- assert(decl.zir_decl_index != .none);
- const zir = decl.getFileScope(mod).zir;
- return @enumFromInt(zir.extra[@intFromEnum(decl.zir_decl_index) + 6]);
- }
-
- pub fn zirAlignRef(decl: Decl, mod: *Module) Zir.Inst.Ref {
- if (!decl.has_align) return .none;
- assert(decl.zir_decl_index != .none);
- const zir = decl.getFileScope(mod).zir;
- return @enumFromInt(zir.extra[@intFromEnum(decl.zir_decl_index) + 8]);
- }
-
- pub fn zirLinksectionRef(decl: Decl, mod: *Module) Zir.Inst.Ref {
- if (!decl.has_linksection_or_addrspace) return .none;
- assert(decl.zir_decl_index != .none);
- const zir = decl.getFileScope(mod).zir;
- const extra_index = @intFromEnum(decl.zir_decl_index) + 8 + @intFromBool(decl.has_align);
- return @enumFromInt(zir.extra[extra_index]);
- }
-
- pub fn zirAddrspaceRef(decl: Decl, mod: *Module) Zir.Inst.Ref {
- if (!decl.has_linksection_or_addrspace) return .none;
- assert(decl.zir_decl_index != .none);
- const zir = decl.getFileScope(mod).zir;
- const extra_index = @intFromEnum(decl.zir_decl_index) + 8 + @intFromBool(decl.has_align) + 1;
- return @enumFromInt(zir.extra[extra_index]);
- }
-
- pub fn relativeToLine(decl: Decl, offset: u32) u32 {
- return decl.src_line + offset;
+ pub fn zirBodies(decl: Decl, zcu: *Zcu) Zir.Inst.Declaration.Bodies {
+ const zir = decl.getFileScope(zcu).zir;
+ const zir_index = decl.zir_decl_index.unwrap().?;
+ const pl_node = zir.instructions.items(.data)[@intFromEnum(zir_index)].pl_node;
+ const extra = zir.extraData(Zir.Inst.Declaration, pl_node.payload_index);
+ return extra.data.getBodies(@intCast(extra.end), zir);
}
pub fn relativeToNodeIndex(decl: Decl, offset: i32) Ast.Node.Index {
@@ -3015,16 +2947,12 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {
// The root decl will be null if the previous ZIR had AST errors.
const root_decl = file.root_decl.unwrap() orelse return;
- // Maps from old ZIR to new ZIR, struct_decl, enum_decl, etc. Any instruction which
- // creates a namespace, gets mapped from old to new here.
+ // Maps from old ZIR to new ZIR, declaration, struct_decl, enum_decl, etc. Any instruction which
+ // creates a namespace, and any `declaration` instruction, gets mapped from old to new here.
var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{};
defer inst_map.deinit(gpa);
- // Maps from old ZIR to new ZIR, the extra data index for the sub-decl item.
- // e.g. the thing that Decl.zir_decl_index points to.
- var extra_map: std.AutoHashMapUnmanaged(Zir.ExtraIndex, Zir.ExtraIndex) = .{};
- defer extra_map.deinit(gpa);
- try mapOldZirToNew(gpa, old_zir, new_zir, &inst_map, &extra_map);
+ try mapOldZirToNew(gpa, old_zir, new_zir, &inst_map);
// Walk the Decl graph, updating ZIR indexes, strings, and populating
// the deleted and outdated lists.
@@ -3050,7 +2978,7 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {
// Anonymous decls should not be marked outdated. They will be re-generated
// if their owner decl is marked outdated.
if (decl.zir_decl_index.unwrap()) |old_zir_decl_index| {
- const new_zir_decl_index = extra_map.get(old_zir_decl_index) orelse {
+ const new_zir_decl_index = inst_map.get(old_zir_decl_index) orelse {
try file.deleted_decls.append(gpa, decl_index);
continue;
};
@@ -3098,9 +3026,9 @@ pub fn mapOldZirToNew(
old_zir: Zir,
new_zir: Zir,
inst_map: *std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index),
- extra_map: *std.AutoHashMapUnmanaged(Zir.ExtraIndex, Zir.ExtraIndex),
) Allocator.Error!void {
- // Contain ZIR indexes of declaration instructions.
+ // Contain ZIR indexes of namespace declaration instructions, e.g. struct_decl, union_decl, etc.
+ // Not `declaration`, as this does not create a namespace.
const MatchedZirDecl = struct {
old_inst: Zir.Inst.Index,
new_inst: Zir.Inst.Index,
@@ -3108,47 +3036,113 @@ pub fn mapOldZirToNew(
var match_stack: ArrayListUnmanaged(MatchedZirDecl) = .{};
defer match_stack.deinit(gpa);
- // Main struct inst is always the same
+ // Main struct inst is always matched
try match_stack.append(gpa, .{
.old_inst = .main_struct_inst,
.new_inst = .main_struct_inst,
});
+ // Used as temporary buffers for namespace declaration instructions
var old_decls = std.ArrayList(Zir.Inst.Index).init(gpa);
defer old_decls.deinit();
var new_decls = std.ArrayList(Zir.Inst.Index).init(gpa);
defer new_decls.deinit();
while (match_stack.popOrNull()) |match_item| {
+ // Match the namespace declaration itself
try inst_map.put(gpa, match_item.old_inst, match_item.new_inst);
- // Maps name to extra index of decl sub item.
- var decl_map: std.StringHashMapUnmanaged(Zir.ExtraIndex) = .{};
- defer decl_map.deinit(gpa);
+ // Maps decl name to `declaration` instruction.
+ var named_decls: std.StringHashMapUnmanaged(Zir.Inst.Index) = .{};
+ defer named_decls.deinit(gpa);
+ // Maps test name to `declaration` instruction.
+ var named_tests: std.StringHashMapUnmanaged(Zir.Inst.Index) = .{};
+ defer named_tests.deinit(gpa);
+ // All unnamed tests, in order, for a best-effort match.
+ var unnamed_tests: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
+ defer unnamed_tests.deinit(gpa);
+ // All comptime declarations, in order, for a best-effort match.
+ var comptime_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
+ defer comptime_decls.deinit(gpa);
+ // All usingnamespace declarations, in order, for a best-effort match.
+ var usingnamespace_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
+ defer usingnamespace_decls.deinit(gpa);
{
var old_decl_it = old_zir.declIterator(match_item.old_inst);
- while (old_decl_it.next()) |old_decl| {
- try decl_map.put(gpa, old_decl.name, old_decl.sub_index);
+ while (old_decl_it.next()) |old_decl_inst| {
+ const old_decl, _ = old_zir.getDeclaration(old_decl_inst);
+ switch (old_decl.name) {
+ .@"comptime" => try comptime_decls.append(gpa, old_decl_inst),
+ .@"usingnamespace" => try usingnamespace_decls.append(gpa, old_decl_inst),
+ .unnamed_test, .decltest => try unnamed_tests.append(gpa, old_decl_inst),
+ _ => {
+ const name_nts = old_decl.name.toString(old_zir).?;
+ const name = old_zir.nullTerminatedString(name_nts);
+ if (old_decl.name.isNamedTest(old_zir)) {
+ try named_tests.put(gpa, name, old_decl_inst);
+ } else {
+ try named_decls.put(gpa, name, old_decl_inst);
+ }
+ },
+ }
}
}
+ var unnamed_test_idx: u32 = 0;
+ var comptime_decl_idx: u32 = 0;
+ var usingnamespace_decl_idx: u32 = 0;
+
var new_decl_it = new_zir.declIterator(match_item.new_inst);
- while (new_decl_it.next()) |new_decl| {
- const old_extra_index = decl_map.get(new_decl.name) orelse continue;
- const new_extra_index = new_decl.sub_index;
- try extra_map.put(gpa, old_extra_index, new_extra_index);
-
- try old_zir.findDecls(&old_decls, old_extra_index);
- try new_zir.findDecls(&new_decls, new_extra_index);
- var i: usize = 0;
- while (true) : (i += 1) {
- if (i >= old_decls.items.len) break;
- if (i >= new_decls.items.len) break;
- try match_stack.append(gpa, .{
- .old_inst = old_decls.items[i],
- .new_inst = new_decls.items[i],
- });
+ while (new_decl_it.next()) |new_decl_inst| {
+ const new_decl, _ = new_zir.getDeclaration(new_decl_inst);
+ // Attempt to match this to a declaration in the old ZIR:
+ // * For named declarations (`const`/`var`/`fn`), we match based on name.
+ // * For named tests (`test "foo"`), we also match based on name.
+ // * For unnamed tests and decltests, we match based on order.
+ // * For comptime blocks, we match based on order.
+ // * For usingnamespace decls, we match based on order.
+ // If we cannot match this declaration, we can't match anything nested inside of it either, so we just `continue`.
+ const old_decl_inst = switch (new_decl.name) {
+ .@"comptime" => inst: {
+ if (comptime_decl_idx == comptime_decls.items.len) continue;
+ defer comptime_decl_idx += 1;
+ break :inst comptime_decls.items[comptime_decl_idx];
+ },
+ .@"usingnamespace" => inst: {
+ if (usingnamespace_decl_idx == usingnamespace_decls.items.len) continue;
+ defer usingnamespace_decl_idx += 1;
+ break :inst usingnamespace_decls.items[usingnamespace_decl_idx];
+ },
+ .unnamed_test, .decltest => inst: {
+ if (unnamed_test_idx == unnamed_tests.items.len) continue;
+ defer unnamed_test_idx += 1;
+ break :inst unnamed_tests.items[unnamed_test_idx];
+ },
+ _ => inst: {
+ const name_nts = new_decl.name.toString(old_zir).?;
+ const name = new_zir.nullTerminatedString(name_nts);
+ if (new_decl.name.isNamedTest(new_zir)) {
+ break :inst named_tests.get(name) orelse continue;
+ } else {
+ break :inst named_decls.get(name) orelse continue;
+ }
+ },
+ };
+
+ // Match the `declaration` instruction
+ try inst_map.put(gpa, old_decl_inst, new_decl_inst);
+
+ // Find namespace declarations within this declaration
+ try old_zir.findDecls(&old_decls, old_decl_inst);
+ try new_zir.findDecls(&new_decls, new_decl_inst);
+
+ // We don't have any smart way of matching up these namespace declarations, so we always
+ // correlate them based on source order.
+ const n = @min(old_decls.items.len, new_decls.items.len);
+ try match_stack.ensureUnusedCapacity(gpa, n);
+ for (old_decls.items[0..n], new_decls.items[0..n]) |old_inst, new_inst| {
+ match_stack.appendAssumeCapacity(.{ .old_inst = old_inst, .new_inst = new_inst });
}
}
}
@@ -3457,8 +3451,6 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
new_decl.src_line = 0;
new_decl.is_pub = true;
new_decl.is_exported = false;
- new_decl.has_align = false;
- new_decl.has_linksection_or_addrspace = false;
new_decl.ty = Type.type;
new_decl.alignment = .none;
new_decl.@"linksection" = .none;
@@ -3561,7 +3553,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
const gpa = mod.gpa;
const zir = decl.getFileScope(mod).zir;
- const zir_datas = zir.instructions.items(.data);
const builtin_type_target_index: InternPool.Index = blk: {
const std_mod = mod.std_mod;
@@ -3639,11 +3630,9 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
};
defer block_scope.instructions.deinit(gpa);
- const zir_block_index = decl.zirBlockIndex(mod);
- const inst_data = zir_datas[@intFromEnum(zir_block_index)].pl_node;
- const extra = zir.extraData(Zir.Inst.Block, inst_data.payload_index);
- const body = zir.extra[extra.end..][0..extra.data.body_len];
- const result_ref = (try sema.analyzeBodyBreak(&block_scope, @ptrCast(body))).?.operand;
+ const decl_bodies = decl.zirBodies(mod);
+
+ const result_ref = (try sema.analyzeBodyBreak(&block_scope, decl_bodies.value_body)).?.operand;
// We'll do some other bits with the Sema. Clear the type target index just
// in case they analyze any type.
sema.builtin_type_target_index = .none;
@@ -3760,13 +3749,13 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
decl.ty = decl_tv.ty;
decl.val = Value.fromInterned((try decl_tv.val.intern(decl_tv.ty, mod)));
decl.alignment = blk: {
- const align_ref = decl.zirAlignRef(mod);
- if (align_ref == .none) break :blk .none;
+ const align_body = decl_bodies.align_body orelse break :blk .none;
+ const align_ref = (try sema.analyzeBodyBreak(&block_scope, align_body)).?.operand;
break :blk try sema.resolveAlign(&block_scope, align_src, align_ref);
};
decl.@"linksection" = blk: {
- const linksection_ref = decl.zirLinksectionRef(mod);
- if (linksection_ref == .none) break :blk .none;
+ const linksection_body = decl_bodies.linksection_body orelse break :blk .none;
+ const linksection_ref = (try sema.analyzeBodyBreak(&block_scope, linksection_body)).?.operand;
const bytes = try sema.resolveConstString(&block_scope, section_src, linksection_ref, .{
.needed_comptime_reason = "linksection must be comptime-known",
});
@@ -3786,15 +3775,15 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
};
const target = sema.mod.getTarget();
- break :blk switch (decl.zirAddrspaceRef(mod)) {
- .none => switch (addrspace_ctx) {
- .function => target_util.defaultAddressSpace(target, .function),
- .variable => target_util.defaultAddressSpace(target, .global_mutable),
- .constant => target_util.defaultAddressSpace(target, .global_constant),
- else => unreachable,
- },
- else => |addrspace_ref| try sema.analyzeAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx),
+
+ const addrspace_body = decl_bodies.addrspace_body orelse break :blk switch (addrspace_ctx) {
+ .function => target_util.defaultAddressSpace(target, .function),
+ .variable => target_util.defaultAddressSpace(target, .global_mutable),
+ .constant => target_util.defaultAddressSpace(target, .global_constant),
+ else => unreachable,
};
+ const addrspace_ref = (try sema.analyzeBodyBreak(&block_scope, addrspace_body)).?.operand;
+ break :blk try sema.analyzeAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx);
};
decl.has_tv = true;
decl.analysis = .complete;
@@ -4133,52 +4122,32 @@ fn newEmbedFile(
}
pub fn scanNamespace(
- mod: *Module,
+ zcu: *Zcu,
namespace_index: Namespace.Index,
- extra_start: usize,
- decls_len: u32,
+ decls: []const Zir.Inst.Index,
parent_decl: *Decl,
-) Allocator.Error!usize {
+) Allocator.Error!void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = mod.gpa;
- const namespace = mod.namespacePtr(namespace_index);
- const zir = namespace.file_scope.zir;
+ const gpa = zcu.gpa;
+ const namespace = zcu.namespacePtr(namespace_index);
- try mod.comp.work_queue.ensureUnusedCapacity(decls_len);
- try namespace.decls.ensureTotalCapacity(gpa, decls_len);
+ try zcu.comp.work_queue.ensureUnusedCapacity(decls.len);
+ try namespace.decls.ensureTotalCapacity(gpa, decls.len);
- const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
- var extra_index = extra_start + bit_bags_count;
- var bit_bag_index: usize = extra_start;
- var cur_bit_bag: u32 = undefined;
- var decl_i: u32 = 0;
var scan_decl_iter: ScanDeclIter = .{
- .module = mod,
+ .zcu = zcu,
.namespace_index = namespace_index,
.parent_decl = parent_decl,
};
- while (decl_i < decls_len) : (decl_i += 1) {
- if (decl_i % 8 == 0) {
- cur_bit_bag = zir.extra[bit_bag_index];
- bit_bag_index += 1;
- }
- const flags = @as(u4, @truncate(cur_bit_bag));
- cur_bit_bag >>= 4;
-
- const decl_sub_index = extra_index;
- extra_index += 8; // src_hash(4) + line(1) + name(1) + value(1) + doc_comment(1)
- extra_index += @as(u1, @truncate(flags >> 2)); // Align
- extra_index += @as(u2, @as(u1, @truncate(flags >> 3))) * 2; // Link section or address space, consists of 2 Refs
-
- try scanDecl(&scan_decl_iter, decl_sub_index, flags);
+ for (decls) |decl_inst| {
+ try scanDecl(&scan_decl_iter, decl_inst);
}
- return extra_index;
}
const ScanDeclIter = struct {
- module: *Module,
+ zcu: *Zcu,
namespace_index: Namespace.Index,
parent_decl: *Decl,
usingnamespace_index: usize = 0,
@@ -4186,119 +4155,112 @@ const ScanDeclIter = struct {
unnamed_test_index: usize = 0,
};
-fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Error!void {
+fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void {
const tracy = trace(@src());
defer tracy.end();
- const mod = iter.module;
+ const zcu = iter.zcu;
const namespace_index = iter.namespace_index;
- const namespace = mod.namespacePtr(namespace_index);
- const gpa = mod.gpa;
+ const namespace = zcu.namespacePtr(namespace_index);
+ const gpa = zcu.gpa;
const zir = namespace.file_scope.zir;
- const ip = &mod.intern_pool;
+ const ip = &zcu.intern_pool;
+
+ const pl_node = zir.instructions.items(.data)[@intFromEnum(decl_inst)].pl_node;
+ const extra = zir.extraData(Zir.Inst.Declaration, pl_node.payload_index);
+ const declaration = extra.data;
- // zig fmt: off
- const is_pub = (flags & 0b0001) != 0;
- const export_bit = (flags & 0b0010) != 0;
- const has_align = (flags & 0b0100) != 0;
- const has_linksection_or_addrspace = (flags & 0b1000) != 0;
- // zig fmt: on
-
- const line_off = zir.extra[decl_sub_index + 4];
- const line = iter.parent_decl.relativeToLine(line_off);
- const decl_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[decl_sub_index + 5]);
- const decl_doccomment_index = zir.extra[decl_sub_index + 7];
- const decl_zir_index = zir.extra[decl_sub_index + 6];
- const decl_block_inst_data = zir.instructions.items(.data)[decl_zir_index].pl_node;
- const decl_node = iter.parent_decl.relativeToNodeIndex(decl_block_inst_data.src_node);
+ const line = iter.parent_decl.src_line + declaration.line_offset;
+ const decl_node = iter.parent_decl.relativeToNodeIndex(pl_node.src_node);
// Every Decl needs a name.
- var is_named_test = false;
- var kind: Decl.Kind = .named;
- const decl_name: InternPool.NullTerminatedString = switch (decl_name_index) {
- .empty => name: {
- if (export_bit) {
- const i = iter.usingnamespace_index;
- iter.usingnamespace_index += 1;
- kind = .@"usingnamespace";
- break :name try ip.getOrPutStringFmt(gpa, "usingnamespace_{d}", .{i});
- } else {
- const i = iter.comptime_index;
- iter.comptime_index += 1;
- kind = .@"comptime";
- break :name try ip.getOrPutStringFmt(gpa, "comptime_{d}", .{i});
- }
+ const decl_name: InternPool.NullTerminatedString, const kind: Decl.Kind, const is_named_test: bool = switch (declaration.name) {
+ .@"comptime" => info: {
+ const i = iter.comptime_index;
+ iter.comptime_index += 1;
+ break :info .{
+ try ip.getOrPutStringFmt(gpa, "comptime_{d}", .{i}),
+ .@"comptime",
+ false,
+ };
+ },
+ .@"usingnamespace" => info: {
+ const i = iter.usingnamespace_index;
+ iter.usingnamespace_index += 1;
+ break :info .{
+ try ip.getOrPutStringFmt(gpa, "usingnamespace_{d}", .{i}),
+ .@"usingnamespace",
+ false,
+ };
},
- .unnamed_test_decl => name: {
+ .unnamed_test => info: {
const i = iter.unnamed_test_index;
iter.unnamed_test_index += 1;
- kind = .@"test";
- break :name try ip.getOrPutStringFmt(gpa, "test_{d}", .{i});
+ break :info .{
+ try ip.getOrPutStringFmt(gpa, "test_{d}", .{i}),
+ .@"test",
+ false,
+ };
},
- .decltest => name: {
- is_named_test = true;
- const test_name = zir.nullTerminatedString(@enumFromInt(decl_doccomment_index));
- kind = .@"test";
- break :name try ip.getOrPutStringFmt(gpa, "decltest.{s}", .{test_name});
+ .decltest => info: {
+ assert(declaration.flags.has_doc_comment);
+ const name = zir.nullTerminatedString(@enumFromInt(zir.extra[extra.end]));
+ break :info .{
+ try ip.getOrPutStringFmt(gpa, "decltest.{s}", .{name}),
+ .@"test",
+ true,
+ };
},
- _ => name: {
- const raw_name = zir.nullTerminatedString(decl_name_index);
- if (raw_name.len == 0) {
- is_named_test = true;
- const test_name = zir.nullTerminatedString(@enumFromInt(@intFromEnum(decl_name_index) + 1));
- kind = .@"test";
- break :name try ip.getOrPutStringFmt(gpa, "test.{s}", .{test_name});
- } else {
- break :name try ip.getOrPutString(gpa, raw_name);
- }
+ _ => if (declaration.name.isNamedTest(zir)) .{
+ try ip.getOrPutStringFmt(gpa, "test.{s}", .{zir.nullTerminatedString(declaration.name.toString(zir).?)}),
+ .@"test",
+ true,
+ } else .{
+ try ip.getOrPutString(gpa, zir.nullTerminatedString(declaration.name.toString(zir).?)),
+ .named,
+ false,
},
};
- const is_exported = export_bit and decl_name_index != .empty;
if (kind == .@"usingnamespace") try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1);
// We create a Decl for it regardless of analysis status.
const gop = try namespace.decls.getOrPutContextAdapted(
gpa,
decl_name,
- DeclAdapter{ .mod = mod },
- Namespace.DeclContext{ .module = mod },
+ DeclAdapter{ .mod = zcu },
+ Namespace.DeclContext{ .module = zcu },
);
- const comp = mod.comp;
+ const comp = zcu.comp;
if (!gop.found_existing) {
- const new_decl_index = try mod.allocateNewDecl(namespace_index, decl_node, iter.parent_decl.src_scope);
- const new_decl = mod.declPtr(new_decl_index);
+ const new_decl_index = try zcu.allocateNewDecl(namespace_index, decl_node, iter.parent_decl.src_scope);
+ const new_decl = zcu.declPtr(new_decl_index);
new_decl.kind = kind;
new_decl.name = decl_name;
if (kind == .@"usingnamespace") {
- namespace.usingnamespace_set.putAssumeCapacity(new_decl_index, is_pub);
+ namespace.usingnamespace_set.putAssumeCapacity(new_decl_index, declaration.flags.is_pub);
}
new_decl.src_line = line;
gop.key_ptr.* = new_decl_index;
// Exported decls, comptime decls, usingnamespace decls, and
// test decls if in test mode, get analyzed.
const decl_mod = namespace.file_scope.mod;
- const want_analysis = is_exported or switch (decl_name_index) {
- .empty => true, // comptime or usingnamespace decl
- .unnamed_test_decl => blk: {
- // test decl with no name. Skip the part where we check against
- // the test name filter.
- if (!comp.config.is_test) break :blk false;
- if (decl_mod != mod.main_mod) break :blk false;
- try mod.test_functions.put(gpa, new_decl_index, {});
- break :blk true;
- },
- else => blk: {
- if (!is_named_test) break :blk false;
- if (!comp.config.is_test) break :blk false;
- if (decl_mod != mod.main_mod) break :blk false;
- if (comp.test_filter) |test_filter| {
- if (mem.indexOf(u8, ip.stringToSlice(decl_name), test_filter) == null) {
- break :blk false;
+ const want_analysis = declaration.flags.is_export or switch (kind) {
+ .anon => unreachable,
+ .@"comptime", .@"usingnamespace" => true,
+ .named => false,
+ .@"test" => a: {
+ if (!comp.config.is_test) break :a false;
+ if (decl_mod != zcu.main_mod) break :a false;
+ if (is_named_test) {
+ if (comp.test_filter) |test_filter| {
+ if (mem.indexOf(u8, ip.stringToSlice(decl_name), test_filter) == null) {
+ break :a false;
+ }
}
}
- try mod.test_functions.put(gpa, new_decl_index, {});
- break :blk true;
+ try zcu.test_functions.put(gpa, new_decl_index, {});
+ break :a true;
},
};
if (want_analysis) {
@@ -4307,46 +4269,42 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
});
comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl_index });
}
- new_decl.is_pub = is_pub;
- new_decl.is_exported = is_exported;
- new_decl.has_align = has_align;
- new_decl.has_linksection_or_addrspace = has_linksection_or_addrspace;
- new_decl.zir_decl_index = @enumFromInt(decl_sub_index);
+ new_decl.is_pub = declaration.flags.is_pub;
+ new_decl.is_exported = declaration.flags.is_export;
+ new_decl.zir_decl_index = decl_inst.toOptional();
new_decl.alive = true; // This Decl corresponds to an AST node and therefore always alive.
return;
}
const decl_index = gop.key_ptr.*;
- const decl = mod.declPtr(decl_index);
+ const decl = zcu.declPtr(decl_index);
if (kind == .@"test") {
const src_loc = SrcLoc{
- .file_scope = decl.getFileScope(mod),
+ .file_scope = decl.getFileScope(zcu),
.parent_decl_node = decl.src_node,
.lazy = .{ .token_offset = 1 },
};
const msg = try ErrorMsg.create(gpa, src_loc, "duplicate test name: {}", .{
- decl_name.fmt(&mod.intern_pool),
+ decl_name.fmt(ip),
});
errdefer msg.destroy(gpa);
- try mod.failed_decls.putNoClobber(gpa, decl_index, msg);
+ try zcu.failed_decls.putNoClobber(gpa, decl_index, msg);
const other_src_loc = SrcLoc{
.file_scope = namespace.file_scope,
.parent_decl_node = decl_node,
.lazy = .{ .token_offset = 1 },
};
- try mod.errNoteNonLazy(other_src_loc, msg, "other test here", .{});
+ try zcu.errNoteNonLazy(other_src_loc, msg, "other test here", .{});
}
// Update the AST node of the decl; even if its contents are unchanged, it may
// have been re-ordered.
decl.src_node = decl_node;
decl.src_line = line;
- decl.is_pub = is_pub;
- decl.is_exported = is_exported;
+ decl.is_pub = declaration.flags.is_pub;
+ decl.is_exported = declaration.flags.is_export;
decl.kind = kind;
- decl.has_align = has_align;
- decl.has_linksection_or_addrspace = has_linksection_or_addrspace;
- decl.zir_decl_index = @enumFromInt(decl_sub_index);
- if (decl.getOwnedFunction(mod) != null) {
+ decl.zir_decl_index = decl_inst.toOptional();
+ if (decl.getOwnedFunction(zcu) != null) {
// TODO Look into detecting when this would be unnecessary by storing enough state
// in `Decl` to notice that the line number did not change.
comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
@@ -4730,8 +4688,6 @@ pub fn allocateNewDecl(
.generation = 0,
.is_pub = false,
.is_exported = false,
- .has_linksection_or_addrspace = false,
- .has_align = false,
.alive = false,
.kind = .anon,
});
src/print_zir.zig
@@ -521,6 +521,8 @@ const Writer = struct {
.@"defer" => try self.writeDefer(stream, inst),
.defer_err_code => try self.writeDeferErrCode(stream, inst),
+ .declaration => try self.writeDeclaration(stream, inst),
+
.extended => try self.writeExtended(stream, inst),
}
}
@@ -1454,8 +1456,9 @@ const Writer = struct {
try stream.writeAll("{\n");
self.indent += 2;
- extra_index = try self.writeDecls(stream, decls_len, extra_index);
+ try self.writeBody(stream, self.code.bodySlice(extra_index, decls_len));
self.indent -= 2;
+ extra_index += decls_len;
try stream.writeByteNTimes(' ', self.indent);
try stream.writeAll("}, ");
}
@@ -1634,8 +1637,9 @@ const Writer = struct {
try stream.writeAll("{\n");
self.indent += 2;
- extra_index = try self.writeDecls(stream, decls_len, extra_index);
+ try self.writeBody(stream, self.code.bodySlice(extra_index, decls_len));
self.indent -= 2;
+ extra_index += decls_len;
try stream.writeByteNTimes(' ', self.indent);
try stream.writeAll("}");
}
@@ -1727,124 +1731,6 @@ const Writer = struct {
try self.writeSrcNode(stream, src_node);
}
- fn writeDecls(self: *Writer, stream: anytype, decls_len: u32, extra_start: usize) !usize {
- const parent_decl_node = self.parent_decl_node;
- const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
- var extra_index = extra_start + bit_bags_count;
- var bit_bag_index: usize = extra_start;
- var cur_bit_bag: u32 = undefined;
- var decl_i: u32 = 0;
- while (decl_i < decls_len) : (decl_i += 1) {
- if (decl_i % 8 == 0) {
- cur_bit_bag = self.code.extra[bit_bag_index];
- bit_bag_index += 1;
- }
- const is_pub = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 1;
- const is_exported = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 1;
- const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 1;
- const has_section_or_addrspace = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 1;
-
- const sub_index = extra_index;
-
- const hash_u32s = self.code.extra[extra_index..][0..4];
- extra_index += 4;
- const line = self.code.extra[extra_index];
- extra_index += 1;
- const decl_name_index = self.code.extra[extra_index];
- extra_index += 1;
- const decl_index: Zir.Inst.Index = @enumFromInt(self.code.extra[extra_index]);
- extra_index += 1;
- const doc_comment_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]);
- extra_index += 1;
-
- const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: {
- const inst = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
- extra_index += 1;
- break :inst inst;
- };
- const section_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
- const inst = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
- extra_index += 1;
- break :inst inst;
- };
- const addrspace_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
- const inst = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
- extra_index += 1;
- break :inst inst;
- };
-
- const pub_str = if (is_pub) "pub " else "";
- const hash_bytes: [16]u8 = @bitCast(hash_u32s.*);
- if (decl_name_index == 0) {
- try stream.writeByteNTimes(' ', self.indent);
- const name = if (is_exported) "usingnamespace" else "comptime";
- try stream.writeAll(pub_str);
- try stream.writeAll(name);
- } else if (decl_name_index == 1) {
- try stream.writeByteNTimes(' ', self.indent);
- try stream.writeAll("test");
- } else if (decl_name_index == 2) {
- try stream.writeByteNTimes(' ', self.indent);
- try stream.print("[{d}] decltest {s}", .{ sub_index, self.code.nullTerminatedString(doc_comment_index) });
- } else {
- const raw_decl_name = self.code.nullTerminatedString(@enumFromInt(decl_name_index));
- const decl_name = if (raw_decl_name.len == 0)
- self.code.nullTerminatedString(@enumFromInt(decl_name_index + 1))
- else
- raw_decl_name;
- const test_str = if (raw_decl_name.len == 0) "test \"" else "";
- const export_str = if (is_exported) "export " else "";
-
- try self.writeDocComment(stream, doc_comment_index);
-
- try stream.writeByteNTimes(' ', self.indent);
- const endquote_if_test: []const u8 = if (raw_decl_name.len == 0) "\"" else "";
- try stream.print("[{d}] {s}{s}{s}{}{s}", .{
- sub_index, pub_str, test_str, export_str, std.zig.fmtId(decl_name), endquote_if_test,
- });
- if (align_inst != .none) {
- try stream.writeAll(" align(");
- try self.writeInstRef(stream, align_inst);
- try stream.writeAll(")");
- }
- if (addrspace_inst != .none) {
- try stream.writeAll(" addrspace(");
- try self.writeInstRef(stream, addrspace_inst);
- try stream.writeAll(")");
- }
- if (section_inst != .none) {
- try stream.writeAll(" linksection(");
- try self.writeInstRef(stream, section_inst);
- try stream.writeAll(")");
- }
- }
-
- if (self.recurse_decls) {
- const tag = self.code.instructions.items(.tag)[@intFromEnum(decl_index)];
- try stream.print(" line({d}) hash({}): %{d} = {s}(", .{
- line, std.fmt.fmtSliceHexLower(&hash_bytes), @intFromEnum(decl_index), @tagName(tag),
- });
-
- const decl_block_inst_data = self.code.instructions.items(.data)[@intFromEnum(decl_index)].pl_node;
- const sub_decl_node_off = decl_block_inst_data.src_node;
- self.parent_decl_node = self.relativeToNodeIndex(sub_decl_node_off);
- try self.writePlNodeBlockWithoutSrc(stream, decl_index);
- self.parent_decl_node = parent_decl_node;
- try self.writeSrc(stream, decl_block_inst_data.src());
- try stream.writeAll("\n");
- } else {
- try stream.print(" line({d}) hash({}): %{d} = ...\n", .{
- line, std.fmt.fmtSliceHexLower(&hash_bytes), @intFromEnum(decl_index),
- });
- }
- }
- return extra_index;
- }
-
fn writeEnumDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
const small = @as(Zir.Inst.EnumDecl.Small, @bitCast(extended.small));
var extra_index: usize = extended.operand;
@@ -1891,8 +1777,9 @@ const Writer = struct {
try stream.writeAll("{\n");
self.indent += 2;
- extra_index = try self.writeDecls(stream, decls_len, extra_index);
+ try self.writeBody(stream, self.code.bodySlice(extra_index, decls_len));
self.indent -= 2;
+ extra_index += decls_len;
try stream.writeByteNTimes(' ', self.indent);
try stream.writeAll("}, ");
}
@@ -1988,7 +1875,7 @@ const Writer = struct {
try stream.writeAll("{\n");
self.indent += 2;
- _ = try self.writeDecls(stream, decls_len, extra_index);
+ try self.writeBody(stream, self.code.bodySlice(extra_index, decls_len));
self.indent -= 2;
try stream.writeByteNTimes(' ', self.indent);
try stream.writeAll("})");
@@ -2762,6 +2649,64 @@ const Writer = struct {
try stream.writeByte(')');
}
+ fn writeDeclaration(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
+ const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
+ const extra = self.code.extraData(Zir.Inst.Declaration, inst_data.payload_index);
+ const doc_comment: ?Zir.NullTerminatedString = if (extra.data.flags.has_doc_comment) dc: {
+ break :dc @enumFromInt(self.code.extra[extra.end]);
+ } else null;
+ if (extra.data.flags.is_pub) try stream.writeAll("pub ");
+ if (extra.data.flags.is_export) try stream.writeAll("export ");
+ switch (extra.data.name) {
+ .@"comptime" => try stream.writeAll("comptime"),
+ .@"usingnamespace" => try stream.writeAll("usingnamespace"),
+ .unnamed_test => try stream.writeAll("test"),
+ .decltest => try stream.print("decltest '{s}'", .{self.code.nullTerminatedString(doc_comment.?)}),
+ _ => {
+ const name = extra.data.name.toString(self.code).?;
+ const prefix = if (extra.data.name.isNamedTest(self.code)) "test " else "";
+ try stream.print("{s}'{s}'", .{ prefix, self.code.nullTerminatedString(name) });
+ },
+ }
+ const src_hash_arr: [4]u32 = .{
+ extra.data.src_hash_0,
+ extra.data.src_hash_1,
+ extra.data.src_hash_2,
+ extra.data.src_hash_3,
+ };
+ const src_hash_bytes: [16]u8 = @bitCast(src_hash_arr);
+ try stream.print(" line(+{d}) hash({})", .{ extra.data.line_offset, std.fmt.fmtSliceHexLower(&src_hash_bytes) });
+
+ {
+ const prev_parent_decl_node = self.parent_decl_node;
+ defer self.parent_decl_node = prev_parent_decl_node;
+ self.parent_decl_node = self.relativeToNodeIndex(inst_data.src_node);
+
+ const bodies = extra.data.getBodies(@intCast(extra.end), self.code);
+
+ try stream.writeAll(" value=");
+ try self.writeBracedDecl(stream, bodies.value_body);
+
+ if (bodies.align_body) |b| {
+ try stream.writeAll(" align=");
+ try self.writeBracedDecl(stream, b);
+ }
+
+ if (bodies.linksection_body) |b| {
+ try stream.writeAll(" linksection=");
+ try self.writeBracedDecl(stream, b);
+ }
+
+ if (bodies.addrspace_body) |b| {
+ try stream.writeAll(" addrspace=");
+ try self.writeBracedDecl(stream, b);
+ }
+ }
+
+ try stream.writeAll(") ");
+ try self.writeSrc(stream, inst_data.src());
+ }
+
fn writeInstRef(self: *Writer, stream: anytype, ref: Zir.Inst.Ref) !void {
if (ref == .none) {
return stream.writeAll(".none");
src/Sema.zig
@@ -1224,6 +1224,10 @@ fn analyzeBodyInner(
.trap => break sema.zirTrap(block, inst),
// zig fmt: on
+ // This instruction never exists in an analyzed body. It exists only in the declaration
+ // list for a container type.
+ .declaration => unreachable,
+
.extended => ext: {
const extended = datas[@intFromEnum(inst)].extended;
break :ext switch (extended.opcode) {
@@ -2736,7 +2740,9 @@ pub fn getStructType(
}
}
- extra_index = try mod.scanNamespace(namespace, extra_index, decls_len, mod.declPtr(decl));
+ const decls = sema.code.bodySlice(extra_index, decls_len);
+ try mod.scanNamespace(namespace, decls, mod.declPtr(decl));
+ extra_index += decls_len;
const ty = try ip.getStructType(gpa, .{
.decl = decl,
@@ -2973,7 +2979,9 @@ fn zirEnumDecl(
const new_namespace = mod.namespacePtr(new_namespace_index);
errdefer if (!done) mod.destroyNamespace(new_namespace_index);
- extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl);
+ const decls = sema.code.bodySlice(extra_index, decls_len);
+ try mod.scanNamespace(new_namespace_index, decls, new_decl);
+ extra_index += decls_len;
const body = sema.code.bodySlice(extra_index, body_len);
extra_index += body.len;
@@ -3263,7 +3271,8 @@ fn zirUnionDecl(
new_decl.val = Value.fromInterned(union_ty);
new_namespace.ty = Type.fromInterned(union_ty);
- _ = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl);
+ const decls = sema.code.bodySlice(extra_index, decls_len);
+ try mod.scanNamespace(new_namespace_index, decls, new_decl);
const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
try mod.finalizeAnonDecl(new_decl_index);
@@ -3326,7 +3335,8 @@ fn zirOpaqueDecl(
new_decl.val = Value.fromInterned(opaque_ty);
new_namespace.ty = Type.fromInterned(opaque_ty);
- extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl);
+ const decls = sema.code.bodySlice(extra_index, decls_len);
+ try mod.scanNamespace(new_namespace_index, decls, new_decl);
const decl_val = sema.analyzeDeclVal(block, src, new_decl_index);
try mod.finalizeAnonDecl(new_decl_index);
@@ -36331,9 +36341,7 @@ fn structZirInfo(zir: Zir, zir_index: Zir.Inst.Index) struct {
}
// Skip over decls.
- var decls_it = zir.declIteratorInner(extra_index, decls_len);
- while (decls_it.next()) |_| {}
- extra_index = decls_it.extra_index;
+ extra_index += decls_len;
return .{ fields_len, small, extra_index };
}
@@ -36802,9 +36810,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un
} else 0;
// Skip over decls.
- var decls_it = zir.declIteratorInner(extra_index, decls_len);
- while (decls_it.next()) |_| {}
- extra_index = decls_it.extra_index;
+ extra_index += decls_len;
const body = zir.bodySlice(extra_index, body_len);
extra_index += body.len;
@@ -37801,10 +37807,12 @@ pub fn analyzeAddressSpace(
ctx: AddressSpaceContext,
) !std.builtin.AddressSpace {
const mod = sema.mod;
- const addrspace_tv = try sema.resolveInstConst(block, src, zir_ref, .{
+ const air_ref = try sema.resolveInst(zir_ref);
+ const coerced = try sema.coerce(block, Type.fromInterned(.address_space_type), air_ref, src);
+ const addrspace_val = try sema.resolveConstDefinedValue(block, src, coerced, .{
.needed_comptime_reason = "address space must be comptime-known",
});
- const address_space = mod.toEnum(std.builtin.AddressSpace, addrspace_tv.val);
+ const address_space = mod.toEnum(std.builtin.AddressSpace, addrspace_val);
const target = sema.mod.getTarget();
const arch = target.cpu.arch;
src/Zir.zig
@@ -30,7 +30,7 @@ instructions: std.MultiArrayList(Inst).Slice,
/// is referencing the data here whether they want to store both index and length,
/// thus allowing null bytes, or store only index, and use null-termination. The
/// `string_bytes` array is agnostic to either usage.
-/// Indexes 0 and 1 are reserved for special cases.
+/// Index 0 is reserved for special cases.
string_bytes: []u8,
/// The meaning of this data is determined by `Inst.Tag` value.
/// The first few indexes are reserved. See `ExtraIndex` for the values.
@@ -60,21 +60,6 @@ pub const ExtraIndex = enum(u32) {
imports,
_,
-
- pub fn toOptional(i: ExtraIndex) OptionalExtraIndex {
- return @enumFromInt(@intFromEnum(i));
- }
-};
-
-pub const OptionalExtraIndex = enum(u32) {
- compile_errors,
- imports,
- none = std.math.maxInt(u32),
- _,
-
- pub fn unwrap(oi: OptionalExtraIndex) ?ExtraIndex {
- return if (oi == .none) null else @enumFromInt(@intFromEnum(oi));
- }
};
fn ExtraData(comptime T: type) type {
@@ -93,6 +78,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) ExtraData(T) {
Inst.Ref,
Inst.Index,
+ Inst.Declaration.Name,
NullTerminatedString,
=> @enumFromInt(code.extra[i]),
@@ -102,6 +88,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) ExtraData(T) {
Inst.SwitchBlock.Bits,
Inst.SwitchBlockErrUnion.Bits,
Inst.FuncFancy.Bits,
+ Inst.Declaration.Flags,
=> @bitCast(code.extra[i]),
else => @compileError("bad field type"),
@@ -116,8 +103,6 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) ExtraData(T) {
pub const NullTerminatedString = enum(u32) {
empty = 0,
- unnamed_test_decl = 1,
- decltest = 2,
_,
};
@@ -304,6 +289,12 @@ pub const Inst = struct {
/// a noreturn instruction.
/// Uses the `pl_node` union field. Payload is `Block`.
block_inline,
+ /// This instruction may only ever appear in the list of declarations for a
+ /// namespace type, e.g. within a `struct_decl` instruction. It represents a
+ /// single source declaration (`const`/`var`/`fn`), containing the name,
+ /// attributes, type, and value of the declaration.
+ /// Uses the `pl_node` union field. Payload is `Declaration`.
+ declaration,
/// Implements `suspend {...}`.
/// Uses the `pl_node` union field. Payload is `Block`.
suspend_block,
@@ -1092,6 +1083,7 @@ pub const Inst = struct {
.block,
.block_comptime,
.block_inline,
+ .declaration,
.suspend_block,
.loop,
.bool_br_and,
@@ -1405,6 +1397,7 @@ pub const Inst = struct {
.block,
.block_comptime,
.block_inline,
+ .declaration,
.suspend_block,
.loop,
.bool_br_and,
@@ -1639,6 +1632,7 @@ pub const Inst = struct {
.block = .pl_node,
.block_comptime = .pl_node,
.block_inline = .pl_node,
+ .declaration = .pl_node,
.suspend_block = .pl_node,
.bool_not = .un_node,
.bool_br_and = .bool_br,
@@ -2508,6 +2502,7 @@ pub const Inst = struct {
/// If this is 1 it means return_type is a simple Ref
ret_body_len: u32,
/// Points to the block that contains the param instructions for this function.
+ /// If this is a `declaration`, it refers to the declaration's value body.
param_block: Index,
body_len: u32,
@@ -2565,6 +2560,7 @@ pub const Inst = struct {
/// 18. src_locs: Func.SrcLocs // if body_len != 0
pub const FuncFancy = struct {
/// Points to the block that contains the param instructions for this function.
+ /// If this is a `declaration`, it refers to the declaration's value body.
param_block: Index,
body_len: u32,
bits: Bits,
@@ -2632,6 +2628,116 @@ pub const Inst = struct {
body_len: u32,
};
+ /// Trailing:
+ /// 0. doc_comment: u32 // if `has_doc_comment`; null-terminated string index
+ /// 1. align_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `align`
+ /// 2. linksection_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `linksection`
+ /// 3. addrspace_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `addrspace`
+ /// 4. value_body_inst: Zir.Inst.Index
+ /// - for each `value_body_len`
+ /// - body to be exited via `break_inline` to this `declaration` instruction
+ /// 5. align_body_inst: Zir.Inst.Index
+ /// - for each `align_body_len`
+ /// - body to be exited via `break_inline` to this `declaration` instruction
+ /// 6. linksection_body_inst: Zir.Inst.Index
+ /// - for each `linksection_body_len`
+ /// - body to be exited via `break_inline` to this `declaration` instruction
+ /// 7. addrspace_body_inst: Zir.Inst.Index
+ /// - for each `addrspace_body_len`
+ /// - body to be exited via `break_inline` to this `declaration` instruction
+ pub const Declaration = struct {
+ // These fields should be concatenated and reinterpreted as a `std.zig.SrcHash`.
+ src_hash_0: u32,
+ src_hash_1: u32,
+ src_hash_2: u32,
+ src_hash_3: u32,
+ /// The name of this `Decl`. Also indicates whether it is a test, comptime block, etc.
+ name: Name,
+ /// This Decl's line number relative to that of its parent.
+ /// TODO: column must be encoded similarly to respect non-formatted code!
+ line_offset: u32,
+ flags: Flags,
+
+ pub const Flags = packed struct(u32) {
+ value_body_len: u28,
+ is_pub: bool,
+ is_export: bool,
+ has_doc_comment: bool,
+ has_align_linksection_addrspace: bool,
+ };
+
+ pub const Name = enum(u32) {
+ @"comptime" = std.math.maxInt(u32),
+ @"usingnamespace" = std.math.maxInt(u32) - 1,
+ unnamed_test = std.math.maxInt(u32) - 2,
+ /// In this case, `has_doc_comment` will be true, and the doc
+ /// comment body is the identifier name.
+ decltest = std.math.maxInt(u32) - 3,
+ /// Other values are `NullTerminatedString` values, i.e. index into
+ /// `string_bytes`. If the byte referenced is 0, the decl is a named
+ /// test, and the actual name begins at the following byte.
+ _,
+
+ pub fn isNamedTest(name: Name, zir: Zir) bool {
+ return switch (name) {
+ .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => false,
+ _ => zir.string_bytes[@intFromEnum(name)] == 0,
+ };
+ }
+ pub fn toString(name: Name, zir: Zir) ?NullTerminatedString {
+ switch (name) {
+ .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => return null,
+ _ => {},
+ }
+ const idx: u32 = @intFromEnum(name);
+ if (zir.string_bytes[idx] == 0) {
+ // Named test
+ return @enumFromInt(idx + 1);
+ }
+ return @enumFromInt(idx);
+ }
+ };
+
+ pub const Bodies = struct {
+ value_body: []const Index,
+ align_body: ?[]const Index,
+ linksection_body: ?[]const Index,
+ addrspace_body: ?[]const Index,
+ };
+
+ pub fn getBodies(declaration: Declaration, extra_end: u32, zir: Zir) Bodies {
+ var extra_index: u32 = extra_end;
+ extra_index += @intFromBool(declaration.flags.has_doc_comment);
+ const value_body_len = declaration.flags.value_body_len;
+ const align_body_len, const linksection_body_len, const addrspace_body_len = lens: {
+ if (!declaration.flags.has_align_linksection_addrspace) {
+ break :lens .{ 0, 0, 0 };
+ }
+ const lens = zir.extra[extra_index..][0..3].*;
+ extra_index += 3;
+ break :lens lens;
+ };
+ return .{
+ .value_body = b: {
+ defer extra_index += value_body_len;
+ break :b zir.bodySlice(extra_index, value_body_len);
+ },
+ .align_body = if (align_body_len == 0) null else b: {
+ defer extra_index += align_body_len;
+ break :b zir.bodySlice(extra_index, align_body_len);
+ },
+ .linksection_body = if (linksection_body_len == 0) null else b: {
+ defer extra_index += linksection_body_len;
+ break :b zir.bodySlice(extra_index, linksection_body_len);
+ },
+ .addrspace_body = if (addrspace_body_len == 0) null else b: {
+ defer extra_index += addrspace_body_len;
+ break :b zir.bodySlice(extra_index, addrspace_body_len);
+ },
+ };
+ }
+ };
+
/// Stored inside extra, with trailing arguments according to `args_len`.
/// Implicit 0. arg_0_start: u32, // always same as `args_len`
/// 1. arg_end: u32, // for each `args_len`
@@ -2913,37 +3019,14 @@ pub const Inst = struct {
/// 3. backing_int_body_len: u32, // if has_backing_int
/// 4. backing_int_ref: Ref, // if has_backing_int and backing_int_body_len is 0
/// 5. backing_int_body_inst: Inst, // if has_backing_int and backing_int_body_len is > 0
- /// 6. 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 or an address space expression
- /// 7. decl: { // for every decls_len
- /// src_hash: [4]u32, // hash of source bytes
- /// line: u32, // line number of decl, relative to parent
- /// name: NullTerminatedString, // null terminated string index
- /// - 0 means comptime or usingnamespace decl.
- /// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace
- /// - 1 means test decl with no name.
- /// - 2 means that the test is a decltest, doc_comment gives the name of the identifier
- /// - if there is a 0 byte at the position `name` indexes, it indicates
- /// this is a test decl, and the name starts at `name+1`.
- /// value: Index,
- /// doc_comment: u32, .empty if no doc comment, if this is a decltest, doc_comment references the decl name in the string table
- /// align: Ref, // if corresponding bit is set
- /// link_section_or_address_space: { // if corresponding bit is set.
- /// link_section: Ref,
- /// address_space: Ref,
- /// }
- /// }
- /// 8. flags: u32 // for every 8 fields
+ /// 6. decl: Index, // for every decls_len; points to a `declaration` instruction
+ /// 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: whether corresponding field has a type expression
- /// 9. fields: { // for every fields_len
+ /// 8. fields: { // for every fields_len
/// field_name: u32, // if !is_tuple
/// doc_comment: NullTerminatedString, // .empty if no doc comment
/// field_type: Ref, // if corresponding bit is not set. none means anytype.
@@ -3009,33 +3092,11 @@ pub const Inst = struct {
/// 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 or an address space expression
- /// 6. decl: { // for every decls_len
- /// src_hash: [4]u32, // hash of source bytes
- /// line: u32, // line number of decl, relative to parent
- /// name: NullTerminatedString, // null terminated string index
- /// - 0 means comptime or usingnamespace decl.
- /// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace
- /// - 1 means test decl with no name.
- /// - if there is a 0 byte at the position `name` indexes, it indicates
- /// this is a test decl, and the name starts at `name+1`.
- /// value: Index,
- /// doc_comment: u32, // .empty if no doc_comment
- /// align: Ref, // if corresponding bit is set
- /// link_section_or_address_space: { // if corresponding bit is set.
- /// link_section: Ref,
- /// address_space: Ref,
- /// }
- /// }
- /// 7. inst: Index // for every body_len
- /// 8. has_bits: u32 // for every 32 fields
+ /// 5. decl: Index, // for every decls_len; points to a `declaration` instruction
+ /// 6. inst: Index // for every body_len
+ /// 7. has_bits: u32 // for every 32 fields
/// - the bit is whether corresponding field has an value expression
- /// 9. fields: { // for every fields_len
+ /// 8. fields: { // for every fields_len
/// field_name: u32,
/// doc_comment: u32, // .empty if no doc_comment
/// value: Ref, // if corresponding bit is set
@@ -3059,37 +3120,15 @@ pub const Inst = struct {
/// 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 or an address space expression
- /// 6. decl: { // for every decls_len
- /// src_hash: [4]u32, // hash of source bytes
- /// line: u32, // line number of decl, relative to parent
- /// name: NullTerminatedString, // null terminated string index
- /// - 0 means comptime or usingnamespace decl.
- /// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace
- /// - 1 means test decl with no name.
- /// - if there is a 0 byte at the position `name` indexes, it indicates
- /// this is a test decl, and the name starts at `name+1`.
- /// value: Index,
- /// doc_comment: NullTerminatedString, // .empty if no doc comment
- /// align: Ref, // if corresponding bit is set
- /// link_section_or_address_space: { // if corresponding bit is set.
- /// link_section: Ref,
- /// address_space: Ref,
- /// }
- /// }
- /// 7. inst: Index // for every body_len
- /// 8. has_bits: u32 // for every 8 fields
+ /// 5. decl: Index, // for every decls_len; points to a `declaration` instruction
+ /// 6. inst: Index // for every body_len
+ /// 7. 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
- /// 9. fields: { // for every fields_len
+ /// 8. fields: { // for every fields_len
/// field_name: NullTerminatedString, // null terminated string index
/// doc_comment: NullTerminatedString, // .empty if no doc comment
/// field_type: Ref, // if corresponding bit is set
@@ -3121,29 +3160,7 @@ pub const Inst = struct {
/// Trailing:
/// 0. src_node: i32, // if has_src_node
/// 1. decls_len: u32, // if has_decls_len
- /// 2. 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 or an address space expression
- /// 3. decl: { // for every decls_len
- /// src_hash: [4]u32, // hash of source bytes
- /// line: u32, // line number of decl, relative to parent
- /// name: NullTerminatedString, // null terminated string index
- /// - 0 means comptime or usingnamespace decl.
- /// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace
- /// - 1 means test decl with no name.
- /// - if there is a 0 byte at the position `name` indexes, it indicates
- /// this is a test decl, and the name starts at `name+1`.
- /// value: Index,
- /// doc_comment: NullTerminatedString, // .empty if no doc comment,
- /// align: Ref, // if corresponding bit is set
- /// link_section_or_address_space: { // if corresponding bit is set.
- /// link_section: Ref,
- /// address_space: Ref,
- /// }
- /// }
+ /// 2. decl: Index, // for every decls_len; points to a `declaration` instruction
pub const OpaqueDecl = struct {
pub const Small = packed struct {
has_src_node: bool,
@@ -3407,44 +3424,17 @@ pub const Inst = struct {
pub const SpecialProng = enum { none, @"else", under };
pub const DeclIterator = struct {
- extra_index: usize,
- bit_bag_index: usize,
- cur_bit_bag: u32,
- decl_i: u32,
- decls_len: u32,
+ extra_index: u32,
+ decls_remaining: u32,
zir: Zir,
- pub const Item = struct {
- name: [:0]const u8,
- sub_index: ExtraIndex,
- flags: u4,
- };
-
- pub fn next(it: *DeclIterator) ?Item {
- if (it.decl_i >= it.decls_len) return null;
-
- if (it.decl_i % 8 == 0) {
- it.cur_bit_bag = it.zir.extra[it.bit_bag_index];
- it.bit_bag_index += 1;
- }
- it.decl_i += 1;
-
- const flags: u4 = @truncate(it.cur_bit_bag);
- it.cur_bit_bag >>= 4;
-
- const sub_index: ExtraIndex = @enumFromInt(it.extra_index);
- it.extra_index += 5; // src_hash(4) + line(1)
- const name = it.zir.nullTerminatedString(@enumFromInt(it.zir.extra[it.extra_index]));
- it.extra_index += 3; // name(1) + value(1) + doc_comment(1)
- it.extra_index += @as(u1, @truncate(flags >> 2)); // align
- it.extra_index += @as(u1, @truncate(flags >> 3)); // link_section
- it.extra_index += @as(u1, @truncate(flags >> 3)); // address_space
-
- return Item{
- .sub_index = sub_index,
- .name = name,
- .flags = flags,
- };
+ pub fn next(it: *DeclIterator) ?Inst.Index {
+ if (it.decls_remaining == 0) return null;
+ const decl_inst: Zir.Inst.Index = @enumFromInt(it.zir.extra[it.extra_index]);
+ it.extra_index += 1;
+ it.decls_remaining -= 1;
+ assert(it.zir.instructions.items(.tag)[@intFromEnum(decl_inst)] == .declaration);
+ return decl_inst;
}
};
@@ -3454,14 +3444,18 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator {
switch (tags[@intFromEnum(decl_inst)]) {
// Functions are allowed and yield no iterations.
// There is one case matching this in the extended instruction set below.
- .func, .func_inferred, .func_fancy => return declIteratorInner(zir, 0, 0),
+ .func, .func_inferred, .func_fancy => return .{
+ .extra_index = undefined,
+ .decls_remaining = 0,
+ .zir = zir,
+ },
.extended => {
const extended = datas[@intFromEnum(decl_inst)].extended;
switch (extended.opcode) {
.struct_decl => {
const small: Inst.StructDecl.Small = @bitCast(extended.small);
- var extra_index: usize = extended.operand;
+ var extra_index: u32 = extended.operand;
extra_index += @intFromBool(small.has_src_node);
extra_index += @intFromBool(small.has_fields_len);
const decls_len = if (small.has_decls_len) decls_len: {
@@ -3480,11 +3474,15 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator {
}
}
- return declIteratorInner(zir, extra_index, decls_len);
+ return .{
+ .extra_index = extra_index,
+ .decls_remaining = decls_len,
+ .zir = zir,
+ };
},
.enum_decl => {
const small: Inst.EnumDecl.Small = @bitCast(extended.small);
- var extra_index: usize = extended.operand;
+ var extra_index: u32 = extended.operand;
extra_index += @intFromBool(small.has_src_node);
extra_index += @intFromBool(small.has_tag_type);
extra_index += @intFromBool(small.has_body_len);
@@ -3495,11 +3493,15 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator {
break :decls_len decls_len;
} else 0;
- return declIteratorInner(zir, extra_index, decls_len);
+ return .{
+ .extra_index = extra_index,
+ .decls_remaining = decls_len,
+ .zir = zir,
+ };
},
.union_decl => {
const small: Inst.UnionDecl.Small = @bitCast(extended.small);
- var extra_index: usize = extended.operand;
+ var extra_index: u32 = extended.operand;
extra_index += @intFromBool(small.has_src_node);
extra_index += @intFromBool(small.has_tag_type);
extra_index += @intFromBool(small.has_body_len);
@@ -3510,11 +3512,15 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator {
break :decls_len decls_len;
} else 0;
- return declIteratorInner(zir, extra_index, decls_len);
+ return .{
+ .extra_index = extra_index,
+ .decls_remaining = decls_len,
+ .zir = zir,
+ };
},
.opaque_decl => {
const small: Inst.OpaqueDecl.Small = @bitCast(extended.small);
- var extra_index: usize = extended.operand;
+ var extra_index: u32 = extended.operand;
extra_index += @intFromBool(small.has_src_node);
const decls_len = if (small.has_decls_len) decls_len: {
const decls_len = zir.extra[extra_index];
@@ -3522,7 +3528,11 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator {
break :decls_len decls_len;
} else 0;
- return declIteratorInner(zir, extra_index, decls_len);
+ return .{
+ .extra_index = extra_index,
+ .decls_remaining = decls_len,
+ .zir = zir,
+ };
},
else => unreachable,
}
@@ -3531,25 +3541,17 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator {
}
}
-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 = extra_index + bit_bags_count,
- .bit_bag_index = extra_index,
- .cur_bit_bag = undefined,
- .decl_i = 0,
- .decls_len = decls_len,
- };
-}
-
/// The iterator would have to allocate memory anyway to iterate. So here we populate
/// an ArrayList as the result.
-pub fn findDecls(zir: Zir, list: *std.ArrayList(Inst.Index), decl_sub_index: ExtraIndex) !void {
- const block_inst: Zir.Inst.Index = @enumFromInt(zir.extra[@intFromEnum(decl_sub_index) + 6]);
+pub fn findDecls(zir: Zir, list: *std.ArrayList(Inst.Index), decl_inst: Zir.Inst.Index) !void {
list.clearRetainingCapacity();
+ const declaration, const extra_end = zir.getDeclaration(decl_inst);
+ const bodies = declaration.getBodies(extra_end, zir);
- return zir.findDeclsInner(list, block_inst);
+ try zir.findDeclsBody(list, bodies.value_body);
+ if (bodies.align_body) |b| try zir.findDeclsBody(list, b);
+ if (bodies.linksection_body) |b| try zir.findDeclsBody(list, b);
+ if (bodies.addrspace_body) |b| try zir.findDeclsBody(list, b);
}
fn findDeclsInner(
@@ -3791,8 +3793,17 @@ pub fn getParamBody(zir: Zir, fn_inst: Inst.Index) []const Zir.Inst.Index {
else => unreachable,
};
- const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(param_block_index)].pl_node.payload_index);
- return zir.bodySlice(param_block.end, param_block.data.body_len);
+ switch (tags[@intFromEnum(param_block_index)]) {
+ .block, .block_comptime, .block_inline => {
+ const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(param_block_index)].pl_node.payload_index);
+ return zir.bodySlice(param_block.end, param_block.data.body_len);
+ },
+ .declaration => {
+ const decl, const extra_end = zir.getDeclaration(param_block_index);
+ return decl.getBodies(extra_end, zir).value_body;
+ },
+ else => unreachable,
+ }
}
pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
@@ -3888,12 +3899,17 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
},
else => unreachable,
};
- switch (tags[@intFromEnum(info.param_block)]) {
- .block, .block_comptime, .block_inline => {}, // OK
- else => unreachable, // assertion failure
- }
- const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(info.param_block)].pl_node.payload_index);
- const param_body = zir.bodySlice(param_block.end, param_block.data.body_len);
+ const param_body = switch (tags[@intFromEnum(info.param_block)]) {
+ .block, .block_comptime, .block_inline => param_body: {
+ const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(info.param_block)].pl_node.payload_index);
+ break :param_body zir.bodySlice(param_block.end, param_block.data.body_len);
+ },
+ .declaration => param_body: {
+ const decl, const extra_end = zir.getDeclaration(info.param_block);
+ break :param_body decl.getBodies(extra_end, zir).value_body;
+ },
+ else => unreachable,
+ };
var total_params_len: u32 = 0;
for (param_body) |inst| {
switch (tags[@intFromEnum(inst)]) {
@@ -3912,3 +3928,13 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
.total_params_len = total_params_len,
};
}
+
+pub fn getDeclaration(zir: Zir, inst: Zir.Inst.Index) struct { Inst.Declaration, u32 } {
+ assert(zir.instructions.items(.tag)[@intFromEnum(inst)] == .declaration);
+ const pl_node = zir.instructions.items(.data)[@intFromEnum(inst)].pl_node;
+ const extra = zir.extraData(Inst.Declaration, pl_node.payload_index);
+ return .{
+ extra.data,
+ @intCast(extra.end),
+ };
+}