From e986e3a8c0fcd6f9738b26a5d9308896486ce405 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 25 Jun 2025 15:08:57 +0200 Subject: [PATCH] Refactoring. --- src/compiler/compiler_internal.h | 16 +++--- src/compiler/sema_expr.c | 4 +- src/compiler/sema_internal.h | 19 +++---- src/compiler/sema_stmts.c | 89 +++++++++++++------------------- src/compiler/semantic_analyser.c | 4 +- 5 files changed, 62 insertions(+), 70 deletions(-) diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 19d82605a..64179db42 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1706,6 +1706,12 @@ typedef struct }; } CallEnv; +typedef struct JumpTarget_ +{ + Ast *target; + AstId defer; +} JumpTarget; + typedef struct InliningSpan_ { SourceSpan span; @@ -1723,14 +1729,12 @@ struct SemaContext_ InliningSpan *inlined_at; ScopeId scope_id; unsigned macro_call_depth; - Ast *break_target; - AstId break_defer; - Ast *continue_target; - AstId continue_defer; + // Jump tracking + JumpTarget break_jump; + JumpTarget continue_jump; + JumpTarget next_jump; AstId block_return_defer; - Ast *next_target; Ast *next_switch; - AstId next_defer; struct { uint32_t original_inline_line; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index d926ea3d0..87a63838a 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -8847,11 +8847,11 @@ static inline bool sema_expr_analyse_rethrow(SemaContext *context, Expr *expr) } vec_add(context->block_returns, NULL); expr->rethrow_expr.in_block = context->block_exit_ref; - expr->rethrow_expr.cleanup = context_get_defers(context, context->active_scope.defer_last, context->block_return_defer, false); + expr->rethrow_expr.cleanup = context_get_defers(context, context->block_return_defer, false); } else { - expr->rethrow_expr.cleanup = context_get_defers(context, context->active_scope.defer_last, 0, false); + expr->rethrow_expr.cleanup = context_get_defers(context, 0, false); expr->rethrow_expr.in_block = NULL; if (context->rtype && context->rtype->type_kind != TYPE_OPTIONAL) { diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index d369ca737..694609b93 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -30,14 +30,14 @@ #define SCOPE_END ASSERT(context->active_scope.defer_last == context->active_scope.defer_start); context->active_scope = old_scope; } while(0) #define SCOPE_POP_ERROR() ((bool)(context->active_scope = old_scope, false)) #define SCOPE_ERROR_END_OUTER() do { context->active_scope = stored_scope; } while(0) -#define PUSH_X(ast, X) AstId _old_##X##_defer = context->X##_defer; Ast *_old_##X = context->X##_target; context->X##_target = ast; context->X##_defer = context->active_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) -#define PUSH_BREAK(ast) PUSH_X(ast, break) -#define POP_BREAK() POP_X(break) -#define PUSH_NEXT(ast, sast) PUSH_X(ast, next); Ast *_old_next_switch = context->next_switch; context->next_switch = sast -#define POP_NEXT() POP_X(next); context->next_switch = _old_next_switch +#define PUSH_Y(ast, X) JumpTarget _old_##X = context->X##_jump; context->X##_jump = (JumpTarget) { ast, context->active_scope.defer_last } +#define POP_Y(X) context->X##_jump = _old_##X; +#define PUSH_CONTINUE(ast) PUSH_Y(ast, continue) +#define POP_CONTINUE() POP_Y(continue) +#define PUSH_BREAK(ast) PUSH_Y(ast, break) +#define POP_BREAK() POP_Y(break) +#define PUSH_NEXT(ast, sast) PUSH_Y(ast, next); Ast *_old_next_switch = context->next_switch; context->next_switch = sast +#define POP_NEXT() POP_Y(next); context->next_switch = _old_next_switch #define PUSH_BREAKCONT(ast) PUSH_CONTINUE(ast); PUSH_BREAK(ast) #define POP_BREAKCONT() POP_CONTINUE(); POP_BREAK() #define CHECK_ON_DEFINED(ref__) do { if (!ref__) break; *ref__ = true; return false; } while(0) @@ -47,7 +47,7 @@ Decl **global_context_acquire_locals_list(void); void generic_context_release_locals_list(Decl **); const char *context_filename(SemaContext *context); -AstId context_get_defers(SemaContext *context, AstId defer_top, AstId defer_bottom, bool is_success); +AstId context_get_defers(SemaContext *context, AstId defer_bottom, bool is_success); void context_pop_defers(SemaContext *context, AstId *next); void context_pop_defers_and_replace_ast(SemaContext *context, Ast *ast); void context_change_scope_for_label(SemaContext *context, DeclId label); @@ -253,3 +253,4 @@ static inline TypeProperty type_property_by_name(const char *name) } return TYPE_PROPERTY_NONE; } + diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 9cdcfe2a4..a31fa48e5 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -202,7 +202,7 @@ static inline bool sema_analyse_break_stmt(SemaContext *context, Ast *statement) { // If there is no break target and there is no label, // we skip. - if (!context->break_target && !statement->contbreak_stmt.is_label) + if (!context->break_jump.target && !statement->contbreak_stmt.is_label) { if (context_labels_exist_in_scope(context)) { @@ -215,30 +215,28 @@ static inline bool sema_analyse_break_stmt(SemaContext *context, Ast *statement) SET_JUMP_END(context, statement); statement->contbreak_stmt.is_resolved = true; - AstId defer_begin; - Ast *parent; + JumpTarget jump_target = { .target = NULL }; if (statement->contbreak_stmt.label.name) { // If we have a label, pick it and set the parent astid to that target. ASSIGN_DECL_OR_RET(Decl *target, sema_analyse_label(context, statement), false); // We don't need to do any checking since all(!) label constructs support break. - parent = astptr(target->label.parent); - defer_begin = target->label.defer; + + jump_target = (JumpTarget) { astptr(target->label.parent), target->label.defer }; } else { // Jump to the default break target. - parent = context->break_target; - defer_begin = context->break_defer; + jump_target = context->break_jump; } - ASSERT(parent); - parent->flow.has_break = true; - statement->contbreak_stmt.ast = astid(parent); + ASSERT(jump_target.target); + jump_target.target->flow.has_break = true; + statement->contbreak_stmt.ast = astid(jump_target.target); // Append the defers. - statement->contbreak_stmt.defers = context_get_defers(context, context->active_scope.defer_last, defer_begin, true); + statement->contbreak_stmt.defers = context_get_defers(context, jump_target.defer, true); return true; } @@ -285,22 +283,20 @@ static inline bool sema_analyse_ct_compound_stmt(SemaContext *context, Ast *stat static inline bool sema_analyse_continue_stmt(SemaContext *context, Ast *statement) { // If we have a plain continue and no continue label, we just failed. - if (!context->continue_target && !statement->contbreak_stmt.label.name) + if (!context->continue_jump.target && !statement->contbreak_stmt.label.name) { RETURN_SEMA_ERROR(statement, "'continue' is not allowed here."); } - AstId defer_id; - Ast *parent; + JumpTarget jump_target = { .target = NULL }; if (statement->contbreak_stmt.label.name) { // If we have a label grab it. ASSIGN_DECL_OR_RET(Decl *target, sema_analyse_label(context, statement), false); - defer_id = target->label.defer; - parent = astptr(target->label.parent); + jump_target = (JumpTarget) { astptr(target->label.parent), target->label.defer }; // Continue can only be used with "for" statements, skipping the "do { };" statement - if (!ast_supports_continue(parent)) + if (!ast_supports_continue(jump_target.target)) { RETURN_SEMA_ERROR(statement, "'continue' may only be used with 'for', 'while' and 'do-while' statements."); } @@ -308,17 +304,16 @@ static inline bool sema_analyse_continue_stmt(SemaContext *context, Ast *stateme else { // Use default defer and ast. - defer_id = context->continue_defer; - parent = context->continue_target; + jump_target = context->continue_jump; } // This makes the active scope jump. SET_JUMP_END(context, statement); // Link the parent and add the defers. - statement->contbreak_stmt.ast = astid(parent); + statement->contbreak_stmt.ast = astid(jump_target.target); statement->contbreak_stmt.is_resolved = true; - statement->contbreak_stmt.defers = context_get_defers(context, context->active_scope.defer_last, defer_id, true); + statement->contbreak_stmt.defers = context_get_defers(context, jump_target.defer, true); return true; } @@ -456,16 +451,16 @@ static inline bool sema_defer_has_try_or_catch(AstId defer_top, AstId defer_bott } // Print defers at return (from macro/block or from function) -static inline void sema_inline_return_defers(SemaContext *context, Ast *stmt, AstId defer_top, AstId defer_bottom) +static inline void sema_inline_return_defers(SemaContext *context, Ast *stmt, AstId defer_bottom) { // Store the cleanup defers, which will happen on try. - stmt->return_stmt.cleanup = context_get_defers(context, defer_top, defer_bottom, true); + stmt->return_stmt.cleanup = context_get_defers(context, defer_bottom, true); // If we have an optional return, then we create a cleanup_fail if (stmt->return_stmt.expr && IS_OPTIONAL(stmt->return_stmt.expr) && sema_defer_has_try_or_catch(context->active_scope.defer_last, context->block_return_defer)) { - stmt->return_stmt.cleanup_fail = context_get_defers(context, context->active_scope.defer_last, context->block_return_defer, false); + stmt->return_stmt.cleanup_fail = context_get_defers(context, context->block_return_defer, false); return; } // Otherwise we make the cleanup fail be the same as the cleanup. @@ -590,7 +585,7 @@ static inline bool sema_analyse_block_exit_stmt(SemaContext *context, Ast *state } } statement->return_stmt.block_exit_ref = context->block_exit_ref; - sema_inline_return_defers(context, statement, context->active_scope.defer_last, context->block_return_defer); + sema_inline_return_defers(context, statement, context->block_return_defer); if (!sema_analyse_macro_constant_ensures(context, ret_expr)) return false; vec_add(context->block_returns, statement); return true; @@ -689,7 +684,7 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement if (!sema_check_not_stack_variable_escape(context, return_expr)) return false; if (!sema_check_return_matches_opt_returns(context, return_expr)) return false; // Process any ensures. - sema_inline_return_defers(context, statement, context->active_scope.defer_last, 0); + sema_inline_return_defers(context, statement, 0); } else { @@ -698,7 +693,7 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement SEMA_ERROR(statement, "Expected to return a result of type %s.", type_to_error_string(expected_rtype)); return false; } - statement->return_stmt.cleanup = context_get_defers(context, context->active_scope.defer_last, 0, true); + statement->return_stmt.cleanup = context_get_defers(context, 0, true); } if (context->call_env.ensures) @@ -1970,7 +1965,7 @@ END: is_invalid = context->active_scope.is_poisoned; SCOPE_OUTER_END; - if (is_invalid) context->active_scope.is_poisoned = is_invalid; + if (is_invalid) context->active_scope.is_poisoned = true; if (!success) { if (then_jump && sema_catch_in_cond(cond)) context->active_scope.is_poisoned = true; @@ -2007,8 +2002,7 @@ static bool sema_analyse_asm_string_stmt(SemaContext *context, Ast *stmt) if (!sema_analyse_ct_expr(context, body)) return false; if (!expr_is_const_string(body)) { - SEMA_ERROR(body, "The asm statement expects a constant string."); - return false; + RETURN_SEMA_ERROR(body, "The asm statement expects a constant string."); } return true; } @@ -2094,18 +2088,14 @@ static bool context_labels_exist_in_scope(SemaContext *context) static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement) { - SET_JUMP_END(context, statement); - if (!context->next_target && !statement->nextcase_stmt.label.name && !statement->nextcase_stmt.expr && !statement->nextcase_stmt.is_default) + SET_JUMP_END(context, statement); + if (!context->next_jump.target && !statement->nextcase_stmt.label.name && !statement->nextcase_stmt.expr && !statement->nextcase_stmt.is_default) { if (context->next_switch) { - SEMA_ERROR(statement, "A plain 'nextcase' is not allowed on the last case."); + RETURN_SEMA_ERROR(statement, "A plain 'nextcase' is not allowed on the last case."); } - else - { - SEMA_ERROR(statement, "'nextcase' can only be used inside of a switch."); - } - return false; + RETURN_SEMA_ERROR(statement, "'nextcase' can only be used inside of a switch."); } Ast *parent = context->next_switch; @@ -2139,7 +2129,7 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement) } } if (!default_ast) RETURN_SEMA_ERROR(statement, "There is no 'default' in the switch to jump to."); - statement->nextcase_stmt.defer_id = context_get_defers(context, context->active_scope.defer_last, parent->switch_stmt.defer, true); + statement->nextcase_stmt.defer_id = context_get_defers(context, parent->switch_stmt.defer, true); statement->nextcase_stmt.case_switch_stmt = astid(default_ast); statement->nextcase_stmt.switch_expr = NULL; return true; @@ -2149,9 +2139,9 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement) statement->nextcase_stmt.switch_expr = NULL; if (!value) { - ASSERT(context->next_target); - statement->nextcase_stmt.defer_id = context_get_defers(context, context->active_scope.defer_last, parent->switch_stmt.defer, true); - statement->nextcase_stmt.case_switch_stmt = astid(context->next_target); + ASSERT(context->next_jump.target); + statement->nextcase_stmt.defer_id = context_get_defers(context, parent->switch_stmt.defer, true); + statement->nextcase_stmt.case_switch_stmt = astid(context->next_jump.target); return true; } @@ -2166,7 +2156,7 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement) { TypeInfo *type_info = value->type_expr; if (!sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return false; - statement->nextcase_stmt.defer_id = context_get_defers(context, context->active_scope.defer_last, parent->switch_stmt.defer, true); + statement->nextcase_stmt.defer_id = context_get_defers(context, parent->switch_stmt.defer, true); if (cond->type->canonical != type_typeid) { SEMA_ERROR(statement, "Unexpected 'type' in as an 'nextcase' destination."); @@ -2192,7 +2182,7 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement) if (!sema_analyse_expr_rhs(context, expected_type, value, false, NULL, false)) return false; - statement->nextcase_stmt.defer_id = context_get_defers(context, context->active_scope.defer_last, parent->switch_stmt.defer, true); + statement->nextcase_stmt.defer_id = context_get_defers(context, parent->switch_stmt.defer, true); if (sema_cast_const(value)) { @@ -2209,8 +2199,7 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement) return true; } } - SEMA_ERROR(value, "There is no 'case %s' in the switch, please check if a case is missing or if this value is incorrect.", expr_const_to_error_string(&value->const_expr)); - return false; + RETURN_SEMA_ERROR(value, "There is no 'case %s' in the switch, please check if a case is missing or if this value is incorrect.", expr_const_to_error_string(&value->const_expr)); } VARIABLE_JUMP: statement->nextcase_stmt.case_switch_stmt = astid(parent); @@ -2475,8 +2464,7 @@ static bool sema_analyse_switch_body(SemaContext *context, Ast *statement, Sourc { if (!type_is_comparable(switch_type)) { - sema_error_at(context, expr_span, "You cannot test '%s' for equality, and only values that supports '==' for comparison can be used in a switch.", type_to_error_string(switch_type)); - return false; + RETURN_SEMA_ERROR_AT(expr_span, "You cannot test '%s' for equality, and only values that supports '==' for comparison can be used in a switch.", type_to_error_string(switch_type)); } // We need an if-chain if this isn't an enum/integer type. Type *flat = type_flatten(switch_type); @@ -3276,10 +3264,7 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func) // Clear returns vec_resize(context->block_returns, 0); context->scope_id = 0; - context->continue_target = NULL; - context->next_target = 0; - context->next_switch = 0; - context->break_target = 0; + context->break_jump = context->continue_jump = context->next_jump = (JumpTarget) { .target = NULL }; ASSERT(func->func_decl.body); Ast *body = astptr(func->func_decl.body); Decl **lambda_params = NULL; diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index c5cc4d3dc..07239f549 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -75,8 +75,10 @@ void context_change_scope_for_label(SemaContext *context, DeclId label_id) } } -AstId context_get_defers(SemaContext *context, AstId defer_top, AstId defer_bottom, bool is_success) + +AstId context_get_defers(SemaContext *context, AstId defer_bottom, bool is_success) { + AstId defer_top = context->active_scope.defer_last; AstId first = 0; AstId *next = &first; while (defer_bottom != defer_top)