]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: contracts and -fimplicit-constexpr
authorJason Merrill <jason@redhat.com>
Thu, 5 Mar 2026 22:54:30 +0000 (17:54 -0500)
committerJason Merrill <jason@redhat.com>
Thu, 19 Mar 2026 04:48:14 +0000 (00:48 -0400)
Several of the contracts tests were failing with -fimplicit-constexpr.  Some
of this was just needing to handle some additional tree codes, but there
were more significant issues with the handling of failed contracts in
speculative (i.e. non-manifestly-constant-evaluated) constant evaluation,
leading to additional errors and/or emitting a constant value despite its
evaluation involving a failed contract.

I've addressed this by making non-MCE evaluation silently fail (and so fall
back to run-time evaluation) if we encountered a contract that is not known
to be satisfied.  We could consider an optional warning if we encounter a
predicate that evaluates to false, but that seems like a follow-on
enhancement.

gcc/cp/ChangeLog:

* constexpr.cc (build_constexpr_constructor_member_initializers):
Handle TRY_FINALLY_EXPR.
(potential_constant_expression_1): Handle EH_ELSE_EXPR.
(check_for_failed_contracts): Handle non-MCE case.
(cxx_eval_outermost_constant_expr): Call it sooner.

gcc/cp/constexpr.cc

index 4325a49e9aecb78c40984fb85ba30c0ff04720d5..9c32c13ae64930ca7830f0124a9079fe5c2f0a79 100644 (file)
@@ -635,6 +635,7 @@ build_constexpr_constructor_member_initializers (tree type, tree body)
       {
       case MUST_NOT_THROW_EXPR:
       case EH_SPEC_BLOCK:
+      case TRY_FINALLY_EXPR: // For C++26 postconditions.
        body = TREE_OPERAND (body, 0);
        break;
 
@@ -10741,8 +10742,9 @@ mark_non_constant (tree t)
    a failed or non-constant contract that would invalidate this.  */
 
 static bool
-check_for_failed_contracts (constexpr_global_ctx *global_ctx)
+check_for_failed_contracts (constexpr_ctx *ctx)
 {
+  constexpr_global_ctx *const global_ctx = ctx->global;
   if (!flag_contracts || !global_ctx->contract_statement)
     return false;
 
@@ -10751,7 +10753,12 @@ check_for_failed_contracts (constexpr_global_ctx *global_ctx)
   bool error = false;
   /* [intro.compliance.general]/2.3.4. */
   /* [basic.contract.eval]/8. */
-  if (contract_terminating_p (global_ctx->contract_statement))
+  if (ctx->manifestly_const_eval != mce_true)
+    {
+      /* When !MCE, silently return not constant.  */
+      return true;
+    }
+  else if (contract_terminating_p (global_ctx->contract_statement))
     {
       kind = diagnostics::kind::error;
       error = true;
@@ -11124,6 +11131,19 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
 
   if (non_constant_p && !allow_non_constant)
     return error_mark_node;
+  else if (check_for_failed_contracts (&ctx))
+    {
+      if (manifestly_const_eval == mce_true)
+       /* If MCE, we gave a hard error and return error_mark_node.  */
+       return error_mark_node;
+      else
+       {
+         /* Otherwise treat it as non-constant so the violation is still
+            detectable at run-time.  */
+         gcc_checking_assert (allow_non_constant);
+         return t;
+       }
+    }
   else if (non_constant_p && TREE_CONSTANT (r))
     r = mark_non_constant (r);
   else if (non_constant_p)
@@ -11131,10 +11151,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
 
   if (constexpr_dtor)
     {
-      if (check_for_failed_contracts (&global_ctx))
-       r = mark_non_constant (r);
-      else
-        DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = true;
+      DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = true;
       return r;
     }
 
@@ -11184,8 +11201,6 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
   if (location_t loc = EXPR_LOCATION (t))
     protected_set_expr_location (r, loc);
 
-  if (check_for_failed_contracts (&global_ctx))
-    r = mark_non_constant (r);
   return r;
 }
 
@@ -12656,6 +12671,12 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
       return (RECUR (TREE_OPERAND (t, 0), want_rval)
              && RECUR (TREE_OPERAND (t, 1), any));
 
+    case EH_ELSE_EXPR:
+      /* maybe_apply_function_contracts uses this to check postconditions only
+        on normal return.  */
+      return (RECUR (TREE_OPERAND (t, 1), any)
+             || RECUR (TREE_OPERAND (t, 0), any));
+
     case SCOPE_REF:
       return RECUR (TREE_OPERAND (t, 1), want_rval);