diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index bd72533491e..9b66b1753ad 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -7808,6 +7808,22 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 				      non_constant_p, overflow_p);
       break;
 
+    case EH_ELSE_EXPR:
+      /* Evaluate any cleanup that applies to non-EH exits, this only for
+	 the output of the diagnostics ??? what is really meant to happen
+	 at constexpr-time?...  */
+      cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_discard,
+				      non_constant_p, overflow_p);
+
+      /* The presence of a contract should not affect the constexpr.  */
+      if (CONTRACT_EH_ELSE_P (t))
+	break;
+      if (!*non_constant_p)
+	/* Also evaluate the EH handler.  */
+	cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard,
+				      non_constant_p, overflow_p);
+      break;
+
     case CLEANUP_STMT:
       r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
 					non_constant_p, overflow_p,
diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc
index 0822624a910..064f0b6a9d8 100644
--- a/gcc/cp/contracts.cc
+++ b/gcc/cp/contracts.cc
@@ -88,64 +88,7 @@ along with GCC; see the file COPYING3.  If not see
        return -v;
      }
 
-   The original decl is left alone and instead calls are generated to pre/post
-   functions within the body:
-
-     void fun.pre(int v)
-     {
-       [[ assert: v > 0 ]];
-     }
-     int fun.post(int v, int __r)
-     {
-       [[ assert: __r < 0 ]];
-       return __r;
-     }
-     int fun(int v)
-     {
-       fun.pre(v);
-       return fun.post(v, -v);
-     }
-
-   If fun returns in memory, the return value is not passed through the post
-   function; instead, the return object is initialized directly and then passed
-   to the post function by invisible reference.
-
-   This sides steps a number of issues with having to rewrite the bodies or
-   rewrite the parsed conditions as the parameters to the original function
-   changes (as happens during redeclaration). The ultimate goal is to get
-   something that optimizes well along the lines of
-
-     int fun(int v)
-     {
-       [[ assert: v > 0 ]];
-       auto &&__r = -v;
-       goto out;
-     out:
-       [[ assert: __r < 0 ]];
-       return __r;
-     }
-
-   With the idea being that multiple return statements could collapse the
-   function epilogue after inlining the pre/post functions. clang is able
-   to collapse common function epilogues, while gcc needs -O3 -Os combined.
-
-   Directly laying the pre contracts down in the function body doesn't have
-   many issues. The post contracts may need to be repeated multiple times, once
-   for each return, or a goto epilogue would need to be generated.
-   For this initial implementation, generating function calls and letting
-   later optimizations decide whether to inline and duplicate the actual
-   checks or whether to collapse the shared epilogue was chosen.
-
-   For cdtors a post contract is implemented using a CLEANUP_STMT.
-
-   FIXME the compiler already shores cleanup code on multiple exit paths, so
-   this outlining seems unnecessary if we represent the postcondition as a
-   cleanup for all functions.
-
-   More helpful for optimization might be to make the contracts a wrapper
-   function (for non-variadic functions), that could be inlined into a
-   caller while preserving the call to the actual function?  Either that or
-   mirror a never-continue post contract with an assume in the caller.  */
+  TODO: reiterate the revised implementation.  */
 
 #include "config.h"
 #include "system.h"
@@ -801,7 +744,6 @@ finish_contract_attribute (tree identifier, tree contract)
   tree attribute = build_tree_list (build_tree_list (NULL_TREE, identifier),
 				    build_tree_list (NULL_TREE, contract));
 
-
   /* Mark the attribute as dependent if the condition is dependent.
 
      TODO: I'm not sure this is strictly necessary. It's going to be marked as
@@ -1444,10 +1386,8 @@ build_contract_condition_function (tree fndecl, bool pre)
       *last = build_tree_list (NULL_TREE, value_type);
       TREE_CHAIN (*last) = void_list_node;
 
-      if (aggregate_value_p (value_type, fndecl))
-	/* If FNDECL returns in memory, don't return the value from the
-	   postcondition.  */
-	value_type = void_type_node;
+      /* The handler is a void return.  */
+      value_type = void_type_node;
     }
 
   TREE_TYPE (fn) = build_function_type (value_type, arg_types);
@@ -1882,15 +1822,12 @@ emit_preconditions (tree attr)
   return emit_contract_conditions (attr, PRECONDITION_STMT);
 }
 
