From 95790ad3687223ec001c7fc870b259685f816803 Mon Sep 17 00:00:00 2001 From: Vexu Date: Sun, 15 Dec 2019 00:05:13 +0200 Subject: [PATCH] improve extern enum --- src-self-hosted/translate_c.zig | 3 +++ src/analyze.cpp | 2 +- src/codegen.cpp | 2 +- src/ir.cpp | 6 +++++- test/compile_errors.zig | 19 +++++++++++++++++++ test/stage1/behavior/cast.zig | 1 + test/stage1/behavior/enum.zig | 27 +++++++++++++++++++++++++++ 7 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 4478233270dd..560e60fe9778 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -275,6 +275,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { .PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern", .{}), .Auto => unreachable, // Not legal on functions .Register => unreachable, // Not legal on functions + else => unreachable, }, }; const proto_node = switch (ZigClangType_getTypeClass(fn_type)) { @@ -702,6 +703,7 @@ fn transBinaryOperator( .XorAssign, .OrAssign, => unreachable, + else => unreachable, } } @@ -995,6 +997,7 @@ fn transStringLiteral( "TODO: support string literal kind {}", .{kind}, ), + else => unreachable, } } diff --git a/src/analyze.cpp b/src/analyze.cpp index 35a4bd7d4a6c..ba3554fc1748 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2682,7 +2682,7 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { // Make sure the value is unique auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node); - if (entry != nullptr) { + if (entry != nullptr && enum_type->data.enumeration.layout != ContainerLayoutExtern) { enum_type->data.enumeration.resolve_status = ResolveStatusInvalid; Buf *val_buf = buf_alloc(); diff --git a/src/codegen.cpp b/src/codegen.cpp index cad67e378899..d371eca632d9 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3271,7 +3271,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable, LLVMValueRef tag_int_value = gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), instruction->target->value->type, tag_int_type, target_val); - if (ir_want_runtime_safety(g, &instruction->base)) { + if (ir_want_runtime_safety(g, &instruction->base) && wanted_type->data.enumeration.layout != ContainerLayoutExtern) { LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue"); LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue"); size_t field_count = wanted_type->data.enumeration.src_field_count; diff --git a/src/ir.cpp b/src/ir.cpp index da208b62c665..0240f296e776 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12728,7 +12728,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour return ira->codegen->invalid_instruction; TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint); - if (field == nullptr) { + if (field == nullptr && wanted_type->data.enumeration.layout != ContainerLayoutExtern) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &val->data.x_bigint, 10); ErrorMsg *msg = ir_add_error(ira, source_instr, @@ -25791,6 +25791,10 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, } } if (!instruction->have_else_prong) { + if (switch_type->data.enumeration.layout == ContainerLayoutExtern) { + ir_add_error(ira, &instruction->base, + buf_sprintf("switch on an extern enum must have an else prong")); + } for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index d2f5082d7451..7185f93febe5 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,6 +2,25 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add("switch on extern enum missing else prong", + \\const i = extern enum { + \\ n = 0, + \\ o = 2, + \\ p = 4, + \\ q = 4, + \\}; + \\pub fn main() void { + \\ var x = @intToEnum(i, 52); + \\ switch (x) { + \\ .n, + \\ .o, + \\ .p => unreachable, + \\ } + \\} + , &[_][]const u8{ + "tmp.zig:9:5: error: switch on an extern enum must have an else prong", + }); + cases.add("invalid float literal", \\const std = @import("std"); \\ diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig index 72b74bc643f4..aca8c6fc3432 100644 --- a/test/stage1/behavior/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -618,6 +618,7 @@ test "peer resolution of string literals" { .b => "two", .c => "three", .d => "four", + else => unreachable, }; expect(mem.eql(u8, cmd, "two")); } diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig index f20523487d7f..d5bb2ddbb2ef 100644 --- a/test/stage1/behavior/enum.zig +++ b/test/stage1/behavior/enum.zig @@ -1,6 +1,33 @@ const expect = @import("std").testing.expect; const mem = @import("std").mem; +test "extern enum" { + const S = struct { + const i = extern enum { + n = 0, + o = 2, + p = 4, + q = 4, + }; + fn doTheTest(y: c_int) void { + var x = i.o; + expect(@enumToInt(x) == 2); + x = @intToEnum(i, 12); + expect(@enumToInt(x) == 12); + x = @intToEnum(i, y); + expect(@enumToInt(x) == 52); + switch (x) { + .n, + .o, + .p => unreachable, + else => {}, + } + } + }; + S.doTheTest(52); + comptime S.doTheTest(52); +} + test "enum type" { const foo1 = Foo{ .One = 13 }; const foo2 = Foo{