cjdb updated this revision to Diff 450581.
cjdb retitled this revision from "WORK-IN-PROGRESS [clang] adds builtin 
`std::invoke` and `std::invoke_r`" to "[clang] adds builtin `std::invoke` and 
`std::invoke_r`".
cjdb edited the summary of this revision.
cjdb added a comment.

updates commit message

- changes message so that it's accurate
- no longer adding type traits in this commit
- adds bechmark

updates diagnoses so that they're more accurate


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D130867/new/

https://reviews.llvm.org/D130867

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/Basic/Builtins.def
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Sema/Sema.h
  clang/lib/AST/ExprConstant.cpp
  clang/lib/CodeGen/CGBuiltin.cpp
  clang/lib/Sema/SemaChecking.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaExpr.cpp
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaOverload.cpp
  clang/test/SemaCXX/builtin-std-invoke.cpp

Index: clang/test/SemaCXX/builtin-std-invoke.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/builtin-std-invoke.cpp
@@ -0,0 +1,468 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify %s
+
+namespace std {
+template <class F, class... Args>
+void invoke(F &&, Args &&...); // expected-note{{requires at least 1 argument, but 0 were provided}}
+
+template <class R, class F, class... Args>
+R invoke_r(F &&, Args &&...); // expected-note{{requires at least 1 argument, but 0 were provided}}
+
+// Slightly different to the real deal to simplify test.
+template <class T>
+class reference_wrapper {
+public:
+  constexpr reference_wrapper(T &t) : data(&t) {}
+
+  constexpr operator T &() const noexcept { return *data; }
+
+private:
+  T *data;
+};
+} // namespace std
+
+#define assert(...)   \
+  if (!(__VA_ARGS__)) \
+    __builtin_unreachable();
+
+struct ThrowingInt {
+  constexpr ThrowingInt(int x) : value(x) {}
+
+  int value;
+};
+
+template <class Returns, bool IsNoexcept, int ExpectedResult, class F, class T, class... Args>
+constexpr void bullet_1(F f, T &&t, Args... args) {
+  assert(std::invoke(f, static_cast<T &&>(t), args...) == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke(f, static_cast<T &&>(t), args...)), Returns));
+  static_assert(noexcept(std::invoke(f, static_cast<T &&>(t), args...)) == IsNoexcept);
+
+  assert(std::invoke_r<double>(f, static_cast<T &&>(t), args...) == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke_r<double>(f, static_cast<T &&>(t), args...)), double));
+  static_assert(noexcept(std::invoke_r<double>(f, static_cast<T &&>(t), args...)) == IsNoexcept);
+
+  assert(std::invoke_r<ThrowingInt>(f, static_cast<T &&>(t), args...).value == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke_r<ThrowingInt>(f, static_cast<T &&>(t), args...)), ThrowingInt));
+  static_assert(!noexcept(std::invoke_r<ThrowingInt>(f, static_cast<T &&>(t), args...)));
+}
+
+template <class Returns, bool IsNoexcept, int ExpectedResult, class F, class T, class... Args>
+constexpr void bullet_2(F f, T &t, Args... args) {
+  std::reference_wrapper<T> rw(t);
+  assert(std::invoke(f, rw, args...) == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke(f, rw, args...)), Returns));
+  static_assert(noexcept(std::invoke(f, rw, args...)) == IsNoexcept);
+
+  assert(std::invoke_r<double>(f, rw, args...) == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke_r<double>(f, rw, args...)), double));
+  static_assert(noexcept(std::invoke_r<double>(f, rw, args...)) == IsNoexcept);
+
+  assert(std::invoke_r<ThrowingInt>(f, rw, args...).value == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke_r<ThrowingInt>(f, rw, args...)), ThrowingInt));
+  static_assert(!noexcept(std::invoke_r<ThrowingInt>(f, rw, args...)));
+}
+
+template <class T>
+class PointerWrapper {
+public:
+  constexpr explicit PointerWrapper(T &t) noexcept : p(&t) {}
+
+  constexpr T &operator*() const noexcept { return *p; }
+
+private:
+  T *p;
+};
+
+template <class Returns, bool IsNoexcept, int ExpectedResult, class F, class T, class... Args>
+constexpr void bullet_3(F f, T &t, Args... args) {
+  assert(std::invoke(f, &t, args...) == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke(f, &t, args...)), Returns));
+  static_assert(noexcept(std::invoke(f, &t, args...)) == IsNoexcept);
+
+  assert(std::invoke(f, PointerWrapper(t), args...) == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke(f, PointerWrapper(t), args...)), Returns));
+  static_assert(noexcept(std::invoke(f, PointerWrapper(t), args...)) == IsNoexcept);
+
+  assert(std::invoke_r<double>(f, &t, args...) == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke_r<double>(f, &t, args...)), double));
+  static_assert(noexcept(std::invoke_r<double>(f, &t, args...)) == IsNoexcept);
+
+  assert(std::invoke_r<double>(f, PointerWrapper(t), args...) == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke_r<double>(f, PointerWrapper(t), args...)), double));
+  static_assert(noexcept(std::invoke_r<double>(f, PointerWrapper(t), args...)) == IsNoexcept);
+
+  assert(std::invoke_r<ThrowingInt>(f, &t, args...).value == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke_r<ThrowingInt>(f, &t, args...)), ThrowingInt));
+  static_assert(!noexcept(std::invoke_r<ThrowingInt>(f, &t, args...)));
+
+  assert(std::invoke_r<ThrowingInt>(f, PointerWrapper(t), args...).value == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke_r<ThrowingInt>(f, PointerWrapper(t), args...)), ThrowingInt));
+  static_assert(!noexcept(std::invoke_r<ThrowingInt>(f, PointerWrapper(t), args...)));
+}
+
+template <class T>
+constexpr bool bullets_1_through_3(T t) {
+  bullet_1</*Returns=*/int &, /*IsNoexcept=*/true, /*ExpectedResult=*/0>(&T::plus, t, 3, -3);
+  bullet_1</*Returns=*/const int &, /*IsNoexcept=*/true, /*ExpectedResult=*/-4>(&T::minus, static_cast<const T &>(t), 1, 2, 3);
+  bullet_1</*Returns=*/int &&, /*IsNoexcept=*/false, /*ExpectedResult=*/49>(&T::square, static_cast<__remove_reference(T) &&>(t), 7);
+  bullet_1</*Returns=*/const int &&, /*IsNoexcept=*/false, /*ExpectedResult=*/-63>(&T::sum, static_cast<const T &&>(t), -1, -2, -4, -8, -16, -32);
+
+  bullet_2</*Returns=*/int &, /*IsNoexcept=*/true, /*ExpectedResult=*/0>(&T::plus, t, 3, -3);
+  bullet_2</*Returns=*/const int &, /*IsNoexcept=*/true, /*ExpectedResult=*/-4>(&T::minus, static_cast<const T &>(t), 1, 2, 3);
+
+  bullet_3</*Returns=*/int &, /*IsNoexcept=*/true, /*ExpectedResult=*/0>(&T::plus, t, 3, -3);
+  bullet_3</*Returns=*/const int &, /*IsNoexcept=*/true, /*ExpectedResult=*/-4>(&T::minus, static_cast<const T &>(t), 1, 2, 3);
+
+  return true;
+}
+
+template <class Returns, class F, class T, class U>
+constexpr void bullet_4(F f, T &&t, U ExpectedResult) {
+  assert(std::invoke(f, static_cast<T &&>(t)) == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke(f, static_cast<T &&>(t))), Returns));
+  static_assert(noexcept(std::invoke(f, static_cast<T &&>(t))));
+
+  assert(std::invoke_r<double>(f, static_cast<T &&>(t)) == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke_r<double>(f, static_cast<T &&>(t))), double));
+  static_assert(noexcept(std::invoke_r<double>(f, static_cast<T &&>(t))));
+
+  assert(std::invoke_r<ThrowingInt>(f, static_cast<T &&>(t)).value == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke_r<ThrowingInt>(f, static_cast<T &&>(t))), ThrowingInt));
+  static_assert(!noexcept(std::invoke_r<ThrowingInt>(f, static_cast<T &&>(t))));
+}
+
+template <class Returns, class F, class T, class U>
+constexpr void bullet_5(F f, T &t, U ExpectedResult) {
+  std::reference_wrapper<T> rw(t);
+  assert(std::invoke(f, rw) == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke(f, rw)), Returns));
+  static_assert(noexcept(std::invoke(f, rw)));
+
+  assert(std::invoke_r<double>(f, rw) == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke_r<double>(f, rw)), double));
+  static_assert(noexcept(std::invoke_r<double>(f, rw)));
+
+  assert(std::invoke_r<ThrowingInt>(f, rw).value == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke_r<ThrowingInt>(f, rw)), ThrowingInt));
+  static_assert(!noexcept(std::invoke_r<ThrowingInt>(f, rw)));
+}
+
+template <class Returns, class F, class T, class U>
+constexpr void bullet_6(F f, T &t, U ExpectedResult) {
+  assert(std::invoke(f, &t) == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke(f, &t)), Returns));
+  static_assert(noexcept(std::invoke(f, &t)));
+
+  assert(std::invoke(f, PointerWrapper(t)) == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke(f, PointerWrapper(t))), Returns));
+  static_assert(noexcept(std::invoke(f, PointerWrapper(t))));
+
+  assert(std::invoke_r<double>(f, &t) == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke_r<double>(f, &t)), double));
+  static_assert(noexcept(std::invoke_r<double>(f, &t)));
+
+  assert(std::invoke_r<double>(f, PointerWrapper(t)) == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke_r<double>(f, PointerWrapper(t))), double));
+  static_assert(noexcept(std::invoke_r<double>(f, PointerWrapper(t))));
+
+  assert(std::invoke_r<ThrowingInt>(f, &t).value == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke_r<ThrowingInt>(f, &t)), ThrowingInt));
+  static_assert(!noexcept(std::invoke_r<ThrowingInt>(f, &t)));
+
+  assert(std::invoke_r<ThrowingInt>(f, PointerWrapper(t)).value == ExpectedResult);
+  static_assert(__is_same(decltype(std::invoke_r<ThrowingInt>(f, PointerWrapper(t))), ThrowingInt));
+  static_assert(!noexcept(std::invoke_r<ThrowingInt>(f, PointerWrapper(t))));
+}
+
+template <class F, class T, class U>
+constexpr bool bullets_4_through_6(F f, T t, U ExpectedResult) {
+  bullet_4</*Returns=*/U &>(f, t, ExpectedResult);
+  bullet_4</*Returns=*/const U &>(f, static_cast<const T &>(t), ExpectedResult);
+  bullet_4</*Returns=*/U &&>(f, static_cast<T &&>(t), ExpectedResult);
+  bullet_4</*Returns=*/const U &&>(f, static_cast<const T &&>(t), ExpectedResult);
+
+  bullet_5</*Returns=*/U &>(f, t, ExpectedResult);
+  bullet_5</*Returns=*/const U &>(f, static_cast<const T &>(t), ExpectedResult);
+
+  bullet_6</*Returns=*/U &>(f, t, ExpectedResult);
+  bullet_6</*Returns=*/const U &>(f, static_cast<const T &>(t), ExpectedResult);
+
+  return true;
+}
+
+constexpr int zero() { return 0; }
+constexpr int square(int x) { return x * x; } // expected-note 4 {{'square' declared here}}
+constexpr double product(double x, double y) { return x * y; }
+
+struct summation {
+  template <class... Ts>
+  constexpr auto operator()(Ts... ts) {
+    return (ts + ...);
+  }
+};
+
+struct callable {
+  int x;
+
+  constexpr int &operator()() &noexcept { return x; }                                          // expected-note 2 {{candidate function not viable: requires 0 arguments, but 2 were provided}}
+  constexpr const int &operator()() const &noexcept { return x; }                              // expected-note 2 {{candidate function not viable: requires 0 arguments, but 2 were provided}}
+  constexpr int &&operator()() &&noexcept { return static_cast<int &&>(x); }                   // expected-note 2 {{candidate function not viable: requires 0 arguments, but 2 were provided}}
+  constexpr const int &&operator()() const &&noexcept { return static_cast<const int &&>(x); } // expected-note 2 {{candidate function not viable: requires 0 arguments, but 2 were provided}}
+};
+
+template <class T, auto expected, bool IsNoexcept, class F, class... Args>
+constexpr bool bullet_7(F &&f, Args &&...args) {
+  assert(std::invoke(static_cast<F>(f), static_cast<Args>(args)...) == expected);
+  static_assert(__is_same(decltype(std::invoke(static_cast<F>(f), static_cast<Args>(args)...)), T));
+  static_assert(noexcept(std::invoke(static_cast<F>(f), static_cast<Args>(args)...)) == IsNoexcept);
+
+  assert(std::invoke_r<double>(f, args...) == expected);
+  static_assert(__is_same(decltype(std::invoke_r<double>(f, args...)), double));
+  static_assert(noexcept(std::invoke_r<double>(f, args...)) == IsNoexcept);
+
+  assert(std::invoke_r<ThrowingInt>(f, args...).value == expected);
+  static_assert(__is_same(decltype(std::invoke_r<ThrowingInt>(f, args...)), ThrowingInt));
+  static_assert(!noexcept(std::invoke_r<ThrowingInt>(f, args...)));
+
+  return true;
+}
+
+template <class T, class Expected>
+constexpr bool bullet_7_1() {
+  callable c{21};
+  return std::invoke(static_cast<T>(c)) == 21 &&
+         __is_same(decltype(std::invoke(static_cast<T>(c))), Expected) &&noexcept(std::invoke(static_cast<T>(c)));
+}
+
+struct Base {
+  mutable int data;
+
+  constexpr int &plus(int x, int y) &noexcept {
+    data = x + y;
+    return data;
+  }
+
+  constexpr const int &minus(int x, int y, int z) const &noexcept {
+    data = x - y - z;
+    return data;
+  }
+
+  constexpr int &&square(int x) && {
+    data = x * x;
+    return static_cast<int &&>(data);
+  }
+
+  constexpr const int &&sum(int a, int b, int c, int d, int e, int f) const && {
+    data = a + b + c + d + e + f;
+    return static_cast<const int &&>(data);
+  }
+};
+
+struct Derived : Base {
+  double data2;
+};
+
+void test_invoke() {
+  static_assert(bullets_1_through_3(Base{}));
+  static_assert(bullets_1_through_3(Derived{}));
+
+  static_assert(bullets_4_through_6(&Base::data, Base{21}, 21));
+  static_assert(bullets_4_through_6(&Base::data, Derived{-96, 18}, -96));
+  static_assert(bullets_4_through_6(&Derived::data2, Derived{21, 34}, 34.0));
+
+  static_assert(bullet_7<int, 0, false>(zero));
+  static_assert(bullet_7<int, 25, false>(square, 5.0));
+  static_assert(bullet_7<double, 9, false>(product, 9, 1));
+  static_assert(bullet_7<int, 0, false>(&zero));
+  static_assert(bullet_7<long, 55L, false>(summation{}, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10L));
+  static_assert(bullet_7<int, 18, false>([] { return 18; }));
+
+  static_assert(bullet_7_1<callable &, int &>());
+  static_assert(bullet_7_1<const callable &, const int &>());
+  static_assert(bullet_7_1<callable &&, int &&>());
+  static_assert(bullet_7_1<const callable &&, const int &&>());
+}
+
+struct Ambiguous {
+  int operator()(int) const;           // expected-note 2 {{candidate function}}
+  long operator()(unsigned int) const; // expected-note 2 {{candidate function}}
+};
+
+struct Deleted {
+  int operator()() const = delete; // expected-note 2 {{candidate function has been explicitly deleted}}
+};
+
+int deleted_function() = delete; // expected-note 2 {{'deleted_function' has been explicitly marked deleted here}}
+
+struct Incompatible {};
+
+void test_errors() {
+  // TODO: add cases for bullets 1--6 where 2nd param is an int
+  std::invoke();        // expected-error{{no matching function for call to 'invoke'}}
+  std::invoke_r<int>(); // expected-error{{no matching function for call to 'invoke_r'}}
+
+  { // Concerning bullet 1
+    std::invoke(&Base::plus);
+    // expected-error@-1{{can't invoke pointer-to-member function: 'std::invoke' must have at least 2 arguments for a pointer-to-member function, got 1}}
+    std::invoke_r<double>(&Base::plus);
+    // expected-error@-1{{can't invoke pointer-to-member function: 'std::invoke_r' must have at least 2 arguments for a pointer-to-member function, got 1}}
+    std::invoke(&Base::plus, Incompatible{}, 1, 2);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected second argument to be a reference to a class compatible with 'Base', got 'Incompatible'}}
+    std::invoke_r<double>(&Base::plus, Incompatible{}, 1, 2);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected second argument to be a reference to a class compatible with 'Base', got 'Incompatible'}}
+
+    std::invoke(&Base::sum, Base{}, 0);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected 6 arguments, got 1}}
+    std::invoke_r<double>(&Base::sum, Base{}, 0);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected 6 arguments, got 1}}
+
+    std::invoke(&Base::sum, Base{}, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected 6 arguments, got 10}}
+    std::invoke_r<double>(&Base::sum, Base{}, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected 6 arguments, got 10}}
+
+    std::invoke_r<void *>(&Base::sum, Base{}, 0, 1, 2, 3, 4, 5);
+    // expected-error@-1{{can't invoke pointer-to-member function: return type 'const int' isn't convertible to 'void *'}}
+  }
+  { // Concerning bullet 2
+    Base b;
+    std::reference_wrapper<Base> rw(b);
+
+    Incompatible p;
+    std::reference_wrapper<Incompatible> pw(p);
+
+    std::invoke(&Base::plus, pw, 1, 2);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected second argument to be a wrapee to a class compatible with 'Base', got 'Incompatible'}}
+    std::invoke_r<double>(&Base::plus, pw, 1, 2);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected second argument to be a wrapee to a class compatible with 'Base', got 'Incompatible'}}
+
+    std::invoke(&Base::plus, rw, 0);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected 2 arguments, got 1}}
+    std::invoke_r<double>(&Base::plus, rw, 0);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected 2 arguments, got 1}}
+
+    std::invoke(&Base::plus, rw, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected 2 arguments, got 10}}
+    std::invoke_r<double>(&Base::plus, rw, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected 2 arguments, got 10}}
+
+    std::invoke_r<void *>(&Base::plus, rw, 4, 1);
+    // expected-error@-1{{can't invoke pointer-to-member function: return type 'int' isn't convertible to 'void *'}}
+  }
+  { // Concerning bullet 3
+    Base b;
+    Incompatible p;
+
+    std::invoke(&Base::plus, &p, 1, 2);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected second argument to be a pointer to a class compatible with 'Base', got 'Incompatible *'}}
+    std::invoke_r<double>(&Base::plus, &p, 1, 2);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected second argument to be a pointer to a class compatible with 'Base', got 'Incompatible *'}}
+
+    std::invoke(&Base::plus, &b, 0);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected 2 arguments, got 1}}
+    std::invoke_r<double>(&Base::plus, &b, 0);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected 2 arguments, got 1}}
+
+    std::invoke(&Base::plus, &b, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected 2 arguments, got 10}}
+    std::invoke_r<double>(&Base::plus, &b, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+    // expected-error@-1{{can't invoke pointer-to-member function: expected 2 arguments, got 10}}
+
+    std::invoke_r<void *>(&Base::plus, &b, 4, 1);
+    // expected-error@-1{{can't invoke pointer-to-member function: return type 'int' isn't convertible to 'void *'}}
+  }
+  { // Concerning bullet 4
+    std::invoke(&Base::data);
+    // expected-error@-1{{can't invoke pointer-to-data member: 'std::invoke' must have exactly 2 arguments for a pointer-to-data member, got 1}}
+    std::invoke_r<double>(&Base::data);
+    // expected-error@-1{{can't invoke pointer-to-data member: 'std::invoke_r' must have exactly 2 arguments for a pointer-to-data member, got 1}}
+
+    std::invoke(&Base::data, Incompatible{});
+    // expected-error@-1{{can't invoke pointer-to-data member: expected second argument to be a reference to a class compatible with 'Base', got 'Incompatible'}}
+    std::invoke_r<double>(&Base::data, Incompatible{});
+    // expected-error@-1{{can't invoke pointer-to-data member: expected second argument to be a reference to a class compatible with 'Base', got 'Incompatible'}}
+
+    std::invoke(&Base::data, Base{}, Base{});
+    // expected-error@-1{{can't invoke pointer-to-data member: 'std::invoke' must have exactly 2 arguments for a pointer-to-data member, got 1}}
+    std::invoke_r<double>(&Base::data, Base{}, Base{});
+    // expected-error@-1{{can't invoke pointer-to-data member: 'std::invoke_r' must have exactly 2 arguments for a pointer-to-data member, got 1}}
+
+    std::invoke_r<void *>(&Base::data, Base{});
+    // expected-error@-1{{can't invoke pointer-to-data member: return type 'int' isn't convertible to 'void *'}}
+  }
+  { // Concerning bullet 5
+    Base b;
+    std::reference_wrapper<Base> rw(b);
+
+    Incompatible p;
+    std::reference_wrapper<Incompatible> pw(p);
+
+    std::invoke(&Base::data, pw);
+    // expected-error@-1{{can't invoke pointer-to-data member: expected second argument to be a wrapee to a class compatible with 'Base', got 'Incompatible'}}
+    std::invoke_r<double>(&Base::data, pw);
+    // expected-error@-1{{can't invoke pointer-to-data member: expected second argument to be a wrapee to a class compatible with 'Base', got 'Incompatible'}}
+
+    std::invoke(&Base::data, rw, Base{});
+    // expected-error@-1{{can't invoke pointer-to-data member: 'std::invoke' must have exactly 2 arguments for a pointer-to-data member, got 1}}
+    std::invoke_r<double>(&Base::data, rw, Base{});
+    // expected-error@-1{{can't invoke pointer-to-data member: 'std::invoke_r' must have exactly 2 arguments for a pointer-to-data member, got 1}}
+
+    std::invoke_r<void *>(&Base::data, rw);
+    // expected-error@-1{{can't invoke pointer-to-data member: return type 'int' isn't convertible to 'void *'}}
+  }
+  { // Concerning bullet 6
+    Base b;
+    Incompatible p;
+
+    std::invoke(&Base::data, &p);
+    // expected-error@-1{{can't invoke pointer-to-data member: expected second argument to be a pointer to a class compatible with 'Base', got 'Incompatible *'}}
+    std::invoke_r<double>(&Base::data, &p);
+    // expected-error@-1{{can't invoke pointer-to-data member: expected second argument to be a pointer to a class compatible with 'Base', got 'Incompatible *'}}
+
+    std::invoke(&Base::data, &b, Base{});
+    // expected-error@-1{{can't invoke pointer-to-data member: 'std::invoke' must have exactly 2 arguments for a pointer-to-data member, got 1}}
+    std::invoke_r<double>(&Base::data, &b, Base{});
+    // expected-error@-1{{can't invoke pointer-to-data member: 'std::invoke_r' must have exactly 2 arguments for a pointer-to-data member, got 1}}
+
+    std::invoke_r<void *>(&Base::data, &b);
+    // expected-error@-1{{can't invoke pointer-to-data member: return type 'int' isn't convertible to 'void *'}}
+  }
+  { // Concerning bullet 7
+    std::invoke(square);
+    // expected-error@-1{{can't invoke function: expected 1 argument, got 0}}
+    (void)std::invoke_r<double>(square);
+    // expected-error@-1{{can't invoke function: expected 1 argument, got 0}}
+
+    std::invoke(square, 1, 2);
+    // expected-error@-1{{can't invoke function: expected 1 argument, got 2}}
+    (void)std::invoke_r<double>(square, 1, 2);
+    // expected-error@-1{{can't invoke function: expected 1 argument, got 2}}
+
+    std::invoke(&product, 1);
+    // expected-error@-1{{can't invoke function: expected 2 arguments, got 1}}
+    (void)std::invoke_r<double>(&product, 1);
+    // expected-error@-1{{can't invoke function: expected 2 arguments, got 1}}
+
+    std::invoke(deleted_function);
+    // expected-error@-1{{attempt to use a deleted function}}
+    (void)std::invoke_r<double>(deleted_function);
+    // expected-error@-1{{attempt to use a deleted function}}
+
+    std::invoke(callable{1}, 1, 2);
+    // expected-error@-1{{can't invoke 'callable' function object: no suitable overload found}}
+    (void)std::invoke_r<double>(callable{1}, 1, 2);
+    // expected-error@-1{{can't invoke 'callable' function object: no suitable overload found}}
+
+    (void)std::invoke_r<callable>(callable{1});
+    // expected-error@-1{{can't invoke 'callable' function object: return type 'int' isn't convertible to 'callable'}}
+
+    std::invoke(Ambiguous{}, 0.0);
+    // expected-error@-1{{can't invoke 'Ambiguous' function object: 2 suitable overloads found, which makes choosing ambiguous}}
+    (void)std::invoke_r<double>(Ambiguous{}, 0.0);
+    // expected-error@-1{{can't invoke 'Ambiguous' function object: 2 suitable overloads found, which makes choosing ambiguous}}
+
+    std::invoke(Deleted{});
+    // expected-error@-1{{can't invoke 'Deleted' function object: chosen overload candidate is deleted}}
+    (void)std::invoke_r<double>(Deleted{});
+    // expected-error@-1{{can't invoke 'Deleted' function object: chosen overload candidate is deleted}}
+  }
+}
Index: clang/lib/Sema/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -52,8 +52,8 @@
 static ExprResult CreateFunctionRefExpr(
     Sema &S, FunctionDecl *Fn, NamedDecl *FoundDecl, const Expr *Base,
     bool HadMultipleCandidates, SourceLocation Loc = SourceLocation(),
-    const DeclarationNameLoc &LocInfo = DeclarationNameLoc()) {
-  if (S.DiagnoseUseOfDecl(FoundDecl, Loc))
+    const DeclarationNameLoc &LocInfo = DeclarationNameLoc(), bool IsStdInvoke = false) {
+  if (S.DiagnoseUseOfDecl(FoundDecl, Loc, {}, {}, {}, {}, IsStdInvoke))
     return ExprError();
   // If FoundDecl is different from Fn (such as if one is a template
   // and the other a specialization), make sure DiagnoseUseOfDecl is
@@ -6412,7 +6412,7 @@
       NamedDecl *ND = Function;
       if (auto *SpecInfo = Function->getTemplateSpecializationInfo())
         ND = SpecInfo->getTemplate();
-      
+
       if (ND->getFormalLinkage() == Linkage::InternalLinkage) {
         Candidate.Viable = false;
         Candidate.FailureKind = ovl_fail_module_mismatched;
@@ -9671,7 +9671,7 @@
                                           const OverloadCandidate &Cand1,
                                           const OverloadCandidate &Cand2) {
   // FIXME: Per P2113R0 we also need to compare the template parameter lists
-  // when comparing template functions. 
+  // when comparing template functions.
   if (Cand1.Function && Cand2.Function && Cand1.Function->hasPrototype() &&
       Cand2.Function->hasPrototype()) {
     auto *PT1 = cast<FunctionProtoType>(Cand1.Function->getFunctionType());
@@ -13401,7 +13401,7 @@
 ExprResult
 Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc,
                               const UnresolvedSetImpl &Fns,
-                              Expr *Input, bool PerformADL) {
+                              Expr *Input, bool PerformADL, bool IsStdInvoke) {
   OverloadedOperatorKind Op = UnaryOperator::getOverloadedOperator(Opc);
   assert(Op != OO_None && "Invalid opcode for overloaded unary operator");
   DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(Op);
@@ -13571,7 +13571,7 @@
   // Either we found no viable overloaded operator or we matched a
   // built-in operator. In either case, fall through to trying to
   // build a built-in operation.
-  return CreateBuiltinUnaryOp(OpLoc, Opc, Input);
+  return CreateBuiltinUnaryOp(OpLoc, Opc, Input, IsStdInvoke);
 }
 
 /// Perform lookup for an overloaded binary operator.
@@ -14384,7 +14384,8 @@
                                            MultiExprArg Args,
                                            SourceLocation RParenLoc,
                                            Expr *ExecConfig, bool IsExecConfig,
-                                           bool AllowRecovery) {
+                                           bool AllowRecovery, bool IsStdInvoke)
+{
   assert(MemExprE->getType() == Context.BoundMemberTy ||
          MemExprE->getType() == Context.OverloadTy);
 
@@ -14418,6 +14419,7 @@
     difference.removeAddressSpace();
     if (difference) {
       std::string qualsString = difference.getAsString();
+      // TODO: add invoke equivalent
       Diag(LParenLoc, diag::err_pointer_to_member_call_drops_quals)
         << fnType.getUnqualifiedType()
         << qualsString
@@ -14432,7 +14434,7 @@
                             call, nullptr))
       return ExprError();
 
-    if (ConvertArgumentsForCall(call, op, nullptr, proto, Args, RParenLoc))
+    if (ConvertArgumentsForCall(call, op, nullptr, proto, Args, RParenLoc, false, IsStdInvoke))
       return ExprError();
 
     if (CheckOtherCall(call, proto))
@@ -14679,7 +14681,8 @@
 Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
                                    SourceLocation LParenLoc,
                                    MultiExprArg Args,
-                                   SourceLocation RParenLoc) {
+                                   SourceLocation RParenLoc,
+                                   bool IsStdInvoke) {
   if (checkPlaceholderForOverload(*this, Obj))
     return ExprError();
   ExprResult Object = Obj;
@@ -14768,6 +14771,8 @@
 
   // Perform overload resolution.
   OverloadCandidateSet::iterator Best;
+  QualType T = Object.get()->getType();
+  SourceRange SR = Object.get()->getSourceRange();
   switch (CandidateSet.BestViableFunction(*this, Object.get()->getBeginLoc(),
                                           Best)) {
   case OR_Success:
@@ -14777,32 +14782,29 @@
 
   case OR_No_Viable_Function: {
     PartialDiagnostic PD =
-        CandidateSet.empty()
-            ? (PDiag(diag::err_ovl_no_oper)
-               << Object.get()->getType() << /*call*/ 1
-               << Object.get()->getSourceRange())
-            : (PDiag(diag::err_ovl_no_viable_object_call)
-               << Object.get()->getType() << Object.get()->getSourceRange());
+        CandidateSet.empty() ? (PDiag(diag::err_ovl_no_oper) << T << /*call*/ 1 << SR)
+      : !IsStdInvoke ? (PDiag(diag::err_ovl_no_viable_object_call) << T << SR)
+                     : (PDiag(diag::err_invoke_function_object) << T << /*no*/0 << SR);
     CandidateSet.NoteCandidates(
         PartialDiagnosticAt(Object.get()->getBeginLoc(), PD), *this,
         OCD_AllCandidates, Args);
     break;
   }
-  case OR_Ambiguous:
+  case OR_Ambiguous: {
+    PartialDiagnostic PD =
+      !IsStdInvoke ? (PDiag(diag::err_ovl_ambiguous_object_call) << T << SR)
+                   : (PDiag(diag::err_invoke_function_object) << T << /*plural*/1 << CandidateSet.size() << SR);
     CandidateSet.NoteCandidates(
-        PartialDiagnosticAt(Object.get()->getBeginLoc(),
-                            PDiag(diag::err_ovl_ambiguous_object_call)
-                                << Object.get()->getType()
-                                << Object.get()->getSourceRange()),
+        PartialDiagnosticAt(Object.get()->getBeginLoc(), PD),
         *this, OCD_AmbiguousCandidates, Args);
     break;
-
+  }
   case OR_Deleted:
+    PartialDiagnostic PD =
+      !IsStdInvoke ? PDiag(diag::err_ovl_deleted_object_call) << T << SR
+                   : PDiag(diag::err_invoke_function_object_deleted) << 2 << T << SR;
     CandidateSet.NoteCandidates(
-        PartialDiagnosticAt(Object.get()->getBeginLoc(),
-                            PDiag(diag::err_ovl_deleted_object_call)
-                                << Object.get()->getType()
-                                << Object.get()->getSourceRange()),
+        PartialDiagnosticAt(Object.get()->getBeginLoc(), PD),
         *this, OCD_AllCandidates, Args);
     break;
   }
@@ -14863,7 +14865,7 @@
   ExprResult NewFn = CreateFunctionRefExpr(*this, Method, Best->FoundDecl,
                                            Obj, HadMultipleCandidates,
                                            OpLocInfo.getLoc(),
-                                           OpLocInfo.getInfo());
+                                           OpLocInfo.getInfo(), IsStdInvoke);
   if (NewFn.isInvalid())
     return true;
 
