Simplified defer and now also allow nested defers.

This commit is contained in:
Christoffer Lerno
2020-07-27 11:23:58 +02:00
committed by Christoffer Lerno
parent a533cbb1a7
commit c839eb50c8
6 changed files with 123 additions and 33 deletions

View File

@@ -0,0 +1,46 @@
extern func void printf(char* message, ...);
public func void defer1() {}
public func void defer2() {}
public func void defer3() {}
public func void defer4() {}
public func void defer5() {}
public func void defer6() {}
public func void defer7() {}
public func void defer8() {}
public func void defer9() {}
public func void defer10() {}
public func void defer11() {}
public func int main(int argc)
{
int a = 0;
{
defer
{
if (a == 1) break;
defer1();
}
defer2();
}
defer defer3();
while (a)
{
defer defer4();
if (argc == 1) break;
defer defer5();
defer6();
}
defer defer7();
while (a)
{
defer defer8();
if (argc == 1) break;
defer defer9();
defer10();
break;
defer11();
}
return 0;
}

View File

@@ -953,8 +953,8 @@ typedef struct _DynamicScope
bool jump_end : 1;
ScopeFlags flags;
Decl **local_decl_start;
DeferList defers;
Ast *current_defer;
AstId defer_last;
Ast *in_defer;
} DynamicScope;

View File

