diff --git a/src/compiler/ast.c b/src/compiler/ast.c index d3cb0541d..32d876e1d 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -270,7 +270,6 @@ bool expr_is_pure(Expr *expr) case EXPR_COND: case EXPR_DESIGNATOR: case EXPR_DECL: - case EXPR_OR_ERROR: case EXPR_EXPR_BLOCK: case EXPR_FAILABLE: case EXPR_RETHROW: @@ -281,7 +280,6 @@ bool expr_is_pure(Expr *expr) case EXPR_INITIALIZER_LIST: case EXPR_DESIGNATED_INITIALIZER_LIST: case EXPR_POST_UNARY: - case EXPR_SCOPED_EXPR: case EXPR_SLICE_ASSIGN: case EXPR_TRY_UNWRAP: case EXPR_TRY_UNWRAP_CHAIN: @@ -330,7 +328,6 @@ bool expr_is_simple(Expr *expr) case EXPR_GROUP: expr = expr->inner_expr; goto RETRY; - case EXPR_OR_ERROR: case EXPR_TERNARY: return false; case EXPR_RETHROW: @@ -401,6 +398,7 @@ BinaryOp binary_op[TOKEN_LAST + 1] = { [TOKEN_SHR] = BINARYOP_SHR, [TOKEN_AND] = BINARYOP_AND, [TOKEN_OR] = BINARYOP_OR, + [TOKEN_QUESTQUEST] = BINARYOP_OR_ERR, [TOKEN_AMP] = BINARYOP_BIT_AND, [TOKEN_BIT_OR] = BINARYOP_BIT_OR, [TOKEN_BIT_XOR] = BINARYOP_BIT_XOR, diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 9fca0defa..219cb6463 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -581,8 +581,6 @@ typedef struct Decl_ bool is_packed : 1; bool is_substruct : 1; bool has_variable_array : 1; - bool no_scope : 1; - bool escaping : 1; bool is_value : 1; bool is_autoimport : 1; bool has_extname : 1; @@ -650,18 +648,6 @@ typedef struct Decl_ -typedef struct -{ - bool is_jump : 1; - bool widen : 1; - Expr *expr; - union - { - Expr *or_error_expr; - Ast *or_error_stmt; - }; -} ExprOrError; - typedef struct @@ -826,11 +812,6 @@ typedef struct Ast *ast; } ExprBodyExpansion; -typedef struct -{ - Expr *expr; - AstId defer_stmts; -} ExprScope; typedef struct { @@ -839,7 +820,6 @@ typedef struct typedef struct { - bool no_scope; AstId first_stmt; Expr **args; Decl **params; @@ -956,7 +936,6 @@ struct Expr_ ExprArgv argv_expr; // 16 ExprGuard rethrow_expr; // 16 Decl *decl_expr; // 8 - ExprOrError or_error_expr; // 24 ExprSliceAssign slice_assign_expr; // 8 ExprBinary binary_expr; // 12 ExprTernary ternary_expr; // 16 @@ -982,7 +961,6 @@ struct Expr_ Expr** expression_list; // 8 Expr** initializer_list; // 8 Expr** designated_init_list; // 8 - ExprScope expr_scope; // 16 ExprFuncBlock expr_block; // 4 ExprMacroBlock macro_block; // 24 Expr** cond_expr; // 8 @@ -1425,7 +1403,6 @@ typedef struct SemaContext_ Ast *yield_body; Type *expected_block_type; Ast **returns; - bool expr_failable_return; // Reusable returns cache. Ast **returns_cache; }; diff --git a/src/compiler/copying.c b/src/compiler/copying.c index a98dae8fd..5eb066fdd 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -237,17 +237,6 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) case EXPR_COND: MACRO_COPY_EXPR_LIST(expr->cond_expr); return expr; - case EXPR_OR_ERROR: - MACRO_COPY_EXPR(expr->or_error_expr.expr); - if (expr->or_error_expr.is_jump) - { - MACRO_COPY_EXPR(expr->or_error_expr.or_error_expr); - } - else - { - MACRO_COPY_AST(expr->or_error_expr.or_error_stmt); - } - return expr; case EXPR_MACRO_BLOCK: UNREACHABLE case EXPR_COMPOUND_LITERAL: @@ -288,6 +277,7 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) } else { + MACRO_COPY_EXPRID(expr->call_expr.function); } MACRO_COPY_ASTID(expr->call_expr.body); @@ -302,6 +292,14 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) case EXPR_BITACCESS: case EXPR_ACCESS: MACRO_COPY_EXPR(expr->access_expr.parent); + if (expr->resolve_status == RESOLVE_DONE) + { + fixup_decl(c, &expr->access_expr.ref); + } + else + { + MACRO_COPY_EXPR(expr->access_expr.child); + } return expr; case EXPR_INITIALIZER_LIST: MACRO_COPY_EXPR_LIST(expr->initializer_list); @@ -316,11 +314,6 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) MACRO_COPY_EXPRID(expr->cast_expr.expr); MACRO_COPY_TYPEID(expr->cast_expr.type_info); return expr; - case EXPR_SCOPED_EXPR: - SCOPE_FIXUP_START - MACRO_COPY_EXPR(expr->expr_scope.expr); - SCOPE_FIXUP_END; - return expr; } UNREACHABLE } diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 6e4a07589..f940160be 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -19,6 +19,7 @@ typedef enum BINARYOP_BIT_AND, BINARYOP_AND, BINARYOP_OR, + BINARYOP_OR_ERR, // Don't change the ordering for GT to EQ or things will break BINARYOP_GT, BINARYOP_GE, @@ -189,7 +190,6 @@ typedef enum EXPR_COND, EXPR_DECL, EXPR_DESIGNATOR, - EXPR_OR_ERROR, EXPR_EXPR_BLOCK, EXPR_EXPRESSION_LIST, EXPR_FAILABLE, @@ -207,7 +207,6 @@ typedef enum EXPR_LEN, EXPR_PTR, EXPR_POST_UNARY, - EXPR_SCOPED_EXPR, EXPR_SLICE, EXPR_SLICE_ASSIGN, EXPR_SUBSCRIPT, @@ -665,8 +664,6 @@ typedef enum ATTRIBUTE_UNUSED, ATTRIBUTE_USED, ATTRIBUTE_NAKED, - ATTRIBUTE_NOSCOPE, - ATTRIBUTE_ESCAPING, ATTRIBUTE_CDECL, ATTRIBUTE_STDCALL, ATTRIBUTE_VECCALL, diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index bb5a96382..a2ac72d0a 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -1083,17 +1083,6 @@ static inline void gencontext_emit_access_addr(GenContext *context, BEValue *be_ gencontext_emit_member_addr(context, be_value, type_lowering(parent->type)->decl, member); } -static void llvm_emit_scoped_expr(GenContext *c, BEValue *value, Expr *expr) -{ - llvm_emit_expr(c, value, expr->expr_scope.expr); - AstId current = expr->expr_scope.defer_stmts; - while (current) - { - Ast *ast = astptr(current); - llvm_emit_stmt(c, ast); - current = ast->next; - } -} static inline void llvm_emit_initialize_reference(GenContext *c, BEValue *value, Expr *expr); @@ -3058,9 +3047,86 @@ void llvm_emit_comparison(GenContext *c, BEValue *be_value, BEValue *lhs, BEValu } TODO } + +static void gencontext_emit_or_error(GenContext *c, BEValue *be_value, Expr *expr) +{ + LLVMBasicBlockRef else_block = llvm_basic_block_new(c, "else_block"); + LLVMBasicBlockRef phi_block = llvm_basic_block_new(c, "phi_block"); + + // Store catch/error var + PUSH_ERROR(); + + // Set the catch/error var + c->error_var = NULL; + c->catch_block = else_block; + + BEValue normal_value; + llvm_emit_exprid(c, &normal_value, expr->binary_expr.left); + llvm_value_rvalue(c, &normal_value); + + // Restore. + POP_ERROR(); + + // Emit success and jump to phi. + LLVMBasicBlockRef success_end_block = llvm_get_current_block_if_in_use(c); + + if (success_end_block) llvm_emit_br(c, phi_block); + + // Emit else + llvm_emit_block(c, else_block); + + BEValue else_value; + llvm_emit_exprid(c, &else_value, expr->binary_expr.right); + llvm_value_rvalue(c, &else_value); + + LLVMBasicBlockRef else_block_exit = llvm_get_current_block_if_in_use(c); + + if (else_block_exit) llvm_emit_br(c, phi_block); + + llvm_emit_block(c, phi_block); + + if (!else_block_exit) + { + *be_value = normal_value; + return; + } + if (!success_end_block) + { + *be_value = else_value; + return; + } + + if (expr->type->type_kind == TYPE_BOOL) + { + + } + LLVMValueRef logic_values[2] = { normal_value.value, else_value.value }; + LLVMBasicBlockRef blocks[2] = { success_end_block, else_block_exit }; + + + if (expr->type->type_kind == TYPE_BOOL) + { + LLVMValueRef phi = LLVMBuildPhi(c->builder, c->bool_type, "val"); + LLVMAddIncoming(phi, logic_values, blocks, 2); + llvm_value_set_bool(be_value, phi); + return; + } + else + { + LLVMValueRef phi = LLVMBuildPhi(c->builder, llvm_get_type(c, expr->type), "val"); + LLVMAddIncoming(phi, logic_values, blocks, 2); + llvm_value_set(be_value, phi, expr->type); + } + +} + void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValue *lhs_loaded, BinaryOp binary_op) { - + if (binary_op == BINARYOP_OR_ERR) + { + gencontext_emit_or_error(c, be_value, expr); + return; + } if (binary_op == BINARYOP_AND || binary_op == BINARYOP_OR) { gencontext_emit_logical_and_or(c, be_value, expr, binary_op); @@ -3098,6 +3164,7 @@ void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValu switch (binary_op) { case BINARYOP_ERROR: + case BINARYOP_OR_ERR: UNREACHABLE case BINARYOP_MULT: if (is_float) @@ -3439,113 +3506,7 @@ void llvm_emit_try_expr(GenContext *c, BEValue *value, Expr *expr) llvm_value_set(value, phi, expr->type); } -static inline void gencontext_emit_else_jump_expr(GenContext *c, BEValue *be_value, Expr *expr) -{ - LLVMBasicBlockRef else_block = llvm_basic_block_new(c, "else_block"); - LLVMBasicBlockRef no_err_block = llvm_basic_block_new(c, "noerr_block"); - // Store catch/error var - PUSH_ERROR(); - - // Set the catch/error var - c->error_var = NULL; - c->catch_block = else_block; - - - llvm_emit_expr(c, be_value, expr->or_error_expr.expr); - llvm_value_rvalue(c, be_value); - - // Restore. - POP_ERROR(); - - // Emit success and to end. - llvm_emit_br(c, no_err_block); - - // Emit else - llvm_emit_block(c, else_block); - llvm_emit_stmt(c, expr->or_error_expr.or_error_stmt); - llvm_emit_br(c, no_err_block); - - llvm_emit_block(c, no_err_block); -} - - -static void gencontext_emit_or_error(GenContext *c, BEValue *be_value, Expr *expr) -{ - if (expr->or_error_expr.is_jump) - { - gencontext_emit_else_jump_expr(c, be_value, expr); - return; - } - LLVMBasicBlockRef else_block = llvm_basic_block_new(c, "else_block"); - LLVMBasicBlockRef phi_block = llvm_basic_block_new(c, "phi_block"); - - // Store catch/error var - PUSH_ERROR(); - - // Set the catch/error var - c->error_var = NULL; - c->catch_block = else_block; - - BEValue normal_value; - llvm_emit_expr(c, &normal_value, expr->or_error_expr.expr); - llvm_value_rvalue(c, &normal_value); - - // Restore. - POP_ERROR(); - - // Emit success and jump to phi. - LLVMBasicBlockRef success_end_block = llvm_get_current_block_if_in_use(c); - - if (success_end_block) llvm_emit_br(c, phi_block); - - // Emit else - llvm_emit_block(c, else_block); - - BEValue else_value; - llvm_emit_expr(c, &else_value, expr->or_error_expr.or_error_expr); - llvm_value_rvalue(c, &else_value); - - LLVMBasicBlockRef else_block_exit = llvm_get_current_block_if_in_use(c); - - if (else_block_exit) llvm_emit_br(c, phi_block); - - llvm_emit_block(c, phi_block); - - if (!else_block_exit) - { - *be_value = normal_value; - return; - } - if (!success_end_block) - { - *be_value = else_value; - return; - } - - if (expr->type->type_kind == TYPE_BOOL) - { - - } - LLVMValueRef logic_values[2] = { normal_value.value, else_value.value }; - LLVMBasicBlockRef blocks[2] = { success_end_block, else_block_exit }; - - - if (expr->type->type_kind == TYPE_BOOL) - { - LLVMValueRef phi = LLVMBuildPhi(c->builder, c->bool_type, "val"); - LLVMAddIncoming(phi, logic_values, blocks, 2); - llvm_value_set_bool(be_value, phi); - return; - } - else - { - LLVMValueRef phi = LLVMBuildPhi(c->builder, llvm_get_type(c, expr->type), "val"); - LLVMAddIncoming(phi, logic_values, blocks, 2); - llvm_value_set(be_value, phi, expr->type); - } - -} /** * This is the foo? instruction. @@ -5036,7 +4997,7 @@ static inline void llvm_emit_return_block(GenContext *context, BEValue *be_value LLVMValueRef error_out = context->error_var; LLVMBasicBlockRef error_block = context->catch_block; - if (type_lowered != type_void) + if (type_no_fail(type_lowered) != type_void) { return_out = llvm_emit_alloca_aligned(context, type_lowered, "blockret"); } @@ -5095,7 +5056,6 @@ static inline void llvm_emit_return_block(GenContext *context, BEValue *be_value if (llvm_basic_block_is_unused(expr_block)) { // Skip the expr block. - assert(!context->return_out); llvm_value_set(be_value, NULL, type_void); goto DONE; } @@ -5169,16 +5129,6 @@ static inline void llvm_emit_macro_block(GenContext *context, BEValue *be_value, llvm_emit_expr(context, &value, expr->macro_block.args[i]); llvm_store_decl_raw(context, decl, llvm_load_value_store(context, &value)); } - if (expr->macro_block.no_scope) - { - AstId current = expr->macro_block.first_stmt; - while (current) - { - llvm_emit_stmt(context, ast_next(¤t)); - } - llvm_value_set(be_value, NULL, type_void); - return; - } llvm_emit_return_block(context, be_value, expr->type, expr->macro_block.first_stmt); } @@ -5595,9 +5545,6 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_NOP: llvm_value_set(value, NULL, type_void); return; - case EXPR_OR_ERROR: - gencontext_emit_or_error(c, value, expr); - return; case EXPR_MACRO_BLOCK: llvm_emit_macro_block(c, value, expr); return; @@ -5610,9 +5557,6 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_EXPR_BLOCK: llvm_emit_expr_block(c, value, expr); return; - case EXPR_SCOPED_EXPR: - llvm_emit_scoped_expr(c, value, expr); - return; case EXPR_UNARY: gencontext_emit_unary_expr(c, value, expr); return; diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index ce03b0b78..0a165ccd5 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -152,6 +152,16 @@ static inline void llvm_emit_return(GenContext *c, Ast *ast) PUSH_ERROR(); + Expr *expr = ast->return_stmt.expr; + if (expr && expr->expr_kind == EXPR_FAILABLE) + { + BEValue be_value; + llvm_emit_expr(c, &be_value, expr->inner_expr); + llvm_emit_statement_chain(c, ast->return_stmt.cleanup); + llvm_emit_return_abi(c, NULL, &be_value); + return; + } + LLVMBasicBlockRef error_return_block = NULL; LLVMValueRef error_out = NULL; if (type_is_failable(c->cur_func_decl->type->func.prototype->rtype)) @@ -208,10 +218,18 @@ static inline void llvm_emit_block_exit_return(GenContext *c, Ast *ast) c->error_var = c->block_error_var; c->catch_block = c->block_failable_exit; - bool has_return_value = ast->return_stmt.expr != NULL; + LLVMBasicBlockRef err_cleanup_block = NULL; + Expr *ret_expr = ast->return_stmt.expr; + BEValue return_value = { 0 }; - if (has_return_value) + if (ret_expr) { + if (ast->return_stmt.cleanup && IS_FAILABLE(ret_expr)) + { + assert(c->catch_block); + err_cleanup_block = llvm_basic_block_new(c, "opt_block_cleanup"); + c->catch_block = err_cleanup_block; + } llvm_emit_expr(c, &return_value, ast->return_stmt.expr); llvm_value_fold_failable(c, &return_value); } @@ -219,12 +237,22 @@ static inline void llvm_emit_block_exit_return(GenContext *c, Ast *ast) POP_ERROR(); llvm_emit_statement_chain(c, ast->return_stmt.cleanup); - - if (c->return_out) + if (c->return_out && return_value.value) { llvm_store_value_aligned(c, c->return_out, &return_value, type_alloca_alignment(return_value.type)); } - llvm_emit_jmp(c, c->block_return_exit); + + if (err_cleanup_block) + { + llvm_emit_br(c, c->block_return_exit); + llvm_emit_block(c, err_cleanup_block); + llvm_emit_statement_chain(c, ast->return_stmt.cleanup); + llvm_emit_jmp(c, c->block_failable_exit); + } + else + { + llvm_emit_jmp(c, c->block_return_exit); + } } diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 660331b83..c580fb4ea 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -27,19 +27,9 @@ inline Expr *parse_precedence_with_left_side(ParseContext *c, Expr *left_side, P { TokenType tok = c->tok; Precedence token_precedence = rules[tok].precedence; - bool special_question = false; - if (tok == TOKEN_QUESTION) - { - ParseRule rule = rules[peek(c)]; - if (!rule.prefix) - { - token_precedence = PREC_CALL; - special_question = true; - } - } if (precedence > token_precedence) break; if (!expr_ok(left_side)) return left_side; - ParseFn infix_rule = special_question ? &parse_rethrow_expr : rules[tok].infix; + ParseFn infix_rule = rules[tok].infix; if (!infix_rule) { SEMA_ERROR_HERE("An expression was expected."); @@ -521,6 +511,13 @@ static Expr *parse_ternary_expr(ParseContext *c, Expr *left_side) else { advance_and_verify(c, TOKEN_QUESTION); + if (!rules[c->tok].prefix) + { + expr_ternary->expr_kind = EXPR_RETHROW; + expr_ternary->rethrow_expr.inner = left_side; + RANGE_EXTEND_PREV(expr_ternary); + return expr_ternary; + } ASSIGN_EXPR_OR_RET(Expr * true_expr, parse_precedence(c, PREC_TERNARY + 1), poisoned_expr); expr_ternary->ternary_expr.then_expr = exprid(true_expr); CONSUME_OR_RET(TOKEN_COLON, poisoned_expr); @@ -1072,14 +1069,6 @@ static Expr *parse_try_expr(ParseContext *c, Expr *left) return try_expr; } -static Expr *parse_rethrow_expr(ParseContext *c, Expr *left) -{ - Expr *rethrow_expr = EXPR_NEW_EXPR(EXPR_RETHROW, left); - advance(c); - rethrow_expr->rethrow_expr.inner = left; - RANGE_EXTEND_PREV(rethrow_expr); - return rethrow_expr; -} static Expr *parse_force_unwrap_expr(ParseContext *c, Expr *left) { @@ -1090,36 +1079,6 @@ static Expr *parse_force_unwrap_expr(ParseContext *c, Expr *left) return force_unwrap_expr; } -static Expr *parse_or_error_expr(ParseContext *c, Expr *left) -{ - Expr *else_expr = EXPR_NEW_TOKEN(EXPR_OR_ERROR); - advance_and_verify(c, TOKEN_QUESTQUEST); - else_expr->or_error_expr.expr = left; - switch (c->tok) - { - case TOKEN_RETURN: - case TOKEN_BREAK: - case TOKEN_CONTINUE: - case TOKEN_NEXTCASE: - { - ASSIGN_AST_OR_RET(Ast *ast, parse_jump_stmt_no_eos(c), poisoned_expr); - else_expr->or_error_expr.is_jump = true; - else_expr->or_error_expr.or_error_stmt = ast; - if (!tok_is(c, TOKEN_EOS)) - { - SEMA_ERROR(ast, "An else jump statement must end with a ';'"); - return poisoned_expr; - } - break; - } - default: - { - ASSIGN_EXPR_OR_RET(else_expr->or_error_expr.or_error_expr, parse_precedence(c, PREC_ASSIGNMENT), poisoned_expr); - break; - } - } - return else_expr; -} static Expr *parse_builtin(ParseContext *c, Expr *left) { @@ -1757,7 +1716,7 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_VARIANT] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_QUESTION] = { NULL, parse_ternary_expr, PREC_TERNARY }, - [TOKEN_QUESTQUEST] = { NULL, parse_or_error_expr, PREC_TERNARY}, + [TOKEN_QUESTQUEST] = { NULL, parse_binary, PREC_TERNARY}, [TOKEN_ELVIS] = { NULL, parse_ternary_expr, PREC_TERNARY }, [TOKEN_PLUSPLUS] = { parse_unary_expr, parse_post_unary, PREC_CALL }, [TOKEN_MINUSMINUS] = { parse_unary_expr, parse_post_unary, PREC_CALL }, diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 51fa7e7ac..52cf4045f 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -703,6 +703,7 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type) case BINARYOP_ADD: case BINARYOP_DIV: case BINARYOP_MOD: + case BINARYOP_OR_ERR: { Expr *res = recursive_may_narrow_float(exprptr(expr->binary_expr.left), type); if (res) return res; @@ -757,13 +758,6 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type) case EXPR_RETVAL: if (type_size(expr->type) > type_size(type)) return expr; return NULL; - case EXPR_OR_ERROR: - { - Expr *res = recursive_may_narrow_float(expr->or_error_expr.expr, type); - if (res) return res; - if (expr->or_error_expr.is_jump) return NULL; - return recursive_may_narrow_float(expr->or_error_expr.or_error_expr, type); - } case EXPR_EXPRESSION_LIST: return recursive_may_narrow_float(VECLAST(expr->expression_list), type); case EXPR_GROUP: @@ -820,8 +814,6 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type) UNREACHABLE case EXPR_POST_UNARY: return recursive_may_narrow_float(expr->unary_expr.expr, type); - case EXPR_SCOPED_EXPR: - return recursive_may_narrow_float(expr->expr_scope.expr, type); case EXPR_TRY: return recursive_may_narrow_float(expr->inner_expr, type); case EXPR_UNARY: @@ -869,6 +861,7 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type) case BINARYOP_BIT_OR: case BINARYOP_BIT_XOR: case BINARYOP_BIT_AND: + case BINARYOP_OR_ERR: { Expr *res = recursive_may_narrow_int(exprptr(expr->binary_expr.left), type); if (res) return res; @@ -923,13 +916,6 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type) case EXPR_LEN: if (type_size(type) < type_size(type_cint)) return expr; return NULL; - case EXPR_OR_ERROR: - { - Expr *res = recursive_may_narrow_int(expr->or_error_expr.expr, type); - if (res) return res; - if (expr->or_error_expr.is_jump) return NULL; - return recursive_may_narrow_int(expr->or_error_expr.or_error_expr, type); - } case EXPR_EXPRESSION_LIST: return recursive_may_narrow_int(VECLAST(expr->expression_list), type); case EXPR_RETHROW: @@ -981,8 +967,6 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type) UNREACHABLE case EXPR_POST_UNARY: return recursive_may_narrow_int(expr->unary_expr.expr, type); - case EXPR_SCOPED_EXPR: - return recursive_may_narrow_int(expr->expr_scope.expr, type); case EXPR_TRY: case EXPR_CATCH: case EXPR_GROUP: diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 133c7a824..bda36685a 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -1183,8 +1183,6 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute [ATTRIBUTE_REGCALL] = ATTR_FUNC, [ATTRIBUTE_FASTCALL] = ATTR_FUNC, [ATTRIBUTE_OVERLAP] = ATTR_BITSTRUCT, - [ATTRIBUTE_NOSCOPE] = ATTR_MACRO, - [ATTRIBUTE_ESCAPING] = ATTR_MACRO, [ATTRIBUTE_AUTOIMPORT] = ATTR_MACRO | ATTR_FUNC, [ATTRIBUTE_OPERATOR] = ATTR_MACRO | ATTR_FUNC, }; @@ -1683,14 +1681,6 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl) had = decl->operator > 0; decl->operator = attr->operator; break; - case ATTRIBUTE_NOSCOPE: - had = decl->no_scope; - decl->no_scope = true; - break; - case ATTRIBUTE_ESCAPING: - had = decl->escaping; - decl->escaping = true; - break; case ATTRIBUTE_AUTOIMPORT: decl->is_autoimport = true; break; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index d0ce99372..abcf21394 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -338,10 +338,6 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) case EXPR_DESIGNATOR: expr = expr->designator_expr.value; goto RETRY; - case EXPR_OR_ERROR: - if (expr->or_error_expr.is_jump) return false; - assert(!expr_is_constant_eval(expr->or_error_expr.expr, eval_kind)); - return false; case EXPR_EXPR_BLOCK: case EXPR_DECL: case EXPR_CALL: @@ -350,7 +346,6 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) case EXPR_TRY_UNWRAP: case EXPR_TRY_UNWRAP_CHAIN: case EXPR_POST_UNARY: - case EXPR_SCOPED_EXPR: case EXPR_SLICE_ASSIGN: case EXPR_MACRO_BLOCK: case EXPR_RETHROW: @@ -1655,10 +1650,17 @@ static inline Type *unify_returns(SemaContext *context) bool all_returns_need_casts = false; Type *common_type = NULL; + bool only_has_rethrow = true; // 1. Loop through the returns. VECEACH(context->returns, i) { Ast *return_stmt = context->returns[i]; + if (!return_stmt) + { + common_type = common_type ? type_find_max_type(common_type, type_anyfail) : type_anyfail; + continue; + } + only_has_rethrow = false; Expr *ret_expr = return_stmt->return_stmt.expr; Type *rtype = ret_expr ? ret_expr->type : type_void; @@ -1689,12 +1691,16 @@ static inline Type *unify_returns(SemaContext *context) all_returns_need_casts = true; } + // If we have no return and only rethrows, then the type is "void!" + if (common_type == type_anyfail && only_has_rethrow) common_type = type_get_failable(type_void); + // 7. Insert casts. if (all_returns_need_casts) { VECEACH(context->returns, i) { Ast *return_stmt = context->returns[i]; + if (!return_stmt) continue; Expr *ret_expr = return_stmt->return_stmt.expr; // 8. All casts should work. if (!cast_implicit(ret_expr, type_no_fail(common_type))) @@ -1826,34 +1832,22 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s Ast *body = ast_macro_copy(astptr(decl->macro_decl.body)); - bool no_scope = decl->no_scope; - bool escaping = decl->escaping; - DynamicScope old_scope = context->active_scope; - if (!no_scope) - { - context_change_scope_with_flags(context, SCOPE_NONE); - } + context_change_scope_with_flags(context, SCOPE_NONE); SemaContext macro_context; Type *rtype = NULL; - if (no_scope) - { + sema_context_init(¯o_context, decl->macro_decl.unit); + macro_context.compilation_unit = context->unit; + macro_context.current_function = context->current_function; + rtype = decl->macro_decl.rtype ? type_infoptr(decl->macro_decl.rtype)->type : NULL; + macro_context.expected_block_type = rtype; + + context_change_scope_with_flags(¯o_context, SCOPE_MACRO); + + macro_context.block_return_defer = macro_context.active_scope.defer_last; - macro_context = *context; - macro_context.unit = decl->macro_decl.unit; - } - else - { - sema_context_init(¯o_context, decl->macro_decl.unit); - macro_context.compilation_unit = context->unit; - macro_context.current_function = context->current_function; - rtype = decl->macro_decl.rtype ? type_infoptr(decl->macro_decl.rtype)->type : NULL; - macro_context.expected_block_type = rtype; - context_change_scope_with_flags(¯o_context, SCOPE_MACRO); - macro_context.block_return_defer = macro_context.active_scope.defer_last; - } macro_context.current_macro = decl; AstId body_id = call_expr->call_expr.body; macro_context.yield_body = body_id ? astptr(body_id) : NULL; @@ -1875,68 +1869,62 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s bool is_no_return = decl->macro_decl.attr_noreturn; - if (no_scope) + if (!vec_size(macro_context.returns)) { - call_expr->type = type_void; + if (rtype && type_no_fail(rtype) != type_void) + { + SEMA_ERROR(decl, + "Missing return in macro that should evaluate to %s.", + type_quoted_error_string(rtype)); + return SCOPE_POP_ERROR(); + } + } + else if (is_no_return) + { + SEMA_ERROR(context->returns[0], "Return used despite macro being marked '@noreturn'."); + return SCOPE_POP_ERROR(); + } + + if (rtype) + { + VECEACH(macro_context.returns, i) + { + Ast *return_stmt = macro_context.returns[i]; + Expr *ret_expr = return_stmt->return_stmt.expr; + if (!ret_expr) + { + if (rtype == type_void) continue; + SEMA_ERROR(return_stmt, "Expected returning a value of type %s.", type_quoted_error_string(rtype)); + return SCOPE_POP_ERROR(); + } + Type *type = ret_expr->type; + if (!cast_may_implicit(type, rtype, true, true)) + { + SEMA_ERROR(ret_expr, "Expected %s, not %s.", type_quoted_error_string(rtype), + type_quoted_error_string(type)); + return SCOPE_POP_ERROR(); + } + bool success = cast_implicit(ret_expr, rtype); + assert(success); + } + call_expr->type = type_get_opt_fail(rtype, failable); } else { - if (!vec_size(macro_context.returns)) + Type *sum_returns = unify_returns(¯o_context); + if (!sum_returns) return SCOPE_POP_ERROR(); + call_expr->type = type_get_opt_fail(sum_returns, failable); + } + if (vec_size(macro_context.returns) == 1) + { + Ast *ret = macro_context.returns[0]; + Expr *result = ret ? ret->return_stmt.expr : NULL; + if (result && expr_is_constant_eval(result, CONSTANT_EVAL_ANY)) { - if (rtype && type_no_fail(rtype) != type_void) + if (sema_check_stmt_compile_time(¯o_context, body)) { - SEMA_ERROR(decl, - "Missing return in macro that should evaluate to %s.", - type_quoted_error_string(rtype)); - return SCOPE_POP_ERROR(); - } - } - else if (is_no_return) - { - SEMA_ERROR(context->returns[0], "Return used despite macro being marked '@noreturn'."); - return SCOPE_POP_ERROR(); - } - - if (rtype) - { - VECEACH(macro_context.returns, i) - { - Ast *return_stmt = macro_context.returns[i]; - Expr *ret_expr = return_stmt->return_stmt.expr; - if (!ret_expr) - { - if (rtype == type_void) continue; - SEMA_ERROR(return_stmt, "Expected returning a value of type %s.", type_quoted_error_string(rtype)); - return SCOPE_POP_ERROR(); - } - Type *type = ret_expr->type; - if (!cast_may_implicit(type, rtype, true, true)) - { - SEMA_ERROR(ret_expr, "Expected %s, not %s.", type_quoted_error_string(rtype), - type_quoted_error_string(type)); - return SCOPE_POP_ERROR(); - } - bool success = cast_implicit(ret_expr, rtype); - assert(success); - } - call_expr->type = type_get_opt_fail(rtype, failable); - } - else - { - Type *sum_returns = unify_returns(¯o_context); - if (!sum_returns) return SCOPE_POP_ERROR(); - call_expr->type = type_get_opt_fail(sum_returns, failable); - } - if (vec_size(macro_context.returns) == 1) - { - Expr *result = macro_context.returns[0]->return_stmt.expr; - if (result && expr_is_constant_eval(result, CONSTANT_EVAL_ANY)) - { - if (sema_check_stmt_compile_time(¯o_context, body)) - { - expr_replace(call_expr, result); - goto EXIT; - } + expr_replace(call_expr, result); + goto EXIT; } } } @@ -1945,20 +1933,9 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s call_expr->macro_block.first_stmt = body->compound_stmt.first_stmt; call_expr->macro_block.params = params; call_expr->macro_block.args = args; - call_expr->macro_block.no_scope = no_scope; EXIT: - if (no_scope) - { - context->active_scope.jump_end = macro_context.active_scope.jump_end; - context->active_scope.defer_last = macro_context.active_scope.defer_last; - context->active_scope.defer_start = macro_context.active_scope.defer_start; - } - else - { - context_pop_defers_and_replace_expr(¯o_context, call_expr); - assert(context->active_scope.defer_last == context->active_scope.defer_start); - context->active_scope = old_scope; - } + assert(context->active_scope.defer_last == context->active_scope.defer_start); + context->active_scope = old_scope; if (is_no_return) context->active_scope.jump_end = true; return true; EXIT_FAIL: @@ -5848,8 +5825,63 @@ static inline bool sema_expr_analyse_bitassign(SemaContext *context, Expr *expr) { TODO } + +static inline bool sema_expr_analyse_or_error(SemaContext *context, Expr *expr) +{ + Expr *lhs = exprptr(expr->binary_expr.left); + Expr *rhs = exprptr(expr->binary_expr.right); + if (lhs->expr_kind == EXPR_TERNARY || rhs->expr_kind == EXPR_TERNARY) + { + SEMA_ERROR(expr, "Unclear precedence using ternary with ??, please use () to remove ambiguity."); + return false; + } + if (!sema_analyse_expr(context, lhs)) return false; + + if (expr->binary_expr.widen && !sema_widen_top_down(lhs, expr->type)) return false; + + Type *type = lhs->type; + if (!type_is_failable(type)) + { + SEMA_ERROR(lhs, "No failable to use '\?\?' with, please remove the '\?\?'."); + return false; + } + + // First we analyse the "else" and try to implictly cast. + if (!sema_analyse_expr(context, rhs)) return false; + if (expr->binary_expr.widen && !sema_widen_top_down(rhs, expr->type)) return false; + + + // Here we might need to insert casts. + Type *else_type = rhs->type; + + type = type_is_failable_any(type) ? else_type : type->failable; + + if (else_type->type_kind == TYPE_FAILABLE) + { + SEMA_ERROR(rhs, "The default value may not be a failable."); + return false; + } + Type *common = type_find_max_type(type, else_type); + if (!common) + { + SEMA_ERROR(rhs, "Cannot find a common type for %s and %s.", type_quoted_error_string(type), + type_quoted_error_string(else_type)); + return false; + } + if (!cast_implicit(lhs, common)) return false; + if (!cast_implicit(rhs, common)) return false; + if (IS_FAILABLE(rhs)) + { + SEMA_ERROR(rhs, "The expression must be a non-failable."); + return false; + } + expr->type = common; + return true; +} + static inline bool sema_expr_analyse_binary(SemaContext *context, Expr *expr) { + if (expr->binary_expr.operator == BINARYOP_OR_ERR) return sema_expr_analyse_or_error(context, expr); assert(expr->resolve_status == RESOLVE_RUNNING); Expr *left = exprptr(expr->binary_expr.left); Expr *right = exprptr(expr->binary_expr.right); @@ -5861,6 +5893,8 @@ static inline bool sema_expr_analyse_binary(SemaContext *context, Expr *expr) } switch (expr->binary_expr.operator) { + case BINARYOP_OR_ERR: + UNREACHABLE // Handled previously case BINARYOP_ASSIGN: return sema_expr_analyse_assign(context, expr, left, right); case BINARYOP_MULT: @@ -5973,69 +6007,21 @@ static inline bool sema_expr_analyse_catch(SemaContext *context, Expr *expr) return true; } -static inline bool sema_expr_analyse_or_error(SemaContext *context, Expr *expr) -{ - Expr *inner = expr->or_error_expr.expr; - if (inner->expr_kind == EXPR_TERNARY || expr->or_error_expr.or_error_expr->expr_kind == EXPR_TERNARY) - { - SEMA_ERROR(expr, "Unclear precedence using ternary with ??, please use () to remove ambiguity."); - return false; - } - if (!sema_analyse_expr(context, inner)) return false; - - if (expr->or_error_expr.widen && !sema_widen_top_down(inner, expr->type)) return false; - - Type *type = inner->type; - if (type->type_kind != TYPE_FAILABLE) - { - SEMA_ERROR(inner, "No failable to use '\?\?' with, please remove the '\?\?'."); - return false; - } - type = type->failable; - if (expr->or_error_expr.is_jump) - { - if (!sema_analyse_statement(context, expr->or_error_expr.or_error_stmt)) return false; - expr->type = inner->type; - return true; - } - - // First we analyse the "else" and try to implictly cast. - Expr *else_expr = expr->or_error_expr.or_error_expr; - if (!sema_analyse_expr(context, else_expr)) return false; - if (expr->or_error_expr.widen && !sema_widen_top_down(else_expr, expr->type)) return false; - - // Here we might need to insert casts. - Type *else_type = else_expr->type; - if (else_type->type_kind == TYPE_FAILABLE) - { - SEMA_ERROR(else_expr, "The default value may not be a failable."); - return false; - } - Type *common = type_find_max_type(type, else_type); - if (!common) - { - SEMA_ERROR(else_expr, "Cannot find a common type for %s and %s.", type_quoted_error_string(type), - type_quoted_error_string(else_type)); - return false; - } - if (!cast_implicit(inner, common)) return false; - if (!cast_implicit(else_expr, common)) return false; - if (IS_FAILABLE(else_expr)) - { - SEMA_ERROR(else_expr, "The expression must be a non-failable."); - return false; - } - expr->type = common; - - return true; -} - static inline bool sema_expr_analyse_rethrow(SemaContext *context, Expr *expr) { + if (!context->current_function) + { + SEMA_ERROR(expr, "Rethrow cannot be used outside of a function."); + return false; + } Expr *inner = expr->rethrow_expr.inner; if (!sema_analyse_expr(context, inner)) return false; - REMINDER("Return defers must be work with blocks"); + if (context->active_scope.in_defer) + { + SEMA_ERROR(expr, "Returns are not allowed inside of defers."); + return false; + } expr->rethrow_expr.cleanup = context_get_defers(context, context->active_scope.defer_last, 0); if (inner->type == type_anyfail) { @@ -6050,12 +6036,17 @@ static inline bool sema_expr_analyse_rethrow(SemaContext *context, Expr *expr) return false; } - if (context->rtype->type_kind != TYPE_FAILABLE) + if (context->rtype && context->rtype->type_kind != TYPE_FAILABLE) { SEMA_ERROR(expr, "This expression implicitly returns with a failable result, but the function does not allow failable results. Did you mean to use 'else' instead?"); return false; } + if (context->active_scope.flags & (SCOPE_EXPR_BLOCK | SCOPE_MACRO)) + { + vec_add(context->returns, NULL); + } + return true; } @@ -6098,7 +6089,6 @@ static inline bool sema_expr_analyse_expr_block(SemaContext *context, Type *infe { bool success = true; expr->type = type_void; - bool saved_expr_failable_return = context->expr_failable_return; Ast **saved_returns = context_push_returns(context); Type *stored_block_type = context->expected_block_type; context->expected_block_type = infer_type; @@ -6144,7 +6134,6 @@ static inline bool sema_expr_analyse_expr_block(SemaContext *context, Type *infe SCOPE_END; context->expected_block_type = stored_block_type; context_pop_returns(context, saved_returns); - context->expr_failable_return = saved_expr_failable_return; return success; } @@ -6897,7 +6886,6 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) // Created during semantic analysis UNREACHABLE case EXPR_MACRO_BLOCK: - case EXPR_SCOPED_EXPR: UNREACHABLE case EXPR_TYPEINFO: expr->type = type_typeinfo; @@ -6910,8 +6898,6 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) return sema_expr_analyse_try(context, expr); case EXPR_CATCH: return sema_expr_analyse_catch(context, expr); - case EXPR_OR_ERROR: - return sema_expr_analyse_or_error(context, expr); case EXPR_COMPOUND_LITERAL: return sema_expr_analyse_compound_literal(context, expr); case EXPR_EXPR_BLOCK: @@ -7213,6 +7199,7 @@ void insert_widening_type(Expr *expr, Type *infer_type) case BINARYOP_BIT_OR: case BINARYOP_BIT_XOR: case BINARYOP_BIT_AND: + case BINARYOP_OR_ERR: if (!expr_is_simple(exprptr(expr->binary_expr.left)) || !expr_is_simple(exprptr(expr->binary_expr.right))) return; expr->type = infer_type; expr->binary_expr.widen = true; @@ -7220,12 +7207,6 @@ void insert_widening_type(Expr *expr, Type *infer_type) default: return; } - case EXPR_OR_ERROR: - if (!expr_is_simple(expr->or_error_expr.expr)) return; - if (!expr->or_error_expr.is_jump && !expr_is_simple(expr->or_error_expr.or_error_expr)) return; - expr->type = infer_type; - expr->or_error_expr.widen = true; - return; case EXPR_GROUP: insert_widening_type(expr->inner_expr, infer_type); return; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index d1b19f6a4..7c044d6c8 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -36,8 +36,7 @@ do { \ AstId context_get_defers(SemaContext *context, AstId defer_top, AstId defer_bottom); void context_pop_defers(SemaContext *context, AstId *next); -Expr *context_pop_defers_and_wrap_expr(SemaContext *context, Expr *expr); -void context_pop_defers_and_replace_expr(SemaContext *context, Expr *expr); + void context_pop_defers_and_replace_ast(SemaContext *context, Ast *ast); void context_change_scope_for_label(SemaContext *context, Decl *label); void context_change_scope_with_flags(SemaContext *context, ScopeFlags flags); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 0fa852f87..9467fe6fb 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -145,6 +145,12 @@ static inline bool sema_analyse_block_exit_stmt(SemaContext *context, Ast *state */ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement) { + if (context->active_scope.in_defer) + { + SEMA_ERROR(statement, "Return is not allowed inside of a defer."); + return false; + } + // This might be a return in a function block or a macro which must be treated differently. if (context->active_scope.flags & (SCOPE_EXPR_BLOCK | SCOPE_MACRO)) { @@ -710,6 +716,11 @@ static inline bool sema_analyse_expr_stmt(SemaContext *context, Ast *statement) bool sema_analyse_defer_stmt_body(SemaContext *context, Ast *statement, Ast *body) { + if (body->ast_kind == AST_DEFER_STMT) + { + SEMA_ERROR(body, "A defer may not have a body consisting of a raw 'defer', this looks like a mistake."); + return false; + } // TODO special parsing of "catch" bool success; SCOPE_START @@ -729,7 +740,7 @@ bool sema_analyse_defer_stmt_body(SemaContext *context, Ast *statement, Ast *bod POP_BREAKCONT(); POP_NEXT(); - context_pop_defers_and_replace_ast(context, body); + // We should never need to replace any defers here. SCOPE_END; @@ -765,8 +776,6 @@ static inline bool sema_analyse_for_cond(SemaContext *context, ExprId *cond_ref, if (!sema_analyse_cond_expr(context, cond)) return false; } - cond = context_pop_defers_and_wrap_expr(context, cond); - // If this is const true, then set this to infinite and remove the expression. Expr *cond_last = cond->expr_kind == EXPR_COND ? VECLAST(cond->cond_expr) : cond; assert(cond_last); @@ -790,6 +799,18 @@ static inline bool sema_analyse_for_stmt(SemaContext *context, Ast *statement) bool success = true; bool is_infinite = false; + Ast *body = astptr(statement->for_stmt.body); + assert(body); + if (body->ast_kind == AST_DEFER_STMT) + { + SEMA_ERROR(body, "Looping over a raw 'defer' is not allowed, was this a mistake?"); + return false; + } + bool do_loop = statement->for_stmt.flow.skip_first; + if (body->ast_kind != AST_COMPOUND_STMT && do_loop) + { + SEMA_ERROR(body, "A do loop must use { } around its body."); + } // Enter for scope SCOPE_OUTER_START @@ -801,7 +822,7 @@ static inline bool sema_analyse_for_stmt(SemaContext *context, Ast *statement) // Conditional scope start SCOPE_START_WITH_LABEL(statement->for_stmt.flow.label) - if (!statement->for_stmt.flow.skip_first) + if (!do_loop) { if (!sema_analyse_for_cond(context, &statement->for_stmt.cond, &is_infinite) || !success) { @@ -810,8 +831,6 @@ static inline bool sema_analyse_for_stmt(SemaContext *context, Ast *statement) } } - assert(statement->for_stmt.body); - Ast *body = astptr(statement->for_stmt.body); PUSH_BREAKCONT(statement); success = sema_analyse_statement(context, body); @@ -838,9 +857,7 @@ static inline bool sema_analyse_for_stmt(SemaContext *context, Ast *statement) { // Incr scope start SCOPE_START - Expr *incr = exprptr(statement->for_stmt.incr); - success = sema_analyse_expr(context, incr); - statement->for_stmt.incr = exprid(context_pop_defers_and_wrap_expr(context, incr)); + success = sema_analyse_expr(context, exprptr(statement->for_stmt.incr)); // Incr scope end SCOPE_END; } @@ -951,9 +968,6 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen return SCOPE_POP_ERROR(); } - // Pop any possible defers. - enumerator = context_pop_defers_and_wrap_expr(context, enumerator); - // And pop the cond scope. SCOPE_END; @@ -1211,6 +1225,11 @@ static inline bool sema_analyse_if_stmt(SemaContext *context, Ast *statement) Expr *cond = exprptr(statement->if_stmt.cond); Ast *then = astptr(statement->if_stmt.then_body); + if (then->ast_kind == AST_DEFER_STMT) + { + SEMA_ERROR(then, "An 'if' statement may not be followed by a raw 'defer' statement, this looks like a mistake."); + return false; + } AstId else_id = statement->if_stmt.else_body; Ast *else_body = else_id ? astptr(else_id) : NULL; SCOPE_OUTER_START diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index a253f6408..bd495efc8 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -42,6 +42,11 @@ void context_change_scope_with_flags(SemaContext *context, ScopeFlags flags) assert(parent_defer < 1000000); // Defer and expression blocks introduce their own return/break/continue // otherwise just merge with the old flags. + if (flags & (SCOPE_EXPR_BLOCK | SCOPE_MACRO)) + { + previous_defer = 0; + parent_defer = 0; + } if (!(flags & SCOPE_EXPR_BLOCK)) { flags = context->active_scope.flags | flags; @@ -110,35 +115,6 @@ void context_pop_defers(SemaContext *context, AstId *next) } -Expr *context_pop_defers_and_wrap_expr(SemaContext *context, Expr *expr) -{ - AstId defer_first = 0; - context_pop_defers(context, &defer_first); - if (defer_first) - { - Expr *wrap = expr_new(EXPR_SCOPED_EXPR, expr->span); - wrap->type = expr->type; - wrap->resolve_status = RESOLVE_DONE; - wrap->expr_scope.expr = expr; - wrap->expr_scope.defer_stmts = defer_first; - return wrap; - } - return expr; -} - -void context_pop_defers_and_replace_expr(SemaContext *context, Expr *expr) -{ - AstId defer_first = 0; - context_pop_defers(context, &defer_first); - if (defer_first) - { - Expr *inner = expr_copy(expr); - expr->expr_kind = EXPR_SCOPED_EXPR; - expr->expr_scope.expr = inner; - expr->expr_scope.defer_stmts = defer_first; - } -} - void context_pop_defers_and_replace_ast(SemaContext *context, Ast *ast) { AstId defer_first = 0; diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 5d67fb29b..159be6b56 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -207,8 +207,6 @@ void symtab_init(uint32_t capacity) attribute_list[ATTRIBUTE_UNUSED] = KW_DEF("unused"); attribute_list[ATTRIBUTE_USED] = KW_DEF("used"); attribute_list[ATTRIBUTE_NAKED] = KW_DEF("naked"); - attribute_list[ATTRIBUTE_NOSCOPE] = KW_DEF("noscope"); - attribute_list[ATTRIBUTE_ESCAPING] = KW_DEF("escaping"); attribute_list[ATTRIBUTE_CDECL] = KW_DEF("cdecl"); attribute_list[ATTRIBUTE_STDCALL] = KW_DEF("stdcall"); attribute_list[ATTRIBUTE_VECCALL] = KW_DEF("veccall"); diff --git a/src/version.h b/src/version.h index f53adbdb1..0ed34da97 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "PRE.28" \ No newline at end of file +#define COMPILER_VERSION "PRE.29" \ No newline at end of file diff --git a/test/test_suite/defer/defer_and_expr_block.c3t b/test/test_suite/defer/defer_and_expr_block.c3t new file mode 100644 index 000000000..5c87446a8 --- /dev/null +++ b/test/test_suite/defer/defer_and_expr_block.c3t @@ -0,0 +1,40 @@ +// #target: x64-darwin +module foo; +extern fn void printf(char*,...); + +fn void main() +{ + defer printf("On exit\n"); + {| + defer printf("Baz\n"); + defer {| + defer printf("Hello!\n"); + defer printf("1\n"); + if (true) return 12; + defer printf("2\n"); + return 34; + |}; + |}; + defer printf("On 2\n"); +} + +/* #expect: foo.ll + +define void @foo.main() #0 { +entry: + %blockret = alloca i32, align 4 + br label %if.then + +if.then: ; preds = %entry + call void (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0)) + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.1, i32 0, i32 0)) + store i32 12, i32* %blockret, align 4 + br label %expr_block.exit + +expr_block.exit: ; preds = %if.then + call void (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.5, i32 0, i32 0)) + call void (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.6, i32 0, i32 0)) + call void (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.7, i32 0, i32 0)) + ret void +} + diff --git a/test/test_suite/defer/defer_single_stmt.c3 b/test/test_suite/defer/defer_single_stmt.c3 new file mode 100644 index 000000000..d6bb0cffc --- /dev/null +++ b/test/test_suite/defer/defer_single_stmt.c3 @@ -0,0 +1,41 @@ +fn void test1() +{ + while (1) defer foo(); // #error: Looping over a raw 'defer' +} + +fn void test1a() +{ + while (1) { defer foo(); } +} + +fn void test2() +{ + if (1) defer foo(); // #error: An 'if' statement may not be followed by a raw 'defer' +} + +fn void test2a() +{ + if (1) { defer foo(); } +} + +fn void test3() +{ + defer defer foo(); // #error: A defer may not have a body consisting of a raw 'defer' +} + +fn void test3a() +{ + defer { defer foo(); } +} + +fn void test4() +{ + for (;;) defer foo(); // #error: Looping over a raw 'defer' +} + +fn void test4a() +{ + for(;;) { defer foo(); } +} + +fn void foo() {} diff --git a/test/test_suite/defer/defer_with_rethrow.c3 b/test/test_suite/defer/defer_with_rethrow.c3 new file mode 100644 index 000000000..265f2fa0e --- /dev/null +++ b/test/test_suite/defer/defer_with_rethrow.c3 @@ -0,0 +1,14 @@ +fn int! foo() +{ return 1; } + + +fn int! bar() +{ + defer { + {| + foo()?; + |}; + } + defer foo()?; // #error: Returns are not allowed inside of defers. + return 1; +} \ No newline at end of file diff --git a/test/test_suite/defer/defer_with_return.c3 b/test/test_suite/defer/defer_with_return.c3 new file mode 100644 index 000000000..eeabe94d6 --- /dev/null +++ b/test/test_suite/defer/defer_with_return.c3 @@ -0,0 +1,10 @@ +fn int! bar() +{ + defer { + {| + return 4; + |}; + } + defer return 3; // #error: Return is not allowed inside of a defer + return 1; +} \ No newline at end of file diff --git a/test/test_suite/errors/anyerr_void.c3t b/test/test_suite/errors/anyerr_void.c3t index 979b25ea7..1534ad044 100644 --- a/test/test_suite/errors/anyerr_void.c3t +++ b/test/test_suite/errors/anyerr_void.c3t @@ -29,16 +29,7 @@ fn void main() ; Function Attrs: nounwind define i64 @anyerr_void.errorThing() #0 { entry: - %reterr = alloca i64, align 8 - store i64 ptrtoint (i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"anyerr_void.MyError$elements", i64 0, i64 1) to i64), i64* %reterr, align 8 - br label %err_retblock - -postfailed: ; No predecessors! - ret i64 0 - -err_retblock: ; preds = %entry - %0 = load i64, i64* %reterr, align 8 - ret i64 %0 + ret i64 ptrtoint (i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"anyerr_void.MyError$elements", i64 0, i64 1) to i64) } ; Function Attrs: nounwind diff --git a/test/test_suite/errors/error_regression_2.c3t b/test/test_suite/errors/error_regression_2.c3t index aa5d8bb84..35bb4318c 100644 --- a/test/test_suite/errors/error_regression_2.c3t +++ b/test/test_suite/errors/error_regression_2.c3t @@ -266,39 +266,37 @@ define i64 @test.readDoc(%Doc* %0, i8* %1, i64 %2) #0 { entry: %url = alloca %"char[]", align 8 %taddr = alloca %"char[]", align 8 - %reterr = alloca i64, align 8 %taddr5 = alloca %"char[]", align 8 - %reterr9 = alloca i64, align 8 + %reterr = alloca i64, align 8 %literal = alloca %Doc, align 8 - %taddr13 = alloca %"char[]", align 8 - %reterr17 = alloca i64, align 8 - %literal18 = alloca %Doc, align 8 + %taddr12 = alloca %"char[]", align 8 + %reterr16 = alloca i64, align 8 + %literal17 = alloca %Doc, align 8 %error_var = alloca i64, align 8 %value = alloca %Head, align 8 - %literal19 = alloca %Head, align 8 + %literal18 = alloca %Head, align 8 %temp = alloca %Head*, align 8 - %taddr26 = alloca %"char[]", align 8 - %reterr30 = alloca i64, align 8 - %literal31 = alloca %Doc, align 8 - %error_var32 = alloca i64, align 8 - %value33 = alloca %Head, align 8 - %literal34 = alloca %Head, align 8 - %error_var35 = alloca i64, align 8 - %value36 = alloca %"char[]", align 8 - %temp37 = alloca %"char[]"*, align 8 - %temp45 = alloca %Head*, align 8 + %taddr24 = alloca %"char[]", align 8 + %reterr28 = alloca i64, align 8 + %literal29 = alloca %Doc, align 8 + %error_var30 = alloca i64, align 8 + %value31 = alloca %Head, align 8 + %literal32 = alloca %Head, align 8 + %error_var33 = alloca i64, align 8 + %value34 = alloca %"char[]", align 8 + %temp35 = alloca %"char[]"*, align 8 + %temp42 = alloca %Head*, align 8 %len = alloca i32, align 4 %str = alloca i8*, align 8 %reterr56 = alloca i64, align 8 - %reterr63 = alloca i64, align 8 - %literal64 = alloca %Doc, align 8 - %error_var65 = alloca i64, align 8 - %value66 = alloca %Head, align 8 - %literal67 = alloca %Head, align 8 - %error_var68 = alloca i64, align 8 - %value69 = alloca %"char[]", align 8 - %temp70 = alloca %"char[]"*, align 8 - %temp78 = alloca %Head*, align 8 + %literal57 = alloca %Doc, align 8 + %error_var58 = alloca i64, align 8 + %value59 = alloca %Head, align 8 + %literal60 = alloca %Head, align 8 + %error_var61 = alloca i64, align 8 + %value62 = alloca %"char[]", align 8 + %temp63 = alloca %"char[]"*, align 8 + %temp70 = alloca %Head*, align 8 %pair = bitcast %"char[]"* %url to { i8*, i64 }* %3 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 0 store i8* %1, i8** %3, align 8 @@ -320,281 +318,263 @@ entry: br i1 %12, label %if.then, label %if.exit if.then: ; preds = %entry - store i64 ptrtoint ([2 x i8*]* @"test.ReadError$elements" to i64), i64* %reterr, align 8 - br label %err_retblock - -postfailed: ; No predecessors! - store %Doc undef, %Doc* %0, align 8 - ret i64 0 - -err_retblock: ; preds = %if.then - %13 = load i64, i64* %reterr, align 8 - ret i64 %13 + ret i64 ptrtoint ([2 x i8*]* @"test.ReadError$elements" to i64) if.exit: ; preds = %entry - %14 = bitcast %"char[]"* %url to { i8*, i64 }* - %15 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %14, i32 0, i32 0 - %lo3 = load i8*, i8** %15, align 8 - %16 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %14, i32 0, i32 1 - %hi4 = load i64, i64* %16, align 8 + %13 = bitcast %"char[]"* %url to { i8*, i64 }* + %14 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %13, i32 0, i32 0 + %lo3 = load i8*, i8** %14, align 8 + %15 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %13, i32 0, i32 1 + %hi4 = load i64, i64* %15, align 8 store %"char[]" { i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.1, i32 0, i32 0), i64 12 }, %"char[]"* %taddr5, align 8 - %17 = bitcast %"char[]"* %taddr5 to { i8*, i64 }* - %18 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %17, i32 0, i32 0 - %lo6 = load i8*, i8** %18, align 8 - %19 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %17, i32 0, i32 1 - %hi7 = load i64, i64* %19, align 8 - %20 = call i8 @test.contains(i8* %lo3, i64 %hi4, i8* %lo6, i64 %hi7) - %21 = trunc i8 %20 to i1 - br i1 %21, label %if.then8, label %if.exit10 + %16 = bitcast %"char[]"* %taddr5 to { i8*, i64 }* + %17 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %16, i32 0, i32 0 + %lo6 = load i8*, i8** %17, align 8 + %18 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %16, i32 0, i32 1 + %hi7 = load i64, i64* %18, align 8 + %19 = call i8 @test.contains(i8* %lo3, i64 %hi4, i8* %lo6, i64 %hi7) + %20 = trunc i8 %19 to i1 + br i1 %20, label %if.then8, label %if.exit9 if.then8: ; preds = %if.exit - %22 = getelementptr inbounds %Doc, %Doc* %literal, i32 0, i32 0 - store %Head* null, %Head** %22, align 8 - %23 = bitcast %Doc* %0 to i8* - %24 = bitcast %Doc* %literal to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %23, i8* align 8 %24, i32 8, i1 false) + %21 = getelementptr inbounds %Doc, %Doc* %literal, i32 0, i32 0 + store %Head* null, %Head** %21, align 8 + %22 = bitcast %Doc* %0 to i8* + %23 = bitcast %Doc* %literal to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %22, i8* align 8 %23, i32 8, i1 false) ret i64 0 -if.exit10: ; preds = %if.exit - %25 = bitcast %"char[]"* %url to { i8*, i64 }* - %26 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %25, i32 0, i32 0 - %lo11 = load i8*, i8** %26, align 8 - %27 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %25, i32 0, i32 1 - %hi12 = load i64, i64* %27, align 8 - store %"char[]" { i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.2, i32 0, i32 0), i64 13 }, %"char[]"* %taddr13, align 8 - %28 = bitcast %"char[]"* %taddr13 to { i8*, i64 }* - %29 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %28, i32 0, i32 0 - %lo14 = load i8*, i8** %29, align 8 - %30 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %28, i32 0, i32 1 - %hi15 = load i64, i64* %30, align 8 - %31 = call i8 @test.contains(i8* %lo11, i64 %hi12, i8* %lo14, i64 %hi15) - %32 = trunc i8 %31 to i1 - br i1 %32, label %if.then16, label %if.exit23 +if.exit9: ; preds = %if.exit + %24 = bitcast %"char[]"* %url to { i8*, i64 }* + %25 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %24, i32 0, i32 0 + %lo10 = load i8*, i8** %25, align 8 + %26 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %24, i32 0, i32 1 + %hi11 = load i64, i64* %26, align 8 + store %"char[]" { i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.2, i32 0, i32 0), i64 13 }, %"char[]"* %taddr12, align 8 + %27 = bitcast %"char[]"* %taddr12 to { i8*, i64 }* + %28 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %27, i32 0, i32 0 + %lo13 = load i8*, i8** %28, align 8 + %29 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %27, i32 0, i32 1 + %hi14 = load i64, i64* %29, align 8 + %30 = call i8 @test.contains(i8* %lo10, i64 %hi11, i8* %lo13, i64 %hi14) + %31 = trunc i8 %30 to i1 + br i1 %31, label %if.then15, label %if.exit21 -if.then16: ; preds = %if.exit10 - %33 = getelementptr inbounds %Doc, %Doc* %literal18, i32 0, i32 0 - %34 = getelementptr inbounds %Head, %Head* %literal19, i32 0, i32 0 - store %"char[]"* null, %"char[]"** %34, align 8 - %35 = load %Head, %Head* %literal19, align 8 - store %Head %35, %Head* %value, align 8 - %36 = call i8* @"std::mem.alloc"(i64 8, i64 1) #2 - %ptrptr = bitcast i8* %36 to %Head* +if.then15: ; preds = %if.exit9 + %32 = getelementptr inbounds %Doc, %Doc* %literal17, i32 0, i32 0 + %33 = getelementptr inbounds %Head, %Head* %literal18, i32 0, i32 0 + store %"char[]"* null, %"char[]"** %33, align 8 + %34 = load %Head, %Head* %literal18, align 8 + store %Head %34, %Head* %value, align 8 + %35 = call i8* @"std::mem.alloc"(i64 8, i64 1) #2 + %ptrptr = bitcast i8* %35 to %Head* store %Head* %ptrptr, %Head** %temp, align 8 - %37 = load %Head*, %Head** %temp, align 8 - %not = icmp eq %Head* %37, null - br i1 %not, label %if.then20, label %if.exit22 + %36 = load %Head*, %Head** %temp, align 8 + %not = icmp eq %Head* %36, null + br i1 %not, label %if.then19, label %if.exit20 -if.then20: ; preds = %if.then16 +if.then19: ; preds = %if.then15 store i64 ptrtoint (i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"test.ReadError$elements", i64 0, i64 1) to i64), i64* %error_var, align 8 br label %guard_block -if.exit22: ; preds = %if.then16 - %38 = load %Head*, %Head** %temp, align 8 - %39 = bitcast %Head* %38 to i8* - %40 = bitcast %Head* %value to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %39, i8* align 8 %40, i32 8, i1 false) +if.exit20: ; preds = %if.then15 + %37 = load %Head*, %Head** %temp, align 8 + %38 = bitcast %Head* %37 to i8* + %39 = bitcast %Head* %value to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %38, i8* align 8 %39, i32 8, i1 false) br label %noerr_block -guard_block: ; preds = %if.then20 - %41 = load i64, i64* %error_var, align 8 - ret i64 %41 +guard_block: ; preds = %if.then19 + %40 = load i64, i64* %error_var, align 8 + ret i64 %40 -noerr_block: ; preds = %if.exit22 - %42 = load %Head*, %Head** %temp, align 8 - store %Head* %42, %Head** %33, align 8 - %43 = bitcast %Doc* %0 to i8* - %44 = bitcast %Doc* %literal18 to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %43, i8* align 8 %44, i32 8, i1 false) +noerr_block: ; preds = %if.exit20 + %41 = load %Head*, %Head** %temp, align 8 + store %Head* %41, %Head** %32, align 8 + %42 = bitcast %Doc* %0 to i8* + %43 = bitcast %Doc* %literal17 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %42, i8* align 8 %43, i32 8, i1 false) ret i64 0 -if.exit23: ; preds = %if.exit10 - %45 = bitcast %"char[]"* %url to { i8*, i64 }* - %46 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %45, i32 0, i32 0 - %lo24 = load i8*, i8** %46, align 8 - %47 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %45, i32 0, i32 1 - %hi25 = load i64, i64* %47, align 8 - store %"char[]" { i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.3, i32 0, i32 0), i64 11 }, %"char[]"* %taddr26, align 8 - %48 = bitcast %"char[]"* %taddr26 to { i8*, i64 }* - %49 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %48, i32 0, i32 0 - %lo27 = load i8*, i8** %49, align 8 - %50 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %48, i32 0, i32 1 - %hi28 = load i64, i64* %50, align 8 - %51 = call i8 @test.contains(i8* %lo24, i64 %hi25, i8* %lo27, i64 %hi28) - %52 = trunc i8 %51 to i1 - br i1 %52, label %if.then29, label %if.exit53 +if.exit21: ; preds = %if.exit9 + %44 = bitcast %"char[]"* %url to { i8*, i64 }* + %45 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %44, i32 0, i32 0 + %lo22 = load i8*, i8** %45, align 8 + %46 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %44, i32 0, i32 1 + %hi23 = load i64, i64* %46, align 8 + store %"char[]" { i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.3, i32 0, i32 0), i64 11 }, %"char[]"* %taddr24, align 8 + %47 = bitcast %"char[]"* %taddr24 to { i8*, i64 }* + %48 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %47, i32 0, i32 0 + %lo25 = load i8*, i8** %48, align 8 + %49 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %47, i32 0, i32 1 + %hi26 = load i64, i64* %49, align 8 + %50 = call i8 @test.contains(i8* %lo22, i64 %hi23, i8* %lo25, i64 %hi26) + %51 = trunc i8 %50 to i1 + br i1 %51, label %if.then27, label %if.exit49 -if.then29: ; preds = %if.exit23 - %53 = getelementptr inbounds %Doc, %Doc* %literal31, i32 0, i32 0 - %54 = bitcast %Head* %literal34 to %"char[]"** - store %"char[]"* null, %"char[]"** %54, align 8 - %55 = getelementptr inbounds %Head, %Head* %literal34, i32 0, i32 0 - store %"char[]" { i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str.4, i32 0, i32 0), i64 0 }, %"char[]"* %value36, align 8 - %56 = call i8* @"std::mem.alloc"(i64 16, i64 1) #2 - %ptrptr38 = bitcast i8* %56 to %"char[]"* - store %"char[]"* %ptrptr38, %"char[]"** %temp37, align 8 - %57 = load %"char[]"*, %"char[]"** %temp37, align 8 - %not39 = icmp eq %"char[]"* %57, null - br i1 %not39, label %if.then40, label %if.exit42 +if.then27: ; preds = %if.exit21 + %52 = getelementptr inbounds %Doc, %Doc* %literal29, i32 0, i32 0 + %53 = bitcast %Head* %literal32 to %"char[]"** + store %"char[]"* null, %"char[]"** %53, align 8 + %54 = getelementptr inbounds %Head, %Head* %literal32, i32 0, i32 0 + store %"char[]" { i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str.4, i32 0, i32 0), i64 0 }, %"char[]"* %value34, align 8 + %55 = call i8* @"std::mem.alloc"(i64 16, i64 1) #2 + %ptrptr36 = bitcast i8* %55 to %"char[]"* + store %"char[]"* %ptrptr36, %"char[]"** %temp35, align 8 + %56 = load %"char[]"*, %"char[]"** %temp35, align 8 + %not37 = icmp eq %"char[]"* %56, null + br i1 %not37, label %if.then38, label %if.exit39 -if.then40: ; preds = %if.then29 - store i64 ptrtoint (i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"test.ReadError$elements", i64 0, i64 1) to i64), i64* %error_var35, align 8 - br label %guard_block43 +if.then38: ; preds = %if.then27 + store i64 ptrtoint (i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"test.ReadError$elements", i64 0, i64 1) to i64), i64* %error_var33, align 8 + br label %guard_block40 -if.exit42: ; preds = %if.then29 - %58 = load %"char[]"*, %"char[]"** %temp37, align 8 - %59 = bitcast %"char[]"* %58 to i8* - %60 = bitcast %"char[]"* %value36 to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %59, i8* align 8 %60, i32 16, i1 false) - br label %noerr_block44 +if.exit39: ; preds = %if.then27 + %57 = load %"char[]"*, %"char[]"** %temp35, align 8 + %58 = bitcast %"char[]"* %57 to i8* + %59 = bitcast %"char[]"* %value34 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %58, i8* align 8 %59, i32 16, i1 false) + br label %noerr_block41 -guard_block43: ; preds = %if.then40 - %61 = load i64, i64* %error_var35, align 8 - ret i64 %61 +guard_block40: ; preds = %if.then38 + %60 = load i64, i64* %error_var33, align 8 + ret i64 %60 -noerr_block44: ; preds = %if.exit42 - %62 = load %"char[]"*, %"char[]"** %temp37, align 8 - store %"char[]"* %62, %"char[]"** %55, align 8 - %63 = load %Head, %Head* %literal34, align 8 - store %Head %63, %Head* %value33, align 8 - %64 = call i8* @"std::mem.alloc"(i64 8, i64 1) #2 - %ptrptr46 = bitcast i8* %64 to %Head* - store %Head* %ptrptr46, %Head** %temp45, align 8 - %65 = load %Head*, %Head** %temp45, align 8 - %not47 = icmp eq %Head* %65, null - br i1 %not47, label %if.then48, label %if.exit50 +noerr_block41: ; preds = %if.exit39 + %61 = load %"char[]"*, %"char[]"** %temp35, align 8 + store %"char[]"* %61, %"char[]"** %54, align 8 + %62 = load %Head, %Head* %literal32, align 8 + store %Head %62, %Head* %value31, align 8 + %63 = call i8* @"std::mem.alloc"(i64 8, i64 1) #2 + %ptrptr43 = bitcast i8* %63 to %Head* + store %Head* %ptrptr43, %Head** %temp42, align 8 + %64 = load %Head*, %Head** %temp42, align 8 + %not44 = icmp eq %Head* %64, null + br i1 %not44, label %if.then45, label %if.exit46 -if.then48: ; preds = %noerr_block44 - store i64 ptrtoint (i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"test.ReadError$elements", i64 0, i64 1) to i64), i64* %error_var32, align 8 - br label %guard_block51 +if.then45: ; preds = %noerr_block41 + store i64 ptrtoint (i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"test.ReadError$elements", i64 0, i64 1) to i64), i64* %error_var30, align 8 + br label %guard_block47 -if.exit50: ; preds = %noerr_block44 - %66 = load %Head*, %Head** %temp45, align 8 - %67 = bitcast %Head* %66 to i8* - %68 = bitcast %Head* %value33 to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %67, i8* align 8 %68, i32 8, i1 false) - br label %noerr_block52 +if.exit46: ; preds = %noerr_block41 + %65 = load %Head*, %Head** %temp42, align 8 + %66 = bitcast %Head* %65 to i8* + %67 = bitcast %Head* %value31 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %66, i8* align 8 %67, i32 8, i1 false) + br label %noerr_block48 -guard_block51: ; preds = %if.then48 - %69 = load i64, i64* %error_var32, align 8 - ret i64 %69 +guard_block47: ; preds = %if.then45 + %68 = load i64, i64* %error_var30, align 8 + ret i64 %68 -noerr_block52: ; preds = %if.exit50 - %70 = load %Head*, %Head** %temp45, align 8 - store %Head* %70, %Head** %53, align 8 - %71 = bitcast %Doc* %0 to i8* - %72 = bitcast %Doc* %literal31 to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %71, i8* align 8 %72, i32 8, i1 false) +noerr_block48: ; preds = %if.exit46 + %69 = load %Head*, %Head** %temp42, align 8 + store %Head* %69, %Head** %52, align 8 + %70 = bitcast %Doc* %0 to i8* + %71 = bitcast %Doc* %literal29 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %70, i8* align 8 %71, i32 8, i1 false) ret i64 0 -if.exit53: ; preds = %if.exit23 - %73 = getelementptr inbounds %"char[]", %"char[]"* %url, i32 0, i32 1 - %74 = load i64, i64* %73, align 8 - %uisitrunc = trunc i64 %74 to i32 - %75 = getelementptr inbounds %"char[]", %"char[]"* %url, i32 0, i32 0 - %76 = load i8*, i8** %75, align 8 - %77 = call i32 (i8*, i64, i8*, ...) @snprintf(i8* null, i64 0, i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.5, i32 0, i32 0), i32 %uisitrunc, i8* %76) - store i32 %77, i32* %len, align 4 - %78 = load i32, i32* %len, align 4 - %siuiext = sext i32 %78 to i64 +if.exit49: ; preds = %if.exit21 + %72 = getelementptr inbounds %"char[]", %"char[]"* %url, i32 0, i32 1 + %73 = load i64, i64* %72, align 8 + %uisitrunc = trunc i64 %73 to i32 + %74 = getelementptr inbounds %"char[]", %"char[]"* %url, i32 0, i32 0 + %75 = load i8*, i8** %74, align 8 + %76 = call i32 (i8*, i64, i8*, ...) @snprintf(i8* null, i64 0, i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.5, i32 0, i32 0), i32 %uisitrunc, i8* %75) + store i32 %76, i32* %len, align 4 + %77 = load i32, i32* %len, align 4 + %siuiext = sext i32 %77 to i64 %add = add i64 %siuiext, 1 - %79 = call i8* @"std::mem.alloc"(i64 %add, i64 1) #2 - store i8* %79, i8** %str, align 8 + %78 = call i8* @"std::mem.alloc"(i64 %add, i64 1) #2 + store i8* %78, i8** %str, align 8 + %79 = load i8*, i8** %str, align 8 + %not50 = icmp eq i8* %79, null + br i1 %not50, label %if.then51, label %if.exit52 + +if.then51: ; preds = %if.exit49 + ret i64 ptrtoint (i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"test.ReadError$elements", i64 0, i64 1) to i64) + +if.exit52: ; preds = %if.exit49 %80 = load i8*, i8** %str, align 8 - %not54 = icmp eq i8* %80, null - br i1 %not54, label %if.then55, label %if.exit59 - -if.then55: ; preds = %if.exit53 - store i64 ptrtoint (i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"test.ReadError$elements", i64 0, i64 1) to i64), i64* %reterr56, align 8 - br label %err_retblock58 - -postfailed57: ; No predecessors! - store %Doc undef, %Doc* %0, align 8 - ret i64 0 - -err_retblock58: ; preds = %if.then55 - %81 = load i64, i64* %reterr56, align 8 - ret i64 %81 - -if.exit59: ; preds = %if.exit53 - %82 = load i8*, i8** %str, align 8 - %83 = load i32, i32* %len, align 4 - %siuiext60 = sext i32 %83 to i64 - %add61 = add i64 %siuiext60, 1 - %84 = getelementptr inbounds %"char[]", %"char[]"* %url, i32 0, i32 1 - %85 = load i64, i64* %84, align 8 - %uisitrunc62 = trunc i64 %85 to i32 - %86 = getelementptr inbounds %"char[]", %"char[]"* %url, i32 0, i32 0 - %87 = load i8*, i8** %86, align 8 - %88 = call i32 (i8*, i64, i8*, ...) @snprintf(i8* %82, i64 %add61, i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.6, i32 0, i32 0), i32 %uisitrunc62, i8* %87) - %89 = getelementptr inbounds %Doc, %Doc* %literal64, i32 0, i32 0 - %90 = bitcast %Head* %literal67 to %"char[]"** - store %"char[]"* null, %"char[]"** %90, align 8 - %91 = getelementptr inbounds %Head, %Head* %literal67, i32 0, i32 0 - %92 = load i8*, i8** %str, align 8 - %93 = load i32, i32* %len, align 4 - %sub = sub i32 %93, 1 + %81 = load i32, i32* %len, align 4 + %siuiext53 = sext i32 %81 to i64 + %add54 = add i64 %siuiext53, 1 + %82 = getelementptr inbounds %"char[]", %"char[]"* %url, i32 0, i32 1 + %83 = load i64, i64* %82, align 8 + %uisitrunc55 = trunc i64 %83 to i32 + %84 = getelementptr inbounds %"char[]", %"char[]"* %url, i32 0, i32 0 + %85 = load i8*, i8** %84, align 8 + %86 = call i32 (i8*, i64, i8*, ...) @snprintf(i8* %80, i64 %add54, i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.6, i32 0, i32 0), i32 %uisitrunc55, i8* %85) + %87 = getelementptr inbounds %Doc, %Doc* %literal57, i32 0, i32 0 + %88 = bitcast %Head* %literal60 to %"char[]"** + store %"char[]"* null, %"char[]"** %88, align 8 + %89 = getelementptr inbounds %Head, %Head* %literal60, i32 0, i32 0 + %90 = load i8*, i8** %str, align 8 + %91 = load i32, i32* %len, align 4 + %sub = sub i32 %91, 1 %sisiext = sext i32 %sub to i64 - %94 = add i64 %sisiext, 1 - %size = sub i64 %94, 0 - %ptroffset = getelementptr inbounds i8, i8* %92, i64 0 - %95 = insertvalue %"char[]" undef, i8* %ptroffset, 0 - %96 = insertvalue %"char[]" %95, i64 %size, 1 - store %"char[]" %96, %"char[]"* %value69, align 8 - %97 = call i8* @"std::mem.alloc"(i64 16, i64 1) #2 - %ptrptr71 = bitcast i8* %97 to %"char[]"* - store %"char[]"* %ptrptr71, %"char[]"** %temp70, align 8 - %98 = load %"char[]"*, %"char[]"** %temp70, align 8 - %not72 = icmp eq %"char[]"* %98, null - br i1 %not72, label %if.then73, label %if.exit75 + %92 = add i64 %sisiext, 1 + %size = sub i64 %92, 0 + %ptroffset = getelementptr inbounds i8, i8* %90, i64 0 + %93 = insertvalue %"char[]" undef, i8* %ptroffset, 0 + %94 = insertvalue %"char[]" %93, i64 %size, 1 + store %"char[]" %94, %"char[]"* %value62, align 8 + %95 = call i8* @"std::mem.alloc"(i64 16, i64 1) #2 + %ptrptr64 = bitcast i8* %95 to %"char[]"* + store %"char[]"* %ptrptr64, %"char[]"** %temp63, align 8 + %96 = load %"char[]"*, %"char[]"** %temp63, align 8 + %not65 = icmp eq %"char[]"* %96, null + br i1 %not65, label %if.then66, label %if.exit67 -if.then73: ; preds = %if.exit59 - store i64 ptrtoint (i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"test.ReadError$elements", i64 0, i64 1) to i64), i64* %error_var68, align 8 - br label %guard_block76 +if.then66: ; preds = %if.exit52 + store i64 ptrtoint (i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"test.ReadError$elements", i64 0, i64 1) to i64), i64* %error_var61, align 8 + br label %guard_block68 -if.exit75: ; preds = %if.exit59 - %99 = load %"char[]"*, %"char[]"** %temp70, align 8 - %100 = bitcast %"char[]"* %99 to i8* - %101 = bitcast %"char[]"* %value69 to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %100, i8* align 8 %101, i32 16, i1 false) - br label %noerr_block77 +if.exit67: ; preds = %if.exit52 + %97 = load %"char[]"*, %"char[]"** %temp63, align 8 + %98 = bitcast %"char[]"* %97 to i8* + %99 = bitcast %"char[]"* %value62 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %98, i8* align 8 %99, i32 16, i1 false) + br label %noerr_block69 -guard_block76: ; preds = %if.then73 - %102 = load i64, i64* %error_var68, align 8 - ret i64 %102 +guard_block68: ; preds = %if.then66 + %100 = load i64, i64* %error_var61, align 8 + ret i64 %100 -noerr_block77: ; preds = %if.exit75 - %103 = load %"char[]"*, %"char[]"** %temp70, align 8 - store %"char[]"* %103, %"char[]"** %91, align 8 - %104 = load %Head, %Head* %literal67, align 8 - store %Head %104, %Head* %value66, align 8 - %105 = call i8* @"std::mem.alloc"(i64 8, i64 1) #2 - %ptrptr79 = bitcast i8* %105 to %Head* - store %Head* %ptrptr79, %Head** %temp78, align 8 - %106 = load %Head*, %Head** %temp78, align 8 - %not80 = icmp eq %Head* %106, null - br i1 %not80, label %if.then81, label %if.exit83 +noerr_block69: ; preds = %if.exit67 + %101 = load %"char[]"*, %"char[]"** %temp63, align 8 + store %"char[]"* %101, %"char[]"** %89, align 8 + %102 = load %Head, %Head* %literal60, align 8 + store %Head %102, %Head* %value59, align 8 + %103 = call i8* @"std::mem.alloc"(i64 8, i64 1) #2 + %ptrptr71 = bitcast i8* %103 to %Head* + store %Head* %ptrptr71, %Head** %temp70, align 8 + %104 = load %Head*, %Head** %temp70, align 8 + %not72 = icmp eq %Head* %104, null + br i1 %not72, label %if.then73, label %if.exit74 -if.then81: ; preds = %noerr_block77 - store i64 ptrtoint (i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"test.ReadError$elements", i64 0, i64 1) to i64), i64* %error_var65, align 8 - br label %guard_block84 +if.then73: ; preds = %noerr_block69 + store i64 ptrtoint (i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"test.ReadError$elements", i64 0, i64 1) to i64), i64* %error_var58, align 8 + br label %guard_block75 -if.exit83: ; preds = %noerr_block77 - %107 = load %Head*, %Head** %temp78, align 8 - %108 = bitcast %Head* %107 to i8* - %109 = bitcast %Head* %value66 to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %108, i8* align 8 %109, i32 8, i1 false) - br label %noerr_block85 +if.exit74: ; preds = %noerr_block69 + %105 = load %Head*, %Head** %temp70, align 8 + %106 = bitcast %Head* %105 to i8* + %107 = bitcast %Head* %value59 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %106, i8* align 8 %107, i32 8, i1 false) + br label %noerr_block76 -guard_block84: ; preds = %if.then81 - %110 = load i64, i64* %error_var65, align 8 - ret i64 %110 +guard_block75: ; preds = %if.then73 + %108 = load i64, i64* %error_var58, align 8 + ret i64 %108 -noerr_block85: ; preds = %if.exit83 - %111 = load %Head*, %Head** %temp78, align 8 - store %Head* %111, %Head** %89, align 8 - %112 = bitcast %Doc* %0 to i8* - %113 = bitcast %Doc* %literal64 to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %112, i8* align 8 %113, i32 8, i1 false) +noerr_block76: ; preds = %if.exit74 + %109 = load %Head*, %Head** %temp70, align 8 + store %Head* %109, %Head** %87, align 8 + %110 = bitcast %Doc* %0 to i8* + %111 = bitcast %Doc* %literal57 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %110, i8* align 8 %111, i32 8, i1 false) ret i64 0 } @@ -693,10 +673,8 @@ phi_block: ; preds = %else_block, %after. define i64 @test.isTitleNonEmpty(i8* %0, %Head* %1) #0 { entry: %doc = alloca %Doc, align 8 - %reterr = alloca i64, align 8 %head = alloca %"char[]"*, align 8 - %reterr3 = alloca i64, align 8 - %reterr7 = alloca i64, align 8 + %reterr = alloca i64, align 8 %2 = getelementptr inbounds %Doc, %Doc* %doc, i32 0, i32 0 store %Head* %1, %Head** %2, align 8 %3 = getelementptr inbounds %Doc, %Doc* %doc, i32 0, i32 0 @@ -705,46 +683,28 @@ entry: br i1 %not, label %if.then, label %if.exit if.then: ; preds = %entry - store i64 ptrtoint ([1 x i8*]* @"test.TitleResult$elements" to i64), i64* %reterr, align 8 - br label %err_retblock - -postfailed: ; No predecessors! - store i8 undef, i8* %0, align 1 - ret i64 0 - -err_retblock: ; preds = %if.then - %5 = load i64, i64* %reterr, align 8 - ret i64 %5 + ret i64 ptrtoint ([1 x i8*]* @"test.TitleResult$elements" to i64) if.exit: ; preds = %entry - %6 = getelementptr inbounds %Doc, %Doc* %doc, i32 0, i32 0 - %7 = load %Head*, %Head** %6, align 8 - %8 = getelementptr inbounds %Head, %Head* %7, i32 0, i32 0 - %9 = load %"char[]"*, %"char[]"** %8, align 8 - store %"char[]"* %9, %"char[]"** %head, align 8 - %10 = load %"char[]"*, %"char[]"** %head, align 8 - %not1 = icmp eq %"char[]"* %10, null - br i1 %not1, label %if.then2, label %if.exit6 + %5 = getelementptr inbounds %Doc, %Doc* %doc, i32 0, i32 0 + %6 = load %Head*, %Head** %5, align 8 + %7 = getelementptr inbounds %Head, %Head* %6, i32 0, i32 0 + %8 = load %"char[]"*, %"char[]"** %7, align 8 + store %"char[]"* %8, %"char[]"** %head, align 8 + %9 = load %"char[]"*, %"char[]"** %head, align 8 + %not1 = icmp eq %"char[]"* %9, null + br i1 %not1, label %if.then2, label %if.exit3 if.then2: ; preds = %if.exit - store i64 ptrtoint ([1 x i8*]* @"test.TitleResult$elements" to i64), i64* %reterr3, align 8 - br label %err_retblock5 + ret i64 ptrtoint ([1 x i8*]* @"test.TitleResult$elements" to i64) -postfailed4: ; No predecessors! - store i8 undef, i8* %0, align 1 - ret i64 0 - -err_retblock5: ; preds = %if.then2 - %11 = load i64, i64* %reterr3, align 8 - ret i64 %11 - -if.exit6: ; preds = %if.exit - %12 = load %"char[]"*, %"char[]"** %head, align 8 - %13 = getelementptr inbounds %"char[]", %"char[]"* %12, i32 0, i32 1 - %14 = load i64, i64* %13, align 8 - %lt = icmp ult i64 0, %14 - %15 = zext i1 %lt to i8 - store i8 %15, i8* %0, align 1 +if.exit3: ; preds = %if.exit + %10 = load %"char[]"*, %"char[]"** %head, align 8 + %11 = getelementptr inbounds %"char[]", %"char[]"* %10, i32 0, i32 1 + %12 = load i64, i64* %11, align 8 + %lt = icmp ult i64 0, %12 + %13 = zext i1 %lt to i8 + store i8 %13, i8* %0, align 1 ret i64 0 } diff --git a/test/test_suite/errors/general_error_regression.c3t b/test/test_suite/errors/general_error_regression.c3t index d91c9280a..9157ffad7 100644 --- a/test/test_suite/errors/general_error_regression.c3t +++ b/test/test_suite/errors/general_error_regression.c3t @@ -1,3 +1,4 @@ +// #target: x64-darwin module foo; import std::io; diff --git a/test/test_suite/errors/macro_err.c3t b/test/test_suite/errors/macro_err.c3t new file mode 100644 index 000000000..3194606b4 --- /dev/null +++ b/test/test_suite/errors/macro_err.c3t @@ -0,0 +1,53 @@ +// #target: x64-darwin +module test; +fn int! abc() +{ + return 1; +} +macro test() +{ + return abc()?; +} + +fn void main() +{ + libc::printf("%d\n", @test() ?? 2); +} + +/* #expect: test.ll + + +define i64 @test.abc(i32* %0) #0 { +entry: + %reterr = alloca i64, align 8 + store i32 1, i32* %0, align 4 + ret i64 0 +} + +; Function Attrs: nounwind +define void @test.main() #0 { +entry: + %error_var = alloca i64, align 8 + %retparam = alloca i32, align 4 + %0 = call i64 @test.abc(i32* %retparam) + %not_err = icmp eq i64 %0, 0 + br i1 %not_err, label %after.errcheck, label %error + +error: ; preds = %entry + store i64 %0, i64* %error_var, align 8 + br label %guard_block + +after.errcheck: ; preds = %entry + br label %noerr_block + +guard_block: ; preds = %error + ret void + +noerr_block: ; preds = %after.errcheck + %1 = load i32, i32* %retparam, align 4 + br label %phi_block + +phi_block: ; preds = %noerr_block + %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %1) + ret void +} diff --git a/test/test_suite/errors/macro_err2.c3t b/test/test_suite/errors/macro_err2.c3t new file mode 100644 index 000000000..fec44cd1a --- /dev/null +++ b/test/test_suite/errors/macro_err2.c3t @@ -0,0 +1,58 @@ +// #target: x64-darwin +module test; +optenum Tester { FOO } + +fn int! abc() +{ + return Tester.FOO!; +} +macro test() +{ + defer libc::printf("Test2\n"); + return abc(); +} + +fn void main() +{ + defer libc::printf("Test1\n"); + libc::printf("%d\n", @test() ?? 2); +} + +/* #expect: test.ll + +define i64 @test.abc(i32* %0) #0 { +entry: + ret i64 ptrtoint ([1 x i8*]* @"test.Tester$elements" to i64) +} + +define void @test.main() #0 { +entry: + %blockret = alloca i32, align 4 + %retparam = alloca i32, align 4 + %0 = call i64 @test.abc(i32* %retparam) + %not_err = icmp eq i64 %0, 0 + br i1 %not_err, label %after.errcheck, label %opt_block_cleanup + +after.errcheck: ; preds = %entry + %1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.1, i32 0, i32 0)) + %2 = load i32, i32* %retparam, align 4 + store i32 %2, i32* %blockret, align 4 + br label %expr_block.exit + +opt_block_cleanup: ; preds = %entry + %3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.2, i32 0, i32 0)) + br label %else_block + +expr_block.exit: ; preds = %after.errcheck + %4 = load i32, i32* %blockret, align 4 + br label %phi_block + +else_block: ; preds = %opt_block_cleanup + br label %phi_block + +phi_block: ; preds = %else_block, %expr_block.exit + %val = phi i32 [ %4, %expr_block.exit ], [ 2, %else_block ] + %5 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %val) + %6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.3, i32 0, i32 0)) + ret void +} \ No newline at end of file diff --git a/test/test_suite/errors/macro_err3.c3t b/test/test_suite/errors/macro_err3.c3t new file mode 100644 index 000000000..7d880f362 --- /dev/null +++ b/test/test_suite/errors/macro_err3.c3t @@ -0,0 +1,36 @@ +// #target: x64-darwin +module test; +optenum Tester { FOO } + + +macro test() +{ + defer libc::printf("Test2\n"); + return Tester.FOO!; +} + +fn void main() +{ + defer libc::printf("Test1\n"); + libc::printf("%d\n", @test() ?? 2); +} + +/* #expect: test.ll + +define void @test.main() #0 { +entry: + %blockret = alloca i32, align 4 + br label %opt_block_cleanup + +opt_block_cleanup: ; preds = %entry + %0 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.2, i32 0, i32 0)) + br label %else_block + +else_block: ; preds = %opt_block_cleanup + br label %phi_block + +phi_block: ; preds = %else_block + %1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 2) + %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.3, i32 0, i32 0)) + ret void +} diff --git a/test/test_suite/errors/no_common.c3 b/test/test_suite/errors/no_common.c3 new file mode 100644 index 000000000..b896300a3 --- /dev/null +++ b/test/test_suite/errors/no_common.c3 @@ -0,0 +1,13 @@ +fn int! abc() +{ + return 1; +} +macro test() +{ + abc()?; +} + +fn void main() +{ + @test() ?? 2; // #error: Cannot find a common type for 'void' and 'int' +} \ No newline at end of file diff --git a/test/test_suite/errors/or_err_bool.c3t b/test/test_suite/errors/or_err_bool.c3t index 942d362e9..39f2aa564 100644 --- a/test/test_suite/errors/or_err_bool.c3t +++ b/test/test_suite/errors/or_err_bool.c3t @@ -1,3 +1,5 @@ +// #target: x64-darwin + module test; fn void tester() diff --git a/test/test_suite/from_docs/examples_if_catch.c3t b/test/test_suite/from_docs/examples_if_catch.c3t index 71d4208a5..359022259 100644 --- a/test/test_suite/from_docs/examples_if_catch.c3t +++ b/test/test_suite/from_docs/examples_if_catch.c3t @@ -49,26 +49,16 @@ fn void main() define i64 @demo.divide(double* %0, i32 %1, i32 %2) #0 { entry: %reterr = alloca i64, align 8 - %reterr1 = alloca i64, align 8 %eq = icmp eq i32 %2, 0 br i1 %eq, label %if.then, label %if.exit if.then: ; preds = %entry - store i64 ptrtoint ([1 x i8*]* @"demo.MathError$elements" to i64), i64* %reterr, align 8 - br label %err_retblock - -postfailed: ; No predecessors! - store double undef, double* %0, align 8 - ret i64 0 - -err_retblock: ; preds = %if.then - %3 = load i64, i64* %reterr, align 8 - ret i64 %3 + ret i64 ptrtoint ([1 x i8*]* @"demo.MathError$elements" to i64) if.exit: ; preds = %entry %sifp = sitofp i32 %1 to double - %sifp2 = sitofp i32 %2 to double - %fdiv = fdiv double %sifp, %sifp2 + %sifp1 = sitofp i32 %2 to double + %fdiv = fdiv double %sifp, %sifp1 store double %fdiv, double* %0, align 8 ret i64 0 } @@ -77,6 +67,7 @@ define i64 @demo.testMayError() #0 { entry: %error_var = alloca i64, align 8 %retparam = alloca double, align 8 + %reterr = alloca i64, align 8 %0 = call i32 @demo.foo() %1 = call i32 @demo.bar() %2 = call i64 @demo.divide(double* %retparam, i32 %0, i32 %1) @@ -122,6 +113,7 @@ after.errcheck: ; preds = %entry br label %after_assign after_assign: ; preds = %after.errcheck, %error + store i64 0, i64* %err, align 8 br label %testblock testblock: ; preds = %after_assign diff --git a/test/test_suite/macros/macro_defer_noscope.c3t b/test/test_suite/macros/macro_defer_noscope.c3t deleted file mode 100644 index 4b87726b1..000000000 --- a/test/test_suite/macros/macro_defer_noscope.c3t +++ /dev/null @@ -1,50 +0,0 @@ -// #target: x64-darwin -module foo; -extern fn void puts(char *); - -fn void foo1() -{ - puts("foo1"); -} - -module foo2; -import foo; -fn void foo2() -{ - foo::puts("foo2"); -} -module bar; -import foo2; -macro bar1(#test) @noscope -{ - defer foo2::foo2(); - #test; - #test; -} - -module baz; -import foo; -import bar; -extern fn void printf(char *, ...); -fn void main() -{ - var $foo = 1; - @bar::bar1(foo::foo1()); - @bar::bar1($foo += 1); - printf("End: %d\n", $foo); -} - -/* #expect: baz.ll - -declare void @foo2.foo2() -declare void @foo.foo1() - -define void @baz.main() #0 { -entry: - call void @foo.foo1() - call void @foo.foo1() - call void (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str, i32 0, i32 0), i32 3) - call void @foo2.foo2() - call void @foo2.foo2() - ret void -} diff --git a/test/test_suite/statements/defer_in_defer.c3t b/test/test_suite/statements/defer_in_defer.c3t index 4e7d3aa01..6d9e15b73 100644 --- a/test/test_suite/statements/defer_in_defer.c3t +++ b/test/test_suite/statements/defer_in_defer.c3t @@ -17,7 +17,7 @@ fn void test() fn void test_line() { - defer defer test1(); + defer { defer test1(); } } // #expect: defer_in_defer.ll diff --git a/test/test_suite/statements/do_without_compound.c3 b/test/test_suite/statements/do_without_compound.c3 new file mode 100644 index 000000000..b37b73729 --- /dev/null +++ b/test/test_suite/statements/do_without_compound.c3 @@ -0,0 +1,5 @@ +fn void test1() +{ + do test1(); while(1); // #error: A do loop must use { } around its body +} +