From 3cb94a285745b4b1c6c5cfbdaabc8835e8c46fac Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 14 Mar 2023 23:25:52 +0100 Subject: [PATCH] Temporarily disable LLVM 17. Generic module contracts enabled. --- .github/workflows/main.yml | 2 +- lib/std/collections/enumset.c3 | 2 +- src/compiler/compiler_internal.h | 8 +-- src/compiler/copying.c | 20 +++--- src/compiler/enums.h | 18 ++--- src/compiler/llvm_codegen_stmt.c | 2 +- src/compiler/parse_global.c | 114 +++++++++++++++++++++---------- src/compiler/sema_decls.c | 81 +++++++++++++++++++--- src/compiler/sema_expr.c | 2 +- src/compiler/sema_internal.h | 4 +- src/compiler/sema_liveness.c | 2 +- src/compiler/sema_stmts.c | 63 ++++++++--------- src/version.h | 2 +- 13 files changed, 210 insertions(+), 110 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 75e2d6e73..e3e9897b4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -181,7 +181,7 @@ jobs: fail-fast: false matrix: build_type: [Release, Debug] - llvm_version: [15, 16, 17] + llvm_version: [15, 16] steps: - uses: actions/checkout@v3 diff --git a/lib/std/collections/enumset.c3 b/lib/std/collections/enumset.c3 index 6cc36c126..1de2d9cba 100644 --- a/lib/std/collections/enumset.c3 +++ b/lib/std/collections/enumset.c3 @@ -3,7 +3,7 @@ // a copy of which can be found in the LICENSE_STDLIB file. /** - * @require $Type.kindof == TypeKind.ENUM "Only enums maybe be used with an enumset" + * @require Enum.kindof == TypeKind.ENUM : "Only enums maybe be used with an enumset" **/ module std::collections::enumset; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 5f0ea5275..2e86db1ee 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1418,7 +1418,7 @@ typedef struct typedef struct AstDocDirective_ { SourceSpan span; - DocDirectiveKind kind : 4; + ContractKind kind : 4; union { struct @@ -1441,7 +1441,7 @@ typedef struct AstDocDirective_ const char *rest_of_line; } generic; }; -} AstDocStmt; +} AstContractStmt; typedef struct Ast_ { @@ -1472,7 +1472,7 @@ typedef struct Ast_ AstId ct_else_stmt; // 4 AstCtForeachStmt ct_foreach_stmt; // 40 AstAssertStmt assert_stmt; // 16 - AstDocStmt doc_stmt; + AstContractStmt contract; }; } Ast; @@ -1493,7 +1493,7 @@ typedef struct Module_ bool no_extprefix : 1; AnalysisStage stage : 6; - AstId docs; + AstId contracts; Decl** private_method_extensions; HTable symbols; struct CompilationUnit_ **units; diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 65848d3ae..8ad8a9da3 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -517,19 +517,19 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) UNREACHABLE } -void doc_ast_copy(CopyStruct *c, AstDocStmt *doc) +void doc_ast_copy(CopyStruct *c, AstContractStmt *doc) { switch (doc->kind) { - case DOC_DIRECTIVE_REQUIRE: - case DOC_DIRECTIVE_ENSURE: - case DOC_DIRECTIVE_CHECKED: + case CONTRACT_REQUIRE: + case CONTRACT_ENSURE: + case CONTRACT_CHECKED: MACRO_COPY_EXPR(doc->contract.decl_exprs); break; - case DOC_DIRECTIVE_PARAM: - case DOC_DIRECTIVE_ERRORS: - case DOC_DIRECTIVE_PURE: - case DOC_DIRECTIVE_UNKNOWN: + case CONTRACT_PARAM: + case CONTRACT_ERRORS: + case CONTRACT_PURE: + case CONTRACT_UNKNOWN: break; } } @@ -553,8 +553,8 @@ RETRY: case AST_DECLS_STMT: MACRO_COPY_DECL_LIST(ast->decls_stmt); break; - case AST_DOC_STMT: - doc_ast_copy(c, &source->doc_stmt); + case AST_CONTRACT: + doc_ast_copy(c, &source->contract); break; case AST_ASM_BLOCK_STMT: if (ast->asm_block_stmt.is_string) diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 426c63aaf..983f991b6 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -73,7 +73,7 @@ typedef enum AST_BLOCK_EXIT_STMT, AST_SWITCH_STMT, AST_NEXT_STMT, - AST_DOC_STMT, + AST_CONTRACT, } AstKind; @@ -167,14 +167,14 @@ typedef enum typedef enum { - DOC_DIRECTIVE_UNKNOWN, - DOC_DIRECTIVE_PURE, - DOC_DIRECTIVE_REQUIRE, - DOC_DIRECTIVE_CHECKED, - DOC_DIRECTIVE_PARAM, - DOC_DIRECTIVE_ERRORS, - DOC_DIRECTIVE_ENSURE, -} DocDirectiveKind; + CONTRACT_UNKNOWN, + CONTRACT_PURE, + CONTRACT_REQUIRE, + CONTRACT_CHECKED, + CONTRACT_PARAM, + CONTRACT_ERRORS, + CONTRACT_ENSURE, +} ContractKind; typedef enum { diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index c6680161c..1552b38c4 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -1431,7 +1431,7 @@ void llvm_emit_stmt(GenContext *c, Ast *ast) case AST_POISONED: case AST_IF_CATCH_SWITCH_STMT: case AST_FOREACH_STMT: - case AST_DOC_STMT: + case AST_CONTRACT: case AST_ASM_STMT: UNREACHABLE case AST_EXPR_STMT: diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index f45309f7d..b09f47ea5 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -370,7 +370,7 @@ bool parse_module(ParseContext *c, AstId docs) if (!context_set_module(c, path, generic_parameters)) return false; if (docs) { - AstId old_docs = c->unit->module->docs; + AstId old_docs = c->unit->module->contracts; if (old_docs) { Ast *last = ast_last(astptr(old_docs)); @@ -378,7 +378,27 @@ bool parse_module(ParseContext *c, AstId docs) } else { - c->unit->module->docs = docs; + c->unit->module->contracts = docs; + } + while (docs) + { + Ast *current = astptr(docs); + docs = current->next; + assert(current->ast_kind == AST_CONTRACT); + switch (current->contract.kind) + { + case CONTRACT_UNKNOWN: + case CONTRACT_PURE: + case CONTRACT_PARAM: + case CONTRACT_ERRORS: + case CONTRACT_ENSURE: + break; + case CONTRACT_REQUIRE: + case CONTRACT_CHECKED: + continue; + } + SEMA_ERROR(current, "Invalid constraint - only '@require' and '@checked' are valid for modules."); + return false; } } Visibility visibility = VISIBLE_PUBLIC; @@ -2562,14 +2582,16 @@ static inline bool parse_import(ParseContext *c) - -static inline bool parse_doc_contract(ParseContext *c, AstId **docs_ref, DocDirectiveKind kind) +/** + * contract ::= expression_list (':'? STRING)? + */ +static inline bool parse_doc_contract(ParseContext *c, AstId **docs_ref, ContractKind kind) { - Ast *ast = ast_new_curr(c, AST_DOC_STMT); - ast->doc_stmt.kind = kind; + Ast *ast = ast_new_curr(c, AST_CONTRACT); + ast->contract.kind = kind; const char *start = c->lexer.data.lex_start; advance(c); - ASSIGN_EXPR_OR_RET(ast->doc_stmt.contract.decl_exprs, parse_expression_list(c, kind == DOC_DIRECTIVE_CHECKED), false); + ASSIGN_EXPR_OR_RET(ast->contract.contract.decl_exprs, parse_expression_list(c, kind == CONTRACT_CHECKED), false); const char *end = start; while (*++end != '\n' && *end != '\0') end++; if (end > c->data.lex_start) end = c->data.lex_start; @@ -2577,10 +2599,10 @@ static inline bool parse_doc_contract(ParseContext *c, AstId **docs_ref, DocDire scratch_buffer_clear(); switch (kind) { - case DOC_DIRECTIVE_CHECKED: + case CONTRACT_CHECKED: scratch_buffer_append("@checked \""); break; - case DOC_DIRECTIVE_ENSURE: + case CONTRACT_ENSURE: scratch_buffer_append("@ensure \""); break; default: @@ -2589,31 +2611,43 @@ static inline bool parse_doc_contract(ParseContext *c, AstId **docs_ref, DocDire } scratch_buffer_append_len(start, end - start); scratch_buffer_append("\" violated"); + if (try_consume(c, TOKEN_COLON)) + { + if (!tok_is(c, TOKEN_STRING)) + { + sema_error_at(c->prev_span, "Expected a string after ':'"); + return false; + } + } if (tok_is(c, TOKEN_STRING)) { scratch_buffer_append(": '"); scratch_buffer_append(symstr(c)); scratch_buffer_append("'."); - ast->doc_stmt.contract.comment = scratch_buffer_copy(); + ast->contract.contract.comment = scratch_buffer_copy(); advance(c); } else { scratch_buffer_append("."); - ast->doc_stmt.contract.expr_string = scratch_buffer_copy(); + ast->contract.contract.expr_string = scratch_buffer_copy(); } **docs_ref = astid(ast); *docs_ref = &ast->next; return true; } -static inline bool parse_doc_param(ParseContext *c, AstId **docs_ref) +/** + * param_contract ::= '@param' inout_attribute? any_identifier ( (':' STRING) | STRING )? + * inout_attribute ::= '[' '&'? ('in' | 'inout' | 'out') ']' + */ +static inline bool parse_contract_param(ParseContext *c, AstId **docs_ref) { - Ast *ast = ast_new_curr(c, AST_DOC_STMT); - ast->doc_stmt.kind = DOC_DIRECTIVE_PARAM; + Ast *ast = ast_new_curr(c, AST_CONTRACT); + ast->contract.kind = CONTRACT_PARAM; advance(c); - // [&inout] [in] [out] + // [inout] [in] [out] bool is_ref = false; InOutModifier mod = PARAM_ANY; if (try_consume(c, TOKEN_LBRACKET)) @@ -2657,13 +2691,19 @@ static inline bool parse_doc_param(ParseContext *c, AstId **docs_ref) SEMA_ERROR_HERE("Expected a parameter name here."); return false; } - - ast->doc_stmt.param.name = symstr(c); - ast->doc_stmt.param.span = c->span; - ast->doc_stmt.param.modifier = mod; - ast->doc_stmt.param.by_ref = is_ref; + ast->contract.param.name = symstr(c); + ast->contract.param.span = c->span; + ast->contract.param.modifier = mod; + ast->contract.param.by_ref = is_ref; advance(c); - try_consume(c, TOKEN_STRING); + if (try_consume(c, TOKEN_COLON)) + { + CONSUME_OR_RET(TOKEN_STRING, false); + } + else + { + try_consume(c, TOKEN_STRING); + } **docs_ref = astid(ast); *docs_ref = &ast->next; return true; @@ -2672,8 +2712,8 @@ static inline bool parse_doc_param(ParseContext *c, AstId **docs_ref) static inline bool parse_doc_errors(ParseContext *c, AstId **docs_ref) { DocOptReturn *returns = NULL; - Ast *ast = ast_new_curr(c, AST_DOC_STMT); - ast->doc_stmt.kind = DOC_DIRECTIVE_ERRORS; + Ast *ast = ast_new_curr(c, AST_CONTRACT); + ast->contract.kind = CONTRACT_ERRORS; advance(c); while (1) { @@ -2694,19 +2734,19 @@ static inline bool parse_doc_errors(ParseContext *c, AstId **docs_ref) if (!try_consume(c, TOKEN_COMMA)) break; } RANGE_EXTEND_PREV(ast); - ast->doc_stmt.optreturns = returns; + ast->contract.optreturns = returns; **docs_ref = astid(ast); *docs_ref = &ast->next; return true; } -static bool parse_docs(ParseContext *c, AstId *docs_ref) +static bool parse_contracts(ParseContext *c, AstId *contracts_ref) { - *docs_ref = 0; + *contracts_ref = 0; if (!try_consume(c, TOKEN_DOCS_START)) return true; - AstId *last = docs_ref; + AstId *last = contracts_ref; uint32_t row_last_row = c->span.row; while (1) { @@ -2719,7 +2759,7 @@ static bool parse_docs(ParseContext *c, AstId *docs_ref) const char *name = symstr(c); if (name == kw_at_param) { - if (!parse_doc_param(c, &last)) return false; + if (!parse_contract_param(c, &last)) return false; break; } else if (name == kw_at_return) @@ -2737,30 +2777,30 @@ static bool parse_docs(ParseContext *c, AstId *docs_ref) } else if (name == kw_at_require) { - if (!parse_doc_contract(c, &docs_ref, DOC_DIRECTIVE_REQUIRE)) return false; + if (!parse_doc_contract(c, &contracts_ref, CONTRACT_REQUIRE)) return false; break; } else if (name == kw_at_checked) { - if (!parse_doc_contract(c, &docs_ref, DOC_DIRECTIVE_CHECKED)) return false; + if (!parse_doc_contract(c, &contracts_ref, CONTRACT_CHECKED)) return false; break; } else if (name == kw_at_ensure) { - if (!parse_doc_contract(c, &docs_ref, DOC_DIRECTIVE_ENSURE)) return false; + if (!parse_doc_contract(c, &contracts_ref, CONTRACT_ENSURE)) return false; break; } else if (name == kw_at_optreturn) { - if (!parse_doc_errors(c, &docs_ref)) return false; + if (!parse_doc_errors(c, &contracts_ref)) return false; break; } else if (name == kw_at_pure) { - Ast *ast = ast_new_curr(c, AST_DOC_STMT); - ast->doc_stmt.kind = DOC_DIRECTIVE_PURE; - *docs_ref = astid(ast); - docs_ref = &ast->next; + Ast *ast = ast_new_curr(c, AST_CONTRACT); + ast->contract.kind = CONTRACT_PURE; + *contracts_ref = astid(ast); + contracts_ref = &ast->next; advance(c); break; } @@ -2871,7 +2911,7 @@ END: Decl *parse_top_level_statement(ParseContext *c, ParseContext **c_ref) { AstId docs = 0; - if (!parse_docs(c, &docs)) return poisoned_decl; + if (!parse_contracts(c, &docs)) return poisoned_decl; Decl *decl; TokenType tok = c->tok; if (tok != TOKEN_MODULE && !c->unit->module) diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 53425a2b7..2816ea834 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -51,7 +51,8 @@ static inline bool sema_analyse_distinct(SemaContext *context, Decl *decl); static CompilationUnit *unit_copy(Module *module, CompilationUnit *unit); static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl); -static Module *module_instantiate_generic(Module *module, Path *path, Expr **params); +static Module *module_instantiate_generic(SemaContext *context, Module *module, Path *path, Expr **params, + SourceSpan from); static inline bool sema_analyse_enum_param(SemaContext *context, Decl *param, bool *has_default); static inline bool sema_analyse_enum(SemaContext *context, Decl *decl); @@ -1911,8 +1912,8 @@ static inline bool sema_analyse_doc_header(AstId doc, Decl **params, Decl **extr { Ast *directive = astptr(doc); doc = directive->next; - DocDirectiveKind directive_kind = directive->doc_stmt.kind; - if (directive_kind == DOC_DIRECTIVE_PURE) + ContractKind directive_kind = directive->contract.kind; + if (directive_kind == CONTRACT_PURE) { if (*pure_ref) { @@ -1922,8 +1923,8 @@ static inline bool sema_analyse_doc_header(AstId doc, Decl **params, Decl **extr *pure_ref = true; continue; } - if (directive_kind != DOC_DIRECTIVE_PARAM) continue; - const char *param_name = directive->doc_stmt.param.name; + if (directive_kind != CONTRACT_PARAM) continue; + const char *param_name = directive->contract.param.name; Decl *extra_param = NULL; Decl *param = NULL; VECEACH(params, j) @@ -1936,13 +1937,13 @@ static inline bool sema_analyse_doc_header(AstId doc, Decl **params, Decl **extr param = extra_params[j]; if (param->name == param_name) goto NEXT; } - SEMA_ERROR(&directive->doc_stmt.param, "There is no parameter '%s', did you misspell it?", param_name); + SEMA_ERROR(&directive->contract.param, "There is no parameter '%s', did you misspell it?", param_name); return false; NEXT:; Type *type = param->type; if (type) type = type_flatten(type); bool may_be_pointer = !type || type_is_pointer(type); - if (directive->doc_stmt.param.by_ref) + if (directive->contract.param.by_ref) { if (!may_be_pointer) { @@ -1951,7 +1952,7 @@ static inline bool sema_analyse_doc_header(AstId doc, Decl **params, Decl **extr } param->var.not_null = true; } - switch (directive->doc_stmt.param.modifier) + switch (directive->contract.param.modifier) { case PARAM_ANY: goto ADDED; @@ -2813,7 +2814,8 @@ static CompilationUnit *unit_copy(Module *module, CompilationUnit *unit) return copy; } -static Module *module_instantiate_generic(Module *module, Path *path, Expr **params) +static Module *module_instantiate_generic(SemaContext *context, Module *module, Path *path, Expr **params, + SourceSpan from) { Module *new_module = compiler_find_or_create_module(path, NULL); new_module->is_generic = false; @@ -2823,6 +2825,12 @@ static Module *module_instantiate_generic(Module *module, Path *path, Expr **par vec_add(new_module->units, unit_copy(new_module, units[i])); } CompilationUnit *first_context = new_module->units[0]; + if (module->contracts) + { + copy_begin(); + new_module->contracts = astid(copy_ast_macro(astptr(module->contracts))); + copy_end(); + } VECEACH(module->parameters, i) { const char *param_name = module->parameters[i]; @@ -2939,6 +2947,47 @@ static bool sema_append_generate_parameterized_name(SemaContext *c, Module *modu scratch_buffer_append_char(mangled ? '$' : '>'); return true; } + +static bool sema_analyse_generic_module_contracts(SemaContext *c, Module *module, SourceSpan error_span) +{ + assert(module->contracts); + AstId contract = module->contracts; + while (contract) + { + Ast *ast = astptr(contract); + contract = ast->next; + assert(ast->ast_kind == AST_CONTRACT); + SemaContext temp_context; + + assert(ast->contract.kind == CONTRACT_CHECKED || ast->contract.kind == CONTRACT_REQUIRE); + SemaContext *new_context = context_transform_for_eval(c, &temp_context, module->units[0]); + if (ast->contract.kind == CONTRACT_CHECKED) + { + if (!sema_analyse_checked(new_context, ast, error_span)) return false; + } + else + { + FOREACH_BEGIN(Expr *expr, ast->contract.contract.decl_exprs->expression_list) + int res = sema_check_comp_time_bool(new_context, expr); + if (res == -1) return false; + if (res) continue; + if (ast->contract.contract.comment) + { + sema_error_at(error_span, + "Parameter(s) would violate constraint: %s.", + ast->contract.contract.comment); + } + else + { + sema_error_at(error_span, "Parameter(s) failed validation: %s", ast->contract.contract.expr_string); + } + return false; + FOREACH_END(); + } + sema_context_destroy(&temp_context); + } + return true; +} static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) { Path *decl_path; @@ -2996,17 +3045,29 @@ static bool sema_analyse_parameterized_define(SemaContext *c, Decl *decl) TokenType ident_type = TOKEN_IDENT; const char *path_string = scratch_buffer_interned(); Module *instantiated_module = global_context_find_module(path_string); + + bool was_initiated = false; if (!instantiated_module) { + was_initiated = true; Path *path = CALLOCS(Path); path->module = path_string; path->span = module->name->span; path->len = scratch_buffer.len; - instantiated_module = module_instantiate_generic(module, path, decl->define_decl.generic_params); + instantiated_module = module_instantiate_generic(c, module, path, + decl->define_decl.generic_params, decl->span); sema_analyze_stage(instantiated_module, c->unit->module->stage - 1); } if (global_context.errors_found) return decl_poison(decl); Decl *symbol = module_find_symbol(instantiated_module, name); + if (was_initiated && instantiated_module->contracts) + { + SourceSpan error_span = extend_span_with_token(params[0]->span, params[parameter_count - 1]->span); + if (!sema_analyse_generic_module_contracts(c, instantiated_module, error_span)) + { + return decl_poison(decl); + } + } assert(symbol); unit_register_external_symbol(c->compilation_unit, symbol); switch (decl->define_decl.define_kind) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 194fae449..84693eac6 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1841,7 +1841,7 @@ 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)) return false; + if (!sema_analyse_contracts(¯o_context, docs, &next, call_expr->span)) return false; sema_append_contract_asserts(assert_first, body); if (!sema_analyse_statement(¯o_context, body)) goto EXIT_FAIL; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index c6795904c..cca84cf3d 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -47,7 +47,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); +bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts, SourceSpan span); void sema_append_contract_asserts(AstId assert_first, Ast* compound_stmt); void sema_analyse_pass_top(Module *module); @@ -84,6 +84,8 @@ bool cast_promote_vararg(Expr *arg); Type *cast_numeric_arithmetic_promotion(Type *type); void cast_to_int_to_max_bit_size(SemaContext *context, Expr *lhs, Expr *rhs, Type *left_type, Type *right_type); +bool sema_analyse_checked(SemaContext *context, Ast *directive, SourceSpan span); + INLINE bool sema_set_abi_alignment(SemaContext *context, Type *type, AlignSize *result); INLINE bool sema_set_alloca_alignment(SemaContext *context, Type *type, AlignSize *result); void sema_display_deprecated_warning_on_use(SemaContext *context, Decl *decl, SourceSpan use); diff --git a/src/compiler/sema_liveness.c b/src/compiler/sema_liveness.c index 54ee2ed02..83387a5ec 100644 --- a/src/compiler/sema_liveness.c +++ b/src/compiler/sema_liveness.c @@ -82,7 +82,7 @@ static void sema_trace_stmt_liveness(Ast *ast) case AST_CT_IF_STMT: case AST_CT_ASSERT: case AST_CT_SWITCH_STMT: - case AST_DOC_STMT: + case AST_CONTRACT: case AST_FOREACH_STMT: UNREACHABLE case AST_ASM_STMT: diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 8e3eee321..f36adfa90 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -49,7 +49,6 @@ static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *state static bool sema_analyse_require(SemaContext *context, Ast *directive, AstId **asserts); static bool sema_analyse_ensure(SemaContext *context, Ast *directive); static bool sema_analyse_errors(SemaContext *context, Ast *directive); -static bool sema_analyse_checked(SemaContext *context, Ast *directive, AstId **asserts); static inline bool sema_analyse_asm_stmt(SemaContext *context, Ast *stmt) { @@ -278,7 +277,7 @@ 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) { - Expr *declexpr = directive->doc_stmt.contract.decl_exprs; + Expr *declexpr = directive->contract.contract.decl_exprs; assert(declexpr->expr_kind == EXPR_EXPRESSION_LIST); Expr **exprs = declexpr->expression_list; @@ -292,8 +291,8 @@ static inline bool assert_create_from_contract(SemaContext *context, Ast *direct } if (!sema_analyse_cond_expr(context, expr)) return false; - const char *comment = directive->doc_stmt.contract.comment; - if (!comment) comment = directive->doc_stmt.contract.expr_string; + const char *comment = directive->contract.contract.comment; + if (!comment) comment = directive->contract.contract.expr_string; if (expr_is_const(expr)) { assert(expr->const_expr.const_kind == CONST_BOOL); @@ -482,7 +481,7 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement while (doc_directive) { Ast *directive = astptr(doc_directive); - if (directive->doc_stmt.kind == DOC_DIRECTIVE_ENSURE) + if (directive->contract.kind == CONTRACT_ENSURE) { if (!assert_create_from_contract(context, directive, &append_id, statement->span)) return false; } @@ -2689,7 +2688,7 @@ static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *state { case AST_POISONED: case AST_IF_CATCH_SWITCH_STMT: - case AST_DOC_STMT: + case AST_CONTRACT: case AST_ASM_STMT: UNREACHABLE case AST_DECLS_STMT: @@ -2766,7 +2765,7 @@ static bool sema_analyse_require(SemaContext *context, Ast *directive, AstId **a static bool sema_analyse_ensure(SemaContext *context, Ast *directive) { - Expr *declexpr = directive->doc_stmt.contract.decl_exprs; + Expr *declexpr = directive->contract.contract.decl_exprs; assert(declexpr->expr_kind == EXPR_EXPRESSION_LIST); VECEACH(declexpr->expression_list, j) @@ -2783,7 +2782,7 @@ static bool sema_analyse_ensure(SemaContext *context, Ast *directive) static bool sema_analyse_errors(SemaContext *context, Ast *directive) { - DocOptReturn *returns = directive->doc_stmt.optreturns; + DocOptReturn *returns = directive->contract.optreturns; VECEACH(returns, i) { TypeInfo *type_info = returns[i].type; @@ -2823,30 +2822,28 @@ NEXT:; return true; } -static bool sema_analyse_checked(SemaContext *context, Ast *directive, AstId **asserts) +bool sema_analyse_checked(SemaContext *context, Ast *directive, SourceSpan span) { - Expr *declexpr = directive->doc_stmt.contract.decl_exprs; + Expr *declexpr = directive->contract.contract.decl_exprs; bool success = true; bool suppress_error = global_context.suppress_errors; global_context.suppress_errors = true; + CallEnvKind eval_kind = context->call_env.kind; + context->call_env.kind = CALL_ENV_CHECKS; SCOPE_START_WITH_FLAGS(SCOPE_CHECKS) VECEACH(declexpr->cond_expr, j) { Expr *expr = declexpr->cond_expr[j]; - const char *comment = directive->doc_stmt.contract.comment; - global_context.suppress_errors = comment != NULL; - if (!sema_analyse_cond_expr(context, expr)) - { - if (comment) - { - global_context.suppress_errors = false; - SEMA_ERROR(expr, comment); - } - success = false; - goto END; - } + if (sema_analyse_expr(context, expr)) continue; + const char *comment = directive->contract.contract.comment; + global_context.suppress_errors = suppress_error; + sema_error_at(span.row == 0 ? expr->span : span, "Contraint failed: %s", + comment ? comment : directive->contract.contract.expr_string); + success = false; + goto END; } END: + context->call_env.kind = eval_kind; SCOPE_END; global_context.suppress_errors = suppress_error; return success; @@ -2861,28 +2858,28 @@ 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) +bool sema_analyse_contracts(SemaContext *context, AstId doc, AstId **asserts, SourceSpan call_span) { while (doc) { Ast *directive = astptr(doc); - switch (directive->doc_stmt.kind) + switch (directive->contract.kind) { - case DOC_DIRECTIVE_UNKNOWN: - case DOC_DIRECTIVE_PURE: + case CONTRACT_UNKNOWN: + case CONTRACT_PURE: break; - case DOC_DIRECTIVE_REQUIRE: + case CONTRACT_REQUIRE: if (!sema_analyse_require(context, directive, asserts)) return false; break; - case DOC_DIRECTIVE_CHECKED: - if (!sema_analyse_checked(context, directive, asserts)) return false; + case CONTRACT_CHECKED: + if (!sema_analyse_checked(context, directive, call_span)) return false; break; - case DOC_DIRECTIVE_PARAM: + case CONTRACT_PARAM: break; - case DOC_DIRECTIVE_ERRORS: + case CONTRACT_ERRORS: if (!sema_analyse_errors(context, directive)) return false; break; - case DOC_DIRECTIVE_ENSURE: + case CONTRACT_ENSURE: if (!sema_analyse_ensure(context, directive)) return false; context->call_env.ensures = true; break; @@ -2930,7 +2927,7 @@ 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)) return false; + if (!sema_analyse_contracts(context, func->func_decl.docs, &next, INVALID_SPAN)) return false; if (func->func_decl.attr_naked) { AstId current = body->compound_stmt.first_stmt; diff --git a/src/version.h b/src/version.h index 03d257220..177712965 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.108" \ No newline at end of file +#define COMPILER_VERSION "0.4.109" \ No newline at end of file