Commit 0f38410ea6

Vexu <git@vexu.eu>
2019-12-14 23:05:13
improve extern enum
1 parent 59de23d
Changed files (7)
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();
src/codegen.cpp
@@ -3292,7 +3292,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;
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];
 
src-self-hosted/translate_c.zig
@@ -282,6 +282,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)) {
@@ -710,6 +711,7 @@ fn transBinaryOperator(
         .XorAssign,
         .OrAssign,
         => unreachable,
+        else => unreachable,
     }
 }
 
@@ -1001,6 +1003,7 @@ fn transStringLiteral(
             "TODO: support string literal kind {}",
             .{kind},
         ),
+        else => unreachable,
     }
 }
 
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"));
         }
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{
test/compile_errors.zig
@@ -10,6 +10,25 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
         "tmp.zig:2:13: error: pointer type '[*]align(4) u8' requires aligned address",
     });
 
+    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");
         \\