| author | |
| committer | |
| log | 81b5df347a2b92566dc679fdd4e110812dfe27d0 |
| tree | 2e2580b944374f490797ef3211eb752c7b1bc051 |
| parent | edfada4317760a152ad9c36b39f5a2e68eeaebf8 |
When struct types have no field names, the names are implicitly
understood to be strings corresponding to the field indexes in
declaration order. It used to be the case that a NullTerminatedString
would be stored for each field in this case, however, now, callers must
handle the possibility that there are no names stored at all. This
commit introduces `legacyStructFieldName`, a function to fake the
previous behavior. Probably something better could be done by reworking
all the callsites of this function.6 files changed, 66 insertions(+), 36 deletions(-)
src/InternPool.zig+11| ... | ... | @@ -656,6 +656,17 @@ pub const Key = union(enum) { |
| 656 | 656 | pub fn isTuple(self: AnonStructType) bool { |
| 657 | 657 | return self.names.len == 0; |
| 658 | 658 | } |
| 659 | ||
| 660 | pub fn fieldName( | |
| 661 | self: AnonStructType, | |
| 662 | ip: *const InternPool, | |
| 663 | index: u32, | |
| 664 | ) OptionalNullTerminatedString { | |
| 665 | if (self.names.len == 0) | |
| 666 | return .none; | |
| 667 | ||
| 668 | return self.names.get(ip)[index].toOptional(); | |
| 669 | } | |
| 659 | 670 | }; |
| 660 | 671 | |
| 661 | 672 | /// Serves two purposes: |
src/Sema.zig+19-17| ... | ... | @@ -4699,12 +4699,13 @@ fn validateStructInit( |
| 4699 | 4699 | // In this case the only thing we need to do is evaluate the implicit |
| 4700 | 4700 | // store instructions for default field values, and report any missing fields. |
| 4701 | 4701 | // Avoid the cost of the extra machinery for detecting a comptime struct init value. |
| 4702 | for (found_fields, 0..) |field_ptr, i| { | |
| 4702 | for (found_fields, 0..) |field_ptr, i_usize| { | |
| 4703 | const i: u32 = @intCast(i_usize); | |
| 4703 | 4704 | if (field_ptr != 0) continue; |
| 4704 | 4705 | |
| 4705 | 4706 | const default_val = struct_ty.structFieldDefaultValue(i, mod); |
| 4706 | 4707 | if (default_val.toIntern() == .unreachable_value) { |
| 4707 | if (struct_ty.isTuple(mod)) { | |
| 4708 | const field_name = struct_ty.structFieldName(i, mod).unwrap() orelse { | |
| 4708 | 4709 | const template = "missing tuple field with index {d}"; |
| 4709 | 4710 | if (root_msg) |msg| { |
| 4710 | 4711 | try sema.errNote(block, init_src, msg, template, .{i}); |
| ... | ... | @@ -4712,8 +4713,7 @@ fn validateStructInit( |
| 4712 | 4713 | root_msg = try sema.errMsg(block, init_src, template, .{i}); |
| 4713 | 4714 | } |
| 4714 | 4715 | continue; |
| 4715 | } | |
| 4716 | const field_name = struct_ty.structFieldName(i, mod); | |
| 4716 | }; | |
| 4717 | 4717 | const template = "missing struct field: {}"; |
| 4718 | 4718 | const args = .{field_name.fmt(ip)}; |
| 4719 | 4719 | if (root_msg) |msg| { |
| ... | ... | @@ -4763,7 +4763,8 @@ fn validateStructInit( |
| 4763 | 4763 | // ends up being comptime-known. |
| 4764 | 4764 | const field_values = try sema.arena.alloc(InternPool.Index, struct_ty.structFieldCount(mod)); |
| 4765 | 4765 | |
| 4766 | field: for (found_fields, 0..) |field_ptr, i| { | |
| 4766 | field: for (found_fields, 0..) |field_ptr, i_usize| { | |
| 4767 | const i: u32 = @intCast(i_usize); | |
| 4767 | 4768 | if (field_ptr != 0) { |
| 4768 | 4769 | // Determine whether the value stored to this pointer is comptime-known. |
| 4769 | 4770 | const field_ty = struct_ty.structFieldType(i, mod); |
| ... | ... | @@ -4842,7 +4843,7 @@ fn validateStructInit( |
| 4842 | 4843 | |
| 4843 | 4844 | const default_val = struct_ty.structFieldDefaultValue(i, mod); |
| 4844 | 4845 | if (default_val.toIntern() == .unreachable_value) { |
| 4845 | if (struct_ty.isTuple(mod)) { | |
| 4846 | const field_name = struct_ty.structFieldName(i, mod).unwrap() orelse { | |
| 4846 | 4847 | const template = "missing tuple field with index {d}"; |
| 4847 | 4848 | if (root_msg) |msg| { |
| 4848 | 4849 | try sema.errNote(block, init_src, msg, template, .{i}); |
| ... | ... | @@ -4850,8 +4851,7 @@ fn validateStructInit( |
| 4850 | 4851 | root_msg = try sema.errMsg(block, init_src, template, .{i}); |
| 4851 | 4852 | } |
| 4852 | 4853 | continue; |
| 4853 | } | |
| 4854 | const field_name = struct_ty.structFieldName(i, mod); | |
| 4854 | }; | |
| 4855 | 4855 | const template = "missing struct field: {}"; |
| 4856 | 4856 | const args = .{field_name.fmt(ip)}; |
| 4857 | 4857 | if (root_msg) |msg| { |
| ... | ... | @@ -21862,10 +21862,10 @@ fn ptrCastFull( |
| 21862 | 21862 | const msg = try sema.errMsg(block, src, "cast increases pointer alignment", .{}); |
| 21863 | 21863 | errdefer msg.destroy(sema.gpa); |
| 21864 | 21864 | try sema.errNote(block, operand_src, msg, "'{}' has alignment '{d}'", .{ |
| 21865 | operand_ty.fmt(mod), src_align, | |
| 21865 | operand_ty.fmt(mod), src_align.toByteUnits(0), | |
| 21866 | 21866 | }); |
| 21867 | 21867 | try sema.errNote(block, src, msg, "'{}' has alignment '{d}'", .{ |
| 21868 | dest_ty.fmt(mod), dest_align, | |
| 21868 | dest_ty.fmt(mod), dest_align.toByteUnits(0), | |
| 21869 | 21869 | }); |
| 21870 | 21870 | try sema.errNote(block, src, msg, "use @alignCast to assert pointer alignment", .{}); |
| 21871 | 21871 | break :msg msg; |
| ... | ... | @@ -26376,7 +26376,7 @@ fn fieldCallBind( |
| 26376 | 26376 | const max = concrete_ty.structFieldCount(mod); |
| 26377 | 26377 | for (0..max) |i_usize| { |
| 26378 | 26378 | const i: u32 = @intCast(i_usize); |
| 26379 | if (field_name == concrete_ty.structFieldName(i, mod)) { | |
| 26379 | if (field_name == concrete_ty.structFieldName(i, mod).unwrap().?) { | |
| 26380 | 26380 | return sema.finishFieldCallBind(block, src, ptr_ty, concrete_ty.structFieldType(i, mod), i, object_ptr); |
| 26381 | 26381 | } |
| 26382 | 26382 | } |
| ... | ... | @@ -31143,7 +31143,8 @@ fn coerceTupleToTuple( |
| 31143 | 31143 | var root_msg: ?*Module.ErrorMsg = null; |
| 31144 | 31144 | errdefer if (root_msg) |msg| msg.destroy(sema.gpa); |
| 31145 | 31145 | |
| 31146 | for (field_refs, 0..) |*field_ref, i| { | |
| 31146 | for (field_refs, 0..) |*field_ref, i_usize| { | |
| 31147 | const i: u32 = @intCast(i_usize); | |
| 31147 | 31148 | if (field_ref.* != .none) continue; |
| 31148 | 31149 | |
| 31149 | 31150 | const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) { |
| ... | ... | @@ -31154,7 +31155,7 @@ fn coerceTupleToTuple( |
| 31154 | 31155 | |
| 31155 | 31156 | const field_src = inst_src; // TODO better source location |
| 31156 | 31157 | if (default_val == .none) { |
| 31157 | if (tuple_ty.isTuple(mod)) { | |
| 31158 | const field_name = tuple_ty.structFieldName(i, mod).unwrap() orelse { | |
| 31158 | 31159 | const template = "missing tuple field: {d}"; |
| 31159 | 31160 | if (root_msg) |msg| { |
| 31160 | 31161 | try sema.errNote(block, field_src, msg, template, .{i}); |
| ... | ... | @@ -31162,9 +31163,9 @@ fn coerceTupleToTuple( |
| 31162 | 31163 | root_msg = try sema.errMsg(block, field_src, template, .{i}); |
| 31163 | 31164 | } |
| 31164 | 31165 | continue; |
| 31165 | } | |
| 31166 | }; | |
| 31166 | 31167 | const template = "missing struct field: {}"; |
| 31167 | const args = .{tuple_ty.structFieldName(i, mod).fmt(ip)}; | |
| 31168 | const args = .{field_name.fmt(ip)}; | |
| 31168 | 31169 | if (root_msg) |msg| { |
| 31169 | 31170 | try sema.errNote(block, field_src, msg, template, args); |
| 31170 | 31171 | } else { |
| ... | ... | @@ -33897,8 +33898,9 @@ fn resolvePeerTypesInner( |
| 33897 | 33898 | } |
| 33898 | 33899 | |
| 33899 | 33900 | if (!is_tuple) { |
| 33900 | for (field_names, 0..) |expected, field_idx| { | |
| 33901 | const actual = ty.structFieldName(field_idx, mod); | |
| 33901 | for (field_names, 0..) |expected, field_index_usize| { | |
| 33902 | const field_index: u32 = @intCast(field_index_usize); | |
| 33903 | const actual = ty.structFieldName(field_index, mod).unwrap().?; | |
| 33902 | 33904 | if (actual == expected) continue; |
| 33903 | 33905 | return .{ .conflict = .{ |
| 33904 | 33906 | .peer_idx_a = first_idx, |
src/TypedValue.zig+3-3| ... | ... | @@ -355,11 +355,11 @@ pub fn print( |
| 355 | 355 | const container_ty = ptr_container_ty.childType(mod); |
| 356 | 356 | switch (container_ty.zigTypeTag(mod)) { |
| 357 | 357 | .Struct => { |
| 358 | if (container_ty.isTuple(mod)) { | |
| 358 | if (container_ty.structFieldName(@intCast(field.index), mod).unwrap()) |field_name| { | |
| 359 | try writer.print(".{i}", .{field_name.fmt(ip)}); | |
| 360 | } else { | |
| 359 | 361 | try writer.print("[{d}]", .{field.index}); |
| 360 | 362 | } |
| 361 | const field_name = container_ty.structFieldName(@as(usize, @intCast(field.index)), mod); | |
| 362 | try writer.print(".{i}", .{field_name.fmt(ip)}); | |
| 363 | 363 | }, |
| 364 | 364 | .Union => { |
| 365 | 365 | const field_name = mod.typeToUnion(container_ty).?.field_names.get(ip)[@intCast(field.index)]; |
src/codegen/c.zig+8-6| ... | ... | @@ -5226,7 +5226,8 @@ fn fieldLocation( |
| 5226 | 5226 | const container_ty = container_ptr_ty.childType(mod); |
| 5227 | 5227 | return switch (container_ty.zigTypeTag(mod)) { |
| 5228 | 5228 | .Struct => switch (container_ty.containerLayout(mod)) { |
| 5229 | .Auto, .Extern => for (field_index..container_ty.structFieldCount(mod)) |next_field_index| { | |
| 5229 | .Auto, .Extern => for (field_index..container_ty.structFieldCount(mod)) |next_field_index_usize| { | |
| 5230 | const next_field_index: u32 = @intCast(next_field_index_usize); | |
| 5230 | 5231 | if (container_ty.structFieldIsComptime(next_field_index, mod)) continue; |
| 5231 | 5232 | const field_ty = container_ty.structFieldType(next_field_index, mod); |
| 5232 | 5233 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; |
| ... | ... | @@ -5234,7 +5235,7 @@ fn fieldLocation( |
| 5234 | 5235 | break .{ .field = if (container_ty.isSimpleTuple(mod)) |
| 5235 | 5236 | .{ .field = next_field_index } |
| 5236 | 5237 | else |
| 5237 | .{ .identifier = ip.stringToSlice(container_ty.structFieldName(next_field_index, mod)) } }; | |
| 5238 | .{ .identifier = ip.stringToSlice(container_ty.legacyStructFieldName(next_field_index, mod)) } }; | |
| 5238 | 5239 | } else if (container_ty.hasRuntimeBitsIgnoreComptime(mod)) .end else .begin, |
| 5239 | 5240 | .Packed => if (field_ptr_ty.ptrInfo(mod).packed_offset.host_size == 0) |
| 5240 | 5241 | .{ .byte_offset = container_ty.packedStructFieldByteOffset(field_index, mod) + @divExact(container_ptr_ty.ptrInfo(mod).packed_offset.bit_offset, 8) } |
| ... | ... | @@ -5421,7 +5422,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5421 | 5422 | .Auto, .Extern => if (struct_ty.isSimpleTuple(mod)) |
| 5422 | 5423 | .{ .field = extra.field_index } |
| 5423 | 5424 | else |
| 5424 | .{ .identifier = ip.stringToSlice(struct_ty.structFieldName(extra.field_index, mod)) }, | |
| 5425 | .{ .identifier = ip.stringToSlice(struct_ty.legacyStructFieldName(extra.field_index, mod)) }, | |
| 5425 | 5426 | .Packed => { |
| 5426 | 5427 | const struct_type = mod.typeToStruct(struct_ty).?; |
| 5427 | 5428 | const int_info = struct_ty.intInfo(mod); |
| ... | ... | @@ -5483,7 +5484,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5483 | 5484 | .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len == 0) |
| 5484 | 5485 | .{ .field = extra.field_index } |
| 5485 | 5486 | else |
| 5486 | .{ .identifier = ip.stringToSlice(struct_ty.structFieldName(extra.field_index, mod)) }, | |
| 5487 | .{ .identifier = ip.stringToSlice(struct_ty.legacyStructFieldName(extra.field_index, mod)) }, | |
| 5487 | 5488 | |
| 5488 | 5489 | .union_type => |union_type| field_name: { |
| 5489 | 5490 | const union_obj = ip.loadUnionType(union_type); |
| ... | ... | @@ -6816,7 +6817,8 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6816 | 6817 | } |
| 6817 | 6818 | }, |
| 6818 | 6819 | .Struct => switch (inst_ty.containerLayout(mod)) { |
| 6819 | .Auto, .Extern => for (resolved_elements, 0..) |element, field_i| { | |
| 6820 | .Auto, .Extern => for (resolved_elements, 0..) |element, field_i_usize| { | |
| 6821 | const field_i: u32 = @intCast(field_i_usize); | |
| 6820 | 6822 | if (inst_ty.structFieldIsComptime(field_i, mod)) continue; |
| 6821 | 6823 | const field_ty = inst_ty.structFieldType(field_i, mod); |
| 6822 | 6824 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; |
| ... | ... | @@ -6825,7 +6827,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6825 | 6827 | try f.writeCValueMember(writer, local, if (inst_ty.isSimpleTuple(mod)) |
| 6826 | 6828 | .{ .field = field_i } |
| 6827 | 6829 | else |
| 6828 | .{ .identifier = ip.stringToSlice(inst_ty.structFieldName(field_i, mod)) }); | |
| 6830 | .{ .identifier = ip.stringToSlice(inst_ty.legacyStructFieldName(field_i, mod)) }); | |
| 6829 | 6831 | try a.assign(f, writer); |
| 6830 | 6832 | try f.writeCValue(writer, element, .Other); |
| 6831 | 6833 | try a.end(f, writer); |
src/codegen/c/type.zig+9-6| ... | ... | @@ -1953,7 +1953,8 @@ pub const CType = extern union { |
| 1953 | 1953 | |
| 1954 | 1954 | const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len); |
| 1955 | 1955 | var c_field_i: usize = 0; |
| 1956 | for (0..fields_len) |field_i| { | |
| 1956 | for (0..fields_len) |field_i_usize| { | |
| 1957 | const field_i: u32 = @intCast(field_i_usize); | |
| 1957 | 1958 | const field_ty = ty.structFieldType(field_i, mod); |
| 1958 | 1959 | if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i, mod)) or |
| 1959 | 1960 | !field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; |
| ... | ... | @@ -1964,7 +1965,7 @@ pub const CType = extern union { |
| 1964 | 1965 | std.fmt.allocPrintZ(arena, "f{}", .{field_i}) |
| 1965 | 1966 | else |
| 1966 | 1967 | arena.dupeZ(u8, ip.stringToSlice(switch (zig_ty_tag) { |
| 1967 | .Struct => ty.structFieldName(field_i, mod), | |
| 1968 | .Struct => ty.legacyStructFieldName(field_i, mod), | |
| 1968 | 1969 | .Union => mod.typeToUnion(ty).?.field_names.get(ip)[field_i], |
| 1969 | 1970 | else => unreachable, |
| 1970 | 1971 | })), |
| ... | ... | @@ -2097,7 +2098,8 @@ pub const CType = extern union { |
| 2097 | 2098 | .Struct => ty.structFieldCount(mod), |
| 2098 | 2099 | .Union => mod.typeToUnion(ty).?.field_names.len, |
| 2099 | 2100 | else => unreachable, |
| 2100 | }) |field_i| { | |
| 2101 | }) |field_i_usize| { | |
| 2102 | const field_i: u32 = @intCast(field_i_usize); | |
| 2101 | 2103 | const field_ty = ty.structFieldType(field_i, mod); |
| 2102 | 2104 | if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i, mod)) or |
| 2103 | 2105 | !field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; |
| ... | ... | @@ -2116,7 +2118,7 @@ pub const CType = extern union { |
| 2116 | 2118 | std.fmt.bufPrintZ(&name_buf, "f{}", .{field_i}) catch unreachable |
| 2117 | 2119 | else |
| 2118 | 2120 | ip.stringToSlice(switch (zig_ty_tag) { |
| 2119 | .Struct => ty.structFieldName(field_i, mod), | |
| 2121 | .Struct => ty.legacyStructFieldName(field_i, mod), | |
| 2120 | 2122 | .Union => mod.typeToUnion(ty).?.field_names.get(ip)[field_i], |
| 2121 | 2123 | else => unreachable, |
| 2122 | 2124 | }), |
| ... | ... | @@ -2225,7 +2227,8 @@ pub const CType = extern union { |
| 2225 | 2227 | .Struct => ty.structFieldCount(mod), |
| 2226 | 2228 | .Union => mod.typeToUnion(ty).?.field_names.len, |
| 2227 | 2229 | else => unreachable, |
| 2228 | }) |field_i| { | |
| 2230 | }) |field_i_usize| { | |
| 2231 | const field_i: u32 = @intCast(field_i_usize); | |
| 2229 | 2232 | const field_ty = ty.structFieldType(field_i, mod); |
| 2230 | 2233 | if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i, mod)) or |
| 2231 | 2234 | !field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; |
| ... | ... | @@ -2240,7 +2243,7 @@ pub const CType = extern union { |
| 2240 | 2243 | std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable |
| 2241 | 2244 | else |
| 2242 | 2245 | mod.intern_pool.stringToSlice(switch (zig_ty_tag) { |
| 2243 | .Struct => ty.structFieldName(field_i, mod), | |
| 2246 | .Struct => ty.legacyStructFieldName(field_i, mod), | |
| 2244 | 2247 | .Union => mod.typeToUnion(ty).?.field_names.get(ip)[field_i], |
| 2245 | 2248 | else => unreachable, |
| 2246 | 2249 | })); |
src/type.zig+16-4| ... | ... | @@ -2907,16 +2907,28 @@ pub const Type = struct { |
| 2907 | 2907 | return enum_type.tagValueIndex(ip, int_tag); |
| 2908 | 2908 | } |
| 2909 | 2909 | |
| 2910 | pub fn structFieldName(ty: Type, field_index: usize, mod: *Module) InternPool.NullTerminatedString { | |
| 2910 | /// Returns none in the case of a tuple which uses the integer index as the field name. | |
| 2911 | pub fn structFieldName(ty: Type, field_index: u32, mod: *Module) InternPool.OptionalNullTerminatedString { | |
| 2911 | 2912 | const ip = &mod.intern_pool; |
| 2912 | 2913 | return switch (ip.indexToKey(ty.toIntern())) { |
| 2913 | .struct_type => |struct_type| struct_type.field_names.get(ip)[field_index], | |
| 2914 | .anon_struct_type => |anon_struct| anon_struct.names.get(ip)[field_index], | |
| 2914 | .struct_type => |struct_type| struct_type.fieldName(ip, field_index), | |
| 2915 | .anon_struct_type => |anon_struct| anon_struct.fieldName(ip, field_index), | |
| 2915 | 2916 | else => unreachable, |
| 2916 | 2917 | }; |
| 2917 | 2918 | } |
| 2918 | 2919 | |
| 2919 | pub fn structFieldCount(ty: Type, mod: *Module) usize { | |
| 2920 | /// When struct types have no field names, the names are implicitly understood to be | |
| 2921 | /// strings corresponding to the field indexes in declaration order. It used to be the | |
| 2922 | /// case that a NullTerminatedString would be stored for each field in this case, however, | |
| 2923 | /// now, callers must handle the possibility that there are no names stored at all. | |
| 2924 | /// Here we fake the previous behavior. Probably something better could be done by examining | |
| 2925 | /// all the callsites of this function. | |
| 2926 | pub fn legacyStructFieldName(ty: Type, i: u32, mod: *Module) InternPool.NullTerminatedString { | |
| 2927 | return ty.structFieldName(i, mod).unwrap() orelse | |
| 2928 | mod.intern_pool.getOrPutStringFmt(mod.gpa, "{d}", .{i}) catch @panic("OOM"); | |
| 2929 | } | |
| 2930 | ||
| 2931 | pub fn structFieldCount(ty: Type, mod: *Module) u32 { | |
| 2920 | 2932 | const ip = &mod.intern_pool; |
| 2921 | 2933 | return switch (ip.indexToKey(ty.toIntern())) { |
| 2922 | 2934 | .struct_type => |struct_type| struct_type.field_types.len, |