From: Nina Ranns <[email protected]>

Changes since v3

 see: 
https://inbox.sourceware.org/gcc-patches/0b4fe7d5fb28d1cbf84e5c0e10bea39dfd877592.1763334718.git.i...@sandoe.co.uk/T/#t
 - rebased onto r16-7033-gfb6a2e3f1fa842 (includes reflection changes).

Changes since v2

 - Addressed Jason's review comments
 - Addressed Sandra's review comments
 - rebased onto r16-6757-g460edeb8bea11e (includes new year (c) change).

Changes since v1
 - fixed a merge error in the removal of C++2a code.
 - rebased onto r16-5785-g3b30d09ac7bbf8 (includes change to default to
   C++20).

--- 8< ---

This a (currently GCC-only) extension that implements caller-side
checking of pre and post conditions.  It is completely in scope
with the C++26 CDIS wording, but is not mandated.

The implementation here allows applying caller or callee-side
checking independently.

gcc/c-family/ChangeLog:

        * c.opt (fcontracts-definition-check=,
        fcontracts-client-check=): New.

gcc/cp/ChangeLog:

        * call.cc (build_cxx_call): Where enabled, wrap calls to
        functions with contract specifiers.
        * contracts.cc (enum contract_match_kind): Move to contracts
        header.
        (build_contract_condition_function): Copy caller-side wrapper
        state.
        (set_contract_wrapper_function, get_contract_wrapper_function,
        get_orig_func_for_wrapper, contracts_fixup_cdtorname,
        build_contract_wrapper_function,
        get_or_create_contract_wrapper_function): New.
        (start_function_contracts): Handle caller-side wrappers.
        (maybe_apply_function_contracts): Likewise.
        (copy_and_remap_contracts): Likewise.
        (should_contract_wrap_call, maybe_contract_wrap_call,
        define_contract_wrapper_func, emit_contract_wrapper_func): New.
        (finish_function_contracts): Handle caller-side wrappers.
        (get_src_loc_impl_ptr): Likewise.
        * contracts.h (DECL_IS_WRAPPER_FN_P): New.
        (enum contract_match_kind): Moved from contracts.cc.
        (copy_and_remap_contracts): Allow selection on the specific
        contract kind.
        (maybe_contract_wrap_call, emit_contract_wrapper_func): New.
        (set_decl_contracts): Delete dead code.
        * cp-tree.h (struct lang_decl_fn): Add wrapper function bit.
        (DECL_CONTRACT_WRAPPER): New.
        * decl2.cc (c_parse_final_cleanups): Emit wrappers.

gcc/testsuite/ChangeLog:

        * g++.dg/contracts/cpp26/callerside-checks/callerside-checks-all.C: New 
test.
        * 
g++.dg/contracts/cpp26/callerside-checks/callerside-checks-non-trivial.C: New 
test.
        * g++.dg/contracts/cpp26/callerside-checks/callerside-checks-none.C: 
New test.
        * g++.dg/contracts/cpp26/callerside-checks/callerside-checks-pre.C: New 
test.
        * g++.dg/contracts/cpp26/callerside-checks/ctor.C: New test.
        * g++.dg/contracts/cpp26/callerside-checks/freefunc-noexcept-post.C: 
New test.
        * g++.dg/contracts/cpp26/callerside-checks/freefunc-noexcept-pre.C: New 
test.
        * 
g++.dg/contracts/cpp26/definition-checks/contract-assert-no-def-check.C: New 
test.
        * g++.dg/contracts/cpp26/non-trivial-ice.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                            |  29 ++
 gcc/cp/call.cc                                |   8 +-
 gcc/cp/contracts.cc                           | 302 +++++++++++++++++-
 gcc/cp/contracts.h                            |  22 +-
 gcc/cp/cp-tree.h                              |   8 +-
 gcc/cp/decl2.cc                               |  11 +-
 gcc/doc/invoke.texi                           |  20 ++
 .../callerside-checks/callerside-checks-all.C |  52 +++
 .../callerside-checks-non-trivial.C           |  18 ++
 .../callerside-checks-none.C                  |  64 ++++
 .../callerside-checks/callerside-checks-pre.C |  65 ++++
 .../contracts/cpp26/callerside-checks/ctor.C  |  23 ++
 .../freefunc-noexcept-post.C                  |  49 +++
 .../callerside-checks/freefunc-noexcept-pre.C |  49 +++
 .../contract-assert-no-def-check.C            |  25 ++
 .../g++.dg/contracts/cpp26/non-trivial-ice.C  |  21 ++
 16 files changed, 740 insertions(+), 26 deletions(-)
 create mode 100644 
gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-all.C
 create mode 100644 
gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-non-trivial.C
 create mode 100644 
gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-none.C
 create mode 100644 
gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-pre.C
 create mode 100644 
gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/ctor.C
 create mode 100644 
gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/freefunc-noexcept-post.C
 create mode 100644 
gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/freefunc-noexcept-pre.C
 create mode 100644 
