| author | |
| committer | |
| log | 6291e8e4926c2e7dbd5cfa651f862c7dbd2e5bda |
| tree | c30178b457a4afcca39e355da12502f2c6216a7f |
| parent | c4a2734aa08a9e810680d7be2c976fe3ae67cc5b |
| parent | 62de32a18c1eecedc29055e4199fa364f9e9b7c6 |
| signature |
closes #4559
closes #39917 files changed, 78 insertions(+), 17 deletions(-)
lib/std/fmt.zig+2-4| ... | @@ -414,10 +414,9 @@ pub fn formatType( | ... | @@ -414,10 +414,9 @@ pub fn formatType( |
| 414 | if (max_depth == 0) { | 414 | if (max_depth == 0) { |
| 415 | return output(context, "{ ... }"); | 415 | return output(context, "{ ... }"); |
| 416 | } | 416 | } |
| 417 | comptime var field_i = 0; | ||
| 418 | try output(context, "{"); | 417 | try output(context, "{"); |
| 419 | inline for (StructT.fields) |f| { | 418 | inline for (StructT.fields) |f, i| { |
| 420 | if (field_i == 0) { | 419 | if (i == 0) { |
| 421 | try output(context, " ."); | 420 | try output(context, " ."); |
| 422 | } else { | 421 | } else { |
| 423 | try output(context, ", ."); | 422 | try output(context, ", ."); |
| ... | @@ -425,7 +424,6 @@ pub fn formatType( | ... | @@ -425,7 +424,6 @@ pub fn formatType( |
| 425 | try output(context, f.name); | 424 | try output(context, f.name); |
| 426 | try output(context, " = "); | 425 | try output(context, " = "); |
| 427 | try formatType(@field(value, f.name), fmt, options, context, Errors, output, max_depth - 1); | 426 | try formatType(@field(value, f.name), fmt, options, context, Errors, output, max_depth - 1); |
| 428 | field_i += 1; | ||
| 429 | } | 427 | } |
| 430 | try output(context, " }"); | 428 | try output(context, " }"); |
| 431 | }, | 429 | }, |
src-self-hosted/ir.zig+1-1| ... | @@ -1803,7 +1803,7 @@ pub const Builder = struct { | ... | @@ -1803,7 +1803,7 @@ pub const Builder = struct { |
| 1803 | 1803 | ||
| 1804 | // Look at the params and ref() other instructions | 1804 | // Look at the params and ref() other instructions |
| 1805 | inline for (@typeInfo(I.Params).Struct.fields) |f| { | 1805 | inline for (@typeInfo(I.Params).Struct.fields) |f| { |
| 1806 | switch (f.fiedl_type) { | 1806 | switch (f.field_type) { |
| 1807 | *Inst => @field(inst.params, f.name).ref(self), | 1807 | *Inst => @field(inst.params, f.name).ref(self), |
| 1808 | *BasicBlock => @field(inst.params, f.name).ref(self), | 1808 | *BasicBlock => @field(inst.params, f.name).ref(self), |
| 1809 | ?*Inst => if (@field(inst.params, f.name)) |other| other.ref(self), | 1809 | ?*Inst => if (@field(inst.params, f.name)) |other| other.ref(self), |
src/codegen.cpp+26-7| ... | @@ -3329,11 +3329,24 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutableGen *executabl | ... | @@ -3329,11 +3329,24 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutableGen *executabl |
| 3329 | LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue"); | 3329 | LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue"); |
| 3330 | size_t field_count = wanted_type->data.enumeration.src_field_count; | 3330 | size_t field_count = wanted_type->data.enumeration.src_field_count; |
| 3331 | LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count); | 3331 | LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count); |
| 3332 | |||
| 3333 | HashMap<BigInt, Buf *, bigint_hash, bigint_eql> occupied_tag_values = {}; | ||
| 3334 | occupied_tag_values.init(field_count); | ||
| 3335 | |||
| 3332 | for (size_t field_i = 0; field_i < field_count; field_i += 1) { | 3336 | for (size_t field_i = 0; field_i < field_count; field_i += 1) { |
| 3337 | TypeEnumField *type_enum_field = &wanted_type->data.enumeration.fields[field_i]; | ||
| 3338 | |||
| 3339 | Buf *name = type_enum_field->name; | ||
| 3340 | auto entry = occupied_tag_values.put_unique(type_enum_field->value, name); | ||
| 3341 | if (entry != nullptr) { | ||
| 3342 | continue; | ||
| 3343 | } | ||
| 3344 | |||
| 3333 | LLVMValueRef this_tag_int_value = bigint_to_llvm_const(get_llvm_type(g, tag_int_type), | 3345 | LLVMValueRef this_tag_int_value = bigint_to_llvm_const(get_llvm_type(g, tag_int_type), |
| 3334 | &wanted_type->data.enumeration.fields[field_i].value); | 3346 | &type_enum_field->value); |
| 3335 | LLVMAddCase(switch_instr, this_tag_int_value, ok_value_block); | 3347 | LLVMAddCase(switch_instr, this_tag_int_value, ok_value_block); |
| 3336 | } | 3348 | } |
| 3349 | occupied_tag_values.deinit(); | ||
| 3337 | LLVMPositionBuilderAtEnd(g->builder, bad_value_block); | 3350 | LLVMPositionBuilderAtEnd(g->builder, bad_value_block); |
| 3338 | gen_safety_crash(g, PanicMsgIdBadEnumValue); | 3351 | gen_safety_crash(g, PanicMsgIdBadEnumValue); |
| 3339 | 3352 | ||
| ... | @@ -5031,8 +5044,18 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) { | ... | @@ -5031,8 +5044,18 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) { |
| 5031 | LLVMConstNull(usize->llvm_type), | 5044 | LLVMConstNull(usize->llvm_type), |
| 5032 | }; | 5045 | }; |
| 5033 | 5046 | ||
| 5047 | HashMap<BigInt, Buf *, bigint_hash, bigint_eql> occupied_tag_values = {}; | ||
| 5048 | occupied_tag_values.init(field_count); | ||
| 5049 | |||
| 5034 | for (size_t field_i = 0; field_i < field_count; field_i += 1) { | 5050 | for (size_t field_i = 0; field_i < field_count; field_i += 1) { |
| 5035 | Buf *name = enum_type->data.enumeration.fields[field_i].name; | 5051 | TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; |
| 5052 | |||
| 5053 | Buf *name = type_enum_field->name; | ||
| 5054 | auto entry = occupied_tag_values.put_unique(type_enum_field->value, name); | ||
| 5055 | if (entry != nullptr) { | ||
| 5056 | continue; | ||
| 5057 | } | ||
| 5058 | |||
| 5036 | LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true); | 5059 | LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true); |
| 5037 | LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), ""); | 5060 | LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), ""); |
| 5038 | LLVMSetInitializer(str_global, str_init); | 5061 | LLVMSetInitializer(str_global, str_init); |
| ... | @@ -5062,6 +5085,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) { | ... | @@ -5062,6 +5085,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) { |
| 5062 | LLVMPositionBuilderAtEnd(g->builder, return_block); | 5085 | LLVMPositionBuilderAtEnd(g->builder, return_block); |
| 5063 | LLVMBuildRet(g->builder, slice_global); | 5086 | LLVMBuildRet(g->builder, slice_global); |
| 5064 | } | 5087 | } |
| 5088 | occupied_tag_values.deinit(); | ||
| 5065 | 5089 | ||
| 5066 | LLVMPositionBuilderAtEnd(g->builder, bad_value_block); | 5090 | LLVMPositionBuilderAtEnd(g->builder, bad_value_block); |
| 5067 | if (g->build_mode == BuildModeDebug || g->build_mode == BuildModeSafeRelease) { | 5091 | if (g->build_mode == BuildModeDebug || g->build_mode == BuildModeSafeRelease) { |
| ... | @@ -5086,11 +5110,6 @@ static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutableGen *executa | ... | @@ -5086,11 +5110,6 @@ static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutableGen *executa |
| 5086 | { | 5110 | { |
| 5087 | ZigType *enum_type = instruction->target->value->type; | 5111 | ZigType *enum_type = instruction->target->value->type; |
| 5088 | assert(enum_type->id == ZigTypeIdEnum); | 5112 | assert(enum_type->id == ZigTypeIdEnum); |
| 5089 | if (enum_type->data.enumeration.non_exhaustive) { | ||
| 5090 | add_node_error(g, instruction->base.base.source_node, | ||
| 5091 | buf_sprintf("TODO @tagName on non-exhaustive enum https://github.com/ziglang/zig/issues/3991")); | ||
| 5092 | codegen_report_errors_and_exit(g); | ||
| 5093 | } | ||
| 5094 | 5113 | ||
| 5095 | LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type); | 5114 | LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type); |
| 5096 | 5115 |
src/ir.cpp+7-4| ... | @@ -23190,12 +23190,15 @@ static IrInstGen *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstSrc | ... | @@ -23190,12 +23190,15 @@ static IrInstGen *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstSrc |
| 23190 | if (instr_is_comptime(target)) { | 23190 | if (instr_is_comptime(target)) { |
| 23191 | if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown))) | 23191 | if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown))) |
| 23192 | return ira->codegen->invalid_inst_gen; | 23192 | return ira->codegen->invalid_inst_gen; |
| 23193 | if (target->value->type->data.enumeration.non_exhaustive) { | 23193 | TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint); |
| 23194 | ir_add_error(ira, &instruction->base.base, | 23194 | if (field == nullptr) { |
| 23195 | buf_sprintf("TODO @tagName on non-exhaustive enum https://github.com/ziglang/zig/issues/3991")); | 23195 | Buf *int_buf = buf_alloc(); |
| 23196 | bigint_append_buf(int_buf, &target->value->data.x_bigint, 10); | ||
| 23197 | |||
| 23198 | ir_add_error(ira, &target->base, | ||
| 23199 | buf_sprintf("no tag by value %s", buf_ptr(int_buf))); | ||
| 23196 | return ira->codegen->invalid_inst_gen; | 23200 | return ira->codegen->invalid_inst_gen; |
| 23197 | } | 23201 | } |
| 23198 | TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint); | ||
| 23199 | ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee; | 23202 | ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee; |
| 23200 | IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); | 23203 | IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); |
| 23201 | init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(field->name), true); | 23204 | init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(field->name), true); |
test/compile_errors.zig+9| ... | @@ -3,6 +3,15 @@ const builtin = @import("builtin"); | ... | @@ -3,6 +3,15 @@ const builtin = @import("builtin"); |
| 3 | const Target = @import("std").Target; | 3 | const Target = @import("std").Target; |
| 4 | 4 | ||
| 5 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 5 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 6 | cases.addTest("@tagName on invalid value of non-exhaustive enum", | ||
| 7 | \\test "enum" { | ||
| 8 | \\ const E = enum(u8) {A, B, _}; | ||
| 9 | \\ _ = @tagName(@intToEnum(E, 5)); | ||
| 10 | \\} | ||
| 11 | , &[_][]const u8{ | ||
| 12 | "tmp.zig:3:18: error: no tag by value 5", | ||
| 13 | }); | ||
| 14 | |||
| 6 | cases.addTest("@ptrToInt with pointer to zero-sized type", | 15 | cases.addTest("@ptrToInt with pointer to zero-sized type", |
| 7 | \\export fn entry() void { | 16 | \\export fn entry() void { |
| 8 | \\ var pointer: ?*u0 = null; | 17 | \\ var pointer: ?*u0 = null; |
test/stage1/behavior/cast.zig+11| ... | @@ -491,6 +491,17 @@ test "@intToEnum passed a comptime_int to an enum with one item" { | ... | @@ -491,6 +491,17 @@ test "@intToEnum passed a comptime_int to an enum with one item" { |
| 491 | expect(x == E.A); | 491 | expect(x == E.A); |
| 492 | } | 492 | } |
| 493 | 493 | ||
| 494 | test "@intToEnum runtime to an extern enum with duplicate values" { | ||
| 495 | const E = extern enum(u8) { | ||
| 496 | A = 1, | ||
| 497 | B = 1, | ||
| 498 | }; | ||
| 499 | var a: u8 = 1; | ||
| 500 | var x = @intToEnum(E, a); | ||
| 501 | expect(x == E.A); | ||
| 502 | expect(x == E.B); | ||
| 503 | } | ||
| 504 | |||
| 494 | test "@intCast to u0 and use the result" { | 505 | test "@intCast to u0 and use the result" { |
| 495 | const S = struct { | 506 | const S = struct { |
| 496 | fn doTheTest(zero: u1, one: u1, bigzero: i32) void { | 507 | fn doTheTest(zero: u1, one: u1, bigzero: i32) void { |
test/stage1/behavior/enum.zig+22-1| ... | @@ -198,7 +198,17 @@ test "@tagName" { | ... | @@ -198,7 +198,17 @@ test "@tagName" { |
| 198 | comptime expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); | 198 | comptime expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); |
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | fn testEnumTagNameBare(n: BareNumber) []const u8 { | 201 | test "@tagName extern enum with duplicates" { |
| 202 | expect(mem.eql(u8, testEnumTagNameBare(ExternDuplicates.B), "A")); | ||
| 203 | comptime expect(mem.eql(u8, testEnumTagNameBare(ExternDuplicates.B), "A")); | ||
| 204 | } | ||
| 205 | |||
| 206 | test "@tagName non-exhaustive enum" { | ||
| 207 | expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B")); | ||
| 208 | comptime expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B")); | ||
| 209 | } | ||
| 210 | |||
| 211 | fn testEnumTagNameBare(n: var) []const u8 { | ||
| 202 | return @tagName(n); | 212 | return @tagName(n); |
| 203 | } | 213 | } |
| 204 | 214 | ||
| ... | @@ -208,6 +218,17 @@ const BareNumber = enum { | ... | @@ -208,6 +218,17 @@ const BareNumber = enum { |
| 208 | Three, | 218 | Three, |
| 209 | }; | 219 | }; |
| 210 | 220 | ||
| 221 | const ExternDuplicates = extern enum(u8) { | ||
| 222 | A = 1, | ||
| 223 | B = 1, | ||
| 224 | }; | ||
| 225 | |||
| 226 | const NonExhaustive = enum(u8) { | ||
| 227 | A, | ||
| 228 | B, | ||
| 229 | _, | ||
| 230 | }; | ||
| 231 | |||
| 211 | test "enum alignment" { | 232 | test "enum alignment" { |
| 212 | comptime { | 233 | comptime { |
| 213 | expect(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); | 234 | expect(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); |