Skip to content

Commit

Permalink
fix generation of error defers for fns inside fns
Browse files Browse the repository at this point in the history
closes #878
  • Loading branch information
andrewrk committed Jul 19, 2018
1 parent 0736e6a commit 0a880d5
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 25 deletions.
84 changes: 59 additions & 25 deletions src/ir.cpp
Expand Up @@ -2961,16 +2961,34 @@ static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_sco
results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0;

while (inner_scope != outer_scope) {
assert(inner_scope);
if (inner_scope->id == ScopeIdDefer) {
AstNode *defer_node = inner_scope->source_node;
assert(defer_node->type == NodeTypeDefer);
ReturnKind defer_kind = defer_node->data.defer.kind;
results[defer_kind] += 1;
Scope *scope = inner_scope;

while (scope != outer_scope) {
assert(scope);
switch (scope->id) {
case ScopeIdDefer: {
AstNode *defer_node = scope->source_node;
assert(defer_node->type == NodeTypeDefer);
ReturnKind defer_kind = defer_node->data.defer.kind;
results[defer_kind] += 1;
scope = scope->parent;
continue;
}
case ScopeIdDecls:
case ScopeIdFnDef:
return;
case ScopeIdBlock:
case ScopeIdVarDecl:
case ScopeIdLoop:
case ScopeIdSuspend:
case ScopeIdCompTime:
scope = scope->parent;
continue;
case ScopeIdDeferExpr:
case ScopeIdCImport:
case ScopeIdCoroPrelude:
zig_unreachable();
}
inner_scope = inner_scope->parent;
}
}

Expand All @@ -2986,27 +3004,43 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o
if (!scope)
return is_noreturn;

if (scope->id == ScopeIdDefer) {
AstNode *defer_node = scope->source_node;
assert(defer_node->type == NodeTypeDefer);
ReturnKind defer_kind = defer_node->data.defer.kind;
if (defer_kind == ReturnKindUnconditional ||
(gen_error_defers && defer_kind == ReturnKindError))
{
AstNode *defer_expr_node = defer_node->data.defer.expr;
Scope *defer_expr_scope = defer_node->data.defer.expr_scope;
IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);
if (defer_expr_value != irb->codegen->invalid_instruction) {
if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) {
is_noreturn = true;
} else {
ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value));
switch (scope->id) {
case ScopeIdDefer: {
AstNode *defer_node = scope->source_node;
assert(defer_node->type == NodeTypeDefer);
ReturnKind defer_kind = defer_node->data.defer.kind;
if (defer_kind == ReturnKindUnconditional ||
(gen_error_defers && defer_kind == ReturnKindError))
{
AstNode *defer_expr_node = defer_node->data.defer.expr;
Scope *defer_expr_scope = defer_node->data.defer.expr_scope;
IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);
if (defer_expr_value != irb->codegen->invalid_instruction) {
if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) {
is_noreturn = true;
} else {
ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value));
}
}
}
scope = scope->parent;
continue;
}

case ScopeIdDecls:
case ScopeIdFnDef:
return is_noreturn;
case ScopeIdBlock:
case ScopeIdVarDecl:
case ScopeIdLoop:
case ScopeIdSuspend:
case ScopeIdCompTime:
scope = scope->parent;
continue;
case ScopeIdDeferExpr:
case ScopeIdCImport:
case ScopeIdCoroPrelude:
zig_unreachable();
}
scope = scope->parent;
}
return is_noreturn;
}
Expand Down
15 changes: 15 additions & 0 deletions test/cases/defer.zig
Expand Up @@ -61,3 +61,18 @@ test "defer and labeled break" {

assert(i == 1);
}

test "errdefer does not apply to fn inside fn" {
if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assert(e == error.Bad);
}

fn testNestedFnErrDefer() error!void {
var a: i32 = 0;
errdefer a += 1;
const S = struct {
fn baz() error {
return error.Bad;
}
};
return S.baz();
}

0 comments on commit 0a880d5

Please sign in to comment.