On Fri, 19 Dec 2025, Egas Ribeiro wrote:

> Regtested on x86_64-pc-linux-gnu. Ok for trunk?
> 
> Also I believe we still wanna check DECL_FRIEND_CONTEXT (decl) because
> otherwise we get a segfault in CLASSTYPE_IMPLICIT_INSTANTIATION.
> 
> -- >8 --
> 
> 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
> 2. Template friends whose constraints depend on outer template parms
> 
> In both cases, the enclosing class scope must be templated. This 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.
> 
> Signed-off-by: Egas Ribeiro <[email protected]>
> ---
>  gcc/cp/decl.cc                                 |  1 +
>  gcc/testsuite/g++.dg/cpp2a/concepts-friend18.C | 18 ++++++++++++++++++
>  2 files changed, 19 insertions(+)
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend18.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..5a6b0d67ef7
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend18.C
> @@ -0,0 +1,18 @@
> +// PR c++/122550
> +// { dg-do compile { target c++20 } }
> +
> +class 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) {}
> +};
> +
> +void f(Hasher& hasher, unsigned long integer) {
> +  add(hasher, (unsigned int) integer);
> +}

Can you add another version of this test where the friend is defined
inside an explicit specialization of (the now class template) Hasher?
This would demonstrate why we need to check
CLASSTYPE_IMPLICIT_INSTANTIATION instead of CLASSTYPE_TEMPLATE_INFO.
I'd name this test concepts-friend18a.C to convey that it's a slight
variant of the main test.

While we're at it let's replace 'f' with the simpler:

  int main() {
    Hasher h;
    add(h, 0);
  }

Reply via email to