Index: clang/lib/Sema/SemaExprCXX.cpp
===================================================================
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -17,15 +17,20 @@
 #include "clang/AST/ASTLambda.h"
 #include "clang/AST/CXXInheritance.h"
 #include "clang/AST/CharUnits.h"
+#include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Type.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/AlignedAllocation.h"
 #include "clang/Basic/DiagnosticSema.h"
 #include "clang/Basic/PartialDiagnostic.h"
+#include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TokenKinds.h"
 #include "clang/Basic/TypeTraits.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/DeclSpec.h"
@@ -5680,6 +5685,178 @@
   llvm_unreachable("Unknown type trait or not implemented");
 }
 
+static ExprResult HandleInvokePointerToMemberFunction(
+    Sema &S, const MemberPointerType *CalleeType, bool,
+    SourceLocation LParenLoc, Expr *F, MultiExprArg Args,
+    SourceLocation RParenLoc) {
+  assert(CalleeType->isMemberFunctionPointer());
+  ExprResult B = S.BuildBinOp(S.getCurScope(), LParenLoc,
+                              BinaryOperatorKind::BO_PtrMemD, Args[0], F);
+  if (B.isInvalid()) {
+    return ExprError();
+  }
+
+  return S.BuildCallToMemberFunction(S.getCurScope(), B.get(), LParenLoc,
+                                     Args.drop_front(), RParenLoc, /*ExecConfig*/nullptr, /*IsExecConfig*/false, /*AllowRecovery*/false, /*IsStdInvoke*/true);
+}
+
+static ExprResult
+HandleInvokePointerToDataMember(Sema &S, const MemberPointerType *CalleeType,
+                                bool IsInvokeR, SourceLocation LParenLoc,
+                                Expr *F, MultiExprArg Args,
+                                SourceLocation RParenLoc) {
+  return S.BuildBinOp(S.getCurScope(), LParenLoc,
+                      BinaryOperatorKind::BO_PtrMemD, Args[0], F);
+}
+
+static ExprResult
+HandleInvokePointerToMember(Sema &S, const MemberPointerType *CalleeType,
+                            bool IsInvokeR, SourceLocation LParenLoc, Expr *F,
+                            MultiExprArg Args, SourceLocation RParenLoc) {
+  auto *Fn = CalleeType->isMemberFunctionPointer()
+                 ? HandleInvokePointerToMemberFunction
+                 : HandleInvokePointerToDataMember;
+  return Fn(S, CalleeType, IsInvokeR, LParenLoc, F, Args, RParenLoc);
+}
+
+static ExprResult UnwrapReferenceWrapper(Sema &S, QualType &FirstArgType,
+                                         Expr *Arg) {
+  FirstArgType = S.BuiltinAddReference(
+      dyn_cast<TemplateSpecializationType>(
+          dyn_cast<ElaboratedType>(FirstArgType)->getNamedType())
+          ->getArg(0)
+          .getAsType(),
+      Sema::UTTKind::AddLvalueReference, {});
+  return S.BuildCXXNamedCast({}, tok::kw_static_cast,
+                             S.Context.getTrivialTypeSourceInfo(FirstArgType),
+                             Arg, {}, {});
+}
+
+static ExprResult HandleInvokePointerToMember(Sema &S, bool IsInvokeR,
+                                              QualType CalleeType,
+                                              SourceLocation LParenLoc, Expr *F,
+                                              MultiExprArg Args,
+                                              SourceLocation RParenLoc) {
+  auto *PtrToMember = CalleeType->getAs<MemberPointerType>();
+  if (Args.size() == 0 || (Args.size() > 1 && CalleeType->isMemberDataPointerType())) {
+    S.Diag(LParenLoc, diag::err_invoke_pointer_to_member_too_few_args)
+        << PtrToMember->isMemberFunctionPointer() << IsInvokeR << 1;
+    return ExprError();
+  }
+
+  QualType FirstArgType = Args[0]->getType();
+  QualType ClassType = QualType(PtrToMember->getClass(), 0);
+  bool IsBase = EvaluateBinaryTypeTrait(S, TypeTrait::BTT_IsBaseOf, ClassType,
+                                        FirstArgType.getNonReferenceType(), {});
+  if (IsBase)
+    return HandleInvokePointerToMember(S, PtrToMember, IsInvokeR, LParenLoc, F,
+                                       Args, RParenLoc);
+
+  if (RecordDecl *D = FirstArgType->getAsCXXRecordDecl()) {
+    bool IsReferenceWrapper =
+        D->isInStdNamespace() && D->getName() == "reference_wrapper";
+    if (IsReferenceWrapper) {
+      Args[0] = UnwrapReferenceWrapper(S, FirstArgType, Args[0]).get();
+      FirstArgType = Args[0]->getType();
+    }
+
+    IsBase = EvaluateBinaryTypeTrait(S, TypeTrait::BTT_IsBaseOf, ClassType,
+                                     FirstArgType, {});
+    if (IsBase)
+      return HandleInvokePointerToMember(S, PtrToMember, IsInvokeR, LParenLoc,
+                                         F, Args, RParenLoc);
+
+    if (IsReferenceWrapper) {
+      S.Diag(F->getBeginLoc(), diag::err_invoke_pointer_to_member_incompatible_second_arg)
+        << CalleeType->isMemberFunctionPointerType() << 1 << PtrToMember->getClass()->getAsRecordDecl()
+        << FirstArgType;
+      return ExprError();
+    }
+  }
+
+  ExprResult Deref = S.BuildUnaryOp(S.getCurScope(), LParenLoc,
+                                    UnaryOperatorKind::UO_Deref, Args[0], true);
+  if (Deref.isInvalid()) {
+    S.Diag(F->getBeginLoc(), diag::err_invoke_pointer_to_member_incompatible_second_arg)
+      << CalleeType->isMemberFunctionPointerType()
+      << 0
+      << PtrToMember->getClass()->getAsCXXRecordDecl()
+      << FirstArgType;
+    return ExprError();
+  }
+
+  Args[0] = Deref.get();
+  IsBase = EvaluateBinaryTypeTrait(S, TypeTrait::BTT_IsBaseOf, ClassType,
+                                   Args[0]->getType(), {});
+  if (!IsBase) {
+    S.Diag(LParenLoc,
+           diag::err_invoke_pointer_to_member_incompatible_second_arg)
+        << PtrToMember->isMemberFunctionPointer() << 2 << PtrToMember->getClass()->getAsCXXRecordDecl()
+        << FirstArgType;
+    return ExprError();
+  }
+
+  return HandleInvokePointerToMember(S, PtrToMember, IsInvokeR, LParenLoc, F,
+                                     Args, RParenLoc);
+}
+
+static ExprResult HandleInvoke(Sema &S, CallExpr *TheCall, bool IsInvokeR) {
+  Expr *F = TheCall->getArgs()[0];
+  QualType CalleeType = F->getType();
+  MultiExprArg Args(TheCall->getArgs() + 1, TheCall->getNumArgs() - 1);
+
+  // FIXME: remove this comment block once notes are addressed.
+  // Reviewer note 1: It really feels like there should be some way to use
+  // Context.getSubstTemplateTypeParmType, but it's not clear what the second
+  // argument should be.
+  // Reviewer note 2: Do we need to consider SubstTemplateTypeParmPackType too?
+  if (auto *T = dyn_cast<SubstTemplateTypeParmType>(CalleeType))
+    CalleeType = T->getReplacementType();
+
+  if (!CalleeType->isMemberPointerType()) {
+    return S.BuildCallExpr(S.getCurScope(), F, TheCall->getBeginLoc(), Args,
+                           TheCall->getEndLoc(), /*ExecConfig*/ nullptr,
+                           /*IsExecConfig*/ false, /*AllowRecovery*/ false,
+                           /*IsStdInvoke*/ true);
+  }
+
+  return HandleInvokePointerToMember(S, IsInvokeR, CalleeType,
+                                     TheCall->getBeginLoc(), F, Args,
+                                     TheCall->getEndLoc());
+}
+
+ExprResult Sema::BuildStdInvokeCall(CallExpr *TheCall, FunctionDecl *FDecl,
+                                    unsigned int BuiltinID) {
+  assert(TheCall->getNumArgs() > 0);
+
+  ExprResult Result =
+      HandleInvoke(*this, TheCall, BuiltinID == Builtin::BIinvoke_r);
+  if (BuiltinID == Builtin::BIinvoke || Result.isInvalid())
+    return Result;
+
+  QualType ResultType = Result.get()->getType();
+  QualType InvokeRType = FDecl->getReturnType();
+  if (!EvaluateBinaryTypeTrait(*this, TypeTrait::BTT_IsConvertibleTo,
+                               ResultType, InvokeRType, {})) {
+    QualType T = TheCall->getArgs()[0]->getType();
+    unsigned Kind =
+          T->isRecordType()                ? 4 // function object
+        : T->isMemberFunctionPointerType() ? 3 // pointer-to-member function
+        : T->isMemberDataPointerType()     ? 2 // pointer-to-data member
+        : T->isBlockPointerType()          ? 1  // block
+                                           : 0; // function
+    SemaDiagnosticBuilder B = Diag(TheCall->getBeginLoc(), diag::err_invoke_bad_conversion)
+        << Kind << ResultType << InvokeRType;
+    if (T->isRecordType())
+      B << T;
+    return ExprError();
+  }
+
+  return BuildCXXNamedCast(TheCall->getBeginLoc(), tok::kw_static_cast,
+                           Context.getTrivialTypeSourceInfo(InvokeRType),
+                           Result.get(), {}, TheCall->getBeginLoc());
+}
+
 ExprResult Sema::ActOnArrayTypeTrait(ArrayTypeTrait ATT,
                                      SourceLocation KWLoc,
                                      ParsedType Ty,
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -222,7 +222,8 @@
                              const ObjCInterfaceDecl *UnknownObjCClass,
                              bool ObjCPropertyAccess,
                              bool AvoidPartialAvailabilityChecks,
-                             ObjCInterfaceDecl *ClassReceiver) {
+                             ObjCInterfaceDecl *ClassReceiver,
+                             bool IsStdInvoke) {
   SourceLocation Loc = Locs.front();
   if (getLangOpts().CPlusPlus && isa<FunctionDecl>(D)) {
     // If there were any diagnostics suppressed by template argument deduction,
@@ -263,13 +264,15 @@
     // See if this is a deleted function.
     if (FD->isDeleted()) {
       auto *Ctor = dyn_cast<CXXConstructorDecl>(FD);
-      if (Ctor && Ctor->isInheritingConstructor())
-        Diag(Loc, diag::err_deleted_inherited_ctor_use)
-            << Ctor->getParent()
-            << Ctor->getInheritedConstructor().getConstructor()->getParent();
-      else
-        Diag(Loc, diag::err_deleted_function_use);
-      NoteDeletedFunction(FD);
+      if (!IsStdInvoke) {
+        if (Ctor && Ctor->isInheritingConstructor())
+          Diag(Loc, diag::err_deleted_inherited_ctor_use)
+              << Ctor->getParent()
+              << Ctor->getInheritedConstructor().getConstructor()->getParent();
+        else
+          Diag(Loc, diag::err_deleted_function_use);
+        NoteDeletedFunction(FD);
+      }
       return true;
     }
 
@@ -6001,11 +6004,12 @@
 /// true if the call is ill-formed.
 bool
 Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
-                              FunctionDecl *FDecl,
-                              const FunctionProtoType *Proto,
-                              ArrayRef<Expr *> Args,
-                              SourceLocation RParenLoc,
-                              bool IsExecConfig) {
+                                   FunctionDecl *FDecl,
+                                   const FunctionProtoType *Proto,
+                                   ArrayRef<Expr *> Args,
+                                   SourceLocation RParenLoc,
+                                   bool IsExecConfig,
+                                   bool IsStdInvoke) {
   // Bail out early if calling a builtin with custom typechecking.
   if (FDecl)
     if (unsigned ID = FDecl->getBuiltinID())
@@ -6027,7 +6031,17 @@
   if (Args.size() < NumParams) {
     if (Args.size() < MinArgs) {
       TypoCorrection TC;
-      if (FDecl && (TC = TryTypoCorrectionForCall(*this, Fn, FDecl, Args))) {
+      if (IsStdInvoke)  {
+        QualType FnType = Fn->getType();
+        unsigned Kind =
+          FnType->isRecordType()                ? 3  // function object
+        : isa<BinaryOperator>(Fn)               ? 2  // pointer-to-member-function
+        : FnType->isBlockPointerType()          ? 1  // block
+                                                : 0; // function
+        Diag(Call->getBeginLoc(), diag::err_invoke_wrong_number_of_args)
+            << Kind << NumParams << (NumParams != 1) << Args.size();
+      }
+      else if (FDecl && (TC = TryTypoCorrectionForCall(*this, Fn, FDecl, Args))) {
         unsigned diag_id =
             MinArgs == NumParams && !Proto->isVariadic()
                 ? diag::err_typecheck_call_too_few_args_suggest
@@ -6065,7 +6079,17 @@
   if (Args.size() > NumParams) {
     if (!Proto->isVariadic()) {
       TypoCorrection TC;
-      if (FDecl && (TC = TryTypoCorrectionForCall(*this, Fn, FDecl, Args))) {
+      if (IsStdInvoke) {
+        QualType FnType = Fn->getType();
+        unsigned Kind =
+          FnType->isRecordType()                ? 3  // function object
+        : isa<BinaryOperator>(Fn)               ? 2  // pointer-to-member-function
+        : FnType->isBlockPointerType()          ? 1  // block
+                                                : 0; // function
+        Diag(Call->getBeginLoc(), diag::err_invoke_wrong_number_of_args)
+            << Kind << NumParams << (NumParams != 1) << Args.size();
+      }
+      else if (FDecl && (TC = TryTypoCorrectionForCall(*this, Fn, FDecl, Args))) {
         unsigned diag_id =
             MinArgs == NumParams && !Proto->isVariadic()
                 ? diag::err_typecheck_call_too_many_args_suggest
@@ -6634,7 +6658,7 @@
 ExprResult Sema::BuildCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
                                MultiExprArg ArgExprs, SourceLocation RParenLoc,
                                Expr *ExecConfig, bool IsExecConfig,
-                               bool AllowRecovery) {
+                               bool AllowRecovery, bool IsStdInvoke) {
   // Since this might be a postfix expression, get rid of ParenListExprs.
   ExprResult Result = MaybeConvertParenListExprToParenExpr(Scope, Fn);
   if (Result.isInvalid()) return ExprError();
@@ -6685,7 +6709,7 @@
     // Determine whether this is a call to an object (C++ [over.call.object]).
     if (Fn->getType()->isRecordType())
       return BuildCallToObjectOfClassType(Scope, Fn, LParenLoc, ArgExprs,
-                                          RParenLoc);
+                                          RParenLoc, IsStdInvoke);
 
     if (Fn->getType() == Context.UnknownAnyTy) {
       ExprResult result = rebuildUnknownAnyFunction(*this, Fn);
@@ -6833,7 +6857,8 @@
                             CurFPFeatureOverrides());
   }
   return BuildResolvedCallExpr(Fn, NDecl, LParenLoc, ArgExprs, RParenLoc,
-                               ExecConfig, IsExecConfig);
+                               ExecConfig, IsExecConfig, ADLCallKind::NotADL,
+                               IsStdInvoke);
 }
 
 /// BuildBuiltinCallExpr - Create a call to a builtin function specified by Id
@@ -6908,7 +6933,8 @@
                                        SourceLocation LParenLoc,
                                        ArrayRef<Expr *> Args,
                                        SourceLocation RParenLoc, Expr *Config,
-                                       bool IsExecConfig, ADLCallKind UsesADL) {
+                                       bool IsExecConfig, ADLCallKind UsesADL,
+                                       bool IsStdInvoke) {
   FunctionDecl *FDecl = dyn_cast_or_null<FunctionDecl>(NDecl);
   unsigned BuiltinID = (FDecl ? FDecl->getBuiltinID() : 0);
 
@@ -7084,7 +7110,7 @@
 
   if (Proto) {
     if (ConvertArgumentsForCall(TheCall, Fn, FDecl, Proto, Args, RParenLoc,
-                                IsExecConfig))
+                                IsExecConfig, IsStdInvoke))
       return ExprError();
   } else {
     assert(isa<FunctionNoProtoType>(FuncT) && "Unknown FunctionType!");
@@ -14479,7 +14505,7 @@
 
 /// CheckIndirectionOperand - Type check unary indirection (prefix '*').
 static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK,
-                                        SourceLocation OpLoc) {
+                                        SourceLocation OpLoc, bool IsStdInvoke = false) {
   if (Op->isTypeDependent())
     return S.Context.DependentTy;
 
@@ -14511,8 +14537,9 @@
   }
 
   if (Result.isNull()) {
-    S.Diag(OpLoc, diag::err_typecheck_indirection_requires_pointer)
-      << OpTy << Op->getSourceRange();
+    if (!IsStdInvoke)
+      S.Diag(OpLoc, diag::err_typecheck_indirection_requires_pointer)
+        << OpTy << Op->getSourceRange();
     return QualType();
   }
 
@@ -15517,7 +15544,7 @@
 
 ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
                                       UnaryOperatorKind Opc,
-                                      Expr *InputExpr) {
+                                      Expr *InputExpr, bool IsStdInvoke) {
   ExprResult Input = InputExpr;
   ExprValueKind VK = VK_PRValue;
   ExprObjectKind OK = OK_Ordinary;
@@ -15567,7 +15594,7 @@
   case UO_Deref: {
     Input = DefaultFunctionArrayLvalueConversion(Input.get());
     if (Input.isInvalid()) return ExprError();
-    resultType = CheckIndirectionOperand(*this, Input.get(), VK, OpLoc);
+    resultType = CheckIndirectionOperand(*this, Input.get(), VK, OpLoc, IsStdInvoke);
     break;
   }
   case UO_Plus:
@@ -15785,7 +15812,7 @@
 }
 
 ExprResult Sema::BuildUnaryOp(Scope *S, SourceLocation OpLoc,
-                              UnaryOperatorKind Opc, Expr *Input) {
+                              UnaryOperatorKind Opc, Expr *Input, bool IsStdInvoke) {
   // First things first: handle placeholders so that the
   // overloaded-operator check considers the right type.
   if (const BuiltinType *pty = Input->getType()->getAsPlaceholderType()) {
@@ -15821,7 +15848,7 @@
     if (S && OverOp != OO_None)
       LookupOverloadedOperatorName(OverOp, S, Functions);
 
-    return CreateOverloadedUnaryOp(OpLoc, Opc, Functions, Input);
+    return CreateOverloadedUnaryOp(OpLoc, Opc, Functions, Input, /*RequiresADL*/true, IsStdInvoke);
   }
 
   return CreateBuiltinUnaryOp(OpLoc, Opc, Input);
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -9424,6 +9424,9 @@
     const auto *FPT = FD->getType()->castAs<FunctionProtoType>();
     return FPT->getNumParams() == 1 && !FPT->isVariadic();
   }
+  case Builtin::BIinvoke:
+  case Builtin::BIinvoke_r:
+    return true;
 
   default:
     return false;
Index: clang/lib/Sema/SemaChecking.cpp
===================================================================
--- clang/lib/Sema/SemaChecking.cpp
+++ clang/lib/Sema/SemaChecking.cpp
@@ -28,6 +28,7 @@
 #include "clang/AST/ExprOpenMP.h"
 #include "clang/AST/FormatString.h"
 #include "clang/AST/NSAPI.h"
+#include "clang/AST/NestedNameSpecifier.h"
 #include "clang/AST/NonTrivialTypeVisitor.h"
 #include "clang/AST/OperationKinds.h"
 #include "clang/AST/RecordLayout.h"
@@ -2423,6 +2424,11 @@
     }
     break;
   }
+  case Builtin::BIinvoke:
+  case Builtin::BIinvoke_r: {
+    TheCallResult = BuildStdInvokeCall(TheCall, FDecl, BuiltinID);
+    break;
+  }
   // OpenCL v2.0, s6.13.16 - Pipe functions
   case Builtin::BIread_pipe:
   case Builtin::BIwrite_pipe:
Index: clang/lib/CodeGen/CGBuiltin.cpp
===================================================================
--- clang/lib/CodeGen/CGBuiltin.cpp
+++ clang/lib/CodeGen/CGBuiltin.cpp
@@ -4633,6 +4633,8 @@
   case Builtin::BImove_if_noexcept:
   case Builtin::BIforward:
   case Builtin::BIas_const:
+  case Builtin::BIinvoke:
+  case Builtin::BIinvoke_r:
     return RValue::get(EmitLValue(E->getArg(0)).getPointer(*this));
   case Builtin::BI__GetExceptionInfo: {
     if (llvm::GlobalVariable *GV =
Index: clang/lib/AST/ExprConstant.cpp
===================================================================
--- clang/lib/AST/ExprConstant.cpp
+++ clang/lib/AST/ExprConstant.cpp
@@ -8320,6 +8320,8 @@
   switch (E->getBuiltinCallee()) {
   case Builtin::BIas_const:
   case Builtin::BIforward:
+  case Builtin::BIinvoke:
+  case Builtin::BIinvoke_r:
   case Builtin::BImove:
   case Builtin::BImove_if_noexcept:
     if (cast<FunctionDecl>(E->getCalleeDecl())->isConstexpr())
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -4093,7 +4093,7 @@
   ExprResult CreateOverloadedUnaryOp(SourceLocation OpLoc,
                                      UnaryOperatorKind Opc,
                                      const UnresolvedSetImpl &Fns,
-                                     Expr *input, bool RequiresADL = true);
+                                     Expr *input, bool RequiresADL = true, bool IsStdInvoke = false);
 
   void LookupOverloadedBinOp(OverloadCandidateSet &CandidateSet,
                              OverloadedOperatorKind Op,
@@ -4121,11 +4121,13 @@
                                        SourceLocation RParenLoc,
                                        Expr *ExecConfig = nullptr,
                                        bool IsExecConfig = false,
-                                       bool AllowRecovery = false);
+                                       bool AllowRecovery = false,
+                                       bool IsStdInvoke = false);
   ExprResult
   BuildCallToObjectOfClassType(Scope *S, Expr *Object, SourceLocation LParenLoc,
                                MultiExprArg Args,
-                               SourceLocation RParenLoc);
+                               SourceLocation RParenLoc,
+                               bool IsStdInvoke = false);
 
   ExprResult BuildOverloadedArrowExpr(Scope *S, Expr *Base,
                                       SourceLocation OpLoc,
@@ -5249,7 +5251,8 @@
                          const ObjCInterfaceDecl *UnknownObjCClass = nullptr,
                          bool ObjCPropertyAccess = false,
                          bool AvoidPartialAvailabilityChecks = false,
-                         ObjCInterfaceDecl *ClassReciever = nullptr);
+                         ObjCInterfaceDecl *ClassReciever = nullptr,
+                         bool IsStdInvoke = false);
   void NoteDeletedFunction(FunctionDecl *FD);
   void NoteDeletedInheritingConstructor(CXXConstructorDecl *CD);
   bool DiagnosePropertyAccessorMismatch(ObjCPropertyDecl *PD,
@@ -5559,9 +5562,9 @@
 
   // Binary/Unary Operators.  'Tok' is the token for the operator.
   ExprResult CreateBuiltinUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc,
-                                  Expr *InputExpr);
+                                  Expr *InputExpr, bool IsStdInvoke = false);
   ExprResult BuildUnaryOp(Scope *S, SourceLocation OpLoc,
-                          UnaryOperatorKind Opc, Expr *Input);
+                          UnaryOperatorKind Opc, Expr *Input, bool IsStdInvoke = false);
   ExprResult ActOnUnaryOp(Scope *S, SourceLocation OpLoc,
                           tok::TokenKind Op, Expr *Input);
 
