public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc/devel/c++-contracts] c++: friend contracts are in complete-class context
@ 2022-11-03 19:45 Jason Merrill
  0 siblings, 0 replies; only message in thread
From: Jason Merrill @ 2022-11-03 19:45 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:e378b7f8250d1e1b029c0254dc17492fa16e9de4

commit e378b7f8250d1e1b029c0254dc17492fa16e9de4
Author: Jason Merrill <jason@redhat.com>
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;
   }

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-11-03 19:45 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-03 19:45 [gcc/devel/c++-contracts] c++: friend contracts are in complete-class context Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).