From: Nina Ranns <[email protected]>

In this variant of the contracts handling, we emit the contract
checks for pre and post conditions into small TU-local functions
that are then called in the relevant positions at the start of
the function and on each return edge (as a try-finally).

The rationale for adding this is that it is possible to treat
these outlined functions specially (for example, with different
(potentially fixed) optimisation settings from the code body.
Doing this can be a mechanism to work around cases where
optimisation of the contract conditions (or some function that
they might call) can lead to the elision of checks.

In order to pass parameters through to these small outlined
functions, we need similar functionality to that used for thunk
calls with respect copies of non-trivial values.  We are calling
this a "thunk-like" call, since none of the adjustments are
relevant here.

gcc/c-family/ChangeLog:

        * c.opt (fcontract-checks-outlined,
        fcontract-disable-optimized-checks): New.

gcc/cp/ChangeLog:

        * call.cc (build_call_a): Use the split out builder.
        (build_call_a_1): Split out the functionality needed
        for thunk-like calls.
        (build_over_call): Ensure that we pass the original
        function decl to build_cxx_call.
        (build_cxx_call): Use the original function decl when
        available.
        * contracts.cc (handle_contracts_p): Check that we are
        handling an original function, not an outlined check.
        (set_precondition_function, set_postcondition_function,
        get_orig_for_outlined, contracts_fixup_name,
        build_contract_condition_function,
        build_precondition_function, build_postcondition_function,
        build_contract_function_decls): New.
        (start_function_contracts): Update for the case that we
        outline the contract checks.
        (build_arg_list, build_thunk_like_call,
        add_pre_condition_fn_call,
        get_postcondition_result_parameter,
        add_post_condition_fn_call): New.
        (apply_preconditions): Allow outlined checks.
        (apply_postconditions): Likewise.
        (get_precondition_function, get_postcondition_function,
        set_contract_functions, remap_and_emit_conditions,
        finish_function_contracts): New.
        (get_src_loc_impl_ptr): Handle outlined checks.
        (build_contract_check): Likewise.
        * contracts.h (DECL_PRE_FN, DECL_POST_FN,
        DECL_IS_PRE_FN_P, DECL_IS_POST_FN_P,
        get_precondition_function, get_postcondition_function,
        get_orig_for_outlined, finish_function_contracts,
        set_contract_functions): New.
        * cp-tree.h (enum lang_contract_helper): New.
        (struct lang_decl_fn): Add contract helper enum.
        (CONTRACT_HELPER): New.
        (mangle_decl_string): New.
        * decl.cc (finish_function): Emit outlined checks when
        in use.
        * mangle.cc (mangle_decl_string): Made public.
        (write_mangled_name): Handle outlined contract checks.
        * module.cc (trees_out::fn_parms_init): Stream pre and post
        outlined checks.
        (trees_in::fn_parms_init): Reload pre and post outlined checks.
        (check_mergeable_decl): Handle pre and post outlined functions.
        (module_state_config::get_dialect): Add contracts dialect.

gcc/testsuite/ChangeLog:

        * g++.dg/contracts/cpp26/outline-checks/freefunc-noexcept-post.C: New 
test.
        * g++.dg/contracts/cpp26/outline-checks/freefunc-noexcept-pre.C: New 
test.
        * g++.dg/contracts/cpp26/outline-checks/func-noexcept-assert.C: New 
test.
        * g++.dg/contracts/cpp26/outline-checks/memberfunc-noexcept-post.C: New 
test.
        * g++.dg/contracts/cpp26/outline-checks/memberfunc-noexcept-pre.C: New 
test.

Co-Authored-by: Iain Sandoe <[email protected]>
Co-Authored-by: Ville Voutilainen <[email protected]>

Signed-off-by: Iain Sandoe <[email protected]>
---
 gcc/c-family/c.opt                            |   8 +
 gcc/cp/call.cc                                |  36 +-
 gcc/cp/contracts.cc                           | 536 +++++++++++++++++-
 gcc/cp/contracts.h                            |  26 +
 gcc/cp/cp-tree.h                              |  14 +-
 gcc/cp/decl.cc                                |   4 +
 gcc/cp/mangle.cc                              |  25 +-
 gcc/cp/module.cc                              |  30 +-
 gcc/cp/tree.cc                                |   1 +
 .../outline-checks/freefunc-noexcept-post.C   |  46 ++
 .../outline-checks/freefunc-noexcept-pre.C    |  46 ++
 .../outline-checks/func-noexcept-assert.C     |  55 ++
 .../outline-checks/memberfunc-noexcept-post.C |  49 ++
 .../outline-checks/memberfunc-noexcept-pre.C  |  49 ++
 14 files changed, 891 insertions(+), 34 deletions(-)
 create mode 100644 
gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/freefunc-noexcept-post.C
 create mode 100644 
gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/freefunc-noexcept-pre.C
 create mode 100644 
gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/func-noexcept-assert.C
 create mode 100644 
gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/memberfunc-noexcept-post.C
 create mode 100644 
gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/memberfunc-noexcept-pre.C

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 0ff9f14a459..f29f4f2d27f 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1915,6 +1915,14 @@ fcontract-evaluation-semantic=
 C++ ObjC++ Joined RejectNegative Enum(contract_semantic) 
Var(flag_contract_evaluation_semantic) Init(3)
 -fcontract-evaluation-semantic=[ignore|observe|enforce|quick_enforce]  Select 
the contract evaluation semantic (defaults to enforce).
 
+fcontract-checks-outlined
+C++ ObjC++ Var(flag_contract_checks_outlined) Init(0)
+-fcontract-checks-outlined     Build contract checks using outlined functions.
+
+fcontract-disable-optimized-checks
+C++ ObjC++ Var(flag_contract_disable_optimized_checks) Init(0)
+-fcontract-disable-optimized-checks    Disable optimisation of contract checks.
+
 fcontract-disable-check-epochs
 C++ ObjC++ Var(flag_contract_disable_check_epochs) Init(0)
 -fcontract-disable-epoch-checks        Disable insertion of epoch markers 
after each contract check.
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index f80d597b339..92e3f0bec6d 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -45,6 +45,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-type-mismatch.h"
 #include "tristate.h"
 #include "tree-pretty-print-markup.h"
+#include "contracts.h"
 
 /* The various kinds of conversion.  */
 
@@ -360,12 +361,11 @@ set_flags_from_callee (tree call)
 }
 
 tree
