diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 7e425132affbe..dd2eeeaf896ef 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -1964,6 +1964,10 @@ fcontracts-nonattr-client-contracts= C++ Joined RejectNegative Enum(client_contract_check) Var(flag_contract_nonattr_client_check) Init (0) -fcontracts-nonattr-client-check=[none|pre|all] Select which contracts will be checked on the client side for non virtual functions +fcontracts-nonattr-definition-check= +C++ Joined RejectNegative Enum(on_off) Var(flag_contracts_nonattr_definition_check) Init(1) +-fcontracts-nonattr-definition-check=[on|off] Enable or disable contract checks on the definition side for all functions (default on). + fcoroutines C++ LTO Var(flag_coroutines) Enable C++ coroutines (experimental). diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc index 6b70e910869c1..9e9f73df6a23d 100644 --- a/gcc/cp/contracts.cc +++ b/gcc/cp/contracts.cc @@ -1867,6 +1867,9 @@ build_contract_wrapper_function (tree fndecl, bool is_cvh, bool check_post = tru /* no function body at present * */ DECL_INITIAL (wrapdecl) = error_mark_node; + /* This declaration is a contract wrapper function. */ + DECL_CONTRACT_WRAPPER(wrapdecl) = true; + /* Build our result decl. */ tree resdecl = build_decl (loc, RESULT_DECL, 0, wrapper_return_type); DECL_CONTEXT (resdecl) = wrapdecl; @@ -2769,6 +2772,12 @@ start_function_contracts (tree decl1) if (!handle_contracts_p (decl1)) return; + /* If this is not a client side check and definition side checks are + disabled, do nothing. */ + if (!flag_contracts_nonattr_definition_check && + !DECL_CONTRACT_WRAPPER(decl1)) + return; + /* Check that the user did not try to shadow a function parameter with the specified postcondition result name. */ if (flag_contracts_nonattr) @@ -2884,6 +2893,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_nonattr_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... */ @@ -3017,6 +3032,12 @@ finish_function_contracts (tree fndecl) || !outline_contracts_p (fndecl)) return; + /* If this is not a client side check and definition side checks are + disabled, do nothing. */ + if (!flag_contracts_nonattr_definition_check && + !DECL_CONTRACT_WRAPPER(fndecl)) + return; + for (tree ca = DECL_CONTRACTS (fndecl); ca; ca = CONTRACT_CHAIN (ca)) { tree contract = CONTRACT_STATEMENT (ca); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index d44587a4b2ef8..3e886f29cda46 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -3039,9 +3039,11 @@ struct GTY(()) lang_decl_fn { unsigned coroutine_p : 1; unsigned implicit_constexpr : 1; unsigned escalated_p : 1; + unsigned xobj_func : 1; + unsigned contract_wrapper : 1; - unsigned spare : 7; + unsigned spare : 6; /* 32-bits padding on 64-bit host. */ @@ -3462,6 +3464,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/decl.cc b/gcc/cp/decl.cc index 69f03a9754048..ad35bdca49204 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -19059,9 +19059,6 @@ finish_function (bool inline_p) if (fndecl == NULL_TREE || fndecl == error_mark_node) return error_mark_node; - bool do_contracts = (DECL_HAS_CONTRACTS_P (fndecl) - && !processing_template_decl); - if (!DECL_OMP_DECLARE_REDUCTION_P (fndecl)) finish_lambda_scope (); @@ -19104,7 +19101,7 @@ finish_function (bool inline_p) current_eh_spec_block); /* If outlining succeeded, then add contracts handling if needed. */ - if (coroutine->cp_valid_coroutine () && do_contracts) + if (coroutine->cp_valid_coroutine ()) maybe_apply_function_contracts (fndecl); } else @@ -19122,8 +19119,7 @@ finish_function (bool inline_p) (TREE_TYPE (current_function_decl)), current_eh_spec_block); - if (do_contracts) - maybe_apply_function_contracts (current_function_decl); + maybe_apply_function_contracts (current_function_decl); } diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/definition-checks/virtual-func-no-def-check.C b/gcc/testsuite/g++.dg/contracts/cpp26/definition-checks/virtual-func-no-def-check.C new file mode 100644 index 0000000000000..c8a17538d36cf --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/definition-checks/virtual-func-no-def-check.C @@ -0,0 +1,29 @@ +// check that an invocation of a virtual function through the base class does not +// check contracts of the derived function, which are definition side contracts +// { dg-do run } +// { dg-options "-std=c++2a -fcontracts -fcontracts-nonattr -fcontracts-nonattr-definition-check=off " } + +struct Base +{ + virtual int f(const int a){ return 0;}; +}; + +struct Child : Base +{ + virtual int f(const int a) pre (a > 14) post(r:r >2){ return 1;} +}; + +int fooBase(Base& b) +{ + return b.f(1); +} + +int main(int, char**) +{ + Base b; + Child c; + + fooBase (c); + + return 0; +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/definition-checks/virtual-func-no-def-check2.C b/gcc/testsuite/g++.dg/contracts/cpp26/definition-checks/virtual-func-no-def-check2.C new file mode 100644 index 0000000000000..3053ea4fb7b18 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/definition-checks/virtual-func-no-def-check2.C @@ -0,0 +1,31 @@ +// check that an invocation of a virtual function through the base class checks +// the base class contracts when definition side contracts are turned off +// { dg-do run } +// { dg-options "-std=c++2a -fcontracts -fcontracts-nonattr -fcontracts-nonattr-definition-check=off -fcontract-continuation-mode=on" } + +struct Base +{ + virtual int f(const int a) pre (a > 14) post(r:r > 2){ return 0;}; +}; + +struct Child : Base +{ + virtual int f(const int a) pre (a > 14) post(r:r > 2){ return 1;} +}; + +int fooBase(Base& b) +{ + return b.f(1); +} + +int main(int, char**) +{ + Base b; + Child c; + + fooBase (c); + + return 0; +} +// { dg-output "contract violation in function .*contract_wrapper at .*: a > 14.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function .*contract_wrapper at .*: r > 2.*(\n|\r\n|\r)" }