gcc/testsuite/g++.dg/contracts/cpp26/definition-checks/contract-assert-no-def-check.C
 create mode 100644 gcc/testsuite/g++.dg/contracts/cpp26/non-trivial-ice.C

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index d473377d91b8..1f18e91541b5 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1938,6 +1938,35 @@ fcontract-disable-optimized-checks
 C++ ObjC++ Var(flag_contract_disable_optimized_checks) Init(0)
 -fcontract-disable-optimized-checks    Disable optimisation of contract checks.
 
+Enum
+Name(client_contract_check) Type(int)  UnknownError(unrecognized client 
contract check option %qs)
+
+EnumValue
+Enum(client_contract_check) String(none) Value(0)
+
+EnumValue
+Enum(client_contract_check) String(pre) Value(1)
+
+EnumValue
+Enum(client_contract_check) String(all) Value(2)
+
+fcontracts-client-check=
+C++ ObjC++ Joined RejectNegative Enum(client_contract_check) 
Var(flag_contract_client_check) Init (0)
+-fcontracts-client-check=[none|pre|all] Select which contracts will be checked 
on the client side for non virtual functions
+
+Enum
+Name(on_off) Type(int) UnknownError(argument %qs must be either %<on%> or 
%<off%>)
+
+EnumValue
+Enum(on_off) String(off) Value(0)
+
+EnumValue
+Enum(on_off) String(on) Value(1)
+
+fcontracts-definition-check=
+C++ ObjC++ Joined RejectNegative Enum(on_off) 
Var(flag_contracts_definition_check) Init(1)
+-fcontracts-definition-check=[on|off]  Enable or disable contract checks on 
the definition side for all functions (default on).
+
 fcoroutines
 C++ ObjC++ LTO Var(flag_coroutines)
 Enable C++ coroutines (experimental).
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 103e68ae9db8..62d25ce6ebed 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" // maybe_contract_wrap_call
 
 /* The various kinds of conversion.  */
 
@@ -4932,7 +4933,7 @@ implicit_conversion_error (location_t loc, tree type, 
tree expr,
           && !CP_AGGREGATE_TYPE_P (type))
     error_at (loc, "designated initializers cannot be used with a "
              "non-aggregate type %qT", type);
-  else 
+  else
     {
       auto_diagnostic_group d;
       if (is_stub_object (expr))
@@ -11577,7 +11578,7 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
     }
 
   if (VOID_TYPE_P (TREE_TYPE (fn)))
-    return fn;
+    return maybe_contract_wrap_call (fndecl, fn);
 
   /* 5.2.2/11: If a function call is a prvalue of object type: if the
      function call is either the operand of a decltype-specifier or the
@@ -11589,6 +11590,7 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
       fn = require_complete_type (fn, complain);
       if (fn == error_mark_node)
        return error_mark_node;
+      fn = maybe_contract_wrap_call (fndecl, fn);
 
       if (MAYBE_CLASS_TYPE_P (TREE_TYPE (fn)))
        {
@@ -11596,6 +11598,8 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
          maybe_warn_parm_abi (TREE_TYPE (fn), loc);
        }
     }
+  else
+    fn = maybe_contract_wrap_call (fndecl, fn);
   return convert_from_reference (fn);
 }
 
diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc
index a153306b275d..f05f89eddf46 100644
--- a/gcc/cp/contracts.cc
+++ b/gcc/cp/contracts.cc
@@ -658,12 +658,13 @@ get_orig_for_outlined (tree fndecl)
    PRE specifies if we need an identifier for a pre or post contract check.  */
 
 static void
-contracts_fixup_names (tree new_fn, tree old_fn, bool pre)
+contracts_fixup_names (tree new_fn, tree old_fn, bool pre, bool wrapper)
 {
   bool cdtor = DECL_CXX_CONSTRUCTOR_P (old_fn)
               || DECL_CXX_DESTRUCTOR_P (old_fn);
   const char *fname = IDENTIFIER_POINTER (DECL_NAME (old_fn));
-  const char *pre_or_post = pre ? "pre" : "post";
+  const char *append = wrapper ? "contract_wrapper"
+                              : (pre ? "pre" : "post");
   size_t len = strlen (fname);
   /* Cdtor names have a space at the end.  We need to remove that space
      when forming the new identifier.  */
@@ -671,13 +672,13 @@ contracts_fixup_names (tree new_fn, tree old_fn, bool pre)
                        cdtor ? (int)len-1 : int(len),
                        fname,
                        JOIN_STR,
-                       pre_or_post);
+                       append);
   DECL_NAME (new_fn) = get_identifier (nn);
   free (nn);
 
   /* Now do the mangled version.  */
   fname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (old_fn));
-  nn = xasprintf ("%s%s%s", fname, JOIN_STR, pre_or_post);
+  nn = xasprintf ("%s%s%s", fname, JOIN_STR, append);
   SET_DECL_ASSEMBLER_NAME (new_fn, get_identifier (nn));
   free (nn);
 }
@@ -779,10 +780,12 @@ build_contract_condition_function (tree fndecl, bool pre)
   DECL_VIRTUAL_P (fn) = false;
 
   /* Append .pre / .post to a usable name for the original function.  */