-build_call_a (tree function, int n, tree *argarray)
+build_call_a_1(tree function, int n, tree *argarray)
 {
   tree decl;
   tree result_type;
   tree fntype;
-  int i;
 
   function = build_addr_func (function, tf_warning_or_error);
 
@@ -383,6 +383,23 @@ build_call_a (tree function, int n, tree *argarray)
 
   decl = get_callee_fndecl (function);
 
+  require_complete_eh_spec_types (fntype, decl);
+
+  TREE_HAS_CONSTRUCTOR (function) = (decl && DECL_CONSTRUCTOR_P (decl));
+
+  return function;
+}
+
+tree
+build_call_a (tree function, int n, tree *argarray)
+{
+  int i;
+  tree decl;
+
+  function = build_call_a_1 (function, n, argarray);
+
+  decl = get_callee_fndecl (function);
+
   if (decl && !TREE_USED (decl))
     {
       /* We invoke build_call directly for several library
@@ -395,10 +412,6 @@ build_call_a (tree function, int n, tree *argarray)
       mark_used (decl);
     }
 
-  require_complete_eh_spec_types (fntype, decl);
-
-  TREE_HAS_CONSTRUCTOR (function) = (decl && DECL_CONSTRUCTOR_P (decl));
-
   /* Don't pass empty class objects by value.  This is useful
      for tags in STL, which are used to control overload resolution.
      We don't need to handle other cases of copying empty classes.  */
@@ -5300,9 +5313,7 @@ build_new_function_call (tree fn, vec<tree, va_gc> **args,
       result = error_mark_node;
     }
   else
-    {
-      result = build_over_call (cand, LOOKUP_NORMAL, complain);
-    }
+    result = build_over_call (cand, LOOKUP_NORMAL, complain);
 
   if (flag_coroutines
       && result
@@ -11020,6 +11031,7 @@ build_over_call (struct z_candidate *cand, int flags, 
tsubst_flags_t complain)
       && DECL_BUILT_IN_CLASS (fn) == BUILT_IN_NORMAL)
     maybe_warn_class_memaccess (input_location, fn, args);
 
+  tree orig_fn = fn;
   if (DECL_VINDEX (fn) && (flags & LOOKUP_NONVIRTUAL) == 0)
     {
       tree t;
@@ -11053,7 +11065,7 @@ build_over_call (struct z_candidate *cand, int flags, 
tsubst_flags_t complain)
       ADDR_EXPR_DENOTES_CALL_P (fn) = true;
     }
 
-  tree call = build_cxx_call (fn, nargs, argarray, complain|decltype_flag);
+  tree call = build_cxx_call (fn, nargs, argarray, complain|decltype_flag, 
orig_fn);
   if (call == error_mark_node)
     return call;
   if (cand->flags & LOOKUP_LIST_INIT_CTOR)
@@ -11525,7 +11537,9 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
   SET_EXPR_LOCATION (fn, loc);
 
   fndecl = get_callee_fndecl (fn);
-  if (!orig_fndecl)
+  if (!fndecl && orig_fndecl)
+    fndecl = orig_fndecl;
+  else if (!orig_fndecl)
     orig_fndecl = fndecl;
 
   /* Check that arguments to builtin functions match the expectations.  */
diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc
index 43ba7275b45..56247d775f9 100644
--- a/gcc/cp/contracts.cc
+++ b/gcc/cp/contracts.cc
@@ -332,10 +332,10 @@ handle_contracts_p (tree fndecl)
 {
   return (flag_contracts
          && !processing_template_decl
+         && (CONTRACT_HELPER (fndecl) == ldf_contract_none)
          && contract_any_active_p (fndecl));
 }
 
-
 /* For use with the tree inliner. This preserves non-mapped local variables,
    such as postcondition result variables, during remapping.  */
 
@@ -658,14 +658,255 @@ check_postconditions_in_redecl (tree olddecl, tree 
newdecl)
     }
 }
 
