mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
- Using defer catch with a (void), would cause an assertion. #2580
- Fix testcase
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
- Casting a distinct type based on a pointer to an `any` would accidentally be permitted. #2575
|
||||
- `overflow_*` vector ops now correctly return a bool vector.
|
||||
- Regression vector ABI: npot vectors would load incorrectly from pointers and other things. #2576
|
||||
- Using `defer catch` with a (void), would cause an assertion. #2580
|
||||
|
||||
### Stdlib changes
|
||||
|
||||
|
||||
@@ -1310,6 +1310,7 @@ typedef struct
|
||||
Expr *expr; // May be NULL
|
||||
AstId cleanup;
|
||||
AstId cleanup_fail;
|
||||
bool cleanup_catch;
|
||||
BlockExit** block_exit_ref; // For block exits
|
||||
} AstReturnStmt;
|
||||
|
||||
|
||||
@@ -307,9 +307,16 @@ static inline void llvm_emit_return(GenContext *c, Ast *ast)
|
||||
|
||||
static inline void llvm_emit_block_exit_return(GenContext *c, Ast *ast)
|
||||
{
|
||||
|
||||
BlockExit *exit = *ast->return_stmt.block_exit_ref;
|
||||
|
||||
// In the (void) case, we might not have a variable to handle the defer catch, if so we create it here.
|
||||
BEValue error_out_ref = { .value = NULL };
|
||||
if (!exit->block_error_var && ast->return_stmt.cleanup_catch)
|
||||
{
|
||||
error_out_ref = llvm_emit_alloca_b(c, type_fault, "defer_block_fault");
|
||||
exit->block_error_var = error_out_ref.value;
|
||||
}
|
||||
|
||||
PUSH_CATCH_VAR_BLOCK(exit->block_error_var, exit->block_optional_exit);
|
||||
|
||||
LLVMBasicBlockRef err_cleanup_block = NULL;
|
||||
@@ -337,6 +344,7 @@ static inline void llvm_emit_block_exit_return(GenContext *c, Ast *ast)
|
||||
{
|
||||
llvm_store_to_ptr_aligned(c, exit->block_return_out, &return_value, type_alloca_alignment(return_value.type));
|
||||
}
|
||||
|
||||
llvm_emit_statement_chain(c, cleanup);
|
||||
|
||||
if (err_cleanup_block)
|
||||
@@ -352,6 +360,10 @@ static inline void llvm_emit_block_exit_return(GenContext *c, Ast *ast)
|
||||
{
|
||||
llvm_emit_jmp(c, exit->block_return_exit);
|
||||
}
|
||||
if (error_out_ref.value)
|
||||
{
|
||||
exit->block_error_var = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement
|
||||
static inline bool sema_analyse_switch_stmt(SemaContext *context, Ast *statement);
|
||||
|
||||
static inline bool sema_check_return_matches_opt_returns(SemaContext *context, Expr *ret_expr);
|
||||
static inline bool sema_defer_has_try_or_catch(AstId defer_top, AstId defer_bottom);
|
||||
static inline bool sema_defer_has_try_or_catch(AstId defer_top, AstId defer_bottom, bool *has_catch_ref);
|
||||
static inline bool sema_analyse_block_exit_stmt(SemaContext *context, Ast *statement);
|
||||
static inline bool sema_analyse_defer_stmt_body(SemaContext *context, Ast *statement);
|
||||
static inline bool sema_analyse_for_cond(SemaContext *context, ExprId *cond_ref, bool *infinite);
|
||||
@@ -440,17 +440,27 @@ static inline bool assert_create_from_contract(SemaContext *context, Ast *direct
|
||||
}
|
||||
|
||||
// Check whether a defer chain contains a try or a catch.
|
||||
static inline bool sema_defer_has_try_or_catch(AstId defer_top, AstId defer_bottom)
|
||||
static inline bool sema_defer_has_try_or_catch(AstId defer_top, AstId defer_bottom, bool *has_catch_ref)
|
||||
{
|
||||
bool has_try = false;
|
||||
while (defer_bottom != defer_top)
|
||||
{
|
||||
Ast *defer = astptr(defer_top);
|
||||
if (defer->defer_stmt.is_catch || defer->defer_stmt.is_try) return true;
|
||||
if (defer->defer_stmt.is_catch)
|
||||
{
|
||||
if (has_catch_ref) *has_catch_ref = true;
|
||||
return true;
|
||||
}
|
||||
if (defer->defer_stmt.is_try)
|
||||
{
|
||||
has_try = true;
|
||||
}
|
||||
defer_top = defer->defer_stmt.prev_defer;
|
||||
}
|
||||
return false;
|
||||
return has_try;
|
||||
}
|
||||
|
||||
|
||||
// Print defers at return (from macro/block or from function)
|
||||
static inline void sema_inline_return_defers(SemaContext *context, Ast *stmt, AstId defer_bottom)
|
||||
{
|
||||
@@ -458,9 +468,11 @@ static inline void sema_inline_return_defers(SemaContext *context, Ast *stmt, As
|
||||
stmt->return_stmt.cleanup = context_get_defers(context, defer_bottom, true);
|
||||
|
||||
// If we have an optional return, then we create a cleanup_fail
|
||||
bool has_catch = false;
|
||||
if (stmt->return_stmt.expr && IS_OPTIONAL(stmt->return_stmt.expr)
|
||||
&& sema_defer_has_try_or_catch(context->active_scope.defer_last, context->block_return_defer))
|
||||
&& sema_defer_has_try_or_catch(context->active_scope.defer_last, context->block_return_defer, &has_catch))
|
||||
{
|
||||
stmt->return_stmt.cleanup_catch = has_catch;
|
||||
stmt->return_stmt.cleanup_fail = context_get_defers(context, context->block_return_defer, false);
|
||||
return;
|
||||
}
|
||||
|
||||
133
test/test_suite/defer/defer_void.c3t
Normal file
133
test/test_suite/defer/defer_void.c3t
Normal file
@@ -0,0 +1,133 @@
|
||||
// #target: macos-x64
|
||||
module test;
|
||||
import std::io;
|
||||
|
||||
fn void main()
|
||||
{
|
||||
(void)works();
|
||||
(void)also_works();
|
||||
(void)doesnt_work();
|
||||
(void)also_doesnt_work();
|
||||
}
|
||||
macro works()
|
||||
{
|
||||
defer (catch err) io::printfn("got error when using something: %s", err);
|
||||
return "hi".to_int() ?? NOT_FOUND?!;
|
||||
}
|
||||
macro also_works()
|
||||
{
|
||||
defer (catch err) io::printfn("got error when using something: %s", err);
|
||||
return NOT_FOUND?!;
|
||||
}
|
||||
macro doesnt_work()
|
||||
{
|
||||
defer (catch err) io::printfn("got error when using something: %s", err);
|
||||
return "hi".to_int() ?? NOT_FOUND?;
|
||||
}
|
||||
macro also_doesnt_work()
|
||||
{
|
||||
defer (catch err) io::printfn("got error when using something: %s", err);
|
||||
return NOT_FOUND?;
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
define void @test.main() #0 {
|
||||
entry:
|
||||
%retparam = alloca i32, align 4
|
||||
%error_var = alloca i64, align 8
|
||||
%err = alloca i64, align 8
|
||||
%varargslots = alloca [1 x %any], align 16
|
||||
%retparam1 = alloca i64, align 8
|
||||
%error_var4 = alloca i64, align 8
|
||||
%err6 = alloca i64, align 8
|
||||
%varargslots7 = alloca [1 x %any], align 16
|
||||
%retparam8 = alloca i64, align 8
|
||||
%blockret = alloca i32, align 4
|
||||
%defer_block_fault = alloca i64, align 8
|
||||
%retparam11 = alloca i32, align 4
|
||||
%err16 = alloca i64, align 8
|
||||
%varargslots17 = alloca [1 x %any], align 16
|
||||
%retparam18 = alloca i64, align 8
|
||||
%defer_block_fault22 = alloca i64, align 8
|
||||
%err24 = alloca i64, align 8
|
||||
%varargslots25 = alloca [1 x %any], align 16
|
||||
%retparam26 = alloca i64, align 8
|
||||
%0 = call i64 @String.to_int(ptr %retparam, ptr @.str, i64 2, i32 10)
|
||||
%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 i32, ptr %retparam, align 4
|
||||
br label %phi_block
|
||||
|
||||
else_block: ; preds = %entry
|
||||
store i64 ptrtoint (ptr @std.core.builtin.NOT_FOUND to i64), ptr %error_var, align 8
|
||||
br label %guard_block
|
||||
|
||||
guard_block: ; preds = %else_block
|
||||
%3 = load i64, ptr %error_var, align 8
|
||||
store i64 %3, ptr %err, align 8
|
||||
%4 = insertvalue %any undef, ptr %err, 0
|
||||
%5 = insertvalue %any %4, i64 ptrtoint (ptr @"$ct.fault" to i64), 1
|
||||
store %any %5, ptr %varargslots, align 16
|
||||
%6 = call i64 @std.io.printfn(ptr %retparam1, ptr @.str.1, i64 34, ptr %varargslots, i64 1)
|
||||
br label %phi_block
|
||||
|
||||
phi_block: ; preds = %guard_block, %after_check
|
||||
store i64 ptrtoint (ptr @std.core.builtin.NOT_FOUND to i64), ptr %error_var4, align 8
|
||||
br label %guard_block5
|
||||
|
||||
guard_block5: ; preds = %phi_block
|
||||
%7 = load i64, ptr %error_var4, align 8
|
||||
store i64 %7, ptr %err6, align 8
|
||||
%8 = insertvalue %any undef, ptr %err6, 0
|
||||
%9 = insertvalue %any %8, i64 ptrtoint (ptr @"$ct.fault" to i64), 1
|
||||
store %any %9, ptr %varargslots7, align 16
|
||||
%10 = call i64 @std.io.printfn(ptr %retparam8, ptr @.str.2, i64 34, ptr %varargslots7, i64 1)
|
||||
br label %unreachable
|
||||
|
||||
unreachable: ; preds = %guard_block5
|
||||
%11 = call i64 @String.to_int(ptr %retparam11, ptr @.str.3, i64 2, i32 10)
|
||||
%not_err12 = icmp eq i64 %11, 0
|
||||
%12 = call i1 @llvm.expect.i1(i1 %not_err12, i1 true)
|
||||
br i1 %12, label %after_check13, label %else_block14
|
||||
|
||||
after_check13: ; preds = %unreachable
|
||||
%13 = load i32, ptr %retparam11, align 4
|
||||
br label %phi_block15
|
||||
|
||||
else_block14: ; preds = %unreachable
|
||||
store i64 ptrtoint (ptr @std.core.builtin.NOT_FOUND to i64), ptr %defer_block_fault, align 8
|
||||
br label %opt_block_cleanup
|
||||
|
||||
phi_block15: ; preds = %after_check13
|
||||
store i32 %13, ptr %blockret, align 4
|
||||
br label %expr_block.exit
|
||||
|
||||
opt_block_cleanup: ; preds = %else_block14
|
||||
%14 = load i64, ptr %defer_block_fault, align 8
|
||||
store i64 %14, ptr %err16, align 8
|
||||
%15 = insertvalue %any undef, ptr %err16, 0
|
||||
%16 = insertvalue %any %15, i64 ptrtoint (ptr @"$ct.fault" to i64), 1
|
||||
store %any %16, ptr %varargslots17, align 16
|
||||
%17 = call i64 @std.io.printfn(ptr %retparam18, ptr @.str.4, i64 34, ptr %varargslots17, i64 1)
|
||||
br label %expr_block.exit
|
||||
|
||||
expr_block.exit: ; preds = %opt_block_cleanup, %phi_block15
|
||||
store i64 ptrtoint (ptr @std.core.builtin.NOT_FOUND to i64), ptr %defer_block_fault22, align 8
|
||||
br label %opt_block_cleanup23
|
||||
|
||||
opt_block_cleanup23: ; preds = %expr_block.exit
|
||||
%18 = load i64, ptr %defer_block_fault22, align 8
|
||||
store i64 %18, ptr %err24, align 8
|
||||
%19 = insertvalue %any undef, ptr %err24, 0
|
||||
%20 = insertvalue %any %19, i64 ptrtoint (ptr @"$ct.fault" to i64), 1
|
||||
store %any %20, ptr %varargslots25, align 16
|
||||
%21 = call i64 @std.io.printfn(ptr %retparam26, ptr @.str.5, i64 34, ptr %varargslots25, i64 1)
|
||||
br label %unreachable29
|
||||
|
||||
unreachable29: ; preds = %opt_block_cleanup23
|
||||
ret void
|
||||
}
|
||||
@@ -10,11 +10,6 @@ fn int x(Char8 a, Char8 b)
|
||||
|
||||
/* #expect: foo.ll
|
||||
|
||||
; ModuleID = 'foo'
|
||||
source_filename = "foo"
|
||||
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-apple-macosx10.13.0"
|
||||
|
||||
%.introspect = type { i8, i64, ptr, i64, i64, i64, [0 x i64] }
|
||||
|
||||
@"$ct.foo.Char8" = linkonce global %.introspect { i8 18, i64 ptrtoint (ptr @"$ct.siv8$char" to i64), ptr null, i64 8, i64 ptrtoint (ptr @"$ct.siv8$char" to i64), i64 0, [0 x i64] zeroinitializer }, align 8
|
||||
|
||||
Reference in New Issue
Block a user