-  contracts_fixup_names (fn, fndecl, pre);
+  contracts_fixup_names (fn, fndecl, pre, /*wrapper*/false);
 
   DECL_INITIAL (fn) = NULL_TREE;
   CONTRACT_HELPER (fn) = pre ? ldf_contract_pre : ldf_contract_post;
+  /* We might have a pre/post for a wrapper.  */
+  DECL_CONTRACT_WRAPPER (fn) = DECL_CONTRACT_WRAPPER (fndecl);
 
   /* Make these functions internal if we can, i.e. if the guarded function is
      not vague linkage, or if we can put them in a comdat group with the
@@ -858,6 +861,136 @@ build_contract_function_decls (tree fndecl)
       set_postcondition_function (fndecl, post);
 }
 
+/* Map from FUNCTION_DECL to a FUNCTION_DECL for contract wrapper.  */
+
+static GTY(()) hash_map<tree, tree> *decl_wrapper_fn = nullptr;
+
+/* Map from the function decl of a wrapper to the function that it wraps.  */
+
+static GTY(()) hash_map<tree, tree> *decl_for_wrapper = nullptr;
+
+/* Makes wrapper the precondition function for FNDECL.  */
+
+static void
+set_contract_wrapper_function (tree fndecl, tree wrapper)
+{
+  gcc_checking_assert (wrapper && fndecl);
+  hash_map_maybe_create<hm_ggc> (decl_wrapper_fn);
+  gcc_checking_assert (decl_wrapper_fn && !decl_wrapper_fn->get (fndecl));
+  decl_wrapper_fn->put (fndecl, wrapper);
+
+  /* We need to know the wrapped function when composing the diagnostic.  */
+  hash_map_maybe_create<hm_ggc> (decl_for_wrapper);
+  gcc_checking_assert (decl_for_wrapper && !decl_for_wrapper->get (wrapper));
+  decl_for_wrapper->put (wrapper, fndecl);
+}
+
+/* Returns the wrapper function decl for FNDECL, or null if not set.  */
+
+static tree
+get_contract_wrapper_function (tree fndecl)
+{
+  gcc_checking_assert (fndecl);
+  tree *result = hash_map_safe_get (decl_wrapper_fn, fndecl);
+  return result ? *result : NULL_TREE;
+}
+
+/* Given a wrapper function WRAPPER, find the original function decl.  */
+
+static tree
+get_orig_func_for_wrapper (tree wrapper)
+{
+  gcc_checking_assert (wrapper);
+  tree *result = hash_map_safe_get (decl_for_wrapper, wrapper);
+  return result ? *result : NULL_TREE;
+}
+
+/* Build a declaration for the contract wrapper of a caller FNDECL.
+   We're making a caller side contract check wrapper. For caller side contract
+   checks, postconditions are only checked if check_post is true.
+   Defer the attachment of the contracts to this function until the callee
+   is non-dependent, or we get cases where the conditions can be non-dependent
+   but still need tsubst-ing.  */
+
+static tree
+build_contract_wrapper_function (tree fndecl)
+{
+  if (error_operand_p (fndecl))
+    return error_mark_node;
+
+  /* We should not be trying to build wrappers for templates or functions that
+     are still dependent.  */
+  gcc_checking_assert (!processing_template_decl
+                      && !TYPE_DEPENDENT_P (TREE_TYPE (fndecl)));
+
+  location_t loc = DECL_SOURCE_LOCATION (fndecl);
+
+  /* Fill in the names later.  */
+  tree wrapdecl
+    = build_lang_decl_loc (loc, FUNCTION_DECL, NULL_TREE, TREE_TYPE (fndecl));
+
+  /* Put the wrapper in the same context as the callee.  */
+  DECL_CONTEXT (wrapdecl) = DECL_CONTEXT (fndecl);
+
+  /* This declaration is a contract wrapper function.  */
+  DECL_CONTRACT_WRAPPER (wrapdecl) = true;
+
+  contracts_fixup_names (wrapdecl, fndecl, /*pre*/false, /*wrapper*/true);
+
+  DECL_SOURCE_LOCATION (wrapdecl) = loc;
+  /* The declaration was implicitly generated by the compiler.  */
+  DECL_ARTIFICIAL (wrapdecl) = true;
+  /* Declaration, no definition yet.  */
+  DECL_INITIAL (wrapdecl) = NULL_TREE;
+
+  /* Let the start function code fill in the result decl.  */
+  DECL_RESULT (wrapdecl) = NULL_TREE;
+
+  /* Copy the function parameters, if present.  Suppress (e.g. unused)
+     warnings on them.  */
+  DECL_ARGUMENTS (wrapdecl) = NULL_TREE;
+  if (tree p = DECL_ARGUMENTS (fndecl))
+    {
+      tree *last_a = &DECL_ARGUMENTS (wrapdecl);
+      for (; p; p = TREE_CHAIN (p))
+       {
+         *last_a = copy_decl (p);
+         suppress_warning (*last_a);
+         DECL_CONTEXT (*last_a) = wrapdecl;
+         last_a = &TREE_CHAIN (*last_a);
+       }
+    }
+
+  /* Copy selected attributes from the original function.  */
+  TREE_USED (wrapdecl) = TREE_USED (fndecl);
+
+  /* Copy any alignment added.  */
+  if (DECL_ALIGN (fndecl))
+    SET_DECL_ALIGN (wrapdecl, DECL_ALIGN (fndecl));
+  DECL_USER_ALIGN (wrapdecl) = DECL_USER_ALIGN (fndecl);
+
+  /* Make this function internal.  */
+  TREE_PUBLIC (wrapdecl) = false;
+  DECL_EXTERNAL (wrapdecl) = false;
+  DECL_WEAK (wrapdecl) = false;
+
+  /* We know this is an internal function.  */
+  DECL_INTERFACE_KNOWN (wrapdecl) = true;
+  return wrapdecl;
+}
+
+static tree
+get_or_create_contract_wrapper_function (tree fndecl)
+{
+  tree wrapdecl = get_contract_wrapper_function (fndecl);
+  if (!wrapdecl)
+    {
+      wrapdecl = build_contract_wrapper_function (fndecl);
+      set_contract_wrapper_function (fndecl, wrapdecl);
+    }
+  return wrapdecl;
+}
+
 void
 start_function_contracts (tree fndecl)
 {
@@ -867,6 +1000,12 @@ start_function_contracts (tree fndecl)
   if (!handle_contracts_p (fndecl))
     return;
 
+  /* If this is not a client side check and definition side checks are
+     disabled, do nothing.  */
+  if (!flag_contracts_definition_check
+      && !DECL_CONTRACT_WRAPPER (fndecl))
+    return;
+
   /* Check that the postcondition result name, if any, does not shadow a
      function parameter.  */
   for (tree ca = get_fn_contract_specifiers (fndecl); ca; ca = TREE_CHAIN (ca))
@@ -1032,15 +1171,6 @@ add_post_condition_fn_call (tree fndecl)
   finish_expr_stmt (call);
 }
 
