From 9092defd460eac87c64bdea867fa0b4b8a910e7c Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Fri, 31 Jan 2025 14:39:51 +0100 Subject: [PATCH] defer is broken when placed before a $foreach #1912 --- lib/std/collections/list.c3 | 16 +- releasenotes.md | 1 + resources/examples/contextfree/boolerr.c3 | 8 +- src/compiler/ast.c | 9 + src/compiler/c_codegen.c | 1 + src/compiler/compiler_internal.h | 1 + src/compiler/copying.c | 3 + src/compiler/enums.h | 13 +- src/compiler/llvm_codegen_stmt.c | 4 + src/compiler/parse_stmt.c | 8 +- src/compiler/sema_decls.c | 136 ++++----- src/compiler/sema_liveness.c | 3 + src/compiler/sema_stmts.c | 42 ++- .../compile_time/ct_forach_with_defer.c3t | 258 ++++++++++++++++++ test/test_suite/debug_symbols/ct_foreach.c3t | 33 +-- 15 files changed, 432 insertions(+), 104 deletions(-) create mode 100644 test/test_suite/compile_time/ct_forach_with_defer.c3t diff --git a/lib/std/collections/list.c3 b/lib/std/collections/list.c3 index 60b4b9709..8863ad748 100644 --- a/lib/std/collections/list.c3 +++ b/lib/std/collections/list.c3 @@ -19,6 +19,20 @@ struct List (Printable) Type *entries; } +<* + @param initial_capacity "The initial capacity to reserve" + @param [&inout] allocator "The allocator to use, defaults to the heap allocator" +*> +fn List* List.init(&self, usz initial_capacity = 16, Allocator allocator) +{ + self.allocator = allocator; + self.size = 0; + self.capacity = 0; + self.entries = null; + self.reserve(initial_capacity); + return self; +} + <* @param initial_capacity "The initial capacity to reserve" @param [&inout] allocator "The allocator to use, defaults to the heap allocator" @@ -40,7 +54,7 @@ fn List* List.new_init(&self, usz initial_capacity = 16, Allocator allocator = a *> fn List* List.temp_init(&self, usz initial_capacity = 16) { - return self.new_init(initial_capacity, allocator::temp()) @inline; + return self.init(initial_capacity, allocator::temp()) @inline; } <* diff --git a/releasenotes.md b/releasenotes.md index 7113a9be6..a06ac6450 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -36,6 +36,7 @@ - Missing error when placing a single statement for-body on a new row #1892. - Fix bug where in dead code, only the first statement would be turned into a nop. - Remove unused $inline argument to mem::copy. +- defer is broken when placed before a $foreach #1912. ### Stdlib changes - Added '%h' and '%H' for printing out binary data in hexadecimal using the formatter. diff --git a/resources/examples/contextfree/boolerr.c3 b/resources/examples/contextfree/boolerr.c3 index a581e697b..9f2ccbb9c 100644 --- a/resources/examples/contextfree/boolerr.c3 +++ b/resources/examples/contextfree/boolerr.c3 @@ -3,10 +3,10 @@ import libc; import std::io; import std::collections::maybe; -def MaybeString = Maybe(); -def MaybeHead = Maybe(); -def new_head_val = maybe::value(); -def new_string_val = maybe::value(); +def MaybeString = Maybe<[String]>; +def MaybeHead = Maybe<[Head]>; +def new_head_val = maybe::value<[Head]>; +def new_string_val = maybe::value<[String]>; fault TitleResult { diff --git a/src/compiler/ast.c b/src/compiler/ast.c index b7d9287e5..a5bcab549 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -342,6 +342,15 @@ bool ast_is_compile_time(Ast *ast) return expr_is_runtime_const(ast->return_stmt.expr); case AST_EXPR_STMT: return expr_is_runtime_const(ast->expr_stmt); + case AST_CT_COMPOUND_STMT: + { + AstId current = ast->ct_compound_stmt; + while (current) + { + if (!ast_is_compile_time(ast_next(¤t))) return false; + } + return true; + } case AST_COMPOUND_STMT: { AstId current = ast->compound_stmt.first_stmt; diff --git a/src/compiler/c_codegen.c b/src/compiler/c_codegen.c index 5c9d6a38f..ca5c9028e 100644 --- a/src/compiler/c_codegen.c +++ b/src/compiler/c_codegen.c @@ -821,6 +821,7 @@ static void c_emit_stmt(GenContext *c, Ast *stmt) break; case AST_CT_ECHO_STMT: break; + case AST_CT_COMPOUND_STMT: case AST_CT_ELSE_STMT: break; case AST_CT_FOREACH_STMT: diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index a7bf48ef9..c08bbb43c 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1488,6 +1488,7 @@ typedef struct Ast_ AstAssertStmt assert_stmt; // 16 AstCaseStmt case_stmt; // 32 AstCompoundStmt compound_stmt; // 12 + AstId ct_compound_stmt; AstContinueBreakStmt contbreak_stmt;// 24 AstContractStmt contract_stmt; // 32 AstDocFault contract_fault; // 24 diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 6d9dc0fcb..35bd49ba7 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -697,6 +697,9 @@ RETRY: MACRO_COPY_EXPRID(ast->ct_switch_stmt.cond); MACRO_COPY_AST_LIST(ast->ct_switch_stmt.body); break; + case AST_CT_COMPOUND_STMT: + MACRO_COPY_ASTID(ast->ct_compound_stmt); + break; case AST_DECLARE_STMT: MACRO_COPY_DECL(ast->declare_stmt); break; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 1a6748f25..add02b9b5 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -189,15 +189,19 @@ typedef enum typedef enum { AST_POISONED, - AST_ASM_STMT, AST_ASM_BLOCK_STMT, AST_ASM_LABEL, + AST_ASM_STMT, AST_ASSERT_STMT, + AST_BLOCK_EXIT_STMT, AST_BREAK_STMT, AST_CASE_STMT, AST_COMPOUND_STMT, AST_CONTINUE_STMT, + AST_CONTRACT, + AST_CONTRACT_FAULT, AST_CT_ASSERT, + AST_CT_COMPOUND_STMT, AST_CT_ECHO_STMT, AST_CT_ELSE_STMT, AST_CT_FOREACH_STMT, @@ -209,17 +213,14 @@ typedef enum AST_DEFAULT_STMT, AST_DEFER_STMT, AST_EXPR_STMT, - AST_FOR_STMT, AST_FOREACH_STMT, + AST_FOR_STMT, AST_IF_CATCH_SWITCH_STMT, AST_IF_STMT, + AST_NEXTCASE_STMT, AST_NOP_STMT, AST_RETURN_STMT, - AST_BLOCK_EXIT_STMT, AST_SWITCH_STMT, - AST_NEXTCASE_STMT, - AST_CONTRACT, - AST_CONTRACT_FAULT, } AstKind; typedef enum diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 906396ea3..44b9ffb41 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -29,6 +29,7 @@ void llvm_emit_compound_stmt(GenContext *c, Ast *ast) if (old_block) c->debug.block_stack = old_block; } + void llvm_emit_local_static(GenContext *c, Decl *decl, BEValue *value) { // In defers we might already have generated this variable. @@ -1690,6 +1691,9 @@ void llvm_emit_stmt(GenContext *c, Ast *ast) case AST_BLOCK_EXIT_STMT: llvm_emit_block_exit_return(c, ast); break; + case AST_CT_COMPOUND_STMT: + llvm_emit_statement_chain(c, ast->ct_compound_stmt); + break; case AST_COMPOUND_STMT: llvm_emit_compound_stmt(c, ast); break; diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 94b6519d7..37d076f22 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -1100,9 +1100,9 @@ static inline Ast* parse_ct_foreach_stmt(ParseContext *c) TRY_CONSUME_OR_RET(TOKEN_COLON, "Expected ':'.", poisoned_ast); ASSIGN_EXPRID_OR_RET(ast->ct_foreach_stmt.expr, parse_expr(c), poisoned_ast); CONSUME_OR_RET(TOKEN_RPAREN, poisoned_ast); - Ast *body = new_ast(AST_COMPOUND_STMT, ast->span); + Ast *body = new_ast(AST_CT_COMPOUND_STMT, ast->span); ast->ct_foreach_stmt.body = astid(body); - AstId *current = &body->compound_stmt.first_stmt; + AstId *current = &body->ct_compound_stmt; while (!try_consume(c, TOKEN_CT_ENDFOREACH)) { ASSIGN_AST_OR_RET(Ast *stmt, parse_stmt(c), poisoned_ast); @@ -1140,9 +1140,9 @@ static inline Ast* parse_ct_for_stmt(ParseContext *c) CONSUME_OR_RET(TOKEN_RPAREN, poisoned_ast); - Ast *body = new_ast(AST_COMPOUND_STMT, ast->span); + Ast *body = new_ast(AST_CT_COMPOUND_STMT, ast->span); ast->for_stmt.body = astid(body); - AstId *current = &body->compound_stmt.first_stmt; + AstId *current = &body->ct_compound_stmt; while (!try_consume(c, TOKEN_CT_ENDFOR)) { ASSIGN_AST_OR_RET(Ast *stmt, parse_stmt(c), poisoned_ast); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 7978cd8b4..d362bf2a4 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -3763,6 +3763,78 @@ INLINE bool sema_analyse_macro_body(SemaContext *context, Decl **body_parameters } return true; } + +static inline bool sema_check_body_const(SemaContext *context, Ast *body) +{ + while (body) + { + switch (body->ast_kind) + { + case AST_CT_ASSERT: + case AST_CT_ECHO_STMT: + case AST_CT_FOREACH_STMT: + case AST_CT_FOR_STMT: + case AST_CT_IF_STMT: + case AST_CT_SWITCH_STMT: + case AST_CT_ELSE_STMT: + case AST_DECLARE_STMT: + case AST_DECLS_STMT: + case AST_NOP_STMT: + body = astptrzero(body->next); + continue; + case AST_CT_COMPOUND_STMT: + if (!sema_check_body_const(context, body)) return false; + body = astptrzero(body->next); + continue; + case AST_RETURN_STMT: + if (!body->return_stmt.expr) RETURN_SEMA_ERROR(body, "The 'return' in an `@const` must provide a value, e.g. 'return Foo.typeid;'"); + if (body->next) RETURN_SEMA_ERROR(body, "There should not be any statements after 'return'."); + body = NULL; + continue; + case AST_EXPR_STMT: + // For some cases we KNOW it's not correct. + switch (body->expr_stmt->expr_kind) + { + case EXPR_IDENTIFIER: + case EXPR_UNRESOLVED_IDENTIFIER: + case EXPR_LAMBDA: + case EXPR_FORCE_UNWRAP: + case EXPR_ASM: + case EXPR_EXPR_BLOCK: + case EXPR_TERNARY: + case EXPR_RETHROW: + break; + default: + body = astptrzero(body->next); + continue; + } + FALLTHROUGH; + case AST_POISONED: + case AST_ASM_STMT: + case AST_ASM_LABEL: + case AST_ASM_BLOCK_STMT: + case AST_ASSERT_STMT: + case AST_BREAK_STMT: + case AST_CASE_STMT: + case AST_COMPOUND_STMT: + case AST_CONTINUE_STMT: + case AST_DEFAULT_STMT: + case AST_DEFER_STMT: + case AST_FOR_STMT: + case AST_FOREACH_STMT: + case AST_IF_CATCH_SWITCH_STMT: + case AST_IF_STMT: + case AST_BLOCK_EXIT_STMT: + case AST_SWITCH_STMT: + case AST_NEXTCASE_STMT: + case AST_CONTRACT: + case AST_CONTRACT_FAULT: + RETURN_SEMA_ERROR(body, "Only 'return' and compile time statements are allowed in an '@const' macro."); + } + UNREACHABLE + } + return true; +} static inline bool sema_analyse_macro(SemaContext *context, Decl *decl, bool *erase_decl) { decl->func_decl.unit = context->unit; @@ -3800,69 +3872,7 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl, bool *er ASSERT(body->ast_kind == AST_COMPOUND_STMT); body = astptrzero(body->compound_stmt.first_stmt); if (!body) RETURN_SEMA_ERROR(decl, "'@const' macros cannot have an empty body."); - while (body) - { - switch (body->ast_kind) - { - case AST_CT_ASSERT: - case AST_CT_ECHO_STMT: - case AST_CT_FOREACH_STMT: - case AST_CT_FOR_STMT: - case AST_CT_IF_STMT: - case AST_CT_SWITCH_STMT: - case AST_CT_ELSE_STMT: - case AST_DECLARE_STMT: - case AST_DECLS_STMT: - case AST_NOP_STMT: - body = astptrzero(body->next); - continue; - case AST_RETURN_STMT: - if (!body->return_stmt.expr) RETURN_SEMA_ERROR(body, "The 'return' in an `@const` must provide a value, e.g. 'return Foo.typeid;'"); - if (body->next) RETURN_SEMA_ERROR(body, "There should not be any statements after 'return'."); - body = NULL; - continue; - case AST_EXPR_STMT: - // For some cases we KNOW it's not correct. - switch (body->expr_stmt->expr_kind) - { - case EXPR_IDENTIFIER: - case EXPR_UNRESOLVED_IDENTIFIER: - case EXPR_LAMBDA: - case EXPR_FORCE_UNWRAP: - case EXPR_ASM: - case EXPR_EXPR_BLOCK: - case EXPR_TERNARY: - case EXPR_RETHROW: - break; - default: - body = astptrzero(body->next); - continue; - } - FALLTHROUGH; - case AST_POISONED: - case AST_ASM_STMT: - case AST_ASM_LABEL: - case AST_ASM_BLOCK_STMT: - case AST_ASSERT_STMT: - case AST_BREAK_STMT: - case AST_CASE_STMT: - case AST_COMPOUND_STMT: - case AST_CONTINUE_STMT: - case AST_DEFAULT_STMT: - case AST_DEFER_STMT: - case AST_FOR_STMT: - case AST_FOREACH_STMT: - case AST_IF_CATCH_SWITCH_STMT: - case AST_IF_STMT: - case AST_BLOCK_EXIT_STMT: - case AST_SWITCH_STMT: - case AST_NEXTCASE_STMT: - case AST_CONTRACT: - case AST_CONTRACT_FAULT: - RETURN_SEMA_ERROR(body, "Only 'return' and compile time statements are allowed in an '@const' macro."); - } - UNREACHABLE - } + sema_check_body_const(context, body); } decl->type = type_void; return true; diff --git a/src/compiler/sema_liveness.c b/src/compiler/sema_liveness.c index ec356dfec..a9cada486 100644 --- a/src/compiler/sema_liveness.c +++ b/src/compiler/sema_liveness.c @@ -113,6 +113,9 @@ static void sema_trace_stmt_liveness(Ast *ast) case AST_NOP_STMT: case AST_ASM_LABEL: return; + case AST_CT_COMPOUND_STMT: + sema_trace_stmt_chain_liveness(ast->ct_compound_stmt); + return; case AST_COMPOUND_STMT: sema_trace_stmt_chain_liveness(ast->compound_stmt.first_stmt); return; diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 2589450e9..39557dd1e 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -232,6 +232,27 @@ static inline bool sema_analyse_compound_stmt(SemaContext *context, Ast *stateme return success; } +/** + * Ct compound statement + */ +static inline bool sema_analyse_ct_compound_stmt(SemaContext *context, Ast *statement) +{ + if (!ast_ok(statement)) return false; + AstId current = statement->ct_compound_stmt; + Ast *ast = NULL; + bool all_ok = true; + while (current) + { + ast = ast_next(¤t); + if (!sema_analyse_statement(context, ast)) + { + ast_poison(ast); + all_ok = false; + } + } + return all_ok; +} + /** * continue and continue FOO; */ @@ -2824,13 +2845,20 @@ static inline bool sema_analyse_ct_foreach_stmt(SemaContext *context, Ast *state index->var.init_expr = expr_new_const_int(index->span, type_int, i); index->type = type_int; } - if (!sema_analyse_compound_statement_no_scope(context, compound_stmt)) goto FAILED; + if (!sema_analyse_statement(context, compound_stmt)) goto FAILED; *current = astid(compound_stmt); current = &compound_stmt->next; } sema_context_pop_ct_stack(context, ct_context); - statement->ast_kind = AST_COMPOUND_STMT; - statement->compound_stmt = (AstCompoundStmt) { .first_stmt = start }; + if (!start) + { + statement->ast_kind = AST_NOP_STMT; + } + else + { + statement->ast_kind = AST_CT_COMPOUND_STMT; + statement->ct_compound_stmt = start; + } return true; FAILED_NO_LIST: SEMA_ERROR(collection, "Expected a list to iterate over, but this was a non-list expression of type %s.", @@ -3046,7 +3074,7 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement Ast *compound_stmt = copy_ast_single(body); // Analyse the body - if (!sema_analyse_compound_statement_no_scope(context, compound_stmt)) goto FAILED; + if (!sema_analyse_statement(context, compound_stmt)) goto FAILED; // Append it. *current = astid(compound_stmt); @@ -3059,8 +3087,8 @@ static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement } } // Analysis is done turn the generated statements into a compound statement for lowering. - statement->ast_kind = AST_COMPOUND_STMT; - statement->compound_stmt = (AstCompoundStmt) { .first_stmt = start }; + statement->ast_kind = AST_CT_COMPOUND_STMT; + statement->ct_compound_stmt = start; return true; FAILED: sema_context_pop_ct_stack(context, for_context); @@ -3091,6 +3119,8 @@ static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *state RETURN_SEMA_ERROR(statement, "Unexpected 'case' outside of switch"); case AST_COMPOUND_STMT: return sema_analyse_compound_stmt(context, statement); + case AST_CT_COMPOUND_STMT: + return sema_analyse_ct_compound_stmt(context, statement); case AST_CONTINUE_STMT: return sema_analyse_continue_stmt(context, statement); case AST_CT_ASSERT: diff --git a/test/test_suite/compile_time/ct_forach_with_defer.c3t b/test/test_suite/compile_time/ct_forach_with_defer.c3t new file mode 100644 index 000000000..a1176febe --- /dev/null +++ b/test/test_suite/compile_time/ct_forach_with_defer.c3t @@ -0,0 +1,258 @@ +// #target: macos-x64 +module test; +import std; +// Issue #1912 +fn void test() +{ + defer io::printn("World!"); + const HELLO = "Hello"; + $for (var $i = 0; $i < 5; $i++) + io::printf("%c", HELLO[$i]); + $endfor +} + +fn void main() +{ + defer io::printn("World!"); + + $foreach ($c : "Hello") + io::printf("%c", $c); + $endforeach + io::printn(); +} + +/* #expect: test.ll + +define void @test.test() #0 { +entry: + %varargslots = alloca [1 x %any], align 16 + %taddr = alloca i8, align 1 + %retparam = alloca i64, align 8 + %varargslots1 = alloca [1 x %any], align 16 + %taddr2 = alloca i8, align 1 + %retparam3 = alloca i64, align 8 + %varargslots4 = alloca [1 x %any], align 16 + %taddr5 = alloca i8, align 1 + %retparam6 = alloca i64, align 8 + %varargslots7 = alloca [1 x %any], align 16 + %taddr8 = alloca i8, align 1 + %retparam9 = alloca i64, align 8 + %varargslots10 = alloca [1 x %any], align 16 + %taddr11 = alloca i8, align 1 + %retparam12 = alloca i64, align 8 + %len = alloca i64, align 8 + %error_var = alloca i64, align 8 + %retparam14 = alloca i64, align 8 + %error_var15 = alloca i64, align 8 + %error_var21 = alloca i64, align 8 + store i8 72, ptr %taddr, align 1 + %0 = insertvalue %any undef, ptr %taddr, 0 + %1 = insertvalue %any %0, i64 ptrtoint (ptr @"$ct.char" to i64), 1 + store %any %1, ptr %varargslots, align 16 + %2 = call i64 @std.io.printf(ptr %retparam, ptr @.str.1, i64 2, ptr %varargslots, i64 1) + store i8 101, ptr %taddr2, align 1 + %3 = insertvalue %any undef, ptr %taddr2, 0 + %4 = insertvalue %any %3, i64 ptrtoint (ptr @"$ct.char" to i64), 1 + store %any %4, ptr %varargslots1, align 16 + %5 = call i64 @std.io.printf(ptr %retparam3, ptr @.str.2, i64 2, ptr %varargslots1, i64 1) + store i8 108, ptr %taddr5, align 1 + %6 = insertvalue %any undef, ptr %taddr5, 0 + %7 = insertvalue %any %6, i64 ptrtoint (ptr @"$ct.char" to i64), 1 + store %any %7, ptr %varargslots4, align 16 + %8 = call i64 @std.io.printf(ptr %retparam6, ptr @.str.3, i64 2, ptr %varargslots4, i64 1) + store i8 108, ptr %taddr8, align 1 + %9 = insertvalue %any undef, ptr %taddr8, 0 + %10 = insertvalue %any %9, i64 ptrtoint (ptr @"$ct.char" to i64), 1 + store %any %10, ptr %varargslots7, align 16 + %11 = call i64 @std.io.printf(ptr %retparam9, ptr @.str.4, i64 2, ptr %varargslots7, i64 1) + store i8 111, ptr %taddr11, align 1 + %12 = insertvalue %any undef, ptr %taddr11, 0 + %13 = insertvalue %any %12, i64 ptrtoint (ptr @"$ct.char" to i64), 1 + store %any %13, ptr %varargslots10, align 16 + %14 = call i64 @std.io.printf(ptr %retparam12, ptr @.str.5, i64 2, ptr %varargslots10, i64 1) + %15 = call ptr @std.io.stdout() + %16 = call i64 @std.io.File.write(ptr %retparam14, ptr %15, ptr @.str.6, i64 6) + %not_err = icmp eq i64 %16, 0 + %17 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %17, label %after_check, label %assign_optional +assign_optional: ; preds = %entry + store i64 %16, ptr %error_var, align 8 + br label %guard_block +after_check: ; preds = %entry + br label %noerr_block +guard_block: ; preds = %assign_optional + br label %voiderr +noerr_block: ; preds = %after_check + %18 = load i64, ptr %retparam14, align 8 + store i64 %18, ptr %len, align 8 + %19 = call i64 @std.io.File.write_byte(ptr %15, i8 zeroext 10) + %not_err16 = icmp eq i64 %19, 0 + %20 = call i1 @llvm.expect.i1(i1 %not_err16, i1 true) + br i1 %20, label %after_check18, label %assign_optional17 +assign_optional17: ; preds = %noerr_block + store i64 %19, ptr %error_var15, align 8 + br label %guard_block19 +after_check18: ; preds = %noerr_block + br label %noerr_block20 +guard_block19: ; preds = %assign_optional17 + br label %voiderr +noerr_block20: ; preds = %after_check18 + %21 = call i64 @std.io.File.flush(ptr %15) + %not_err22 = icmp eq i64 %21, 0 + %22 = call i1 @llvm.expect.i1(i1 %not_err22, i1 true) + br i1 %22, label %after_check24, label %assign_optional23 +assign_optional23: ; preds = %noerr_block20 + store i64 %21, ptr %error_var21, align 8 + br label %guard_block25 +after_check24: ; preds = %noerr_block20 + br label %noerr_block26 +guard_block25: ; preds = %assign_optional23 + br label %voiderr +noerr_block26: ; preds = %after_check24 + %23 = load i64, ptr %len, align 8 + %add = add i64 %23, 1 + br label %voiderr +voiderr: ; preds = %noerr_block26, %guard_block25, %guard_block19, %guard_block + ret void +} + +define void @test.main() #0 { +entry: + %varargslots = alloca [1 x %any], align 16 + %taddr = alloca i8, align 1 + %retparam = alloca i64, align 8 + %varargslots1 = alloca [1 x %any], align 16 + %taddr2 = alloca i8, align 1 + %retparam3 = alloca i64, align 8 + %varargslots4 = alloca [1 x %any], align 16 + %taddr5 = alloca i8, align 1 + %retparam6 = alloca i64, align 8 + %varargslots7 = alloca [1 x %any], align 16 + %taddr8 = alloca i8, align 1 + %retparam9 = alloca i64, align 8 + %varargslots10 = alloca [1 x %any], align 16 + %taddr11 = alloca i8, align 1 + %retparam12 = alloca i64, align 8 + %len = alloca i64, align 8 + %error_var = alloca i64, align 8 + %retparam14 = alloca i64, align 8 + %error_var15 = alloca i64, align 8 + %error_var21 = alloca i64, align 8 + %len27 = alloca i64, align 8 + %error_var28 = alloca i64, align 8 + %retparam30 = alloca i64, align 8 + %error_var36 = alloca i64, align 8 + %error_var42 = alloca i64, align 8 + store i8 72, ptr %taddr, align 1 + %0 = insertvalue %any undef, ptr %taddr, 0 + %1 = insertvalue %any %0, i64 ptrtoint (ptr @"$ct.char" to i64), 1 + store %any %1, ptr %varargslots, align 16 + %2 = call i64 @std.io.printf(ptr %retparam, ptr @.str.7, i64 2, ptr %varargslots, i64 1) + store i8 101, ptr %taddr2, align 1 + %3 = insertvalue %any undef, ptr %taddr2, 0 + %4 = insertvalue %any %3, i64 ptrtoint (ptr @"$ct.char" to i64), 1 + store %any %4, ptr %varargslots1, align 16 + %5 = call i64 @std.io.printf(ptr %retparam3, ptr @.str.8, i64 2, ptr %varargslots1, i64 1) + store i8 108, ptr %taddr5, align 1 + %6 = insertvalue %any undef, ptr %taddr5, 0 + %7 = insertvalue %any %6, i64 ptrtoint (ptr @"$ct.char" to i64), 1 + store %any %7, ptr %varargslots4, align 16 + %8 = call i64 @std.io.printf(ptr %retparam6, ptr @.str.9, i64 2, ptr %varargslots4, i64 1) + store i8 108, ptr %taddr8, align 1 + %9 = insertvalue %any undef, ptr %taddr8, 0 + %10 = insertvalue %any %9, i64 ptrtoint (ptr @"$ct.char" to i64), 1 + store %any %10, ptr %varargslots7, align 16 + %11 = call i64 @std.io.printf(ptr %retparam9, ptr @.str.10, i64 2, ptr %varargslots7, i64 1) + store i8 111, ptr %taddr11, align 1 + %12 = insertvalue %any undef, ptr %taddr11, 0 + %13 = insertvalue %any %12, i64 ptrtoint (ptr @"$ct.char" to i64), 1 + store %any %13, ptr %varargslots10, align 16 + %14 = call i64 @std.io.printf(ptr %retparam12, ptr @.str.11, i64 2, ptr %varargslots10, i64 1) + %15 = call ptr @std.io.stdout() + %16 = call i64 @std.io.File.write(ptr %retparam14, ptr %15, ptr null, i64 0) + %not_err = icmp eq i64 %16, 0 + %17 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %17, label %after_check, label %assign_optional +assign_optional: ; preds = %entry + store i64 %16, ptr %error_var, align 8 + br label %guard_block +after_check: ; preds = %entry + br label %noerr_block +guard_block: ; preds = %assign_optional + br label %voiderr +noerr_block: ; preds = %after_check + %18 = load i64, ptr %retparam14, align 8 + store i64 %18, ptr %len, align 8 + %19 = call i64 @std.io.File.write_byte(ptr %15, i8 zeroext 10) + %not_err16 = icmp eq i64 %19, 0 + %20 = call i1 @llvm.expect.i1(i1 %not_err16, i1 true) + br i1 %20, label %after_check18, label %assign_optional17 +assign_optional17: ; preds = %noerr_block + store i64 %19, ptr %error_var15, align 8 + br label %guard_block19 +after_check18: ; preds = %noerr_block + br label %noerr_block20 +guard_block19: ; preds = %assign_optional17 + br label %voiderr +noerr_block20: ; preds = %after_check18 + %21 = call i64 @std.io.File.flush(ptr %15) + %not_err22 = icmp eq i64 %21, 0 + %22 = call i1 @llvm.expect.i1(i1 %not_err22, i1 true) + br i1 %22, label %after_check24, label %assign_optional23 +assign_optional23: ; preds = %noerr_block20 + store i64 %21, ptr %error_var21, align 8 + br label %guard_block25 +after_check24: ; preds = %noerr_block20 + br label %noerr_block26 +guard_block25: ; preds = %assign_optional23 + br label %voiderr +noerr_block26: ; preds = %after_check24 + %23 = load i64, ptr %len, align 8 + %add = add i64 %23, 1 + br label %voiderr +voiderr: ; preds = %noerr_block26, %guard_block25, %guard_block19, %guard_block + %24 = call ptr @std.io.stdout() + %25 = call i64 @std.io.File.write(ptr %retparam30, ptr %24, ptr @.str.12, i64 6) + %not_err31 = icmp eq i64 %25, 0 + %26 = call i1 @llvm.expect.i1(i1 %not_err31, i1 true) + br i1 %26, label %after_check33, label %assign_optional32 +assign_optional32: ; preds = %voiderr + store i64 %25, ptr %error_var28, align 8 + br label %guard_block34 +after_check33: ; preds = %voiderr + br label %noerr_block35 +guard_block34: ; preds = %assign_optional32 + br label %voiderr49 +noerr_block35: ; preds = %after_check33 + %27 = load i64, ptr %retparam30, align 8 + store i64 %27, ptr %len27, align 8 + %28 = call i64 @std.io.File.write_byte(ptr %24, i8 zeroext 10) + %not_err37 = icmp eq i64 %28, 0 + %29 = call i1 @llvm.expect.i1(i1 %not_err37, i1 true) + br i1 %29, label %after_check39, label %assign_optional38 +assign_optional38: ; preds = %noerr_block35 + store i64 %28, ptr %error_var36, align 8 + br label %guard_block40 +after_check39: ; preds = %noerr_block35 + br label %noerr_block41 +guard_block40: ; preds = %assign_optional38 + br label %voiderr49 +noerr_block41: ; preds = %after_check39 + %30 = call i64 @std.io.File.flush(ptr %24) + %not_err43 = icmp eq i64 %30, 0 + %31 = call i1 @llvm.expect.i1(i1 %not_err43, i1 true) + br i1 %31, label %after_check45, label %assign_optional44 +assign_optional44: ; preds = %noerr_block41 + store i64 %30, ptr %error_var42, align 8 + br label %guard_block46 +after_check45: ; preds = %noerr_block41 + br label %noerr_block47 +guard_block46: ; preds = %assign_optional44 + br label %voiderr49 +noerr_block47: ; preds = %after_check45 + %32 = load i64, ptr %len27, align 8 + %add48 = add i64 %32, 1 + br label %voiderr49 +voiderr49: ; preds = %noerr_block47, %guard_block46, %guard_block40, %guard_block34 + ret void +} diff --git a/test/test_suite/debug_symbols/ct_foreach.c3t b/test/test_suite/debug_symbols/ct_foreach.c3t index 5cb6a032b..c9fd73633 100644 --- a/test/test_suite/debug_symbols/ct_foreach.c3t +++ b/test/test_suite/debug_symbols/ct_foreach.c3t @@ -16,15 +16,14 @@ fn void main() { /* #expect: test.ll - define void @test.foo() #0 !dbg !8 { entry: %values = alloca [1 x i32], align 4 + call void @llvm.dbg.declare(metadata ptr %values, metadata !12, metadata !DIExpression()), !dbg !17 store i32 0, ptr %values, align 4, !dbg !17 store i32 0, ptr %values, align 4, !dbg !18 ret void, !dbg !18 } - !0 = !{i32 2, !"Dwarf Version", i32 4} !1 = !{i32 2, !"Debug Info Version", i32 3} !2 = !{i32 2, !"wchar_size", i32 4} @@ -43,21 +42,15 @@ entry: !15 = !{!16} !16 = !DISubrange(count: 1, lowerBound: 0) !17 = !DILocation(line: 5, column: 10, scope: !8) -!18 = !DILocation(line: 7, column: 18, scope: !19) -!19 = distinct !DILexicalBlock(scope: !20, file: !7, line: 6, column: 3) -!20 = distinct !DILexicalBlock(scope: !8, file: !7, line: 6, column: 3) -!21 = distinct !DISubprogram(name: "main", linkageName: "test.main", scope: !7, file: !7, line: 11, type: !9, scopeLine: 11, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !6) -!22 = !DILocation(line: 12, column: 3, scope: !21) -!23 = distinct !DISubprogram(name: "_$main", linkageName: "main", scope: !7, file: !7, line: 11, type: !24, scopeLine: 11, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !6, retainedNodes: !11) -!24 = !DISubroutineType(types: !25) -!25 = !{!14, !14, !26} -!26 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "char**", baseType: !27, size: 64, align: 64, dwarfAddressSpace: 0) -!27 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "char*", baseType: !28, size: 64, align: 64, dwarfAddressSpace: 0) -!28 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_unsigned_char) -!29 = !DILocalVariable(name: ".anon", arg: 1, scope: !23, file: !7, line: 11, type: !14) -!30 = !DILocation(line: 11, column: 9, scope: !23) -!31 = !DILocalVariable(name: ".anon", arg: 2, scope: !23, file: !7, line: 11, type: !26) -!32 = !DILocation(line: 18, column: 2, scope: !33, inlinedAt: !30) -!33 = distinct !DISubprogram(name: "@main_to_void_main", linkageName: "@main_to_void_main", scope: !34, file: !34, line: 16, scopeLine: 16, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !6) -!34 = !DIFile(filename: "main_stub.c3" -!35 = !DILocation(line: 19, column: 9, scope: !33, inlinedAt: !30) +!18 = !DILocation(line: 7, column: 18, scope: !8) +!19 = distinct !DISubprogram(name: "main", linkageName: "test.main", scope: !7, file: !7, line: 11, type: !9, scopeLine: 11, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !6) +!20 = !DILocation(line: 12, column: 3, scope: !19) +!21 = distinct !DISubprogram(name: "_$main", linkageName: "main", scope: !7, file: !7, line: 11, type: !22, scopeLine: 11, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !6, retainedNodes: !11) +!22 = !DISubroutineType(types: !23) +!23 = !{!14, !14, !24} +!24 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "char**", baseType: !25, size: 64, align: 64, dwarfAddressSpace: 0) +!25 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "char*", baseType: !26, size: 64, align: 64, dwarfAddressSpace: 0) +!26 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_unsigned_char) +!27 = !DILocalVariable(name: ".anon", arg: 1, scope: !21, file: !7, line: 11, type: !14) +!28 = !DILocation(line: 11, column: 9, scope: !21) +!29 = !DILocalVariable(name: ".anon", arg: 2, scope: !21, file: !7, line: 11, type: !24)