Commit 18362ebe13
Changed files (14)
lib
std
src
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 = §ion_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,
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;
src/codegen/llvm.zig
@@ -2939,7 +2939,6 @@ pub const Object = struct {
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 },
};
@@ -4803,7 +4802,7 @@ pub const NavGen = struct {
const resolved = nav.status.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 },
};
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
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);
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 };
@@ -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);
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 };
@@ -2341,24 +2319,13 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
},
.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);
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 };
@@ -2585,12 +2552,11 @@ 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.
@@ -2601,7 +2567,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
const parent_namespace_ptr = ip.namespacePtr(ip.getCau(cau).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 };
@@ -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);
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,
@@ -1252,10 +1244,7 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
};
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) };
- };
+ const zir_decl = zir.getDeclaration(inst_info.inst);
// 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
@@ -1265,7 +1254,134 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
.nav => |nav| ip.getNav(nav),
};
- const result_ref = try sema.resolveInlineBody(&block, decl_bodies.value_body, inst_info.inst);
+ 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 });
+
+ // 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.
+
+ const decl_ty: Type, const early_val: ?Value = if (zir_decl.type_body) |type_body| ty: {
+ // We evaluate only the type now; no need for the value yet.
+ const uncoerced_type_ref = try sema.resolveInlineBody(&block, type_body, inst_info.inst);
+ const type_ref = try sema.coerce(&block, .type, uncoerced_type_ref, ty_src);
+ break :ty .{ .fromInterned(type_ref.toInterned().?), null };
+ } else ty: {
+ // We don't have a type body, so we need to evaluate the value immediately.
+ const value_body = zir_decl.value_body.?;
+ const result_ref = try sema.resolveInlineBody(&block, value_body, inst_info.inst);
+ const val = try sema.resolveFinalDeclValue(&block, init_src, result_ref);
+ break :ty .{ val.typeOf(zcu), val };
+ };
+
+ switch (zir_decl.kind) {
+ .unnamed_test, .@"test", .decltest => assert(decl_ty.zigTypeTag(zcu) == .@"fn"),
+ .@"comptime" => assert(decl_ty.toIntern() == .void_type),
+ .@"usingnamespace" => {},
+ .@"const" => {},
+ .@"var" => try sema.validateVarType(
+ &block,
+ if (zir_decl.type_body != null) ty_src else init_src,
+ decl_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 alignment: InternPool.Alignment = a: {
+ const align_body = zir_decl.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 = zir_decl.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 (zir_decl.kind) {
+ .@"var" => .variable,
+ else => switch (decl_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, inst_info.inst);
+ break :as try sema.analyzeAsAddressSpace(&block, addrspace_src, addrspace_ref, addrspace_ctx);
+ };
+
+ // Lastly, we must evaluate the value if we have not already done so. Note, however, that extern declarations
+ // don't have an associated value body.
+
+ const final_val: ?Value = early_val orelse if (zir_decl.value_body) |value_body| val: {
+ // Put the resolved type into `inst_map` to be used as the result type of the init.
+ try sema.inst_map.ensureSpaceForInstructions(gpa, &.{inst_info.inst});
+ sema.inst_map.putAssumeCapacity(inst_info.inst, Air.internedToRef(decl_ty.toIntern()));
+ const uncoerced_result_ref = try sema.resolveInlineBody(&block, value_body, inst_info.inst);
+ assert(sema.inst_map.remove(inst_info.inst));
+
+ const result_ref = try sema.coerce(&block, decl_ty, uncoerced_result_ref, init_src);
+ break :val try sema.resolveFinalDeclValue(&block, init_src, result_ref);
+ } else null;
+
+ // TODO: missing validation?
+
+ const decl_val: Value = switch (zir_decl.linkage) {
+ .normal, .@"export" => switch (zir_decl.kind) {
+ .@"var" => .fromInterned(try pt.intern(.{ .variable = .{
+ .ty = decl_ty.toIntern(),
+ .init = final_val.?.toIntern(),
+ .owner_nav = cau.owner.unwrap().nav,
+ .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_info.name,
+ .ty = decl_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 = alignment,
+ .@"addrspace" = @"addrspace",
+ .zir_index = cau.zir_index, // `declaration` instruction
+ .owner_nav = undefined, // ignored by `getExtern`
+ }));
+ },
+ };
const nav_index = switch (cau.owner.unwrap()) {
.none => {
@@ -1282,15 +1398,6 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
.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
@@ -1331,50 +1438,10 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
};
// 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())) {
+ 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 decl_ty.comptimeOnlySema(pt)) {
@@ -1383,13 +1450,13 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
.func => "function alias", // slightly clearer message, since you *can* specify these on function *declarations*
else => "comptime-only type",
};
- if (decl_bodies.align_body != null) {
+ if (zir_decl.align_body != null) {
return sema.fail(&block, align_src, "cannot specify alignment of {s}", .{reason});
}
- if (decl_bodies.linksection_body != null) {
+ if (zir_decl.linksection_body != null) {
return sema.fail(&block, section_src, "cannot specify linksection of {s}", .{reason});
}
- if (decl_bodies.addrspace_body != null) {
+ if (zir_decl.addrspace_body != null) {
return sema.fail(&block, addrspace_src, "cannot specify addrspace of {s}", .{reason});
}
}
@@ -1404,9 +1471,9 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
// 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).?);
+ 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_index);
}
@@ -1919,13 +1986,11 @@ 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) {
+ const maybe_name: InternPool.OptionalNullTerminatedString, const kind: Kind, const is_named_test: bool = switch (decl.kind) {
.@"comptime" => info: {
if (iter.pass != .unnamed) return;
break :info .{
@@ -1954,21 +2019,22 @@ const ScanDeclIter = struct {
false,
};
},
- _ => if (declaration.name.isNamedTest(zir)) info: {
+ .@"test", .decltest => |kind| info: {
// 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";
+ const prefix = @tagName(kind);
break :info .{
- (try iter.avoidNameConflict("{s}.{s}", .{ prefix, zir.nullTerminatedString(declaration.name.toString(zir).?) })).toOptional(),
+ (try iter.avoidNameConflict("{s}.{s}", .{ prefix, zir.nullTerminatedString(decl.name) })).toOptional(),
.@"test",
true,
};
- } else info: {
+ },
+ .@"const", .@"var" => info: {
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, {});
@@ -2030,7 +2096,7 @@ const ScanDeclIter = struct {
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);
@@ -2056,7 +2122,7 @@ const ScanDeclIter = struct {
break :a true;
},
.named => a: {
- if (declaration.flags.is_pub) {
+ if (decl.is_pub) {
try namespace.pub_decls.putContext(gpa, nav, {}, .{ .zcu = zcu });
} else {
try namespace.priv_decls.putContext(gpa, nav, {}, .{ .zcu = zcu });
@@ -2068,7 +2134,7 @@ const ScanDeclIter = struct {
},
};
- if (existing_cau == null and (want_analysis or declaration.flags.is_export)) {
+ if (existing_cau == 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 },
src/codegen.zig
@@ -853,7 +853,7 @@ 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 },
+ .variable => |variable| .{ variable.owner_nav, false, .none, variable.is_threadlocal },
.@"extern" => |@"extern"| .{ @"extern".owner_nav, true, @"extern".lib_name, @"extern".is_threadlocal },
else => .{ ref_nav_index, false, .none, false },
};
src/InternPool.zig
@@ -2018,7 +2018,6 @@ pub const Key = union(enum) {
ty: Index,
init: Index,
owner_nav: Nav.Index,
- lib_name: OptionalNullTerminatedString,
is_threadlocal: bool,
is_weak_linkage: bool,
};
@@ -2741,7 +2740,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;
},
@@ -5573,9 +5571,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) {
@@ -6928,7 +6923,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,
} };
@@ -7575,7 +7569,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,
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(") ");
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", .{});
}
@@ -9525,8 +9518,8 @@ fn zirFunc(
} else sema.owner.unwrap().cau;
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 zir_decl = sema.code.getDeclaration(decl_inst);
+ break :exported zir_decl.linkage == .@"export";
};
if (fn_is_exported) {
break :cc target.cCallingConvention() orelse {
@@ -9557,10 +9550,8 @@ fn zirFunc(
ret_ty,
false,
inferred_error_set,
- false,
has_body,
src_locs,
- null,
0,
false,
);
@@ -9619,7 +9610,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 +9834,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,7 +9987,6 @@ 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);
@@ -10050,32 +10038,6 @@ 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(),
@@ -26711,135 +26673,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 +26690,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;
@@ -26895,8 +26721,8 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
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) {
+ const zir_decl = sema.code.getDeclaration(decl_inst.resolve(&zcu.intern_pool) orelse return error.AnalysisFail);
+ 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 +26784,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 +26794,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,
);
@@ -27467,7 +27290,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,
@@ -29881,7 +29704,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,
@@ -38843,13 +38666,6 @@ fn getOwnerCauNav(sema: *Sema) InternPool.Nav.Index {
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 {
src/Zcu.zig
@@ -2679,24 +2679,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 +2697,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 +2705,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 +2721,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;
},
};
@@ -3353,20 +3342,20 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
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);
+ const want_analysis = switch (decl.kind) {
.@"usingnamespace" => unreachable,
+ .@"const", .@"var" => unreachable,
.@"comptime" => true,
- else => a: {
+ .unnamed_test => comp.config.is_test and file.mod == zcu.main_mod,
+ .@"test", .decltest => 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 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;
break :a true;
},
};
@@ -3388,8 +3377,8 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
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 decl = zir.getDeclaration(inst_info.inst);
+ if (decl.linkage == .@"export") {
const unit = AnalUnit.wrap(.{ .cau = cau });
if (!result.contains(unit)) {
log.debug("type '{}': ref cau %{}", .{
@@ -3407,8 +3396,8 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
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 decl = zir.getDeclaration(inst_info.inst);
+ if (decl.linkage == .@"export") {
const unit = AnalUnit.wrap(.{ .cau = cau });
if (!result.contains(unit)) {
log.debug("type '{}': ref cau %{}", .{
@@ -3522,9 +3511,7 @@ 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 {
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
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
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