@@ -2804,7 +2804,7 @@ static inline bool sema_expr_analyse_guard(Context *context, Type *to, Expr *exp
{
Expr *inner = expr->guard_expr.inner;
bool success = sema_analyse_expr(context, to, inner);
expr->guard_expr.defer = context->current_scope->defers.end;
expr->guard_expr.defer = context->current_scope->defer_last;
if (!success) return false;
expr->type = inner->type;
if (!inner->failable)

View File

@@ -11,11 +11,12 @@ int sema_check_comp_time_bool(Context *context, Expr *expr);
bool sema_analyse_function_body(Context *context, Decl *func);
void context_pop_scope(Context *context);
void context_push_scope_with_flags(Context *context, ScopeFlags flags);
AstId context_start_defer(Context *context);
static inline void context_push_scope(Context *context)
{
context_push_scope_with_flags(context, SCOPE_NONE);
}
#define PUSH_X(ast, X) AstId _old_##X##_defer = context->X##_defer; AstId _old_##X = context->X##_target; context->X##_target = ast ? astid(ast) : 0; context->X##_defer = context->current_scope->defers.start
#define PUSH_X(ast, X) AstId _old_##X##_defer = context->X##_defer; AstId _old_##X = context->X##_target; context->X##_target = ast ? astid(ast) : 0; context->X##_defer = context->current_scope->defer_last
#define POP_X(X) context->X##_target = _old_##X; context->X##_defer = _old_##X##_defer
#define PUSH_CONTINUE(ast) PUSH_X(ast, continue)
#define POP_CONTINUE() POP_X(continue)

View File

@@ -14,8 +14,8 @@ void context_push_scope_with_flags(Context *context, ScopeFlags flags)
FATAL_ERROR("Too deeply nested scopes.");
}
ScopeFlags previous_flags = context->current_scope->flags;
Ast *previous_defer = context->current_scope->current_defer;
AstId parent_defer = context->current_scope->defers.start;
Ast *previous_defer = context->current_scope->in_defer;
AstId parent_defer = context->current_scope->defer_last;
context->current_scope++;
context->current_scope->scope_id = ++context->scope_id;
context->current_scope->allow_dead_code = false;
@@ -25,9 +25,8 @@ void context_push_scope_with_flags(Context *context, ScopeFlags flags)
FATAL_ERROR("Too many scopes.");
}
context->current_scope->local_decl_start = context->last_local;
context->current_scope->current_defer = previous_defer;
context->current_scope->defers.start = parent_defer;
context->current_scope->defers.end = parent_defer;
context->current_scope->in_defer = previous_defer;
context->current_scope->defer_last = parent_defer;
if (flags & (SCOPE_DEFER | SCOPE_EXPR_BLOCK))
{
@@ -49,22 +48,19 @@ void context_push_scope_with_label(Context *context, Decl *label)
if (label)
{
label->label.defer = context->current_scope->defers.end;
label->label.defer = context->current_scope->defer_last;
sema_add_local(context, label);
label->label.scope_id = context->current_scope->scope_id;
}
}
static inline void context_pop_defers(Context *context)
{
context->current_scope->defers.start = context->current_scope->defers.end;
}
static inline void context_pop_defers_to(Context *context, DeferList *list)
{
*list = context->current_scope->defers;
context_pop_defers(context);
list->end = context_start_defer(context);
list->start = context->current_scope->defer_last;
context->current_scope->defer_last = list->end;
}
@@ -73,7 +69,9 @@ void context_pop_scope(Context *context)
{
assert(context->current_scope != &context->scopes[0]);
context->last_local = context->current_scope->local_decl_start;
assert (context->current_scope->defers.end == context->current_scope->defers.start);
assert(context_start_defer(context) == context->current_scope->defer_last);
context->current_scope--;
}
@@ -108,6 +106,13 @@ static void context_pop_defers_and_replace_ast(Context *context, Ast *ast)
ast->scoped_stmt.defers = defers;
}
AstId context_start_defer(Context *context)
{
if (context->current_scope == context->scopes) return 0;
if (context->current_scope->in_defer != context->current_scope[-1].in_defer) return 0;
return context->current_scope[-1].defer_last;
}
#pragma mark --- Helper functions
@@ -140,7 +145,7 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement)
context->current_scope->jump_end = true;
Type *expected_rtype = context->rtype;
Expr *return_expr = statement->return_stmt.expr;
statement->return_stmt.defer = context->current_scope->defers.start;
statement->return_stmt.defer = context->current_scope->defer_last;
if (return_expr == NULL)
{
if (!expected_rtype)
@@ -362,9 +367,9 @@ static inline bool sema_analyse_defer_stmt(Context *context, Ast *statement)
{
// TODO special parsing of "catch"
context_push_scope_with_flags(context, SCOPE_DEFER); // NOLINT(hicpp-signed-bitwise)
context->current_scope->defers.start = 0;
context->current_scope->defers.end = 0;
context->current_scope->current_defer = statement;
context->current_scope->defer_last = 0;
context->current_scope->in_defer = statement;
PUSH_CONTINUE(NULL);
PUSH_BREAK(statement);
@@ -378,12 +383,14 @@ static inline bool sema_analyse_defer_stmt(Context *context, Ast *statement)
POP_BREAKCONT();
POP_NEXT();
context_pop_defers_and_replace_ast(context, statement->defer_stmt.body);
context_pop_scope(context);
if (!success) return false;
statement->defer_stmt.prev_defer = context->current_scope->defers.start;
context->current_scope->defers.start = astid(statement);
statement->defer_stmt.prev_defer = context->current_scope->defer_last;
context->current_scope->defer_last = astid(statement);
return true;
}
@@ -543,10 +550,10 @@ static inline Decl *sema_analyse_label(Context *context, Ast *stmt)
SEMA_TOKID_ERROR(stmt->contbreak_stmt.label.span, "Expected the name to match a label, not a constant.");
return poisoned_decl;
}
if (context->current_scope->current_defer)
if (context->current_scope->in_defer)
{
DynamicScope *scope = context_find_scope_by_id(context, target->label.scope_id);
if (scope->current_defer != context->current_scope->current_defer)
if (scope->in_defer != context->current_scope->in_defer)
{
SEMA_ERROR(stmt, stmt->ast_kind == AST_BREAK_STMT ? "You cannot break out of a defer." : "You cannot use continue out of a defer.");
return poisoned_decl;
@@ -580,7 +587,7 @@ static bool sema_analyse_break_stmt(Context *context, Ast *statement)
return false;
}
statement->contbreak_stmt.defers.start = context->current_scope->defers.start;
statement->contbreak_stmt.defers.start = context->current_scope->defer_last;
if (statement->contbreak_stmt.label.name)
{
Decl *target = sema_analyse_label(context, statement);
@@ -632,11 +639,11 @@ static bool sema_analyse_next_stmt(Context *context, Ast *statement)
bool defer_mismatch = false;
if (parent->ast_kind == AST_SWITCH_STMT)
{
defer_mismatch = context->current_scope->current_defer != context_find_scope_by_id(context, parent->switch_stmt.scope_id)->current_defer;
defer_mismatch = context->current_scope->in_defer != context_find_scope_by_id(context, parent->switch_stmt.scope_id)->in_defer;
}
else
{
defer_mismatch = context->current_scope->current_defer != context_find_scope_by_id(context, parent->catch_stmt.scope_id)->current_defer;
defer_mismatch = context->current_scope->in_defer != context_find_scope_by_id(context, parent->catch_stmt.scope_id)->in_defer;
}
if (defer_mismatch)
{
@@ -646,7 +653,7 @@ static bool sema_analyse_next_stmt(Context *context, Ast *statement)
assert(statement->next_stmt.target);
}
statement->next_stmt.defers.start = context->current_scope->defers.start;
statement->next_stmt.defers.start = context->current_scope->defer_last;
statement->next_stmt.defers.end = parent->switch_stmt.defer;
// Plain next.
if (!statement->next_stmt.target)
@@ -750,7 +757,7 @@ static bool sema_analyse_next_stmt(Context *context, Ast *statement)
static bool sema_analyse_continue_stmt(Context *context, Ast *statement)
{
context->current_scope->jump_end = true;
statement->contbreak_stmt.defers.start = context->current_scope->defers.start;
statement->contbreak_stmt.defers.start = context->current_scope->defer_last;
if (!context->continue_target && !statement->contbreak_stmt.label.name)
{
@@ -969,7 +976,7 @@ static bool sema_analyse_switch_body(Context *context, Ast *statement, SourceSpa
return false;
}
Ast *default_case = NULL;
assert(context->current_scope->defers.start == context->current_scope->defers.end);
assert(context_start_defer(context) == context->current_scope->defer_last);
bool exhaustive = false;
unsigned case_count = vec_size(cases);
@@ -1041,7 +1048,7 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement)
Type *switch_type = ast_cond_type(cond)->canonical;
statement->switch_stmt.defer = context->current_scope->defers.start;
statement->switch_stmt.defer = context->current_scope->defer_last;
bool success = sema_analyse_switch_body(context, statement, cond->span,
switch_type->canonical,
statement->switch_stmt.cases);
@@ -1068,7 +1075,7 @@ static bool sema_analyse_catch_stmt(Context *context, Ast *statement)
statement->catch_stmt.scope_id = context->current_scope->scope_id;
context_push_scope_with_label(context, statement->catch_stmt.flow.label);
statement->catch_stmt.defer = context->current_scope->defers.start;
statement->catch_stmt.defer = context->current_scope->defer_last;
if (catch_expr->expr_kind == EXPR_BINARY && catch_expr->binary_expr.operator == BINARYOP_ASSIGN)
{
Expr *left = catch_expr->binary_expr.left;

View File

@@ -0,0 +1,36 @@
func void test1() {}
func void test2() {}
func void test3() {}
func void test4() {}
func void test()
{
defer
{
defer test1();
test2();
defer test3();
}
test4();
}
func void test_line()
{
defer defer test1();
}
// #expect: defer_in_defer.ll
entry:
call void @defer_in_defer.test4()
call void @defer_in_defer.test2()
call void @defer_in_defer.test3()
br label %exit
exit:
call void @defer_in_defer.test1()
entry:
call void @defer_in_defer.test1()
br label %exit