Tested x86_64-pc-linux-gnu, applying to trunk.

-- 8< --

r13-4035 avoided a problem with overloading of constrained hidden friends by
checking satisfaction, but checking satisfaction early is inconsistent with
the usual late checking and can lead to hard errors, so let's not do that
after all.

We were wrongly treating the different instantiations of the same friend
template as the same function because maybe_substitute_reqs_for was failing
to actually substitute in the case of a non-template friend.  But we don't
actually need to do the substitution anyway, because [temp.friend] says that
such a friend can't be the same as any other declaration.

After fixing that, instead of a redefinition error we got an ambiguous
overload error, fixed by allowing constrained hidden friends to coexist
until overload resolution, at which point they probably won't be in the same
ADL overload set anyway.

And we avoid mangling collisions by following the proposed mangling for
these friends as a member function with an extra 'F' before the name.  I
demangle this by just adding [friend] to the name of the function because
it's not feasible to reconstruct the actual scope of the function since the
mangling ABI doesn't distinguish between class and namespace scopes.

        PR c++/109751

gcc/cp/ChangeLog:

        * cp-tree.h (member_like_constrained_friend_p): Declare.
        * decl.cc (member_like_constrained_friend_p): New.
        (function_requirements_equivalent_p): Check it.
        (duplicate_decls): Check it.
        (grokfndecl): Check friend template constraints.
        * mangle.cc (decl_mangling_context): Check it.
        (write_unqualified_name): Check it.
        * pt.cc (uses_outer_template_parms_in_constraints): Fix for friends.
        (tsubst_friend_function): Don't check satisfaction.

include/ChangeLog:

        * demangle.h (enum demangle_component_type): Add
        DEMANGLE_COMPONENT_FRIEND.

libiberty/ChangeLog:

        * cp-demangle.c (d_make_comp): Handle DEMANGLE_COMPONENT_FRIEND.
        (d_count_templates_scopes): Likewise.
        (d_print_comp_inner): Likewise.
        (d_unqualified_name): Handle member-like friend mangling.
        * testsuite/demangle-expected: Add test.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp2a/concepts-friend11.C: Now works.  Add template.
        * g++.dg/cpp2a/concepts-friend15.C: New test.
---
 gcc/cp/cp-tree.h                              |  3 +-
 include/demangle.h                            |  2 +
 gcc/cp/decl.cc                                | 49 ++++++++++++++++++-
 gcc/cp/mangle.cc                              | 10 ++++
 gcc/cp/pt.cc                                  | 14 ++++--
 .../g++.dg/cpp2a/concepts-friend11.C          | 26 ++++++----
 .../g++.dg/cpp2a/concepts-friend11a.C         | 15 ++++++
 .../g++.dg/cpp2a/concepts-friend15.C          | 22 +++++++++
 libiberty/cp-demangle.c                       | 17 +++++++
 libiberty/testsuite/demangle-expected         |  3 ++
 10 files changed, 145 insertions(+), 16 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend11a.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend15.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d051ee85f70..356d7ffb6d6 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6859,6 +6859,7 @@ extern void note_break_stmt                       (void);
 extern bool note_iteration_stmt_body_start     (void);
 extern void note_iteration_stmt_body_end       (bool);
 extern void determine_local_discriminator      (tree);
+extern bool member_like_constrained_friend_p   (tree);
 extern bool fns_correspond                     (tree, tree);
 extern int decls_match                         (tree, tree, bool = true);
 extern bool maybe_version_functions            (tree, tree, bool);
@@ -7385,7 +7386,7 @@ extern tree lookup_template_function              (tree, 
tree);
 extern tree lookup_template_variable           (tree, tree, tsubst_flags_t);
 extern bool uses_template_parms                        (tree);
 extern bool uses_template_parms_level          (tree, int);
-extern bool uses_outer_template_parms_in_constraints (tree);
+extern bool uses_outer_template_parms_in_constraints (tree, tree = NULL_TREE);
 extern bool need_generic_capture               (void);
 extern tree instantiate_class_template         (tree);
 extern tree instantiate_template               (tree, tree, tsubst_flags_t);
diff --git a/include/demangle.h b/include/demangle.h
index 769137e03e5..f062d7731c6 100644
--- a/include/demangle.h
+++ b/include/demangle.h
@@ -448,6 +448,8 @@ enum demangle_component_type
   DEMANGLE_COMPONENT_TRANSACTION_SAFE,
   /* A cloned function.  */
   DEMANGLE_COMPONENT_CLONE,
+  /* A member-like friend function.  */
+  DEMANGLE_COMPONENT_FRIEND,
   DEMANGLE_COMPONENT_NOEXCEPT,
   DEMANGLE_COMPONENT_THROW_SPEC,
 
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 62c34bf9abe..bea0ee92106 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -951,6 +951,30 @@ determine_local_discriminator (tree decl)
 }
 
 
