llvm/clang/test/SemaTemplate/concepts-friends.cpp

// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s

template <typename T>
concept constraint = false;

namespace temp_friend_9 {
// A non-template friend declaration with a requires-clause shall be a
// definition. ...Such a constrained friend function ... does not declare the
// same function or function template as a declaration in any other scope.
template <typename T>
struct NonTemplateFriend {
  friend void foo()
    requires true
  {}

  friend void baz() // expected-error {{non-template friend declaration with a requires clause must be a definition}}
    requires true;
};

struct TempP9NotShownIfFunctionWouldBeInvalidAnyway {
  friend void foo()
    requires true; // expected-error {{non-templated function cannot have a requires clause}}
};

// A friend function template with a constraint that depends on a template
// parameter from an enclosing template shall be a definition.  Such a ...
// function template declaration does not declare the same function or
// function template as a declaration in any other scope.
template <typename T>
struct TemplateFromEnclosing {
  template <typename U>
  friend void bar2() // expected-error {{friend declaration with a constraint that depends on an enclosing template parameter must be a definition}}
    requires constraint<T>;

  template <typename U>
  friend void foo()
    requires constraint<T>
  {}

  T variable;
  template <typename U>
  friend void foo2()
    requires constraint<decltype(variable)>
  {}

  template <typename U>
  friend void foo3(T parmvar)
    requires constraint<decltype(parmvar)>
  {}

  template <typename U>
  friend void foo4()
    requires requires(T &req) { (void)req; }
  {}

  using Alias = T;
  template <typename U>
  friend void foo5()
    requires constraint<Alias>
  {}

  // All of these refer to a parent, so these are not duplicate definitions.
  struct ChildOfEnclosing {
    template <typename U>
    friend void foo6()
      requires constraint<T>
    {}
    template <typename U>
    friend void foo7()
      requires constraint<decltype(variable)>
    {}
    template <typename U>
    friend void foo8(T parmvar)
      requires constraint<decltype(parmvar)>
    {}
    // This is NOT a duplicate since it itself is not a template.
    friend void foo9()
      requires true
    {}
  };
  template <typename T2>
  struct TemplChildOfEnclosing {
    template <typename U>
    friend void foo10()
      requires constraint<T>
    {}
  };
};

// Doesn't meet either of the requirements in the above as they don't refer to
// an enclosing scope.
template <typename T>
struct Redefinition {
  template <typename U>
  friend void foo() // #REDEF
    requires constraint<U>
  {}

  struct ChildOfRedef {
    template <typename U>
    friend void foo2() // #REDEF2
      requires constraint<U>
    {}
  };
  template <typename T2>
  struct ChildOfRedef2 {
    template <typename U>
    friend void foo3() // #REDEF3
      requires constraint<U>
    {}
  };
};

void bar() {
  NonTemplateFriend<int> S1;
  NonTemplateFriend<float> S2;
  TemplateFromEnclosing<int> S3;
  TemplateFromEnclosing<int>::ChildOfEnclosing S3b;
  TemplateFromEnclosing<float> S4;
  TemplateFromEnclosing<float>::ChildOfEnclosing S4b;
  Redefinition<int> S5;
  Redefinition<float> S6;
  // expected-error@#REDEF {{redefinition of 'foo'}}
  // expected-note@-2{{in instantiation of template class }}
  // expected-note@#REDEF {{previous definition is here}}
  Redefinition<int>::ChildOfRedef S7;
  Redefinition<float>::ChildOfRedef S8;
  // expected-error@#REDEF2 {{redefinition of 'foo2'}}
  // expected-note@-2{{in instantiation of member class }}
  // expected-note@#REDEF2 {{previous definition is here}}

  Redefinition<int>::ChildOfRedef2<int> S9;
  Redefinition<float>::ChildOfRedef2<float> S10;
  // expected-error@#REDEF3 {{redefinition of 'foo3'}}
  // expected-note@-2{{in instantiation of template class }}
  // expected-note@#REDEF3 {{previous definition is here}}
}
} // namespace temp_friend_9