-/* Allow specifying a sub-set of contract kinds to copy.  */
-
-enum contract_match_kind
-{
-  cmk_pre,
-  cmk_post,
-  cmk_all
-};
-
 /* Copy (possibly a sub-set of) contracts from CONTRACTS on FNDECL.  */
 
 static tree
@@ -1195,6 +1325,12 @@ maybe_apply_function_contracts (tree fndecl)
        popped by our caller.  */
     return;
 
+  /* If this is not a client side check and definition side checks are
+     disabled, do nothing.  */
+  if (!flag_contracts_definition_check
+      && !DECL_CONTRACT_WRAPPER (fndecl))
+    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... */
@@ -1360,12 +1496,21 @@ remap_contract (tree src, tree dst, tree contract, bool 
duplicate_p)
    PARM_DECLs have been rewritten to the corresponding PARM_DECL in DEST.  */
 
 tree
-copy_and_remap_contracts (tree dest, tree source)
+copy_and_remap_contracts (tree dest, tree source,
+                         contract_match_kind remap_kind)
 {
   tree last = NULL_TREE, contracts_copy= NULL_TREE;
   tree contracts = get_fn_contract_specifiers (source);
   for (; contracts; contracts = TREE_CHAIN (contracts))
     {
+      if ((remap_kind == cmk_pre
+          && (TREE_CODE (CONTRACT_STATEMENT (contracts))
+              == POSTCONDITION_STMT))
+         || (remap_kind == cmk_post
+             && (TREE_CODE (CONTRACT_STATEMENT (contracts))
+                 == PRECONDITION_STMT)))
+       continue;
+
       /* The first part is copying of the legacy attribute layout - eventually
         this will go away.  */
       tree c = copy_node (contracts);
@@ -1603,6 +1748,124 @@ update_contract_arguments (tree srcdecl, tree destdecl)
     }
 }
 