-/* Emit statements for postcondition attributes.  */
+/* Emit statements for precondition attributes.  */
 
 static void
-emit_postconditions_cleanup (tree contracts)
+emit_postconditions (tree attr)
 {
-  tree stmts = push_stmt_list ();
-  emit_contract_conditions (contracts, POSTCONDITION_STMT);
-  stmts = pop_stmt_list (stmts);
-  push_cleanup (NULL_TREE, stmts, /*eh_only*/false);
+  return emit_contract_conditions (attr, POSTCONDITION_STMT);
 }
 
 /* We're compiling the pre/postcondition function CONDFN; remap any FN
@@ -1993,32 +1930,152 @@ start_function_contracts (tree decl1)
   if (!handle_contracts_p (decl1))
     return;
 
+  /* For cdtors, we evaluate the contracts check inline.  */
   if (!outline_contracts_p (decl1))
-    {
-      emit_preconditions (DECL_CONTRACTS (current_function_decl));
-      emit_postconditions_cleanup (DECL_CONTRACTS (current_function_decl));
-      return;
-    }
+    return;
 
   /* Contracts may have just been added without a chance to parse them, though
      we still need the PRE_FN available to generate a call to it.  */
   if (!DECL_PRE_FN (decl1))
     build_contract_function_decls (decl1);
 