@@ -5706,16 +5709,18 @@
                   const TemplateArgumentListInfo *TemplateArgs = nullptr);
 
   void ActOnDefaultCtorInitializers(Decl *CDtorDecl);
-  bool ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
-                               FunctionDecl *FDecl,
+  bool ConvertArgumentsForCall(CallExpr *Call, Expr *Fn, FunctionDecl *FDecl,
                                const FunctionProtoType *Proto,
-                               ArrayRef<Expr *> Args,
-                               SourceLocation RParenLoc,
-                               bool ExecConfig = false);
+                               ArrayRef<Expr *> Args, SourceLocation RParenLoc,
+                               bool ExecConfig = false,
+                               bool IsStdInvoke = false);
   void CheckStaticArrayArgument(SourceLocation CallLoc,
                                 ParmVarDecl *Param,
                                 const Expr *ArgExpr);
 
+  ExprResult BuildStdInvokeCall(CallExpr *TheCall, FunctionDecl *FDecl,
+                                unsigned int BuiltinID);
+
   /// ActOnCallExpr - Handle a call to Fn with the specified array of arguments.
   /// This provides the location of the left/right parens and a list of comma
   /// locations.
@@ -5726,7 +5731,8 @@
                            MultiExprArg ArgExprs, SourceLocation RParenLoc,
                            Expr *ExecConfig = nullptr,
                            bool IsExecConfig = false,