-void
-maybe_update_postconditions (tree fndecl)
+/* Map from FUNCTION_DECL to a FUNCTION_DECL for either the PRE_FN or POST_FN.
+   These are used to parse contract conditions and are called inside the body
+   of the guarded function.  */
+static GTY(()) hash_map<tree, tree> *decl_pre_fn;
+static GTY(()) hash_map<tree, tree> *decl_post_fn;
+
+
+/* Given a pre or post function decl (for an outlined check function) return
+   the decl for the function for which the outlined checks are being
+   performed.  */
+static GTY(()) hash_map<tree, tree> *orig_from_outlined;
+
+/* Makes PRE the precondition function for FNDECL.  */
+
+static void
+set_precondition_function (tree fndecl, tree pre)
 {
-  /* Update any postconditions and the postcondition checking function
-     as needed.  If there are postconditions, we'll use those to rewrite
-     return statements to check postconditions.  */
-  if (has_active_postconditions (fndecl))
-    rebuild_postconditions (fndecl);
+  gcc_assert (pre);
+  hash_map_maybe_create<hm_ggc> (decl_pre_fn);
+  gcc_assert (!decl_pre_fn->get (fndecl));
+  decl_pre_fn->put (fndecl, pre);
+
+  hash_map_maybe_create<hm_ggc> (orig_from_outlined);
+  gcc_assert (!orig_from_outlined->get (pre));
+  orig_from_outlined->put (pre, fndecl);
+}
+
+/* Makes POST the postcondition function for FNDECL.  */
+
+static void
+set_postcondition_function (tree fndecl, tree post)
+{
+  gcc_assert (post);
+  hash_map_maybe_create<hm_ggc> (decl_post_fn);
+  gcc_assert (!decl_post_fn->get (fndecl));
+  decl_post_fn->put (fndecl, post);
+
+  hash_map_maybe_create<hm_ggc> (orig_from_outlined);
+  gcc_assert (!orig_from_outlined->get (post));
+  orig_from_outlined->put (post, fndecl);
+}
+
+/* For a given pre or post condition function, find the checked function. */
+tree
+get_orig_for_outlined (tree fndecl)
+{
+  gcc_checking_assert (fndecl);
+  tree *result = hash_map_safe_get (orig_from_outlined, fndecl);
+  return result ? *result : NULL_TREE;
+}
+
+/* For a given function decl name identifier, return identifier representing
+ the name of the contracts check. Using the same identifier is not possible
+ with functions with special meaning names (i.e. main and cdtors). For
+ consistency reasons we use the same naming convention for all contract check
+ functions.
+ PRE specifies if we need an identifier for a pre or post contract check.
+ CDTOR specifies if the checked function is a cdtor.  */
+static tree
+contracts_fixup_name (tree idin, bool pre, bool cdtor)
+{
+  const char *fname = IDENTIFIER_POINTER (idin);
+  size_t len = strlen (fname);
+  /* Cdtor names have a space at the end. We need to remove that space
+     when forming the new identifier. */
+  char *nn = xasprintf ("%.*s%s%s",
+                       cdtor ? (int)len-1 : int(len),
+                       fname,
+                       JOIN_STR,
+                       pre ? "pre" : "post");
+  tree newid = get_identifier (nn);
+  free (nn);
+  return newid;
+}
+
+/* Build a declaration for the pre- or postcondition of a guarded FNDECL.  */
+
+static tree
+build_contract_condition_function (tree fndecl, bool pre)
+{
+  if (error_operand_p (fndecl))
+    return error_mark_node;
+
+  if (DECL_IOBJ_MEMBER_FUNCTION_P (fndecl)
+      && !TYPE_METHOD_BASETYPE (TREE_TYPE (fndecl)))
+    return error_mark_node;
+
+  /* Start the copy.  */
+  tree fn = copy_decl (fndecl);
+
+  /* Don't propagate declaration attributes to the checking function,
+     including the original contracts.  */
+  DECL_ATTRIBUTES (fn) = NULL_TREE;
+
+  /* DECL_ASSEMBLER_NAME will be copied from FNDECL if it's already set. Unset
+     it so fn name later gets mangled according to the rules for pre and post
+     functions.  */
+  SET_DECL_ASSEMBLER_NAME (fn, NULL_TREE);
+
+  /* If requested, disable optimisation of checking functions; this can, in
+     some cases, prevent UB from eliding the checks themselves.  */
+  if (flag_contract_disable_optimized_checks)
+    DECL_ATTRIBUTES (fn)
+      = tree_cons (get_identifier ("optimize"),
+                  build_tree_list (NULL_TREE, build_string (3, "-O0")),
+                  NULL_TREE);
+
+  /* Now parse and add any internal representation of these attrs to the
+     decl.  */
+  if (DECL_ATTRIBUTES (fn))
+    cplus_decl_attributes (&fn, DECL_ATTRIBUTES (fn), 0);
+
+  /* FIXME will later optimizations delete unused args to prevent extra arg
+     passing? do we care? */
+  /* Handle the args list.  */
+  tree arg_types = NULL_TREE;
+  tree *last = &arg_types;
+  tree class_type = NULL_TREE;
+  for (tree arg_type = TYPE_ARG_TYPES (TREE_TYPE (fn));
+      arg_type && arg_type != void_list_node;
+      arg_type = TREE_CHAIN (arg_type))
+    {
+      if (DECL_IOBJ_MEMBER_FUNCTION_P (fndecl)
+         && TYPE_ARG_TYPES (TREE_TYPE (fn)) == arg_type)
+      {
+       class_type = TREE_TYPE (TREE_VALUE (arg_type));
+       continue;
+      }
+      *last = build_tree_list (TREE_PURPOSE (arg_type), TREE_VALUE (arg_type));
+      last = &TREE_CHAIN (*last);
+    }
+
+  /* Copy the function parameters, if present.  Disable warnings for them.  */
+  DECL_ARGUMENTS (fn) = NULL_TREE;
+  if (DECL_ARGUMENTS (fndecl))
+    {
+      tree *last_a = &DECL_ARGUMENTS (fn);
+      for (tree p = DECL_ARGUMENTS (fndecl); p; p = TREE_CHAIN (p))
+       {
+         *last_a = copy_decl (p);
+         suppress_warning (*last_a);
+         DECL_CONTEXT (*last_a) = fn;
+         last_a = &TREE_CHAIN (*last_a);
+       }
+    }
+
+  tree orig_fn_value_type = TREE_TYPE (TREE_TYPE (fn));
+  if (!pre && !VOID_TYPE_P (orig_fn_value_type))
+    {
+      /* For post contracts that deal with a non-void function, append a
+        parameter to pass the return value.  */
+      tree name = get_identifier ("__r");
+      tree parm = build_lang_decl (PARM_DECL, name, orig_fn_value_type);
+      DECL_CONTEXT (parm) = fn;
+      DECL_ARTIFICIAL (parm) = true;
+      suppress_warning (parm);
+      DECL_ARGUMENTS (fn) = chainon (DECL_ARGUMENTS (fn), parm);
+      *last = build_tree_list (NULL_TREE, orig_fn_value_type);
+      last = &TREE_CHAIN (*last);
+    }
+
+  *last = void_list_node;
+
+  /* The handlers are void fns.  */
+  tree adjusted_type = build_function_type (void_type_node, arg_types);
+
+  /* If the original function is noexcept, build a noexcept function. */
+  if (flag_exceptions && type_noexcept_p (TREE_TYPE (fndecl)))
+    adjusted_type = build_exception_variant (adjusted_type, 
noexcept_true_spec);
+
+  TREE_TYPE (fn) = adjusted_type;
+  DECL_RESULT (fn) = NULL_TREE; /* Let the start function code fill it in.  */
+
+  if (DECL_IOBJ_MEMBER_FUNCTION_P (fndecl))
+    TREE_TYPE (fn) = build_method_type (class_type, TREE_TYPE (fn));
+
+  /* The contract check functions are never a cdtor */
+  DECL_CXX_DESTRUCTOR_P (fn) = DECL_CXX_CONSTRUCTOR_P (fn) = 0;
+
+  DECL_NAME (fn) = contracts_fixup_name (DECL_NAME(fndecl),
+                                        pre,
+                                        DECL_CXX_CONSTRUCTOR_P (fndecl)
+                                        || DECL_CXX_DESTRUCTOR_P (fndecl));
+
+  DECL_INITIAL (fn) = NULL_TREE;
+  CONTRACT_HELPER (fn) = pre ? ldf_contract_pre : ldf_contract_post;
+
+  DECL_VIRTUAL_P (fn) = false;
+
+  /* These functions should be internal.  */
+  TREE_PUBLIC (fn) = false;
+  DECL_EXTERNAL (fn) = false;
+  DECL_WEAK (fn) = false;
+  DECL_COMDAT (fn) = false;
+  DECL_INTERFACE_KNOWN (fn) = true;
+
+  DECL_ARTIFICIAL (fn) = true;
+
+  /* Always inline these functions. */
+  DECL_DISREGARD_INLINE_LIMITS (fn) = true;
+  suppress_warning (fn);
+
+  return fn;
+}
+
+/* Build the precondition checking function for FNDECL.  */
+
+static tree
+build_precondition_function (tree fndecl)
+{
+  if (!has_active_preconditions (fndecl))
+    return NULL_TREE;
+
+  return build_contract_condition_function (fndecl, /*pre=*/true);
+}
+
+/* Build the postcondition checking function for FNDECL. If the return
+   type is undeduced, don't build the function yet. We do that in
+   apply_deduced_return_type.  */
+
+static tree
+build_postcondition_function (tree fndecl)
+{
+  if (!has_active_postconditions (fndecl))
+    return NULL_TREE;
+
+  tree type = TREE_TYPE (TREE_TYPE (fndecl));
+  if (is_auto (type))
+    return NULL_TREE;
+
+  return build_contract_condition_function (fndecl, /*pre=*/false);
+}
+
+/* If we're outlining the contract, build the functions to do the
+   precondition and postcondition checks, and associate them with
+   the function decl FNDECL.
+ */
+
+static void
+build_contract_function_decls (tree fndecl)
+{
+  /* Build the pre/post functions (or not).  */
+  if (!get_precondition_function (fndecl))
+    if (tree pre = build_precondition_function (fndecl))
+      set_precondition_function (fndecl, pre);
+
+  if (!get_postcondition_function (fndecl))
+    if (tree post = build_postcondition_function (fndecl))
+      set_postcondition_function (fndecl, post);
 }
 
 void
