diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d051ee85f7017b27a85c0a7ab0b32949e44657a7..356d7ffb6d610376f0fabbcdfcca48a05c139470 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/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 62c34bf9abe819a10bc21d96c0c8c247784ae03e..bea0ee92106889f43b3881cc577f20ade2ccf6fd 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 bef0fda6d2291e4ca40b141b721012798a8aff6e..bb0e9d38203a5104f098098652f798eb01571421 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 a4809f034dcefec585cb72b9a8181ce97fb5d18b..f4e77d172b9d9e990e0640c29dd2c11af6f0e224 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 0350ac3553e1bf48ed0347169a79d530686ad9cf..93cb1f05ad0f328298366e351dadf3edb11b3f1d 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
+
+// { dg-final { scan-assembler "_ZN1SILi1EEF3fooER4Base" } }
+int x = foo(s1);  // OK, selects #1
+// { dg-final { scan-assembler "_ZN1SILi2EEF3fooER4Base" } }
+int y = foo(s2);  // OK, selects #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 "_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 0000000000000000000000000000000000000000..f3481b6731e4d55d76978c6cf730fdfb5737c10f
--- /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 0000000000000000000000000000000000000000..c37d547bbdfab1cfd7138f159baeb74fef4c366c
--- /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/include/demangle.h b/include/demangle.h
index 769137e03e5469d1d9e8ba60eed2a926f0e2bc97..f062d7731c6bab974ba064f75ca76ab9f64e2395 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/libiberty/cp-demangle.c b/libiberty/cp-demangle.c
index 3bd303a7544581ac77a7106f64244de9edde0c2e..2ce984f85cf460a34c60eabff0e803a27ce4707f 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 0acd2d635db048a1594ddf0282cc72313593ae9c..01ca22278cd31c558df85a00a3a5673bbadc1b94 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&)