From 0120498ec88955d3ae04eaa456932b66b2b9e67c Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 22 Aug 2023 21:34:54 +0200 Subject: [PATCH] Fix seeder, update with dynamic interface for random. Insert unreachable after panic in asserts. Macro ensure static check. --- lib/std/core/builtin.c3 | 2 +- lib/std/math/math.random.c3 | 4 + lib/std/math/random/math.seeder.c3 | 2 +- lib/std/math/random/math.simple_random.c3 | 8 +- src/compiler/compiler_internal.h | 150 +++++++++--------- src/compiler/enums.h | 2 + src/compiler/llvm_codegen_stmt.c | 2 +- src/compiler/sema_expr.c | 30 ++-- src/compiler/sema_internal.h | 2 +- src/compiler/sema_stmts.c | 78 ++++++++- src/compiler/semantic_analyser.c | 3 + src/version.h | 2 +- test/test_suite/assert/assertf.c3t | 9 +- .../contracts/macro_ensure_static.c3 | 16 ++ 14 files changed, 203 insertions(+), 107 deletions(-) create mode 100644 test/test_suite/contracts/macro_ensure_static.c3 diff --git a/lib/std/core/builtin.c3 b/lib/std/core/builtin.c3 index ba3d86d90..774f6b41a 100644 --- a/lib/std/core/builtin.c3 +++ b/lib/std/core/builtin.c3 @@ -157,7 +157,7 @@ macro void unsupported(String string = "Unsupported function invoked") @builtin * @param $Type "the type to cast to" * * @require $sizeof(expr) == $Type.sizeof "Cannot bitcast between types of different size." - * @ensure @typeis(result, $Type) + * @ensure @typeis(return, $Type) **/ macro bitcast(expr, $Type) @builtin { diff --git a/lib/std/math/math.random.c3 b/lib/std/math/math.random.c3 index 85c09e5fa..5de44cd5c 100644 --- a/lib/std/math/math.random.c3 +++ b/lib/std/math/math.random.c3 @@ -6,6 +6,10 @@ struct Random void* state; } +fn uint any.next_random(void*, int bits) @interface; +fn void any.next_random_bytes(void*, char[] buffer) @interface; +fn void any.set_random_seed(void*, char[] seed) @interface; + def RandomSeedFn = fn void(Random*, char[] seed); def RandomNextBytesFn = fn void(Random*, char[] buffer); def RandomNextFn = fn uint(Random*, int bits); diff --git a/lib/std/math/random/math.seeder.c3 b/lib/std/math/random/math.seeder.c3 index e72ecfba3..743f5f502 100644 --- a/lib/std/math/random/math.seeder.c3 +++ b/lib/std/math/random/math.seeder.c3 @@ -16,7 +16,7 @@ fn char[] seeder(usz outChars, char[] input) words[..] = ODD_PHI64; // Add word at a time - for (usz i = 0; i <= input.len / 8; i++) + for (usz i = 0; i < input.len / 8; i++) { usz j = i % words.len; words[j] -= bitcast(*(char[8]*)&input[i * 8], ulong) * MUL_LCG64; diff --git a/lib/std/math/random/math.simple_random.c3 b/lib/std/math/random/math.simple_random.c3 index 1c7e57e45..122ba6dfe 100644 --- a/lib/std/math/random/math.simple_random.c3 +++ b/lib/std/math/random/math.simple_random.c3 @@ -7,8 +7,8 @@ const long SIMPLE_RANDOM_ADDEND @local = 0xB; const long SIMPLE_RANDOM_MASK @local = (1u64 << 48) - 1; RandomInterface simple_random_interface = { - .seed_fn = fn (random, seed) => ((SimpleRandom*)random.state).set_seeds(seed), - .next_fn = fn (random, bits) => ((SimpleRandom*)random.state).next(bits) + .seed_fn = fn (random, seed) => ((SimpleRandom*)random.state).set_random_seed(seed), + .next_fn = fn (random, bits) => ((SimpleRandom*)random.state).next_random(bits) }; fn Random SimpleRandom.as_random(&random) @@ -16,7 +16,7 @@ fn Random SimpleRandom.as_random(&random) return { .fns = simple_random_interface, .state = random }; } -fn uint SimpleRandom.next(&r, int bits) +fn uint SimpleRandom.next_random(&r, int bits) @dynamic { ulong nextseed = ((ulong)*r * SIMPLE_RANDOM_MULTIPLIER + SIMPLE_RANDOM_ADDEND) & SIMPLE_RANDOM_MASK; *r = (SimpleRandom)nextseed; @@ -27,7 +27,7 @@ fn void SimpleRandom.set_seed(&r, ulong seed) { *r = (SimpleRandom)((seed ^ SIMPLE_RANDOM_MULTIPLIER) & SIMPLE_RANDOM_MASK); } -fn void SimpleRandom.set_seeds(&r, char[] seed) +fn void SimpleRandom.set_random_seed(&r, char[] seed) @dynamic { char[8] full; foreach (i, c : seed) diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 9024e511a..4b58ef9e2 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -702,31 +702,30 @@ typedef struct Decl_ Decl **methods; union { - // Unions, Struct use strukt - StructDecl strukt; + BitStructDecl bitstruct; // Enums and Fault EnumDecl enums; DistinctDecl distinct_decl; - BitStructDecl bitstruct; + // Unions, Struct use strukt + StructDecl strukt; }; }; - IncludeDecl include; - ExecDecl exec_decl; - Decl** body_params; - ImportDecl import; - VarDecl var; - LabelDecl label; - EnumConstantDecl enum_constant; - FuncDecl func_decl; - Signature fntype_decl; AttrDecl attr_decl; - Decl** decls; - TypedefDecl typedef_decl; - DefineDecl define_decl; + Decl** body_params; Ast *ct_assert_decl; Ast *ct_echo_decl; Decl** ct_else_decl; - + Decl** decls; + DefineDecl define_decl; + EnumConstantDecl enum_constant; + ExecDecl exec_decl; + Signature fntype_decl; + FuncDecl func_decl; + ImportDecl import; + IncludeDecl include; + LabelDecl label; + TypedefDecl typedef_decl; + VarDecl var; }; } Decl; @@ -1141,54 +1140,54 @@ struct Expr_ ExprKind expr_kind : 8; ResolveStatus resolve_status : 4; union { - Range vasplat_expr; - ExprTypeidInfo typeid_info_expr; // 8 - ExprAnySwitch any_switch; // 32 - ExprCast cast_expr; // 12 - ExprAny any_expr; // 8 - ExprPointerOffset pointer_offset_expr; - ExprAsmArg expr_asm_arg; // 24 - OperatorOverload overload_expr; // 4 - TypeInfo *type_expr; // 8 - ExprConst const_expr; // 32 - ExprGuard rethrow_expr; // 16 - Decl *decl_expr; // 8 - Decl *lambda_expr; // 8 - ExprSliceAssign slice_assign_expr; // 8 - ExprBinary binary_expr; // 12 - ExprTernary ternary_expr; // 16 - ExprUnary unary_expr; // 16 - Expr** try_unwrap_chain_expr; // 8 - ExprTryUnwrap try_unwrap_expr; // 24 - ExprCall call_expr; // 40 - Expr *inner_expr; // 8 - ExprEmbedExpr embed_expr; // 16 - ExprBuiltinAccess builtin_access_expr; // 8 - ExprGenericIdent generic_ident_expr; - ExprCatchUnwrap catch_unwrap_expr; // 24 - ExprSubscript subscript_expr; // 12 - ExprSubscriptAssign subscript_assign_expr; ExprAccess access_expr; // 16 - ExprSwizzle swizzle_expr; - ExprDesignator designator_expr; // 16 - ExprIdentifier identifier_expr; // 24 - ExprIdentifierRaw ct_ident_expr; // 24 - ExprCtCall ct_call_expr; // 24 - ExprCtArg ct_arg_expr; - ExprIdentifierRaw hash_ident_expr; // 24 - TypeInfo *typeid_expr; // 8 + ExprAny any_expr; // 8 + ExprAnySwitch any_switch; // 32 + ExprBinary binary_expr; // 12 ExprBodyExpansion body_expansion_expr; // 24 + ExprBuiltinAccess builtin_access_expr; // 8 + ExprBuiltin builtin_expr; // 16 + ExprCall call_expr; // 40 + ExprCast cast_expr; // 12 + ExprCatchUnwrap catch_unwrap_expr; // 24 + Expr** cond_expr; // 8 + ExprConst const_expr; // 32 + ExprCtArg ct_arg_expr; + ExprCtCall ct_call_expr; // 24 + ExprIdentifierRaw ct_ident_expr; // 24 + Decl *decl_expr; // 8 + Expr** designated_init_list; // 8 + ExprDesignator designator_expr; // 16 + ExprEmbedExpr embed_expr; // 16 + Expr** exec_expr; // 8 + ExprAsmArg expr_asm_arg; // 24 + ExprFuncBlock expr_block; // 4 ExprCompoundLiteral expr_compound_literal; // 16 Expr** expression_list; // 8 - Expr** exec_expr; // 8 + ExprGenericIdent generic_ident_expr; + ExprIdentifierRaw hash_ident_expr; // 24 + ExprIdentifier identifier_expr; // 24 Expr** initializer_list; // 8 - Expr** designated_init_list; // 8 - ExprFuncBlock expr_block; // 4 + Expr *inner_expr; // 8 + Decl *lambda_expr; // 8 ExprMacroBlock macro_block; // 24 - Expr** cond_expr; // 8 - ExprBuiltin builtin_expr; // 16 - BuiltinDefine test_hook_expr; ExprMacroBody macro_body_expr; // 16; + OperatorOverload overload_expr; // 4 + ExprPointerOffset pointer_offset_expr; + ExprGuard rethrow_expr; // 16 + ExprSliceAssign slice_assign_expr; // 8 + ExprSubscriptAssign subscript_assign_expr; + ExprSubscript subscript_expr; // 12 + ExprSwizzle swizzle_expr; + ExprTernary ternary_expr; // 16 + BuiltinDefine test_hook_expr; + Expr** try_unwrap_chain_expr; // 8 + ExprTryUnwrap try_unwrap_expr; // 24 + TypeInfo *type_expr; // 8 + TypeInfo *typeid_expr; // 8 + ExprTypeidInfo typeid_info_expr; // 8 + ExprUnary unary_expr; // 16 + Range vasplat_expr; }; }; //static_assert(sizeof(ExprConst) == 32, "Not expected size"); @@ -1462,27 +1461,27 @@ typedef struct Ast_ FlowCommon flow; // Shared struct AstAsmBlock asm_block_stmt; AstAsmStmt asm_stmt; - AstCompoundStmt compound_stmt; // 12 - Decl *declare_stmt; // 8 - Decl **decls_stmt; - Expr *expr_stmt; // 8 - Decl *var_stmt; // 8 - AstReturnStmt return_stmt; // 16 - AstIfStmt if_stmt; // 32 - AstDeferStmt defer_stmt; // 8 - AstSwitchStmt switch_stmt; // 40 - AstCaseStmt case_stmt; // 32 - AstCtSwitchStmt ct_switch_stmt; // 16 - AstContinueBreakStmt contbreak_stmt;// 24 - AstNextcaseStmt nextcase_stmt; // 32 - AstForStmt for_stmt; // 32 - AstForeachStmt foreach_stmt; // 40 - AstCtIfStmt ct_if_stmt; // 24 - AstId ct_else_stmt; // 4 - AstCtForeachStmt ct_foreach_stmt; // 40 AstAssertStmt assert_stmt; // 16 + AstCaseStmt case_stmt; // 32 + AstCompoundStmt compound_stmt; // 12 + AstContinueBreakStmt contbreak_stmt;// 24 AstContractStmt contract_stmt; // 32 AstDocFault contract_fault; // 24 + AstId ct_else_stmt; // 4 + AstCtForeachStmt ct_foreach_stmt; // 40 + AstCtIfStmt ct_if_stmt; // 24 + AstCtSwitchStmt ct_switch_stmt; // 16 + Decl *declare_stmt; // 8 + Decl **decls_stmt; + AstDeferStmt defer_stmt; // 8 + Expr *expr_stmt; // 8 + AstForStmt for_stmt; // 32 + AstForeachStmt foreach_stmt; // 40 + AstIfStmt if_stmt; // 32 + AstNextcaseStmt nextcase_stmt; // 32 + AstReturnStmt return_stmt; // 16 + AstSwitchStmt switch_stmt; // 40 + Decl *var_stmt; // 8 }; } Ast; @@ -1690,6 +1689,7 @@ typedef struct SemaContext_ Ast **returns_cache; Expr **macro_varargs; Decl **macro_params; + bool macro_has_ensures; Decl** ct_locals; }; Type *rtype; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 10e48b85e..975ebaf46 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -357,6 +357,8 @@ typedef enum { SCOPE_NONE = 0, SCOPE_CHECKS = 1 << 0, + SCOPE_ENSURE = 1 << 2, + SCOPE_ENSURE_MACRO = 1 << 3, SCOPE_EXPR_BLOCK = 1 << 5, SCOPE_MACRO = 1 << 6, } ScopeFlags; diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 39c6d6bbb..c9cb47bce 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -1025,7 +1025,7 @@ static inline void llvm_emit_assert_stmt(GenContext *c, Ast *ast) } } llvm_emit_panic(c, error, loc, fmt, values); - llvm_emit_br(c, on_ok); + llvm_emit_unreachable(c); llvm_emit_block(c, on_ok); } llvm_emit_assume(c, exprptr(ast->assert_stmt.expr)); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 669e5ecb0..a573dc48a 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1931,8 +1931,10 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s AstId assert_first = 0; AstId* next = &assert_first; - if (!sema_analyse_contracts(¯o_context, docs, &next, call_expr->span)) goto EXIT_FAIL; + bool has_ensures = false; + if (!sema_analyse_contracts(¯o_context, docs, &next, call_expr->span, &has_ensures)) goto EXIT_FAIL; sema_append_contract_asserts(assert_first, body); + macro_context.macro_has_ensures = has_ensures; if (!sema_analyse_statement(¯o_context, body)) goto EXIT_FAIL; @@ -7709,21 +7711,29 @@ static inline TypeProperty type_property_by_name(const char *name) static inline bool sema_expr_analyse_retval(SemaContext *c, Expr *expr) { - if (c->active_scope.flags & SCOPE_MACRO) + if ((c->active_scope.flags & (SCOPE_ENSURE | SCOPE_ENSURE_MACRO)) == 0) { - TODO - } - expr->type = type_no_optional(c->rtype); - if (expr->type == type_void) - { - SEMA_ERROR(expr, "'return' cannot be used on void functions."); - return false; + RETURN_SEMA_ERROR(expr, "'return' as a value can only be used inside of an '@ensure'."); } Expr *return_value = c->return_expr; + bool is_macro_ensure = (c->active_scope.flags & SCOPE_ENSURE_MACRO) != 0; + if (is_macro_ensure) + { + expr->type = type_no_optional(return_value->type); + } + else + { + expr->type = type_no_optional(c->rtype); + if (expr->type == type_void) + { + SEMA_ERROR(expr, "'return' cannot be used on void functions."); + return false; + } + } assert(return_value); if (sema_flattened_expr_is_const(c, return_value)) { - expr_replace(expr, return_value); + expr_replace(expr, copy_expr_single(return_value)); } return true; } diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 74143e064..d52203053 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -46,7 +46,7 @@ unsigned sema_context_push_ct_stack(SemaContext *context); void sema_context_pop_ct_stack(SemaContext *context, unsigned old_state); bool sema_analyse_function_body(SemaContext *context, Decl *func); -bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts, SourceSpan span); +bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts, SourceSpan span, bool *has_ensures); void sema_append_contract_asserts(AstId assert_first, Ast* compound_stmt); void sema_analyse_pass_top(Module *module); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 113d7e9cc..fe73d779d 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -272,10 +272,12 @@ static void sema_unwrappable_from_catch_in_else(SemaContext *c, Expr *cond) static inline bool assert_create_from_contract(SemaContext *context, Ast *directive, AstId **asserts, SourceSpan evaluation_location) { + directive = copy_ast_single(directive); Expr *declexpr = directive->contract_stmt.contract.decl_exprs; assert(declexpr->expr_kind == EXPR_EXPRESSION_LIST); Expr **exprs = declexpr->expression_list; + VECEACH(exprs, j) { Expr *expr = exprs[j]; @@ -355,6 +357,62 @@ static inline bool sema_return_optional_check_is_valid_in_scope(SemaContext *con SEMA_ERROR(ret_expr, "This value does not match declared optional returns, it needs to be declared with the other optional returns."); return false; } + +static bool sema_analyse_macro_constant_ensures(SemaContext *context, Expr *ret_expr) +{ + assert(context->current_macro); + // This is a per return check, so we don't do it if the return expression is missing, + // or if it is optional, or – obviously - if there are no '@ensure'. + if (!ret_expr || !context->macro_has_ensures || IS_OPTIONAL(ret_expr)) return true; + + // If the return expression can't be flattened to a constant value, then + // we won't be able to do any constant ensure checks anyway, so skip. + if (!sema_flattened_expr_is_const(context, ret_expr)) return true; + + AstId doc_directive = context->current_macro->func_decl.docs; + // We store the old return_expr for retval + Expr *return_expr_old = context->return_expr; + // And set our new one. + context->return_expr = ret_expr; + bool success = true; + SCOPE_START_WITH_FLAGS(SCOPE_ENSURE_MACRO); + while (doc_directive) + { + Ast *directive = astptr(doc_directive); + doc_directive = directive->next; + if (directive->contract_stmt.kind != CONTRACT_ENSURE) continue; + Expr *checks = copy_expr_single(directive->contract_stmt.contract.decl_exprs); + assert(checks->expr_kind == EXPR_EXPRESSION_LIST); + Expr **exprs = checks->expression_list; + FOREACH_BEGIN(Expr *expr, exprs) + if (expr->expr_kind == EXPR_DECL) + { + SEMA_ERROR(expr, "Only expressions are allowed."); + success = false; + goto END; + } + if (!sema_analyse_cond_expr(context, expr)) + { + success = false; + goto END; + } + // Skipping non-const. + if (!expr_is_const(expr)) continue; + // It was ok. + assert(expr->const_expr.const_kind == CONST_BOOL); + if (expr->const_expr.b) continue; + const char *comment = directive->contract_stmt.contract.comment; + if (!comment) comment = directive->contract_stmt.contract.expr_string; + SEMA_ERROR(ret_expr, "%s", comment); + success = false; + goto END; + FOREACH_END(); + } +END: + SCOPE_END; + context->return_expr = return_expr_old; + return success; +} /** * Handle exit in a macro or in an expression block. * @param context @@ -363,6 +421,7 @@ static inline bool sema_return_optional_check_is_valid_in_scope(SemaContext *con */ static inline bool sema_analyse_block_exit_stmt(SemaContext *context, Ast *statement) { + bool is_macro = (context->active_scope.flags & SCOPE_MACRO) != 0; assert(context->active_scope.flags & (SCOPE_EXPR_BLOCK | SCOPE_MACRO)); statement->ast_kind = AST_BLOCK_EXIT_STMT; context->active_scope.jump_end = true; @@ -378,8 +437,7 @@ static inline bool sema_analyse_block_exit_stmt(SemaContext *context, Ast *state { if (!sema_analyse_expr(context, ret_expr)) return false; } - if ((context->active_scope.flags & SCOPE_MACRO) - && !sema_return_optional_check_is_valid_in_scope(context, ret_expr)) return false; + if (is_macro && !sema_return_optional_check_is_valid_in_scope(context, ret_expr)) return false; } else @@ -392,6 +450,8 @@ static inline bool sema_analyse_block_exit_stmt(SemaContext *context, Ast *state } statement->return_stmt.block_exit_ref = context->block_exit_ref; sema_inline_return_defers(context, statement, context->active_scope.defer_last, context->block_return_defer); + + if (is_macro && !sema_analyse_macro_constant_ensures(context, ret_expr)) return false; vec_add(context->returns, statement); return true; } @@ -502,7 +562,11 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement Ast *directive = astptr(doc_directive); if (directive->contract_stmt.kind == CONTRACT_ENSURE) { - if (!assert_create_from_contract(context, directive, &append_id, statement->span)) return false; + bool success; + SCOPE_START_WITH_FLAGS(SCOPE_ENSURE); + success = assert_create_from_contract(context, directive, &append_id, statement->span); + SCOPE_END; + if (!success) return false; } doc_directive = directive->next; } @@ -2936,7 +3000,7 @@ void sema_append_contract_asserts(AstId assert_first, Ast* compound_stmt) ast_prepend(&compound_stmt->compound_stmt.first_stmt, ast); } -bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts, SourceSpan call_span) +bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts, SourceSpan call_span, bool *has_ensures) { while (doc) { @@ -2959,7 +3023,7 @@ bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts, So break; case CONTRACT_ENSURE: if (!sema_analyse_ensure(context, directive)) return false; - context->call_env.ensures = true; + *has_ensures = true; break; } doc = directive->next; @@ -3071,7 +3135,9 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func) } AstId assert_first = 0; AstId *next = &assert_first; - if (!sema_analyse_contracts(context, func->func_decl.docs, &next, INVALID_SPAN)) return false; + bool has_ensures = false; + if (!sema_analyse_contracts(context, func->func_decl.docs, &next, INVALID_SPAN, &has_ensures)) return false; + context->call_env.ensures = has_ensures; if (func->func_decl.attr_naked) { AstId current = body->compound_stmt.first_stmt; diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 2f614cdfe..4f67f2b15 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -27,8 +27,11 @@ void context_change_scope_with_flags(SemaContext *context, ScopeFlags flags) bool new_label_scope = (flags & (SCOPE_EXPR_BLOCK | SCOPE_MACRO)) != 0; if (!(flags & SCOPE_EXPR_BLOCK)) { + bool is_macro = (flags & SCOPE_MACRO) != 0; flags = context->active_scope.flags | flags; + if (is_macro) flags &= ~(SCOPE_ENSURE | SCOPE_ENSURE_MACRO); } + unsigned label_start = new_label_scope ? last_local : context->active_scope.label_start; context->active_scope = (DynamicScope) { .scope_id = ++context->scope_id, diff --git a/src/version.h b/src/version.h index 9eb9084e2..c4d521bff 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.617" \ No newline at end of file +#define COMPILER_VERSION "0.4.618" \ No newline at end of file diff --git a/test/test_suite/assert/assertf.c3t b/test/test_suite/assert/assertf.c3t index 50e257336..85ce95143 100644 --- a/test/test_suite/assert/assertf.c3t +++ b/test/test_suite/assert/assertf.c3t @@ -20,17 +20,14 @@ entry: %indirectarg = alloca %"any[]", align 8 store i64 0, ptr %i, align 8 br label %loop.cond - loop.cond: ; preds = %assert_ok, %entry %0 = load i64, ptr %i, align 8 %gt = icmp ugt i64 100000000, %0 br i1 %gt, label %loop.body, label %loop.exit - loop.body: ; preds = %loop.cond %1 = load i64, ptr %i, align 8 %neq = icmp ne i64 2, %1 br i1 %neq, label %assert_ok, label %assert_fail - assert_fail: ; preds = %loop.body %2 = insertvalue %any undef, ptr %i, 0 %3 = insertvalue %any %2, i64 ptrtoint (ptr @"$ct.ulong" to i64), 1 @@ -47,9 +44,8 @@ assert_fail: ; preds = %loop.body %"#temp#" = insertvalue %"any[]" %9, i64 2, 1 store %"any[]" %"#temp#", ptr %indirectarg, align 8 call void @std.core.builtin.panicf(ptr @.panic_msg, i64 10, ptr @.file, i64 10, ptr @.func, i64 4, i32 7, ptr byval(%"any[]") align 8 %indirectarg) - br label %assert_ok - -assert_ok: ; preds = %assert_fail, %loop.body + unreachable +assert_ok: ; preds = %loop.body %10 = load i64, ptr %i, align 8 %neq1 = icmp ne i64 2, %10 call void @llvm.assume(i1 %neq1) @@ -57,7 +53,6 @@ assert_ok: ; preds = %assert_fail, %loop. %add = add i64 %11, 1 store i64 %add, ptr %i, align 8 br label %loop.cond - loop.exit: ; preds = %loop.cond ret i64 0 } diff --git a/test/test_suite/contracts/macro_ensure_static.c3 b/test/test_suite/contracts/macro_ensure_static.c3 new file mode 100644 index 000000000..0f3f92179 --- /dev/null +++ b/test/test_suite/contracts/macro_ensure_static.c3 @@ -0,0 +1,16 @@ +module debugstuff; + + +/** + * @ensure return > 0 + **/ +macro check(int a) +{ + if (a > 0) return 1; + if (a < 0) return -1; // #error: @ensure + return 100; +} +fn void! main() +{ + check(43); +} \ No newline at end of file