namespace SameScopeRedefs {
template <typename T>
struct NonTemplateFriend {
  friend void foo() // #NTF1
    requires true
  {}
  friend void foo() // #NTF2
    requires true
  {}
};

template <typename T>
struct TemplateFromEnclosing {
  template <typename U>
  friend void foo() // #TFE1
    requires constraint<T>
  {}
  template <typename U>
  friend void foo() // #TFE2
    requires constraint<T>
  {}
};
// Same as above, but doesn't require an instantiation pair to cause.
template <typename T>
struct Redefinition {
  template <typename U>
  friend void foo() // #RD1
    requires constraint<U>
  {}
  template <typename U>
  friend void foo() // #RD2
    requires constraint<U>
  {}
};
void bar() {
  NonTemplateFriend<int> S1;
  // expected-error@#NTF2 {{redefinition of 'foo'}}
  // expected-note@-2{{in instantiation of template class}}
  // expected-note@#NTF1 {{previous definition is here}}

  TemplateFromEnclosing<int> S2;
  // expected-error@#TFE2 {{redefinition of 'foo'}}
  // expected-note@-2{{in instantiation of template class}}
  // expected-note@#TFE1 {{previous definition is here}}

  Redefinition<int> S3;
  // expected-error@#RD2 {{redefinition of 'foo'}}
  // expected-note@-2{{in instantiation of template class}}
  // expected-note@#RD1 {{previous definition is here}}
}
} // namespace SameScopeRedefs