+/* True if DECL is a constrained hidden friend as per [temp.friend]/9:
+
+   A non-template friend declaration with a requires-clause shall be a
+   definition. A friend function template with a constraint that depends on a
+   template parameter from an enclosing template shall be a definition. Such a
+   constrained friend function or function template declaration does not
+   declare the same function or function template as a declaration in any other
+   scope.
+
+   The ABI calls this a "member-like constrained friend" and mangles it like a
+   member function to avoid collisions.  */
+
+bool
+member_like_constrained_friend_p (tree decl)
+{
+  return (TREE_CODE (decl) == FUNCTION_DECL
+         && DECL_UNIQUE_FRIEND_P (decl)
+         && DECL_FRIEND_CONTEXT (decl)
+         && get_constraints (decl)
+         && (!DECL_TEMPLATE_INFO (decl)
+             || !PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (decl))
+             || (uses_outer_template_parms_in_constraints
+                 (most_general_template (decl)))));
+}
 
 /* Returns true if functions FN1 and FN2 have equivalent trailing
    requires clauses.  */
@@ -968,6 +992,13 @@ function_requirements_equivalent_p (tree newfn, tree oldfn)
       return cp_tree_equal (req1, req2);
     }
 
+  /* [temp.friend]/9 "Such a constrained friend function does not declare the
+     same function as a declaration in any other scope."  So no need to
+     actually compare the requirements.  */
+  if (member_like_constrained_friend_p (newfn)
+      || member_like_constrained_friend_p (oldfn))
+    return false;
+
   /* Compare only trailing requirements.  */
   tree reqs1 = get_trailing_function_requirements (newfn);
   tree reqs2 = get_trailing_function_requirements (oldfn);
@@ -1936,6 +1967,10 @@ duplicate_decls (tree newdecl, tree olddecl, bool 
hiding, bool was_hidden)
             are not ambiguous.  */
          else if ((!DECL_FUNCTION_VERSIONED (newdecl)
                    && !DECL_FUNCTION_VERSIONED (olddecl))
+                  /* Let constrained hidden friends coexist for now, we'll
+                     check satisfaction later.  */
+                  && !member_like_constrained_friend_p (newdecl)
+                  && !member_like_constrained_friend_p (olddecl)
                    // The functions have the same parameter types.
                   && compparms (TYPE_ARG_TYPES (TREE_TYPE (newdecl)),
                                 TYPE_ARG_TYPES (TREE_TYPE (olddecl)))
@@ -10305,16 +10340,28 @@ grokfndecl (tree ctype,
           ci = NULL_TREE;
         }
       /* C++20 CA378: Remove non-templated constrained functions.  */
+      /* [temp.friend]/9 A non-template friend declaration with a
+        requires-clause shall be a definition. A friend function template with
+        a constraint that depends on a template parameter from an enclosing
+        template shall be a definition. */
       if (ci
          && (block_local
              || (!flag_concepts_ts
                  && (!processing_template_decl
                      || (friendp && !memtmpl && !funcdef_flag)))))
        {
-         error_at (location, "constraints on a non-templated function");
+         if (!friendp || !processing_template_decl)
+           error_at (location, "constraints on a non-templated function");
+         else
+           error_at (location, "constrained non-template friend declaration"
+                     " must be a definition");
          ci = NULL_TREE;
        }
       set_constraints (decl, ci);
+      if (ci && friendp && memtmpl && !funcdef_flag
+         && uses_outer_template_parms_in_constraints (decl, ctx))
+       error_at (location, "friend function template with constraints that "
+                 "depend on outer template parameters must be a definition");
     }
 
   if (TREE_CODE (type) == METHOD_TYPE)
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index bef0fda6d22..bb0e9d38203 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -963,6 +963,9 @@ decl_mangling_context (tree decl)
 
   tcontext = CP_DECL_CONTEXT (decl);
 
