Commit f094c4bce5

r00ster91 <r00ster91@proton.me>
2022-12-01 21:03:46
Sema: detect duplicate enum tag values
1 parent 19dd6de
src/Sema.zig
@@ -18264,9 +18264,9 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
                 .mod = mod,
             });
 
-            var i: usize = 0;
-            while (i < fields_len) : (i += 1) {
-                const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i);
+            var field_i: usize = 0;
+            while (field_i < fields_len) : (field_i += 1) {
+                const elem_val = try fields_val.elemValue(sema.mod, sema.arena, field_i);
                 const field_struct_val: []const Value = elem_val.castTag(.aggregate).?.data;
                 // TODO use reflection instead of magic numbers here
                 // name: []const u8
@@ -18289,17 +18289,31 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
                     });
                 }
 
-                const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name);
-                if (gop.found_existing) {
-                    // TODO: better source location
-                    return sema.fail(block, src, "duplicate enum tag {s}", .{field_name});
+                const gop_field = enum_obj.fields.getOrPutAssumeCapacity(field_name);
+                if (gop_field.found_existing) {
+                    const msg = msg: {
+                        const msg = try sema.errMsg(block, src, "duplicate enum field '{s}'", .{field_name});
+                        errdefer msg.destroy(gpa);
+                        try sema.errNote(block, src, msg, "other field here", .{});
+                        break :msg msg;
+                    };
+                    return sema.failWithOwnedErrorMsg(msg);
                 }
 
                 const copied_tag_val = try value_val.copy(new_decl_arena_allocator);
-                enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{
+                const gop_val = enum_obj.values.getOrPutAssumeCapacityContext(copied_tag_val, .{
                     .ty = enum_obj.tag_ty,
                     .mod = mod,
                 });
+                if (gop_val.found_existing) {
+                    const msg = msg: {
+                        const msg = try sema.errMsg(block, src, "enum tag value {} already taken", .{value_val.fmtValue(Type.comptime_int, mod)});
+                        errdefer msg.destroy(gpa);
+                        try sema.errNote(block, src, msg, "other enum tag value here", .{});
+                        break :msg msg;
+                    };
+                    return sema.failWithOwnedErrorMsg(msg);
+                }
             }
 
             try new_decl.finalizeNewArena(&new_decl_arena);
test/cases/compile_errors/reify_enum_with_duplicate_field.zig
@@ -0,0 +1,21 @@
+export fn entry() void {
+    _ = @Type(.{
+        .Enum = .{
+            .layout = .Auto,
+            .tag_type = u32,
+            .fields = &.{
+                .{ .name = "A", .value = 0 },
+                .{ .name = "A", .value = 1 },
+            },
+            .decls = &.{},
+            .is_exhaustive = false,
+        },
+    });
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :2:9: error: duplicate enum field 'A'
+// :2:9: note: other field here
test/cases/compile_errors/reify_enum_with_duplicate_tag_value.zig
@@ -0,0 +1,21 @@
+export fn entry() void {
+    _ = @Type(.{
+        .Enum = .{
+            .layout = .Auto,
+            .tag_type = u32,
+            .fields = &.{
+                .{ .name = "A", .value = 10 },
+                .{ .name = "B", .value = 10 },
+            },
+            .decls = &.{},
+            .is_exhaustive = false,
+        },
+    });
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :2:9: error: enum tag value 10 already taken
+// :2:9: note: other enum tag value here