authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-21 17:29:34-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-21 17:29:34-07:00
log81b5df347a2b92566dc679fdd4e110812dfe27d0
tree2e2580b944374f490797ef3211eb752c7b1bc051
parentedfada4317760a152ad9c36b39f5a2e68eeaebf8

compiler: fix structFieldName crash for tuples

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) {
656656 pub fn isTuple(self: AnonStructType) bool {
657657 return self.names.len == 0;
658658 }
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 }
659670 };
660671
661672 /// Serves two purposes:
src/Sema.zig+19-17
......@@ -4699,12 +4699,13 @@ fn validateStructInit(
46994699 // In this case the only thing we need to do is evaluate the implicit
47004700 // store instructions for default field values, and report any missing fields.
47014701 // 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);
47034704 if (field_ptr != 0) continue;
47044705
47054706 const default_val = struct_ty.structFieldDefaultValue(i, mod);
47064707 if (default_val.toIntern() == .unreachable_value) {
4707 if (struct_ty.isTuple(mod)) {
4708 const field_name = struct_ty.structFieldName(i, mod).unwrap() orelse {
47084709 const template = "missing tuple field with index {d}";
47094710 if (root_msg) |msg| {
47104711 try sema.errNote(block, init_src, msg, template, .{i});
......@@ -4712,8 +4713,7 @@ fn validateStructInit(
47124713 root_msg = try sema.errMsg(block, init_src, template, .{i});
47134714 }
47144715 continue;
4715 }
4716 const field_name = struct_ty.structFieldName(i, mod);
4716 };
47174717 const template = "missing struct field: {}";
47184718 const args = .{field_name.fmt(ip)};
47194719 if (root_msg) |msg| {
......@@ -4763,7 +4763,8 @@ fn validateStructInit(
47634763 // ends up being comptime-known.
47644764 const field_values = try sema.arena.alloc(InternPool.Index, struct_ty.structFieldCount(mod));
47654765
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);
47674768 if (field_ptr != 0) {
47684769 // Determine whether the value stored to this pointer is comptime-known.
47694770 const field_ty = struct_ty.structFieldType(i, mod);
......@@ -4842,7 +4843,7 @@ fn validateStructInit(
48424843
48434844 const default_val = struct_ty.structFieldDefaultValue(i, mod);
48444845 if (default_val.toIntern() == .unreachable_value) {
4845 if (struct_ty.isTuple(mod)) {
4846 const field_name = struct_ty.structFieldName(i, mod).unwrap() orelse {
48464847 const template = "missing tuple field with index {d}";
48474848 if (root_msg) |msg| {
48484849 try sema.errNote(block, init_src, msg, template, .{i});
......@@ -4850,8 +4851,7 @@ fn validateStructInit(
48504851 root_msg = try sema.errMsg(block, init_src, template, .{i});
48514852 }
48524853 continue;
4853 }
4854 const field_name = struct_ty.structFieldName(i, mod);
4854 };
48554855 const template = "missing struct field: {}";
48564856 const args = .{field_name.fmt(ip)};
48574857 if (root_msg) |msg| {
......@@ -21862,10 +21862,10 @@ fn ptrCastFull(
2186221862 const msg = try sema.errMsg(block, src, "cast increases pointer alignment", .{});
2186321863 errdefer msg.destroy(sema.gpa);
2186421864 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),
2186621866 });
2186721867 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),
2186921869 });
2187021870 try sema.errNote(block, src, msg, "use @alignCast to assert pointer alignment", .{});
2187121871 break :msg msg;
......@@ -26376,7 +26376,7 @@ fn fieldCallBind(
2637626376 const max = concrete_ty.structFieldCount(mod);
2637726377 for (0..max) |i_usize| {
2637826378 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().?) {
2638026380 return sema.finishFieldCallBind(block, src, ptr_ty, concrete_ty.structFieldType(i, mod), i, object_ptr);
2638126381 }
2638226382 }
......@@ -31143,7 +31143,8 @@ fn coerceTupleToTuple(
3114331143 var root_msg: ?*Module.ErrorMsg = null;
3114431144 errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
3114531145
31146 for (field_refs, 0..) |*field_ref, i| {
31146 for (field_refs, 0..) |*field_ref, i_usize| {
31147 const i: u32 = @intCast(i_usize);
3114731148 if (field_ref.* != .none) continue;
3114831149
3114931150 const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) {
......@@ -31154,7 +31155,7 @@ fn coerceTupleToTuple(
3115431155
3115531156 const field_src = inst_src; // TODO better source location
3115631157 if (default_val == .none) {
31157 if (tuple_ty.isTuple(mod)) {
31158 const field_name = tuple_ty.structFieldName(i, mod).unwrap() orelse {
3115831159 const template = "missing tuple field: {d}";
3115931160 if (root_msg) |msg| {
3116031161 try sema.errNote(block, field_src, msg, template, .{i});
......@@ -31162,9 +31163,9 @@ fn coerceTupleToTuple(
3116231163 root_msg = try sema.errMsg(block, field_src, template, .{i});
3116331164 }
3116431165 continue;
31165 }
31166 };
3116631167 const template = "missing struct field: {}";
31167 const args = .{tuple_ty.structFieldName(i, mod).fmt(ip)};
31168 const args = .{field_name.fmt(ip)};
3116831169 if (root_msg) |msg| {
3116931170 try sema.errNote(block, field_src, msg, template, args);
3117031171 } else {
......@@ -33897,8 +33898,9 @@ fn resolvePeerTypesInner(
3389733898 }
3389833899
3389933900 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().?;
3390233904 if (actual == expected) continue;
3390333905 return .{ .conflict = .{
3390433906 .peer_idx_a = first_idx,
src/TypedValue.zig+3-3
......@@ -355,11 +355,11 @@ pub fn print(
355355 const container_ty = ptr_container_ty.childType(mod);
356356 switch (container_ty.zigTypeTag(mod)) {
357357 .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 {
359361 try writer.print("[{d}]", .{field.index});
360362 }
361 const field_name = container_ty.structFieldName(@as(usize, @intCast(field.index)), mod);
362 try writer.print(".{i}", .{field_name.fmt(ip)});
363363 },
364364 .Union => {
365365 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(
52265226 const container_ty = container_ptr_ty.childType(mod);
52275227 return switch (container_ty.zigTypeTag(mod)) {
52285228 .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);
52305231 if (container_ty.structFieldIsComptime(next_field_index, mod)) continue;
52315232 const field_ty = container_ty.structFieldType(next_field_index, mod);
52325233 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
......@@ -5234,7 +5235,7 @@ fn fieldLocation(
52345235 break .{ .field = if (container_ty.isSimpleTuple(mod))
52355236 .{ .field = next_field_index }
52365237 else
5237 .{ .identifier = ip.stringToSlice(container_ty.structFieldName(next_field_index, mod)) } };
5238 .{ .identifier = ip.stringToSlice(container_ty.legacyStructFieldName(next_field_index, mod)) } };
52385239 } else if (container_ty.hasRuntimeBitsIgnoreComptime(mod)) .end else .begin,
52395240 .Packed => if (field_ptr_ty.ptrInfo(mod).packed_offset.host_size == 0)
52405241 .{ .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 {
54215422 .Auto, .Extern => if (struct_ty.isSimpleTuple(mod))
54225423 .{ .field = extra.field_index }
54235424 else
5424 .{ .identifier = ip.stringToSlice(struct_ty.structFieldName(extra.field_index, mod)) },
5425 .{ .identifier = ip.stringToSlice(struct_ty.legacyStructFieldName(extra.field_index, mod)) },
54255426 .Packed => {
54265427 const struct_type = mod.typeToStruct(struct_ty).?;
54275428 const int_info = struct_ty.intInfo(mod);
......@@ -5483,7 +5484,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
54835484 .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len == 0)
54845485 .{ .field = extra.field_index }
54855486 else
5486 .{ .identifier = ip.stringToSlice(struct_ty.structFieldName(extra.field_index, mod)) },
5487 .{ .identifier = ip.stringToSlice(struct_ty.legacyStructFieldName(extra.field_index, mod)) },
54875488
54885489 .union_type => |union_type| field_name: {
54895490 const union_obj = ip.loadUnionType(union_type);
......@@ -6816,7 +6817,8 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
68166817 }
68176818 },
68186819 .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);
68206822 if (inst_ty.structFieldIsComptime(field_i, mod)) continue;
68216823 const field_ty = inst_ty.structFieldType(field_i, mod);
68226824 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
......@@ -6825,7 +6827,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
68256827 try f.writeCValueMember(writer, local, if (inst_ty.isSimpleTuple(mod))
68266828 .{ .field = field_i }
68276829 else
6828 .{ .identifier = ip.stringToSlice(inst_ty.structFieldName(field_i, mod)) });
6830 .{ .identifier = ip.stringToSlice(inst_ty.legacyStructFieldName(field_i, mod)) });
68296831 try a.assign(f, writer);
68306832 try f.writeCValue(writer, element, .Other);
68316833 try a.end(f, writer);
src/codegen/c/type.zig+9-6
......@@ -1953,7 +1953,8 @@ pub const CType = extern union {
19531953
19541954 const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len);
19551955 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);
19571958 const field_ty = ty.structFieldType(field_i, mod);
19581959 if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i, mod)) or
19591960 !field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
......@@ -1964,7 +1965,7 @@ pub const CType = extern union {
19641965 std.fmt.allocPrintZ(arena, "f{}", .{field_i})
19651966 else
19661967 arena.dupeZ(u8, ip.stringToSlice(switch (zig_ty_tag) {
1967 .Struct => ty.structFieldName(field_i, mod),
1968 .Struct => ty.legacyStructFieldName(field_i, mod),
19681969 .Union => mod.typeToUnion(ty).?.field_names.get(ip)[field_i],
19691970 else => unreachable,
19701971 })),
......@@ -2097,7 +2098,8 @@ pub const CType = extern union {
20972098 .Struct => ty.structFieldCount(mod),
20982099 .Union => mod.typeToUnion(ty).?.field_names.len,
20992100 else => unreachable,
2100 }) |field_i| {
2101 }) |field_i_usize| {
2102 const field_i: u32 = @intCast(field_i_usize);
21012103 const field_ty = ty.structFieldType(field_i, mod);
21022104 if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i, mod)) or
21032105 !field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
......@@ -2116,7 +2118,7 @@ pub const CType = extern union {
21162118 std.fmt.bufPrintZ(&name_buf, "f{}", .{field_i}) catch unreachable
21172119 else
21182120 ip.stringToSlice(switch (zig_ty_tag) {
2119 .Struct => ty.structFieldName(field_i, mod),
2121 .Struct => ty.legacyStructFieldName(field_i, mod),
21202122 .Union => mod.typeToUnion(ty).?.field_names.get(ip)[field_i],
21212123 else => unreachable,
21222124 }),
......@@ -2225,7 +2227,8 @@ pub const CType = extern union {
22252227 .Struct => ty.structFieldCount(mod),
22262228 .Union => mod.typeToUnion(ty).?.field_names.len,
22272229 else => unreachable,
2228 }) |field_i| {
2230 }) |field_i_usize| {
2231 const field_i: u32 = @intCast(field_i_usize);
22292232 const field_ty = ty.structFieldType(field_i, mod);
22302233 if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i, mod)) or
22312234 !field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
......@@ -2240,7 +2243,7 @@ pub const CType = extern union {
22402243 std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable
22412244 else
22422245 mod.intern_pool.stringToSlice(switch (zig_ty_tag) {
2243 .Struct => ty.structFieldName(field_i, mod),
2246 .Struct => ty.legacyStructFieldName(field_i, mod),
22442247 .Union => mod.typeToUnion(ty).?.field_names.get(ip)[field_i],
22452248 else => unreachable,
22462249 }));
src/type.zig+16-4
......@@ -2907,16 +2907,28 @@ pub const Type = struct {
29072907 return enum_type.tagValueIndex(ip, int_tag);
29082908 }
29092909
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 {
29112912 const ip = &mod.intern_pool;
29122913 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),
29152916 else => unreachable,
29162917 };
29172918 }
29182919
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 {
29202932 const ip = &mod.intern_pool;
29212933 return switch (ip.indexToKey(ty.toIntern())) {
29222934 .struct_type => |struct_type| struct_type.field_types.len,