+  if (member_like_constrained_friend_p (decl))
+    tcontext = DECL_FRIEND_CONTEXT (decl);
+
   /* Ignore the artificial declare reduction functions.  */
   if (tcontext
       && TREE_CODE (tcontext) == FUNCTION_DECL
@@ -1419,6 +1422,7 @@ anon_aggr_naming_decl (tree type)
                        ::= [<module-name>] <source-name>
                        ::= [<module-name>] <unnamed-type-name>
                        ::= <local-source-name> 
+                       ::= F <source-name> # member-like constrained friend
 
     <local-source-name>        ::= L <source-name> <discriminator> */
 
@@ -1476,6 +1480,12 @@ write_unqualified_name (tree decl)
   else if (DECL_DECLARES_FUNCTION_P (decl))
     {
       found = true;
+
+      /* A constrained hidden friend is mangled like a member function, with
+        the name prefixed by 'F'.  */
+      if (member_like_constrained_friend_p (decl))
+       write_char ('F');
+
       if (DECL_CONSTRUCTOR_P (decl))
        write_special_name_constructor (decl);
       else if (DECL_DESTRUCTOR_P (decl))
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index a4809f034dc..f4e77d172b9 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -11049,14 +11049,21 @@ uses_outer_template_parms (tree decl)
    from its enclosing scope.  */
 
 bool
-uses_outer_template_parms_in_constraints (tree decl)
+uses_outer_template_parms_in_constraints (tree decl, tree ctx/*=NULL_TREE*/)
 {
   tree ci = get_constraints (decl);
   if (ci)
     ci = CI_ASSOCIATED_CONSTRAINTS (ci);
   if (!ci)
     return false;
-  int depth = template_class_depth (CP_DECL_CONTEXT (decl));
+  if (!ctx)
+    {
+      if (tree fc = DECL_FRIEND_CONTEXT (decl))
+       ctx = fc;
+      else
+       ctx = CP_DECL_CONTEXT (decl);
+    }
+  int depth = template_class_depth (ctx);
   if (depth == 0)
     return false;
   return for_each_template_parm (ci, template_parm_outer_level,
@@ -11393,9 +11400,6 @@ tsubst_friend_function (tree decl, tree args)
          not_tmpl = DECL_TEMPLATE_RESULT (new_friend);
          new_friend_result_template_info = DECL_TEMPLATE_INFO (not_tmpl);
        }
-      else if (!constraints_satisfied_p (new_friend))
-       /* Only define a constrained hidden friend when satisfied.  */
-       return error_mark_node;
 
       /* Inside pushdecl_namespace_level, we will push into the
         current namespace. However, the friend function should go
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend11.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-friend11.C
index 0350ac3553e..93cb1f05ad0 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-friend11.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend11.C
@@ -1,21 +1,29 @@
 // CWG2596
 // { dg-do compile { target c++20 } }
+// { dg-additional-options -fno-implicit-constexpr }
 
 struct Base {};
 
-int foo(Base&) { return 0; } // #0
-
 template<int N>
 struct S : Base {
   friend int foo(Base&) requires (N == 1) { return 1; }  // #1
-  // friend int foo(Base&) requires (N == 2) { return 3; }  // #2
+  friend int foo(Base&) requires (N == 2) { return 3; }  // #2
+
+  template <class T>
+  friend int bar(Base&) requires (N == 1) { return 1; }
+  template <class T>
+  friend int bar(Base&) requires (N == 2) { return 3; }
 };
 
 S<1> s1;
-S<2> s2;          // OK, no conflict between #1 and #0
-int x = foo(s1);  // { dg-error "ambiguous" }
-int y = foo(s2);  // OK, selects #0
+S<2> s2;          // OK, no conflict between #1 and #2
 
-// ??? currently the foos all mangle the same, so comment out #2
-// and only test that #1 isn't multiply defined and overloads with #0.
-// The 2596 example does not include #0 and expects both calls to work.
+// { dg-final { scan-assembler "_ZN1SILi1EEF3fooER4Base" } }
+int x = foo(s1);  // OK, selects #1
+// { dg-final { scan-assembler "_ZN1SILi2EEF3fooER4Base" } }
+int y = foo(s2);  // OK, selects #2
+
+// { dg-final { scan-assembler "_ZN1SILi1EEF3barIiEEiR4Base" } }
+int x2 = bar<int>(s1);  // OK, selects #1
+// { dg-final { scan-assembler "_ZN1SILi2EEF3barIiEEiR4Base" } }
+int y2 = bar<int>(s2);  // OK, selects #2
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend11a.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-friend11a.C
new file mode 100644
index 00000000000..f3481b6731e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend11a.C
@@ -0,0 +1,15 @@
+// CWG2596
+// { dg-do compile { target c++20 } }
+
+struct Base {};
+
+template<int N>
+struct S : Base {
+  friend int foo(Base&) requires (N == 1); // { dg-error "must be a 
definition" }
+  friend int foo(Base&) requires (N == 2); // { dg-error "must be a 
definition" }
+
+  template <class T>
+  friend int bar(Base&) requires (N == 1); // { dg-error "must be a 
definition" }
+  template <class T>
+  friend int bar(Base&) requires (N == 2); // { dg-error "must be a 
definition" }
+};
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend15.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-friend15.C
new file mode 100644
index 00000000000..c37d547bbdf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend15.C
@@ -0,0 +1,22 @@
+// PR c++/109751
+// { dg-do compile { target c++20 } }
+
+template<typename _Tp> concept cmpeq
+  = requires(_Tp __t, _Tp __u) { { __u != __t } ; };
+
+template<typename D>
+struct iterator_interface
+{
+  friend constexpr bool operator>=(D lhs, D rhs)
+    requires cmpeq<D> { return true; }
+};
+
+template<typename T>
+struct iterator : iterator_interface<iterator<T>>
+{
+    bool operator==(iterator) const;
+    iterator &operator++();
+    iterator &operator++(int);
+};
+
+static_assert(cmpeq<iterator<int>>);
diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c
index 3bd303a7544..2ce984f85cf 100644
--- a/libiberty/cp-demangle.c
+++ b/libiberty/cp-demangle.c
@@ -1036,6 +1036,7 @@ d_make_comp (struct d_info *di, enum 
demangle_component_type type,
     case DEMANGLE_COMPONENT_TEMPLATE_NON_TYPE_PARM:
     case DEMANGLE_COMPONENT_TEMPLATE_TEMPLATE_PARM:
     case DEMANGLE_COMPONENT_TEMPLATE_PACK_PARM:
+    case DEMANGLE_COMPONENT_FRIEND:
       if (left == NULL)
        return NULL;
       break;
@@ -1681,6 +1682,7 @@ d_maybe_module_name (struct d_info *di, struct 
demangle_component **name)
 /* <unqualified-name> ::= [<module-name>] <operator-name> [<abi-tags>]
                       ::= [<module-name>] <ctor-dtor-name> [<abi-tags>]
                       ::= [<module-name>] <source-name> [<abi-tags>]
+                     ::= [<module-name>] F <source-name> [<abi-tags>]
                      ::= [<module-name>] <local-source-name>  [<abi-tags>]
                       ::= [<module-name>] DC <source-name>+ E [<abi-tags>]
     <local-source-name>        ::= L <source-name> <discriminator> [<abi-tags>]
@@ -1692,11 +1694,18 @@ d_unqualified_name (struct d_info *di, struct 
demangle_component *scope,
 {
   struct demangle_component *ret;
   char peek;
+  int member_like_friend = 0;
 
   if (!d_maybe_module_name (di, &module))
     return NULL;
 
   peek = d_peek_char (di);
+  if (peek == 'F')
+    {
+      member_like_friend = 1;
+      d_advance (di, 1);
+      peek = d_peek_char (di);
+    }
   if (IS_DIGIT (peek))
     ret = d_source_name (di);
   else if (IS_LOWER (peek))
@@ -1773,6 +1782,8 @@ d_unqualified_name (struct d_info *di, struct 
demangle_component *scope,
     ret = d_make_comp (di, DEMANGLE_COMPONENT_MODULE_ENTITY, ret, module);
   if (d_peek_char (di) == 'B')
     ret = d_abi_tags (di, ret);
+  if (member_like_friend)
+    ret = d_make_comp (di, DEMANGLE_COMPONENT_FRIEND, ret, NULL);
   if (scope)
     ret = d_make_comp (di, DEMANGLE_COMPONENT_QUAL_NAME, scope, ret);
 
@@ -4459,6 +4470,7 @@ d_count_templates_scopes (struct d_print_info *dpi,
     case DEMANGLE_COMPONENT_GLOBAL_CONSTRUCTORS:
     case DEMANGLE_COMPONENT_GLOBAL_DESTRUCTORS:
     case DEMANGLE_COMPONENT_MODULE_ENTITY:
+    case DEMANGLE_COMPONENT_FRIEND:
       d_count_templates_scopes (dpi, d_left (dc));
       break;
 
@@ -6197,6 +6209,11 @@ d_print_comp_inner (struct d_print_info *dpi, int 
options,
       d_append_char (dpi, ']');
       return;
 
+    case DEMANGLE_COMPONENT_FRIEND:
+      d_print_comp (dpi, options, d_left (dc));
+      d_append_string (dpi, "[friend]");
+      return;
+
     case DEMANGLE_COMPONENT_TEMPLATE_HEAD:
       {
        d_append_char (dpi, '<');
diff --git a/libiberty/testsuite/demangle-expected 
b/libiberty/testsuite/demangle-expected
index 0acd2d635db..01ca22278cd 100644
--- a/libiberty/testsuite/demangle-expected
+++ b/libiberty/testsuite/demangle-expected
@@ -1689,3 +1689,6 @@ X::operator Z<int><int>()::y
 
 _ZZN1XIfEcv1ZIT_EIiEEvE1y
 X<float>::operator Z<int><int>()::y
+
+_ZN1SILi1EEF3barIiEEiR4Base
+int S<1>::bar[friend]<int>(Base&)

base-commit: 3571cc93511b39f7a403fe5eab0e316cd7e86220
-- 
2.39.3

Reply via email to