+/* Checks if a contract check wrapper is needed for fndecl.  */
+
+static bool
+should_contract_wrap_call (bool do_pre, bool do_post)
+{
+  /* Only if the target function actually has any contracts.  */
+  if (!do_pre && !do_post)
+    return false;
+
+
+  return ((flag_contract_client_check > 1)
+         || ((flag_contract_client_check > 0)
+             && do_pre));
+}
+
+/* Possibly replace call with a call to a wrapper function which
+   will do the contracts check required around a CALL to FNDECL.  */
+
+tree
+maybe_contract_wrap_call (tree fndecl, tree call)
+{
+  /* We can be called from build_cxx_call without a known callee.  */
+  if (!fndecl)
+    return call;
+
+  if (error_operand_p (fndecl) || !call || call == error_mark_node)
+    return error_mark_node;
+
+  if (!handle_contracts_p (fndecl))
+    return call;
+
+  bool do_pre = has_active_preconditions (fndecl);
+  bool do_post = has_active_postconditions (fndecl);
+
+  /* Check if we need a wrapper.  */
+  if (!should_contract_wrap_call (do_pre, do_post))
+    return call;
+
+  /* Build the declaration of the wrapper, if we need to.  */
+  tree wrapdecl = get_or_create_contract_wrapper_function (fndecl);
+
+  unsigned nargs = call_expr_nargs (call);
+  vec<tree, va_gc> *argwrap;
+  vec_alloc (argwrap, nargs);
+
+  tree arg;
+  call_expr_arg_iterator iter;
+  FOR_EACH_CALL_EXPR_ARG (arg, iter, call)
+    argwrap->quick_push (arg);
+
+  tree wrapcall = build_call_expr_loc_vec (DECL_SOURCE_LOCATION (wrapdecl),
+                                          wrapdecl, argwrap);
+
+  return wrapcall;
+}
+
+/* Map traversal callback to define a wrapper function.
+   This generates code for client-side contract check wrappers and the
+   noexcept wrapper around the contract violation handler.  */
+
+bool
+define_contract_wrapper_func (const tree& fndecl, const tree& wrapdecl, void*)
+{
+  /* If we already built this function on a previous pass, then do nothing.  */
+  if (DECL_INITIAL (wrapdecl) && DECL_INITIAL (wrapdecl) != error_mark_node)
+    return true;
+
+  gcc_checking_assert (!DECL_HAS_CONTRACTS_P (wrapdecl));
+  /* We check postconditions if postcondition checks are enabled for clients.
+    We should not get here unless there are some checks to make.  */
+  bool check_post = flag_contract_client_check > 1;
+  /* For wrappers on CDTORs we need to refer to the original contracts,
+     when the wrapper is around a clone.  */
+  set_fn_contract_specifiers ( wrapdecl,
+                     copy_and_remap_contracts (wrapdecl, DECL_ORIGIN (fndecl),
+                                               check_post? cmk_all : cmk_pre));
+
+  start_preparsed_function (wrapdecl, /*DECL_ATTRIBUTES*/NULL_TREE,
+                           SF_DEFAULT | SF_PRE_PARSED);
+  tree body = begin_function_body ();
+  tree compound_stmt = begin_compound_stmt (BCS_FN_BODY);
+
+  vec<tree, va_gc> * args = build_arg_list (wrapdecl);
+
+  /* We do not support contracts on virtual functions yet.  */
+  gcc_checking_assert (!DECL_IOBJ_MEMBER_FUNCTION_P (fndecl)
+                      || !DECL_VIRTUAL_P (fndecl));
+
+  tree call = build_thunk_like_call (fndecl, args->length (), args->address 
());
+
+  finish_return_stmt (call);
+
+  finish_compound_stmt (compound_stmt);
+  finish_function_body (body);
+  expand_or_defer_fn (finish_function (/*inline_p=*/false));
+  return true;
+}
+
+/* If any wrapper functions have been declared, emit their definition.
+   This might be called multiple times, as we instantiate functions. When
+   the processing here adds more wrappers, then flag to the caller that
+   possible additional instantiations should be considered.
+   Once instantiations are complete, this will be called with done == true.  */
+
+bool
+emit_contract_wrapper_func (bool done)
+{
+  if (!decl_wrapper_fn || decl_wrapper_fn->is_empty ())
+    return false;
+  size_t start_elements = decl_wrapper_fn->elements ();
+  decl_wrapper_fn->traverse<void *, define_contract_wrapper_func>(NULL);
+  bool more = decl_wrapper_fn->elements () > start_elements;
+  if (done)
+    decl_wrapper_fn->empty ();
+  gcc_checking_assert (!done || !more);
+  return more;
+}
+
 /* Mark most of a contract as being invalid.  */
 
 tree
@@ -1951,6 +2214,12 @@ finish_function_outlined_contracts (tree fndecl)
       || !flag_contract_checks_outlined)
     return;
 
+  /* If this is not a client side check and definition side checks are
+     disabled, do nothing.  */
+  if (!flag_contracts_definition_check
+      && !DECL_CONTRACT_WRAPPER (fndecl))
+    return;
+
   /* 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);
@@ -2460,6 +2729,9 @@ get_src_loc_impl_ptr (location_t loc)
   /* 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);
+  /* We might be a wrapper.  */
+  if (DECL_IS_WRAPPER_FN_P (fndecl))
+    fndecl = get_orig_func_for_wrapper (fndecl);
 
   gcc_checking_assert (fndecl);
   tree impl__
