diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2024-03-07 18:41:45 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-03-07 18:41:45 -0800 |
| commit | 97aa5f7b8a059f91b78ce7cd70cba0f3aa2c5118 (patch) | |
| tree | 4e2dc7d8083a24bb01cf8e94e684e3256b7f0a31 /src/Sema.zig | |
| parent | 377ecc6afb14a112a07c6d2c3570e2b77b12a116 (diff) | |
| parent | 38331b1cabf86586bdf70c80ed98f74c60305160 (diff) | |
| download | zig-97aa5f7b8a059f91b78ce7cd70cba0f3aa2c5118.tar.gz zig-97aa5f7b8a059f91b78ce7cd70cba0f3aa2c5118.zip | |
Merge pull request #19190 from mlugg/struct-equivalence
compiler: namespace type equivalence based on AST node + captures
Diffstat (limited to 'src/Sema.zig')
| -rw-r--r-- | src/Sema.zig | 1915 |
1 files changed, 993 insertions, 922 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index bf0aacabe5..cff7404a3b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -155,7 +155,6 @@ const Namespace = Module.Namespace; const CompileError = Module.CompileError; const SemaError = Module.SemaError; const Decl = Module.Decl; -const CaptureScope = Module.CaptureScope; const LazySrcLoc = std.zig.LazySrcLoc; const RangeSet = @import("RangeSet.zig"); const target_util = @import("target.zig"); @@ -331,8 +330,6 @@ pub const Block = struct { /// used to add a `func_instance` into the `InternPool`. params: std.MultiArrayList(Param) = .{}, - wip_capture_scope: CaptureScope.Index, - label: ?*Label = null, inlining: ?*Inlining, /// If runtime_index is not 0 then one of these is guaranteed to be non null. @@ -475,7 +472,6 @@ pub const Block = struct { .src_decl = parent.src_decl, .namespace = parent.namespace, .instructions = .{}, - .wip_capture_scope = parent.wip_capture_scope, .label = null, .inlining = parent.inlining, .is_comptime = parent.is_comptime, @@ -974,12 +970,6 @@ fn analyzeBodyInner( try sema.inst_map.ensureSpaceForInstructions(sema.gpa, body); - // Most of the time, we don't need to construct a new capture scope for a - // block. However, successive iterations of comptime loops can capture - // different values for the same Zir.Inst.Index, so in those cases, we will - // have to create nested capture scopes; see the `.repeat` case below. - const parent_capture_scope = block.wip_capture_scope; - const mod = sema.mod; const map = &sema.inst_map; const tags = sema.code.instructions.items(.tag); @@ -1028,7 +1018,6 @@ fn analyzeBodyInner( .c_import => try sema.zirCImport(block, inst), .call => try sema.zirCall(block, inst, .direct), .field_call => try sema.zirCall(block, inst, .field), - .closure_get => try sema.zirClosureGet(block, inst), .cmp_lt => try sema.zirCmp(block, inst, .lt), .cmp_lte => try sema.zirCmp(block, inst, .lte), .cmp_eq => try sema.zirCmpEq(block, inst, .eq, Air.Inst.Tag.fromCmpOp(.eq, block.float_mode == .Optimized)), @@ -1275,6 +1264,7 @@ fn analyzeBodyInner( .work_group_size => try sema.zirWorkItem( block, extended, extended.opcode), .work_group_id => try sema.zirWorkItem( block, extended, extended.opcode), .in_comptime => try sema.zirInComptime( block), + .closure_get => try sema.zirClosureGet( block, extended), // zig fmt: on .fence => { @@ -1453,11 +1443,6 @@ fn analyzeBodyInner( i += 1; continue; }, - .closure_capture => { - try sema.zirClosureCapture(block, inst); - i += 1; - continue; - }, .memcpy => { try sema.zirMemcpy(block, inst); i += 1; @@ -1534,11 +1519,6 @@ fn analyzeBodyInner( // Send comptime control flow back to the beginning of this block. const src = LazySrcLoc.nodeOffset(datas[@intFromEnum(inst)].node); try sema.emitBackwardBranch(block, src); - - // We need to construct new capture scopes for the next loop iteration so it - // can capture values without clobbering the earlier iteration's captures. - block.wip_capture_scope = try mod.createCaptureScope(parent_capture_scope); - i = 0; continue; } else { @@ -1552,11 +1532,6 @@ fn analyzeBodyInner( // Send comptime control flow back to the beginning of this block. const src = LazySrcLoc.nodeOffset(datas[@intFromEnum(inst)].node); try sema.emitBackwardBranch(block, src); - - // We need to construct new capture scopes for the next loop iteration so it - // can capture values without clobbering the earlier iteration's captures. - block.wip_capture_scope = try mod.createCaptureScope(parent_capture_scope); - i = 0; continue; }, @@ -1855,10 +1830,6 @@ fn analyzeBodyInner( map.putAssumeCapacity(inst, air_inst); i += 1; } - - // We may have overwritten the capture scope due to a `repeat` instruction where - // the body had a capture; restore it now. - block.wip_capture_scope = parent_capture_scope; } pub fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref { @@ -2698,21 +2669,61 @@ fn analyzeAsInt( return (try val.getUnsignedIntAdvanced(mod, sema)).?; } -pub fn getStructType( +/// Given a ZIR extra index which points to a list of `Zir.Inst.Capture`, +/// resolves this into a list of `InternPool.CaptureValue` allocated by `arena`. +fn getCaptures(sema: *Sema, block: *Block, extra_index: usize, captures_len: u32) ![]InternPool.CaptureValue { + const zcu = sema.mod; + const ip = &zcu.intern_pool; + const parent_captures: InternPool.CaptureValue.Slice = zcu.namespacePtr(block.namespace).getType(zcu).getCaptures(zcu); + + const captures = try sema.arena.alloc(InternPool.CaptureValue, captures_len); + + for (sema.code.extra[extra_index..][0..captures_len], captures) |raw, *capture| { + const zir_capture: Zir.Inst.Capture = @bitCast(raw); + capture.* = switch (zir_capture.unwrap()) { + .nested => |parent_idx| parent_captures.get(ip)[parent_idx], + .instruction => |inst| InternPool.CaptureValue.wrap(capture: { + const air_ref = try sema.resolveInst(inst.toRef()); + if (try sema.resolveValueResolveLazy(air_ref)) |val| { + break :capture .{ .@"comptime" = val.toIntern() }; + } + break :capture .{ .runtime = sema.typeOf(air_ref).toIntern() }; + }), + .decl_val => |str| capture: { + const decl_name = try ip.getOrPutString(sema.gpa, sema.code.nullTerminatedString(str)); + const decl = try sema.lookupIdentifier(block, .unneeded, decl_name); // TODO: could we need this src loc? + break :capture InternPool.CaptureValue.wrap(.{ .decl_val = decl }); + }, + .decl_ref => |str| capture: { + const decl_name = try ip.getOrPutString(sema.gpa, sema.code.nullTerminatedString(str)); + const decl = try sema.lookupIdentifier(block, .unneeded, decl_name); // TODO: could we need this src loc? + break :capture InternPool.CaptureValue.wrap(.{ .decl_ref = decl }); + }, + }; + } + + return captures; +} + +fn zirStructDecl( sema: *Sema, - decl: InternPool.DeclIndex, - namespace: InternPool.NamespaceIndex, - tracked_inst: InternPool.TrackedInst.Index, -) !InternPool.Index { + block: *Block, + extended: Zir.Inst.Extended.InstData, + inst: Zir.Inst.Index, +) CompileError!Air.Inst.Ref { const mod = sema.mod; const gpa = sema.gpa; const ip = &mod.intern_pool; - const zir_index = tracked_inst.resolve(ip); - const extended = sema.code.instructions.items(.data)[@intFromEnum(zir_index)].extended; - assert(extended.opcode == .struct_decl); const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); + const extra = sema.code.extraData(Zir.Inst.StructDecl, extended.operand); + const src = extra.data.src(); + var extra_index = extra.end; - var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len; + const captures_len = if (small.has_captures_len) blk: { + const captures_len = sema.code.extra[extra_index]; + extra_index += 1; + break :blk captures_len; + } else 0; const fields_len = if (small.has_fields_len) blk: { const fields_len = sema.code.extra[extra_index]; extra_index += 1; @@ -2724,6 +2735,9 @@ pub fn getStructType( break :blk decls_len; } else 0; + const captures = try sema.getCaptures(block, extra_index, captures_len); + extra_index += captures_len; + if (small.has_backing_int) { const backing_int_body_len = sema.code.extra[extra_index]; extra_index += 1; // backing_int_body_len @@ -2734,49 +2748,38 @@ pub fn getStructType( } } - const decls = sema.code.bodySlice(extra_index, decls_len); - try mod.scanNamespace(namespace, decls, mod.declPtr(decl)); - extra_index += decls_len; - - const ty = try ip.getStructType(gpa, .{ - .decl = decl, - .namespace = namespace.toOptional(), - .zir_index = tracked_inst.toOptional(), + const wip_ty = switch (try ip.getStructType(gpa, .{ .layout = small.layout, - .known_non_opv = small.known_non_opv, - .is_tuple = small.is_tuple, .fields_len = fields_len, + .known_non_opv = small.known_non_opv, .requires_comptime = if (small.known_comptime_only) .yes else .unknown, - .any_default_inits = small.any_default_inits, + .is_tuple = small.is_tuple, .any_comptime_fields = small.any_comptime_fields, + .any_default_inits = small.any_default_inits, .inits_resolved = false, .any_aligned_fields = small.any_aligned_fields, - }); - - return ty; -} - -fn zirStructDecl( - sema: *Sema, - block: *Block, - extended: Zir.Inst.Extended.InstData, - inst: Zir.Inst.Index, -) CompileError!Air.Inst.Ref { - const mod = sema.mod; - const ip = &mod.intern_pool; - const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); - const src = sema.code.extraData(Zir.Inst.StructDecl, extended.operand).data.src(); - - // Because these three things each reference each other, `undefined` - // placeholders are used before being set after the struct type gains an - // InternPool index. + .has_namespace = true or decls_len > 0, // TODO: see below + .key = .{ .declared = .{ + .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst), + .captures = captures, + } }, + })) { + .existing => |ty| return Air.internedToRef(ty), + .wip => |wip| wip: { + if (sema.builtin_type_target_index == .none) break :wip wip; + var new = wip; + new.index = sema.builtin_type_target_index; + ip.resolveBuiltinType(new.index, wip.index); + break :wip new; + }, + }; + errdefer wip_ty.cancel(ip); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", + .ty = Type.type, + .val = Value.fromInterned(wip_ty.index), }, small.name_strategy, "struct", inst); - const new_decl = mod.declPtr(new_decl_index); - new_decl.owns_tv = true; + mod.declPtr(new_decl_index).owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); if (sema.mod.comp.debug_incremental) { @@ -2787,31 +2790,21 @@ fn zirStructDecl( ); } - const new_namespace_index = try mod.createNamespace(.{ + // TODO: if AstGen tells us `@This` was not used in the fields, we can elide the namespace. + const new_namespace_index: InternPool.OptionalNamespaceIndex = if (true or decls_len > 0) (try mod.createNamespace(.{ .parent = block.namespace.toOptional(), .decl_index = new_decl_index, .file_scope = block.getFileScope(mod), - }); - errdefer mod.destroyNamespace(new_namespace_index); - - const struct_ty = ty: { - const tracked_inst = try ip.trackZir(mod.gpa, block.getFileScope(mod), inst); - const ty = try sema.getStructType(new_decl_index, new_namespace_index, tracked_inst); - if (sema.builtin_type_target_index != .none) { - ip.resolveBuiltinType(sema.builtin_type_target_index, ty); - break :ty sema.builtin_type_target_index; - } - break :ty ty; - }; - // TODO: figure out InternPool removals for incremental compilation - //errdefer ip.remove(struct_ty); + })).toOptional() else .none; + errdefer if (new_namespace_index.unwrap()) |ns| mod.destroyNamespace(ns); - new_decl.ty = Type.type; - new_decl.val = Value.fromInterned(struct_ty); + if (new_namespace_index.unwrap()) |ns| { + const decls = sema.code.bodySlice(extra_index, decls_len); + try mod.scanNamespace(ns, decls, mod.declPtr(new_decl_index)); + } - const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); try mod.finalizeAnonDecl(new_decl_index); - return decl_val; + return Air.internedToRef(wip_ty.finish(ip, new_decl_index, new_namespace_index)); } fn createAnonymousDeclTypeNamed( @@ -2827,10 +2820,9 @@ fn createAnonymousDeclTypeNamed( const ip = &mod.intern_pool; const gpa = sema.gpa; const namespace = block.namespace; - const src_scope = block.wip_capture_scope; const src_decl = mod.declPtr(block.src_decl); const src_node = src_decl.relativeToNodeIndex(src.node_offset.x); - const new_decl_index = try mod.allocateNewDecl(namespace, src_node, src_scope); + const new_decl_index = try mod.allocateNewDecl(namespace, src_node); errdefer mod.destroyDecl(new_decl_index); switch (name_strategy) { @@ -2922,6 +2914,7 @@ fn zirEnumDecl( const mod = sema.mod; const gpa = sema.gpa; + const ip = &mod.intern_pool; const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small); const extra = sema.code.extraData(Zir.Inst.EnumDecl, extended.operand); var extra_index: usize = extra.end; @@ -2935,6 +2928,12 @@ fn zirEnumDecl( break :blk tag_type_ref; } else .none; + const captures_len = if (small.has_captures_len) blk: { + const captures_len = sema.code.extra[extra_index]; + extra_index += 1; + break :blk captures_len; + } else 0; + const body_len = if (small.has_body_len) blk: { const body_len = sema.code.extra[extra_index]; extra_index += 1; @@ -2953,14 +2952,57 @@ fn zirEnumDecl( break :blk decls_len; } else 0; - // Because these three things each reference each other, `undefined` - // placeholders are used before being set after the enum type gains an - // InternPool index. + const captures = try sema.getCaptures(block, extra_index, captures_len); + extra_index += captures_len; + const decls = sema.code.bodySlice(extra_index, decls_len); + extra_index += decls_len; + + const body = sema.code.bodySlice(extra_index, body_len); + extra_index += body.len; + + const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; + const body_end = extra_index; + extra_index += bit_bags_count; + + const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { + if (bag != 0) break true; + } else false; + + const wip_ty = switch (try ip.getEnumType(gpa, .{ + .has_namespace = true or decls_len > 0, // TODO: see below + .has_values = any_values, + .tag_mode = if (small.nonexhaustive) + .nonexhaustive + else if (tag_type_ref == .none) + .auto + else + .explicit, + .fields_len = fields_len, + .key = .{ .declared = .{ + .zir_index = try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst), + .captures = captures, + } }, + })) { + .wip => |wip| wip: { + if (sema.builtin_type_target_index == .none) break :wip wip; + var new = wip; + new.index = sema.builtin_type_target_index; + ip.resolveBuiltinType(new.index, wip.index); + break :wip new; + }, + .existing => |ty| return Air.internedToRef(ty), + }; + + // Once this is `true`, we will not delete the decl or type even upon failure, since we + // have finished constructing the type and are in the process of analyzing it. var done = false; + + errdefer if (!done) wip_ty.cancel(ip); + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", + .ty = Type.type, + .val = Value.fromInterned(wip_ty.index), }, small.name_strategy, "enum", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; @@ -2974,56 +3016,21 @@ fn zirEnumDecl( ); } - const new_namespace_index = try mod.createNamespace(.{ + // TODO: if AstGen tells us `@This` was not used in the fields, we can elide the namespace. + const new_namespace_index: InternPool.OptionalNamespaceIndex = if (true or decls_len > 0) (try mod.createNamespace(.{ .parent = block.namespace.toOptional(), .decl_index = new_decl_index, .file_scope = block.getFileScope(mod), - }); - errdefer if (!done) mod.destroyNamespace(new_namespace_index); - - const decls = sema.code.bodySlice(extra_index, decls_len); - try mod.scanNamespace(new_namespace_index, decls, new_decl); - extra_index += decls_len; - - const body = sema.code.bodySlice(extra_index, body_len); - extra_index += body.len; - - const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; - const body_end = extra_index; - extra_index += bit_bags_count; - - const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { - if (bag != 0) break true; - } else false; - - const incomplete_enum = incomplete_enum: { - var incomplete_enum = try mod.intern_pool.getIncompleteEnum(gpa, .{ - .decl = new_decl_index, - .namespace = new_namespace_index.toOptional(), - .fields_len = fields_len, - .has_values = any_values, - .tag_mode = if (small.nonexhaustive) - .nonexhaustive - else if (tag_type_ref == .none) - .auto - else - .explicit, - .zir_index = (try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst)).toOptional(), - }); - if (sema.builtin_type_target_index != .none) { - mod.intern_pool.resolveBuiltinType(sema.builtin_type_target_index, incomplete_enum.index); - incomplete_enum.index = sema.builtin_type_target_index; - } - break :incomplete_enum incomplete_enum; - }; - // TODO: figure out InternPool removals for incremental compilation - //errdefer if (!done) mod.intern_pool.remove(incomplete_enum.index); + })).toOptional() else .none; + errdefer if (!done) if (new_namespace_index.unwrap()) |ns| mod.destroyNamespace(ns); - new_decl.ty = Type.type; - new_decl.val = Value.fromInterned(incomplete_enum.index); + if (new_namespace_index.unwrap()) |ns| { + try mod.scanNamespace(ns, decls, new_decl); + } - const decl_val = try sema.analyzeDeclVal(block, src, new_decl_index); - try mod.finalizeAnonDecl(new_decl_index); + // We've finished the initial construction of this type, and are about to perform analysis. + // Set the decl and namespace appropriately, and don't destroy anything on failure. + wip_ty.prepare(ip, new_decl_index, new_namespace_index); done = true; const int_tag_ty = ty: { @@ -3053,8 +3060,7 @@ fn zirEnumDecl( .parent = null, .sema = sema, .src_decl = new_decl_index, - .namespace = new_namespace_index, - .wip_capture_scope = try mod.createCaptureScope(new_decl.src_scope), + .namespace = new_namespace_index.unwrap() orelse block.namespace, .instructions = .{}, .inlining = null, .is_comptime = true, @@ -3070,7 +3076,6 @@ fn zirEnumDecl( if (ty.zigTypeTag(mod) != .Int and ty.zigTypeTag(mod) != .ComptimeInt) { return sema.fail(block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(sema.mod)}); } - incomplete_enum.setTagType(&mod.intern_pool, ty.toIntern()); break :ty ty; } else if (fields_len == 0) { break :ty try mod.intType(.unsigned, 0); @@ -3080,6 +3085,8 @@ fn zirEnumDecl( } }; + wip_ty.setTagTy(ip, int_tag_ty.toIntern()); + if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) { if (fields_len > 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(mod)) { return sema.fail(block, src, "non-exhaustive enum specifies every value", .{}); @@ -3103,7 +3110,6 @@ fn zirEnumDecl( extra_index += 2; // field name, doc comment const field_name = try mod.intern_pool.getOrPutString(gpa, field_name_zir); - assert(incomplete_enum.addFieldName(&mod.intern_pool, field_name) == null); const tag_overflow = if (has_tag_value) overflow: { const tag_val_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); @@ -3124,12 +3130,13 @@ fn zirEnumDecl( }; if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true; last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty); - if (incomplete_enum.addFieldValue(&mod.intern_pool, last_tag_val.?.toIntern())) |other_index| { + if (wip_ty.nextField(&mod.intern_pool, field_name, last_tag_val.?.toIntern())) |conflict| { + assert(conflict.kind == .value); // AstGen validated names are unique const value_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i, .range = .value, }).lazy; - const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy; + const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = conflict.prev_field_idx }).lazy; const msg = msg: { const msg = try sema.errMsg(block, value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)}); errdefer msg.destroy(gpa); @@ -3146,9 +3153,10 @@ fn zirEnumDecl( else try mod.intValue(int_tag_ty, 0); if (overflow != null) break :overflow true; - if (incomplete_enum.addFieldValue(&mod.intern_pool, last_tag_val.?.toIntern())) |other_index| { + if (wip_ty.nextField(&mod.intern_pool, field_name, last_tag_val.?.toIntern())) |conflict| { + assert(conflict.kind == .value); // AstGen validated names are unique const field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i }).lazy; - const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy; + const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = conflict.prev_field_idx }).lazy; const msg = msg: { const msg = try sema.errMsg(block, field_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)}); errdefer msg.destroy(gpa); @@ -3159,6 +3167,7 @@ fn zirEnumDecl( } break :overflow false; } else overflow: { + assert(wip_ty.nextField(&mod.intern_pool, field_name, .none) == null); last_tag_val = try mod.intValue(Type.comptime_int, field_i); if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true; last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty); @@ -3176,7 +3185,9 @@ fn zirEnumDecl( return sema.failWithOwnedErrorMsg(block, msg); } } - return decl_val; + + try mod.finalizeAnonDecl(new_decl_index); + return Air.internedToRef(wip_ty.index); } fn zirUnionDecl( @@ -3190,6 +3201,7 @@ fn zirUnionDecl( const mod = sema.mod; const gpa = sema.gpa; + const ip = &mod.intern_pool; const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); const extra = sema.code.extraData(Zir.Inst.UnionDecl, extended.operand); var extra_index: usize = extra.end; @@ -3197,6 +3209,11 @@ fn zirUnionDecl( const src = extra.data.src(); extra_index += @intFromBool(small.has_tag_type); + const captures_len = if (small.has_captures_len) blk: { + const captures_len = sema.code.extra[extra_index]; + extra_index += 1; + break :blk captures_len; + } else 0; extra_index += @intFromBool(small.has_body_len); const fields_len = if (small.has_fields_len) blk: { const fields_len = sema.code.extra[extra_index]; @@ -3210,16 +3227,53 @@ fn zirUnionDecl( break :blk decls_len; } else 0; - // Because these three things each reference each other, `undefined` - // placeholders are used before being set after the union type gains an - // InternPool index. + const captures = try sema.getCaptures(block, extra_index, captures_len); + extra_index += captures_len; + + const wip_ty = switch (try ip.getUnionType(gpa, .{ + .flags = .{ + .layout = small.layout, + .status = .none, + .runtime_tag = if (small.has_tag_type or small.auto_enum_tag) + .tagged + else if (small.layout != .Auto) + .none + else switch (block.wantSafety()) { + true => .safety, + false => .none, + }, + .any_aligned_fields = small.any_aligned_fields, + .requires_comptime = .unknown, + .assumed_runtime_bits = false, + .assumed_pointer_aligned = false, + .alignment = .none, + }, + .has_namespace = true or decls_len != 0, // TODO: see below + .fields_len = fields_len, + .enum_tag_ty = .none, // set later + .field_types = &.{}, // set later + .field_aligns = &.{}, // set later + .key = .{ .declared = .{ + .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst), + .captures = captures, + } }, + })) { + .wip => |wip| wip: { + if (sema.builtin_type_target_index == .none) break :wip wip; + var new = wip; + new.index = sema.builtin_type_target_index; + ip.resolveBuiltinType(new.index, wip.index); + break :wip new; + }, + .existing => |ty| return Air.internedToRef(ty), + }; + errdefer wip_ty.cancel(ip); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", + .ty = Type.type, + .val = Value.fromInterned(wip_ty.index), }, small.name_strategy, "union", inst); - const new_decl = mod.declPtr(new_decl_index); - new_decl.owns_tv = true; + mod.declPtr(new_decl_index).owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); if (sema.mod.comp.debug_incremental) { @@ -3230,58 +3284,22 @@ fn zirUnionDecl( ); } - const new_namespace_index = try mod.createNamespace(.{ + // TODO: if AstGen tells us `@This` was not used in the fields, we can elide the namespace. + const new_namespace_index: InternPool.OptionalNamespaceIndex = if (true or decls_len > 0) (try mod.createNamespace(.{ .parent = block.namespace.toOptional(), .decl_index = new_decl_index, .file_scope = block.getFileScope(mod), - }); - errdefer mod.destroyNamespace(new_namespace_index); - - const union_ty = ty: { - const ty = try mod.intern_pool.getUnionType(gpa, .{ - .flags = .{ - .layout = small.layout, - .status = .none, - .runtime_tag = if (small.has_tag_type or small.auto_enum_tag) - .tagged - else if (small.layout != .Auto) - .none - else switch (block.wantSafety()) { - true => .safety, - false => .none, - }, - .any_aligned_fields = small.any_aligned_fields, - .requires_comptime = .unknown, - .assumed_runtime_bits = false, - .assumed_pointer_aligned = false, - .alignment = .none, - }, - .decl = new_decl_index, - .namespace = new_namespace_index, - .zir_index = (try mod.intern_pool.trackZir(gpa, block.getFileScope(mod), inst)).toOptional(), - .fields_len = fields_len, - .enum_tag_ty = .none, - .field_types = &.{}, - .field_aligns = &.{}, - }); - if (sema.builtin_type_target_index != .none) { - mod.intern_pool.resolveBuiltinType(sema.builtin_type_target_index, ty); - break :ty sema.builtin_type_target_index; - } - break :ty ty; - }; - // TODO: figure out InternPool removals for incremental compilation - //errdefer mod.intern_pool.remove(union_ty); - - new_decl.ty = Type.type; - new_decl.val = Value.fromInterned(union_ty); + })).toOptional() else .none; + errdefer if (new_namespace_index.unwrap()) |ns| mod.destroyNamespace(ns); - const decls = sema.code.bodySlice(extra_index, decls_len); - try mod.scanNamespace(new_namespace_index, decls, new_decl); + if (new_namespace_index.unwrap()) |ns| { + const decls = sema.code.bodySlice(extra_index, decls_len); + try mod.scanNamespace(ns, decls, mod.declPtr(new_decl_index)); + } - const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); try mod.finalizeAnonDecl(new_decl_index); - return decl_val; + + return Air.internedToRef(wip_ty.finish(ip, new_decl_index, new_namespace_index)); } fn zirOpaqueDecl( @@ -3294,62 +3312,72 @@ fn zirOpaqueDecl( defer tracy.end(); const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; + const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small); const extra = sema.code.extraData(Zir.Inst.OpaqueDecl, extended.operand); var extra_index: usize = extra.end; const src = extra.data.src(); + const captures_len = if (small.has_captures_len) blk: { + const captures_len = sema.code.extra[extra_index]; + extra_index += 1; + break :blk captures_len; + } else 0; + const decls_len = if (small.has_decls_len) blk: { const decls_len = sema.code.extra[extra_index]; extra_index += 1; break :blk decls_len; } else 0; - // Because these three things each reference each other, `undefined` - // placeholders are used in two places before being set after the opaque - // type gains an InternPool index. + const captures = try sema.getCaptures(block, extra_index, captures_len); + extra_index += captures_len; + + const wip_ty = switch (try ip.getOpaqueType(gpa, .{ + .has_namespace = decls_len != 0, + .key = .{ .declared = .{ + .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst), + .captures = captures, + } }, + })) { + .wip => |wip| wip, + .existing => |ty| return Air.internedToRef(ty), + }; + errdefer wip_ty.cancel(ip); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", + .ty = Type.type, + .val = Value.fromInterned(wip_ty.index), }, small.name_strategy, "opaque", inst); - const new_decl = mod.declPtr(new_decl_index); - new_decl.owns_tv = true; + mod.declPtr(new_decl_index).owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); if (sema.mod.comp.debug_incremental) { - try mod.intern_pool.addDependency( - sema.gpa, + try ip.addDependency( + gpa, InternPool.Depender.wrap(.{ .decl = new_decl_index }), - .{ .src_hash = try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst) }, + .{ .src_hash = try ip.trackZir(gpa, block.getFileScope(mod), inst) }, ); } - const new_namespace_index = try mod.createNamespace(.{ + const new_namespace_index: InternPool.OptionalNamespaceIndex = if (decls_len > 0) (try mod.createNamespace(.{ .parent = block.namespace.toOptional(), .decl_index = new_decl_index, .file_scope = block.getFileScope(mod), - }); - errdefer mod.destroyNamespace(new_namespace_index); - - const opaque_ty = try mod.intern(.{ .opaque_type = .{ - .decl = new_decl_index, - .namespace = new_namespace_index, - .zir_index = (try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst)).toOptional(), - } }); - // TODO: figure out InternPool removals for incremental compilation - //errdefer mod.intern_pool.remove(opaque_ty); - - new_decl.ty = Type.type; - new_decl.val = Value.fromInterned(opaque_ty); + })).toOptional() else .none; + errdefer if (new_namespace_index.unwrap()) |ns| mod.destroyNamespace(ns); - const decls = sema.code.bodySlice(extra_index, decls_len); - try mod.scanNamespace(new_namespace_index, decls, new_decl); + if (new_namespace_index.unwrap()) |ns| { + const decls = sema.code.bodySlice(extra_index, decls_len); + try mod.scanNamespace(ns, decls, mod.declPtr(new_decl_index)); + } - const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); try mod.finalizeAnonDecl(new_decl_index); - return decl_val; + + return Air.internedToRef(wip_ty.finish(ip, new_decl_index, new_namespace_index)); } fn zirErrorSetDecl( @@ -5333,7 +5361,7 @@ fn failWithBadMemberAccess( fn failWithBadStructFieldAccess( sema: *Sema, block: *Block, - struct_type: InternPool.Key.StructType, + struct_type: InternPool.LoadedStructType, field_src: LazySrcLoc, field_name: InternPool.NullTerminatedString, ) CompileError { @@ -5359,7 +5387,7 @@ fn failWithBadStructFieldAccess( fn failWithBadUnionFieldAccess( sema: *Sema, block: *Block, - union_obj: InternPool.UnionType, + union_obj: InternPool.LoadedUnionType, field_src: LazySrcLoc, field_name: InternPool.NullTerminatedString, ) CompileError { @@ -5780,7 +5808,6 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr .sema = sema, .src_decl = parent_block.src_decl, .namespace = parent_block.namespace, - .wip_capture_scope = parent_block.wip_capture_scope, .instructions = .{}, .inlining = parent_block.inlining, .is_comptime = true, @@ -5831,6 +5858,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr .global = comp.config, .parent = parent_mod, .builtin_mod = parent_mod.getBuiltinDependency(), + .builtin_modules = null, // `builtin_mod` is set }) catch |err| switch (err) { // None of these are possible because we are creating a package with // the exact same configuration as the parent package, which already @@ -5900,7 +5928,6 @@ fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index, force_compt .sema = sema, .src_decl = parent_block.src_decl, .namespace = parent_block.namespace, - .wip_capture_scope = parent_block.wip_capture_scope, .instructions = .{}, .label = &label, .inlining = parent_block.inlining, @@ -7515,7 +7542,6 @@ fn analyzeCall( .sema = sema, .src_decl = module_fn.owner_decl, .namespace = fn_owner_decl.src_namespace, - .wip_capture_scope = try mod.createCaptureScope(fn_owner_decl.src_scope), .instructions = .{}, .label = null, .inlining = &inlining, @@ -8036,7 +8062,6 @@ fn instantiateGenericCall( .sema = &child_sema, .src_decl = generic_owner_func.owner_decl, .namespace = namespace_index, - .wip_capture_scope = try mod.createCaptureScope(fn_owner_decl.src_scope), .instructions = .{}, .inlining = null, .is_comptime = true, @@ -11409,7 +11434,6 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp .sema = sema, .src_decl = block.src_decl, .namespace = block.namespace, - .wip_capture_scope = block.wip_capture_scope, .instructions = .{}, .label = &label, .inlining = block.inlining, @@ -12117,7 +12141,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r .sema = sema, .src_decl = block.src_decl, .namespace = block.namespace, - .wip_capture_scope = block.wip_capture_scope, .instructions = .{}, .label = &label, .inlining = block.inlining, @@ -12281,7 +12304,6 @@ fn analyzeSwitchRuntimeBlock( extra_index += info.body_len; case_block.instructions.shrinkRetainingCapacity(0); - case_block.wip_capture_scope = try mod.createCaptureScope(child_block.wip_capture_scope); const item = case_vals.items[scalar_i]; // `item` is already guaranteed to be constant known. @@ -12339,7 +12361,6 @@ fn analyzeSwitchRuntimeBlock( case_val_idx += items_len; case_block.instructions.shrinkRetainingCapacity(0); - case_block.wip_capture_scope = child_block.wip_capture_scope; // Generate all possible cases as scalar prongs. if (info.is_inline) { @@ -12371,7 +12392,6 @@ fn analyzeSwitchRuntimeBlock( const item_ref = Air.internedToRef(item.toIntern()); case_block.instructions.shrinkRetainingCapacity(0); - case_block.wip_capture_scope = child_block.wip_capture_scope; if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) { error.NeededSourceLocation => { @@ -12411,7 +12431,6 @@ fn analyzeSwitchRuntimeBlock( cases_len += 1; case_block.instructions.shrinkRetainingCapacity(0); - case_block.wip_capture_scope = child_block.wip_capture_scope; const analyze_body = if (union_originally) blk: { const item_val = sema.resolveConstDefinedValue(block, .unneeded, item, undefined) catch unreachable; @@ -12557,7 +12576,6 @@ fn analyzeSwitchRuntimeBlock( defer gpa.free(cond_body); case_block.instructions.shrinkRetainingCapacity(0); - case_block.wip_capture_scope = try mod.createCaptureScope(child_block.wip_capture_scope); const body = sema.code.bodySlice(extra_index, info.body_len); extra_index += info.body_len; @@ -12618,7 +12636,6 @@ fn analyzeSwitchRuntimeBlock( const item_ref = Air.internedToRef(item_val.toIntern()); case_block.instructions.shrinkRetainingCapacity(0); - case_block.wip_capture_scope = child_block.wip_capture_scope; const analyze_body = if (union_originally) blk: { const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?; @@ -12669,7 +12686,6 @@ fn analyzeSwitchRuntimeBlock( const item_ref = Air.internedToRef(item_val); case_block.instructions.shrinkRetainingCapacity(0); - case_block.wip_capture_scope = child_block.wip_capture_scope; if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); emit_bb = true; @@ -12700,7 +12716,6 @@ fn analyzeSwitchRuntimeBlock( const item_ref = Air.internedToRef(cur); case_block.instructions.shrinkRetainingCapacity(0); - case_block.wip_capture_scope = child_block.wip_capture_scope; if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); emit_bb = true; @@ -12728,7 +12743,6 @@ fn analyzeSwitchRuntimeBlock( cases_len += 1; case_block.instructions.shrinkRetainingCapacity(0); - case_block.wip_capture_scope = child_block.wip_capture_scope; if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); emit_bb = true; @@ -12754,7 +12768,6 @@ fn analyzeSwitchRuntimeBlock( cases_len += 1; case_block.instructions.shrinkRetainingCapacity(0); - case_block.wip_capture_scope = child_block.wip_capture_scope; if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src); emit_bb = true; @@ -12783,7 +12796,6 @@ fn analyzeSwitchRuntimeBlock( }; case_block.instructions.shrinkRetainingCapacity(0); - case_block.wip_capture_scope = try mod.createCaptureScope(child_block.wip_capture_scope); if (mod.backendSupportsFeature(.is_named_enum_value) and special.body.len != 0 and block.wantSafety() and @@ -13327,7 +13339,7 @@ fn validateSwitchItemEnum( const ip = &sema.mod.intern_pool; const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, src_node_offset, switch_prong_src, .none); const int = ip.indexToKey(item.val).enum_tag.int; - const field_index = ip.indexToKey(ip.typeOf(item.val)).enum_type.tagValueIndex(ip, int) orelse { + const field_index = ip.loadEnumType(ip.typeOf(item.val)).tagValueIndex(ip, int) orelse { const maybe_prev_src = try range_set.add(int, int, switch_prong_src); try sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); return item.ref; @@ -13607,15 +13619,15 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai break :hf field_index < ty.structFieldCount(mod); } }, - .struct_type => |struct_type| { - break :hf struct_type.nameIndex(ip, field_name) != null; + .struct_type => { + break :hf ip.loadStructType(ty.toIntern()).nameIndex(ip, field_name) != null; }, - .union_type => |union_type| { - const union_obj = ip.loadUnionType(union_type); - break :hf union_obj.nameIndex(ip, field_name) != null; + .union_type => { + const union_type = ip.loadUnionType(ty.toIntern()); + break :hf union_type.loadTagType(ip).nameIndex(ip, field_name) != null; }, - .enum_type => |enum_type| { - break :hf enum_type.nameIndex(ip, field_name) != null; + .enum_type => { + break :hf ip.loadEnumType(ty.toIntern()).nameIndex(ip, field_name) != null; }, .array_type => break :hf ip.stringEqlSlice(field_name, "len"), else => {}, @@ -17264,49 +17276,19 @@ fn zirThis( return sema.analyzeDeclVal(block, src, this_decl_index); } -fn zirClosureCapture(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { +fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { const mod = sema.mod; - const gpa = sema.gpa; - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok; - // Closures are not necessarily constant values. For example, the - // code might do something like this: - // fn foo(x: anytype) void { const S = struct {field: @TypeOf(x)}; } - // ...in which case the closure_capture instruction has access to a runtime - // value only. In such case only the type is saved into the scope. - const operand = try sema.resolveInst(inst_data.operand); - const ty = sema.typeOf(operand); - const key: CaptureScope.Key = .{ - .zir_index = inst, - .index = block.wip_capture_scope, - }; - if (try sema.resolveValue(operand)) |val| { - try mod.comptime_capture_scopes.put(gpa, key, try val.intern(ty, mod)); - } else { - try mod.runtime_capture_scopes.put(gpa, key, ty.toIntern()); - } -} + const ip = &mod.intern_pool; + const captures = mod.namespacePtr(block.namespace).getType(mod).getCaptures(mod); -fn zirClosureGet(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const mod = sema.mod; - //const ip = &mod.intern_pool; - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].inst_node; - var scope: CaptureScope.Index = mod.declPtr(block.src_decl).src_scope; - assert(scope != .none); - // Note: The target closure must be in this scope list. - // If it's not here, the zir is invalid, or the list is broken. - const capture_ty = while (true) { - // Note: We don't need to add a dependency here, because - // decls always depend on their lexical parents. - const key: CaptureScope.Key = .{ - .zir_index = inst_data.inst, - .index = scope, - }; - if (mod.comptime_capture_scopes.get(key)) |val| - return Air.internedToRef(val); - if (mod.runtime_capture_scopes.get(key)) |ty| - break ty; - scope = scope.parent(mod); - assert(scope != .none); + const src_node: i32 = @bitCast(extended.operand); + const src = LazySrcLoc.nodeOffset(src_node); + + const capture_ty = switch (captures.get(ip)[extended.small].unwrap()) { + .@"comptime" => |index| return Air.internedToRef(index), + .runtime => |index| index, + .decl_val => |decl_index| return sema.analyzeDeclVal(block, src, decl_index), + .decl_ref => |decl_index| return sema.analyzeDeclRef(decl_index), }; // The comptime case is handled already above. Runtime case below. @@ -17322,15 +17304,15 @@ fn zirClosureGet(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! }); break :name null; }; - const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node); + const node = sema.owner_decl.relativeToNodeIndex(src_node); const token = tree.nodes.items(.main_token)[node]; break :name tree.tokenSlice(token); }; const msg = if (name) |some| - try sema.errMsg(block, inst_data.src(), "'{s}' not accessible outside function scope", .{some}) + try sema.errMsg(block, src, "'{s}' not accessible outside function scope", .{some}) else - try sema.errMsg(block, inst_data.src(), "variable not accessible outside function scope", .{}); + try sema.errMsg(block, src, "variable not accessible outside function scope", .{}); errdefer msg.destroy(sema.gpa); // TODO add "declared here" note @@ -17350,15 +17332,15 @@ fn zirClosureGet(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! }); break :name null; }; - const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node); + const node = sema.owner_decl.relativeToNodeIndex(src_node); const token = tree.nodes.items(.main_token)[node]; break :name tree.tokenSlice(token); }; const msg = if (name) |some| - try sema.errMsg(block, inst_data.src(), "'{s}' not accessible from inner function", .{some}) + try sema.errMsg(block, src, "'{s}' not accessible from inner function", .{some}) else - try sema.errMsg(block, inst_data.src(), "variable not accessible from inner function", .{}); + try sema.errMsg(block, src, "variable not accessible from inner function", .{}); errdefer msg.destroy(sema.gpa); try sema.errNote(block, LazySrcLoc.nodeOffset(0), msg, "crossed function definition here", .{}); @@ -17954,7 +17936,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } }))); }, .Enum => { - const is_exhaustive = Value.makeBool(ip.indexToKey(ty.toIntern()).enum_type.tag_mode != .nonexhaustive); + const is_exhaustive = Value.makeBool(ip.loadEnumType(ty.toIntern()).tag_mode != .nonexhaustive); const enum_field_ty = t: { const enum_field_ty_decl_index = (try sema.namespaceLookup( @@ -17968,9 +17950,9 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai break :t enum_field_ty_decl.val.toType(); }; - const enum_field_vals = try sema.arena.alloc(InternPool.Index, ip.indexToKey(ty.toIntern()).enum_type.names.len); + const enum_field_vals = try sema.arena.alloc(InternPool.Index, ip.loadEnumType(ty.toIntern()).names.len); for (enum_field_vals, 0..) |*field_val, i| { - const enum_type = ip.indexToKey(ty.toIntern()).enum_type; + const enum_type = ip.loadEnumType(ty.toIntern()); const value_val = if (enum_type.values.len > 0) try mod.intern_pool.getCoercedInts( mod.gpa, @@ -18045,7 +18027,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } }); }; - const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ip.indexToKey(ty.toIntern()).enum_type.namespace); + const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ip.loadEnumType(ty.toIntern()).namespace); const type_enum_ty = t: { const type_enum_ty_decl_index = (try sema.namespaceLookup( @@ -18061,7 +18043,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const field_values = .{ // tag_type: type, - ip.indexToKey(ty.toIntern()).enum_type.tag_ty, + ip.loadEnumType(ty.toIntern()).tag_ty, // fields: []const EnumField, fields_val, // decls: []const Declaration, @@ -18105,14 +18087,15 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai try sema.resolveTypeLayout(ty); // Getting alignment requires type layout const union_obj = mod.typeToUnion(ty).?; + const tag_type = union_obj.loadTagType(ip); const layout = union_obj.getLayout(ip); - const union_field_vals = try gpa.alloc(InternPool.Index, union_obj.field_names.len); + const union_field_vals = try gpa.alloc(InternPool.Index, tag_type.names.len); defer gpa.free(union_field_vals); for (union_field_vals, 0..) |*field_val, i| { // TODO: write something like getCoercedInts to avoid needing to dupe - const name = try sema.arena.dupeZ(u8, ip.stringToSlice(union_obj.field_names.get(ip)[i])); + const name = try sema.arena.dupeZ(u8, ip.stringToSlice(tag_type.names.get(ip)[i])); const name_val = v: { const new_decl_ty = try mod.arrayType(.{ .len = name.len, @@ -18314,7 +18297,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } break :fv; }, - .struct_type => |s| s, + .struct_type => ip.loadStructType(ty.toIntern()), else => unreachable, }; struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len); @@ -18631,7 +18614,6 @@ fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr .sema = sema, .src_decl = block.src_decl, .namespace = block.namespace, - .wip_capture_scope = block.wip_capture_scope, .instructions = .{}, .inlining = block.inlining, .is_comptime = false, @@ -18710,7 +18692,6 @@ fn zirTypeofPeer( .sema = sema, .src_decl = block.src_decl, .namespace = block.namespace, - .wip_capture_scope = block.wip_capture_scope, .instructions = .{}, .inlining = block.inlining, .is_comptime = false, @@ -19186,7 +19167,6 @@ fn ensurePostHoc(sema: *Sema, block: *Block, dest_block: Zir.Inst.Index) !*Label .sema = sema, .src_decl = block.src_decl, .namespace = block.namespace, - .wip_capture_scope = block.wip_capture_scope, .instructions = .{}, .label = &labeled_block.label, .inlining = block.inlining, @@ -20082,7 +20062,8 @@ fn finishStructInit( } } }, - .struct_type => |struct_type| { + .struct_type => { + const struct_type = ip.loadStructType(struct_ty.toIntern()); for (0..struct_type.field_types.len) |i| { if (field_inits[i] != .none) { // Coerce the init value to the field type. @@ -20683,7 +20664,8 @@ fn fieldType( try sema.anonStructFieldIndex(block, cur_ty, field_name, field_src); return Air.internedToRef(anon_struct.types.get(ip)[field_index]); }, - .struct_type => |struct_type| { + .struct_type => { + const struct_type = ip.loadStructType(cur_ty.toIntern()); const field_index = struct_type.nameIndex(ip, field_name) orelse return sema.failWithBadStructFieldAccess(block, struct_type, field_src, field_name); const field_ty = struct_type.field_types.get(ip)[field_index]; @@ -20693,7 +20675,7 @@ fn fieldType( }, .Union => { const union_obj = mod.typeToUnion(cur_ty).?; - const field_index = union_obj.nameIndex(ip, field_name) orelse + const field_index = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); const field_ty = union_obj.field_types.get(ip)[field_index]; return Air.internedToRef(field_ty); @@ -21022,7 +21004,7 @@ fn zirReify( .AnyFrame => return sema.failWithUseOfAsync(block, src), .EnumLiteral => return .enum_literal_type, .Int => { - const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; + const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const signedness_val = try Value.fromInterned(union_val.val).fieldValue( mod, struct_type.nameIndex(ip, try ip.getOrPutString(gpa, "signedness")).?, @@ -21038,7 +21020,7 @@ fn zirReify( return Air.internedToRef(ty.toIntern()); }, .Vector => { - const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; + const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const len_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, try ip.getOrPutString(gpa, "len"), @@ -21060,7 +21042,7 @@ fn zirReify( return Air.internedToRef(ty.toIntern()); }, .Float => { - const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; + const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const bits_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, try ip.getOrPutString(gpa, "bits"), @@ -21078,7 +21060,7 @@ fn zirReify( return Air.internedToRef(ty.toIntern()); }, .Pointer => { - const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; + const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const size_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, try ip.getOrPutString(gpa, "size"), @@ -21190,7 +21172,7 @@ fn zirReify( return Air.internedToRef(ty.toIntern()); }, .Array => { - const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; + const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const len_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, try ip.getOrPutString(gpa, "len"), @@ -21219,7 +21201,7 @@ fn zirReify( return Air.internedToRef(ty.toIntern()); }, .Optional => { - const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; + const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const child_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, try ip.getOrPutString(gpa, "child"), @@ -21231,7 +21213,7 @@ fn zirReify( return Air.internedToRef(ty.toIntern()); }, .ErrorUnion => { - const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; + const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const error_set_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, try ip.getOrPutString(gpa, "error_set"), @@ -21260,7 +21242,7 @@ fn zirReify( try names.ensureUnusedCapacity(sema.arena, len); for (0..len) |i| { const elem_val = try payload_val.elemValue(mod, i); - const elem_struct_type = ip.indexToKey(ip.typeOf(elem_val.toIntern())).struct_type; + const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern())); const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( ip, try ip.getOrPutString(gpa, "name"), @@ -21280,7 +21262,7 @@ fn zirReify( return Air.internedToRef(ty.toIntern()); }, .Struct => { - const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; + const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const layout_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, try ip.getOrPutString(gpa, "layout"), @@ -21316,7 +21298,7 @@ fn zirReify( return try sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_val, name_strategy, is_tuple_val.toBool()); }, .Enum => { - const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; + const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, try ip.getOrPutString(gpa, "tag_type"), @@ -21334,105 +21316,14 @@ fn zirReify( try ip.getOrPutString(gpa, "is_exhaustive"), ).?); - // Decls if (decls_val.sliceLen(mod) > 0) { return sema.fail(block, src, "reified enums must have no decls", .{}); } - const int_tag_ty = tag_type_val.toType(); - if (int_tag_ty.zigTypeTag(mod) != .Int) { - return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{}); - } - - // Because these things each reference each other, `undefined` - // placeholders are used before being set after the enum type gains - // an InternPool index. - - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", - }, name_strategy, "enum", inst); - const new_decl = mod.declPtr(new_decl_index); - new_decl.owns_tv = true; - errdefer { - new_decl.has_tv = false; // namespace and val were destroyed by later errdefers - mod.abortAnonDecl(new_decl_index); - } - - // Define our empty enum decl - const fields_len: u32 = @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod))); - const incomplete_enum = try ip.getIncompleteEnum(gpa, .{ - .decl = new_decl_index, - .namespace = .none, - .fields_len = fields_len, - .has_values = true, - .tag_mode = if (!is_exhaustive_val.toBool()) - .nonexhaustive - else - .explicit, - .tag_ty = int_tag_ty.toIntern(), - .zir_index = .none, - }); - // TODO: figure out InternPool removals for incremental compilation - //errdefer ip.remove(incomplete_enum.index); - - new_decl.ty = Type.type; - new_decl.val = Value.fromInterned(incomplete_enum.index); - - for (0..fields_len) |field_i| { - const elem_val = try fields_val.elemValue(mod, field_i); - const elem_struct_type = ip.indexToKey(ip.typeOf(elem_val.toIntern())).struct_type; - const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "name"), - ).?); - const value_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "value"), - ).?); - - const field_name = try name_val.toIpString(Type.slice_const_u8, mod); - - if (!try sema.intFitsInType(value_val, int_tag_ty, null)) { - // TODO: better source location - return sema.fail(block, src, "field '{}' with enumeration value '{}' is too large for backing int type '{}'", .{ - field_name.fmt(ip), - value_val.fmtValue(Type.comptime_int, mod), - int_tag_ty.fmt(mod), - }); - } - - if (incomplete_enum.addFieldName(ip, field_name)) |other_index| { - const msg = msg: { - const msg = try sema.errMsg(block, src, "duplicate enum field '{}'", .{ - field_name.fmt(ip), - }); - errdefer msg.destroy(gpa); - _ = other_index; // TODO: this note is incorrect - try sema.errNote(block, src, msg, "other field here", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - - if (incomplete_enum.addFieldValue(ip, (try mod.getCoerced(value_val, int_tag_ty)).toIntern())) |other| { - const msg = msg: { - const msg = try sema.errMsg(block, src, "enum tag value {} already taken", .{value_val.fmtValue(Type.comptime_int, mod)}); - errdefer msg.destroy(gpa); - _ = other; // TODO: this note is incorrect - try sema.errNote(block, src, msg, "other enum tag value here", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - } - - const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); - try mod.finalizeAnonDecl(new_decl_index); - return decl_val; + return sema.reifyEnum(block, inst, src, tag_type_val.toType(), is_exhaustive_val.toBool(), fields_val, name_strategy); }, .Opaque => { - const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; + const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const decls_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, try ip.getOrPutString(gpa, "decls"), @@ -21443,45 +21334,30 @@ fn zirReify( return sema.fail(block, src, "reified opaque must have no decls", .{}); } - // Because these three things each reference each other, - // `undefined` placeholders are used in two places before being set - // after the opaque type gains an InternPool index. + const wip_ty = switch (try ip.getOpaqueType(gpa, .{ + .has_namespace = false, + .key = .{ .reified = .{ + .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst), + } }, + })) { + .existing => |ty| return Air.internedToRef(ty), + .wip => |wip| wip, + }; + errdefer wip_ty.cancel(ip); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", + .ty = Type.type, + .val = Value.fromInterned(wip_ty.index), }, name_strategy, "opaque", inst); - const new_decl = mod.declPtr(new_decl_index); - new_decl.owns_tv = true; - errdefer { - new_decl.has_tv = false; // namespace and val were destroyed by later errdefers - mod.abortAnonDecl(new_decl_index); - } - - const new_namespace_index = try mod.createNamespace(.{ - .parent = block.namespace.toOptional(), - .decl_index = new_decl_index, - .file_scope = block.getFileScope(mod), - }); - errdefer mod.destroyNamespace(new_namespace_index); - - const opaque_ty = try mod.intern(.{ .opaque_type = .{ - .decl = new_decl_index, - .namespace = new_namespace_index, - .zir_index = .none, - } }); - // TODO: figure out InternPool removals for incremental compilation - //errdefer ip.remove(opaque_ty); + mod.declPtr(new_decl_index).owns_tv = true; + errdefer mod.abortAnonDecl(new_decl_index); - new_decl.ty = Type.type; - new_decl.val = Value.fromInterned(opaque_ty); - - const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); try mod.finalizeAnonDecl(new_decl_index); - return decl_val; + + return Air.internedToRef(wip_ty.finish(ip, new_decl_index, .none)); }, .Union => { - const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; + const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const layout_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, try ip.getOrPutString(gpa, "layout"), @@ -21499,216 +21375,15 @@ fn zirReify( try ip.getOrPutString(gpa, "decls"), ).?); - // Decls if (decls_val.sliceLen(mod) > 0) { return sema.fail(block, src, "reified unions must have no decls", .{}); } const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val); - const fields_len: u32 = @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod))); - - // Tag type - var explicit_tags_seen: []bool = &.{}; - var enum_field_names: []InternPool.NullTerminatedString = &.{}; - var enum_tag_ty: InternPool.Index = .none; - if (tag_type_val.optionalValue(mod)) |payload_val| { - enum_tag_ty = payload_val.toType().toIntern(); - - const enum_type = switch (ip.indexToKey(enum_tag_ty)) { - .enum_type => |x| x, - else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}), - }; - - explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len); - @memset(explicit_tags_seen, false); - } else { - enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); - } - - // Fields - var any_aligned_fields: bool = false; - var union_fields: std.MultiArrayList(struct { - type: InternPool.Index, - alignment: InternPool.Alignment, - }) = .{}; - var field_name_table: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; - try field_name_table.ensureTotalCapacity(sema.arena, fields_len); - - for (0..fields_len) |i| { - const elem_val = try fields_val.elemValue(mod, i); - const elem_struct_type = ip.indexToKey(ip.typeOf(elem_val.toIntern())).struct_type; - const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "name"), - ).?); - const type_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "type"), - ).?); - const alignment_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "alignment"), - ).?); - - const field_name = try name_val.toIpString(Type.slice_const_u8, mod); - - if (enum_field_names.len != 0) { - enum_field_names[i] = field_name; - } - - if (enum_tag_ty != .none) { - const tag_info = ip.indexToKey(enum_tag_ty).enum_type; - const enum_index = tag_info.nameIndex(ip, field_name) orelse { - return sema.fail(block, src, "no field named '{}' in enum '{}'", .{ - field_name.fmt(ip), Type.fromInterned(enum_tag_ty).fmt(mod), - }); - }; - assert(explicit_tags_seen.len == tag_info.names.len); - // No check for duplicate because the check already happened in order - // to create the enum type in the first place. - assert(!explicit_tags_seen[enum_index]); - explicit_tags_seen[enum_index] = true; - } - - const gop = field_name_table.getOrPutAssumeCapacity(field_name); - if (gop.found_existing) { - // TODO: better source location - return sema.fail(block, src, "duplicate union field {}", .{field_name.fmt(ip)}); - } - - const field_ty = type_val.toType(); - const alignment_val_int = (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?; - if (alignment_val_int > 0 and !math.isPowerOfTwo(alignment_val_int)) { - // TODO: better source location - return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{ - alignment_val_int, - }); - } - const field_align = Alignment.fromByteUnits(alignment_val_int); - any_aligned_fields = any_aligned_fields or field_align != .none; - - try union_fields.append(sema.arena, .{ - .type = field_ty.toIntern(), - .alignment = field_align, - }); - - if (field_ty.zigTypeTag(mod) == .Opaque) { - const msg = msg: { - const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); - errdefer msg.destroy(gpa); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - if (layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) { - const msg = msg: { - const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); - errdefer msg.destroy(gpa); - - const src_decl = mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(msg, src_decl.toSrcLoc(src, mod), field_ty, .union_field); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } else if (layout == .Packed and !try sema.validatePackedType(field_ty)) { - const msg = msg: { - const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); - errdefer msg.destroy(gpa); - - const src_decl = mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotPacked(msg, src_decl.toSrcLoc(src, mod), field_ty); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - } - - if (enum_tag_ty != .none) { - const tag_info = ip.indexToKey(enum_tag_ty).enum_type; - if (tag_info.names.len > fields_len) { - const msg = msg: { - const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{}); - errdefer msg.destroy(gpa); - assert(explicit_tags_seen.len == tag_info.names.len); - for (tag_info.names.get(ip), 0..) |field_name, field_index| { - if (explicit_tags_seen[field_index]) continue; - try sema.addFieldErrNote(Type.fromInterned(enum_tag_ty), field_index, msg, "field '{}' missing, declared here", .{ - field_name.fmt(ip), - }); - } - try sema.addDeclaredHereNote(msg, Type.fromInterned(enum_tag_ty)); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - } else { - enum_tag_ty = try sema.generateUnionTagTypeSimple(block, enum_field_names, .none); - } - - // Because these three things each reference each other, `undefined` - // placeholders are used before being set after the union type gains an - // InternPool index. - - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", - }, name_strategy, "union", inst); - const new_decl = mod.declPtr(new_decl_index); - new_decl.owns_tv = true; - errdefer { - new_decl.has_tv = false; // namespace and val were destroyed by later errdefers - mod.abortAnonDecl(new_decl_index); - } - - const new_namespace_index = try mod.createNamespace(.{ - .parent = block.namespace.toOptional(), - .decl_index = new_decl_index, - .file_scope = block.getFileScope(mod), - }); - errdefer mod.destroyNamespace(new_namespace_index); - - const union_ty = try ip.getUnionType(gpa, .{ - .decl = new_decl_index, - .namespace = new_namespace_index, - .enum_tag_ty = enum_tag_ty, - .fields_len = fields_len, - .zir_index = .none, - .flags = .{ - .layout = layout, - .status = .have_field_types, - .runtime_tag = if (!tag_type_val.isNull(mod)) - .tagged - else if (layout != .Auto) - .none - else switch (block.wantSafety()) { - true => .safety, - false => .none, - }, - .any_aligned_fields = any_aligned_fields, - .requires_comptime = .unknown, - .assumed_runtime_bits = false, - .assumed_pointer_aligned = false, - .alignment = .none, - }, - .field_types = union_fields.items(.type), - .field_aligns = if (any_aligned_fields) union_fields.items(.alignment) else &.{}, - }); - - new_decl.ty = Type.type; - new_decl.val = Value.fromInterned(union_ty); - - const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); - try mod.finalizeAnonDecl(new_decl_index); - return decl_val; + return sema.reifyUnion(block, inst, src, layout, tag_type_val, fields_val, name_strategy); }, .Fn => { - const struct_type = ip.indexToKey(ip.typeOf(union_val.val)).struct_type; + const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const calling_convention_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, try ip.getOrPutString(gpa, "calling_convention"), @@ -21759,7 +21434,7 @@ fn zirReify( var noalias_bits: u32 = 0; for (param_types, 0..) |*param_type, i| { const elem_val = try params_val.elemValue(mod, i); - const elem_struct_type = ip.indexToKey(ip.typeOf(elem_val.toIntern())).struct_type; + const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern())); const param_is_generic_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( ip, try ip.getOrPutString(gpa, "is_generic"), @@ -21804,126 +21479,492 @@ fn zirReify( } } -fn reifyStruct( +fn reifyEnum( sema: *Sema, block: *Block, inst: Zir.Inst.Index, src: LazySrcLoc, - layout: std.builtin.Type.ContainerLayout, - backing_int_val: Value, + tag_ty: Type, + is_exhaustive: bool, fields_val: Value, name_strategy: Zir.Inst.NameStrategy, - is_tuple: bool, ) CompileError!Air.Inst.Ref { const mod = sema.mod; const gpa = sema.gpa; const ip = &mod.intern_pool; - if (is_tuple) switch (layout) { - .Extern => return sema.fail(block, src, "extern tuples are not supported", .{}), - .Packed => return sema.fail(block, src, "packed tuples are not supported", .{}), - .Auto => {}, + // This logic must stay in sync with the structure of `std.builtin.Type.Enum` - search for `fieldValue`. + + const fields_len: u32 = @intCast(fields_val.sliceLen(mod)); + + // The validation work here is non-trivial, and it's possible the type already exists. + // So in this first pass, let's just construct a hash to optimize for this case. If the + // inputs turn out to be invalid, we can cancel the WIP type later. + + // For deduplication purposes, we must create a hash including all details of this type. + // TODO: use a longer hash! + var hasher = std.hash.Wyhash.init(0); + std.hash.autoHash(&hasher, tag_ty.toIntern()); + std.hash.autoHash(&hasher, is_exhaustive); + std.hash.autoHash(&hasher, fields_len); + + for (0..fields_len) |field_idx| { + const field_info = try fields_val.elemValue(mod, field_idx); + + const field_name_val = try field_info.fieldValue(mod, 0); + const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(mod, 1)); + + const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod); + + std.hash.autoHash(&hasher, .{ + field_name, + field_value_val.toIntern(), + }); + } + + const wip_ty = switch (try ip.getEnumType(gpa, .{ + .has_namespace = false, + .has_values = true, + .tag_mode = if (is_exhaustive) .explicit else .nonexhaustive, + .fields_len = fields_len, + .key = .{ .reified = .{ + .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst), + .type_hash = hasher.final(), + } }, + })) { + .wip => |wip| wip, + .existing => |ty| return Air.internedToRef(ty), }; + errdefer wip_ty.cancel(ip); + + if (tag_ty.zigTypeTag(mod) != .Int) { + return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{}); + } + + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ + .ty = Type.type, + .val = Value.fromInterned(wip_ty.index), + }, name_strategy, "enum", inst); + mod.declPtr(new_decl_index).owns_tv = true; + errdefer mod.abortAnonDecl(new_decl_index); + + wip_ty.prepare(ip, new_decl_index, .none); + wip_ty.setTagTy(ip, tag_ty.toIntern()); + + for (0..fields_len) |field_idx| { + const field_info = try fields_val.elemValue(mod, field_idx); + + const field_name_val = try field_info.fieldValue(mod, 0); + const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(mod, 1)); + + const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod); + + if (!try sema.intFitsInType(field_value_val, tag_ty, null)) { + // TODO: better source location + return sema.fail(block, src, "field '{}' with enumeration value '{}' is too large for backing int type '{}'", .{ + field_name.fmt(ip), + field_value_val.fmtValue(Type.comptime_int, mod), + tag_ty.fmt(mod), + }); + } + + const coerced_field_val = try mod.getCoerced(field_value_val, tag_ty); + if (wip_ty.nextField(ip, field_name, coerced_field_val.toIntern())) |conflict| { + return sema.failWithOwnedErrorMsg(block, switch (conflict.kind) { + .name => msg: { + const msg = try sema.errMsg(block, src, "duplicate enum field '{}'", .{field_name.fmt(ip)}); + errdefer msg.destroy(gpa); + _ = conflict.prev_field_idx; // TODO: this note is incorrect + try sema.errNote(block, src, msg, "other field here", .{}); + break :msg msg; + }, + .value => msg: { + const msg = try sema.errMsg(block, src, "enum tag value {} already taken", .{field_value_val.fmtValue(Type.comptime_int, mod)}); + errdefer msg.destroy(gpa); + _ = conflict.prev_field_idx; // TODO: this note is incorrect + try sema.errNote(block, src, msg, "other enum tag value here", .{}); + break :msg msg; + }, + }); + } + } - const fields_len: u32 = @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod))); + if (!is_exhaustive and fields_len > 1 and std.math.log2_int(u64, fields_len) == tag_ty.bitSize(mod)) { + return sema.fail(block, src, "non-exhaustive enum specified every value", .{}); + } - // Because these three things each reference each other, `undefined` - // placeholders are used before being set after the struct type gains an - // InternPool index. + try mod.finalizeAnonDecl(new_decl_index); + return Air.internedToRef(wip_ty.index); +} + +fn reifyUnion( + sema: *Sema, + block: *Block, + inst: Zir.Inst.Index, + src: LazySrcLoc, + layout: std.builtin.Type.ContainerLayout, + opt_tag_type_val: Value, + fields_val: Value, + name_strategy: Zir.Inst.NameStrategy, +) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; + + // This logic must stay in sync with the structure of `std.builtin.Type.Union` - search for `fieldValue`. + + const fields_len: u32 = @intCast(fields_val.sliceLen(mod)); + + // The validation work here is non-trivial, and it's possible the type already exists. + // So in this first pass, let's just construct a hash to optimize for this case. If the + // inputs turn out to be invalid, we can cancel the WIP type later. + + // For deduplication purposes, we must create a hash including all details of this type. + // TODO: use a longer hash! + var hasher = std.hash.Wyhash.init(0); + std.hash.autoHash(&hasher, layout); + std.hash.autoHash(&hasher, opt_tag_type_val.toIntern()); + std.hash.autoHash(&hasher, fields_len); + + var any_aligns = false; + + for (0..fields_len) |field_idx| { + const field_info = try fields_val.elemValue(mod, field_idx); + + const field_name_val = try field_info.fieldValue(mod, 0); + const field_type_val = try field_info.fieldValue(mod, 1); + const field_align_val = try sema.resolveLazyValue(try field_info.fieldValue(mod, 2)); + + const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod); + + std.hash.autoHash(&hasher, .{ + field_name, + field_type_val.toIntern(), + field_align_val.toIntern(), + }); + + if (field_align_val.toUnsignedInt(mod) != 0) { + any_aligns = true; + } + } + + const wip_ty = switch (try ip.getUnionType(gpa, .{ + .flags = .{ + .layout = layout, + .status = .none, + .runtime_tag = if (opt_tag_type_val.optionalValue(mod) != null) + .tagged + else if (layout != .Auto) + .none + else switch (block.wantSafety()) { + true => .safety, + false => .none, + }, + .any_aligned_fields = any_aligns, + .requires_comptime = .unknown, + .assumed_runtime_bits = false, + .assumed_pointer_aligned = false, + .alignment = .none, + }, + .has_namespace = false, + .fields_len = fields_len, + .enum_tag_ty = .none, // set later because not yet validated + .field_types = &.{}, // set later + .field_aligns = &.{}, // set later + .key = .{ .reified = .{ + .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst), + .type_hash = hasher.final(), + } }, + })) { + .wip => |wip| wip, + .existing => |ty| return Air.internedToRef(ty), + }; + errdefer wip_ty.cancel(ip); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", - }, name_strategy, "struct", inst); - const new_decl = mod.declPtr(new_decl_index); - new_decl.owns_tv = true; - errdefer { - new_decl.has_tv = false; // namespace and val were destroyed by later errdefers - mod.abortAnonDecl(new_decl_index); + .ty = Type.type, + .val = Value.fromInterned(wip_ty.index), + }, name_strategy, "union", inst); + mod.declPtr(new_decl_index).owns_tv = true; + errdefer mod.abortAnonDecl(new_decl_index); + + const field_types = try sema.arena.alloc(InternPool.Index, fields_len); + const field_aligns = if (any_aligns) try sema.arena.alloc(InternPool.Alignment, fields_len) else undefined; + + const enum_tag_ty, const has_explicit_tag = if (opt_tag_type_val.optionalValue(mod)) |tag_type_val| tag_ty: { + switch (ip.indexToKey(tag_type_val.toIntern())) { + .enum_type => {}, + else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}), + } + const enum_tag_ty = tag_type_val.toType(); + + // We simply track which fields of the tag type have been seen. + const tag_ty_fields_len = enum_tag_ty.enumFieldCount(mod); + var seen_tags = try std.DynamicBitSetUnmanaged.initEmpty(sema.arena, tag_ty_fields_len); + + for (field_types, 0..) |*field_ty, field_idx| { + const field_info = try fields_val.elemValue(mod, field_idx); + + const field_name_val = try field_info.fieldValue(mod, 0); + const field_type_val = try field_info.fieldValue(mod, 1); + + const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod); + + const enum_index = enum_tag_ty.enumFieldIndex(field_name, mod) orelse { + // TODO: better source location + return sema.fail(block, src, "no field named '{}' in enum '{}'", .{ + field_name.fmt(ip), enum_tag_ty.fmt(mod), + }); + }; + if (seen_tags.isSet(enum_index)) { + // TODO: better source location + return sema.fail(block, src, "duplicate union field {}", .{field_name.fmt(ip)}); + } + seen_tags.set(enum_index); + + field_ty.* = field_type_val.toIntern(); + if (any_aligns) { + const byte_align = try (try field_info.fieldValue(mod, 2)).toUnsignedIntAdvanced(sema); + if (byte_align > 0 and !math.isPowerOfTwo(byte_align)) { + // TODO: better source location + return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{byte_align}); + } + field_aligns[field_idx] = Alignment.fromByteUnits(byte_align); + } + } + + if (tag_ty_fields_len > fields_len) return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(block, src, "enum fields missing in union", .{}); + errdefer msg.destroy(gpa); + var it = seen_tags.iterator(.{ .kind = .unset }); + while (it.next()) |enum_index| { + const field_name = enum_tag_ty.enumFieldName(enum_index, mod); + try sema.addFieldErrNote(enum_tag_ty, enum_index, msg, "field '{}' missing, declared here", .{ + field_name.fmt(ip), + }); + } + try sema.addDeclaredHereNote(msg, enum_tag_ty); + break :msg msg; + }); + + break :tag_ty .{ enum_tag_ty.toIntern(), true }; + } else tag_ty: { + // We must track field names and set up the tag type ourselves. + var field_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; + try field_names.ensureTotalCapacity(sema.arena, fields_len); + + for (field_types, 0..) |*field_ty, field_idx| { + const field_info = try fields_val.elemValue(mod, field_idx); + + const field_name_val = try field_info.fieldValue(mod, 0); + const field_type_val = try field_info.fieldValue(mod, 1); + + const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod); + const gop = field_names.getOrPutAssumeCapacity(field_name); + if (gop.found_existing) { + // TODO: better source location + return sema.fail(block, src, "duplicate union field {}", .{field_name.fmt(ip)}); + } + + field_ty.* = field_type_val.toIntern(); + if (any_aligns) { + const byte_align = try (try field_info.fieldValue(mod, 2)).toUnsignedIntAdvanced(sema); + if (byte_align > 0 and !math.isPowerOfTwo(byte_align)) { + // TODO: better source location + return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{byte_align}); + } + field_aligns[field_idx] = Alignment.fromByteUnits(byte_align); + } + } + + const enum_tag_ty = try sema.generateUnionTagTypeSimple(block, field_names.keys(), mod.declPtr(new_decl_index)); + break :tag_ty .{ enum_tag_ty, false }; + }; + errdefer if (!has_explicit_tag) ip.remove(enum_tag_ty); // remove generated tag type on error + + for (field_types) |field_ty_ip| { + const field_ty = Type.fromInterned(field_ty_ip); + if (field_ty.zigTypeTag(mod) == .Opaque) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); + errdefer msg.destroy(gpa); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + } + if (layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); + errdefer msg.destroy(gpa); + + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(msg, src_decl.toSrcLoc(src, mod), field_ty, .union_field); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + } else if (layout == .Packed and !try sema.validatePackedType(field_ty)) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); + errdefer msg.destroy(gpa); + + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotPacked(msg, src_decl.toSrcLoc(src, mod), field_ty); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + } } - const ty = try ip.getStructType(gpa, .{ - .decl = new_decl_index, - .namespace = .none, - .zir_index = .none, + const loaded_union = ip.loadUnionType(wip_ty.index); + loaded_union.setFieldTypes(ip, field_types); + if (any_aligns) { + loaded_union.setFieldAligns(ip, field_aligns); + } + loaded_union.tagTypePtr(ip).* = enum_tag_ty; + loaded_union.flagsPtr(ip).status = .have_field_types; + + try mod.finalizeAnonDecl(new_decl_index); + return Air.internedToRef(wip_ty.finish(ip, new_decl_index, .none)); +} + +fn reifyStruct( + sema: *Sema, + block: *Block, + inst: Zir.Inst.Index, + src: LazySrcLoc, + layout: std.builtin.Type.ContainerLayout, + opt_backing_int_val: Value, + fields_val: Value, + name_strategy: Zir.Inst.NameStrategy, + is_tuple: bool, +) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; + + // This logic must stay in sync with the structure of `std.builtin.Type.Struct` - search for `fieldValue`. + + const fields_len: u32 = @intCast(fields_val.sliceLen(mod)); + + // The validation work here is non-trivial, and it's possible the type already exists. + // So in this first pass, let's just construct a hash to optimize for this case. If the + // inputs turn out to be invalid, we can cancel the WIP type later. + + // For deduplication purposes, we must create a hash including all details of this type. + // TODO: use a longer hash! + var hasher = std.hash.Wyhash.init(0); + std.hash.autoHash(&hasher, layout); + std.hash.autoHash(&hasher, opt_backing_int_val.toIntern()); + std.hash.autoHash(&hasher, is_tuple); + std.hash.autoHash(&hasher, fields_len); + + var any_comptime_fields = false; + var any_default_inits = false; + var any_aligned_fields = false; + + for (0..fields_len) |field_idx| { + const field_info = try fields_val.elemValue(mod, field_idx); + + const field_name_val = try field_info.fieldValue(mod, 0); + const field_type_val = try field_info.fieldValue(mod, 1); + const field_default_value_val = try field_info.fieldValue(mod, 2); + const field_is_comptime_val = try field_info.fieldValue(mod, 3); + const field_alignment_val = try sema.resolveLazyValue(try field_info.fieldValue(mod, 4)); + + const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod); + const field_is_comptime = field_is_comptime_val.toBool(); + const field_default_value: InternPool.Index = if (field_default_value_val.optionalValue(mod)) |ptr_val| d: { + const ptr_ty = try mod.singleConstPtrType(field_type_val.toType()); + // We need to do this deref here, so we won't check for this error case later on. + const val = try sema.pointerDeref(block, src, ptr_val, ptr_ty) orelse return sema.failWithNeededComptime( + block, + src, + .{ .needed_comptime_reason = "struct field default value must be comptime-known" }, + ); + // Resolve the value so that lazy values do not create distinct types. + break :d (try sema.resolveLazyValue(val)).toIntern(); + } else .none; + + std.hash.autoHash(&hasher, .{ + field_name, + field_type_val.toIntern(), + field_default_value, + field_is_comptime, + field_alignment_val.toIntern(), + }); + + if (field_is_comptime) any_comptime_fields = true; + if (field_default_value != .none) any_default_inits = true; + switch (try field_alignment_val.orderAgainstZeroAdvanced(mod, sema)) { + .eq => {}, + .gt => any_aligned_fields = true, + .lt => unreachable, + } + } + + const wip_ty = switch (try ip.getStructType(gpa, .{ .layout = layout, - .known_non_opv = false, .fields_len = fields_len, + .known_non_opv = false, .requires_comptime = .unknown, .is_tuple = is_tuple, - // So that we don't have to scan ahead, we allocate space in the struct - // type for alignments, comptime fields, and default inits. This might - // result in wasted space, however, this is a permitted encoding of - // struct types. - .any_comptime_fields = true, - .any_default_inits = true, + .any_comptime_fields = any_comptime_fields, + .any_default_inits = any_default_inits, + .any_aligned_fields = any_aligned_fields, .inits_resolved = true, - .any_aligned_fields = true, - }); - // TODO: figure out InternPool removals for incremental compilation - //errdefer ip.remove(ty); - const struct_type = ip.indexToKey(ty).struct_type; + .has_namespace = false, + .key = .{ .reified = .{ + .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst), + .type_hash = hasher.final(), + } }, + })) { + .wip => |wip| wip, + .existing => |ty| return Air.internedToRef(ty), + }; + errdefer wip_ty.cancel(ip); - new_decl.ty = Type.type; - new_decl.val = Value.fromInterned(ty); - - // Fields - for (0..fields_len) |i| { - const elem_val = try fields_val.elemValue(mod, i); - const elem_struct_type = ip.indexToKey(ip.typeOf(elem_val.toIntern())).struct_type; - const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "name"), - ).?); - const type_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "type"), - ).?); - const default_value_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "default_value"), - ).?); - const is_comptime_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "is_comptime"), - ).?); - const alignment_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "alignment"), - ).?); - - if (!try sema.intFitsInType(alignment_val, Type.u32, null)) { - return sema.fail(block, src, "alignment must fit in 'u32'", .{}); - } - const abi_align = (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?; - - if (layout == .Packed) { - if (abi_align != 0) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{}); - if (is_comptime_val.toBool()) return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{}); - } else { - if (abi_align > 0 and !math.isPowerOfTwo(abi_align)) return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{abi_align}); - struct_type.field_aligns.get(ip)[i] = Alignment.fromByteUnits(abi_align); - } - if (layout == .Extern and is_comptime_val.toBool()) { - return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{}); - } + if (is_tuple) switch (layout) { + .Extern => return sema.fail(block, src, "extern tuples are not supported", .{}), + .Packed => return sema.fail(block, src, "packed tuples are not supported", .{}), + .Auto => {}, + }; - const field_name = try name_val.toIpString(Type.slice_const_u8, mod); + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ + .ty = Type.type, + .val = Value.fromInterned(wip_ty.index), + }, name_strategy, "struct", inst); + mod.declPtr(new_decl_index).owns_tv = true; + errdefer mod.abortAnonDecl(new_decl_index); + + const struct_type = ip.loadStructType(wip_ty.index); + for (0..fields_len) |field_idx| { + const field_info = try fields_val.elemValue(mod, field_idx); + + const field_name_val = try field_info.fieldValue(mod, 0); + const field_type_val = try field_info.fieldValue(mod, 1); + const field_default_value_val = try field_info.fieldValue(mod, 2); + const field_is_comptime_val = try field_info.fieldValue(mod, 3); + const field_alignment_val = try field_info.fieldValue(mod, 4); + + const field_ty = field_type_val.toType(); + const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod); if (is_tuple) { - const field_index = field_name.toUnsigned(ip) orelse return sema.fail( + const field_name_index = field_name.toUnsigned(ip) orelse return sema.fail( block, src, "tuple cannot have non-numeric field '{}'", .{field_name.fmt(ip)}, ); - - if (field_index >= fields_len) { + if (field_name_index != field_idx) { return sema.fail( block, src, - "tuple field {} exceeds tuple field count", - .{field_index}, + "tuple field name '{}' does not match field index {}", + .{ field_name_index, field_idx }, ); } } else if (struct_type.addFieldName(ip, field_name)) |prev_index| { @@ -21931,45 +21972,72 @@ fn reifyStruct( return sema.fail(block, src, "duplicate struct field name {}", .{field_name.fmt(ip)}); } - const field_ty = type_val.toType(); - const default_val = if (default_value_val.optionalValue(mod)) |opt_val| - (try sema.pointerDeref(block, src, opt_val, try mod.singleConstPtrType(field_ty)) orelse - return sema.failWithNeededComptime(block, src, .{ - .needed_comptime_reason = "struct field default value must be comptime-known", - })).toIntern() - else - .none; - if (is_comptime_val.toBool() and default_val == .none) { + if (any_aligned_fields) { + if (!try sema.intFitsInType(field_alignment_val, Type.u32, null)) { + return sema.fail(block, src, "alignment must fit in 'u32'", .{}); + } + + const byte_align = try field_alignment_val.toUnsignedIntAdvanced(sema); + if (byte_align == 0) { + if (layout != .Packed) { + struct_type.field_aligns.get(ip)[field_idx] = .none; + } + } else { + if (layout == .Packed) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{}); + if (!math.isPowerOfTwo(byte_align)) return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{byte_align}); + struct_type.field_aligns.get(ip)[field_idx] = Alignment.fromNonzeroByteUnits(byte_align); + } + } + + const field_is_comptime = field_is_comptime_val.toBool(); + if (field_is_comptime) { + assert(any_comptime_fields); + switch (layout) { + .Extern => return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{}), + .Packed => return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{}), + .Auto => struct_type.setFieldComptime(ip, field_idx), + } + } + + const field_default: InternPool.Index = d: { + if (!any_default_inits) break :d .none; + const ptr_val = field_default_value_val.optionalValue(mod) orelse break :d .none; + const ptr_ty = try mod.singleConstPtrType(field_ty); + // Asserted comptime-dereferencable above. + const val = (try sema.pointerDeref(block, src, ptr_val, ptr_ty)).?; + // We already resolved this for deduplication, so we may as well do it now. + break :d (try sema.resolveLazyValue(val)).toIntern(); + }; + + if (field_is_comptime and field_default == .none) { return sema.fail(block, src, "comptime field without default initialization value", .{}); } - struct_type.field_types.get(ip)[i] = field_ty.toIntern(); - struct_type.field_inits.get(ip)[i] = default_val; - if (is_comptime_val.toBool()) - struct_type.setFieldComptime(ip, i); + struct_type.field_types.get(ip)[field_idx] = field_type_val.toIntern(); + if (field_default != .none) { + struct_type.field_inits.get(ip)[field_idx] = field_default; + } if (field_ty.zigTypeTag(mod) == .Opaque) { - const msg = msg: { + return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); errdefer msg.destroy(gpa); try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); + }); } if (field_ty.zigTypeTag(mod) == .NoReturn) { - const msg = msg: { + return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(block, src, "struct fields cannot be 'noreturn'", .{}); errdefer msg.destroy(gpa); try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); + }); } if (layout == .Extern and !try sema.validateExternType(field_ty, .struct_field)) { - const msg = msg: { + return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(block, src, "extern structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); errdefer msg.destroy(gpa); @@ -21978,10 +22046,9 @@ fn reifyStruct( try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); + }); } else if (layout == .Packed and !try sema.validatePackedType(field_ty)) { - const msg = msg: { + return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(block, src, "packed structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); errdefer msg.destroy(gpa); @@ -21990,32 +22057,27 @@ fn reifyStruct( try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); + }); } } if (layout == .Packed) { - for (0..struct_type.field_types.len) |index| { - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[index]); + var fields_bit_sum: u64 = 0; + for (0..struct_type.field_types.len) |field_idx| { + const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_idx]); sema.resolveTypeLayout(field_ty) catch |err| switch (err) { error.AnalysisFail => { const msg = sema.err orelse return err; - try sema.addFieldErrNote(Type.fromInterned(ty), index, msg, "while checking this field", .{}); + try sema.errNote(block, src, msg, "while checking a field of this struct", .{}); return err; }, else => return err, }; - } - - var fields_bit_sum: u64 = 0; - for (0..struct_type.field_types.len) |i| { - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); fields_bit_sum += field_ty.bitSize(mod); } - if (backing_int_val.optionalValue(mod)) |backing_int_ty_val| { - const backing_int_ty = backing_int_ty_val.toType(); + if (opt_backing_int_val.optionalValue(mod)) |backing_int_val| { + const backing_int_ty = backing_int_val.toType(); try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum); struct_type.backingIntType(ip).* = backing_int_ty.toIntern(); } else { @@ -22024,9 +22086,8 @@ fn reifyStruct( } } - const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); try mod.finalizeAnonDecl(new_decl_index); - return decl_val; + return Air.internedToRef(wip_ty.finish(ip, new_decl_index, .none)); } fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref { @@ -23241,7 +23302,7 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 switch (ty.containerLayout(mod)) { .Packed => { var bit_sum: u64 = 0; - const struct_type = ip.indexToKey(ty.toIntern()).struct_type; + const struct_type = ip.loadStructType(ty.toIntern()); for (0..struct_type.field_types.len) |i| { if (i == field_index) { return bit_sum; @@ -25919,7 +25980,7 @@ fn zirBuiltinExtern( // TODO check duplicate extern - const new_decl_index = try mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node, .none); + const new_decl_index = try mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node); errdefer mod.destroyDecl(new_decl_index); const new_decl = mod.declPtr(new_decl_index); new_decl.name = options.name; @@ -26515,7 +26576,6 @@ fn addSafetyCheck( .sema = sema, .src_decl = parent_block.src_decl, .namespace = parent_block.namespace, - .wip_capture_scope = parent_block.wip_capture_scope, .instructions = .{}, .inlining = parent_block.inlining, .is_comptime = false, @@ -26624,7 +26684,6 @@ fn panicUnwrapError( .sema = sema, .src_decl = parent_block.src_decl, .namespace = parent_block.namespace, - .wip_capture_scope = parent_block.wip_capture_scope, .instructions = .{}, .inlining = parent_block.inlining, .is_comptime = false, @@ -26741,7 +26800,6 @@ fn safetyCheckFormatted( .sema = sema, .src_decl = parent_block.src_decl, .namespace = parent_block.namespace, - .wip_capture_scope = parent_block.wip_capture_scope, .instructions = .{}, .inlining = parent_block.inlining, .is_comptime = false, @@ -27268,8 +27326,7 @@ fn fieldCallBind( .Union => { try sema.resolveTypeFields(concrete_ty); const union_obj = mod.typeToUnion(concrete_ty).?; - _ = union_obj.nameIndex(ip, field_name) orelse break :find_field; - + _ = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse break :find_field; const field_ptr = try unionFieldPtr(sema, block, src, object_ptr, field_name, field_name_src, concrete_ty, false); return .{ .direct = try sema.analyzeLoad(block, src, field_ptr, src) }; }, @@ -27643,7 +27700,8 @@ fn structFieldVal( try sema.resolveTypeFields(struct_ty); switch (ip.indexToKey(struct_ty.toIntern())) { - .struct_type => |struct_type| { + .struct_type => { + const struct_type = ip.loadStructType(struct_ty.toIntern()); if (struct_type.isTuple(ip)) return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty); @@ -27849,7 +27907,7 @@ fn unionFieldPtr( try sema.requireRuntimeBlock(block, src, null); if (!initializing and union_obj.getLayout(ip) == .Auto and block.wantSafety() and - union_ty.unionTagTypeSafety(mod) != null and union_obj.field_names.len > 1) + union_ty.unionTagTypeSafety(mod) != null and union_obj.field_types.len > 1) { const wanted_tag_val = try mod.enumValueFieldIndex(Type.fromInterned(union_obj.enum_tag_ty), enum_field_index); const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern()); @@ -27927,7 +27985,7 @@ fn unionFieldVal( try sema.requireRuntimeBlock(block, src, null); if (union_obj.getLayout(ip) == .Auto and block.wantSafety() and - union_ty.unionTagTypeSafety(mod) != null and union_obj.field_names.len > 1) + union_ty.unionTagTypeSafety(mod) != null and union_obj.field_types.len > 1) { const wanted_tag_val = try mod.enumValueFieldIndex(Type.fromInterned(union_obj.enum_tag_ty), enum_field_index); const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern()); @@ -31686,7 +31744,7 @@ fn coerceEnumToUnion( const msg = try sema.errMsg(block, inst_src, "cannot initialize 'noreturn' field of union", .{}); errdefer msg.destroy(sema.gpa); - const field_name = union_obj.field_names.get(ip)[field_index]; + const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index]; try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{ field_name.fmt(ip), }); @@ -31697,7 +31755,7 @@ fn coerceEnumToUnion( } const opv = (try sema.typeHasOnePossibleValue(field_ty)) orelse { const msg = msg: { - const field_name = union_obj.field_names.get(ip)[field_index]; + const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index]; const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{}'", .{ inst_ty.fmt(sema.mod), union_ty.fmt(sema.mod), field_ty.fmt(sema.mod), field_name.fmt(ip), @@ -31769,8 +31827,8 @@ fn coerceEnumToUnion( ); errdefer msg.destroy(sema.gpa); - for (0..union_obj.field_names.len) |field_index| { - const field_name = union_obj.field_names.get(ip)[field_index]; + for (0..union_obj.field_types.len) |field_index| { + const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index]; const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); if (!(try sema.typeHasRuntimeBits(field_ty))) continue; try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' has type '{}'", .{ @@ -31803,8 +31861,8 @@ fn coerceAnonStructToUnion( .{ .name = anon_struct_type.names.get(ip)[0] } else .{ .count = anon_struct_type.names.len }, - .struct_type => |struct_type| name: { - const field_names = struct_type.field_names.get(ip); + .struct_type => name: { + const field_names = ip.loadStructType(inst_ty.toIntern()).field_names.get(ip); break :name if (field_names.len == 1) .{ .name = field_names[0] } else @@ -32113,7 +32171,7 @@ fn coerceTupleToStruct( var runtime_src: ?LazySrcLoc = null; const field_count = switch (ip.indexToKey(inst_ty.toIntern())) { .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, - .struct_type => |s| s.field_types.len, + .struct_type => ip.loadStructType(inst_ty.toIntern()).field_types.len, else => unreachable, }; for (0..field_count) |field_index_usize| { @@ -32125,7 +32183,7 @@ fn coerceTupleToStruct( anon_struct_type.names.get(ip)[field_i] else try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}), - .struct_type => |s| s.field_names.get(ip)[field_i], + .struct_type => ip.loadStructType(inst_ty.toIntern()).field_names.get(ip)[field_i], else => unreachable, }; const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src); @@ -32213,7 +32271,7 @@ fn coerceTupleToTuple( const ip = &mod.intern_pool; const dest_field_count = switch (ip.indexToKey(tuple_ty.toIntern())) { .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, - .struct_type => |struct_type| struct_type.field_types.len, + .struct_type => ip.loadStructType(tuple_ty.toIntern()).field_types.len, else => unreachable, }; const field_vals = try sema.arena.alloc(InternPool.Index, dest_field_count); @@ -32223,7 +32281,7 @@ fn coerceTupleToTuple( const inst_ty = sema.typeOf(inst); const src_field_count = switch (ip.indexToKey(inst_ty.toIntern())) { .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, - .struct_type => |struct_type| struct_type.field_types.len, + .struct_type => ip.loadStructType(inst_ty.toIntern()).field_types.len, else => unreachable, }; if (src_field_count > dest_field_count) return error.NotCoercible; @@ -32238,10 +32296,14 @@ fn coerceTupleToTuple( anon_struct_type.names.get(ip)[field_i] else try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}), - .struct_type => |struct_type| if (struct_type.field_names.len > 0) - struct_type.field_names.get(ip)[field_i] - else - try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}), + .struct_type => s: { + const struct_type = ip.loadStructType(inst_ty.toIntern()); + if (struct_type.field_names.len > 0) { + break :s struct_type.field_names.get(ip)[field_i]; + } else { + break :s try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}); + } + }, else => unreachable, }; @@ -32250,12 +32312,12 @@ fn coerceTupleToTuple( const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) { .anon_struct_type => |anon_struct_type| anon_struct_type.types.get(ip)[field_index_usize], - .struct_type => |struct_type| struct_type.field_types.get(ip)[field_index_usize], + .struct_type => ip.loadStructType(tuple_ty.toIntern()).field_types.get(ip)[field_index_usize], else => unreachable, }; const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { .anon_struct_type => |anon_struct_type| anon_struct_type.values.get(ip)[field_index_usize], - .struct_type => |struct_type| struct_type.fieldInit(ip, field_index_usize), + .struct_type => ip.loadStructType(tuple_ty.toIntern()).fieldInit(ip, field_index_usize), else => unreachable, }; @@ -32294,7 +32356,7 @@ fn coerceTupleToTuple( const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { .anon_struct_type => |anon_struct_type| anon_struct_type.values.get(ip)[i], - .struct_type => |struct_type| struct_type.fieldInit(ip, i), + .struct_type => ip.loadStructType(tuple_ty.toIntern()).fieldInit(ip, i), else => unreachable, }; @@ -35534,7 +35596,7 @@ pub fn resolveTypeLayout(sema: *Sema, ty: Type) CompileError!void { pub fn resolveStructAlignment( sema: *Sema, ty: InternPool.Index, - struct_type: InternPool.Key.StructType, + struct_type: InternPool.LoadedStructType, ) CompileError!Alignment { const mod = sema.mod; const ip = &mod.intern_pool; @@ -35674,7 +35736,7 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { } } - const RuntimeOrder = InternPool.Key.StructType.RuntimeOrder; + const RuntimeOrder = InternPool.LoadedStructType.RuntimeOrder; const AlignSortContext = struct { aligns: []const Alignment, @@ -35726,7 +35788,7 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { _ = try sema.typeRequiresComptime(ty); } -fn semaBackingIntType(mod: *Module, struct_type: InternPool.Key.StructType) CompileError!void { +fn semaBackingIntType(mod: *Module, struct_type: InternPool.LoadedStructType) CompileError!void { const gpa = mod.gpa; const ip = &mod.intern_pool; @@ -35766,7 +35828,6 @@ fn semaBackingIntType(mod: *Module, struct_type: InternPool.Key.StructType) Comp .sema = &sema, .src_decl = decl_index, .namespace = struct_type.namespace.unwrap() orelse decl.src_namespace, - .wip_capture_scope = try mod.createCaptureScope(decl.src_scope), .instructions = .{}, .inlining = null, .is_comptime = true, @@ -35789,9 +35850,16 @@ fn semaBackingIntType(mod: *Module, struct_type: InternPool.Key.StructType) Comp if (small.has_backing_int) { var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len; + const captures_len = if (small.has_captures_len) blk: { + const captures_len = zir.extra[extra_index]; + extra_index += 1; + break :blk captures_len; + } else 0; extra_index += @intFromBool(small.has_fields_len); extra_index += @intFromBool(small.has_decls_len); + extra_index += captures_len; + const backing_int_body_len = zir.extra[extra_index]; extra_index += 1; @@ -35879,7 +35947,7 @@ fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void pub fn resolveUnionAlignment( sema: *Sema, ty: Type, - union_type: InternPool.Key.UnionType, + union_type: InternPool.LoadedUnionType, ) CompileError!Alignment { const mod = sema.mod; const ip = &mod.intern_pool; @@ -35899,13 +35967,12 @@ pub fn resolveUnionAlignment( try sema.resolveTypeFieldsUnion(ty, union_type); - const union_obj = ip.loadUnionType(union_type); var max_align: Alignment = .@"1"; - for (0..union_obj.field_names.len) |field_index| { - const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); + for (0..union_type.field_types.len) |field_index| { + const field_ty = Type.fromInterned(union_type.field_types.get(ip)[field_index]); if (!(try sema.typeHasRuntimeBits(field_ty))) continue; - const explicit_align = union_obj.fieldAlign(ip, @intCast(field_index)); + const explicit_align = union_type.fieldAlign(ip, @intCast(field_index)); const field_align = if (explicit_align != .none) explicit_align else @@ -35923,16 +35990,17 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { const mod = sema.mod; const ip = &mod.intern_pool; - const union_type = ip.indexToKey(ty.ip_index).union_type; - try sema.resolveTypeFieldsUnion(ty, union_type); + try sema.resolveTypeFieldsUnion(ty, ip.loadUnionType(ty.ip_index)); - const union_obj = ip.loadUnionType(union_type); - switch (union_obj.flagsPtr(ip).status) { + // Load again, since the tag type might have changed due to resolution. + const union_type = ip.loadUnionType(ty.ip_index); + + switch (union_type.flagsPtr(ip).status) { .none, .have_field_types => {}, .field_types_wip, .layout_wip => { const msg = try Module.ErrorMsg.create( sema.gpa, - mod.declPtr(union_obj.decl).srcLoc(mod), + mod.declPtr(union_type.decl).srcLoc(mod), "union '{}' depends on itself", .{ty.fmt(mod)}, ); @@ -35941,17 +36009,17 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { .have_layout, .fully_resolved_wip, .fully_resolved => return, } - const prev_status = union_obj.flagsPtr(ip).status; - errdefer if (union_obj.flagsPtr(ip).status == .layout_wip) { - union_obj.flagsPtr(ip).status = prev_status; + const prev_status = union_type.flagsPtr(ip).status; + errdefer if (union_type.flagsPtr(ip).status == .layout_wip) { + union_type.flagsPtr(ip).status = prev_status; }; - union_obj.flagsPtr(ip).status = .layout_wip; + union_type.flagsPtr(ip).status = .layout_wip; var max_size: u64 = 0; var max_align: Alignment = .@"1"; - for (0..union_obj.field_names.len) |field_index| { - const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); + for (0..union_type.field_types.len) |field_index| { + const field_ty = Type.fromInterned(union_type.field_types.get(ip)[field_index]); if (!(try sema.typeHasRuntimeBits(field_ty))) continue; max_size = @max(max_size, sema.typeAbiSize(field_ty) catch |err| switch (err) { @@ -35963,7 +36031,7 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { else => return err, }); - const explicit_align = union_obj.fieldAlign(ip, @intCast(field_index)); + const explicit_align = union_type.fieldAlign(ip, @intCast(field_index)); const field_align = if (explicit_align != .none) explicit_align else @@ -35972,10 +36040,10 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { max_align = max_align.max(field_align); } - const flags = union_obj.flagsPtr(ip); - const has_runtime_tag = flags.runtime_tag.hasTag() and try sema.typeHasRuntimeBits(Type.fromInterned(union_obj.enum_tag_ty)); + const flags = union_type.flagsPtr(ip); + const has_runtime_tag = flags.runtime_tag.hasTag() and try sema.typeHasRuntimeBits(Type.fromInterned(union_type.enum_tag_ty)); const size, const alignment, const padding = if (has_runtime_tag) layout: { - const enum_tag_type = Type.fromInterned(union_obj.enum_tag_ty); + const enum_tag_type = Type.fromInterned(union_type.enum_tag_ty); const tag_align = try sema.typeAbiAlignment(enum_tag_type); const tag_size = try sema.typeAbiSize(enum_tag_type); @@ -36009,22 +36077,22 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { flags.alignment = alignment; flags.status = .have_layout; - if (union_obj.flagsPtr(ip).assumed_runtime_bits and !(try sema.typeHasRuntimeBits(ty))) { + if (union_type.flagsPtr(ip).assumed_runtime_bits and !(try sema.typeHasRuntimeBits(ty))) { const msg = try Module.ErrorMsg.create( sema.gpa, - mod.declPtr(union_obj.decl).srcLoc(mod), + mod.declPtr(union_type.decl).srcLoc(mod), "union layout depends on it having runtime bits", .{}, ); return sema.failWithOwnedErrorMsg(null, msg); } - if (union_obj.flagsPtr(ip).assumed_pointer_aligned and + if (union_type.flagsPtr(ip).assumed_pointer_aligned and alignment.compareStrict(.neq, Alignment.fromByteUnits(@divExact(mod.getTarget().ptrBitWidth(), 8)))) { const msg = try Module.ErrorMsg.create( sema.gpa, - mod.declPtr(union_obj.decl).srcLoc(mod), + mod.declPtr(union_type.decl).srcLoc(mod), "union layout depends on being pointer aligned", .{}, ); @@ -36212,12 +36280,11 @@ pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!void { else => switch (ip.items.items(.tag)[@intFromEnum(ty_ip)]) { .type_struct, - .type_struct_ns, .type_struct_packed, .type_struct_packed_inits, - => try sema.resolveTypeFieldsStruct(ty_ip, ip.indexToKey(ty_ip).struct_type), + => try sema.resolveTypeFieldsStruct(ty_ip, ip.loadStructType(ty_ip)), - .type_union => try sema.resolveTypeFieldsUnion(Type.fromInterned(ty_ip), ip.indexToKey(ty_ip).union_type), + .type_union => try sema.resolveTypeFieldsUnion(Type.fromInterned(ty_ip), ip.loadUnionType(ty_ip)), .simple_type => try sema.resolveSimpleType(ip.indexToKey(ty_ip).simple_type), else => {}, }, @@ -36249,7 +36316,7 @@ fn resolveSimpleType(sema: *Sema, simple_type: InternPool.SimpleType) CompileErr pub fn resolveTypeFieldsStruct( sema: *Sema, ty: InternPool.Index, - struct_type: InternPool.Key.StructType, + struct_type: InternPool.LoadedStructType, ) CompileError!void { const mod = sema.mod; const ip = &mod.intern_pool; @@ -36309,7 +36376,7 @@ pub fn resolveStructFieldInits(sema: *Sema, ty: Type) CompileError!void { struct_type.setHaveFieldInits(ip); } -pub fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_type: InternPool.Key.UnionType) CompileError!void { +pub fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_type: InternPool.LoadedUnionType) CompileError!void { const mod = sema.mod; const ip = &mod.intern_pool; const owner_decl = mod.declPtr(union_type.decl); @@ -36500,6 +36567,12 @@ fn structZirInfo(zir: Zir, zir_index: Zir.Inst.Index) struct { const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len; + const captures_len = if (small.has_captures_len) blk: { + const captures_len = zir.extra[extra_index]; + extra_index += 1; + break :blk captures_len; + } else 0; + const fields_len = if (small.has_fields_len) blk: { const fields_len = zir.extra[extra_index]; extra_index += 1; @@ -36512,6 +36585,8 @@ fn structZirInfo(zir: Zir, zir_index: Zir.Inst.Index) struct { break :decls_len decls_len; } else 0; + extra_index += captures_len; + // The backing integer cannot be handled until `resolveStructLayout()`. if (small.has_backing_int) { const backing_int_body_len = zir.extra[extra_index]; @@ -36532,7 +36607,7 @@ fn structZirInfo(zir: Zir, zir_index: Zir.Inst.Index) struct { fn semaStructFields( mod: *Module, arena: Allocator, - struct_type: InternPool.Key.StructType, + struct_type: InternPool.LoadedStructType, ) CompileError!void { const gpa = mod.gpa; const ip = &mod.intern_pool; @@ -36584,7 +36659,6 @@ fn semaStructFields( .sema = &sema, .src_decl = decl_index, .namespace = namespace_index, - .wip_capture_scope = try mod.createCaptureScope(decl.src_scope), .instructions = .{}, .inlining = null, .is_comptime = true, @@ -36800,7 +36874,7 @@ fn semaStructFields( fn semaStructFieldInits( mod: *Module, arena: Allocator, - struct_type: InternPool.Key.StructType, + struct_type: InternPool.LoadedStructType, ) CompileError!void { const gpa = mod.gpa; const ip = &mod.intern_pool; @@ -36842,7 +36916,6 @@ fn semaStructFieldInits( .sema = &sema, .src_decl = decl_index, .namespace = namespace_index, - .wip_capture_scope = try mod.createCaptureScope(decl.src_scope), .instructions = .{}, .inlining = null, .is_comptime = true, @@ -36952,15 +37025,15 @@ fn semaStructFieldInits( } } -fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.UnionType) CompileError!void { +fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.LoadedUnionType) CompileError!void { const tracy = trace(@src()); defer tracy.end(); const gpa = mod.gpa; const ip = &mod.intern_pool; const decl_index = union_type.decl; - const zir = mod.namespacePtr(union_type.namespace).file_scope.zir; - const zir_index = union_type.zir_index.unwrap().?.resolve(ip); + const zir = mod.namespacePtr(union_type.namespace.unwrap().?).file_scope.zir; + const zir_index = union_type.zir_index.resolve(ip); const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; assert(extended.opcode == .union_decl); const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); @@ -36974,6 +37047,12 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un break :blk ty_ref; } else .none; + const captures_len = if (small.has_captures_len) blk: { + const captures_len = zir.extra[extra_index]; + extra_index += 1; + break :blk captures_len; + } else 0; + const body_len = if (small.has_body_len) blk: { const body_len = zir.extra[extra_index]; extra_index += 1; @@ -36992,8 +37071,8 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un break :decls_len decls_len; } else 0; - // Skip over decls. - extra_index += decls_len; + // Skip over captures and decls. + extra_index += captures_len + decls_len; const body = zir.bodySlice(extra_index, body_len); extra_index += body.len; @@ -37027,8 +37106,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un .parent = null, .sema = &sema, .src_decl = decl_index, - .namespace = union_type.namespace, - .wip_capture_scope = try mod.createCaptureScope(decl.src_scope), + .namespace = union_type.namespace.unwrap().?, .instructions = .{}, .inlining = null, .is_comptime = true, @@ -37079,7 +37157,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un // The provided type is the enum tag type. union_type.tagTypePtr(ip).* = provided_ty.toIntern(); const enum_type = switch (ip.indexToKey(provided_ty.toIntern())) { - .enum_type => |x| x, + .enum_type => ip.loadEnumType(provided_ty.toIntern()), else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{provided_ty.fmt(mod)}), }; // The fields of the union must match the enum exactly. @@ -37216,7 +37294,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un } if (explicit_tags_seen.len > 0) { - const tag_info = ip.indexToKey(union_type.tagTypePtr(ip).*).enum_type; + const tag_info = ip.loadEnumType(union_type.tagTypePtr(ip).*); const enum_index = tag_info.nameIndex(ip, field_name) orelse { const ty_src = mod.fieldSrcLoc(union_type.decl, .{ .index = field_i, @@ -37327,7 +37405,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un union_type.setFieldAligns(ip, field_aligns.items); if (explicit_tags_seen.len > 0) { - const tag_info = ip.indexToKey(union_type.tagTypePtr(ip).*).enum_type; + const tag_info = ip.loadEnumType(union_type.tagTypePtr(ip).*); if (tag_info.names.len > fields_len) { const msg = msg: { const msg = try sema.errMsg(&block_scope, src, "enum field(s) missing in union", .{}); @@ -37348,7 +37426,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un const enum_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), mod.declPtr(union_type.decl)); union_type.tagTypePtr(ip).* = enum_ty; } else { - const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_type.decl.toOptional()); + const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, mod.declPtr(union_type.decl)); union_type.tagTypePtr(ip).* = enum_ty; } } @@ -37365,16 +37443,16 @@ fn generateUnionTagTypeNumbered( block: *Block, enum_field_names: []const InternPool.NullTerminatedString, enum_field_vals: []const InternPool.Index, - decl: *Module.Decl, + union_owner_decl: *Module.Decl, ) !InternPool.Index { const mod = sema.mod; const gpa = sema.gpa; const ip = &mod.intern_pool; const src_decl = mod.declPtr(block.src_decl); - const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope); + const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node); errdefer mod.destroyDecl(new_decl_index); - const fqn = try decl.fullyQualifiedName(mod); + const fqn = try union_owner_decl.fullyQualifiedName(mod); const name = try ip.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(ip)}); try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, .{ .ty = Type.noreturn, @@ -37386,9 +37464,9 @@ fn generateUnionTagTypeNumbered( new_decl.owns_tv = true; new_decl.name_fully_qualified = true; - const enum_ty = try ip.getEnum(gpa, .{ + const enum_ty = try ip.getGeneratedTagEnumType(gpa, .{ .decl = new_decl_index, - .namespace = .none, + .owner_union_ty = union_owner_decl.val.toIntern(), .tag_ty = if (enum_field_vals.len == 0) (try mod.intType(.unsigned, 0)).toIntern() else @@ -37396,7 +37474,6 @@ fn generateUnionTagTypeNumbered( .names = enum_field_names, .values = enum_field_vals, .tag_mode = .explicit, - .zir_index = .none, }); new_decl.ty = Type.type; @@ -37410,22 +37487,16 @@ fn generateUnionTagTypeSimple( sema: *Sema, block: *Block, enum_field_names: []const InternPool.NullTerminatedString, - maybe_decl_index: InternPool.OptionalDeclIndex, + union_owner_decl: *Module.Decl, ) !InternPool.Index { const mod = sema.mod; const ip = &mod.intern_pool; const gpa = sema.gpa; const new_decl_index = new_decl_index: { - const decl_index = maybe_decl_index.unwrap() orelse { - break :new_decl_index try mod.createAnonymousDecl(block, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", - }); - }; - const fqn = try mod.declPtr(decl_index).fullyQualifiedName(mod); + const fqn = try union_owner_decl.fullyQualifiedName(mod); const src_decl = mod.declPtr(block.src_decl); - const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope); + const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node); errdefer mod.destroyDecl(new_decl_index); const name = try ip.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(ip)}); try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, .{ @@ -37437,9 +37508,9 @@ fn generateUnionTagTypeSimple( }; errdefer mod.abortAnonDecl(new_decl_index); - const enum_ty = try ip.getEnum(gpa, .{ + const enum_ty = try ip.getGeneratedTagEnumType(gpa, .{ .decl = new_decl_index, - .namespace = .none, + .owner_union_ty = union_owner_decl.val.toIntern(), .tag_ty = if (enum_field_names.len == 0) (try mod.intType(.unsigned, 0)).toIntern() else @@ -37447,7 +37518,6 @@ fn generateUnionTagTypeSimple( .names = enum_field_names, .values = &.{}, .tag_mode = .auto, - .zir_index = .none, }); const new_decl = mod.declPtr(new_decl_index); @@ -37460,7 +37530,6 @@ fn generateUnionTagTypeSimple( } fn getBuiltin(sema: *Sema, name: []const u8) CompileError!Air.Inst.Ref { - const mod = sema.mod; const gpa = sema.gpa; const src = LazySrcLoc.nodeOffset(0); @@ -37469,7 +37538,6 @@ fn getBuiltin(sema: *Sema, name: []const u8) CompileError!Air.Inst.Ref { .sema = sema, .src_decl = sema.owner_decl_index, .namespace = sema.owner_decl.src_namespace, - .wip_capture_scope = try mod.createCaptureScope(sema.owner_decl.src_scope), .instructions = .{}, .inlining = null, .is_comptime = true, @@ -37510,7 +37578,6 @@ fn getBuiltinDecl(sema: *Sema, block: *Block, name: []const u8) CompileError!Int } fn getBuiltinType(sema: *Sema, name: []const u8) CompileError!Type { - const mod = sema.mod; const ty_inst = try sema.getBuiltin(name); var block: Block = .{ @@ -37518,7 +37585,6 @@ fn getBuiltinType(sema: *Sema, name: []const u8) CompileError!Type { .sema = sema, .src_decl = sema.owner_decl_index, .namespace = sema.owner_decl.src_namespace, - .wip_capture_scope = try mod.createCaptureScope(sema.owner_decl.src_scope), .instructions = .{}, .inlining = null, .is_comptime = true, @@ -37636,6 +37702,8 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { => unreachable, _ => switch (ip.items.items(.tag)[@intFromEnum(ty.toIntern())]) { + .removed => unreachable, + .type_int_signed, // i0 handled above .type_int_unsigned, // u0 handled above .type_pointer, @@ -37713,7 +37781,6 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { .type_enum_explicit, .type_enum_nonexhaustive, .type_struct, - .type_struct_ns, .type_struct_anon, .type_struct_packed, .type_struct_packed_inits, @@ -37736,8 +37803,9 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { return null; }, - .struct_type => |struct_type| { - try sema.resolveTypeFields(ty); + .struct_type => { + const struct_type = ip.loadStructType(ty.toIntern()); + try sema.resolveTypeFieldsStruct(ty.toIntern(), struct_type); if (struct_type.field_types.len == 0) { // In this case the struct has no fields at all and @@ -37795,10 +37863,10 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { } }))); }, - .union_type => |union_type| { - try sema.resolveTypeFields(ty); - const union_obj = ip.loadUnionType(union_type); - const tag_val = (try sema.typeHasOnePossibleValue(Type.fromInterned(union_obj.enum_tag_ty))) orelse + .union_type => { + const union_obj = ip.loadUnionType(ty.toIntern()); + try sema.resolveTypeFieldsUnion(ty, union_obj); + const tag_val = (try sema.typeHasOnePossibleValue(Type.fromInterned(union_obj.tagTypePtr(ip).*))) orelse return null; if (union_obj.field_types.len == 0) { const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() }); @@ -37825,39 +37893,42 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { return Value.fromInterned(only); }, - .enum_type => |enum_type| switch (enum_type.tag_mode) { - .nonexhaustive => { - if (enum_type.tag_ty == .comptime_int_type) return null; + .enum_type => { + const enum_type = ip.loadEnumType(ty.toIntern()); + switch (enum_type.tag_mode) { + .nonexhaustive => { + if (enum_type.tag_ty == .comptime_int_type) return null; - if (try sema.typeHasOnePossibleValue(Type.fromInterned(enum_type.tag_ty))) |int_opv| { - const only = try mod.intern(.{ .enum_tag = .{ - .ty = ty.toIntern(), - .int = int_opv.toIntern(), - } }); - return Value.fromInterned(only); - } + if (try sema.typeHasOnePossibleValue(Type.fromInterned(enum_type.tag_ty))) |int_opv| { + const only = try mod.intern(.{ .enum_tag = .{ + .ty = ty.toIntern(), + .int = int_opv.toIntern(), + } }); + return Value.fromInterned(only); + } - return null; - }, - .auto, .explicit => { - if (Type.fromInterned(enum_type.tag_ty).hasRuntimeBits(mod)) return null; - - return Value.fromInterned(switch (enum_type.names.len) { - 0 => try mod.intern(.{ .empty_enum_value = ty.toIntern() }), - 1 => try mod.intern(.{ .enum_tag = .{ - .ty = ty.toIntern(), - .int = if (enum_type.values.len == 0) - (try mod.intValue(Type.fromInterned(enum_type.tag_ty), 0)).toIntern() - else - try mod.intern_pool.getCoercedInts( - mod.gpa, - mod.intern_pool.indexToKey(enum_type.values.get(ip)[0]).int, - enum_type.tag_ty, - ), - } }), - else => return null, - }); - }, + return null; + }, + .auto, .explicit => { + if (Type.fromInterned(enum_type.tag_ty).hasRuntimeBits(mod)) return null; + + return Value.fromInterned(switch (enum_type.names.len) { + 0 => try mod.intern(.{ .empty_enum_value = ty.toIntern() }), + 1 => try mod.intern(.{ .enum_tag = .{ + .ty = ty.toIntern(), + .int = if (enum_type.values.len == 0) + (try mod.intValue(Type.fromInterned(enum_type.tag_ty), 0)).toIntern() + else + try mod.intern_pool.getCoercedInts( + mod.gpa, + mod.intern_pool.indexToKey(enum_type.values.get(ip)[0]).int, + enum_type.tag_ty, + ), + } }), + else => return null, + }); + }, + } }, else => unreachable, @@ -38189,7 +38260,7 @@ fn typeAbiAlignment(sema: *Sema, ty: Type) CompileError!Alignment { /// Not valid to call for packed unions. /// Keep implementation in sync with `Module.unionFieldNormalAlignment`. -fn unionFieldAlignment(sema: *Sema, u: InternPool.UnionType, field_index: u32) !Alignment { +fn unionFieldAlignment(sema: *Sema, u: InternPool.LoadedUnionType, field_index: u32) !Alignment { const mod = sema.mod; const ip = &mod.intern_pool; const field_align = u.fieldAlign(ip, field_index); @@ -38237,7 +38308,7 @@ fn unionFieldIndex( const ip = &mod.intern_pool; try sema.resolveTypeFields(union_ty); const union_obj = mod.typeToUnion(union_ty).?; - const field_index = union_obj.nameIndex(ip, field_name) orelse + const field_index = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); return @intCast(field_index); } @@ -38274,7 +38345,7 @@ fn anonStructFieldIndex( .anon_struct_type => |anon_struct_type| for (anon_struct_type.names.get(ip), 0..) |name, i| { if (name == field_name) return @intCast(i); }, - .struct_type => |struct_type| if (struct_type.nameIndex(ip, field_name)) |i| return i, + .struct_type => if (ip.loadStructType(struct_ty.toIntern()).nameIndex(ip, field_name)) |i| return i, else => unreachable, } return sema.fail(block, field_src, "no field named '{}' in anonymous struct '{}'", .{ @@ -38710,7 +38781,7 @@ fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool { /// Asserts the type is an enum. fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool { const mod = sema.mod; - const enum_type = mod.intern_pool.indexToKey(ty.toIntern()).enum_type; + const enum_type = mod.intern_pool.loadEnumType(ty.toIntern()); assert(enum_type.tag_mode != .nonexhaustive); // The `tagValueIndex` function call below relies on the type being the integer tag type. // `getCoerced` assumes the value will fit the new type. |