@@ -677,6 +918,10 @@ start_function_contracts (tree fndecl)
   if (!handle_contracts_p (fndecl))
     return;
 
+  /* Even if we will use an outlined function for the check (which will be the
+     same one we might use on the callee-side) we still need to check the re-
+     mapped contracts for shadowing.  */
+
   /* Check that the user did not try to shadow a function parameter with the
      specified postcondition result name.  */
   for (tree ca = get_fn_contract_specifiers (fndecl); ca; ca = TREE_CHAIN (ca))
@@ -710,6 +955,137 @@ start_function_contracts (tree fndecl)
                CONTRACT_CONDITION (CONTRACT_STATEMENT (ca)) = error_mark_node;
            }
        }
+
+  /* If we are expanding contract assertions inline then no need to declare
+     the outline function decls.  */
+  if (!flag_contract_checks_outlined)
+    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.  */
+  /* Do we already have declarations generated ? */
+  if (!DECL_PRE_FN (fndecl) && !DECL_POST_FN (fndecl))
+    build_contract_function_decls (fndecl);
+}
+
+void
+maybe_update_postconditions (tree fndecl)
+{
+  /* Update any postconditions and the postcondition checking function
+     as needed.  If there are postconditions, we'll use those to rewrite
+     return statements to check postconditions.  */
+  if (has_active_postconditions (fndecl))
+    {
+      rebuild_postconditions (fndecl);
+      tree post = build_postcondition_function (fndecl);
+      set_postcondition_function (fndecl, post);
+    }
+}
+
+/* Build and return an argument list containing all the parameters of the
+   (presumably guarded) function decl FNDECL.  This can be used to forward
+   all of FNDECL arguments to a function taking the same list of arguments
+   -- namely the unchecked form of FNDECL.
+
+   We use CALL_FROM_THUNK_P instead of forward_parm for forwarding
+   semantics.  */
+
+static vec<tree, va_gc> *
+build_arg_list (tree fndecl)
+{
+  vec<tree, va_gc> *args = make_tree_vector ();
+  for (tree t = DECL_ARGUMENTS (fndecl); t; t = DECL_CHAIN (t))
+    vec_safe_push (args, t);
+  return args;
+}
+
+/* Build and return a thunk like call to FUNCTION using the supplied
+ arguments.  The call is like a thunk call in the fact that we do not
+ want to create additional copies of the arguments. However, we can
+ not simply reuse the thunk machinery as it does more than we want.
+ More specifically, we don't want to mark the calling function as
+ `DECL_THUNK_P`, we only want the special treatment for the parameters
+ of the call we are about to generate.
+ We reuse most of build_call_a, modulo special handling for empty
+ classes which relies on `DECL_THUNK_P` to know that the call we're building
+ is going to be a thunk call.
+ We also mark the call as a thunk call to allow for correct gimplification
+ of the arguments.
+ */
+
+static tree
+build_thunk_like_call (tree function, int n, tree *argarray)
+{
+
+  function = build_call_a_1 (function, n, argarray);
+
+  tree decl = get_callee_fndecl (function);
+
+  /* Set TREE_USED for the benefit of -Wunused.  */
+  if (decl && !TREE_USED (decl))
+    mark_used (decl);
+
+  CALL_FROM_THUNK_P (function) = true;
+
+  return function;
+}
+
+/* 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.  */
+  gcc_checking_assert (DECL_PRE_FN (fndecl)
+                      && DECL_PRE_FN (fndecl) != error_mark_node);
+
+  releasing_vec args = build_arg_list (fndecl);
+  tree call = build_thunk_like_call (DECL_PRE_FN (fndecl), args->length (),
+                           args->address ());
+
+  finish_expr_stmt (call);
+}
+
+/* Returns the parameter corresponding to the return value of a guarded
+   function FNDECL.  Returns NULL_TREE if FNDECL has no postconditions or
+   is void.  */
+
+static tree
+get_postcondition_result_parameter (tree fndecl)
+{
+  if (!fndecl || fndecl == error_mark_node)
+    return NULL_TREE;
+
+  if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (fndecl))))
+    return NULL_TREE;
+
+  tree post = DECL_POST_FN (fndecl);
+  if (!post || post == error_mark_node)
+    return NULL_TREE;
+
+  for (tree arg = DECL_ARGUMENTS (post); arg; arg = TREE_CHAIN (arg))
+    if (!TREE_CHAIN (arg))
+      return arg;
+
+  return NULL_TREE;
+}
+
+/* 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_thunk_like_call (DECL_POST_FN (fndecl), args->length (),
+                           args->address ());
+  finish_expr_stmt (call);
 }
 
 /* Returns a copy of FNDECL contracts. This is used when emiting a contract.
@@ -760,9 +1136,14 @@ emit_contract_attr (tree attr)
 static void
 apply_preconditions (tree fndecl)
 {
-  tree contract_copy = copy_contracts (fndecl, cmk_pre);
-  for (; contract_copy; contract_copy = NEXT_CONTRACT_ATTR (contract_copy))
-    emit_contract_attr (contract_copy);
+  if (flag_contract_checks_outlined)
+    add_pre_condition_fn_call (fndecl);
+  else
+  {
+    tree contract_copy = copy_contracts (fndecl, cmk_pre);
+    for (; contract_copy; contract_copy = NEXT_CONTRACT_ATTR (contract_copy))
+      emit_contract_attr (contract_copy);
+  }
 }
 
 /* Add a call or a direct evaluation of the post checks.  */