+}
+
+/* If we have a precondition function and it's valid, call it.  */
+
+static void
+add_pre_condition_fn_call (tree fndecl)
+{
   /* If we're starting a guarded function with valid contracts, we need to
      insert a call to the pre function.  */
-  if (DECL_PRE_FN (decl1)
-      && DECL_PRE_FN (decl1) != error_mark_node)
+  gcc_checking_assert (DECL_PRE_FN (fndecl)
+      && DECL_PRE_FN (fndecl) != error_mark_node);
+
+  releasing_vec args = build_arg_list (fndecl);
+  tree call = build_call_a (DECL_PRE_FN (fndecl), args->length (),
+			    args->address ());
+  CALL_FROM_THUNK_P (call) = true;
+  finish_expr_stmt (call);
+}
+
+/* Build and add a call to the post-condition checking function, when that
+   is in use.  */
+
+static void
+add_post_condition_fn_call (tree fndecl)
+{
+  gcc_checking_assert (DECL_POST_FN (fndecl)
+      && DECL_POST_FN (fndecl) != error_mark_node);
+
+  releasing_vec args = build_arg_list (fndecl);
+  if (get_postcondition_result_parameter (fndecl))
+    vec_safe_push (args, DECL_RESULT (fndecl));
+  tree call = build_call_a (DECL_POST_FN (fndecl), args->length (),
+			    args->address ());
+  CALL_FROM_THUNK_P (call) = true;
+  finish_expr_stmt (call);
+}
+
+/* Add a call or a direct evaluation of the pre checks.  */
+
+static void
+apply_preconditions (tree fndecl)
+{
+  if (outline_contracts_p (fndecl))
+    add_pre_condition_fn_call (fndecl);
+  else
+    emit_preconditions (DECL_CONTRACTS (fndecl));
+}
+
+/* Add a call or a direct evaluation of the post checks.  */
+
+static void
+apply_postconditions (tree fndecl)
+{
+  if (outline_contracts_p (fndecl))
+    add_post_condition_fn_call (fndecl);
+  else
+    emit_postconditions (DECL_CONTRACTS (fndecl));
+}
+
+/* Add contract handling to the function in FNDECL.
+
+   When we have only pre-conditions, this simply prepends a call (or a direct
+   evaluation, for cdtors) to the existing function body.
+
+   When we have post conditions we build a try-finally block.
+   If the function might throw then the handler in the try-finally is an
+   EH_ELSE expression, where the post condition check is applied to the
+   non-exceptional path, and a rethrow is applied to the EH path.  If the
+   function has a non-throwing eh spec, then the handler is simply the post
+   contract checker (either a call or, for cdtors, a direct evaluation).  */
+
+void
+maybe_apply_function_contracts (tree fndecl)
+{
+  if (!handle_contracts_p (fndecl))
+    /* We did nothing and the original function body statement list will be
+       popped by our caller.  */
+    return;
+
+  bool do_pre = has_active_preconditions (fndecl);
+  bool do_post = has_active_postconditions (fndecl);
+  /* We should not have reached here with nothing to do... */
+  gcc_checking_assert (do_pre || do_post);
+
+  /* This copies the approach used for function try blocks.  */
+  tree fnbody = pop_stmt_list (DECL_SAVED_TREE (fndecl));
+  DECL_SAVED_TREE (fndecl) = push_stmt_list ();
+  tree compound_stmt = begin_compound_stmt (0);
+  current_binding_level->artificial = 1;
+
+  /* FIXME : find some better location to use - perhaps the position of the
+     function opening brace, if that is available.  */
+  location_t loc = UNKNOWN_LOCATION;
+
+  /* For other cases, we call a function to process the check.  */
+
+  /* IF we hsve a pre - but not a post, then just emit that.  */
+  if (!do_post)
     {
-      releasing_vec args = build_arg_list (decl1);
-      tree call = build_call_a (DECL_PRE_FN (decl1),
-				args->length (),
-				args->address ());
-      CALL_FROM_THUNK_P (call) = true;
-      finish_expr_stmt (call);
+      apply_preconditions (fndecl);
+      add_stmt (fnbody);
+      finish_compound_stmt (compound_stmt);
+      return;
     }
+
+  tree try_fin = build_stmt (loc, TRY_FINALLY_EXPR, NULL_TREE, NULL_TREE);
+  add_stmt (try_fin);
+  TREE_OPERAND (try_fin, 0) = push_stmt_list ();
+  if (do_pre)
+    /* Add a precondition call, if we have one. */
+    apply_preconditions (fndecl);
+  add_stmt (fnbody);
+  TREE_OPERAND (try_fin, 0) = pop_stmt_list (TREE_OPERAND (try_fin, 0));
+  TREE_OPERAND (try_fin, 1) = push_stmt_list ();
+  if (!type_noexcept_p (TREE_TYPE (fndecl)))
+    {
+      tree eh_else = build_stmt (loc, EH_ELSE_EXPR, NULL_TREE, NULL_TREE);
+      /* We need to ignore this in constexpr considerations.  */
+      CONTRACT_EH_ELSE_P (eh_else) = true;
+      add_stmt (eh_else);
+      TREE_OPERAND (eh_else, 0) = push_stmt_list ();
+      apply_postconditions (fndecl);
+      TREE_OPERAND (eh_else, 0) = pop_stmt_list (TREE_OPERAND (eh_else, 0));
+      TREE_OPERAND (eh_else, 1) = push_stmt_list ();
+      tree rethrow = build_throw (input_location, NULL_TREE, tf_warning_or_error);
+      suppress_warning (rethrow);
+      finish_expr_stmt (rethrow);
+      TREE_OPERAND (eh_else, 1) = pop_stmt_list (TREE_OPERAND (eh_else, 1));
+    }
+  else
+    apply_postconditions (fndecl);
+  TREE_OPERAND (try_fin, 1) = pop_stmt_list (TREE_OPERAND (try_fin, 1));
+  finish_compound_stmt (compound_stmt);
+  /* The DECL_SAVED_TREE stmt list will be popped by our caller.  */
 }
 
+
 /* Finish up the pre & post function definitions for a guarded FNDECL,
    and compile those functions all the way to assembler language output.  */
 
@@ -2074,34 +2131,6 @@ finish_function_contracts (tree fndecl)
     }
 }
 
-/* Rewrite the expression of a returned expression so that it invokes the
-   postcondition function as needed.  */
-
-tree
-apply_postcondition_to_return (tree expr)
-{
-  tree fn = current_function_decl;
-  tree post = DECL_POST_FN (fn);
-  if (!post)
-    return NULL_TREE;
-
-  /* If FN returns in memory, POST has a void return type and we call it when
-     EXPR is DECL_RESULT (fn).  If FN returns a scalar, POST has the same
-     return type and we call it when EXPR is the value being returned.  */
-  if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (post)))
-      != (expr == DECL_RESULT (fn)))
-    return NULL_TREE;
-
-  releasing_vec args = build_arg_list (fn);
-  if (get_postcondition_result_parameter (fn))
-    vec_safe_push (args, expr);
-  tree call = build_call_a (post,
-			    args->length (),
-			    args->address ());
-  CALL_FROM_THUNK_P (call) = true;
-
-  return call;
-}
 
 /* A subroutine of duplicate_decls. Diagnose issues in the redeclaration of
    guarded functions.  */