namespace LibCXXOperatorRedef {
template <typename T, typename U> struct is_same {
  static constexpr bool value = false;
};
template <typename T> struct is_same<T, T> {
  static constexpr bool value = false;
};

template <typename T, typename U>
concept same_as = is_same<T, U>::value;

// An issue found from libcxx when trying to commit the deferred concepts patch.
// This caused an error of 'redefinition of funcN'.
template <class _Tp> struct __range_adaptor_closure {
  template <typename _View, typename _Closure>
    requires same_as<_Tp, _Closure>
  friend constexpr decltype(auto) R1func1(_View &&__view,
                                          _Closure &&__closure){};
  template <typename _View, typename _Closure>
  friend constexpr decltype(auto) R1func2(_View &&__view,
                                          _Closure &&__closure)
    requires same_as<_Tp, _Closure>
  {};
  template <same_as<_Tp> _View, typename _Closure>
  friend constexpr decltype(auto) R1func3(_View &&__view,
                                          _Closure &&__closure){};
};

struct A : __range_adaptor_closure<A> {};
struct B : __range_adaptor_closure<B> {};

// These three fail because after the 1st pass of instantiation, they are still
// identical.
template <class _Tp> struct __range_adaptor_closure2 {
  template <typename _View, typename _Closure>
    requires same_as<_View, _Closure>
  friend constexpr decltype(auto) R2func1(_View &&__view, // #FUNC1
                                          _Closure &&__closure){};
  template <typename _View, typename _Closure>
  friend constexpr decltype(auto) R2func2(_View &&__view, // #FUNC2
                                          _Closure &&__closure)
    requires same_as<_View, _Closure>
  {};
  template <typename _View, same_as<_View> _Closure>
  friend constexpr decltype(auto) R2func3(_View &&__view, // #FUNC3
                                          _Closure &&__closure){};
};

struct A2 : __range_adaptor_closure2<A2> {};
struct B2 : __range_adaptor_closure2<B2> {};
// expected-error@#FUNC1{{redefinition of 'R2func1'}}
// expected-note@-2{{in instantiation of template class}}
// expected-note@#FUNC1{{previous definition is here}}
// expected-error@#FUNC2{{redefinition of 'R2func2'}}
// expected-note@#FUNC2{{previous definition is here}}
// expected-error@#FUNC3{{redefinition of 'R2func3'}}
// expected-note@#FUNC3{{previous definition is here}}

// These three are fine, they all depend on the parent template parameter, so
// are different despite ::type not being valid.
template <class _Tp> struct __range_adaptor_closure3 {
  template <typename _View, typename _Closure>
    requires same_as<typename _Tp::type, _Closure>
  friend constexpr decltype(auto) R3func1(_View &&__view,
                                          _Closure &&__closure){};
  template <typename _View, typename _Closure>
  friend constexpr decltype(auto) R3func2(_View &&__view,
                                          _Closure &&__closure)
    requires same_as<typename _Tp::type, _Closure>
  {};
  template <same_as<typename _Tp::type> _View, typename _Closure>
  friend constexpr decltype(auto) R3func3(_View &&__view,
                                          _Closure &&__closure){};
};

struct A3 : __range_adaptor_closure3<A3> {};
struct B3 : __range_adaptor_closure3<B3> {};

template <class _Tp> struct __range_adaptor_closure4 {
  template <typename _View, typename _Closure>
    requires same_as<_Tp, _View>
  // expected-note@+1{{previous definition is here}}
  void foo1(_View &&, _Closure &&) {}
  template <typename _View, typename _Closure>
    requires same_as<_Tp, _View>
  // expected-error@+1{{class member cannot be redeclared}}
  void foo1(_View &&, _Closure &&) {}

  template <typename _View, typename _Closure>
  // expected-note@+1{{previous definition is here}}
  void foo2(_View &&, _Closure &&)
    requires same_as<_Tp, _View>
  {}
  template <typename _View, typename _Closure>
  // expected-error@+1{{class member cannot be redeclared}}
  void foo2(_View &&, _Closure &&)
    requires same_as<_Tp, _View>
  {}

  template <same_as<_Tp> _View, typename _Closure>
  // expected-note@+1{{previous definition is here}}
  void foo3(_View &&, _Closure &&) {}
  template <same_as<_Tp> _View, typename _Closure>
  // expected-error@+1{{class member cannot be redeclared}}
  void foo3(_View &&, _Closure &&) {}
};

// Requires instantiation to fail, so no errors here.
template <class _Tp> struct __range_adaptor_closure5 {
  template <same_as<_Tp> U>
  friend void foo() {}
  template <same_as<_Tp> U>
  friend void foo() {}
};

template <class _Tp> struct __range_adaptor_closure6 {
  template <same_as<_Tp> U>
  friend void foo() {} // #RAC6FOO1
  template <same_as<_Tp> U>
  friend void foo() {} // #RAC6FOO2
};
struct A6 : __range_adaptor_closure6<A6> {};
// expected-error@#RAC6FOO2{{redefinition of 'foo'}}
// expected-note@-2{{in instantiation of template class}}
// expected-note@#RAC6FOO1{{previous definition is here}}

template <class T> struct S1 {
  template <typename U>
  friend void dupe() {} // #S1DUPE

  template <typename U>
    requires same_as<U, U>
  friend void dupe2() {} // #S1DUPE2
};
template <class T> struct S2 {
  template <typename U>
  friend void dupe() {} // #S2DUPE

  template <typename U>
    requires same_as<U, U>
  friend void dupe2() {} // #S2DUPE2
};

template <class T> struct S3 {
  template <typename U>
    requires same_as<T, U>
  friend void dupe() {}
};
template <class T> struct S4 {
  template <typename U>
    requires same_as<T, U>
  friend void dupe() {}
};

// Same as S3 and S4, but aren't instantiated with the same T.
template <class T> struct S5 {
  template <typename U>
    requires same_as<T, U>
  friend void not_dupe() {}
};
template <class T> struct S6 {
  template <typename U>
    requires same_as<T, U>
  friend void not_dupe() {}
};

template <class T> struct S7 {
  void not_dupe()
    requires same_as<T, T>
  {}
};

void useS() {
  S1<int> s1;
  S2<double> s2;
  // expected-error@#S2DUPE{{redefinition}}
  // expected-note@-2{{in instantiation of template class}}
  // expected-note@#S1DUPE{{previous definition is here}}
  // expected-error@#S2DUPE2{{redefinition}}
  // expected-note@#S1DUPE2{{previous definition is here}}

  // OK, they have different 'scopes'.
  S3<int> s3;
  S4<int> s4;

  // OK, because only instantiated with different T.
  S5<int> s5;
  S6<double> s6;

  S7<int> s7;
}

} // namespace LibCXXOperatorRedef