@@ -770,9 +1151,14 @@ apply_preconditions (tree fndecl)
 static void
 apply_postconditions (tree fndecl)
 {
-  tree contract_copy = copy_contracts (fndecl, cmk_post);
-  for (; contract_copy; contract_copy = NEXT_CONTRACT_ATTR (contract_copy))
-    emit_contract_attr (contract_copy);
+  if (flag_contract_checks_outlined)
+    add_post_condition_fn_call (fndecl);
+  else
+    {
+      tree contract_copy = copy_contracts (fndecl, cmk_post);
+      for (; contract_copy; contract_copy = NEXT_CONTRACT_ATTR (contract_copy))
+       emit_contract_attr (contract_copy);
+    }
 }
 
 /* Add contract handling to the function in FNDECL.
@@ -1507,6 +1893,123 @@ update_late_contract (tree contract, tree result, 
cp_expr condition)
   CONTRACT_CONDITION (contract) = condition;
 }
 
+/* Returns the precondition funtion for FNDECL, or null if not set.  */
+
+tree
+get_precondition_function (tree fndecl)
+{
+  gcc_checking_assert (fndecl);
+  tree *result = hash_map_safe_get (decl_pre_fn, fndecl);
+  return result ? *result : NULL_TREE;
+}
+
+/* Returns the postcondition funtion for FNDECL, or null if not set.  */
+
+tree
+get_postcondition_function (tree fndecl)
+{
+  gcc_checking_assert (fndecl);
+  tree *result = hash_map_safe_get (decl_post_fn, fndecl);
+  return result ? *result : NULL_TREE;
+}
+
+/* Set the PRE and POST functions for FNDECL.  Note that PRE and POST can
+   be null in this case. If so the functions are not recorded.  Used by the
+   modules code.  */
+
+void
+set_contract_functions (tree fndecl, tree pre, tree post)
+{
+  if (pre)
+    set_precondition_function (fndecl, pre);
+
+  if (post)
+    set_postcondition_function (fndecl, post);
+}
+
+
+/* We're compiling the pre/postcondition function CONDFN; remap any FN
+   attributes that match CODE and emit them.  */
+
+static void
+remap_and_emit_conditions (tree fn, tree condfn, tree_code code)
+{
+  gcc_assert (code == PRECONDITION_STMT || code == POSTCONDITION_STMT);
+  tree attr = get_fn_contract_specifiers (fn);
+  for (; attr; attr = NEXT_CONTRACT_ATTR (attr))
+    {
+      tree contract = CONTRACT_STATEMENT (attr);
+      if (TREE_CODE (contract) == code)
+       {
+         contract = copy_node (contract);
+         remap_contract (fn, condfn, contract, /*duplicate_p=*/false);
+         if (!emit_contract_statement (contract))
+           continue;
+       }
+    }
+}
+
+/* Finish up the pre & post function definitions for a guarded FNDECL,
+   and compile those functions all the way to assembler language output.  */
+
+void
+finish_function_contracts (tree fndecl)
+{
+  /* If the guarded func is either already decided to be ill-formed or is
+     not yet complete return early.  */
+  if (error_operand_p (fndecl)
+      || !DECL_INITIAL (fndecl)
+      || DECL_INITIAL (fndecl) == error_mark_node)
+    return;
+
+  /* If there are no contracts here, or we're building them in-line then we
+     do not need to build the outlined functions.  */
+  if (!handle_contracts_p (fndecl)
+      || !flag_contract_checks_outlined)
+    return;
+
+  tree attr = get_fn_contract_specifiers (fndecl);
+  for (; attr; attr = NEXT_CONTRACT_ATTR (attr))
+    {
+      tree contract = CONTRACT_STATEMENT (attr);
+      if (!CONTRACT_CONDITION (contract)
+         || CONTRACT_CONDITION (contract) == error_mark_node)
+       return;
+      /* We are generating code, deferred parses should be complete.  */
+      gcc_checking_assert (!CONTRACT_CONDITION_DEFERRED_P (contract));
+    }
+
+  int flags = SF_DEFAULT | SF_PRE_PARSED;
+
+  /* If either the pre or post functions are bad, don't bother emitting
+     any contracts.  The program is already ill-formed.  */
+  tree pre = DECL_PRE_FN (fndecl);
+  tree post = DECL_POST_FN (fndecl);
+  if (pre == error_mark_node || post == error_mark_node)
+    return;
+
+  if (pre && !DECL_INITIAL (pre))
+    {
+      DECL_PENDING_INLINE_P (pre) = false;
+      start_preparsed_function (pre, DECL_ATTRIBUTES (pre), flags);
+      remap_and_emit_conditions (fndecl, pre, PRECONDITION_STMT);
+      finish_return_stmt (NULL_TREE);
+      pre = finish_function (false);
+      expand_or_defer_fn (pre);
+    }
+
+  if (post && !DECL_INITIAL (post))
+    {
+      DECL_PENDING_INLINE_P (post) = false;
+      start_preparsed_function (post, DECL_ATTRIBUTES (post), flags);
+      remap_and_emit_conditions (fndecl, post, POSTCONDITION_STMT);
+      gcc_checking_assert (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (post))));
+      finish_return_stmt (NULL_TREE);
+      post = finish_function (false);
+      expand_or_defer_fn (post);
+    }
+}
+
 /* ===== Code generation ===== */
 
 /* Insert a BUILT_IN_OBSERVABLE_CHECKPOINT epoch marker.  */
