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

struct A {
  int &f(int*);
  float &f(int*) const noexcept;
  
  int *ptr;
  auto g1() noexcept(noexcept(f(ptr))) -> decltype(f(this->ptr));
  auto g2() const noexcept(noexcept(f((*this).ptr))) -> decltype(f(ptr));
};

void testA(A &a) {
  int &ir = a.g1();
  float &fr = a.g2();
  static_assert(!noexcept(a.g1()), "exception-specification failure");
  static_assert(noexcept(a.g2()), "exception-specification failure");
}

struct B {
  char g();
  template<class T> auto f(T t) -> decltype(t + g())
  { return t + g(); }
};

template auto B::f(int t) -> decltype(t + g());

template<typename T>
struct C {
  int &f(T*);
  float &f(T*) const noexcept;

  T* ptr;
  auto g1() noexcept(noexcept(f(ptr))) -> decltype(f(ptr));
  auto g2() const noexcept(noexcept(f(((this))->ptr))) -> decltype(f(ptr));
  auto g3() noexcept(noexcept(f(this->ptr))) -> decltype(f((*this).ptr));
  auto g4() const noexcept(noexcept(f(((this))->ptr))) -> decltype(f(this->ptr));
  auto g5() noexcept(noexcept(this->f(ptr))) -> decltype(this->f(ptr));
  auto g6() const noexcept(noexcept(this->f(((this))->ptr))) -> decltype(this->f(ptr));
  auto g7() noexcept(noexcept(this->f(this->ptr))) -> decltype(this->f((*this).ptr));
  auto g8() const noexcept(noexcept(this->f(((this))->ptr))) -> decltype(this->f(this->ptr));
};

void test_C(C<int> ci) {
  int &ir = ci.g1();
  float &fr = ci.g2();
  int &ir2 = ci.g3();
  float &fr2 = ci.g4();
  int &ir3 = ci.g5();
  float &fr3 = ci.g6();
  int &ir4 = ci.g7();
  float &fr4 = ci.g8();
  static_assert(!noexcept(ci.g1()), "exception-specification failure");
  static_assert(noexcept(ci.g2()), "exception-specification failure");
  static_assert(!noexcept(ci.g3()), "exception-specification failure");
  static_assert(noexcept(ci.g4()), "exception-specification failure");
  static_assert(!noexcept(ci.g5()), "exception-specification failure");
  static_assert(noexcept(ci.g6()), "exception-specification failure");
  static_assert(!noexcept(ci.g7()), "exception-specification failure");
  static_assert(noexcept(ci.g8()), "exception-specification failure");
}

namespace PR14263 {
  template<typename T> struct X {
    void f();
    T f() const;

    auto g()       -> decltype(this->f()) { return f(); }
    auto g() const -> decltype(this->f()) { return f(); }
  };
  template struct X<int>;
}

namespace PR10036 {
  template <class I>
  void
  iter_swap(I x, I y) noexcept;

  template <class T>
  class A
  {
    T t_;
  public:
    void swap(A& a) noexcept(noexcept(iter_swap(&t_, &a.t_)));
  };

  void test() {
    A<int> i, j;
    i.swap(j);
  }
}

namespace PR15290 {
  template<typename T>
  class A {
    T v_;
    friend int add_to_v(A &t) noexcept(noexcept(v_ + 42))
    {
      return t.v_ + 42;
    }
  };
  void f()
  {
    A<int> t;
    add_to_v(t);
  }
}

namespace Static {
  struct X1 {
    int m;
    // FIXME: This should be accepted.
    static auto f() -> decltype(m); // expected-error{{'this' cannot be implicitly used in a static member function declaration}}
    static auto g() -> decltype(this->m); // expected-error{{'this' cannot be used in a static member function declaration}}

    static int h();
    
    static int i() noexcept(noexcept(m + 2)); // expected-error{{'this' cannot be implicitly used in a static member function declaration}}
  };

  auto X1::h() -> decltype(m) { return 0; } // expected-error{{'this' cannot be implicitly used in a static member function declaration}}

  template<typename T>
  struct X2 {
    int m;

    T f(T*);
    static T f(int);

    auto g(T x) -> decltype(f(x)) { return 0; }
  };

  void test_X2() {
    X2<int>().g(0);
  }
}

namespace PR12564 {
  struct Base {
    void bar(Base&) {}
  };

  struct Derived : Base {
    void foo(Derived& d) noexcept(noexcept(d.bar(d))) {}
  };
}

namespace rdar13473493 {
  template <typename F>
  class wrap
  {
  public:
    template <typename... Args>
    auto operator()(Args&&... args) const -> decltype(wrapped(args...)) // expected-note{{candidate template ignored: substitution failure [with Args = <int>]: use of undeclared identifier 'wrapped'}}
    {
      return wrapped(args...);
    }
  
  private:
    F wrapped;
  };

  void test(wrap<int (*)(int)> w) {
    w(5); // expected-error{{no matching function for call to object of type 'wrap<int (*)(int)>'}}
  }
}
