From 8f0dac01ef00574167ca99b716d9903c86a96a3d Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Fri, 18 Mar 2022 17:35:30 +0100 Subject: [PATCH 1/9] sema: add more info to error message for invalid comptime union field access --- src/Sema.zig | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 65550bec00..5f997f131e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17484,9 +17484,15 @@ fn unionFieldVal( if (tag_matches) { return sema.addConstant(field.ty, tag_and_val.val); } else { - // TODO enhance this saying which one was active - // and which one was accessed, and showing where the union was declared. - return sema.fail(block, src, "access of inactive union field", .{}); + const msg = msg: { + const active_index = tag_and_val.tag.castTag(.enum_field_index).?.data; + const active_field_name = union_obj.fields.keys()[active_index]; + const msg = try sema.errMsg(block, src, "access of union field '{s}' while field '{s}' is active", .{ field_name, active_field_name }); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, union_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); } }, .Packed, .Extern => { From f7f4702795d76c606d119941e2cf5d5c3e2045b6 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sat, 19 Mar 2022 12:55:58 +0100 Subject: [PATCH 2/9] sema: add more info to error messages for enum->union coercion --- src/Sema.zig | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 5f997f131e..e7bc442f84 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -19596,13 +19596,17 @@ fn coerceEnumToUnion( const field = union_obj.fields.values()[field_index]; const field_ty = try sema.resolveTypeFields(block, inst_src, field.ty); const opv = (try sema.typeHasOnePossibleValue(block, inst_src, field_ty)) orelse { - // TODO resolve the field names and include in the error message, - // also instead of 'union declared here' make it 'field "foo" declared here'. const msg = msg: { - const msg = try sema.errMsg(block, inst_src, "coercion to union {} must initialize {} field", .{ - union_ty.fmt(target), field_ty.fmt(target), + const field_name = union_obj.fields.keys()[field_index]; + const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{s}'", .{ + inst_ty.fmt(target), union_ty.fmt(target), field_ty.fmt(target), field_name, }); errdefer msg.destroy(sema.gpa); + + const tree = try sema.getAstTree(block); + const union_decl = union_obj.owner_decl; + const field_src = enumFieldSrcLoc(union_decl, tree.*, union_obj.node_offset, field_index); + try sema.mod.errNoteNonLazy(field_src.toSrcLoc(union_decl), msg, "field '{s}' declared here", .{field_name}); try sema.addDeclaredHereNote(msg, union_ty); break :msg msg; }; @@ -19634,13 +19638,27 @@ fn coerceEnumToUnion( return block.addBitCast(union_ty, enum_tag); } - // TODO resolve the field names and add a hint that says "field 'foo' has type 'bar'" - // instead of the "union declared here" hint const msg = msg: { - const msg = try sema.errMsg(block, inst_src, "runtime coercion to union {} which has non-void fields", .{ - union_ty.fmt(target), - }); + const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const msg = try sema.errMsg( + block, + inst_src, + "runtime coercion from enum '{}' to union '{}' which has non-void fields", + .{ tag_ty.fmt(target), union_ty.fmt(target) }, + ); errdefer msg.destroy(sema.gpa); + + const tree = try sema.getAstTree(block); + const union_decl = union_obj.owner_decl; + var it = union_obj.fields.iterator(); + var field_index: usize = 0; + while (it.next()) |field| { + const field_name = field.key_ptr.*; + const field_ty = field.value_ptr.ty; + const field_src = enumFieldSrcLoc(union_decl, tree.*, union_obj.node_offset, field_index); + try sema.mod.errNoteNonLazy(field_src.toSrcLoc(union_decl), msg, "field '{s}' has type '{}'", .{ field_name, field_ty.fmt(target) }); + field_index += 1; + } try sema.addDeclaredHereNote(msg, union_ty); break :msg msg; }; From b922caf1691111e5c23f01afbe1f1c9b5104807b Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sat, 19 Mar 2022 19:32:31 +0100 Subject: [PATCH 3/9] sema: add compile error for missing/extra enum fields in union decl --- src/Sema.zig | 44 ++++++++++++++++++++++++++++++++++++++++++-- src/type.zig | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index e7bc442f84..4e8acb0030 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -21727,7 +21727,7 @@ fn resolveTypeFieldsUnion( } union_obj.status = .field_types_wip; - try semaUnionFields(sema.mod, union_obj); + try semaUnionFields(block, sema.mod, union_obj); union_obj.status = .have_field_types; } @@ -21967,7 +21967,7 @@ fn semaStructFields( } } -fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { +fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) CompileError!void { const tracy = trace(@src()); defer tracy.end(); @@ -22067,6 +22067,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { var int_tag_ty: Type = undefined; var enum_field_names: ?*Module.EnumNumbered.NameMap = null; var enum_value_map: ?*Module.EnumNumbered.ValueMap = null; + var tag_ty_field_names: ?Module.EnumFull.NameMap = null; if (tag_type_ref != .none) { const provided_ty = try sema.resolveType(&block_scope, src, tag_type_ref); if (small.auto_enum_tag) { @@ -22079,6 +22080,10 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { } else { // The provided type is the enum tag type. union_obj.tag_ty = try provided_ty.copy(decl_arena_allocator); + // The fields of the union must match the enum exactly. + // Store a copy of the enum field names so we can check for + // missing or extraneous fields later. + tag_ty_field_names = try union_obj.tag_ty.enumFields().clone(decl_arena_allocator); } } else { // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis @@ -22172,6 +22177,20 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { set.putAssumeCapacity(field_name, {}); } + if (tag_ty_field_names) |*names| { + const enum_has_field = names.contains(field_name); + if (!enum_has_field) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(target), field_name }); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, union_obj.tag_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + _ = names.orderedRemove(field_name); + } + const field_ty: Type = if (!has_type) Type.void else if (field_type_ref == .none) @@ -22202,6 +22221,27 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { gop.value_ptr.abi_align = 0; } } + + if (tag_ty_field_names) |names| { + if (names.count() > 0) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{}); + errdefer msg.destroy(sema.gpa); + + const enum_ty = union_obj.tag_ty; + const tree = try sema.getAstTree(block); + const enum_decl = enum_ty.getOwnerDecl(); + for (names.keys()) |field_name| { + const field_index = enum_ty.enumFieldIndex(field_name).?; + const field_src = enumFieldSrcLoc(enum_decl, tree.*, enum_ty.getNodeOffset(), field_index); + try sema.mod.errNoteNonLazy(field_src.toSrcLoc(enum_decl), msg, "field '{s}' missing, declared here", .{field_name}); + } + try sema.addDeclaredHereNote(msg, union_obj.tag_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + } } fn generateUnionTagTypeNumbered( diff --git a/src/type.zig b/src/type.zig index bcb6e63f6e..735227f9d2 100644 --- a/src/type.zig +++ b/src/type.zig @@ -5305,6 +5305,50 @@ pub const Type = extern union { } } + pub fn getNodeOffset(ty: Type) i32 { + switch (ty.tag()) { + .enum_full, .enum_nonexhaustive => { + const enum_full = ty.cast(Payload.EnumFull).?.data; + return enum_full.node_offset; + }, + .enum_numbered => return ty.castTag(.enum_numbered).?.data.node_offset, + .enum_simple => { + const enum_simple = ty.castTag(.enum_simple).?.data; + return enum_simple.node_offset; + }, + .@"struct" => { + const struct_obj = ty.castTag(.@"struct").?.data; + return struct_obj.node_offset; + }, + .error_set => { + const error_set = ty.castTag(.error_set).?.data; + return error_set.node_offset; + }, + .@"union", .union_tagged => { + const union_obj = ty.cast(Payload.Union).?.data; + return union_obj.node_offset; + }, + .@"opaque" => { + const opaque_obj = ty.cast(Payload.Opaque).?.data; + return opaque_obj.node_offset; + }, + .atomic_order, + .atomic_rmw_op, + .calling_convention, + .address_space, + .float_mode, + .reduce_op, + .call_options, + .prefetch_options, + .export_options, + .extern_options, + .type_info, + => unreachable, // These need to be resolved earlier. + + else => unreachable, + } + } + /// Asserts the type is an enum. pub fn enumHasInt(ty: Type, int: Value, target: Target) bool { const S = struct { From e4d427f12e2052e9bcd6af40e7ddbc4e544451e6 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sun, 20 Mar 2022 13:47:03 +0100 Subject: [PATCH 4/9] refactor: add Sema.addFieldErrNote --- src/Sema.zig | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 4e8acb0030..fb37f3f1ba 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1589,6 +1589,21 @@ fn errNote( return sema.mod.errNoteNonLazy(src.toSrcLoc(block.src_decl), parent, format, args); } +fn addFieldErrNote( + sema: *Sema, + block: *Block, + container_ty: Type, + field_index: usize, + parent: *Module.ErrorMsg, + comptime format: []const u8, + args: anytype, +) !void { + const decl = container_ty.getOwnerDecl(); + const tree = try sema.getAstTree(block); + const field_src = enumFieldSrcLoc(decl, tree.*, container_ty.getNodeOffset(), field_index); + try sema.mod.errNoteNonLazy(field_src.toSrcLoc(decl), parent, format, args); +} + fn errMsg( sema: *Sema, block: *Block, @@ -19603,10 +19618,7 @@ fn coerceEnumToUnion( }); errdefer msg.destroy(sema.gpa); - const tree = try sema.getAstTree(block); - const union_decl = union_obj.owner_decl; - const field_src = enumFieldSrcLoc(union_decl, tree.*, union_obj.node_offset, field_index); - try sema.mod.errNoteNonLazy(field_src.toSrcLoc(union_decl), msg, "field '{s}' declared here", .{field_name}); + try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' declared here", .{field_name}); try sema.addDeclaredHereNote(msg, union_ty); break :msg msg; }; @@ -19648,15 +19660,12 @@ fn coerceEnumToUnion( ); errdefer msg.destroy(sema.gpa); - const tree = try sema.getAstTree(block); - const union_decl = union_obj.owner_decl; var it = union_obj.fields.iterator(); var field_index: usize = 0; while (it.next()) |field| { const field_name = field.key_ptr.*; const field_ty = field.value_ptr.ty; - const field_src = enumFieldSrcLoc(union_decl, tree.*, union_obj.node_offset, field_index); - try sema.mod.errNoteNonLazy(field_src.toSrcLoc(union_decl), msg, "field '{s}' has type '{}'", .{ field_name, field_ty.fmt(target) }); + try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' has type '{}'", .{ field_name, field_ty.fmt(target) }); field_index += 1; } try sema.addDeclaredHereNote(msg, union_ty); @@ -22229,12 +22238,9 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil errdefer msg.destroy(sema.gpa); const enum_ty = union_obj.tag_ty; - const tree = try sema.getAstTree(block); - const enum_decl = enum_ty.getOwnerDecl(); for (names.keys()) |field_name| { const field_index = enum_ty.enumFieldIndex(field_name).?; - const field_src = enumFieldSrcLoc(enum_decl, tree.*, enum_ty.getNodeOffset(), field_index); - try sema.mod.errNoteNonLazy(field_src.toSrcLoc(enum_decl), msg, "field '{s}' missing, declared here", .{field_name}); + try sema.addFieldErrNote(block, enum_ty, field_index, msg, "field '{s}' missing, declared here", .{field_name}); } try sema.addDeclaredHereNote(msg, union_obj.tag_ty); break :msg msg; From fd1ce329b3f978a4ac2ae272afee7f07b5841990 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sat, 26 Mar 2022 19:34:55 +0100 Subject: [PATCH 5/9] stage2: add union compile error tests --- .../stage2/union_access_of_inactive_field.zig | 14 ++++++++++++ .../stage2/union_enum_field_missing.zig | 20 +++++++++++++++++ .../stage2/union_extra_field.zig | 19 ++++++++++++++++ .../union_runtime_coercion_from_enum.zig | 22 +++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 test/compile_errors/stage2/union_access_of_inactive_field.zig create mode 100644 test/compile_errors/stage2/union_enum_field_missing.zig create mode 100644 test/compile_errors/stage2/union_extra_field.zig create mode 100644 test/compile_errors/stage2/union_runtime_coercion_from_enum.zig diff --git a/test/compile_errors/stage2/union_access_of_inactive_field.zig b/test/compile_errors/stage2/union_access_of_inactive_field.zig new file mode 100644 index 0000000000..34fa661d79 --- /dev/null +++ b/test/compile_errors/stage2/union_access_of_inactive_field.zig @@ -0,0 +1,14 @@ +const U = union { + a: void, + b: u64, +}; +comptime { + var u: U = .{.a = {}}; + const v = u.b; + _ = v; +} + +// access of inactive union field +// +// :7:16: error: access of union field 'b' while field 'a' is active +// :1:11: note: union declared here diff --git a/test/compile_errors/stage2/union_enum_field_missing.zig b/test/compile_errors/stage2/union_enum_field_missing.zig new file mode 100644 index 0000000000..b29ca83d3a --- /dev/null +++ b/test/compile_errors/stage2/union_enum_field_missing.zig @@ -0,0 +1,20 @@ +const E = enum { + a, + b, + c, +}; + +const U = union(E) { + a: i32, + b: f64, +}; + +export fn entry() usize { + return @sizeOf(U); +} + +// enum field missing in union +// +// :7:1: error: enum field(s) missing in union +// :4:5: note: field 'c' missing, declared here +// :1:11: note: enum declared here diff --git a/test/compile_errors/stage2/union_extra_field.zig b/test/compile_errors/stage2/union_extra_field.zig new file mode 100644 index 0000000000..4a0ab41936 --- /dev/null +++ b/test/compile_errors/stage2/union_extra_field.zig @@ -0,0 +1,19 @@ +const E = enum { + a, + b, + c, +}; +const U = union(E) { + a: i32, + b: f64, + c: f64, + d: f64, +}; +export fn entry() usize { + return @sizeOf(U); +} + +// union extra field +// +// :6:1: error: enum 'tmp.E' hs no field named 'd' +// :1:11: note: enum declared here diff --git a/test/compile_errors/stage2/union_runtime_coercion_from_enum.zig b/test/compile_errors/stage2/union_runtime_coercion_from_enum.zig new file mode 100644 index 0000000000..f7e96834fd --- /dev/null +++ b/test/compile_errors/stage2/union_runtime_coercion_from_enum.zig @@ -0,0 +1,22 @@ +const E = enum { + a, + b, +}; +const U = union(E) { + a: u32, + b: u64, +}; +fn foo() E { + return E.b; +} +export fn doTheTest() u64 { + var u: U = foo(); + return u.b; +} + +// runtime coercion from enum to union +// +// :13:19: error: runtime coercion from enum 'tmp.E' to union 'tmp.U' which has non-void fields +// :6:5: note: field 'a' has type 'u32' +// :7:5: note: field 'b' has type 'u64' +// :5:11: note: union declared here From 174a889364d285e32534016f7425e8adaf9cab3c Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sun, 3 Apr 2022 14:01:06 +0200 Subject: [PATCH 6/9] sema: add compile error for duplicate union field --- src/Sema.zig | 17 ++++++++++++++++- .../stage2/union_duplicate_field_definition.zig | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 test/compile_errors/stage2/union_duplicate_field_definition.zig diff --git a/src/Sema.zig b/src/Sema.zig index fb37f3f1ba..8744548892 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22215,7 +22215,22 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil } const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); - assert(!gop.found_existing); + if (gop.found_existing) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{s}'", .{field_name}); + errdefer msg.destroy(gpa); + + const prev_field_index = union_obj.fields.getIndex(field_name).?; + const prev_field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, prev_field_index); + try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{}); + try sema.errNote(&block_scope, src, msg, "union declared here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } + gop.value_ptr.* = .{ .ty = try field_ty.copy(decl_arena_allocator), .abi_align = 0, diff --git a/test/compile_errors/stage2/union_duplicate_field_definition.zig b/test/compile_errors/stage2/union_duplicate_field_definition.zig new file mode 100644 index 0000000000..6ad2ae4f4e --- /dev/null +++ b/test/compile_errors/stage2/union_duplicate_field_definition.zig @@ -0,0 +1,15 @@ +const U = union { + foo: u32, + foo: u32, +}; + +export fn entry() void { + const u: U = .{ .foo = 100 }; + _ = u; +} + +// duplicate union field name +// +// :3:5: error: duplicate union field: 'foo' +// :2:5: note: other field here +// :1:11: note: union declared here From 6bbc2cd59af250ea164a4114e3d9fa15b27c2c8e Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sun, 3 Apr 2022 14:07:13 +0200 Subject: [PATCH 7/9] sema: add compile error for duplicate struct field --- src/Sema.zig | 16 +++++++++++++++- .../stage2/struct_duplicate_field_name.zig | 15 +++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 test/compile_errors/stage2/struct_duplicate_field_name.zig diff --git a/src/Sema.zig b/src/Sema.zig index 8744548892..17a7a8eb1c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -21945,7 +21945,21 @@ fn semaStructFields( } const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); - assert(!gop.found_existing); + if (gop.found_existing) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, field_i); + const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{s}'", .{field_name}); + errdefer msg.destroy(gpa); + + const prev_field_index = struct_obj.fields.getIndex(field_name).?; + const prev_field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, prev_field_index); + try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{}); + try sema.errNote(&block_scope, src, msg, "struct declared here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } gop.value_ptr.* = .{ .ty = try field_ty.copy(decl_arena_allocator), .abi_align = 0, diff --git a/test/compile_errors/stage2/struct_duplicate_field_name.zig b/test/compile_errors/stage2/struct_duplicate_field_name.zig new file mode 100644 index 0000000000..274dce4e4a --- /dev/null +++ b/test/compile_errors/stage2/struct_duplicate_field_name.zig @@ -0,0 +1,15 @@ +const S = struct { + foo: u32, + foo: u32, +}; + +export fn entry() void { + const s: S = .{ .foo = 100 }; + _ = s; +} + +// duplicate struct field name +// +// :3:5: error: duplicate struct field: 'foo' +// :2:5: note: other field here +// :1:11: note: struct declared here From 17b804f56b0e75962d3e78a8bade1affa7984b4e Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Sun, 3 Apr 2022 14:19:55 +0200 Subject: [PATCH 8/9] Fix typo in compile error test --- test/compile_errors/stage2/union_extra_field.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/compile_errors/stage2/union_extra_field.zig b/test/compile_errors/stage2/union_extra_field.zig index 4a0ab41936..e8ba581aad 100644 --- a/test/compile_errors/stage2/union_extra_field.zig +++ b/test/compile_errors/stage2/union_extra_field.zig @@ -15,5 +15,5 @@ export fn entry() usize { // union extra field // -// :6:1: error: enum 'tmp.E' hs no field named 'd' +// :6:1: error: enum 'tmp.E' has no field named 'd' // :1:11: note: enum declared here From 94fd914e584d466808f40b9eb5fac49c1cc3c66a Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Mon, 4 Apr 2022 16:48:01 +0200 Subject: [PATCH 9/9] Address review comments --- src/Sema.zig | 29 +++++++++---------- .../stage2/union_duplicate_enum_field.zig | 16 ++++++++++ 2 files changed, 30 insertions(+), 15 deletions(-) create mode 100644 test/compile_errors/stage2/union_duplicate_enum_field.zig diff --git a/src/Sema.zig b/src/Sema.zig index 17a7a8eb1c..ae13298337 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22106,7 +22106,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil // The fields of the union must match the enum exactly. // Store a copy of the enum field names so we can check for // missing or extraneous fields later. - tag_ty_field_names = try union_obj.tag_ty.enumFields().clone(decl_arena_allocator); + tag_ty_field_names = try union_obj.tag_ty.enumFields().clone(sema.arena); } } else { // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis @@ -22200,20 +22200,6 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil set.putAssumeCapacity(field_name, {}); } - if (tag_ty_field_names) |*names| { - const enum_has_field = names.contains(field_name); - if (!enum_has_field) { - const msg = msg: { - const msg = try sema.errMsg(block, src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(target), field_name }); - errdefer msg.destroy(sema.gpa); - try sema.addDeclaredHereNote(msg, union_obj.tag_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - _ = names.orderedRemove(field_name); - } - const field_ty: Type = if (!has_type) Type.void else if (field_type_ref == .none) @@ -22245,6 +22231,19 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil return sema.failWithOwnedErrorMsg(&block_scope, msg); } + if (tag_ty_field_names) |*names| { + const enum_has_field = names.orderedRemove(field_name); + if (!enum_has_field) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(target), field_name }); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, union_obj.tag_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + } + gop.value_ptr.* = .{ .ty = try field_ty.copy(decl_arena_allocator), .abi_align = 0, diff --git a/test/compile_errors/stage2/union_duplicate_enum_field.zig b/test/compile_errors/stage2/union_duplicate_enum_field.zig new file mode 100644 index 0000000000..9044f9e97e --- /dev/null +++ b/test/compile_errors/stage2/union_duplicate_enum_field.zig @@ -0,0 +1,16 @@ +const E = enum {a, b}; +const U = union(E) { + a: u32, + a: u32, +}; + +export fn foo() void { + var u: U = .{ .a = 123 }; + _ = u; +} + +// union with enum and duplicate fields +// +// :4:5: error: duplicate union field: 'a' +// :3:5: note: other field here +// :2:11: note: union declared here