@@ -1953,6 +2456,9 @@ get_src_loc_impl_ptr (location_t loc)
     get_contracts_source_location_impl_type ();
 
   tree fndecl = current_function_decl;
+  /* We might be an outlined function.  */
+  if (DECL_IS_PRE_FN_P (fndecl) || DECL_IS_POST_FN_P (fndecl))
+    fndecl = get_orig_for_outlined (fndecl);
 
   gcc_checking_assert (fndecl);
   tree impl__
@@ -2158,7 +2664,7 @@ build_contract_check (tree contract)
   if (condition == error_mark_node)
     return NULL_TREE;
 
-  if (POSTCONDITION_P (contract))
+  if (!flag_contract_checks_outlined && POSTCONDITION_P (contract))
     {
       remap_retval (current_function_decl, contract);
       condition = CONTRACT_CONDITION (contract);
diff --git a/gcc/cp/contracts.h b/gcc/cp/contracts.h
index c77adaf6e1e..6826a8aea61 100644
--- a/gcc/cp/contracts.h
+++ b/gcc/cp/contracts.h
@@ -125,6 +125,26 @@ enum detection_mode : uint16_t {
 #define POSTCONDITION_IDENTIFIER(NODE) \
   (TREE_OPERAND (POSTCONDITION_STMT_CHECK (NODE), 5))
 
+/* For a FUNCTION_DECL of a guarded function, this holds the function decl
+   where pre contract checks are emitted.  */
+#define DECL_PRE_FN(NODE) \
+  (get_precondition_function ((NODE)))
+
+/* For a FUNCTION_DECL of a guarded function, this holds the function decl
+   where post contract checks are emitted.  */
+#define DECL_POST_FN(NODE) \
+  (get_postcondition_function ((NODE)))
+
+/* True iff the FUNCTION_DECL is the pre function for a guarded function.  */
+#define DECL_IS_PRE_FN_P(NODE) \
+  (DECL_DECLARES_FUNCTION_P (NODE) && DECL_LANG_SPECIFIC (NODE) && \
+   CONTRACT_HELPER (NODE) == ldf_contract_pre)
+
+/* True iff the FUNCTION_DECL is the post function for a guarded function.  */
+#define DECL_IS_POST_FN_P(NODE) \
+  (DECL_DECLARES_FUNCTION_P (NODE) && DECL_LANG_SPECIFIC (NODE) && \
+   CONTRACT_HELPER (NODE) == ldf_contract_post)
+
 /* contracts.cc */
 
 extern tree grok_contract                      (tree, tree, tree, cp_expr, 
location_t);
@@ -154,8 +174,14 @@ extern bool check_postcondition_result             (tree, 
tree, location_t);
 
 extern bool contract_any_deferred_p            (tree);
 
+extern tree get_precondition_function          (tree);
+extern tree get_postcondition_function         (tree);
+extern tree get_orig_for_outlined              (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 void maybe_emit_violation_handler_wrappers (void);
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7ef91f29f27..f5252eba50b 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3071,6 +3071,13 @@ struct GTY(()) lang_decl_min {
   tree access;
 };
 
+enum lang_contract_helper
+{
+  ldf_contract_none = 0,
+  ldf_contract_pre,
+  ldf_contract_post
+};
+
 /* Additional DECL_LANG_SPECIFIC information for functions.  */
 
 struct GTY(()) lang_decl_fn {
@@ -3100,8 +3107,9 @@ struct GTY(()) lang_decl_fn {
   unsigned escalated_p : 1;
 
   unsigned xobj_func : 1;
+  ENUM_BITFIELD(lang_contract_helper) contract_helper : 2;
 
-  unsigned spare : 7;
+  unsigned spare : 5;
 
   /* 32-bits padding on 64-bit host.  */
 
@@ -3249,6 +3257,9 @@ struct GTY(()) lang_decl {
 
 #endif /* ENABLE_TREE_CHECKING */
 
+#define CONTRACT_HELPER(NODE) \
+ (LANG_DECL_FN_CHECK (NODE)->contract_helper)
+
 /* For a FUNCTION_DECL or a VAR_DECL, the language linkage for the
    declaration.  Some entities (like a member function in a local
    class, or a local variable) do not have linkage at all, and this
@@ -8816,6 +8827,7 @@ extern tree mangle_tls_wrapper_fn         (tree);
 extern bool decl_tls_wrapper_p                 (tree);
 extern tree mangle_ref_init_variable           (tree);
 extern tree mangle_template_parm_object                (tree);
+extern tree mangle_decl_string                  (const tree);
 extern char *get_mangled_vtable_map_var_name    (tree);
 extern bool mangle_return_type_p               (tree);
 extern tree mangle_decomp                      (tree, vec<tree> &);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 0ccba2afab7..8fca7e069a0 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -20498,6 +20498,10 @@ finish_function (bool inline_p)
       delete coroutine;
     }
 
+  /* If we have used outlined contracts checking functions, build and emit
+     them here.  */
+  finish_function_contracts (fndecl);
+
   return fndecl;
 }
 
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index cbcce52e736..4d190710f8a 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -238,7 +238,6 @@ static int discriminator_for_string_literal (tree, tree);
 static void write_discriminator (const int);
 static void write_local_name (tree, const tree, const tree);
 static void dump_substitution_candidates (void);
-static tree mangle_decl_string (const tree);
 static void maybe_check_abi_tags (tree, tree = NULL_TREE, int = 10);
 
 /* Control functions.  */
@@ -800,10 +799,19 @@ write_mangled_name (const tree decl, bool top_level)
 
   check_abi_tags (decl);
 
-  if (unmangled_name_p (decl))
+  bool pre = DECL_IS_PRE_FN_P(decl);
+  bool post = DECL_IS_POST_FN_P(decl);
+  tree mdecl = decl;
+
+  /* If we have a pre or post check, mangle the name of the checked
+   function. */
+  if (pre || post)
+    mdecl = get_orig_for_outlined (decl);
+
+  if (unmangled_name_p (mdecl))
     {
       if (top_level)
-       write_string (IDENTIFIER_POINTER (DECL_NAME (decl)));
+       write_string (IDENTIFIER_POINTER (DECL_NAME (mdecl)));
       else
        {
          /* The standard notes: "The <encoding> of an extern "C"
@@ -812,15 +820,20 @@ write_mangled_name (const tree decl, bool top_level)
             overloaded operators that way though, because it contains
             characters invalid in assembler.  */
          write_string ("_Z");
-         write_source_name (DECL_NAME (decl));
+         write_source_name (DECL_NAME (mdecl));
        }
     }
   else
     {
       write_string ("_Z");
-      write_encoding (decl);
+      write_encoding (mdecl);
     }
 
+  if (pre)
+    write_string (JOIN_STR "pre");
+  else if (post)
+    write_string (JOIN_STR "post");
+
   /* If this is a coroutine helper, then append an appropriate string to
      identify which.  */
   if (tree ramp = DECL_RAMP_FN (decl))
@@ -4543,7 +4556,7 @@ mangle_module_global_init (int module)
 
 /* Generate the mangled name of DECL.  */
 
-static tree
+tree
 mangle_decl_string (const tree decl)
 {
   tree result;
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index a7ed9ffe408..c91be9d45d9 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -232,6 +232,7 @@ Classes used:
 #include "attribs.h"
 #include "intl.h"
 #include "langhooks.h"
+#include "contracts.h"
 /* This TU doesn't need or want to see the networking.  */
 #define CODY_NETWORKING 0
 #include "mapper-client.h"
@@ -11158,6 +11159,19 @@ trees_out::fn_parms_init (tree fn)
                   base_tag - ix, ix, parm, fn);
       tree_node_vals (parm);
     }
+
+  if (!streaming_p ())
+    {
+      /* We must walk contract specifiers so the dependency graph is complete. 
*/
+      tree contract = get_fn_contract_specifiers (fn);
+      for (; contract; contract = NEXT_CONTRACT_ATTR (contract))
+       tree_node (contract);
+    }
+
+  /* Write a reference to contracts pre/post functions, if any, to avoid
+     regenerating them in importers.  */
+  tree_node (DECL_PRE_FN (fn));
+  tree_node (DECL_POST_FN (fn));
 }
 
 /* Build skeleton parm nodes, read their flags, type & parm indices.  */
@@ -11192,6 +11206,11 @@ trees_in::fn_parms_init (tree fn)
        return 0;
     }
 
+  /* Reload references to contract functions, if any.  */
+  tree pre_fn = tree_node ();
+  tree post_fn = tree_node ();
+  set_contract_functions (fn, pre_fn, post_fn);
+
   return base_tag;
 }
 
@@ -11887,7 +11906,15 @@ check_mergeable_decl (merge_kind mk, tree decl, tree 
ovl, merge_key const &key)
                   Matches decls_match behaviour.  */
                && (!DECL_IS_UNDECLARED_BUILTIN (m_inner)
                    || !DECL_EXTERN_C_P (m_inner)
-                   || DECL_EXTERN_C_P (d_inner)))
+                   || DECL_EXTERN_C_P (d_inner))
+               /* Reject if one is a different member of a
+                  guarded/pre/post fn set.  */
+               && (!flag_contracts
+                   || (DECL_IS_PRE_FN_P (d_inner)
+                       == DECL_IS_PRE_FN_P (m_inner)))
+               && (!flag_contracts
+                   || (DECL_IS_POST_FN_P (d_inner)
+                       == DECL_IS_POST_FN_P (m_inner))))
              {
                tree m_reqs = get_constraints (m_inner);
                if (m_reqs)
@@ -16778,6 +16805,7 @@ module_state_config::get_dialect ()
                      (cxx_dialect < cxx20 && flag_coroutines
                       ? "/coroutines" : ""),
                      flag_module_implicit_inline ? "/implicit-inline" : "",
+                     flag_contracts ? "/contracts" : "",
                      NULL);
 
   return dialect;
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index e1f8130b132..4e2783819e6 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -6523,6 +6523,7 @@ cp_free_lang_data (tree t)
       DECL_EXTERNAL (t) = 1;
       TREE_STATIC (t) = 0;
     }