namespace NamedDeclRefs {
  namespace my_std {
    template<typename T, typename U>
      concept Outer = true;
    template<typename T>
      using Inner = T;
  }
  template<typename T>
    struct Proxy {
      template<class U>
        friend constexpr void RefOuter()
        requires my_std::Outer<my_std::Inner<T>, my_std::Inner<U>>{}
      template<class U>
        friend constexpr void NoRefOuter() // #NOREFOUTER
        requires my_std::Outer<my_std::Inner<U>, my_std::Inner<U>>{}
    };
  void use() {
    Proxy<int> p;
    Proxy<float> p2;
    // expected-error@#NOREFOUTER {{redefinition of 'NoRefOuter'}}
    // expected-note@-2{{in instantiation of template class}}
    // expected-note@#NOREFOUTER{{previous definition is here}}
  }
} // namespace NamedDeclRefs

namespace RefersToParentInConstraint {
  // No diagnostic, these aren't duplicates.
  template<typename T, typename U>
  concept similar = true;

  template <typename X>
  struct S{
    friend void f(similar<S> auto && self){}
    friend void f2(similar<S<X>> auto && self){}
  };

  void use() {
    S<int> x;
    S<long> y;
  }
} // namespace RefersToParentInConstraint

namespace NTTP {
  struct Base{};
  template<int N>
  struct S : Base {
    // N is from the parent template.
    template<typename T>
      friend int templ_func(Base&) requires(N > 0)
      { return 10; }
  };

  template<typename T>
  struct U : Base {
    template<T N>
      friend int templ_func(Base&) requires(N>0)
      { return 10; }
  };

  void use() {
    S<1> s1;
    templ_func<float>(s1);
    S<2> s2;
    templ_func<float>(s2);

    U<int> u1;
    templ_func<1>(u1);
    U<short> u2;
    templ_func<1>(u2);
  }
}


namespace FriendOfFriend {

template <typename>
concept Concept = true;

template <Concept> class FriendOfBar;

template <Concept> class Bar {
  template <Concept> friend class FriendOfBar;
};

Bar<void> BarInstance;

namespace internal {
void FriendOfFoo(FriendOfBar<void>);
}

template <Concept> class Foo {
  friend void internal::FriendOfFoo(FriendOfBar<void>);
};

} // namespace FriendOfFriend

namespace GH86769 {

template <typename T>
concept X = true;

template <X T> struct Y {
  Y(T) {}
  template <X U> friend struct Y;
  template <X U> friend struct Y;
  template <X U> friend struct Y;
};

template <class T>
struct Z {
  // FIXME: This is ill-formed per C++11 N3337 [temp.param]p12:
  // A default template argument shall not be specified in a friend class
  // template declaration.
  template <X U = void> friend struct Y;
};

template struct Y<int>;
template struct Z<int>;
Y y(1);

}

namespace GH98258 {

struct S {
  template <typename U>
  friend void f() requires requires { []<typename V>(V){}; } {
    return;
  }

  template <typename U>
  friend void f2() requires requires { [](auto){}; } {
    return;
  }

  template <typename U>
  friend void f3() requires requires { []<int X>(){ return X; }; } {
    return;
  }
};

}

namespace GH78101 {

template <typename T, int i>
concept True = true;

template <typename T, int I> struct Template {
  static constexpr int i = I;

  friend constexpr auto operator+(True<i> auto f) { return i; }
};

template <int I> struct Template<float, I> {
  static constexpr int i = I;

  friend constexpr auto operator+(True<i> auto f) { return i; }
};

Template<void, 4> f{};

static_assert(+Template<float, 5>{} == 5);

} // namespace GH78101