Commit d505ea6caf

Vexu <git@vexu.eu>
2020-02-26 00:03:58
fix `@tagName` on extern and non-exhaustive enums
1 parent c4a2734
Changed files (4)
src/codegen.cpp
@@ -5031,8 +5031,18 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) {
         LLVMConstNull(usize->llvm_type),
     };
 
+    HashMap<BigInt, Buf *, bigint_hash, bigint_eql> occupied_tag_values = {};
+    occupied_tag_values.init(field_count);
+
     for (size_t field_i = 0; field_i < field_count; field_i += 1) {
-        Buf *name = enum_type->data.enumeration.fields[field_i].name;
+        TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
+        
+        Buf *name = type_enum_field->name;
+        auto entry = occupied_tag_values.put_unique(type_enum_field->value, name);
+        if (entry != nullptr) {
+            continue;
+        }
+
         LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true);
         LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), "");
         LLVMSetInitializer(str_global, str_init);
@@ -5086,11 +5096,6 @@ static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutableGen *executa
 {
     ZigType *enum_type = instruction->target->value->type;
     assert(enum_type->id == ZigTypeIdEnum);
-    if (enum_type->data.enumeration.non_exhaustive) {
-        add_node_error(g, instruction->base.base.source_node,
-            buf_sprintf("TODO @tagName on non-exhaustive enum https://github.com/ziglang/zig/issues/3991"));
-        codegen_report_errors_and_exit(g);
-    }
 
     LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type);
 
src/ir.cpp
@@ -23190,12 +23190,15 @@ static IrInstGen *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstSrc
     if (instr_is_comptime(target)) {
         if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown)))
             return ira->codegen->invalid_inst_gen;
-        if (target->value->type->data.enumeration.non_exhaustive) {
-            ir_add_error(ira, &instruction->base.base,
-                buf_sprintf("TODO @tagName on non-exhaustive enum https://github.com/ziglang/zig/issues/3991"));
+        TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint);
+        if (field == nullptr) {
+            Buf *int_buf = buf_alloc();
+            bigint_append_buf(int_buf, &target->value->data.x_bigint, 10);
+
+            ir_add_error(ira, &target->base,
+                buf_sprintf("no tag by value %s", buf_ptr(int_buf)));
             return ira->codegen->invalid_inst_gen;
         }
-        TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint);
         ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee;
         IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr);
         init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(field->name), true);
test/stage1/behavior/enum.zig
@@ -198,7 +198,17 @@ test "@tagName" {
     comptime expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three"));
 }
 
-fn testEnumTagNameBare(n: BareNumber) []const u8 {
+test "@tagName extern enum with duplicates" {
+    expect(mem.eql(u8, testEnumTagNameBare(ExternDuplicates.B), "A"));
+    comptime expect(mem.eql(u8, testEnumTagNameBare(ExternDuplicates.B), "A"));
+}
+
+test "@tagName non-exhaustive enum" {
+    expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));
+    comptime expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));
+}
+
+fn testEnumTagNameBare(n: var) []const u8 {
     return @tagName(n);
 }
 
@@ -208,6 +218,17 @@ const BareNumber = enum {
     Three,
 };
 
+const ExternDuplicates = extern enum(u8) {
+    A = 1,
+    B = 1,
+};
+
+const NonExhaustive = enum(u8) {
+    A,
+    B,
+    _,
+};
+
 test "enum alignment" {
     comptime {
         expect(@alignOf(AlignTestEnum) >= @alignOf([9]u8));
test/compile_errors.zig
@@ -3,6 +3,15 @@ const builtin = @import("builtin");
 const Target = @import("std").Target;
 
 pub fn addCases(cases: *tests.CompileErrorContext) void {
+    cases.addTest("@tagName on invalid value of non-exhaustive enum",
+        \\test "enum" {
+        \\    const E = enum(u8) {A, B, _};
+        \\    _ = @tagName(@intToEnum(E, 5));
+        \\}
+    , &[_][]const u8{
+        "tmp.zig:3:18: error: no tag by value 5",
+    });
+
     cases.addTest("@ptrToInt with pointer to zero-sized type",
         \\export fn entry() void {
         \\    var pointer: ?*u0 = null;