+
   if (TREE_CODE (t) == NAMESPACE_DECL)
     /* We do not need the leftover chaining of namespaces from the
        binding level.  */
diff --git 
a/gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/freefunc-noexcept-post.C 
b/gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/freefunc-noexcept-post.C
new file mode 100644
index 00000000000..2cbccba810c
--- /dev/null
+++ 
b/gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/freefunc-noexcept-post.C
@@ -0,0 +1,46 @@
+// Throwing violation handler in a pre/post check on a noexcept function
+// behaves as if the function exited via an exception.
+// This tests the behaviour of a pre condition on a member function
+// { dg-do run { target c++20 } }
+// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe 
-fcontract-checks-outlined" }
+
+#include <contracts>
+#include <exception>
+#include <cstdlib>
+
+struct MyException{};
+
+// Test that there is an active exception when we reach the terminate handler.
+void my_term()
+{
+  try { throw; }
+  catch(MyException) { std::exit(0); }
+}
+
+
+void handle_contract_violation(const std::contracts::contract_violation& 
violation)
+{
+  throw MyException{};
+}
+
+void f(const int x) noexcept post(x>1)
+{
+  try{
+   int i = 1;
+  }
+  catch(...) {
+  }
+}
+
+int main()
+{
+  std::set_terminate (my_term);
+  try
+  {
+      f(-42);
+  } catch (...) {
+  }
+  // We should not get here
+  return 1;
+
+}
diff --git 
a/gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/freefunc-noexcept-pre.C 
b/gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/freefunc-noexcept-pre.C
new file mode 100644
index 00000000000..3dd12f0619b
--- /dev/null
+++ 
b/gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/freefunc-noexcept-pre.C
@@ -0,0 +1,46 @@
+// Throwing violation handler in a pre/post check on a noexcept function
+// behaves as if the function exited via an exception.
+// This tests the behaviour of a pre condition on a member function
+// { dg-do run { target c++20 } }
+// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe 
-fcontract-checks-outlined" }
+
+#include <contracts>
+#include <exception>
+#include <cstdlib>
+
+struct MyException{};
+
+// Test that there is an active exception when we reach the terminate handler.
+void my_term()
+{
+  try { throw; }
+  catch(MyException) { std::exit(0); }
+}
+
+
+void handle_contract_violation(const std::contracts::contract_violation& 
violation)
+{
+  throw MyException{};
+}
+
+void f(int x) noexcept pre(x>1)
+{
+  try{
+   int i = 1;
+  }
+  catch(...) {
+  }
+}
+
+int main()
+{
+  std::set_terminate (my_term);
+  try
+  {
+      f(-42);
+  } catch (...) {
+  }
+  // We should not get here
+  return 1;
+
+}
diff --git 
a/gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/func-noexcept-assert.C 
b/gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/func-noexcept-assert.C
new file mode 100644
index 00000000000..407f8f71b5f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/func-noexcept-assert.C
@@ -0,0 +1,55 @@
+// Throwing violation handler in an assert check in a noexcept function
+// can be caught by the function.
+// { dg-do run { target c++20 } }
+// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe 
-fcontract-checks-outlined" }
+
+#include <contracts>
+#include <exception>
+#include <cstdlib>
+
+struct MyException{};
+
+
+void handle_contract_violation(const std::contracts::contract_violation& 
violation)
+{
+  throw MyException{};
+}
+
+void free_f(const int x) {
+  try {
+         contract_assert(x>1);
+         int i = 1;
+  }
+  catch(...){}
+}
+
+struct X
+{
+    void f(const int x) {
+      try {
+         contract_assert(x>1);
+         int i = 1;
+      }
+      catch(...){}
+    }
+
+    virtual void virt_f(const int x) {
+      try {
+         contract_assert(x>1);
+         int i = 1;
+      }
+      catch(...){}
+    }
+
+};
+
+int main()
+{
+  free_f(-42);
+
+  X x;
+  x.f(-42);
+  x.virt_f(-42);
+
+
+}
diff --git 
a/gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/memberfunc-noexcept-post.C
 
