From a07766f4f2b4d2f06209793b28e90a728d5fdbbf Mon Sep 17 00:00:00 2001 From: Nina Ranns Date: Wed, 22 Jan 2025 15:06:00 +0000 Subject: [PATCH 1/2] adding flag_contract_nonattr_client_check flag --- gcc/c-family/c.opt | 4 +++ gcc/cp/contracts.cc | 21 +++++++++++++ gcc/cp/cp-tree.h | 9 +++++- gcc/cp/decl.cc | 8 ++--- .../virtual-func-no-def-check.C | 29 +++++++++++++++++ .../virtual-func-no-def-check2.C | 31 +++++++++++++++++++ 6 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 gcc/testsuite/g++.dg/contracts/cpp26/definition-checks/virtual-func-no-def-check.C create mode 100644 gcc/testsuite/g++.dg/contracts/cpp26/definition-checks/virtual-func-no-def-check2.C diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 7e425132affbe..1152ce042e501 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-def-contracts= +C++ Var(flag_contracts_nonattr_def_contracts) Enum(on_off) Joined Init(1) RejectNegative +-fcontracts-nonattr-def-contract=[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..a473f043324b1 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_def_contracts && + !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_def_contracts && + !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_def_contracts && + !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..39c4c1dd37c5f --- /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-def-contracts=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..faf2da4b71da4 --- /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-def-contracts=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)" } From 8e8888cc60e4daf6f713f7f60c7f9c29db69422a Mon Sep 17 00:00:00 2001 From: Nina Ranns Date: Wed, 22 Jan 2025 18:12:41 +0000 Subject: [PATCH 2/2] review comments --- gcc/c-family/c.opt | 6 +++--- gcc/cp/contracts.cc | 6 +++--- .../cpp26/definition-checks/virtual-func-no-def-check.C | 2 +- .../cpp26/definition-checks/virtual-func-no-def-check2.C | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 1152ce042e501..dd2eeeaf896ef 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -1964,9 +1964,9 @@ 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-def-contracts= -C++ Var(flag_contracts_nonattr_def_contracts) Enum(on_off) Joined Init(1) RejectNegative --fcontracts-nonattr-def-contract=[on|off] Enable or disable contract checks on the definition side for all functions (default on). +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) diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc index a473f043324b1..9e9f73df6a23d 100644 --- a/gcc/cp/contracts.cc +++ b/gcc/cp/contracts.cc @@ -2774,7 +2774,7 @@ start_function_contracts (tree decl1) /* If this is not a client side check and definition side checks are disabled, do nothing. */ - if (!flag_contracts_nonattr_def_contracts && + if (!flag_contracts_nonattr_definition_check && !DECL_CONTRACT_WRAPPER(decl1)) return; @@ -2895,7 +2895,7 @@ maybe_apply_function_contracts (tree fndecl) /* If this is not a client side check and definition side checks are disabled, do nothing. */ - if (!flag_contracts_nonattr_def_contracts && + if (!flag_contracts_nonattr_definition_check && !DECL_CONTRACT_WRAPPER(fndecl)) return; @@ -3034,7 +3034,7 @@ finish_function_contracts (tree fndecl) /* If this is not a client side check and definition side checks are disabled, do nothing. */ - if (!flag_contracts_nonattr_def_contracts && + if (!flag_contracts_nonattr_definition_check && !DECL_CONTRACT_WRAPPER(fndecl)) return; 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 index 39c4c1dd37c5f..c8a17538d36cf 100644 --- 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 @@ -1,7 +1,7 @@ // 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-def-contracts=off " } +// { dg-options "-std=c++2a -fcontracts -fcontracts-nonattr -fcontracts-nonattr-definition-check=off " } struct Base { 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 index faf2da4b71da4..3053ea4fb7b18 100644 --- 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 @@ -1,7 +1,7 @@ // 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-def-contracts=off -fcontract-continuation-mode=on" } +// { dg-options "-std=c++2a -fcontracts -fcontracts-nonattr -fcontracts-nonattr-definition-check=off -fcontract-continuation-mode=on" } struct Base {