Hey Egas,

Thanks for looking into this recent regression!

On Wed, 17 Dec 2025, Egas Ribeiro wrote:

> Regtested on x86_64-pc-linux-gnu.
> 
> This fix works, but I believe the real issue is that 
> member_like_constrained_friend_p is incorrectly returning false because
> DECL_TEMPLATE_INFO isn't set up for the friend before duplicate_decls is
> called. I tried a few fixes to defer pushdecl_namespace_level after we
> call build_template_info in push_template_decl, but they caused some
> regressions. Suggestions on how i might fix this correctly?
> (also, using && probably goes against [temp.friend]/9 requirements)

Yeah, one would expect DECL_TEMPLATE_INFO to point back to the template
friend's TEMPLATE_DECL here (whose DECL_TEMPLATE_RESULT points to the
FUNCTION_DECL as expected).  We probably should fix that..

Luckily I think we can work around this missing DECL_TEMPLATE_INFO for
this PR.  The predicate needs to return true for non-template
constrained friends and template friends whose constraints depend on
outer template parameters.  In the former case the function must be
templated (since only templated functions can have constraints) and
therefore the class scope must be templated.  In the latter case the
class scope must also be templated since we assume there are outer
template parameters on which the constraint can depend.  So in either
case the class scope must be templated!

So I think it should work to add

  CLASSTYPE_TEMPLATE_INFO (DECL_FRIEND_CONTEXT (decl))

to the predicate, which will rule out non class template scope friends
such as in the PR?

> 
> -- >8 --
> 
> function_requirements_equivalent_p was incorrectly treating a constrained
> friend function template and its namespace-scope forward declaration as
> having inequivalent requirements, causing an ambiguity error.
> 
>       PR c++/122550
> 
> gcc/cp/ChangeLog:
> 
>       * decl.cc (function_requirements_equivalent_p): Use && instead
>       of || when checking member_like_constrained_friend_p.
> 
> gcc/testsuite/ChangeLog:
> 
>       * g++.dg/cpp2a/concepts-friend18.C: New test.
> 
> Signed-off-by: Egas Ribeiro <[email protected]>
> ---
>  gcc/cp/decl.cc                                 |  2 +-
>  gcc/testsuite/g++.dg/cpp2a/concepts-friend18.C | 18 ++++++++++++++++++
>  2 files changed, 19 insertions(+), 1 deletion(-)
>  create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend18.C
> 
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 74c862ec1c7..689332363aa 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -1146,7 +1146,7 @@ function_requirements_equivalent_p (tree newfn, tree 
> oldfn)
>       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))
> +      && member_like_constrained_friend_p (oldfn))
>      return false;
>  
>    /* Compare only trailing requirements.  */
> 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);
> +}
> -- 
> 2.52.0
> 
> 

Reply via email to