b/gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/memberfunc-noexcept-post.C
new file mode 100644
index 00000000000..8ae0984a752
--- /dev/null
+++ 
b/gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/memberfunc-noexcept-post.C
@@ -0,0 +1,49 @@
+// Throwing violation handler in a pre/post check on a noexcept function
+// behaves as if the function exited via an exception.
+// This tests the behaviour of a post condition on a member function
+// { dg-do run { target c++20 } }
+// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe 
-fcontract-checks-outlined" }
+
+#include <contracts>
+#include <exception>
+#include <cstdlib>
+
+struct MyException{};
+
+// Test that there is an active exception when we reach the terminate handler.
+void my_term()
+{
+  try { throw; }
+  catch(MyException) { std::exit(0); }
+}
+
+
+void handle_contract_violation(const std::contracts::contract_violation& 
violation)
+{
+  throw MyException{};
+}
+
+struct X
+{
+    void f(const int x) noexcept post(x>1) {
+      try{
+       int i = 1;
+      }
+      catch(...) {
+      }
+    }
+};
+
+int main()
+{
+  std::set_terminate (my_term);
+  try
+  {
+      X x;
+      x.f(-42);
+  } catch (...) {
+  }
+  // We should not get here
+  return 1;
+
+}
diff --git 
a/gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/memberfunc-noexcept-pre.C 
b/gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/memberfunc-noexcept-pre.C
new file mode 100644
index 00000000000..f40882c1f00
--- /dev/null
+++ 
b/gcc/testsuite/g++.dg/contracts/cpp26/outline-checks/memberfunc-noexcept-pre.C
@@ -0,0 +1,49 @@
+// Throwing violation handler in a pre/post check on a noexcept function
+// behaves as if the function exited via an exception.
+// This tests the behaviour of a pre condition on a member function
+// { dg-do run { target c++20 } }
+// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe 
-fcontract-checks-outlined" }
+
+#include <contracts>
+#include <exception>
+#include <cstdlib>
+
+struct MyException{};
+
+// Test that there is an active exception when we reach the terminate handler.
+void my_term()
+{
+  try { throw; }
+  catch(MyException) { std::exit(0); }
+}
+
+
+void handle_contract_violation(const std::contracts::contract_violation& 
violation)
+{
+  throw MyException{};
+}
+
+struct X
+{
+    void f(int x) noexcept pre(x>1) {
+      try{
+       int i = 1;
+      }
+      catch(...) {
+      }
+    }
+};
+
+int main()
+{
+  std::set_terminate (my_term);
+  try
+  {
+      X x;
+      x.f(-42);
+  } catch (...) {
+  }
+  // We should not get here
+  return 1;
+
+}
-- 
2.39.5 (Apple Git-154)

Reply via email to