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(); - // Before we stack more stuff onto `decl_gz`, add its final instruction. - _ = try decl_gz.addBreak(.break_inline, decl_inst, func_inst); + // The scope stack looks like this: + // body_gz (top) + // param2 + // param1 + // param0 + // decl_gz (bottom) - // Now that `cc_gz,` `ret_gz`, and `body_gz` are unstacked, we evaluate align, addrspace, and linksection. + // 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); - // We're jumping back in source, so restore the cursor. - astgen.restoreSourceCursor(saved_cursor); + 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; - 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); + 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(decl_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)); } - 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); - } - - 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); - } - - // *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 var_inst: Zir.Inst.Ref = if (var_decl.ast.init_node != 0) vi: { + const decl_column = astgen.source_column; + + const decl_inst = try gz.makeDeclaration(node); + wip_members.nextDecl(decl_inst); + + if (var_decl.ast.init_node != 0) { if (is_extern) { return astgen.failNode( var_decl.ast.init_node, @@ -4581,102 +4587,91 @@ fn globalVarDecl( .{}, ); } - - 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; - - 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, - ); - - 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 { + if (!is_extern) { + return astgen.failNode(node, "variables must be initialized", .{}); + } + } + + if (is_extern and var_decl.ast.type_node == 0) { return astgen.failNode(node, "unable to infer variable type", .{}); + } + + assert(var_decl.comptime_token == null); // handled by parser + + 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(); - // 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); + 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); + } + + 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 value_len = astgen.countBodyLenAfterFixups(value_body); + 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; + }, + }, + }; + + 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(src_hash); + 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 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 Flags = packed struct(u32) { - value_body_len: u28, + pub const Unwrapped = struct { + pub const Kind = enum { + unnamed_test, + @"test", + decltest, + @"comptime", + @"usingnamespace", + @"const", + @"var", + }; + + pub const Linkage = enum { + normal, + @"extern", + @"export", + }; + + src_node: Ast.Node.Index, + + src_line: u32, + src_column: u32, + + kind: Kind, + /// Always `.empty` for `kind` of `unnamed_test`, `.@"comptime"`, `.@"usingnamespace"`. + name: NullTerminatedString, + /// Always `false` for `kind` of `unnamed_test`, `.@"test"`, `.decltest`, `.@"comptime"`. is_pub: bool, - is_export: bool, - test_is_decltest: bool, - has_align_linksection_addrspace: bool, + /// Always `false` for `kind != .@"var"`. + is_threadlocal: bool, + /// Always `.normal` for `kind != .@"const" and kind != .@"var"`. + linkage: Linkage, + /// Always `.empty` for `linkage != .@"extern"`. + lib_name: NullTerminatedString, + + /// Always populated for `linkage == .@"extern". + type_body: ?[]const Inst.Index, + align_body: ?[]const Inst.Index, + linksection_body: ?[]const Inst.Index, + addrspace_body: ?[]const Inst.Index, + /// Always populated for `linkage != .@"extern". + value_body: ?[]const Inst.Index, + }; + + pub const Flags = packed struct(u64) { + src_line: u30, + src_column: u29, + id: Id, + + pub const Id = enum(u5) { + unnamed_test, + @"test", + decltest, + @"comptime", + + @"usingnamespace", + pub_usingnamespace, + + const_simple, + const_typed, + @"const", + pub_const_simple, + pub_const_typed, + pub_const, + + extern_const_simple, + extern_const, + pub_extern_const_simple, + pub_extern_const, + + export_const, + pub_export_const, + + var_simple, + @"var", + var_threadlocal, + pub_var_simple, + pub_var, + pub_var_threadlocal, + + extern_var, + extern_var_threadlocal, + pub_extern_var, + pub_extern_var_threadlocal, + + export_var, + export_var_threadlocal, + pub_export_var, + pub_export_var_threadlocal, + + pub fn hasName(id: Id) bool { + return switch (id) { + .unnamed_test, + .@"comptime", + .@"usingnamespace", + .pub_usingnamespace, + => false, + else => true, + }; + } + + pub fn hasLibName(id: Id) bool { + return switch (id) { + .extern_const, + .pub_extern_const, + .extern_var, + .extern_var_threadlocal, + .pub_extern_var, + .pub_extern_var_threadlocal, + => true, + else => false, + }; + } + + pub fn hasTypeBody(id: Id) bool { + return switch (id) { + .unnamed_test, + .@"test", + .decltest, + .@"comptime", + .@"usingnamespace", + .pub_usingnamespace, + => false, // these constructs are untyped + .const_simple, + .pub_const_simple, + .var_simple, + .pub_var_simple, + => false, // these reprs omit type bodies + else => true, + }; + } + + pub fn hasValueBody(id: Id) bool { + return switch (id) { + .extern_const_simple, + .extern_const, + .pub_extern_const_simple, + .pub_extern_const, + .extern_var, + .extern_var_threadlocal, + .pub_extern_var, + .pub_extern_var_threadlocal, + => false, // externs do not have values + else => true, + }; + } + + pub fn hasSpecialBodies(id: Id) bool { + return switch (id) { + .unnamed_test, + .@"test", + .decltest, + .@"comptime", + .@"usingnamespace", + .pub_usingnamespace, + => false, // these constructs are untyped + .const_simple, + .const_typed, + .pub_const_simple, + .pub_const_typed, + .extern_const_simple, + .pub_extern_const_simple, + .var_simple, + .pub_var_simple, + => false, // these reprs omit special bodies + else => true, + }; + } + + pub fn linkage(id: Id) Declaration.Unwrapped.Linkage { + return switch (id) { + .extern_const_simple, + .extern_const, + .pub_extern_const_simple, + .pub_extern_const, + .extern_var, + .extern_var_threadlocal, + .pub_extern_var, + .pub_extern_var_threadlocal, + => .@"extern", + .export_const, + .pub_export_const, + .export_var, + .export_var_threadlocal, + .pub_export_var, + .pub_export_var_threadlocal, + => .@"export", + else => .normal, + }; + } + + pub fn kind(id: Id) Declaration.Unwrapped.Kind { + return switch (id) { + .unnamed_test => .unnamed_test, + .@"test" => .@"test", + .decltest => .decltest, + .@"comptime" => .@"comptime", + .@"usingnamespace", .pub_usingnamespace => .@"usingnamespace", + .const_simple, + .const_typed, + .@"const", + .pub_const_simple, + .pub_const_typed, + .pub_const, + .extern_const_simple, + .extern_const, + .pub_extern_const_simple, + .pub_extern_const, + .export_const, + .pub_export_const, + => .@"const", + .var_simple, + .@"var", + .var_threadlocal, + .pub_var_simple, + .pub_var, + .pub_var_threadlocal, + .extern_var, + .extern_var_threadlocal, + .pub_extern_var, + .pub_extern_var_threadlocal, + .export_var, + .export_var_threadlocal, + .pub_export_var, + .pub_export_var_threadlocal, + => .@"var", + }; + } + + pub fn isPub(id: Id) bool { + return switch (id) { + .pub_usingnamespace, + .pub_const_simple, + .pub_const_typed, + .pub_const, + .pub_extern_const_simple, + .pub_extern_const, + .pub_export_const, + .pub_var_simple, + .pub_var, + .pub_var_threadlocal, + .pub_extern_var, + .pub_extern_var_threadlocal, + .pub_export_var, + .pub_export_var_threadlocal, + => true, + else => false, + }; + } + + pub fn isThreadlocal(id: Id) bool { + return switch (id) { + .var_threadlocal, + .pub_var_threadlocal, + .extern_var_threadlocal, + .pub_extern_var_threadlocal, + .export_var_threadlocal, + .pub_export_var_threadlocal, + => true, + else => false, + }; + } + }; }; pub const Name = enum(u32) { @@ -2647,17 +2882,24 @@ pub const Inst = struct { }; pub const Bodies = struct { - value_body: []const Index, + type_body: ?[]const Index, align_body: ?[]const Index, linksection_body: ?[]const Index, addrspace_body: ?[]const Index, + value_body: ?[]const Index, }; pub fn getBodies(declaration: Declaration, extra_end: u32, zir: Zir) Bodies { var extra_index: u32 = extra_end; - const value_body_len = declaration.flags.value_body_len; + const value_body_len = declaration.value_body_len; + const type_body_len: u32 = len: { + if (!declaration.flags().kind.hasTypeBody()) break :len 0; + const len = zir.extra[extra_index]; + extra_index += 1; + break :len len; + }; const align_body_len, const linksection_body_len, const addrspace_body_len = lens: { - if (!declaration.flags.has_align_linksection_addrspace) { + if (!declaration.flags.kind.hasSpecialBodies()) { break :lens .{ 0, 0, 0 }; } const lens = zir.extra[extra_index..][0..3].*; @@ -2665,21 +2907,30 @@ pub const Inst = struct { break :lens lens; }; return .{ - .value_body = b: { - defer extra_index += value_body_len; - break :b zir.bodySlice(extra_index, value_body_len); + .type_body = if (type_body_len == 0) null else b: { + const b = zir.bodySlice(extra_index, type_body_len); + extra_index += type_body_len; + break :b b; }, .align_body = if (align_body_len == 0) null else b: { - defer extra_index += align_body_len; - break :b zir.bodySlice(extra_index, align_body_len); + const b = zir.bodySlice(extra_index, align_body_len); + extra_index += align_body_len; + break :b b; }, .linksection_body = if (linksection_body_len == 0) null else b: { - defer extra_index += linksection_body_len; - break :b zir.bodySlice(extra_index, linksection_body_len); + const b = zir.bodySlice(extra_index, linksection_body_len); + extra_index += linksection_body_len; + break :b b; }, .addrspace_body = if (addrspace_body_len == 0) null else b: { - defer extra_index += addrspace_body_len; - break :b zir.bodySlice(extra_index, addrspace_body_len); + const b = zir.bodySlice(extra_index, addrspace_body_len); + extra_index += addrspace_body_len; + break :b b; + }, + .value_body = if (value_body_len == 0) null else b: { + const b = zir.bodySlice(extra_index, value_body_len); + extra_index += value_body_len; + break :b b; }, }; } @@ -3711,18 +3962,18 @@ pub const DeclContents = struct { pub fn findTrackable(zir: Zir, gpa: Allocator, contents: *DeclContents, decl_inst: Zir.Inst.Index) !void { contents.clear(); - const declaration, const extra_end = zir.getDeclaration(decl_inst); - const bodies = declaration.getBodies(extra_end, zir); + const decl = zir.getDeclaration(decl_inst); // `defer` instructions duplicate the same body arbitrarily many times, but we only want to traverse // their contents once per defer. So, we store the extra index of the body here to deduplicate. var found_defers: std.AutoHashMapUnmanaged(u32, void) = .empty; defer found_defers.deinit(gpa); - try zir.findTrackableBody(gpa, contents, &found_defers, bodies.value_body); - if (bodies.align_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b); - if (bodies.linksection_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b); - if (bodies.addrspace_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b); + if (decl.type_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b); + if (decl.align_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b); + if (decl.linksection_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b); + if (decl.addrspace_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b); + if (decl.value_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b); } /// Like `findTrackable`, but only considers the `main_struct_inst` instruction. This may return more than @@ -3991,7 +4242,6 @@ fn findTrackableInner( .value_placeholder => unreachable, // Once again, we start with the boring tags. - .variable, .this, .ret_addr, .builtin_src, @@ -4237,7 +4487,6 @@ fn findTrackableInner( const inst_data = datas[@intFromEnum(inst)].pl_node; const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index); var extra_index: usize = extra.end; - extra_index += @intFromBool(extra.data.bits.has_lib_name); if (extra.data.bits.has_cc_body) { const body_len = zir.extra[extra_index]; @@ -4470,8 +4719,7 @@ pub fn getParamBody(zir: Zir, fn_inst: Inst.Index) []const Zir.Inst.Index { return zir.bodySlice(param_block.end, param_block.data.body_len); }, .declaration => { - const decl, const extra_end = zir.getDeclaration(param_block_index); - return decl.getBodies(extra_end, zir).value_body; + return zir.getDeclaration(param_block_index).value_body.?; }, else => unreachable, } @@ -4526,7 +4774,6 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { var ret_ty_ref: Inst.Ref = .void_type; var ret_ty_body: []const Inst.Index = &.{}; - extra_index += @intFromBool(extra.data.bits.has_lib_name); if (extra.data.bits.has_cc_body) { extra_index += zir.extra[extra_index] + 1; } else if (extra.data.bits.has_cc_ref) { @@ -4555,17 +4802,7 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { }, else => unreachable, }; - const param_body = switch (tags[@intFromEnum(info.param_block)]) { - .block, .block_comptime, .block_inline => param_body: { - const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(info.param_block)].pl_node.payload_index); - break :param_body zir.bodySlice(param_block.end, param_block.data.body_len); - }, - .declaration => param_body: { - const decl, const extra_end = zir.getDeclaration(info.param_block); - break :param_body decl.getBodies(extra_end, zir).value_body; - }, - else => unreachable, - }; + const param_body = zir.getParamBody(fn_inst); var total_params_len: u32 = 0; for (param_body) |inst| { switch (tags[@intFromEnum(inst)]) { @@ -4585,13 +4822,74 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { }; } -pub fn getDeclaration(zir: Zir, inst: Zir.Inst.Index) struct { Inst.Declaration, u32 } { +pub fn getDeclaration(zir: Zir, inst: Zir.Inst.Index) Inst.Declaration.Unwrapped { assert(zir.instructions.items(.tag)[@intFromEnum(inst)] == .declaration); const pl_node = zir.instructions.items(.data)[@intFromEnum(inst)].declaration; const extra = zir.extraData(Inst.Declaration, pl_node.payload_index); + + const flags_vals: [2]u32 = .{ extra.data.flags_0, extra.data.flags_1 }; + const flags: Inst.Declaration.Flags = @bitCast(flags_vals); + + var extra_index = extra.end; + + const name: NullTerminatedString = if (flags.id.hasName()) name: { + const name = zir.extra[extra_index]; + extra_index += 1; + break :name @enumFromInt(name); + } else .empty; + + const lib_name: NullTerminatedString = if (flags.id.hasLibName()) lib_name: { + const lib_name = zir.extra[extra_index]; + extra_index += 1; + break :lib_name @enumFromInt(lib_name); + } else .empty; + + const type_body_len: u32 = if (flags.id.hasTypeBody()) len: { + const len = zir.extra[extra_index]; + extra_index += 1; + break :len len; + } else 0; + const align_body_len: u32, const linksection_body_len: u32, const addrspace_body_len: u32 = lens: { + if (!flags.id.hasSpecialBodies()) break :lens .{ 0, 0, 0 }; + const lens = zir.extra[extra_index..][0..3].*; + extra_index += 3; + break :lens lens; + }; + const value_body_len: u32 = if (flags.id.hasValueBody()) len: { + const len = zir.extra[extra_index]; + extra_index += 1; + break :len len; + } else 0; + + const type_body = zir.bodySlice(extra_index, type_body_len); + extra_index += type_body_len; + const align_body = zir.bodySlice(extra_index, align_body_len); + extra_index += align_body_len; + const linksection_body = zir.bodySlice(extra_index, linksection_body_len); + extra_index += linksection_body_len; + const addrspace_body = zir.bodySlice(extra_index, addrspace_body_len); + extra_index += addrspace_body_len; + const value_body = zir.bodySlice(extra_index, value_body_len); + extra_index += value_body_len; + return .{ - extra.data, - @intCast(extra.end), + .src_node = pl_node.src_node, + + .src_line = flags.src_line, + .src_column = flags.src_column, + + .kind = flags.id.kind(), + .name = name, + .is_pub = flags.id.isPub(), + .is_threadlocal = flags.id.isThreadlocal(), + .linkage = flags.id.linkage(), + .lib_name = lib_name, + + .type_body = if (type_body_len == 0) null else type_body, + .align_body = if (align_body_len == 0) null else align_body, + .linksection_body = if (linksection_body_len == 0) null else linksection_body, + .addrspace_body = if (addrspace_body_len == 0) null else addrspace_body, + .value_body = if (value_body_len == 0) null else value_body, }; } @@ -4636,7 +4934,6 @@ pub fn getAssociatedSrcHash(zir: Zir, inst: Zir.Inst.Index) ?std.zig.SrcHash { } const bits = extra.data.bits; var extra_index = extra.end; - extra_index += @intFromBool(bits.has_lib_name); if (bits.has_cc_body) { const body_len = zir.extra[extra_index]; extra_index += 1 + body_len; diff --git a/src/InternPool.zig b/src/InternPool.zig index f923332f88..920f0df4ee 100644 --- a/src/InternPool.zig +++ b/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, diff --git a/src/Sema.zig b/src/Sema.zig index 110476adf9..75d8be4759 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1284,7 +1284,6 @@ fn analyzeBodyInner( const extended = datas[@intFromEnum(inst)].extended; break :ext switch (extended.opcode) { // zig fmt: off - .variable => try sema.zirVarExtended( block, extended), .struct_decl => try sema.zirStructDecl( block, extended, inst), .enum_decl => try sema.zirEnumDecl( block, extended, inst), .union_decl => try sema.zirUnionDecl( block, extended, inst), @@ -2114,13 +2113,33 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) } /// Return the Value corresponding to a given AIR ref, or `null` if it refers to a runtime value. -/// InternPool key `variable` is considered a runtime value. /// Generic poison causes `error.GenericPoison` to be returned. fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value { - const val = (try sema.resolveValueAllowVariables(inst)) orelse return null; - if (val.isGenericPoison()) return error.GenericPoison; - if (sema.pt.zcu.intern_pool.isVariable(val.toIntern())) return null; - return val; + const zcu = sema.pt.zcu; + assert(inst != .none); + + if (try sema.typeHasOnePossibleValue(sema.typeOf(inst))) |opv| { + return opv; + } + + if (inst.toInterned()) |ip_index| { + const val: Value = .fromInterned(ip_index); + + assert(val.getVariable(zcu) == null); + if (val.isPtrRuntimeValue(zcu)) return null; + if (val.isGenericPoison()) return error.GenericPoison; + + return val; + } else { + // Runtime-known value. + const air_tags = sema.air_instructions.items(.tag); + switch (air_tags[@intFromEnum(inst.toIndex().?)]) { + .inferred_alloc => unreachable, // assertion failure + .inferred_alloc_comptime => unreachable, // assertion failure + else => {}, + } + return null; + } } /// Like `resolveValue`, but emits an error if the value is not comptime-known. @@ -2183,35 +2202,6 @@ fn resolveValueIntable(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value { return try sema.resolveLazyValue(val); } -/// Returns all InternPool keys representing values, including `variable`, `undef`, and `generic_poison`. -fn resolveValueAllowVariables(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value { - const pt = sema.pt; - assert(inst != .none); - // First section of indexes correspond to a set number of constant values. - if (@intFromEnum(inst) < InternPool.static_len) { - return Value.fromInterned(@as(InternPool.Index, @enumFromInt(@intFromEnum(inst)))); - } - - const air_tags = sema.air_instructions.items(.tag); - if (try sema.typeHasOnePossibleValue(sema.typeOf(inst))) |opv| { - if (inst.toInterned()) |ip_index| { - const val = Value.fromInterned(ip_index); - if (val.getVariable(pt.zcu) != null) return val; - } - return opv; - } - const ip_index = inst.toInterned() orelse { - switch (air_tags[@intFromEnum(inst.toIndex().?)]) { - .inferred_alloc => unreachable, - .inferred_alloc_comptime => unreachable, - else => return null, - } - }; - const val = Value.fromInterned(ip_index); - if (val.isPtrRuntimeValue(pt.zcu)) return null; - return val; -} - /// Value Tag may be `undef` or `variable`. pub fn resolveFinalDeclValue( sema: *Sema, @@ -2221,8 +2211,13 @@ pub fn resolveFinalDeclValue( ) CompileError!Value { const zcu = sema.pt.zcu; - const val = try sema.resolveValueAllowVariables(air_ref) orelse { - const value_comptime_reason: ?[]const u8 = if (air_ref.toInterned()) |_| + const val = try sema.resolveValue(air_ref) orelse { + const is_runtime_ptr = rt_ptr: { + const ip_index = air_ref.toInterned() orelse break :rt_ptr false; + const val: Value = .fromInterned(ip_index); + break :rt_ptr val.isPtrRuntimeValue(zcu); + }; + const value_comptime_reason: ?[]const u8 = if (is_runtime_ptr) "thread local and dll imported variables have runtime-known addresses" else null; @@ -2232,10 +2227,8 @@ pub fn resolveFinalDeclValue( .value_comptime_reason = value_comptime_reason, }); }; - if (val.isGenericPoison()) return error.GenericPoison; - const init_val: Value = if (val.getVariable(zcu)) |v| .fromInterned(v.init) else val; - if (init_val.canMutateComptimeVarState(zcu)) { + if (val.canMutateComptimeVarState(zcu)) { return sema.fail(block, src, "global variable contains reference to comptime var", .{}); } @@ -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 { diff --git a/src/Zcu.zig b/src/Zcu.zig index 771bfdcf61..bb6a25802e 100644 --- a/src/Zcu.zig +++ b/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 { diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index ca2e76bcf7..2cf6e3670c 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -469,12 +469,8 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { { var it = old_zir.declIterator(old_inst); while (it.next()) |decl_inst| { - const decl_name = old_zir.getDeclaration(decl_inst)[0].name; - switch (decl_name) { - .@"comptime", .@"usingnamespace", .unnamed_test => continue, - _ => if (decl_name.isNamedTest(old_zir)) continue, - } - const name_zir = decl_name.toString(old_zir).?; + const name_zir = old_zir.getDeclaration(decl_inst).name; + if (name_zir == .empty) continue; const name_ip = try zcu.intern_pool.getOrPutString( zcu.gpa, pt.tid, @@ -488,12 +484,8 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { { var it = new_zir.declIterator(new_inst); while (it.next()) |decl_inst| { - const decl_name = new_zir.getDeclaration(decl_inst)[0].name; - switch (decl_name) { - .@"comptime", .@"usingnamespace", .unnamed_test => continue, - _ => if (decl_name.isNamedTest(new_zir)) continue, - } - const name_zir = decl_name.toString(new_zir).?; + const name_zir = new_zir.getDeclaration(decl_inst).name; + if (name_zir == .empty) continue; const name_ip = try zcu.intern_pool.getOrPutString( zcu.gpa, pt.tid, @@ -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 }, diff --git a/src/codegen.zig b/src/codegen.zig index 2b179979f0..898629d69b 100644 --- a/src/codegen.zig +++ b/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 }, }; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index b94ea07995..154a7114cf 100644 --- a/src/codegen/llvm.zig +++ b/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 }, }; diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 426b9d21c9..20db035c93 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -2259,24 +2259,13 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In switch (ip.indexToKey(nav_val.toIntern())) { else => { assert(file.zir_loaded); - const decl = file.zir.getDeclaration(inst_info.inst)[0]; + const decl = file.zir.getDeclaration(inst_info.inst); const parent_type, const accessibility: u8 = if (nav.analysis_owner.unwrap()) |cau| parent: { const parent_namespace_ptr = ip.namespacePtr(ip.getCau(cau).namespace); 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); diff --git a/src/link/Wasm/ZigObject.zig b/src/link/Wasm/ZigObject.zig index 0b5d2efb47..0166b28743 100644 --- a/src/link/Wasm/ZigObject.zig +++ b/src/link/Wasm/ZigObject.zig @@ -241,7 +241,7 @@ pub fn updateNav( const nav_val = zcu.navValue(nav_index); const is_extern, const lib_name, const nav_init = switch (ip.indexToKey(nav_val.toIntern())) { - .variable => |variable| .{ false, variable.lib_name, Value.fromInterned(variable.init) }, + .variable => |variable| .{ false, .none, Value.fromInterned(variable.init) }, .func => return, .@"extern" => |@"extern"| if (ip.isFunctionType(nav.typeOf(ip))) return diff --git a/src/print_zir.zig b/src/print_zir.zig index f8f7db89e8..80fbb908b6 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -542,7 +542,6 @@ const Writer = struct { .@"asm" => try self.writeAsm(stream, extended, false), .asm_expr => try self.writeAsm(stream, extended, true), - .variable => try self.writeVarExtended(stream, extended), .alloc => try self.writeAllocExtended(stream, extended), .compile_log => try self.writeNodeMultiOp(stream, extended), @@ -2347,7 +2346,6 @@ const Writer = struct { inferred_error_set, false, false, - false, .none, &.{}, @@ -2371,13 +2369,6 @@ const Writer = struct { var ret_ty_ref: Zir.Inst.Ref = .none; var ret_ty_body: []const Zir.Inst.Index = &.{}; - if (extra.data.bits.has_lib_name) { - const lib_name = self.code.nullTerminatedString(@enumFromInt(self.code.extra[extra_index])); - extra_index += 1; - try stream.print("lib_name=\"{}\", ", .{std.zig.fmtEscapes(lib_name)}); - } - try self.writeFlag(stream, "test, ", extra.data.bits.is_test); - if (extra.data.bits.has_cc_body) { const body_len = self.code.extra[extra_index]; extra_index += 1; @@ -2414,7 +2405,6 @@ const Writer = struct { stream, extra.data.bits.is_inferred_error, extra.data.bits.is_var_args, - extra.data.bits.is_extern, extra.data.bits.is_noinline, cc_ref, cc_body, @@ -2427,36 +2417,6 @@ const Writer = struct { ); } - fn writeVarExtended(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { - const extra = self.code.extraData(Zir.Inst.ExtendedVar, extended.operand); - const small = @as(Zir.Inst.ExtendedVar.Small, @bitCast(extended.small)); - - try self.writeInstRef(stream, extra.data.var_type); - - var extra_index: usize = extra.end; - if (small.has_lib_name) { - const lib_name_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]); - const lib_name = self.code.nullTerminatedString(lib_name_index); - extra_index += 1; - try stream.print(", lib_name=\"{}\"", .{std.zig.fmtEscapes(lib_name)}); - } - const align_inst: Zir.Inst.Ref = if (!small.has_align) .none else blk: { - const align_inst = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); - extra_index += 1; - break :blk align_inst; - }; - const init_inst: Zir.Inst.Ref = if (!small.has_init) .none else blk: { - const init_inst = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index])); - extra_index += 1; - break :blk init_inst; - }; - try self.writeFlag(stream, ", is_extern", small.is_extern); - try self.writeFlag(stream, ", is_threadlocal", small.is_threadlocal); - try self.writeOptionalInstRef(stream, ", align=", align_inst); - try self.writeOptionalInstRef(stream, ", init=", init_inst); - try stream.writeAll("))"); - } - fn writeAllocExtended(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { const extra = self.code.extraData(Zir.Inst.AllocExtended, extended.operand); const small = @as(Zir.Inst.AllocExtended.Small, @bitCast(extended.small)); @@ -2604,7 +2564,6 @@ const Writer = struct { stream: anytype, inferred_error_set: bool, var_args: bool, - is_extern: bool, is_noinline: bool, cc_ref: Zir.Inst.Ref, cc_body: []const Zir.Inst.Index, @@ -2618,7 +2577,6 @@ const Writer = struct { try self.writeOptionalInstRefOrBody(stream, "cc=", cc_ref, cc_body); try self.writeOptionalInstRefOrBody(stream, "ret_ty=", ret_ty_ref, ret_ty_body); try self.writeFlag(stream, "vargs, ", var_args); - try self.writeFlag(stream, "extern, ", is_extern); try self.writeFlag(stream, "inferror, ", inferred_error_set); try self.writeFlag(stream, "noinline, ", is_noinline); @@ -2664,56 +2622,58 @@ const Writer = struct { } fn writeDeclaration(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { - const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].declaration; - const extra = self.code.extraData(Zir.Inst.Declaration, inst_data.payload_index); + const decl = self.code.getDeclaration(inst); const prev_parent_decl_node = self.parent_decl_node; defer self.parent_decl_node = prev_parent_decl_node; - self.parent_decl_node = inst_data.src_node; + self.parent_decl_node = decl.src_node; - if (extra.data.flags.is_pub) try stream.writeAll("pub "); - if (extra.data.flags.is_export) try stream.writeAll("export "); - switch (extra.data.name) { + if (decl.is_pub) try stream.writeAll("pub "); + switch (decl.linkage) { + .normal => {}, + .@"export" => try stream.writeAll("export "), + .@"extern" => try stream.writeAll("extern "), + } + switch (decl.kind) { .@"comptime" => try stream.writeAll("comptime"), .@"usingnamespace" => try stream.writeAll("usingnamespace"), .unnamed_test => try stream.writeAll("test"), - _ => { - const name = extra.data.name.toString(self.code).?; - const prefix = if (extra.data.name.isNamedTest(self.code)) p: { - break :p if (extra.data.flags.test_is_decltest) "decltest " else "test "; - } else ""; - try stream.print("{s}'{s}'", .{ prefix, self.code.nullTerminatedString(name) }); + .@"test", .decltest, .@"const", .@"var" => { + try stream.print("{s} '{s}'", .{ @tagName(decl.kind), self.code.nullTerminatedString(decl.name) }); }, } - const src_hash_arr: [4]u32 = .{ - extra.data.src_hash_0, - extra.data.src_hash_1, - extra.data.src_hash_2, - extra.data.src_hash_3, - }; - const src_hash_bytes: [16]u8 = @bitCast(src_hash_arr); - try stream.print(" line({d}) hash({})", .{ extra.data.src_line, std.fmt.fmtSliceHexLower(&src_hash_bytes) }); + const src_hash = self.code.getAssociatedSrcHash(inst).?; + try stream.print(" line({d}) column({d}) hash({})", .{ + decl.src_line, + decl.src_column, + std.fmt.fmtSliceHexLower(&src_hash), + }); { - const bodies = extra.data.getBodies(@intCast(extra.end), self.code); + if (decl.type_body) |b| { + try stream.writeAll(" type="); + try self.writeBracedDecl(stream, b); + } - try stream.writeAll(" value="); - try self.writeBracedDecl(stream, bodies.value_body); - - if (bodies.align_body) |b| { + if (decl.align_body) |b| { try stream.writeAll(" align="); try self.writeBracedDecl(stream, b); } - if (bodies.linksection_body) |b| { + if (decl.linksection_body) |b| { try stream.writeAll(" linksection="); try self.writeBracedDecl(stream, b); } - if (bodies.addrspace_body) |b| { + if (decl.addrspace_body) |b| { try stream.writeAll(" addrspace="); try self.writeBracedDecl(stream, b); } + + if (decl.value_body) |b| { + try stream.writeAll(" value="); + try self.writeBracedDecl(stream, b); + } } try stream.writeAll(") "); diff --git a/test/cases/compile_errors/address_of_threadlocal_not_comptime_known.zig b/test/cases/compile_errors/address_of_threadlocal_not_comptime_known.zig index d2d62c82ab..1a6b649094 100644 --- a/test/cases/compile_errors/address_of_threadlocal_not_comptime_known.zig +++ b/test/cases/compile_errors/address_of_threadlocal_not_comptime_known.zig @@ -10,4 +10,5 @@ pub export fn entry() void { // target=native // // :2:36: error: unable to resolve comptime value -// :2:36: note: container level variable initializers must be comptime-known +// :2:36: note: global variable initializer must be comptime-known +// :2:36: note: thread local and dll imported variables have runtime-known addresses diff --git a/test/cases/compile_errors/type_variables_must_be_constant.zig b/test/cases/compile_errors/type_variables_must_be_constant.zig index 1dbddc126c..4789ea3e92 100644 --- a/test/cases/compile_errors/type_variables_must_be_constant.zig +++ b/test/cases/compile_errors/type_variables_must_be_constant.zig @@ -7,5 +7,5 @@ export fn entry() foo { // backend=stage2 // target=native // -// :1:5: error: variable of type 'type' must be const or comptime -// :1:5: note: types are not available at runtime +// :1:11: error: variable of type 'type' must be const or comptime +// :1:11: note: types are not available at runtime diff --git a/test/cases/compile_errors/use_invalid_number_literal_as_array_index.zig b/test/cases/compile_errors/use_invalid_number_literal_as_array_index.zig index 437d100c0e..b1d101efaa 100644 --- a/test/cases/compile_errors/use_invalid_number_literal_as_array_index.zig +++ b/test/cases/compile_errors/use_invalid_number_literal_as_array_index.zig @@ -8,5 +8,5 @@ export fn entry() void { // backend=stage2 // target=native // -// :1:5: error: variable of type 'comptime_int' must be const or comptime -// :1:5: note: to modify this variable at runtime, it must be given an explicit fixed-size number type +// :1:9: error: variable of type 'comptime_int' must be const or comptime +// :1:9: note: to modify this variable at runtime, it must be given an explicit fixed-size number type