aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Lugg <mlugg@mlugg.co.uk>2024-12-25 02:58:27 +0000
committerGitHub <noreply@github.com>2024-12-25 02:58:27 +0000
commit497592c9b45a94fb7b6028bf45b80f183e395a9b (patch)
tree467873c408750cb4223f3ccf31775e42ec9fbd5c
parentaf5e731729592af4a5716edd3b1e03264d66ea46 (diff)
parent3afda4322c34dedc2319701fdfac3505c8d311e9 (diff)
downloadzig-497592c9b45a94fb7b6028bf45b80f183e395a9b.tar.gz
zig-497592c9b45a94fb7b6028bf45b80f183e395a9b.zip
Merge pull request #22303 from mlugg/131-new
compiler: analyze type and value of global declarations separately
-rw-r--r--lib/std/zig/AstGen.zig1118
-rw-r--r--lib/std/zig/Zir.zig487
-rw-r--r--src/Compilation.zig81
-rw-r--r--src/InternPool.zig691
-rw-r--r--src/Sema.zig677
-rw-r--r--src/Sema/comptime_ptr_access.zig5
-rw-r--r--src/Type.zig4
-rw-r--r--src/Value.zig7
-rw-r--r--src/Zcu.zig334
-rw-r--r--src/Zcu/PerThread.zig1627
-rw-r--r--src/arch/wasm/CodeGen.zig10
-rw-r--r--src/codegen.zig17
-rw-r--r--src/codegen/c.zig61
-rw-r--r--src/codegen/llvm.zig67
-rw-r--r--src/codegen/spirv.zig29
-rw-r--r--src/link.zig2
-rw-r--r--src/link/C.zig20
-rw-r--r--src/link/Coff.zig17
-rw-r--r--src/link/Dwarf.zig86
-rw-r--r--src/link/Elf/ZigObject.zig25
-rw-r--r--src/link/MachO/ZigObject.zig23
-rw-r--r--src/link/Plan9.zig4
-rw-r--r--src/link/Wasm/ZigObject.zig15
-rw-r--r--src/print_zir.zig98
-rw-r--r--test/behavior/globals.zig96
-rw-r--r--test/cases/compile_errors/address_of_threadlocal_not_comptime_known.zig3
-rw-r--r--test/cases/compile_errors/self_reference_missing_const.zig11
-rw-r--r--test/cases/compile_errors/type_variables_must_be_constant.zig4
-rw-r--r--test/cases/compile_errors/use_invalid_number_literal_as_array_index.zig4
29 files changed, 3086 insertions, 2537 deletions
diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig
index fcec69ed8f..4a9b948beb 100644
--- a/lib/std/zig/AstGen.zig
+++ b/lib/std/zig/AstGen.zig
@@ -106,7 +106,6 @@ 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"),
@@ -1317,12 +1316,45 @@ fn fnProtoExpr(
return astgen.failTok(some, "function type cannot have a name", .{});
}
+ if (fn_proto.ast.align_expr != 0) {
+ return astgen.failNode(fn_proto.ast.align_expr, "function type cannot have an alignment", .{});
+ }
+
+ if (fn_proto.ast.addrspace_expr != 0) {
+ return astgen.failNode(fn_proto.ast.addrspace_expr, "function type cannot have an addrspace", .{});
+ }
+
+ if (fn_proto.ast.section_expr != 0) {
+ return astgen.failNode(fn_proto.ast.section_expr, "function type cannot have a linksection", .{});
+ }
+
+ const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
+ const is_inferred_error = token_tags[maybe_bang] == .bang;
+ if (is_inferred_error) {
+ return astgen.failTok(maybe_bang, "function type cannot have an inferred error set", .{});
+ }
+
const is_extern = blk: {
const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
break :blk token_tags[maybe_extern_token] == .keyword_extern;
};
assert(!is_extern);
+ return fnProtoExprInner(gz, scope, ri, node, fn_proto, false);
+}
+
+fn fnProtoExprInner(
+ gz: *GenZir,
+ scope: *Scope,
+ ri: ResultInfo,
+ node: Ast.Node.Index,
+ fn_proto: Ast.full.FnProto,
+ implicit_ccc: bool,
+) InnerError!Zir.Inst.Ref {
+ const astgen = gz.astgen;
+ const tree = astgen.tree;
+ const token_tags = tree.tokens.items(.tag);
+
var block_scope = gz.makeSubBlock(scope);
defer block_scope.unstack();
@@ -1386,18 +1418,6 @@ fn fnProtoExpr(
break :is_var_args false;
};
- if (fn_proto.ast.align_expr != 0) {
- return astgen.failNode(fn_proto.ast.align_expr, "function type cannot have an alignment", .{});
- }
-
- if (fn_proto.ast.addrspace_expr != 0) {
- return astgen.failNode(fn_proto.ast.addrspace_expr, "function type cannot have an addrspace", .{});
- }
-
- if (fn_proto.ast.section_expr != 0) {
- return astgen.failNode(fn_proto.ast.section_expr, "function type cannot have a linksection", .{});
- }
-
const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0)
try expr(
&block_scope,
@@ -1405,14 +1425,11 @@ fn fnProtoExpr(
.{ .rl = .{ .coerced_ty = try block_scope.addBuiltinValue(fn_proto.ast.callconv_expr, .calling_convention) } },
fn_proto.ast.callconv_expr,
)
+ else if (implicit_ccc)
+ try block_scope.addBuiltinValue(node, .calling_convention_c)
else
- Zir.Inst.Ref.none;
+ .none;
- const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
- const is_inferred_error = token_tags[maybe_bang] == .bang;
- if (is_inferred_error) {
- return astgen.failTok(maybe_bang, "function type cannot have an inferred error set", .{});
- }
const ret_ty = try expr(&block_scope, scope, coerced_type_ri, fn_proto.ast.return_type);
const result = try block_scope.addFunc(.{
@@ -1428,11 +1445,8 @@ fn fnProtoExpr(
.param_block = block_inst,
.body_gz = null,
- .lib_name = .empty,
.is_var_args = is_var_args,
.is_inferred_error = false,
- .is_test = false,
- .is_extern = false,
.is_noinline = false,
.noalias_bits = noalias_bits,
@@ -4121,17 +4135,6 @@ fn fnDecl(
const saved_cursor = astgen.saveSourceCursor();
- var decl_gz: GenZir = .{
- .is_comptime = true,
- .decl_node_index = fn_proto.ast.proto_node,
- .decl_line = astgen.source_line,
- .parent = scope,
- .astgen = astgen,
- .instructions = gz.instructions,
- .instructions_top = gz.instructions.items.len,
- };
- defer decl_gz.unstack();
-
const decl_column = astgen.source_column;
// Set this now, since parameter types, return type, etc may be generic.
@@ -4152,12 +4155,140 @@ fn fnDecl(
const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
break :blk token_tags[maybe_inline_token] == .keyword_inline;
};
+ const lib_name = if (fn_proto.lib_name) |lib_name_token| blk: {
+ const lib_name_str = try astgen.strLitAsString(lib_name_token);
+ const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len];
+ if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
+ return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
+ } else if (lib_name_str.len == 0) {
+ return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
+ }
+ break :blk lib_name_str.index;
+ } else .empty;
+ if (fn_proto.ast.callconv_expr != 0 and has_inline_keyword) {
+ return astgen.failNode(
+ fn_proto.ast.callconv_expr,
+ "explicit callconv incompatible with inline keyword",
+ .{},
+ );
+ }
+ const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
+ const is_inferred_error = token_tags[maybe_bang] == .bang;
+ if (body_node == 0) {
+ if (!is_extern) {
+ return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{});
+ }
+ if (is_inferred_error) {
+ return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
+ }
+ } else {
+ assert(!is_extern); // validated by parser (TODO why???)
+ }
+
+ wip_members.nextDecl(decl_inst);
+
+ var type_gz: GenZir = .{
+ .is_comptime = true,
+ .decl_node_index = fn_proto.ast.proto_node,
+ .decl_line = astgen.source_line,
+ .parent = scope,
+ .astgen = astgen,
+ .instructions = gz.instructions,
+ .instructions_top = gz.instructions.items.len,
+ };
+ defer type_gz.unstack();
+
+ if (is_extern) {
+ // We include a function *type*, not a value.
+ const type_inst = try fnProtoExprInner(&type_gz, &type_gz.base, .{ .rl = .none }, decl_node, fn_proto, true);
+ _ = try type_gz.addBreakWithSrcNode(.break_inline, decl_inst, type_inst, decl_node);
+ }
+
+ var align_gz = type_gz.makeSubBlock(scope);
+ defer align_gz.unstack();
+
+ if (fn_proto.ast.align_expr != 0) {
+ astgen.restoreSourceCursor(saved_cursor);
+ const inst = try expr(&align_gz, &align_gz.base, coerced_align_ri, fn_proto.ast.align_expr);
+ _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node);
+ }
+
+ var linksection_gz = align_gz.makeSubBlock(scope);
+ defer linksection_gz.unstack();
+
+ if (fn_proto.ast.section_expr != 0) {
+ astgen.restoreSourceCursor(saved_cursor);
+ const inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, fn_proto.ast.section_expr);
+ _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node);
+ }
+
+ var addrspace_gz = linksection_gz.makeSubBlock(scope);
+ defer addrspace_gz.unstack();
+
+ if (fn_proto.ast.addrspace_expr != 0) {
+ astgen.restoreSourceCursor(saved_cursor);
+ const addrspace_ty = try addrspace_gz.addBuiltinValue(fn_proto.ast.addrspace_expr, .address_space);
+ const inst = try expr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, fn_proto.ast.section_expr);
+ _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node);
+ }
+
+ var value_gz = addrspace_gz.makeSubBlock(scope);
+ defer value_gz.unstack();
+
+ if (!is_extern) {
+ // We include a function *value*, not a type.
+ astgen.restoreSourceCursor(saved_cursor);
+ try astgen.fnDeclInner(&value_gz, &value_gz.base, saved_cursor, decl_inst, decl_node, body_node, fn_proto);
+ }
+
+ // *Now* we can incorporate the full source code into the hasher.
+ astgen.src_hasher.update(tree.getNodeSource(decl_node));
+
+ var hash: std.zig.SrcHash = undefined;
+ astgen.src_hasher.final(&hash);
+ try setDeclaration(decl_inst, .{
+ .src_hash = hash,
+ .src_line = type_gz.decl_line,
+ .src_column = decl_column,
+
+ .kind = .@"const",
+ .name = try astgen.identAsString(fn_name_token),
+ .is_pub = is_pub,
+ .is_threadlocal = false,
+ .linkage = if (is_extern) .@"extern" else if (is_export) .@"export" else .normal,
+ .lib_name = lib_name,
+
+ .type_gz = &type_gz,
+ .align_gz = &align_gz,
+ .linksection_gz = &linksection_gz,
+ .addrspace_gz = &addrspace_gz,
+ .value_gz = &value_gz,
+ });
+}
+
+fn fnDeclInner(
+ astgen: *AstGen,
+ decl_gz: *GenZir,
+ scope: *Scope,
+ saved_cursor: SourceCursor,
+ decl_inst: Zir.Inst.Index,
+ decl_node: Ast.Node.Index,
+ body_node: Ast.Node.Index,
+ fn_proto: Ast.full.FnProto,
+) InnerError!void {
+ const tree = astgen.tree;
+ const token_tags = tree.tokens.items(.tag);
+
const is_noinline = blk: {
const maybe_noinline_token = fn_proto.extern_export_inline_token orelse break :blk false;
break :blk token_tags[maybe_noinline_token] == .keyword_noinline;
};
-
- wip_members.nextDecl(decl_inst);
+ const has_inline_keyword = blk: {
+ const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
+ break :blk token_tags[maybe_inline_token] == .keyword_inline;
+ };
+ const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
+ const is_inferred_error = token_tags[maybe_bang] == .bang;
// Note that the capacity here may not be sufficient, as this does not include `anytype` parameters.
var param_insts: std.ArrayListUnmanaged(Zir.Inst.Index) = try .initCapacity(astgen.arena, fn_proto.ast.params.len);
@@ -4192,11 +4323,9 @@ fn fnDecl(
break :blk .empty;
const param_name = try astgen.identAsString(name_token);
- if (!is_extern) {
- try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes, .@"function parameter");
- }
+ try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes, .@"function parameter");
break :blk param_name;
- } else if (!is_extern) {
+ } else {
if (param.anytype_ellipsis3) |tok| {
return astgen.failTok(tok, "missing parameter name", .{});
} else {
@@ -4225,7 +4354,7 @@ fn fnDecl(
}
return astgen.failNode(param.type_expr, "missing parameter name", .{});
}
- } else .empty;
+ };
const param_inst = if (is_anytype) param: {
const name_token = param.name_token orelse param.anytype_ellipsis3.?;
@@ -4251,12 +4380,12 @@ fn fnDecl(
break :param param_inst.toRef();
};
- if (param_name == .empty or is_extern) continue;
+ if (param_name == .empty) continue;
const sub_scope = try astgen.arena.create(Scope.LocalVal);
sub_scope.* = .{
.parent = params_scope,
- .gen_zir = &decl_gz,
+ .gen_zir = decl_gz,
.name = param_name,
.inst = param_inst,
.token_src = param.name_token.?,
@@ -4268,23 +4397,9 @@ fn fnDecl(
break :is_var_args false;
};
- const lib_name = if (fn_proto.lib_name) |lib_name_token| blk: {
- const lib_name_str = try astgen.strLitAsString(lib_name_token);
- const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len];
- if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
- return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
- } else if (lib_name_str.len == 0) {
- return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
- }
- break :blk lib_name_str.index;
- } else .empty;
-
- const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
- const is_inferred_error = token_tags[maybe_bang] == .bang;
-
// After creating the function ZIR instruction, it will need to update the break
- // instructions inside the expression blocks for align, addrspace, cc, and ret_ty
- // to use the function instruction as the "block" to break from.
+ // instructions inside the expression blocks for cc and ret_ty to use the function
+ // instruction as the body to break from.
var ret_gz = decl_gz.makeSubBlock(params_scope);
defer ret_gz.unstack();
@@ -4309,13 +4424,6 @@ fn fnDecl(
defer cc_gz.unstack();
const cc_ref: Zir.Inst.Ref = blk: {
if (fn_proto.ast.callconv_expr != 0) {
- if (has_inline_keyword) {
- return astgen.failNode(
- fn_proto.ast.callconv_expr,
- "explicit callconv incompatible with inline keyword",
- .{},
- );
- }
const inst = try expr(
&cc_gz,
scope,
@@ -4328,10 +4436,6 @@ fn fnDecl(
}
_ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst);
break :blk inst;
- } else if (is_extern) {
- const inst = try cc_gz.addBuiltinValue(decl_node, .calling_convention_c);
- _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst);
- break :blk inst;
} else if (has_inline_keyword) {
const inst = try cc_gz.addBuiltinValue(decl_node, .calling_convention_inline);
_ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst);
@@ -4341,167 +4445,86 @@ fn fnDecl(
}
};
- const func_inst: Zir.Inst.Ref = if (body_node == 0) func: {
- if (!is_extern) {
- return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{});
- }
- if (is_inferred_error) {
- return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
- }
- break :func try decl_gz.addFunc(.{
- .src_node = decl_node,
- .cc_ref = cc_ref,
- .cc_gz = &cc_gz,
- .ret_ref = ret_ref,
- .ret_gz = &ret_gz,
- .ret_param_refs = ret_body_param_refs,
- .param_block = decl_inst,
- .param_insts = param_insts.items,
- .body_gz = null,
- .lib_name = lib_name,
- .is_var_args = is_var_args,
- .is_inferred_error = false,
- .is_test = false,
- .is_extern = true,
- .is_noinline = is_noinline,
- .noalias_bits = noalias_bits,
- .proto_hash = undefined, // ignored for `body_gz == null`
- });
- } else func: {
- var body_gz: GenZir = .{
- .is_comptime = false,
- .decl_node_index = fn_proto.ast.proto_node,
- .decl_line = decl_gz.decl_line,
- .parent = params_scope,
- .astgen = astgen,
- .instructions = gz.instructions,
- .instructions_top = gz.instructions.items.len,
- };
- defer body_gz.unstack();
-
- // We want `params_scope` to be stacked like this:
- // body_gz (top)
- // param2
- // param1
- // param0
- // decl_gz (bottom)
-
- // Construct the prototype hash.
- // Leave `astgen.src_hasher` unmodified; this will be used for hashing
- // the *whole* function declaration, including its body.
- var proto_hasher = astgen.src_hasher;
- const proto_node = tree.nodes.items(.data)[decl_node].lhs;
- proto_hasher.update(tree.getNodeSource(proto_node));
- var proto_hash: std.zig.SrcHash = undefined;
- proto_hasher.final(&proto_hash);
-
- const prev_fn_block = astgen.fn_block;
- const prev_fn_ret_ty = astgen.fn_ret_ty;
- defer {
- astgen.fn_block = prev_fn_block;
- astgen.fn_ret_ty = prev_fn_ret_ty;
- }
- astgen.fn_block = &body_gz;
- astgen.fn_ret_ty = if (is_inferred_error or ret_ref.toIndex() != null) r: {
- // We're essentially guaranteed to need the return type at some point,
- // since the return type is likely not `void` or `noreturn` so there
- // will probably be an explicit return requiring RLS. Fetch this
- // return type now so the rest of the function can use it.
- break :r try body_gz.addNode(.ret_type, decl_node);
- } else ret_ref;
-
- const prev_var_args = astgen.fn_var_args;
- astgen.fn_var_args = is_var_args;
- defer astgen.fn_var_args = prev_var_args;
-
- astgen.advanceSourceCursorToNode(body_node);
- const lbrace_line = astgen.source_line - decl_gz.decl_line;
- const lbrace_column = astgen.source_column;
-
- _ = try fullBodyExpr(&body_gz, &body_gz.base, .{ .rl = .none }, body_node, .allow_branch_hint);
- try checkUsed(gz, scope, params_scope);
-
- if (!body_gz.endsWithNoReturn()) {
- // As our last action before the return, "pop" the error trace if needed
- _ = try body_gz.addRestoreErrRetIndex(.ret, .always, decl_node);
-
- // Add implicit return at end of function.
- _ = try body_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
- }
-
- break :func try decl_gz.addFunc(.{
- .src_node = decl_node,
- .cc_ref = cc_ref,
- .cc_gz = &cc_gz,
- .ret_ref = ret_ref,
- .ret_gz = &ret_gz,
- .ret_param_refs = ret_body_param_refs,
- .lbrace_line = lbrace_line,
- .lbrace_column = lbrace_column,
- .param_block = decl_inst,
- .param_insts = param_insts.items,
- .body_gz = &body_gz,
- .lib_name = lib_name,
- .is_var_args = is_var_args,
- .is_inferred_error = is_inferred_error,
- .is_test = false,
- .is_extern = false,
- .is_noinline = is_noinline,
- .noalias_bits = noalias_bits,
- .proto_hash = proto_hash,
- });
+ var body_gz: GenZir = .{
+ .is_comptime = false,
+ .decl_node_index = fn_proto.ast.proto_node,
+ .decl_line = decl_gz.decl_line,
+ .parent = params_scope,
+ .astgen = astgen,
+ .instructions = decl_gz.instructions,
+ .instructions_top = decl_gz.instructions.items.len,
};
+ defer body_gz.unstack();
+
+ // The scope stack looks like this:
+ // body_gz (top)
+ // param2
+ // param1
+ // param0
+ // decl_gz (bottom)
+
+ // Construct the prototype hash.
+ // Leave `astgen.src_hasher` unmodified; this will be used for hashing
+ // the *whole* function declaration, including its body.
+ var proto_hasher = astgen.src_hasher;
+ const proto_node = tree.nodes.items(.data)[decl_node].lhs;
+ proto_hasher.update(tree.getNodeSource(proto_node));
+ var proto_hash: std.zig.SrcHash = undefined;
+ proto_hasher.final(&proto_hash);
- // Before we stack more stuff onto `decl_gz`, add its final instruction.
- _ = try decl_gz.addBreak(.break_inline, decl_inst, func_inst);
+ const prev_fn_block = astgen.fn_block;
+ const prev_fn_ret_ty = astgen.fn_ret_ty;
+ defer {
+ astgen.fn_block = prev_fn_block;
+ astgen.fn_ret_ty = prev_fn_ret_ty;
+ }
+ astgen.fn_block = &body_gz;
+ astgen.fn_ret_ty = if (is_inferred_error or ret_ref.toIndex() != null) r: {
+ // We're essentially guaranteed to need the return type at some point,
+ // since the return type is likely not `void` or `noreturn` so there
+ // will probably be an explicit return requiring RLS. Fetch this
+ // return type now so the rest of the function can use it.
+ break :r try body_gz.addNode(.ret_type, decl_node);
+ } else ret_ref;
- // Now that `cc_gz,` `ret_gz`, and `body_gz` are unstacked, we evaluate align, addrspace, and linksection.
+ const prev_var_args = astgen.fn_var_args;
+ astgen.fn_var_args = is_var_args;
+ defer astgen.fn_var_args = prev_var_args;
- // We're jumping back in source, so restore the cursor.
- astgen.restoreSourceCursor(saved_cursor);
+ astgen.advanceSourceCursorToNode(body_node);
+ const lbrace_line = astgen.source_line - decl_gz.decl_line;
+ const lbrace_column = astgen.source_column;
- var align_gz = decl_gz.makeSubBlock(scope);
- defer align_gz.unstack();
- if (fn_proto.ast.align_expr != 0) {
- const inst = try expr(&decl_gz, &decl_gz.base, coerced_align_ri, fn_proto.ast.align_expr);
- _ = try align_gz.addBreak(.break_inline, decl_inst, inst);
- }
+ _ = try fullBodyExpr(&body_gz, &body_gz.base, .{ .rl = .none }, body_node, .allow_branch_hint);
+ try checkUsed(decl_gz, scope, params_scope);
- var section_gz = align_gz.makeSubBlock(scope);
- defer section_gz.unstack();
- if (fn_proto.ast.section_expr != 0) {
- const inst = try expr(&decl_gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, fn_proto.ast.section_expr);
- _ = try section_gz.addBreak(.break_inline, decl_inst, inst);
- }
+ if (!body_gz.endsWithNoReturn()) {
+ // As our last action before the return, "pop" the error trace if needed
+ _ = try body_gz.addRestoreErrRetIndex(.ret, .always, decl_node);
- var addrspace_gz = section_gz.makeSubBlock(scope);
- defer addrspace_gz.unstack();
- if (fn_proto.ast.addrspace_expr != 0) {
- const addrspace_ty = try decl_gz.addBuiltinValue(fn_proto.ast.addrspace_expr, .address_space);
- const inst = try expr(&decl_gz, scope, .{ .rl = .{ .coerced_ty = addrspace_ty } }, fn_proto.ast.addrspace_expr);
- _ = try addrspace_gz.addBreak(.break_inline, decl_inst, inst);
+ // Add implicit return at end of function.
+ _ = try body_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
}
- // *Now* we can incorporate the full source code into the hasher.
- astgen.src_hasher.update(tree.getNodeSource(decl_node));
-
- var hash: std.zig.SrcHash = undefined;
- astgen.src_hasher.final(&hash);
- try setDeclaration(
- decl_inst,
- hash,
- .{ .named = fn_name_token },
- decl_gz.decl_line,
- decl_column,
- is_pub,
- is_export,
- &decl_gz,
- .{
- .align_gz = &align_gz,
- .linksection_gz = &section_gz,
- .addrspace_gz = &addrspace_gz,
- },
- );
+ const func_inst = try decl_gz.addFunc(.{
+ .src_node = decl_node,
+ .cc_ref = cc_ref,
+ .cc_gz = &cc_gz,
+ .ret_ref = ret_ref,
+ .ret_gz = &ret_gz,
+ .ret_param_refs = ret_body_param_refs,
+ .lbrace_line = lbrace_line,
+ .lbrace_column = lbrace_column,
+ .param_block = decl_inst,
+ .param_insts = param_insts.items,
+ .body_gz = &body_gz,
+ .is_var_args = is_var_args,
+ .is_inferred_error = is_inferred_error,
+ .is_noinline = is_noinline,
+ .noalias_bits = noalias_bits,
+ .proto_hash = proto_hash,
+ });
+ _ = try decl_gz.addBreakWithSrcNode(.break_inline, decl_inst, func_inst, decl_node);
}
fn globalVarDecl(
@@ -4522,26 +4545,7 @@ fn globalVarDecl(
astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
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 decl_inst = try gz.makeDeclaration(node);
-
const name_token = var_decl.ast.mut_token + 1;
- astgen.advanceSourceCursorToNode(node);
-
- var block_scope: GenZir = .{
- .parent = scope,
- .decl_node_index = node,
- .decl_line = astgen.source_line,
- .astgen = astgen,
- .is_comptime = true,
- .instructions = gz.instructions,
- .instructions_top = gz.instructions.items.len,
- };
- defer block_scope.unstack();
-
- const decl_column = astgen.source_column;
-
const is_pub = var_decl.visib_token != null;
const is_export = blk: {
const maybe_export_token = var_decl.extern_export_token orelse break :blk false;
@@ -4551,15 +4555,12 @@ fn globalVarDecl(
const maybe_extern_token = var_decl.extern_export_token orelse break :blk false;
break :blk token_tags[maybe_extern_token] == .keyword_extern;
};
- wip_members.nextDecl(decl_inst);
-
const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
if (!is_mutable) {
return astgen.failTok(tok, "threadlocal variable cannot be constant", .{});
}
break :blk true;
} else false;
-
const lib_name = if (var_decl.lib_name) |lib_name_token| blk: {
const lib_name_str = try astgen.strLitAsString(lib_name_token);
const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len];
@@ -4571,9 +4572,14 @@ fn globalVarDecl(
break :blk lib_name_str.index;
} else .empty;
- assert(var_decl.comptime_token == null); // handled by parser
+ astgen.advanceSourceCursorToNode(node);
+
+ const decl_column = astgen.source_column;
+
+ const decl_inst = try gz.makeDeclaration(node);
+ wip_members.nextDecl(decl_inst);
- const var_inst: Zir.Inst.Ref = if (var_decl.ast.init_node != 0) vi: {
+ if (var_decl.ast.init_node != 0) {
if (is_extern) {
return astgen.failNode(
var_decl.ast.init_node,
@@ -4581,102 +4587,91 @@ fn globalVarDecl(
.{},
);
}
+ } else {
+ if (!is_extern) {
+ return astgen.failNode(node, "variables must be initialized", .{});
+ }
+ }
- const type_inst: Zir.Inst.Ref = if (var_decl.ast.type_node != 0)
- try expr(
- &block_scope,
- &block_scope.base,
- coerced_type_ri,
- var_decl.ast.type_node,
- )
- else
- .none;
-
- block_scope.anon_name_strategy = .parent;
+ if (is_extern and var_decl.ast.type_node == 0) {
+ return astgen.failNode(node, "unable to infer variable type", .{});
+ }
- const init_inst = try expr(
- &block_scope,
- &block_scope.base,
- if (type_inst != .none) .{ .rl = .{ .ty = type_inst } } else .{ .rl = .none },
- var_decl.ast.init_node,
- );
+ assert(var_decl.comptime_token == null); // handled by parser
- if (is_mutable) {
- const var_inst = try block_scope.addVar(.{
- .var_type = type_inst,
- .lib_name = .empty,
- .align_inst = .none, // passed via the decls data
- .init = init_inst,
- .is_extern = false,
- .is_const = !is_mutable,
- .is_threadlocal = is_threadlocal,
- });
- break :vi var_inst;
- } else {
- break :vi init_inst;
- }
- } else if (!is_extern) {
- return astgen.failNode(node, "variables must be initialized", .{});
- } else if (var_decl.ast.type_node != 0) vi: {
- // Extern variable which has an explicit type.
- const type_inst = try typeExpr(&block_scope, &block_scope.base, var_decl.ast.type_node);
-
- block_scope.anon_name_strategy = .parent;
-
- const var_inst = try block_scope.addVar(.{
- .var_type = type_inst,
- .lib_name = lib_name,
- .align_inst = .none, // passed via the decls data
- .init = .none,
- .is_extern = true,
- .is_const = !is_mutable,
- .is_threadlocal = is_threadlocal,
- });
- break :vi var_inst;
- } else {
- return astgen.failNode(node, "unable to infer variable type", .{});
+ var type_gz: GenZir = .{
+ .parent = scope,
+ .decl_node_index = node,
+ .decl_line = astgen.source_line,
+ .astgen = astgen,
+ .is_comptime = true,
+ .instructions = gz.instructions,
+ .instructions_top = gz.instructions.items.len,
};
+ defer type_gz.unstack();
+
+ if (var_decl.ast.type_node != 0) {
+ const type_inst = try expr(&type_gz, &type_gz.base, coerced_type_ri, var_decl.ast.type_node);
+ _ = try type_gz.addBreakWithSrcNode(.break_inline, decl_inst, type_inst, node);
+ }
- // 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, decl_inst, var_inst, node);
+ var align_gz = type_gz.makeSubBlock(scope);
+ defer align_gz.unstack();
- var align_gz = block_scope.makeSubBlock(scope);
if (var_decl.ast.align_node != 0) {
- const align_inst = try fullBodyExpr(&align_gz, &align_gz.base, coerced_align_ri, var_decl.ast.align_node, .normal);
+ 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);
}
- var linksection_gz = align_gz.makeSubBlock(scope);
+ var linksection_gz = type_gz.makeSubBlock(scope);
+ defer linksection_gz.unstack();
+
if (var_decl.ast.section_node != 0) {
- const linksection_inst = try fullBodyExpr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, var_decl.ast.section_node, .normal);
+ 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);
+ var addrspace_gz = type_gz.makeSubBlock(scope);
+ defer addrspace_gz.unstack();
+
if (var_decl.ast.addrspace_node != 0) {
const addrspace_ty = try addrspace_gz.addBuiltinValue(var_decl.ast.addrspace_node, .address_space);
- const addrspace_inst = try fullBodyExpr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, var_decl.ast.addrspace_node, .normal);
+ const addrspace_inst = try expr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, var_decl.ast.addrspace_node);
_ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node);
}
+ var init_gz = type_gz.makeSubBlock(scope);
+ defer init_gz.unstack();
+
+ if (var_decl.ast.init_node != 0) {
+ init_gz.anon_name_strategy = .parent;
+ const init_ri: ResultInfo = if (var_decl.ast.type_node != 0) .{
+ .rl = .{ .coerced_ty = decl_inst.toRef() },
+ } else .{ .rl = .none };
+ const init_inst = try expr(&init_gz, &init_gz.base, init_ri, var_decl.ast.init_node);
+ _ = try init_gz.addBreakWithSrcNode(.break_inline, decl_inst, init_inst, node);
+ }
+
var hash: std.zig.SrcHash = undefined;
astgen.src_hasher.final(&hash);
- try setDeclaration(
- decl_inst,
- hash,
- .{ .named = name_token },
- block_scope.decl_line,
- decl_column,
- is_pub,
- is_export,
- &block_scope,
- .{
- .align_gz = &align_gz,
- .linksection_gz = &linksection_gz,
- .addrspace_gz = &addrspace_gz,
- },
- );
+ try setDeclaration(decl_inst, .{
+ .src_hash = hash,
+ .src_line = type_gz.decl_line,
+ .src_column = decl_column,
+
+ .kind = if (is_mutable) .@"var" else .@"const",
+ .name = try astgen.identAsString(name_token),
+ .is_pub = is_pub,
+ .is_threadlocal = is_threadlocal,
+ .linkage = if (is_extern) .@"extern" else if (is_export) .@"export" else .normal,
+ .lib_name = lib_name,
+
+ .type_gz = &type_gz,
+ .align_gz = &align_gz,
+ .linksection_gz = &linksection_gz,
+ .addrspace_gz = &addrspace_gz,
+ .value_gz = &init_gz,
+ });
}
fn comptimeDecl(
@@ -4702,37 +4697,45 @@ fn comptimeDecl(
wip_members.nextDecl(decl_inst);
astgen.advanceSourceCursorToNode(node);
- var decl_block: GenZir = .{
+ // This is just needed for the `setDeclaration` call.
+ var dummy_gz = gz.makeSubBlock(scope);
+ defer dummy_gz.unstack();
+
+ var comptime_gz: GenZir = .{
.is_comptime = true,
.decl_node_index = node,
.decl_line = astgen.source_line,
.parent = scope,
.astgen = astgen,
- .instructions = gz.instructions,
- .instructions_top = gz.instructions.items.len,
+ .instructions = dummy_gz.instructions,
+ .instructions_top = dummy_gz.instructions.items.len,
};
- defer decl_block.unstack();
+ defer comptime_gz.unstack();
const decl_column = astgen.source_column;
- const block_result = try fullBodyExpr(&decl_block, &decl_block.base, .{ .rl = .none }, body_node, .normal);
- if (decl_block.isEmpty() or !decl_block.refIsNoReturn(block_result)) {
- _ = try decl_block.addBreak(.break_inline, decl_inst, .void_value);
+ const block_result = try fullBodyExpr(&comptime_gz, &comptime_gz.base, .{ .rl = .none }, body_node, .normal);
+ if (comptime_gz.isEmpty() or !comptime_gz.refIsNoReturn(block_result)) {
+ _ = try comptime_gz.addBreak(.break_inline, decl_inst, .void_value);
}
var hash: std.zig.SrcHash = undefined;
astgen.src_hasher.final(&hash);
- try setDeclaration(
- decl_inst,
- hash,
- .@"comptime",
- decl_block.decl_line,
- decl_column,
- false,
- false,
- &decl_block,
- null,
- );
+ try setDeclaration(decl_inst, .{
+ .src_hash = hash,
+ .src_line = comptime_gz.decl_line,
+ .src_column = decl_column,
+ .kind = .@"comptime",
+ .name = .empty,
+ .is_pub = false,
+ .is_threadlocal = false,
+ .linkage = .normal,
+ .type_gz = &dummy_gz,
+ .align_gz = &dummy_gz,
+ .linksection_gz = &dummy_gz,
+ .addrspace_gz = &dummy_gz,
+ .value_gz = &comptime_gz,
+ });
}
fn usingnamespaceDecl(
@@ -4764,7 +4767,11 @@ fn usingnamespaceDecl(
wip_members.nextDecl(decl_inst);
astgen.advanceSourceCursorToNode(node);
- var decl_block: GenZir = .{
+ // This is just needed for the `setDeclaration` call.
+ var dummy_gz = gz.makeSubBlock(scope);
+ defer dummy_gz.unstack();
+
+ var usingnamespace_gz: GenZir = .{
.is_comptime = true,
.decl_node_index = node,
.decl_line = astgen.source_line,
@@ -4773,26 +4780,30 @@ fn usingnamespaceDecl(
.instructions = gz.instructions,
.instructions_top = gz.instructions.items.len,
};
- defer decl_block.unstack();
+ defer usingnamespace_gz.unstack();
const decl_column = astgen.source_column;
- const namespace_inst = try typeExpr(&decl_block, &decl_block.base, type_expr);
- _ = try decl_block.addBreak(.break_inline, decl_inst, namespace_inst);
+ const namespace_inst = try typeExpr(&usingnamespace_gz, &usingnamespace_gz.base, type_expr);
+ _ = try usingnamespace_gz.addBreak(.break_inline, decl_inst, namespace_inst);
var hash: std.zig.SrcHash = undefined;
astgen.src_hasher.final(&hash);
- try setDeclaration(
- decl_inst,
- hash,
- .@"usingnamespace",
- decl_block.decl_line,
- decl_column,
- is_pub,
- false,
- &decl_block,
- null,
- );
+ try setDeclaration(decl_inst, .{
+ .src_hash = hash,
+ .src_line = usingnamespace_gz.decl_line,
+ .src_column = decl_column,
+ .kind = .@"usingnamespace",
+ .name = .empty,
+ .is_pub = is_pub,
+ .is_threadlocal = false,
+ .linkage = .normal,
+ .type_gz = &dummy_gz,
+ .align_gz = &dummy_gz,
+ .linksection_gz = &dummy_gz,
+ .addrspace_gz = &dummy_gz,
+ .value_gz = &usingnamespace_gz,
+ });
}
fn testDecl(
@@ -4819,14 +4830,18 @@ fn testDecl(
wip_members.nextDecl(decl_inst);
astgen.advanceSourceCursorToNode(node);
+ // This is just needed for the `setDeclaration` call.
+ var dummy_gz: GenZir = gz.makeSubBlock(scope);
+ defer dummy_gz.unstack();
+
var decl_block: GenZir = .{
.is_comptime = true,
.decl_node_index = node,
.decl_line = astgen.source_line,
.parent = scope,
.astgen = astgen,
- .instructions = gz.instructions,
- .instructions_top = gz.instructions.items.len,
+ .instructions = dummy_gz.instructions,
+ .instructions_top = dummy_gz.instructions.items.len,
};
defer decl_block.unstack();
@@ -4835,11 +4850,21 @@ fn testDecl(
const main_tokens = tree.nodes.items(.main_token);
const token_tags = tree.tokens.items(.tag);
const test_token = main_tokens[node];
+
const test_name_token = test_token + 1;
- const test_name: DeclarationName = switch (token_tags[test_name_token]) {
- else => .unnamed_test,
- .string_literal => .{ .named_test = test_name_token },
- .identifier => blk: {
+ const test_name: Zir.NullTerminatedString = switch (token_tags[test_name_token]) {
+ else => .empty,
+ .string_literal => name: {
+ const name = try astgen.strLitAsString(test_name_token);
+ const slice = astgen.string_bytes.items[@intFromEnum(name.index)..][0..name.len];
+ if (mem.indexOfScalar(u8, slice, 0) != null) {
+ return astgen.failTok(test_name_token, "test name cannot contain null bytes", .{});
+ } else if (slice.len == 0) {
+ return astgen.failTok(test_name_token, "empty test name must be omitted", .{});
+ }
+ break :name name.index;
+ },
+ .identifier => name: {
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", .{});
@@ -4909,7 +4934,7 @@ fn testDecl(
return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});
}
- break :blk .{ .decltest = test_name_token };
+ break :name try astgen.identAsString(test_name_token);
},
};
@@ -4965,11 +4990,8 @@ fn testDecl(
.lbrace_column = lbrace_column,
.param_block = decl_inst,
.body_gz = &fn_block,
- .lib_name = .empty,
.is_var_args = false,
.is_inferred_error = false,
- .is_test = true,
- .is_extern = false,
.is_noinline = false,
.noalias_bits = 0,
@@ -4981,17 +5003,27 @@ fn testDecl(
var hash: std.zig.SrcHash = undefined;
astgen.src_hasher.final(&hash);
- try setDeclaration(
- decl_inst,
- hash,
- test_name,
- decl_block.decl_line,
- decl_column,
- false,
- false,
- &decl_block,
- null,
- );
+ try setDeclaration(decl_inst, .{
+ .src_hash = hash,
+ .src_line = decl_block.decl_line,
+ .src_column = decl_column,
+
+ .kind = switch (token_tags[test_name_token]) {
+ .string_literal => .@"test",
+ .identifier => .decltest,
+ else => .unnamed_test,
+ },
+ .name = test_name,
+ .is_pub = false,
+ .is_threadlocal = false,
+ .linkage = .normal,
+
+ .type_gz = &dummy_gz,
+ .align_gz = &dummy_gz,
+ .linksection_gz = &dummy_gz,
+ .addrspace_gz = &dummy_gz,
+ .value_gz = &decl_block,
+ });
}
fn structDeclInner(
@@ -5882,7 +5914,8 @@ fn containerMember(
try addFailedDeclaration(
wip_members,
gz,
- .{ .named = full.name_token.? },
+ .@"const",
+ try astgen.identAsString(full.name_token.?),
full.ast.proto_node,
full.visib_token != null,
);
@@ -5904,7 +5937,8 @@ fn containerMember(
try addFailedDeclaration(
wip_members,
gz,
- .{ .named = full.ast.mut_token + 1 },
+ .@"const", // doesn't really matter
+ try astgen.identAsString(full.ast.mut_token + 1),
member_node,
full.visib_token != null,
);
@@ -5922,6 +5956,7 @@ fn containerMember(
wip_members,
gz,
.@"comptime",
+ .empty,
member_node,
false,
);
@@ -5938,6 +5973,7 @@ fn containerMember(
wip_members,
gz,
.@"usingnamespace",
+ .empty,
member_node,
is_pub: {
const main_tokens = tree.nodes.items(.main_token);
@@ -5962,6 +5998,7 @@ fn containerMember(
wip_members,
gz,
.unnamed_test,
+ .empty,
member_node,
false,
);
@@ -11670,23 +11707,6 @@ fn strLitNodeAsString(astgen: *AstGen, node: Ast.Node.Index) !IndexSlice {
};
}
-fn testNameString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !Zir.NullTerminatedString {
- const gpa = astgen.gpa;
- const string_bytes = &astgen.string_bytes;
- const str_index: u32 = @intCast(string_bytes.items.len);
- const token_bytes = astgen.tree.tokenSlice(str_lit_token);
- try string_bytes.append(gpa, 0); // Indicates this is a test.
- try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
- const slice = string_bytes.items[str_index + 1 ..];
- if (mem.indexOfScalar(u8, slice, 0) != null) {
- return astgen.failTok(str_lit_token, "test name cannot contain null bytes", .{});
- } else if (slice.len == 0) {
- return astgen.failTok(str_lit_token, "empty test name must be omitted", .{});
- }
- try string_bytes.append(gpa, 0);
- return @enumFromInt(str_index);
-}
-
const Scope = struct {
tag: Tag,
@@ -12077,12 +12097,9 @@ const GenZir = struct {
cc_ref: Zir.Inst.Ref,
ret_ref: Zir.Inst.Ref,
- lib_name: Zir.NullTerminatedString,
noalias_bits: u32,
is_var_args: bool,
is_inferred_error: bool,
- is_test: bool,
- is_extern: bool,
is_noinline: bool,
/// Ignored if `body_gz == null`.
@@ -12150,9 +12167,8 @@ const GenZir = struct {
const body_len = astgen.countBodyLenAfterFixupsExtraRefs(body, args.param_insts);
- const tag: Zir.Inst.Tag, const payload_index: u32 = if (args.cc_ref != .none or args.lib_name != .empty or
- args.is_var_args or args.is_test or args.is_extern or
- args.noalias_bits != 0 or args.is_noinline)
+ const tag: Zir.Inst.Tag, const payload_index: u32 = if (args.cc_ref != .none or
+ args.is_var_args or args.noalias_bits != 0 or args.is_noinline)
inst_info: {
try astgen.extra.ensureUnusedCapacity(
gpa,
@@ -12160,7 +12176,6 @@ const GenZir = struct {
fancyFnExprExtraLen(astgen, &.{}, cc_body, args.cc_ref) +
fancyFnExprExtraLen(astgen, args.ret_param_refs, ret_body, ret_ref) +
body_len + src_locs_and_hash.len +
- @intFromBool(args.lib_name != .empty) +
@intFromBool(args.noalias_bits != 0),
);
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{
@@ -12169,10 +12184,7 @@ const GenZir = struct {
.bits = .{
.is_var_args = args.is_var_args,
.is_inferred_error = args.is_inferred_error,
- .is_test = args.is_test,
- .is_extern = args.is_extern,
.is_noinline = args.is_noinline,
- .has_lib_name = args.lib_name != .empty,
.has_any_noalias = args.noalias_bits != 0,
.has_cc_ref = args.cc_ref != .none,
@@ -12182,9 +12194,6 @@ const GenZir = struct {
.has_ret_ty_body = ret_body.len != 0,
},
});
- if (args.lib_name != .empty) {
- astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name));
- }
const zir_datas = astgen.instructions.items(.data);
if (cc_body.len != 0) {
@@ -12279,61 +12288,6 @@ const GenZir = struct {
@intFromBool(main_body.len > 0 or ref != .none);
}
- fn addVar(gz: *GenZir, args: struct {
- align_inst: Zir.Inst.Ref,
- lib_name: Zir.NullTerminatedString,
- var_type: Zir.Inst.Ref,
- init: Zir.Inst.Ref,
- is_extern: bool,
- is_const: bool,
- is_threadlocal: bool,
- }) !Zir.Inst.Ref {
- const astgen = gz.astgen;
- const gpa = astgen.gpa;
-
- try gz.instructions.ensureUnusedCapacity(gpa, 1);
- try astgen.instructions.ensureUnusedCapacity(gpa, 1);
-
- try astgen.extra.ensureUnusedCapacity(
- gpa,
- @typeInfo(Zir.Inst.ExtendedVar).@"struct".fields.len +
- @intFromBool(args.lib_name != .empty) +
- @intFromBool(args.align_inst != .none) +
- @intFromBool(args.init != .none),
- );
- const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedVar{
- .var_type = args.var_type,
- });
- if (args.lib_name != .empty) {
- astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name));
- }
- if (args.align_inst != .none) {
- astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_inst));
- }
- if (args.init != .none) {
- astgen.extra.appendAssumeCapacity(@intFromEnum(args.init));
- }
-
- const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
- astgen.instructions.appendAssumeCapacity(.{
- .tag = .extended,
- .data = .{ .extended = .{
- .opcode = .variable,
- .small = @bitCast(Zir.Inst.ExtendedVar.Small{
- .has_lib_name = args.lib_name != .empty,
- .has_align = args.align_inst != .none,
- .has_init = args.init != .none,
- .is_extern = args.is_extern,
- .is_const = args.is_const,
- .is_threadlocal = args.is_threadlocal,
- }),
- .operand = payload_index,
- } },
- });
- gz.instructions.appendAssumeCapacity(new_index);
- return new_index.toRef();
- }
-
fn addInt(gz: *GenZir, integer: u64) !Zir.Inst.Ref {
return gz.add(.{
.tag = .int,
@@ -13909,14 +13863,18 @@ const DeclarationName = union(enum) {
fn addFailedDeclaration(
wip_members: *WipMembers,
gz: *GenZir,
- name: DeclarationName,
+ kind: Zir.Inst.Declaration.Unwrapped.Kind,
+ name: Zir.NullTerminatedString,
src_node: Ast.Node.Index,
is_pub: bool,
) !void {
const decl_inst = try gz.makeDeclaration(src_node);
wip_members.nextDecl(decl_inst);
- var decl_gz = gz.makeSubBlock(&gz.base); // scope doesn't matter here
- _ = try decl_gz.add(.{
+
+ var dummy_gz = gz.makeSubBlock(&gz.base);
+
+ var value_gz = gz.makeSubBlock(&gz.base); // scope doesn't matter here
+ _ = try value_gz.add(.{
.tag = .extended,
.data = .{ .extended = .{
.opcode = .astgen_error,
@@ -13924,110 +13882,198 @@ fn addFailedDeclaration(
.operand = undefined,
} },
});
- try setDeclaration(
- decl_inst,
- @splat(0), // use a fixed hash to represent an AstGen failure; we don't care about source changes if AstGen still failed!
- name,
- gz.astgen.source_line,
- gz.astgen.source_column,
- is_pub,
- false, // we don't care about exports since semantic analysis will fail
- &decl_gz,
- null,
- );
+
+ try setDeclaration(decl_inst, .{
+ .src_hash = @splat(0), // use a fixed hash to represent an AstGen failure; we don't care about source changes if AstGen still failed!
+ .src_line = gz.astgen.source_line,
+ .src_column = gz.astgen.source_column,
+ .kind = kind,
+ .name = name,
+ .is_pub = is_pub,
+ .is_threadlocal = false,
+ .linkage = .normal,
+ .type_gz = &dummy_gz,
+ .align_gz = &dummy_gz,
+ .linksection_gz = &dummy_gz,
+ .addrspace_gz = &dummy_gz,
+ .value_gz = &value_gz,
+ });
}
/// Sets all extra data for a `declaration` instruction.
-/// Unstacks `value_gz`, `align_gz`, `linksection_gz`, and `addrspace_gz`.
+/// Unstacks `type_gz`, `align_gz`, `linksection_gz`, `addrspace_gz`, and `value_gz`.
fn setDeclaration(
decl_inst: Zir.Inst.Index,
- src_hash: std.zig.SrcHash,
- name: DeclarationName,
- src_line: u32,
- src_column: u32,
- is_pub: bool,
- is_export: bool,
- 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`.
+ args: struct {
+ src_hash: std.zig.SrcHash,
+ src_line: u32,
+ src_column: u32,
+
+ kind: Zir.Inst.Declaration.Unwrapped.Kind,
+ name: Zir.NullTerminatedString,
+ is_pub: bool,
+ is_threadlocal: bool,
+ linkage: Zir.Inst.Declaration.Unwrapped.Linkage,
+ lib_name: Zir.NullTerminatedString = .empty,
+
+ type_gz: *GenZir,
+ /// Must be stacked on `type_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.
+ /// Must be stacked on `linksection_gz`.
addrspace_gz: *GenZir,
+ /// Must be stacked on `addrspace_gz` and have nothing stacked on top of it.
+ value_gz: *GenZir,
},
) !void {
- const astgen = value_gz.astgen;
+ const astgen = args.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 type_body = args.type_gz.instructionsSliceUpto(args.align_gz);
+ const align_body = args.align_gz.instructionsSliceUpto(args.linksection_gz);
+ const linksection_body = args.linksection_gz.instructionsSliceUpto(args.addrspace_gz);
+ const addrspace_body = args.addrspace_gz.instructionsSliceUpto(args.value_gz);
+ const value_body = args.value_gz.instructionsSlice();
+
+ const has_name = args.name != .empty;
+ const has_lib_name = args.lib_name != .empty;
+ const has_type_body = type_body.len != 0;
+ const has_special_body = align_body.len != 0 or linksection_body.len != 0 or addrspace_body.len != 0;
+ const has_value_body = value_body.len != 0;
+
+ const id: Zir.Inst.Declaration.Flags.Id = switch (args.kind) {
+ .unnamed_test => .unnamed_test,
+ .@"test" => .@"test",
+ .decltest => .decltest,
+ .@"comptime" => .@"comptime",
+ .@"usingnamespace" => if (args.is_pub) .pub_usingnamespace else .@"usingnamespace",
+ .@"const" => switch (args.linkage) {
+ .normal => if (args.is_pub) id: {
+ if (has_special_body) break :id .pub_const;
+ if (has_type_body) break :id .pub_const_typed;
+ break :id .pub_const_simple;
+ } else id: {
+ if (has_special_body) break :id .@"const";
+ if (has_type_body) break :id .const_typed;
+ break :id .const_simple;
+ },
+ .@"extern" => if (args.is_pub) id: {
+ if (has_lib_name) break :id .pub_extern_const;
+ if (has_special_body) break :id .pub_extern_const;
+ break :id .pub_extern_const_simple;
+ } else id: {
+ if (has_lib_name) break :id .extern_const;
+ if (has_special_body) break :id .extern_const;
+ break :id .extern_const_simple;
+ },
+ .@"export" => if (args.is_pub) .pub_export_const else .export_const,
+ },
+ .@"var" => switch (args.linkage) {
+ .normal => if (args.is_pub) id: {
+ if (args.is_threadlocal) break :id .pub_var_threadlocal;
+ if (has_special_body) break :id .pub_var;
+ if (has_type_body) break :id .pub_var;
+ break :id .pub_var_simple;
+ } else id: {
+ if (args.is_threadlocal) break :id .var_threadlocal;
+ if (has_special_body) break :id .@"var";
+ if (has_type_body) break :id .@"var";
+ break :id .var_simple;
+ },
+ .@"extern" => if (args.is_pub) id: {
+ if (args.is_threadlocal) break :id .pub_extern_var_threadlocal;
+ break :id .pub_extern_var;
+ } else id: {
+ if (args.is_threadlocal) break :id .extern_var_threadlocal;
+ break :id .extern_var;
+ },
+ .@"export" => if (args.is_pub) id: {
+ if (args.is_threadlocal) break :id .pub_export_var_threadlocal;
+ break :id .pub_export_var;
+ } else id: {
+ if (args.is_threadlocal) break :id .export_var_threadlocal;
+ break :id .export_var;
+ },
+ },
+ };
- const value_len = astgen.countBodyLenAfterFixups(value_body);
+ assert(id.hasTypeBody() or !has_type_body);
+ assert(id.hasSpecialBodies() or !has_special_body);
+ assert(id.hasValueBody() == has_value_body);
+ assert(id.linkage() == args.linkage);
+ assert(id.hasName() == has_name);
+ assert(id.hasLibName() or !has_lib_name);
+ assert(id.isPub() == args.is_pub);
+ assert(id.isThreadlocal() == args.is_threadlocal);
+
+ const type_len = astgen.countBodyLenAfterFixups(type_body);
const align_len = astgen.countBodyLenAfterFixups(align_body);
const linksection_len = astgen.countBodyLenAfterFixups(linksection_body);
const addrspace_len = astgen.countBodyLenAfterFixups(addrspace_body);
+ const value_len = astgen.countBodyLenAfterFixups(value_body);
+
+ const src_hash_arr: [4]u32 = @bitCast(args.src_hash);
+ const flags: Zir.Inst.Declaration.Flags = .{
+ .src_line = @intCast(args.src_line),
+ .src_column = @intCast(args.src_column),
+ .id = id,
+ };
+ const flags_arr: [2]u32 = @bitCast(flags);
- const src_hash_arr: [4]u32 = @bitCast(src_hash);
+ const need_extra: usize =
+ @typeInfo(Zir.Inst.Declaration).@"struct".fields.len +
+ @as(usize, @intFromBool(id.hasName())) +
+ @as(usize, @intFromBool(id.hasLibName())) +
+ @as(usize, @intFromBool(id.hasTypeBody())) +
+ 3 * @as(usize, @intFromBool(id.hasSpecialBodies())) +
+ @as(usize, @intFromBool(id.hasValueBody())) +
+ type_len + align_len + linksection_len + addrspace_len + value_len;
+
+ try astgen.extra.ensureUnusedCapacity(gpa, need_extra);
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))),
- .decltest => |tok| @enumFromInt(str_idx: {
- const idx = astgen.string_bytes.items.len;
- try astgen.string_bytes.append(gpa, 0); // indicates this is a test
- try astgen.appendIdentStr(tok, &astgen.string_bytes);
- try astgen.string_bytes.append(gpa, 0); // end of the string
- break :str_idx idx;
- }),
- .unnamed_test => .unnamed_test,
- .@"comptime" => .@"comptime",
- .@"usingnamespace" => .@"usingnamespace",
- },
- .src_line = src_line,
- .src_column = src_column,
- .flags = .{
- .value_body_len = @intCast(value_len),
- .is_pub = is_pub,
- .is_export = is_export,
- .test_is_decltest = name == .decltest,
- .has_align_linksection_addrspace = align_len != 0 or linksection_len != 0 or addrspace_len != 0,
- },
+ .flags_0 = flags_arr[0],
+ .flags_1 = flags_arr[1],
};
- astgen.instructions.items(.data)[@intFromEnum(decl_inst)].declaration.payload_index = try astgen.addExtra(extra);
- if (extra.flags.has_align_linksection_addrspace) {
- try astgen.extra.appendSlice(gpa, &.{
+ astgen.instructions.items(.data)[@intFromEnum(decl_inst)].declaration.payload_index =
+ astgen.addExtraAssumeCapacity(extra);
+
+ if (id.hasName()) {
+ astgen.extra.appendAssumeCapacity(@intFromEnum(args.name));
+ }
+ if (id.hasLibName()) {
+ astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name));
+ }
+ if (id.hasTypeBody()) {
+ astgen.extra.appendAssumeCapacity(type_len);
+ }
+ if (id.hasSpecialBodies()) {
+ astgen.extra.appendSliceAssumeCapacity(&.{
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 (id.hasValueBody()) {
+ astgen.extra.appendAssumeCapacity(value_len);
}
- if (extra_gzs) |e| {
- e.addrspace_gz.unstack();
- e.linksection_gz.unstack();
- e.align_gz.unstack();
- }
- value_gz.unstack();
+ astgen.appendBodyWithFixups(type_body);
+ astgen.appendBodyWithFixups(align_body);
+ astgen.appendBodyWithFixups(linksection_body);
+ astgen.appendBodyWithFixups(addrspace_body);
+ astgen.appendBodyWithFixups(value_body);
+
+ args.value_gz.unstack();
+ args.addrspace_gz.unstack();
+ args.linksection_gz.unstack();
+ args.align_gz.unstack();
+ args.type_gz.unstack();
}
/// Given a list of instructions, returns a list of all instructions which are a `ref` of one of the originals,
diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig
index f2aceb14ae..0f9611aa30 100644
--- a/lib/std/zig/Zir.zig
+++ b/lib/std/zig/Zir.zig
@@ -1868,10 +1868,6 @@ pub const Inst = struct {
/// Rarer instructions are here; ones that do not fit in the 8-bit `Tag` enum.
/// `noreturn` instructions may not go here; they must be part of the main `Tag` enum.
pub const Extended = enum(u16) {
- /// Declares a global variable.
- /// `operand` is payload index to `ExtendedVar`.
- /// `small` is `ExtendedVar.Small`.
- variable,
/// A struct type definition. Contains references to ZIR instructions for
/// the field types, defaults, and alignments.
/// `operand` is payload index to `StructDecl`.
@@ -2493,26 +2489,25 @@ pub const Inst = struct {
};
/// Trailing:
- /// 0. lib_name: NullTerminatedString, // null terminated string index, if has_lib_name is set
/// if (has_cc_ref and !has_cc_body) {
- /// 1. cc: Ref,
+ /// 0. cc: Ref,
/// }
/// if (has_cc_body) {
- /// 2. cc_body_len: u32
- /// 3. cc_body: u32 // for each cc_body_len
+ /// 1. cc_body_len: u32
+ /// 2. cc_body: u32 // for each cc_body_len
/// }
/// if (has_ret_ty_ref and !has_ret_ty_body) {
- /// 4. ret_ty: Ref,
+ /// 3. ret_ty: Ref,
/// }
/// if (has_ret_ty_body) {
- /// 5. ret_ty_body_len: u32
- /// 6. ret_ty_body: u32 // for each ret_ty_body_len
+ /// 4. ret_ty_body_len: u32
+ /// 5. ret_ty_body: u32 // for each ret_ty_body_len
/// }
- /// 7. noalias_bits: u32 // if has_any_noalias
+ /// 6. noalias_bits: u32 // if has_any_noalias
/// - each bit starting with LSB corresponds to parameter indexes
- /// 8. body: Index // for each body_len
- /// 9. src_locs: Func.SrcLocs // if body_len != 0
- /// 10. proto_hash: std.zig.SrcHash // if body_len != 0; hash of function prototype
+ /// 7. body: Index // for each body_len
+ /// 8. src_locs: Func.SrcLocs // if body_len != 0
+ /// 9. proto_hash: std.zig.SrcHash // if body_len != 0; hash of function prototype
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.
@@ -2522,38 +2517,16 @@ pub const Inst = struct {
/// If both has_cc_ref and has_cc_body are false, it means auto calling convention.
/// If both has_ret_ty_ref and has_ret_ty_body are false, it means void return type.
- pub const Bits = packed struct {
+ pub const Bits = packed struct(u32) {
is_var_args: bool,
is_inferred_error: bool,
- is_test: bool,
- is_extern: bool,
is_noinline: bool,
has_cc_ref: bool,
has_cc_body: bool,
has_ret_ty_ref: bool,
has_ret_ty_body: bool,
- has_lib_name: bool,
has_any_noalias: bool,
- _: u21 = undefined,
- };
- };
-
- /// Trailing:
- /// 0. lib_name: NullTerminatedString, // null terminated string index, if has_lib_name is set
- /// 1. align: Ref, // if has_align is set
- /// 2. init: Ref // if has_init is set
- /// The source node is obtained from the containing `block_inline`.
- pub const ExtendedVar = struct {
- var_type: Ref,
-
- pub const Small = packed struct {
- has_lib_name: bool,
- has_align: bool,
- has_init: bool,
- is_extern: bool,
- is_const: bool,
- is_threadlocal: bool,
- _: u10 = undefined,
+ _: u24 = undefined,
};
};
@@ -2582,39 +2555,301 @@ pub const Inst = struct {
};
/// Trailing:
- /// 0. align_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `align`
- /// 1. linksection_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `linksection`
- /// 2. addrspace_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `addrspace`
- /// 3. value_body_inst: Zir.Inst.Index
- /// - for each `value_body_len`
+ /// 0. name: NullTerminatedString // if `flags.id.hasName()`
+ /// 1. lib_name: NullTerminatedString // if `flags.id.hasLibName()`
+ /// 2. type_body_len: u32 // if `flags.id.hasTypeBody()`
+ /// 3. align_body_len: u32 // if `flags.id.hasSpecialBodies()`
+ /// 4. linksection_body_len: u32 // if `flags.id.hasSpecialBodies()`
+ /// 5. addrspace_body_len: u32 // if `flags.id.hasSpecialBodies()`
+ /// 6. value_body_len: u32 // if `flags.id.hasValueBody()`
+ /// 7. type_body_inst: Zir.Inst.Index
+ /// - for each `type_body_len`
/// - body to be exited via `break_inline` to this `declaration` instruction
- /// 4. align_body_inst: Zir.Inst.Index
+ /// 8. align_body_inst: Zir.Inst.Index
/// - for each `align_body_len`
/// - body to be exited via `break_inline` to this `declaration` instruction
- /// 5. linksection_body_inst: Zir.Inst.Index
+ /// 9. linksection_body_inst: Zir.Inst.Index
/// - for each `linksection_body_len`
/// - body to be exited via `break_inline` to this `declaration` instruction
- /// 6. addrspace_body_inst: Zir.Inst.Index
+ /// 10. addrspace_body_inst: Zir.Inst.Index
/// - for each `addrspace_body_len`
/// - body to be exited via `break_inline` to this `declaration` instruction
+ /// 11. value_body_inst: Zir.Inst.Index
+ /// - for each `value_body_len`
+ /// - body to be exited via `break_inline` to this `declaration` instruction
+ /// - within this body, the `declaration` instruction refers to the resolved type from the type body
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,
- src_line: u32,
- src_column: u32,
- flags: Flags,
+ // These fields should be concatenated and reinterpreted as a `Flags`.
+ flags_0: u32,
+ flags_1: u32,
+
+ pub const Unwrapped = struct {
+ pub const Kind = enum {
+ unnamed_test,
+ @"test",
+ decltest,
+ @"comptime",
+ @"usingnamespace",
+ @"const",
+ @"var",
+ };
- pub const Flags = packed struct(u32) {
- value_body_len: u28,
+ pub const Linkage = enum {
+ normal,
+ @"extern",
+ @"export",
+ };
+
+ src_node: Ast.Node.Index,
+
+ src_line: u32,
+ src_column: u32,
+
+ kind: Kind,
+ /// Always `.empty` for `kind` of `unnamed_test`, `.@"comptime"`, `.@"usingnamespace"`.
+ name: NullTerminatedString,
+ /// Always `false` for `kind` of `unnamed_test`, `.@"test"`, `.decltest`, `.@"comptime"`.
is_pub: bool,
- is_export: bool,
- test_is_decltest: bool,
- has_align_linksection_addrspace: bool,
+ /// Always `false` for `kind != .@"var"`.
+ is_threadlocal: bool,
+ /// Always `.normal` for `kind != .@"const" and kind != .@"var"`.
+ linkage: Linkage,
+ /// Always `.empty` for `linkage != .@"extern"`.
+ lib_name: NullTerminatedString,
+
+ /// Always populated for `linkage == .@"extern".
+ type_body: ?[]const Inst.Index,
+ align_body: ?[]const Inst.Index,
+ linksection_body: ?[]const Inst.Index,
+ addrspace_body: ?[]const Inst.Index,
+ /// Always populated for `linkage != .@"extern".
+ value_body: ?[]const Inst.Index,
+ };
+
+ pub const Flags = packed struct(u64) {
+ src_line: u30,
+ src_column: u29,
+ id: Id,
+
+ pub const Id = enum(u5) {
+ unnamed_test,
+ @"test",
+ decltest,
+ @"comptime",
+
+ @"usingnamespace",
+ pub_usingnamespace,
+
+ const_simple,
+ const_typed,
+ @"const",
+ pub_const_simple,
+ pub_const_typed,
+ pub_const,
+
+ extern_const_simple,
+ extern_const,
+ pub_extern_const_simple,
+ pub_extern_const,
+
+ export_const,
+ pub_export_const,
+
+ var_simple,
+ @"var",
+ var_threadlocal,
+ pub_var_simple,
+ pub_var,
+ pub_var_threadlocal,
+
+ extern_var,
+ extern_var_threadlocal,
+ pub_extern_var,
+ pub_extern_var_threadlocal,
+
+ export_var,
+ export_var_threadlocal,
+ pub_export_var,
+ pub_export_var_threadlocal,
+
+ pub fn hasName(id: Id) bool {
+ return switch (id) {
+ .unnamed_test,
+ .@"comptime",
+ .@"usingnamespace",
+ .pub_usingnamespace,
+ => false,
+ else => true,
+ };
+ }
+
+ pub fn hasLibName(id: Id) bool {
+ return switch (id) {
+ .extern_const,
+ .pub_extern_const,
+ .extern_var,
+ .extern_var_threadlocal,
+ .pub_extern_var,
+ .pub_extern_var_threadlocal,
+ => true,
+ else => false,
+ };
+ }
+
+ pub fn hasTypeBody(id: Id) bool {
+ return switch (id) {
+ .unnamed_test,
+ .@"test",
+ .decltest,
+ .@"comptime",
+ .@"usingnamespace",
+ .pub_usingnamespace,
+ => false, // these constructs are untyped
+ .const_simple,
+ .pub_const_simple,
+ .var_simple,
+ .pub_var_simple,
+ => false, // these reprs omit type bodies
+ else => true,
+ };
+ }
+
+ pub fn hasValueBody(id: Id) bool {
+ return switch (id) {
+ .extern_const_simple,
+ .extern_const,
+ .pub_extern_const_simple,
+ .pub_extern_const,
+ .extern_var,
+ .extern_var_threadlocal,
+ .pub_extern_var,
+ .pub_extern_var_threadlocal,
+ => false, // externs do not have values
+ else => true,
+ };
+ }
+
+ pub fn hasSpecialBodies(id: Id) bool {
+ return switch (id) {
+ .unnamed_test,
+ .@"test",
+ .decltest,
+ .@"comptime",
+ .@"usingnamespace",
+ .pub_usingnamespace,
+ => false, // these constructs are untyped
+ .const_simple,
+ .const_typed,
+ .pub_const_simple,
+ .pub_const_typed,
+ .extern_const_simple,
+ .pub_extern_const_simple,
+ .var_simple,
+ .pub_var_simple,
+ => false, // these reprs omit special bodies
+ else => true,
+ };
+ }
+
+ pub fn linkage(id: Id) Declaration.Unwrapped.Linkage {
+ return switch (id) {
+ .extern_const_simple,
+ .extern_const,
+ .pub_extern_const_simple,
+ .pub_extern_const,
+ .extern_var,
+ .extern_var_threadlocal,
+ .pub_extern_var,
+ .pub_extern_var_threadlocal,
+ => .@"extern",
+ .export_const,
+ .pub_export_const,
+ .export_var,
+ .export_var_threadlocal,
+ .pub_export_var,
+ .pub_export_var_threadlocal,
+ => .@"export",
+ else => .normal,
+ };
+ }
+
+ pub fn kind(id: Id) Declaration.Unwrapped.Kind {
+ return switch (id) {
+ .unnamed_test => .unnamed_test,
+ .@"test" => .@"test",
+ .decltest => .decltest,
+ .@"comptime" => .@"comptime",
+ .@"usingnamespace", .pub_usingnamespace => .@"usingnamespace",
+ .const_simple,
+ .const_typed,
+ .@"const",
+ .pub_const_simple,
+ .pub_const_typed,
+ .pub_const,
+ .extern_const_simple,
+ .extern_const,
+ .pub_extern_const_simple,
+ .pub_extern_const,
+ .export_const,
+ .pub_export_const,
+ => .@"const",
+ .var_simple,
+ .@"var",
+ .var_threadlocal,
+ .pub_var_simple,
+ .pub_var,
+ .pub_var_threadlocal,
+ .extern_var,
+ .extern_var_threadlocal,
+ .pub_extern_var,
+ .pub_extern_var_threadlocal,
+ .export_var,
+ .export_var_threadlocal,
+ .pub_export_var,
+ .pub_export_var_threadlocal,
+ => .@"var",
+ };
+ }
+
+ pub fn isPub(id: Id) bool {
+ return switch (id) {
+ .pub_usingnamespace,
+ .pub_const_simple,
+ .pub_const_typed,
+ .pub_const,
+ .pub_extern_const_simple,
+ .pub_extern_const,
+ .pub_export_const,
+ .pub_var_simple,
+ .pub_var,
+ .pub_var_threadlocal,
+ .pub_extern_var,
+ .pub_extern_var_threadlocal,
+ .pub_export_var,
+ .pub_export_var_threadlocal,
+ => true,
+ else => false,
+ };
+ }
+
+ pub fn isThreadlocal(id: Id) bool {
+ return switch (id) {
+ .var_threadlocal,
+ .pub_var_threadlocal,
+ .extern_var_threadlocal,
+ .pub_extern_var_threadlocal,
+ .export_var_threadlocal,
+ .pub_export_var_threadlocal,
+ => true,
+ else => false,
+ };
+ }
+ };
};
pub const Name = enum(u32) {
@@ -2647,17 +2882,24 @@ pub const Inst = struct {
};
pub const Bodies = struct {
- value_body: []const Index,
+ type_body: ?[]const Index,
align_body: ?[]const Index,
linksection_body: ?[]const Index,
addrspace_body: ?[]const Index,
+ value_body: ?[]const Index,
};
pub fn getBodies(declaration: Declaration, extra_end: u32, zir: Zir) Bodies {
var extra_index: u32 = extra_end;
- const value_body_len = declaration.flags.value_body_len;
+ const value_body_len = declaration.value_body_len;
+ const type_body_len: u32 = len: {
+ if (!declaration.flags().kind.hasTypeBody()) break :len 0;
+ const len = zir.extra[extra_index];
+ extra_index += 1;
+ break :len len;
+ };
const align_body_len, const linksection_body_len, const addrspace_body_len = lens: {
- if (!declaration.flags.has_align_linksection_addrspace) {
+ if (!declaration.flags.kind.hasSpecialBodies()) {
break :lens .{ 0, 0, 0 };
}
const lens = zir.extra[extra_index..][0..3].*;
@@ -2665,21 +2907,30 @@ pub const Inst = struct {
break :lens lens;
};
return .{
- .value_body = b: {
- defer extra_index += value_body_len;
- break :b zir.bodySlice(extra_index, value_body_len);
+ .type_body = if (type_body_len == 0) null else b: {
+ const b = zir.bodySlice(extra_index, type_body_len);
+ extra_index += type_body_len;
+ break :b b;
},
.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);
+ const b = zir.bodySlice(extra_index, align_body_len);
+ extra_index += align_body_len;
+ break :b b;
},
.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);
+ const b = zir.bodySlice(extra_index, linksection_body_len);
+ extra_index += linksection_body_len;
+ break :b b;
},
.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);
+ const b = zir.bodySlice(extra_index, addrspace_body_len);
+ extra_index += addrspace_body_len;
+ break :b b;
+ },
+ .value_body = if (value_body_len == 0) null else b: {
+ const b = zir.bodySlice(extra_index, value_body_len);
+ extra_index += value_body_len;
+ break :b b;
},
};
}
@@ -3711,18 +3962,18 @@ pub const DeclContents = struct {
pub fn findTrackable(zir: Zir, gpa: Allocator, contents: *DeclContents, decl_inst: Zir.Inst.Index) !void {
contents.clear();
- const declaration, const extra_end = zir.getDeclaration(decl_inst);
- const bodies = declaration.getBodies(extra_end, zir);
+ const decl = zir.getDeclaration(decl_inst);
// `defer` instructions duplicate the same body arbitrarily many times, but we only want to traverse
// their contents once per defer. So, we store the extra index of the body here to deduplicate.
var found_defers: std.AutoHashMapUnmanaged(u32, void) = .empty;
defer found_defers.deinit(gpa);
- try zir.findTrackableBody(gpa, contents, &found_defers, bodies.value_body);
- if (bodies.align_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
- if (bodies.linksection_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
- if (bodies.addrspace_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
+ if (decl.type_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
+ if (decl.align_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
+ if (decl.linksection_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
+ if (decl.addrspace_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
+ if (decl.value_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
}
/// Like `findTrackable`, but only considers the `main_struct_inst` instruction. This may return more than
@@ -3991,7 +4242,6 @@ fn findTrackableInner(
.value_placeholder => unreachable,
// Once again, we start with the boring tags.
- .variable,
.this,
.ret_addr,
.builtin_src,
@@ -4237,7 +4487,6 @@ fn findTrackableInner(
const inst_data = datas[@intFromEnum(inst)].pl_node;
const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);
var extra_index: usize = extra.end;
- extra_index += @intFromBool(extra.data.bits.has_lib_name);
if (extra.data.bits.has_cc_body) {
const body_len = zir.extra[extra_index];
@@ -4470,8 +4719,7 @@ pub fn getParamBody(zir: Zir, fn_inst: Inst.Index) []const Zir.Inst.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;
+ return zir.getDeclaration(param_block_index).value_body.?;
},
else => unreachable,
}
@@ -4526,7 +4774,6 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
var ret_ty_ref: Inst.Ref = .void_type;
var ret_ty_body: []const Inst.Index = &.{};
- extra_index += @intFromBool(extra.data.bits.has_lib_name);
if (extra.data.bits.has_cc_body) {
extra_index += zir.extra[extra_index] + 1;
} else if (extra.data.bits.has_cc_ref) {
@@ -4555,17 +4802,7 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
},
else => unreachable,
};
- 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,
- };
+ const param_body = zir.getParamBody(fn_inst);
var total_params_len: u32 = 0;
for (param_body) |inst| {
switch (tags[@intFromEnum(inst)]) {
@@ -4585,13 +4822,74 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
};
}
-pub fn getDeclaration(zir: Zir, inst: Zir.Inst.Index) struct { Inst.Declaration, u32 } {
+pub fn getDeclaration(zir: Zir, inst: Zir.Inst.Index) Inst.Declaration.Unwrapped {
assert(zir.instructions.items(.tag)[@intFromEnum(inst)] == .declaration);
const pl_node = zir.instructions.items(.data)[@intFromEnum(inst)].declaration;
const extra = zir.extraData(Inst.Declaration, pl_node.payload_index);
+
+ const flags_vals: [2]u32 = .{ extra.data.flags_0, extra.data.flags_1 };
+ const flags: Inst.Declaration.Flags = @bitCast(flags_vals);
+
+ var extra_index = extra.end;
+
+ const name: NullTerminatedString = if (flags.id.hasName()) name: {
+ const name = zir.extra[extra_index];
+ extra_index += 1;
+ break :name @enumFromInt(name);
+ } else .empty;
+
+ const lib_name: NullTerminatedString = if (flags.id.hasLibName()) lib_name: {
+ const lib_name = zir.extra[extra_index];
+ extra_index += 1;
+ break :lib_name @enumFromInt(lib_name);
+ } else .empty;
+
+ const type_body_len: u32 = if (flags.id.hasTypeBody()) len: {
+ const len = zir.extra[extra_index];
+ extra_index += 1;
+ break :len len;
+ } else 0;
+ const align_body_len: u32, const linksection_body_len: u32, const addrspace_body_len: u32 = lens: {
+ if (!flags.id.hasSpecialBodies()) break :lens .{ 0, 0, 0 };
+ const lens = zir.extra[extra_index..][0..3].*;
+ extra_index += 3;
+ break :lens lens;
+ };
+ const value_body_len: u32 = if (flags.id.hasValueBody()) len: {
+ const len = zir.extra[extra_index];
+ extra_index += 1;
+ break :len len;
+ } else 0;
+
+ const type_body = zir.bodySlice(extra_index, type_body_len);
+ extra_index += type_body_len;
+ const align_body = zir.bodySlice(extra_index, align_body_len);
+ extra_index += align_body_len;
+ const linksection_body = zir.bodySlice(extra_index, linksection_body_len);
+ extra_index += linksection_body_len;
+ const addrspace_body = zir.bodySlice(extra_index, addrspace_body_len);
+ extra_index += addrspace_body_len;
+ const value_body = zir.bodySlice(extra_index, value_body_len);
+ extra_index += value_body_len;
+
return .{
- extra.data,
- @intCast(extra.end),
+ .src_node = pl_node.src_node,
+
+ .src_line = flags.src_line,
+ .src_column = flags.src_column,
+
+ .kind = flags.id.kind(),
+ .name = name,
+ .is_pub = flags.id.isPub(),
+ .is_threadlocal = flags.id.isThreadlocal(),
+ .linkage = flags.id.linkage(),
+ .lib_name = lib_name,
+
+ .type_body = if (type_body_len == 0) null else type_body,
+ .align_body = if (align_body_len == 0) null else align_body,
+ .linksection_body = if (linksection_body_len == 0) null else linksection_body,
+ .addrspace_body = if (addrspace_body_len == 0) null else addrspace_body,
+ .value_body = if (value_body_len == 0) null else value_body,
};
}
@@ -4636,7 +4934,6 @@ pub fn getAssociatedSrcHash(zir: Zir, inst: Zir.Inst.Index) ?std.zig.SrcHash {
}
const bits = extra.data.bits;
var extra_index = extra.end;
- extra_index += @intFromBool(bits.has_lib_name);
if (bits.has_cc_body) {
const body_len = zir.extra[extra_index];
extra_index += 1 + body_len;
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 8b158390b6..28c5efab6c 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -348,12 +348,15 @@ const Job = union(enum) {
/// Corresponds to the task in `link.Task`.
/// Only needed for backends that haven't yet been updated to not race against Sema.
codegen_type: InternPool.Index,
- /// The `Cau` must be semantically analyzed (and possibly export itself).
+ /// The `AnalUnit`, which is *not* a `func`, must be semantically analyzed.
+ /// This may be its first time being analyzed, or it may be outdated.
+ /// If the unit is a function, a `codegen_func` job will then be queued.
+ analyze_comptime_unit: InternPool.AnalUnit,
+ /// This function must be semantically analyzed.
/// This may be its first time being analyzed, or it may be outdated.
- analyze_cau: InternPool.Cau.Index,
- /// Analyze the body of a runtime function.
/// After analysis, a `codegen_func` job will be queued.
/// These must be separate jobs to ensure any needed type resolution occurs *before* codegen.
+ /// This job is separate from `analyze_comptime_unit` because it has a different priority.
analyze_func: InternPool.Index,
/// The main source file for the module needs to be analyzed.
analyze_mod: *Package.Module,
@@ -2903,6 +2906,7 @@ const Header = extern struct {
file_deps_len: u32,
src_hash_deps_len: u32,
nav_val_deps_len: u32,
+ nav_ty_deps_len: u32,
namespace_deps_len: u32,
namespace_name_deps_len: u32,
first_dependency_len: u32,
@@ -2946,6 +2950,7 @@ pub fn saveState(comp: *Compilation) !void {
.file_deps_len = @intCast(ip.file_deps.count()),
.src_hash_deps_len = @intCast(ip.src_hash_deps.count()),
.nav_val_deps_len = @intCast(ip.nav_val_deps.count()),
+ .nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()),
.namespace_deps_len = @intCast(ip.namespace_deps.count()),
.namespace_name_deps_len = @intCast(ip.namespace_name_deps.count()),
.first_dependency_len = @intCast(ip.first_dependency.count()),
@@ -2976,6 +2981,8 @@ pub fn saveState(comp: *Compilation) !void {
addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.values()));
addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.keys()));
addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.values()));
+ addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.keys()));
+ addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.values()));
addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.keys()));
addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.values()));
addBuf(&bufs, mem.sliceAsBytes(ip.namespace_name_deps.keys()));
@@ -3141,8 +3148,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
}
const file_index = switch (anal_unit.unwrap()) {
- .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope,
- .func => |ip_index| (zcu.funcInfo(ip_index).zir_body_inst.resolveFull(ip) orelse continue).file,
+ .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index.resolveFile(ip),
+ .nav_val, .nav_ty => |nav| ip.getNav(nav).analysis.?.zir_index.resolveFile(ip),
+ .type => |ty| Type.fromInterned(ty).typeDeclInst(zcu).?.resolveFile(ip),
+ .func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFile(ip),
};
// Skip errors for AnalUnits within files that had a parse failure.
@@ -3374,11 +3383,9 @@ pub fn addModuleErrorMsg(
const rt_file_path = try src.file_scope.fullPath(gpa);
defer gpa.free(rt_file_path);
const name = switch (ref.referencer.unwrap()) {
- .cau => |cau| switch (ip.getCau(cau).owner.unwrap()) {
- .nav => |nav| ip.getNav(nav).name.toSlice(ip),
- .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
- .none => "comptime",
- },
+ .@"comptime" => "comptime",
+ .nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip),
+ .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
.func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip),
};
try ref_traces.append(gpa, .{
@@ -3641,10 +3648,14 @@ fn performAllTheWorkInner(
// If there's no work queued, check if there's anything outdated
// which we need to work on, and queue it if so.
if (try zcu.findOutdatedToAnalyze()) |outdated| {
- switch (outdated.unwrap()) {
- .cau => |cau| try comp.queueJob(.{ .analyze_cau = cau }),
- .func => |func| try comp.queueJob(.{ .analyze_func = func }),
- }
+ try comp.queueJob(switch (outdated.unwrap()) {
+ .func => |f| .{ .analyze_func = f },
+ .@"comptime",
+ .nav_ty,
+ .nav_val,
+ .type,
+ => .{ .analyze_comptime_unit = outdated },
+ });
continue;
}
}
@@ -3667,13 +3678,13 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
.codegen_nav => |nav_index| {
const zcu = comp.zcu.?;
const nav = zcu.intern_pool.getNav(nav_index);
- if (nav.analysis_owner.unwrap()) |cau| {
- const unit = InternPool.AnalUnit.wrap(.{ .cau = cau });
+ if (nav.analysis != null) {
+ const unit: InternPool.AnalUnit = .wrap(.{ .nav_val = nav_index });
if (zcu.failed_analysis.contains(unit) or zcu.transitive_failed_analysis.contains(unit)) {
return;
}
}
- assert(nav.status == .resolved);
+ assert(nav.status == .fully_resolved);
comp.dispatchCodegenTask(tid, .{ .codegen_nav = nav_index });
},
.codegen_func => |func| {
@@ -3688,36 +3699,48 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
defer pt.deactivate();
- pt.ensureFuncBodyAnalyzed(func) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
+
+ pt.ensureFuncBodyUpToDate(func) catch |err| switch (err) {
+ error.OutOfMemory => |e| return e,
error.AnalysisFail => return,
};
},
- .analyze_cau => |cau_index| {
+ .analyze_comptime_unit => |unit| {
+ const named_frame = tracy.namedFrame("analyze_comptime_unit");
+ defer named_frame.end();
+
const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
defer pt.deactivate();
- pt.ensureCauAnalyzed(cau_index) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
+
+ const maybe_err: Zcu.SemaError!void = switch (unit.unwrap()) {
+ .@"comptime" => |cu| pt.ensureComptimeUnitUpToDate(cu),
+ .nav_ty => |nav| pt.ensureNavTypeUpToDate(nav),
+ .nav_val => |nav| pt.ensureNavValUpToDate(nav),
+ .type => |ty| if (pt.ensureTypeUpToDate(ty)) |_| {} else |err| err,
+ .func => unreachable,
+ };
+ maybe_err catch |err| switch (err) {
+ error.OutOfMemory => |e| return e,
error.AnalysisFail => return,
};
+
queue_test_analysis: {
if (!comp.config.is_test) break :queue_test_analysis;
+ const nav = switch (unit.unwrap()) {
+ .nav_val => |nav| nav,
+ else => break :queue_test_analysis,
+ };
// Check if this is a test function.
const ip = &pt.zcu.intern_pool;
- const cau = ip.getCau(cau_index);
- const nav_index = switch (cau.owner.unwrap()) {
- .none, .type => break :queue_test_analysis,
- .nav => |nav| nav,
- };
- if (!pt.zcu.test_functions.contains(nav_index)) {
+ if (!pt.zcu.test_functions.contains(nav)) {
break :queue_test_analysis;
}
// Tests are always emitted in test binaries. The decl_refs are created by
// Zcu.populateTestFunctions, but this will not queue body analysis, so do
// that now.
- try pt.zcu.ensureFuncBodyAnalysisQueued(ip.getNav(nav_index).status.resolved.val);
+ try pt.zcu.ensureFuncBodyAnalysisQueued(ip.getNav(nav).status.fully_resolved.val);
}
},
.resolve_type_fully => |ty| {
diff --git a/src/InternPool.zig b/src/InternPool.zig
index f923332f88..64cf95c7b2 100644
--- a/src/InternPool.zig
+++ b/src/InternPool.zig
@@ -34,6 +34,9 @@ src_hash_deps: std.AutoArrayHashMapUnmanaged(TrackedInst.Index, DepEntry.Index),
/// Dependencies on the value of a Nav.
/// Value is index into `dep_entries` of the first dependency on this Nav value.
nav_val_deps: std.AutoArrayHashMapUnmanaged(Nav.Index, DepEntry.Index),
+/// Dependencies on the type of a Nav.
+/// Value is index into `dep_entries` of the first dependency on this Nav value.
+nav_ty_deps: std.AutoArrayHashMapUnmanaged(Nav.Index, DepEntry.Index),
/// Dependencies on an interned value, either:
/// * a runtime function (invalidated when its IES changes)
/// * a container type requiring resolution (invalidated when the type must be recreated at a new index)
@@ -80,6 +83,7 @@ pub const empty: InternPool = .{
.file_deps = .empty,
.src_hash_deps = .empty,
.nav_val_deps = .empty,
+ .nav_ty_deps = .empty,
.interned_deps = .empty,
.namespace_deps = .empty,
.namespace_name_deps = .empty,
@@ -363,33 +367,56 @@ pub fn rehashTrackedInsts(
}
/// Analysis Unit. Represents a single entity which undergoes semantic analysis.
-/// This is either a `Cau` or a runtime function.
-/// The LSB is used as a tag bit.
/// This is the "source" of an incremental dependency edge.
-pub const AnalUnit = packed struct(u32) {
- kind: enum(u1) { cau, func },
- index: u31,
- pub const Unwrapped = union(enum) {
- cau: Cau.Index,
+pub const AnalUnit = packed struct(u64) {
+ kind: Kind,
+ id: u32,
+
+ pub const Kind = enum(u32) {
+ @"comptime",
+ nav_val,
+ nav_ty,
+ type,
+ func,
+ };
+
+ pub const Unwrapped = union(Kind) {
+ /// This `AnalUnit` analyzes the body of the given `comptime` declaration.
+ @"comptime": ComptimeUnit.Id,
+ /// This `AnalUnit` resolves the value of the given `Nav`.
+ nav_val: Nav.Index,
+ /// This `AnalUnit` resolves the type of the given `Nav`.
+ nav_ty: Nav.Index,
+ /// This `AnalUnit` resolves the given `struct`/`union`/`enum` type.
+ /// Generated tag enums are never used here (they do not undergo type resolution).
+ type: InternPool.Index,
+ /// This `AnalUnit` analyzes the body of the given runtime function.
func: InternPool.Index,
};
- pub fn unwrap(as: AnalUnit) Unwrapped {
- return switch (as.kind) {
- .cau => .{ .cau = @enumFromInt(as.index) },
- .func => .{ .func = @enumFromInt(as.index) },
+
+ pub fn unwrap(au: AnalUnit) Unwrapped {
+ return switch (au.kind) {
+ inline else => |tag| @unionInit(
+ Unwrapped,
+ @tagName(tag),
+ @enumFromInt(au.id),
+ ),
};
}
pub fn wrap(raw: Unwrapped) AnalUnit {
return switch (raw) {
- .cau => |cau| .{ .kind = .cau, .index = @intCast(@intFromEnum(cau)) },
- .func => |func| .{ .kind = .func, .index = @intCast(@intFromEnum(func)) },
+ inline else => |id, tag| .{
+ .kind = tag,
+ .id = @intFromEnum(id),
+ },
};
}
+
pub fn toOptional(as: AnalUnit) Optional {
- return @enumFromInt(@as(u32, @bitCast(as)));
+ return @enumFromInt(@as(u64, @bitCast(as)));
}
- pub const Optional = enum(u32) {
- none = std.math.maxInt(u32),
+ pub const Optional = enum(u64) {
+ none = std.math.maxInt(u64),
_,
pub fn unwrap(opt: Optional) ?AnalUnit {
return switch (opt) {
@@ -400,97 +427,30 @@ pub const AnalUnit = packed struct(u32) {
};
};
-/// Comptime Analysis Unit. This is the "subject" of semantic analysis where the root context is
-/// comptime; every `Sema` is owned by either a `Cau` or a runtime function (see `AnalUnit`).
-/// The state stored here is immutable.
-///
-/// * Every ZIR `declaration` has a `Cau` (post-instantiation) to analyze the declaration body.
-/// * Every `struct`, `union`, and `enum` has a `Cau` for type resolution.
-///
-/// The analysis status of a `Cau` is known only from state in `Zcu`.
-/// An entry in `Zcu.failed_analysis` indicates an analysis failure with associated error message.
-/// An entry in `Zcu.transitive_failed_analysis` indicates a transitive analysis failure.
-///
-/// 12 bytes.
-pub const Cau = struct {
- /// The `declaration`, `struct_decl`, `enum_decl`, or `union_decl` instruction which this `Cau` analyzes.
+pub const ComptimeUnit = extern struct {
zir_index: TrackedInst.Index,
- /// The namespace which this `Cau` should be analyzed within.
namespace: NamespaceIndex,
- /// This field essentially tells us what to do with the information resulting from
- /// semantic analysis. See `Owner.Unwrapped` for details.
- owner: Owner,
-
- /// See `Owner.Unwrapped` for details. In terms of representation, the `InternPool.Index`
- /// or `Nav.Index` is cast to a `u31` and stored in `index`. As a special case, if
- /// `@as(u32, @bitCast(owner)) == 0xFFFF_FFFF`, then the value is treated as `.none`.
- pub const Owner = packed struct(u32) {
- kind: enum(u1) { type, nav },
- index: u31,
-
- pub const Unwrapped = union(enum) {
- /// This `Cau` exists in isolation. It is a global `comptime` declaration, or (TODO ANYTHING ELSE?).
- /// After semantic analysis completes, the result is discarded.
- none,
- /// This `Cau` is owned by the given type for type resolution.
- /// This is a `struct`, `union`, or `enum` type.
- type: InternPool.Index,
- /// This `Cau` is owned by the given `Nav` to resolve its value.
- /// When analyzing the `Cau`, the resulting value is stored as the value of this `Nav`.
- nav: Nav.Index,
- };
- pub fn unwrap(owner: Owner) Unwrapped {
- if (@as(u32, @bitCast(owner)) == std.math.maxInt(u32)) {
- return .none;
- }
- return switch (owner.kind) {
- .type => .{ .type = @enumFromInt(owner.index) },
- .nav => .{ .nav = @enumFromInt(owner.index) },
- };
- }
-
- fn wrap(raw: Unwrapped) Owner {
- return switch (raw) {
- .none => @bitCast(@as(u32, std.math.maxInt(u32))),
- .type => |ty| .{ .kind = .type, .index = @intCast(@intFromEnum(ty)) },
- .nav => |nav| .{ .kind = .nav, .index = @intCast(@intFromEnum(nav)) },
- };
- }
- };
+ comptime {
+ assert(std.meta.hasUniqueRepresentation(ComptimeUnit));
+ }
- pub const Index = enum(u32) {
+ pub const Id = enum(u32) {
_,
- pub const Optional = enum(u32) {
- none = std.math.maxInt(u32),
- _,
- pub fn unwrap(opt: Optional) ?Cau.Index {
- return switch (opt) {
- .none => null,
- _ => @enumFromInt(@intFromEnum(opt)),
- };
- }
-
- const debug_state = InternPool.debug_state;
- };
- pub fn toOptional(i: Cau.Index) Optional {
- return @enumFromInt(@intFromEnum(i));
- }
const Unwrapped = struct {
tid: Zcu.PerThread.Id,
index: u32,
-
- fn wrap(unwrapped: Unwrapped, ip: *const InternPool) Cau.Index {
+ fn wrap(unwrapped: Unwrapped, ip: *const InternPool) ComptimeUnit.Id {
assert(@intFromEnum(unwrapped.tid) <= ip.getTidMask());
- assert(unwrapped.index <= ip.getIndexMask(u31));
- return @enumFromInt(@as(u32, @intFromEnum(unwrapped.tid)) << ip.tid_shift_31 |
+ assert(unwrapped.index <= ip.getIndexMask(u32));
+ return @enumFromInt(@as(u32, @intFromEnum(unwrapped.tid)) << ip.tid_shift_32 |
unwrapped.index);
}
};
- fn unwrap(cau_index: Cau.Index, ip: *const InternPool) Unwrapped {
+ fn unwrap(id: Id, ip: *const InternPool) Unwrapped {
return .{
- .tid = @enumFromInt(@intFromEnum(cau_index) >> ip.tid_shift_31 & ip.getTidMask()),
- .index = @intFromEnum(cau_index) & ip.getIndexMask(u31),
+ .tid = @enumFromInt(@intFromEnum(id) >> ip.tid_shift_32 & ip.getTidMask()),
+ .index = @intFromEnum(id) & ip.getIndexMask(u31),
};
}
@@ -507,6 +467,11 @@ pub const Cau = struct {
/// * Generic instances have a `Nav` corresponding to the instantiated function.
/// * `@extern` calls create a `Nav` whose value is a `.@"extern"`.
///
+/// This data structure is optimized for the `analysis_info != null` case, because this is much more
+/// common in practice; the other case is used only for externs and for generic instances. At the time
+/// of writing, in the compiler itself, around 74% of all `Nav`s have `analysis_info != null`.
+/// (Specifically, 104225 / 140923)
+///
/// `Nav.Repr` is the in-memory representation.
pub const Nav = struct {
/// The unqualified name of this `Nav`. Namespace lookups use this name, and error messages may use it.
@@ -514,16 +479,31 @@ pub const Nav = struct {
name: NullTerminatedString,
/// The fully-qualified name of this `Nav`.
fqn: NullTerminatedString,
- /// If the value of this `Nav` is resolved by semantic analysis, it is within this `Cau`.
- /// If this is `.none`, then `status == .resolved` always.
- analysis_owner: Cau.Index.Optional,
+ /// This field is populated iff this `Nav` is resolved by semantic analysis.
+ /// If this is `null`, then `status == .resolved` always.
+ analysis: ?struct {
+ namespace: NamespaceIndex,
+ zir_index: TrackedInst.Index,
+ },
/// TODO: this is a hack! If #20663 isn't accepted, let's figure out something a bit better.
is_usingnamespace: bool,
status: union(enum) {
- /// This `Nav` is pending semantic analysis through `analysis_owner`.
+ /// This `Nav` is pending semantic analysis.
unresolved,
+ /// The type of this `Nav` is resolved; the value is queued for resolution.
+ type_resolved: struct {
+ type: InternPool.Index,
+ alignment: Alignment,
+ @"linksection": OptionalNullTerminatedString,
+ @"addrspace": std.builtin.AddressSpace,
+ is_const: bool,
+ is_threadlocal: bool,
+ /// This field is whether this `Nav` is a literal `extern` definition.
+ /// It does *not* tell you whether this might alias an extern fn (see #21027).
+ is_extern_decl: bool,
+ },
/// The value of this `Nav` is resolved.
- resolved: struct {
+ fully_resolved: struct {
val: InternPool.Index,
alignment: Alignment,
@"linksection": OptionalNullTerminatedString,
@@ -531,30 +511,96 @@ pub const Nav = struct {
},
},
- /// Asserts that `status == .resolved`.
+ /// Asserts that `status != .unresolved`.
pub fn typeOf(nav: Nav, ip: *const InternPool) InternPool.Index {
- return ip.typeOf(nav.status.resolved.val);
+ return switch (nav.status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| r.type,
+ .fully_resolved => |r| ip.typeOf(r.val),
+ };
+ }
+
+ /// Always returns `null` for `status == .type_resolved`. This function is inteded
+ /// to be used by code generation, since semantic analysis will ensure that any `Nav`
+ /// which is potentially `extern` is fully resolved.
+ /// Asserts that `status != .unresolved`.
+ pub fn getExtern(nav: Nav, ip: *const InternPool) ?Key.Extern {
+ return switch (nav.status) {
+ .unresolved => unreachable,
+ .type_resolved => null,
+ .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
+ .@"extern" => |e| e,
+ else => null,
+ },
+ };
}
- /// Asserts that `status == .resolved`.
- pub fn isExtern(nav: Nav, ip: *const InternPool) bool {
- return ip.indexToKey(nav.status.resolved.val) == .@"extern";
+ /// Asserts that `status != .unresolved`.
+ pub fn getAddrspace(nav: Nav) std.builtin.AddressSpace {
+ return switch (nav.status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| r.@"addrspace",
+ .fully_resolved => |r| r.@"addrspace",
+ };
+ }
+
+ /// Asserts that `status != .unresolved`.
+ pub fn getAlignment(nav: Nav) Alignment {
+ return switch (nav.status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| r.alignment,
+ .fully_resolved => |r| r.alignment,
+ };
+ }
+
+ /// Asserts that `status != .unresolved`.
+ pub fn isThreadlocal(nav: Nav, ip: *const InternPool) bool {
+ return switch (nav.status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| r.is_threadlocal,
+ .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
+ .@"extern" => |e| e.is_threadlocal,
+ .variable => |v| v.is_threadlocal,
+ else => false,
+ },
+ };
+ }
+
+ /// If this returns `true`, then a pointer to this `Nav` might actually be encoded as a pointer
+ /// to some other `Nav` due to an extern definition or extern alias (see #21027).
+ /// This query is valid on `Nav`s for whom only the type is resolved.
+ /// Asserts that `status != .unresolved`.
+ pub fn isExternOrFn(nav: Nav, ip: *const InternPool) bool {
+ return switch (nav.status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| {
+ if (r.is_extern_decl) return true;
+ const tag = ip.zigTypeTagOrPoison(r.type) catch unreachable;
+ if (tag == .@"fn") return true;
+ return false;
+ },
+ .fully_resolved => |r| {
+ if (ip.indexToKey(r.val) == .@"extern") return true;
+ const tag = ip.zigTypeTagOrPoison(ip.typeOf(r.val)) catch unreachable;
+ if (tag == .@"fn") return true;
+ return false;
+ },
+ };
}
/// Get the ZIR instruction corresponding to this `Nav`, used to resolve source locations.
/// This is a `declaration`.
pub fn srcInst(nav: Nav, ip: *const InternPool) TrackedInst.Index {
- if (nav.analysis_owner.unwrap()) |cau| {
- return ip.getCau(cau).zir_index;
+ if (nav.analysis) |a| {
+ return a.zir_index;
}
- // A `Nav` with no corresponding `Cau` always has a resolved value.
- return switch (ip.indexToKey(nav.status.resolved.val)) {
+ // A `Nav` which does not undergo analysis always has a resolved value.
+ return switch (ip.indexToKey(nav.status.fully_resolved.val)) {
.func => |func| {
- // Since there was no `analysis_owner`, this must be an instantiation.
- // Go up to the generic owner and consult *its* `analysis_owner`.
+ // Since `analysis` was not populated, this must be an instantiation.
+ // Go up to the generic owner and consult *its* `analysis` field.
const go_nav = ip.getNav(ip.indexToKey(func.generic_owner).func.owner_nav);
- const go_cau = ip.getCau(go_nav.analysis_owner.unwrap().?);
- return go_cau.zir_index;
+ return go_nav.analysis.?.zir_index;
},
.@"extern" => |@"extern"| @"extern".zir_index, // extern / @extern
else => unreachable,
@@ -600,24 +646,29 @@ pub const Nav = struct {
};
/// The compact in-memory representation of a `Nav`.
- /// 18 bytes.
+ /// 26 bytes.
const Repr = struct {
name: NullTerminatedString,
fqn: NullTerminatedString,
- analysis_owner: Cau.Index.Optional,
- /// Populated only if `bits.status == .resolved`.
- val: InternPool.Index,
- /// Populated only if `bits.status == .resolved`.
+ // The following 1 fields are either both populated, or both `.none`.
+ analysis_namespace: OptionalNamespaceIndex,
+ analysis_zir_index: TrackedInst.Index.Optional,
+ /// Populated only if `bits.status != .unresolved`.
+ type_or_val: InternPool.Index,
+ /// Populated only if `bits.status != .unresolved`.
@"linksection": OptionalNullTerminatedString,
bits: Bits,
const Bits = packed struct(u16) {
- status: enum(u1) { unresolved, resolved },
- /// Populated only if `bits.status == .resolved`.
+ status: enum(u2) { unresolved, type_resolved, fully_resolved, type_resolved_extern_decl },
+ /// Populated only if `bits.status != .unresolved`.
alignment: Alignment,
- /// Populated only if `bits.status == .resolved`.
+ /// Populated only if `bits.status != .unresolved`.
@"addrspace": std.builtin.AddressSpace,
- _: u3 = 0,
+ /// Populated only if `bits.status == .type_resolved`.
+ is_const: bool,
+ /// Populated only if `bits.status == .type_resolved`.
+ is_threadlocal: bool,
is_usingnamespace: bool,
};
@@ -625,12 +676,27 @@ pub const Nav = struct {
return .{
.name = repr.name,
.fqn = repr.fqn,
- .analysis_owner = repr.analysis_owner,
+ .analysis = if (repr.analysis_namespace.unwrap()) |namespace| .{
+ .namespace = namespace,
+ .zir_index = repr.analysis_zir_index.unwrap().?,
+ } else a: {
+ assert(repr.analysis_zir_index == .none);
+ break :a null;
+ },
.is_usingnamespace = repr.bits.is_usingnamespace,
.status = switch (repr.bits.status) {
.unresolved => .unresolved,
- .resolved => .{ .resolved = .{
- .val = repr.val,
+ .type_resolved, .type_resolved_extern_decl => .{ .type_resolved = .{
+ .type = repr.type_or_val,
+ .alignment = repr.bits.alignment,
+ .@"linksection" = repr.@"linksection",
+ .@"addrspace" = repr.bits.@"addrspace",
+ .is_const = repr.bits.is_const,
+ .is_threadlocal = repr.bits.is_threadlocal,
+ .is_extern_decl = repr.bits.status == .type_resolved_extern_decl,
+ } },
+ .fully_resolved => .{ .fully_resolved = .{
+ .val = repr.type_or_val,
.alignment = repr.bits.alignment,
.@"linksection" = repr.@"linksection",
.@"addrspace" = repr.bits.@"addrspace",
@@ -646,14 +712,17 @@ pub const Nav = struct {
return .{
.name = nav.name,
.fqn = nav.fqn,
- .analysis_owner = nav.analysis_owner,
- .val = switch (nav.status) {
+ .analysis_namespace = if (nav.analysis) |a| a.namespace.toOptional() else .none,
+ .analysis_zir_index = if (nav.analysis) |a| a.zir_index.toOptional() else .none,
+ .type_or_val = switch (nav.status) {
.unresolved => .none,
- .resolved => |r| r.val,
+ .type_resolved => |r| r.type,
+ .fully_resolved => |r| r.val,
},
.@"linksection" = switch (nav.status) {
.unresolved => .none,
- .resolved => |r| r.@"linksection",
+ .type_resolved => |r| r.@"linksection",
+ .fully_resolved => |r| r.@"linksection",
},
.bits = switch (nav.status) {
.unresolved => .{
@@ -661,12 +730,24 @@ pub const Nav = struct {
.alignment = .none,
.@"addrspace" = .generic,
.is_usingnamespace = nav.is_usingnamespace,
+ .is_const = false,
+ .is_threadlocal = false,
+ },
+ .type_resolved => |r| .{
+ .status = if (r.is_extern_decl) .type_resolved_extern_decl else .type_resolved,
+ .alignment = r.alignment,
+ .@"addrspace" = r.@"addrspace",
+ .is_usingnamespace = nav.is_usingnamespace,
+ .is_const = r.is_const,
+ .is_threadlocal = r.is_threadlocal,
},
- .resolved => |r| .{
- .status = .resolved,
+ .fully_resolved => |r| .{
+ .status = .fully_resolved,
.alignment = r.alignment,
.@"addrspace" = r.@"addrspace",
.is_usingnamespace = nav.is_usingnamespace,
+ .is_const = false,
+ .is_threadlocal = false,
},
},
};
@@ -677,6 +758,7 @@ pub const Dependee = union(enum) {
file: FileIndex,
src_hash: TrackedInst.Index,
nav_val: Nav.Index,
+ nav_ty: Nav.Index,
interned: Index,
namespace: TrackedInst.Index,
namespace_name: NamespaceNameKey,
@@ -726,6 +808,7 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI
.file => |x| ip.file_deps.get(x),
.src_hash => |x| ip.src_hash_deps.get(x),
.nav_val => |x| ip.nav_val_deps.get(x),
+ .nav_ty => |x| ip.nav_ty_deps.get(x),
.interned => |x| ip.interned_deps.get(x),
.namespace => |x| ip.namespace_deps.get(x),
.namespace_name => |x| ip.namespace_name_deps.get(x),
@@ -763,6 +846,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
.file => ip.file_deps,
.src_hash => ip.src_hash_deps,
.nav_val => ip.nav_val_deps,
+ .nav_ty => ip.nav_ty_deps,
.interned => ip.interned_deps,
.namespace => ip.namespace_deps,
.namespace_name => ip.namespace_name_deps,
@@ -862,8 +946,8 @@ const Local = struct {
tracked_insts: ListMutate,
files: ListMutate,
maps: ListMutate,
- caus: ListMutate,
navs: ListMutate,
+ comptime_units: ListMutate,
namespaces: BucketListMutate,
} align(std.atomic.cache_line),
@@ -876,8 +960,8 @@ const Local = struct {
tracked_insts: TrackedInsts,
files: List(File),
maps: Maps,
- caus: Caus,
navs: Navs,
+ comptime_units: ComptimeUnits,
namespaces: Namespaces,
@@ -899,8 +983,8 @@ const Local = struct {
const Strings = List(struct { u8 });
const TrackedInsts = List(struct { TrackedInst.MaybeLost });
const Maps = List(struct { FieldMap });
- const Caus = List(struct { Cau });
const Navs = List(Nav.Repr);
+ const ComptimeUnits = List(struct { ComptimeUnit });
const namespaces_bucket_width = 8;
const namespaces_bucket_mask = (1 << namespaces_bucket_width) - 1;
@@ -1275,21 +1359,21 @@ const Local = struct {
};
}
- pub fn getMutableCaus(local: *Local, gpa: Allocator) Caus.Mutable {
+ pub fn getMutableNavs(local: *Local, gpa: Allocator) Navs.Mutable {
return .{
.gpa = gpa,
.arena = &local.mutate.arena,
- .mutate = &local.mutate.caus,
- .list = &local.shared.caus,
+ .mutate = &local.mutate.navs,
+ .list = &local.shared.navs,
};
}
- pub fn getMutableNavs(local: *Local, gpa: Allocator) Navs.Mutable {
+ pub fn getMutableComptimeUnits(local: *Local, gpa: Allocator) ComptimeUnits.Mutable {
return .{
.gpa = gpa,
.arena = &local.mutate.arena,
- .mutate = &local.mutate.navs,
- .list = &local.shared.navs,
+ .mutate = &local.mutate.comptime_units,
+ .list = &local.shared.comptime_units,
};
}
@@ -2018,7 +2102,6 @@ pub const Key = union(enum) {
ty: Index,
init: Index,
owner_nav: Nav.Index,
- lib_name: OptionalNullTerminatedString,
is_threadlocal: bool,
is_weak_linkage: bool,
};
@@ -2111,36 +2194,36 @@ pub const Key = union(enum) {
return @atomicLoad(FuncAnalysis, func.analysisPtr(ip), .unordered);
}
- pub fn setAnalysisState(func: Func, ip: *InternPool, state: FuncAnalysis.State) void {
+ pub fn setCallsOrAwaitsErrorableFn(func: Func, ip: *InternPool, value: bool) void {
const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
const analysis_ptr = func.analysisPtr(ip);
var analysis = analysis_ptr.*;
- analysis.state = state;
+ analysis.calls_or_awaits_errorable_fn = value;
@atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
}
- pub fn setCallsOrAwaitsErrorableFn(func: Func, ip: *InternPool, value: bool) void {
+ pub fn setBranchHint(func: Func, ip: *InternPool, hint: std.builtin.BranchHint) void {
const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
const analysis_ptr = func.analysisPtr(ip);
var analysis = analysis_ptr.*;
- analysis.calls_or_awaits_errorable_fn = value;
+ analysis.branch_hint = hint;
@atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
}
- pub fn setBranchHint(func: Func, ip: *InternPool, hint: std.builtin.BranchHint) void {
+ pub fn setAnalyzed(func: Func, ip: *InternPool) void {
const extra_mutex = &ip.getLocal(func.tid).mutate.extra.mutex;
extra_mutex.lock();
defer extra_mutex.unlock();
const analysis_ptr = func.analysisPtr(ip);
var analysis = analysis_ptr.*;
- analysis.branch_hint = hint;
+ analysis.is_analyzed = true;
@atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
}
@@ -2741,7 +2824,6 @@ pub const Key = union(enum) {
return a_info.owner_nav == b_info.owner_nav and
a_info.ty == b_info.ty and
a_info.init == b_info.init and
- a_info.lib_name == b_info.lib_name and
a_info.is_threadlocal == b_info.is_threadlocal and
a_info.is_weak_linkage == b_info.is_weak_linkage;
},
@@ -3054,8 +3136,6 @@ pub const LoadedUnionType = struct {
// TODO: the non-fqn will be needed by the new dwarf structure
/// The name of this union type.
name: NullTerminatedString,
- /// The `Cau` within which type resolution occurs.
- cau: Cau.Index,
/// Represents the declarations inside this union.
namespace: NamespaceIndex,
/// The enum tag type.
@@ -3372,7 +3452,6 @@ pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType {
.tid = unwrapped_index.tid,
.extra_index = data,
.name = type_union.data.name,
- .cau = type_union.data.cau,
.namespace = type_union.data.namespace,
.enum_tag_ty = type_union.data.tag_ty,
.field_types = field_types,
@@ -3389,8 +3468,6 @@ pub const LoadedStructType = struct {
// TODO: the non-fqn will be needed by the new dwarf structure
/// The name of this struct type.
name: NullTerminatedString,
- /// The `Cau` within which type resolution occurs.
- cau: Cau.Index,
namespace: NamespaceIndex,
/// Index of the `struct_decl` or `reify` ZIR instruction.
zir_index: TrackedInst.Index,
@@ -3981,7 +4058,6 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
switch (item.tag) {
.type_struct => {
const name: NullTerminatedString = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "name").?]);
- const cau: Cau.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "cau").?]);
const namespace: NamespaceIndex = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?]);
const zir_index: TrackedInst.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "zir_index").?]);
const fields_len = extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "fields_len").?];
@@ -4068,7 +4144,6 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
.tid = unwrapped_index.tid,
.extra_index = item.data,
.name = name,
- .cau = cau,
.namespace = namespace,
.zir_index = zir_index,
.layout = if (flags.is_extern) .@"extern" else .auto,
@@ -4085,7 +4160,6 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
},
.type_struct_packed, .type_struct_packed_inits => {
const name: NullTerminatedString = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?]);
- const cau: Cau.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "cau").?]);
const zir_index: TrackedInst.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "zir_index").?]);
const fields_len = extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "fields_len").?];
const namespace: NamespaceIndex = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?]);
@@ -4132,7 +4206,6 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
.tid = unwrapped_index.tid,
.extra_index = item.data,
.name = name,
- .cau = cau,
.namespace = namespace,
.zir_index = zir_index,
.layout = .@"packed",
@@ -4155,9 +4228,6 @@ pub const LoadedEnumType = struct {
// TODO: the non-fqn will be needed by the new dwarf structure
/// The name of this enum type.
name: NullTerminatedString,
- /// The `Cau` within which type resolution occurs.
- /// `null` if this is a generated tag type.
- cau: Cau.Index.Optional,
/// Represents the declarations inside this enum.
namespace: NamespaceIndex,
/// An integer type which is used for the numerical value of the enum.
@@ -4234,21 +4304,15 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType {
.type_enum_auto => {
const extra = extraDataTrail(extra_list, EnumAuto, item.data);
var extra_index: u32 = @intCast(extra.end);
- const cau: Cau.Index.Optional = if (extra.data.zir_index == .none) cau: {
+ if (extra.data.zir_index == .none) {
extra_index += 1; // owner_union
- break :cau .none;
- } else cau: {
- const cau: Cau.Index = @enumFromInt(extra_list.view().items(.@"0")[extra_index]);
- extra_index += 1; // cau
- break :cau cau.toOptional();
- };
+ }
const captures_len = if (extra.data.captures_len == std.math.maxInt(u32)) c: {
extra_index += 2; // type_hash: PackedU64
break :c 0;
} else extra.data.captures_len;
return .{
.name = extra.data.name,
- .cau = cau,
.namespace = extra.data.namespace,
.tag_ty = extra.data.int_tag_type,
.names = .{
@@ -4274,21 +4338,15 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType {
};
const extra = extraDataTrail(extra_list, EnumExplicit, item.data);
var extra_index: u32 = @intCast(extra.end);
- const cau: Cau.Index.Optional = if (extra.data.zir_index == .none) cau: {
+ if (extra.data.zir_index == .none) {
extra_index += 1; // owner_union
- break :cau .none;
- } else cau: {
- const cau: Cau.Index = @enumFromInt(extra_list.view().items(.@"0")[extra_index]);
- extra_index += 1; // cau
- break :cau cau.toOptional();
- };
+ }
const captures_len = if (extra.data.captures_len == std.math.maxInt(u32)) c: {
extra_index += 2; // type_hash: PackedU64
break :c 0;
} else extra.data.captures_len;
return .{
.name = extra.data.name,
- .cau = cau,
.namespace = extra.data.namespace,
.tag_ty = extra.data.int_tag_type,
.names = .{
@@ -5258,7 +5316,6 @@ pub const Tag = enum(u8) {
.payload = EnumExplicit,
.trailing = struct {
owner_union: Index,
- cau: ?Cau.Index,
captures: ?[]CaptureValue,
type_hash: ?u64,
field_names: []NullTerminatedString,
@@ -5304,7 +5361,6 @@ pub const Tag = enum(u8) {
.payload = EnumAuto,
.trailing = struct {
owner_union: ?Index,
- cau: ?Cau.Index,
captures: ?[]CaptureValue,
type_hash: ?u64,
field_names: []NullTerminatedString,
@@ -5573,9 +5629,6 @@ pub const Tag = enum(u8) {
/// May be `none`.
init: Index,
owner_nav: Nav.Index,
- /// Library name if specified.
- /// For example `extern "c" var stderrp = ...` would have 'c' as library name.
- lib_name: OptionalNullTerminatedString,
flags: Flags,
pub const Flags = packed struct(u32) {
@@ -5684,7 +5737,6 @@ pub const Tag = enum(u8) {
size: u32,
/// Only valid after .have_layout
padding: u32,
- cau: Cau.Index,
namespace: NamespaceIndex,
/// The enum that provides the list of field names and values.
tag_ty: Index,
@@ -5715,7 +5767,6 @@ pub const Tag = enum(u8) {
/// 5. init: Index for each fields_len // if tag is type_struct_packed_inits
pub const TypeStructPacked = struct {
name: NullTerminatedString,
- cau: Cau.Index,
zir_index: TrackedInst.Index,
fields_len: u32,
namespace: NamespaceIndex,
@@ -5763,7 +5814,6 @@ pub const Tag = enum(u8) {
/// 8. field_offset: u32 // for each field in declared order, undef until layout_resolved
pub const TypeStruct = struct {
name: NullTerminatedString,
- cau: Cau.Index,
zir_index: TrackedInst.Index,
namespace: NamespaceIndex,
fields_len: u32,
@@ -5820,7 +5870,7 @@ pub const Tag = enum(u8) {
/// equality or hashing, except for `inferred_error_set` which is considered
/// to be part of the type of the function.
pub const FuncAnalysis = packed struct(u32) {
- state: State,
+ is_analyzed: bool,
branch_hint: std.builtin.BranchHint,
is_noinline: bool,
calls_or_awaits_errorable_fn: bool,
@@ -5828,20 +5878,7 @@ pub const FuncAnalysis = packed struct(u32) {
inferred_error_set: bool,
disable_instrumentation: bool,
- _: u23 = 0,
-
- pub const State = enum(u2) {
- /// The runtime function has never been referenced.
- /// As such, it has never been analyzed, nor is it queued for analysis.
- unreferenced,
- /// The runtime function has been referenced, but has not yet been analyzed.
- /// Its semantic analysis is queued.
- queued,
- /// The runtime function has been (or is currently being) semantically analyzed.
- /// To know if analysis succeeded, consult `zcu.[transitive_]failed_analysis`.
- /// To know if analysis is up-to-date, consult `zcu.[potentially_]outdated`.
- analyzed,
- };
+ _: u24 = 0,
};
pub const Bytes = struct {
@@ -6093,11 +6130,10 @@ pub const Array = struct {
/// Trailing:
/// 0. owner_union: Index // if `zir_index == .none`
-/// 1. cau: Cau.Index // if `zir_index != .none`
-/// 2. capture: CaptureValue // for each `captures_len`
-/// 3. type_hash: PackedU64 // if reified (`captures_len == std.math.maxInt(u32)`)
-/// 4. field name: NullTerminatedString for each fields_len; declaration order
-/// 5. tag value: Index for each fields_len; declaration order
+/// 1. capture: CaptureValue // for each `captures_len`
+/// 2. type_hash: PackedU64 // if reified (`captures_len == std.math.maxInt(u32)`)
+/// 3. field name: NullTerminatedString for each fields_len; declaration order
+/// 4. tag value: Index for each fields_len; declaration order
pub const EnumExplicit = struct {
name: NullTerminatedString,
/// `std.math.maxInt(u32)` indicates this type is reified.
@@ -6120,10 +6156,9 @@ pub const EnumExplicit = struct {
/// Trailing:
/// 0. owner_union: Index // if `zir_index == .none`
-/// 1. cau: Cau.Index // if `zir_index != .none`
-/// 2. capture: CaptureValue // for each `captures_len`
-/// 3. type_hash: PackedU64 // if reified (`captures_len == std.math.maxInt(u32)`)
-/// 4. field name: NullTerminatedString for each fields_len; declaration order
+/// 1. capture: CaptureValue // for each `captures_len`
+/// 2. type_hash: PackedU64 // if reified (`captures_len == std.math.maxInt(u32)`)
+/// 3. field name: NullTerminatedString for each fields_len; declaration order
pub const EnumAuto = struct {
name: NullTerminatedString,
/// `std.math.maxInt(u32)` indicates this type is reified.
@@ -6413,32 +6448,32 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void {
ip.locals = try gpa.alloc(Local, used_threads);
@memset(ip.locals, .{
.shared = .{
- .items = Local.List(Item).empty,
- .extra = Local.Extra.empty,
- .limbs = Local.Limbs.empty,
- .strings = Local.Strings.empty,
- .tracked_insts = Local.TrackedInsts.empty,
- .files = Local.List(File).empty,
- .maps = Local.Maps.empty,
- .caus = Local.Caus.empty,
- .navs = Local.Navs.empty,
-
- .namespaces = Local.Namespaces.empty,
+ .items = .empty,
+ .extra = .empty,
+ .limbs = .empty,
+ .strings = .empty,
+ .tracked_insts = .empty,
+ .files = .empty,
+ .maps = .empty,
+ .navs = .empty,
+ .comptime_units = .empty,
+
+ .namespaces = .empty,
},
.mutate = .{
.arena = .{},
- .items = Local.ListMutate.empty,
- .extra = Local.ListMutate.empty,
- .limbs = Local.ListMutate.empty,
- .strings = Local.ListMutate.empty,
- .tracked_insts = Local.ListMutate.empty,
- .files = Local.ListMutate.empty,
- .maps = Local.ListMutate.empty,
- .caus = Local.ListMutate.empty,
- .navs = Local.ListMutate.empty,
+ .items = .empty,
+ .extra = .empty,
+ .limbs = .empty,
+ .strings = .empty,
+ .tracked_insts = .empty,
+ .files = .empty,
+ .maps = .empty,
+ .navs = .empty,
+ .comptime_units = .empty,
- .namespaces = Local.BucketListMutate.empty,
+ .namespaces = .empty,
},
});
@@ -6486,6 +6521,7 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
ip.file_deps.deinit(gpa);
ip.src_hash_deps.deinit(gpa);
ip.nav_val_deps.deinit(gpa);
+ ip.nav_ty_deps.deinit(gpa);
ip.interned_deps.deinit(gpa);
ip.namespace_deps.deinit(gpa);
ip.namespace_name_deps.deinit(gpa);
@@ -6511,7 +6547,8 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
namespace.priv_decls.deinit(gpa);
namespace.pub_usingnamespace.deinit(gpa);
namespace.priv_usingnamespace.deinit(gpa);
- namespace.other_decls.deinit(gpa);
+ namespace.comptime_decls.deinit(gpa);
+ namespace.test_decls.deinit(gpa);
}
};
const maps = local.getMutableMaps(gpa);
@@ -6530,8 +6567,6 @@ pub fn activate(ip: *const InternPool) void {
_ = OptionalString.debug_state;
_ = NullTerminatedString.debug_state;
_ = OptionalNullTerminatedString.debug_state;
- _ = Cau.Index.debug_state;
- _ = Cau.Index.Optional.debug_state;
_ = Nav.Index.debug_state;
_ = Nav.Index.Optional.debug_state;
std.debug.assert(debug_state.intern_pool == null);
@@ -6716,14 +6751,14 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
if (extra.data.captures_len == std.math.maxInt(u32)) {
break :ns .{ .reified = .{
.zir_index = zir_index,
- .type_hash = extraData(extra_list, PackedU64, extra.end + 1).get(),
+ .type_hash = extraData(extra_list, PackedU64, extra.end).get(),
} };
}
break :ns .{ .declared = .{
.zir_index = zir_index,
.captures = .{ .owned = .{
.tid = unwrapped_index.tid,
- .start = extra.end + 1,
+ .start = extra.end,
.len = extra.data.captures_len,
} },
} };
@@ -6740,14 +6775,14 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
if (extra.data.captures_len == std.math.maxInt(u32)) {
break :ns .{ .reified = .{
.zir_index = zir_index,
- .type_hash = extraData(extra_list, PackedU64, extra.end + 1).get(),
+ .type_hash = extraData(extra_list, PackedU64, extra.end).get(),
} };
}
break :ns .{ .declared = .{
.zir_index = zir_index,
.captures = .{ .owned = .{
.tid = unwrapped_index.tid,
- .start = extra.end + 1,
+ .start = extra.end,
.len = extra.data.captures_len,
} },
} };
@@ -6928,7 +6963,6 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.ty = extra.ty,
.init = extra.init,
.owner_nav = extra.owner_nav,
- .lib_name = extra.lib_name,
.is_threadlocal = extra.flags.is_threadlocal,
.is_weak_linkage = extra.flags.is_weak_linkage,
} };
@@ -6944,8 +6978,8 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.is_threadlocal = extra.flags.is_threadlocal,
.is_weak_linkage = extra.flags.is_weak_linkage,
.is_dll_import = extra.flags.is_dll_import,
- .alignment = nav.status.resolved.alignment,
- .@"addrspace" = nav.status.resolved.@"addrspace",
+ .alignment = nav.status.fully_resolved.alignment,
+ .@"addrspace" = nav.status.fully_resolved.@"addrspace",
.zir_index = extra.zir_index,
.owner_nav = extra.owner_nav,
} };
@@ -7575,7 +7609,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
.ty = variable.ty,
.init = variable.init,
.owner_nav = variable.owner_nav,
- .lib_name = variable.lib_name,
.flags = .{
.is_const = false,
.is_threadlocal = variable.is_threadlocal,
@@ -8330,7 +8363,6 @@ pub fn getUnionType(
.size = std.math.maxInt(u32),
.padding = std.math.maxInt(u32),
.name = undefined, // set by `finish`
- .cau = undefined, // set by `finish`
.namespace = undefined, // set by `finish`
.tag_ty = ini.enum_tag_ty,
.zir_index = switch (ini.key) {
@@ -8382,7 +8414,6 @@ pub fn getUnionType(
.tid = tid,
.index = gop.put(),
.type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name").?,
- .cau_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "cau").?,
.namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "namespace").?,
} };
}
@@ -8391,7 +8422,6 @@ pub const WipNamespaceType = struct {
tid: Zcu.PerThread.Id,
index: Index,
type_name_extra_index: u32,
- cau_extra_index: ?u32,
namespace_extra_index: u32,
pub fn setName(
@@ -8407,18 +8437,11 @@ pub const WipNamespaceType = struct {
pub fn finish(
wip: WipNamespaceType,
ip: *InternPool,
- analysis_owner: Cau.Index.Optional,
namespace: NamespaceIndex,
) Index {
const extra = ip.getLocalShared(wip.tid).extra.acquire();
const extra_items = extra.view().items(.@"0");
- if (wip.cau_extra_index) |i| {
- extra_items[i] = @intFromEnum(analysis_owner.unwrap().?);
- } else {
- assert(analysis_owner == .none);
- }
-
extra_items[wip.namespace_extra_index] = @intFromEnum(namespace);
return wip.index;
@@ -8517,7 +8540,6 @@ pub fn getStructType(
ini.fields_len); // inits
const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStructPacked{
.name = undefined, // set by `finish`
- .cau = undefined, // set by `finish`
.zir_index = zir_index,
.fields_len = ini.fields_len,
.namespace = undefined, // set by `finish`
@@ -8562,7 +8584,6 @@ pub fn getStructType(
.tid = tid,
.index = gop.put(),
.type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?,
- .cau_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "cau").?,
.namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?,
} };
},
@@ -8585,7 +8606,6 @@ pub fn getStructType(
1); // names_map
const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStruct{
.name = undefined, // set by `finish`
- .cau = undefined, // set by `finish`
.zir_index = zir_index,
.namespace = undefined, // set by `finish`
.fields_len = ini.fields_len,
@@ -8654,7 +8674,6 @@ pub fn getStructType(
.tid = tid,
.index = gop.put(),
.type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name").?,
- .cau_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "cau").?,
.namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?,
} };
}
@@ -8878,7 +8897,7 @@ pub fn getFuncDecl(
const func_decl_extra_index = addExtraAssumeCapacity(extra, Tag.FuncDecl{
.analysis = .{
- .state = .unreferenced,
+ .is_analyzed = false,
.branch_hint = .none,
.is_noinline = key.is_noinline,
.calls_or_awaits_errorable_fn = false,
@@ -8987,7 +9006,7 @@ pub fn getFuncDeclIes(
const func_decl_extra_index = addExtraAssumeCapacity(extra, Tag.FuncDecl{
.analysis = .{
- .state = .unreferenced,
+ .is_analyzed = false,
.branch_hint = .none,
.is_noinline = key.is_noinline,
.calls_or_awaits_errorable_fn = false,
@@ -9183,7 +9202,7 @@ pub fn getFuncInstance(
const func_extra_index = addExtraAssumeCapacity(extra, Tag.FuncInstance{
.analysis = .{
- .state = .unreferenced,
+ .is_analyzed = false,
.branch_hint = .none,
.is_noinline = arg.is_noinline,
.calls_or_awaits_errorable_fn = false,
@@ -9281,7 +9300,7 @@ pub fn getFuncInstanceIes(
const func_extra_index = addExtraAssumeCapacity(extra, Tag.FuncInstance{
.analysis = .{
- .state = .unreferenced,
+ .is_analyzed = false,
.branch_hint = .none,
.is_noinline = arg.is_noinline,
.calls_or_awaits_errorable_fn = false,
@@ -9390,7 +9409,7 @@ fn finishFuncInstance(
func_extra_index: u32,
) Allocator.Error!void {
const fn_owner_nav = ip.getNav(ip.funcDeclInfo(generic_owner).owner_nav);
- const fn_namespace = ip.getCau(fn_owner_nav.analysis_owner.unwrap().?).namespace;
+ const fn_namespace = fn_owner_nav.analysis.?.namespace;
// TODO: improve this name
const nav_name = try ip.getOrPutStringFmt(gpa, tid, "{}__anon_{d}", .{
@@ -9400,9 +9419,9 @@ fn finishFuncInstance(
.name = nav_name,
.fqn = try ip.namespacePtr(fn_namespace).internFullyQualifiedName(ip, gpa, tid, nav_name),
.val = func_index,
- .alignment = fn_owner_nav.status.resolved.alignment,
- .@"linksection" = fn_owner_nav.status.resolved.@"linksection",
- .@"addrspace" = fn_owner_nav.status.resolved.@"addrspace",
+ .alignment = fn_owner_nav.status.fully_resolved.alignment,
+ .@"linksection" = fn_owner_nav.status.fully_resolved.@"linksection",
+ .@"addrspace" = fn_owner_nav.status.fully_resolved.@"addrspace",
});
// Populate the owner_nav field which was left undefined until now.
@@ -9436,7 +9455,6 @@ pub const WipEnumType = struct {
index: Index,
tag_ty_index: u32,
type_name_extra_index: u32,
- cau_extra_index: u32,
namespace_extra_index: u32,
names_map: MapIndex,
names_start: u32,
@@ -9456,13 +9474,11 @@ pub const WipEnumType = struct {
pub fn prepare(
wip: WipEnumType,
ip: *InternPool,
- analysis_owner: Cau.Index,
namespace: NamespaceIndex,
) void {
const extra = ip.getLocalShared(wip.tid).extra.acquire();
const extra_items = extra.view().items(.@"0");
- extra_items[wip.cau_extra_index] = @intFromEnum(analysis_owner);
extra_items[wip.namespace_extra_index] = @intFromEnum(namespace);
}
@@ -9563,7 +9579,6 @@ pub fn getEnumType(
.reified => 2, // type_hash: PackedU64
} +
// zig fmt: on
- 1 + // cau
ini.fields_len); // field types
const extra_index = addExtraAssumeCapacity(extra, EnumAuto{
@@ -9584,8 +9599,6 @@ pub fn getEnumType(
.tag = .type_enum_auto,
.data = extra_index,
});
- const cau_extra_index = extra.view().len;
- extra.appendAssumeCapacity(undefined); // `cau` will be set by `finish`
switch (ini.key) {
.declared => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}),
.declared_owned_captures => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))}),
@@ -9598,7 +9611,6 @@ pub fn getEnumType(
.index = gop.put(),
.tag_ty_index = extra_index + std.meta.fieldIndex(EnumAuto, "int_tag_type").?,
.type_name_extra_index = extra_index + std.meta.fieldIndex(EnumAuto, "name").?,
- .cau_extra_index = @intCast(cau_extra_index),
.namespace_extra_index = extra_index + std.meta.fieldIndex(EnumAuto, "namespace").?,
.names_map = names_map,
.names_start = @intCast(names_start),
@@ -9623,7 +9635,6 @@ pub fn getEnumType(
.reified => 2, // type_hash: PackedU64
} +
// zig fmt: on
- 1 + // cau
ini.fields_len + // field types
ini.fields_len * @intFromBool(ini.has_values)); // field values
@@ -9650,8 +9661,6 @@ pub fn getEnumType(
},
.data = extra_index,
});
- const cau_extra_index = extra.view().len;
- extra.appendAssumeCapacity(undefined); // `cau` will be set by `finish`
switch (ini.key) {
.declared => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}),
.declared_owned_captures => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))}),
@@ -9668,7 +9677,6 @@ pub fn getEnumType(
.index = gop.put(),
.tag_ty_index = extra_index + std.meta.fieldIndex(EnumExplicit, "int_tag_type").?,
.type_name_extra_index = extra_index + std.meta.fieldIndex(EnumExplicit, "name").?,
- .cau_extra_index = @intCast(cau_extra_index),
.namespace_extra_index = extra_index + std.meta.fieldIndex(EnumExplicit, "namespace").?,
.names_map = names_map,
.names_start = @intCast(names_start),
@@ -9865,7 +9873,6 @@ pub fn getOpaqueType(
.tid = tid,
.index = gop.put(),
.type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "name").?,
- .cau_extra_index = null, // opaques do not undergo type resolution
.namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "namespace").?,
},
};
@@ -9981,7 +9988,6 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 {
inline for (@typeInfo(@TypeOf(item)).@"struct".fields) |field| {
extra.appendAssumeCapacity(.{switch (field.type) {
Index,
- Cau.Index,
Nav.Index,
NamespaceIndex,
OptionalNamespaceIndex,
@@ -10044,7 +10050,6 @@ fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { dat
const extra_item = extra_items[extra_index];
@field(result, field.name) = switch (field.type) {
Index,
- Cau.Index,
Nav.Index,
NamespaceIndex,
OptionalNamespaceIndex,
@@ -11065,12 +11070,6 @@ pub fn dumpGenericInstancesFallible(ip: *const InternPool, allocator: Allocator)
try bw.flush();
}
-pub fn getCau(ip: *const InternPool, index: Cau.Index) Cau {
- const unwrapped = index.unwrap(ip);
- const caus = ip.getLocalShared(unwrapped.tid).caus.acquire();
- return caus.view().items(.@"0")[unwrapped.index];
-}
-
pub fn getNav(ip: *const InternPool, index: Nav.Index) Nav {
const unwrapped = index.unwrap(ip);
const navs = ip.getLocalShared(unwrapped.tid).navs.acquire();
@@ -11084,51 +11083,34 @@ pub fn namespacePtr(ip: *InternPool, namespace_index: NamespaceIndex) *Zcu.Names
return &namespaces_bucket[unwrapped_namespace_index.index];
}
-/// Create a `Cau` associated with the type at the given `InternPool.Index`.
-pub fn createTypeCau(
+/// Create a `ComptimeUnit`, forming an `AnalUnit` for a `comptime` declaration.
+pub fn createComptimeUnit(
ip: *InternPool,
gpa: Allocator,
tid: Zcu.PerThread.Id,
zir_index: TrackedInst.Index,
namespace: NamespaceIndex,
- owner_type: InternPool.Index,
-) Allocator.Error!Cau.Index {
- const caus = ip.getLocal(tid).getMutableCaus(gpa);
- const index_unwrapped: Cau.Index.Unwrapped = .{
+) Allocator.Error!ComptimeUnit.Id {
+ const comptime_units = ip.getLocal(tid).getMutableComptimeUnits(gpa);
+ const id_unwrapped: ComptimeUnit.Id.Unwrapped = .{
.tid = tid,
- .index = caus.mutate.len,
+ .index = comptime_units.mutate.len,
};
- try caus.append(.{.{
+ try comptime_units.append(.{.{
.zir_index = zir_index,
.namespace = namespace,
- .owner = Cau.Owner.wrap(.{ .type = owner_type }),
}});
- return index_unwrapped.wrap(ip);
+ return id_unwrapped.wrap(ip);
}
-/// Create a `Cau` for a `comptime` declaration.
-pub fn createComptimeCau(
- ip: *InternPool,
- gpa: Allocator,
- tid: Zcu.PerThread.Id,
- zir_index: TrackedInst.Index,
- namespace: NamespaceIndex,
-) Allocator.Error!Cau.Index {
- const caus = ip.getLocal(tid).getMutableCaus(gpa);
- const index_unwrapped: Cau.Index.Unwrapped = .{
- .tid = tid,
- .index = caus.mutate.len,
- };
- try caus.append(.{.{
- .zir_index = zir_index,
- .namespace = namespace,
- .owner = Cau.Owner.wrap(.none),
- }});
- return index_unwrapped.wrap(ip);
+pub fn getComptimeUnit(ip: *const InternPool, id: ComptimeUnit.Id) ComptimeUnit {
+ const unwrapped = id.unwrap(ip);
+ const comptime_units = ip.getLocalShared(unwrapped.tid).comptime_units.acquire();
+ return comptime_units.view().items(.@"0")[unwrapped.index];
}
-/// Create a `Nav` not associated with any `Cau`.
-/// Since there is no analysis owner, the `Nav`'s value must be known at creation time.
+/// Create a `Nav` which does not undergo semantic analysis.
+/// Since it is never analyzed, the `Nav`'s value must be known at creation time.
pub fn createNav(
ip: *InternPool,
gpa: Allocator,
@@ -11150,8 +11132,8 @@ pub fn createNav(
try navs.append(Nav.pack(.{
.name = opts.name,
.fqn = opts.fqn,
- .analysis_owner = .none,
- .status = .{ .resolved = .{
+ .analysis = null,
+ .status = .{ .fully_resolved = .{
.val = opts.val,
.alignment = opts.alignment,
.@"linksection" = opts.@"linksection",
@@ -11162,10 +11144,9 @@ pub fn createNav(
return index_unwrapped.wrap(ip);
}
-/// Create a `Cau` and `Nav` which are paired. The value of the `Nav` is
-/// determined by semantic analysis of the `Cau`. The value of the `Nav`
-/// is initially unresolved.
-pub fn createPairedCauNav(
+/// Create a `Nav` which undergoes semantic analysis because it corresponds to a source declaration.
+/// The value of the `Nav` is initially unresolved.
+pub fn createDeclNav(
ip: *InternPool,
gpa: Allocator,
tid: Zcu.PerThread.Id,
@@ -11175,36 +11156,72 @@ pub fn createPairedCauNav(
namespace: NamespaceIndex,
/// TODO: this is hacky! See `Nav.is_usingnamespace`.
is_usingnamespace: bool,
-) Allocator.Error!struct { Cau.Index, Nav.Index } {
- const caus = ip.getLocal(tid).getMutableCaus(gpa);
+) Allocator.Error!Nav.Index {
const navs = ip.getLocal(tid).getMutableNavs(gpa);
- try caus.ensureUnusedCapacity(1);
try navs.ensureUnusedCapacity(1);
- const cau = Cau.Index.Unwrapped.wrap(.{
- .tid = tid,
- .index = caus.mutate.len,
- }, ip);
const nav = Nav.Index.Unwrapped.wrap(.{
.tid = tid,
.index = navs.mutate.len,
}, ip);
- caus.appendAssumeCapacity(.{.{
- .zir_index = zir_index,
- .namespace = namespace,
- .owner = Cau.Owner.wrap(.{ .nav = nav }),
- }});
navs.appendAssumeCapacity(Nav.pack(.{
.name = name,
.fqn = fqn,
- .analysis_owner = cau.toOptional(),
+ .analysis = .{
+ .namespace = namespace,
+ .zir_index = zir_index,
+ },
.status = .unresolved,
.is_usingnamespace = is_usingnamespace,
}));
- return .{ cau, nav };
+ return nav;
+}
+
+/// Resolve the type of a `Nav` with an analysis owner.
+/// If its status is already `resolved`, the old value is discarded.
+pub fn resolveNavType(
+ ip: *InternPool,
+ nav: Nav.Index,
+ resolved: struct {
+ type: InternPool.Index,
+ alignment: Alignment,
+ @"linksection": OptionalNullTerminatedString,
+ @"addrspace": std.builtin.AddressSpace,
+ is_const: bool,
+ is_threadlocal: bool,
+ is_extern_decl: bool,
+ },
+) void {
+ const unwrapped = nav.unwrap(ip);
+
+ const local = ip.getLocal(unwrapped.tid);
+ local.mutate.extra.mutex.lock();
+ defer local.mutate.extra.mutex.unlock();
+
+ const navs = local.shared.navs.view();
+
+ const nav_analysis_namespace = navs.items(.analysis_namespace);
+ const nav_analysis_zir_index = navs.items(.analysis_zir_index);
+ const nav_types = navs.items(.type_or_val);
+ const nav_linksections = navs.items(.@"linksection");
+ const nav_bits = navs.items(.bits);
+
+ assert(nav_analysis_namespace[unwrapped.index] != .none);
+ assert(nav_analysis_zir_index[unwrapped.index] != .none);
+
+ @atomicStore(InternPool.Index, &nav_types[unwrapped.index], resolved.type, .release);
+ @atomicStore(OptionalNullTerminatedString, &nav_linksections[unwrapped.index], resolved.@"linksection", .release);
+
+ var bits = nav_bits[unwrapped.index];
+ bits.status = if (resolved.is_extern_decl) .type_resolved_extern_decl else .type_resolved;
+ bits.alignment = resolved.alignment;
+ bits.@"addrspace" = resolved.@"addrspace";
+ bits.is_const = resolved.is_const;
+ bits.is_threadlocal = resolved.is_threadlocal;
+ @atomicStore(Nav.Repr.Bits, &nav_bits[unwrapped.index], bits, .release);
}
/// Resolve the value of a `Nav` with an analysis owner.
@@ -11227,18 +11244,20 @@ pub fn resolveNavValue(
const navs = local.shared.navs.view();
- const nav_analysis_owners = navs.items(.analysis_owner);
- const nav_vals = navs.items(.val);
+ const nav_analysis_namespace = navs.items(.analysis_namespace);
+ const nav_analysis_zir_index = navs.items(.analysis_zir_index);
+ const nav_vals = navs.items(.type_or_val);
const nav_linksections = navs.items(.@"linksection");
const nav_bits = navs.items(.bits);
- assert(nav_analysis_owners[unwrapped.index] != .none);
+ assert(nav_analysis_namespace[unwrapped.index] != .none);
+ assert(nav_analysis_zir_index[unwrapped.index] != .none);
@atomicStore(InternPool.Index, &nav_vals[unwrapped.index], resolved.val, .release);
@atomicStore(OptionalNullTerminatedString, &nav_linksections[unwrapped.index], resolved.@"linksection", .release);
var bits = nav_bits[unwrapped.index];
- bits.status = .resolved;
+ bits.status = .fully_resolved;
bits.alignment = resolved.alignment;
bits.@"addrspace" = resolved.@"addrspace";
@atomicStore(Nav.Repr.Bits, &nav_bits[unwrapped.index], bits, .release);
diff --git a/src/Sema.zig b/src/Sema.zig
index 110476adf9..ccc9f63a56 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -1284,7 +1284,6 @@ fn analyzeBodyInner(
const extended = datas[@intFromEnum(inst)].extended;
break :ext switch (extended.opcode) {
// zig fmt: off
- .variable => try sema.zirVarExtended( block, extended),
.struct_decl => try sema.zirStructDecl( block, extended, inst),
.enum_decl => try sema.zirEnumDecl( block, extended, inst),
.union_decl => try sema.zirUnionDecl( block, extended, inst),
@@ -2114,13 +2113,33 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize)
}
/// Return the Value corresponding to a given AIR ref, or `null` if it refers to a runtime value.
-/// InternPool key `variable` is considered a runtime value.
/// Generic poison causes `error.GenericPoison` to be returned.
fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
- const val = (try sema.resolveValueAllowVariables(inst)) orelse return null;
- if (val.isGenericPoison()) return error.GenericPoison;
- if (sema.pt.zcu.intern_pool.isVariable(val.toIntern())) return null;
- return val;
+ const zcu = sema.pt.zcu;
+ assert(inst != .none);
+
+ if (try sema.typeHasOnePossibleValue(sema.typeOf(inst))) |opv| {
+ return opv;
+ }
+
+ if (inst.toInterned()) |ip_index| {
+ const val: Value = .fromInterned(ip_index);
+
+ assert(val.getVariable(zcu) == null);
+ if (val.isPtrRuntimeValue(zcu)) return null;
+ if (val.isGenericPoison()) return error.GenericPoison;
+
+ return val;
+ } else {
+ // Runtime-known value.
+ const air_tags = sema.air_instructions.items(.tag);
+ switch (air_tags[@intFromEnum(inst.toIndex().?)]) {
+ .inferred_alloc => unreachable, // assertion failure
+ .inferred_alloc_comptime => unreachable, // assertion failure
+ else => {},
+ }
+ return null;
+ }
}
/// Like `resolveValue`, but emits an error if the value is not comptime-known.
@@ -2183,35 +2202,6 @@ fn resolveValueIntable(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
return try sema.resolveLazyValue(val);
}
-/// Returns all InternPool keys representing values, including `variable`, `undef`, and `generic_poison`.
-fn resolveValueAllowVariables(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
- const pt = sema.pt;
- assert(inst != .none);
- // First section of indexes correspond to a set number of constant values.
- if (@intFromEnum(inst) < InternPool.static_len) {
- return Value.fromInterned(@as(InternPool.Index, @enumFromInt(@intFromEnum(inst))));
- }
-
- const air_tags = sema.air_instructions.items(.tag);
- if (try sema.typeHasOnePossibleValue(sema.typeOf(inst))) |opv| {
- if (inst.toInterned()) |ip_index| {
- const val = Value.fromInterned(ip_index);
- if (val.getVariable(pt.zcu) != null) return val;
- }
- return opv;
- }
- const ip_index = inst.toInterned() orelse {
- switch (air_tags[@intFromEnum(inst.toIndex().?)]) {
- .inferred_alloc => unreachable,
- .inferred_alloc_comptime => unreachable,
- else => return null,
- }
- };
- const val = Value.fromInterned(ip_index);
- if (val.isPtrRuntimeValue(pt.zcu)) return null;
- return val;
-}
-
/// Value Tag may be `undef` or `variable`.
pub fn resolveFinalDeclValue(
sema: *Sema,
@@ -2221,8 +2211,13 @@ pub fn resolveFinalDeclValue(
) CompileError!Value {
const zcu = sema.pt.zcu;
- const val = try sema.resolveValueAllowVariables(air_ref) orelse {
- const value_comptime_reason: ?[]const u8 = if (air_ref.toInterned()) |_|
+ const val = try sema.resolveValue(air_ref) orelse {
+ const is_runtime_ptr = rt_ptr: {
+ const ip_index = air_ref.toInterned() orelse break :rt_ptr false;
+ const val: Value = .fromInterned(ip_index);
+ break :rt_ptr val.isPtrRuntimeValue(zcu);
+ };
+ const value_comptime_reason: ?[]const u8 = if (is_runtime_ptr)
"thread local and dll imported variables have runtime-known addresses"
else
null;
@@ -2232,10 +2227,8 @@ pub fn resolveFinalDeclValue(
.value_comptime_reason = value_comptime_reason,
});
};
- if (val.isGenericPoison()) return error.GenericPoison;
- const init_val: Value = if (val.getVariable(zcu)) |v| .fromInterned(v.init) else val;
- if (init_val.canMutateComptimeVarState(zcu)) {
+ if (val.canMutateComptimeVarState(zcu)) {
return sema.fail(block, src, "global variable contains reference to comptime var", .{});
}
@@ -2877,7 +2870,7 @@ fn zirStructDecl(
};
const wip_ty = switch (try ip.getStructType(gpa, pt.tid, struct_init, false)) {
.existing => |ty| {
- const new_ty = try pt.ensureTypeUpToDate(ty, false);
+ const new_ty = try pt.ensureTypeUpToDate(ty);
// Make sure we update the namespace if the declaration is re-analyzed, to pick
// up on e.g. changed comptime decls.
@@ -2907,12 +2900,10 @@ fn zirStructDecl(
});
errdefer pt.destroyNamespace(new_namespace_index);
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
-
if (pt.zcu.comp.incremental) {
try ip.addDependency(
sema.gpa,
- AnalUnit.wrap(.{ .cau = new_cau_index }),
+ AnalUnit.wrap(.{ .type = wip_ty.index }),
.{ .src_hash = tracked_inst },
);
}
@@ -2929,7 +2920,7 @@ fn zirStructDecl(
}
try sema.declareDependency(.{ .interned = wip_ty.index });
try sema.addTypeReferenceEntry(src, wip_ty.index);
- return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));
+ return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
}
fn createTypeName(
@@ -3107,7 +3098,7 @@ fn zirEnumDecl(
};
const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, enum_init, false)) {
.existing => |ty| {
- const new_ty = try pt.ensureTypeUpToDate(ty, false);
+ const new_ty = try pt.ensureTypeUpToDate(ty);
// Make sure we update the namespace if the declaration is re-analyzed, to pick
// up on e.g. changed comptime decls.
@@ -3143,16 +3134,14 @@ fn zirEnumDecl(
});
errdefer if (!done) pt.destroyNamespace(new_namespace_index);
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
-
try pt.scanNamespace(new_namespace_index, decls);
try sema.declareDependency(.{ .interned = wip_ty.index });
try sema.addTypeReferenceEntry(src, wip_ty.index);
// We've finished the initial construction of this type, and are about to perform analysis.
- // Set the Cau and namespace appropriately, and don't destroy anything on failure.
- wip_ty.prepare(ip, new_cau_index, new_namespace_index);
+ // Set the namespace appropriately, and don't destroy anything on failure.
+ wip_ty.prepare(ip, new_namespace_index);
done = true;
try Sema.resolveDeclaredEnum(
@@ -3162,7 +3151,6 @@ fn zirEnumDecl(
tracked_inst,
new_namespace_index,
type_name,
- new_cau_index,
small,
body,
tag_type_ref,
@@ -3252,7 +3240,7 @@ fn zirUnionDecl(
};
const wip_ty = switch (try ip.getUnionType(gpa, pt.tid, union_init, false)) {
.existing => |ty| {
- const new_ty = try pt.ensureTypeUpToDate(ty, false);
+ const new_ty = try pt.ensureTypeUpToDate(ty);
// Make sure we update the namespace if the declaration is re-analyzed, to pick
// up on e.g. changed comptime decls.
@@ -3282,12 +3270,10 @@ fn zirUnionDecl(
});
errdefer pt.destroyNamespace(new_namespace_index);
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
-
if (pt.zcu.comp.incremental) {
try zcu.intern_pool.addDependency(
gpa,
- AnalUnit.wrap(.{ .cau = new_cau_index }),
+ AnalUnit.wrap(.{ .type = wip_ty.index }),
.{ .src_hash = tracked_inst },
);
}
@@ -3304,7 +3290,7 @@ fn zirUnionDecl(
}
try sema.declareDependency(.{ .interned = wip_ty.index });
try sema.addTypeReferenceEntry(src, wip_ty.index);
- return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));
+ return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
}
fn zirOpaqueDecl(
@@ -3389,7 +3375,7 @@ fn zirOpaqueDecl(
try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
}
try sema.addTypeReferenceEntry(src, wip_ty.index);
- return Air.internedToRef(wip_ty.finish(ip, .none, new_namespace_index));
+ return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
}
fn zirErrorSetDecl(
@@ -6509,9 +6495,9 @@ pub fn analyzeExport(
if (options.linkage == .internal)
return;
- try sema.ensureNavResolved(src, orig_nav_index);
+ try sema.ensureNavResolved(src, orig_nav_index, .fully);
- const exported_nav_index = switch (ip.indexToKey(ip.getNav(orig_nav_index).status.resolved.val)) {
+ const exported_nav_index = switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) {
.variable => |v| v.owner_nav,
.@"extern" => |e| e.owner_nav,
.func => |f| f.owner_nav,
@@ -6534,7 +6520,7 @@ pub fn analyzeExport(
}
// TODO: some backends might support re-exporting extern decls
- if (exported_nav.isExtern(ip)) {
+ if (exported_nav.getExtern(ip) != null) {
return sema.fail(block, src, "export target cannot be extern", .{});
}
@@ -6554,7 +6540,11 @@ fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
const ip = &zcu.intern_pool;
const func = switch (sema.owner.unwrap()) {
.func => |func| func,
- .cau => return, // does nothing outside a function
+ .@"comptime",
+ .nav_val,
+ .nav_ty,
+ .type,
+ => return, // does nothing outside a function
};
ip.funcSetDisableInstrumentation(func);
sema.allow_memoize = false;
@@ -6865,8 +6855,8 @@ fn lookupInNamespace(
}
for (usingnamespaces.items) |sub_ns_nav| {
- try sema.ensureNavResolved(src, sub_ns_nav);
- const sub_ns_ty = Type.fromInterned(ip.getNav(sub_ns_nav).status.resolved.val);
+ try sema.ensureNavResolved(src, sub_ns_nav, .fully);
+ const sub_ns_ty = Type.fromInterned(ip.getNav(sub_ns_nav).status.fully_resolved.val);
const sub_ns = zcu.namespacePtr(sub_ns_ty.getNamespaceIndex(zcu));
try checked_namespaces.put(gpa, sub_ns, {});
}
@@ -6875,11 +6865,8 @@ fn lookupInNamespace(
ignore_self: {
const skip_nav = switch (sema.owner.unwrap()) {
- .func => break :ignore_self,
- .cau => |cau| switch (ip.getCau(cau).owner.unwrap()) {
- .none, .type => break :ignore_self,
- .nav => |nav| nav,
- },
+ .@"comptime", .type, .func => break :ignore_self,
+ .nav_ty, .nav_val => |nav| nav,
};
var i: usize = 0;
while (i < candidates.items.len) {
@@ -7139,7 +7126,7 @@ fn zirCall(
const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call);
switch (sema.owner.unwrap()) {
- .cau => input_is_error = false,
+ .@"comptime", .type, .nav_ty, .nav_val => input_is_error = false,
.func => |owner_func| if (!zcu.intern_pool.funcAnalysisUnordered(owner_func).calls_or_awaits_errorable_fn) {
// No errorable fn actually called; we have no error return trace
input_is_error = false;
@@ -7700,12 +7687,13 @@ fn analyzeCall(
.ptr => |ptr| blk: {
switch (ptr.base_addr) {
.nav => |nav_index| if (ptr.byte_offset == 0) {
+ try sema.ensureNavResolved(call_src, nav_index, .fully);
const nav = ip.getNav(nav_index);
- if (nav.isExtern(ip))
+ if (nav.getExtern(ip) != null)
return sema.fail(block, call_src, "{s} call of extern function pointer", .{
if (is_comptime_call) "comptime" else "inline",
});
- break :blk nav.status.resolved.val;
+ break :blk nav.status.fully_resolved.val;
},
else => {},
}
@@ -7754,11 +7742,9 @@ fn analyzeCall(
// The call site definitely depends on the function's signature.
try sema.declareDependency(.{ .src_hash = module_fn.zir_body_inst });
- // This is not a function instance, so the function's `Nav` has a
- // `Cau` -- we don't need to check `generic_owner`.
+ // This is not a function instance, so the function's `Nav` has analysis
+ // state -- we don't need to check `generic_owner`.
const fn_nav = ip.getNav(module_fn.owner_nav);
- const fn_cau_index = fn_nav.analysis_owner.unwrap().?;
- const fn_cau = ip.getCau(fn_cau_index);
// We effectively want a child Sema here, but can't literally do that, because we need AIR
// to be shared. InlineCallSema is a wrapper which handles this for us. While `ics` is in
@@ -7766,7 +7752,7 @@ fn analyzeCall(
// whenever performing an operation where the difference matters.
var ics = InlineCallSema.init(
sema,
- zcu.cauFileScope(fn_cau_index).zir,
+ zcu.navFileScope(module_fn.owner_nav).zir,
module_fn_index,
block.error_return_trace_index,
);
@@ -7776,7 +7762,7 @@ fn analyzeCall(
.parent = null,
.sema = sema,
// The function body exists in the same namespace as the corresponding function declaration.
- .namespace = fn_cau.namespace,
+ .namespace = fn_nav.analysis.?.namespace,
.instructions = .{},
.label = null,
.inlining = &inlining,
@@ -7787,7 +7773,7 @@ fn analyzeCall(
.runtime_cond = block.runtime_cond,
.runtime_loop = block.runtime_loop,
.runtime_index = block.runtime_index,
- .src_base_inst = fn_cau.zir_index,
+ .src_base_inst = fn_nav.analysis.?.zir_index,
.type_name_ctx = fn_nav.fqn,
};
@@ -7802,7 +7788,7 @@ fn analyzeCall(
// mutate comptime state.
// TODO: comptime call memoization is currently not supported under incremental compilation
// since dependencies are not marked on callers. If we want to keep this around (we should
- // check that it's worthwhile first!), each memoized call needs a `Cau`.
+ // check that it's worthwhile first!), each memoized call needs an `AnalUnit`.
var should_memoize = !zcu.comp.incremental;
// If it's a comptime function call, we need to memoize it as long as no external
@@ -7911,7 +7897,7 @@ fn analyzeCall(
// Since we're doing an inline call, we depend on the source code of the whole
// function declaration.
- try sema.declareDependency(.{ .src_hash = fn_cau.zir_index });
+ try sema.declareDependency(.{ .src_hash = fn_nav.analysis.?.zir_index });
new_fn_info.return_type = sema.fn_ret_ty.toIntern();
if (!is_comptime_call and !block.is_typeof) {
@@ -8023,7 +8009,7 @@ fn analyzeCall(
if (call_dbg_node) |some| try sema.zirDbgStmt(block, some);
switch (sema.owner.unwrap()) {
- .cau => {},
+ .@"comptime", .nav_ty, .nav_val, .type => {},
.func => |owner_func| if (Type.fromInterned(func_ty_info.return_type).isError(zcu)) {
ip.funcSetCallsOrAwaitsErrorableFn(owner_func);
},
@@ -8062,7 +8048,10 @@ fn analyzeCall(
switch (zcu.intern_pool.indexToKey(func_val.toIntern())) {
.func => break :skip_safety,
.ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {
- .nav => |nav| if (!ip.getNav(nav).isExtern(ip)) break :skip_safety,
+ .nav => |nav| {
+ try sema.ensureNavResolved(call_src, nav, .fully);
+ if (ip.getNav(nav).getExtern(ip) == null) break :skip_safety;
+ },
else => {},
},
else => {},
@@ -8259,7 +8248,7 @@ fn instantiateGenericCall(
});
const generic_owner = switch (zcu.intern_pool.indexToKey(func_val.toIntern())) {
.func => func_val.toIntern(),
- .ptr => |ptr| ip.getNav(ptr.base_addr.nav).status.resolved.val,
+ .ptr => |ptr| ip.getNav(ptr.base_addr.nav).status.fully_resolved.val,
else => unreachable,
};
const generic_owner_func = zcu.intern_pool.indexToKey(generic_owner).func;
@@ -8275,10 +8264,9 @@ fn instantiateGenericCall(
// The actual monomorphization happens via adding `func_instance` to
// `InternPool`.
- // Since we are looking at the generic owner here, it has a `Cau`.
+ // Since we are looking at the generic owner here, it has analysis state.
const fn_nav = ip.getNav(generic_owner_func.owner_nav);
- const fn_cau = ip.getCau(fn_nav.analysis_owner.unwrap().?);
- const fn_zir = zcu.namespacePtr(fn_cau.namespace).fileScope(zcu).zir;
+ const fn_zir = zcu.navFileScope(generic_owner_func.owner_nav).zir;
const fn_info = fn_zir.getFnInfo(generic_owner_func.zir_body_inst.resolve(ip) orelse return error.AnalysisFail);
const comptime_args = try sema.arena.alloc(InternPool.Index, args_info.count());
@@ -8319,11 +8307,11 @@ fn instantiateGenericCall(
var child_block: Block = .{
.parent = null,
.sema = &child_sema,
- .namespace = fn_cau.namespace,
+ .namespace = fn_nav.analysis.?.namespace,
.instructions = .{},
.inlining = null,
.is_comptime = true,
- .src_base_inst = fn_cau.zir_index,
+ .src_base_inst = fn_nav.analysis.?.zir_index,
.type_name_ctx = fn_nav.fqn,
};
defer child_block.instructions.deinit(gpa);
@@ -8488,7 +8476,7 @@ fn instantiateGenericCall(
if (call_dbg_node) |some| try sema.zirDbgStmt(block, some);
switch (sema.owner.unwrap()) {
- .cau => {},
+ .@"comptime", .nav_ty, .nav_val, .type => {},
.func => |owner_func| if (Type.fromInterned(func_ty_info.return_type).isError(zcu)) {
ip.funcSetCallsOrAwaitsErrorableFn(owner_func);
},
@@ -9517,16 +9505,13 @@ fn zirFunc(
// the callconv based on whether it is exported. Otherwise, the callconv defaults
// to `.auto`.
const cc: std.builtin.CallingConvention = if (has_body) cc: {
- const func_decl_cau = if (sema.generic_owner != .none) cau: {
- const generic_owner_fn = zcu.funcInfo(sema.generic_owner);
- // The generic owner definitely has a `Cau` for the corresponding function declaration.
- const generic_owner_nav = ip.getNav(generic_owner_fn.owner_nav);
- break :cau generic_owner_nav.analysis_owner.unwrap().?;
- } else sema.owner.unwrap().cau;
+ const func_decl_nav = if (sema.generic_owner != .none) nav: {
+ break :nav zcu.funcInfo(sema.generic_owner).owner_nav;
+ } else sema.owner.unwrap().nav_val;
const fn_is_exported = exported: {
- const decl_inst = ip.getCau(func_decl_cau).zir_index.resolve(ip) orelse return error.AnalysisFail;
- const zir_decl = sema.code.getDeclaration(decl_inst)[0];
- break :exported zir_decl.flags.is_export;
+ const decl_inst = ip.getNav(func_decl_nav).analysis.?.zir_index.resolve(ip) orelse return error.AnalysisFail;
+ const zir_decl = sema.code.getDeclaration(decl_inst);
+ break :exported zir_decl.linkage == .@"export";
};
if (fn_is_exported) {
break :cc target.cCallingConvention() orelse {
@@ -9557,10 +9542,8 @@ fn zirFunc(
ret_ty,
false,
inferred_error_set,
- false,
has_body,
src_locs,
- null,
0,
false,
);
@@ -9619,7 +9602,7 @@ fn resolveGenericBody(
/// respective `Decl` (either `ExternFn` or `Var`).
/// The liveness of the duped library name is tied to liveness of `Zcu`.
/// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`).
-fn handleExternLibName(
+pub fn handleExternLibName(
sema: *Sema,
block: *Block,
src_loc: LazySrcLoc,
@@ -9843,10 +9826,8 @@ fn funcCommon(
bare_return_type: Type,
var_args: bool,
inferred_error_set: bool,
- is_extern: bool,
has_body: bool,
src_locs: Zir.Inst.Func.SrcLocs,
- opt_lib_name: ?[]const u8,
noalias_bits: u32,
is_noinline: bool,
) CompileError!Air.Inst.Ref {
@@ -9998,12 +9979,11 @@ fn funcCommon(
}
if (inferred_error_set) {
- assert(!is_extern);
assert(has_body);
if (!ret_poison)
try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src);
const func_index = try ip.getFuncDeclIes(gpa, pt.tid, .{
- .owner_nav = sema.getOwnerCauNav(),
+ .owner_nav = sema.owner.unwrap().nav_val,
.param_types = param_types,
.noalias_bits = noalias_bits,
@@ -10050,35 +10030,9 @@ fn funcCommon(
.is_noinline = is_noinline,
});
- if (is_extern) {
- assert(comptime_bits == 0);
- assert(!is_generic);
- if (opt_lib_name) |lib_name| try sema.handleExternLibName(block, block.src(.{
- .node_offset_lib_name = src_node_offset,
- }), lib_name);
- const extern_func_index = try sema.resolveExternDecl(block, .fromInterned(func_ty), opt_lib_name, true, false);
- return finishFunc(
- sema,
- block,
- extern_func_index,
- func_ty,
- ret_poison,
- bare_return_type,
- ret_ty_src,
- cc,
- is_source_decl,
- ret_ty_requires_comptime,
- func_inst,
- cc_src,
- is_noinline,
- is_generic,
- final_is_generic,
- );
- }
-
if (has_body) {
const func_index = try ip.getFuncDecl(gpa, pt.tid, .{
- .owner_nav = sema.getOwnerCauNav(),
+ .owner_nav = sema.owner.unwrap().nav_val,
.ty = func_ty,
.cc = cc,
.is_noinline = is_noinline,
@@ -17702,7 +17656,7 @@ fn zirAsm(
if (is_volatile) {
return sema.fail(block, src, "volatile keyword is redundant on module-level assembly", .{});
}
- try zcu.addGlobalAssembly(sema.owner.unwrap().cau, asm_source);
+ try zcu.addGlobalAssembly(sema.owner, asm_source);
return .void_value;
}
@@ -18193,7 +18147,7 @@ fn zirThis(
_ = extended;
const pt = sema.pt;
const namespace = pt.zcu.namespacePtr(block.namespace);
- const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type, false);
+ const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type);
switch (pt.zcu.intern_pool.indexToKey(new_ty)) {
.struct_type, .union_type, .enum_type => try sema.declareDependency(.{ .interned = new_ty }),
.opaque_type => {},
@@ -19359,13 +19313,11 @@ fn typeInfoNamespaceDecls(
}
for (namespace.pub_usingnamespace.items) |nav| {
- if (ip.getNav(nav).analysis_owner.unwrap()) |cau| {
- if (zcu.analysis_in_progress.contains(AnalUnit.wrap(.{ .cau = cau }))) {
- continue;
- }
+ if (zcu.analysis_in_progress.contains(.wrap(.{ .nav_val = nav }))) {
+ continue;
}
- try sema.ensureNavResolved(src, nav);
- const namespace_ty = Type.fromInterned(ip.getNav(nav).status.resolved.val);
+ try sema.ensureNavResolved(src, nav, .fully);
+ const namespace_ty = Type.fromInterned(ip.getNav(nav).status.fully_resolved.val);
try sema.typeInfoNamespaceDecls(block, src, namespace_ty.getNamespaceIndex(zcu).toOptional(), declaration_ty, decl_vals, seen_namespaces);
}
}
@@ -21225,14 +21177,13 @@ fn structInitAnon(
.file_scope = block.getFileScopeIndex(zcu),
.generation = zcu.generation,
});
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip.index);
try zcu.comp.queueJob(.{ .resolve_type_fully = wip.index });
codegen_type: {
if (zcu.comp.config.use_llvm) break :codegen_type;
if (block.ownerModule().strip) break :codegen_type;
try zcu.comp.queueJob(.{ .codegen_type = wip.index });
}
- break :ty wip.finish(ip, new_cau_index.toOptional(), new_namespace_index);
+ break :ty wip.finish(ip, new_namespace_index);
},
.existing => |ty| ty,
};
@@ -21656,7 +21607,7 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
.func => |func| if (ip.funcAnalysisUnordered(func).calls_or_awaits_errorable_fn and block.ownerModule().error_tracing) {
return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
},
- .cau => {},
+ .@"comptime", .nav_ty, .nav_val, .type => {},
}
return Air.internedToRef(try pt.intern(.{ .opt = .{
.ty = opt_ptr_stack_trace_ty.toIntern(),
@@ -22334,7 +22285,7 @@ fn zirReify(
});
try sema.addTypeReferenceEntry(src, wip_ty.index);
- return Air.internedToRef(wip_ty.finish(ip, .none, new_namespace_index));
+ return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
},
.@"union" => {
const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
@@ -22543,11 +22494,9 @@ fn reifyEnum(
.generation = zcu.generation,
});
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
-
try sema.declareDependency(.{ .interned = wip_ty.index });
try sema.addTypeReferenceEntry(src, wip_ty.index);
- wip_ty.prepare(ip, new_cau_index, new_namespace_index);
+ wip_ty.prepare(ip, new_namespace_index);
wip_ty.setTagTy(ip, tag_ty.toIntern());
done = true;
@@ -22849,8 +22798,6 @@ fn reifyUnion(
.generation = zcu.generation,
});
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
-
try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
codegen_type: {
if (zcu.comp.config.use_llvm) break :codegen_type;
@@ -22860,7 +22807,7 @@ fn reifyUnion(
}
try sema.declareDependency(.{ .interned = wip_ty.index });
try sema.addTypeReferenceEntry(src, wip_ty.index);
- return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));
+ return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
}
fn reifyTuple(
@@ -23208,8 +23155,6 @@ fn reifyStruct(
.generation = zcu.generation,
});
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
-
try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
codegen_type: {
if (zcu.comp.config.use_llvm) break :codegen_type;
@@ -23219,7 +23164,7 @@ fn reifyStruct(
}
try sema.declareDependency(.{ .interned = wip_ty.index });
try sema.addTypeReferenceEntry(src, wip_ty.index);
- return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));
+ return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
}
fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref {
@@ -26711,135 +26656,6 @@ fn zirAwaitNosuspend(
return sema.failWithUseOfAsync(block, src);
}
-fn zirVarExtended(
- sema: *Sema,
- block: *Block,
- extended: Zir.Inst.Extended.InstData,
-) CompileError!Air.Inst.Ref {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const ip = &zcu.intern_pool;
- const extra = sema.code.extraData(Zir.Inst.ExtendedVar, extended.operand);
- const ty_src = block.src(.{ .node_offset_var_decl_ty = 0 });
- const init_src = block.src(.{ .node_offset_var_decl_init = 0 });
- const small: Zir.Inst.ExtendedVar.Small = @bitCast(extended.small);
-
- var extra_index: usize = extra.end;
-
- const lib_name = if (small.has_lib_name) lib_name: {
- const lib_name_index: Zir.NullTerminatedString = @enumFromInt(sema.code.extra[extra_index]);
- const lib_name = sema.code.nullTerminatedString(lib_name_index);
- extra_index += 1;
- try sema.handleExternLibName(block, ty_src, lib_name);
- break :lib_name lib_name;
- } else null;
-
- // ZIR supports encoding this information but it is not used; the information
- // is encoded via the Decl entry.
- assert(!small.has_align);
-
- const uncasted_init: Air.Inst.Ref = if (small.has_init) blk: {
- const init_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
- extra_index += 1;
- break :blk try sema.resolveInst(init_ref);
- } else .none;
-
- const have_ty = extra.data.var_type != .none;
- const var_ty = if (have_ty)
- try sema.resolveType(block, ty_src, extra.data.var_type)
- else
- sema.typeOf(uncasted_init);
-
- const init_val = if (uncasted_init != .none) blk: {
- const init = if (have_ty)
- try sema.coerce(block, var_ty, uncasted_init, init_src)
- else
- uncasted_init;
-
- break :blk ((try sema.resolveValue(init)) orelse {
- return sema.failWithNeededComptime(block, init_src, .{
- .needed_comptime_reason = "container level variable initializers must be comptime-known",
- });
- }).toIntern();
- } else .none;
-
- try sema.validateVarType(block, ty_src, var_ty, small.is_extern);
-
- if (small.is_extern) {
- const extern_val = try sema.resolveExternDecl(block, var_ty, lib_name, small.is_const, small.is_threadlocal);
- return Air.internedToRef(extern_val);
- }
- assert(!small.is_const); // non-const non-extern variable is not legal
- return Air.internedToRef(try pt.intern(.{ .variable = .{
- .ty = var_ty.toIntern(),
- .init = init_val,
- .owner_nav = sema.getOwnerCauNav(),
- .lib_name = try ip.getOrPutStringOpt(sema.gpa, pt.tid, lib_name, .no_embedded_nulls),
- .is_threadlocal = small.is_threadlocal,
- .is_weak_linkage = false,
- } }));
-}
-
-fn resolveExternDecl(
- sema: *Sema,
- block: *Block,
- ty: Type,
- opt_lib_name: ?[]const u8,
- is_const: bool,
- is_threadlocal: bool,
-) CompileError!InternPool.Index {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const ip = &zcu.intern_pool;
-
- // We need to resolve the alignment and addrspace early.
- // Keep in sync with logic in `Zcu.PerThread.semaCau`.
- const align_src = block.src(.{ .node_offset_var_decl_align = 0 });
- const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = 0 });
-
- const decl_inst, const decl_bodies = decl: {
- const decl_inst = sema.getOwnerCauDeclInst().resolve(ip) orelse return error.AnalysisFail;
- const zir_decl, const extra_end = sema.code.getDeclaration(decl_inst);
- break :decl .{ decl_inst, zir_decl.getBodies(extra_end, sema.code) };
- };
-
- const alignment: InternPool.Alignment = a: {
- const align_body = decl_bodies.align_body orelse break :a .none;
- const align_ref = try sema.resolveInlineBody(block, align_body, decl_inst);
- break :a try sema.analyzeAsAlign(block, align_src, align_ref);
- };
-
- const @"addrspace": std.builtin.AddressSpace = as: {
- const addrspace_ctx: Sema.AddressSpaceContext = switch (ip.indexToKey(ty.toIntern())) {
- .func_type => .function,
- else => .variable,
- };
- const target = zcu.getTarget();
- const addrspace_body = decl_bodies.addrspace_body orelse break :as 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.resolveInlineBody(block, addrspace_body, decl_inst);
- break :as try sema.analyzeAsAddressSpace(block, addrspace_src, addrspace_ref, addrspace_ctx);
- };
-
- return pt.getExtern(.{
- .name = sema.getOwnerCauNavName(),
- .ty = ty.toIntern(),
- .lib_name = try ip.getOrPutStringOpt(sema.gpa, pt.tid, opt_lib_name, .no_embedded_nulls),
- .is_const = is_const,
- .is_threadlocal = is_threadlocal,
- .is_weak_linkage = false,
- .is_dll_import = false,
- .alignment = alignment,
- .@"addrspace" = @"addrspace",
- .zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
- .owner_nav = undefined, // ignored by `getExtern`
- });
-}
-
fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
@@ -26857,13 +26673,6 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
var extra_index: usize = extra.end;
- const lib_name: ?[]const u8 = if (extra.data.bits.has_lib_name) blk: {
- const lib_name_index: Zir.NullTerminatedString = @enumFromInt(sema.code.extra[extra_index]);
- const lib_name = sema.code.nullTerminatedString(lib_name_index);
- extra_index += 1;
- break :blk lib_name;
- } else null;
-
const cc: std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: {
const body_len = sema.code.extra[extra_index];
extra_index += 1;
@@ -26887,16 +26696,14 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
break :blk try sema.analyzeValueAsCallconv(block, cc_src, cc_val);
} else cc: {
if (has_body) {
- const decl_inst = if (sema.generic_owner != .none) decl_inst: {
+ const func_decl_nav = if (sema.generic_owner != .none) nav: {
// Generic instance -- use the original function declaration to
// look for the `export` syntax.
- const nav = zcu.intern_pool.getNav(zcu.funcInfo(sema.generic_owner).owner_nav);
- const cau = zcu.intern_pool.getCau(nav.analysis_owner.unwrap().?);
- break :decl_inst cau.zir_index;
- } else sema.getOwnerCauDeclInst(); // not an instantiation so we're analyzing a function declaration Cau
-
- const zir_decl = sema.code.getDeclaration(decl_inst.resolve(&zcu.intern_pool) orelse return error.AnalysisFail)[0];
- if (zir_decl.flags.is_export) {
+ break :nav zcu.funcInfo(sema.generic_owner).owner_nav;
+ } else sema.owner.unwrap().nav_val;
+ const func_decl_inst = ip.getNav(func_decl_nav).analysis.?.zir_index.resolve(&zcu.intern_pool) orelse return error.AnalysisFail;
+ const zir_decl = sema.code.getDeclaration(func_decl_inst);
+ if (zir_decl.linkage == .@"export") {
break :cc target.cCallingConvention() orelse {
// This target has no default C calling convention. We sometimes trigger a similar
// error by trying to evaluate `std.builtin.CallingConvention.c`, so for consistency,
@@ -26958,7 +26765,6 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
const is_var_args = extra.data.bits.is_var_args;
const is_inferred_error = extra.data.bits.is_inferred_error;
- const is_extern = extra.data.bits.is_extern;
const is_noinline = extra.data.bits.is_noinline;
return sema.funcCommon(
@@ -26969,10 +26775,8 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
ret_ty,
is_var_args,
is_inferred_error,
- is_extern,
has_body,
src_locs,
- lib_name,
noalias_bits,
is_noinline,
);
@@ -27285,8 +27089,16 @@ fn zirBuiltinExtern(
// `builtin_extern` doesn't provide enough information, and isn't currently tracked.
// So, for now, just use our containing `declaration`.
.zir_index = switch (sema.owner.unwrap()) {
- .cau => sema.getOwnerCauDeclInst(),
- .func => sema.getOwnerFuncDeclInst(),
+ .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index,
+ .type => |owner_ty| Type.fromInterned(owner_ty).typeDeclInst(zcu).?,
+ .nav_ty, .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index,
+ .func => |func| zir_index: {
+ const func_info = zcu.funcInfo(func);
+ const owner_func_info = if (func_info.generic_owner != .none) owner: {
+ break :owner zcu.funcInfo(func_info.generic_owner);
+ } else func_info;
+ break :zir_index ip.getNav(owner_func_info.owner_nav).analysis.?.zir_index;
+ },
},
.owner_nav = undefined, // ignored by `getExtern`
});
@@ -27467,7 +27279,7 @@ fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src:
}
/// Emit a compile error if type cannot be used for a runtime variable.
-fn validateVarType(
+pub fn validateVarType(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
@@ -27934,7 +27746,7 @@ fn preparePanicId(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Zcu.Pan
error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable,
error.OutOfMemory => |e| return e,
}).?;
- try sema.ensureNavResolved(src, msg_nav_index);
+ try sema.ensureNavResolved(src, msg_nav_index, .fully);
zcu.panic_messages[@intFromEnum(panic_id)] = msg_nav_index.toOptional();
return msg_nav_index;
}
@@ -29881,7 +29693,7 @@ fn elemPtrSlice(
return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
}
-fn coerce(
+pub fn coerce(
sema: *Sema,
block: *Block,
dest_ty_unresolved: Type,
@@ -32841,34 +32653,45 @@ fn addTypeReferenceEntry(
try zcu.addTypeReference(sema.owner, referenced_type, src);
}
-pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!void {
+pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index, kind: enum { type, fully }) CompileError!void {
const pt = sema.pt;
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
-
- const cau_index = nav.analysis_owner.unwrap() orelse {
- assert(nav.status == .resolved);
+ if (nav.analysis == null) {
+ assert(nav.status == .fully_resolved);
return;
- };
+ }
- // Note that even if `nav.status == .resolved`, we must still trigger `ensureCauAnalyzed`
- // to make sure the value is up-to-date on incremental updates.
+ try sema.declareDependency(switch (kind) {
+ .type => .{ .nav_ty = nav_index },
+ .fully => .{ .nav_val = nav_index },
+ });
- assert(ip.getCau(cau_index).owner.unwrap().nav == nav_index);
+ // Note that even if `nav.status == .resolved`, we must still trigger `ensureNavValUpToDate`
+ // to make sure the value is up-to-date on incremental updates.
- const anal_unit = AnalUnit.wrap(.{ .cau = cau_index });
+ const anal_unit: AnalUnit = .wrap(switch (kind) {
+ .type => .{ .nav_ty = nav_index },
+ .fully => .{ .nav_val = nav_index },
+ });
try sema.addReferenceEntry(src, anal_unit);
if (zcu.analysis_in_progress.contains(anal_unit)) {
return sema.failWithOwnedErrorMsg(null, try sema.errMsg(.{
- .base_node_inst = ip.getCau(cau_index).zir_index,
+ .base_node_inst = nav.analysis.?.zir_index,
.offset = LazySrcLoc.Offset.nodeOffset(0),
}, "dependency loop detected", .{}));
}
- return pt.ensureCauAnalyzed(cau_index);
+ switch (kind) {
+ .type => {
+ try zcu.ensureNavValAnalysisQueued(nav_index);
+ return pt.ensureNavTypeUpToDate(nav_index);
+ },
+ .fully => return pt.ensureNavValUpToDate(nav_index),
+ }
}
fn optRefValue(sema: *Sema, opt_val: ?Value) !Value {
@@ -32887,36 +32710,44 @@ fn analyzeNavRef(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index)
return sema.analyzeNavRefInner(src, nav_index, true);
}
-/// Analyze a reference to the `Nav` at the given index. Ensures the underlying `Nav` is analyzed, but
-/// only triggers analysis for function bodies if `analyze_fn_body` is true. If it's possible for a
-/// decl_ref to end up in runtime code, the function body must be analyzed: `analyzeNavRef` wraps
-/// this function with `analyze_fn_body` set to true.
-fn analyzeNavRefInner(sema: *Sema, src: LazySrcLoc, orig_nav_index: InternPool.Nav.Index, analyze_fn_body: bool) CompileError!Air.Inst.Ref {
+/// Analyze a reference to the `Nav` at the given index. Ensures the underlying `Nav` is analyzed.
+/// If this pointer will be used directly, `is_ref` must be `true`.
+/// If this pointer will be immediately loaded (i.e. a `decl_val` instruction), `is_ref` must be `false`.
+fn analyzeNavRefInner(sema: *Sema, src: LazySrcLoc, orig_nav_index: InternPool.Nav.Index, is_ref: bool) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- // TODO: if this is a `decl_ref` of a non-variable Nav, only depend on Nav type
- try sema.declareDependency(.{ .nav_val = orig_nav_index });
- try sema.ensureNavResolved(src, orig_nav_index);
+ try sema.ensureNavResolved(src, orig_nav_index, if (is_ref) .type else .fully);
+
+ const nav_index = nav: {
+ if (ip.getNav(orig_nav_index).isExternOrFn(ip)) {
+ // Getting a pointer to this `Nav` might mean we actually get a pointer to something else!
+ // We need to resolve the value to know for sure.
+ if (is_ref) try sema.ensureNavResolved(src, orig_nav_index, .fully);
+ switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) {
+ .func => |f| break :nav f.owner_nav,
+ .@"extern" => |e| break :nav e.owner_nav,
+ else => {},
+ }
+ }
+ break :nav orig_nav_index;
+ };
- const nav_val = zcu.navValue(orig_nav_index);
- const nav_index, const is_const = switch (ip.indexToKey(nav_val.toIntern())) {
- .variable => |v| .{ v.owner_nav, false },
- .func => |f| .{ f.owner_nav, true },
- .@"extern" => |e| .{ e.owner_nav, e.is_const },
- else => .{ orig_nav_index, true },
+ const ty, const alignment, const @"addrspace", const is_const = switch (ip.getNav(nav_index).status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const },
+ .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", zcu.navValIsConst(r.val) },
};
- const nav_info = ip.getNav(nav_index).status.resolved;
const ptr_ty = try pt.ptrTypeSema(.{
- .child = nav_val.typeOf(zcu).toIntern(),
+ .child = ty,
.flags = .{
- .alignment = nav_info.alignment,
+ .alignment = alignment,
.is_const = is_const,
- .address_space = nav_info.@"addrspace",
+ .address_space = @"addrspace",
},
});
- if (analyze_fn_body) {
+ if (is_ref) {
try sema.maybeQueueFuncBodyAnalysis(src, nav_index);
}
return Air.internedToRef((try pt.intern(.{ .ptr = .{
@@ -32927,11 +32758,22 @@ fn analyzeNavRefInner(sema: *Sema, src: LazySrcLoc, orig_nav_index: InternPool.N
}
fn maybeQueueFuncBodyAnalysis(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav.Index) !void {
- const zcu = sema.pt.zcu;
+ const pt = sema.pt;
+ const zcu = pt.zcu;
const ip = &zcu.intern_pool;
+
+ // To avoid forcing too much resolution, let's first resolve the type, and check if it's a function.
+ // If it is, we can resolve the *value*, and queue analysis as needed.
+
+ try sema.ensureNavResolved(src, nav_index, .type);
+ const nav_ty: Type = .fromInterned(ip.getNav(nav_index).typeOf(ip));
+ if (nav_ty.zigTypeTag(zcu) != .@"fn") return;
+ if (!try nav_ty.fnHasRuntimeBitsSema(pt)) return;
+
+ try sema.ensureNavResolved(src, nav_index, .fully);
const nav_val = zcu.navValue(nav_index);
if (!ip.isFuncBody(nav_val.toIntern())) return;
- if (!try nav_val.typeOf(zcu).fnHasRuntimeBitsSema(sema.pt)) return;
+
try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .func = nav_val.toIntern() }));
try zcu.ensureFuncBodyAnalysisQueued(nav_val.toIntern());
}
@@ -35818,7 +35660,7 @@ pub fn resolveStructAlignment(
const ip = &zcu.intern_pool;
const target = zcu.getTarget();
- assert(sema.owner.unwrap().cau == struct_type.cau);
+ assert(sema.owner.unwrap().type == ty);
assert(struct_type.layout != .@"packed");
assert(struct_type.flagsUnordered(ip).alignment == .none);
@@ -35861,7 +35703,7 @@ pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void {
const ip = &zcu.intern_pool;
const struct_type = zcu.typeToStruct(ty) orelse return;
- assert(sema.owner.unwrap().cau == struct_type.cau);
+ assert(sema.owner.unwrap().type == ty.toIntern());
if (struct_type.haveLayout(ip))
return;
@@ -36008,15 +35850,13 @@ fn backingIntType(
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
- const cau_index = struct_type.cau;
-
var analysis_arena = std.heap.ArenaAllocator.init(gpa);
defer analysis_arena.deinit();
var block: Block = .{
.parent = null,
.sema = sema,
- .namespace = ip.getCau(cau_index).namespace,
+ .namespace = struct_type.namespace,
.instructions = .{},
.inlining = null,
.is_comptime = true,
@@ -36148,7 +35988,7 @@ pub fn resolveUnionAlignment(
const ip = &zcu.intern_pool;
const target = zcu.getTarget();
- assert(sema.owner.unwrap().cau == union_type.cau);
+ assert(sema.owner.unwrap().type == ty.toIntern());
assert(!union_type.haveLayout(ip));
@@ -36188,7 +36028,7 @@ pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {
// Load again, since the tag type might have changed due to resolution.
const union_type = ip.loadUnionType(ty.ip_index);
- assert(sema.owner.unwrap().cau == union_type.cau);
+ assert(sema.owner.unwrap().type == ty.toIntern());
const old_flags = union_type.flagsUnordered(ip);
switch (old_flags.status) {
@@ -36303,7 +36143,7 @@ pub fn resolveStructFully(sema: *Sema, ty: Type) SemaError!void {
const ip = &zcu.intern_pool;
const struct_type = zcu.typeToStruct(ty).?;
- assert(sema.owner.unwrap().cau == struct_type.cau);
+ assert(sema.owner.unwrap().type == ty.toIntern());
if (struct_type.setFullyResolved(ip)) return;
errdefer struct_type.clearFullyResolved(ip);
@@ -36326,7 +36166,7 @@ pub fn resolveUnionFully(sema: *Sema, ty: Type) SemaError!void {
const ip = &zcu.intern_pool;
const union_obj = zcu.typeToUnion(ty).?;
- assert(sema.owner.unwrap().cau == union_obj.cau);
+ assert(sema.owner.unwrap().type == ty.toIntern());
switch (union_obj.flagsUnordered(ip).status) {
.none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {},
@@ -36361,7 +36201,7 @@ pub fn resolveStructFieldTypes(
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- assert(sema.owner.unwrap().cau == struct_type.cau);
+ assert(sema.owner.unwrap().type == ty);
if (struct_type.haveFieldTypes(ip)) return;
@@ -36387,7 +36227,7 @@ pub fn resolveStructFieldInits(sema: *Sema, ty: Type) SemaError!void {
const ip = &zcu.intern_pool;
const struct_type = zcu.typeToStruct(ty) orelse return;
- assert(sema.owner.unwrap().cau == struct_type.cau);
+ assert(sema.owner.unwrap().type == ty.toIntern());
// Inits can start as resolved
if (struct_type.haveFieldInits(ip)) return;
@@ -36416,7 +36256,7 @@ pub fn resolveUnionFieldTypes(sema: *Sema, ty: Type, union_type: InternPool.Load
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- assert(sema.owner.unwrap().cau == union_type.cau);
+ assert(sema.owner.unwrap().type == ty.toIntern());
switch (union_type.flagsUnordered(ip).status) {
.none => {},
@@ -36492,7 +36332,7 @@ fn resolveInferredErrorSet(
// In this case we are dealing with the actual InferredErrorSet object that
// corresponds to the function, not one created to track an inline/comptime call.
try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .func = func_index }));
- try pt.ensureFuncBodyAnalyzed(func_index);
+ try pt.ensureFuncBodyUpToDate(func_index);
}
// This will now have been resolved by the logic at the end of `Zcu.analyzeFnBody`
@@ -36649,8 +36489,7 @@ fn structFields(
const zcu = pt.zcu;
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
- const cau_index = struct_type.cau;
- const namespace_index = ip.getCau(cau_index).namespace;
+ const namespace_index = struct_type.namespace;
const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir;
const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
@@ -36848,8 +36687,7 @@ fn structFieldInits(
assert(!struct_type.haveFieldInits(ip));
- const cau_index = struct_type.cau;
- const namespace_index = ip.getCau(cau_index).namespace;
+ const namespace_index = struct_type.namespace;
const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir;
const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
const fields_len, _, var extra_index = structZirInfo(zir, zir_index);
@@ -38650,14 +38488,17 @@ pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {
// of a type and they use `@This()`. This dependency would be unnecessary, and in fact would
// just result in over-analysis since `Zcu.findOutdatedToAnalyze` would never be able to resolve
// the loop.
+ // Note that this also disallows a `nav_val`
switch (sema.owner.unwrap()) {
- .cau => |cau| switch (dependee) {
- .nav_val => |nav| if (zcu.intern_pool.getNav(nav).analysis_owner == cau.toOptional()) {
- return;
- },
+ .nav_val => |this_nav| switch (dependee) {
+ .nav_val => |other_nav| if (this_nav == other_nav) return,
else => {},
},
- .func => {},
+ .nav_ty => |this_nav| switch (dependee) {
+ .nav_ty => |other_nav| if (this_nav == other_nav) return,
+ else => {},
+ },
+ else => {},
}
try zcu.intern_pool.addDependency(sema.gpa, sema.owner, dependee);
@@ -38836,45 +38677,6 @@ pub fn flushExports(sema: *Sema) !void {
}
}
-/// Given that this `Sema` is owned by the `Cau` of a `declaration`, fetches
-/// the corresponding `Nav`.
-fn getOwnerCauNav(sema: *Sema) InternPool.Nav.Index {
- const cau = sema.owner.unwrap().cau;
- return sema.pt.zcu.intern_pool.getCau(cau).owner.unwrap().nav;
-}
-
-/// Given that this `Sema` is owned by the `Cau` of a `declaration`, fetches
-/// the declaration name from its corresponding `Nav`.
-fn getOwnerCauNavName(sema: *Sema) InternPool.NullTerminatedString {
- const nav = sema.getOwnerCauNav();
- return sema.pt.zcu.intern_pool.getNav(nav).name;
-}
-
-/// Given that this `Sema` is owned by the `Cau` of a `declaration`, fetches
-/// the `TrackedInst` corresponding to this `declaration` instruction.
-fn getOwnerCauDeclInst(sema: *Sema) InternPool.TrackedInst.Index {
- const ip = &sema.pt.zcu.intern_pool;
- const cau = ip.getCau(sema.owner.unwrap().cau);
- assert(cau.owner.unwrap() == .nav);
- return cau.zir_index;
-}
-
-/// Given that this `Sema` is owned by a runtime function, fetches the
-/// `TrackedInst` corresponding to its `declaration` instruction.
-fn getOwnerFuncDeclInst(sema: *Sema) InternPool.TrackedInst.Index {
- const zcu = sema.pt.zcu;
- const ip = &zcu.intern_pool;
- const func = sema.owner.unwrap().func;
- const func_info = zcu.funcInfo(func);
- const cau = if (func_info.generic_owner == .none) cau: {
- break :cau ip.getNav(func_info.owner_nav).analysis_owner.unwrap().?;
- } else cau: {
- const generic_owner = zcu.funcInfo(func_info.generic_owner);
- break :cau ip.getNav(generic_owner.owner_nav).analysis_owner.unwrap().?;
- };
- return ip.getCau(cau).zir_index;
-}
-
/// Called as soon as a `declared` enum type is created.
/// Resolves the tag type and field inits.
/// Marks the `src_inst` dependency on the enum's declaration, so call sites need not do this.
@@ -38885,7 +38687,6 @@ pub fn resolveDeclaredEnum(
tracked_inst: InternPool.TrackedInst.Index,
namespace: InternPool.NamespaceIndex,
type_name: InternPool.NullTerminatedString,
- enum_cau: InternPool.Cau.Index,
small: Zir.Inst.EnumDecl.Small,
body: []const Zir.Inst.Index,
tag_type_ref: Zir.Inst.Ref,
@@ -38903,7 +38704,7 @@ pub fn resolveDeclaredEnum(
const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(0) };
const tag_ty_src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = .{ .node_offset_container_tag = 0 } };
- const anal_unit = AnalUnit.wrap(.{ .cau = enum_cau });
+ const anal_unit = AnalUnit.wrap(.{ .type = wip_ty.index });
var arena = std.heap.ArenaAllocator.init(gpa);
defer arena.deinit();
@@ -39115,8 +38916,8 @@ fn getBuiltinInnerType(
const nav = opt_nav orelse return sema.fail(block, src, "std.builtin.{s} missing {s}", .{
compile_error_parent_name, inner_name,
});
- try sema.ensureNavResolved(src, nav);
- const val = Value.fromInterned(ip.getNav(nav).status.resolved.val);
+ try sema.ensureNavResolved(src, nav, .fully);
+ const val = Value.fromInterned(ip.getNav(nav).status.fully_resolved.val);
const ty = val.toType();
try ty.resolveFully(pt);
return ty;
@@ -39127,6 +38928,74 @@ fn getBuiltin(sema: *Sema, name: []const u8) SemaError!Air.Inst.Ref {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const nav = try pt.getBuiltinNav(name);
- try pt.ensureCauAnalyzed(ip.getNav(nav).analysis_owner.unwrap().?);
- return Air.internedToRef(ip.getNav(nav).status.resolved.val);
+ try pt.ensureNavValUpToDate(nav);
+ return Air.internedToRef(ip.getNav(nav).status.fully_resolved.val);
+}
+
+pub const NavPtrModifiers = struct {
+ alignment: Alignment,
+ @"linksection": InternPool.OptionalNullTerminatedString,
+ @"addrspace": std.builtin.AddressSpace,
+};
+
+pub fn resolveNavPtrModifiers(
+ sema: *Sema,
+ block: *Block,
+ zir_decl: Zir.Inst.Declaration.Unwrapped,
+ decl_inst: Zir.Inst.Index,
+ nav_ty: Type,
+) CompileError!NavPtrModifiers {
+ const pt = sema.pt;
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+
+ const align_src = block.src(.{ .node_offset_var_decl_align = 0 });
+ const section_src = block.src(.{ .node_offset_var_decl_section = 0 });
+ const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = 0 });
+
+ const alignment: InternPool.Alignment = a: {
+ const align_body = zir_decl.align_body orelse break :a .none;
+ const align_ref = try sema.resolveInlineBody(block, align_body, decl_inst);
+ break :a try sema.analyzeAsAlign(block, align_src, align_ref);
+ };
+
+ const @"linksection": InternPool.OptionalNullTerminatedString = ls: {
+ const linksection_body = zir_decl.linksection_body orelse break :ls .none;
+ const linksection_ref = try sema.resolveInlineBody(block, linksection_body, decl_inst);
+ const bytes = try sema.toConstString(block, section_src, linksection_ref, .{
+ .needed_comptime_reason = "linksection must be comptime-known",
+ });
+ if (std.mem.indexOfScalar(u8, bytes, 0) != null) {
+ return sema.fail(block, section_src, "linksection cannot contain null bytes", .{});
+ } else if (bytes.len == 0) {
+ return sema.fail(block, section_src, "linksection cannot be empty", .{});
+ }
+ break :ls try ip.getOrPutStringOpt(gpa, pt.tid, bytes, .no_embedded_nulls);
+ };
+
+ const @"addrspace": std.builtin.AddressSpace = as: {
+ const addrspace_ctx: Sema.AddressSpaceContext = switch (zir_decl.kind) {
+ .@"var" => .variable,
+ else => switch (nav_ty.zigTypeTag(zcu)) {
+ .@"fn" => .function,
+ else => .constant,
+ },
+ };
+ const target = zcu.getTarget();
+ const addrspace_body = zir_decl.addrspace_body orelse break :as 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.resolveInlineBody(block, addrspace_body, decl_inst);
+ break :as try sema.analyzeAsAddressSpace(block, addrspace_src, addrspace_ref, addrspace_ctx);
+ };
+
+ return .{
+ .alignment = alignment,
+ .@"linksection" = @"linksection",
+ .@"addrspace" = @"addrspace",
+ };
}
diff --git a/src/Sema/comptime_ptr_access.zig b/src/Sema/comptime_ptr_access.zig
index 10e81d7a9e..ceddb9457d 100644
--- a/src/Sema/comptime_ptr_access.zig
+++ b/src/Sema/comptime_ptr_access.zig
@@ -219,9 +219,8 @@ fn loadComptimePtrInner(
const base_val: MutableValue = switch (ptr.base_addr) {
.nav => |nav| val: {
- try sema.declareDependency(.{ .nav_val = nav });
- try sema.ensureNavResolved(src, nav);
- const val = ip.getNav(nav).status.resolved.val;
+ try sema.ensureNavResolved(src, nav, .fully);
+ const val = ip.getNav(nav).status.fully_resolved.val;
switch (ip.indexToKey(val)) {
.variable => return .runtime_load,
// We let `.@"extern"` through here if it's a function.
diff --git a/src/Type.zig b/src/Type.zig
index 850e50c82e..b47a45d1fc 100644
--- a/src/Type.zig
+++ b/src/Type.zig
@@ -3851,7 +3851,7 @@ fn resolveStructInner(
const gpa = zcu.gpa;
const struct_obj = zcu.typeToStruct(ty).?;
- const owner = InternPool.AnalUnit.wrap(.{ .cau = struct_obj.cau });
+ const owner: InternPool.AnalUnit = .wrap(.{ .type = ty.toIntern() });
if (zcu.failed_analysis.contains(owner) or zcu.transitive_failed_analysis.contains(owner)) {
return error.AnalysisFail;
@@ -3905,7 +3905,7 @@ fn resolveUnionInner(
const gpa = zcu.gpa;
const union_obj = zcu.typeToUnion(ty).?;
- const owner = InternPool.AnalUnit.wrap(.{ .cau = union_obj.cau });
+ const owner: InternPool.AnalUnit = .wrap(.{ .type = ty.toIntern() });
if (zcu.failed_analysis.contains(owner) or zcu.transitive_failed_analysis.contains(owner)) {
return error.AnalysisFail;
diff --git a/src/Value.zig b/src/Value.zig
index 59fbdf67d5..25f5b50166 100644
--- a/src/Value.zig
+++ b/src/Value.zig
@@ -1343,7 +1343,12 @@ pub fn isLazySize(val: Value, zcu: *Zcu) bool {
pub fn isPtrRuntimeValue(val: Value, zcu: *Zcu) bool {
const ip = &zcu.intern_pool;
const nav = ip.getBackingNav(val.toIntern()).unwrap() orelse return false;
- return switch (ip.indexToKey(ip.getNav(nav).status.resolved.val)) {
+ const nav_val = switch (ip.getNav(nav).status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| return r.is_threadlocal,
+ .fully_resolved => |r| r.val,
+ };
+ return switch (ip.indexToKey(nav_val)) {
.@"extern" => |e| e.is_threadlocal or e.is_dll_import,
.variable => |v| v.is_threadlocal,
else => false,
diff --git a/src/Zcu.zig b/src/Zcu.zig
index 771bfdcf61..8b3125039a 100644
--- a/src/Zcu.zig
+++ b/src/Zcu.zig
@@ -170,6 +170,9 @@ outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .empty,
/// it as outdated.
retryable_failures: std.ArrayListUnmanaged(AnalUnit) = .empty,
+func_body_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty,
+nav_val_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,
+
/// These are the modules which we initially queue for analysis in `Compilation.update`.
/// `resolveReferences` will use these as the root of its reachability traversal.
analysis_roots: std.BoundedArray(*Package.Module, 3) = .{},
@@ -192,7 +195,7 @@ compile_log_text: std.ArrayListUnmanaged(u8) = .empty,
test_functions: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,
-global_assembly: std.AutoArrayHashMapUnmanaged(InternPool.Cau.Index, []u8) = .empty,
+global_assembly: std.AutoArrayHashMapUnmanaged(AnalUnit, []u8) = .empty,
/// Key is the `AnalUnit` *performing* the reference. This representation allows
/// incremental updates to quickly delete references caused by a specific `AnalUnit`.
@@ -282,7 +285,11 @@ pub const Exported = union(enum) {
pub fn getAlign(exported: Exported, zcu: *Zcu) Alignment {
return switch (exported) {
- .nav => |nav| zcu.intern_pool.getNav(nav).status.resolved.alignment,
+ .nav => |nav| switch (zcu.intern_pool.getNav(nav).status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| r.alignment,
+ .fully_resolved => |r| r.alignment,
+ },
.uav => .none,
};
}
@@ -344,9 +351,12 @@ pub const Namespace = struct {
pub_usingnamespace: std.ArrayListUnmanaged(InternPool.Nav.Index) = .empty,
/// All `usingnamespace` declarations in this namespace which are *not* marked `pub`.
priv_usingnamespace: std.ArrayListUnmanaged(InternPool.Nav.Index) = .empty,
- /// All `comptime` and `test` declarations in this namespace. We store these purely so that
- /// incremental compilation can re-use the existing `Cau`s when a namespace changes.
- other_decls: std.ArrayListUnmanaged(InternPool.Cau.Index) = .empty,
+ /// All `comptime` declarations in this namespace. We store these purely so that incremental
+ /// compilation can re-use the existing `ComptimeUnit`s when a namespace changes.
+ comptime_decls: std.ArrayListUnmanaged(InternPool.ComptimeUnit.Id) = .empty,
+ /// All `test` declarations in this namespace. We store these purely so that incremental
+ /// compilation can re-use the existing `Nav`s when a namespace changes.
+ test_decls: std.ArrayListUnmanaged(InternPool.Nav.Index) = .empty,
pub const Index = InternPool.NamespaceIndex;
pub const OptionalIndex = InternPool.OptionalNamespaceIndex;
@@ -2238,6 +2248,9 @@ pub fn deinit(zcu: *Zcu) void {
zcu.outdated_ready.deinit(gpa);
zcu.retryable_failures.deinit(gpa);
+ zcu.func_body_analysis_queued.deinit(gpa);
+ zcu.nav_val_analysis_queued.deinit(gpa);
+
zcu.test_functions.deinit(gpa);
for (zcu.global_assembly.values()) |s| {
@@ -2436,11 +2449,10 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
// If this is a Decl, we must recursively mark dependencies on its tyval
// as no longer PO.
switch (depender.unwrap()) {
- .cau => |cau| switch (zcu.intern_pool.getCau(cau).owner.unwrap()) {
- .nav => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_val = nav }),
- .type => |ty| try zcu.markPoDependeeUpToDate(.{ .interned = ty }),
- .none => {},
- },
+ .@"comptime" => {},
+ .nav_val => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_val = nav }),
+ .nav_ty => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_ty = nav }),
+ .type => |ty| try zcu.markPoDependeeUpToDate(.{ .interned = ty }),
.func => |func| try zcu.markPoDependeeUpToDate(.{ .interned = func }),
}
}
@@ -2451,11 +2463,10 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) !void {
const ip = &zcu.intern_pool;
const dependee: InternPool.Dependee = switch (maybe_outdated.unwrap()) {
- .cau => |cau| switch (ip.getCau(cau).owner.unwrap()) {
- .nav => |nav| .{ .nav_val = nav }, // TODO: also `nav_ref` deps when introduced
- .type => |ty| .{ .interned = ty },
- .none => return, // analysis of this `Cau` can't outdate any dependencies
- },
+ .@"comptime" => return, // analysis of a comptime decl can't outdate any dependencies
+ .nav_val => |nav| .{ .nav_val = nav },
+ .nav_ty => |nav| .{ .nav_ty = nav },
+ .type => |ty| .{ .interned = ty },
.func => |func_index| .{ .interned = func_index }, // IES
};
log.debug("potentially outdated dependee: {}", .{zcu.fmtDependee(dependee)});
@@ -2512,14 +2523,14 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
}
// There is no single AnalUnit which is ready for re-analysis. Instead, we must assume that some
- // Cau with PO dependencies is outdated -- e.g. in the above example we arbitrarily pick one of
- // A or B. We should select a Cau, since a Cau is definitely responsible for the loop in the
- // dependency graph (since IES dependencies can't have loops). We should also, of course, not
- // select a Cau owned by a `comptime` declaration, since you can't depend on those!
+ // AnalUnit with PO dependencies is outdated -- e.g. in the above example we arbitrarily pick one of
+ // A or B. We should definitely not select a function, since a function can't be responsible for the
+ // loop (IES dependencies can't have loops). We should also, of course, not select a `comptime`
+ // declaration, since you can't depend on those!
- // The choice of this Cau could have a big impact on how much total analysis we perform, since
+ // The choice of this unit could have a big impact on how much total analysis we perform, since
// if analysis concludes any dependencies on its result are up-to-date, then other PO AnalUnit
- // may be resolved as up-to-date. To hopefully avoid doing too much work, let's find a Decl
+ // may be resolved as up-to-date. To hopefully avoid doing too much work, let's find a unit
// which the most things depend on - the idea is that this will resolve a lot of loops (but this
// is only a heuristic).
@@ -2530,33 +2541,29 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
const ip = &zcu.intern_pool;
- var chosen_cau: ?InternPool.Cau.Index = null;
- var chosen_cau_dependers: u32 = undefined;
+ var chosen_unit: ?AnalUnit = null;
+ var chosen_unit_dependers: u32 = undefined;
inline for (.{ zcu.outdated.keys(), zcu.potentially_outdated.keys() }) |outdated_units| {
for (outdated_units) |unit| {
- const cau = switch (unit.unwrap()) {
- .cau => |cau| cau,
- .func => continue, // a `func` definitely can't be causing the loop so it is a bad choice
- };
- const cau_owner = ip.getCau(cau).owner;
-
var n: u32 = 0;
- var it = ip.dependencyIterator(switch (cau_owner.unwrap()) {
- .none => continue, // there can be no dependencies on this `Cau` so it is a terrible choice
+ var it = ip.dependencyIterator(switch (unit.unwrap()) {
+ .func => continue, // a `func` definitely can't be causing the loop so it is a bad choice
+ .@"comptime" => continue, // a `comptime` block can't even be depended on so it is a terrible choice
.type => |ty| .{ .interned = ty },
- .nav => |nav| .{ .nav_val = nav },
+ .nav_val => |nav| .{ .nav_val = nav },
+ .nav_ty => |nav| .{ .nav_ty = nav },
});
while (it.next()) |_| n += 1;
- if (chosen_cau == null or n > chosen_cau_dependers) {
- chosen_cau = cau;
- chosen_cau_dependers = n;
+ if (chosen_unit == null or n > chosen_unit_dependers) {
+ chosen_unit = unit;
+ chosen_unit_dependers = n;
}
}
}
- if (chosen_cau == null) {
+ if (chosen_unit == null) {
for (zcu.outdated.keys(), zcu.outdated.values()) |o, opod| {
const func = o.unwrap().func;
const nav = zcu.funcInfo(func).owner_nav;
@@ -2570,11 +2577,11 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
}
log.debug("findOutdatedToAnalyze: heuristic returned '{}' ({d} dependers)", .{
- zcu.fmtAnalUnit(AnalUnit.wrap(.{ .cau = chosen_cau.? })),
- chosen_cau_dependers,
+ zcu.fmtAnalUnit(chosen_unit.?),
+ chosen_unit_dependers,
});
- return AnalUnit.wrap(.{ .cau = chosen_cau.? });
+ return chosen_unit.?;
}
/// During an incremental update, before semantic analysis, call this to flush all values from
@@ -2679,24 +2686,14 @@ pub fn mapOldZirToNew(
{
var old_decl_it = old_zir.declIterator(match_item.old_inst);
while (old_decl_it.next()) |old_decl_inst| {
- const old_decl, _ = old_zir.getDeclaration(old_decl_inst);
- switch (old_decl.name) {
+ const old_decl = old_zir.getDeclaration(old_decl_inst);
+ switch (old_decl.kind) {
.@"comptime" => try comptime_decls.append(gpa, old_decl_inst),
.@"usingnamespace" => try usingnamespace_decls.append(gpa, old_decl_inst),
.unnamed_test => 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)) {
- if (old_decl.flags.test_is_decltest) {
- try named_decltests.put(gpa, name, old_decl_inst);
- } else {
- try named_tests.put(gpa, name, old_decl_inst);
- }
- } else {
- try named_decls.put(gpa, name, old_decl_inst);
- }
- },
+ .@"test" => try named_tests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
+ .decltest => try named_decltests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
+ .@"const", .@"var" => try named_decls.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
}
}
}
@@ -2707,7 +2704,7 @@ pub fn mapOldZirToNew(
var new_decl_it = new_zir.declIterator(match_item.new_inst);
while (new_decl_it.next()) |new_decl_inst| {
- const new_decl, _ = new_zir.getDeclaration(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"`) and decltests (`test foo`), we also match based on name.
@@ -2715,7 +2712,7 @@ pub fn mapOldZirToNew(
// * 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) {
+ const old_decl_inst = switch (new_decl.kind) {
.@"comptime" => inst: {
if (comptime_decl_idx == comptime_decls.items.len) continue;
defer comptime_decl_idx += 1;
@@ -2731,18 +2728,17 @@ pub fn mapOldZirToNew(
defer unnamed_test_idx += 1;
break :inst unnamed_tests.items[unnamed_test_idx];
},
- _ => inst: {
- const name_nts = new_decl.name.toString(new_zir).?;
- const name = new_zir.nullTerminatedString(name_nts);
- if (new_decl.name.isNamedTest(new_zir)) {
- if (new_decl.flags.test_is_decltest) {
- break :inst named_decltests.get(name) orelse continue;
- } else {
- break :inst named_tests.get(name) orelse continue;
- }
- } else {
- break :inst named_decls.get(name) orelse continue;
- }
+ .@"test" => inst: {
+ const name = new_zir.nullTerminatedString(new_decl.name);
+ break :inst named_tests.get(name) orelse continue;
+ },
+ .decltest => inst: {
+ const name = new_zir.nullTerminatedString(new_decl.name);
+ break :inst named_decltests.get(name) orelse continue;
+ },
+ .@"const", .@"var" => inst: {
+ const name = new_zir.nullTerminatedString(new_decl.name);
+ break :inst named_decls.get(name) orelse continue;
},
};
@@ -2797,14 +2793,39 @@ pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func_index: InternPool.Index) !vo
const ip = &zcu.intern_pool;
const func = zcu.funcInfo(func_index);
- switch (func.analysisUnordered(ip).state) {
- .unreferenced => {}, // We're the first reference!
- .queued => return, // Analysis is already queued.
- .analyzed => return, // Analysis is complete; if it's out-of-date, it'll be re-analyzed later this update.
+ if (zcu.func_body_analysis_queued.contains(func_index)) return;
+
+ if (func.analysisUnordered(ip).is_analyzed) {
+ if (!zcu.outdated.contains(.wrap(.{ .func = func_index })) and
+ !zcu.potentially_outdated.contains(.wrap(.{ .func = func_index })))
+ {
+ // This function has been analyzed before and is definitely up-to-date.
+ return;
+ }
}
+ try zcu.func_body_analysis_queued.ensureUnusedCapacity(zcu.gpa, 1);
try zcu.comp.queueJob(.{ .analyze_func = func_index });
- func.setAnalysisState(ip, .queued);
+ zcu.func_body_analysis_queued.putAssumeCapacityNoClobber(func_index, {});
+}
+
+pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav_id: InternPool.Nav.Index) !void {
+ const ip = &zcu.intern_pool;
+
+ if (zcu.nav_val_analysis_queued.contains(nav_id)) return;
+
+ if (ip.getNav(nav_id).status == .fully_resolved) {
+ if (!zcu.outdated.contains(.wrap(.{ .nav_val = nav_id })) and
+ !zcu.potentially_outdated.contains(.wrap(.{ .nav_val = nav_id })))
+ {
+ // This `Nav` has been analyzed before and is definitely up-to-date.
+ return;
+ }
+ }
+
+ try zcu.nav_val_analysis_queued.ensureUnusedCapacity(zcu.gpa, 1);
+ try zcu.comp.queueJob(.{ .analyze_comptime_unit = .wrap(.{ .nav_val = nav_id }) });
+ zcu.nav_val_analysis_queued.putAssumeCapacityNoClobber(nav_id, {});
}
pub const ImportFileResult = struct {
@@ -3030,9 +3051,9 @@ pub fn handleUpdateExports(
};
}
-pub fn addGlobalAssembly(zcu: *Zcu, cau: InternPool.Cau.Index, source: []const u8) !void {
+pub fn addGlobalAssembly(zcu: *Zcu, unit: AnalUnit, source: []const u8) !void {
const gpa = zcu.gpa;
- const gop = try zcu.global_assembly.getOrPut(gpa, cau);
+ const gop = try zcu.global_assembly.getOrPut(gpa, unit);
if (gop.found_existing) {
const new_value = try std.fmt.allocPrint(gpa, "{s}\n{s}", .{ gop.value_ptr.*, source });
gpa.free(gop.value_ptr.*);
@@ -3315,23 +3336,22 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
log.debug("handle type '{}'", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)});
- // If this type has a `Cau` for resolution, it's automatically referenced.
- const resolution_cau: InternPool.Cau.Index.Optional = switch (ip.indexToKey(ty)) {
- .struct_type => ip.loadStructType(ty).cau.toOptional(),
- .union_type => ip.loadUnionType(ty).cau.toOptional(),
- .enum_type => ip.loadEnumType(ty).cau,
- .opaque_type => .none,
+ // If this type undergoes type resolution, the corresponding `AnalUnit` is automatically referenced.
+ const has_resolution: bool = switch (ip.indexToKey(ty)) {
+ .struct_type, .union_type => true,
+ .enum_type => |k| k != .generated_tag,
+ .opaque_type => false,
else => unreachable,
};
- if (resolution_cau.unwrap()) |cau| {
+ if (has_resolution) {
// this should only be referenced by the type
- const unit = AnalUnit.wrap(.{ .cau = cau });
+ const unit: AnalUnit = .wrap(.{ .type = ty });
assert(!result.contains(unit));
try unit_queue.putNoClobber(gpa, unit, referencer);
}
// If this is a union with a generated tag, its tag type is automatically referenced.
- // We don't add this reference for non-generated tags, as those will already be referenced via the union's `Cau`, with a better source location.
+ // We don't add this reference for non-generated tags, as those will already be referenced via the union's type resolution, with a better source location.
if (zcu.typeToUnion(Type.fromInterned(ty))) |union_obj| {
const tag_ty = union_obj.enum_tag_ty;
if (tag_ty != .none) {
@@ -3346,53 +3366,61 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
// Queue any decls within this type which would be automatically analyzed.
// Keep in sync with analysis queueing logic in `Zcu.PerThread.ScanDeclIter.scanDecl`.
const ns = Type.fromInterned(ty).getNamespace(zcu).unwrap().?;
- for (zcu.namespacePtr(ns).other_decls.items) |cau| {
- // These are `comptime` and `test` declarations.
- // `comptime` decls are always analyzed; `test` declarations are analyzed depending on the test filter.
- const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
+ for (zcu.namespacePtr(ns).comptime_decls.items) |cu| {
+ // `comptime` decls are always analyzed.
+ const unit: AnalUnit = .wrap(.{ .@"comptime" = cu });
+ if (!result.contains(unit)) {
+ log.debug("type '{}': ref comptime %{}", .{
+ Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
+ @intFromEnum(ip.getComptimeUnit(cu).zir_index.resolve(ip) orelse continue),
+ });
+ try unit_queue.put(gpa, unit, referencer);
+ }
+ }
+ for (zcu.namespacePtr(ns).test_decls.items) |nav_id| {
+ const nav = ip.getNav(nav_id);
+ // `test` declarations are analyzed depending on the test filter.
+ const inst_info = nav.analysis.?.zir_index.resolveFull(ip) orelse continue;
const file = zcu.fileByIndex(inst_info.file);
// If the file failed AstGen, the TrackedInst refers to the old ZIR.
const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
- const declaration = zir.getDeclaration(inst_info.inst)[0];
- const want_analysis = switch (declaration.name) {
+ const decl = zir.getDeclaration(inst_info.inst);
+
+ if (!comp.config.is_test or file.mod != zcu.main_mod) continue;
+
+ const want_analysis = switch (decl.kind) {
.@"usingnamespace" => unreachable,
- .@"comptime" => true,
- else => a: {
- if (!comp.config.is_test) break :a false;
- if (file.mod != zcu.main_mod) break :a false;
- if (declaration.name.isNamedTest(zir)) {
- const nav = ip.getCau(cau).owner.unwrap().nav;
- const fqn_slice = ip.getNav(nav).fqn.toSlice(ip);
- for (comp.test_filters) |test_filter| {
- if (std.mem.indexOf(u8, fqn_slice, test_filter) != null) break;
- } else break :a false;
- }
+ .@"const", .@"var" => unreachable,
+ .@"comptime" => unreachable,
+ .unnamed_test => true,
+ .@"test", .decltest => a: {
+ const fqn_slice = nav.fqn.toSlice(ip);
+ for (comp.test_filters) |test_filter| {
+ if (std.mem.indexOf(u8, fqn_slice, test_filter) != null) break;
+ } else break :a false;
break :a true;
},
};
if (want_analysis) {
- const unit = AnalUnit.wrap(.{ .cau = cau });
- if (!result.contains(unit)) {
- log.debug("type '{}': ref cau %{}", .{
- Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
- @intFromEnum(inst_info.inst),
- });
- try unit_queue.put(gpa, unit, referencer);
- }
+ log.debug("type '{}': ref test %{}", .{
+ Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
+ @intFromEnum(inst_info.inst),
+ });
+ const unit: AnalUnit = .wrap(.{ .nav_val = nav_id });
+ try unit_queue.put(gpa, unit, referencer);
}
}
for (zcu.namespacePtr(ns).pub_decls.keys()) |nav| {
// These are named declarations. They are analyzed only if marked `export`.
- const cau = ip.getNav(nav).analysis_owner.unwrap().?;
- const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
+ const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue;
const file = zcu.fileByIndex(inst_info.file);
// If the file failed AstGen, the TrackedInst refers to the old ZIR.
const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
- const declaration = zir.getDeclaration(inst_info.inst)[0];
- if (declaration.flags.is_export) {
- const unit = AnalUnit.wrap(.{ .cau = cau });
+ const decl = zir.getDeclaration(inst_info.inst);
+ if (decl.linkage == .@"export") {
+ const unit: AnalUnit = .wrap(.{ .nav_val = nav });
if (!result.contains(unit)) {
- log.debug("type '{}': ref cau %{}", .{
+ log.debug("type '{}': ref named %{}", .{
Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
@intFromEnum(inst_info.inst),
});
@@ -3402,16 +3430,15 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
}
for (zcu.namespacePtr(ns).priv_decls.keys()) |nav| {
// These are named declarations. They are analyzed only if marked `export`.
- const cau = ip.getNav(nav).analysis_owner.unwrap().?;
- const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
+ const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue;
const file = zcu.fileByIndex(inst_info.file);
// If the file failed AstGen, the TrackedInst refers to the old ZIR.
const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
- const declaration = zir.getDeclaration(inst_info.inst)[0];
- if (declaration.flags.is_export) {
- const unit = AnalUnit.wrap(.{ .cau = cau });
+ const decl = zir.getDeclaration(inst_info.inst);
+ if (decl.linkage == .@"export") {
+ const unit: AnalUnit = .wrap(.{ .nav_val = nav });
if (!result.contains(unit)) {
- log.debug("type '{}': ref cau %{}", .{
+ log.debug("type '{}': ref named %{}", .{
Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
@intFromEnum(inst_info.inst),
});
@@ -3422,13 +3449,11 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
// Incremental compilation does not support `usingnamespace`.
// These are only included to keep good reference traces in non-incremental updates.
for (zcu.namespacePtr(ns).pub_usingnamespace.items) |nav| {
- const cau = ip.getNav(nav).analysis_owner.unwrap().?;
- const unit = AnalUnit.wrap(.{ .cau = cau });
+ const unit: AnalUnit = .wrap(.{ .nav_val = nav });
if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);
}
for (zcu.namespacePtr(ns).priv_usingnamespace.items) |nav| {
- const cau = ip.getNav(nav).analysis_owner.unwrap().?;
- const unit = AnalUnit.wrap(.{ .cau = cau });
+ const unit: AnalUnit = .wrap(.{ .nav_val = nav });
if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);
}
continue;
@@ -3437,6 +3462,17 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
const unit = kv.key;
try result.putNoClobber(gpa, unit, kv.value);
+ // `nav_val` and `nav_ty` reference each other *implicitly* to save memory.
+ queue_paired: {
+ const other: AnalUnit = .wrap(switch (unit.unwrap()) {
+ .nav_val => |n| .{ .nav_ty = n },
+ .nav_ty => |n| .{ .nav_val = n },
+ .@"comptime", .type, .func => break :queue_paired,
+ });
+ if (result.contains(other)) break :queue_paired;
+ try unit_queue.put(gpa, other, kv.value); // same reference location
+ }
+
log.debug("handle unit '{}'", .{zcu.fmtAnalUnit(unit)});
if (zcu.reference_table.get(unit)) |first_ref_idx| {
@@ -3522,13 +3558,11 @@ pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 {
const ip = &zcu.intern_pool;
const inst_info = ip.getNav(nav_index).srcInst(ip).resolveFull(ip).?;
const zir = zcu.fileByIndex(inst_info.file).zir;
- const inst = zir.instructions.get(@intFromEnum(inst_info.inst));
- assert(inst.tag == .declaration);
- return zir.extraData(Zir.Inst.Declaration, inst.data.declaration.payload_index).data.src_line;
+ return zir.getDeclaration(inst_info.inst).src_line;
}
pub fn navValue(zcu: *const Zcu, nav_index: InternPool.Nav.Index) Value {
- return Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.resolved.val);
+ return Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.fully_resolved.val);
}
pub fn navFileScopeIndex(zcu: *Zcu, nav: InternPool.Nav.Index) File.Index {
@@ -3540,12 +3574,6 @@ pub fn navFileScope(zcu: *Zcu, nav: InternPool.Nav.Index) *File {
return zcu.fileByIndex(zcu.navFileScopeIndex(nav));
}
-pub fn cauFileScope(zcu: *Zcu, cau: InternPool.Cau.Index) *File {
- const ip = &zcu.intern_pool;
- const file_index = ip.getCau(cau).zir_index.resolveFile(ip);
- return zcu.fileByIndex(file_index);
-}
-
pub fn fmtAnalUnit(zcu: *Zcu, unit: AnalUnit) std.fmt.Formatter(formatAnalUnit) {
return .{ .data = .{ .unit = unit, .zcu = zcu } };
}
@@ -3558,19 +3586,18 @@ fn formatAnalUnit(data: struct { unit: AnalUnit, zcu: *Zcu }, comptime fmt: []co
const zcu = data.zcu;
const ip = &zcu.intern_pool;
switch (data.unit.unwrap()) {
- .cau => |cau_index| {
- const cau = ip.getCau(cau_index);
- switch (cau.owner.unwrap()) {
- .nav => |nav| return writer.print("cau(decl='{}')", .{ip.getNav(nav).fqn.fmt(ip)}),
- .type => |ty| return writer.print("cau(ty='{}')", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}),
- .none => if (cau.zir_index.resolveFull(ip)) |resolved| {
- const file_path = zcu.fileByIndex(resolved.file).sub_file_path;
- return writer.print("cau(inst=('{s}', %{}))", .{ file_path, @intFromEnum(resolved.inst) });
- } else {
- return writer.writeAll("cau(inst=<lost>)");
- },
+ .@"comptime" => |cu_id| {
+ const cu = ip.getComptimeUnit(cu_id);
+ if (cu.zir_index.resolveFull(ip)) |resolved| {
+ const file_path = zcu.fileByIndex(resolved.file).sub_file_path;
+ return writer.print("comptime(inst=('{s}', %{}))", .{ file_path, @intFromEnum(resolved.inst) });
+ } else {
+ return writer.writeAll("comptime(inst=<list>)");
}
},
+ .nav_val => |nav| return writer.print("nav_val('{}')", .{ip.getNav(nav).fqn.fmt(ip)}),
+ .nav_ty => |nav| return writer.print("nav_ty('{}')", .{ip.getNav(nav).fqn.fmt(ip)}),
+ .type => |ty| return writer.print("ty('{}')", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}),
.func => |func| {
const nav = zcu.funcInfo(func).owner_nav;
return writer.print("func('{}')", .{ip.getNav(nav).fqn.fmt(ip)});
@@ -3595,7 +3622,11 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com
},
.nav_val => |nav| {
const fqn = ip.getNav(nav).fqn;
- return writer.print("nav('{}')", .{fqn.fmt(ip)});
+ return writer.print("nav_val('{}')", .{fqn.fmt(ip)});
+ },
+ .nav_ty => |nav| {
+ const fqn = ip.getNav(nav).fqn;
+ return writer.print("nav_ty('{}')", .{fqn.fmt(ip)});
},
.interned => |ip_index| switch (ip.indexToKey(ip_index)) {
.struct_type, .union_type, .enum_type => return writer.print("type('{}')", .{Type.fromInterned(ip_index).containerTypeName(ip).fmt(ip)}),
@@ -3772,3 +3803,12 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu
if (!backend_ok) return .{ .bad_backend = backend };
return .ok;
}
+
+/// Given that a `Nav` has value `val`, determine if a ref of that `Nav` gives a `const` pointer.
+pub fn navValIsConst(zcu: *const Zcu, val: InternPool.Index) bool {
+ return switch (zcu.intern_pool.indexToKey(val)) {
+ .variable => false,
+ .@"extern" => |e| e.is_const,
+ else => true,
+ };
+}
diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig
index ca2e76bcf7..21908f769f 100644
--- a/src/Zcu/PerThread.zig
+++ b/src/Zcu/PerThread.zig
@@ -469,12 +469,8 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
{
var it = old_zir.declIterator(old_inst);
while (it.next()) |decl_inst| {
- const decl_name = old_zir.getDeclaration(decl_inst)[0].name;
- switch (decl_name) {
- .@"comptime", .@"usingnamespace", .unnamed_test => continue,
- _ => if (decl_name.isNamedTest(old_zir)) continue,
- }
- const name_zir = decl_name.toString(old_zir).?;
+ const name_zir = old_zir.getDeclaration(decl_inst).name;
+ if (name_zir == .empty) continue;
const name_ip = try zcu.intern_pool.getOrPutString(
zcu.gpa,
pt.tid,
@@ -488,12 +484,8 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
{
var it = new_zir.declIterator(new_inst);
while (it.next()) |decl_inst| {
- const decl_name = new_zir.getDeclaration(decl_inst)[0].name;
- switch (decl_name) {
- .@"comptime", .@"usingnamespace", .unnamed_test => continue,
- _ => if (decl_name.isNamedTest(new_zir)) continue,
- }
- const name_zir = decl_name.toString(new_zir).?;
+ const name_zir = new_zir.getDeclaration(decl_inst).name;
+ if (name_zir == .empty) continue;
const name_ip = try zcu.intern_pool.getOrPutString(
zcu.gpa,
pt.tid,
@@ -553,118 +545,268 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
pub fn ensureFileAnalyzed(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
const file_root_type = pt.zcu.fileRootType(file_index);
if (file_root_type != .none) {
- _ = try pt.ensureTypeUpToDate(file_root_type, false);
+ _ = try pt.ensureTypeUpToDate(file_root_type);
} else {
return pt.semaFile(file_index);
}
}
-/// This ensures that the state of the `Cau`, and of its corresponding `Nav` or type,
-/// is fully up-to-date. Note that the type of the `Nav` may not be fully resolved.
-/// Returns `error.AnalysisFail` if the `Cau` has an error.
-pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu.SemaError!void {
+/// Ensures that the state of the given `ComptimeUnit` is fully up-to-date, performing re-analysis
+/// if necessary. Returns `error.AnalysisFail` if an analysis error is encountered; the caller is
+/// free to ignore this, since the error is already registered.
+pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu.SemaError!void {
const tracy = trace(@src());
defer tracy.end();
const zcu = pt.zcu;
const gpa = zcu.gpa;
- const ip = &zcu.intern_pool;
- const anal_unit = AnalUnit.wrap(.{ .cau = cau_index });
- const cau = ip.getCau(cau_index);
+ const anal_unit: AnalUnit = .wrap(.{ .@"comptime" = cu_id });
- log.debug("ensureCauAnalyzed {}", .{zcu.fmtAnalUnit(anal_unit)});
+ log.debug("ensureComptimeUnitUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)});
assert(!zcu.analysis_in_progress.contains(anal_unit));
- // Determine whether or not this Cau is outdated, i.e. requires re-analysis
- // even if `complete`. If a Cau is PO, we pessismistically assume that it
- // *does* require re-analysis, to ensure that the Cau is definitely
- // up-to-date when this function returns.
-
- // If analysis occurs in a poor order, this could result in over-analysis.
- // We do our best to avoid this by the other dependency logic in this file
- // which tries to limit re-analysis to Caus whose previously listed
- // dependencies are all up-to-date.
+ // Determine whether or not this `ComptimeUnit` is outdated. For this kind of `AnalUnit`, that's
+ // the only indicator as to whether or not analysis is required; when a `ComptimeUnit` is first
+ // created, it's marked as outdated.
+ //
+ // Note that if the unit is PO, we pessimistically assume that it *does* require re-analysis, to
+ // ensure that the unit is definitely up-to-date when this function returns. This mechanism could
+ // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by
+ // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`.
- const cau_outdated = zcu.outdated.swapRemove(anal_unit) or
+ const was_outdated = zcu.outdated.swapRemove(anal_unit) or
zcu.potentially_outdated.swapRemove(anal_unit);
- const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit);
-
- if (cau_outdated) {
+ if (was_outdated) {
_ = zcu.outdated_ready.swapRemove(anal_unit);
+ // `was_outdated` can be true in the initial update for comptime units, so this isn't a `dev.check`.
+ if (dev.env.supports(.incremental)) {
+ zcu.deleteUnitExports(anal_unit);
+ zcu.deleteUnitReferences(anal_unit);
+ if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
+ kv.value.destroy(gpa);
+ }
+ _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
+ }
} else {
- // We can trust the current information about this `Cau`.
- if (prev_failed) {
+ // We can trust the current information about this unit.
+ if (zcu.failed_analysis.contains(anal_unit)) return error.AnalysisFail;
+ if (zcu.transitive_failed_analysis.contains(anal_unit)) return error.AnalysisFail;
+ return;
+ }
+
+ const unit_prog_node = zcu.sema_prog_node.start("comptime", 0);
+ defer unit_prog_node.end();
+
+ return pt.analyzeComptimeUnit(cu_id) catch |err| switch (err) {
+ error.AnalysisFail => {
+ if (!zcu.failed_analysis.contains(anal_unit)) {
+ // If this unit caused the error, it would have an entry in `failed_analysis`.
+ // Since it does not, this must be a transitive failure.
+ try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
+ log.debug("mark transitive analysis failure for {}", .{zcu.fmtAnalUnit(anal_unit)});
+ }
return error.AnalysisFail;
+ },
+ error.OutOfMemory => {
+ // TODO: it's unclear how to gracefully handle this.
+ // To report the error cleanly, we need to add a message to `failed_analysis` and a
+ // corresponding entry to `retryable_failures`; but either of these things is quite
+ // likely to OOM at this point.
+ // If that happens, what do we do? Perhaps we could have a special field on `Zcu`
+ // for reporting OOM errors without allocating.
+ return error.OutOfMemory;
+ },
+ error.GenericPoison => unreachable,
+ error.ComptimeReturn => unreachable,
+ error.ComptimeBreak => unreachable,
+ };
+}
+
+/// Re-analyzes a `ComptimeUnit`. The unit has already been determined to be out-of-date, and old
+/// side effects (exports/references/etc) have been dropped. If semantic analysis fails, this
+/// function will return `error.AnalysisFail`, and it is the caller's reponsibility to add an entry
+/// to `transitive_failed_analysis` if necessary.
+fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu.CompileError!void {
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+
+ const anal_unit: AnalUnit = .wrap(.{ .@"comptime" = cu_id });
+ const comptime_unit = ip.getComptimeUnit(cu_id);
+
+ log.debug("analyzeComptimeUnit {}", .{zcu.fmtAnalUnit(anal_unit)});
+
+ const inst_resolved = comptime_unit.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
+ const file = zcu.fileByIndex(inst_resolved.file);
+ // TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is
+ // unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends
+ // in `ensureComptimeUnitUpToDate`.
+ if (file.status != .success_zir) return error.AnalysisFail;
+ const zir = file.zir;
+
+ // We are about to re-analyze this unit; drop its depenndencies.
+ zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
+
+ try zcu.analysis_in_progress.put(gpa, anal_unit, {});
+ defer assert(zcu.analysis_in_progress.swapRemove(anal_unit));
+
+ var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
+ defer analysis_arena.deinit();
+
+ var comptime_err_ret_trace: std.ArrayList(Zcu.LazySrcLoc) = .init(gpa);
+ defer comptime_err_ret_trace.deinit();
+
+ var sema: Sema = .{
+ .pt = pt,
+ .gpa = gpa,
+ .arena = analysis_arena.allocator(),
+ .code = zir,
+ .owner = anal_unit,
+ .func_index = .none,
+ .func_is_naked = false,
+ .fn_ret_ty = .void,
+ .fn_ret_ty_ies = null,
+ .comptime_err_ret_trace = &comptime_err_ret_trace,
+ };
+ defer sema.deinit();
+
+ // The comptime unit declares on the source of the corresponding `comptime` declaration.
+ try sema.declareDependency(.{ .src_hash = comptime_unit.zir_index });
+
+ var block: Sema.Block = .{
+ .parent = null,
+ .sema = &sema,
+ .namespace = comptime_unit.namespace,
+ .instructions = .{},
+ .inlining = null,
+ .is_comptime = true,
+ .src_base_inst = comptime_unit.zir_index,
+ .type_name_ctx = try ip.getOrPutStringFmt(gpa, pt.tid, "{}.comptime", .{
+ Type.fromInterned(zcu.namespacePtr(comptime_unit.namespace).owner_type).containerTypeName(ip).fmt(ip),
+ }, .no_embedded_nulls),
+ };
+ defer block.instructions.deinit(gpa);
+
+ const zir_decl = zir.getDeclaration(inst_resolved.inst);
+ assert(zir_decl.kind == .@"comptime");
+ assert(zir_decl.type_body == null);
+ assert(zir_decl.align_body == null);
+ assert(zir_decl.linksection_body == null);
+ assert(zir_decl.addrspace_body == null);
+ const value_body = zir_decl.value_body.?;
+
+ const result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
+ assert(result_ref == .void_value); // AstGen should always uphold this
+
+ // Nothing else to do -- for a comptime decl, all we care about are the side effects.
+ // Just make sure to `flushExports`.
+ try sema.flushExports();
+}
+
+/// Ensures that the resolved value of the given `Nav` is fully up-to-date, performing re-analysis
+/// if necessary. Returns `error.AnalysisFail` if an analysis error is encountered; the caller is
+/// free to ignore this, since the error is already registered.
+pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.SemaError!void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ // TODO: document this elsewhere mlugg!
+ // For my own benefit, here's how a namespace update for a normal (non-file-root) type works:
+ // `const S = struct { ... };`
+ // We are adding or removing a declaration within this `struct`.
+ // * `S` registers a dependency on `.{ .src_hash = (declaration of S) }`
+ // * Any change to the `struct` body -- including changing a declaration -- invalidates this
+ // * `S` is re-analyzed, but notes:
+ // * there is an existing struct instance (at this `TrackedInst` with these captures)
+ // * the struct's resolution is up-to-date (because nothing about the fields changed)
+ // * so, it uses the same `struct`
+ // * but this doesn't stop it from updating the namespace!
+ // * we basically do `scanDecls`, updating the namespace as needed
+ // * so everyone lived happily ever after
+
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+
+ _ = zcu.nav_val_analysis_queued.swapRemove(nav_id);
+
+ const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_id });
+ const nav = ip.getNav(nav_id);
+
+ log.debug("ensureNavValUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)});
+
+ // Determine whether or not this `Nav`'s value is outdated. This also includes checking if the
+ // status is `.unresolved`, which indicates that the value is outdated because it has *never*
+ // been analyzed so far.
+ //
+ // Note that if the unit is PO, we pessimistically assume that it *does* require re-analysis, to
+ // ensure that the unit is definitely up-to-date when this function returns. This mechanism could
+ // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by
+ // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`.
+
+ const was_outdated = zcu.outdated.swapRemove(anal_unit) or
+ zcu.potentially_outdated.swapRemove(anal_unit);
+
+ const prev_failed = zcu.failed_analysis.contains(anal_unit) or
+ zcu.transitive_failed_analysis.contains(anal_unit);
+
+ if (was_outdated) {
+ dev.check(.incremental);
+ _ = zcu.outdated_ready.swapRemove(anal_unit);
+ zcu.deleteUnitExports(anal_unit);
+ zcu.deleteUnitReferences(anal_unit);
+ if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
+ kv.value.destroy(gpa);
}
- // If it wasn't failed and wasn't marked outdated, then either...
- // * it is a type and is up-to-date, or
- // * it is a `comptime` decl and is up-to-date, or
- // * it is another decl and is EITHER up-to-date OR never-referenced (so unresolved)
- // We just need to check for that last case.
- switch (cau.owner.unwrap()) {
- .type, .none => return,
- .nav => |nav| if (ip.getNav(nav).status == .resolved) return,
+ _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
+ } else {
+ // We can trust the current information about this unit.
+ if (prev_failed) return error.AnalysisFail;
+ switch (nav.status) {
+ .unresolved, .type_resolved => {},
+ .fully_resolved => return,
}
}
- const sema_result: SemaCauResult, const analysis_fail = if (pt.ensureCauAnalyzedInner(cau_index, cau_outdated)) |result|
- // This `Cau` has gone from failed to success, so even if the value of the owner `Nav` didn't actually
- // change, we need to invalidate the dependencies anyway.
- .{ .{
- .invalidate_decl_val = result.invalidate_decl_val or prev_failed,
- .invalidate_decl_ref = result.invalidate_decl_ref or prev_failed,
- }, false }
- else |err| switch (err) {
+ const unit_prog_node = zcu.sema_prog_node.start(nav.fqn.toSlice(ip), 0);
+ defer unit_prog_node.end();
+
+ const invalidate_value: bool, const new_failed: bool = if (pt.analyzeNavVal(nav_id)) |result| res: {
+ break :res .{
+ // If the unit has gone from failed to success, we still need to invalidate the dependencies.
+ result.val_changed or prev_failed,
+ false,
+ };
+ } else |err| switch (err) {
error.AnalysisFail => res: {
if (!zcu.failed_analysis.contains(anal_unit)) {
- // If this `Cau` caused the error, it would have an entry in `failed_analysis`.
+ // If this unit caused the error, it would have an entry in `failed_analysis`.
// Since it does not, this must be a transitive failure.
try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
log.debug("mark transitive analysis failure for {}", .{zcu.fmtAnalUnit(anal_unit)});
}
- // We consider this `Cau` to be outdated if:
- // * Previous analysis succeeded; in this case, we need to re-analyze dependants to ensure
- // they hit a transitive error here, rather than reporting a different error later (which
- // may now be invalid).
- // * The `Cau` is a type; in this case, the declaration site may require re-analysis to
- // construct a valid type.
- const outdated = !prev_failed or cau.owner.unwrap() == .type;
- break :res .{ .{
- .invalidate_decl_val = outdated,
- .invalidate_decl_ref = outdated,
- }, true };
+ break :res .{ !prev_failed, true };
},
- error.OutOfMemory => res: {
- try zcu.failed_analysis.ensureUnusedCapacity(gpa, 1);
- try zcu.retryable_failures.ensureUnusedCapacity(gpa, 1);
- const msg = try Zcu.ErrorMsg.create(
- gpa,
- .{ .base_node_inst = cau.zir_index, .offset = Zcu.LazySrcLoc.Offset.nodeOffset(0) },
- "unable to analyze: OutOfMemory",
- .{},
- );
- zcu.retryable_failures.appendAssumeCapacity(anal_unit);
- zcu.failed_analysis.putAssumeCapacityNoClobber(anal_unit, msg);
- break :res .{ .{
- .invalidate_decl_val = true,
- .invalidate_decl_ref = true,
- }, true };
+ error.OutOfMemory => {
+ // TODO: it's unclear how to gracefully handle this.
+ // To report the error cleanly, we need to add a message to `failed_analysis` and a
+ // corresponding entry to `retryable_failures`; but either of these things is quite
+ // likely to OOM at this point.
+ // If that happens, what do we do? Perhaps we could have a special field on `Zcu`
+ // for reporting OOM errors without allocating.
+ return error.OutOfMemory;
},
+ error.GenericPoison => unreachable,
+ error.ComptimeReturn => unreachable,
+ error.ComptimeBreak => unreachable,
};
- if (cau_outdated) {
- // TODO: we do not yet have separate dependencies for decl values vs types.
- const invalidate = sema_result.invalidate_decl_val or sema_result.invalidate_decl_ref;
- const dependee: InternPool.Dependee = switch (cau.owner.unwrap()) {
- .none => return, // there are no dependencies on a `comptime` decl!
- .nav => |nav_index| .{ .nav_val = nav_index },
- .type => |ty| .{ .interned = ty },
- };
-
- if (invalidate) {
+ if (was_outdated) {
+ const dependee: InternPool.Dependee = .{ .nav_val = nav_id };
+ if (invalidate_value) {
// This dependency was marked as PO, meaning dependees were waiting
// on its analysis result, and it has turned out to be outdated.
// Update dependees accordingly.
@@ -676,67 +818,526 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
}
}
- if (analysis_fail) return error.AnalysisFail;
+ if (new_failed) return error.AnalysisFail;
}
-fn ensureCauAnalyzedInner(
- pt: Zcu.PerThread,
- cau_index: InternPool.Cau.Index,
- cau_outdated: bool,
-) Zcu.SemaError!SemaCauResult {
+fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { val_changed: bool } {
const zcu = pt.zcu;
+ const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
- const cau = ip.getCau(cau_index);
- const anal_unit = AnalUnit.wrap(.{ .cau = cau_index });
+ const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_id });
+ const old_nav = ip.getNav(nav_id);
- const inst_info = cau.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
+ log.debug("analyzeNavVal {}", .{zcu.fmtAnalUnit(anal_unit)});
- // TODO: document this elsewhere mlugg!
- // For my own benefit, here's how a namespace update for a normal (non-file-root) type works:
- // `const S = struct { ... };`
- // We are adding or removing a declaration within this `struct`.
- // * `S` registers a dependency on `.{ .src_hash = (declaration of S) }`
- // * Any change to the `struct` body -- including changing a declaration -- invalidates this
- // * `S` is re-analyzed, but notes:
- // * there is an existing struct instance (at this `TrackedInst` with these captures)
- // * the struct's `Cau` is up-to-date (because nothing about the fields changed)
- // * so, it uses the same `struct`
- // * but this doesn't stop it from updating the namespace!
- // * we basically do `scanDecls`, updating the namespace as needed
- // * so everyone lived happily ever after
+ const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
+ const file = zcu.fileByIndex(inst_resolved.file);
+ // TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is
+ // unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends
+ // in `ensureComptimeUnitUpToDate`.
+ if (file.status != .success_zir) return error.AnalysisFail;
+ const zir = file.zir;
- if (zcu.fileByIndex(inst_info.file).status != .success_zir) {
- return error.AnalysisFail;
+ // We are about to re-analyze this unit; drop its depenndencies.
+ zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
+
+ try zcu.analysis_in_progress.put(gpa, anal_unit, {});
+ errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
+
+ var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
+ defer analysis_arena.deinit();
+
+ var comptime_err_ret_trace: std.ArrayList(Zcu.LazySrcLoc) = .init(gpa);
+ defer comptime_err_ret_trace.deinit();
+
+ var sema: Sema = .{
+ .pt = pt,
+ .gpa = gpa,
+ .arena = analysis_arena.allocator(),
+ .code = zir,
+ .owner = anal_unit,
+ .func_index = .none,
+ .func_is_naked = false,
+ .fn_ret_ty = .void,
+ .fn_ret_ty_ies = null,
+ .comptime_err_ret_trace = &comptime_err_ret_trace,
+ };
+ defer sema.deinit();
+
+ // Every `Nav` declares a dependency on the source of the corresponding declaration.
+ try sema.declareDependency(.{ .src_hash = old_nav.analysis.?.zir_index });
+
+ // In theory, we would also add a reference to the corresponding `nav_val` unit here: there are
+ // always references in both directions between a `nav_val` and `nav_ty`. However, to save memory,
+ // these references are known implicitly. See logic in `Zcu.resolveReferences`.
+
+ var block: Sema.Block = .{
+ .parent = null,
+ .sema = &sema,
+ .namespace = old_nav.analysis.?.namespace,
+ .instructions = .{},
+ .inlining = null,
+ .is_comptime = true,
+ .src_base_inst = old_nav.analysis.?.zir_index,
+ .type_name_ctx = old_nav.fqn,
+ };
+ defer block.instructions.deinit(gpa);
+
+ const zir_decl = zir.getDeclaration(inst_resolved.inst);
+ assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace"));
+
+ const ty_src = block.src(.{ .node_offset_var_decl_ty = 0 });
+ const init_src = block.src(.{ .node_offset_var_decl_init = 0 });
+ const align_src = block.src(.{ .node_offset_var_decl_align = 0 });
+ const section_src = block.src(.{ .node_offset_var_decl_section = 0 });
+ const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = 0 });
+
+ const maybe_ty: ?Type = if (zir_decl.type_body != null) ty: {
+ // Since we have a type body, the type is resolved separately!
+ // Of course, we need to make sure we depend on it properly.
+ try sema.declareDependency(.{ .nav_ty = nav_id });
+ try pt.ensureNavTypeUpToDate(nav_id);
+ break :ty .fromInterned(ip.getNav(nav_id).status.type_resolved.type);
+ } else null;
+
+ const final_val: ?Value = if (zir_decl.value_body) |value_body| val: {
+ if (maybe_ty) |ty| {
+ // Put the resolved type into `inst_map` to be used as the result type of the init.
+ try sema.inst_map.ensureSpaceForInstructions(gpa, &.{inst_resolved.inst});
+ sema.inst_map.putAssumeCapacity(inst_resolved.inst, Air.internedToRef(ty.toIntern()));
+ const uncoerced_result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
+ assert(sema.inst_map.remove(inst_resolved.inst));
+
+ const result_ref = try sema.coerce(&block, ty, uncoerced_result_ref, init_src);
+ break :val try sema.resolveFinalDeclValue(&block, init_src, result_ref);
+ } else {
+ // Just analyze the value; we have no type to offer.
+ const result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
+ break :val try sema.resolveFinalDeclValue(&block, init_src, result_ref);
+ }
+ } else null;
+
+ const nav_ty: Type = maybe_ty orelse final_val.?.typeOf(zcu);
+
+ // First, we must resolve the declaration's type. To do this, we analyze the type body if available,
+ // or otherwise, we analyze the value body, populating `early_val` in the process.
+
+ switch (zir_decl.kind) {
+ .@"comptime" => unreachable, // this is not a Nav
+ .unnamed_test, .@"test", .decltest => assert(nav_ty.zigTypeTag(zcu) == .@"fn"),
+ .@"usingnamespace" => {},
+ .@"const" => {},
+ .@"var" => try sema.validateVarType(
+ &block,
+ if (zir_decl.type_body != null) ty_src else init_src,
+ nav_ty,
+ zir_decl.linkage == .@"extern",
+ ),
+ }
+
+ // Now that we know the type, we can evaluate the alignment, linksection, and addrspace, to determine
+ // the full pointer type of this declaration.
+
+ const modifiers: Sema.NavPtrModifiers = if (zir_decl.type_body != null) m: {
+ // `analyzeNavType` (from the `ensureNavTypeUpToDate` call above) has already populated this data into
+ // the `Nav`. Load the new one, and pull the modifiers out.
+ switch (ip.getNav(nav_id).status) {
+ .unresolved => unreachable, // `analyzeNavType` will never leave us in this state
+ inline .type_resolved, .fully_resolved => |r| break :m .{
+ .alignment = r.alignment,
+ .@"linksection" = r.@"linksection",
+ .@"addrspace" = r.@"addrspace",
+ },
+ }
+ } else m: {
+ // `analyzeNavType` is essentially a stub which calls us. We are responsible for resolving this data.
+ break :m try sema.resolveNavPtrModifiers(&block, zir_decl, inst_resolved.inst, nav_ty);
+ };
+
+ // Lastly, we must figure out the actual interned value to store to the `Nav`.
+ // This isn't necessarily the same as `final_val`!
+
+ const nav_val: Value = switch (zir_decl.linkage) {
+ .normal, .@"export" => switch (zir_decl.kind) {
+ .@"var" => .fromInterned(try pt.intern(.{ .variable = .{
+ .ty = nav_ty.toIntern(),
+ .init = final_val.?.toIntern(),
+ .owner_nav = nav_id,
+ .is_threadlocal = zir_decl.is_threadlocal,
+ .is_weak_linkage = false,
+ } })),
+ else => final_val.?,
+ },
+ .@"extern" => val: {
+ assert(final_val == null); // extern decls do not have a value body
+ const lib_name: ?[]const u8 = if (zir_decl.lib_name != .empty) l: {
+ break :l zir.nullTerminatedString(zir_decl.lib_name);
+ } else null;
+ if (lib_name) |l| {
+ const lib_name_src = block.src(.{ .node_offset_lib_name = 0 });
+ try sema.handleExternLibName(&block, lib_name_src, l);
+ }
+ break :val .fromInterned(try pt.getExtern(.{
+ .name = old_nav.name,
+ .ty = nav_ty.toIntern(),
+ .lib_name = try ip.getOrPutStringOpt(gpa, pt.tid, lib_name, .no_embedded_nulls),
+ .is_const = zir_decl.kind == .@"const",
+ .is_threadlocal = zir_decl.is_threadlocal,
+ .is_weak_linkage = false,
+ .is_dll_import = false,
+ .alignment = modifiers.alignment,
+ .@"addrspace" = modifiers.@"addrspace",
+ .zir_index = old_nav.analysis.?.zir_index, // `declaration` instruction
+ .owner_nav = undefined, // ignored by `getExtern`
+ }));
+ },
+ };
+
+ switch (nav_val.toIntern()) {
+ .generic_poison => unreachable, // assertion failure
+ .unreachable_value => unreachable, // assertion failure
+ else => {},
+ }
+
+ // This resolves the type of the resolved value, not that value itself. If `nav_val` is a struct type,
+ // this resolves the type `type` (which needs no resolution), not the struct itself.
+ try nav_ty.resolveLayout(pt);
+
+ // TODO: this is jank. If #20663 is rejected, let's think about how to better model `usingnamespace`.
+ if (zir_decl.kind == .@"usingnamespace") {
+ if (nav_ty.toIntern() != .type_type) {
+ return sema.fail(&block, ty_src, "expected type, found {}", .{nav_ty.fmt(pt)});
+ }
+ if (nav_val.toType().getNamespace(zcu) == .none) {
+ return sema.fail(&block, ty_src, "type {} has no namespace", .{nav_val.toType().fmt(pt)});
+ }
+ ip.resolveNavValue(nav_id, .{
+ .val = nav_val.toIntern(),
+ .alignment = .none,
+ .@"linksection" = .none,
+ .@"addrspace" = .generic,
+ });
+ // TODO: usingnamespace cannot participate in incremental compilation
+ assert(zcu.analysis_in_progress.swapRemove(anal_unit));
+ return .{ .val_changed = true };
+ }
+
+ const queue_linker_work, const is_owned_fn = switch (ip.indexToKey(nav_val.toIntern())) {
+ .func => |f| .{ true, f.owner_nav == nav_id }, // note that this lets function aliases reach codegen
+ .variable => |v| .{ v.owner_nav == nav_id, false },
+ .@"extern" => |e| .{
+ false,
+ Type.fromInterned(e.ty).zigTypeTag(zcu) == .@"fn" and zir_decl.linkage == .@"extern",
+ },
+ else => .{ true, false },
+ };
+
+ if (is_owned_fn) {
+ // linksection etc are legal, except some targets do not support function alignment.
+ if (zir_decl.align_body != null and !target_util.supportsFunctionAlignment(zcu.getTarget())) {
+ return sema.fail(&block, align_src, "target does not support function alignment", .{});
+ }
+ } else if (try nav_ty.comptimeOnlySema(pt)) {
+ // alignment, linksection, addrspace annotations are not allowed for comptime-only types.
+ const reason: []const u8 = switch (ip.indexToKey(nav_val.toIntern())) {
+ .func => "function alias", // slightly clearer message, since you *can* specify these on function *declarations*
+ else => "comptime-only type",
+ };
+ if (zir_decl.align_body != null) {
+ return sema.fail(&block, align_src, "cannot specify alignment of {s}", .{reason});
+ }
+ if (zir_decl.linksection_body != null) {
+ return sema.fail(&block, section_src, "cannot specify linksection of {s}", .{reason});
+ }
+ if (zir_decl.addrspace_body != null) {
+ return sema.fail(&block, addrspace_src, "cannot specify addrspace of {s}", .{reason});
+ }
+ }
+
+ ip.resolveNavValue(nav_id, .{
+ .val = nav_val.toIntern(),
+ .alignment = modifiers.alignment,
+ .@"linksection" = modifiers.@"linksection",
+ .@"addrspace" = modifiers.@"addrspace",
+ });
+
+ // Mark the unit as completed before evaluating the export!
+ assert(zcu.analysis_in_progress.swapRemove(anal_unit));
+
+ if (zir_decl.type_body == null) {
+ // In this situation, it's possible that we were triggered by `analyzeNavType` up the stack. In that
+ // case, we must also signal that the *type* is now populated to make this export behave correctly.
+ // An alternative strategy would be to just put something on the job queue to perform the export, but
+ // this is a little more straightforward, if perhaps less elegant.
+ _ = zcu.analysis_in_progress.swapRemove(.wrap(.{ .nav_ty = nav_id }));
+ }
+
+ if (zir_decl.linkage == .@"export") {
+ const export_src = block.src(.{ .token_offset = @intFromBool(zir_decl.is_pub) });
+ const name_slice = zir.nullTerminatedString(zir_decl.name);
+ const name_ip = try ip.getOrPutString(gpa, pt.tid, name_slice, .no_embedded_nulls);
+ try sema.analyzeExport(&block, export_src, .{ .name = name_ip }, nav_id);
+ }
+
+ try sema.flushExports();
+
+ queue_codegen: {
+ if (!queue_linker_work) break :queue_codegen;
+
+ if (!try nav_ty.hasRuntimeBitsSema(pt)) {
+ if (zcu.comp.config.use_llvm) break :queue_codegen;
+ if (file.mod.strip) break :queue_codegen;
+ }
+
+ // This job depends on any resolve_type_fully jobs queued up before it.
+ try zcu.comp.queueJob(.{ .codegen_nav = nav_id });
}
- // `cau_outdated` can be true in the initial update for `comptime` declarations,
- // so this isn't a `dev.check`.
- if (cau_outdated and dev.env.supports(.incremental)) {
- // The exports this `Cau` performs will be re-discovered, so we remove them here
- // prior to re-analysis.
+ switch (old_nav.status) {
+ .unresolved, .type_resolved => return .{ .val_changed = true },
+ .fully_resolved => |old| return .{ .val_changed = old.val != nav_val.toIntern() },
+ }
+}
+
+pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.SemaError!void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+
+ const anal_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id });
+ const nav = ip.getNav(nav_id);
+
+ log.debug("ensureNavTypeUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)});
+
+ // Determine whether or not this `Nav`'s type is outdated. This also includes checking if the
+ // status is `.unresolved`, which indicates that the value is outdated because it has *never*
+ // been analyzed so far.
+ //
+ // Note that if the unit is PO, we pessimistically assume that it *does* require re-analysis, to
+ // ensure that the unit is definitely up-to-date when this function returns. This mechanism could
+ // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by
+ // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`.
+
+ const was_outdated = zcu.outdated.swapRemove(anal_unit) or
+ zcu.potentially_outdated.swapRemove(anal_unit);
+
+ const prev_failed = zcu.failed_analysis.contains(anal_unit) or
+ zcu.transitive_failed_analysis.contains(anal_unit);
+
+ if (was_outdated) {
+ dev.check(.incremental);
+ _ = zcu.outdated_ready.swapRemove(anal_unit);
zcu.deleteUnitExports(anal_unit);
zcu.deleteUnitReferences(anal_unit);
if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
- kv.value.destroy(zcu.gpa);
+ kv.value.destroy(gpa);
}
_ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
+ } else {
+ // We can trust the current information about this unit.
+ if (prev_failed) return error.AnalysisFail;
+ switch (nav.status) {
+ .unresolved => {},
+ .type_resolved, .fully_resolved => return,
+ }
}
- const decl_prog_node = zcu.sema_prog_node.start(switch (cau.owner.unwrap()) {
- .nav => |nav| ip.getNav(nav).fqn.toSlice(ip),
- .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
- .none => "comptime",
- }, 0);
- defer decl_prog_node.end();
+ const unit_prog_node = zcu.sema_prog_node.start(nav.fqn.toSlice(ip), 0);
+ defer unit_prog_node.end();
- return pt.semaCau(cau_index) catch |err| switch (err) {
- error.GenericPoison, error.ComptimeBreak, error.ComptimeReturn => unreachable,
- error.AnalysisFail, error.OutOfMemory => |e| return e,
+ const invalidate_type: bool, const new_failed: bool = if (pt.analyzeNavType(nav_id)) |result| res: {
+ break :res .{
+ // If the unit has gone from failed to success, we still need to invalidate the dependencies.
+ result.type_changed or prev_failed,
+ false,
+ };
+ } else |err| switch (err) {
+ error.AnalysisFail => res: {
+ if (!zcu.failed_analysis.contains(anal_unit)) {
+ // If this unit caused the error, it would have an entry in `failed_analysis`.
+ // Since it does not, this must be a transitive failure.
+ try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
+ log.debug("mark transitive analysis failure for {}", .{zcu.fmtAnalUnit(anal_unit)});
+ }
+ break :res .{ !prev_failed, true };
+ },
+ error.OutOfMemory => {
+ // TODO: it's unclear how to gracefully handle this.
+ // To report the error cleanly, we need to add a message to `failed_analysis` and a
+ // corresponding entry to `retryable_failures`; but either of these things is quite
+ // likely to OOM at this point.
+ // If that happens, what do we do? Perhaps we could have a special field on `Zcu`
+ // for reporting OOM errors without allocating.
+ return error.OutOfMemory;
+ },
+ error.GenericPoison => unreachable,
+ error.ComptimeReturn => unreachable,
+ error.ComptimeBreak => unreachable,
+ };
+
+ if (was_outdated) {
+ const dependee: InternPool.Dependee = .{ .nav_ty = nav_id };
+ if (invalidate_type) {
+ // This dependency was marked as PO, meaning dependees were waiting
+ // on its analysis result, and it has turned out to be outdated.
+ // Update dependees accordingly.
+ try zcu.markDependeeOutdated(.marked_po, dependee);
+ } else {
+ // This dependency was previously PO, but turned out to be up-to-date.
+ // We do not need to queue successive analysis.
+ try zcu.markPoDependeeUpToDate(dependee);
+ }
+ }
+
+ if (new_failed) return error.AnalysisFail;
+}
+
+fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { type_changed: bool } {
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+
+ const anal_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id });
+ const old_nav = ip.getNav(nav_id);
+
+ log.debug("analyzeNavType {}", .{zcu.fmtAnalUnit(anal_unit)});
+
+ const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
+ const file = zcu.fileByIndex(inst_resolved.file);
+ // TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is
+ // unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends
+ // in `ensureComptimeUnitUpToDate`.
+ if (file.status != .success_zir) return error.AnalysisFail;
+ const zir = file.zir;
+
+ // We are about to re-analyze this unit; drop its depenndencies.
+ zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
+
+ try zcu.analysis_in_progress.put(gpa, anal_unit, {});
+ defer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
+
+ var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
+ defer analysis_arena.deinit();
+
+ var comptime_err_ret_trace: std.ArrayList(Zcu.LazySrcLoc) = .init(gpa);
+ defer comptime_err_ret_trace.deinit();
+
+ var sema: Sema = .{
+ .pt = pt,
+ .gpa = gpa,
+ .arena = analysis_arena.allocator(),
+ .code = zir,
+ .owner = anal_unit,
+ .func_index = .none,
+ .func_is_naked = false,
+ .fn_ret_ty = .void,
+ .fn_ret_ty_ies = null,
+ .comptime_err_ret_trace = &comptime_err_ret_trace,
};
+ defer sema.deinit();
+
+ // Every `Nav` declares a dependency on the source of the corresponding declaration.
+ try sema.declareDependency(.{ .src_hash = old_nav.analysis.?.zir_index });
+
+ // In theory, we would also add a reference to the corresponding `nav_val` unit here: there are
+ // always references in both directions between a `nav_val` and `nav_ty`. However, to save memory,
+ // these references are known implicitly. See logic in `Zcu.resolveReferences`.
+
+ var block: Sema.Block = .{
+ .parent = null,
+ .sema = &sema,
+ .namespace = old_nav.analysis.?.namespace,
+ .instructions = .{},
+ .inlining = null,
+ .is_comptime = true,
+ .src_base_inst = old_nav.analysis.?.zir_index,
+ .type_name_ctx = old_nav.fqn,
+ };
+ defer block.instructions.deinit(gpa);
+
+ const zir_decl = zir.getDeclaration(inst_resolved.inst);
+ assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace"));
+
+ const type_body = zir_decl.type_body orelse {
+ // The type of this `Nav` is inferred from the value.
+ // In other words, this `nav_ty` depends on the corresponding `nav_val`.
+ try sema.declareDependency(.{ .nav_val = nav_id });
+ try pt.ensureNavValUpToDate(nav_id);
+ // Note that the above call, if it did any work, has removed our `analysis_in_progress` entry for us.
+ // (Our `defer` will run anyway, but it does nothing in this case.)
+
+ // There's not a great way for us to know whether the type actually changed.
+ // For instance, perhaps the `nav_val` was already up-to-date, but this `nav_ty` is being
+ // analyzed because this declaration had a type annotation on the *previous* update.
+ // However, such cases are rare, and it's not unreasonable to re-analyze in them; and in
+ // other cases where we get here, it's because the `nav_val` was already re-analyzed and
+ // is outdated.
+ return .{ .type_changed = true };
+ };
+
+ const ty_src = block.src(.{ .node_offset_var_decl_ty = 0 });
+
+ const resolved_ty: Type = ty: {
+ const uncoerced_type_ref = try sema.resolveInlineBody(&block, type_body, inst_resolved.inst);
+ const type_ref = try sema.coerce(&block, .type, uncoerced_type_ref, ty_src);
+ break :ty .fromInterned(type_ref.toInterned().?);
+ };
+
+ // In the case where the type is specified, this function is also responsible for resolving
+ // the pointer modifiers, i.e. alignment, linksection, addrspace.
+ const modifiers = try sema.resolveNavPtrModifiers(&block, zir_decl, inst_resolved.inst, resolved_ty);
+
+ // Usually, we can infer this information from the resolved `Nav` value; see `Zcu.navValIsConst`.
+ // However, since we don't have one, we need to quickly check the ZIR to figure this out.
+ const is_const = switch (zir_decl.kind) {
+ .@"comptime" => unreachable,
+ .unnamed_test, .@"test", .decltest, .@"usingnamespace", .@"const" => true,
+ .@"var" => false,
+ };
+
+ const is_extern_decl = zir_decl.linkage == .@"extern";
+
+ // Now for the question of the day: are the type and modifiers the same as before?
+ // If they are, then we should actually keep the `Nav` as `fully_resolved` if it currently is.
+ // That's because `analyzeNavVal` will later want to look at the resolved value to figure out
+ // whether it's changed: if we threw that data away now, it would have to assume that the value
+ // had changed, potentially spinning off loads of unnecessary re-analysis!
+ const changed = switch (old_nav.status) {
+ .unresolved => true,
+ .type_resolved => |r| r.type != resolved_ty.toIntern() or
+ r.alignment != modifiers.alignment or
+ r.@"linksection" != modifiers.@"linksection" or
+ r.@"addrspace" != modifiers.@"addrspace" or
+ r.is_const != is_const or
+ r.is_extern_decl != is_extern_decl,
+ .fully_resolved => |r| ip.typeOf(r.val) != resolved_ty.toIntern() or
+ r.alignment != modifiers.alignment or
+ r.@"linksection" != modifiers.@"linksection" or
+ r.@"addrspace" != modifiers.@"addrspace" or
+ zcu.navValIsConst(r.val) != is_const or
+ (old_nav.getExtern(ip) != null) != is_extern_decl,
+ };
+
+ if (!changed) return .{ .type_changed = false };
+
+ ip.resolveNavType(nav_id, .{
+ .type = resolved_ty.toIntern(),
+ .alignment = modifiers.alignment,
+ .@"linksection" = modifiers.@"linksection",
+ .@"addrspace" = modifiers.@"addrspace",
+ .is_const = is_const,
+ .is_threadlocal = zir_decl.is_threadlocal,
+ .is_extern_decl = is_extern_decl,
+ });
+
+ return .{ .type_changed = true };
}
-pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: InternPool.Index) Zcu.SemaError!void {
+pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: InternPool.Index) Zcu.SemaError!void {
dev.check(.sema);
const tracy = trace(@src());
@@ -746,35 +1347,43 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
+ _ = zcu.func_body_analysis_queued.swapRemove(maybe_coerced_func_index);
+
// We only care about the uncoerced function.
const func_index = ip.unwrapCoercedFunc(maybe_coerced_func_index);
- const anal_unit = AnalUnit.wrap(.{ .func = func_index });
+ const anal_unit: AnalUnit = .wrap(.{ .func = func_index });
- log.debug("ensureFuncBodyAnalyzed {}", .{zcu.fmtAnalUnit(anal_unit)});
+ log.debug("ensureFuncBodyUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)});
const func = zcu.funcInfo(maybe_coerced_func_index);
- const func_outdated = zcu.outdated.swapRemove(anal_unit) or
+ const was_outdated = zcu.outdated.swapRemove(anal_unit) or
zcu.potentially_outdated.swapRemove(anal_unit);
const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit);
- if (func_outdated) {
+ if (was_outdated) {
+ dev.check(.incremental);
_ = zcu.outdated_ready.swapRemove(anal_unit);
+ zcu.deleteUnitExports(anal_unit);
+ zcu.deleteUnitReferences(anal_unit);
+ if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
+ kv.value.destroy(gpa);
+ }
+ _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
} else {
// We can trust the current information about this function.
if (prev_failed) {
return error.AnalysisFail;
}
- switch (func.analysisUnordered(ip).state) {
- .unreferenced => {}, // this is the first reference
- .queued => {}, // we're waiting on first-time analysis
- .analyzed => return, // up-to-date
- }
+ if (func.analysisUnordered(ip).is_analyzed) return;
}
- const ies_outdated, const analysis_fail = if (pt.ensureFuncBodyAnalyzedInner(func_index, func_outdated)) |result|
- .{ result.ies_outdated, false }
+ const func_prog_node = zcu.sema_prog_node.start(ip.getNav(func.owner_nav).fqn.toSlice(ip), 0);
+ defer func_prog_node.end();
+
+ const ies_outdated, const new_failed = if (pt.analyzeFuncBody(func_index)) |result|
+ .{ prev_failed or result.ies_outdated, false }
else |err| switch (err) {
error.AnalysisFail => res: {
if (!zcu.failed_analysis.contains(anal_unit)) {
@@ -788,10 +1397,18 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
// a different error later (which may now be invalid).
break :res .{ !prev_failed, true };
},
- error.OutOfMemory => return error.OutOfMemory, // TODO: graceful handling like `ensureCauAnalyzed`
+ error.OutOfMemory => {
+ // TODO: it's unclear how to gracefully handle this.
+ // To report the error cleanly, we need to add a message to `failed_analysis` and a
+ // corresponding entry to `retryable_failures`; but either of these things is quite
+ // likely to OOM at this point.
+ // If that happens, what do we do? Perhaps we could have a special field on `Zcu`
+ // for reporting OOM errors without allocating.
+ return error.OutOfMemory;
+ },
};
- if (func_outdated) {
+ if (was_outdated) {
if (ies_outdated) {
try zcu.markDependeeOutdated(.marked_po, .{ .interned = func_index });
} else {
@@ -799,13 +1416,12 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
}
}
- if (analysis_fail) return error.AnalysisFail;
+ if (new_failed) return error.AnalysisFail;
}
-fn ensureFuncBodyAnalyzedInner(
+fn analyzeFuncBody(
pt: Zcu.PerThread,
func_index: InternPool.Index,
- func_outdated: bool,
) Zcu.SemaError!struct { ies_outdated: bool } {
const zcu = pt.zcu;
const gpa = zcu.gpa;
@@ -820,8 +1436,8 @@ fn ensureFuncBodyAnalyzedInner(
if (func.generic_owner == .none) {
// Among another things, this ensures that the function's `zir_body_inst` is correct.
- try pt.ensureCauAnalyzed(ip.getNav(func.owner_nav).analysis_owner.unwrap().?);
- if (ip.getNav(func.owner_nav).status.resolved.val != func_index) {
+ try pt.ensureNavValUpToDate(func.owner_nav);
+ if (ip.getNav(func.owner_nav).status.fully_resolved.val != func_index) {
// This function is no longer referenced! There's no point in re-analyzing it.
// Just mark a transitive failure and move on.
return error.AnalysisFail;
@@ -829,8 +1445,8 @@ fn ensureFuncBodyAnalyzedInner(
} else {
const go_nav = zcu.funcInfo(func.generic_owner).owner_nav;
// Among another things, this ensures that the function's `zir_body_inst` is correct.
- try pt.ensureCauAnalyzed(ip.getNav(go_nav).analysis_owner.unwrap().?);
- if (ip.getNav(go_nav).status.resolved.val != func.generic_owner) {
+ try pt.ensureNavValUpToDate(go_nav);
+ if (ip.getNav(go_nav).status.fully_resolved.val != func.generic_owner) {
// The generic owner is no longer referenced, so this function is also unreferenced.
// There's no point in re-analyzing it. Just mark a transitive failure and move on.
return error.AnalysisFail;
@@ -844,38 +1460,13 @@ fn ensureFuncBodyAnalyzedInner(
else
.none;
- if (func_outdated) {
- dev.check(.incremental);
- zcu.deleteUnitExports(anal_unit);
- zcu.deleteUnitReferences(anal_unit);
- if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
- kv.value.destroy(gpa);
- }
- _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
- }
-
- if (!func_outdated) {
- // We can trust the current information about this function.
- if (zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit)) {
- return error.AnalysisFail;
- }
- switch (func.analysisUnordered(ip).state) {
- .unreferenced => {}, // this is the first reference
- .queued => {}, // we're waiting on first-time analysis
- .analyzed => return .{ .ies_outdated = false }, // up-to-date
- }
- }
-
- log.debug("analyze and generate fn body {}; reason='{s}'", .{
- zcu.fmtAnalUnit(anal_unit),
- if (func_outdated) "outdated" else "never analyzed",
- });
+ log.debug("analyze and generate fn body {}", .{zcu.fmtAnalUnit(anal_unit)});
- var air = try pt.analyzeFnBody(func_index);
+ var air = try pt.analyzeFnBodyInner(func_index);
errdefer air.deinit(gpa);
- const ies_outdated = func_outdated and
- (!func.analysisUnordered(ip).inferred_error_set or func.resolvedErrorSetUnordered(ip) != old_resolved_ies);
+ const ies_outdated = !func.analysisUnordered(ip).inferred_error_set or
+ func.resolvedErrorSetUnordered(ip) != old_resolved_ies;
const comp = zcu.comp;
@@ -1043,12 +1634,11 @@ fn createFileRootStruct(
wip_ty.setName(ip, try file.internFullyQualifiedName(pt));
ip.namespacePtr(namespace_index).owner_type = wip_ty.index;
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, namespace_index, wip_ty.index);
if (zcu.comp.incremental) {
try ip.addDependency(
gpa,
- AnalUnit.wrap(.{ .cau = new_cau_index }),
+ .wrap(.{ .type = wip_ty.index }),
.{ .src_hash = tracked_inst },
);
}
@@ -1062,7 +1652,7 @@ fn createFileRootStruct(
try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
}
zcu.setFileRootType(file_index, wip_ty.index);
- return wip_ty.finish(ip, new_cau_index.toOptional(), namespace_index);
+ return wip_ty.finish(ip, namespace_index);
}
/// Re-scan the namespace of a file's root struct type on an incremental update.
@@ -1155,294 +1745,6 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
}
}
-const SemaCauResult = packed struct {
- /// Whether the value of a `decl_val` of the corresponding Nav changed.
- invalidate_decl_val: bool,
- /// Whether the type of a `decl_ref` of the corresponding Nav changed.
- invalidate_decl_ref: bool,
-};
-
-/// Performs semantic analysis on the given `Cau`, storing results to its owner `Nav` if needed.
-/// If analysis fails, returns `error.AnalysisFail`, storing an error in `zcu.failed_analysis` unless
-/// the error is transitive.
-/// On success, returns information about whether the `Nav` value changed.
-fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
- const zcu = pt.zcu;
- const gpa = zcu.gpa;
- const ip = &zcu.intern_pool;
-
- const anal_unit = AnalUnit.wrap(.{ .cau = cau_index });
-
- const cau = ip.getCau(cau_index);
- const inst_info = cau.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
- const file = zcu.fileByIndex(inst_info.file);
- const zir = file.zir;
-
- if (file.status != .success_zir) {
- return error.AnalysisFail;
- }
-
- // We are about to re-analyze this `Cau`; drop its depenndencies.
- zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
-
- switch (cau.owner.unwrap()) {
- .none => {}, // `comptime` decl -- we will re-analyze its body.
- .nav => {}, // Other decl -- we will re-analyze its value.
- .type => |ty| {
- // This is an incremental update, and this type is being re-analyzed because it is outdated.
- // Create a new type in its place, and mark the old one as outdated so that use sites will
- // be re-analyzed and discover an up-to-date type.
- const new_ty = try pt.ensureTypeUpToDate(ty, true);
- assert(new_ty != ty);
- return .{
- .invalidate_decl_val = true,
- .invalidate_decl_ref = true,
- };
- },
- }
-
- const is_usingnamespace = switch (cau.owner.unwrap()) {
- .nav => |nav| ip.getNav(nav).is_usingnamespace,
- .none, .type => false,
- };
-
- log.debug("semaCau {}", .{zcu.fmtAnalUnit(anal_unit)});
-
- try zcu.analysis_in_progress.put(gpa, anal_unit, {});
- errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
-
- var analysis_arena = std.heap.ArenaAllocator.init(gpa);
- defer analysis_arena.deinit();
-
- var comptime_err_ret_trace = std.ArrayList(Zcu.LazySrcLoc).init(gpa);
- defer comptime_err_ret_trace.deinit();
-
- var sema: Sema = .{
- .pt = pt,
- .gpa = gpa,
- .arena = analysis_arena.allocator(),
- .code = zir,
- .owner = anal_unit,
- .func_index = .none,
- .func_is_naked = false,
- .fn_ret_ty = Type.void,
- .fn_ret_ty_ies = null,
- .comptime_err_ret_trace = &comptime_err_ret_trace,
- };
- defer sema.deinit();
-
- // Every `Cau` has a dependency on the source of its own ZIR instruction.
- try sema.declareDependency(.{ .src_hash = cau.zir_index });
-
- var block: Sema.Block = .{
- .parent = null,
- .sema = &sema,
- .namespace = cau.namespace,
- .instructions = .{},
- .inlining = null,
- .is_comptime = true,
- .src_base_inst = cau.zir_index,
- .type_name_ctx = switch (cau.owner.unwrap()) {
- .nav => |nav| ip.getNav(nav).fqn,
- .type => |ty| Type.fromInterned(ty).containerTypeName(ip),
- .none => try ip.getOrPutStringFmt(gpa, pt.tid, "{}.comptime", .{
- Type.fromInterned(zcu.namespacePtr(cau.namespace).owner_type).containerTypeName(ip).fmt(ip),
- }, .no_embedded_nulls),
- },
- };
- defer block.instructions.deinit(gpa);
-
- const zir_decl: Zir.Inst.Declaration, const decl_bodies: Zir.Inst.Declaration.Bodies = decl: {
- const decl, const extra_end = zir.getDeclaration(inst_info.inst);
- break :decl .{ decl, decl.getBodies(extra_end, zir) };
- };
-
- // We have to fetch this state before resolving the body because of the `nav_already_populated`
- // case below. We might change the language in future so that align/linksection/etc for functions
- // work in a way more in line with other declarations, in which case that logic will go away.
- const old_nav_info = switch (cau.owner.unwrap()) {
- .none, .type => undefined, // we'll never use `old_nav_info`
- .nav => |nav| ip.getNav(nav),
- };
-
- const result_ref = try sema.resolveInlineBody(&block, decl_bodies.value_body, inst_info.inst);
-
- const nav_index = switch (cau.owner.unwrap()) {
- .none => {
- // This is a `comptime` decl, so we are done -- the side effects are all we care about.
- // Just make sure to `flushExports`.
- try sema.flushExports();
- assert(zcu.analysis_in_progress.swapRemove(anal_unit));
- return .{
- .invalidate_decl_val = false,
- .invalidate_decl_ref = false,
- };
- },
- .nav => |nav| nav, // We will resolve this `Nav` below.
- .type => unreachable, // Handled at top of function.
- };
-
- const align_src = block.src(.{ .node_offset_var_decl_align = 0 });
- const section_src = block.src(.{ .node_offset_var_decl_section = 0 });
- const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = 0 });
- const ty_src = block.src(.{ .node_offset_var_decl_ty = 0 });
- const init_src = block.src(.{ .node_offset_var_decl_init = 0 });
-
- const decl_val = try sema.resolveFinalDeclValue(&block, init_src, result_ref);
- const decl_ty = decl_val.typeOf(zcu);
-
- switch (decl_val.toIntern()) {
- .generic_poison => unreachable, // assertion failure
- .unreachable_value => unreachable, // assertion failure
- else => {},
- }
-
- // This resolves the type of the resolved value, not that value itself. If `decl_val` is a struct type,
- // this resolves the type `type` (which needs no resolution), not the struct itself.
- try decl_ty.resolveLayout(pt);
-
- // TODO: this is jank. If #20663 is rejected, let's think about how to better model `usingnamespace`.
- if (is_usingnamespace) {
- if (decl_ty.toIntern() != .type_type) {
- return sema.fail(&block, ty_src, "expected type, found {}", .{decl_ty.fmt(pt)});
- }
- if (decl_val.toType().getNamespace(zcu) == .none) {
- return sema.fail(&block, ty_src, "type {} has no namespace", .{decl_val.toType().fmt(pt)});
- }
- ip.resolveNavValue(nav_index, .{
- .val = decl_val.toIntern(),
- .alignment = .none,
- .@"linksection" = .none,
- .@"addrspace" = .generic,
- });
- // TODO: usingnamespace cannot participate in incremental compilation
- assert(zcu.analysis_in_progress.swapRemove(anal_unit));
- return .{
- .invalidate_decl_val = true,
- .invalidate_decl_ref = true,
- };
- }
-
- const queue_linker_work, const is_owned_fn = switch (ip.indexToKey(decl_val.toIntern())) {
- .func => |f| .{ true, f.owner_nav == nav_index }, // note that this lets function aliases reach codegen
- .variable => |v| .{ v.owner_nav == nav_index, false },
- .@"extern" => |e| .{ false, Type.fromInterned(e.ty).zigTypeTag(zcu) == .@"fn" },
- else => .{ true, false },
- };
-
- // Keep in sync with logic in `Sema.zirVarExtended`.
- const alignment: InternPool.Alignment = a: {
- const align_body = decl_bodies.align_body orelse break :a .none;
- const align_ref = try sema.resolveInlineBody(&block, align_body, inst_info.inst);
- break :a try sema.analyzeAsAlign(&block, align_src, align_ref);
- };
-
- const @"linksection": InternPool.OptionalNullTerminatedString = ls: {
- const linksection_body = decl_bodies.linksection_body orelse break :ls .none;
- const linksection_ref = try sema.resolveInlineBody(&block, linksection_body, inst_info.inst);
- const bytes = try sema.toConstString(&block, section_src, linksection_ref, .{
- .needed_comptime_reason = "linksection must be comptime-known",
- });
- if (std.mem.indexOfScalar(u8, bytes, 0) != null) {
- return sema.fail(&block, section_src, "linksection cannot contain null bytes", .{});
- } else if (bytes.len == 0) {
- return sema.fail(&block, section_src, "linksection cannot be empty", .{});
- }
- break :ls try ip.getOrPutStringOpt(gpa, pt.tid, bytes, .no_embedded_nulls);
- };
-
- const @"addrspace": std.builtin.AddressSpace = as: {
- const addrspace_ctx: Sema.AddressSpaceContext = switch (ip.indexToKey(decl_val.toIntern())) {
- .func => .function,
- .variable => .variable,
- .@"extern" => |e| if (ip.indexToKey(e.ty) == .func_type)
- .function
- else
- .variable,
- else => .constant,
- };
- const target = zcu.getTarget();
- const addrspace_body = decl_bodies.addrspace_body orelse break :as 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.resolveInlineBody(&block, addrspace_body, inst_info.inst);
- break :as try sema.analyzeAsAddressSpace(&block, addrspace_src, addrspace_ref, addrspace_ctx);
- };
-
- if (is_owned_fn) {
- // linksection etc are legal, except some targets do not support function alignment.
- if (decl_bodies.align_body != null and !target_util.supportsFunctionAlignment(zcu.getTarget())) {
- return sema.fail(&block, align_src, "target does not support function alignment", .{});
- }
- } else if (try decl_ty.comptimeOnlySema(pt)) {
- // alignment, linksection, addrspace annotations are not allowed for comptime-only types.
- const reason: []const u8 = switch (ip.indexToKey(decl_val.toIntern())) {
- .func => "function alias", // slightly clearer message, since you *can* specify these on function *declarations*
- else => "comptime-only type",
- };
- if (decl_bodies.align_body != null) {
- return sema.fail(&block, align_src, "cannot specify alignment of {s}", .{reason});
- }
- if (decl_bodies.linksection_body != null) {
- return sema.fail(&block, section_src, "cannot specify linksection of {s}", .{reason});
- }
- if (decl_bodies.addrspace_body != null) {
- return sema.fail(&block, addrspace_src, "cannot specify addrspace of {s}", .{reason});
- }
- }
-
- ip.resolveNavValue(nav_index, .{
- .val = decl_val.toIntern(),
- .alignment = alignment,
- .@"linksection" = @"linksection",
- .@"addrspace" = @"addrspace",
- });
-
- // Mark the `Cau` as completed before evaluating the export!
- assert(zcu.analysis_in_progress.swapRemove(anal_unit));
-
- if (zir_decl.flags.is_export) {
- const export_src = block.src(.{ .token_offset = @intFromBool(zir_decl.flags.is_pub) });
- const name_slice = zir.nullTerminatedString(zir_decl.name.toString(zir).?);
- const name_ip = try ip.getOrPutString(gpa, pt.tid, name_slice, .no_embedded_nulls);
- try sema.analyzeExport(&block, export_src, .{ .name = name_ip }, nav_index);
- }
-
- try sema.flushExports();
-
- queue_codegen: {
- if (!queue_linker_work) break :queue_codegen;
-
- if (!try decl_ty.hasRuntimeBitsSema(pt)) {
- if (zcu.comp.config.use_llvm) break :queue_codegen;
- if (file.mod.strip) break :queue_codegen;
- }
-
- // This job depends on any resolve_type_fully jobs queued up before it.
- try zcu.comp.queueJob(.{ .codegen_nav = nav_index });
- }
-
- switch (old_nav_info.status) {
- .unresolved => return .{
- .invalidate_decl_val = true,
- .invalidate_decl_ref = true,
- },
- .resolved => |old| {
- const new = ip.getNav(nav_index).status.resolved;
- return .{
- .invalidate_decl_val = new.val != old.val,
- .invalidate_decl_ref = ip.typeOf(new.val) != ip.typeOf(old.val) or
- new.alignment != old.alignment or
- new.@"linksection" != old.@"linksection" or
- new.@"addrspace" != old.@"addrspace",
- };
- },
- }
-}
-
pub fn importPkg(pt: Zcu.PerThread, mod: *Module) !Zcu.ImportFileResult {
const zcu = pt.zcu;
const gpa = zcu.gpa;
@@ -1813,45 +2115,42 @@ pub fn scanNamespace(
// For incremental updates, `scanDecl` wants to look up existing decls by their ZIR index rather
// than their name. We'll build an efficient mapping now, then discard the current `decls`.
- // We map to the `Cau`, since not every declaration has a `Nav`.
- var existing_by_inst: std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, InternPool.Cau.Index) = .empty;
+ // We map to the `AnalUnit`, since not every declaration has a `Nav`.
+ var existing_by_inst: std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, InternPool.AnalUnit) = .empty;
defer existing_by_inst.deinit(gpa);
try existing_by_inst.ensureTotalCapacity(gpa, @intCast(
namespace.pub_decls.count() + namespace.priv_decls.count() +
namespace.pub_usingnamespace.items.len + namespace.priv_usingnamespace.items.len +
- namespace.other_decls.items.len,
+ namespace.comptime_decls.items.len +
+ namespace.test_decls.items.len,
));
for (namespace.pub_decls.keys()) |nav| {
- const cau_index = ip.getNav(nav).analysis_owner.unwrap().?;
- const zir_index = ip.getCau(cau_index).zir_index;
- existing_by_inst.putAssumeCapacityNoClobber(zir_index, cau_index);
+ const zir_index = ip.getNav(nav).analysis.?.zir_index;
+ existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
}
for (namespace.priv_decls.keys()) |nav| {
- const cau_index = ip.getNav(nav).analysis_owner.unwrap().?;
- const zir_index = ip.getCau(cau_index).zir_index;
- existing_by_inst.putAssumeCapacityNoClobber(zir_index, cau_index);
+ const zir_index = ip.getNav(nav).analysis.?.zir_index;
+ existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
}
for (namespace.pub_usingnamespace.items) |nav| {
- const cau_index = ip.getNav(nav).analysis_owner.unwrap().?;
- const zir_index = ip.getCau(cau_index).zir_index;
- existing_by_inst.putAssumeCapacityNoClobber(zir_index, cau_index);
+ const zir_index = ip.getNav(nav).analysis.?.zir_index;
+ existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
}
for (namespace.priv_usingnamespace.items) |nav| {
- const cau_index = ip.getNav(nav).analysis_owner.unwrap().?;
- const zir_index = ip.getCau(cau_index).zir_index;
- existing_by_inst.putAssumeCapacityNoClobber(zir_index, cau_index);
- }
- for (namespace.other_decls.items) |cau_index| {
- const cau = ip.getCau(cau_index);
- existing_by_inst.putAssumeCapacityNoClobber(cau.zir_index, cau_index);
- // If this is a test, it'll be re-added to `test_functions` later on
- // if still alive. Remove it for now.
- switch (cau.owner.unwrap()) {
- .none, .type => {},
- .nav => |nav| _ = zcu.test_functions.swapRemove(nav),
- }
+ const zir_index = ip.getNav(nav).analysis.?.zir_index;
+ existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
+ }
+ for (namespace.comptime_decls.items) |cu| {
+ const zir_index = ip.getComptimeUnit(cu).zir_index;
+ existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .@"comptime" = cu }));
+ }
+ for (namespace.test_decls.items) |nav| {
+ const zir_index = ip.getNav(nav).analysis.?.zir_index;
+ existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
+ // This test will be re-added to `test_functions` later on if it's still alive. Remove it for now.
+ _ = zcu.test_functions.swapRemove(nav);
}
var seen_decls: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty;
@@ -1861,7 +2160,8 @@ pub fn scanNamespace(
namespace.priv_decls.clearRetainingCapacity();
namespace.pub_usingnamespace.clearRetainingCapacity();
namespace.priv_usingnamespace.clearRetainingCapacity();
- namespace.other_decls.clearRetainingCapacity();
+ namespace.comptime_decls.clearRetainingCapacity();
+ namespace.test_decls.clearRetainingCapacity();
var scan_decl_iter: ScanDeclIter = .{
.pt = pt,
@@ -1883,7 +2183,7 @@ const ScanDeclIter = struct {
pt: Zcu.PerThread,
namespace_index: Zcu.Namespace.Index,
seen_decls: *std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void),
- existing_by_inst: *const std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, InternPool.Cau.Index),
+ existing_by_inst: *const std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, InternPool.AnalUnit),
/// Decl scanning is run in two passes, so that we can detect when a generated
/// name would clash with an explicit name and use a different one.
pass: enum { named, unnamed },
@@ -1919,64 +2219,41 @@ const ScanDeclIter = struct {
const zir = file.zir;
const ip = &zcu.intern_pool;
- const inst_data = zir.instructions.items(.data)[@intFromEnum(decl_inst)].declaration;
- const extra = zir.extraData(Zir.Inst.Declaration, inst_data.payload_index);
- const declaration = extra.data;
+ const decl = zir.getDeclaration(decl_inst);
- const Kind = enum { @"comptime", @"usingnamespace", @"test", named };
-
- const maybe_name: InternPool.OptionalNullTerminatedString, const kind: Kind, const is_named_test: bool = switch (declaration.name) {
- .@"comptime" => info: {
+ const maybe_name: InternPool.OptionalNullTerminatedString = switch (decl.kind) {
+ .@"comptime" => name: {
if (iter.pass != .unnamed) return;
- break :info .{
- .none,
- .@"comptime",
- false,
- };
+ break :name .none;
},
- .@"usingnamespace" => info: {
+ .@"usingnamespace" => name: {
if (iter.pass != .unnamed) return;
const i = iter.usingnamespace_index;
iter.usingnamespace_index += 1;
- break :info .{
- (try iter.avoidNameConflict("usingnamespace_{d}", .{i})).toOptional(),
- .@"usingnamespace",
- false,
- };
+ break :name (try iter.avoidNameConflict("usingnamespace_{d}", .{i})).toOptional();
},
- .unnamed_test => info: {
+ .unnamed_test => name: {
if (iter.pass != .unnamed) return;
const i = iter.unnamed_test_index;
iter.unnamed_test_index += 1;
- break :info .{
- (try iter.avoidNameConflict("test_{d}", .{i})).toOptional(),
- .@"test",
- false,
- };
+ break :name (try iter.avoidNameConflict("test_{d}", .{i})).toOptional();
},
- _ => if (declaration.name.isNamedTest(zir)) info: {
+ .@"test", .decltest => |kind| name: {
// We consider these to be unnamed since the decl name can be adjusted to avoid conflicts if necessary.
if (iter.pass != .unnamed) return;
- const prefix = if (declaration.flags.test_is_decltest) "decltest" else "test";
- break :info .{
- (try iter.avoidNameConflict("{s}.{s}", .{ prefix, zir.nullTerminatedString(declaration.name.toString(zir).?) })).toOptional(),
- .@"test",
- true,
- };
- } else info: {
+ const prefix = @tagName(kind);
+ break :name (try iter.avoidNameConflict("{s}.{s}", .{ prefix, zir.nullTerminatedString(decl.name) })).toOptional();
+ },
+ .@"const", .@"var" => name: {
if (iter.pass != .named) return;
const name = try ip.getOrPutString(
gpa,
pt.tid,
- zir.nullTerminatedString(declaration.name.toString(zir).?),
+ zir.nullTerminatedString(decl.name),
.no_embedded_nulls,
);
try iter.seen_decls.putNoClobber(gpa, name, {});
- break :info .{
- name.toOptional(),
- .named,
- false,
- };
+ break :name name.toOptional();
},
};
@@ -1985,60 +2262,59 @@ const ScanDeclIter = struct {
.inst = decl_inst,
});
- const existing_cau = iter.existing_by_inst.get(tracked_inst);
+ const existing_unit = iter.existing_by_inst.get(tracked_inst);
- const cau, const want_analysis = switch (kind) {
- .@"comptime" => cau: {
- const cau = existing_cau orelse try ip.createComptimeCau(gpa, pt.tid, tracked_inst, namespace_index);
+ const unit, const want_analysis = switch (decl.kind) {
+ .@"comptime" => unit: {
+ const cu = if (existing_unit) |eu|
+ eu.unwrap().@"comptime"
+ else
+ try ip.createComptimeUnit(gpa, pt.tid, tracked_inst, namespace_index);
- try namespace.other_decls.append(gpa, cau);
+ const unit: AnalUnit = .wrap(.{ .@"comptime" = cu });
- if (existing_cau == null) {
- // For a `comptime` declaration, whether to analyze is based solely on whether the
- // `Cau` is outdated. So, add this one to `outdated` and `outdated_ready` if not already.
- const unit = AnalUnit.wrap(.{ .cau = cau });
- if (zcu.potentially_outdated.fetchSwapRemove(unit)) |kv| {
- try zcu.outdated.ensureUnusedCapacity(gpa, 1);
- try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1);
- zcu.outdated.putAssumeCapacityNoClobber(unit, kv.value);
- if (kv.value == 0) { // no PO deps
- zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {});
- }
- } else if (!zcu.outdated.contains(unit)) {
- try zcu.outdated.ensureUnusedCapacity(gpa, 1);
- try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1);
- zcu.outdated.putAssumeCapacityNoClobber(unit, 0);
- zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {});
- }
+ try namespace.comptime_decls.append(gpa, cu);
+
+ if (existing_unit == null) {
+ // For a `comptime` declaration, whether to analyze is based solely on whether the unit
+ // is outdated. So, add this fresh one to `outdated` and `outdated_ready`.
+ try zcu.outdated.ensureUnusedCapacity(gpa, 1);
+ try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1);
+ zcu.outdated.putAssumeCapacityNoClobber(unit, 0);
+ zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {});
}
- break :cau .{ cau, true };
+ break :unit .{ unit, true };
},
- else => cau: {
+ else => unit: {
const name = maybe_name.unwrap().?;
const fqn = try namespace.internFullyQualifiedName(ip, gpa, pt.tid, name);
- const cau, const nav = if (existing_cau) |cau_index| cau_nav: {
- const nav_index = ip.getCau(cau_index).owner.unwrap().nav;
- const nav = ip.getNav(nav_index);
- assert(nav.name == name);
- assert(nav.fqn == fqn);
- break :cau_nav .{ cau_index, nav_index };
- } else try ip.createPairedCauNav(gpa, pt.tid, name, fqn, tracked_inst, namespace_index, kind == .@"usingnamespace");
- const want_analysis = switch (kind) {
+ const nav = if (existing_unit) |eu|
+ eu.unwrap().nav_val
+ else
+ try ip.createDeclNav(gpa, pt.tid, name, fqn, tracked_inst, namespace_index, decl.kind == .@"usingnamespace");
+
+ const unit: AnalUnit = .wrap(.{ .nav_val = nav });
+
+ assert(ip.getNav(nav).name == name);
+ assert(ip.getNav(nav).fqn == fqn);
+
+ const want_analysis = switch (decl.kind) {
.@"comptime" => unreachable,
.@"usingnamespace" => a: {
if (comp.incremental) {
@panic("'usingnamespace' is not supported by incremental compilation");
}
- if (declaration.flags.is_pub) {
+ if (decl.is_pub) {
try namespace.pub_usingnamespace.append(gpa, nav);
} else {
try namespace.priv_usingnamespace.append(gpa, nav);
}
break :a true;
},
- .@"test" => a: {
- try namespace.other_decls.append(gpa, cau);
+ .unnamed_test, .@"test", .decltest => a: {
+ const is_named = decl.kind != .unnamed_test;
+ try namespace.test_decls.append(gpa, nav);
// TODO: incremental compilation!
// * remove from `test_functions` if no longer matching filter
// * add to `test_functions` if newly passing filter
@@ -2046,7 +2322,7 @@ const ScanDeclIter = struct {
// Perhaps we should add all test indiscriminately and filter at the end of the update.
if (!comp.config.is_test) break :a false;
if (file.mod != zcu.main_mod) break :a false;
- if (is_named_test and comp.test_filters.len > 0) {
+ if (is_named and comp.test_filters.len > 0) {
const fqn_slice = fqn.toSlice(ip);
for (comp.test_filters) |test_filter| {
if (std.mem.indexOf(u8, fqn_slice, test_filter) != null) break;
@@ -2055,8 +2331,8 @@ const ScanDeclIter = struct {
try zcu.test_functions.put(gpa, nav, {});
break :a true;
},
- .named => a: {
- if (declaration.flags.is_pub) {
+ .@"const", .@"var" => a: {
+ if (decl.is_pub) {
try namespace.pub_decls.putContext(gpa, nav, {}, .{ .zcu = zcu });
} else {
try namespace.priv_decls.putContext(gpa, nav, {}, .{ .zcu = zcu });
@@ -2064,23 +2340,23 @@ const ScanDeclIter = struct {
break :a false;
},
};
- break :cau .{ cau, want_analysis };
+ break :unit .{ unit, want_analysis };
},
};
- if (existing_cau == null and (want_analysis or declaration.flags.is_export)) {
+ if (existing_unit == null and (want_analysis or decl.linkage == .@"export")) {
log.debug(
- "scanDecl queue analyze_cau file='{s}' cau_index={d}",
- .{ namespace.fileScope(zcu).sub_file_path, cau },
+ "scanDecl queue analyze_comptime_unit file='{s}' unit={}",
+ .{ namespace.fileScope(zcu).sub_file_path, zcu.fmtAnalUnit(unit) },
);
- try comp.queueJob(.{ .analyze_cau = cau });
+ try comp.queueJob(.{ .analyze_comptime_unit = unit });
}
// TODO: we used to do line number updates here, but this is an inappropriate place for this logic to live.
}
};
-fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!Air {
+fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!Air {
const tracy = trace(@src());
defer tracy.end();
@@ -2097,26 +2373,19 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!
try zcu.analysis_in_progress.put(gpa, anal_unit, {});
errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
- func.setAnalysisState(ip, .analyzed);
+ func.setAnalyzed(ip);
if (func.analysisUnordered(ip).inferred_error_set) {
func.setResolvedErrorSet(ip, .none);
}
- // This is the `Cau` corresponding to the `declaration` instruction which the function or its generic owner originates from.
- const decl_cau = ip.getCau(cau: {
- const orig_nav = if (func.generic_owner == .none)
- func.owner_nav
- else
- zcu.funcInfo(func.generic_owner).owner_nav;
-
- break :cau ip.getNav(orig_nav).analysis_owner.unwrap().?;
- });
+ // This is the `Nau` corresponding to the `declaration` instruction which the function or its generic owner originates from.
+ const decl_nav = ip.getNav(if (func.generic_owner == .none)
+ func.owner_nav
+ else
+ zcu.funcInfo(func.generic_owner).owner_nav);
const func_nav = ip.getNav(func.owner_nav);
- const decl_prog_node = zcu.sema_prog_node.start(func_nav.fqn.toSlice(ip), 0);
- defer decl_prog_node.end();
-
zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
var analysis_arena = std.heap.ArenaAllocator.init(gpa);
@@ -2150,7 +2419,7 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!
// Every runtime function has a dependency on the source of the Decl it originates from.
// It also depends on the value of its owner Decl.
- try sema.declareDependency(.{ .src_hash = decl_cau.zir_index });
+ try sema.declareDependency(.{ .src_hash = decl_nav.analysis.?.zir_index });
try sema.declareDependency(.{ .nav_val = func.owner_nav });
if (func.analysisUnordered(ip).inferred_error_set) {
@@ -2170,11 +2439,11 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!
var inner_block: Sema.Block = .{
.parent = null,
.sema = &sema,
- .namespace = decl_cau.namespace,
+ .namespace = decl_nav.analysis.?.namespace,
.instructions = .{},
.inlining = null,
.is_comptime = false,
- .src_base_inst = decl_cau.zir_index,
+ .src_base_inst = decl_nav.analysis.?.zir_index,
.type_name_ctx = func_nav.fqn,
};
defer inner_block.instructions.deinit(gpa);
@@ -2476,14 +2745,14 @@ fn processExportsInner(
.nav => |nav_index| if (failed: {
const nav = ip.getNav(nav_index);
if (zcu.failed_codegen.contains(nav_index)) break :failed true;
- if (nav.analysis_owner.unwrap()) |cau| {
- const cau_unit = AnalUnit.wrap(.{ .cau = cau });
- if (zcu.failed_analysis.contains(cau_unit)) break :failed true;
- if (zcu.transitive_failed_analysis.contains(cau_unit)) break :failed true;
+ if (nav.analysis != null) {
+ const unit: AnalUnit = .wrap(.{ .nav_val = nav_index });
+ if (zcu.failed_analysis.contains(unit)) break :failed true;
+ if (zcu.transitive_failed_analysis.contains(unit)) break :failed true;
}
const val = switch (nav.status) {
- .unresolved => break :failed true,
- .resolved => |r| Value.fromInterned(r.val),
+ .unresolved, .type_resolved => break :failed true,
+ .fully_resolved => |r| Value.fromInterned(r.val),
};
// If the value is a function, we also need to check if that function succeeded analysis.
if (val.typeOf(zcu).zigTypeTag(zcu) == .@"fn") {
@@ -2527,15 +2796,14 @@ pub fn populateTestFunctions(
Zcu.Namespace.NameAdapter{ .zcu = zcu },
).?;
{
- // We have to call `ensureCauAnalyzed` here in case `builtin.test_functions`
+ // We have to call `ensureNavValUpToDate` here in case `builtin.test_functions`
// was not referenced by start code.
zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
defer {
zcu.sema_prog_node.end();
zcu.sema_prog_node = std.Progress.Node.none;
}
- const cau_index = ip.getNav(nav_index).analysis_owner.unwrap().?;
- pt.ensureCauAnalyzed(cau_index) catch |err| switch (err) {
+ pt.ensureNavValUpToDate(nav_index) catch |err| switch (err) {
error.AnalysisFail => return,
error.OutOfMemory => return error.OutOfMemory,
};
@@ -2556,8 +2824,7 @@ pub fn populateTestFunctions(
{
// The test declaration might have failed; if that's the case, just return, as we'll
// be emitting a compile error anyway.
- const cau = test_nav.analysis_owner.unwrap().?;
- const anal_unit: AnalUnit = .wrap(.{ .cau = cau });
+ const anal_unit: AnalUnit = .wrap(.{ .nav_val = test_nav_index });
if (zcu.failed_analysis.contains(anal_unit) or
zcu.transitive_failed_analysis.contains(anal_unit))
{
@@ -2682,8 +2949,8 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error
"unable to codegen: {s}",
.{@errorName(err)},
));
- if (nav.analysis_owner.unwrap()) |cau| {
- try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .cau = cau }));
+ if (nav.analysis != null) {
+ try zcu.retryable_failures.append(zcu.gpa, .wrap(.{ .nav_val = nav_index }));
} else {
// TODO: we don't have a way to indicate that this failure is retryable!
// Since these are really rare, we could as a cop-out retry the whole build next update.
@@ -3189,31 +3456,30 @@ pub fn getBuiltinNav(pt: Zcu.PerThread, name: []const u8) Allocator.Error!Intern
const builtin_str = try ip.getOrPutString(gpa, pt.tid, "builtin", .no_embedded_nulls);
const builtin_nav = std_namespace.pub_decls.getKeyAdapted(builtin_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse
@panic("lib/std.zig is corrupt and missing 'builtin'");
- pt.ensureCauAnalyzed(ip.getNav(builtin_nav).analysis_owner.unwrap().?) catch @panic("std.builtin is corrupt");
- const builtin_type = Type.fromInterned(ip.getNav(builtin_nav).status.resolved.val);
+ pt.ensureNavValUpToDate(builtin_nav) catch @panic("std.builtin is corrupt");
+ const builtin_type = Type.fromInterned(ip.getNav(builtin_nav).status.fully_resolved.val);
const builtin_namespace = zcu.namespacePtr(builtin_type.getNamespace(zcu).unwrap() orelse @panic("std.builtin is corrupt"));
const name_str = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls);
return builtin_namespace.pub_decls.getKeyAdapted(name_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse @panic("lib/std/builtin.zig is corrupt");
}
-pub fn navPtrType(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Allocator.Error!Type {
+pub fn navPtrType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Allocator.Error!Type {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- const r = ip.getNav(nav_index).status.resolved;
- const ty = Value.fromInterned(r.val).typeOf(zcu);
+ const ty, const alignment, const @"addrspace", const is_const = switch (ip.getNav(nav_id).status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const },
+ .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", zcu.navValIsConst(r.val) },
+ };
return pt.ptrType(.{
- .child = ty.toIntern(),
+ .child = ty,
.flags = .{
- .alignment = if (r.alignment == ty.abiAlignment(zcu))
+ .alignment = if (alignment == Type.fromInterned(ty).abiAlignment(zcu))
.none
else
- r.alignment,
- .address_space = r.@"addrspace",
- .is_const = switch (ip.indexToKey(r.val)) {
- .variable => false,
- .@"extern" => |e| e.is_const,
- else => true,
- },
+ alignment,
+ .address_space = @"addrspace",
+ .is_const = is_const,
},
});
}
@@ -3233,76 +3499,57 @@ pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error!
// TODO: this shouldn't need a `PerThread`! Fix the signature of `Type.abiAlignment`.
pub fn navAlignment(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) InternPool.Alignment {
const zcu = pt.zcu;
- const r = zcu.intern_pool.getNav(nav_index).status.resolved;
- if (r.alignment != .none) return r.alignment;
- return Value.fromInterned(r.val).typeOf(zcu).abiAlignment(zcu);
+ const ty: Type, const alignment = switch (zcu.intern_pool.getNav(nav_index).status) {
+ .unresolved => unreachable,
+ .type_resolved => |r| .{ .fromInterned(r.type), r.alignment },
+ .fully_resolved => |r| .{ Value.fromInterned(r.val).typeOf(zcu), r.alignment },
+ };
+ if (alignment != .none) return alignment;
+ return ty.abiAlignment(zcu);
}
/// Given a container type requiring resolution, ensures that it is up-to-date.
/// If not, the type is recreated at a new `InternPool.Index`.
/// The new index is returned. This is the same as the old index if the fields were up-to-date.
-/// If `already_updating` is set, assumes the type is already outdated and undergoing re-analysis rather than checking `zcu.outdated`.
-pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index, already_updating: bool) Zcu.SemaError!InternPool.Index {
+pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError!InternPool.Index {
const zcu = pt.zcu;
+ const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
+
+ const anal_unit: AnalUnit = .wrap(.{ .type = ty });
+ const outdated = zcu.outdated.swapRemove(anal_unit) or
+ zcu.potentially_outdated.swapRemove(anal_unit);
+
+ if (!outdated) return ty;
+
+ // We will recreate the type at a new `InternPool.Index`.
+
+ _ = zcu.outdated_ready.swapRemove(anal_unit);
+ try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty });
+
+ // Delete old state which is no longer in use. Technically, this is not necessary: these exports,
+ // references, etc, will be ignored because the type itself is unreferenced. However, it allows
+ // reusing the memory which is currently being used to track this state.
+ zcu.deleteUnitExports(anal_unit);
+ zcu.deleteUnitReferences(anal_unit);
+ if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
+ kv.value.destroy(gpa);
+ }
+ _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
+ zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
+
switch (ip.indexToKey(ty)) {
- .struct_type => |key| {
- const struct_obj = ip.loadStructType(ty);
- const outdated = already_updating or o: {
- const anal_unit = AnalUnit.wrap(.{ .cau = struct_obj.cau });
- const o = zcu.outdated.swapRemove(anal_unit) or
- zcu.potentially_outdated.swapRemove(anal_unit);
- if (o) {
- _ = zcu.outdated_ready.swapRemove(anal_unit);
- try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty });
- }
- break :o o;
- };
- if (!outdated) return ty;
- return pt.recreateStructType(key, struct_obj);
- },
- .union_type => |key| {
- const union_obj = ip.loadUnionType(ty);
- const outdated = already_updating or o: {
- const anal_unit = AnalUnit.wrap(.{ .cau = union_obj.cau });
- const o = zcu.outdated.swapRemove(anal_unit) or
- zcu.potentially_outdated.swapRemove(anal_unit);
- if (o) {
- _ = zcu.outdated_ready.swapRemove(anal_unit);
- try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty });
- }
- break :o o;
- };
- if (!outdated) return ty;
- return pt.recreateUnionType(key, union_obj);
- },
- .enum_type => |key| {
- const enum_obj = ip.loadEnumType(ty);
- const outdated = already_updating or o: {
- const anal_unit = AnalUnit.wrap(.{ .cau = enum_obj.cau.unwrap().? });
- const o = zcu.outdated.swapRemove(anal_unit) or
- zcu.potentially_outdated.swapRemove(anal_unit);
- if (o) {
- _ = zcu.outdated_ready.swapRemove(anal_unit);
- try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty });
- }
- break :o o;
- };
- if (!outdated) return ty;
- return pt.recreateEnumType(key, enum_obj);
- },
- .opaque_type => {
- assert(!already_updating);
- return ty;
- },
+ .struct_type => |key| return pt.recreateStructType(ty, key),
+ .union_type => |key| return pt.recreateUnionType(ty, key),
+ .enum_type => |key| return pt.recreateEnumType(ty, key),
else => unreachable,
}
}
fn recreateStructType(
pt: Zcu.PerThread,
+ old_ty: InternPool.Index,
full_key: InternPool.Key.NamespaceType,
- struct_obj: InternPool.LoadedStructType,
) Zcu.SemaError!InternPool.Index {
const zcu = pt.zcu;
const gpa = zcu.gpa;
@@ -3339,8 +3586,7 @@ fn recreateStructType(
if (captures_len != key.captures.owned.len) return error.AnalysisFail;
- // The old type will be unused, so drop its dependency information.
- ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .cau = struct_obj.cau }));
+ const struct_obj = ip.loadStructType(old_ty);
const wip_ty = switch (try ip.getStructType(gpa, pt.tid, .{
.layout = small.layout,
@@ -3362,17 +3608,16 @@ fn recreateStructType(
errdefer wip_ty.cancel(ip, pt.tid);
wip_ty.setName(ip, struct_obj.name);
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, key.zir_index, struct_obj.namespace, wip_ty.index);
try ip.addDependency(
gpa,
- AnalUnit.wrap(.{ .cau = new_cau_index }),
+ .wrap(.{ .type = wip_ty.index }),
.{ .src_hash = key.zir_index },
);
zcu.namespacePtr(struct_obj.namespace).owner_type = wip_ty.index;
// No need to re-scan the namespace -- `zirStructDecl` will ultimately do that if the type is still alive.
try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
- const new_ty = wip_ty.finish(ip, new_cau_index.toOptional(), struct_obj.namespace);
+ const new_ty = wip_ty.finish(ip, struct_obj.namespace);
if (inst_info.inst == .main_struct_inst) {
// This is the root type of a file! Update the reference.
zcu.setFileRootType(inst_info.file, new_ty);
@@ -3382,8 +3627,8 @@ fn recreateStructType(
fn recreateUnionType(
pt: Zcu.PerThread,
+ old_ty: InternPool.Index,
full_key: InternPool.Key.NamespaceType,
- union_obj: InternPool.LoadedUnionType,
) Zcu.SemaError!InternPool.Index {
const zcu = pt.zcu;
const gpa = zcu.gpa;
@@ -3422,8 +3667,7 @@ fn recreateUnionType(
if (captures_len != key.captures.owned.len) return error.AnalysisFail;
- // The old type will be unused, so drop its dependency information.
- ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .cau = union_obj.cau }));
+ const union_obj = ip.loadUnionType(old_ty);
const namespace_index = union_obj.namespace;
@@ -3460,22 +3704,21 @@ fn recreateUnionType(
errdefer wip_ty.cancel(ip, pt.tid);
wip_ty.setName(ip, union_obj.name);
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, key.zir_index, namespace_index, wip_ty.index);
try ip.addDependency(
gpa,
- AnalUnit.wrap(.{ .cau = new_cau_index }),
+ .wrap(.{ .type = wip_ty.index }),
.{ .src_hash = key.zir_index },
);
zcu.namespacePtr(namespace_index).owner_type = wip_ty.index;
// No need to re-scan the namespace -- `zirUnionDecl` will ultimately do that if the type is still alive.
try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
- return wip_ty.finish(ip, new_cau_index.toOptional(), namespace_index);
+ return wip_ty.finish(ip, namespace_index);
}
fn recreateEnumType(
pt: Zcu.PerThread,
+ old_ty: InternPool.Index,
full_key: InternPool.Key.NamespaceType,
- enum_obj: InternPool.LoadedEnumType,
) Zcu.SemaError!InternPool.Index {
const zcu = pt.zcu;
const gpa = zcu.gpa;
@@ -3544,8 +3787,7 @@ fn recreateEnumType(
if (bag != 0) break true;
} else false;
- // The old type will be unused, so drop its dependency information.
- ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .cau = enum_obj.cau.unwrap().? }));
+ const enum_obj = ip.loadEnumType(old_ty);
const namespace_index = enum_obj.namespace;
@@ -3571,12 +3813,10 @@ fn recreateEnumType(
wip_ty.setName(ip, enum_obj.name);
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, key.zir_index, namespace_index, wip_ty.index);
-
zcu.namespacePtr(namespace_index).owner_type = wip_ty.index;
// No need to re-scan the namespace -- `zirEnumDecl` will ultimately do that if the type is still alive.
- wip_ty.prepare(ip, new_cau_index, namespace_index);
+ wip_ty.prepare(ip, namespace_index);
done = true;
Sema.resolveDeclaredEnum(
@@ -3586,7 +3826,6 @@ fn recreateEnumType(
key.zir_index,
namespace_index,
enum_obj.name,
- new_cau_index,
small,
body,
tag_type_ref,
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index ccdf38a474..49961042bc 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -3218,15 +3218,7 @@ fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) Inn
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- // check if decl is an alias to a function, in which case we
- // want to lower the actual decl, rather than the alias itself.
- const owner_nav = switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) {
- .func => |function| function.owner_nav,
- .variable => |variable| variable.owner_nav,
- .@"extern" => |@"extern"| @"extern".owner_nav,
- else => nav_index,
- };
- const nav_ty = ip.getNav(owner_nav).typeOf(ip);
+ const nav_ty = ip.getNav(nav_index).typeOf(ip);
if (!ip.isFunctionType(nav_ty) and !Type.fromInterned(nav_ty).hasRuntimeBitsIgnoreComptime(zcu)) {
return .{ .imm32 = 0xaaaaaaaa };
}
diff --git a/src/codegen.zig b/src/codegen.zig
index 2b179979f0..7b607f13f9 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -817,7 +817,7 @@ fn genNavRef(
pt: Zcu.PerThread,
src_loc: Zcu.LazySrcLoc,
val: Value,
- ref_nav_index: InternPool.Nav.Index,
+ nav_index: InternPool.Nav.Index,
target: std.Target,
) CodeGenError!GenResult {
const zcu = pt.zcu;
@@ -851,14 +851,15 @@ fn genNavRef(
}
}
- const nav_index, const is_extern, const lib_name, const is_threadlocal = switch (ip.indexToKey(zcu.navValue(ref_nav_index).toIntern())) {
- .func => |func| .{ func.owner_nav, false, .none, false },
- .variable => |variable| .{ variable.owner_nav, false, variable.lib_name, variable.is_threadlocal },
- .@"extern" => |@"extern"| .{ @"extern".owner_nav, true, @"extern".lib_name, @"extern".is_threadlocal },
- else => .{ ref_nav_index, false, .none, false },
- };
+ const nav = ip.getNav(nav_index);
+
+ const is_extern, const lib_name, const is_threadlocal = if (nav.getExtern(ip)) |e|
+ .{ true, e.lib_name, e.is_threadlocal }
+ else
+ .{ false, .none, nav.isThreadlocal(ip) };
+
const single_threaded = zcu.navFileScope(nav_index).mod.single_threaded;
- const name = ip.getNav(nav_index).name;
+ const name = nav.name;
if (lf.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?;
if (is_extern) {
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index c3e3c7fbdc..2368f202da 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -770,11 +770,14 @@ pub const DeclGen = struct {
const ctype_pool = &dg.ctype_pool;
// Chase function values in order to be able to reference the original function.
- const owner_nav = switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) {
- .variable => |variable| variable.owner_nav,
- .func => |func| func.owner_nav,
- .@"extern" => |@"extern"| @"extern".owner_nav,
- else => nav_index,
+ const owner_nav = switch (ip.getNav(nav_index).status) {
+ .unresolved => unreachable,
+ .type_resolved => nav_index, // this can't be an extern or a function
+ .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
+ .func => |f| f.owner_nav,
+ .@"extern" => |e| e.owner_nav,
+ else => nav_index,
+ },
};
// Render an undefined pointer if we have a pointer to a zero-bit or comptime type.
@@ -2237,7 +2240,7 @@ pub const DeclGen = struct {
Type.fromInterned(nav.typeOf(ip)),
.{ .nav = nav_index },
CQualifiers.init(.{ .@"const" = flags.is_const }),
- nav.status.resolved.alignment,
+ nav.getAlignment(),
.complete,
);
try fwd.writeAll(";\n");
@@ -2246,19 +2249,19 @@ pub const DeclGen = struct {
fn renderNavName(dg: *DeclGen, writer: anytype, nav_index: InternPool.Nav.Index) !void {
const zcu = dg.pt.zcu;
const ip = &zcu.intern_pool;
- switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) {
- .@"extern" => |@"extern"| try writer.print("{ }", .{
+ const nav = ip.getNav(nav_index);
+ if (nav.getExtern(ip)) |@"extern"| {
+ try writer.print("{ }", .{
fmtIdent(ip.getNav(@"extern".owner_nav).name.toSlice(ip)),
- }),
- else => {
- // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
- // expand to 3x the length of its input, but let's cut it off at a much shorter limit.
- const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);
- try writer.print("{}__{d}", .{
- fmtIdent(fqn_slice[0..@min(fqn_slice.len, 100)]),
- @intFromEnum(nav_index),
- });
- },
+ });
+ } else {
+ // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
+ // expand to 3x the length of its input, but let's cut it off at a much shorter limit.
+ const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);
+ try writer.print("{}__{d}", .{
+ fmtIdent(fqn_slice[0..@min(fqn_slice.len, 100)]),
+ @intFromEnum(nav_index),
+ });
}
}
@@ -2826,7 +2829,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
const fwd = o.dg.fwdDeclWriter();
try fwd.print("static zig_{s} ", .{@tagName(key)});
- try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).status.resolved.alignment, .forward, .{
+ try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).getAlignment(), .forward, .{
.fmt_ctype_pool_string = fn_name,
});
try fwd.writeAll(";\n");
@@ -2867,13 +2870,13 @@ pub fn genFunc(f: *Function) !void {
try o.dg.renderFunctionSignature(
fwd,
nav_val,
- nav.status.resolved.alignment,
+ nav.status.fully_resolved.alignment,
.forward,
.{ .nav = nav_index },
);
try fwd.writeAll(";\n");
- if (nav.status.resolved.@"linksection".toSlice(ip)) |s|
+ if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s|
try o.writer().print("zig_linksection_fn({s}) ", .{fmtStringLiteral(s, null)});
try o.dg.renderFunctionSignature(
o.writer(),
@@ -2952,7 +2955,7 @@ pub fn genDecl(o: *Object) !void {
const nav_ty = Type.fromInterned(nav.typeOf(ip));
if (!nav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return;
- switch (ip.indexToKey(nav.status.resolved.val)) {
+ switch (ip.indexToKey(nav.status.fully_resolved.val)) {
.@"extern" => |@"extern"| {
if (!ip.isFunctionType(nav_ty.toIntern())) return o.dg.renderFwdDecl(o.dg.pass.nav, .{
.is_extern = true,
@@ -2965,8 +2968,8 @@ pub fn genDecl(o: *Object) !void {
try fwd.writeAll("zig_extern ");
try o.dg.renderFunctionSignature(
fwd,
- Value.fromInterned(nav.status.resolved.val),
- nav.status.resolved.alignment,
+ Value.fromInterned(nav.status.fully_resolved.val),
+ nav.status.fully_resolved.alignment,
.forward,
.{ .@"export" = .{
.main_name = nav.name,
@@ -2985,14 +2988,14 @@ pub fn genDecl(o: *Object) !void {
const w = o.writer();
if (variable.is_weak_linkage) try w.writeAll("zig_weak_linkage ");
if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal ");
- if (nav.status.resolved.@"linksection".toSlice(&zcu.intern_pool)) |s|
+ if (nav.status.fully_resolved.@"linksection".toSlice(&zcu.intern_pool)) |s|
try w.print("zig_linksection({s}) ", .{fmtStringLiteral(s, null)});
try o.dg.renderTypeAndName(
w,
nav_ty,
.{ .nav = o.dg.pass.nav },
.{},
- nav.status.resolved.alignment,
+ nav.status.fully_resolved.alignment,
.complete,
);
try w.writeAll(" = ");
@@ -3002,10 +3005,10 @@ pub fn genDecl(o: *Object) !void {
},
else => try genDeclValue(
o,
- Value.fromInterned(nav.status.resolved.val),
+ Value.fromInterned(nav.status.fully_resolved.val),
.{ .nav = o.dg.pass.nav },
- nav.status.resolved.alignment,
- nav.status.resolved.@"linksection",
+ nav.status.fully_resolved.alignment,
+ nav.status.fully_resolved.@"linksection",
),
}
}
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index b94ea07995..5b36644019 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -1476,7 +1476,7 @@ pub const Object = struct {
} }, &o.builder);
}
- if (nav.status.resolved.@"linksection".toSlice(ip)) |section|
+ if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |section|
function_index.setSection(try o.builder.string(section), &o.builder);
var deinit_wip = true;
@@ -1684,7 +1684,7 @@ pub const Object = struct {
const file = try o.getDebugFile(file_scope);
const line_number = zcu.navSrcLine(func.owner_nav) + 1;
- const is_internal_linkage = ip.indexToKey(nav.status.resolved.val) != .@"extern";
+ const is_internal_linkage = ip.indexToKey(nav.status.fully_resolved.val) != .@"extern";
const debug_decl_type = try o.lowerDebugType(fn_ty);
const subprogram = try o.builder.debugSubprogram(
@@ -2928,9 +2928,7 @@ pub const Object = struct {
const gpa = o.gpa;
const nav = ip.getNav(nav_index);
const owner_mod = zcu.navFileScope(nav_index).mod;
- const resolved = nav.status.resolved;
- const val = Value.fromInterned(resolved.val);
- const ty = val.typeOf(zcu);
+ const ty: Type = .fromInterned(nav.typeOf(ip));
const gop = try o.nav_map.getOrPut(gpa, nav_index);
if (gop.found_existing) return gop.value_ptr.ptr(&o.builder).kind.function;
@@ -2938,15 +2936,14 @@ pub const Object = struct {
const target = owner_mod.resolved_target.result;
const sret = firstParamSRet(fn_info, zcu, target);
- const is_extern, const lib_name = switch (ip.indexToKey(val.toIntern())) {
- .variable => |variable| .{ false, variable.lib_name },
- .@"extern" => |@"extern"| .{ true, @"extern".lib_name },
- else => .{ false, .none },
- };
+ const is_extern, const lib_name = if (nav.getExtern(ip)) |@"extern"|
+ .{ true, @"extern".lib_name }
+ else
+ .{ false, .none };
const function_index = try o.builder.addFunction(
try o.lowerType(ty),
try o.builder.strtabString((if (is_extern) nav.name else nav.fqn).toSlice(ip)),
- toLlvmAddressSpace(resolved.@"addrspace", target),
+ toLlvmAddressSpace(nav.getAddrspace(), target),
);
gop.value_ptr.* = function_index.ptrConst(&o.builder).global;
@@ -3064,8 +3061,8 @@ pub const Object = struct {
}
}
- if (resolved.alignment != .none)
- function_index.setAlignment(resolved.alignment.toLlvm(), &o.builder);
+ if (nav.getAlignment() != .none)
+ function_index.setAlignment(nav.getAlignment().toLlvm(), &o.builder);
// Function attributes that are independent of analysis results of the function body.
try o.addCommonFnAttributes(
@@ -3250,17 +3247,21 @@ pub const Object = struct {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
- const resolved = nav.status.resolved;
- const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (ip.indexToKey(resolved.val)) {
- .variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
- .@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
- else => .{ false, false, false, false },
+ const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (nav.status) {
+ .unresolved => unreachable,
+ .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
+ .variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
+ .@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
+ else => .{ false, false, false, false },
+ },
+ // This means it's a source declaration which is not `extern`!
+ .type_resolved => |r| .{ false, r.is_threadlocal, false, false },
};
const variable_index = try o.builder.addVariable(
try o.builder.strtabString((if (is_extern) nav.name else nav.fqn).toSlice(ip)),
try o.lowerType(Type.fromInterned(nav.typeOf(ip))),
- toLlvmGlobalAddressSpace(resolved.@"addrspace", zcu.getTarget()),
+ toLlvmGlobalAddressSpace(nav.getAddrspace(), zcu.getTarget()),
);
gop.value_ptr.* = variable_index.ptrConst(&o.builder).global;
@@ -4529,20 +4530,10 @@ pub const Object = struct {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- // In the case of something like:
- // fn foo() void {}
- // const bar = foo;
- // ... &bar;
- // `bar` is just an alias and we actually want to lower a reference to `foo`.
- const owner_nav_index = switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) {
- .func => |func| func.owner_nav,
- .@"extern" => |@"extern"| @"extern".owner_nav,
- else => nav_index,
- };
- const owner_nav = ip.getNav(owner_nav_index);
+ const nav = ip.getNav(nav_index);
- const nav_ty = Type.fromInterned(owner_nav.typeOf(ip));
- const ptr_ty = try pt.navPtrType(owner_nav_index);
+ const nav_ty = Type.fromInterned(nav.typeOf(ip));
+ const ptr_ty = try pt.navPtrType(nav_index);
const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn";
if ((!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) or
@@ -4552,13 +4543,13 @@ pub const Object = struct {
}
const llvm_global = if (is_fn_body)
- (try o.resolveLlvmFunction(owner_nav_index)).ptrConst(&o.builder).global
+ (try o.resolveLlvmFunction(nav_index)).ptrConst(&o.builder).global
else
- (try o.resolveGlobalNav(owner_nav_index)).ptrConst(&o.builder).global;
+ (try o.resolveGlobalNav(nav_index)).ptrConst(&o.builder).global;
const llvm_val = try o.builder.convConst(
llvm_global.toConst(),
- try o.builder.ptrType(toLlvmAddressSpace(owner_nav.status.resolved.@"addrspace", zcu.getTarget())),
+ try o.builder.ptrType(toLlvmAddressSpace(nav.getAddrspace(), zcu.getTarget())),
);
return o.builder.convConst(llvm_val, try o.lowerType(ptr_ty));
@@ -4800,10 +4791,10 @@ pub const NavGen = struct {
const ip = &zcu.intern_pool;
const nav_index = ng.nav_index;
const nav = ip.getNav(nav_index);
- const resolved = nav.status.resolved;
+ const resolved = nav.status.fully_resolved;
const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
- .variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav },
+ .variable => |variable| .{ false, .none, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, false, true, resolved.val, nav_index },
};
@@ -5766,7 +5757,7 @@ pub const FuncGen = struct {
const msg_nav_index = zcu.panic_messages[@intFromEnum(panic_id)].unwrap().?;
const msg_nav = ip.getNav(msg_nav_index);
const msg_len = Type.fromInterned(msg_nav.typeOf(ip)).childType(zcu).arrayLen(zcu);
- const msg_ptr = try o.lowerValue(msg_nav.status.resolved.val);
+ const msg_ptr = try o.lowerValue(msg_nav.status.fully_resolved.val);
const null_opt_addr_global = try fg.resolveNullOptUsize();
const target = zcu.getTarget();
const llvm_usize = try o.lowerType(Type.usize);
diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig
index 16b4a6dfbd..91e2c4f7e7 100644
--- a/src/codegen/spirv.zig
+++ b/src/codegen/spirv.zig
@@ -268,7 +268,7 @@ pub const Object = struct {
// TODO: Extern fn?
const kind: SpvModule.Decl.Kind = if (ip.isFunctionType(nav.typeOf(ip)))
.func
- else switch (nav.status.resolved.@"addrspace") {
+ else switch (nav.getAddrspace()) {
.generic => .invocation_global,
else => .global,
};
@@ -1279,17 +1279,20 @@ const NavGen = struct {
const ip = &zcu.intern_pool;
const ty_id = try self.resolveType(ty, .direct);
const nav = ip.getNav(nav_index);
- const nav_val = zcu.navValue(nav_index);
- const nav_ty = nav_val.typeOf(zcu);
-
- switch (ip.indexToKey(nav_val.toIntern())) {
- .func => {
- // TODO: Properly lower function pointers. For now we are going to hack around it and
- // just generate an empty pointer. Function pointers are represented by a pointer to usize.
- return try self.spv.constUndef(ty_id);
+ const nav_ty: Type = .fromInterned(nav.typeOf(ip));
+
+ switch (nav.status) {
+ .unresolved => unreachable,
+ .type_resolved => {}, // this is not a function or extern
+ .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
+ .func => {
+ // TODO: Properly lower function pointers. For now we are going to hack around it and
+ // just generate an empty pointer. Function pointers are represented by a pointer to usize.
+ return try self.spv.constUndef(ty_id);
+ },
+ .@"extern" => if (ip.isFunctionType(nav_ty.toIntern())) @panic("TODO"),
+ else => {},
},
- .@"extern" => assert(!ip.isFunctionType(nav_ty.toIntern())), // TODO
- else => {},
}
if (!nav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) {
@@ -1305,7 +1308,7 @@ const NavGen = struct {
.global, .invocation_global => spv_decl.result_id,
};
- const storage_class = self.spvStorageClass(nav.status.resolved.@"addrspace");
+ const storage_class = self.spvStorageClass(nav.getAddrspace());
try self.addFunctionDep(spv_decl_index, storage_class);
const decl_ptr_ty_id = try self.ptrType(nav_ty, storage_class);
@@ -3182,7 +3185,7 @@ const NavGen = struct {
};
assert(maybe_init_val == null); // TODO
- const storage_class = self.spvStorageClass(nav.status.resolved.@"addrspace");
+ const storage_class = self.spvStorageClass(nav.getAddrspace());
assert(storage_class != .Generic); // These should be instance globals
const ptr_ty_id = try self.ptrType(ty, storage_class);
diff --git a/src/link.zig b/src/link.zig
index f0f6e9b01d..58c5cf35af 100644
--- a/src/link.zig
+++ b/src/link.zig
@@ -692,7 +692,7 @@ pub const File = struct {
/// May be called before or after updateExports for any given Nav.
pub fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) UpdateNavError!void {
const nav = pt.zcu.intern_pool.getNav(nav_index);
- assert(nav.status == .resolved);
+ assert(nav.status == .fully_resolved);
switch (base.tag) {
inline else => |tag| {
dev.check(tag.devFeature());
diff --git a/src/link/C.zig b/src/link/C.zig
index f42a467ee8..d84f29eb4b 100644
--- a/src/link/C.zig
+++ b/src/link/C.zig
@@ -217,7 +217,7 @@ pub fn updateFunc(
.mod = zcu.navFileScope(func.owner_nav).mod,
.error_msg = null,
.pass = .{ .nav = func.owner_nav },
- .is_naked_fn = zcu.navValue(func.owner_nav).typeOf(zcu).fnCallingConvention(zcu) == .naked,
+ .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked,
.fwd_decl = fwd_decl.toManaged(gpa),
.ctype_pool = ctype_pool.*,
.scratch = .{},
@@ -320,11 +320,11 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
- const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {
+ const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
.func => return,
.@"extern" => .none,
.variable => |variable| variable.init,
- else => nav.status.resolved.val,
+ else => nav.status.fully_resolved.val,
};
if (nav_init != .none and !Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) return;
@@ -499,7 +499,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
av_block,
self.exported_navs.getPtr(nav),
export_names,
- if (ip.indexToKey(zcu.navValue(nav).toIntern()) == .@"extern")
+ if (ip.getNav(nav).getExtern(ip) != null)
ip.getNav(nav).name.toOptional()
else
.none,
@@ -544,13 +544,11 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
},
self.getString(av_block.code),
);
- for (self.navs.keys(), self.navs.values()) |nav, av_block| f.appendCodeAssumeCapacity(
- if (self.exported_navs.contains(nav)) .default else switch (ip.indexToKey(zcu.navValue(nav).toIntern())) {
- .@"extern" => .zig_extern,
- else => .static,
- },
- self.getString(av_block.code),
- );
+ for (self.navs.keys(), self.navs.values()) |nav, av_block| f.appendCodeAssumeCapacity(storage: {
+ if (self.exported_navs.contains(nav)) break :storage .default;
+ if (ip.getNav(nav).getExtern(ip) != null) break :storage .zig_extern;
+ break :storage .static;
+ }, self.getString(av_block.code));
const file = self.base.file.?;
try file.setEndPos(f.file_size);
diff --git a/src/link/Coff.zig b/src/link/Coff.zig
index e5b717ce1b..f13863cfb9 100644
--- a/src/link/Coff.zig
+++ b/src/link/Coff.zig
@@ -1110,6 +1110,8 @@ pub fn updateFunc(coff: *Coff, pt: Zcu.PerThread, func_index: InternPool.Index,
const atom_index = try coff.getOrCreateAtomForNav(func.owner_nav);
coff.freeRelocations(atom_index);
+ coff.navs.getPtr(func.owner_nav).?.section = coff.text_section_index.?;
+
var code_buffer = std.ArrayList(u8).init(gpa);
defer code_buffer.deinit();
@@ -1223,6 +1225,8 @@ pub fn updateNav(
coff.freeRelocations(atom_index);
const atom = coff.getAtom(atom_index);
+ coff.navs.getPtr(nav_index).?.section = coff.getNavOutputSection(nav_index);
+
var code_buffer = std.ArrayList(u8).init(gpa);
defer code_buffer.deinit();
@@ -1342,7 +1346,8 @@ pub fn getOrCreateAtomForNav(coff: *Coff, nav_index: InternPool.Nav.Index) !Atom
if (!gop.found_existing) {
gop.value_ptr.* = .{
.atom = try coff.createAtom(),
- .section = coff.getNavOutputSection(nav_index),
+ // If necessary, this will be modified by `updateNav` or `updateFunc`.
+ .section = coff.rdata_section_index.?,
.exports = .{},
};
}
@@ -1355,7 +1360,7 @@ fn getNavOutputSection(coff: *Coff, nav_index: InternPool.Nav.Index) u16 {
const nav = ip.getNav(nav_index);
const ty = Type.fromInterned(nav.typeOf(ip));
const zig_ty = ty.zigTypeTag(zcu);
- const val = Value.fromInterned(nav.status.resolved.val);
+ const val = Value.fromInterned(nav.status.fully_resolved.val);
const index: u16 = blk: {
if (val.isUndefDeep(zcu)) {
// TODO in release-fast and release-small, we should put undef in .bss
@@ -2348,10 +2353,10 @@ pub fn getNavVAddr(
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
log.debug("getNavVAddr {}({d})", .{ nav.fqn.fmt(ip), nav_index });
- const sym_index = switch (ip.indexToKey(nav.status.resolved.val)) {
- .@"extern" => |@"extern"| try coff.getGlobalSymbol(nav.name.toSlice(ip), @"extern".lib_name.toSlice(ip)),
- else => coff.getAtom(try coff.getOrCreateAtomForNav(nav_index)).getSymbolIndex().?,
- };
+ const sym_index = if (nav.getExtern(ip)) |e|
+ try coff.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip))
+ else
+ coff.getAtom(try coff.getOrCreateAtomForNav(nav_index)).getSymbolIndex().?;
const atom_index = coff.getAtomIndexForSymbol(.{
.sym_index = reloc_info.parent.atom_index,
.file = null,
diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig
index 426b9d21c9..5af6f80410 100644
--- a/src/link/Dwarf.zig
+++ b/src/link/Dwarf.zig
@@ -2259,24 +2259,13 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
switch (ip.indexToKey(nav_val.toIntern())) {
else => {
assert(file.zir_loaded);
- const decl = file.zir.getDeclaration(inst_info.inst)[0];
+ const decl = file.zir.getDeclaration(inst_info.inst);
- const parent_type, const accessibility: u8 = if (nav.analysis_owner.unwrap()) |cau| parent: {
- const parent_namespace_ptr = ip.namespacePtr(ip.getCau(cau).namespace);
+ const parent_type, const accessibility: u8 = if (nav.analysis) |a| parent: {
+ const parent_namespace_ptr = ip.namespacePtr(a.namespace);
break :parent .{
parent_namespace_ptr.owner_type,
- switch (decl.name) {
- .@"comptime",
- .@"usingnamespace",
- .unnamed_test,
- => DW.ACCESS.private,
- _ => if (decl.name.isNamedTest(file.zir))
- DW.ACCESS.private
- else if (decl.flags.is_pub)
- DW.ACCESS.public
- else
- DW.ACCESS.private,
- },
+ if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private,
};
} else .{ zcu.fileRootType(inst_info.file), DW.ACCESS.private };
@@ -2292,7 +2281,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
const nav_ty = nav_val.typeOf(zcu);
const nav_ty_reloc_index = try wip_nav.refForward();
try wip_nav.infoExprloc(.{ .addr = .{ .sym = sym_index } });
- try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
+ try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse
nav_ty.abiAlignment(zcu).toByteUnits().?);
try diw.writeByte(@intFromBool(false));
wip_nav.finishForward(nav_ty_reloc_index);
@@ -2301,24 +2290,13 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
},
.variable => |variable| {
assert(file.zir_loaded);
- const decl = file.zir.getDeclaration(inst_info.inst)[0];
+ const decl = file.zir.getDeclaration(inst_info.inst);
- const parent_type, const accessibility: u8 = if (nav.analysis_owner.unwrap()) |cau| parent: {
- const parent_namespace_ptr = ip.namespacePtr(ip.getCau(cau).namespace);
+ const parent_type, const accessibility: u8 = if (nav.analysis) |a| parent: {
+ const parent_namespace_ptr = ip.namespacePtr(a.namespace);
break :parent .{
parent_namespace_ptr.owner_type,
- switch (decl.name) {
- .@"comptime",
- .@"usingnamespace",
- .unnamed_test,
- => DW.ACCESS.private,
- _ => if (decl.name.isNamedTest(file.zir))
- DW.ACCESS.private
- else if (decl.flags.is_pub)
- DW.ACCESS.public
- else
- DW.ACCESS.private,
- },
+ if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private,
};
} else .{ zcu.fileRootType(inst_info.file), DW.ACCESS.private };
@@ -2335,30 +2313,19 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
try wip_nav.refType(ty);
const addr: Loc = .{ .addr = .{ .sym = sym_index } };
try wip_nav.infoExprloc(if (variable.is_threadlocal) .{ .form_tls_address = &addr } else addr);
- try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
+ try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse
ty.abiAlignment(zcu).toByteUnits().?);
try diw.writeByte(@intFromBool(false));
},
.func => |func| {
assert(file.zir_loaded);
- const decl = file.zir.getDeclaration(inst_info.inst)[0];
+ const decl = file.zir.getDeclaration(inst_info.inst);
- const parent_type, const accessibility: u8 = if (nav.analysis_owner.unwrap()) |cau| parent: {
- const parent_namespace_ptr = ip.namespacePtr(ip.getCau(cau).namespace);
+ const parent_type, const accessibility: u8 = if (nav.analysis) |a| parent: {
+ const parent_namespace_ptr = ip.namespacePtr(a.namespace);
break :parent .{
parent_namespace_ptr.owner_type,
- switch (decl.name) {
- .@"comptime",
- .@"usingnamespace",
- .unnamed_test,
- => DW.ACCESS.private,
- _ => if (decl.name.isNamedTest(file.zir))
- DW.ACCESS.private
- else if (decl.flags.is_pub)
- DW.ACCESS.public
- else
- DW.ACCESS.private,
- },
+ if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private,
};
} else .{ zcu.fileRootType(inst_info.file), DW.ACCESS.private };
@@ -2421,7 +2388,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
wip_nav.func_high_pc = @intCast(wip_nav.debug_info.items.len);
try diw.writeInt(u32, 0, dwarf.endian);
const target = file.mod.resolved_target.result;
- try uleb128(diw, switch (nav.status.resolved.alignment) {
+ try uleb128(diw, switch (nav.status.fully_resolved.alignment) {
.none => target_info.defaultFunctionAlignment(target),
else => |a| a.maxStrict(target_info.minFunctionAlignment(target)),
}.toByteUnits().?);
@@ -2585,23 +2552,22 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
const inst_info = nav.srcInst(ip).resolveFull(ip).?;
const file = zcu.fileByIndex(inst_info.file);
assert(file.zir_loaded);
- const decl = file.zir.getDeclaration(inst_info.inst)[0];
+ const decl = file.zir.getDeclaration(inst_info.inst);
- const is_test = switch (decl.name) {
- .unnamed_test => true,
- .@"comptime", .@"usingnamespace" => false,
- _ => decl.name.isNamedTest(file.zir),
+ const is_test = switch (decl.kind) {
+ .unnamed_test, .@"test", .decltest => true,
+ .@"comptime", .@"usingnamespace", .@"const", .@"var" => false,
};
if (is_test) {
// This isn't actually a comptime Nav! It's a test, so it'll definitely never be referenced at comptime.
return;
}
- const parent_type, const accessibility: u8 = if (nav.analysis_owner.unwrap()) |cau| parent: {
- const parent_namespace_ptr = ip.namespacePtr(ip.getCau(cau).namespace);
+ const parent_type, const accessibility: u8 = if (nav.analysis) |a| parent: {
+ const parent_namespace_ptr = ip.namespacePtr(a.namespace);
break :parent .{
parent_namespace_ptr.owner_type,
- if (decl.flags.is_pub) DW.ACCESS.public else DW.ACCESS.private,
+ if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private,
};
} else .{ zcu.fileRootType(inst_info.file), DW.ACCESS.private };
@@ -2986,7 +2952,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
const nav_ty = nav_val.typeOf(zcu);
try wip_nav.refType(nav_ty);
try wip_nav.blockValue(nav_src_loc, nav_val);
- try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
+ try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse
nav_ty.abiAlignment(zcu).toByteUnits().?);
try diw.writeByte(@intFromBool(false));
},
@@ -3011,7 +2977,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
try wip_nav.strp(nav.name.toSlice(ip));
try wip_nav.strp(nav.fqn.toSlice(ip));
const nav_ty_reloc_index = try wip_nav.refForward();
- try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
+ try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse
nav_ty.abiAlignment(zcu).toByteUnits().?);
try diw.writeByte(@intFromBool(false));
if (has_runtime_bits) try wip_nav.blockValue(nav_src_loc, nav_val);
@@ -4198,9 +4164,7 @@ pub fn updateNavLineNumber(dwarf: *Dwarf, zcu: *Zcu, nav_index: InternPool.Nav.I
assert(inst_info.inst != .main_struct_inst);
const file = zcu.fileByIndex(inst_info.file);
- const inst = file.zir.instructions.get(@intFromEnum(inst_info.inst));
- assert(inst.tag == .declaration);
- const line = file.zir.extraData(Zir.Inst.Declaration, inst.data.declaration.payload_index).data.src_line;
+ const line = file.zir.getDeclaration(inst_info.inst).src_line;
var line_buf: [4]u8 = undefined;
std.mem.writeInt(u32, &line_buf, line, dwarf.endian);
diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig
index fd6eceb556..5a2a7a8009 100644
--- a/src/link/Elf/ZigObject.zig
+++ b/src/link/Elf/ZigObject.zig
@@ -925,14 +925,11 @@ pub fn getNavVAddr(
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
log.debug("getNavVAddr {}({d})", .{ nav.fqn.fmt(ip), nav_index });
- const this_sym_index = switch (ip.indexToKey(nav.status.resolved.val)) {
- .@"extern" => |@"extern"| try self.getGlobalSymbol(
- elf_file,
- nav.name.toSlice(ip),
- @"extern".lib_name.toSlice(ip),
- ),
- else => try self.getOrCreateMetadataForNav(zcu, nav_index),
- };
+ const this_sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol(
+ elf_file,
+ nav.name.toSlice(ip),
+ @"extern".lib_name.toSlice(ip),
+ ) else try self.getOrCreateMetadataForNav(zcu, nav_index);
const this_sym = self.symbol(this_sym_index);
const vaddr = this_sym.address(.{}, elf_file);
switch (reloc_info.parent) {
@@ -1107,15 +1104,13 @@ pub fn freeNav(self: *ZigObject, elf_file: *Elf, nav_index: InternPool.Nav.Index
pub fn getOrCreateMetadataForNav(self: *ZigObject, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index {
const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
const gop = try self.navs.getOrPut(gpa, nav_index);
if (!gop.found_existing) {
const symbol_index = try self.newSymbolWithAtom(gpa, 0);
- const nav_val = Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.resolved.val);
const sym = self.symbol(symbol_index);
- if (nav_val.getVariable(zcu)) |variable| {
- if (variable.is_threadlocal and zcu.comp.config.any_non_single_threaded) {
- sym.flags.is_tls = true;
- }
+ if (ip.getNav(nav_index).isThreadlocal(ip) and zcu.comp.config.any_non_single_threaded) {
+ sym.flags.is_tls = true;
}
gop.value_ptr.* = .{ .symbol_index = symbol_index };
}
@@ -1547,7 +1542,7 @@ pub fn updateNav(
log.debug("updateNav {}({d})", .{ nav.fqn.fmt(ip), nav_index });
- const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {
+ const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
.func => .none,
.variable => |variable| variable.init,
.@"extern" => |@"extern"| {
@@ -1560,7 +1555,7 @@ pub fn updateNav(
self.symbol(sym_index).flags.is_extern_ptr = true;
return;
},
- else => nav.status.resolved.val,
+ else => nav.status.fully_resolved.val,
};
if (nav_init != .none and Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) {
diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig
index e18bc078df..511cb6839d 100644
--- a/src/link/MachO/ZigObject.zig
+++ b/src/link/MachO/ZigObject.zig
@@ -608,14 +608,11 @@ pub fn getNavVAddr(
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
log.debug("getNavVAddr {}({d})", .{ nav.fqn.fmt(ip), nav_index });
- const sym_index = switch (ip.indexToKey(nav.status.resolved.val)) {
- .@"extern" => |@"extern"| try self.getGlobalSymbol(
- macho_file,
- nav.name.toSlice(ip),
- @"extern".lib_name.toSlice(ip),
- ),
- else => try self.getOrCreateMetadataForNav(macho_file, nav_index),
- };
+ const sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol(
+ macho_file,
+ nav.name.toSlice(ip),
+ @"extern".lib_name.toSlice(ip),
+ ) else try self.getOrCreateMetadataForNav(macho_file, nav_index);
const sym = self.symbols.items[sym_index];
const vaddr = sym.getAddress(.{}, macho_file);
switch (reloc_info.parent) {
@@ -882,7 +879,7 @@ pub fn updateNav(
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
- const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {
+ const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
.func => .none,
.variable => |variable| variable.init,
.@"extern" => |@"extern"| {
@@ -895,7 +892,7 @@ pub fn updateNav(
sym.flags.is_extern_ptr = true;
return;
},
- else => nav.status.resolved.val,
+ else => nav.status.fully_resolved.val,
};
if (nav_init != .none and Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) {
@@ -1561,11 +1558,7 @@ fn isThreadlocal(macho_file: *MachO, nav_index: InternPool.Nav.Index) bool {
if (!macho_file.base.comp.config.any_non_single_threaded)
return false;
const ip = &macho_file.base.comp.zcu.?.intern_pool;
- return switch (ip.indexToKey(ip.getNav(nav_index).status.resolved.val)) {
- .variable => |variable| variable.is_threadlocal,
- .@"extern" => |@"extern"| @"extern".is_threadlocal,
- else => false,
- };
+ return ip.getNav(nav_index).isThreadlocal(ip);
}
fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index {
diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig
index 3144b2ac10..8e27e20ec7 100644
--- a/src/link/Plan9.zig
+++ b/src/link/Plan9.zig
@@ -1021,7 +1021,7 @@ pub fn seeNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
const atom_idx = gop.value_ptr.index;
// handle externs here because they might not get updateDecl called on them
const nav = ip.getNav(nav_index);
- if (ip.indexToKey(nav.status.resolved.val) == .@"extern") {
+ if (nav.getExtern(ip) != null) {
// this is a "phantom atom" - it is never actually written to disk, just convenient for us to store stuff about externs
if (nav.name.eqlSlice("etext", ip)) {
self.etext_edata_end_atom_indices[0] = atom_idx;
@@ -1370,7 +1370,7 @@ pub fn getNavVAddr(
const ip = &pt.zcu.intern_pool;
const nav = ip.getNav(nav_index);
log.debug("getDeclVAddr for {}", .{nav.name.fmt(ip)});
- if (ip.indexToKey(nav.status.resolved.val) == .@"extern") {
+ if (nav.getExtern(ip) != null) {
if (nav.name.eqlSlice("etext", ip)) {
try self.addReloc(reloc_info.parent.atom_index, .{
.target = undefined,
diff --git a/src/link/Wasm/ZigObject.zig b/src/link/Wasm/ZigObject.zig
index 0b5d2efb47..09d7647730 100644
--- a/src/link/Wasm/ZigObject.zig
+++ b/src/link/Wasm/ZigObject.zig
@@ -241,7 +241,7 @@ pub fn updateNav(
const nav_val = zcu.navValue(nav_index);
const is_extern, const lib_name, const nav_init = switch (ip.indexToKey(nav_val.toIntern())) {
- .variable => |variable| .{ false, variable.lib_name, Value.fromInterned(variable.init) },
+ .variable => |variable| .{ false, .none, Value.fromInterned(variable.init) },
.func => return,
.@"extern" => |@"extern"| if (ip.isFunctionType(nav.typeOf(ip)))
return
@@ -734,15 +734,14 @@ pub fn getNavVAddr(
const target_atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index);
const target_atom = wasm.getAtom(target_atom_index);
const target_symbol_index = @intFromEnum(target_atom.sym_index);
- switch (ip.indexToKey(nav.status.resolved.val)) {
- .@"extern" => |@"extern"| try zig_object.addOrUpdateImport(
+ if (nav.getExtern(ip)) |@"extern"| {
+ try zig_object.addOrUpdateImport(
wasm,
nav.name.toSlice(ip),
target_atom.sym_index,
@"extern".lib_name.toSlice(ip),
null,
- ),
- else => {},
+ );
}
std.debug.assert(reloc_info.parent.atom_index != 0);
@@ -945,8 +944,8 @@ pub fn freeNav(zig_object: *ZigObject, wasm: *Wasm, nav_index: InternPool.Nav.In
segment.name = &.{}; // Ensure no accidental double free
}
- const nav_val = zcu.navValue(nav_index).toIntern();
- if (ip.indexToKey(nav_val) == .@"extern") {
+ const nav = ip.getNav(nav_index);
+ if (nav.getExtern(ip) != null) {
std.debug.assert(zig_object.imports.remove(atom.sym_index));
}
std.debug.assert(wasm.symbol_atom.remove(atom.symbolLoc()));
@@ -960,7 +959,7 @@ pub fn freeNav(zig_object: *ZigObject, wasm: *Wasm, nav_index: InternPool.Nav.In
if (sym.isGlobal()) {
std.debug.assert(zig_object.global_syms.remove(atom.sym_index));
}
- if (ip.isFunctionType(ip.typeOf(nav_val))) {
+ if (ip.isFunctionType(nav.typeOf(ip))) {
zig_object.functions_free_list.append(gpa, sym.index) catch {};
std.debug.assert(zig_object.atom_types.remove(atom_index));
} else {
diff --git a/src/print_zir.zig b/src/print_zir.zig
index f8f7db89e8..80fbb908b6 100644
--- a/src/print_zir.zig
+++ b/src/print_zir.zig
@@ -542,7 +542,6 @@ const Writer = struct {
.@"asm" => try self.writeAsm(stream, extended, false),
.asm_expr => try self.writeAsm(stream, extended, true),
- .variable => try self.writeVarExtended(stream, extended),
.alloc => try self.writeAllocExtended(stream, extended),
.compile_log => try self.writeNodeMultiOp(stream, extended),
@@ -2347,7 +2346,6 @@ const Writer = struct {
inferred_error_set,
false,
false,
- false,
.none,
&.{},
@@ -2371,13 +2369,6 @@ const Writer = struct {
var ret_ty_ref: Zir.Inst.Ref = .none;
var ret_ty_body: []const Zir.Inst.Index = &.{};
- if (extra.data.bits.has_lib_name) {
- const lib_name = self.code.nullTerminatedString(@enumFromInt(self.code.extra[extra_index]));
- extra_index += 1;
- try stream.print("lib_name=\"{}\", ", .{std.zig.fmtEscapes(lib_name)});
- }
- try self.writeFlag(stream, "test, ", extra.data.bits.is_test);
-
if (extra.data.bits.has_cc_body) {
const body_len = self.code.extra[extra_index];
extra_index += 1;
@@ -2414,7 +2405,6 @@ const Writer = struct {
stream,
extra.data.bits.is_inferred_error,
extra.data.bits.is_var_args,
- extra.data.bits.is_extern,
extra.data.bits.is_noinline,
cc_ref,
cc_body,
@@ -2427,36 +2417,6 @@ const Writer = struct {
);
}
- fn writeVarExtended(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
- const extra = self.code.extraData(Zir.Inst.ExtendedVar, extended.operand);
- const small = @as(Zir.Inst.ExtendedVar.Small, @bitCast(extended.small));
-
- try self.writeInstRef(stream, extra.data.var_type);
-
- var extra_index: usize = extra.end;
- if (small.has_lib_name) {
- const lib_name_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]);
- const lib_name = self.code.nullTerminatedString(lib_name_index);
- extra_index += 1;
- try stream.print(", lib_name=\"{}\"", .{std.zig.fmtEscapes(lib_name)});
- }
- const align_inst: Zir.Inst.Ref = if (!small.has_align) .none else blk: {
- const align_inst = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
- extra_index += 1;
- break :blk align_inst;
- };
- const init_inst: Zir.Inst.Ref = if (!small.has_init) .none else blk: {
- const init_inst = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
- extra_index += 1;
- break :blk init_inst;
- };
- try self.writeFlag(stream, ", is_extern", small.is_extern);
- try self.writeFlag(stream, ", is_threadlocal", small.is_threadlocal);
- try self.writeOptionalInstRef(stream, ", align=", align_inst);
- try self.writeOptionalInstRef(stream, ", init=", init_inst);
- try stream.writeAll("))");
- }
-
fn writeAllocExtended(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
const extra = self.code.extraData(Zir.Inst.AllocExtended, extended.operand);
const small = @as(Zir.Inst.AllocExtended.Small, @bitCast(extended.small));
@@ -2604,7 +2564,6 @@ const Writer = struct {
stream: anytype,
inferred_error_set: bool,
var_args: bool,
- is_extern: bool,
is_noinline: bool,
cc_ref: Zir.Inst.Ref,
cc_body: []const Zir.Inst.Index,
@@ -2618,7 +2577,6 @@ const Writer = struct {
try self.writeOptionalInstRefOrBody(stream, "cc=", cc_ref, cc_body);
try self.writeOptionalInstRefOrBody(stream, "ret_ty=", ret_ty_ref, ret_ty_body);
try self.writeFlag(stream, "vargs, ", var_args);
- try self.writeFlag(stream, "extern, ", is_extern);
try self.writeFlag(stream, "inferror, ", inferred_error_set);
try self.writeFlag(stream, "noinline, ", is_noinline);
@@ -2664,56 +2622,58 @@ const Writer = struct {
}
fn writeDeclaration(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
- const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].declaration;
- const extra = self.code.extraData(Zir.Inst.Declaration, inst_data.payload_index);
+ const decl = self.code.getDeclaration(inst);
const prev_parent_decl_node = self.parent_decl_node;
defer self.parent_decl_node = prev_parent_decl_node;
- self.parent_decl_node = inst_data.src_node;
+ self.parent_decl_node = decl.src_node;
- if (extra.data.flags.is_pub) try stream.writeAll("pub ");
- if (extra.data.flags.is_export) try stream.writeAll("export ");
- switch (extra.data.name) {
+ if (decl.is_pub) try stream.writeAll("pub ");
+ switch (decl.linkage) {
+ .normal => {},
+ .@"export" => try stream.writeAll("export "),
+ .@"extern" => try stream.writeAll("extern "),
+ }
+ switch (decl.kind) {
.@"comptime" => try stream.writeAll("comptime"),
.@"usingnamespace" => try stream.writeAll("usingnamespace"),
.unnamed_test => try stream.writeAll("test"),
- _ => {
- const name = extra.data.name.toString(self.code).?;
- const prefix = if (extra.data.name.isNamedTest(self.code)) p: {
- break :p if (extra.data.flags.test_is_decltest) "decltest " else "test ";
- } else "";
- try stream.print("{s}'{s}'", .{ prefix, self.code.nullTerminatedString(name) });
+ .@"test", .decltest, .@"const", .@"var" => {
+ try stream.print("{s} '{s}'", .{ @tagName(decl.kind), self.code.nullTerminatedString(decl.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.src_line, std.fmt.fmtSliceHexLower(&src_hash_bytes) });
+ const src_hash = self.code.getAssociatedSrcHash(inst).?;
+ try stream.print(" line({d}) column({d}) hash({})", .{
+ decl.src_line,
+ decl.src_column,
+ std.fmt.fmtSliceHexLower(&src_hash),
+ });
{
- const bodies = extra.data.getBodies(@intCast(extra.end), self.code);
-
- try stream.writeAll(" value=");
- try self.writeBracedDecl(stream, bodies.value_body);
+ if (decl.type_body) |b| {
+ try stream.writeAll(" type=");
+ try self.writeBracedDecl(stream, b);
+ }
- if (bodies.align_body) |b| {
+ if (decl.align_body) |b| {
try stream.writeAll(" align=");
try self.writeBracedDecl(stream, b);
}
- if (bodies.linksection_body) |b| {
+ if (decl.linksection_body) |b| {
try stream.writeAll(" linksection=");
try self.writeBracedDecl(stream, b);
}
- if (bodies.addrspace_body) |b| {
+ if (decl.addrspace_body) |b| {
try stream.writeAll(" addrspace=");
try self.writeBracedDecl(stream, b);
}
+
+ if (decl.value_body) |b| {
+ try stream.writeAll(" value=");
+ try self.writeBracedDecl(stream, b);
+ }
}
try stream.writeAll(") ");
diff --git a/test/behavior/globals.zig b/test/behavior/globals.zig
index 89dc20c5c7..7c5645be19 100644
--- a/test/behavior/globals.zig
+++ b/test/behavior/globals.zig
@@ -66,3 +66,99 @@ test "global loads can affect liveness" {
S.f();
try std.testing.expect(y.a == 1);
}
+
+test "global const can be self-referential" {
+ const S = struct {
+ self: *const @This(),
+ x: u32,
+
+ const foo: @This() = .{ .self = &foo, .x = 123 };
+ };
+
+ try std.testing.expect(S.foo.x == 123);
+ try std.testing.expect(S.foo.self.x == 123);
+ try std.testing.expect(S.foo.self.self.x == 123);
+ try std.testing.expect(S.foo.self == &S.foo);
+ try std.testing.expect(S.foo.self.self == &S.foo);
+}
+
+test "global var can be self-referential" {
+ const S = struct {
+ self: *@This(),
+ x: u32,
+
+ var foo: @This() = .{ .self = &foo, .x = undefined };
+ };
+
+ S.foo.x = 123;
+
+ try std.testing.expect(S.foo.x == 123);
+ try std.testing.expect(S.foo.self.x == 123);
+ try std.testing.expect(S.foo.self == &S.foo);
+
+ S.foo.self.x = 456;
+
+ try std.testing.expect(S.foo.x == 456);
+ try std.testing.expect(S.foo.self.x == 456);
+ try std.testing.expect(S.foo.self == &S.foo);
+
+ S.foo.self.self.x = 789;
+
+ try std.testing.expect(S.foo.x == 789);
+ try std.testing.expect(S.foo.self.x == 789);
+ try std.testing.expect(S.foo.self == &S.foo);
+}
+
+test "global const can be indirectly self-referential" {
+ const S = struct {
+ other: *const @This(),
+ x: u32,
+
+ const foo: @This() = .{ .other = &bar, .x = 123 };
+ const bar: @This() = .{ .other = &foo, .x = 456 };
+ };
+
+ try std.testing.expect(S.foo.x == 123);
+ try std.testing.expect(S.foo.other.x == 456);
+ try std.testing.expect(S.foo.other.other.x == 123);
+ try std.testing.expect(S.foo.other.other.other.x == 456);
+ try std.testing.expect(S.foo.other == &S.bar);
+ try std.testing.expect(S.foo.other.other == &S.foo);
+
+ try std.testing.expect(S.bar.x == 456);
+ try std.testing.expect(S.bar.other.x == 123);
+ try std.testing.expect(S.bar.other.other.x == 456);
+ try std.testing.expect(S.bar.other.other.other.x == 123);
+ try std.testing.expect(S.bar.other == &S.foo);
+ try std.testing.expect(S.bar.other.other == &S.bar);
+}
+
+test "global var can be indirectly self-referential" {
+ const S = struct {
+ other: *@This(),
+ x: u32,
+
+ var foo: @This() = .{ .other = &bar, .x = undefined };
+ var bar: @This() = .{ .other = &foo, .x = undefined };
+ };
+
+ S.foo.other.x = 123; // bar.x
+ S.foo.other.other.x = 456; // foo.x
+
+ try std.testing.expect(S.foo.x == 456);
+ try std.testing.expect(S.foo.other.x == 123);
+ try std.testing.expect(S.foo.other.other.x == 456);
+ try std.testing.expect(S.foo.other.other.other.x == 123);
+ try std.testing.expect(S.foo.other == &S.bar);
+ try std.testing.expect(S.foo.other.other == &S.foo);
+
+ S.bar.other.x = 111; // foo.x
+ S.bar.other.other.x = 222; // bar.x
+
+ try std.testing.expect(S.bar.x == 222);
+ try std.testing.expect(S.bar.other.x == 111);
+ try std.testing.expect(S.bar.other.other.x == 222);
+ try std.testing.expect(S.bar.other.other.other.x == 111);
+ try std.testing.expect(S.bar.other == &S.foo);
+ try std.testing.expect(S.bar.other.other == &S.bar);
+}
diff --git a/test/cases/compile_errors/address_of_threadlocal_not_comptime_known.zig b/test/cases/compile_errors/address_of_threadlocal_not_comptime_known.zig
index d2d62c82ab..1a6b649094 100644
--- a/test/cases/compile_errors/address_of_threadlocal_not_comptime_known.zig
+++ b/test/cases/compile_errors/address_of_threadlocal_not_comptime_known.zig
@@ -10,4 +10,5 @@ pub export fn entry() void {
// target=native
//
// :2:36: error: unable to resolve comptime value
-// :2:36: note: container level variable initializers must be comptime-known
+// :2:36: note: global variable initializer must be comptime-known
+// :2:36: note: thread local and dll imported variables have runtime-known addresses
diff --git a/test/cases/compile_errors/self_reference_missing_const.zig b/test/cases/compile_errors/self_reference_missing_const.zig
new file mode 100644
index 0000000000..72b0ac1561
--- /dev/null
+++ b/test/cases/compile_errors/self_reference_missing_const.zig
@@ -0,0 +1,11 @@
+const S = struct { self: *S, x: u32 };
+const s: S = .{ .self = &s, .x = 123 };
+
+comptime {
+ _ = s;
+}
+
+// error
+//
+// :2:18: error: expected type '*tmp.S', found '*const tmp.S'
+// :2:18: note: cast discards const qualifier
diff --git a/test/cases/compile_errors/type_variables_must_be_constant.zig b/test/cases/compile_errors/type_variables_must_be_constant.zig
index 1dbddc126c..4789ea3e92 100644
--- a/test/cases/compile_errors/type_variables_must_be_constant.zig
+++ b/test/cases/compile_errors/type_variables_must_be_constant.zig
@@ -7,5 +7,5 @@ export fn entry() foo {
// backend=stage2
// target=native
//
-// :1:5: error: variable of type 'type' must be const or comptime
-// :1:5: note: types are not available at runtime
+// :1:11: error: variable of type 'type' must be const or comptime
+// :1:11: note: types are not available at runtime
diff --git a/test/cases/compile_errors/use_invalid_number_literal_as_array_index.zig b/test/cases/compile_errors/use_invalid_number_literal_as_array_index.zig
index 437d100c0e..b1d101efaa 100644
--- a/test/cases/compile_errors/use_invalid_number_literal_as_array_index.zig
+++ b/test/cases/compile_errors/use_invalid_number_literal_as_array_index.zig
@@ -8,5 +8,5 @@ export fn entry() void {
// backend=stage2
// target=native
//
-// :1:5: error: variable of type 'comptime_int' must be const or comptime
-// :1:5: note: to modify this variable at runtime, it must be given an explicit fixed-size number type
+// :1:9: error: variable of type 'comptime_int' must be const or comptime
+// :1:9: note: to modify this variable at runtime, it must be given an explicit fixed-size number type