diff options
| author | Matthew Lugg <mlugg@mlugg.co.uk> | 2024-12-25 02:58:27 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-12-25 02:58:27 +0000 |
| commit | 497592c9b45a94fb7b6028bf45b80f183e395a9b (patch) | |
| tree | 467873c408750cb4223f3ccf31775e42ec9fbd5c /lib/std | |
| parent | af5e731729592af4a5716edd3b1e03264d66ea46 (diff) | |
| parent | 3afda4322c34dedc2319701fdfac3505c8d311e9 (diff) | |
| download | zig-497592c9b45a94fb7b6028bf45b80f183e395a9b.tar.gz zig-497592c9b45a94fb7b6028bf45b80f183e395a9b.zip | |
Merge pull request #22303 from mlugg/131-new
compiler: analyze type and value of global declarations separately
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/zig/AstGen.zig | 1118 | ||||
| -rw-r--r-- | lib/std/zig/Zir.zig | 487 |
2 files changed, 974 insertions, 631 deletions
diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index fcec69ed8f..4a9b948beb 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -106,7 +106,6 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void { Zir.Inst.SwitchBlock.Bits, Zir.Inst.SwitchBlockErrUnion.Bits, Zir.Inst.FuncFancy.Bits, - Zir.Inst.Declaration.Flags, => @bitCast(@field(extra, field.name)), else => @compileError("bad field type"), @@ -1317,12 +1316,45 @@ fn fnProtoExpr( return astgen.failTok(some, "function type cannot have a name", .{}); } + if (fn_proto.ast.align_expr != 0) { + return astgen.failNode(fn_proto.ast.align_expr, "function type cannot have an alignment", .{}); + } + + if (fn_proto.ast.addrspace_expr != 0) { + return astgen.failNode(fn_proto.ast.addrspace_expr, "function type cannot have an addrspace", .{}); + } + + if (fn_proto.ast.section_expr != 0) { + return astgen.failNode(fn_proto.ast.section_expr, "function type cannot have a linksection", .{}); + } + + const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; + const is_inferred_error = token_tags[maybe_bang] == .bang; + if (is_inferred_error) { + return astgen.failTok(maybe_bang, "function type cannot have an inferred error set", .{}); + } + const is_extern = blk: { const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false; break :blk token_tags[maybe_extern_token] == .keyword_extern; }; assert(!is_extern); + return fnProtoExprInner(gz, scope, ri, node, fn_proto, false); +} + +fn fnProtoExprInner( + gz: *GenZir, + scope: *Scope, + ri: ResultInfo, + node: Ast.Node.Index, + fn_proto: Ast.full.FnProto, + implicit_ccc: bool, +) InnerError!Zir.Inst.Ref { + const astgen = gz.astgen; + const tree = astgen.tree; + const token_tags = tree.tokens.items(.tag); + var block_scope = gz.makeSubBlock(scope); defer block_scope.unstack(); @@ -1386,18 +1418,6 @@ fn fnProtoExpr( break :is_var_args false; }; - if (fn_proto.ast.align_expr != 0) { - return astgen.failNode(fn_proto.ast.align_expr, "function type cannot have an alignment", .{}); - } - - if (fn_proto.ast.addrspace_expr != 0) { - return astgen.failNode(fn_proto.ast.addrspace_expr, "function type cannot have an addrspace", .{}); - } - - if (fn_proto.ast.section_expr != 0) { - return astgen.failNode(fn_proto.ast.section_expr, "function type cannot have a linksection", .{}); - } - const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0) try expr( &block_scope, @@ -1405,14 +1425,11 @@ fn fnProtoExpr( .{ .rl = .{ .coerced_ty = try block_scope.addBuiltinValue(fn_proto.ast.callconv_expr, .calling_convention) } }, fn_proto.ast.callconv_expr, ) + else if (implicit_ccc) + try block_scope.addBuiltinValue(node, .calling_convention_c) else - Zir.Inst.Ref.none; + .none; - const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; - const is_inferred_error = token_tags[maybe_bang] == .bang; - if (is_inferred_error) { - return astgen.failTok(maybe_bang, "function type cannot have an inferred error set", .{}); - } const ret_ty = try expr(&block_scope, scope, coerced_type_ri, fn_proto.ast.return_type); const result = try block_scope.addFunc(.{ @@ -1428,11 +1445,8 @@ fn fnProtoExpr( .param_block = block_inst, .body_gz = null, - .lib_name = .empty, .is_var_args = is_var_args, .is_inferred_error = false, - .is_test = false, - .is_extern = false, .is_noinline = false, .noalias_bits = noalias_bits, @@ -4121,17 +4135,6 @@ fn fnDecl( const saved_cursor = astgen.saveSourceCursor(); - var decl_gz: GenZir = .{ - .is_comptime = true, - .decl_node_index = fn_proto.ast.proto_node, - .decl_line = astgen.source_line, - .parent = scope, - .astgen = astgen, - .instructions = gz.instructions, - .instructions_top = gz.instructions.items.len, - }; - defer decl_gz.unstack(); - const decl_column = astgen.source_column; // Set this now, since parameter types, return type, etc may be generic. @@ -4152,12 +4155,140 @@ fn fnDecl( const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false; break :blk token_tags[maybe_inline_token] == .keyword_inline; }; + const lib_name = if (fn_proto.lib_name) |lib_name_token| blk: { + const lib_name_str = try astgen.strLitAsString(lib_name_token); + const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len]; + if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) { + return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{}); + } else if (lib_name_str.len == 0) { + return astgen.failTok(lib_name_token, "library name cannot be empty", .{}); + } + break :blk lib_name_str.index; + } else .empty; + if (fn_proto.ast.callconv_expr != 0 and has_inline_keyword) { + return astgen.failNode( + fn_proto.ast.callconv_expr, + "explicit callconv incompatible with inline keyword", + .{}, + ); + } + const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; + const is_inferred_error = token_tags[maybe_bang] == .bang; + if (body_node == 0) { + if (!is_extern) { + return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{}); + } + if (is_inferred_error) { + return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{}); + } + } else { + assert(!is_extern); // validated by parser (TODO why???) + } + + wip_members.nextDecl(decl_inst); + + var type_gz: GenZir = .{ + .is_comptime = true, + .decl_node_index = fn_proto.ast.proto_node, + .decl_line = astgen.source_line, + .parent = scope, + .astgen = astgen, + .instructions = gz.instructions, + .instructions_top = gz.instructions.items.len, + }; + defer type_gz.unstack(); + + if (is_extern) { + // We include a function *type*, not a value. + const type_inst = try fnProtoExprInner(&type_gz, &type_gz.base, .{ .rl = .none }, decl_node, fn_proto, true); + _ = try type_gz.addBreakWithSrcNode(.break_inline, decl_inst, type_inst, decl_node); + } + + var align_gz = type_gz.makeSubBlock(scope); + defer align_gz.unstack(); + + if (fn_proto.ast.align_expr != 0) { + astgen.restoreSourceCursor(saved_cursor); + const inst = try expr(&align_gz, &align_gz.base, coerced_align_ri, fn_proto.ast.align_expr); + _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node); + } + + var linksection_gz = align_gz.makeSubBlock(scope); + defer linksection_gz.unstack(); + + if (fn_proto.ast.section_expr != 0) { + astgen.restoreSourceCursor(saved_cursor); + const inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, fn_proto.ast.section_expr); + _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node); + } + + var addrspace_gz = linksection_gz.makeSubBlock(scope); + defer addrspace_gz.unstack(); + + if (fn_proto.ast.addrspace_expr != 0) { + astgen.restoreSourceCursor(saved_cursor); + const addrspace_ty = try addrspace_gz.addBuiltinValue(fn_proto.ast.addrspace_expr, .address_space); + const inst = try expr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, fn_proto.ast.section_expr); + _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node); + } + + var value_gz = addrspace_gz.makeSubBlock(scope); + defer value_gz.unstack(); + + if (!is_extern) { + // We include a function *value*, not a type. + astgen.restoreSourceCursor(saved_cursor); + try astgen.fnDeclInner(&value_gz, &value_gz.base, saved_cursor, decl_inst, decl_node, body_node, fn_proto); + } + + // *Now* we can incorporate the full source code into the hasher. + astgen.src_hasher.update(tree.getNodeSource(decl_node)); + + var hash: std.zig.SrcHash = undefined; + astgen.src_hasher.final(&hash); + try setDeclaration(decl_inst, .{ + .src_hash = hash, + .src_line = type_gz.decl_line, + .src_column = decl_column, + + .kind = .@"const", + .name = try astgen.identAsString(fn_name_token), + .is_pub = is_pub, + .is_threadlocal = false, + .linkage = if (is_extern) .@"extern" else if (is_export) .@"export" else .normal, + .lib_name = lib_name, + + .type_gz = &type_gz, + .align_gz = &align_gz, + .linksection_gz = &linksection_gz, + .addrspace_gz = &addrspace_gz, + .value_gz = &value_gz, + }); +} + +fn fnDeclInner( + astgen: *AstGen, + decl_gz: *GenZir, + scope: *Scope, + saved_cursor: SourceCursor, + decl_inst: Zir.Inst.Index, + decl_node: Ast.Node.Index, + body_node: Ast.Node.Index, + fn_proto: Ast.full.FnProto, +) InnerError!void { + const tree = astgen.tree; + const token_tags = tree.tokens.items(.tag); + const is_noinline = blk: { const maybe_noinline_token = fn_proto.extern_export_inline_token orelse break :blk false; break :blk token_tags[maybe_noinline_token] == .keyword_noinline; }; - - wip_members.nextDecl(decl_inst); + const has_inline_keyword = blk: { + const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false; + break :blk token_tags[maybe_inline_token] == .keyword_inline; + }; + const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; + const is_inferred_error = token_tags[maybe_bang] == .bang; // Note that the capacity here may not be sufficient, as this does not include `anytype` parameters. var param_insts: std.ArrayListUnmanaged(Zir.Inst.Index) = try .initCapacity(astgen.arena, fn_proto.ast.params.len); @@ -4192,11 +4323,9 @@ fn fnDecl( break :blk .empty; const param_name = try astgen.identAsString(name_token); - if (!is_extern) { - try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes, .@"function parameter"); - } + try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes, .@"function parameter"); break :blk param_name; - } else if (!is_extern) { + } else { if (param.anytype_ellipsis3) |tok| { return astgen.failTok(tok, "missing parameter name", .{}); } else { @@ -4225,7 +4354,7 @@ fn fnDecl( } return astgen.failNode(param.type_expr, "missing parameter name", .{}); } - } else .empty; + }; const param_inst = if (is_anytype) param: { const name_token = param.name_token orelse param.anytype_ellipsis3.?; @@ -4251,12 +4380,12 @@ fn fnDecl( break :param param_inst.toRef(); }; - if (param_name == .empty or is_extern) continue; + if (param_name == .empty) continue; const sub_scope = try astgen.arena.create(Scope.LocalVal); sub_scope.* = .{ .parent = params_scope, - .gen_zir = &decl_gz, + .gen_zir = decl_gz, .name = param_name, .inst = param_inst, .token_src = param.name_token.?, @@ -4268,23 +4397,9 @@ fn fnDecl( break :is_var_args false; }; - const lib_name = if (fn_proto.lib_name) |lib_name_token| blk: { - const lib_name_str = try astgen.strLitAsString(lib_name_token); - const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len]; - if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) { - return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{}); - } else if (lib_name_str.len == 0) { - return astgen.failTok(lib_name_token, "library name cannot be empty", .{}); - } - break :blk lib_name_str.index; - } else .empty; - - const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; - const is_inferred_error = token_tags[maybe_bang] == .bang; - // After creating the function ZIR instruction, it will need to update the break - // instructions inside the expression blocks for align, addrspace, cc, and ret_ty - // to use the function instruction as the "block" to break from. + // instructions inside the expression blocks for cc and ret_ty to use the function + // instruction as the body to break from. var ret_gz = decl_gz.makeSubBlock(params_scope); defer ret_gz.unstack(); @@ -4309,13 +4424,6 @@ fn fnDecl( defer cc_gz.unstack(); const cc_ref: Zir.Inst.Ref = blk: { if (fn_proto.ast.callconv_expr != 0) { - if (has_inline_keyword) { - return astgen.failNode( - fn_proto.ast.callconv_expr, - "explicit callconv incompatible with inline keyword", - .{}, - ); - } const inst = try expr( &cc_gz, scope, @@ -4328,10 +4436,6 @@ fn fnDecl( } _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst); break :blk inst; - } else if (is_extern) { - const inst = try cc_gz.addBuiltinValue(decl_node, .calling_convention_c); - _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst); - break :blk inst; } else if (has_inline_keyword) { const inst = try cc_gz.addBuiltinValue(decl_node, .calling_convention_inline); _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst); @@ -4341,167 +4445,86 @@ fn fnDecl( } }; - const func_inst: Zir.Inst.Ref = if (body_node == 0) func: { - if (!is_extern) { - return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{}); - } - if (is_inferred_error) { - return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{}); - } - break :func try decl_gz.addFunc(.{ - .src_node = decl_node, - .cc_ref = cc_ref, - .cc_gz = &cc_gz, - .ret_ref = ret_ref, - .ret_gz = &ret_gz, - .ret_param_refs = ret_body_param_refs, - .param_block = decl_inst, - .param_insts = param_insts.items, - .body_gz = null, - .lib_name = lib_name, - .is_var_args = is_var_args, - .is_inferred_error = false, - .is_test = false, - .is_extern = true, - .is_noinline = is_noinline, - .noalias_bits = noalias_bits, - .proto_hash = undefined, // ignored for `body_gz == null` - }); - } else func: { - var body_gz: GenZir = .{ - .is_comptime = false, - .decl_node_index = fn_proto.ast.proto_node, - .decl_line = decl_gz.decl_line, - .parent = params_scope, - .astgen = astgen, - .instructions = gz.instructions, - .instructions_top = gz.instructions.items.len, - }; - defer body_gz.unstack(); - - // We want `params_scope` to be stacked like this: - // body_gz (top) - // param2 - // param1 - // param0 - // decl_gz (bottom) - - // Construct the prototype hash. - // Leave `astgen.src_hasher` unmodified; this will be used for hashing - // the *whole* function declaration, including its body. - var proto_hasher = astgen.src_hasher; - const proto_node = tree.nodes.items(.data)[decl_node].lhs; - proto_hasher.update(tree.getNodeSource(proto_node)); - var proto_hash: std.zig.SrcHash = undefined; - proto_hasher.final(&proto_hash); - - const prev_fn_block = astgen.fn_block; - const prev_fn_ret_ty = astgen.fn_ret_ty; - defer { - astgen.fn_block = prev_fn_block; - astgen.fn_ret_ty = prev_fn_ret_ty; - } - astgen.fn_block = &body_gz; - astgen.fn_ret_ty = if (is_inferred_error or ret_ref.toIndex() != null) r: { - // We're essentially guaranteed to need the return type at some point, - // since the return type is likely not `void` or `noreturn` so there - // will probably be an explicit return requiring RLS. Fetch this - // return type now so the rest of the function can use it. - break :r try body_gz.addNode(.ret_type, decl_node); - } else ret_ref; - - const prev_var_args = astgen.fn_var_args; - astgen.fn_var_args = is_var_args; - defer astgen.fn_var_args = prev_var_args; - - astgen.advanceSourceCursorToNode(body_node); - const lbrace_line = astgen.source_line - decl_gz.decl_line; - const lbrace_column = astgen.source_column; - - _ = try fullBodyExpr(&body_gz, &body_gz.base, .{ .rl = .none }, body_node, .allow_branch_hint); - try checkUsed(gz, scope, params_scope); - - if (!body_gz.endsWithNoReturn()) { - // As our last action before the return, "pop" the error trace if needed - _ = try body_gz.addRestoreErrRetIndex(.ret, .always, decl_node); - - // Add implicit return at end of function. - _ = try body_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node)); - } - - break :func try decl_gz.addFunc(.{ - .src_node = decl_node, - .cc_ref = cc_ref, - .cc_gz = &cc_gz, - .ret_ref = ret_ref, - .ret_gz = &ret_gz, - .ret_param_refs = ret_body_param_refs, - .lbrace_line = lbrace_line, - .lbrace_column = lbrace_column, - .param_block = decl_inst, - .param_insts = param_insts.items, - .body_gz = &body_gz, - .lib_name = lib_name, - .is_var_args = is_var_args, - .is_inferred_error = is_inferred_error, - .is_test = false, - .is_extern = false, - .is_noinline = is_noinline, - .noalias_bits = noalias_bits, - .proto_hash = proto_hash, - }); + var body_gz: GenZir = .{ + .is_comptime = false, + .decl_node_index = fn_proto.ast.proto_node, + .decl_line = decl_gz.decl_line, + .parent = params_scope, + .astgen = astgen, + .instructions = decl_gz.instructions, + .instructions_top = decl_gz.instructions.items.len, }; + defer body_gz.unstack(); + + // The scope stack looks like this: + // body_gz (top) + // param2 + // param1 + // param0 + // decl_gz (bottom) + + // Construct the prototype hash. + // Leave `astgen.src_hasher` unmodified; this will be used for hashing + // the *whole* function declaration, including its body. + var proto_hasher = astgen.src_hasher; + const proto_node = tree.nodes.items(.data)[decl_node].lhs; + proto_hasher.update(tree.getNodeSource(proto_node)); + var proto_hash: std.zig.SrcHash = undefined; + proto_hasher.final(&proto_hash); - // Before we stack more stuff onto `decl_gz`, add its final instruction. - _ = try decl_gz.addBreak(.break_inline, decl_inst, func_inst); + const prev_fn_block = astgen.fn_block; + const prev_fn_ret_ty = astgen.fn_ret_ty; + defer { + astgen.fn_block = prev_fn_block; + astgen.fn_ret_ty = prev_fn_ret_ty; + } + astgen.fn_block = &body_gz; + astgen.fn_ret_ty = if (is_inferred_error or ret_ref.toIndex() != null) r: { + // We're essentially guaranteed to need the return type at some point, + // since the return type is likely not `void` or `noreturn` so there + // will probably be an explicit return requiring RLS. Fetch this + // return type now so the rest of the function can use it. + break :r try body_gz.addNode(.ret_type, decl_node); + } else ret_ref; - // Now that `cc_gz,` `ret_gz`, and `body_gz` are unstacked, we evaluate align, addrspace, and linksection. + const prev_var_args = astgen.fn_var_args; + astgen.fn_var_args = is_var_args; + defer astgen.fn_var_args = prev_var_args; - // We're jumping back in source, so restore the cursor. - astgen.restoreSourceCursor(saved_cursor); + astgen.advanceSourceCursorToNode(body_node); + const lbrace_line = astgen.source_line - decl_gz.decl_line; + const lbrace_column = astgen.source_column; - var align_gz = decl_gz.makeSubBlock(scope); - defer align_gz.unstack(); - if (fn_proto.ast.align_expr != 0) { - const inst = try expr(&decl_gz, &decl_gz.base, coerced_align_ri, fn_proto.ast.align_expr); - _ = try align_gz.addBreak(.break_inline, decl_inst, inst); - } + _ = try fullBodyExpr(&body_gz, &body_gz.base, .{ .rl = .none }, body_node, .allow_branch_hint); + try checkUsed(decl_gz, scope, params_scope); - var section_gz = align_gz.makeSubBlock(scope); - defer section_gz.unstack(); - if (fn_proto.ast.section_expr != 0) { - const inst = try expr(&decl_gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, fn_proto.ast.section_expr); - _ = try section_gz.addBreak(.break_inline, decl_inst, inst); - } + if (!body_gz.endsWithNoReturn()) { + // As our last action before the return, "pop" the error trace if needed + _ = try body_gz.addRestoreErrRetIndex(.ret, .always, decl_node); - var addrspace_gz = section_gz.makeSubBlock(scope); - defer addrspace_gz.unstack(); - if (fn_proto.ast.addrspace_expr != 0) { - const addrspace_ty = try decl_gz.addBuiltinValue(fn_proto.ast.addrspace_expr, .address_space); - const inst = try expr(&decl_gz, scope, .{ .rl = .{ .coerced_ty = addrspace_ty } }, fn_proto.ast.addrspace_expr); - _ = try addrspace_gz.addBreak(.break_inline, decl_inst, inst); + // Add implicit return at end of function. + _ = try body_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node)); } - // *Now* we can incorporate the full source code into the hasher. - astgen.src_hasher.update(tree.getNodeSource(decl_node)); - - var hash: std.zig.SrcHash = undefined; - astgen.src_hasher.final(&hash); - try setDeclaration( - decl_inst, - hash, - .{ .named = fn_name_token }, - decl_gz.decl_line, - decl_column, - is_pub, - is_export, - &decl_gz, - .{ - .align_gz = &align_gz, - .linksection_gz = §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, diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index f2aceb14ae..0f9611aa30 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -1868,10 +1868,6 @@ pub const Inst = struct { /// Rarer instructions are here; ones that do not fit in the 8-bit `Tag` enum. /// `noreturn` instructions may not go here; they must be part of the main `Tag` enum. pub const Extended = enum(u16) { - /// Declares a global variable. - /// `operand` is payload index to `ExtendedVar`. - /// `small` is `ExtendedVar.Small`. - variable, /// A struct type definition. Contains references to ZIR instructions for /// the field types, defaults, and alignments. /// `operand` is payload index to `StructDecl`. @@ -2493,26 +2489,25 @@ pub const Inst = struct { }; /// Trailing: - /// 0. lib_name: NullTerminatedString, // null terminated string index, if has_lib_name is set /// if (has_cc_ref and !has_cc_body) { - /// 1. cc: Ref, + /// 0. cc: Ref, /// } /// if (has_cc_body) { - /// 2. cc_body_len: u32 - /// 3. cc_body: u32 // for each cc_body_len + /// 1. cc_body_len: u32 + /// 2. cc_body: u32 // for each cc_body_len /// } /// if (has_ret_ty_ref and !has_ret_ty_body) { - /// 4. ret_ty: Ref, + /// 3. ret_ty: Ref, /// } /// if (has_ret_ty_body) { - /// 5. ret_ty_body_len: u32 - /// 6. ret_ty_body: u32 // for each ret_ty_body_len + /// 4. ret_ty_body_len: u32 + /// 5. ret_ty_body: u32 // for each ret_ty_body_len /// } - /// 7. noalias_bits: u32 // if has_any_noalias + /// 6. noalias_bits: u32 // if has_any_noalias /// - each bit starting with LSB corresponds to parameter indexes - /// 8. body: Index // for each body_len - /// 9. src_locs: Func.SrcLocs // if body_len != 0 - /// 10. proto_hash: std.zig.SrcHash // if body_len != 0; hash of function prototype + /// 7. body: Index // for each body_len + /// 8. src_locs: Func.SrcLocs // if body_len != 0 + /// 9. proto_hash: std.zig.SrcHash // if body_len != 0; hash of function prototype pub const FuncFancy = struct { /// Points to the block that contains the param instructions for this function. /// If this is a `declaration`, it refers to the declaration's value body. @@ -2522,38 +2517,16 @@ pub const Inst = struct { /// If both has_cc_ref and has_cc_body are false, it means auto calling convention. /// If both has_ret_ty_ref and has_ret_ty_body are false, it means void return type. - pub const Bits = packed struct { + pub const Bits = packed struct(u32) { is_var_args: bool, is_inferred_error: bool, - is_test: bool, - is_extern: bool, is_noinline: bool, has_cc_ref: bool, has_cc_body: bool, has_ret_ty_ref: bool, has_ret_ty_body: bool, - has_lib_name: bool, has_any_noalias: bool, - _: u21 = undefined, - }; - }; - - /// Trailing: - /// 0. lib_name: NullTerminatedString, // null terminated string index, if has_lib_name is set - /// 1. align: Ref, // if has_align is set - /// 2. init: Ref // if has_init is set - /// The source node is obtained from the containing `block_inline`. - pub const ExtendedVar = struct { - var_type: Ref, - - pub const Small = packed struct { - has_lib_name: bool, - has_align: bool, - has_init: bool, - is_extern: bool, - is_const: bool, - is_threadlocal: bool, - _: u10 = undefined, + _: u24 = undefined, }; }; @@ -2582,39 +2555,301 @@ pub const Inst = struct { }; /// Trailing: - /// 0. align_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `align` - /// 1. linksection_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `linksection` - /// 2. addrspace_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `addrspace` - /// 3. value_body_inst: Zir.Inst.Index - /// - for each `value_body_len` + /// 0. name: NullTerminatedString // if `flags.id.hasName()` + /// 1. lib_name: NullTerminatedString // if `flags.id.hasLibName()` + /// 2. type_body_len: u32 // if `flags.id.hasTypeBody()` + /// 3. align_body_len: u32 // if `flags.id.hasSpecialBodies()` + /// 4. linksection_body_len: u32 // if `flags.id.hasSpecialBodies()` + /// 5. addrspace_body_len: u32 // if `flags.id.hasSpecialBodies()` + /// 6. value_body_len: u32 // if `flags.id.hasValueBody()` + /// 7. type_body_inst: Zir.Inst.Index + /// - for each `type_body_len` /// - body to be exited via `break_inline` to this `declaration` instruction - /// 4. align_body_inst: Zir.Inst.Index + /// 8. align_body_inst: Zir.Inst.Index /// - for each `align_body_len` /// - body to be exited via `break_inline` to this `declaration` instruction - /// 5. linksection_body_inst: Zir.Inst.Index + /// 9. linksection_body_inst: Zir.Inst.Index /// - for each `linksection_body_len` /// - body to be exited via `break_inline` to this `declaration` instruction - /// 6. addrspace_body_inst: Zir.Inst.Index + /// 10. addrspace_body_inst: Zir.Inst.Index /// - for each `addrspace_body_len` /// - body to be exited via `break_inline` to this `declaration` instruction + /// 11. value_body_inst: Zir.Inst.Index + /// - for each `value_body_len` + /// - body to be exited via `break_inline` to this `declaration` instruction + /// - within this body, the `declaration` instruction refers to the resolved type from the type body pub const Declaration = struct { // These fields should be concatenated and reinterpreted as a `std.zig.SrcHash`. src_hash_0: u32, src_hash_1: u32, src_hash_2: u32, src_hash_3: u32, - /// The name of this `Decl`. Also indicates whether it is a test, comptime block, etc. - name: Name, - src_line: u32, - src_column: u32, - flags: Flags, + // These fields should be concatenated and reinterpreted as a `Flags`. + flags_0: u32, + flags_1: u32, + + pub const Unwrapped = struct { + pub const Kind = enum { + unnamed_test, + @"test", + decltest, + @"comptime", + @"usingnamespace", + @"const", + @"var", + }; - pub const Flags = packed struct(u32) { - value_body_len: u28, + pub const Linkage = enum { + normal, + @"extern", + @"export", + }; + + src_node: Ast.Node.Index, + + src_line: u32, + src_column: u32, + + kind: Kind, + /// Always `.empty` for `kind` of `unnamed_test`, `.@"comptime"`, `.@"usingnamespace"`. + name: NullTerminatedString, + /// Always `false` for `kind` of `unnamed_test`, `.@"test"`, `.decltest`, `.@"comptime"`. is_pub: bool, - is_export: bool, - test_is_decltest: bool, - has_align_linksection_addrspace: bool, + /// Always `false` for `kind != .@"var"`. + is_threadlocal: bool, + /// Always `.normal` for `kind != .@"const" and kind != .@"var"`. + linkage: Linkage, + /// Always `.empty` for `linkage != .@"extern"`. + lib_name: NullTerminatedString, + + /// Always populated for `linkage == .@"extern". + type_body: ?[]const Inst.Index, + align_body: ?[]const Inst.Index, + linksection_body: ?[]const Inst.Index, + addrspace_body: ?[]const Inst.Index, + /// Always populated for `linkage != .@"extern". + value_body: ?[]const Inst.Index, + }; + + pub const Flags = packed struct(u64) { + src_line: u30, + src_column: u29, + id: Id, + + pub const Id = enum(u5) { + unnamed_test, + @"test", + decltest, + @"comptime", + + @"usingnamespace", + pub_usingnamespace, + + const_simple, + const_typed, + @"const", + pub_const_simple, + pub_const_typed, + pub_const, + + extern_const_simple, + extern_const, + pub_extern_const_simple, + pub_extern_const, + + export_const, + pub_export_const, + + var_simple, + @"var", + var_threadlocal, + pub_var_simple, + pub_var, + pub_var_threadlocal, + + extern_var, + extern_var_threadlocal, + pub_extern_var, + pub_extern_var_threadlocal, + + export_var, + export_var_threadlocal, + pub_export_var, + pub_export_var_threadlocal, + + pub fn hasName(id: Id) bool { + return switch (id) { + .unnamed_test, + .@"comptime", + .@"usingnamespace", + .pub_usingnamespace, + => false, + else => true, + }; + } + + pub fn hasLibName(id: Id) bool { + return switch (id) { + .extern_const, + .pub_extern_const, + .extern_var, + .extern_var_threadlocal, + .pub_extern_var, + .pub_extern_var_threadlocal, + => true, + else => false, + }; + } + + pub fn hasTypeBody(id: Id) bool { + return switch (id) { + .unnamed_test, + .@"test", + .decltest, + .@"comptime", + .@"usingnamespace", + .pub_usingnamespace, + => false, // these constructs are untyped + .const_simple, + .pub_const_simple, + .var_simple, + .pub_var_simple, + => false, // these reprs omit type bodies + else => true, + }; + } + + pub fn hasValueBody(id: Id) bool { + return switch (id) { + .extern_const_simple, + .extern_const, + .pub_extern_const_simple, + .pub_extern_const, + .extern_var, + .extern_var_threadlocal, + .pub_extern_var, + .pub_extern_var_threadlocal, + => false, // externs do not have values + else => true, + }; + } + + pub fn hasSpecialBodies(id: Id) bool { + return switch (id) { + .unnamed_test, + .@"test", + .decltest, + .@"comptime", + .@"usingnamespace", + .pub_usingnamespace, + => false, // these constructs are untyped + .const_simple, + .const_typed, + .pub_const_simple, + .pub_const_typed, + .extern_const_simple, + .pub_extern_const_simple, + .var_simple, + .pub_var_simple, + => false, // these reprs omit special bodies + else => true, + }; + } + + pub fn linkage(id: Id) Declaration.Unwrapped.Linkage { + return switch (id) { + .extern_const_simple, + .extern_const, + .pub_extern_const_simple, + .pub_extern_const, + .extern_var, + .extern_var_threadlocal, + .pub_extern_var, + .pub_extern_var_threadlocal, + => .@"extern", + .export_const, + .pub_export_const, + .export_var, + .export_var_threadlocal, + .pub_export_var, + .pub_export_var_threadlocal, + => .@"export", + else => .normal, + }; + } + + pub fn kind(id: Id) Declaration.Unwrapped.Kind { + return switch (id) { + .unnamed_test => .unnamed_test, + .@"test" => .@"test", + .decltest => .decltest, + .@"comptime" => .@"comptime", + .@"usingnamespace", .pub_usingnamespace => .@"usingnamespace", + .const_simple, + .const_typed, + .@"const", + .pub_const_simple, + .pub_const_typed, + .pub_const, + .extern_const_simple, + .extern_const, + .pub_extern_const_simple, + .pub_extern_const, + .export_const, + .pub_export_const, + => .@"const", + .var_simple, + .@"var", + .var_threadlocal, + .pub_var_simple, + .pub_var, + .pub_var_threadlocal, + .extern_var, + .extern_var_threadlocal, + .pub_extern_var, + .pub_extern_var_threadlocal, + .export_var, + .export_var_threadlocal, + .pub_export_var, + .pub_export_var_threadlocal, + => .@"var", + }; + } + + pub fn isPub(id: Id) bool { + return switch (id) { + .pub_usingnamespace, + .pub_const_simple, + .pub_const_typed, + .pub_const, + .pub_extern_const_simple, + .pub_extern_const, + .pub_export_const, + .pub_var_simple, + .pub_var, + .pub_var_threadlocal, + .pub_extern_var, + .pub_extern_var_threadlocal, + .pub_export_var, + .pub_export_var_threadlocal, + => true, + else => false, + }; + } + + pub fn isThreadlocal(id: Id) bool { + return switch (id) { + .var_threadlocal, + .pub_var_threadlocal, + .extern_var_threadlocal, + .pub_extern_var_threadlocal, + .export_var_threadlocal, + .pub_export_var_threadlocal, + => true, + else => false, + }; + } + }; }; pub const Name = enum(u32) { @@ -2647,17 +2882,24 @@ pub const Inst = struct { }; pub const Bodies = struct { - value_body: []const Index, + type_body: ?[]const Index, align_body: ?[]const Index, linksection_body: ?[]const Index, addrspace_body: ?[]const Index, + value_body: ?[]const Index, }; pub fn getBodies(declaration: Declaration, extra_end: u32, zir: Zir) Bodies { var extra_index: u32 = extra_end; - const value_body_len = declaration.flags.value_body_len; + const value_body_len = declaration.value_body_len; + const type_body_len: u32 = len: { + if (!declaration.flags().kind.hasTypeBody()) break :len 0; + const len = zir.extra[extra_index]; + extra_index += 1; + break :len len; + }; const align_body_len, const linksection_body_len, const addrspace_body_len = lens: { - if (!declaration.flags.has_align_linksection_addrspace) { + if (!declaration.flags.kind.hasSpecialBodies()) { break :lens .{ 0, 0, 0 }; } const lens = zir.extra[extra_index..][0..3].*; @@ -2665,21 +2907,30 @@ pub const Inst = struct { break :lens lens; }; return .{ - .value_body = b: { - defer extra_index += value_body_len; - break :b zir.bodySlice(extra_index, value_body_len); + .type_body = if (type_body_len == 0) null else b: { + const b = zir.bodySlice(extra_index, type_body_len); + extra_index += type_body_len; + break :b b; }, .align_body = if (align_body_len == 0) null else b: { - defer extra_index += align_body_len; - break :b zir.bodySlice(extra_index, align_body_len); + const b = zir.bodySlice(extra_index, align_body_len); + extra_index += align_body_len; + break :b b; }, .linksection_body = if (linksection_body_len == 0) null else b: { - defer extra_index += linksection_body_len; - break :b zir.bodySlice(extra_index, linksection_body_len); + const b = zir.bodySlice(extra_index, linksection_body_len); + extra_index += linksection_body_len; + break :b b; }, .addrspace_body = if (addrspace_body_len == 0) null else b: { - defer extra_index += addrspace_body_len; - break :b zir.bodySlice(extra_index, addrspace_body_len); + const b = zir.bodySlice(extra_index, addrspace_body_len); + extra_index += addrspace_body_len; + break :b b; + }, + .value_body = if (value_body_len == 0) null else b: { + const b = zir.bodySlice(extra_index, value_body_len); + extra_index += value_body_len; + break :b b; }, }; } @@ -3711,18 +3962,18 @@ pub const DeclContents = struct { pub fn findTrackable(zir: Zir, gpa: Allocator, contents: *DeclContents, decl_inst: Zir.Inst.Index) !void { contents.clear(); - const declaration, const extra_end = zir.getDeclaration(decl_inst); - const bodies = declaration.getBodies(extra_end, zir); + const decl = zir.getDeclaration(decl_inst); // `defer` instructions duplicate the same body arbitrarily many times, but we only want to traverse // their contents once per defer. So, we store the extra index of the body here to deduplicate. var found_defers: std.AutoHashMapUnmanaged(u32, void) = .empty; defer found_defers.deinit(gpa); - try zir.findTrackableBody(gpa, contents, &found_defers, bodies.value_body); - if (bodies.align_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b); - if (bodies.linksection_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b); - if (bodies.addrspace_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b); + if (decl.type_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b); + if (decl.align_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b); + if (decl.linksection_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b); + if (decl.addrspace_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b); + if (decl.value_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b); } /// Like `findTrackable`, but only considers the `main_struct_inst` instruction. This may return more than @@ -3991,7 +4242,6 @@ fn findTrackableInner( .value_placeholder => unreachable, // Once again, we start with the boring tags. - .variable, .this, .ret_addr, .builtin_src, @@ -4237,7 +4487,6 @@ fn findTrackableInner( const inst_data = datas[@intFromEnum(inst)].pl_node; const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index); var extra_index: usize = extra.end; - extra_index += @intFromBool(extra.data.bits.has_lib_name); if (extra.data.bits.has_cc_body) { const body_len = zir.extra[extra_index]; @@ -4470,8 +4719,7 @@ pub fn getParamBody(zir: Zir, fn_inst: Inst.Index) []const Zir.Inst.Index { return zir.bodySlice(param_block.end, param_block.data.body_len); }, .declaration => { - const decl, const extra_end = zir.getDeclaration(param_block_index); - return decl.getBodies(extra_end, zir).value_body; + return zir.getDeclaration(param_block_index).value_body.?; }, else => unreachable, } @@ -4526,7 +4774,6 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { var ret_ty_ref: Inst.Ref = .void_type; var ret_ty_body: []const Inst.Index = &.{}; - extra_index += @intFromBool(extra.data.bits.has_lib_name); if (extra.data.bits.has_cc_body) { extra_index += zir.extra[extra_index] + 1; } else if (extra.data.bits.has_cc_ref) { @@ -4555,17 +4802,7 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { }, else => unreachable, }; - const param_body = switch (tags[@intFromEnum(info.param_block)]) { - .block, .block_comptime, .block_inline => param_body: { - const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(info.param_block)].pl_node.payload_index); - break :param_body zir.bodySlice(param_block.end, param_block.data.body_len); - }, - .declaration => param_body: { - const decl, const extra_end = zir.getDeclaration(info.param_block); - break :param_body decl.getBodies(extra_end, zir).value_body; - }, - else => unreachable, - }; + const param_body = zir.getParamBody(fn_inst); var total_params_len: u32 = 0; for (param_body) |inst| { switch (tags[@intFromEnum(inst)]) { @@ -4585,13 +4822,74 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { }; } -pub fn getDeclaration(zir: Zir, inst: Zir.Inst.Index) struct { Inst.Declaration, u32 } { +pub fn getDeclaration(zir: Zir, inst: Zir.Inst.Index) Inst.Declaration.Unwrapped { assert(zir.instructions.items(.tag)[@intFromEnum(inst)] == .declaration); const pl_node = zir.instructions.items(.data)[@intFromEnum(inst)].declaration; const extra = zir.extraData(Inst.Declaration, pl_node.payload_index); + + const flags_vals: [2]u32 = .{ extra.data.flags_0, extra.data.flags_1 }; + const flags: Inst.Declaration.Flags = @bitCast(flags_vals); + + var extra_index = extra.end; + + const name: NullTerminatedString = if (flags.id.hasName()) name: { + const name = zir.extra[extra_index]; + extra_index += 1; + break :name @enumFromInt(name); + } else .empty; + + const lib_name: NullTerminatedString = if (flags.id.hasLibName()) lib_name: { + const lib_name = zir.extra[extra_index]; + extra_index += 1; + break :lib_name @enumFromInt(lib_name); + } else .empty; + + const type_body_len: u32 = if (flags.id.hasTypeBody()) len: { + const len = zir.extra[extra_index]; + extra_index += 1; + break :len len; + } else 0; + const align_body_len: u32, const linksection_body_len: u32, const addrspace_body_len: u32 = lens: { + if (!flags.id.hasSpecialBodies()) break :lens .{ 0, 0, 0 }; + const lens = zir.extra[extra_index..][0..3].*; + extra_index += 3; + break :lens lens; + }; + const value_body_len: u32 = if (flags.id.hasValueBody()) len: { + const len = zir.extra[extra_index]; + extra_index += 1; + break :len len; + } else 0; + + const type_body = zir.bodySlice(extra_index, type_body_len); + extra_index += type_body_len; + const align_body = zir.bodySlice(extra_index, align_body_len); + extra_index += align_body_len; + const linksection_body = zir.bodySlice(extra_index, linksection_body_len); + extra_index += linksection_body_len; + const addrspace_body = zir.bodySlice(extra_index, addrspace_body_len); + extra_index += addrspace_body_len; + const value_body = zir.bodySlice(extra_index, value_body_len); + extra_index += value_body_len; + return .{ - extra.data, - @intCast(extra.end), + .src_node = pl_node.src_node, + + .src_line = flags.src_line, + .src_column = flags.src_column, + + .kind = flags.id.kind(), + .name = name, + .is_pub = flags.id.isPub(), + .is_threadlocal = flags.id.isThreadlocal(), + .linkage = flags.id.linkage(), + .lib_name = lib_name, + + .type_body = if (type_body_len == 0) null else type_body, + .align_body = if (align_body_len == 0) null else align_body, + .linksection_body = if (linksection_body_len == 0) null else linksection_body, + .addrspace_body = if (addrspace_body_len == 0) null else addrspace_body, + .value_body = if (value_body_len == 0) null else value_body, }; } @@ -4636,7 +4934,6 @@ pub fn getAssociatedSrcHash(zir: Zir, inst: Zir.Inst.Index) ?std.zig.SrcHash { } const bits = extra.data.bits; var extra_index = extra.end; - extra_index += @intFromBool(bits.has_lib_name); if (bits.has_cc_body) { const body_len = zir.extra[extra_index]; extra_index += 1 + body_len; |
