]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: friend contracts are in complete-class context
authorJason Merrill <jason@redhat.com>
Wed, 2 Nov 2022 19:21:29 +0000 (15:21 -0400)
committerJason Merrill <jason@redhat.com>
Wed, 2 Nov 2022 20:23:20 +0000 (16:23 -0400)
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.

gcc/cp/contracts.cc
gcc/cp/contracts.h
gcc/cp/decl.cc
gcc/cp/parser.cc
gcc/cp/parser.h
gcc/testsuite/g++.dg/contracts/contracts-friend1.C
gcc/testsuite/g++.dg/contracts/contracts-nested-class1.C
gcc/testsuite/g++.dg/contracts/contracts-redecl7.C
gcc/testsuite/g++.dg/contracts/contracts-redecl8.C

index 6db3b750fda556f55a656c72e032ed2a026a8118..38eb4ad9bfe94f58910358901c1bfeaaf2569dff 100644 (file)
@@ -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
index ad382cf187bc702bdd38c58c32f3f0a74a3d3493..4050a38708b46f26a78e2a066d19bde50fe630e7 100644 (file)
@@ -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);
index 9e591d592cf8794c94c2771013e6abb4edc10c63..f4d6ee5dedef5037dadebc7be512c43832694226 100644 (file)
@@ -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.  */
index 96e1537356e8b681662eee4672498aba648ab70f..4d4475b80f08b9e7dbd246517abf65d5fde33f02 100644 (file)
@@ -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
index f695f4566b44d69463802c40e223e69a48be6c8b..5737146dd424daf7cde7abfe8364f6958c8bd0ba 100644 (file)
@@ -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;
 
index 01cebb0c2fb2b8624b2ccb5b80424989afae31b9..0ccfbe2c7c3d8471716d25b77551fa6d087c55d9 100644 (file)
@@ -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)*" }
index e6c362ab2a73edc6f6fde0b559abfde1f78f5d09..05c1cf131c446d34e8f252a4c91eb01de020c4fe 100644 (file)
@@ -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};
index 050c8ee319132c9bd3c5331fe52512d085e72428..e3a57eea6322aea230884945070fddf4207d6852 100644 (file)
@@ -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)*" }
 
index ffa41dee5e5d1e04cbebcdda2f2b435cd4f046db..933adce79f9fad9241a5a2e57c4a8b3de4a109e2 100644 (file)
@@ -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;
   }