diff options
| author | Matthew Lugg <mlugg@mlugg.co.uk> | 2024-11-01 01:43:08 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-11-01 01:43:08 +0000 |
| commit | 3f7fac5fff9beed535a7674679a5e2c1f3cd74d2 (patch) | |
| tree | c5f7affd52d47632874da1f533c888ef648e8304 /src/Sema.zig | |
| parent | a916bc7fdd3975a9e2ef13c44f814c71ce017193 (diff) | |
| parent | 24babde746621492c5111ffcd8edf575cb176d65 (diff) | |
| download | zig-3f7fac5fff9beed535a7674679a5e2c1f3cd74d2.tar.gz zig-3f7fac5fff9beed535a7674679a5e2c1f3cd74d2.zip | |
Merge pull request #21817 from mlugg/no-anon-structs
compiler: remove anonymous struct types, unify all tuples
Diffstat (limited to 'src/Sema.zig')
| -rw-r--r-- | src/Sema.zig | 808 |
1 files changed, 384 insertions, 424 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index fdf39b8305..2194b9e96c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -844,6 +844,7 @@ pub const Block = struct { fn trackZir(block: *Block, inst: Zir.Inst.Index) Allocator.Error!InternPool.TrackedInst.Index { const pt = block.sema.pt; + block.sema.code.assertTrackable(inst); return pt.zcu.intern_pool.trackZir(pt.zcu.gpa, pt.tid, .{ .file = block.getFileScopeIndex(pt.zcu), .inst = inst, @@ -1277,6 +1278,7 @@ fn analyzeBodyInner( .enum_decl => try sema.zirEnumDecl( block, extended, inst), .union_decl => try sema.zirUnionDecl( block, extended, inst), .opaque_decl => try sema.zirOpaqueDecl( block, extended, inst), + .tuple_decl => try sema.zirTupleDecl( block, extended), .this => try sema.zirThis( block, extended), .ret_addr => try sema.zirRetAddr( block, extended), .builtin_src => try sema.zirBuiltinSrc( block, extended), @@ -2338,7 +2340,7 @@ fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazyS const struct_type = zcu.typeToStruct(container_ty) orelse break :msg msg; try sema.errNote(.{ - .base_node_inst = struct_type.zir_index.unwrap().?, + .base_node_inst = struct_type.zir_index, .offset = .{ .container_field_value = @intCast(field_index) }, }, msg, "default value set here", .{}); break :msg msg; @@ -2651,6 +2653,94 @@ fn analyzeValueAsCallconv( }; } +fn zirTupleDecl( + sema: *Sema, + block: *Block, + extended: Zir.Inst.Extended.InstData, +) CompileError!Air.Inst.Ref { + const gpa = sema.gpa; + const pt = sema.pt; + const zcu = pt.zcu; + const fields_len = extended.small; + const extra = sema.code.extraData(Zir.Inst.TupleDecl, extended.operand); + var extra_index = extra.end; + + const types = try sema.arena.alloc(InternPool.Index, fields_len); + const inits = try sema.arena.alloc(InternPool.Index, fields_len); + + const extra_as_refs: []const Zir.Inst.Ref = @ptrCast(sema.code.extra); + + for (types, inits, 0..) |*field_ty, *field_init, field_index| { + const zir_field_ty, const zir_field_init = extra_as_refs[extra_index..][0..2].*; + extra_index += 2; + + const type_src = block.src(.{ .tuple_field_type = .{ + .tuple_decl_node_offset = extra.data.src_node, + .elem_index = @intCast(field_index), + } }); + const init_src = block.src(.{ .tuple_field_init = .{ + .tuple_decl_node_offset = extra.data.src_node, + .elem_index = @intCast(field_index), + } }); + + const uncoerced_field_ty = try sema.resolveInst(zir_field_ty); + const field_type = try sema.analyzeAsType(block, type_src, uncoerced_field_ty); + try sema.validateTupleFieldType(block, field_type, type_src); + + field_ty.* = field_type.toIntern(); + field_init.* = init: { + if (zir_field_init != .none) { + const uncoerced_field_init = try sema.resolveInst(zir_field_init); + const coerced_field_init = try sema.coerce(block, field_type, uncoerced_field_init, init_src); + const field_init_val = try sema.resolveConstDefinedValue(block, init_src, coerced_field_init, .{ + .needed_comptime_reason = "tuple field default value must be comptime-known", + }); + if (field_init_val.canMutateComptimeVarState(zcu)) { + return sema.fail(block, init_src, "field default value contains reference to comptime-mutable memory", .{}); + } + break :init field_init_val.toIntern(); + } + if (try sema.typeHasOnePossibleValue(field_type)) |opv| { + break :init opv.toIntern(); + } + break :init .none; + }; + } + + return Air.internedToRef(try zcu.intern_pool.getTupleType(gpa, pt.tid, .{ + .types = types, + .values = inits, + })); +} + +fn validateTupleFieldType( + sema: *Sema, + block: *Block, + field_ty: Type, + field_ty_src: LazySrcLoc, +) CompileError!void { + const gpa = sema.gpa; + const zcu = sema.pt.zcu; + if (field_ty.zigTypeTag(zcu) == .@"opaque") { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(field_ty_src, "opaque types have unknown size and therefore cannot be directly embedded in tuples", .{}); + errdefer msg.destroy(gpa); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + } + if (field_ty.zigTypeTag(zcu) == .noreturn) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(field_ty_src, "tuple fields cannot be 'noreturn'", .{}); + errdefer msg.destroy(gpa); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + } +} + /// 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, type_src: LazySrcLoc, extra_index: usize, captures_len: u32) ![]InternPool.CaptureValue { @@ -2774,7 +2864,6 @@ fn zirStructDecl( .fields_len = fields_len, .known_non_opv = small.known_non_opv, .requires_comptime = if (small.known_comptime_only) .yes else .unknown, - .is_tuple = small.is_tuple, .any_comptime_fields = small.any_comptime_fields, .any_default_inits = small.any_default_inits, .inits_resolved = false, @@ -4912,7 +5001,7 @@ fn validateStructInit( const default_field_ptr = if (struct_ty.isTuple(zcu)) try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true) else - try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), field_src, struct_ty, true); + try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty); const init = Air.internedToRef(default_val.toIntern()); try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store); } @@ -5104,7 +5193,7 @@ fn validateStructInit( const default_field_ptr = if (struct_ty.isTuple(zcu)) try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true) else - try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), field_src, struct_ty, true); + try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty); try sema.checkKnownAllocPtr(block, struct_ptr, default_field_ptr); const init = Air.internedToRef(field_values[i]); try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store); @@ -8430,22 +8519,6 @@ fn instantiateGenericCall( return result; } -fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { - const pt = sema.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const tuple = switch (ip.indexToKey(ty.toIntern())) { - .anon_struct_type => |tuple| tuple, - else => return, - }; - for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, field_val| { - try sema.resolveTupleLazyValues(block, src, Type.fromInterned(field_ty)); - if (field_val == .none) continue; - // TODO: mutate in intern pool - _ = try sema.resolveLazyValue(Value.fromInterned(field_val)); - } -} - fn zirIntType(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const int_type = sema.code.instructions.items(.data)[@intFromEnum(inst)].int_type; const ty = try sema.pt.intType(int_type.signedness, int_type.bit_count); @@ -14321,13 +14394,9 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }, else => {}, }, - .anon_struct_type => |anon_struct| { - if (anon_struct.names.len != 0) { - break :hf mem.indexOfScalar(InternPool.NullTerminatedString, anon_struct.names.get(ip), field_name) != null; - } else { - const field_index = field_name.toUnsigned(ip) orelse break :hf false; - break :hf field_index < ty.structFieldCount(zcu); - } + .tuple_type => |tuple| { + const field_index = field_name.toUnsigned(ip) orelse break :hf false; + break :hf field_index < tuple.types.len; }, .struct_type => { break :hf ip.loadStructType(ty.toIntern()).nameIndex(ip, field_name) != null; @@ -14882,7 +14951,7 @@ fn analyzeTupleCat( const dest_fields = lhs_len + rhs_len; if (dest_fields == 0) { - return Air.internedToRef(Value.empty_struct.toIntern()); + return .empty_tuple; } if (lhs_len == 0) { return rhs; @@ -14928,10 +14997,9 @@ fn analyzeTupleCat( break :rs runtime_src; }; - const tuple_ty = try zcu.intern_pool.getAnonStructType(zcu.gpa, pt.tid, .{ + const tuple_ty = try zcu.intern_pool.getTupleType(zcu.gpa, pt.tid, .{ .types = types, .values = values, - .names = &.{}, }); const runtime_src = opt_runtime_src orelse { @@ -15263,7 +15331,7 @@ fn analyzeTupleMul( return sema.fail(block, len_src, "operation results in overflow", .{}); if (final_len == 0) { - return Air.internedToRef(Value.empty_struct.toIntern()); + return .empty_tuple; } const types = try sema.arena.alloc(InternPool.Index, final_len); const values = try sema.arena.alloc(InternPool.Index, final_len); @@ -15289,10 +15357,9 @@ fn analyzeTupleMul( break :rs runtime_src; }; - const tuple_ty = try zcu.intern_pool.getAnonStructType(zcu.gpa, pt.tid, .{ + const tuple_ty = try zcu.intern_pool.getTupleType(zcu.gpa, pt.tid, .{ .types = types, .values = values, - .names = &.{}, }); const runtime_src = opt_runtime_src orelse { @@ -16689,7 +16756,7 @@ fn zirOverflowArithmetic( const maybe_rhs_val = try sema.resolveValue(rhs); const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty); - const overflow_ty = Type.fromInterned(ip.indexToKey(tuple_ty.toIntern()).anon_struct_type.types.get(ip)[1]); + const overflow_ty = Type.fromInterned(ip.indexToKey(tuple_ty.toIntern()).tuple_type.types.get(ip)[1]); var result: struct { inst: Air.Inst.Ref = .none, @@ -16873,10 +16940,9 @@ fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type { const types = [2]InternPool.Index{ ty.toIntern(), ov_ty.toIntern() }; const values = [2]InternPool.Index{ .none, .none }; - const tuple_ty = try ip.getAnonStructType(zcu.gpa, pt.tid, .{ + const tuple_ty = try ip.getTupleType(zcu.gpa, pt.tid, .{ .types = &types, .values = &values, - .names = &.{}, }); return Type.fromInterned(tuple_ty); } @@ -18908,16 +18974,13 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai defer gpa.free(struct_field_vals); fv: { const struct_type = switch (ip.indexToKey(ty.toIntern())) { - .anon_struct_type => |anon_struct_type| { - struct_field_vals = try gpa.alloc(InternPool.Index, anon_struct_type.types.len); + .tuple_type => |tuple_type| { + struct_field_vals = try gpa.alloc(InternPool.Index, tuple_type.types.len); for (struct_field_vals, 0..) |*struct_field_val, field_index| { - const field_ty = anon_struct_type.types.get(ip)[field_index]; - const field_val = anon_struct_type.values.get(ip)[field_index]; + const field_ty = tuple_type.types.get(ip)[field_index]; + const field_val = tuple_type.values.get(ip)[field_index]; const name_val = v: { - const field_name = if (anon_struct_type.names.len != 0) - anon_struct_type.names.get(ip)[field_index] - else - try ip.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls); + const field_name = try ip.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls); const field_name_len = field_name.length(ip); const new_decl_ty = try pt.arrayType(.{ .len = field_name_len, @@ -20509,8 +20572,8 @@ fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const src = block.nodeOffset(inst_data.src_node); const ty_operand = sema.resolveType(block, src, inst_data.operand) catch |err| switch (err) { - // Generic poison means this is an untyped anonymous empty struct init - error.GenericPoison => return .empty_struct, + // Generic poison means this is an untyped anonymous empty struct/array init + error.GenericPoison => return .empty_tuple, else => |e| return e, }; const init_ty = if (is_byref) ty: { @@ -20671,7 +20734,7 @@ fn zirStructInit( const result_ty = sema.resolveType(block, src, first_field_type_extra.container_type) catch |err| switch (err) { error.GenericPoison => { // The type wasn't actually known, so treat this as an anon struct init. - return sema.structInitAnon(block, src, .typed_init, extra.data, extra.end, is_ref); + return sema.structInitAnon(block, src, inst, .typed_init, extra.data, extra.end, is_ref); }, else => |e| return e, }; @@ -20837,39 +20900,28 @@ fn finishStructInit( errdefer if (root_msg) |msg| msg.destroy(sema.gpa); switch (ip.indexToKey(struct_ty.toIntern())) { - .anon_struct_type => |anon_struct| { + .tuple_type => |tuple| { // We can't get the slices, as the coercion may invalidate them. - for (0..anon_struct.types.len) |i| { + for (0..tuple.types.len) |i| { if (field_inits[i] != .none) { // Coerce the init value to the field type. const field_src = block.src(.{ .init_elem = .{ .init_node_offset = init_src.offset.node_offset.x, .elem_index = @intCast(i), } }); - const field_ty = Type.fromInterned(anon_struct.types.get(ip)[i]); + const field_ty = Type.fromInterned(tuple.types.get(ip)[i]); field_inits[i] = try sema.coerce(block, field_ty, field_inits[i], field_src); continue; } - const default_val = anon_struct.values.get(ip)[i]; + const default_val = tuple.values.get(ip)[i]; if (default_val == .none) { - if (anon_struct.names.len == 0) { - const template = "missing tuple field with index {d}"; - if (root_msg) |msg| { - try sema.errNote(init_src, msg, template, .{i}); - } else { - root_msg = try sema.errMsg(init_src, template, .{i}); - } + const template = "missing tuple field with index {d}"; + if (root_msg) |msg| { + try sema.errNote(init_src, msg, template, .{i}); } else { - const field_name = anon_struct.names.get(ip)[i]; - const template = "missing struct field: {}"; - const args = .{field_name.fmt(ip)}; - if (root_msg) |msg| { - try sema.errNote(init_src, msg, template, args); - } else { - root_msg = try sema.errMsg(init_src, template, args); - } + root_msg = try sema.errMsg(init_src, template, .{i}); } } else { field_inits[i] = Air.internedToRef(default_val); @@ -20894,22 +20946,13 @@ fn finishStructInit( const field_init = struct_type.fieldInit(ip, i); if (field_init == .none) { - if (!struct_type.isTuple(ip)) { - const field_name = struct_type.field_names.get(ip)[i]; - const template = "missing struct field: {}"; - const args = .{field_name.fmt(ip)}; - if (root_msg) |msg| { - try sema.errNote(init_src, msg, template, args); - } else { - root_msg = try sema.errMsg(init_src, template, args); - } + const field_name = struct_type.field_names.get(ip)[i]; + const template = "missing struct field: {}"; + const args = .{field_name.fmt(ip)}; + if (root_msg) |msg| { + try sema.errNote(init_src, msg, template, args); } else { - const template = "missing tuple field with index {d}"; - if (root_msg) |msg| { - try sema.errNote(init_src, msg, template, .{i}); - } else { - root_msg = try sema.errMsg(init_src, template, .{i}); - } + root_msg = try sema.errMsg(init_src, template, args); } } else { field_inits[i] = Air.internedToRef(field_init); @@ -20970,8 +21013,7 @@ fn finishStructInit( const base_ptr = try sema.optEuBasePtrInit(block, alloc, init_src); for (field_inits, 0..) |field_init, i_usize| { const i: u32 = @intCast(i_usize); - const field_src = dest_src; - const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, base_ptr, i, field_src, struct_ty, true); + const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, base_ptr, i, struct_ty); try sema.storePtr(block, dest_src, field_ptr, field_init); } @@ -20995,13 +21037,14 @@ fn zirStructInitAnon( const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; const src = block.nodeOffset(inst_data.src_node); const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); - return sema.structInitAnon(block, src, .anon_init, extra.data, extra.end, false); + return sema.structInitAnon(block, src, inst, .anon_init, extra.data, extra.end, false); } fn structInitAnon( sema: *Sema, block: *Block, src: LazySrcLoc, + inst: Zir.Inst.Index, /// It is possible for a typed struct_init to be downgraded to an anonymous init due to a /// generic poison type. In this case, we need to know to interpret the extra data differently. comptime kind: enum { anon_init, typed_init }, @@ -21022,6 +21065,8 @@ fn structInitAnon( const values = try sema.arena.alloc(InternPool.Index, types.len); const names = try sema.arena.alloc(InternPool.NullTerminatedString, types.len); + var any_values = false; + // Find which field forces the expression to be runtime, if any. const opt_runtime_index = rs: { var runtime_index: ?usize = null; @@ -21063,6 +21108,7 @@ fn structInitAnon( } if (try sema.resolveValue(init)) |init_val| { field_val.* = init_val.toIntern(); + any_values = true; } else { field_val.* = .none; runtime_index = @intCast(i_usize); @@ -21071,18 +21117,76 @@ fn structInitAnon( break :rs runtime_index; }; - const tuple_ty = try ip.getAnonStructType(gpa, pt.tid, .{ - .names = names, - .types = types, - .values = values, - }); + // We treat anonymous struct types as reified types, because there are similarities: + // * They use a form of structural equivalence, which we can easily model using a custom hash + // * They do not have captures + // * They immediately have their fields resolved + // In general, other code should treat anon struct types and reified struct types identically, + // so there's no point having a separate `InternPool.NamespaceType` field for them. + const type_hash: u64 = hash: { + var hasher = std.hash.Wyhash.init(0); + hasher.update(std.mem.sliceAsBytes(types)); + hasher.update(std.mem.sliceAsBytes(values)); + hasher.update(std.mem.sliceAsBytes(names)); + break :hash hasher.final(); + }; + const tracked_inst = try block.trackZir(inst); + const struct_ty = switch (try ip.getStructType(gpa, pt.tid, .{ + .layout = .auto, + .fields_len = extra_data.fields_len, + .known_non_opv = false, + .requires_comptime = .unknown, + .any_comptime_fields = any_values, + .any_default_inits = any_values, + .inits_resolved = true, + .any_aligned_fields = false, + .key = .{ .reified = .{ + .zir_index = tracked_inst, + .type_hash = type_hash, + } }, + }, false)) { + .wip => |wip| ty: { + errdefer wip.cancel(ip, pt.tid); + wip.setName(ip, try sema.createTypeName(block, .anon, "struct", inst, wip.index)); + + const struct_type = ip.loadStructType(wip.index); + + for (names, values, 0..) |name, init_val, field_idx| { + assert(struct_type.addFieldName(ip, name) == null); + if (init_val != .none) struct_type.setFieldComptime(ip, field_idx); + } + + @memcpy(struct_type.field_types.get(ip), types); + if (any_values) { + @memcpy(struct_type.field_inits.get(ip), values); + } + + const new_namespace_index = try pt.createNamespace(.{ + .parent = block.namespace.toOptional(), + .owner_type = wip.index, + .file_scope = block.getFileScopeIndex(zcu), + .generation = zcu.generation, + }); + const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip.index); + try zcu.comp.queueJob(.{ .resolve_type_fully = wip.index }); + codegen_type: { + if (zcu.comp.config.use_llvm) break :codegen_type; + if (block.ownerModule().strip) break :codegen_type; + try zcu.comp.queueJob(.{ .codegen_type = wip.index }); + } + break :ty wip.finish(ip, new_cau_index.toOptional(), new_namespace_index); + }, + .existing => |ty| ty, + }; + try sema.declareDependency(.{ .interned = struct_ty }); + try sema.addTypeReferenceEntry(src, struct_ty); const runtime_index = opt_runtime_index orelse { - const tuple_val = try pt.intern(.{ .aggregate = .{ - .ty = tuple_ty, + const struct_val = try pt.intern(.{ .aggregate = .{ + .ty = struct_ty, .storage = .{ .elems = values }, } }); - return sema.addConstantMaybeRef(tuple_val, is_ref); + return sema.addConstantMaybeRef(struct_val, is_ref); }; try sema.requireRuntimeBlock(block, LazySrcLoc.unneeded, block.src(.{ .init_elem = .{ @@ -21093,7 +21197,7 @@ fn structInitAnon( if (is_ref) { const target = zcu.getTarget(); const alloc_ty = try pt.ptrTypeSema(.{ - .child = tuple_ty, + .child = struct_ty, .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, }); const alloc = try block.addTy(.alloc, alloc_ty); @@ -21131,7 +21235,7 @@ fn structInitAnon( element_refs[i] = try sema.resolveInst(item.data.init); } - return block.addAggregateInit(Type.fromInterned(tuple_ty), element_refs); + return block.addAggregateInit(Type.fromInterned(struct_ty), element_refs); } fn zirArrayInit( @@ -21340,10 +21444,9 @@ fn arrayInitAnon( break :rs runtime_src; }; - const tuple_ty = try ip.getAnonStructType(gpa, pt.tid, .{ + const tuple_ty = try ip.getTupleType(gpa, pt.tid, .{ .types = types, .values = values, - .names = &.{}, }); const runtime_src = opt_runtime_src orelse { @@ -21440,12 +21543,9 @@ fn fieldType( try cur_ty.resolveFields(pt); switch (cur_ty.zigTypeTag(zcu)) { .@"struct" => switch (ip.indexToKey(cur_ty.toIntern())) { - .anon_struct_type => |anon_struct| { - const field_index = if (anon_struct.names.len == 0) - try sema.tupleFieldIndex(block, cur_ty, field_name, field_src) - else - try sema.anonStructFieldIndex(block, cur_ty, field_name, field_src); - return Air.internedToRef(anon_struct.types.get(ip)[field_index]); + .tuple_type => |tuple| { + const field_index = try sema.tupleFieldIndex(block, cur_ty, field_name, field_src); + return Air.internedToRef(tuple.types.get(ip)[field_index]); }, .struct_type => { const struct_type = ip.loadStructType(cur_ty.toIntern()); @@ -22095,7 +22195,16 @@ fn zirReify( .needed_comptime_reason = "struct fields must be comptime-known", }); - return try sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_arr, name_strategy, is_tuple_val.toBool()); + if (is_tuple_val.toBool()) { + 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 => {}, + } + return sema.reifyTuple(block, src, fields_arr); + } else { + return sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_arr, name_strategy); + } }, .@"enum" => { const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); @@ -22696,6 +22805,104 @@ fn reifyUnion( return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index)); } +fn reifyTuple( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + fields_val: Value, +) CompileError!Air.Inst.Ref { + const pt = sema.pt; + const zcu = pt.zcu; + const gpa = sema.gpa; + const ip = &zcu.intern_pool; + + const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu)); + + const types = try sema.arena.alloc(InternPool.Index, fields_len); + const inits = try sema.arena.alloc(InternPool.Index, fields_len); + + for (types, inits, 0..) |*field_ty, *field_init, field_idx| { + const field_info = try fields_val.elemValue(pt, field_idx); + + const field_name_val = try field_info.fieldValue(pt, 0); + const field_type_val = try field_info.fieldValue(pt, 1); + const field_default_value_val = try field_info.fieldValue(pt, 2); + const field_is_comptime_val = try field_info.fieldValue(pt, 3); + const field_alignment_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 4)); + + const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ + .needed_comptime_reason = "tuple field name must be comptime-known", + }); + const field_type = field_type_val.toType(); + const field_default_value: InternPool.Index = if (field_default_value_val.optionalValue(zcu)) |ptr_val| d: { + const ptr_ty = try pt.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 = "tuple 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; + + 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_name_index != field_idx) { + return sema.fail( + block, + src, + "tuple field name '{}' does not match field index {}", + .{ field_name_index, field_idx }, + ); + } + + try sema.validateTupleFieldType(block, field_type, src); + + { + const alignment_ok = ok: { + if (field_alignment_val.toIntern() == .zero) break :ok true; + const given_align = try field_alignment_val.getUnsignedIntSema(pt) orelse break :ok false; + const abi_align = (try field_type.abiAlignmentSema(pt)).toByteUnits() orelse 0; + break :ok abi_align == given_align; + }; + if (!alignment_ok) { + return sema.fail(block, src, "tuple fields cannot specify alignment", .{}); + } + } + + if (field_is_comptime_val.toBool() and field_default_value == .none) { + return sema.fail(block, src, "comptime field without default initialization value", .{}); + } + + if (!field_is_comptime_val.toBool() and field_default_value != .none) { + return sema.fail(block, src, "non-comptime tuple fields cannot specify default initialization value", .{}); + } + + const default_or_opv: InternPool.Index = default: { + if (field_default_value != .none) { + break :default field_default_value; + } + if (try sema.typeHasOnePossibleValue(field_type)) |opv| { + break :default opv.toIntern(); + } + break :default .none; + }; + + field_ty.* = field_type.toIntern(); + field_init.* = default_or_opv; + } + + return Air.internedToRef(try zcu.intern_pool.getTupleType(gpa, pt.tid, .{ + .types = types, + .values = inits, + })); +} + fn reifyStruct( sema: *Sema, block: *Block, @@ -22705,7 +22912,6 @@ fn reifyStruct( opt_backing_int_val: Value, fields_val: Value, name_strategy: Zir.Inst.NameStrategy, - is_tuple: bool, ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; @@ -22725,7 +22931,6 @@ fn reifyStruct( 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; @@ -22781,7 +22986,6 @@ fn reifyStruct( .fields_len = fields_len, .known_non_opv = false, .requires_comptime = .unknown, - .is_tuple = is_tuple, .any_comptime_fields = any_comptime_fields, .any_default_inits = any_default_inits, .any_aligned_fields = any_aligned_fields, @@ -22800,12 +23004,6 @@ fn reifyStruct( }; errdefer wip_ty.cancel(ip, pt.tid); - 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 => {}, - }; - wip_ty.setName(ip, try sema.createTypeName( block, name_strategy, @@ -22828,22 +23026,7 @@ fn reifyStruct( const field_ty = field_type_val.toType(); // Don't pass a reason; first loop acts as an assertion that this is valid. const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined); - if (is_tuple) { - 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_name_index != field_idx) { - return sema.fail( - block, - src, - "tuple field name '{}' does not match field index {}", - .{ field_name_index, field_idx }, - ); - } - } else if (struct_type.addFieldName(ip, field_name)) |prev_index| { + if (struct_type.addFieldName(ip, field_name)) |prev_index| { _ = prev_index; // TODO: better source location return sema.fail(block, src, "duplicate struct field name {}", .{field_name.fmt(ip)}); } @@ -25579,7 +25762,7 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const args = try sema.resolveInst(extra.args); const args_ty = sema.typeOf(args); - if (!args_ty.isTuple(zcu) and args_ty.toIntern() != .empty_struct_type) { + if (!args_ty.isTuple(zcu)) { return sema.fail(block, args_src, "expected a tuple, found '{}'", .{args_ty.fmt(pt)}); } @@ -27471,7 +27654,7 @@ fn explainWhyTypeIsComptimeInner( for (0..struct_type.field_types.len) |i| { const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); const field_src: LazySrcLoc = .{ - .base_node_inst = struct_type.zir_index.unwrap().?, + .base_node_inst = struct_type.zir_index, .offset = .{ .container_field_type = @intCast(i) }, }; @@ -28236,11 +28419,10 @@ fn fieldVal( return Air.internedToRef(enum_val.toIntern()); }, .@"struct", .@"opaque" => { - switch (child_type.toIntern()) { - .empty_struct_type, .anyopaque_type => {}, // no namespace - else => if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { + if (!child_type.isTuple(zcu) and child_type.toIntern() != .anyopaque_type) { + if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { return inst; - }, + } } return sema.failWithBadMemberAccess(block, child_type, src, field_name); }, @@ -28788,9 +28970,6 @@ fn structFieldPtr( } const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src); return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing); - } else if (struct_ty.isAnonStruct(zcu)) { - const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src); - return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing); } const struct_type = zcu.typeToStruct(struct_ty).?; @@ -28798,7 +28977,7 @@ fn structFieldPtr( const field_index = struct_type.nameIndex(ip, field_name) orelse return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name); - return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, field_name_src, struct_ty, initializing); + return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, struct_ty); } fn structFieldPtrByIndex( @@ -28807,16 +28986,11 @@ fn structFieldPtrByIndex( src: LazySrcLoc, struct_ptr: Air.Inst.Ref, field_index: u32, - field_src: LazySrcLoc, struct_ty: Type, - initializing: bool, ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; const ip = &zcu.intern_pool; - if (struct_ty.isAnonStruct(zcu)) { - return sema.tupleFieldPtr(block, src, struct_ptr, field_src, field_index, initializing); - } if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { const val = try struct_ptr_val.ptrField(field_index, pt); @@ -28909,8 +29083,6 @@ fn structFieldVal( switch (ip.indexToKey(struct_ty.toIntern())) { .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); const field_index = struct_type.nameIndex(ip, field_name) orelse return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name); @@ -28935,13 +29107,8 @@ fn structFieldVal( try field_ty.resolveLayout(pt); return block.addStructFieldVal(struct_byval, field_index, field_ty); }, - .anon_struct_type => |anon_struct| { - if (anon_struct.names.len == 0) { - return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty); - } else { - const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src); - return sema.tupleFieldValByIndex(block, src, struct_byval, field_index, struct_ty); - } + .tuple_type => { + return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty); }, else => unreachable, } @@ -30087,39 +30254,7 @@ fn coerceExtra( }, else => {}, }, - .One => switch (Type.fromInterned(dest_info.child).zigTypeTag(zcu)) { - .@"union" => { - // pointer to anonymous struct to pointer to union - if (inst_ty.isSinglePointer(zcu) and - inst_ty.childType(zcu).isAnonStruct(zcu) and - sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) - { - return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src); - } - }, - .@"struct" => { - // pointer to anonymous struct to pointer to struct - if (inst_ty.isSinglePointer(zcu) and - inst_ty.childType(zcu).isAnonStruct(zcu) and - sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) - { - return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src) catch |err| switch (err) { - error.NotCoercible => break :pointer, - else => |e| return e, - }; - } - }, - .array => { - // pointer to tuple to pointer to array - if (inst_ty.isSinglePointer(zcu) and - inst_ty.childType(zcu).isTuple(zcu) and - sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) - { - return sema.coerceTupleToArrayPtrs(block, dest_ty, dest_ty_src, inst, inst_src); - } - }, - else => {}, - }, + .One => {}, .Slice => to_slice: { if (inst_ty.zigTypeTag(zcu) == .array) { return sema.fail( @@ -30368,11 +30503,6 @@ fn coerceExtra( }, .@"union" => switch (inst_ty.zigTypeTag(zcu)) { .@"enum", .enum_literal => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src), - .@"struct" => { - if (inst_ty.isAnonStruct(zcu)) { - return sema.coerceAnonStructToUnion(block, dest_ty, dest_ty_src, inst, inst_src); - } - }, else => {}, }, .array => switch (inst_ty.zigTypeTag(zcu)) { @@ -30402,9 +30532,6 @@ fn coerceExtra( }, .vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), .@"struct" => { - if (inst == .empty_struct) { - return sema.arrayInitEmpty(block, inst_src, dest_ty); - } if (inst_ty.isTuple(zcu)) { return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); } @@ -30421,10 +30548,7 @@ fn coerceExtra( else => {}, }, .@"struct" => blk: { - if (inst == .empty_struct) { - return sema.structInitEmpty(block, dest_ty, dest_ty_src, inst_src); - } - if (inst_ty.isTupleOrAnonStruct(zcu)) { + if (inst_ty.isTuple(zcu)) { return sema.coerceTupleToStruct(block, dest_ty, inst, inst_src) catch |err| switch (err) { error.NotCoercible => break :blk, else => |e| return e, @@ -32208,97 +32332,6 @@ fn coerceEnumToUnion( return sema.failWithOwnedErrorMsg(block, msg); } -fn coerceAnonStructToUnion( - sema: *Sema, - block: *Block, - union_ty: Type, - union_ty_src: LazySrcLoc, - inst: Air.Inst.Ref, - inst_src: LazySrcLoc, -) !Air.Inst.Ref { - const pt = sema.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const inst_ty = sema.typeOf(inst); - const field_info: union(enum) { - name: InternPool.NullTerminatedString, - count: usize, - } = switch (ip.indexToKey(inst_ty.toIntern())) { - .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len == 1) - .{ .name = anon_struct_type.names.get(ip)[0] } - else - .{ .count = anon_struct_type.names.len }, - .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 - .{ .count = field_names.len }; - }, - else => unreachable, - }; - switch (field_info) { - .name => |field_name| { - const init = try sema.structFieldVal(block, inst_src, inst, field_name, inst_src, inst_ty); - return sema.unionInit(block, init, inst_src, union_ty, union_ty_src, field_name, inst_src); - }, - .count => |field_count| { - assert(field_count != 1); - const msg = msg: { - const msg = if (field_count > 1) try sema.errMsg( - inst_src, - "cannot initialize multiple union fields at once; unions can only have one active field", - .{}, - ) else try sema.errMsg( - inst_src, - "union initializer must initialize one field", - .{}, - ); - errdefer msg.destroy(sema.gpa); - - // TODO add notes for where the anon struct was created to point out - // the extra fields. - - try sema.addDeclaredHereNote(msg, union_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - }, - } -} - -fn coerceAnonStructToUnionPtrs( - sema: *Sema, - block: *Block, - ptr_union_ty: Type, - union_ty_src: LazySrcLoc, - ptr_anon_struct: Air.Inst.Ref, - anon_struct_src: LazySrcLoc, -) !Air.Inst.Ref { - const pt = sema.pt; - const zcu = pt.zcu; - const union_ty = ptr_union_ty.childType(zcu); - const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src); - const union_inst = try sema.coerceAnonStructToUnion(block, union_ty, union_ty_src, anon_struct, anon_struct_src); - return sema.analyzeRef(block, union_ty_src, union_inst); -} - -fn coerceAnonStructToStructPtrs( - sema: *Sema, - block: *Block, - ptr_struct_ty: Type, - struct_ty_src: LazySrcLoc, - ptr_anon_struct: Air.Inst.Ref, - anon_struct_src: LazySrcLoc, -) !Air.Inst.Ref { - const pt = sema.pt; - const zcu = pt.zcu; - const struct_ty = ptr_struct_ty.childType(zcu); - const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src); - const struct_inst = try sema.coerceTupleToStruct(block, struct_ty, anon_struct, anon_struct_src); - return sema.analyzeRef(block, struct_ty_src, struct_inst); -} - /// If the lengths match, coerces element-wise. fn coerceArrayLike( sema: *Sema, @@ -32530,7 +32563,7 @@ fn coerceTupleToStruct( try struct_ty.resolveFields(pt); try struct_ty.resolveStructFieldInits(pt); - if (struct_ty.isTupleOrAnonStruct(zcu)) { + if (struct_ty.isTuple(zcu)) { return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src); } @@ -32542,7 +32575,7 @@ fn coerceTupleToStruct( const inst_ty = sema.typeOf(inst); 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, + .tuple_type => |tuple| tuple.types.len, .struct_type => ip.loadStructType(inst_ty.toIntern()).field_types.len, else => unreachable, }; @@ -32557,7 +32590,7 @@ fn coerceTupleToStruct( const coerced = try sema.coerce(block, struct_field_ty, elem_ref, field_src); field_refs[struct_field_index] = coerced; if (struct_type.fieldIsComptime(ip, struct_field_index)) { - const init_val = (try sema.resolveValue(coerced)) orelse { + const init_val = try sema.resolveValue(coerced) orelse { return sema.failWithNeededComptime(block, field_src, .{ .needed_comptime_reason = "value stored in comptime field must be comptime-known", }); @@ -32636,8 +32669,7 @@ fn coerceTupleToTuple( const zcu = pt.zcu; const ip = &zcu.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 => ip.loadStructType(tuple_ty.toIntern()).field_types.len, + .tuple_type => |tuple_type| tuple_type.types.len, else => unreachable, }; const field_vals = try sema.arena.alloc(InternPool.Index, dest_field_count); @@ -32646,8 +32678,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 => ip.loadStructType(inst_ty.toIntern()).field_types.len, + .tuple_type => |tuple_type| tuple_type.types.len, else => unreachable, }; if (src_field_count > dest_field_count) return error.NotCoercible; @@ -32656,24 +32687,19 @@ fn coerceTupleToTuple( for (0..dest_field_count) |field_index_usize| { const field_i: u32 = @intCast(field_index_usize); const field_src = inst_src; // TODO better source location - const field_name = inst_ty.structFieldName(field_index_usize, zcu).unwrap() orelse - try ip.getOrPutStringFmt(sema.gpa, pt.tid, "{d}", .{field_index_usize}, .no_embedded_nulls); - - if (field_name.eqlSlice("len", ip)) - return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{}); const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) { - .anon_struct_type => |anon_struct_type| anon_struct_type.types.get(ip)[field_index_usize], + .tuple_type => |tuple_type| tuple_type.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], + .tuple_type => |tuple_type| tuple_type.values.get(ip)[field_index_usize], .struct_type => ip.loadStructType(tuple_ty.toIntern()).fieldInit(ip, field_index_usize), else => unreachable, }; - const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_src); + const field_index: u32 = @intCast(field_index_usize); const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); const coerced = try sema.coerce(block, Type.fromInterned(field_ty), elem_ref, field_src); @@ -32707,28 +32733,18 @@ fn coerceTupleToTuple( if (field_ref.* != .none) continue; const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { - .anon_struct_type => |anon_struct_type| anon_struct_type.values.get(ip)[i], + .tuple_type => |tuple_type| tuple_type.values.get(ip)[i], .struct_type => ip.loadStructType(tuple_ty.toIntern()).fieldInit(ip, i), else => unreachable, }; const field_src = inst_src; // TODO better source location if (default_val == .none) { - const field_name = tuple_ty.structFieldName(i, zcu).unwrap() orelse { - const template = "missing tuple field: {d}"; - if (root_msg) |msg| { - try sema.errNote(field_src, msg, template, .{i}); - } else { - root_msg = try sema.errMsg(field_src, template, .{i}); - } - continue; - }; - const template = "missing struct field: {}"; - const args = .{field_name.fmt(ip)}; + const template = "missing tuple field: {d}"; if (root_msg) |msg| { - try sema.errNote(field_src, msg, template, args); + try sema.errNote(field_src, msg, template, .{i}); } else { - root_msg = try sema.errMsg(field_src, template, args); + root_msg = try sema.errMsg(field_src, template, .{i}); } continue; } @@ -34265,8 +34281,8 @@ const PeerResolveStrategy = enum { fixed_int, /// The type must be some fixed-width float type. fixed_float, - /// The type must be a struct literal or tuple type. - coercible_struct, + /// The type must be a tuple. + tuple, /// The peers must all be of the same type. exact, @@ -34350,9 +34366,9 @@ const PeerResolveStrategy = enum { .fixed_float => .{ .either, .fixed_float }, else => .{ .all_s1, s1 }, // doesn't override anything later }, - .coercible_struct => switch (s1) { + .tuple => switch (s1) { .exact => .{ .all_s1, .exact }, - else => .{ .all_s0, .coercible_struct }, + else => .{ .all_s0, .tuple }, }, .exact => .{ .all_s0, .exact }, }; @@ -34393,7 +34409,7 @@ const PeerResolveStrategy = enum { .error_set => .error_set, .error_union => .error_union, .enum_literal, .@"enum", .@"union" => .enum_or_union, - .@"struct" => if (ty.isTupleOrAnonStruct(zcu)) .coercible_struct else .exact, + .@"struct" => if (ty.isTuple(zcu)) .tuple else .exact, .@"fn" => .func, }; } @@ -35501,19 +35517,17 @@ fn resolvePeerTypesInner( return .{ .success = opt_cur_ty.? }; }, - .coercible_struct => { - // First, check that every peer has the same approximate structure (field count and names) + .tuple => { + // First, check that every peer has the same approximate structure (field count) var opt_first_idx: ?usize = null; var is_tuple: bool = undefined; var field_count: usize = undefined; - // Only defined for non-tuples. - var field_names: []InternPool.NullTerminatedString = undefined; for (peer_tys, 0..) |opt_ty, i| { const ty = opt_ty orelse continue; - if (!ty.isTupleOrAnonStruct(zcu)) { + if (!ty.isTuple(zcu)) { return .{ .conflict = .{ .peer_idx_a = strat_reason, .peer_idx_b = i, @@ -35524,31 +35538,15 @@ fn resolvePeerTypesInner( opt_first_idx = i; is_tuple = ty.isTuple(zcu); field_count = ty.structFieldCount(zcu); - if (!is_tuple) { - const names = ip.indexToKey(ty.toIntern()).anon_struct_type.names.get(ip); - field_names = try sema.arena.dupe(InternPool.NullTerminatedString, names); - } continue; }; - if (ty.isTuple(zcu) != is_tuple or ty.structFieldCount(zcu) != field_count) { + if (ty.structFieldCount(zcu) != field_count) { return .{ .conflict = .{ .peer_idx_a = first_idx, .peer_idx_b = i, } }; } - - if (!is_tuple) { - for (field_names, 0..) |expected, field_index_usize| { - const field_index: u32 = @intCast(field_index_usize); - const actual = ty.structFieldName(field_index, zcu).unwrap().?; - if (actual == expected) continue; - return .{ .conflict = .{ - .peer_idx_a = first_idx, - .peer_idx_b = i, - } }; - } - } } assert(opt_first_idx != null); @@ -35578,10 +35576,7 @@ fn resolvePeerTypesInner( else => |result| { const result_buf = try sema.arena.create(PeerResolveResult); result_buf.* = result; - const field_name = if (is_tuple) - try ip.getOrPutStringFmt(sema.gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls) - else - field_names[field_index]; + const field_name = try ip.getOrPutStringFmt(sema.gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls); // The error info needs the field types, but we can't reuse sub_peer_tys // since the recursive call may have clobbered it. @@ -35636,9 +35631,8 @@ fn resolvePeerTypesInner( field_val.* = if (comptime_val) |v| v.toIntern() else .none; } - const final_ty = try ip.getAnonStructType(zcu.gpa, pt.tid, .{ + const final_ty = try ip.getTupleType(zcu.gpa, pt.tid, .{ .types = field_types, - .names = if (is_tuple) &.{} else field_names, .values = field_vals, }); @@ -35778,7 +35772,7 @@ pub fn resolveStructAlignment( const ip = &zcu.intern_pool; const target = zcu.getTarget(); - assert(sema.owner.unwrap().cau == struct_type.cau.unwrap().?); + assert(sema.owner.unwrap().cau == struct_type.cau); assert(struct_type.layout != .@"packed"); assert(struct_type.flagsUnordered(ip).alignment == .none); @@ -35821,7 +35815,7 @@ pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void { const ip = &zcu.intern_pool; const struct_type = zcu.typeToStruct(ty) orelse return; - assert(sema.owner.unwrap().cau == struct_type.cau.unwrap().?); + assert(sema.owner.unwrap().cau == struct_type.cau); if (struct_type.haveLayout(ip)) return; @@ -35921,12 +35915,14 @@ pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void { return a_align.compare(.gt, b_align); } }; - if (struct_type.isTuple(ip) or !zcu.backendSupportsFeature(.field_reordering)) { - // TODO: don't handle tuples differently. This logic exists only because it - // uncovers latent bugs if removed. Fix the latent bugs and remove this logic! - // Likewise, implement field reordering support in all the backends! + if (!zcu.backendSupportsFeature(.field_reordering)) { + // TODO: we should probably also reorder tuple fields? This is a bit weird because it'll involve + // mutating the `InternPool` for a non-container type. + // + // TODO: implement field reordering support in all the backends! + // // This logic does not reorder fields; it only moves the omitted ones to the end - // so that logic elsewhere does not need to special-case tuples. + // so that logic elsewhere does not need to special-case here. var i: usize = 0; var off: usize = 0; while (i + off < runtime_order.len) { @@ -35966,7 +35962,7 @@ fn backingIntType( const gpa = zcu.gpa; const ip = &zcu.intern_pool; - const cau_index = struct_type.cau.unwrap().?; + const cau_index = struct_type.cau; var analysis_arena = std.heap.ArenaAllocator.init(gpa); defer analysis_arena.deinit(); @@ -35978,7 +35974,7 @@ fn backingIntType( .instructions = .{}, .inlining = null, .is_comptime = true, - .src_base_inst = struct_type.zir_index.unwrap().?, + .src_base_inst = struct_type.zir_index, .type_name_ctx = struct_type.name, }; defer assert(block.instructions.items.len == 0); @@ -35992,8 +35988,8 @@ fn backingIntType( break :blk accumulator; }; - const zir = zcu.namespacePtr(struct_type.namespace.unwrap().?).fileScope(zcu).zir; - const zir_index = struct_type.zir_index.unwrap().?.resolve(ip) orelse return error.AnalysisFail; + const zir = zcu.namespacePtr(struct_type.namespace).fileScope(zcu).zir; + const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail; const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; assert(extended.opcode == .struct_decl); const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); @@ -36014,7 +36010,7 @@ fn backingIntType( extra_index += 1; const backing_int_src: LazySrcLoc = .{ - .base_node_inst = struct_type.zir_index.unwrap().?, + .base_node_inst = struct_type.zir_index, .offset = .{ .node_offset_container_tag = 0 }, }; const backing_int_ty = blk: { @@ -36261,7 +36257,7 @@ pub fn resolveStructFully(sema: *Sema, ty: Type) SemaError!void { const ip = &zcu.intern_pool; const struct_type = zcu.typeToStruct(ty).?; - assert(sema.owner.unwrap().cau == struct_type.cau.unwrap().?); + assert(sema.owner.unwrap().cau == struct_type.cau); if (struct_type.setFullyResolved(ip)) return; errdefer struct_type.clearFullyResolved(ip); @@ -36319,7 +36315,7 @@ pub fn resolveStructFieldTypes( const zcu = pt.zcu; const ip = &zcu.intern_pool; - assert(sema.owner.unwrap().cau == struct_type.cau.unwrap().?); + assert(sema.owner.unwrap().cau == struct_type.cau); if (struct_type.haveFieldTypes(ip)) return; @@ -36345,7 +36341,7 @@ pub fn resolveStructFieldInits(sema: *Sema, ty: Type) SemaError!void { const ip = &zcu.intern_pool; const struct_type = zcu.typeToStruct(ty) orelse return; - assert(sema.owner.unwrap().cau == struct_type.cau.unwrap().?); + assert(sema.owner.unwrap().cau == struct_type.cau); // Inits can start as resolved if (struct_type.haveFieldInits(ip)) return; @@ -36607,12 +36603,12 @@ fn structFields( const zcu = pt.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; - const cau_index = struct_type.cau.unwrap().?; + const cau_index = struct_type.cau; const namespace_index = ip.getCau(cau_index).namespace; const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir; - const zir_index = struct_type.zir_index.unwrap().?.resolve(ip) orelse return error.AnalysisFail; + const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail; - const fields_len, const small, var extra_index = structZirInfo(zir, zir_index); + const fields_len, _, var extra_index = structZirInfo(zir, zir_index); if (fields_len == 0) switch (struct_type.layout) { .@"packed" => { @@ -36632,7 +36628,7 @@ fn structFields( .instructions = .{}, .inlining = null, .is_comptime = true, - .src_base_inst = struct_type.zir_index.unwrap().?, + .src_base_inst = struct_type.zir_index, .type_name_ctx = struct_type.name, }; defer assert(block_scope.instructions.items.len == 0); @@ -36673,12 +36669,8 @@ fn structFields( if (is_comptime) struct_type.setFieldComptime(ip, field_i); - var opt_field_name_zir: ?[:0]const u8 = null; - if (!small.is_tuple) { - opt_field_name_zir = zir.nullTerminatedString(@enumFromInt(zir.extra[extra_index])); - extra_index += 1; - } - extra_index += 1; // doc_comment + const field_name_zir: [:0]const u8 = zir.nullTerminatedString(@enumFromInt(zir.extra[extra_index])); + extra_index += 2; // field_name, doc_comment fields[field_i] = .{}; @@ -36690,10 +36682,8 @@ fn structFields( extra_index += 1; // This string needs to outlive the ZIR code. - if (opt_field_name_zir) |field_name_zir| { - const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls); - assert(struct_type.addFieldName(ip, field_name) == null); - } + const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls); + assert(struct_type.addFieldName(ip, field_name) == null); if (has_align) { fields[field_i].align_body_len = zir.extra[extra_index]; @@ -36713,7 +36703,7 @@ fn structFields( for (fields, 0..) |zir_field, field_i| { const ty_src: LazySrcLoc = .{ - .base_node_inst = struct_type.zir_index.unwrap().?, + .base_node_inst = struct_type.zir_index, .offset = .{ .container_field_type = @intCast(field_i) }, }; const field_ty: Type = ty: { @@ -36785,7 +36775,7 @@ fn structFields( extra_index += body.len; const align_ref = try sema.resolveInlineBody(&block_scope, body, zir_index); const align_src: LazySrcLoc = .{ - .base_node_inst = struct_type.zir_index.unwrap().?, + .base_node_inst = struct_type.zir_index, .offset = .{ .container_field_align = @intCast(field_i) }, }; const field_align = try sema.analyzeAsAlign(&block_scope, align_src, align_ref); @@ -36812,11 +36802,11 @@ fn structFieldInits( assert(!struct_type.haveFieldInits(ip)); - const cau_index = struct_type.cau.unwrap().?; + const cau_index = struct_type.cau; const namespace_index = ip.getCau(cau_index).namespace; const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir; - const zir_index = struct_type.zir_index.unwrap().?.resolve(ip) orelse return error.AnalysisFail; - const fields_len, const small, var extra_index = structZirInfo(zir, zir_index); + const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail; + const fields_len, _, var extra_index = structZirInfo(zir, zir_index); var block_scope: Block = .{ .parent = null, @@ -36825,7 +36815,7 @@ fn structFieldInits( .instructions = .{}, .inlining = null, .is_comptime = true, - .src_base_inst = struct_type.zir_index.unwrap().?, + .src_base_inst = struct_type.zir_index, .type_name_ctx = struct_type.name, }; defer assert(block_scope.instructions.items.len == 0); @@ -36860,10 +36850,7 @@ fn structFieldInits( const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0; cur_bit_bag >>= 1; - if (!small.is_tuple) { - extra_index += 1; - } - extra_index += 1; // doc_comment + extra_index += 2; // field_name, doc_comment fields[field_i] = .{}; @@ -36901,7 +36888,7 @@ fn structFieldInits( sema.inst_map.putAssumeCapacity(zir_index, type_ref); const init_src: LazySrcLoc = .{ - .base_node_inst = struct_type.zir_index.unwrap().?, + .base_node_inst = struct_type.zir_index, .offset = .{ .container_field_value = @intCast(field_i) }, }; @@ -37430,7 +37417,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { .undefined_type => Value.undef, .optional_noreturn_type => try pt.nullValue(ty), .generic_poison_type => error.GenericPoison, - .empty_struct_type => Value.empty_struct, + .empty_tuple_type => Value.empty_tuple, // values, not types .undef, .zero, @@ -37446,7 +37433,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { .null_value, .bool_true, .bool_false, - .empty_struct, + .empty_tuple, .generic_poison, // invalid .none, @@ -37532,10 +37519,9 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { .type_enum_explicit, .type_enum_nonexhaustive, .type_struct, - .type_struct_anon, .type_struct_packed, .type_struct_packed_inits, - .type_tuple_anon, + .type_tuple, .type_union, => switch (ip.indexToKey(ty.toIntern())) { inline .array_type, .vector_type => |seq_type, seq_tag| { @@ -37594,7 +37580,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { } })); }, - .anon_struct_type => |tuple| { + .tuple_type => |tuple| { for (tuple.values.get(ip)) |val| { if (val == .none) return null; } @@ -37965,35 +37951,9 @@ fn structFieldIndex( const zcu = pt.zcu; const ip = &zcu.intern_pool; try struct_ty.resolveFields(pt); - if (struct_ty.isAnonStruct(zcu)) { - return sema.anonStructFieldIndex(block, struct_ty, field_name, field_src); - } else { - const struct_type = zcu.typeToStruct(struct_ty).?; - return struct_type.nameIndex(ip, field_name) orelse - return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_src, field_name); - } -} - -fn anonStructFieldIndex( - sema: *Sema, - block: *Block, - struct_ty: Type, - field_name: InternPool.NullTerminatedString, - field_src: LazySrcLoc, -) !u32 { - const pt = sema.pt; - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - switch (ip.indexToKey(struct_ty.toIntern())) { - .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 => 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 '{}'", .{ - field_name.fmt(ip), struct_ty.fmt(pt), - }); + const struct_type = zcu.typeToStruct(struct_ty).?; + return struct_type.nameIndex(ip, field_name) orelse + return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_src, field_name); } /// If the value overflowed the type, returns a comptime_int (or vector thereof) instead, setting |
