From d505ea6cafe8bf6549eb2c56396b0a94d8109859 Mon Sep 17 00:00:00 2001 From: Vexu Date: Wed, 26 Feb 2020 01:03:58 +0200 Subject: [PATCH 1/4] fix `@tagName` on extern and non-exhaustive enums --- src/codegen.cpp | 17 +++++++++++------ src/ir.cpp | 11 +++++++---- test/compile_errors.zig | 9 +++++++++ test/stage1/behavior/enum.zig | 23 ++++++++++++++++++++++- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index f9d2566da7..0bf8510843 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5031,8 +5031,18 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) { LLVMConstNull(usize->llvm_type), }; + HashMap 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); diff --git a/src/ir.cpp b/src/ir.cpp index 8a7976995c..4d24ec7dfb 100644 --- a/src/ir.cpp +++ b/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); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 138f7a7633..0260160dca 100644 --- a/test/compile_errors.zig +++ b/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; diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig index 18489ba24b..b6cb86a363 100644 --- a/test/stage1/behavior/enum.zig +++ b/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)); From 215797749d0e0d911feaaf45dc70836c3d12f18e Mon Sep 17 00:00:00 2001 From: Vexu Date: Wed, 26 Feb 2020 01:19:22 +0200 Subject: [PATCH 2/4] fix `@intToEnum` on extern enums --- src/codegen.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 0bf8510843..4d992d25d0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3329,9 +3329,21 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutableGen *executabl LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue"); size_t field_count = wanted_type->data.enumeration.src_field_count; LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count); + + HashMap occupied_tag_values = {}; + occupied_tag_values.init(field_count); + for (size_t field_i = 0; field_i < field_count; field_i += 1) { + TypeEnumField *type_enum_field = &wanted_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 this_tag_int_value = bigint_to_llvm_const(get_llvm_type(g, tag_int_type), - &wanted_type->data.enumeration.fields[field_i].value); + &type_enum_field->value); LLVMAddCase(switch_instr, this_tag_int_value, ok_value_block); } LLVMPositionBuilderAtEnd(g->builder, bad_value_block); From 22432b15e31d506d070171b2932d5df71e9a1b9e Mon Sep 17 00:00:00 2001 From: Vexu Date: Wed, 26 Feb 2020 11:07:47 +0200 Subject: [PATCH 3/4] add test for `@intToEnum` --- lib/std/fmt.zig | 6 ++---- src-self-hosted/ir.zig | 2 +- test/stage1/behavior/cast.zig | 11 +++++++++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index b9c6dd0033..d093101646 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -414,10 +414,9 @@ pub fn formatType( if (max_depth == 0) { return output(context, "{ ... }"); } - comptime var field_i = 0; try output(context, "{"); - inline for (StructT.fields) |f| { - if (field_i == 0) { + inline for (StructT.fields) |f, i| { + if (i == 0) { try output(context, " ."); } else { try output(context, ", ."); @@ -425,7 +424,6 @@ pub fn formatType( try output(context, f.name); try output(context, " = "); try formatType(@field(value, f.name), fmt, options, context, Errors, output, max_depth - 1); - field_i += 1; } try output(context, " }"); }, diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 576294a7d7..2e65962d41 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -1803,7 +1803,7 @@ pub const Builder = struct { // Look at the params and ref() other instructions inline for (@typeInfo(I.Params).Struct.fields) |f| { - switch (f.fiedl_type) { + switch (f.field_type) { *Inst => @field(inst.params, f.name).ref(self), *BasicBlock => @field(inst.params, f.name).ref(self), ?*Inst => if (@field(inst.params, f.name)) |other| other.ref(self), diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig index 76d3b784ec..d2267a3753 100644 --- a/test/stage1/behavior/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -491,6 +491,17 @@ test "@intToEnum passed a comptime_int to an enum with one item" { expect(x == E.A); } +test "@intToEnum runtime to an extern enum with duplicate values" { + const E = extern enum(u8) { + A = 1, + B = 1, + }; + var a: u8 = 1; + var x = @intToEnum(E, a); + expect(x == E.A); + expect(x == E.B); +} + test "@intCast to u0 and use the result" { const S = struct { fn doTheTest(zero: u1, one: u1, bigzero: i32) void { From 62de32a18c1eecedc29055e4199fa364f9e9b7c6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Feb 2020 11:21:10 -0500 Subject: [PATCH 4/4] call deinit on the hashmaps introduced in the prev commit --- src/codegen.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index 4d992d25d0..ee3e05a801 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3346,6 +3346,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutableGen *executabl &type_enum_field->value); LLVMAddCase(switch_instr, this_tag_int_value, ok_value_block); } + occupied_tag_values.deinit(); LLVMPositionBuilderAtEnd(g->builder, bad_value_block); gen_safety_crash(g, PanicMsgIdBadEnumValue); @@ -5084,6 +5085,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) { LLVMPositionBuilderAtEnd(g->builder, return_block); LLVMBuildRet(g->builder, slice_global); } + occupied_tag_values.deinit(); LLVMPositionBuilderAtEnd(g->builder, bad_value_block); if (g->build_mode == BuildModeDebug || g->build_mode == BuildModeSafeRelease) {