diff --git a/README.md b/README.md index d3bc84049..97c606a5a 100644 --- a/README.md +++ b/README.md @@ -96,10 +96,11 @@ work on Windows. Also, parts of the code is still rough and needs to be made sol - [x] Built-in linking - [x] CT only macros evaluating to constants - [x] range initializers e.g. `{ [1..2] = 2 }` +- [x] Trailing body macros e.g. `@foo(1, 100; int a) { bar(a); };` - [ ] Anonymous structs - [ ] Complete C ABI conformance *in progress* - [ ] Debug info *in progress* -- [ ] Virtual type +- [ ] Virtual type *in progress* - [ ] Enum associated data support - [ ] Windows support - [ ] All attributes @@ -111,7 +112,6 @@ work on Windows. Also, parts of the code is still rough and needs to be made sol - [ ] Complex macros - [ ] Escape macros - [ ] Implicit capturing macros -- [ ] Trailing body macros - [ ] Subarray initializers - [ ] Bitstructs - [ ] `asm` section @@ -120,7 +120,6 @@ work on Windows. Also, parts of the code is still rough and needs to be made sol - [ ] Pre-post conditions - [ ] Stdlib inclusion - [ ] String functions -- [ ] Compile time incremental arrays - [ ] Simd vector types #### What can you help with? diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 8bbd763f3..9cfe7c0c4 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -1082,6 +1082,10 @@ static void fprint_ast_recursive(Context *context, FILE *file, Ast *ast, int ind if (!ast) return; switch (ast->ast_kind) { + case AST_YIELD_STMT: + DUMP("(yield"); + printf("Missing data\n"); + DUMPEND(); case AST_CT_ASSERT: DUMP("($assert"); DUMPEXPR(ast->ct_assert_stmt.expr); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 4cfee9da7..f14099dd1 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -290,7 +290,7 @@ typedef struct VarDecl_ union { void *backend_debug_ref; - void *scope; + unsigned scope_depth; }; union { @@ -414,6 +414,7 @@ typedef struct { bool failable : 1; Decl **parameters; + Decl **body_parameters; TypeInfo *rtype; // May be null! struct Ast_ *body; } MacroDecl; @@ -422,6 +423,7 @@ typedef struct { struct Ast_ **cases; Decl **parameters; + Decl **body_parameters; TypeInfo *rtype; // May be null! Path *path; // For redefinition } GenericDecl; @@ -461,7 +463,7 @@ typedef struct bool next_target : 1; void *break_target; void *continue_target; - ScopeId scope_id; + AstId scope_defer; AstId parent; } LabelDecl; @@ -481,6 +483,7 @@ typedef struct Decl_ bool needs_additional_pad : 1; bool is_substruct : 1; bool has_variable_array : 1; + bool has_body_param : 1; void *backend_ref; const char *cname; AlignSize alignment; @@ -593,6 +596,8 @@ typedef struct bool unsplat_last : 1; Expr *function; Expr **arguments; + Decl **body_arguments; + Ast *body; } ExprCall; typedef struct @@ -912,7 +917,7 @@ typedef struct { struct { - ScopeId scope_id; + Ast* scope_defer; }; struct { @@ -963,7 +968,7 @@ typedef struct FlowCommon flow; bool is_switch : 1; bool has_err_var : 1; - ScopeId scope_id; + Ast* scope_defer; AstId defer; union { @@ -1079,6 +1084,13 @@ typedef struct Expr *expr; } AstAssertStmt; +typedef struct +{ + Expr **values; + Decl **declarations; + Ast *ast; +} AstYieldStmt; + typedef struct { DocDirectiveKind kind; @@ -1144,6 +1156,7 @@ typedef struct Ast_ AstAssertStmt assert_stmt; Ast **directives; AstDocDirective doc_directive; + AstYieldStmt yield_stmt; }; } Ast; @@ -1178,10 +1191,24 @@ typedef struct DynamicScope_ bool jump_end : 1; ScopeFlags flags; Decl **local_decl_start; + Decl **current_local; AstId defer_last; + AstId defer_start; Ast *in_defer; + unsigned depth; } DynamicScope; +typedef struct MacroScope_ +{ + Decl *macro; + Decl **locals_start; + unsigned depth; + Decl **yield_symbol_start; + Decl **yield_symbol_end; + Decl **yield_args; + Ast *yield_body; + bool in_yield; +} MacroScope; typedef union { @@ -1252,7 +1279,6 @@ typedef struct Context_ AstId next_target; Ast *next_switch; AstId next_defer; - DynamicScope *current_scope; struct { Type *expected_block_type; @@ -1264,19 +1290,13 @@ typedef struct Context_ Type *rtype; bool failable_return; int in_volatile_section; - struct - { - Decl **macro_locals_start; - int macro_counter; - int macro_nesting; - }; - Decl **last_local; + MacroScope macro_scope; struct { STable external_symbols; Decl **external_symbol_list; }; Decl* locals[MAX_LOCALS]; - DynamicScope scopes[MAX_SCOPE_DEPTH]; + DynamicScope active_scope; Lexer *lexer; Token tok; TokenId prev_tok; diff --git a/src/compiler/copying.c b/src/compiler/copying.c index a74fcf02f..d2668c740 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -337,6 +337,9 @@ Ast *copy_ast(Ast *source) case AST_RETURN_STMT: MACRO_COPY_EXPR(ast->return_stmt.expr); return ast; + case AST_SCOPED_STMT: + MACRO_COPY_AST(ast->scoped_stmt.stmt); + return ast; case AST_SWITCH_STMT: copy_flow(ast); MACRO_COPY_EXPR(ast->switch_stmt.cond); @@ -356,8 +359,8 @@ Ast *copy_ast(Ast *source) MACRO_COPY_EXPR(ast->while_stmt.cond); MACRO_COPY_AST(ast->while_stmt.body); return ast; - case AST_SCOPED_STMT: - MACRO_COPY_AST(ast->scoped_stmt.stmt); + case AST_YIELD_STMT: + MACRO_COPY_EXPR_LIST(ast->yield_stmt.values); return ast; } UNREACHABLE; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index dfbab9141..97894bd96 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -77,6 +77,7 @@ typedef enum AST_UNREACHABLE_STMT, AST_VOLATILE_STMT, AST_WHILE_STMT, + AST_YIELD_STMT, AST_SCOPED_STMT, } AstKind; @@ -438,6 +439,7 @@ typedef enum TOKEN_VIRTUAL, TOKEN_VOLATILE, TOKEN_WHILE, + TOKEN_YIELD, TOKEN_CT_ASSERT, // $assert TOKEN_CT_CASE, // $case diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 0fe78d712..44b0bee82 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -6,12 +6,16 @@ void gencontext_emit_try_stmt(GenContext *context, Ast *pAst); -bool ast_is_empty_compound_stmt(Ast *ast) + +static bool ast_is_not_empty(Ast *ast) { - if (ast->ast_kind != AST_COMPOUND_STMT) return false; - return !vec_size(ast->compound_stmt.stmts) && ast->compound_stmt.defer_list.start == ast->compound_stmt.defer_list.end; + if (!ast) return false; + if (ast->ast_kind != AST_COMPOUND_STMT) return true; + if (vec_size(ast->compound_stmt.stmts)) return true; + return ast->compound_stmt.defer_list.start != ast->compound_stmt.defer_list.end; } + void llvm_emit_compound_stmt(GenContext *context, Ast *ast) { if (llvm_use_debug(context)) @@ -247,13 +251,13 @@ void llvm_emit_if(GenContext *c, Ast *ast) LLVMBasicBlockRef else_block = exit_block; // Only generate a target if - if (!ast_is_empty_compound_stmt(ast->if_stmt.then_body)) + if (ast_is_not_empty(ast->if_stmt.then_body)) { then_block = llvm_basic_block_new(c, "if.then"); } // We have an optional else block. - if (ast->if_stmt.else_body && !ast_is_empty_compound_stmt(ast->if_stmt.else_body)) + if (ast_is_not_empty(ast->if_stmt.else_body)) { else_block = llvm_basic_block_new(c, "if.else"); } @@ -337,7 +341,7 @@ void gencontext_emit_for_stmt(GenContext *c, Ast *ast) // We have 3 optional parts, which makes this code bit complicated. LLVMBasicBlockRef exit_block = llvm_basic_block_new(c, "for.exit"); LLVMBasicBlockRef inc_block = ast->for_stmt.incr ? llvm_basic_block_new(c, "for.inc") : NULL; - LLVMBasicBlockRef body_block = ast->for_stmt.body->compound_stmt.stmts ? llvm_basic_block_new(c, "for.body") : NULL; + LLVMBasicBlockRef body_block = ast_is_not_empty(ast->for_stmt.body) ? llvm_basic_block_new(c, "for.body") : NULL; LLVMBasicBlockRef cond_block = ast->for_stmt.cond ? llvm_basic_block_new(c, "for.cond") : NULL; // Break is simple it always jumps out. @@ -1193,6 +1197,26 @@ void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *pani llvm_emit_block(c, ok_block); } +void llvm_emit_yield_stmt(GenContext *c, Ast *ast) +{ + Decl **declarations = ast->yield_stmt.declarations; + Expr **values = ast->yield_stmt.values; + // Create backend refs on demand. + foreach(declarations, i) + { + Decl *decl = declarations[i]; + if (!decl->backend_ref) llvm_emit_local_var_alloca(c, decl); + } + // Set the values + foreach(values, i) + { + Expr *expr = values[i]; + BEValue value; + llvm_emit_expr(c, &value, expr); + llvm_store_bevalue_aligned(c, declarations[i]->backend_ref, &value, declarations[i]->alignment); + } + llvm_emit_stmt(c, ast->yield_stmt.ast); +} void llvm_emit_stmt(GenContext *c, Ast *ast) { @@ -1205,6 +1229,9 @@ void llvm_emit_stmt(GenContext *c, Ast *ast) case AST_POISONED: case AST_DEFINE_STMT: UNREACHABLE + case AST_YIELD_STMT: + llvm_emit_yield_stmt(c, ast); + break; case AST_TRY_STMT: gencontext_emit_try_stmt(c, ast); break; diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 6400bc334..67cf70a15 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -455,17 +455,37 @@ static Expr *parse_call_expr(Context *context, Expr *left) Expr **params = NULL; advance_and_verify(context, TOKEN_LPAREN); bool unsplat = false; + Decl **body_args = NULL; if (!TOKEN_IS(TOKEN_RPAREN)) { if (!parse_param_list(context, ¶ms, TOKEN_RPAREN, &unsplat)) return poisoned_expr; } - TRY_CONSUME_OR(TOKEN_RPAREN, "Expected the ending ')' here", poisoned_expr); + if (try_consume(context, TOKEN_EOS) && left->expr_kind == EXPR_MACRO_IDENTIFIER) + { + if (!parse_macro_argument_declarations(context, VISIBLE_LOCAL, &body_args, false)) return poisoned_expr; + } + if (!TOKEN_IS(TOKEN_RPAREN)) + { + SEMA_TOKID_ERROR(context->prev_tok, "Expected the ending ')' here."); + return poisoned_expr; + } + advance(context); Expr *call = EXPR_NEW_EXPR(EXPR_CALL, left); call->call_expr.function = left; call->call_expr.arguments = params; call->call_expr.unsplat_last = unsplat; + call->call_expr.body_arguments = body_args; RANGE_EXTEND_PREV(call); + if (body_args && !TOKEN_IS(TOKEN_LBRACE)) + { + SEMA_TOKEN_ERROR(context->tok, "Expected a macro body here."); + return poisoned_expr; + } + if (TOKEN_IS(TOKEN_LBRACE) && left->expr_kind == EXPR_MACRO_IDENTIFIER) + { + call->call_expr.body = TRY_AST_OR(parse_compound_stmt(context), poisoned_expr); + } return call; } diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index fcf6462e1..2857fc7aa 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1381,13 +1381,15 @@ static inline Ast *parse_generics_statements(Context *context) return ast; } -static bool parse_macro_arguments(Context *context, Visibility visibility, Decl ***params_ref) +bool parse_macro_argument_declarations(Context *context, Visibility visibility, Decl ***params_ref, bool allow_vararg) { - CONSUME_OR(TOKEN_LPAREN, false); - *params_ref = NULL; bool vararg = false; - while (!try_consume(context, TOKEN_RPAREN)) + while (!TOKEN_IS(TOKEN_EOS) && !TOKEN_IS(TOKEN_RPAREN)) { + if (*params_ref) + { + TRY_CONSUME(TOKEN_COMMA, false); + } TypeInfo *parm_type = NULL; VarDeclKind param_kind; TEST_TYPE: @@ -1431,14 +1433,14 @@ static bool parse_macro_arguments(Context *context, Visibility visibility, Decl return false; } // We either have "... var" or "int... var" - if (try_consume(context, TOKEN_ELLIPSIS)) + if (allow_vararg && try_consume(context, TOKEN_ELLIPSIS)) { vararg = true; } else { parm_type = TRY_TYPE_OR(parse_type(context), false); - if (try_consume(context, TOKEN_ELLIPSIS)) + if (allow_vararg && try_consume(context, TOKEN_ELLIPSIS)) { vararg = true; } @@ -1449,9 +1451,25 @@ static bool parse_macro_arguments(Context *context, Visibility visibility, Decl param->var.vararg = vararg; advance(context); vec_add(*params_ref, param); - COMMA_RPAREN_OR(false); + TokenType current_token = context->tok.type; } return true; + +} +static bool parse_macro_arguments(Context *context, Visibility visibility, Decl ***params_ref, Decl ***body_params, bool *has_trailing_body) +{ + CONSUME_OR(TOKEN_LPAREN, false); + *params_ref = NULL; + *body_params = NULL; + *has_trailing_body = false; + if (!parse_macro_argument_declarations(context, visibility, params_ref, true)) return false; + if (try_consume(context, TOKEN_EOS)) + { + *has_trailing_body = true; + if (!parse_macro_argument_declarations(context, visibility, body_params, true)) return false; + } + TRY_CONSUME(TOKEN_RPAREN, false); + return true; } /** * generics_declaration @@ -1482,7 +1500,9 @@ static inline Decl *parse_generics_declaration(Context *context, Visibility visi decl->generic_decl.path = path; if (!consume_ident(context, "generic function name")) return poisoned_decl; decl->generic_decl.rtype = rtype; - if (!parse_macro_arguments(context, visibility, &decl->generic_decl.parameters)) return poisoned_decl; + bool trailing_body = false; + if (!parse_macro_arguments(context, visibility, &decl->generic_decl.parameters, &decl->generic_decl.body_parameters, &trailing_body)) return poisoned_decl; + decl->has_body_param = trailing_body; Ast **cases = NULL; if (!parse_switch_body(context, &cases, TOKEN_CASE, TOKEN_DEFAULT, true)) return poisoned_decl; decl->generic_decl.cases = cases; @@ -1776,8 +1796,9 @@ static inline Decl *parse_macro_declaration(Context *context, Visibility visibil } TRY_CONSUME_OR(TOKEN_IDENT, "Expected a macro name here.", poisoned_decl); - - if (!parse_macro_arguments(context, visibility, &decl->macro_decl.parameters)) return poisoned_decl; + bool trailing_body = false; + if (!parse_macro_arguments(context, visibility, &decl->macro_decl.parameters, &decl->macro_decl.body_parameters, &trailing_body)) return poisoned_decl; + decl->has_body_param = trailing_body; decl->macro_decl.body = TRY_AST_OR(parse_stmt(context), poisoned_decl); return decl; } diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 8b55461a9..5d6d337c3 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -679,6 +679,29 @@ static inline Ast *parse_decl_or_expr_stmt(Context *context) return ast; } +/** + * yield_stmt = 'yield' (expr (',' expr)*)? EOS + * @param context + * @return + */ +static inline Ast *parse_yield_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_YIELD_STMT, context->tok); + advance_and_verify(context, TOKEN_YIELD); + Expr **exprs = NULL; + while (!TOKEN_IS(TOKEN_EOS)) + { + if (exprs) + { + CONSUME_OR(TOKEN_COMMA, poisoned_ast); + } + Expr *expr = TRY_EXPR_OR(parse_expr(context), poisoned_ast); + vec_add(exprs, expr); + } + ast->yield_stmt.values = exprs; + RANGE_EXTEND_PREV(ast); + return ast; +} /** * define_stmt * : define CT_IDENT '=' const_expr EOS @@ -1014,6 +1037,8 @@ Ast *parse_stmt(Context *context) return parse_declaration_stmt(context); case TOKEN_AT: return parse_expr_stmt(context); + case TOKEN_YIELD: + return parse_yield_stmt(context); case TOKEN_RETURN: { Ast *ast = TRY_AST(parse_return(context)); diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index bbc15e8a3..329a2d39a 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -52,7 +52,7 @@ bool parse_switch_body(Context *context, Ast ***cases, TokenType case_type, Toke bool allow_multiple_values); Expr *parse_expression_list(Context *context); Decl *parse_decl_after_type(Context *context, TypeInfo *type); - +bool parse_macro_argument_declarations(Context *context, Visibility visibility, Decl ***params_ref, bool allow_vararg); bool parse_param_list(Context *context, Expr ***result, TokenType param_end, bool *unsplat); Expr *parse_type_compound_literal_expr_after_type(Context *context, TypeInfo *type_info); Expr *parse_type_access_expr_after_type(Context *context, TypeInfo *type_info); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 7734b1f18..30930ce95 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -10,6 +10,27 @@ static AttributeType sema_analyse_attribute(Context *context, Attr *attr, Attrib static bool sema_analyse_struct_union(Context *context, Decl *decl); +static bool sema_check_unique_parameters(Decl **decls) +{ + STable *table = &global_context.scratch_table; + stable_clear(table); + + VECEACH(decls, i) + { + Decl *param = decls[i]; + if (param->name) + { + Decl *old = stable_set(table, param->name, param); + if (old) + { + SEMA_ERROR(param, "Parameter name occurred twice, try renaming one of them."); + return false; + } + } + } + return true; +} + static inline bool sema_analyse_struct_member(Context *context, Decl *decl) { if (decl->name) @@ -300,18 +321,32 @@ static bool sema_analyse_struct_union(Context *context, Decl *decl) } DEBUG_LOG("Beginning analysis of %s.", decl->name ? decl->name : "anon"); - if (decl->name) context_push_scope(context); bool success; - if (decl->decl_kind == DECL_UNION) + if (decl->name) { - success = sema_analyse_union_members(context, decl, decl->strukt.members); + SCOPE_START + if (decl->decl_kind == DECL_UNION) + { + success = sema_analyse_union_members(context, decl, decl->strukt.members); + } + else + { + success = sema_analyse_struct_members(context, decl, decl->strukt.members); + } + SCOPE_END; } else { - success = sema_analyse_struct_members(context, decl, decl->strukt.members); + if (decl->decl_kind == DECL_UNION) + { + success = sema_analyse_union_members(context, decl, decl->strukt.members); + } + else + { + success = sema_analyse_struct_members(context, decl, decl->strukt.members); + } } DEBUG_LOG("Struct/union size %d, alignment %d.", (int)decl->strukt.size, (int)decl->alignment); - if (decl->name) context_pop_scope(context); DEBUG_LOG("Analysis complete."); if (!success) return decl_poison(decl); return decl_ok(decl); @@ -804,6 +839,7 @@ static inline bool sema_analyse_macro(Context *context, Decl *decl) VECEACH(decl->macro_decl.parameters, i) { Decl *param = decl->macro_decl.parameters[i]; + param->resolve_status = RESOLVE_RUNNING; assert(param->decl_kind == DECL_VAR); switch (param->var.kind) { @@ -829,7 +865,37 @@ static inline bool sema_analyse_macro(Context *context, Decl *decl) case VARDECL_ALIAS: UNREACHABLE } + param->resolve_status = RESOLVE_DONE; } + VECEACH(decl->macro_decl.body_parameters, i) + { + Decl *param = decl->macro_decl.body_parameters[i]; + param->resolve_status = RESOLVE_RUNNING; + assert(param->decl_kind == DECL_VAR); + switch (param->var.kind) + { + case VARDECL_PARAM: + if (param->var.type_info && !sema_resolve_type_info(context, param->var.type_info)) return false; + break; + case VARDECL_PARAM_EXPR: + case VARDECL_PARAM_CT: + case VARDECL_PARAM_REF: + case VARDECL_PARAM_CT_TYPE: + SEMA_ERROR(param, "Only plain variables are allowed as body parameters."); + break; + case VARDECL_CONST: + case VARDECL_GLOBAL: + case VARDECL_LOCAL: + case VARDECL_MEMBER: + case VARDECL_LOCAL_CT: + case VARDECL_LOCAL_CT_TYPE: + case VARDECL_ALIAS: + UNREACHABLE + } + param->resolve_status = RESOLVE_DONE; + } + if (!sema_check_unique_parameters(decl->macro_decl.parameters)) return false; + if (!sema_check_unique_parameters(decl->macro_decl.body_parameters)) return false; return true; } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index e4dd31972..85b721193 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1103,12 +1103,23 @@ static bool sema_check_stmt_compile_time(Context *context, Ast *ast) static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *call_expr, Decl *decl) { // TODO failable - if (context->macro_nesting >= MAX_MACRO_NESTING) + if (context->macro_scope.depth >= MAX_MACRO_NESTING) { SEMA_ERROR(call_expr, "Too deep nesting (more than %d levels) when evaluating this macro.", MAX_MACRO_NESTING); return false; } + if (decl->has_body_param && !call_expr->call_expr.body) + { + SEMA_ERROR(call_expr, "Expected call to have a trailing statement, did you forget to add it?"); + return false; + } + if (!decl->has_body_param && call_expr->call_expr.body) + { + SEMA_ERROR(call_expr, "This macro does not support trailing statements, please remove it."); + return false; + } + Expr **args = call_expr->call_expr.arguments; Decl **func_params = decl->macro_decl.parameters; @@ -1126,7 +1137,7 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr Decl *param = copy_decl(func_params[i]); vec_add(params, param); assert(param->decl_kind == DECL_VAR); - assert(param->resolve_status == RESOLVE_NOT_DONE); + assert(param->resolve_status == RESOLVE_DONE); param->resolve_status = RESOLVE_RUNNING; // Maybe there's a type, but in general a macro param may be // typeless. @@ -1159,10 +1170,9 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr // #foo // We push a scope here as this will prevent the expression from modifying // compile time variables during evaluation: - context_push_scope(context); - bool ok = sema_analyse_expr_of_required_type(context, param->type, arg, false); - context_pop_scope(context); - if (!ok) return false; + SCOPE_START + if (!sema_analyse_expr_of_required_type(context, param->type, arg, false)) return SCOPE_POP_ERROR(); + SCOPE_END; break; case VARDECL_PARAM_CT: // $foo @@ -1194,8 +1204,7 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr } if (param->type) { - TODO -// if (!cast_implicit(context, arg, param->type)) return false; + if (!cast_implicit(arg, param->type)) return false; } else { @@ -1205,77 +1214,133 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr param->resolve_status = RESOLVE_DONE; } - context->macro_nesting++; - context->macro_counter++; - Decl **old_macro_locals_start = context->macro_locals_start; - context->macro_locals_start = context->last_local; + unsigned body_params = vec_size(call_expr->call_expr.body_arguments); + unsigned expected_body_params = vec_size(decl->macro_decl.body_parameters); + if (expected_body_params > body_params) + { + SEMA_ERROR(call_expr, "Not enough parameters for the macro body, expected %d.", expected_body_params); + return false; + } + if (expected_body_params < body_params) + { + SEMA_ERROR(call_expr, "Too many parameters for the macro body, expected %d.", expected_body_params); + return false; + } + for (unsigned i = 0; i < expected_body_params; i++) + { + Decl *body_param = decl->macro_decl.body_parameters[i]; + assert(body_param->resolve_status == RESOLVE_DONE); + Decl *body_arg = call_expr->call_expr.body_arguments[i]; + if (!body_arg->var.type_info) + { + SEMA_ERROR(body_arg, "Expected a type parameter before this variable name."); + return false; + } + if (!sema_resolve_type_info(context, body_arg->var.type_info)) return false; + body_arg->type = body_arg->var.type_info->type; + if (body_param->var.type_info) + { + Type *declare_type = body_param->var.type_info->type->canonical; + if (declare_type != body_arg->type) + { + SEMA_ERROR(body_arg->var.type_info, "This parameter should be '%s' but was '%s'", + type_to_error_string(declare_type), + type_quoted_error_string(body_arg->type)); + return false; + } + } + if (!body_arg->alignment) body_arg->alignment = type_alloca_alignment(body_arg->type); + } + Decl **first_local = context->macro_scope.macro ? context->macro_scope.locals_start : context->locals; + MacroScope old_macro_scope = context->macro_scope; bool ok = true; - Ast *body = copy_ast(decl->macro_decl.body); + SCOPE_OUTER_START - TypeInfo *foo = decl->macro_decl.rtype; - - Ast **saved_returns = context_push_returns(context); - context->expected_block_type = foo ? foo->type : to; - context_push_scope_with_flags(context, SCOPE_MACRO); - - for (unsigned i = 0; i < num_args; i++) - { - Decl *param = params[i]; - sema_add_local(context, param); - } - - VECEACH(body->compound_stmt.stmts, i) - { - if (!sema_analyse_statement(context, body->compound_stmt.stmts[i])) + for (unsigned i = 0; i < expected_body_params; i++) { - ok = false; - goto EXIT; + Decl *body_arg = call_expr->call_expr.body_arguments[i]; + sema_add_local(context, body_arg); } - } - if (!vec_size(context->returns)) - { - if (to) - { - SEMA_ERROR(decl, "Missing return in macro that evaluates to %s.", type_to_error_string(to)); - ok = false; - goto EXIT; - } - } + context->macro_scope = (MacroScope){ + .macro = decl, + .locals_start = context->active_scope.current_local, + .depth = old_macro_scope.depth + 1, + .yield_symbol_start = first_local, + .yield_body = call_expr->call_expr.body, + .yield_symbol_end = context->active_scope.current_local, + .yield_args = call_expr->call_expr.body_arguments, + }; - Expr *first_return_expr = vec_size(context->returns) ? context->returns[0]->return_stmt.expr : NULL; - Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void; - // Let's unify the return statements. - left_canonical = unify_returns(context, left_canonical); - if (!left_canonical) - { - ok = false; - goto EXIT; - } - expr_set_type(call_expr, left_canonical); - if (vec_size(context->returns) == 1) - { - Expr *result = context->returns[0]->return_stmt.expr; - if (result && expr_is_constant_eval(result)) - { - if (sema_check_stmt_compile_time(context, body)) + + Ast *body = copy_ast(decl->macro_decl.body); + TypeInfo *foo = decl->macro_decl.rtype; + + Ast **saved_returns = context_push_returns(context); + context->expected_block_type = foo ? foo->type : to; + SCOPE_START_WITH_FLAGS(SCOPE_MACRO); + + + for (unsigned i = 0; i < num_args; i++) { - expr_replace(call_expr, result); + Decl *param = params[i]; + sema_add_local(context, param); + } + + VECEACH(body->compound_stmt.stmts, i) + { + if (!sema_analyse_statement(context, body->compound_stmt.stmts[i])) + { + ok = false; + goto EXIT; + } + } + + if (!vec_size(context->returns)) + { + if (to) + { + SEMA_ERROR(decl, "Missing return in macro that evaluates to %s.", type_to_error_string(to)); + ok = false; + goto EXIT; + } + } + + Expr *first_return_expr = vec_size(context->returns) ? context->returns[0]->return_stmt.expr : NULL; + Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void; + // Let's unify the return statements. + left_canonical = unify_returns(context, left_canonical); + if (!left_canonical) + { + ok = false; goto EXIT; } - } - } - call_expr->expr_kind = EXPR_MACRO_BLOCK; - call_expr->macro_block.stmts = body->compound_stmt.stmts; - call_expr->macro_block.params = params; - call_expr->macro_block.args = args; - EXIT: - context_pop_scope(context); - context_pop_returns(context, saved_returns); - context->macro_nesting--; - context->macro_locals_start = old_macro_locals_start; + expr_set_type(call_expr, left_canonical); + if (vec_size(context->returns) == 1) + { + Expr *result = context->returns[0]->return_stmt.expr; + if (result && expr_is_constant_eval(result)) + { + if (sema_check_stmt_compile_time(context, body)) + { + expr_replace(call_expr, result); + goto EXIT; + } + } + } + call_expr->expr_kind = EXPR_MACRO_BLOCK; + call_expr->macro_block.stmts = body->compound_stmt.stmts; + call_expr->macro_block.params = params; + call_expr->macro_block.args = args; + + EXIT: + SCOPE_END; + context_pop_returns(context, saved_returns); + + SCOPE_OUTER_END; + context->macro_scope = old_macro_scope; return ok; } @@ -2042,10 +2107,11 @@ CHECK_DEEPER: } Decl *decl = type->decl; - context_push_scope(context); - add_members_to_context(context, decl); - Decl *member = sema_resolve_symbol_in_current_dynamic_scope(context, kw); - context_pop_scope(context); + Decl *member; + SCOPE_START + add_members_to_context(context, decl); + member = sema_resolve_symbol_in_current_dynamic_scope(context, kw); + SCOPE_END; if (!member) @@ -2969,7 +3035,7 @@ static inline bool sema_expr_analyse_ct_identifier_lvalue(Context *context, Expr return expr_poison(expr); } - if ((intptr_t)decl->var.scope < (intptr_t)context->current_scope) + if (decl->var.scope_depth < context->active_scope.depth) { SEMA_ERROR(expr, "Cannot modify '%s' inside of a runtime scope.", decl->name); return false; @@ -4537,7 +4603,7 @@ static inline bool sema_expr_analyse_guard(Context *context, Type *to, Expr *exp { Expr *inner = expr->guard_expr.inner; bool success = sema_analyse_expr(context, to, inner); - expr->guard_expr.defer = context->current_scope->defer_last; + expr->guard_expr.defer = context->active_scope.defer_last; if (!success) return false; expr_copy_types(expr, inner); expr->pure = false; @@ -4585,36 +4651,36 @@ static inline bool sema_expr_analyse_expr_block(Context *context, Type *to, Expr Type *prev_expected_block_type = context->expected_block_type; Ast **saved_returns = context_push_returns(context); context->expected_block_type = to; - context_push_scope_with_flags(context, SCOPE_EXPR_BLOCK); - VECEACH(expr->expr_block.stmts, i) - { - if (!sema_analyse_statement(context, expr->expr_block.stmts[i])) + SCOPE_START_WITH_FLAGS(SCOPE_EXPR_BLOCK) + VECEACH(expr->expr_block.stmts, i) + { + if (!sema_analyse_statement(context, expr->expr_block.stmts[i])) + { + success = false; + goto EXIT; + } + } + + if (!context->active_scope.jump_end && to) + { + SEMA_ERROR(expr, "Expected the block to return with a value of type %s.", type_to_error_string(to)); + success = false; + } + if (!vec_size(context->returns)) goto EXIT; + + Expr *first_return_expr = context->returns[0]->return_stmt.expr; + Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void; + // Let's unify the return statements. + left_canonical = unify_returns(context, left_canonical); + if (!left_canonical) { success = false; goto EXIT; } - } - - if (!context->current_scope->jump_end && to) - { - SEMA_ERROR(expr, "Expected the block to return with a value of type %s.", type_to_error_string(to)); - success = false; - } - if (!vec_size(context->returns)) goto EXIT; - - Expr *first_return_expr = context->returns[0]->return_stmt.expr; - Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void; - // Let's unify the return statements. - left_canonical = unify_returns(context, left_canonical); - if (!left_canonical) - { - success = false; - goto EXIT; - } - expr_set_type(expr, left_canonical); + expr_set_type(expr, left_canonical); EXIT: - context_pop_scope(context); + SCOPE_END; context_pop_returns(context, saved_returns); expr->failable = context->expr_failable_return; context->expr_failable_return = saved_expr_failable_return; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 40ac7b32e..589efa0c3 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -9,15 +9,38 @@ int sema_check_comp_time_bool(Context *context, Expr *expr); bool sema_analyse_function_body(Context *context, Decl *func); -void context_pop_scope(Context *context); -void context_pop_scope_error(Context *context); -void context_push_scope_with_flags(Context *context, ScopeFlags flags); -AstId context_start_defer(Context *context); -static inline void context_push_scope(Context *context) -{ - context_push_scope_with_flags(context, SCOPE_NONE); -} -#define PUSH_X(ast, X) AstId _old_##X##_defer = context->X##_defer; AstId _old_##X = context->X##_target; context->X##_target = ast ? astid(ast) : 0; context->X##_defer = context->current_scope->defer_last +#define SCOPE_OUTER_START \ +do { \ + DynamicScope stored_scope = context->active_scope; \ + context_change_scope_with_flags(context, SCOPE_NONE); +#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_END \ + assert(context->active_scope.defer_last == context->active_scope.defer_start); \ + context->active_scope = old_scope; \ + } while(0) +#define SCOPE_POP_ERROR() (context->active_scope = old_scope, false) +#define SCOPE_ERROR_END_OUTER() \ + do { context->active_scope = stored_scope; } while(0) + +void context_pop_defers_to(Context *context, DeferList *list); +Expr *context_pop_defers_and_wrap_expr(Context *context, Expr *expr); +void context_pop_defers_and_replace_ast(Context *context, Ast *ast); +void context_change_scope_for_label(Context *context, Decl *label); +void context_change_scope_with_flags(Context *context, ScopeFlags flags); + +#define PUSH_X(ast, X) AstId _old_##X##_defer = context->X##_defer; AstId _old_##X = context->X##_target; context->X##_target = ast ? astid(ast) : 0; context->X##_defer = context->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) diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 73b3da118..8d40e0d46 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -28,15 +28,12 @@ static inline bool matches_subpath(Path *path_to_check, Path *path_to_find) Decl *sema_resolve_symbol_in_current_dynamic_scope(Context *context, const char *symbol) { - if (context->current_scope) + Decl **first = context->active_scope.local_decl_start; + Decl **current = context->active_scope.current_local; + while (current > first) { - Decl **first = context->current_scope->local_decl_start; - Decl **current = context->last_local - 1; - while (current >= first) - { - if (current[0]->name == symbol) return current[0]; - current--; - } + current--; + if (current[0]->name == symbol) return current[0]; } return NULL; } @@ -111,11 +108,19 @@ static Decl *sema_resolve_no_path_symbol(Context *context, const char *symbol, { Decl *decl = NULL; - if (context->current_scope) + if (context->active_scope.current_local > &context->locals[0]) { Decl **first = &context->locals[0]; - if (context->macro_nesting) first = context->macro_locals_start; - Decl **current = context->last_local - 1; + Decl **current = context->active_scope.current_local - 1; + if (context->macro_scope.macro) + { + first = context->macro_scope.locals_start; + if (context->macro_scope.in_yield) + { + first = context->macro_scope.yield_symbol_start; + current = context->macro_scope.yield_symbol_end - 1; + } + } while (current >= first) { if (current[0]->name == symbol) return current[0]; @@ -343,13 +348,13 @@ Decl *sema_resolve_normal_symbol(Context *context, TokenId symbol, Path *path, b static inline bool sema_append_local(Context *context, Decl *decl) { - if (context->last_local == &context->locals[MAX_LOCALS - 1]) + if (context->active_scope.current_local == &context->locals[MAX_LOCALS - 1]) { SEMA_ERROR(decl, "Reached the maximum number of locals."); return false; } - context->last_local[0] = decl; - context->last_local++; + context->active_scope.current_local[0] = decl; + context->active_scope.current_local++; return true; } diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index d5ea8f86a..89bd2e950 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -199,10 +199,14 @@ void sema_analysis_pass_decls(Module *module) VECEACH(module->contexts, index) { Context *context = module->contexts[index]; - context->current_scope = &context->scopes[0]; - assert(context->current_scope->defer_last < 10000000); - context->current_scope->scope_id = 0; - context->last_local = &context->locals[0]; + context->active_scope = (DynamicScope) + { + .depth = 0, + .scope_id = 0, + .local_decl_start = &context->locals[0], + .current_local = &context->locals[0], + }; + context->macro_scope = (MacroScope) {}; VECEACH(context->enums, i) { sema_analyse_decl(context, context->enums[i]); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index ddecebb6d..bf379a77c 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -4,122 +4,6 @@ #include "sema_internal.h" -#pragma mark --- Context help functions - -void context_push_scope_with_flags(Context *context, ScopeFlags flags) -{ - if (context->current_scope == &context->scopes[MAX_SCOPE_DEPTH - 1]) - { - FATAL_ERROR("Too deeply nested scopes."); - } - ScopeFlags previous_flags = context->current_scope->flags; - Ast *previous_defer = context->current_scope->in_defer; - AstId parent_defer = context->current_scope->defer_last; - context->current_scope++; - context->current_scope->scope_id = ++context->scope_id; - context->current_scope->allow_dead_code = false; - context->current_scope->jump_end = false; - if (context->scope_id == 0) - { - FATAL_ERROR("Too many scopes."); - } - context->current_scope->local_decl_start = context->last_local; - context->current_scope->in_defer = previous_defer; - context->current_scope->defer_last = parent_defer; - assert(parent_defer < 1000000); - - if (flags & (SCOPE_DEFER | SCOPE_EXPR_BLOCK)) - { - context->current_scope->flags = flags; - } - else - { - context->current_scope->flags = previous_flags | flags; - } - if (previous_flags & SCOPE_MACRO) - { - context->current_scope->flags = previous_flags | SCOPE_MACRO; - } -} - -void context_push_scope_with_label(Context *context, Decl *label) -{ - context_push_scope_with_flags(context, SCOPE_NONE); - - if (label) - { - label->label.defer = context->current_scope->defer_last; - sema_add_local(context, label); - label->label.scope_id = context->current_scope->scope_id; - } -} - - - -static inline void context_pop_defers_to(Context *context, DeferList *list) -{ - list->end = context_start_defer(context); - assert(context->current_scope->defer_last < 10000000); - list->start = context->current_scope->defer_last; - context->current_scope->defer_last = list->end; -} - - - -void context_pop_scope(Context *context) -{ - assert(context->current_scope != &context->scopes[0]); - context->last_local = context->current_scope->local_decl_start; - - assert(context_start_defer(context) == context->current_scope->defer_last); - - context->current_scope--; -} - -void context_pop_scope_error(Context *context) -{ - context->current_scope->defer_last = context_start_defer(context); - context_pop_scope(context); -} - -static Expr *context_pop_defers_and_wrap_expr(Context *context, Expr *expr) -{ - DeferList defers = { 0, 0 }; - context_pop_defers_to(context, &defers); - if (defers.end == defers.start) return expr; - Expr *wrap = expr_new(EXPR_SCOPED_EXPR, expr->span); - expr_copy_types(wrap, expr); - 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 = { 0, 0 }; - context_pop_defers_to(context, &defers); - if (defers.end == defers.start) return; - if (ast->ast_kind == AST_DEFER_STMT) - { - assert(defers.start == astid(ast)); - *ast = *ast->defer_stmt.body; - return; - } - assert(ast->ast_kind != AST_COMPOUND_STMT); - Ast *replacement = ast_copy(ast); - ast->ast_kind = AST_SCOPED_STMT; - ast->scoped_stmt.stmt = replacement; - ast->scoped_stmt.defers = defers; -} - -AstId context_start_defer(Context *context) -{ - if (context->current_scope == context->scopes) return 0; - if (context->current_scope->in_defer != context->current_scope[-1].in_defer) return 0; - return context->current_scope[-1].defer_last; -} - #pragma mark --- Helper functions @@ -129,8 +13,8 @@ AstId context_start_defer(Context *context) static inline bool sema_analyse_block_return_stmt(Context *context, Ast *statement) { - assert(context->current_scope->flags & (SCOPE_EXPR_BLOCK | SCOPE_MACRO)); - context->current_scope->jump_end = true; + assert(context->active_scope.flags & (SCOPE_EXPR_BLOCK | SCOPE_MACRO)); + context->active_scope.jump_end = true; if (statement->return_stmt.expr) { if (!sema_analyse_expr_of_required_type(context, @@ -157,18 +41,18 @@ static inline bool sema_analyse_block_return_stmt(Context *context, Ast *stateme static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) { // This might be a return in a function block or a macro which must be treated differently. - if (context->current_scope->flags & (SCOPE_EXPR_BLOCK | SCOPE_MACRO)) + if (context->active_scope.flags & (SCOPE_EXPR_BLOCK | SCOPE_MACRO)) { return sema_analyse_block_return_stmt(context, statement); } // 1. We mark that the current scope ends with a jump. - context->current_scope->jump_end = true; + context->active_scope.jump_end = true; Type *expected_rtype = context->rtype; assert(expected_rtype && "We should always have known type from a function return."); Expr *return_expr = statement->return_stmt.expr; - statement->return_stmt.defer = context->current_scope->defer_last; + statement->return_stmt.defer = context->active_scope.defer_last; // 2. First handle the plain return. if (return_expr == NULL) @@ -191,7 +75,7 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) static inline bool sema_analyse_unreachable_stmt(Context *context) { - context->current_scope->jump_end = true; + context->active_scope.jump_end = true; return true; } @@ -338,40 +222,40 @@ static inline bool sema_analyse_while_stmt(Context *context, Ast *statement) Expr *cond = statement->while_stmt.cond; Ast *body = statement->while_stmt.body; + bool success; + // 1. Begin our scope, this is relevant in case we have something like // while (File *f = @getFileAndClose!()) where the macro pushes a defer into the scope. - context_push_scope_with_label(context, statement->while_stmt.flow.label); + SCOPE_START_WITH_LABEL(statement->while_stmt.flow.label) - // 2. Analyze the condition - if (!sema_analyse_cond(context, cond, true)) - { - // 2a. In case of error, pop context and exit. - context_pop_scope_error(context); - return false; - } + // 2. Analyze the condition + if (!sema_analyse_cond(context, cond, true)) + { + // 2a. In case of error, pop context and exit. + return SCOPE_POP_ERROR(); + } - // 4. Push break / continue - which is independent of the scope. - PUSH_BREAKCONT(statement); + // 4. Push break / continue - which is independent of the scope. + PUSH_BREAKCONT(statement); - // 5. Analyse the statement - bool success = sema_analyse_statement(context, body); + // 5. Analyse the statement + success = sema_analyse_statement(context, body); - // 6. Pop break / continue - POP_BREAKCONT(); + // 6. Pop break / continue + POP_BREAKCONT(); - // 7. Check placement, in case of a single statement, it must be placed on the same line. - if (success && !sema_analyse_stmt_placement(cond, body)) - { - SEMA_ERROR(body, "A single statement after 'while' must be placed on the same line, or be enclosed in {}."); - context_pop_scope_error(context); - return false; - } + // 7. Check placement, in case of a single statement, it must be placed on the same line. + if (success && !sema_analyse_stmt_placement(cond, body)) + { + SEMA_ERROR(body, "A single statement after 'while' must be placed on the same line, or be enclosed in {}."); + return SCOPE_POP_ERROR(); + } - // 8. Pop defers attach them to the statement if needed - context_pop_defers_and_replace_ast(context, body); + // 8. Pop defers attach them to the statement if needed + context_pop_defers_and_replace_ast(context, body); // 9. Pop the while scope. - context_pop_scope(context); + SCOPE_END; return success; } @@ -386,24 +270,25 @@ static inline bool sema_analyse_do_stmt(Context *context, Ast *statement) bool success; // 1. Begin pushing the scope and break / continue. - context_push_scope_with_label(context, statement->do_stmt.flow.label); - PUSH_BREAKCONT(statement); + SCOPE_START_WITH_LABEL(statement->do_stmt.flow.label) - // 2. We analyze the statement. - success = sema_analyse_statement(context, body); + PUSH_BREAKCONT(statement); - // 3. Pop break / continue - POP_BREAKCONT(); + // 2. We analyze the statement. + success = sema_analyse_statement(context, body); - // 4. Pop any defers - context_pop_defers_and_replace_ast(context, body); + // 3. Pop break / continue + POP_BREAKCONT(); - // 5. If the current scope ended in a jump, then - // the do statement has no exit. - statement->do_stmt.flow.no_exit = context->current_scope->jump_end; + // 4. Pop any defers + context_pop_defers_and_replace_ast(context, body); + + // 5. If the current scope ended in a jump, then + // the do statement has no exit. + statement->do_stmt.flow.no_exit = context->active_scope.jump_end; // 6. Pop the scope - context_pop_scope(context); + SCOPE_END; // 7. We can now exit if there was an error further up. if (!success) return false; @@ -412,28 +297,26 @@ static inline bool sema_analyse_do_stmt(Context *context, Ast *statement) if (!statement->do_stmt.expr) return true; // 9. We next handle the while test. This is its own scope. - context_push_scope(context); + SCOPE_START - // 10. Try to evaluate and implicitly cast to boolean. - if (!sema_analyse_expr_of_required_type(context, type_bool, expr, false)) - { - // 10a. On failure, pop and return false. - context_pop_scope_error(context); - return false; - } + // 10. Try to evaluate and implicitly cast to boolean. + if (!sema_analyse_expr_of_required_type(context, type_bool, expr, false)) + { + // 10a. On failure, pop and return false. + return SCOPE_POP_ERROR(); + } - // 11. Pop any defers in the expression. - statement->do_stmt.expr = context_pop_defers_and_wrap_expr(context, expr); + // 11. Pop any defers in the expression. + statement->do_stmt.expr = context_pop_defers_and_wrap_expr(context, expr); - // 12. Pop the scope itself. - context_pop_scope(context); + SCOPE_END; // 13. Check for infinite loops using do ... while (1). // If it has no break then that means we've reached a statement which ends with a jump. if (statement->do_stmt.expr->expr_kind == EXPR_CONST && statement->do_stmt.expr->const_expr.b) { // Unless there is a break, this won't ever exit. - context->current_scope->jump_end = !statement->do_stmt.flow.has_break; + context->active_scope.jump_end = !statement->do_stmt.flow.has_break; } return true; } @@ -594,7 +477,8 @@ static inline bool sema_analyse_define_stmt(Context *context, Ast *statement) default: UNREACHABLE } - decl->var.scope = context->current_scope; + + decl->var.scope_depth = context->active_scope.depth; return sema_add_local(context, decl); } @@ -607,31 +491,33 @@ static inline bool sema_analyse_expr_stmt(Context *context, Ast *statement) static inline bool sema_analyse_defer_stmt(Context *context, Ast *statement) { // TODO special parsing of "catch" - context_push_scope_with_flags(context, SCOPE_DEFER); // NOLINT(hicpp-signed-bitwise) + bool success; + SCOPE_START_WITH_FLAGS(SCOPE_DEFER) - context->current_scope->defer_last = 0; - context->current_scope->in_defer = statement; + context->active_scope.defer_last = 0; + context->active_scope.defer_start = 0; + context->active_scope.in_defer = statement; - PUSH_CONTINUE(NULL); - PUSH_BREAK(statement); - PUSH_NEXT(NULL, NULL); + PUSH_CONTINUE(NULL); + PUSH_BREAK(statement); + PUSH_NEXT(NULL, NULL); - // Only ones allowed. - context->current_scope->flags &= SCOPE_DEFER; + // Only ones allowed. + context->active_scope.flags &= SCOPE_DEFER; - bool success = sema_analyse_statement(context, statement->defer_stmt.body); + success = sema_analyse_statement(context, statement->defer_stmt.body); - POP_BREAKCONT(); - POP_NEXT(); + POP_BREAKCONT(); + POP_NEXT(); - context_pop_defers_and_replace_ast(context, statement->defer_stmt.body); + context_pop_defers_and_replace_ast(context, statement->defer_stmt.body); - context_pop_scope(context); + SCOPE_END; if (!success) return false; - statement->defer_stmt.prev_defer = context->current_scope->defer_last; - context->current_scope->defer_last = astid(statement); + statement->defer_stmt.prev_defer = context->active_scope.defer_last; + context->active_scope.defer_last = astid(statement); return true; } @@ -639,63 +525,68 @@ static inline bool sema_analyse_defer_stmt(Context *context, Ast *statement) static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) { bool success = true; + bool is_infinite; // Enter for scope - context_push_scope(context); + SCOPE_OUTER_START - bool is_infinite = statement->for_stmt.cond == NULL; - if (statement->for_stmt.init) - { - success = sema_analyse_decl_expr_list(context, statement->for_stmt.init); - } - - if (success && statement->for_stmt.cond) - { - // Conditional scope start - context_push_scope(context); - Expr *cond = statement->for_stmt.cond; - success = sema_analyse_expr_of_required_type(context, type_bool, cond, false); - statement->for_stmt.cond = context_pop_defers_and_wrap_expr(context, cond); - // If this is const true, then set this to infinite and remove the expression. - if (statement->for_stmt.cond->expr_kind == EXPR_CONST && statement->for_stmt.cond->const_expr.b) + is_infinite = statement->for_stmt.cond == NULL; + if (statement->for_stmt.init) { - statement->for_stmt.cond = NULL; - is_infinite = true; + success = sema_analyse_decl_expr_list(context, statement->for_stmt.init); } - // Conditional scope end - context_pop_scope(context); - } - if (success && statement->for_stmt.incr) - { - // Incr scope start - context_push_scope(context); - 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); - } - if (!success) - { - context_pop_scope_error(context); - return false; - } - // Create the for body scope. - context_push_scope_with_label(context, statement->for_stmt.flow.label); - PUSH_BREAKCONT(statement); - success = sema_analyse_statement(context, statement->for_stmt.body); - statement->for_stmt.flow.no_exit = context->current_scope->jump_end; - POP_BREAKCONT(); - // End for body scope - 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); + if (success && statement->for_stmt.cond) + { + // Conditional scope start + SCOPE_START + Expr *cond = statement->for_stmt.cond; + success = sema_analyse_expr_of_required_type(context, type_bool, cond, false); + statement->for_stmt.cond = context_pop_defers_and_wrap_expr(context, cond); + // If this is const true, then set this to infinite and remove the expression. + if (statement->for_stmt.cond->expr_kind == EXPR_CONST && statement->for_stmt.cond->const_expr.b) + { + statement->for_stmt.cond = NULL; + is_infinite = true; + } + // Conditional scope end + SCOPE_END; + } + if (success && statement->for_stmt.incr) + { + // Incr scope start + SCOPE_START + 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 + SCOPE_END; + } + if (!success) + { + SCOPE_ERROR_END_OUTER(); + return false; + } + + // Create the for body scope. + SCOPE_START_WITH_LABEL(statement->for_stmt.flow.label) + + PUSH_BREAKCONT(statement); + success = sema_analyse_statement(context, statement->for_stmt.body); + statement->for_stmt.flow.no_exit = context->active_scope.jump_end; + POP_BREAKCONT(); + // End for body scope + context_pop_defers_and_replace_ast(context, statement->for_stmt.body); + SCOPE_END; + + context_pop_defers_and_replace_ast(context, statement); + // End for scope + + SCOPE_OUTER_END; + if (statement->for_stmt.flow.no_exit && is_infinite && !statement->for_stmt.flow.has_break) { - context->current_scope->jump_end = true; + context->active_scope.jump_end = true; } return success; } @@ -708,6 +599,8 @@ static inline bool sema_analyse_foreach_stmt(Context *context, Ast *statement) Decl *var = statement->foreach_stmt.variable; Expr *enumerator = statement->foreach_stmt.enumeration; Ast *body = statement->foreach_stmt.body; + bool success = true; + Type *indexed_type; // First fold the enumerator expression, removing any () around it. while (enumerator->expr_kind == EXPR_GROUP) enumerator = enumerator->group_expr; @@ -716,154 +609,156 @@ static inline bool sema_analyse_foreach_stmt(Context *context, Ast *statement) // First analyse the enumerator. // Conditional scope start - context_push_scope_with_flags(context, SCOPE_COND); + SCOPE_START_WITH_FLAGS(SCOPE_COND) - // 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 = NULL; + // 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 = NULL; - // We may have an initializer list, in this case we rely on an inferred type. - if (enumerator->expr_kind == EXPR_INITIALIZER_LIST) - { - bool may_be_array; - bool is_const_size; - ArrayIndex size = sema_get_initializer_const_array_size(context, enumerator, &may_be_array, &is_const_size); - if (!may_be_array) + // We may have an initializer list, in this case we rely on an inferred type. + if (enumerator->expr_kind == EXPR_INITIALIZER_LIST) { - SEMA_ERROR(enumerator, "This initializer appears to be a struct initializer when, an array initializer was expected."); - goto EXIT_FAIL; + bool may_be_array; + bool is_const_size; + ArrayIndex size = sema_get_initializer_const_array_size(context, enumerator, &may_be_array, &is_const_size); + if (!may_be_array) + { + SEMA_ERROR(enumerator, + "This initializer appears to be a struct initializer when, an array initializer was expected."); + return SCOPE_POP_ERROR(); + } + if (!is_const_size) + { + SEMA_ERROR(enumerator, "Only constant sized initializers may be implicitly initialized."); + return SCOPE_POP_ERROR(); + } + if (size < 0) + { + SEMA_ERROR(enumerator, "The initializer mixes designated initialization with array initialization."); + return SCOPE_POP_ERROR(); + } + assert(size >= 0); + + TypeInfo *variable_type_info = var->var.type_info; + + if (!variable_type_info) + { + SEMA_ERROR(var, "Add the type of your variable here if you want to iterate over an initializer list."); + return SCOPE_POP_ERROR(); + } + // First infer the type of the variable. + if (!sema_resolve_type_info(context, variable_type_info)) return false; + // And create the inferred type: + inferred_type = type_get_array(var->var.type_info->type, size); } - if (!is_const_size) + + // because we don't want the index + variable to move into the internal scope + if (!sema_analyse_expr(context, inferred_type, enumerator)) { - SEMA_ERROR(enumerator, "Only constant sized initializers may be implicitly initialized."); - goto EXIT_FAIL; + // Exit early here, because semantic checking might be messed up otherwise. + return SCOPE_POP_ERROR(); } - if (size < 0) + + // Check that we can even index this expression. + indexed_type = type_get_indexed_type(enumerator->type); + if (!indexed_type) { - SEMA_ERROR(enumerator, "The initializer mixes designated initialization with array initialization."); - goto EXIT_FAIL; + SEMA_ERROR(enumerator, "It's not possible to enumerate an expression of type '%s'.", type_to_error_string(enumerator->type)); + return SCOPE_POP_ERROR(); } - assert(size >= 0); - TypeInfo *variable_type_info = var->var.type_info; + // Pop any possible defers. + enumerator = context_pop_defers_and_wrap_expr(context, enumerator); - if (!variable_type_info) - { - SEMA_ERROR(var, "Add the type of your variable here if you want to iterate over an initializer list."); - goto EXIT_FAIL; - } - // First infer the type of the variable. - if (!sema_resolve_type_info(context, variable_type_info)) return false; - // And create the inferred type: - inferred_type = type_get_array(var->var.type_info->type, size); - } - - // because we don't want the index + variable to move into the internal scope - if (!sema_analyse_expr(context, inferred_type, enumerator)) - { - // Exit early here, because semantic checking might be messed up otherwise. - goto EXIT_FAIL; - } - - // Check that we can even index this expression. - Type *indexed_type = type_get_indexed_type(enumerator->type); - if (!indexed_type) - { - SEMA_ERROR(enumerator, "It's not possible to enumerate an expression of type '%s'.", type_to_error_string(enumerator->type)); - goto EXIT_FAIL; - } - - // Pop any possible defers. - enumerator = context_pop_defers_and_wrap_expr(context, enumerator); - - // And pop the cond scope. - context_pop_scope(context); + // And pop the cond scope. + SCOPE_END; // Enter foreach scope, to put index + variable into the internal scope. // the foreach may be labelled. - context_push_scope_with_label(context, statement->foreach_stmt.flow.label); + SCOPE_START_WITH_LABEL(statement->foreach_stmt.flow.label) - if (index) - { - if (statement->foreach_stmt.index_by_ref) + if (index) { - SEMA_ERROR(index, "The index cannot be held by reference, did you accidentally add a '&'?"); - goto EXIT_FAIL; + if (statement->foreach_stmt.index_by_ref) + { + SEMA_ERROR(index, "The index cannot be held by reference, did you accidentally add a '&'?"); + return SCOPE_POP_ERROR(); + } + // Set type to isize if missing. + if (!index->var.type_info) + { + index->var.type_info = type_info_new_base(type_isize, index->span); + } + // Analyse the declaration. + if (!sema_analyse_local_decl(context, index)) return SCOPE_POP_ERROR(); + + // Make sure that the index is an integer. + if (!type_is_integer(type_flatten(index->type))) + { + SEMA_ERROR(index->var.type_info, + "Index must be an integer type, '%s' is not valid.", + type_to_error_string(index->type)); + return SCOPE_POP_ERROR(); + } + if (index->var.failable) + { + SEMA_ERROR(index->var.type_info, "The index may not be a failable."); + return SCOPE_POP_ERROR(); + } } - // Set type to isize if missing. - if (!index->var.type_info) + + // Find the expected type for the value. + Type *expected_var_type = indexed_type; + + // If by ref, then the pointer. + if (statement->foreach_stmt.value_by_ref) expected_var_type = type_get_ptr(expected_var_type); + + // If we type infer, then the expected type is the same as the indexed type. + if (!var->var.type_info) { - index->var.type_info = type_info_new_base(type_isize, index->span); + var->var.type_info = type_info_new_base(expected_var_type, var->span); } - // Analyse the declaration. - if (!sema_analyse_local_decl(context, index)) goto EXIT_FAIL; - // Make sure that the index is an integer. - if (!type_is_integer(type_flatten(index->type))) + // Analyse the value declaration. + if (!sema_analyse_local_decl(context, var)) return SCOPE_POP_ERROR(); + + if (var->var.failable) { - SEMA_ERROR(index->var.type_info, "Index must be an integer type, '%s' is not valid.", type_to_error_string(index->type)); - goto EXIT_FAIL; + SEMA_ERROR(var->var.type_info, "The variable may not be a failable."); + return SCOPE_POP_ERROR(); } - if (index->var.failable) + // TODO consider failables. + + // We may have an implicit cast happening. + statement->foreach_stmt.cast = CAST_ERROR; + + // If the type is different, attempt an implicit cast. + if (var->type != expected_var_type) { - SEMA_ERROR(index->var.type_info, "The index may not be a failable."); - goto EXIT_FAIL; + // This is hackish, replace when cast is refactored. + Expr dummy = { .resolve_status = RESOLVE_DONE, .span = { var->var.type_info->span.loc, + var->span.end_loc }, .expr_kind = EXPR_IDENTIFIER, .type = expected_var_type, .original_type = expected_var_type }; + if (!cast_implicit(&dummy, var->type)) return SCOPE_POP_ERROR(); + + assert(dummy.expr_kind == EXPR_CAST); + statement->foreach_stmt.cast = dummy.cast_expr.kind; } - } - // Find the expected type for the value. - Type *expected_var_type = indexed_type; + // Push the statement on the break/continue stack. + PUSH_BREAKCONT(statement); - // If by ref, then the pointer. - if (statement->foreach_stmt.value_by_ref) expected_var_type = type_get_ptr(expected_var_type); + success = sema_analyse_statement(context, body); + statement->foreach_stmt.flow.no_exit = context->active_scope.jump_end; - // If we type infer, then the expected type is the same as the indexed type. - if (!var->var.type_info) - { - var->var.type_info = type_info_new_base(expected_var_type, var->span); - } + // Pop the stack. + POP_BREAKCONT(); - // Analyse the value declaration. - if (!sema_analyse_local_decl(context, var)) goto EXIT_FAIL; + // End foreach scope + context_pop_defers_and_replace_ast(context, body); - if (var->var.failable) - { - SEMA_ERROR(var->var.type_info, "The variable may not be a failable."); - goto EXIT_FAIL; - } - // TODO consider failables. - - // We may have an implicit cast happening. - statement->foreach_stmt.cast = CAST_ERROR; - - // If the type is different, attempt an implicit cast. - if (var->type != expected_var_type) - { - // This is hackish, replace when cast is refactored. - Expr dummy = { .resolve_status = RESOLVE_DONE, .span = { var->var.type_info->span.loc, var->span.end_loc }, .expr_kind = EXPR_IDENTIFIER, .type = expected_var_type, .original_type = expected_var_type }; - if (!cast_implicit(&dummy, var->type)) goto EXIT_FAIL; - assert(dummy.expr_kind == EXPR_CAST); - statement->foreach_stmt.cast = dummy.cast_expr.kind; - } - - // Push the statement on the break/continue stack. - PUSH_BREAKCONT(statement); - - bool success = sema_analyse_statement(context, body); - statement->foreach_stmt.flow.no_exit = context->current_scope->jump_end; - - // Pop the stack. - POP_BREAKCONT(); - - // End foreach scope - context_pop_defers_and_replace_ast(context, body); - context_pop_scope(context); + SCOPE_END; return success; -EXIT_FAIL: - - context_pop_scope_error(context); - return false; } @@ -876,60 +771,70 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) // if (!x) A(); else B(); // into // if (x) B(); else A(); + + bool else_jump; + bool then_jump; + bool success; + Expr *cond = statement->if_stmt.cond; - context_push_scope(context); - bool success = sema_analyse_cond(context, cond, true); - if (statement->if_stmt.else_body) - { - if (statement->if_stmt.then_body->ast_kind != AST_COMPOUND_STMT) + SCOPE_OUTER_START + success = sema_analyse_cond(context, cond, true); + if (statement->if_stmt.else_body) { - SEMA_ERROR(statement->if_stmt.then_body, - "if-statements with an 'else' must use '{ }' even around a single 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."); + success = false; + } + if (success && statement->if_stmt.else_body->ast_kind != AST_COMPOUND_STMT && + statement->if_stmt.else_body->ast_kind != AST_IF_STMT) + { + SEMA_ERROR(statement->if_stmt.else_body, + "An 'else' must use '{ }' even around a single statement."); + success = false; + } + } + if (success && statement->if_stmt.then_body->ast_kind != AST_COMPOUND_STMT) + { + SourceLocation *end_of_cond = TOKLOC(cond->span.end_loc); + SourceLocation *start_of_then = TOKLOC(statement->if_stmt.then_body->span.loc); + if (end_of_cond->line != start_of_then->line) + { + SEMA_ERROR(statement->if_stmt.then_body, + "The 'then' part of a single line if-statement must start on the same line as the 'if' or use '{ }'"); + success = false; + } + } + if (context->active_scope.jump_end && !context->active_scope.allow_dead_code) + { + SEMA_ERROR(statement->if_stmt.then_body, "This code can never be executed."); success = false; } - if (success && statement->if_stmt.else_body->ast_kind != AST_COMPOUND_STMT && statement->if_stmt.else_body->ast_kind != AST_IF_STMT) - { - SEMA_ERROR(statement->if_stmt.else_body, - "An 'else' must use '{ }' even around a single statement."); - success = false; - } - } - if (success && statement->if_stmt.then_body->ast_kind != AST_COMPOUND_STMT) - { - SourceLocation *end_of_cond = TOKLOC(cond->span.end_loc); - SourceLocation *start_of_then = TOKLOC(statement->if_stmt.then_body->span.loc); - if (end_of_cond->line != start_of_then->line) - { - SEMA_ERROR(statement->if_stmt.then_body, - "The 'then' part of a single line if-statement must start on the same line as the 'if' or use '{ }'"); - success = false; - } - } - if (context->current_scope->jump_end && !context->current_scope->allow_dead_code) - { - SEMA_ERROR(statement->if_stmt.then_body, "This code can never be executed."); - success = false; - } - context_push_scope_with_label(context, statement->if_stmt.flow.label); + SCOPE_START_WITH_LABEL(statement->if_stmt.flow.label); - success = success && sema_analyse_statement(context, statement->if_stmt.then_body); - bool then_jump = context->current_scope->jump_end; - context_pop_scope(context); - bool else_jump = false; - if (statement->if_stmt.else_body) - { - context_push_scope_with_label(context, statement->if_stmt.flow.label); - success = success && sema_analyse_statement(context, statement->if_stmt.else_body); - else_jump = context->current_scope->jump_end; - context_pop_scope(context); - } - context_pop_defers_and_replace_ast(context, statement); - context_pop_scope(context); + success = success && sema_analyse_statement(context, statement->if_stmt.then_body); + then_jump = context->active_scope.jump_end; + + SCOPE_END; + + else_jump = false; + if (statement->if_stmt.else_body) + { + SCOPE_START_WITH_LABEL(statement->if_stmt.flow.label); + success = success && sema_analyse_statement(context, statement->if_stmt.else_body); + else_jump = context->active_scope.jump_end; + SCOPE_END; + } + + context_pop_defers_and_replace_ast(context, statement); + + SCOPE_OUTER_END; if (then_jump && else_jump && !statement->flow.has_break) { - context->current_scope->jump_end = true; + context->active_scope.jump_end = true; } return success; } @@ -943,18 +848,6 @@ static bool sema_analyse_asm_stmt(Context *context __unused, Ast *statement __un TODO } -static DynamicScope *context_find_scope_by_id(Context *context, ScopeId scope_id) -{ - DynamicScope *scope = context->current_scope; - while (1) - { - if (scope->scope_id == scope_id) return scope; - assert(scope != &context->scopes[0]); - scope--; - } - UNREACHABLE -} - static inline Decl *sema_analyse_label(Context *context, Ast *stmt) { Decl *ambiguous; @@ -970,10 +863,9 @@ static inline Decl *sema_analyse_label(Context *context, Ast *stmt) SEMA_TOKID_ERROR(stmt->contbreak_stmt.label.span, "Expected the name to match a label, not a constant."); return poisoned_decl; } - if (context->current_scope->in_defer) + if (context->active_scope.in_defer) { - DynamicScope *scope = context_find_scope_by_id(context, target->label.scope_id); - if (scope->in_defer != context->current_scope->in_defer) + if (target->label.scope_defer != astid(context->active_scope.in_defer)) { SEMA_ERROR(stmt, stmt->ast_kind == AST_BREAK_STMT ? "You cannot break out of a defer." : "You cannot use continue out of a defer."); return poisoned_decl; @@ -984,7 +876,8 @@ static inline Decl *sema_analyse_label(Context *context, Ast *stmt) static bool context_labels_exist_in_scope(Context *context) { - for (Decl **from = &context->locals[0]; from < context->last_local; from++) + Decl **last = context->active_scope.current_local; + for (Decl **from = &context->locals[0]; from < last; from++) { if ((*from)->decl_kind == DECL_LABEL) return true; } @@ -993,7 +886,7 @@ static bool context_labels_exist_in_scope(Context *context) static bool sema_analyse_break_stmt(Context *context, Ast *statement) { - context->current_scope->jump_end = true; + context->active_scope.jump_end = true; if (!context->break_target && !statement->contbreak_stmt.is_label) { if (context_labels_exist_in_scope(context)) @@ -1007,7 +900,7 @@ static bool sema_analyse_break_stmt(Context *context, Ast *statement) return false; } - statement->contbreak_stmt.defers.start = context->current_scope->defer_last; + statement->contbreak_stmt.defers.start = context->active_scope.defer_last; if (statement->contbreak_stmt.label.name) { Decl *target = sema_analyse_label(context, statement); @@ -1026,7 +919,7 @@ static bool sema_analyse_break_stmt(Context *context, Ast *statement) static bool sema_analyse_next_stmt(Context *context, Ast *statement) { - context->current_scope->jump_end = true; + context->active_scope.jump_end = true; if (!context->next_target && !statement->next_stmt.label.name) { SEMA_ERROR(statement, "'next' is not allowed here."); @@ -1057,11 +950,11 @@ static bool sema_analyse_next_stmt(Context *context, Ast *statement) bool defer_mismatch = false; if (parent->ast_kind == AST_SWITCH_STMT) { - defer_mismatch = context->current_scope->in_defer != context_find_scope_by_id(context, parent->switch_stmt.scope_id)->in_defer; + defer_mismatch = context->active_scope.in_defer != parent->switch_stmt.scope_defer; } else { - defer_mismatch = context->current_scope->in_defer != context_find_scope_by_id(context, parent->catch_stmt.scope_id)->in_defer; + defer_mismatch = context->active_scope.in_defer != parent->catch_stmt.scope_defer; } if (defer_mismatch) { @@ -1071,7 +964,7 @@ static bool sema_analyse_next_stmt(Context *context, Ast *statement) assert(statement->next_stmt.target); } - statement->next_stmt.defers.start = context->current_scope->defer_last; + statement->next_stmt.defers.start = context->active_scope.defer_last; statement->next_stmt.defers.end = parent->switch_stmt.defer; // Plain next. if (!statement->next_stmt.target) @@ -1174,8 +1067,8 @@ static bool sema_analyse_next_stmt(Context *context, Ast *statement) static bool sema_analyse_continue_stmt(Context *context, Ast *statement) { - context->current_scope->jump_end = true; - statement->contbreak_stmt.defers.start = context->current_scope->defer_last; + context->active_scope.jump_end = true; + statement->contbreak_stmt.defers.start = context->active_scope.defer_last; if (!context->continue_target && !statement->contbreak_stmt.label.name) { @@ -1303,6 +1196,7 @@ 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); return all_ok; } @@ -1402,7 +1296,7 @@ static bool sema_analyse_switch_body(Context *context, Ast *statement, SourceSpa return false; } Ast *default_case = NULL; - assert(context_start_defer(context) == context->current_scope->defer_last); + assert(context->active_scope.defer_start == context->active_scope.defer_last); bool exhaustive = false; unsigned case_count = vec_size(cases); @@ -1449,16 +1343,16 @@ static bool sema_analyse_switch_body(Context *context, Ast *statement, SourceSpa for (unsigned i = 0; i < case_count; i++) { Ast *stmt = cases[i]; - context_push_scope(context); - PUSH_BREAK(statement); - Ast *next = (i < case_count - 1) ? cases[i + 1] : NULL; - PUSH_NEXT(next, statement); - Ast *body = stmt->case_stmt.body; - success = success && (!body || sema_analyse_compound_statement_no_scope(context, body)); - POP_BREAK(); - POP_NEXT(); - all_jump_end &= (!body | context->current_scope->jump_end); - context_pop_scope(context); + SCOPE_START + PUSH_BREAK(statement); + Ast *next = (i < case_count - 1) ? cases[i + 1] : NULL; + PUSH_NEXT(next, statement); + Ast *body = stmt->case_stmt.body; + success = success && (!body || sema_analyse_compound_statement_no_scope(context, body)); + POP_BREAK(); + POP_NEXT(); + all_jump_end &= (!body | context->active_scope.jump_end); + SCOPE_END; } statement->flow.no_exit = all_jump_end; if (!success) return false; @@ -1561,31 +1455,29 @@ static bool sema_analyse_ct_switch_stmt(Context *context, Ast *statement) static bool sema_analyse_switch_stmt(Context *context, Ast *statement) { - statement->switch_stmt.scope_id = context->current_scope->scope_id; - context_push_scope_with_label(context, statement->switch_stmt.flow.label); - Expr *cond = statement->switch_stmt.cond; - if (!sema_analyse_cond(context, cond, false)) return false; + statement->switch_stmt.scope_defer = context->active_scope.in_defer; + SCOPE_START_WITH_LABEL(statement->switch_stmt.flow.label); + Expr *cond = statement->switch_stmt.cond; + if (!sema_analyse_cond(context, cond, false)) return false; - Type *switch_type = ast_cond_type(cond)->canonical; - statement->switch_stmt.defer = context->current_scope->defer_last; - bool success = sema_analyse_switch_body(context, statement, cond->span, - switch_type->canonical, - statement->switch_stmt.cases); - if (success) - { + Type *switch_type = ast_cond_type(cond)->canonical; + statement->switch_stmt.defer = context->active_scope.defer_last; + if (!sema_analyse_switch_body(context, statement, cond->span, + switch_type->canonical, + statement->switch_stmt.cases)) + { + return SCOPE_POP_ERROR(); + } context_pop_defers_and_replace_ast(context, statement); - context_pop_scope(context); - } - else - { - context_pop_scope_error(context); - } + + SCOPE_END; + if (statement->flow.no_exit && !statement->flow.has_break) { - context->current_scope->jump_end = true; + context->active_scope.jump_end = true; } - return success; + return true; } /** @@ -1599,107 +1491,100 @@ static bool sema_analyse_catch_stmt(Context *context, Ast *statement) Expr *error_expr = catch_expr; Decl *unwrapped = NULL; - statement->catch_stmt.scope_id = context->current_scope->scope_id; - context_push_scope_with_label(context, statement->catch_stmt.flow.label); + statement->catch_stmt.scope_defer = context->active_scope.in_defer; + SCOPE_START_WITH_LABEL(statement->catch_stmt.flow.label); - statement->catch_stmt.defer = context->current_scope->defer_last; - if (catch_expr->expr_kind == EXPR_BINARY && catch_expr->binary_expr.operator == BINARYOP_ASSIGN) - { - Expr *left = catch_expr->binary_expr.left; - if (left->expr_kind == EXPR_IDENTIFIER) + statement->catch_stmt.defer = context->active_scope.defer_last; + + if (catch_expr->expr_kind == EXPR_BINARY && catch_expr->binary_expr.operator == BINARYOP_ASSIGN) { - Decl *ambiguous_decl; - Decl *dummy; - Decl *error_var_decl = sema_resolve_normal_symbol(context, - left->identifier_expr.identifier, - left->identifier_expr.path, false); - if (!error_var_decl) + Expr *left = catch_expr->binary_expr.left; + if (left->expr_kind == EXPR_IDENTIFIER) { - error_var = decl_new_var(left->span.loc, type_info_new_base(type_error, left->span), VARDECL_LOCAL, - VISIBLE_LOCAL); - error_var->type = type_error; - Expr *right = catch_expr->binary_expr.right; - error_var->var.init_expr = right; - error_expr = right; - statement->catch_stmt.has_err_var = true; - statement->catch_stmt.err_var = error_var; + Decl *ambiguous_decl; + Decl *dummy; + Decl *error_var_decl = sema_resolve_normal_symbol(context, + left->identifier_expr.identifier, + left->identifier_expr.path, false); + if (!error_var_decl) + { + error_var = decl_new_var(left->span.loc, type_info_new_base(type_error, left->span), VARDECL_LOCAL, + VISIBLE_LOCAL); + error_var->type = type_error; + Expr *right = catch_expr->binary_expr.right; + error_var->var.init_expr = right; + error_expr = right; + statement->catch_stmt.has_err_var = true; + statement->catch_stmt.err_var = error_var; + } } } - } - bool success = sema_analyse_expr(context, NULL, error_expr); - if (!success) goto EXIT; + if (!sema_analyse_expr(context, NULL, error_expr)) return SCOPE_POP_ERROR(); - if (error_var) - { - sema_add_local(context, error_var); - } - - if (!error_expr->failable) - { - const char *error_type = type_to_error_string(error_expr->type); - if (error_expr->expr_kind == EXPR_IDENTIFIER - && error_expr->identifier_expr.decl->decl_kind == DECL_VAR - && error_expr->identifier_expr.decl->var.kind == VARDECL_ALIAS) + if (error_var) { - SEMA_ERROR(error_expr, - "'%s' is unwrapped to '%s' here, so it cannot be caught.", - error_expr->identifier_expr.decl->name, - error_type); - success = false; - goto EXIT; + sema_add_local(context, error_var); } - SEMA_ERROR(error_expr, "Expected a failable '%s!' not '%s'.", error_type, error_type); - success = false; - goto EXIT; - } - if (catch_expr->expr_kind == EXPR_IDENTIFIER) - { - unwrapped = catch_expr->identifier_expr.decl; - } - else if (error_var) - { - Expr *right = catch_expr->binary_expr.right; - if (right->expr_kind == EXPR_IDENTIFIER) unwrapped = right->identifier_expr.decl; - } + if (!error_expr->failable) + { + const char *error_type = type_to_error_string(error_expr->type); + if (error_expr->expr_kind == EXPR_IDENTIFIER + && error_expr->identifier_expr.decl->decl_kind == DECL_VAR + && error_expr->identifier_expr.decl->var.kind == VARDECL_ALIAS) + { + SEMA_ERROR(error_expr, + "'%s' is unwrapped to '%s' here, so it cannot be caught.", + error_expr->identifier_expr.decl->name, + error_type); + } + else + { + SEMA_ERROR(error_expr, "Expected a failable '%s!' not '%s'.", error_type, error_type); + } + return SCOPE_POP_ERROR(); + } - if (statement->catch_stmt.is_switch) - { - success = sema_analyse_switch_body(context, - statement, - error_expr->span, - type_error, - statement->catch_stmt.cases); - } - else - { - success = sema_analyse_statement(context, statement->catch_stmt.body); - if (context->current_scope->jump_end) statement->flow.no_exit = true; - } - if (success) - { + if (catch_expr->expr_kind == EXPR_IDENTIFIER) + { + unwrapped = catch_expr->identifier_expr.decl; + } + else if (error_var) + { + Expr *right = catch_expr->binary_expr.right; + if (right->expr_kind == EXPR_IDENTIFIER) unwrapped = right->identifier_expr.decl; + } + + if (statement->catch_stmt.is_switch) + { + if (!sema_analyse_switch_body(context, + statement, + error_expr->span, + type_error, + statement->catch_stmt.cases)) + { + return SCOPE_POP_ERROR(); + } + } + else + { + if (!sema_analyse_statement(context, statement->catch_stmt.body)) return SCOPE_POP_ERROR(); + if (context->active_scope.jump_end) statement->flow.no_exit = true; + } context_pop_defers_and_replace_ast(context, statement); - context_pop_scope(context); - } - else - { - context_pop_scope_error(context); - } -EXIT: - if (success) - { - if (unwrapped && !statement->flow.has_break && statement->flow.no_exit) - { - Decl *decl = decl_copy(unwrapped); - decl->var.kind = VARDECL_ALIAS; - decl->var.alias = unwrapped; - decl->var.failable = false; - sema_unwrap_var(context, decl); - } - } - return success; + SCOPE_END; + + if (unwrapped && !statement->flow.has_break && statement->flow.no_exit) + { + Decl *decl = decl_copy(unwrapped); + decl->var.kind = VARDECL_ALIAS; + decl->var.alias = unwrapped; + decl->var.failable = false; + sema_unwrap_var(context, decl); + } + return true; } @@ -1708,42 +1593,42 @@ static bool sema_analyse_try_stmt(Context *context, Ast *stmt) assert(stmt->try_stmt.decl_expr->expr_kind == EXPR_DECL_LIST); Ast **dexprs = stmt->try_stmt.decl_expr->dexpr_list_expr; - context_push_scope(context); - unsigned entries = vec_size(dexprs); - for (unsigned i = 0; i < entries; i++) - { - Ast *ast = dexprs[i]; - if (ast->ast_kind == AST_DECLARE_STMT) + SCOPE_START + unsigned entries = vec_size(dexprs); + for (unsigned i = 0; i < entries; i++) { - ast->declare_stmt->var.unwrap = true; - if (!sema_analyse_statement(context, ast)) goto ERR; - continue; - } - if (!sema_analyse_statement(context, ast)) goto ERR; - Expr *expr = ast->expr_stmt; - if (!expr->failable) - { - SEMA_ERROR(expr, "The expression to 'try' must be failable."); - goto ERR; - } - if (expr->expr_kind == EXPR_IDENTIFIER) - { - Decl *var = expr->identifier_expr.decl; - Decl *decl = decl_copy(var); - decl->var.kind = VARDECL_ALIAS; - decl->var.alias = var; - decl->var.failable = false; - sema_unwrap_var(context, decl); - } - } - if (!sema_analyse_statement(context, stmt->try_stmt.body)) goto ERR; + Ast *ast = dexprs[i]; + if (ast->ast_kind == AST_DECLARE_STMT) + { + ast->declare_stmt->var.unwrap = true; + if (!sema_analyse_statement(context, ast)) return SCOPE_POP_ERROR(); + continue; + } - context_pop_scope(context); + if (!sema_analyse_statement(context, ast)) return SCOPE_POP_ERROR(); + + Expr *expr = ast->expr_stmt; + if (!expr->failable) + { + SEMA_ERROR(expr, "The expression to 'try' must be failable."); + return SCOPE_POP_ERROR(); + } + if (expr->expr_kind == EXPR_IDENTIFIER) + { + Decl *var = expr->identifier_expr.decl; + Decl *decl = decl_copy(var); + decl->var.kind = VARDECL_ALIAS; + decl->var.alias = var; + decl->var.failable = false; + sema_unwrap_var(context, decl); + } + } + if (!sema_analyse_statement(context, stmt->try_stmt.body)) + { + return SCOPE_POP_ERROR(); + } + SCOPE_END; return true; - - ERR: - context_pop_scope_error(context); - return false; } static bool sema_analyse_volatile_stmt(Context *context, Ast *statement) @@ -1803,11 +1688,13 @@ bool sema_analyse_assert_stmt(Context *context, Ast *statement) static bool sema_analyse_compound_stmt(Context *context, Ast *statement) { - context_push_scope(context); - bool success = sema_analyse_compound_statement_no_scope(context, statement); - bool ends_with_jump = context->current_scope->jump_end; - context_pop_scope(context); - context->current_scope->jump_end = ends_with_jump; + bool success; + bool ends_with_jump; + SCOPE_START + success = sema_analyse_compound_statement_no_scope(context, statement); + ends_with_jump = context->active_scope.jump_end; + SCOPE_END; + context->active_scope.jump_end = ends_with_jump; return success; } @@ -1825,21 +1712,56 @@ static bool sema_analyse_ct_compound_stmt(Context *context, Ast *statement) return all_ok; } +static bool sema_analyse_yield_stmt(Context *context, Ast *stmt) +{ + Decl *macro = context->macro_scope.macro; + if (!macro) + { + SEMA_ERROR(stmt, "'yield' can only be used in macros."); + return false; + } + + if (!macro->has_body_param) + { + SEMA_ERROR(stmt, "'yield' can only be used in macros that takes trailing bodies, use ';' after the regular parameters."); + return false; + } + unsigned expressions = vec_size(stmt->yield_stmt.values); + if (expressions != vec_size(macro->macro_decl.body_parameters)) + { + SEMA_ERROR(stmt, "Expected %d parameter(s) for 'yield'.", vec_size(macro->macro_decl.body_parameters)); + return false; + } + for (unsigned i = 0; i < expressions; i++) + { + Expr *expr = stmt->yield_stmt.values[i]; + Decl *param = context->macro_scope.yield_args[i]; + if (!sema_analyse_expr(context, param->type, expr)) return false; + } + stmt->yield_stmt.declarations = context->macro_scope.yield_args; + bool in_yield = context->macro_scope.in_yield; + context->macro_scope.in_yield = true; + stmt->yield_stmt.ast = copy_ast(context->macro_scope.yield_body); + bool success = sema_analyse_statement(context, stmt->yield_stmt.ast); + context->macro_scope.in_yield = in_yield; + return success; +} + static inline bool sema_analyse_statement_inner(Context *context, Ast *statement) { if (statement->ast_kind == AST_POISONED) { return false; } - if (context->current_scope->jump_end && !context->current_scope->allow_dead_code) + if (context->active_scope.jump_end && !context->active_scope.allow_dead_code) { if (statement->ast_kind == AST_UNREACHABLE_STMT) { - context->current_scope->allow_dead_code = true; + context->active_scope.allow_dead_code = true; return true; } //SEMA_ERROR(statement, "This code will never execute."); - context->current_scope->allow_dead_code = true; + context->active_scope.allow_dead_code = true; //return false; } switch (statement->ast_kind) @@ -1849,6 +1771,8 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement case AST_DOCS: case AST_DOC_DIRECTIVE: UNREACHABLE + case AST_YIELD_STMT: + return sema_analyse_yield_stmt(context, statement); case AST_ASM_STMT: return sema_analyse_asm_stmt(context, statement); case AST_ASSERT_STMT: @@ -1932,49 +1856,45 @@ bool sema_analyse_function_body(Context *context, Decl *func) FunctionSignature *signature = &func->func.function_signature; context->active_function_for_analysis = func; context->rtype = signature->rtype->type; - context->current_scope = &context->scopes[0]; - context->current_scope->scope_id = 0; + context->active_scope = (DynamicScope) { + .scope_id = 0, + .depth = 0, + .local_decl_start = &context->locals[0], + .current_local = &context->locals[0] + }; + context->macro_scope = (MacroScope) {}; context->failable_return = signature->failable; - // Clean out the current scope. - memset(context->current_scope, 0, sizeof(*context->current_scope)); - // Clear returns vec_resize(context->returns, 0); context->scope_id = 0; context->expected_block_type = NULL; - context->last_local = &context->locals[0]; context->in_volatile_section = 0; - context->macro_counter = 0; - context->macro_nesting = 0; context->continue_target = 0; context->next_target = 0; context->next_switch = 0; context->break_target = 0; func->func.annotations = CALLOCS(FuncAnnotations); - context_push_scope(context); - Decl **params = signature->params; - assert(context->current_scope == &context->scopes[1]); - VECEACH(params, i) - { - if (!sema_add_local(context, params[i])) return false; - } - if (!sema_analyse_compound_statement_no_scope(context, func->func.body)) return false; - assert(context->current_scope == &context->scopes[1]); - if (!context->current_scope->jump_end) - { - Type *canonical_rtype = signature->rtype->type->canonical; - if (canonical_rtype != type_void) + SCOPE_START + assert(context->active_scope.depth == 1); + Decl **params = signature->params; + VECEACH(params, i) { - // IMPROVE better pointer to end. - SEMA_ERROR(func, "Missing return statement at the end of the function."); - return false; + if (!sema_add_local(context, params[i])) return false; } - } - - - context_pop_scope(context); - context->current_scope = NULL; + if (!sema_analyse_compound_statement_no_scope(context, func->func.body)) return false; + assert(context->active_scope.depth == 1); + if (!context->active_scope.jump_end) + { + Type *canonical_rtype = signature->rtype->type->canonical; + if (canonical_rtype != type_void) + { + // IMPROVE better pointer to end. + SEMA_ERROR(func, "Missing return statement at the end of the function."); + return false; + } + } + SCOPE_END; return true; } diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index f35c9a7fc..f694c9aa9 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -24,3 +24,99 @@ bool sema_resolve_type_info(Context *context, TypeInfo *type_info) { return sema_resolve_type_info_maybe_inferred(context, type_info, false); } + + + +void context_change_scope_with_flags(Context *context, ScopeFlags flags) +{ + unsigned depth = context->active_scope.depth + 1; + if (depth > MAX_SCOPE_DEPTH) + { + FATAL_ERROR("Too deeply nested scopes."); + } + + Ast *previous_defer = context->active_scope.in_defer; + AstId parent_defer = context->active_scope.defer_last; + Decl **last_local = context->active_scope.current_local; + assert(parent_defer < 1000000); + // Defer and expression blocks introduce their own return/break/continue + // otherwise just merge with the old flags. + if (!(flags & (SCOPE_DEFER | SCOPE_EXPR_BLOCK))) + { + flags = context->active_scope.flags | flags; + } + context->active_scope = (DynamicScope) { + .scope_id = ++context->scope_id, + .allow_dead_code = false, + .jump_end = false, + .depth = depth, + .current_local = last_local, + .local_decl_start = last_local, + .in_defer = previous_defer, + .defer_last = parent_defer, + .defer_start = parent_defer, + .flags = flags, + }; + if (context->scope_id == 0) + { + FATAL_ERROR("Too many scopes."); + } +} + +void context_change_scope_for_label(Context *context, Decl *label) +{ + context_change_scope_with_flags(context, SCOPE_NONE); + + if (label) + { + label->label.defer = context->active_scope.defer_last; + sema_add_local(context, label); + label->label.scope_defer = astid(context->active_scope.in_defer); + } +} + + + +void context_pop_defers_to(Context *context, DeferList *list) +{ + list->end = context->active_scope.defer_start; + assert(context->active_scope.defer_last < 10000000); + list->start = context->active_scope.defer_last; + context->active_scope.defer_last = list->end; +} + + + + + +Expr *context_pop_defers_and_wrap_expr(Context *context, Expr *expr) +{ + DeferList defers = { 0, 0 }; + context_pop_defers_to(context, &defers); + if (defers.end == defers.start) return expr; + Expr *wrap = expr_new(EXPR_SCOPED_EXPR, expr->span); + expr_copy_types(wrap, expr); + wrap->resolve_status = RESOLVE_DONE; + wrap->expr_scope.expr = expr; + wrap->expr_scope.defers = defers; + return expr; +} + +void context_pop_defers_and_replace_ast(Context *context, Ast *ast) +{ + DeferList defers = { 0, 0 }; + context_pop_defers_to(context, &defers); + if (defers.end == defers.start) return; + if (ast->ast_kind == AST_DEFER_STMT) + { + assert(defers.start == astid(ast)); + *ast = *ast->defer_stmt.body; + return; + } + assert(ast->ast_kind != AST_COMPOUND_STMT); + Ast *replacement = ast_copy(ast); + ast->ast_kind = AST_SCOPED_STMT; + ast->scoped_stmt.stmt = replacement; + ast->scoped_stmt.defers = defers; +} + diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 84fd73dac..2b3a55245 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -252,12 +252,14 @@ const char *token_type_to_string(TokenType type) return "typeof"; case TOKEN_UNION: return "union"; - case TOKEN_WHILE: - return "while"; case TOKEN_VAR: return "var"; case TOKEN_VOLATILE: return "volatile"; + case TOKEN_WHILE: + return "while"; + case TOKEN_YIELD: + return "yield"; // Named types case TOKEN_VIRTUAL: diff --git a/src/utils/lib.h b/src/utils/lib.h index 015c9695d..ffc6123a6 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -330,6 +330,8 @@ static inline void* _expand(void *vec, size_t element_size) #define CONCAT(a, b) CONCAT_INNER(a, b) #define VECEACH(_vec, _index) \ for (unsigned _index = 0, CONCAT(__vecsize_, __LINE__) = vec_size(_vec); _index < CONCAT(__vecsize_, __LINE__); _index++) +#define foreach(_vec, _index) \ + for (unsigned _index = 0, CONCAT(__vecsize_, __LINE__) = vec_size(_vec); _index < CONCAT(__vecsize_, __LINE__); _index++) #define VECNEW(_type, _capacity) ((_type *)(_vec_new(sizeof(_type), _capacity) + 1)) diff --git a/test/test_suite/macros/macro_with_body.c3t b/test/test_suite/macros/macro_with_body.c3t new file mode 100644 index 000000000..76c40907a --- /dev/null +++ b/test/test_suite/macros/macro_with_body.c3t @@ -0,0 +1,117 @@ +module withbody; + + +extern func int printf(char *, ...); + +struct Foo +{ + int x; +} + +func int Foo.mutate(Foo *foo) +{ + printf("Mutating\n"); + return 10 * ++foo.x; +} + +macro macro_with_body(foo, &x; x, y) +{ + x = foo.x; + yield foo.mutate(), x; +} + +macro repeat(int times; x) +{ + for (int i = 0; i < times; i++) + { + yield i + 1; + } +} + +func void main() +{ + Foo f = { 33 }; + int y; + @macro_with_body(f, y; int x, int dy) + { + printf("Got values %d, %d\n", x, dy); + }; + @repeat(10; int loop) + { + printf("Repeat %d\n", loop); + }; + +} + +// #expect: withbody.ll + +define i32 @withbody.Foo__mutate(%Foo* %0) +entry: + %foo = alloca %Foo*, align 8 + store %Foo* %0, %Foo** %foo, align 8 + %1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.2, i32 0, i32 0)) + %2 = load %Foo*, %Foo** %foo, align 8 + %3 = getelementptr inbounds %Foo, %Foo* %2, i32 0, i32 0 + %4 = load i32, i32* %3, align 8 + %add = add i32 %4, 1 + store i32 %add, i32* %3, align 8 + %mul = mul i32 10, %add + ret i32 %mul + + +define void @main() + %f = alloca %Foo, align 4 + %y = alloca i32, align 4 + %foo = alloca %Foo, align 4 + %x = alloca i32, align 4 + %dy = alloca i32, align 4 + %times = alloca i32, align 4 + %i = alloca i32, align 4 + %loop = alloca i32, align 4 + %0 = bitcast %Foo* %f to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 4 bitcast (%Foo* @.__const to i8*), i32 4, i1 false) + store i32 0, i32* %y, align 4 + %1 = load %Foo, %Foo* %f, align 4 + store %Foo %1, %Foo* %foo, align 4 + %2 = getelementptr inbounds %Foo, %Foo* %foo, i32 0, i32 0 + %3 = load i32, i32* %2, align 4 + store i32 %3, i32* %y, align 4 + %4 = call i32 @withbody.Foo__mutate(%Foo* %foo) + store i32 %4, i32* %x, align 4 + %5 = load i32, i32* %y, align 4 + store i32 %5, i32* %dy, align 4 + %6 = load i32, i32* %x, align 4 + %7 = load i32, i32* %dy, align 4 + %8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* @.str, i32 0, i32 0), i32 %6, i32 %7) + br label %expr_block.exit + +expr_block.exit: + store i32 10, i32* %times, align 4 + store i32 0, i32* %i, align 4 + br label %for.cond + +for.cond: + %9 = load i32, i32* %i, align 4 + %10 = load i32, i32* %times, align 4 + %lt = icmp slt i32 %9, %10 + br i1 %lt, label %for.body, label %for.exit + +for.body: + %11 = load i32, i32* %i, align 4 + %add = add i32 %11, 1 + store i32 %add, i32* %loop, align 4 + %12 = load i32, i32* %loop, align 4 + %13 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.1, i32 0, i32 0), i32 %12) + br label %for.inc + +for.inc: + %14 = load i32, i32* %i, align 4 + %add1 = add i32 %14, 1 + store i32 %add1, i32* %i, align 4 + br label %for.cond + +for.exit: + br label %expr_block.exit2 + +expr_block.exit2: + ret void diff --git a/test/test_suite/macros/no_body.c3 b/test/test_suite/macros/no_body.c3 new file mode 100644 index 000000000..b8362dcd9 --- /dev/null +++ b/test/test_suite/macros/no_body.c3 @@ -0,0 +1,49 @@ +module test; + +extern func int printf(char *, ...); + +macro foo(x; y) +{ + yield x; +} + +func void test() +{ + @foo(10); // #error: Expected call to have a trailing statement, did you forget to add it? +} + +func void test3() +{ + @foo(10) // #error: Not enough parameters + { + }; +} + +func void test5() +{ + @foo(10; int a, int b) // #error: Too many parameters for the macro body, expected + { + }; +} +macro foo_no(x) +{ + return x; +} + +func void test2() +{ + @foo_no(10) // #error: This macro does not support trailing statements + { + printf("foek"); + }; +} + +macro foo2(x) +{ + yield x; // #error: 'yield' can only be used in macros that takes trailing bodies, use ';' after the regular parameters. +} + +func void test4() +{ + @foo2(10); +} \ No newline at end of file