diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 000000000..08a8af70e --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,38 @@ +--- +# Configure clang-tidy for this project. + +# Disabled: +# -google-readability-namespace-comments the *_CLIENT_NS is a macro, and +# clang-tidy fails to match it against the initial value. +Checks: > + -*, + bugprone-*, + google-*, + misc-*, + modernize-*, + performance-*, + portability-*, + readability-*, + -google-readability-namespace-comments, + -google-runtime-int, + -google-runtime-references, + -misc-non-private-member-variables-in-classes, + -readability-named-parameter, + -readability-braces-around-statements, + -readability-magic-numbers + +# Turn all the warnings from the checks above into errors. +WarningsAsErrors: "*" + +CheckOptions: + - { key: readability-identifier-naming.StructCase, value: CamelCase } + - { key: readability-identifier-naming.FunctionCase, value: lower_case } + - { key: readability-identifier-naming.VariableCase, value: lower_case } + - { key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE } + - { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE } + - { key: readability-identifier-naming.ConstexprVariableCase, value: CamelCase } + - { key: readability-identifier-naming.ConstexprVariablePrefix, value: k } + - { key: readability-identifier-naming.GlobalConstantCase, value: CamelCase } + - { key: readability-identifier-naming.GlobalConstantPrefix, value: k } + - { key: readability-identifier-naming.StaticConstantCase, value: CamelCase } + - { key: readability-identifier-naming.StaticConstantPrefix, value: k } \ No newline at end of file diff --git a/README.md b/README.md index eed06d3b2..cb33a010d 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,12 @@ There are some small work being done on the parser here, but most of the structu - `asm` sections. - Macro parameter lists to imports. - auxiliary data for enums. -- Docs not correctly linked to statements/functions/declarations. +- Docs not linked to statements/functions/declarations. #### What's missing in the semantic analyser -- No handling imports. +- Incomplete handling of imports. +- `next` not correct - Function signatures incomplete. - Function typedef not done. - `asm` not done. @@ -51,10 +52,6 @@ There are some small work being done on the parser here, but most of the structu #### What's missing overall -- Exactly how the module system should work together with the import is still -under discussion. -- Exactly how the library imports should work is not 100% decided. -- The compiler currently only works in single file mode. - Integration with C. #### What's working? diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index 64ae92e8f..238bf0b5f 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -120,20 +120,113 @@ func void bob() long deee = (eok ? a : b) + c; } +func int if_test(int x) +{ + switch (x) + { + case 1: + x += 1; + if (x < 10) + { + defer x += 5; + if (x < 7) + { + defer x += 100; + next; + } + x += 99; + } + next; + default: + x += 2; + break; + } + return 1; +} +func int yyyy(int x) +{ + defer printf("A"); + if (x > 0) return 2; + defer printf("B"); + printf("C"); + return 1; +} +func void zzzz() +{ + int x = 0; + defer + { + x += 1; + printf("A"); + } + defer + { + x += 2; + printf("B"); + } + printf("C"); + x += 3; +} + +func int jumpback(int x) +{ + { + defer x += 1; + { + defer x += 2; + LABELX: + x += 3; + } + } + if (x < 100) goto LABELX; + return x + 1; +} func int xxxx(int x) { + { + x += 10; + defer printf("XXX"); + if (x < 100) goto LABELD; + defer printf("EODfe"); + } { defer printf("Defer says hello!\n"); - LABEL: + LABELD: x--; } - if (x > 0) goto LABEL; + if (x > 0) goto LABELD; return 1; } func int main(int x) { + int efd = 9; + int okfe = 1; + switch (int bobe = okfe > 0 ? 1 : 0) + { + case 0: + defer printf("case0-\n"); + case 1: + printf("case 1\n"); + defer printf("case1-\n"); + if (efd < 10) + { + { + defer printf("ef < 10\n"); + if (efd < 7) + { + defer printf("ef < 7\n"); + next; + } + } + } + next; + default: + printf("default\n"); + break; + } + return 1; int aa = x++; int bb = x--; int cc = ++x; diff --git a/resources/testproject/bar.c3 b/resources/testproject/bar.c3 index 2bf7ad476..3ced5c41f 100644 --- a/resources/testproject/bar.c3 +++ b/resources/testproject/bar.c3 @@ -6,6 +6,9 @@ import testbaz; public func void test() { Foo x; + baz::foo::test(); foo::test(); + baz::foo::Foo y; + foo::Foo d; Zab z; } \ No newline at end of file diff --git a/src/compiler/ast.c b/src/compiler/ast.c index dbd3ca523..b7c974ed1 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -893,6 +893,10 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) fprint_ast_recursive(file, ast->while_stmt.cond, indent + 1); fprint_ast_recursive(file, ast->while_stmt.body, indent + 1); break; + case AST_SCOPED_STMT: + fprintf(file, "(scoped\n"); + fprint_ast_recursive(file, ast->scoped_stmt.stmt, indent + 1); + break; case AST_CT_FOR_STMT: if (ast->ct_for_stmt.index.string) { @@ -946,7 +950,7 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) } if (ast->for_stmt.incr) { - fprint_ast_recursive(file, ast->for_stmt.incr, indent + 1); + fprint_expr_recursive(file, ast->for_stmt.incr, indent + 1); } else { @@ -967,7 +971,7 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) case AST_SWITCH_STMT: fprintf(file, "(switchstmt\n"); fprint_ast_recursive(file, ast->switch_stmt.cond, indent + 1); - fprint_ast_recursive(file, ast->switch_stmt.body, indent + 1); + fprint_asts_recursive(file, ast->switch_stmt.cases, indent + 1); break; case AST_CASE_STMT: fprintf(file, "(case\n"); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 9e017ac30..dd99e4c71 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -26,7 +26,6 @@ typedef struct _TypeInfo TypeInfo; typedef struct _Expr Expr; typedef struct _Module Module; typedef struct _Type Type; -typedef uint16_t DeferId; typedef bool(*CastFunc)(Expr *, Type *, Type *, Type *, CastType cast_type); @@ -38,8 +37,8 @@ typedef struct typedef struct { - DeferId start; - DeferId end; + Ast *start; + Ast *end; } DeferList; typedef struct @@ -458,6 +457,11 @@ typedef struct }; } ExprCast; +typedef struct +{ + Expr *expr; + DeferList defers; +} ExprScope; struct _Expr { @@ -482,6 +486,7 @@ struct _Expr ExprType type_expr; Expr** initializer_expr; Expr** expression_list; + ExprScope expr_scope; }; }; @@ -549,15 +554,15 @@ typedef struct bool has_next; }; }; - Ast *block; + Ast *body; void *backend_value; } AstCaseStmt; + typedef struct { Ast *decl; Ast *cond; - Ast *body; Ast **cases; } AstSwitchStmt; @@ -565,32 +570,29 @@ typedef struct { Ast *init; Expr *cond; - Ast *incr; + Expr *incr; Ast *body; - DeferList cond_defer; } AstForStmt; typedef struct { - GotoType type : 2; const char *label_name; Ast *label; - Ast *defer; + DeferList defer; union { struct _Ast *in_defer; - struct _Ast *defer_end; }; } AstGotoStmt; typedef struct _AstDeferStmt { bool emit_boolean : 1; - struct _Ast *body; // Compound statement - struct _Ast *prev_defer; - DeferId id; + Ast *body; // Compound statement + Ast *prev_defer; + void *bool_var; } AstDeferStmt; typedef struct _AstCatchStmt @@ -613,6 +615,12 @@ typedef struct _AstGenericCaseStmt struct _Ast *body; } AstGenericCaseStmt; +typedef struct +{ + Ast *stmt; + DeferList defers; +} AstScopedStmt; + typedef struct { Expr *cond; @@ -635,8 +643,14 @@ typedef struct typedef struct { - Ast **defers; -} AstContinueStmt; + DeferList defers; +} AstContinueBreakStmt; + +typedef struct +{ + Ast *prev; + DeferList defers; +} AstNextStmt; typedef struct _Ast { @@ -663,9 +677,10 @@ typedef struct _Ast AstCaseStmt case_stmt; AstCtSwitchStmt ct_switch_stmt; AstCtCaseStmt ct_case_stmt; - AstContinueStmt continue_stmt; + AstContinueBreakStmt continue_stmt; + AstContinueBreakStmt break_stmt; Ast* ct_default_stmt; - Ast* next_stmt; + AstNextStmt next_stmt; AstCatchStmt catch_stmt; AstGotoStmt goto_stmt; AstForStmt for_stmt; @@ -676,6 +691,7 @@ typedef struct _Ast AstGenericCaseStmt generic_case_stmt; Ast *generic_default_stmt; Ast** decl_expr_stmt; + AstScopedStmt scoped_stmt; }; } Ast; @@ -704,8 +720,7 @@ typedef struct _DynamicScope ScopeFlags flags_created; unsigned errors; Decl **local_decl_start; - DeferId defer_top; - DeferId defer_last; + DeferList defers; ExitType exit; } DynamicScope; @@ -766,10 +781,15 @@ typedef struct _Context Token next_tok; struct { + bool has_stored; const char *current; const char *start; Token tok; Token next_tok; + Token *lead_comment; + Token *trailing_comment; + Token *next_lead_comment; + unsigned comments; } stored; } Context; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 38cc9df63..34bce6dfe 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -103,6 +103,7 @@ typedef enum AST_NEXT_STMT, AST_VOLATILE_STMT, AST_WHILE_STMT, + AST_SCOPED_STMT, } AstKind; typedef enum @@ -241,14 +242,9 @@ typedef enum EXPR_INITIALIZER_LIST, EXPR_EXPRESSION_LIST, EXPR_CAST, + EXPR_SCOPED_EXPR, } ExprKind; -typedef enum -{ - GOTO_NOT_ANALYSED, - GOTO_JUMP_FORWARD, - GOTO_JUMP_BACK -} GotoType; typedef enum @@ -263,7 +259,7 @@ typedef enum { PREC_NONE, PREC_ASSIGNMENT, // =, *=, /=, %=, ... - PREC_TERNARY, // ?: + PREC_TERNARY, // ?: PREC_LOGICAL, // && || PREC_RELATIONAL, // < > <= >= == != PREC_ADDITIVE, // + - diff --git a/src/compiler/expr_analysis.c b/src/compiler/expr_analysis.c index ed870afc0..45d9e98bb 100644 --- a/src/compiler/expr_analysis.c +++ b/src/compiler/expr_analysis.c @@ -87,7 +87,6 @@ static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *e } expr->type = left->type; - return true; } @@ -103,6 +102,9 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr return false; } + // Already handled + if (!decl_ok(decl)) return false; + if (ambiguous_decl) { SEMA_ERROR(expr, @@ -1472,6 +1474,8 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * { case EXPR_POISONED: return false; + case EXPR_SCOPED_EXPR: + UNREACHABLE case EXPR_TRY: return sema_expr_analyse_try(context, to, expr); case EXPR_CONST: diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index fb1b3941f..fd9e79cbd 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -59,6 +59,20 @@ static inline LLVMValueRef gencontext_emit_access_addr(GenContext *context, Expr return LLVMBuildStructGEP2(context->builder, BACKEND_TYPE(expr->access_expr.parent->type), value, (unsigned)expr->access_expr.index, ""); } +LLVMValueRef gencontext_emit_scoped_expr(GenContext *context, Expr *expr) +{ + LLVMValueRef value = gencontext_emit_expr(context, expr->expr_scope.expr); + gencontext_emit_defer(context, expr->expr_scope.defers.start, expr->expr_scope.defers.end); + return value; +} + +LLVMValueRef gencontext_emit_scoped_expr_address(GenContext *context, Expr *expr) +{ + LLVMValueRef value = gencontext_emit_address(context, expr->expr_scope.expr); + gencontext_emit_defer(context, expr->expr_scope.defers.start, expr->expr_scope.defers.end); + return value; +} + LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) { switch (expr->expr_kind) @@ -72,6 +86,8 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) return gencontext_emit_access_addr(context, expr); case EXPR_SUBSCRIPT: return gencontext_emit_subscript_addr(context, expr); + case EXPR_SCOPED_EXPR: + return gencontext_emit_scoped_expr_address(context, expr); case EXPR_CONST: case EXPR_TYPE: case EXPR_POISONED: @@ -614,20 +630,14 @@ static inline LLVMValueRef gencontext_emit_struct_init_values_expr(GenContext *c TODO } -LLVMValueRef gencontext_emit_ast_expr(GenContext *context, Ast *expr) -{ - assert(expr->ast_kind == AST_EXPR_STMT); - LLVMValueRef value = gencontext_emit_expr(context, expr->expr_stmt); -// gencontext_emit_defer(context, expr); - return value; -} - LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) { switch (expr->expr_kind) { case EXPR_POISONED: UNREACHABLE + case EXPR_SCOPED_EXPR: + return gencontext_emit_scoped_expr(context, expr); case EXPR_UNARY: return gencontext_emit_unary_expr(context, expr); case EXPR_CONST: diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index f9c7efceb..6653aa973 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -112,8 +112,10 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl) label->label_stmt.backend_value = gencontext_create_free_block(context, label->label_stmt.name); } - gencontext_emit_compound_stmt(context, decl->func.body); - + VECEACH(decl->func.body->compound_stmt.stmts, i) + { + gencontext_emit_stmt(context, decl->func.body->compound_stmt.stmts[i]); + } if (!LLVMGetFirstInstruction(context->current_block) && !LLVMGetFirstUse(LLVMBasicBlockAsValue(context->current_block))) { @@ -122,10 +124,12 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl) context->current_block = prev_block; LLVMPositionBuilderAtEnd(context->builder, context->current_block); } - // Insert a return if needed. + // Insert a return (and defer) if needed. if (!LLVMGetBasicBlockTerminator(context->current_block)) { assert(decl->func.function_signature.rtype->type->type_kind == TYPE_VOID); + assert(decl->func.body->compound_stmt.defer_list.end == NULL); + gencontext_emit_defer(context, decl->func.body->compound_stmt.defer_list.start, NULL); LLVMBuildRetVoid(context->builder); } diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index c57a43be8..9c2332855 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -72,7 +72,7 @@ typedef struct void gencontext_begin_module(GenContext *context); void gencontext_end_module(GenContext *context); void gencontext_emit_stmt(GenContext *context, Ast *ast); -void gencontext_emit_defer(GenContext *context, DeferList defer_list); +void gencontext_emit_defer(GenContext *context, Ast *defer_start, Ast *defer_end); LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr); LLVMValueRef gencontext_emit_ast_expr(GenContext *context, Ast *expr); LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index b35ced45b..a872c2ed0 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -15,6 +15,7 @@ void gencontext_emit_compound_stmt(GenContext *context, Ast *ast) { gencontext_emit_stmt(context, ast->compound_stmt.stmts[i]); } + gencontext_emit_defer(context, ast->compound_stmt.defer_list.start, ast->compound_stmt.defer_list.end); } @@ -43,7 +44,7 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) { LLVMValueRef value = gencontext_emit_expr(context, decl->var.init_expr); LLVMBuildStore(context->builder, value, decl->var.backend_ref); - return value; + return decl->var.backend_ref; } return decl->var.backend_ref; } @@ -99,20 +100,14 @@ static inline void gencontext_emit_return(GenContext *context, Ast *ast) { // Ensure we are on a branch that is non empty. if (!gencontext_check_block_branch_emit(context)) return; - Ast *defer = ast->return_stmt.defer; - while (defer) - { - gencontext_emit_stmt(context, defer->defer_stmt.body); - // TODO boolean - defer = defer->defer_stmt.prev_defer; - } - if (!ast->return_stmt.expr) + LLVMValueRef ret_value = ast->return_stmt.expr ? gencontext_emit_expr(context, ast->return_stmt.expr) : NULL; + gencontext_emit_defer(context, ast->return_stmt.defer, NULL); + if (!ret_value) { LLVMBuildRetVoid(context->builder); return; } - LLVMValueRef returnValue = gencontext_emit_expr(context, ast->return_stmt.expr); - LLVMBuildRet(context->builder, returnValue); + LLVMBuildRet(context->builder, ret_value); context->current_block = NULL; LLVMBasicBlockRef post_ret_block = gencontext_create_free_block(context, "ret"); gencontext_emit_block(context, post_ret_block); @@ -225,7 +220,6 @@ void gencontext_emit_for_stmt(GenContext *context, Ast *ast) gencontext_emit_br(context, cond_block); gencontext_emit_block(context, cond_block); value = gencontext_emit_expr(context, ast->for_stmt.cond); - gencontext_emit_defer(context, ast->for_stmt.cond_defer); // If we have a body, conditionally jump to it. if (body_block) { @@ -258,7 +252,7 @@ void gencontext_emit_for_stmt(GenContext *context, Ast *ast) { // Emit the block gencontext_emit_block(context, inc_block); - gencontext_emit_expr(context, ast->for_stmt.incr->expr_stmt); + gencontext_emit_expr(context, ast->for_stmt.incr); } // Loop back. @@ -285,7 +279,14 @@ void gencontext_emit_while_stmt(GenContext *context, Ast *ast) // Emit cond gencontext_emit_br(context, begin_block); gencontext_emit_block(context, begin_block); - LLVMValueRef value = gencontext_emit_decl_expr_list(context, ast->while_stmt.cond, true); + DeferList defers = { NULL, NULL }; + Ast *cond = ast->while_stmt.cond; + if (cond->ast_kind == AST_SCOPED_STMT) + { + defers = cond->scoped_stmt.defers; + cond = cond->scoped_stmt.stmt; + } + LLVMValueRef value = gencontext_emit_decl_expr_list(context, cond, true); // If we have a body, conditionally jump to it. if (body_block) { @@ -293,6 +294,9 @@ void gencontext_emit_while_stmt(GenContext *context, Ast *ast) } else { + // Emit defers + gencontext_emit_defer(context, defers.start, defers.end); + // Otherwise jump to inc or cond depending on what's available. gencontext_emit_cond_br(context, value, begin_block, exit_block); } @@ -301,6 +305,9 @@ void gencontext_emit_while_stmt(GenContext *context, Ast *ast) { gencontext_emit_block(context, body_block); gencontext_emit_stmt(context, ast->while_stmt.body); + + // Emit defers + gencontext_emit_defer(context, defers.start, defers.end); } // Loop back. @@ -308,6 +315,9 @@ void gencontext_emit_while_stmt(GenContext *context, Ast *ast) // And insert exit block gencontext_emit_block(context, exit_block); + + // Emit defers + gencontext_emit_defer(context, defers.start, defers.end); gencontext_pop_break_continue(context); } @@ -369,6 +379,7 @@ void gencontext_emit_label(GenContext *context, Ast *ast) void gencontext_emit_switch(GenContext *context, Ast *ast) { + // TODO check defer correctness if (ast->switch_stmt.decl) gencontext_emit_decl_expr_list_ignore_result(context, ast->switch_stmt.decl); LLVMValueRef switch_value = gencontext_emit_decl_expr_list(context, ast->switch_stmt.cond, false); @@ -385,13 +396,13 @@ void gencontext_emit_switch(GenContext *context, Ast *ast) Ast *case_stmt = ast->switch_stmt.cases[i]; if (case_stmt->case_stmt.value_type == CASE_VALUE_DEFAULT) { - if (case_stmt->case_stmt.block) + if (case_stmt->case_stmt.body) { case_stmt->case_stmt.backend_value = gencontext_create_free_block(context, "switch.default"); } default_case = case_stmt; } - else if (case_stmt->case_stmt.block) + else if (case_stmt->case_stmt.body) { case_stmt->case_stmt.backend_value = gencontext_create_free_block(context, "switch.case"); } @@ -444,13 +455,13 @@ void gencontext_emit_switch(GenContext *context, Ast *ast) } // Skip fallthroughs. - if (!case_stmt->case_stmt.block) continue; + if (!case_stmt->case_stmt.body) continue; gencontext_emit_block(context, block); // IMPORTANT! context->current_block_is_target = true; gencontext_push_break_continue(context, NULL, NULL, i < cases - 1 ? ast->switch_stmt.cases[i + 1]->case_stmt.backend_value : exit_block); - gencontext_emit_stmt(context, case_stmt->case_stmt.block); + gencontext_emit_stmt(context, case_stmt->case_stmt.body); gencontext_pop_break_continue(context); gencontext_emit_br(context, exit_block); } @@ -458,15 +469,92 @@ void gencontext_emit_switch(GenContext *context, Ast *ast) gencontext_emit_block(context, exit_block); } -void gencontext_emit_defer(GenContext *context, DeferList defer_list) +LLVMValueRef gencontext_get_defer_bool(GenContext *context, Ast *defer) { - if (defer_list.start == defer_list.end) return; - Ast *defer = context->ast_context->defers[defer_list.start - 1]; - while (defer && defer->defer_stmt.id != defer_list.end) + assert(defer->ast_kind == AST_DEFER_STMT && defer->defer_stmt.emit_boolean); + if (!defer->defer_stmt.bool_var) { - gencontext_emit_stmt(context, defer->defer_stmt.body); - // TODO boolean + defer->defer_stmt.bool_var = gencontext_emit_alloca(context, BACKEND_TYPE(type_bool), "defer"); } + return defer->defer_stmt.bool_var; +} + +void gencontext_emit_defer(GenContext *context, Ast *defer_start, Ast *defer_end) +{ + if (defer_start == defer_end) return; + Ast *defer = defer_start; + while (defer && defer != defer_end) + { + if (defer->defer_stmt.emit_boolean) + { + + // We need at least the exit block and the "then" block. + LLVMBasicBlockRef exit_block = LLVMCreateBasicBlockInContext(context->context, "skip.defer"); + LLVMBasicBlockRef defer_block = LLVMCreateBasicBlockInContext(context->context, "do.defer"); + + LLVMValueRef value = LLVMBuildLoad2(context->builder, BACKEND_TYPE(type_bool), gencontext_get_defer_bool(context, defer), "will.defer"); + + gencontext_emit_cond_br(context, value, defer_block, exit_block); + + // Emit the defer. + gencontext_emit_block(context, defer_block); + gencontext_emit_stmt(context, defer->defer_stmt.body); + + // Jump to exit. + gencontext_emit_br(context, exit_block); + + // And now we just emit the exit block. + gencontext_emit_block(context, exit_block); + } + else + { + gencontext_emit_stmt(context, defer->defer_stmt.body); + } + defer = defer->defer_stmt.prev_defer; + } +} + +void gencontext_emit_goto(GenContext *context, Ast *ast) +{ + gencontext_emit_defer(context, ast->goto_stmt.defer.start, ast->goto_stmt.defer.end); + Ast *defer = ast->goto_stmt.label->label_stmt.defer; + while (defer != ast->goto_stmt.defer.end) + { + LLVMBuildStore(context->builder, LLVMConstInt(BACKEND_TYPE(type_bool), 0, false), + gencontext_get_defer_bool(context, defer)); + defer = defer->defer_stmt.prev_defer; + } + gencontext_emit_jmp(context, ast->goto_stmt.label->label_stmt.backend_value); +} + +void gencontext_emit_break(GenContext *context, Ast *ast) +{ + gencontext_emit_defer(context, ast->break_stmt.defers.start, ast->break_stmt.defers.end); + + assert(context->break_continue_stack_index); + gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].break_block); +} + +void gencontext_emit_continue(GenContext *context, Ast *ast) +{ + gencontext_emit_defer(context, ast->continue_stmt.defers.start, ast->continue_stmt.defers.end); + + assert(context->break_continue_stack_index); + gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].continue_block); +} + +void gencontext_emit_next_stmt(GenContext *context, Ast *ast) +{ + gencontext_emit_defer(context, ast->next_stmt.defers.start, ast->next_stmt.defers.end); + + assert(context->break_continue_stack_index); + gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].next_block); +} + +void gencontext_emit_scoped_stmt(GenContext *context, Ast *ast) +{ + gencontext_emit_stmt(context, ast->scoped_stmt.stmt); + gencontext_emit_defer(context, ast->scoped_stmt.defers.start, ast->scoped_stmt.defers.end); } void gencontext_emit_stmt(GenContext *context, Ast *ast) @@ -477,6 +565,9 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) TODO case AST_POISONED: UNREACHABLE + case AST_SCOPED_STMT: + gencontext_emit_scoped_stmt(context, ast); + break; case AST_EXPR_STMT: gencontext_emit_expr(context, ast->expr_stmt); break; @@ -484,12 +575,10 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) gencontext_emit_decl(context, ast); break; case AST_BREAK_STMT: - assert(context->break_continue_stack_index); - gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].break_block); + gencontext_emit_break(context, ast); break; case AST_CONTINUE_STMT: - assert(context->break_continue_stack_index); - gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].continue_block); + gencontext_emit_continue(context, ast); break; case AST_IF_STMT: gencontext_emit_if(context, ast); @@ -510,11 +599,16 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) gencontext_emit_do_stmt(context, ast); break; case AST_NEXT_STMT: - assert(context->break_continue_stack_index); - gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].next_block); + gencontext_emit_next_stmt(context, ast); + break; + case AST_DEFER_STMT: + if (ast->defer_stmt.emit_boolean) + { + LLVMBuildStore(context->builder, LLVMConstInt(BACKEND_TYPE(type_bool), 1, false), + gencontext_get_defer_bool(context, ast)); + } break; case AST_NOP_STMT: - case AST_DEFER_STMT: break; case AST_CATCH_STMT: case AST_TRY_STMT: @@ -539,7 +633,7 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) gencontext_emit_label(context, ast); break; case AST_GOTO_STMT: - gencontext_emit_jmp(context, ast->goto_stmt.label->label_stmt.backend_value); + gencontext_emit_goto(context, ast); break; case AST_SWITCH_STMT: gencontext_emit_switch(context, ast); diff --git a/src/compiler/parser.c b/src/compiler/parser.c index 9bf22335a..76339c9f3 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -13,7 +13,7 @@ static Expr *parse_paren_expr(Context *context); static Expr *parse_precedence(Context *context, Precedence precedence); static Expr *parse_initializer_list(Context *context); static Expr *parse_initializer(Context *context); -static bool parse_type_or_expr(Context *context, Expr **exprPtr, TypeInfo **typePtr); +static bool parse_type_or_expr(Context *context, Expr **expr_ptr, TypeInfo **type_ptr); static Decl *parse_top_level(Context *context); typedef Expr *(*ParseFn)(Context *context, Expr *); @@ -29,18 +29,31 @@ extern ParseRule rules[TOKEN_EOF + 1]; void context_store_lexer_state(Context *context) { + assert(!context->stored.has_stored && "Nested lexer store is forbidden"); + context->stored.has_stored = true; context->stored.current = context->lexer.current; context->stored.start = context->lexer.lexing_start; context->stored.tok = context->tok; context->stored.next_tok = context->next_tok; + context->stored.lead_comment = context->lead_comment; + context->stored.trailing_comment = context->trailing_comment; + context->stored.next_lead_comment = context->next_lead_comment; + context->stored.comments = vec_size(context->comments); } void context_restore_lexer_state(Context *context) { + assert(context->stored.has_stored && "Tried to restore missing stored state."); + context->stored.has_stored = false; context->lexer.current = context->stored.current; context->lexer.lexing_start = context->stored.start; context->tok = context->stored.tok; context->next_tok = context->stored.next_tok; + context->lead_comment = context->stored.lead_comment; + context->next_lead_comment = context->stored.next_lead_comment; + context->trailing_comment = context->stored.trailing_comment; + context->prev_tok_end = context->tok.span.end_loc; + vec_resize(context->comments, context->stored.comments); } inline void advance(Context *context) @@ -347,13 +360,12 @@ static Path *parse_path_prefix(Context *context) while (context->tok.type == TOKEN_IDENT && context->next_tok.type == TOKEN_SCOPE) { last_range = context->tok.span; - advance(context); - advance(context); scratch_ptr[offset++] = ':'; scratch_ptr[offset++] = ':'; len = context->tok.span.end_loc - context->tok.span.loc; memcpy(scratch_ptr + offset, context->tok.start, len); offset += len; + advance(context); advance(context); } TokenType type = TOKEN_IDENT; @@ -768,7 +780,6 @@ static inline Ast* parse_if_stmt(Context *context) { return if_ast; } - advance_and_verify(context, TOKEN_ELSE); if_ast->if_stmt.else_body = TRY_AST(parse_stmt(context)); return if_ast; } @@ -857,22 +868,7 @@ static inline Ast* parse_do_stmt(Context *context) return do_ast; } -/** - * switch - * : SWITCH '(' control_expression ')' compound_statement - * - * @return - */ -static inline Ast* parse_switch_stmt(Context *context) -{ - Ast *switch_ast = AST_NEW_TOKEN(AST_SWITCH_STMT, context->tok); - advance_and_verify(context, TOKEN_SWITCH); - CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - if (!parse_control_expression(context, &switch_ast->switch_stmt.decl, &switch_ast->switch_stmt.cond)) return &poisoned_ast; - CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); - switch_ast->switch_stmt.body = TRY_AST(parse_compound_stmt(context)); - return switch_ast; -} + /** * for_statement @@ -912,7 +908,7 @@ static inline Ast* parse_for_stmt(Context *context) if (context->tok.type != TOKEN_RPAREN) { - ast->for_stmt.incr = TRY_AST(ast_from_expr(parse_expression_list(context))); + ast->for_stmt.incr = parse_expression_list(context); } CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); @@ -926,21 +922,6 @@ static inline Expr* parse_constant_expr(Context *context) { return parse_precedence(context, PREC_TERNARY); } -/** - * case_stmt - * : CASE constant_expression ':' - * - * @return Ast* - */ -static inline Ast* parse_case_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_CASE_STMT, context->tok); - advance(context); - Expr *expr = TRY_EXPR_OR(parse_constant_expr(context), &poisoned_ast); - ast->case_stmt.expr = expr; - TRY_CONSUME(TOKEN_COLON, "Missing ':' after case"); - return extend_ast_with_prev_token(context, ast); -} static inline Ast* parse_goto_stmt(Context *context) { @@ -1136,7 +1117,7 @@ static Ast *parse_return_stmt(Context *context) advance_and_verify(context, TOKEN_RETURN); Ast *ast = AST_NEW_TOKEN(AST_RETURN_STMT, context->tok); ast->exit = EXIT_RETURN; - ast->return_stmt.defer = 0; + ast->return_stmt.defer = NULL; if (try_consume(context, TOKEN_EOS)) { ast->return_stmt.expr = NULL; @@ -1161,13 +1142,6 @@ static Ast *parse_volatile_stmt(Context *context) return ast; } -static Ast *parse_default_stmt(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_DEFAULT_STMT, context->tok); - advance_and_verify(context, TOKEN_DEFAULT); - TRY_CONSUME_OR(TOKEN_COLON, "Expected ':' after 'default'.", &poisoned_ast); - return ast; -} bool is_valid_try_statement(TokenType type) @@ -1236,7 +1210,10 @@ static bool parse_type_or_expr(Context *context, Expr **exprPtr, TypeInfo **type { // We need a little lookahead to see if this is type or expression. context_store_lexer_state(context); - advance(context); advance(context); + do + { + advance(context); advance(context); + } while (context->tok.type == TOKEN_IDENT && context->next_tok.type == TOKEN_SCOPE); if (context->tok.type == TOKEN_TYPE_IDENT && !is_expr_after_type_ident(context)) { context_restore_lexer_state(context); @@ -1282,21 +1259,106 @@ static inline Ast *parse_decl_or_expr_stmt(Context *context) if (!parse_type_or_expr(context, &expr, &type)) return &poisoned_ast; + Ast *ast; if (expr) { - CONSUME_OR(TOKEN_EOS, &poisoned_ast); - Ast *ast = AST_NEW(AST_EXPR_STMT, expr->span); + ast = AST_NEW(AST_EXPR_STMT, expr->span); ast->expr_stmt = expr; - return ast; } else { Decl *decl = TRY_DECL_OR(parse_decl_after_type(context, false, type), &poisoned_ast); - Ast *ast = AST_NEW(AST_DECLARE_STMT, decl->span); + ast = AST_NEW(AST_DECLARE_STMT, decl->span); ast->declare_stmt = decl; - CONSUME_OR(TOKEN_EOS, &poisoned_ast); - return ast; } + CONSUME_OR(TOKEN_EOS, &poisoned_ast); + return ast; +} + +static inline bool token_type_ends_case(TokenType type) +{ + return type == TOKEN_CASE || type == TOKEN_DEFAULT || type == TOKEN_RBRACE; +} + +static inline Ast *parse_case_stmts(Context *context) +{ + Ast *compound = AST_NEW_TOKEN(AST_COMPOUND_STMT, context->tok); + while (!token_type_ends_case(context->tok.type)) + { + Ast *stmt = TRY_AST(parse_stmt(context)); + vec_add(compound->compound_stmt.stmts, stmt); + } + return compound; +} + +/** + * case_stmt + * : CASE constant_expression ':' + * + * @return Ast* + */ +static inline Ast* parse_case_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_CASE_STMT, context->tok); + advance(context); + Expr *expr = TRY_EXPR_OR(parse_constant_expr(context), &poisoned_ast); + ast->case_stmt.expr = expr; + TRY_CONSUME(TOKEN_COLON, "Missing ':' after case"); + extend_ast_with_prev_token(context, ast); + ast->case_stmt.body = TRY_AST(parse_case_stmts(context)); + return ast; +} + +static Ast *parse_default_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_DEFAULT_STMT, context->tok); + advance_and_verify(context, TOKEN_DEFAULT); + TRY_CONSUME_OR(TOKEN_COLON, "Expected ':' after 'default'.", &poisoned_ast); + extend_ast_with_prev_token(context, ast); + ast->case_stmt.body = TRY_AST(parse_case_stmts(context)); + ast->case_stmt.value_type = CASE_VALUE_DEFAULT; + return ast; +} + +static inline bool parse_switch_body(Context *context, Ast *switch_ast) +{ + Ast *result; + switch (context->tok.type) + { + case TOKEN_CASE: + result = TRY_AST_OR(parse_case_stmt(context), false); + break; + case TOKEN_DEFAULT: + result = TRY_AST_OR(parse_default_stmt(context), false); + break; + default: + SEMA_TOKEN_ERROR(context->tok, "A 'case' or 'default' would be needed here, '%.*s' is not allowed.", source_range_len(context->tok.span), context->tok.start); + return false; + } + vec_add(switch_ast->switch_stmt.cases, result); + return true; +} + + +/** + * switch + * : SWITCH '(' control_expression ')' compound_statement + * + * @return + */ +static inline Ast* parse_switch_stmt(Context *context) +{ + Ast *switch_ast = AST_NEW_TOKEN(AST_SWITCH_STMT, context->tok); + advance_and_verify(context, TOKEN_SWITCH); + CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); + if (!parse_control_expression(context, &switch_ast->switch_stmt.decl, &switch_ast->switch_stmt.cond)) return &poisoned_ast; + CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); + CONSUME_OR(TOKEN_LBRACE, &poisoned_ast); + while (!try_consume(context, TOKEN_RBRACE)) + { + if (!parse_switch_body(context, switch_ast)) return &poisoned_ast; + } + return switch_ast; } static Ast *parse_stmt(Context *context) @@ -1389,7 +1451,9 @@ static Ast *parse_stmt(Context *context) case TOKEN_CONTINUE: return parse_continue_stmt(context); case TOKEN_CASE: - return parse_case_stmt(context); + SEMA_TOKEN_ERROR(context->tok, "'case' was found outside of 'switch', did you mismatch a '{ }' pair?"); + advance(context); + return &poisoned_ast; case TOKEN_BREAK: return parse_break_stmt(context); case TOKEN_NEXT: @@ -1397,7 +1461,9 @@ static Ast *parse_stmt(Context *context) case TOKEN_ASM: return parse_asm_stmt(context); case TOKEN_DEFAULT: - return parse_default_stmt(context); + SEMA_TOKEN_ERROR(context->tok, "'default' was found outside of 'switch', did you mismatch a '{ }' pair?"); + advance(context); + return &poisoned_ast; case TOKEN_CT_IF: return parse_ct_if_stmt(context); case TOKEN_CT_SWITCH: diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index b02e6bb53..923985c6c 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -31,6 +31,7 @@ static Decl *sema_resolve_path_symbol(Context *context, const char *symbol, Path assert(path && "Expected path."); *ambiguous_other_decl = NULL; Decl *decl = NULL; + bool path_found = false; VECEACH(context->imports, i) { Decl *import = context->imports[i]; @@ -39,6 +40,7 @@ static Decl *sema_resolve_path_symbol(Context *context, const char *symbol, Path // Full import, first match the subpath. if (path->len > import->import.path->len) continue; if (!matches_subpath(import->import.path, path)) continue; + path_found = true; Decl *found = module_find_symbol(import->module, symbol, MODULE_SYMBOL_SEARCH_EXTERNAL); if (!found) continue; if (decl) @@ -48,6 +50,15 @@ static Decl *sema_resolve_path_symbol(Context *context, const char *symbol, Path } decl = found; } + if (!decl) + { + if (!path_found) + { + SEMA_ERROR(path, "Unknown module %.*s.", path->len, path->module); + return &poisoned_decl; + } + return NULL; + } context_register_external_symbol(context, decl); return decl; } diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 37c204d9b..ab4e446dc 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -4,14 +4,6 @@ #include "compiler_internal.h" -typedef bool(*AstAnalysis)(Context *, Ast*); - -static inline DeferId defer_id_from_ast(Ast *ast) -{ - if (!ast) return 0; - assert(ast->ast_kind == AST_DEFER_STMT); - return ast->defer_stmt.id; -} static inline Type *ast_cond_type(Ast *ast) { assert(ast->ast_kind == AST_DECL_EXPR_LIST); @@ -42,12 +34,12 @@ static inline void context_push_scope_with_flags(Context *context, ScopeFlags fl FATAL_ERROR("Too deeply nested scopes."); } ScopeFlags previous_flags = context->current_scope->flags; - DeferId parent_defer = context->current_scope->defer_last; + Ast *parent_defer = context->current_scope->defers.start; context->current_scope++; context->current_scope->exit = EXIT_NONE; context->current_scope->local_decl_start = context->last_local; - context->current_scope->defer_top = parent_defer; - context->current_scope->defer_last = parent_defer; + context->current_scope->defers.start = parent_defer; + context->current_scope->defers.end = parent_defer; context->current_scope->flags = previous_flags | flags; context->current_scope->flags_created = flags; } @@ -57,15 +49,23 @@ static inline void context_push_scope(Context *context) context_push_scope_with_flags(context, SCOPE_NONE); } -static inline void context_pop_scope(Context *context, Ast **ast, Expr **expr) +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); +} + +static inline void context_pop_scope(Context *context) { assert(context->current_scope != &context->scopes[0]); context->last_local = context->current_scope->local_decl_start; ExitType exit_type = context->current_scope->exit; - if (context->current_scope->defer_top != context->current_scope->defer_last) - { - TODO; - } + assert (context->current_scope->defers.end == context->current_scope->defers.start); context->current_scope--; if (context->current_scope->exit < exit_type) { @@ -73,6 +73,37 @@ static inline void context_pop_scope(Context *context, Ast **ast, Expr **expr) } } +static Expr *context_pop_defers_and_wrap_expr(Context *context, Expr *expr) +{ + DeferList defers = { NULL, NULL }; + context_pop_defers_to(context, &defers); + if (defers.end == defers.start) return expr; + Expr *wrap = expr_new(EXPR_SCOPED_EXPR, expr->span); + wrap->type = expr->type; + wrap->resolve_status = RESOLVE_DONE; + wrap->expr_scope.expr = expr; + wrap->expr_scope.defers = defers; + return expr; +} + +static void context_pop_defers_and_replace_ast(Context *context, Ast *ast) +{ + DeferList defers = { NULL, NULL }; + context_pop_defers_to(context, &defers); + if (defers.end == defers.start) return; + if (ast->ast_kind == AST_DEFER_STMT) + { + assert(defers.start == ast); + *ast = *ast->defer_stmt.body; + return; + } + assert(ast->ast_kind != AST_COMPOUND_STMT); + Ast *replacement = malloc_arena(sizeof(Ast)); + *replacement = *ast; + ast->ast_kind = AST_SCOPED_STMT; + ast->scoped_stmt.stmt = replacement; + ast->scoped_stmt.defers = defers; +} static bool sema_resolve_ptr_type(Context *context, TypeInfo *type_info) @@ -107,6 +138,7 @@ static bool sema_resolve_array_type(Context *context, TypeInfo *type) type->resolve_status = RESOLVE_DONE; return true; } + static inline bool sema_analyse_struct_member(Context *context, Decl *decl) { if (decl->decl_kind == DECL_STRUCT || decl->decl_kind == DECL_UNION) @@ -174,7 +206,6 @@ static inline bool sema_analyse_struct_union(Context *context, Decl *decl) } - static inline bool sema_analyse_function_param(Context *context, Decl *param, bool is_function) { assert(param->decl_kind == DECL_VAR); @@ -294,6 +325,8 @@ static inline bool sema_analyse_compound_statement_no_scope(Context *context, As all_ok = false; } } + context_pop_defers_to(context, &compound_statement->compound_stmt.defer_list); + /* if (parent->exit < compound_statement->exit) { @@ -307,7 +340,7 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) context->current_scope->exit = EXIT_RETURN; Type *expected_rtype = context->rtype; Expr *return_expr = statement->return_stmt.expr; - statement->return_stmt.defer = VECLAST(context->defers); + statement->return_stmt.defer = context->current_scope->defers.start; if (return_expr == NULL) { if (!expected_rtype) @@ -359,50 +392,6 @@ static inline Ast *convert_expr_to_ast(Expr *expr) ast->expr_stmt = expr; return ast; } -static inline Expr *convert_decl_to_expr(Context *context, Decl *decl) -{ - assert(decl->decl_kind == DECL_VAR); - assert(decl->decl_kind == VARDECL_LOCAL); - if (!decl->var.init_expr) return NULL; - Expr *assign_expr = expr_new(EXPR_BINARY, decl->span); - assign_expr->resolve_status = RESOLVE_DONE; - Expr *identifier = expr_new(EXPR_IDENTIFIER, decl->span); - identifier->resolve_status = RESOLVE_DONE; - identifier->identifier_expr.identifier = decl->name; - identifier->identifier_expr.decl = decl; - assign_expr->binary_expr.left = identifier; - assign_expr->binary_expr.right = decl->var.init_expr; - assign_expr->binary_expr.operator = BINARYOP_ASSIGN; - // Possibly not right v TODO - identifier->type = decl->var.type_info->type; - assign_expr->type = decl->var.init_expr->type; - return assign_expr; -} - -static inline bool convert_decl_for_cond(Context *context, Decl *decl, Ast*** stmt_list, Expr** last, bool is_last) -{ - Expr *expr = convert_decl_to_expr(context, decl); - if (!expr) - { - if (is_last) - { - SEMA_ERROR(decl, "Expected an initializer for '%s'.", decl->name); - return false; - } - // Simply skip declarations if they don't have an initializer, since they're already registered anyway. - return true; - } - if (is_last) - { - *last = expr; - } - else - { - Ast *stmt = convert_expr_to_ast(expr); - *stmt_list = VECADD(*stmt_list, stmt); - } - return true; -} static inline bool sema_analyse_function_block_stmt(Context *context, Ast *stmt) @@ -453,7 +442,7 @@ static inline bool sema_analyse_cond(Context *context, Ast *stmt, bool cast_to_b return false; } if (cast_to_bool && init->type->type_kind != TYPE_BOOL && - cast_to_bool_kind(last->declare_stmt->var.type_info->type) == CAST_ERROR) + cast_to_bool_kind(last->declare_stmt->var.type_info->type) == CAST_ERROR) { SEMA_ERROR(last->declare_stmt->var.init_expr, "The expression needs to be convertible to a boolean."); return false; @@ -473,12 +462,16 @@ static inline bool sema_analyse_while_stmt(Context *context, Ast *statement) context_push_scope(context); bool success = !decl || sema_analyse_statement(context, decl); context_push_scope(context); + success = success && sema_analyse_cond(context, cond, true); context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) success = success && sema_analyse_statement(context, body); - context_pop_scope(context, &body, NULL); - context_pop_scope(context, &cond, NULL); - context_pop_scope(context, &decl, NULL); + context_pop_defers_and_replace_ast(context, body); + context_pop_scope(context); + context_pop_defers_and_replace_ast(context, cond); + context_pop_scope(context); + context_pop_defers_and_replace_ast(context, statement); + context_pop_scope(context); if (!success) return false; return success; } @@ -490,16 +483,17 @@ static inline bool sema_analyse_do_stmt(Context *context, Ast *statement) bool success; context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) success = sema_analyse_statement(context, body); - context_pop_scope(context, &body, NULL); + context_pop_defers_and_replace_ast(context, body); + context_pop_scope(context); if (!success) return false; context_push_scope(context); success = sema_analyse_expr(context, type_bool, expr); - context_pop_scope(context, NULL, &expr); + statement->do_stmt.expr = context_pop_defers_and_wrap_expr(context, expr); + context_pop_scope(context); return success; } - static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement) { Decl *decl = statement->declare_stmt; @@ -520,17 +514,16 @@ static inline bool sema_analyse_defer_stmt(Context *context, Ast *statement) bool success = sema_analyse_statement(context, statement->defer_stmt.body); - context_pop_scope(context, &statement->defer_stmt.body, NULL); + context_pop_scope(context); if (!success) return false; - statement->defer_stmt.prev_defer = VECLAST(context->defers); - vec_add(context->defers, statement); - statement->defer_stmt.id = vec_size(context->defers); + statement->defer_stmt.prev_defer = context->current_scope->defers.start; + context->current_scope->defers.start = statement; return true; } -static inline bool sema_analyse_default_stmt(Context *context, Ast *statement) +static inline bool sema_analyse_default_stmt(Context *context __unused, Ast *statement) { SEMA_ERROR(statement, "Unexpected 'default' outside of switch"); return false; @@ -550,17 +543,21 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) { // Conditional scope start context_push_scope(context); - success = sema_analyse_expr(context, type_bool, statement->for_stmt.cond); + Expr *cond = statement->for_stmt.cond; + success = sema_analyse_expr(context, type_bool, cond); + statement->for_stmt.cond = context_pop_defers_and_wrap_expr(context, cond); // Conditional scope end - context_pop_scope(context, NULL, &statement->for_stmt.cond); + context_pop_scope(context); } if (success && statement->for_stmt.incr) { // Incr scope start context_push_scope(context); - success = sema_analyse_statement(context, statement->for_stmt.incr); + Expr *incr = statement->for_stmt.incr; + success = sema_analyse_expr(context, NULL, incr); + statement->for_stmt.incr = context_pop_defers_and_wrap_expr(context, incr); // Incr scope end - context_pop_scope(context, &statement->for_stmt.incr, NULL); + context_pop_scope(context); } if (!success) return false; @@ -568,20 +565,23 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) success = sema_analyse_statement(context, statement->for_stmt.body); // End for body scope - context_pop_scope(context, &statement->for_stmt.body, NULL); + context_pop_defers_and_replace_ast(context, statement->for_stmt.body); + context_pop_scope(context); + context_pop_defers_and_replace_ast(context, statement); // End for scope - context_pop_scope(context, &statement, NULL); + context_pop_scope(context); return success; } static inline bool sema_analyse_goto_stmt(Context *context, Ast *statement) { + + statement->goto_stmt.defer = context->current_scope->defers; VECEACH(context->labels, i) { Ast *label = context->labels[i]; if (statement->goto_stmt.label_name == label->label_stmt.name) { - statement->goto_stmt.type = GOTO_JUMP_BACK; label->label_stmt.is_used = true; statement->goto_stmt.label = label; } @@ -604,7 +604,8 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) { if (statement->if_stmt.then_body->ast_kind != AST_COMPOUND_STMT) { - SEMA_ERROR(statement->if_stmt.then_body, "if-statements with an 'else' must use '{ }' even around a single statement."); + SEMA_ERROR(statement->if_stmt.then_body, + "if-statements with an 'else' must use '{ }' even around a single statement."); success = false; } if (success && statement->if_stmt.else_body->ast_kind != AST_COMPOUND_STMT) @@ -614,22 +615,19 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) success = false; } } - context_push_scope(context); success = success && sema_analyse_statement(context, statement->if_stmt.then_body); - context_pop_scope(context, &statement->if_stmt.then_body, NULL); - // TODO null flowcheck if (statement->if_stmt.else_body) { - context_push_scope(context); success = success && sema_analyse_statement(context, statement->if_stmt.else_body); - context_pop_scope(context, &statement->if_stmt.else_body, NULL); } - context_pop_scope(context, &statement, NULL); + context_pop_defers_and_replace_ast(context, statement); + context_pop_scope(context); return success; } static inline bool sema_analyse_label(Context *context, Ast *statement) { + statement->label_stmt.defer = context->current_scope->defers.start; VECEACH(context->labels, i) { Ast *label = context->labels[i]; @@ -648,7 +646,6 @@ static inline bool sema_analyse_label(Context *context, Ast *statement) Ast *the_goto = context->gotos[i]; if (the_goto->goto_stmt.label_name == statement->label_stmt.name) { - the_goto->goto_stmt.type = GOTO_JUMP_FORWARD; the_goto->goto_stmt.label = statement; statement->label_stmt.is_used = true; break; @@ -657,18 +654,13 @@ static inline bool sema_analyse_label(Context *context, Ast *statement) return true; } -static inline bool sema_analyse_nop_stmt(Context *context, Ast *statement) -{ - return true; -} - -static bool sema_analyse_catch_stmt(Context *context, Ast *statement) +static bool sema_analyse_catch_stmt(Context *context __unused, Ast *statement __unused) { TODO } -static bool sema_analyse_asm_stmt(Context *context, Ast *statement) +static bool sema_analyse_asm_stmt(Context *context __unused, Ast *statement __unused) { TODO } @@ -681,6 +673,30 @@ static bool sema_analyse_break_stmt(Context *context, Ast *statement) SEMA_ERROR(statement, "'break' is not allowed here."); return false; } + DynamicScope *scope = context->current_scope; + statement->break_stmt.defers.start = scope->defers.start; + while (!(scope->flags_created & SCOPE_BREAK)) // NOLINT(hicpp-signed-bitwise) + { + scope--; + } + statement->break_stmt.defers.end = scope->defers.end; + return true; +} + +static bool sema_analyse_next_stmt(Context *context, Ast *statement) +{ + if (!(context->current_scope->flags & SCOPE_NEXT)) // NOLINT(hicpp-signed-bitwise) + { + SEMA_ERROR(statement, "'next' is not allowed here."); + return false; + } + DynamicScope *scope = context->current_scope; + statement->next_stmt.defers.start = scope->defers.start; + while (!(scope->flags_created & SCOPE_NEXT)) // NOLINT(hicpp-signed-bitwise) + { + scope--; + } + statement->next_stmt.defers.end = scope->defers.end; return true; } @@ -697,6 +713,13 @@ static bool sema_analyse_continue_stmt(Context *context, Ast *statement) SEMA_ERROR(statement, "'continue' is not allowed here."); return false; } + DynamicScope *scope = context->current_scope; + statement->continue_stmt.defers.start = scope->defers.start; + while (!(scope->flags_created & SCOPE_CONTINUE)) // NOLINT(hicpp-signed-bitwise) + { + scope--; + } + statement->continue_stmt.defers.end = scope->defers.end; return true; } @@ -754,112 +777,99 @@ static bool sema_analyse_ct_if_stmt(Context *context, Ast *statement) } } -static bool sema_analyse_switch_case(Context *context, Ast*** prev_cases, Ast *case_stmt, Type *switch_type, Ast **prev_case) + +static bool sema_analyse_case_expr(Context *context, Ast *case_stmt) { - if (case_stmt->ast_kind == AST_CASE_STMT) + Expr *case_expr = case_stmt->case_stmt.expr; + // TODO handle enums + if (!sema_analyse_expr(context, NULL, case_expr)) return false; + if (case_expr->expr_kind != EXPR_CONST) { - if (*prev_case) - { - // sema_build_defer_chain(context, prev_cases); - context_pop_scope(context, prev_case, NULL); - *prev_case = NULL; - } - Expr *case_expr = case_stmt->case_stmt.expr; - if (!sema_analyse_expr(context, switch_type, case_expr)) return false; - if (case_expr->expr_kind != EXPR_CONST) - { - SEMA_ERROR(case_expr, "This must be a constant expression."); - return false; - } - assert(case_expr->const_expr.type == CONST_INT); - case_stmt->case_stmt.value_type = type_is_signed(case_expr->type->canonical) ? CASE_VALUE_INT : CASE_VALUE_UINT; - uint64_t val = case_expr->const_expr.i; - case_stmt->case_stmt.val = val; - *prev_case = case_stmt; - VECEACH(*prev_cases, i) - { - if ((*prev_cases)[i]->case_stmt.val == val) - { - SEMA_ERROR(case_stmt, "Duplicate case value."); - sema_prev_at_range((*prev_cases)[i]->span, "Previous use was here."); - return false; - } - } - context_push_scope_with_flags(context, SCOPE_BREAK); - vec_add(*prev_cases, case_stmt); - return true; - } - if (case_stmt->ast_kind == AST_DEFAULT_STMT) - { - case_stmt->ast_kind = AST_CASE_STMT; - case_stmt->case_stmt.value_type = CASE_VALUE_DEFAULT; - case_stmt->case_stmt.block = NULL; - if (*prev_case) - { - context_pop_scope(context, prev_case, NULL); - } - context_push_scope(context); - *prev_case = case_stmt; - vec_add(*prev_cases, case_stmt); - return true; - } - if (!*prev_case) - { - SEMA_ERROR(case_stmt, "Expected a 'case' or 'default' statement."); + SEMA_ERROR(case_expr, "This must be a constant expression."); return false; } - if (case_stmt->ast_kind == AST_NEXT_STMT) + if (case_expr->const_expr.type != CONST_INT) { - case_stmt->next_stmt = *prev_case; - (*prev_case)->case_stmt.has_next = true; + SEMA_ERROR(case_expr, "The 'case' value must be an integer constant."); + return false; } - else - { - if (!sema_analyse_statement(context, case_stmt)) return false; - } - if (!(*prev_case)->case_stmt.block) - { - (*prev_case)->case_stmt.block = AST_NEW(AST_COMPOUND_STMT, (*prev_case)->span); - } - vec_add((*prev_case)->case_stmt.block->compound_stmt.stmts, case_stmt); + assert(case_expr->const_expr.type == CONST_INT); + case_stmt->case_stmt.value_type = type_is_signed(case_expr->type->canonical) ? CASE_VALUE_INT : CASE_VALUE_UINT; + uint64_t val = case_expr->const_expr.i; + case_stmt->case_stmt.val = val; return true; } static bool sema_analyse_switch_stmt(Context *context, Ast *statement) { context_push_scope(context); - bool success = sema_analyse_statement(context, statement->switch_stmt.cond); Ast *cond = statement->switch_stmt.cond; - success = success && sema_analyse_cond(context, cond, false); - context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_NEXT); // NOLINT(hicpp-signed-bitwise) - Ast *body = statement->switch_stmt.body; - assert(body->ast_kind == AST_COMPOUND_STMT); + bool success = sema_analyse_cond(context, cond, false); + Type *switch_type = ast_cond_type(cond)->canonical; if (!type_is_integer(switch_type)) { SEMA_ERROR(cond, "Expected an integer or enum type, was '%s'.", type_to_error_string(switch_type)); return false; } - Ast *in_case = NULL; - VECEACH(body->compound_stmt.stmts, i) + Ast *default_case = NULL; + assert(context->current_scope->defers.start == context->current_scope->defers.end); + VECEACH(statement->switch_stmt.cases, i) { - success = success && sema_analyse_switch_case(context, &statement->switch_stmt.cases, body->compound_stmt.stmts[i], switch_type, &in_case); + Ast *stmt = statement->switch_stmt.cases[i]; + switch (stmt->ast_kind) + { + case AST_CASE_STMT: + if (!sema_analyse_case_expr(context, stmt)) + { + success = false; + break; + } + for (unsigned j = 0; j < i; j++) + { + Ast *other = statement->switch_stmt.cases[j]; + if (other->ast_kind == AST_CASE_STMT && other->case_stmt.val == stmt->case_stmt.val) + { + SEMA_ERROR(stmt, "The same case value appears more than once."); + SEMA_PREV(other, "Here is the previous use of that value."); + success = false; + } + } + break; + case AST_DEFAULT_STMT: + if (default_case) + { + SEMA_ERROR(stmt, "'default' may only appear once in a single 'switch', please remove one."); + SEMA_PREV(default_case, "Here is the previous use."); + success = false; + } + default_case = stmt; + break; + default: + UNREACHABLE; + } + context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_NEXT); + success = success && sema_analyse_compound_statement_no_scope(context, stmt->case_stmt.body); + context_pop_scope(context); } - if (in_case) + context_pop_defers_and_replace_ast(context, statement); + context_pop_scope(context); + if (!success) return false; + // Is this a typeless switch value? + if (switch_type->type_kind == TYPE_UXX || switch_type->type_kind == TYPE_IXX) { - context_pop_scope(context, &in_case, NULL); + + TODO } - context_pop_scope(context, &cond, NULL); - context_pop_scope(context, &statement, NULL); return success; } -static bool sema_analyse_try_stmt(Context *context, Ast *statement) +static bool sema_analyse_try_stmt(Context *context __unused, Ast *statement __unused) { TODO } -static bool sema_analyse_throw_stmt(Context *context, Ast *statement) +static bool sema_analyse_throw_stmt(Context *context __unused, Ast *statement __unused) { TODO } @@ -877,7 +887,7 @@ static bool sema_analyse_compound_stmt(Context *context, Ast *statement) { context_push_scope(context); bool success = sema_analyse_compound_statement_no_scope(context, statement); - context_pop_scope(context, &statement, NULL); + context_pop_scope(context); return success; } @@ -888,6 +898,7 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement case AST_POISONED: return false; case AST_ATTRIBUTE: + case AST_SCOPED_STMT: UNREACHABLE case AST_ASM_STMT: return sema_analyse_asm_stmt(context, statement); @@ -922,7 +933,7 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement case AST_LABEL: return sema_analyse_label(context, statement); case AST_NOP_STMT: - return sema_analyse_nop_stmt(context, statement); + return true; case AST_RETURN_STMT: return sema_analyse_return_stmt(context, statement); case AST_SWITCH_STMT: @@ -932,7 +943,7 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement case AST_TRY_STMT: return sema_analyse_try_stmt(context, statement); case AST_NEXT_STMT: - UNREACHABLE + return sema_analyse_next_stmt(context, statement); case AST_VOLATILE_STMT: return sema_analyse_volatile_stmt(context, statement); case AST_WHILE_STMT: @@ -1007,6 +1018,7 @@ static inline bool sema_analyse_function_body(Context *context, Decl *func) return false; } } + VECEACH(context->gotos, i) { Ast *goto_stmt = context->gotos[i]; @@ -1018,14 +1030,15 @@ static inline bool sema_analyse_function_body(Context *context, Decl *func) } // If there are no defers, then that's fine. - if (!goto_stmt->goto_stmt.defer && !label_target->label_stmt.defer) continue; - - // First we need to search for the common depth. - int label_depth = defer_depth(label_target->label_stmt.defer); - int goto_depth = defer_depth(goto_stmt->goto_stmt.defer); + if (!goto_stmt->goto_stmt.defer.start && !label_target->label_stmt.defer) continue; Ast *common_depth_label = label_target->label_stmt.defer; - Ast *common_depth_goto = goto_stmt->goto_stmt.defer; + Ast *common_depth_goto = goto_stmt->goto_stmt.defer.start; + + // First we need to search for the common depth. + int label_depth = defer_depth(common_depth_label); + int goto_depth = defer_depth(common_depth_goto); + // Now walk up to the common depth. defer_list_walk_to_common_depth(&common_depth_label, label_depth, goto_depth); @@ -1040,17 +1053,18 @@ static inline bool sema_analyse_function_body(Context *context, Decl *func) } // We now know the top defer (which we won't actually generate) - goto_stmt->goto_stmt.defer_end = common_depth_goto; + goto_stmt->goto_stmt.defer.end = common_depth_goto; // Mark all defers that occur on the way "up" to the common depth conditional. Ast *current = label_target->label_stmt.defer; while (current != common_depth_goto) { current->defer_stmt.emit_boolean = true; + current = current->defer_stmt.prev_defer; } } func->func.labels = context->labels; - context_pop_scope(context, &func->func.body, NULL); + context_pop_scope(context); context->current_scope = NULL; return true; } @@ -1062,7 +1076,9 @@ static inline bool sema_analyse_method_function(Context *context, Decl *decl) if (!sema_resolve_type_info(context, parent_type)) return false; if (!type_may_have_method_functions(parent_type->type)) { - SEMA_ERROR(decl, "Method functions can not be associated with '%s'", type_to_error_string(decl->func.type_parent->type)); + SEMA_ERROR(decl, + "Method functions can not be associated with '%s'", + type_to_error_string(decl->func.type_parent->type)); return false; } Decl *parent = parent_type->type->decl; @@ -1101,7 +1117,7 @@ static inline bool sema_analyse_func(Context *context, Decl *decl) } decl->visibility = VISIBLE_EXTERN; } - DEBUG_LOG("Function analysis done.") + DEBUG_LOG("Function analysis done."); return true; } @@ -1279,7 +1295,6 @@ bool sema_analyse_decl(Context *context, Decl *decl) } - static void append_decls(Context *context, Decl **decls) { VECEACH(decls, i) @@ -1287,6 +1302,7 @@ static void append_decls(Context *context, Decl **decls) context_register_global_decl(context, decls[i]); } } + static inline bool sema_analyse_top_level_if(Context *context, Decl *ct_if) { int res = sema_check_comp_time_bool(context, ct_if->ct_if_decl.expr); @@ -1387,7 +1403,16 @@ bool sema_resolve_type_info(Context *context, TypeInfo *type_info) static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info) { Decl *ambiguous_decl; - Decl *decl = sema_resolve_symbol(context, type_info->unresolved.name_loc.string, type_info->unresolved.path, &ambiguous_decl); + Decl *decl = sema_resolve_symbol(context, + type_info->unresolved.name_loc.string, + type_info->unresolved.path, + &ambiguous_decl); + + // Already handled + if (!decl_ok(decl)) + { + return type_info_poison(type_info); + } if (!decl) { @@ -1397,8 +1422,11 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info) if (ambiguous_decl) { - SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "Ambiguous type '%s' – both defined in %s and %s, please add the module name to resolve the ambiguity", type_info->unresolved.name_loc.string, - decl->module->name->module, ambiguous_decl->module->name->module); + SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, + "Ambiguous type '%s' – both defined in %s and %s, please add the module name to resolve the ambiguity", + type_info->unresolved.name_loc.string, + decl->module->name->module, + ambiguous_decl->module->name->module); return type_info_poison(type_info); } switch (decl->decl_kind) @@ -1453,7 +1481,9 @@ bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info) if (type_info->resolve_status == RESOLVE_RUNNING) { // TODO this is incorrect for unresolved expressions - SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "Circular dependency resolving type '%s'.", type_info->unresolved.name_loc.string); + SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, + "Circular dependency resolving type '%s'.", + type_info->unresolved.name_loc.string); return type_info_poison(type_info); } diff --git a/src/utils/common.h b/src/utils/common.h index 4d3f23ba5..31c18adf8 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -15,4 +15,7 @@ #include #define MAX_IDENTIFIER_LENGTH 31 -#define PROJECT_TOML "project.toml" \ No newline at end of file +#define PROJECT_TOML "project.toml" +#ifndef __unused +#define __unused +#endif \ No newline at end of file diff --git a/src/utils/errors.h b/src/utils/errors.h index 4f793c751..e9264f53f 100644 --- a/src/utils/errors.h +++ b/src/utils/errors.h @@ -27,7 +27,7 @@ void error_exit(const char *format, ...) __attribute__((noreturn)); #ifndef NDEBUG -#define DEBUG_LOG(_string, ...) eprintf("-- DEBUG: "); eprintf(_string, ##__VA_ARGS__); eprintf("\n"); +#define DEBUG_LOG(_string, ...) eprintf("-- DEBUG: "); eprintf(_string, ##__VA_ARGS__); eprintf("\n") #else #define DEBUG_LOG(_string, ...) #endif