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

struct A;
struct B;
struct C;

struct S {};
template <typename> struct TS {};

template <typename ...Pack>
class X {
  friend Pack...;
  static void f() { } // expected-note {{declared private here}}
};

class Y {
  friend A, B, C;
  static void g() { } // expected-note {{declared private here}}
};

struct A {
  A() {
    X<A>::f();
    Y::g();
  };
};

struct B {
  B() {
    X<B, C>::f();
    Y::g();
  };
};

struct C {
  C() {
    X<A, B, C>::f();
    Y::g();
  };
};

struct D {
  D() {
    X<A, B, C>::f(); // expected-error {{'f' is a private member of 'X<A, B, C>'}}
    Y::g(); // expected-error {{'g' is a private member of 'Y'}}
  };
};

void f1() {
  A a;
  B b;
  C c;
  D d;
}

template <typename ...Pack>
struct Z {
  template <template <typename> class Template>
  struct Inner {
    friend Template<Pack>...;
  };
};

void f2() {
  Z<int, long, char> z;
  Z<int, long, char>::Inner<TS> inner;
}

namespace p2893r3_examples {
template<class... Ts>
class Passkey {
  friend Ts...;
  Passkey() {} // expected-note {{declared private here}}
};

class Foo;
class Bar;
class Baz;

class C {
public:
  void f(Passkey<Foo, Bar, Baz>);
};

class Foo {
  Foo() { C c; c.f({}); }
};

class Bar {
  Bar() { C c; c.f({}); }
};

class Baz {
  Baz() { C c; c.f({}); }
};

class Quux {
  Quux() { C c; c.f({}); } // expected-error {{calling a private constructor of class 'p2893r3_examples::Passkey<p2893r3_examples::Foo, p2893r3_examples::Bar, p2893r3_examples::Baz>'}}
};

template<class Derived, class MsgT>
struct Receiver {
  void receive(MsgT) {
    static_cast<Derived*>(this)->private_ += 1;
  }
};

template<class... MsgTs>
struct Dispatcher : Receiver<Dispatcher<MsgTs...>, MsgTs>... {
  using Receiver<Dispatcher, MsgTs>::receive...;
  friend Receiver<Dispatcher, MsgTs>...;

private:
  int private_;
};

void f() {
  Dispatcher<int, float> d;
  d.receive(0);
  d.receive(0.0f);
}
} // namespace p2893r3_examples

namespace p2893r3_note {
template <class... Ts> class R {
  friend Ts...;
};

template <class... Ts, class... Us>
class R<R<Ts...>, R<Us...>> {
  friend Ts::Nested..., Us...;
};

struct E { struct Nested; };
R<R<E>, R<C, int>> rr;
} // namespace p2893r3_note

namespace template_template {
template <typename U, template <typename> typename... Friend>
class S {
  friend class Friend<U>...;
  static constexpr int a = 42;
};

template <typename U>
struct T {
  static_assert(S<U, T>::a == 42);
  static_assert(S<U, T>::a == 43); // expected-error {{static assertion failed due to requirement 'template_template::S<int, template_template::T>::a == 43'}} \
                                   // expected-note {{expression evaluates to '42 == 43'}}
};

void f() {
  T<int> t; // expected-note {{in instantiation of}}
}
}

