fix compiler crash switching on global error with no else
This commit is contained in:
parent
1fb308ceee
commit
e7bf8f3f04
18
src/ir.cpp
18
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;
|
field_prev_uses[start_index] = start_value->source_node;
|
||||||
}
|
}
|
||||||
if (!instruction->have_else_prong) {
|
if (!instruction->have_else_prong) {
|
||||||
for (uint32_t i = 0; i < switch_type->data.error_set.err_count; i += 1) {
|
if (type_is_global_error_set(switch_type)) {
|
||||||
ErrorTableEntry *err_entry = switch_type->data.error_set.errors[i];
|
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];
|
AstNode *prev_node = field_prev_uses[err_entry->value];
|
||||||
if (prev_node == nullptr) {
|
if (prev_node == nullptr) {
|
||||||
ir_add_error(ira, &instruction->base,
|
ir_add_error(ira, &instruction->base,
|
||||||
buf_sprintf("error.%s not handled in switch", buf_ptr(&err_entry->name)));
|
buf_sprintf("error.%s not handled in switch", buf_ptr(&err_entry->name)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -499,7 +499,7 @@ pub fn OutStream(comptime Error: type) type {
|
|||||||
writeFn: fn(self: &Self, bytes: []const u8) Error!void,
|
writeFn: fn(self: &Self, bytes: []const u8) Error!void,
|
||||||
|
|
||||||
pub fn print(self: &Self, comptime format: []const u8, args: ...) !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 {
|
pub fn write(self: &Self, bytes: []const u8) !void {
|
||||||
|
@ -133,7 +133,6 @@ pub const Parser = struct {
|
|||||||
Token.Id.Eof => return Tree {.root_node = root_node},
|
Token.Id.Eof => return Tree {.root_node = root_node},
|
||||||
else => {
|
else => {
|
||||||
self.putBackToken(token);
|
self.putBackToken(token);
|
||||||
// TODO shouldn't need this cast
|
|
||||||
stack.append(State { .TopLevelExtern = null }) catch unreachable;
|
stack.append(State { .TopLevelExtern = null }) catch unreachable;
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
@ -707,7 +706,7 @@ pub const Parser = struct {
|
|||||||
return node;
|
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);
|
const loc = self.tokenizer.getTokenLocation(token);
|
||||||
warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args);
|
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]);
|
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);
|
var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
|
||||||
if (testParse(source, &failing_allocator.allocator)) |_| {
|
if (testParse(source, &failing_allocator.allocator)) |_| {
|
||||||
return error.NondeterministicMemoryUsage;
|
return error.NondeterministicMemoryUsage;
|
||||||
} else |err| {
|
} else |err| switch (err) {
|
||||||
assert(err == error.OutOfMemory);
|
error.OutOfMemory => {
|
||||||
// TODO make this pass
|
// TODO make this pass
|
||||||
//if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
|
//if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
|
||||||
// warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
|
// warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
|
||||||
// fail_index, needed_alloc_count,
|
// fail_index, needed_alloc_count,
|
||||||
// failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
|
// failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
|
||||||
// failing_allocator.index, failing_allocator.deallocations);
|
// failing_allocator.index, failing_allocator.deallocations);
|
||||||
// return error.MemoryLeakDetected;
|
// return error.MemoryLeakDetected;
|
||||||
//}
|
//}
|
||||||
|
},
|
||||||
|
error.ParseError => @panic("test failed"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,18 @@
|
|||||||
const tests = @import("tests.zig");
|
const tests = @import("tests.zig");
|
||||||
|
|
||||||
pub fn addCases(cases: &tests.CompileErrorContext) void {
|
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",
|
cases.add("inferred error set with no returned error",
|
||||||
\\export fn entry() void {
|
\\export fn entry() void {
|
||||||
\\ foo() catch unreachable;
|
\\ foo() catch unreachable;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user