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 {...@@ -445,7 +445,7 @@ pub const ucontext_t = extern struct {
445 sigmask: u64,445 sigmask: u64,
446 mcontext: mcontext_t,446 mcontext: mcontext_t,
447 stack: stack_t,447 stack: stack_t,
448 sigmask: sigset_t,448 sigset: sigset_t,
449};449};
450450
451pub const rlimit_resource = enum(c_int) {451pub const rlimit_resource = enum(c_int) {
src/AstGen.zig+100-1
...@@ -1722,6 +1722,57 @@ fn structInitExpr(...@@ -1722,6 +1722,57 @@ fn structInitExpr(
1722 }1722 }
1723 }1723 }
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
1725 if (struct_init.ast.type_expr != 0) {1776 if (struct_init.ast.type_expr != 0) {
1726 // Typed inits do not use RLS for language simplicity.1777 // Typed inits do not use RLS for language simplicity.
1727 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);1778 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
...@@ -4874,6 +4925,15 @@ fn structDeclInner(...@@ -4874,6 +4925,15 @@ fn structDeclInner(
4874 }4925 }
4875 };4926 };
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
4877 var known_non_opv = false;4937 var known_non_opv = false;
4878 var known_comptime_only = false;4938 var known_comptime_only = false;
4879 var any_comptime_fields = false;4939 var any_comptime_fields = false;
...@@ -4886,11 +4946,22 @@ fn structDeclInner(...@@ -4886,11 +4946,22 @@ fn structDeclInner(
4886 };4946 };
48874947
4888 if (!is_tuple) {4948 if (!is_tuple) {
4949 const field_name = try astgen.identAsString(member.ast.main_token);
4950
4889 member.convertToNonTupleLike(astgen.tree.nodes);4951 member.convertToNonTupleLike(astgen.tree.nodes);
4890 assert(!member.ast.tuple_like);4952 assert(!member.ast.tuple_like);
48914953
4892 const field_name = try astgen.identAsString(member.ast.main_token);
4893 wip_members.appendToField(field_name);4954 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 }
4894 } else if (!member.ast.tuple_like) {4965 } else if (!member.ast.tuple_like) {
4895 return astgen.failTok(member.ast.main_token, "tuple field has a name", .{});4966 return astgen.failTok(member.ast.main_token, "tuple field has a name", .{});
4896 }4967 }
...@@ -4975,6 +5046,34 @@ fn structDeclInner(...@@ -4975,6 +5046,34 @@ fn structDeclInner(
4975 }5046 }
4976 }5047 }
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
4978 try gz.setStruct(decl_inst, .{5077 try gz.setStruct(decl_inst, .{
4979 .src_node = node,5078 .src_node = node,
4980 .layout = layout,5079 .layout = layout,
src/Sema.zig+11-48
...@@ -4690,17 +4690,7 @@ fn validateStructInit(...@@ -4690,17 +4690,7 @@ fn validateStructInit(
4690 try sema.tupleFieldIndex(block, struct_ty, field_name, field_src)4690 try sema.tupleFieldIndex(block, struct_ty, field_name, field_src)
4691 else4691 else
4692 try sema.structFieldIndex(block, struct_ty, field_name, field_src);4692 try sema.structFieldIndex(block, struct_ty, field_name, field_src);
4693 if (found_fields[field_index.*].unwrap()) |other_field_ptr| {4693 assert(found_fields[field_index.*] == .none);
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 }
4704 found_fields[field_index.*] = field_ptr.toOptional();4694 found_fields[field_index.*] = field_ptr.toOptional();
4705 }4695 }
47064696
...@@ -19222,18 +19212,7 @@ fn zirStructInit(...@@ -19222,18 +19212,7 @@ fn zirStructInit(
19222 try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src)19212 try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src)
19223 else19213 else
19224 try sema.structFieldIndex(block, resolved_ty, field_name, field_src);19214 try sema.structFieldIndex(block, resolved_ty, field_name, field_src);
19225 if (field_inits[field_index] != .none) {19215 assert(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 }
19237 found_fields[field_index] = item.data.field_type;19216 found_fields[field_index] = item.data.field_type;
19238 const uncoerced_init = try sema.resolveInst(item.data.init);19217 const uncoerced_init = try sema.resolveInst(item.data.init);
19239 const field_ty = resolved_ty.structFieldType(field_index, mod);19218 const field_ty = resolved_ty.structFieldType(field_index, mod);
...@@ -19533,16 +19512,13 @@ fn structInitAnon(...@@ -19533,16 +19512,13 @@ fn structInitAnon(
1953319512
19534 const types = try sema.arena.alloc(InternPool.Index, extra_data.fields_len);19513 const types = try sema.arena.alloc(InternPool.Index, extra_data.fields_len);
19535 const values = try sema.arena.alloc(InternPool.Index, types.len);19514 const values = try sema.arena.alloc(InternPool.Index, types.len);
1953619515 const names = try sema.arena.alloc(InternPool.NullTerminatedString, types.len);
19537 var fields = std.AutoArrayHashMap(InternPool.NullTerminatedString, u32).init(sema.arena);
19538 try fields.ensureUnusedCapacity(types.len);
1953919516
19540 // Find which field forces the expression to be runtime, if any.19517 // Find which field forces the expression to be runtime, if any.
19541 const opt_runtime_index = rs: {19518 const opt_runtime_index = rs: {
19542 var runtime_index: ?usize = null;19519 var runtime_index: ?usize = null;
19543 var extra_index = extra_end;19520 var extra_index = extra_end;
19544 for (types, 0..) |*field_ty, i_usize| {19521 for (types, values, names, 0..) |*field_ty, *field_val, *field_name, i_usize| {
19545 const i: u32 = @intCast(i_usize);
19546 const item = switch (kind) {19522 const item = switch (kind) {
19547 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),19523 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
19548 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),19524 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
...@@ -19558,29 +19534,16 @@ fn structInitAnon(...@@ -19558,29 +19534,16 @@ fn structInitAnon(
19558 break :name sema.code.nullTerminatedString(field_type_extra.data.name_start);19534 break :name sema.code.nullTerminatedString(field_type_extra.data.name_start);
19559 },19535 },
19560 };19536 };
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.*);19538 const name_ip = try mod.intern_pool.getOrPutString(gpa, name);
19571 try sema.errNote(block, prev_source, msg, "other field here", .{});19539 field_name.* = name_ip;
19572 break :msg msg;
19573 };
19574 return sema.failWithOwnedErrorMsg(block, msg);
19575 }
19576 gop.value_ptr.* = i;
1957719540
19578 const init = try sema.resolveInst(item.data.init);19541 const init = try sema.resolveInst(item.data.init);
19579 field_ty.* = sema.typeOf(init).toIntern();19542 field_ty.* = sema.typeOf(init).toIntern();
19580 if (field_ty.toType().zigTypeTag(mod) == .Opaque) {19543 if (field_ty.toType().zigTypeTag(mod) == .Opaque) {
19581 const msg = msg: {19544 const msg = msg: {
19582 const decl = mod.declPtr(block.src_decl);19545 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));
19584 const msg = try sema.errMsg(block, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});19547 const msg = try sema.errMsg(block, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
19585 errdefer msg.destroy(sema.gpa);19548 errdefer msg.destroy(sema.gpa);
1958619549
...@@ -19590,17 +19553,17 @@ fn structInitAnon(...@@ -19590,17 +19553,17 @@ fn structInitAnon(
19590 return sema.failWithOwnedErrorMsg(block, msg);19553 return sema.failWithOwnedErrorMsg(block, msg);
19591 }19554 }
19592 if (try sema.resolveValue(init)) |init_val| {19555 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);
19594 } else {19557 } else {
19595 values[i] = .none;19558 field_val.* = .none;
19596 runtime_index = i;19559 runtime_index = @intCast(i_usize);
19597 }19560 }
19598 }19561 }
19599 break :rs runtime_index;19562 break :rs runtime_index;
19600 };19563 };
1960119564
19602 const tuple_ty = try ip.getAnonStructType(gpa, .{19565 const tuple_ty = try ip.getAnonStructType(gpa, .{
19603 .names = fields.keys(),19566 .names = names,
19604 .types = types,19567 .types = types,
19605 .values = values,19568 .values = values,
19606 });19569 });
test/cases/compile_errors/duplicate_field_in_anonymous_struct_literal.zig+2-2
...@@ -14,5 +14,5 @@ export fn entry() void {...@@ -14,5 +14,5 @@ export fn entry() void {
14// backend=stage214// backend=stage2
15// target=native15// target=native
16//16//
17// :7:16: error: duplicate field17// :4:14: error: duplicate field
18// :4:16: note: other field here18// :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 {...@@ -6,5 +6,5 @@ pub export fn entry() void {
6// backend=stage26// backend=stage2
7// target=native7// target=native
8//8//
9// :2:21: error: duplicate field9// :2:13: error: duplicate field
10// :2:13: note: other field here10// :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 {...@@ -17,5 +17,5 @@ export fn f() void {
17// backend=stage217// backend=stage2
18// target=native18// target=native
19//19//
20// :11:10: error: duplicate field20// :8:10: error: duplicate field
21// :8:10: note: other field here21// :11:10: note: other field here
test/cases/compile_errors/duplicate_struct_field.zig+22-5
...@@ -2,15 +2,32 @@ const Foo = struct {...@@ -2,15 +2,32 @@ const Foo = struct {
2 Bar: i32,2 Bar: i32,
3 Bar: usize,3 Bar: usize,
4};4};
5export fn entry() void {5
6 const a: Foo = undefined;6const S = struct {
7 _ = a;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;
8}21}
922
10// error23// error
11// backend=stage224// backend=stage2
12// target=native25// target=native
13//26//
14// :3:5: error: duplicate struct field: 'Bar'27// :2:5: error: duplicate struct field: 'Bar'
15// :2:5: note: other field here28// :3:5: note: other field here
16// :1:13: note: struct declared here29// :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 {...@@ -11,6 +11,6 @@ pub export fn entry() void {
11// backend=stage211// backend=stage2
12// target=native12// target=native
13//13//
14// :4:9: error: duplicate struct field: 'e'14// :3:9: error: duplicate struct field: 'e'
15// :3:9: note: other field here15// :4:9: note: other field here
16// :2:22: note: struct declared here16// :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 {...@@ -11,6 +11,6 @@ export fn entry() void {
11// error11// error
12// target=native12// target=native
13//13//
14// :3:5: error: duplicate struct field: 'foo'14// :2:5: error: duplicate struct field: 'foo'
15// :2:5: note: other field here15// :3:5: note: other field here
16// :1:11: note: struct declared here16// :1:11: note: struct declared here
test/cbe.zig+2-2
...@@ -507,8 +507,8 @@ pub fn addCases(ctx: *Cases) !void {...@@ -507,8 +507,8 @@ pub fn addCases(ctx: *Cases) !void {
507 \\ return p.y - p.x - p.x;507 \\ return p.y - p.x - p.x;
508 \\}508 \\}
509 , &.{509 , &.{
510 ":6:10: error: duplicate field",510 ":4:10: error: duplicate field",
511 ":4:10: note: other field here",511 ":6:10: note: other field here",
512 });512 });
513 case.addError(513 case.addError(
514 \\const Point = struct { x: i32, y: i32 };514 \\const Point = struct { x: i32, y: i32 };