-                           bool AllowRecovery = false);
+                           bool AllowRecovery = false,
+                           bool IsStdInvoke = false);
   Expr *BuildBuiltinCallExpr(SourceLocation Loc, Builtin::ID Id,
                              MultiExprArg CallArgs);
   enum class AtomicArgumentOrder { API, AST };
@@ -5739,7 +5745,8 @@
   BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, SourceLocation LParenLoc,
                         ArrayRef<Expr *> Arg, SourceLocation RParenLoc,
                         Expr *Config = nullptr, bool IsExecConfig = false,
-                        ADLCallKind UsesADL = ADLCallKind::NotADL);
+                        ADLCallKind UsesADL = ADLCallKind::NotADL,
+                        bool IsStdInvoke = false);
 
   ExprResult ActOnCUDAExecConfigExpr(Scope *S, SourceLocation LLLLoc,
                                      MultiExprArg ExecConfig,
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8415,6 +8415,28 @@
   "%select{function|block|method|kernel function}0 call, "
   "expected at most %1, have %2; did you mean %3?">;
 
+def err_invoke_pointer_to_member_too_few_args : Error<
+  "can't invoke pointer-to-%select{data member|member function}0: "
+  "'std::invoke%select{|_r}1' must have %select{exactly|at least}0 2 arguments "
+  "for a pointer-to-%select{data member|member function}0, got %2">;
+def err_invoke_pointer_to_member_incompatible_second_arg : Error<
+  "can't invoke pointer-to-%select{data member|member function}0: expected "
+  "second argument to be a %select{reference|wrapee|pointer}1 to a class "
+  "compatible with %2, got %3">;
+def err_invoke_wrong_number_of_args : Error<
+  "can't invoke %select{function|block|pointer-to-member function}0: expected "
+  "%1 %select{argument|arguments}2, got %3">;
+def err_invoke_function_object : Error<
+  "can't invoke %0 function object: %select{no|%2}1 suitable "
+  "overload%select{|s}1 found%select{|, which makes choosing ambiguous}1">;
+def err_invoke_function_object_deleted : Error<
+  "can't invoke %select{function|pointer-to-member function|"
+  "%1 function object}0: chosen overload candidate is deleted">;
+def err_invoke_bad_conversion : Error<
+  "can't invoke %select{function|block|pointer-to-data member|"
+  "pointer-to-member function|%3 function object}0: return type "
+  "%1 isn't convertible to %2">;
+
 def err_arc_typecheck_convert_incompatible_pointer : Error<
   "incompatible pointer types passing retainable parameter of type %0"
   "to a CF function expecting %1 type">;
Index: clang/include/clang/Basic/Builtins.def
===================================================================
--- clang/include/clang/Basic/Builtins.def
+++ clang/include/clang/Basic/Builtins.def
@@ -1559,6 +1559,8 @@
 LANGBUILTIN(__addressof, "v*v&", "zfncT", CXX_LANG)
 LIBBUILTIN(as_const, "v&v&", "zfncTh", "utility", CXX_LANG)
 LIBBUILTIN(forward, "v&v&", "zfncTh", "utility", CXX_LANG)
+LIBBUILTIN(invoke, "v.", "zfTh", "functional", CXX_LANG)
+LIBBUILTIN(invoke_r, "v.", "zfTh", "functional", CXX_LANG)
 LIBBUILTIN(move, "v&v&", "zfncTh", "utility", CXX_LANG)
 LIBBUILTIN(move_if_noexcept, "v&v&", "zfncTh", "utility", CXX_LANG)
 
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -116,6 +116,10 @@
 C++ Language Changes in Clang
 -----------------------------
 
+- Improved ``-O0`` code generation for calls to ``std::invoke``, and
+  ``std::invoke_r``. These are now treated as compiler builtins and implemented
+  directly, rather than instantiating a definition from the standard library.
+
 C++20 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to