diff --git a/gcc/cp/contracts.h b/gcc/cp/contracts.h
index ef904e82f19d..705de1584f8e 100644
--- a/gcc/cp/contracts.h
+++ b/gcc/cp/contracts.h
@@ -140,6 +140,18 @@ enum detection_mode : uint16_t {
   (DECL_DECLARES_FUNCTION_P (NODE) && DECL_LANG_SPECIFIC (NODE) \
    && CONTRACT_HELPER (NODE) == ldf_contract_post)
 
+#define DECL_IS_WRAPPER_FN_P(NODE) \
+  (DECL_DECLARES_FUNCTION_P (NODE) && DECL_LANG_SPECIFIC (NODE) && \
+   DECL_CONTRACT_WRAPPER (NODE))
+
+/* Allow specifying a sub-set of contract kinds to copy.  */
+enum contract_match_kind
+{
+  cmk_all,
+  cmk_pre,
+  cmk_post
+};
+
 /* contracts.cc */
 
 extern void init_contracts                     (void);
@@ -150,7 +162,7 @@ extern tree finish_contract_condition               
(cp_expr);
 extern void update_late_contract               (tree, tree, cp_expr);
 extern void check_redecl_contract              (tree, tree);
 extern tree invalidate_contract                        (tree);
-extern tree copy_and_remap_contracts           (tree, tree);
+extern tree copy_and_remap_contracts           (tree, tree, 
contract_match_kind = cmk_all);
 extern tree constify_contract_access           (tree);
 extern tree view_as_const                      (tree);
 
@@ -180,16 +192,12 @@ extern void maybe_apply_function_contracts        (tree);
 extern void finish_function_outlined_contracts (tree);
 extern void set_contract_functions             (tree, tree, tree);
 
+extern tree maybe_contract_wrap_call           (tree, tree);
+extern bool emit_contract_wrapper_func         (bool);
 extern void maybe_emit_violation_handler_wrappers (void);
 
 extern tree build_contract_check               (tree);
 
-inline void
-set_decl_contracts (tree decl, tree contract_attrs)
-{
-  set_fn_contract_specifiers (decl, contract_attrs);
-}
-
 /* Test if EXP is a contract const wrapper node.  */
 
 inline bool
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index a1f82947d1e7..e17d95184cde 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3219,9 +3219,10 @@ struct GTY(()) lang_decl_fn {
   unsigned escalated_p : 1;
 
   unsigned xobj_func : 1;
+  unsigned contract_wrapper : 1;
   ENUM_BITFIELD(lang_contract_helper) contract_helper : 2;
 
-  unsigned spare : 5;
+  unsigned spare : 4;
 
   /* 32-bits padding on 64-bit host.  */
 
@@ -3645,6 +3646,11 @@ struct GTY(()) lang_decl {
   (TREE_CODE (STRIP_TEMPLATE (NODE)) == FUNCTION_DECL  \
    && DECL_FUNCTION_XOBJ_FLAG (NODE) == 1)
 
+/* Nonzero for FUNCTION_DECL means that this decl is a contract
+   wrapper function.  */
+#define DECL_CONTRACT_WRAPPER(NODE)    \
+  LANG_DECL_FN_CHECK (NODE)->contract_wrapper
+
 /* Nonzero if NODE is a member function with an object argument,
    in other words, a non-static member function.  */
 #define DECL_OBJECT_MEMBER_FUNCTION_P(NODE) \
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 50b4857793a3..20ee662eea22 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -5893,6 +5893,12 @@ c_parse_final_cleanups (void)
           importer.  */
        continue;
 
+      /* Emit wrappers where needed, and if that causes more to be added then
+        make sure we account for possible additional instantiations.  */
+      if (flag_contracts)
+       if (emit_contract_wrapper_func (/*done*/false))
+         reconsider = true;
+
       /* Write out virtual tables as required.  Writing out the
         virtual table for a template class may cause the
         instantiation of members of that class.  If we write out
@@ -6106,7 +6112,10 @@ c_parse_final_cleanups (void)
     }
 
   if (flag_contracts)
-    maybe_emit_violation_handler_wrappers ();
+    {
+      emit_contract_wrapper_func (/*done*/true);
+      maybe_emit_violation_handler_wrappers ();
+    }
 
   /* All templates have been instantiated.  */
   at_eof = 2;
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 727549a72669..49fa503ddeae 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -228,6 +228,8 @@ in the following sections.
 
-fcontract-evaluation-semantic=@r{[}ignore@r{|}observe@r{|}enforce@r{|}quick_enforce@r{]}
 -fcontracts-conservative-ipa -fcontract-checks-outlined
 -fcontract-disable-optimized-checks
+-fcontracts-client-check=@r{[}none@r{|}pre@r{|}all@r{]}
+-fcontracts-definition-check=@r{[}on@r{|}off@r{]}
 -fcoroutines  -fdiagnostics-all-candidates
 -fno-elide-constructors
 -fno-enforce-eh-specs
@@ -3376,6 +3378,24 @@ When @code{pre} and @code{post} condition checks are 
outlined (using
 @option{-fcontract-checks-outlined}) this option disables the optimisation 
steps
 for those functions which can avoid unwanted elision of checking steps.
 
+@opindex fcontracts-client-check
+@item -fcontracts-client-check=@var{mode}
+This option enables insertion of checks at call sites (caller-side checking).
+
+The value of @var{mode} determines which contract checks are made:
+'@code{none}' No caller/client side checking (default)
+
+'@code{pre}' Preconditions are checked at the caller/client side.
+
+'@code{all}' Both pre and post-conditions are checked at the caller/client 
side.
+
+@opindex fcontracts-definition-check
+@item -fcontracts-definition-check=@var{mode}
+This option introduces the ability to disable callee/definition-side checks,
+which are otherwise enabled by default when the contracts feature is active.
+
+The variable @var{mode} has the values '@code{on}' and '@code{off}'.
+
 @opindex fcoroutines
 @opindex fno-coroutines
 @item -fcoroutines
diff --git 
a/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-all.C
 
b/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-all.C
new file mode 100644
index 000000000000..07c09321c406
--- /dev/null
+++ 
b/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-all.C
@@ -0,0 +1,52 @@
+// { dg-do run { target c++20 } }
+// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe 
-fcontracts-client-check=all" }
+
+
+
+int f(const int a, const int b) pre (a > 2) post(r : r > 2){ return b;  }
+
+
+struct S
+{
+  int f(const int a, const int b) pre (a > 3) post(r : r > 3){ return b;  }
+};
+
+template<typename T>
+struct TS
+{
+  int f(const int a, const T b) pre (a > 4) post(r : r > 4){ return b;  }
+
+  template <typename U>
+  int tf(const int a, const U b) pre (a > 5) post(r : r > 5){ return b;  }
+};
+
+int main(int, char**)
+{
+  f(1,1);
+
+  S s;
+  s.f(1,1);
+
+  TS<int> ts;
+  ts.f(1,1);
+
+  ts.tf(1,1);
+  return 0;
+}
+
+// { dg-output "contract violation in function int f.int, int. at .*: a > 
2.*(\n|\r\n|\r)" }
+// { dg-output "contract violation in function int f.int, int. at .*: a > 
2.*(\n|\r\n|\r)" }
+// { dg-output "contract violation in function int f.int, int. at .*: r > 
2.*(\n|\r\n|\r)" }
+// { dg-output "contract violation in function int f.int, int. at .*: r > 
2.*(\n|\r\n|\r)" }
+// { dg-output "contract violation in function int S::f.int, int. at .*: a > 
3.*(\n|\r\n|\r)" }
+// { dg-output "contract violation in function int S::f.int, int. at .*: a > 
3.*(\n|\r\n|\r)" }
+// { dg-output "contract violation in function int S::f.int, int. at .*: r > 
3.*(\n|\r\n|\r)" }
+// { dg-output "contract violation in function int S::f.int, int. at .*: r > 
3.*(\n|\r\n|\r)" }
+// { dg-output "contract violation in function int TS<T>::f.int, T. .with T = 
int. at .*: a > 4.*(\n|\r\n|\r)" }
+// { dg-output "contract violation in function int TS<T>::f.int, T. .with T = 
int. at .*: a > 4.*(\n|\r\n|\r)" }
+// { dg-output "contract violation in function int TS<T>::f.int, T. .with T = 
int. at .*: r > 4.*(\n|\r\n|\r)" }
+// { dg-output "contract violation in function int TS<T>::f.int, T. .with T = 
int. at .*: r > 4.*(\n|\r\n|\r)" }
+// { dg-output "contract violation in function int TS<T>::tf.int, U. .with U = 
int; T = int. at .*: a > 5.*(\n|\r\n|\r)" }
+// { dg-output "contract violation in function int TS<T>::tf.int, U. .with U = 
int; T = int. at .*: a > 5.*(\n|\r\n|\r)" }
+// { dg-output "contract violation in function int TS<T>::tf.int, U. .with U = 
int; T = int. at .*: r > 5.*(\n|\r\n|\r)" }
+// { dg-output "contract violation in function int TS<T>::tf.int, U. .with U = 
int; T = int. at .*: r > 5.*(\n|\r\n|\r)" }
diff --git 
a/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-non-trivial.C
 
b/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-non-trivial.C
new file mode 100644
index 000000000000..5ea8b61e7d36
--- /dev/null
+++ 
b/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-non-trivial.C
@@ -0,0 +1,18 @@
+// { dg-do compile { target c++20 } }
+// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe 
-fcontracts-client-check=all" }
+
+
+struct S{
+  S(){};
+  S(const S&){}
+  ~S(){};
+  int x = 0;
+};
+
+void f(S s) pre(s.x == 1 ) {};
+
+int main()
+{
+  S s;
+  f(s);
+}
diff --git 
a/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-none.C
 
b/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-none.C
new file mode 100644
index 000000000000..22e0ded4241c
--- /dev/null
+++ 
b/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-none.C
@@ -0,0 +1,64 @@
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-fcontracts -fcontracts-client-check=none" }
+static int pre_check = 0;
+bool fpre()
+{
+  pre_check++;
+  return true;
+}
+
+static int post_check = 0;
+bool fpost()
+{
+  post_check++;
+  return true;
+}
+
+
+int f(const int a, const int b) pre (fpre()) post(fpost()){ return b;  }
+
+
+struct S
+{
+  int f(const int a, const int b) post(fpost()){ return b;  }
+};
+
+template<typename T>
+struct TS
+{
+  int f(const int a, const T b) pre (fpre()) { return b;  }
+
+  template <typename U>
+  int tf(const int a, const U b) pre (fpre()) post(fpost()){ return b;  }
+};
+
+int main(int, char**)
+{
+  f(1,1);
+  contract_assert(pre_check == 1);
+  contract_assert(post_check == 1);
+
+  pre_check = 0;
+  post_check = 0;
+
+  S s;
+  s.f(1,1);
+  contract_assert(post_check == 1);
+
+  pre_check = 0;
+  post_check = 0;
+
+  TS<int> ts;
+  ts.f(1,1);
+  contract_assert(pre_check == 1);
+
+  pre_check = 0;
+  post_check = 0;
+
+  ts.tf(1,1);
+  contract_assert(pre_check == 1);
+  contract_assert(post_check == 1);
+
+
+  return 0;
+}
diff --git 
a/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-pre.C
 
b/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-pre.C
new file mode 100644
index 000000000000..1a3a693e3ab2
--- /dev/null
+++ 
b/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-pre.C
@@ -0,0 +1,65 @@
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-fcontracts -fcontracts-client-check=pre" }
+
+static int pre_check = 0;
+bool fpre()
+{
+  pre_check++;
+  return true;
+}
+
+static int post_check = 0;
+bool fpost()
+{
+  post_check++;
+  return true;
+}
+
+
+int f(const int a, const int b) pre (fpre()) post(fpost()){ return b;  }
+
+
+struct S
+{
+  int f(const int a, const int b) post(fpost()){ return b;  }
+};
+
+template<typename T>
+struct TS
+{
+  int f(const int a, const T b) pre (fpre()) { return b;  }
+
+  template <typename U>
+  int tf(const int a, const U b) pre (fpre()) post(fpost()){ return b;  }
+};
+
+int main(int, char**)
+{
+  f(1,1);
+  contract_assert(pre_check == 2);
+  contract_assert(post_check == 1);
+
+  pre_check = 0;
+  post_check = 0;
+
+  S s;
+  s.f(1,1);
+  contract_assert(post_check == 1);
+
+  pre_check = 0;
+  post_check = 0;
+
+  TS<int> ts;
+  ts.f(1,1);
+  contract_assert(pre_check == 2);
+
+  pre_check = 0;
+  post_check = 0;
+
+  ts.tf(1,1);
+  contract_assert(pre_check == 2);
+  contract_assert(post_check == 1);
+
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/ctor.C 
b/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/ctor.C
new file mode 100644
index 000000000000..3aff02766017
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/ctor.C
@@ -0,0 +1,23 @@
+// { dg-do compile { target c++20 } }
+// { dg-additional-options "-fcontracts -fcontracts-client-check=pre" }
+
+struct X
+{
+    X (int x) noexcept
+     pre (x>1)
+    {
+      try {
+       int i = 1;
+      }
+      catch(...) {
+      }
+    }
+};
+
+int main()
+{
+  try {
+    X x(-42);
+  } catch (...) {
+  }
+}
diff --git 
a/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/freefunc-noexcept-post.C
 
b/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/freefunc-noexcept-post.C
new file mode 100644
index 000000000000..99c080f52db2
--- /dev/null
+++ 
b/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/freefunc-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
+// with caller side checks.
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe 
-fcontracts-client-check=all " }
+
+#include <contracts>
+#include <iostream>
+#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 >= 0)
+{
+  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/callerside-checks/freefunc-noexcept-pre.C
 
b/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/freefunc-noexcept-pre.C
new file mode 100644
index 000000000000..7e3968b453bb
--- /dev/null
+++ 
b/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/freefunc-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 post condition on a member function
+// with caller side checks.
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe 
-fcontracts-client-check=all " }
+
+#include <contracts>
+#include <iostream>
+#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 >= 0)
+{
+  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/definition-checks/contract-assert-no-def-check.C
 
b/gcc/testsuite/g++.dg/contracts/cpp26/definition-checks/contract-assert-no-def-check.C
new file mode 100644
index 000000000000..74d743b38e1a
--- /dev/null
+++ 
b/gcc/testsuite/g++.dg/contracts/cpp26/definition-checks/contract-assert-no-def-check.C
@@ -0,0 +1,25 @@
+// Check that contract asserts are checked when the definition side contracts
+// are turned off
+// { dg-do run { target c++26 } }
+// { dg-options "-fcontracts -fcontracts-definition-check=off 
-fcontract-evaluation-semantic=observe" }
+
+#include <cstdlib>
+
+bool terminating_check(){
+  std::exit(-1);
+  return true;
+}
+// pre and post check would cause termination
+void foo(int i) noexcept pre(terminating_check()) post(terminating_check()) {
+
+    contract_assert(i > 4);
+
+}
+
+int main(int, char**)
+{
+
+  foo(1);
+  return 0;
+}
+// { dg-output "contract violation in function void foo.int. at .*: i > 
4.*(\n|\r\n|\r)" }
diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/non-trivial-ice.C 
b/gcc/testsuite/g++.dg/contracts/cpp26/non-trivial-ice.C
new file mode 100644
index 000000000000..6fd8a2f7a0c5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/cpp26/non-trivial-ice.C
@@ -0,0 +1,21 @@
+// Test that there is no ICE with outlined contracts, caller side checks and
+// Nontrivial types in inlined precondition checks
+// { dg-do compile { target c++23 } }
+// { dg-options "-fcontracts -fcontracts-client-check=all" }
+struct NonTrivial{
+  NonTrivial(){};
+  NonTrivial(const NonTrivial&){}
+  ~NonTrivial(){};
+  int x = 0;
+};
+
+void f(const NonTrivial s) pre(s.x >0);
+
+void f(const NonTrivial g) {};
+
+
+int main()
+{
+  NonTrivial nt;
+  f(nt);
+}
-- 
2.50.1 (Apple Git-155)

Reply via email to