From: Nina Ranns <[email protected]>
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.
* mangle.cc (write_mangled_name): Add mangling for 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 | 5 +-
gcc/cp/contracts.cc | 327 +++++++++++++++++-
gcc/cp/contracts.h | 21 +-
gcc/cp/cp-tree.h | 8 +-
gcc/cp/decl2.cc | 11 +-
gcc/cp/mangle.cc | 4 +
.../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, 753 insertions(+), 18 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 f29f4f2d27f..e001d87f5e6 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1932,6 +1932,35 @@ C++ ObjC++ Var(flag_contracts_conservative_ipa) Init(1)
-fcontracts-conservative-ipa Do not allow certain optimisations between
functions in contract assertions.
+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 92e3f0bec6d..f42f4153252 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -11582,7 +11582,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
@@ -11594,6 +11594,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)))
{
@@ -11601,6 +11602,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 56247d775f9..3dfdfaca36b 100644
--- a/gcc/cp/contracts.cc
+++ b/gcc/cp/contracts.cc
@@ -345,13 +345,6 @@ retain_decl (tree decl, copy_body_data *)
return decl;
}
-enum contract_match_kind
-{
- cmk_pre,
- cmk_post,
- cmk_all
-};
-
/* Copy all contracts from ATTR and apply them to FNDECL. */
static tree
@@ -844,6 +837,8 @@ build_contract_condition_function (tree fndecl, bool pre)
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);
DECL_VIRTUAL_P (fn) = false;
@@ -909,6 +904,173 @@ 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;
+}
+
+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;
+}
+
+static tree
+contracts_fixup_cdtorname (tree idin)
+{
+ const char *n = IDENTIFIER_POINTER (idin);
+ size_t l = strlen (n);
+ char *nn = xasprintf ("%.*s_", (int)l-1, n);
+ tree nid = get_identifier (nn);
+ free (nn);
+ return nid;
+}
+
+/* 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;
+
+ tree fnname;
+ if (DECL_NAME (fndecl) && IDENTIFIER_CDTOR_P (DECL_NAME (fndecl)))
+ fnname = contracts_fixup_cdtorname (DECL_NAME (fndecl));
+ else
+ fnname = copy_node (DECL_NAME (fndecl));
+ location_t loc = DECL_SOURCE_LOCATION (fndecl);
+
+ /* Handle the arg types list. */
+ tree wrapper_args = NULL_TREE;
+ tree *last = &wrapper_args;
+ for (tree arg_type = TYPE_ARG_TYPES (TREE_TYPE (fndecl)); arg_type;
+ arg_type = TREE_CHAIN (arg_type))
+ {
+ if (arg_type == void_list_node)
+ {
+ *last = void_list_node;
+ break;
+ }
+ *last = build_tree_list (TREE_PURPOSE (arg_type), TREE_VALUE (arg_type));
+ last = &TREE_CHAIN (*last);
+ }
+
+ tree wrapper_return_type = copy_node (TREE_TYPE (TREE_TYPE (fndecl)));
+ tree wrapper_type = build_function_type (wrapper_return_type, wrapper_args);
+
+ /* Contract violation wrapper function is always noexcept. Otherwise,
+ the wrapper function is noexcept if the checked function is noexcept. */
+
+ if (flag_exceptions && type_noexcept_p (TREE_TYPE (fndecl)))
+ wrapper_type = build_exception_variant (wrapper_type, noexcept_true_spec);
+
+ tree wrapdecl
+ = build_lang_decl_loc (loc, FUNCTION_DECL, fnname, wrapper_type);
+
+ /* 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;
+
+ 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 that the FE added. */
+ if (DECL_ALIGN (fndecl))
+ SET_DECL_ALIGN (wrapdecl, DECL_ALIGN (fndecl));
+ /* Copy any alignment the user added. */
+ DECL_USER_ALIGN (wrapdecl) = DECL_USER_ALIGN (fndecl);
+
+ /* Make this function internal. */
+ TREE_PUBLIC (wrapdecl) = false;
+ DECL_EXTERNAL (wrapdecl) = false;
+ DECL_WEAK (wrapdecl) = false;
+
+ /* ??? copied from build_contract_condition_function. */
+ DECL_INTERFACE_KNOWN (wrapdecl) = true;
+
+ /* Update various inline related declaration properties. */
+ //DECL_DECLARED_INLINE_P (wrapdecl) = true;
+ DECL_DISREGARD_INLINE_LIMITS (wrapdecl) = true;
+ suppress_warning (wrapdecl);
+
+ 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)
{
@@ -918,6 +1080,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;
+
/* 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. */
@@ -1181,6 +1349,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... */
@@ -1358,12 +1532,19 @@ 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, contract_attrs = NULL_TREE;
tree attr = get_fn_contract_specifiers (source);
for (; attr; attr = NEXT_CONTRACT_ATTR (attr))
{
+ if ((remap_kind == cmk_pre
+ && (TREE_CODE (CONTRACT_STATEMENT (attr)) == POSTCONDITION_STMT))
+ || (remap_kind == cmk_post
+ && (TREE_CODE (CONTRACT_STATEMENT (attr)) == PRECONDITION_STMT)))
+ continue;
+
tree c = copy_node (attr);
TREE_VALUE (c) = build_tree_list (TREE_PURPOSE (TREE_VALUE (c)),
copy_node (CONTRACT_STATEMENT (c)));
@@ -1607,6 +1788,127 @@ void 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;
+
+ /* FIXME: Maybe we should check if fndecl is still dependent? */
+
+ 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. Client side
wrapping is
+ not supported for cxx2a contracts. */
+ 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
@@ -1968,6 +2270,12 @@ finish_function_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;
+
tree attr = get_fn_contract_specifiers (fndecl);
for (; attr; attr = NEXT_CONTRACT_ATTR (attr))
{
@@ -2459,6 +2767,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 6826a8aea61..d7a3ea76851 100644
--- a/gcc/cp/contracts.h
+++ b/gcc/cp/contracts.h
@@ -145,6 +145,17 @@ 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))
+
+enum contract_match_kind
+{
+ cmk_all,
+ cmk_pre,
+ cmk_post
+};
+
/* contracts.cc */
extern tree grok_contract (tree, tree, tree, cp_expr,
location_t);
@@ -153,7 +164,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);
@@ -183,17 +194,13 @@ extern void maybe_apply_function_contracts (tree);
extern void finish_function_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 init_builtin_contract_violation_type (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 f5252eba50b..e33caa23492 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3107,9 +3107,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. */
@@ -3533,6 +3534,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 3d83f13769d..da9b406aa3b 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -5598,6 +5598,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
@@ -5809,7 +5815,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/cp/mangle.cc b/gcc/cp/mangle.cc
index 4d190710f8a..c7c47cb7050 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -829,6 +829,10 @@ write_mangled_name (const tree decl, bool top_level)
write_encoding (mdecl);
}
+ /* Identify helper functions for contracts support. */
+ if (DECL_IS_WRAPPER_FN_P (decl))
+ write_string (JOIN_STR "contract_wrapper");
+
if (pre)
write_string (JOIN_STR "pre");
else if (post)
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 00000000000..07c09321c40
--- /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 00000000000..5ea8b61e7d3
--- /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 00000000000..89ec6cd6d00
--- /dev/null
+++
b/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-none.C
@@ -0,0 +1,64 @@
+// { dg-do run { target c++20 } }
+// { 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 00000000000..66a06de8615
--- /dev/null
+++
b/gcc/testsuite/g++.dg/contracts/cpp26/callerside-checks/callerside-checks-pre.C
@@ -0,0 +1,65 @@
+// { dg-do run { target c++20 } }
+// { 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 00000000000..3aff0276601
--- /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 00000000000..cd046250234
--- /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 }
+// { dg-options "-std=c++2a -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 00000000000..88b78726013
--- /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 }
+// { dg-options "-std=c++2a -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 00000000000..b453fed1a70
--- /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++20 } }
+// { 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 00000000000..6fd8a2f7a0c
--- /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.39.5 (Apple Git-154)