From 50c1aac9bb3b8ba7644321928cb2c14f769aafbb Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Fri, 31 Jan 2025 16:19:12 +0100 Subject: [PATCH] Usage of @noreturn macro is type-checked as if it returns #1913. --- releasenotes.md | 3 +- src/compiler/llvm_codegen_expr.c | 17 +++++--- src/compiler/llvm_codegen_internal.h | 2 +- src/compiler/llvm_codegen_stmt.c | 4 +- src/compiler/sema_decls.c | 20 ++++++++-- src/compiler/sema_expr.c | 25 ++++++------ src/compiler/sema_stmts.c | 2 + test/test_suite/debug_symbols/ct_foreach.c3t | 1 - test/test_suite/debug_symbols/defer_macro.c3t | 40 +------------------ test/test_suite/errors/else_unreachable.c3t | 39 ++++++++++++++++++ .../errors/else_unreachable_in_block.c3 | 8 ++++ 11 files changed, 94 insertions(+), 67 deletions(-) create mode 100644 test/test_suite/errors/else_unreachable.c3t create mode 100644 test/test_suite/errors/else_unreachable_in_block.c3 diff --git a/releasenotes.md b/releasenotes.md index a06ac6450..bb3e78e7c 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -36,7 +36,8 @@ - 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. +- Defer is broken when placed before a $foreach #1912. +- Usage of @noreturn macro is type-checked as if it returns #1913. ### Stdlib changes - Added '%h' and '%H' for printing out binary data in hexadecimal using the formatter. diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index a0ecbb9c6..1a741be74 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -5124,7 +5124,7 @@ void llvm_add_abi_call_attributes(GenContext *c, LLVMValueRef call_value, int co } -void llvm_emit_raw_call(GenContext *c, BEValue *result_value, FunctionPrototype *prototype, LLVMTypeRef func_type, LLVMValueRef func, LLVMValueRef *args, unsigned arg_count, int inline_flag, LLVMValueRef error_var, bool sret_return, BEValue *synthetic_return_param) +void llvm_emit_raw_call(GenContext *c, BEValue *result_value, FunctionPrototype *prototype, LLVMTypeRef func_type, LLVMValueRef func, LLVMValueRef *args, unsigned arg_count, int inline_flag, LLVMValueRef error_var, bool sret_return, BEValue *synthetic_return_param, bool no_return) { ABIArgInfo *ret_info = prototype->ret_abi_info; Type *call_return_type = prototype->abi_ret_type; @@ -5134,6 +5134,10 @@ void llvm_emit_raw_call(GenContext *c, BEValue *result_value, FunctionPrototype { LLVMSetInstructionCallConv(call_value, llvm_call_convention_from_call(prototype->call_abi)); } + if (no_return) + { + llvm_attribute_add_call(c, call_value, attribute_id.noreturn, -1, 0); + } switch (inline_flag) { case -1: @@ -5145,7 +5149,6 @@ void llvm_emit_raw_call(GenContext *c, BEValue *result_value, FunctionPrototype default: break; } - ASSERT(!prototype->ret_by_ref || prototype->ret_by_ref_abi_info->kind != ABI_ARG_INDIRECT); llvm_add_abi_call_attributes(c, call_value, vec_size(prototype->param_types), prototype->abi_args); @@ -5442,6 +5445,7 @@ INLINE void llvm_emit_call_invocation(GenContext *c, BEValue *result_value, Expr **args, BEValue *values, int inline_flag, + bool no_return, LLVMValueRef func, LLVMTypeRef func_type, Expr **varargs) @@ -5583,7 +5587,7 @@ INLINE void llvm_emit_call_invocation(GenContext *c, BEValue *result_value, // 10. Create the actual call (remember to emit a loc, because we might have shifted loc emitting the params) EMIT_SPAN(c, span); - llvm_emit_raw_call(c, result_value, prototype, func_type, func, arg_values, arg_count, inline_flag, error_var, sret_return, &synthetic_return_param); + llvm_emit_raw_call(c, result_value, prototype, func_type, func, arg_values, arg_count, inline_flag, error_var, sret_return, &synthetic_return_param, no_return); // 17i. The simple case here is where there is a normal return. // In this case be_value already holds the result @@ -5711,6 +5715,7 @@ static void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr func_type = llvm_get_type(c, function_decl->type); } int inline_flag = 0; + bool no_return = expr->call_expr.no_return; if (expr->call_expr.attr_force_noinline) { inline_flag = -1; @@ -5793,7 +5798,7 @@ static void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr LLVMBasicBlockRef after = llvm_basic_block_new(c, "after_call"); FunctionPrototype *default_prototype = type_get_resolved_prototype(default_method->type); BEValue default_res; - llvm_emit_call_invocation(c, &default_res, target, expr->span, default_prototype, args, values, inline_flag, + llvm_emit_call_invocation(c, &default_res, target, expr->span, default_prototype, args, values, inline_flag, no_return, llvm_get_ref(c, default_method), llvm_get_type(c, default_method->type), varargs); @@ -5805,7 +5810,7 @@ static void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr func_type = llvm_get_type(c, dyn_fn->type); BEValue normal_res; values[0] = result; - llvm_emit_call_invocation(c, &normal_res, target, expr->span, prototype, args, values, inline_flag, func, func_type, + llvm_emit_call_invocation(c, &normal_res, target, expr->span, prototype, args, values, inline_flag, no_return, func, func_type, varargs); LLVMValueRef normal_val = llvm_load_value(c, &normal_res); LLVMBasicBlockRef normal_block = c->current_block; @@ -5830,7 +5835,7 @@ static void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr } - llvm_emit_call_invocation(c, result_value, target, expr->span, prototype, args, values, inline_flag, func, func_type, + llvm_emit_call_invocation(c, result_value, target, expr->span, prototype, args, values, inline_flag, no_return, func, func_type, varargs); } diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index febedf2f0..34c357e08 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -506,7 +506,7 @@ void llvm_emit_coerce_store(GenContext *c, LLVMValueRef addr, AlignSize alignmen LLVMValueRef llvm_emit_coerce(GenContext *c, LLVMTypeRef coerced, BEValue *value, Type *original_type); static inline LLVMCallConv llvm_call_convention_from_call(CallABI abi); -void llvm_emit_raw_call(GenContext *c, BEValue *result_value, FunctionPrototype *prototype, LLVMTypeRef func_type, LLVMValueRef func, LLVMValueRef *args, unsigned arg_count, int inline_flag, LLVMValueRef error_var, bool sret_return, BEValue *synthetic_return_param); +void llvm_emit_raw_call(GenContext *c, BEValue *result_value, FunctionPrototype *prototype, LLVMTypeRef func_type, LLVMValueRef func, LLVMValueRef *args, unsigned arg_count, int inline_flag, LLVMValueRef error_var, bool sret_return, BEValue *synthetic_return_param, bool no_return); void llvm_emit_parameter(GenContext *c, LLVMValueRef *args, unsigned *arg_count_ref, ABIArgInfo *info, BEValue *be_value, Type *type); // -- Dynamic interface -- diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 44b9ffb41..31ca72aa0 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -1582,7 +1582,7 @@ void llvm_emit_panic(GenContext *c, const char *message, SourceSpan loc, const c BEValue res; if (c->debug.builder) llvm_emit_debug_location(c, loc); llvm_emit_raw_call(c, &res, prototype, llvm_func_type(c, prototype), llvm_get_ref(c, panicf), actual_args, - count, 0, NULL, false, NULL); + count, 0, NULL, false, NULL, true); llvm_emit_unreachable(c); return; } @@ -1594,7 +1594,7 @@ void llvm_emit_panic(GenContext *c, const char *message, SourceSpan loc, const c BEValue res; if (c->debug.builder) llvm_emit_debug_location(c, loc); llvm_emit_raw_call(c, &res, prototype, llvm_func_type(c, prototype), val.value, actual_args, - count, 0, NULL, false, NULL); + count, 0, NULL, false, NULL, true); llvm_emit_unreachable(c); } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index d362bf2a4..6498a2e18 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -11,7 +11,7 @@ static inline bool sema_analyse_func_macro(SemaContext *context, Decl *decl, AttributeDomain domain, bool *erase_decl); static inline bool sema_analyse_func(SemaContext *context, Decl *decl, bool *erase_decl); static inline bool sema_analyse_macro(SemaContext *context, Decl *decl, bool *erase_decl); -static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, TypeInfo *method_parent, bool is_export, bool is_deprecated); +static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, TypeInfo *method_parent, bool is_export, bool is_deprecated, SourceSpan span); static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl); static inline bool sema_check_param_uniqueness_and_type(SemaContext *context, Decl **decls, Decl *current, unsigned current_index, unsigned count); @@ -1082,7 +1082,7 @@ ERROR: return decl_poison(decl); } -static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, TypeInfo *method_parent, bool is_export, bool is_deprecated) +static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, TypeInfo *method_parent, bool is_export, bool is_deprecated, SourceSpan span) { Variadic variadic_type = sig->variadic; Decl **params = sig->params; @@ -1100,6 +1100,10 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, is_macro ? RESOLVE_TYPE_ALLOW_INFER : RESOLVE_TYPE_DEFAULT)) return false; rtype = rtype_info->type; + if (sig->attrs.noreturn && !type_is_void(rtype)) + { + RETURN_SEMA_ERROR(rtype_info, "@noreturn cannot be used on %s not returning 'void'.", is_macro ? "macros" : "functions"); + } if (sig->attrs.nodiscard) { if (type_is_void(rtype)) @@ -1337,7 +1341,7 @@ bool sema_analyse_function_signature(SemaContext *context, Decl *func_decl, Type bool deprecated = func_decl->resolved_attributes && func_decl->attrs_resolved && func_decl->attrs_resolved->deprecated; - if (!sema_analyse_signature(context, signature, parent, func_decl->is_export, deprecated)) return false; + if (!sema_analyse_signature(context, signature, parent, func_decl->is_export, deprecated, func_decl->span)) return false; Variadic variadic_type = signature->variadic; @@ -2875,6 +2879,10 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_ decl->func_decl.signature.attrs.always_const = true; break; case ATTRIBUTE_NODISCARD: + if (decl->func_decl.signature.attrs.nodiscard) + { + RETURN_SEMA_ERROR(attr, "@nodiscard cannot be combined with @noreturn."); + } decl->func_decl.signature.attrs.nodiscard = true; break; case ATTRIBUTE_MAYDISCARD: @@ -2885,6 +2893,10 @@ static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_ decl->func_decl.attr_noinline = false; break; case ATTRIBUTE_NORETURN: + if (decl->func_decl.signature.attrs.nodiscard) + { + RETURN_SEMA_ERROR(attr, "@noreturn cannot be combined with @nodiscard."); + } decl->func_decl.signature.attrs.noreturn = true; break; case ATTRIBUTE_NOSANITIZE: @@ -3846,7 +3858,7 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl, bool *er if (!sema_analyse_signature(context, &decl->func_decl.signature, type_infoptrzero(decl->func_decl.type_parent), - false, deprecated)) return false; + false, deprecated, decl->span)) return false; if (!decl->func_decl.signature.is_at_macro && decl->func_decl.body_param && !decl->func_decl.signature.is_safemacro) { diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 2abf095f8..bf9aa15d9 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -91,7 +91,6 @@ static bool sema_expr_analyse_enum_add_sub(SemaContext *context, Expr *expr, Exp static bool sema_expr_analyse_shift(SemaContext *context, Expr *expr, Expr *left, Expr *right); static bool sema_expr_analyse_shift_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right); static bool sema_expr_analyse_and_or(SemaContext *context, Expr *expr, Expr *left, Expr *right); -static bool sema_expr_analyse_add_sub_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right); static bool sema_expr_analyse_slice_assign(SemaContext *context, Expr *expr, Type *left_type, Expr *right, bool is_unwrapped); static bool sema_expr_analyse_ct_identifier_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right); static bool sema_expr_analyse_ct_type_identifier_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right); @@ -298,8 +297,6 @@ Expr *sema_enter_inline_member(Expr *parent, CanonicalType *type) Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr, unsigned *index_ref) { unsigned args = vec_size(context->macro_varargs); - uint64_t index; - Decl *param = NULL; if (!sema_analyse_expr(context, index_expr)) return poisoned_expr; if (!type_is_integer(index_expr->type)) { @@ -954,13 +951,11 @@ static inline bool sema_expr_analyse_ternary(SemaContext *context, Type *infer_t Expr *right = exprptr(expr->ternary_expr.else_expr); if (!sema_analyse_maybe_dead_expr(context, right, path == COND_TRUE, infer_type)) return expr_poison(expr); - bool is_optional = false; Type *left_canonical = left->type->canonical; Type *right_canonical = right->type->canonical; if (left_canonical != right_canonical) { - Type *max; - max = type_find_max_type(type_no_optional(left_canonical), type_no_optional(right_canonical)); + Type *max = type_find_max_type(type_no_optional(left_canonical), type_no_optional(right_canonical)); if (!max) { SEMA_ERROR(expr, "Cannot find a common parent type of '%s' and '%s'", @@ -1039,9 +1034,6 @@ static inline bool sema_identifier_find_possible_inferred(SemaContext *context, static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to, Expr *expr) { - Decl *ambiguous_decl = NULL; - Decl *private_symbol = NULL; - ASSERT_SPAN(expr, expr && expr->unresolved_ident_expr.ident); DEBUG_LOG("Resolving identifier '%s'", expr->unresolved_ident_expr.ident); @@ -1484,7 +1476,6 @@ INLINE bool sema_set_default_argument(SemaContext *context, CalledDecl *callee, if (arg->resolve_status != RESOLVE_DONE) { SemaContext default_context; - Type *rtype = NULL; SemaContext *new_context = context_transform_for_eval(context, &default_context, param->unit); bool success; SCOPE_START @@ -1632,7 +1623,6 @@ INLINE bool sema_call_evaluate_arguments(SemaContext *context, CalledDecl *calle // 2. Loop through the parameters. bool has_named = false; - bool found_splat = false; ArrayIndex last_index = -1; Expr *last_named_arg = INVALID_PTR; Expr *last = NULL; @@ -1956,7 +1946,7 @@ static inline bool sema_call_analyse_func_invocation(SemaContext *context, Decl expr->call_expr.has_optional_arg = optional; - if (rtype != type_void) + if (!type_is_void(rtype)) { bool is_optional_return = type_is_optional(rtype); expr->call_expr.is_optional_return = is_optional_return; @@ -2413,6 +2403,10 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s } call_expr->type = type_add_optional(rtype, optional_return || has_optional_arg); + if (is_no_return && type_is_void(rtype)) + { + call_expr->type = type_wildcard; + } ASSERT_SPAN(call_expr, call_expr->type); bool must_use = false; @@ -7827,10 +7821,12 @@ static inline bool sema_expr_analyse_or_error(SemaContext *context, Expr *expr) expr_replace(expr, lhs); return true; } - SEMA_ERROR(lhs, "No optional to use '\?\?' with, please remove the '\?\?'."); + RETURN_SEMA_ERROR(lhs, "No optional to use '\?\?' with, please remove the '\?\?'."); return false; } + bool active_scope_jump = context->active_scope.jump_end; + // First we analyse the "else" and try to implictly cast. if (!sema_analyse_expr(context, rhs)) return false; @@ -7840,6 +7836,9 @@ static inline bool sema_expr_analyse_or_error(SemaContext *context, Expr *expr) return true; } + // Ignore the jump here. + context->active_scope.jump_end = active_scope_jump; + // Here we might need to insert casts. Type *else_type = rhs->type; diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 39557dd1e..60cbabbaf 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -3171,6 +3171,7 @@ bool sema_analyse_statement(SemaContext *context, Ast *statement) { if (statement->ast_kind == AST_POISONED) return false; bool dead_code = context->active_scope.jump_end; + unsigned returns = vec_size(context->returns); if (!sema_analyse_statement_inner(context, statement)) return ast_poison(statement); if (dead_code) { @@ -3187,6 +3188,7 @@ bool sema_analyse_statement(SemaContext *context, Ast *statement) } // Remove it } + vec_resize(context->returns, returns); statement->ast_kind = AST_NOP_STMT; } return true; diff --git a/test/test_suite/debug_symbols/ct_foreach.c3t b/test/test_suite/debug_symbols/ct_foreach.c3t index c9fd73633..6e099306b 100644 --- a/test/test_suite/debug_symbols/ct_foreach.c3t +++ b/test/test_suite/debug_symbols/ct_foreach.c3t @@ -19,7 +19,6 @@ fn void main() { 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 diff --git a/test/test_suite/debug_symbols/defer_macro.c3t b/test/test_suite/debug_symbols/defer_macro.c3t index 3a931e64a..e009c439d 100644 --- a/test/test_suite/debug_symbols/defer_macro.c3t +++ b/test/test_suite/debug_symbols/defer_macro.c3t @@ -145,21 +145,17 @@ entry: store i32 %1, ptr %a, align 4, !dbg !26 store i64 0, ptr %a.f, align 8, !dbg !26 br label %loop.cond, !dbg !27 - loop.cond: ; preds = %loop.body, %entry %load.err = load i64, ptr %a.f, align 8, !dbg !28 %result = icmp eq i64 %load.err, 0, !dbg !28 br i1 %result, label %loop.body, label %loop.exit, !dbg !28 - loop.body: ; preds = %loop.cond store i32 2, ptr %a, align 4, !dbg !30 store i64 0, ptr %a.f, align 8, !dbg !30 br label %loop.cond, !dbg !30 - loop.exit: ; preds = %loop.cond ret void, !dbg !30 } - ; Function Attrs: nounwind uwtable define i32 @test.main(ptr %0, i64 %1) #0 !dbg !32 { entry: @@ -268,39 +264,30 @@ noerr_block: ; preds = %after_check %not_err6 = icmp eq i64 %16, 0, !dbg !103 %17 = call i1 @llvm.expect.i1(i1 %not_err6, i1 true), !dbg !103 br i1 %17, label %after_check8, label %assign_optional7, !dbg !103 - assign_optional7: ; preds = %noerr_block store i64 %16, ptr %error_var5, align 8, !dbg !103 br label %guard_block9, !dbg !103 - after_check8: ; preds = %noerr_block br label %noerr_block10, !dbg !103 - guard_block9: ; preds = %assign_optional7 br label %voiderr, !dbg !103 - noerr_block10: ; preds = %after_check8 %18 = load ptr, ptr %out, align 8, !dbg !104 %19 = call i64 @std.io.File.flush(ptr %18), !dbg !104 %not_err12 = icmp eq i64 %19, 0, !dbg !104 %20 = call i1 @llvm.expect.i1(i1 %not_err12, i1 true), !dbg !104 br i1 %20, label %after_check14, label %assign_optional13, !dbg !104 - assign_optional13: ; preds = %noerr_block10 store i64 %19, ptr %error_var11, align 8, !dbg !104 br label %guard_block15, !dbg !104 - after_check14: ; preds = %noerr_block10 br label %noerr_block16, !dbg !104 - guard_block15: ; preds = %assign_optional13 br label %voiderr, !dbg !104 - noerr_block16: ; preds = %after_check14 %21 = load i64, ptr %len, align 8, !dbg !105 %add = add i64 %21, 1, !dbg !105 br label %voiderr, !dbg !96 - voiderr: ; preds = %noerr_block16, %guard_block15, %guard_block9, %guard_block ret ptr null, !dbg !106 } @@ -395,11 +382,9 @@ entry: %10 = load i64, ptr %size, align 8, !dbg !160 %i2nb = icmp eq i64 %10, 0, !dbg !160 br i1 %i2nb, label %if.then, label %if.exit, !dbg !160 - if.then: ; preds = %entry store ptr null, ptr %blockret11, align 8, !dbg !163 br label %expr_block.exit, !dbg !163 - if.exit: ; preds = %entry %ptradd = getelementptr inbounds i8, ptr %allocator10, i64 8, !dbg !164 %11 = load i64, ptr %ptradd, align 8, !dbg !164 @@ -407,7 +392,6 @@ if.exit: ; preds = %entry %type = load ptr, ptr %.cachedtype, align 8 %13 = icmp eq ptr %12, %type br i1 %13, label %cache_hit, label %cache_miss - cache_miss: ; preds = %if.exit %ptradd12 = getelementptr inbounds i8, ptr %12, i64 16 %14 = load ptr, ptr %ptradd12, align 8 @@ -415,21 +399,17 @@ cache_miss: ; preds = %if.exit store ptr %15, ptr %.inlinecache, align 8 store ptr %12, ptr %.cachedtype, align 8 br label %16 - cache_hit: ; preds = %if.exit %cache_hit_fn = load ptr, ptr %.inlinecache, align 8 br label %16 - 16: ; preds = %cache_hit, %cache_miss %fn_phi = phi ptr [ %cache_hit_fn, %cache_hit ], [ %15, %cache_miss ] %17 = icmp eq ptr %fn_phi, null br i1 %17, label %missing_function, label %match - missing_function: ; preds = %16 %18 = load ptr, ptr @std.core.builtin.panic, align 8, !dbg !166 - call void %18(ptr @.panic_msg, i64 44, ptr @.file, i64 16, ptr @.func, i64 6, i32 68), !dbg !166 + call void %18(ptr @.panic_msg, i64 44, ptr @.file, i64 16, ptr @.func, i64 6, i32 68) #5, !dbg !166 unreachable, !dbg !166 - match: ; preds = %16 %19 = load ptr, ptr %allocator10, align 8 %20 = load i64, ptr %size, align 8 @@ -437,16 +417,13 @@ match: ; preds = %16 %not_err = icmp eq i64 %21, 0, !dbg !166 %22 = call i1 @llvm.expect.i1(i1 %not_err, i1 true), !dbg !166 br i1 %22, label %after_check, label %assign_optional, !dbg !166 - assign_optional: ; preds = %match store i64 %21, ptr %error_var, align 8, !dbg !166 br label %panic_block, !dbg !166 - after_check: ; preds = %match %23 = load ptr, ptr %retparam, align 8, !dbg !166 store ptr %23, ptr %blockret11, align 8, !dbg !166 br label %expr_block.exit, !dbg !166 - expr_block.exit: ; preds = %after_check, %if.then %24 = load ptr, ptr %blockret11, align 8, !dbg !166 store ptr %24, ptr %taddr, align 8 @@ -457,7 +434,6 @@ expr_block.exit: ; preds = %after_check, %if.th %27 = insertvalue %"char[][]" undef, ptr %25, 0, !dbg !167 %28 = insertvalue %"char[][]" %27, i64 %size13, 1, !dbg !167 br label %noerr_block, !dbg !167 - panic_block: ; preds = %assign_optional %29 = insertvalue %any undef, ptr %error_var, 0, !dbg !167 %30 = insertvalue %any %29, i64 ptrtoint (ptr @"$ct.anyfault" to i64), 1, !dbg !167 @@ -467,19 +443,16 @@ panic_block: ; preds = %assign_optional store %"any[]" %"$$temp", ptr %indirectarg, align 8 call void @std.core.builtin.panicf(ptr @.panic_msg.1 unreachable, !dbg !154 - noerr_block: ; preds = %expr_block.exit store %"char[][]" %28, ptr %list5, align 8, !dbg !154 !170 store i32 0, ptr %i, align 4, !dbg !171 br label %loop.cond, !dbg !171 - loop.cond: ; preds = %loop.exit, %noerr_block %32 = load i32, ptr %i, align 4, !dbg !172 %33 = load i32, ptr %argc2, align 4, !dbg !173 %lt = icmp slt i32 %32, %33, !dbg !172 br i1 %lt, label %loop.body, label %loop.exit26, !dbg !172 - loop.body: ; preds = %loop.cond !176 %34 = load ptr, ptr %argv3, align 8, !dbg !177 @@ -500,7 +473,6 @@ loop.body: ; preds = %loop.cond !187 store i64 0, ptr %len18, align 8, !dbg !189 br label %loop.cond19, !dbg !190 - loop.cond19: ; preds = %loop.body21, %loop.body %41 = load ptr, ptr %ptr, align 8, !dbg !191 %42 = load i64, ptr %len18, align 8, !dbg !193 @@ -508,13 +480,11 @@ loop.cond19: ; preds = %loop.body21, %loop. %43 = load i8, ptr %ptradd20, align 1, !dbg !193 %i2b = icmp ne i8 %43, 0, !dbg !193 br i1 %i2b, label %loop.body21, label %loop.exit, !dbg !193 - loop.body21: ; preds = %loop.cond19 %44 = load i64, ptr %len18, align 8, !dbg !194 %add22 = add i64 %44, 1, !dbg !194 store i64 %add22, ptr %len18, align 8, !dbg !194 br label %loop.cond19, !dbg !194 - loop.exit: ; preds = %loop.cond19 %45 = load i64, ptr %len18, align 8, !dbg !195 %add23 = add i64 0, %45, !dbg !195 @@ -526,7 +496,6 @@ loop.exit: ; preds = %loop.cond19 %add25 = add i32 %48, 1, !dbg !196 store i32 %add25, ptr %i, align 4, !dbg !196 br label %loop.cond, !dbg !196 - loop.exit26: ; preds = %loop.cond call void @llvm.memcpy.p0.p0.i32(ptr align 8 %list, ptr align 8 %list5, i32 16, i1 false), !dbg !197 %lo = load ptr, ptr %list, align 8, !dbg !198 @@ -537,7 +506,6 @@ loop.exit26: ; preds = %loop.cond %50 = load ptr, ptr %list, align 8, !dbg !200 call void @std.core.mem.free(ptr %50) br label %expr_block.exit28, !dbg !202 - expr_block.exit28: ; preds = %loop.exit26 %51 = load i32, ptr %blockret, align 4, !dbg !202 ret i32 %51, !dbg !202 @@ -561,25 +529,20 @@ declare void @arena_scratch_end(ptr, i64) #0 define weak ptr @.dyn_search(ptr %0, ptr %1) unnamed_addr { entry: br label %check - check: ; preds = %no_match, %entry %2 = phi ptr [ %0, %entry ], [ %9, %no_match ] %3 = icmp eq ptr %2, null br i1 %3, label %missing_function, label %compare - missing_function: ; preds = %check ret ptr null - compare: ; preds = %check %4 = getelementptr inbounds %5 = load ptr, ptr %4, align 8 %6 = icmp eq ptr %5, %1 br i1 %6, label %match, label %no_match - match: ; preds = %compare %7 = load ptr, ptr %2, align 8 ret ptr %7 - no_match: ; preds = %compare %8 = getelementptr inbounds %9 = load ptr, ptr %8, align 8 @@ -587,7 +550,6 @@ no_match: ; preds = %compare } !llvm.dbg.cu = !{!6} - !0 = !{i32 2, !"Dwarf Version", i32 4} !1 = !{i32 2, !"Debug Info Version", i32 3} !2 = !{i32 2, !"wchar_size", i32 4} diff --git a/test/test_suite/errors/else_unreachable.c3t b/test/test_suite/errors/else_unreachable.c3t new file mode 100644 index 000000000..6e9cb3199 --- /dev/null +++ b/test/test_suite/errors/else_unreachable.c3t @@ -0,0 +1,39 @@ +// #target: macos-x64 +module test; +import std; +// Issue #1913 +fn void main() +{ + char[] bytes = file::load_temp("config.json") ?? abort("Unable to open config.json file"); + int hello; +} + +/* #expect: test.ll + +define void @test.main() #0 { +entry: + %bytes = alloca %"char[]", align 8 + %retparam = alloca %"char[]", align 8 + %blockret = alloca %"char[]", align 8 + %indirectarg = alloca %"any[]", align 8 + %hello = alloca i32, align 4 + %0 = call i64 @std.io.file.load_temp(ptr %retparam, ptr @.str, i64 11) + %not_err = icmp eq i64 %0, 0 + %1 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %1, label %after_check, label %else_block + +after_check: ; preds = %entry + %2 = load %"char[]", ptr %retparam, align 8 + br label %phi_block + +else_block: ; preds = %entry + store %"any[]" zeroinitializer, ptr %indirectarg, align 8 + call void @std.core.builtin.panicf(ptr @.str.1, i64 31, ptr @.str.2, i64 19, ptr @.str.3, i64 4, i32 6, ptr byval(%"any[]") align 8 %indirectarg) + call void @llvm.trap() + unreachable + +phi_block: ; preds = %after_check + store %"char[]" %2, ptr %bytes, align 8 + store i32 0, ptr %hello, align 4 + ret void +} diff --git a/test/test_suite/errors/else_unreachable_in_block.c3 b/test/test_suite/errors/else_unreachable_in_block.c3 new file mode 100644 index 000000000..1d8c1a2c4 --- /dev/null +++ b/test/test_suite/errors/else_unreachable_in_block.c3 @@ -0,0 +1,8 @@ +module test; +import std; +fn void main() +{ + char[] bytes = file::load_temp("config.json") ?? {| abort("Unable to open config.json file"); // #error: Cannot find a common type for 'char[]' and 'void' + return {}; |}; // #warning: This code will never execute. + char[] bytes2; +}