diff --git a/gcc/cp/contracts.h b/gcc/cp/contracts.h
index 3e5c30db331..c786affd0e3 100644
--- a/gcc/cp/contracts.h
+++ b/gcc/cp/contracts.h
@@ -295,8 +295,9 @@ extern void update_late_contract		(tree, tree, tree);
 extern tree splice_out_contracts		(tree);
 extern bool all_attributes_are_contracts_p	(tree);
 extern void inherit_base_contracts		(tree, tree);
-extern tree apply_postcondition_to_return	(tree);
+//extern tree apply_postcondition_to_return	(tree);
 extern void start_function_contracts		(tree);
+extern void maybe_apply_function_contracts	(tree);
 extern void finish_function_contracts		(tree);
 extern void set_contract_functions		(tree, tree, tree);
 extern tree build_contract_check		(tree);
diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 97bc211ff67..f350fc33e9b 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -4014,6 +4014,8 @@ coro_build_actor_or_destroy_function (tree orig, tree fn_type,
   DECL_USER_ALIGN (fn) = DECL_USER_ALIGN (orig);
   /* Apply attributes from the original fn.  */
   DECL_ATTRIBUTES (fn) = copy_list (DECL_ATTRIBUTES (orig));
+  /* but we do not want ones for contracts.  */
+  remove_contract_attributes (fn);
 
   /* A void return.  */
   tree resdecl = build_decl (loc, RESULT_DECL, 0, void_type_node);
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 416c60b7311..505ee5d4dc9 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -451,6 +451,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       ATOMIC_CONSTR_MAP_INSTANTIATED_P (in ATOMIC_CONSTR)
       contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
       RETURN_EXPR_LOCAL_ADDR_P (in RETURN_EXPR)
+      CONTRACT_EH_ELSE_P (in EH_ELSE_EXPR)
    1: IDENTIFIER_KIND_BIT_1 (in IDENTIFIER_NODE)
       TI_PENDING_TEMPLATE_FLAG.
       TEMPLATE_PARMS_FOR_INLINE.
@@ -723,6 +724,8 @@ typedef struct ptrmem_cst * ptrmem_cst_t;
 
 #define CLEANUP_P(NODE)		TREE_LANG_FLAG_0 (TRY_BLOCK_CHECK (NODE))
 
+#define CONTRACT_EH_ELSE_P(NODE) TREE_LANG_FLAG_0 (EH_ELSE_EXPR_CHECK (NODE))
+
 #define BIND_EXPR_TRY_BLOCK(NODE) \
   TREE_LANG_FLAG_0 (BIND_EXPR_CHECK (NODE))
 
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 03deb1493a4..f3e733a29ba 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -18599,16 +18599,19 @@ finish_function (bool inline_p)
   tree fndecl = current_function_decl;
   tree fntype, ctype = NULL_TREE;
   tree resumer = NULL_TREE, destroyer = NULL_TREE;
-  bool coro_p = flag_coroutines
-		&& !processing_template_decl
-		&& DECL_COROUTINE_P (fndecl);
-  bool coro_emit_helpers = false;
 
   /* When we get some parse errors, we can end up without a
      current_function_decl, so cope.  */
-  if (fndecl == NULL_TREE)
+  if (fndecl == NULL_TREE || fndecl == error_mark_node)
     return error_mark_node;
 
+  bool coro_p = flag_coroutines
+		&& !processing_template_decl
+		&& DECL_COROUTINE_P (fndecl);
+  bool coro_emit_helpers = false;
+  bool do_contracts = DECL_HAS_CONTRACTS_P (fndecl)
+		      && !processing_template_decl;
+
   if (!DECL_OMP_DECLARE_REDUCTION_P (fndecl))
     finish_lambda_scope ();
 
@@ -18644,6 +18647,10 @@ finish_function (bool inline_p)
 	finish_eh_spec_block (TYPE_RAISES_EXCEPTIONS
 			      (TREE_TYPE (fndecl)),
 			      current_eh_spec_block);
+
+     /* If outlining succeeded, then add contracts handling if needed.  */
+     if (coro_emit_helpers && do_contracts)
+	maybe_apply_function_contracts (fndecl);
     }
   else
   /* For a cloned function, we've already got all the code we need;
@@ -18659,6 +18666,10 @@ finish_function (bool inline_p)
 	finish_eh_spec_block (TYPE_RAISES_EXCEPTIONS
 			      (TREE_TYPE (current_function_decl)),
 			      current_eh_spec_block);
+
+     if (do_contracts)
+	maybe_apply_function_contracts (current_function_decl);
+
     }
 
   /* If we're saving up tree structure, tie off the function now.  */
