diff --git a/resources/testfragments/super_simple2.c3 b/resources/testfragments/super_simple2.c3 index 8d1042b23..914c88a04 100644 --- a/resources/testfragments/super_simple2.c3 +++ b/resources/testfragments/super_simple2.c3 @@ -2,19 +2,18 @@ module baz; import gen; extern func int printf(char *hello, ...); +extern func void blurg(); public func void runBaz() { - int x = printf("Baz func\n"); - int y = printf("FOek"); - assert(x > 0 && y > 0); + int i = printf("hello"); - int z = printf("Foefe\n"); - x = printf("Baz func\n"); - y = printf("FOek"); + if (i > 0) blurg(); - assert(x > 0 && y > 0 && printf("Fejei") > 0); + if (i > 0) return; + + $unreachable; } diff --git a/src/compiler/ast.c b/src/compiler/ast.c index a29605116..aa9369615 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -1085,6 +1085,9 @@ static void fprint_ast_recursive(Context *context, FILE *file, Ast *ast, int ind case AST_NOP_STMT: DUMP("(nop)"); return; + case AST_UNREACHABLE_STMT: + DUMP("(unreachable)"); + return; case AST_VOLATILE_STMT: DUMP("(volatile"); DUMPAST(ast->volatile_stmt); diff --git a/src/compiler/enums.h b/src/compiler/enums.h index ecb5ac968..8eec3559e 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -76,6 +76,7 @@ typedef enum AST_RETURN_STMT, AST_SWITCH_STMT, AST_NEXT_STMT, + AST_UNREACHABLE_STMT, AST_VOLATILE_STMT, AST_WHILE_STMT, AST_SCOPED_STMT, @@ -412,6 +413,7 @@ typedef enum TOKEN_TRY, TOKEN_TYPEDEF, TOKEN_UNION, + TOKEN_CT_UNREACHABLE, TOKEN_UNTIL, TOKEN_VAR, // Reserved TOKEN_VOLATILE, diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 89a1f17a0..aa67ee9d7 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -847,6 +847,17 @@ static inline void gencontext_emit_assert_stmt(GenContext *context, Ast *ast) gencontext_emit_assume(context, ast->assert_stmt.expr); } +static inline void gencontext_emit_unreachable_stmt(GenContext *context, Ast *ast) +{ + // TODO emit message + gencontext_emit_call_intrinsic(context, trap_intrinsic_id, NULL, 0, NULL, 0); + LLVMBuildUnreachable(context->builder); + LLVMBasicBlockRef block = gencontext_create_free_block(context, "unreachable_block"); + context->current_block = NULL; + context->current_block_is_target = false; + gencontext_emit_block(context, block); +} + void gencontext_emit_expr_stmt(GenContext *context, Ast *ast) { if (ast->expr_stmt->failable) @@ -1003,13 +1014,15 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) case AST_CT_ELSE_STMT: case AST_CT_FOR_STMT: case AST_CT_SWITCH_STMT: + case AST_CASE_STMT: + case AST_DEFAULT_STMT: UNREACHABLE case AST_SWITCH_STMT: gencontext_emit_switch(context, ast); break; - case AST_CASE_STMT: - case AST_DEFAULT_STMT: - TODO + case AST_UNREACHABLE_STMT: + gencontext_emit_unreachable_stmt(context, ast); + break; case AST_VOLATILE_STMT: TODO } diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index c3c24de04..714afb916 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -6,6 +6,8 @@ #include "parser_internal.h" +Ast *parse_unreachable_stmt(Context *context); + #pragma mark --- Internal functions @@ -913,6 +915,8 @@ Ast *parse_stmt(Context *context) return parse_ct_for_stmt(context); case TOKEN_VOLATILE: return parse_volatile_stmt(context); + case TOKEN_CT_UNREACHABLE: + return parse_unreachable_stmt(context); case TOKEN_STAR: case TOKEN_AMP: case TOKEN_INTEGER: @@ -1034,6 +1038,14 @@ Ast *parse_stmt(Context *context) UNREACHABLE } +Ast *parse_unreachable_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_UNREACHABLE_STMT, context->tok); + advance_and_verify(context, TOKEN_CT_UNREACHABLE); + TRY_CONSUME_EOS_OR(poisoned_ast); + return ast; +} + Ast *parse_jump_stmt_no_eos(Context *context) { switch (context->tok.type) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index a41543f44..a3793a505 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -3395,6 +3395,8 @@ static Ast *ast_copy_from_macro(Context *context, Ast *source) MACRO_COPY_EXPR(ast->try_stmt.decl_expr); MACRO_COPY_AST(ast->try_stmt.body); return ast; + case AST_UNREACHABLE_STMT: + return ast; case AST_VOLATILE_STMT: TODO return ast; diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 9417699fe..4dcd98254 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -178,6 +178,12 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) return true; } +static inline bool sema_analyse_unreachable_stmt(Context *context, Ast *statement) +{ + context->current_scope->jump_end = true; + return true; +} + static inline bool sema_analyse_decl_expr_list(Context *context, Expr *expr) { assert(expr->expr_kind == EXPR_DECL_LIST); @@ -1286,6 +1292,11 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement } if (context->current_scope->jump_end && !context->current_scope->allow_dead_code) { + if (statement->ast_kind == AST_UNREACHABLE_STMT) + { + context->current_scope->allow_dead_code = true; + return true; + } //SEMA_ERROR(statement, "This code will never execute."); context->current_scope->allow_dead_code = true; //return false; @@ -1341,10 +1352,13 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement return sema_analyse_switch_stmt(context, statement); case AST_NEXT_STMT: return sema_analyse_next_stmt(context, statement); + case AST_UNREACHABLE_STMT: + return sema_analyse_unreachable_stmt(context, statement); case AST_VOLATILE_STMT: return sema_analyse_volatile_stmt(context, statement); case AST_WHILE_STMT: return sema_analyse_while_stmt(context, statement); + case AST_CT_ELIF_STMT: case AST_CT_ELSE_STMT: UNREACHABLE diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index cf921c05a..6813f1150 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -344,6 +344,8 @@ const char *token_type_to_string(TokenType type) return "$if"; case TOKEN_CT_SWITCH: return "$switch"; + case TOKEN_CT_UNREACHABLE: + return "$unreachable"; case TOKEN_EOF: return "EOF"; diff --git a/test/test_suite/assert/unreachable.c3t b/test/test_suite/assert/unreachable.c3t new file mode 100644 index 000000000..2422a83b9 --- /dev/null +++ b/test/test_suite/assert/unreachable.c3t @@ -0,0 +1,36 @@ + +public func int foo() +{ + return 1; +} + +public func void test() +{ + int x = foo(); + if (x > 0) return; + $unreachable; + x++; +} + + +// #expect: unreachable.ll + + %x = alloca i32 + %0 = call i32 @unreachable.foo() + store i32 %0, i32* %x + %1 = load i32, i32* %x + %gt = icmp sgt i32 %1, 0 + br i1 %gt, label %if.then, label %if.exit + +if.then: + ret void + +if.exit: + call void @llvm.trap() + unreachable + +unreachable_block: + %2 = load i32, i32* %x + %add = add nsw i32 %2, 1 + store i32 %add, i32* %x + ret void