On Fri, 19 Dec 2025, Egas Ribeiro wrote:

> member_like_constrained_friend_p was incorrectly returning true for
> constrained friend function templates declared in non-template classes,
> causing them to be treated as distinct from their forward declarations.
> This led to ambiguity errors at call sites.
> 
> Per [temp.friend]/9, a constrained friend is only "member-like" (and thus
> declares a different function) in two cases:
> 1. Non-template friends with constraints (must be in a templated class)
> 2. Template friends whose constraints depend on outer template parameters
> 
> In both cases, the enclosing class scope must be templated. The fix adds
> a check for CLASSTYPE_IMPLICIT_INSTANTIATION to ensure the friend's
> context is actually a class template, not a plain class or explicit
> specialization.
> 
>       PR c++/122550
> 
> gcc/cp/ChangeLog:
> 
>       * decl.cc (member_like_constrained_friend_p): Check that the
>       friend's enclosing class is an implicit instantiation.
> 
> gcc/testsuite/ChangeLog:
> 
>       * g++.dg/cpp2a/concepts-friend18.C: New test.
>       * g++.dg/cpp2a/concepts-friend18a.C: New test.

Looks good, pushed!

> 
> Signed-off-by: Egas Ribeiro <[email protected]>
> ---
>  gcc/cp/decl.cc                                |  1 +
>  .../g++.dg/cpp2a/concepts-friend18.C          | 19 ++++++++++++++++
>  .../g++.dg/cpp2a/concepts-friend18a.C         | 22 +++++++++++++++++++
>  3 files changed, 42 insertions(+)
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend18.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend18a.C
> 
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 30f38f1e099..f41b3fc0471 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -1129,6 +1129,7 @@ member_like_constrained_friend_p (tree decl)
>         && DECL_UNIQUE_FRIEND_P (decl)
>         && DECL_FRIEND_CONTEXT (decl)
>         && get_constraints (decl)
> +       && CLASSTYPE_IMPLICIT_INSTANTIATION (DECL_FRIEND_CONTEXT (decl))
>         && (!DECL_TEMPLATE_INFO (decl)
>             || !PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (decl))
>             || (uses_outer_template_parms_in_constraints
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend18.C 
> b/gcc/testsuite/g++.dg/cpp2a/concepts-friend18.C
> new file mode 100644
> index 00000000000..bd1bac6cdac
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend18.C
> @@ -0,0 +1,19 @@
> +// PR c++/122550
> +// { dg-do compile { target c++20 } }
> +
> +struct Hasher;
> +template <class a>
> +concept C = true;
> +
> +template<C T>
> +void add(Hasher&, T);
> +
> +struct Hasher {
> +    template<C T>
> +    friend void add(Hasher& hasher, T integer) {}
> +};
> +
> +int main() {
> +    Hasher h;
> +    add(h, 0);
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend18a.C 
> b/gcc/testsuite/g++.dg/cpp2a/concepts-friend18a.C
> new file mode 100644
> index 00000000000..d29a2fba037
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend18a.C
> @@ -0,0 +1,22 @@
> +// PR c++/122550
> +// { dg-do compile { target c++20 } }
> +
> +template<typename U>
> +struct Hasher;
> +
> +template <class a>
> +concept C = true;
> +
> +template<C T>
> +void add(Hasher<int>&, T);
> +
> +template<>
> +struct Hasher<int> {
> +    template<C T>
> +    friend void add(Hasher& hasher, T integer) {}
> +};
> +
> +int main() {
> +    Hasher<int> h;
> +    add(h, 0);
> +}
> -- 
> 2.52.0
> 
> 

Reply via email to