authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2023-11-16 06:38:16-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-11-16 14:38:16+00:00
log941090d94f6b6c7c5207f06917743e79ff06426f
tree89a9cbd53f32e26b39779f997aecb1ce090ee75d
parentacf9de376d176d35dcfd245d14939766aeba4638
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Move duplicate field detection for struct init expressions into AstGen

Partially addresses #17916.

10 files changed, 146 insertions(+), 67 deletions(-)

lib/std/os/linux/sparc64.zig+1-1
......@@ -445,7 +445,7 @@ pub const ucontext_t = extern struct {
445445 sigmask: u64,
446446 mcontext: mcontext_t,
447447 stack: stack_t,
448 sigmask: sigset_t,
448 sigset: sigset_t,
449449};
450450
451451pub const rlimit_resource = enum(c_int) {
src/AstGen.zig+100-1
......@@ -1722,6 +1722,57 @@ fn structInitExpr(
17221722 }
17231723 }
17241724
1725 {
1726 var sfba = std.heap.stackFallback(256, astgen.arena);
1727 const sfba_allocator = sfba.get();
1728
1729 var duplicate_names = std.AutoArrayHashMap(u32, ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
1730 defer duplicate_names.deinit();
1731 try duplicate_names.ensureTotalCapacity(@intCast(struct_init.ast.fields.len));
1732
1733 // When there aren't errors, use this to avoid a second iteration.
1734 var any_duplicate = false;
1735
1736 for (struct_init.ast.fields) |field| {
1737 const name_token = tree.firstToken(field) - 2;
1738 const name_index = try astgen.identAsString(name_token);
1739
1740 const gop = try duplicate_names.getOrPut(name_index);
1741
1742 if (gop.found_existing) {
1743 try gop.value_ptr.append(sfba_allocator, name_token);
1744 any_duplicate = true;
1745 } else {
1746 gop.value_ptr.* = .{};
1747 try gop.value_ptr.append(sfba_allocator, name_token);
1748 }
1749 }
1750
1751 if (any_duplicate) {
1752 var it = duplicate_names.iterator();
1753
1754 while (it.next()) |entry| {
1755 const record = entry.value_ptr.*;
1756 if (record.items.len > 1) {
1757 var error_notes = std.ArrayList(u32).init(astgen.arena);
1758
1759 for (record.items[1..]) |duplicate| {
1760 try error_notes.append(try astgen.errNoteTok(duplicate, "other field here", .{}));
1761 }
1762
1763 try astgen.appendErrorTokNotes(
1764 record.items[0],
1765 "duplicate field",
1766 .{},
1767 error_notes.items,
1768 );
1769 }
1770 }
1771
1772 return error.AnalysisFail;
1773 }
1774 }
1775
17251776 if (struct_init.ast.type_expr != 0) {
17261777 // Typed inits do not use RLS for language simplicity.
17271778 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
......@@ -4874,6 +4925,15 @@ fn structDeclInner(
48744925 }
48754926 };
48764927
4928 var sfba = std.heap.stackFallback(256, astgen.arena);
4929 const sfba_allocator = sfba.get();
4930
4931 var duplicate_names = std.AutoArrayHashMap(u32, std.ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
4932 try duplicate_names.ensureTotalCapacity(field_count);
4933
4934 // When there aren't errors, use this to avoid a second iteration.
4935 var any_duplicate = false;
4936
48774937 var known_non_opv = false;
48784938 var known_comptime_only = false;
48794939 var any_comptime_fields = false;
......@@ -4886,11 +4946,22 @@ fn structDeclInner(
48864946 };
48874947
48884948 if (!is_tuple) {
4949 const field_name = try astgen.identAsString(member.ast.main_token);
4950
48894951 member.convertToNonTupleLike(astgen.tree.nodes);
48904952 assert(!member.ast.tuple_like);
48914953
4892 const field_name = try astgen.identAsString(member.ast.main_token);
48934954 wip_members.appendToField(field_name);
4955
4956 const gop = try duplicate_names.getOrPut(field_name);
4957
4958 if (gop.found_existing) {
4959 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
4960 any_duplicate = true;
4961 } else {
4962 gop.value_ptr.* = .{};
4963 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
4964 }
48944965 } else if (!member.ast.tuple_like) {
48954966 return astgen.failTok(member.ast.main_token, "tuple field has a name", .{});
48964967 }
......@@ -4975,6 +5046,34 @@ fn structDeclInner(
49755046 }
49765047 }
49775048
5049 if (any_duplicate) {
5050 var it = duplicate_names.iterator();
5051
5052 while (it.next()) |entry| {
5053 const record = entry.value_ptr.*;
5054 if (record.items.len > 1) {
5055 var error_notes = std.ArrayList(u32).init(astgen.arena);
5056
5057 for (record.items[1..]) |duplicate| {
5058 try error_notes.append(try astgen.errNoteTok(duplicate, "other field here", .{}));
5059 }
5060
5061 try error_notes.append(try astgen.errNoteNode(node, "struct declared here", .{}));
5062
5063 try astgen.appendErrorTokNotes(
5064 record.items[0],
5065 "duplicate struct field: '{s}'",
5066 .{try astgen.identifierTokenString(record.items[0])},
5067 error_notes.items,
5068 );
5069 }
5070 }
5071
5072 return error.AnalysisFail;
5073 }
5074
5075 duplicate_names.deinit();
5076
49785077 try gz.setStruct(decl_inst, .{
49795078 .src_node = node,
49805079 .layout = layout,
src/Sema.zig+11-48
......@@ -4690,17 +4690,7 @@ fn validateStructInit(
46904690 try sema.tupleFieldIndex(block, struct_ty, field_name, field_src)
46914691 else
46924692 try sema.structFieldIndex(block, struct_ty, field_name, field_src);
4693 if (found_fields[field_index.*].unwrap()) |other_field_ptr| {
4694 const other_field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(other_field_ptr)].pl_node;
4695 const other_field_src: LazySrcLoc = .{ .node_offset_initializer = other_field_ptr_data.src_node };
4696 const msg = msg: {
4697 const msg = try sema.errMsg(block, field_src, "duplicate field", .{});
4698 errdefer msg.destroy(gpa);
4699 try sema.errNote(block, other_field_src, msg, "other field here", .{});
4700 break :msg msg;
4701 };
4702 return sema.failWithOwnedErrorMsg(block, msg);
4703 }
4693 assert(found_fields[field_index.*] == .none);
47044694 found_fields[field_index.*] = field_ptr.toOptional();
47054695 }
47064696
......@@ -19222,18 +19212,7 @@ fn zirStructInit(
1922219212 try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src)
1922319213 else
1922419214 try sema.structFieldIndex(block, resolved_ty, field_name, field_src);
19225 if (field_inits[field_index] != .none) {
19226 const other_field_type = found_fields[field_index];
19227 const other_field_type_data = zir_datas[@intFromEnum(other_field_type)].pl_node;
19228 const other_field_src: LazySrcLoc = .{ .node_offset_initializer = other_field_type_data.src_node };
19229 const msg = msg: {
19230 const msg = try sema.errMsg(block, field_src, "duplicate field", .{});
19231 errdefer msg.destroy(gpa);
19232 try sema.errNote(block, other_field_src, msg, "other field here", .{});
19233 break :msg msg;
19234 };
19235 return sema.failWithOwnedErrorMsg(block, msg);
19236 }
19215 assert(field_inits[field_index] == .none);
1923719216 found_fields[field_index] = item.data.field_type;
1923819217 const uncoerced_init = try sema.resolveInst(item.data.init);
1923919218 const field_ty = resolved_ty.structFieldType(field_index, mod);
......@@ -19533,16 +19512,13 @@ fn structInitAnon(
1953319512
1953419513 const types = try sema.arena.alloc(InternPool.Index, extra_data.fields_len);
1953519514 const values = try sema.arena.alloc(InternPool.Index, types.len);
19536
19537 var fields = std.AutoArrayHashMap(InternPool.NullTerminatedString, u32).init(sema.arena);
19538 try fields.ensureUnusedCapacity(types.len);
19515 const names = try sema.arena.alloc(InternPool.NullTerminatedString, types.len);
1953919516
1954019517 // Find which field forces the expression to be runtime, if any.
1954119518 const opt_runtime_index = rs: {
1954219519 var runtime_index: ?usize = null;
1954319520 var extra_index = extra_end;
19544 for (types, 0..) |*field_ty, i_usize| {
19545 const i: u32 = @intCast(i_usize);
19521 for (types, values, names, 0..) |*field_ty, *field_val, *field_name, i_usize| {
1954619522 const item = switch (kind) {
1954719523 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
1954819524 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
......@@ -19558,29 +19534,16 @@ fn structInitAnon(
1955819534 break :name sema.code.nullTerminatedString(field_type_extra.data.name_start);
1955919535 },
1956019536 };
19561 const name_ip = try mod.intern_pool.getOrPutString(gpa, name);
19562 const gop = fields.getOrPutAssumeCapacity(name_ip);
19563 if (gop.found_existing) {
19564 const msg = msg: {
19565 const decl = mod.declPtr(block.src_decl);
19566 const field_src = mod.initSrc(src.node_offset.x, decl, i);
19567 const msg = try sema.errMsg(block, field_src, "duplicate field", .{});
19568 errdefer msg.destroy(gpa);
1956919537
19570 const prev_source = mod.initSrc(src.node_offset.x, decl, gop.value_ptr.*);
19571 try sema.errNote(block, prev_source, msg, "other field here", .{});
19572 break :msg msg;
19573 };
19574 return sema.failWithOwnedErrorMsg(block, msg);
19575 }
19576 gop.value_ptr.* = i;
19538 const name_ip = try mod.intern_pool.getOrPutString(gpa, name);
19539 field_name.* = name_ip;
1957719540
1957819541 const init = try sema.resolveInst(item.data.init);
1957919542 field_ty.* = sema.typeOf(init).toIntern();
1958019543 if (field_ty.toType().zigTypeTag(mod) == .Opaque) {
1958119544 const msg = msg: {
1958219545 const decl = mod.declPtr(block.src_decl);
19583 const field_src = mod.initSrc(src.node_offset.x, decl, i);
19546 const field_src = mod.initSrc(src.node_offset.x, decl, @intCast(i_usize));
1958419547 const msg = try sema.errMsg(block, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
1958519548 errdefer msg.destroy(sema.gpa);
1958619549
......@@ -19590,17 +19553,17 @@ fn structInitAnon(
1959019553 return sema.failWithOwnedErrorMsg(block, msg);
1959119554 }
1959219555 if (try sema.resolveValue(init)) |init_val| {
19593 values[i] = try init_val.intern(field_ty.toType(), mod);
19556 field_val.* = try init_val.intern(field_ty.toType(), mod);
1959419557 } else {
19595 values[i] = .none;
19596 runtime_index = i;
19558 field_val.* = .none;
19559 runtime_index = @intCast(i_usize);
1959719560 }
1959819561 }
1959919562 break :rs runtime_index;
1960019563 };
1960119564
1960219565 const tuple_ty = try ip.getAnonStructType(gpa, .{
19603 .names = fields.keys(),
19566 .names = names,
1960419567 .types = types,
1960519568 .values = values,
1960619569 });
test/cases/compile_errors/duplicate_field_in_anonymous_struct_literal.zig+2-2
......@@ -14,5 +14,5 @@ export fn entry() void {
1414// backend=stage2
1515// target=native
1616//
17// :7:16: error: duplicate field
18// :4:16: note: other field here
17// :4:14: error: duplicate field
18// :7:14: note: other field here
test/cases/compile_errors/duplicate_field_in_discarded_anon_init.zig+2-2
......@@ -6,5 +6,5 @@ pub export fn entry() void {
66// backend=stage2
77// target=native
88//
9// :2:21: error: duplicate field
10// :2:13: note: other field here
9// :2:13: error: duplicate field
10// :2:21: note: other field here
test/cases/compile_errors/duplicate_field_in_struct_value_expression.zig+2-2
......@@ -17,5 +17,5 @@ export fn f() void {
1717// backend=stage2
1818// target=native
1919//
20// :11:10: error: duplicate field
21// :8:10: note: other field here
20// :8:10: error: duplicate field
21// :11:10: note: other field here
test/cases/compile_errors/duplicate_struct_field.zig+22-5
......@@ -2,15 +2,32 @@ const Foo = struct {
22 Bar: i32,
33 Bar: usize,
44};
5export fn entry() void {
6 const a: Foo = undefined;
7 _ = a;
5
6const S = struct {
7 a: u32,
8 b: u32,
9 a: u32,
10 a: u64,
11};
12
13export fn a() void {
14 const f: Foo = undefined;
15 _ = f;
16}
17
18export fn b() void {
19 const s: S = undefined;
20 _ = s;
821}
922
1023// error
1124// backend=stage2
1225// target=native
1326//
14// :3:5: error: duplicate struct field: 'Bar'
15// :2:5: note: other field here
27// :2:5: error: duplicate struct field: 'Bar'
28// :3:5: note: other field here
1629// :1:13: note: struct declared here
30// :7:5: error: duplicate struct field: 'a'
31// :9:5: note: other field here
32// :10:5: note: other field here
33// :6:11: note: struct declared here
test/cases/compile_errors/error_in_struct_initializer_doesnt_crash_the_compiler.zig+2-2
......@@ -11,6 +11,6 @@ pub export fn entry() void {
1111// backend=stage2
1212// target=native
1313//
14// :4:9: error: duplicate struct field: 'e'
15// :3:9: note: other field here
14// :3:9: error: duplicate struct field: 'e'
15// :4:9: note: other field here
1616// :2:22: note: struct declared here
test/cases/compile_errors/struct_duplicate_field_name.zig+2-2
......@@ -11,6 +11,6 @@ export fn entry() void {
1111// error
1212// target=native
1313//
14// :3:5: error: duplicate struct field: 'foo'
15// :2:5: note: other field here
14// :2:5: error: duplicate struct field: 'foo'
15// :3:5: note: other field here
1616// :1:11: note: struct declared here
test/cbe.zig+2-2
......@@ -507,8 +507,8 @@ pub fn addCases(ctx: *Cases) !void {
507507 \\ return p.y - p.x - p.x;
508508 \\}
509509 , &.{
510 ":6:10: error: duplicate field",
511 ":4:10: note: other field here",
510 ":4:10: error: duplicate field",
511 ":6:10: note: other field here",
512512 });
513513 case.addError(
514514 \\const Point = struct { x: i32, y: i32 };