@@ -18911,7 +18922,7 @@ finish_function (bool inline_p)
   --function_depth;
 
   /* Clean up.  */
-  current_function_decl = NULL_TREE;
+  gcc_checking_assert (current_function_decl == NULL_TREE);
 
   invoke_plugin_callbacks (PLUGIN_FINISH_PARSE_FUNCTION, fndecl);
 
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 5970ac3d398..4434ba6e2f4 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -11416,13 +11416,7 @@ check_return_expr (tree retval, bool *no_warning, bool *dangling)
 
   /* Actually copy the value returned into the appropriate location.  */
   if (retval && retval != result)
-    {
-      /* If there's a postcondition for a scalar return value, wrap
-	 retval in a call to the postcondition function.  */
-      if (tree post = apply_postcondition_to_return (retval))
-	retval = post;
-      retval = cp_build_init_expr (result, retval);
-    }
+    retval = cp_build_init_expr (result, retval);
 
   if (current_function_return_value == bare_retval)
     INIT_EXPR_NRV_P (retval) = true;
@@ -11430,11 +11424,6 @@ check_return_expr (tree retval, bool *no_warning, bool *dangling)
   if (tree set = maybe_set_retval_sentinel ())
     retval = build2 (COMPOUND_EXPR, void_type_node, retval, set);
 
-  /* If there's a postcondition for an aggregate return value, call the
-     postcondition function after the return object is initialized.  */
-  if (tree post = apply_postcondition_to_return (result))
-    retval = build2 (COMPOUND_EXPR, void_type_node, retval, post);
-
   return retval;
 }
 
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index 622c51d5c3f..43bd4c56d7f 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -227,6 +227,7 @@ struct gimplify_ctx
   unsigned keep_stack : 1;
   unsigned save_stack : 1;
   unsigned in_switch_expr : 1;
+  unsigned in_handler_expr : 1;
 };
 
 enum gimplify_defaultmap_kind
@@ -18401,10 +18402,12 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 	    input_location = UNKNOWN_LOCATION;
 	    eval = cleanup = NULL;
 	    gimplify_and_add (TREE_OPERAND (*expr_p, 0), &eval);
