From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2122) id 7C0CB3858401; Thu, 3 Nov 2022 19:45:59 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 7C0CB3858401 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1667504759; bh=IhIGbB9XIQrXuP1jzEzBSyhPkwCX5IdF1TkuBts8fHE=; h=From:To:Subject:Date:From; b=UbkeXktHfJamPolCOT34ue7CN+RgHf6gOSTHBO9KCNBMHml/MjNpUQOjAtcWIkC8j Pq9OBU1UdcSqMU3qK4W7YeQys4rJaJAVn45cJoiwe0HLy8eiBI4U94Vtx4E00c8hk8 S4GX6M/tJ9Bqy9w8mNHNEWklpmjrezlDp1hB8Ni8= Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Jason Merrill To: gcc-cvs@gcc.gnu.org Subject: [gcc/devel/c++-contracts] c++: friend contracts are in complete-class context X-Act-Checkin: gcc X-Git-Author: Jason Merrill X-Git-Refname: refs/heads/devel/c++-contracts X-Git-Oldrev: a05f106bafd5a74e677e3bddab6eeabbae362dc8 X-Git-Newrev: e378b7f8250d1e1b029c0254dc17492fa16e9de4 Message-Id: <20221103194559.7C0CB3858401@sourceware.org> Date: Thu, 3 Nov 2022 19:45:59 +0000 (GMT) List-Id: https://gcc.gnu.org/g:e378b7f8250d1e1b029c0254dc17492fa16e9de4 commit e378b7f8250d1e1b029c0254dc17492fa16e9de4 Author: Jason Merrill Date: Wed Nov 2 15:21:29 2022 -0400 c++: friend contracts are in complete-class context Comparing friend contracts to a previous declaration was awkward because at the point of duplicate_decls we haven't parsed the new ones yet, and we're about to throw away one of the decls. But conveniently, there's already defer_guarded_contract_match to handle this. This reverts commit 9c0d8bfebc32d5e2c35ed61753440fb175a87dcf. gcc/cp/ChangeLog: * contracts.cc (check_for_mismatched_contracts): Only check new_contract for deferred. (match_deferred_contracts): Set processing_template_decl. (duplicate_contracts): Call defer_guarded_contract_match for friend decl. Handle templates. * decl.cc (duplicate_decls): Use it for templates. * parser.h (struct cp_parser): Remove declaring_friend_p. * parser.cc (cp_parser_new): Don't clear it. (cp_parser_direct_declarator): Don't set it. (cp_parser_contract_attribute_spec): Don't check it. * contracts.h (match_contract_conditions): Remove. gcc/testsuite/ChangeLog: * g++.dg/contracts/contracts-friend1.C: Revert. * g++.dg/contracts/contracts-nested-class1.C: Revert. * g++.dg/contracts/contracts-redecl7.C: Revert. * g++.dg/contracts/contracts-redecl8.C: Revert. Diff: --- gcc/cp/contracts.h | 1 - gcc/cp/parser.h | 6 ---- gcc/cp/contracts.cc | 21 ++++++++++---- gcc/cp/decl.cc | 11 +------- gcc/cp/parser.cc | 17 +++-------- gcc/testsuite/g++.dg/contracts/contracts-friend1.C | 12 +++++--- .../g++.dg/contracts/contracts-nested-class1.C | 4 +-- gcc/testsuite/g++.dg/contracts/contracts-redecl7.C | 33 +++++++++++++++++++++- gcc/testsuite/g++.dg/contracts/contracts-redecl8.C | 3 +- 9 files changed, 64 insertions(+), 44 deletions(-) diff --git a/gcc/cp/contracts.h b/gcc/cp/contracts.h index ad382cf187b..4050a38708b 100644 --- a/gcc/cp/contracts.h +++ b/gcc/cp/contracts.h @@ -286,7 +286,6 @@ extern bool check_postcondition_result (tree, tree, location_t); extern tree get_precondition_function (tree); extern tree get_postcondition_function (tree); extern void duplicate_contracts (tree, tree); -extern bool match_contract_conditions (location_t, tree, location_t, tree, contract_matching_context); extern void match_deferred_contracts (tree); extern void defer_guarded_contract_match (tree, tree, tree); extern bool diagnose_misapplied_contracts (tree); diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h index f695f4566b4..5737146dd42 100644 --- a/gcc/cp/parser.h +++ b/gcc/cp/parser.h @@ -315,12 +315,6 @@ struct GTY(()) cp_parser { direct-declarator. */ bool in_declarator_p; - /* TRUE if the decl-specifier-seq preceding a declarator includes - the 'friend' specifier. This prevents attributes on friend function - declarations from being parsed in the complete class context. */ - /* ??? But they should be; maybe use defer_guarded_contract_match? */ - bool declaring_friend_p; - /* TRUE if we are presently parsing a template-argument-list. */ bool in_template_argument_list_p; diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc index 6db3b750fda..38eb4ad9bfe 100644 --- a/gcc/cp/contracts.cc +++ b/gcc/cp/contracts.cc @@ -1147,9 +1147,8 @@ check_for_mismatched_contracts (tree old_attr, tree new_attr, return true; } - /* Two deferred contracts tentatively match. */ - if (CONTRACT_CONDITION_DEFERRED_P (old_contract) - && CONTRACT_CONDITION_DEFERRED_P (new_contract)) + /* A deferred contract tentatively matches. */ + if (CONTRACT_CONDITION_DEFERRED_P (new_contract)) return false; /* Compare the conditions of the contracts. We fold immediately to avoid @@ -1275,6 +1274,9 @@ match_deferred_contracts (tree decl) gcc_assert(!contract_any_deferred_p (DECL_CONTRACTS (decl))); + processing_template_decl_sentinel ptds; + processing_template_decl = uses_template_parms (decl); + /* Do late contract matching. */ for (tree pending = *tp; pending; pending = TREE_CHAIN (pending)) { @@ -2093,12 +2095,16 @@ apply_postcondition_to_return (tree expr) } /* A subroutine of duplicate_decls. Diagnose issues in the redeclaration of - guarded functions. Note that attributes on new friend declarations have not - been processed yet, so we take those from the global above. */ + guarded functions. */ void duplicate_contracts (tree newdecl, tree olddecl) { + if (TREE_CODE (newdecl) == TEMPLATE_DECL) + newdecl = DECL_TEMPLATE_RESULT (newdecl); + if (TREE_CODE (olddecl) == TEMPLATE_DECL) + olddecl = DECL_TEMPLATE_RESULT (olddecl); + /* Compare contracts to see if they match. */ tree old_contracts = DECL_CONTRACTS (olddecl); tree new_contracts = DECL_CONTRACTS (newdecl); @@ -2134,6 +2140,11 @@ duplicate_contracts (tree newdecl, tree olddecl) new_loc, new_contracts, cmc_declaration)) return; + if (DECL_UNIQUE_FRIEND_P (newdecl)) + /* Newdecl's contracts are still DEFERRED_PARSE, and we're about to + collapse it into olddecl, so stash away olddecl's contracts for + later comparison. */ + defer_guarded_contract_match (olddecl, olddecl, old_contracts); } /* Handle cases where contracts are omitted in one or the other diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 9e591d592cf..f4d6ee5dede 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -2278,16 +2278,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden) gcc_assert (!DECL_TEMPLATE_SPECIALIZATIONS (newdecl)); /* Make sure the contracts are equivalent. */ - tree old_contracts = DECL_CONTRACTS (old_result); - tree new_contracts = DECL_CONTRACTS (new_result); - if (old_contracts && new_contracts) - { - match_contract_conditions (DECL_SOURCE_LOCATION (old_result), - old_contracts, - DECL_SOURCE_LOCATION (new_result), - new_contracts, - cmc_declaration); - } + duplicate_contracts (newdecl, olddecl); /* Remove contracts from old_result so they aren't appended to old_result by the merge function. */ diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 96e1537356e..4d4475b80f0 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -4310,9 +4310,6 @@ cp_parser_new (cp_lexer *lexer) /* We are not processing a declarator. */ parser->in_declarator_p = false; - /* We are not parsing a friend declaration. */ - parser->declaring_friend_p = false; - /* We are not processing a template-argument-list. */ parser->in_template_argument_list_p = false; @@ -23285,10 +23282,7 @@ cp_parser_direct_declarator (cp_parser* parser, = cp_parser_exception_specification_opt (parser, flags); - bool saved_declaring_friend_p = parser->declaring_friend_p; - parser->declaring_friend_p = friend_p; attrs = cp_parser_std_attribute_spec_seq (parser); - parser->declaring_friend_p = saved_declaring_friend_p; cp_omp_declare_simd_data odsd; if ((flag_openmp || flag_openmp_simd) @@ -29635,14 +29629,11 @@ cp_parser_contract_attribute_spec (cp_parser *parser, tree attribute) cp_parser_require (parser, CPP_COLON, RT_COLON); - /* Defer the parsing of pre/post contracts inside class definitions. - Note that friends are not member functions and thus not in the complete - class context. */ + /* Defer the parsing of pre/post contracts inside class definitions. */ tree contract; - if (!assertion_p - && current_class_type - && TYPE_BEING_DEFINED (current_class_type) - && !parser->declaring_friend_p) + if (!assertion_p && + current_class_type && + TYPE_BEING_DEFINED (current_class_type)) { /* Skip until we reach an unenclose ']'. If we ran into an unnested ']' that doesn't close the attribute, return an error and let the attribute diff --git a/gcc/testsuite/g++.dg/contracts/contracts-friend1.C b/gcc/testsuite/g++.dg/contracts/contracts-friend1.C index 01cebb0c2fb..0ccfbe2c7c3 100644 --- a/gcc/testsuite/g++.dg/contracts/contracts-friend1.C +++ b/gcc/testsuite/g++.dg/contracts/contracts-friend1.C @@ -3,6 +3,8 @@ // { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" } struct X { + friend void fn0(X x) [[ pre: x.a > 0 ]] { } + friend void fn2(X x); static void fns0(X x) [[ pre: x.a > 0 ]] { } static void fns1(X x) [[ pre: x.a > 0 ]]; @@ -22,6 +24,7 @@ int main(int, char**) { X x; fn(x); // no contract + fn0(x); fn2(x); X::fns0(x); @@ -30,7 +33,8 @@ int main(int, char**) { return 0; } -// { dg-output "default std::handle_contract_violation called: .*.C 17 fn2 .*(\n|\r\n|\r)*" } -// { dg-output "default std::handle_contract_violation called: .*.C 7 X::fns0 .*(\n|\r\n|\r)*" } -// { dg-output "default std::handle_contract_violation called: .*.C 8 X::fns1 .*(\n|\r\n|\r)*" } -// { dg-output "default std::handle_contract_violation called: .*.C 19 X::fns2 .*(\n|\r\n|\r)*" } +// { dg-output "default std::handle_contract_violation called: .*.C 6 fn0 .*(\n|\r\n|\r)*" } +// { dg-output "default std::handle_contract_violation called: .*.C 19 fn2 .*(\n|\r\n|\r)*" } +// { dg-output "default std::handle_contract_violation called: .*.C 9 X::fns0 .*(\n|\r\n|\r)*" } +// { dg-output "default std::handle_contract_violation called: .*.C 10 X::fns1 .*(\n|\r\n|\r)*" } +// { dg-output "default std::handle_contract_violation called: .*.C 21 X::fns2 .*(\n|\r\n|\r)*" } diff --git a/gcc/testsuite/g++.dg/contracts/contracts-nested-class1.C b/gcc/testsuite/g++.dg/contracts/contracts-nested-class1.C index e6c362ab2a7..05c1cf131c4 100644 --- a/gcc/testsuite/g++.dg/contracts/contracts-nested-class1.C +++ b/gcc/testsuite/g++.dg/contracts/contracts-nested-class1.C @@ -16,7 +16,7 @@ struct Outer { // error about 'p' not being declared because the contracts haven't been // unified or remapped. friend void gfn(int p) [[ pre: p > 0 ]]; - friend void gfn(int q) [[ pre: q > 1 ]]; // { dg-error "mismatched contract" } + friend void gfn(int q) [[ pre: q > 1 ]]; // { dg-error "'q' was not declared" } // This should be okay. friend void gfn2(int q); @@ -24,4 +24,4 @@ struct Outer { static int bob; }; -int Outer::bob{-1}; \ No newline at end of file +int Outer::bob{-1}; diff --git a/gcc/testsuite/g++.dg/contracts/contracts-redecl7.C b/gcc/testsuite/g++.dg/contracts/contracts-redecl7.C index 050c8ee3191..e3a57eea632 100644 --- a/gcc/testsuite/g++.dg/contracts/contracts-redecl7.C +++ b/gcc/testsuite/g++.dg/contracts/contracts-redecl7.C @@ -19,19 +19,38 @@ int both(int a, T *t) [[ pre: a > 0 ]]; struct T { friend int now(int a, T *t); + friend int later(int a, T *t) [[ pre: a > 0 ]] [[ pre: t->pri > 0 ]] + { + printf("later: a: %d, t->pri: %d\n", a, t->pri); + return -a * t->pri; + } friend int both(int a, T *t) [[ pre: a > 0 ]] { printf("both: a: %d, t->pri: %d\n", a, t->pri); return -a * t->pri; } + friend int S::now(int a, T *t); + friend int hidden(int a, T *t) [[ pre: a > 0 ]] [[ pre: t->pri > 0 ]] + { + printf("hidden: a: %d, t->pri: %d\n", a, t->pri); + return -a * t->pri; + } + friend int hidden2(int a, T *t) [[ pre: a > 0 ]] [[ pre: t->pri > 0 ]] + { + printf("hidden2: a: %d, t->pri: %d\n", a, t->pri); + return -a * t->pri; + } + int x{1}; private: int pri{-10}; }; +int hidden2(int a, T *t) [[ pre: a > 0 ]] [[ pre: t->pri > 0 ]]; + int S::now(int a, T *t) { printf("S::now: a: %d, t->pri: %d\n", a, t->pri); @@ -51,7 +70,10 @@ int main(int, char**) s.now(-10, &t); now(-20, &t); + later(-21, &t); both(-22, &t); + hidden(-23, &t); + hidden2(-24, &t); return 0; } @@ -59,6 +81,15 @@ int main(int, char**) // { dg-output "S::now: a: -10, t->pri: -10(\n|\r\n|\r)*" } // { dg-output "default std::handle_contract_violation called: .*.C 15 now .*(\n|\r\n|\r)*" } // { dg-output "now: a: -20, t->pri: -10(\n|\r\n|\r)*" } -// { dg-output "default std::handle_contract_violation called: .*.C 22 both .*(\n|\r\n|\r)*" } +// { dg-output "default std::handle_contract_violation called: .*.C 22 later .*(\n|\r\n|\r)*" } +// { dg-output "default std::handle_contract_violation called: .*.C 22 later .*(\n|\r\n|\r)*" } +// { dg-output "later: a: -21, t->pri: -10(\n|\r\n|\r)*" } +// { dg-output "default std::handle_contract_violation called: .*.C 27 both .*(\n|\r\n|\r)*" } // { dg-output "both: a: -22, t->pri: -10(\n|\r\n|\r)*" } +// { dg-output "default std::handle_contract_violation called: .*.C 36 hidden .*(\n|\r\n|\r)*" } +// { dg-output "default std::handle_contract_violation called: .*.C 36 hidden .*(\n|\r\n|\r)*" } +// { dg-output "hidden: a: -23, t->pri: -10(\n|\r\n|\r)*" } +// { dg-output "default std::handle_contract_violation called: .*.C 41 hidden2 .*(\n|\r\n|\r)*" } +// { dg-output "default std::handle_contract_violation called: .*.C 41 hidden2 .*(\n|\r\n|\r)*" } +// { dg-output "hidden2: a: -24, t->pri: -10(\n|\r\n|\r)*" } diff --git a/gcc/testsuite/g++.dg/contracts/contracts-redecl8.C b/gcc/testsuite/g++.dg/contracts/contracts-redecl8.C index ffa41dee5e5..933adce79f9 100644 --- a/gcc/testsuite/g++.dg/contracts/contracts-redecl8.C +++ b/gcc/testsuite/g++.dg/contracts/contracts-redecl8.C @@ -26,9 +26,8 @@ struct T return 0; } - // friends are not members and thus not in the complete class context. friend int hidden(int x, T *t) - [[ pre: x > 1 ]] [[ pre: t->pri > 0 ]] // { dg-error "has no member" } + [[ pre: x > 1 ]] [[ pre: t->pri > 0 ]] { return x; }