// RUN: %clang_cc1 -fsyntax-only -verify %s
// PR5057
namespace test0 {
  namespace std {
    class X {
    public:
      template<typename T> friend struct Y;
    };
  }

  namespace std {
    template<typename T> struct Y {};
  }
}

namespace test1 {
  template<typename T> void f1(T) { } // expected-note{{here}}

  class X {
    template<typename T> friend void f0(T);
    template<typename T> friend void f1(T);
  };

  template<typename T> void f0(T) { }
  template<typename T> void f1(T) { } // expected-error{{redefinition}}
}

// PR4768
namespace test2 {
  template<typename T> struct X0 {
    template<typename U> friend struct X0;
  };
  
  template<typename T> struct X0<T*> {
    template<typename U> friend struct X0;
  };

  template<> struct X0<int> {
    template<typename U> friend struct X0;
  };

  template<typename T> struct X1 {
    template<typename U> friend void f2(U);
    template<typename U> friend void f3(U);
  };

  template<typename U> void f2(U);

  X1<int> x1i;
  X0<int*> x0ip;

  template<> void f2(int);

  // FIXME: Should this declaration of f3 be required for the specialization of
  // f3<int> (further below) to work? GCC and EDG don't require it, we do...
  template<typename U> void f3(U);

  template<> void f3(int);
}

// PR5332
namespace test3 {
  template <typename T> class Foo {
    template <typename U>
    friend class Foo;
  };

  Foo<int> foo;

  template<typename T, T Value> struct X2a;

  template<typename T, int Size> struct X2b;

  template<typename T>
  class X3 {
    template<typename U, U Value> friend struct X2a;

    // FIXME: the redeclaration note ends up here because redeclaration
    // lookup ends up finding the friend target from X3<int>.
    template<typename U, T Value> friend struct X2b; // expected-error {{template non-type parameter has a different type 'long' in template redeclaration}} \
      // expected-note {{previous non-type template parameter with type 'int' is here}}
  };

  X3<int> x3i; // okay

  X3<long> x3l; // expected-note {{in instantiation}}
}

// PR5716
namespace test4 {
  template<typename> struct A {
    template<typename T> friend void f(const A<T>&);
  };

  template<typename T> void f(const A<T>&) {
    int a[sizeof(T) ? -1 : -1]; // expected-error {{array with a negative size}}
  }

  void f() {
    f(A<int>()); // expected-note {{in instantiation of function template specialization}}
  }
}

namespace test5 {
  class outer {
    class foo;
    template <typename T> friend struct cache;
  };
  class outer::foo {
    template <typename T> friend struct cache;
  };
}

// PR6022
namespace PR6022 {
  template <class T1, class T2 , class T3  > class A;

  namespace inner {
    template<class T1, class T2, class T3, class T> 
    A<T1, T2, T3>& f0(A<T1, T2, T3>&, T);
  } 

  template<class T1, class T2, class T3>
  class A {
    template<class U1, class U2, class U3, class T>  
    friend A<U1, U2, U3>& inner::f0(A<U1, U2, U3>&, T);
  };
}

namespace FriendTemplateDefinition {
  template<unsigned > struct int_c { };

  template<typename T>
  struct X {
    template<unsigned N>
    friend void f(X, int_c<N>) {
      int value = N;
    };
  };

  void test_X(X<int> x, int_c<5> i5) {
    f(x, i5);
  }
}

namespace PR7013a {
  template<class > struct X0
  {
    typedef int type;
  };
  template<typename > struct X1
  {
  };
  template<typename , typename T> struct X2
  {
    typename T::type e;
  };
  namespace N
  {
    template <typename = int, typename = X1<int> > struct X3
    {
      template <typename T1, typename T2, typename B> friend void op(X2<T1, T2>& , B);
    };
    template <typename Ch, typename Tr, typename B> void op(X2<Ch, Tr>& , B)
    {
      X2<int, Tr> s;
    }
  }
  int n()
  {
    X2<int, X0<int> > ngs;
    N::X3<> b;
    op(ngs, b);
    return 0;
  }
}

namespace PR7013b {
  template<class > struct X0
  {
    typedef int type;
  };
  template<typename > struct X1
  {
  };
  template<typename , typename T> struct X2
  {
    typename T::type e;
  };
  namespace N
  {
    template <typename = X1<int> > struct X3
    {
      template <typename T1, typename T2, typename B> friend void op(X2<T1, T2>& , B);
    };
    template <typename Ch, typename Tr, typename B> void op(X2<Ch, Tr>& , B)
    {
      X2<int, Tr> s;
    }
  }
  int n()
  {
    X2<int, X0<int> > ngs;
    N::X3<> b;
    op(ngs, b);
    return 0;
  }

}

namespace PR8649 {
  template<typename T, typename U, unsigned N>
  struct X {
    template<unsigned M> friend class X<T, U, M>; // expected-error{{partial specialization cannot be declared as a friend}}
  };

  X<int, float, 7> x;
}

// Don't crash, and error on invalid friend type template.
namespace friend_type_template_no_tag {
  template <typename T> struct S {
    template <typename U> friend S<U>; // expected-error{{friend type templates must use an elaborated type}}
  };
  template struct S<int>;
}

namespace PR10660 {
  struct A {
    template <> friend class B; // expected-error{{extraneous 'template<>' in declaration of class 'B'}}
  };
}

namespace rdar11147355 {
  template <class T> 
  struct A {
    template <class U> class B;
    template <class S> template <class U> friend class A<S>::B; 
  };
  
  template <class S> template <class U> class A<S>::B {
  }; 
  
  A<double>::B<double>  ab;
}

namespace RedeclUnrelated {
  struct S {
    int packaged_task;
    template<typename> class future {
      template<typename> friend class packaged_task;
    };
    future<void> share;
  };
}

namespace PR12557 {
  template <typename>
  struct Foo;

  template <typename Foo_>
  struct Bar {
    typedef Foo_  Foo; // expected-note {{previous}}

    template <typename> friend struct Foo; // expected-error {{redefinition of 'Foo' as different kind of symbol}}
  };

  Bar<int> b;
}

namespace PR12585 {
  struct A { };
  template<typename> struct B {
    template<typename> friend class A::does_not_exist; // \
     // expected-error {{friend declaration of 'does_not_exist' does not match any declaration in 'PR12585::A'}}
  };

  struct C {
    template<typename> struct D;
  };
  template<typename> class E {
    int n;
    template<typename> friend struct C::D;
  };
  template<typename T> struct C::D {
    int f() {
      return E<int>().n;
    }
  };
  int n = C::D<void*>().f();

  struct F {
    template<int> struct G;
  };
  template<typename T> struct H {
    // FIXME: As with cases above, the note here is on an unhelpful declaration,
    // and should point to the declaration of G within F.
    template<T> friend struct F::G; // \
      // expected-error {{different type 'char' in template redeclaration}} \
      // expected-note {{previous}}
  };
  H<int> h1; // ok
  H<char> h2; // expected-note {{instantiation}}
}
