From: Jason Merrill Date: Fri, 21 Oct 2022 14:48:15 +0000 (-0400) Subject: c++: share code between [[assert]] and contracts X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d7cb97b26da508121906038465609b6437121461;p=thirdparty%2Fgcc.git c++: share code between [[assert]] and contracts gcc/cp/ChangeLog: * constexpr.cc (cxx_eval_assert): Factor out from... (cxx_eval_internal_function): ...here. (cxx_eval_constant_expression): Also use it for contracts. * contracts.cc (build_contract_check): Use build_assume_call. --- diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 9617bca94ec8..b3041d9c3d3c 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see #include "stringpool.h" #include "attribs.h" #include "fold-const.h" +#include "intl.h" static bool verify_constant (tree, bool, bool *, bool *); #define VERIFY_CONSTANT(X) \ @@ -1887,7 +1888,8 @@ find_failing_clause_r (const constexpr_ctx *ctx, tree expr) e = find_failing_clause_r (ctx, TREE_OPERAND (expr, 1)); return e; } - tree e = fold_operand (expr, ctx); + tree e = contextual_conv_bool (expr, tf_none); + e = fold_operand (e, ctx); if (integer_zerop (e)) /* This is the failing clause. */ return expr; @@ -1935,6 +1937,61 @@ diagnose_failing_condition (tree bad, location_t cloc, bool show_expr_p, inform (cloc, "%qE evaluates to false", bad); } +/* Process an assert/assume of ORIG_ARG. If it's not supposed to be evaluated, + do it without changing the current evaluation state. If it evaluates to + false, complain and return false; otherwise, return true. */ + +static bool +cxx_eval_assert (const constexpr_ctx *ctx, tree arg, const char *msg, + location_t loc, bool evaluated, + bool *non_constant_p, bool *overflow_p) +{ + if (*non_constant_p) + return true; + + tree eval; + if (!evaluated) + { + if (!potential_rvalue_constant_expression (arg)) + return true; + + constexpr_ctx new_ctx = *ctx; + new_ctx.quiet = true; + bool new_non_constant_p = false, new_overflow_p = false; + /* Avoid modification of existing values. */ + modifiable_tracker ms (new_ctx.global); + eval = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue, + &new_non_constant_p, + &new_overflow_p); + } + else + eval = cxx_eval_constant_expression (ctx, arg, vc_prvalue, + non_constant_p, + overflow_p); + if (!*non_constant_p && integer_zerop (eval)) + { + if (!ctx->quiet) + { + /* See if we can find which clause was failing + (for logical AND). */ + tree bad = find_failing_clause (ctx, arg); + /* If not, or its location is unusable, fall back to the + previous location. */ + location_t cloc = cp_expr_loc_or_loc (bad, loc); + + /* Report the error. */ + auto_diagnostic_group d; + error_at (cloc, msg); + diagnose_failing_condition (bad, cloc, true, ctx); + return bad; + } + *non_constant_p = true; + return false; + } + + return true; +} + /* Evaluate a call T to a GCC internal function when possible and return the evaluated result or, under the control of CTX, give an error, set NON_CONSTANT_P, and return the unevaluated call T otherwise. */ @@ -1955,41 +2012,11 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t, return void_node; case IFN_ASSUME: - if (potential_rvalue_constant_expression (CALL_EXPR_ARG (t, 0))) - { - constexpr_ctx new_ctx = *ctx; - new_ctx.quiet = true; - tree arg = CALL_EXPR_ARG (t, 0); - bool new_non_constant_p = false, new_overflow_p = false; - /* Avoid modification of existing values. */ - modifiable_tracker ms (new_ctx.global); - arg = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue, - &new_non_constant_p, - &new_overflow_p); - if (!new_non_constant_p && !new_overflow_p && integer_zerop (arg)) - { - if (!*non_constant_p && !ctx->quiet) - { - /* See if we can find which clause was failing - (for logical AND). */ - tree bad = find_failing_clause (&new_ctx, - CALL_EXPR_ARG (t, 0)); - /* If not, or its location is unusable, fall back to the - previous location. */ - location_t cloc = cp_expr_loc_or_loc (bad, EXPR_LOCATION (t)); - - auto_diagnostic_group d; - - /* Report the error. */ - error_at (cloc, - "failed % attribute assumption"); - diagnose_failing_condition (bad, cloc, false, &new_ctx); - } - - *non_constant_p = true; - return t; - } - } + if (!cxx_eval_assert (ctx, CALL_EXPR_ARG (t, 0), + G_("failed % attribute assumption"), + EXPR_LOCATION (t), /*eval*/false, + non_constant_p, overflow_p)) + return t; return void_node; case IFN_ADD_OVERFLOW: @@ -7852,36 +7879,13 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (semantic == CCS_IGNORE) break; - tree c = CONTRACT_CONDITION (t); - if (semantic == CCS_ASSUME) - { -#if 0 - /* For an assume contract, try evaluating it without instantiating - anything. If non-constant, assume it's satisfied. */ - /* This breaks contracts-assume6.C. */ - - if (!cp_tree_defined_p (c)) - break; -#endif - - bool dummy_nc = false, dummy_ov = false; - constexpr_ctx new_ctx = *ctx; - new_ctx.quiet = true; - r = cxx_eval_constant_expression (&new_ctx, c, vc_prvalue, - &dummy_nc, &dummy_ov); - if (dummy_nc) - break; - } - else - /* Evaluate the generated check. */ - r = cxx_eval_constant_expression (ctx, c, vc_prvalue, non_constant_p, - overflow_p); - if (r == boolean_false_node) - { - if (!ctx->quiet) - error_at (EXPR_LOCATION (c), "contract predicate %qE is %qE", c, r); - *non_constant_p = true; - } + if (!cxx_eval_assert (ctx, CONTRACT_CONDITION (t), + G_("contract predicate is false in " + "constant expression"), + EXPR_LOCATION (t), checked_contract_p (semantic), + non_constant_p, overflow_p)) + *non_constant_p = true; + r = void_node; } break; diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc index 11fd93362f3a..0a1626d6b68d 100644 --- a/gcc/cp/contracts.cc +++ b/gcc/cp/contracts.cc @@ -58,8 +58,6 @@ along with GCC; see the file COPYING3. If not see Assumed contracts have a similar transformation that results the body of the if being __builtin_unreachable (); - FIXME use build_assume_call. - Parsing of pre and post contract conditions need to be deferred when the contracts are attached to a member function. The postcondition identifier cannot be used before the deduced return type of an auto function is used, @@ -1659,38 +1657,28 @@ build_contract_check (tree contract) if (condition == error_mark_node) return NULL_TREE; - /* If an assumed contract condition is not fully defined, the current method - of turning it into a compile time assumption fails and emits run time - code. We don't want that, so just turn these into NOP. */ - if (semantic == CCS_ASSUME && !cp_tree_defined_p (condition)) - return void_node; + location_t loc = EXPR_LOCATION (contract); + + if (semantic == CCS_ASSUME) + return build_assume_call (loc, condition); tree if_stmt = begin_if_stmt (); - tree cond = build_x_unary_op (EXPR_LOCATION (contract), + tree cond = build_x_unary_op (loc, TRUTH_NOT_EXPR, condition, NULL_TREE, tf_warning_or_error); finish_if_stmt_cond (cond, if_stmt); - if (semantic == CCS_ASSUME) + /* Get the continuation mode. */ + contract_continuation cmode; + switch (semantic) { - tree unreachable_fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE); - tree call = build_call_n (unreachable_fn, 0); - finish_expr_stmt (call); + case CCS_NEVER: cmode = NEVER_CONTINUE; break; + case CCS_MAYBE: cmode = MAYBE_CONTINUE; break; + default: gcc_unreachable (); } - else - { - /* Get the continuation mode. */ - contract_continuation cmode; - switch (semantic) - { - case CCS_NEVER: cmode = NEVER_CONTINUE; break; - case CCS_MAYBE: cmode = MAYBE_CONTINUE; break; - default: gcc_unreachable (); - } - build_contract_handler_call (contract, cmode); - } + build_contract_handler_call (contract, cmode); finish_then_clause (if_stmt); tree scope = IF_SCOPE (if_stmt);