+	    bool save_in_handler_expr = gimplify_ctxp->in_handler_expr;
 	    if (TREE_CODE (*expr_p) == TRY_FINALLY_EXPR
 		&& TREE_CODE (TREE_OPERAND (*expr_p, 1)) == EH_ELSE_EXPR)
 	      {
 		gimple_seq n = NULL, e = NULL;
+		gimplify_ctxp->in_handler_expr = true;
 		gimplify_and_add (TREE_OPERAND (TREE_OPERAND (*expr_p, 1),
 						0), &n);
 		gimplify_and_add (TREE_OPERAND (TREE_OPERAND (*expr_p, 1),
@@ -18416,7 +18419,11 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 		  }
 	      }
 	    else
-	      gimplify_and_add (TREE_OPERAND (*expr_p, 1), &cleanup);
+	      {
+		gimplify_ctxp->in_handler_expr = true;
+		gimplify_and_add (TREE_OPERAND (*expr_p, 1), &cleanup);
+	      }
+	    gimplify_ctxp->in_handler_expr = save_in_handler_expr;
 	    /* Don't create bogus GIMPLE_TRY with empty cleanup.  */
 	    if (gimple_seq_empty_p (cleanup))
 	      {
@@ -18516,6 +18523,10 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 	  /* When within an OMP context, notice uses of variables.  */
 	  if (gimplify_omp_ctxp)
 	    omp_notice_variable (gimplify_omp_ctxp, *expr_p, true);
+	  /* Handlers can refer to the function result; if that has been
+	     moved, we need to track it.  */
+	  if (gimplify_ctxp->in_handler_expr && gimplify_ctxp->return_temp)
+	    *expr_p = gimplify_ctxp->return_temp;
 	  ret = GS_ALL_DONE;
 	  break;
 
diff --git a/gcc/testsuite/g++.dg/contracts/pr115434.C b/gcc/testsuite/g++.dg/contracts/pr115434.C
new file mode 100644
index 00000000000..e9c847f8969
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/pr115434.C
@@ -0,0 +1,16 @@
+// We were failing to apply post conditions to void functions.
+
+// { dg-do run }
+// { dg-options "-std=c++20 -fcontracts -fcontract-continuation-mode=on" }
+
+
+void foo (const int b)
+[[ post: b == 9 ]]  // contract not checked
+{}
+
+int main()
+{
+   foo(3);
+}
+
+// { dg-output "contract violation in function foo at .*.C:8: b == 9.*(\n|\r\n|\r)" }
diff --git a/gcc/testsuite/g++.dg/coroutines/pr110871.C b/gcc/testsuite/g++.dg/coroutines/pr110871.C
new file mode 100644
index 00000000000..8a667c856ae
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr110871.C
@@ -0,0 +1,62 @@
+// { dg-additional-options "-fcontracts -fcontract-continuation-mode=on" }
+// { dg-do run }
+#include <iostream>
+#include <coroutine>
+
+// In order to test the contract violation diagnostic, we have to set
+// -fcontract-continuation-mode=on; this means that the code will emit
+// the message below - but before that the contract should have been checked.
+void process(int from, int to)
+{
+  if (from > to)
+    std::cout << "would have been a disaster!" << std::endl;
+}
+
+template <typename T>
+struct generator
+{
+    struct promise_type
+    {
+        template <typename... Args>
+        promise_type(Args&&... args) {
+            std::cout << "promise init" << std::endl;
+            process(args...);
+        }
+
+        std::suspend_always yield_value(T) { return {}; }
+
+        std::suspend_always initial_suspend() const noexcept { return {}; }
+        std::suspend_never final_suspend() const noexcept { return {}; }
+        void unhandled_exception() noexcept {}
+
+        generator<T> get_return_object() noexcept { return {}; }
+    };
+};
+
+namespace std {
+template <typename T, typename... Args>
+struct coroutine_traits<generator<T>, Args...>
+{
+    using promise_type = typename generator<T>::promise_type;
+};
+
+};
+
+generator<int> seq(int from, int to) [[pre: from <= to]]
+
+{
+    std::cout << "coro initial" << std::endl;
+    for (int i = from; i <= to; ++i) {
+        co_yield i;
+        std::cout << "coro resumed" << std::endl;
+    }
+}
+
+int main() { 
+    std::cout << "main initial" << std::endl;
+    generator<int> s = seq(10, 5);
+    (void)s;
+    std::cout << "main continues" << std::endl;
+}
+
+// { dg-output "contract violation in function seq at .*.C:45: from \<= to.*(\n|\r\n|\r)" }
diff --git a/gcc/testsuite/g++.dg/coroutines/pr110872.C b/gcc/testsuite/g++.dg/coroutines/pr110872.C
new file mode 100644
index 00000000000..a809986f296
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr110872.C
@@ -0,0 +1,49 @@
+// { dg-additional-options "-fcontracts -fcontract-continuation-mode=on" }
+// { dg-do run }
+
+#include <iostream>
+#include <coroutine>
+
+
+template <typename T>
+struct generator
+{
+    struct promise_type
+    {
+        std::suspend_always yield_value(T) { return {}; }
+
+        std::suspend_always initial_suspend() const noexcept { return {}; }
+        std::suspend_never final_suspend() const noexcept { return {}; }
+        void unhandled_exception() noexcept {}
+
+        generator<T> get_return_object() noexcept { return {}; }
+    };
+
+    bool is_valid() { return false; }
+};
+
+namespace std {
+template <typename T, typename... Args>
+struct coroutine_traits<generator<T>, Args...>
+{
+    using promise_type = typename generator<T>::promise_type;
+};
+
+};
+
+generator<int> val(int v) 
+[[post g: g.is_valid()]]
+{
+    std::cout << "coro initial" << std::endl;
+    co_yield v;
+    std::cout << "coro resumed" << std::endl;
+}
+
+int main() { 
+    std::cout << "main initial" << std::endl;
+    generator<int> s = val(1);
+    (void)s;
+    std::cout << "main continues" << std::endl;
+}
+
+// { dg-output "contract violation in function val at .*.C:35: g.is_valid().*(\n|\r\n|\r)" }
