| author | |
| committer | |
| log | 941090d94f6b6c7c5207f06917743e79ff06426f |
| tree | 89a9cbd53f32e26b39779f997aecb1ce090ee75d |
| parent | acf9de376d176d35dcfd245d14939766aeba4638 |
| signature |
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 | }; |
| 450 | 450 | ||
| 451 | pub const rlimit_resource = enum(c_int) { | 451 | pub 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 | } |
| 1724 | 1724 | ||
| 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 | }; |
| 4876 | 4927 | ||
| 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 | }; |
| 4887 | 4947 | ||
| 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); |
| 4891 | 4953 | ||
| 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 | } |
| 4977 | 5048 | ||
| 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 | else | 4691 | 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 | } |
| 4706 | 4696 | ||
| ... | @@ -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 | else | 19213 | 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( |
| 19533 | 19512 | ||
| 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); |
| 19536 | 19515 | 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); | ||
| 19539 | 19516 | ||
| 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); | ||
| 19569 | 19537 | ||
| 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; | ||
| 19577 | 19540 | ||
| 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); |
| 19586 | 19549 | ||
| ... | @@ -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 | }; |
| 19601 | 19564 | ||
| 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=stage2 | 14 | // backend=stage2 |
| 15 | // target=native | 15 | // target=native |
| 16 | // | 16 | // |
| 17 | // :7:16: error: duplicate field | 17 | // :4:14: error: duplicate field |
| 18 | // :4:16: note: other field here | 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 { | ... | @@ -6,5 +6,5 @@ pub export fn entry() void { |
| 6 | // backend=stage2 | 6 | // backend=stage2 |
| 7 | // target=native | 7 | // target=native |
| 8 | // | 8 | // |
| 9 | // :2:21: error: duplicate field | 9 | // :2:13: error: duplicate field |
| 10 | // :2:13: note: other field here | 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 { | ... | @@ -17,5 +17,5 @@ export fn f() void { |
| 17 | // backend=stage2 | 17 | // backend=stage2 |
| 18 | // target=native | 18 | // target=native |
| 19 | // | 19 | // |
| 20 | // :11:10: error: duplicate field | 20 | // :8:10: error: duplicate field |
| 21 | // :8:10: note: other field here | 21 | // :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 | }; |
| 5 | export fn entry() void { | 5 | |
| 6 | const a: Foo = undefined; | 6 | const S = struct { |
| 7 | _ = a; | 7 | a: u32, |
| 8 | b: u32, | ||
| 9 | a: u32, | ||
| 10 | a: u64, | ||
| 11 | }; | ||
| 12 | |||
| 13 | export fn a() void { | ||
| 14 | const f: Foo = undefined; | ||
| 15 | _ = f; | ||
| 16 | } | ||
| 17 | |||
| 18 | export fn b() void { | ||
| 19 | const s: S = undefined; | ||
| 20 | _ = s; | ||
| 8 | } | 21 | } |
| 9 | 22 | ||
| 10 | // error | 23 | // error |
| 11 | // backend=stage2 | 24 | // backend=stage2 |
| 12 | // target=native | 25 | // 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 here | 28 | // :3:5: note: other field here |
| 16 | // :1:13: note: struct declared here | 29 | // :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=stage2 | 11 | // backend=stage2 |
| 12 | // target=native | 12 | // 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 here | 15 | // :4:9: note: other field here |
| 16 | // :2:22: note: struct declared here | 16 | // :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 | // error | 11 | // error |
| 12 | // target=native | 12 | // 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 here | 15 | // :3:5: note: other field here |
| 16 | // :1:11: note: struct declared here | 16 | // :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 }; |