Commit e7bf8f3f04

Andrew Kelley <superjoe30@gmail.com>
2018-02-09 19:49:58
fix compiler crash switching on global error with no else
1 parent 1fb308c
Changed files (4)
src/ir.cpp
@@ -15663,13 +15663,19 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira
             field_prev_uses[start_index] = start_value->source_node;
         }
         if (!instruction->have_else_prong) {
-            for (uint32_t i = 0; i < switch_type->data.error_set.err_count; i += 1) {
-                ErrorTableEntry *err_entry = switch_type->data.error_set.errors[i];
+            if (type_is_global_error_set(switch_type)) {
+                ir_add_error(ira, &instruction->base,
+                    buf_sprintf("else prong required when switching on type 'error'"));
+                return ira->codegen->builtin_types.entry_invalid;
+            } else {
+                for (uint32_t i = 0; i < switch_type->data.error_set.err_count; i += 1) {
+                    ErrorTableEntry *err_entry = switch_type->data.error_set.errors[i];
 
-                AstNode *prev_node = field_prev_uses[err_entry->value];
-                if (prev_node == nullptr) {
-                    ir_add_error(ira, &instruction->base,
-                        buf_sprintf("error.%s not handled in switch", buf_ptr(&err_entry->name)));
+                    AstNode *prev_node = field_prev_uses[err_entry->value];
+                    if (prev_node == nullptr) {
+                        ir_add_error(ira, &instruction->base,
+                            buf_sprintf("error.%s not handled in switch", buf_ptr(&err_entry->name)));
+                    }
                 }
             }
         }
std/zig/parser.zig
@@ -133,7 +133,6 @@ pub const Parser = struct {
                         Token.Id.Eof => return Tree {.root_node = root_node},
                         else => {
                             self.putBackToken(token);
-                            // TODO shouldn't need this cast
                             stack.append(State { .TopLevelExtern = null }) catch unreachable;
                             continue;
                         },
@@ -707,7 +706,7 @@ pub const Parser = struct {
         return node;
     }
 
-    fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) error {
+    fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) {
         const loc = self.tokenizer.getTokenLocation(token);
         warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args);
         warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
@@ -1082,16 +1081,18 @@ fn testCanonical(source: []const u8) !void {
         var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
         if (testParse(source, &failing_allocator.allocator)) |_| {
             return error.NondeterministicMemoryUsage;
-        } else |err| {
-            assert(err == error.OutOfMemory);
-            // TODO make this pass
-            //if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
-            //    warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
-            //        fail_index, needed_alloc_count,
-            //        failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
-            //        failing_allocator.index, failing_allocator.deallocations);
-            //    return error.MemoryLeakDetected;
-            //}
+        } else |err| switch (err) {
+            error.OutOfMemory => {
+                // TODO make this pass
+                //if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
+                //    warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
+                //        fail_index, needed_alloc_count,
+                //        failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
+                //        failing_allocator.index, failing_allocator.deallocations);
+                //    return error.MemoryLeakDetected;
+                //}
+            },
+            error.ParseError => @panic("test failed"),
         }
     }
 }
std/io.zig
@@ -499,7 +499,7 @@ pub fn OutStream(comptime Error: type) type {
         writeFn: fn(self: &Self, bytes: []const u8) Error!void,
 
         pub fn print(self: &Self, comptime format: []const u8, args: ...) !void {
-            return std.fmt.format(self, error, self.writeFn, format, args);
+            return std.fmt.format(self, Error, self.writeFn, format, args);
         }
 
         pub fn write(self: &Self, bytes: []const u8) !void {
test/compile_errors.zig
@@ -1,6 +1,18 @@
 const tests = @import("tests.zig");
 
 pub fn addCases(cases: &tests.CompileErrorContext) void {
+    cases.add("no else prong on switch on global error set",
+        \\export fn entry() void {
+        \\    foo(error.A);
+        \\}
+        \\fn foo(a: error) void {
+        \\    switch (a) {
+        \\        error.A => {},
+        \\    }
+        \\}
+    ,
+        ".tmp_source.zig:5:5: error: else prong required when switching on type 'error'");
+
     cases.add("inferred error set with no returned error",
         \\export fn entry() void {
         \\    foo() catch unreachable;