diff --git a/releasenotes.md b/releasenotes.md index 03ab53302..4c716c1ea 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -102,6 +102,7 @@ - Unable to access fields of a const inline enum with an aggregate underlying type. #2802 - Using an optional type as generic parameter was not properly caught #2799 - Instantiating an alias of a user-defined type was not properly caught #2798 +- Too deeply nested scopes was a fatal crash and not a regular semantic error. #2796 ### Stdlib changes - Add `ThreadPool` join function to wait for all threads to finish in the pool without destroying the threads. diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index a56efc18a..6286b349c 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1714,7 +1714,7 @@ INLINE bool sema_set_default_argument(SemaContext *context, CalledDecl *callee, new_context->inlined_at = &inlined_at; } bool success; - SCOPE_START + SCOPE_START(arg->span) new_context->original_module = context->original_module; success = sema_analyse_parameter(new_context, arg, param, callee->definition, optional, no_match_ref, callee->macro, false); @@ -2869,7 +2869,7 @@ static inline bool sema_expr_setup_call_analysis(SemaContext *context, CalledDec macro_context->block_exit_ref = block_exit_ref; - context_change_scope_with_flags(macro_context, SCOPE_MACRO); + context_change_scope_with_flags(macro_context, SCOPE_MACRO, call_expr->span); macro_context->block_return_defer = macro_context->active_scope.defer_last; return true; @@ -3051,7 +3051,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s DynamicScope old_scope = context->active_scope; // Create a scope, since the macro itself will not. - context_change_scope_with_flags(context, SCOPE_NONE); + context_change_scope_with_flags(context, SCOPE_NONE, call_expr->span); SemaContext macro_context; Type *rtype = typeget(sig->rtype); @@ -3371,7 +3371,7 @@ static bool sema_call_analyse_body_expansion(SemaContext *macro_context, Expr *c call->body_expansion_expr.values = args; call->body_expansion_expr.declarations = macro_context->yield_params; AstId last_defer = context->active_scope.defer_last; - SCOPE_START + SCOPE_START(call->span); unsigned ct_context = sema_context_push_ct_stack(context); if (macro_defer) { @@ -8586,7 +8586,7 @@ static inline bool sema_rewrite_expr_as_macro_block(SemaContext *context, Expr * bool success; Ast *compound_stmt = ast_new(AST_COMPOUND_STMT, expr->span); compound_stmt->compound_stmt.first_stmt = start; - SCOPE_START_WITH_FLAGS(SCOPE_MACRO) + SCOPE_START_WITH_FLAGS(SCOPE_MACRO, compound_stmt->span) success = sema_analyse_stmt_chain(context, compound_stmt); SCOPE_END; context->expected_block_type = old_expected_block; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 9c16df82a..6aa8cb2e7 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -23,11 +23,11 @@ #define RETURN_VAL_SEMA_ERROR(val__, _node, ...) do { sema_error_at(context, (_node)->span, __VA_ARGS__); return (val__); } while (0) #define RETURN_NULL_SEMA_ERROR(_node, ...) do { sema_error_at(context, (_node)->span, __VA_ARGS__); return NULL; } while (0) #define RETURN_SEMA_ERROR_AT(span__, ...) do { sema_error_at(context, span__, __VA_ARGS__); return false; } while (0) -#define SCOPE_OUTER_START do { DynamicScope stored_scope = context->active_scope; context_change_scope_with_flags(context, SCOPE_NONE); +#define SCOPE_OUTER_START(span__) do { DynamicScope stored_scope = context->active_scope; context_change_scope_with_flags(context, SCOPE_NONE, span__); #define SCOPE_OUTER_END ASSERT(context->active_scope.defer_last == context->active_scope.defer_start); context->active_scope = stored_scope; } while(0) -#define SCOPE_START SCOPE_START_WITH_FLAGS(SCOPE_NONE) -#define SCOPE_START_WITH_FLAGS(flags) do { DynamicScope old_scope = context->active_scope; context_change_scope_with_flags(context, flags); -#define SCOPE_START_WITH_LABEL(label) do { DynamicScope old_scope = context->active_scope; context_change_scope_for_label(context, label); +#define SCOPE_START(span__) SCOPE_START_WITH_FLAGS(SCOPE_NONE, span__) +#define SCOPE_START_WITH_FLAGS(flags, span__) do { DynamicScope old_scope = context->active_scope; context_change_scope_with_flags(context, flags, span__); +#define SCOPE_START_WITH_LABEL(label, span__) do { DynamicScope old_scope = context->active_scope; context_change_scope_for_label(context, label, span__); #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) @@ -51,8 +51,8 @@ const char *context_filename(SemaContext *context); 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); -void context_change_scope_with_flags(SemaContext *context, ScopeFlags flags); +void context_change_scope_for_label(SemaContext *context, DeclId label, SourceSpan span); +void context_change_scope_with_flags(SemaContext *context, ScopeFlags flags, SourceSpan span); SemaContext *context_transform_for_eval(SemaContext *context, SemaContext *temp_context, CompilationUnit *eval_unit); TokenType sema_splitpathref(const char *string, ArraySize len, Path **path_ref, const char **ident_ref); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 737e41c58..971b46d33 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -247,7 +247,7 @@ static inline bool sema_analyse_compound_stmt(SemaContext *context, Ast *stateme { bool success; EndJump ends_with_jump; - SCOPE_START + SCOPE_START(statement->span) success = sema_analyse_compound_statement_no_scope(context, statement); ends_with_jump = context->active_scope.end_jump; SCOPE_END; @@ -532,7 +532,7 @@ static bool sema_analyse_macro_constant_ensures(SemaContext *context, Expr *ret_ // And set our new one. context->return_expr = ret_expr; bool success = true; - SCOPE_START_WITH_FLAGS(SCOPE_ENSURE_MACRO); + SCOPE_START_WITH_FLAGS(SCOPE_ENSURE_MACRO, ret_expr->span); while (doc_directive) { Ast *directive = astptr(doc_directive); @@ -757,7 +757,7 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement if (directive->contract_stmt.kind == CONTRACT_ENSURE) { bool success; - SCOPE_START_WITH_FLAGS(SCOPE_ENSURE); + SCOPE_START_WITH_FLAGS(SCOPE_ENSURE, statement->span); success = assert_create_from_contract(context, directive, &append_id, statement->span); SCOPE_END; if (!success) return false; @@ -1347,7 +1347,7 @@ bool sema_analyse_defer_stmt_body(SemaContext *context, Ast *statement) } body->compound_stmt.parent_defer = astid(statement); bool success = true; - SCOPE_START + SCOPE_START(statement->span) context->active_scope.defer_last = 0; context->active_scope.defer_start = 0; @@ -1434,7 +1434,7 @@ static inline bool sema_analyse_for_stmt(SemaContext *context, Ast *statement) RETURN_SEMA_ERROR(body, "A do loop must use { } around its body."); } // Enter for scope - SCOPE_OUTER_START + SCOPE_OUTER_START(statement->span) if (statement->for_stmt.init) { @@ -1442,7 +1442,7 @@ static inline bool sema_analyse_for_stmt(SemaContext *context, Ast *statement) } // Conditional scope start - SCOPE_START_WITH_LABEL(statement->for_stmt.flow.label) + SCOPE_START_WITH_LABEL(statement->for_stmt.flow.label, statement->span) if (!do_loop) { @@ -1466,7 +1466,7 @@ static inline bool sema_analyse_for_stmt(SemaContext *context, Ast *statement) if (statement->for_stmt.flow.skip_first) { - SCOPE_START + SCOPE_START(statement->span) if (!sema_analyse_for_cond(context, &statement->for_stmt.cond, &is_infinite) || !success) { SCOPE_ERROR_END_OUTER(); @@ -1484,7 +1484,7 @@ static inline bool sema_analyse_for_stmt(SemaContext *context, Ast *statement) if (success && statement->for_stmt.incr) { // Incr scope start - SCOPE_START + SCOPE_START(statement->span) success = sema_analyse_expr_rvalue(context, exprptr(statement->for_stmt.incr)); // Incr scope end SCOPE_END; @@ -1537,7 +1537,7 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen } } // Conditional scope start - SCOPE_START + SCOPE_START(statement->span) // In the case of foreach (int x : { 1, 2, 3 }) we will infer the int[] type, so pick out the number of elements. Type *inferred_type = variable_type_info ? type_get_inferred_array(type_no_optional(variable_type_info->type)) : NULL; @@ -1984,7 +1984,7 @@ static inline bool sema_analyse_if_stmt(SemaContext *context, Ast *statement) CondResult result = COND_MISSING; bool is_invalid = false; bool reverse = false; - SCOPE_OUTER_START + SCOPE_OUTER_START(statement->span) success = sema_analyse_cond(context, cond, COND_TYPE_UNWRAP_BOOL, &result); if (success && cond->expr_kind == EXPR_COND) @@ -2027,7 +2027,7 @@ static inline bool sema_analyse_if_stmt(SemaContext *context, Ast *statement) } } - SCOPE_START_WITH_LABEL(statement->if_stmt.flow.label); + SCOPE_START_WITH_LABEL(statement->if_stmt.flow.label, then->span); if (result == COND_FALSE) context->active_scope.is_dead = true; success = success && sema_analyse_statement(context, then); then_jump = context->active_scope.end_jump.active && !(statement->if_stmt.flow.label && statement->if_stmt.flow.has_break); @@ -2039,11 +2039,11 @@ static inline bool sema_analyse_if_stmt(SemaContext *context, Ast *statement) { bool store_break = statement->if_stmt.flow.has_break; statement->if_stmt.flow.has_break = false; - SCOPE_START_WITH_LABEL(statement->if_stmt.flow.label); + SCOPE_START_WITH_LABEL(statement->if_stmt.flow.label, statement->span); if (result == COND_TRUE) context->active_scope.is_dead = true; sema_remove_unwraps_from_try(context, cond); sema_unwrappable_from_catch_in_else(context, cond); - success = success && sema_analyse_statement(context, else_body); + success = sema_analyse_statement(context, else_body); else_jump = context->active_scope.end_jump.active && !(statement->if_stmt.flow.label && statement->if_stmt.flow.has_break); SCOPE_END; statement->if_stmt.flow.has_break |= store_break; @@ -2636,7 +2636,7 @@ FOUND:; for (unsigned i = 0; i < case_count; i++) { Ast *stmt = cases[i]; - SCOPE_START + SCOPE_START(statement->span) PUSH_BREAK(statement); Ast *next = (i < case_count - 1) ? cases[i + 1] : NULL; PUSH_NEXT(next, statement); @@ -2984,7 +2984,7 @@ static inline bool sema_analyse_switch_stmt(SemaContext *context, Ast *statement { statement->switch_stmt.scope_defer = context->active_scope.in_defer; - SCOPE_START_WITH_LABEL(statement->switch_stmt.flow.label); + SCOPE_START_WITH_LABEL(statement->switch_stmt.flow.label, statement->span); Expr *cond = exprptrzero(statement->switch_stmt.cond); Type *switch_type; @@ -3471,7 +3471,7 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func) Ast *body = astptr(func->func_decl.body); Decl **lambda_params = NULL; - SCOPE_START + SCOPE_START(func->span) ASSERT(context->active_scope.depth == 1); FOREACH(Decl *, param, signature->params) { diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 671b12535..c13a3f3ba 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -8,12 +8,13 @@ char swizzle[256] = { ['x'] = 0x01, ['y'] = 0x02, ['z'] = 0x03, ['w'] = 0x04, ['r'] = 0x11, ['g'] = 0x12, ['b'] = 0x13, ['a'] = 0x14 }; -void context_change_scope_with_flags(SemaContext *context, ScopeFlags flags) +void context_change_scope_with_flags(SemaContext *context, ScopeFlags flags, SourceSpan span) { unsigned depth = context->active_scope.depth + 1; if (depth > MAX_SCOPE_DEPTH) { - FATAL_ERROR("Too deeply nested scopes."); + sema_error_at(context, span, "Resolution failed due to too deeply nested scopes (%u).", depth); + exit_compiler(COMPILER_SUCCESS_EXIT); } bool scope_is_dead = context->active_scope.is_dead; @@ -57,9 +58,9 @@ const char *context_filename(SemaContext *context) return file->full_path; } -void context_change_scope_for_label(SemaContext *context, DeclId label_id) +void context_change_scope_for_label(SemaContext *context, DeclId label_id, SourceSpan span) { - context_change_scope_with_flags(context, SCOPE_NONE); + context_change_scope_with_flags(context, SCOPE_NONE, span); if (label_id) { diff --git a/test/test_suite/functions/function_deep_nesting.c3 b/test/test_suite/functions/function_deep_nesting.c3 new file mode 100644 index 000000000..0fa2d6995 --- /dev/null +++ b/test/test_suite/functions/function_deep_nesting.c3 @@ -0,0 +1,7 @@ +fn void test(int a = test()) // #error: Resolution failed due to too deeply nested scopes (257). +{ +} +fn void main() +{ + test(); +} \ No newline at end of file