cjdb updated this revision to Diff 452357.
cjdb added a comment.
applies a fix
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,496 @@
+// 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 *'}}
+
+ const Base cb;
+ std::invoke(&Base::plus, cb, 1, 2);
+ // expected-error@-1{{can't invoke pointer-to-member function: '&Base::plus' drops 'const' qualifier}}
+ std::invoke_r<double>(&Base::plus, cb, 1, 2);
+ // expected-error@-1{{can't invoke pointer-to-member function: '&Base::plus' drops 'const' qualifier}}
+
+ volatile Base vb;
+ std::invoke(&Base::plus, vb, 1, 2);
+ // expected-error@-1{{can't invoke pointer-to-member function: '&Base::plus' drops 'volatile' qualifier}}
+ std::invoke_r<double>(&Base::plus, vb, 1, 2);
+ // expected-error@-1{{can't invoke pointer-to-member function: '&Base::plus' drops 'volatile' qualifier}}
+
+ const volatile Base cvb;
+ std::invoke(&Base::plus, cvb, 1, 2);
+ // expected-error@-1{{can't invoke pointer-to-member function: '&Base::plus' drops 'const volatile' qualifiers}}
+ std::invoke_r<double>(&Base::plus, cvb, 1, 2);
+ // expected-error@-1{{can't invoke pointer-to-member function: '&Base::plus' drops 'const volatile' qualifiers}}
+
+ std::invoke(&Base::plus, Base{}, 1, 2);
+ // expected-error@-1{{can't invoke pointer-to-member function: '&Base::plus' can only be called on an lvalue}}
+ std::invoke_r<double>(&Base::plus, Base{}, 1, 2);
+ // expected-error@-1{{can't invoke pointer-to-member function: '&Base::plus' can only be called on an lvalue}}
+
+ std::invoke(&Base::sum, cb, 1, 2, 3, 4, 5, 6);
+ // expected-error@-1{{can't invoke pointer-to-member function: '&Base::sum' can only be called on an rvalue}}
+ std::invoke_r<double>(&Base::sum, cb, 1, 2, 3, 4, 5, 6);
+ // expected-error@-1{{can't invoke pointer-to-member function: '&Base::sum' can only be called on an rvalue}}
+ }
+ { // 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}}
+ 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}}
+ 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}}
+ 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}}
+ 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}}
+ std::invoke_r<double>(callable{1}, 1, 2);
+ // expected-error@-1{{can't invoke 'callable' function object: no suitable overload found}}
+
+ 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}}
+ 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}}
+ 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
@@ -49,11 +49,13 @@
}
/// A convenience routine for creating a decayed reference to a function.
-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))
+static ExprResult
+CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn, NamedDecl *FoundDecl,
+ const Expr *Base, bool HadMultipleCandidates,
+ SourceLocation Loc = SourceLocation(),
+ 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 +6414,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 +9673,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());
@@ -13398,10 +13400,11 @@
/// by CreateOverloadedUnaryOp().
///
/// \param Input The input argument.
-ExprResult
-Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc,
- const UnresolvedSetImpl &Fns,
- Expr *Input, bool PerformADL) {
+ExprResult Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc,
+ UnaryOperatorKind Opc,
+ const UnresolvedSetImpl &Fns,
+ 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 +13574,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.
@@ -13661,12 +13664,11 @@
/// the function in question. Such a function is never a candidate in
/// our overload resolution. This also enables synthesizing a three-way
/// comparison from < and == as described in C++20 [class.spaceship]p1.
-ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
- BinaryOperatorKind Opc,
- const UnresolvedSetImpl &Fns, Expr *LHS,
- Expr *RHS, bool PerformADL,
- bool AllowRewrittenCandidates,
- FunctionDecl *DefaultedFn) {
+ExprResult
+Sema::CreateOverloadedBinOp(SourceLocation OpLoc, BinaryOperatorKind Opc,
+ const UnresolvedSetImpl &Fns, Expr *LHS, Expr *RHS,
+ bool PerformADL, bool AllowRewrittenCandidates,
+ FunctionDecl *DefaultedFn, bool IsStdInvoke) {
Expr *Args[2] = { LHS, RHS };
LHS=RHS=nullptr; // Please use only Args instead of LHS/RHS couple
@@ -13727,7 +13729,7 @@
// If this is the .* operator, which is not overloadable, just
// create a built-in binary operator.
if (Opc == BO_PtrMemD)
- return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
+ return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1], IsStdInvoke);
// Build the overload set.
OverloadCandidateSet CandidateSet(
@@ -14379,12 +14381,10 @@
/// parameter). The caller needs to validate that the member
/// expression refers to a non-static member function or an overloaded
/// member function.
-ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
- SourceLocation LParenLoc,
- MultiExprArg Args,
- SourceLocation RParenLoc,
- Expr *ExecConfig, bool IsExecConfig,
- bool AllowRecovery) {
+ExprResult Sema::BuildCallToMemberFunction(
+ Scope *S, Expr *MemExprE, SourceLocation LParenLoc, MultiExprArg Args,
+ SourceLocation RParenLoc, Expr *ExecConfig, bool IsExecConfig,
+ bool AllowRecovery, bool IsStdInvoke) {
assert(MemExprE->getType() == Context.BoundMemberTy ||
MemExprE->getType() == Context.OverloadTy);
@@ -14418,10 +14418,16 @@
difference.removeAddressSpace();
if (difference) {
std::string qualsString = difference.getAsString();
- Diag(LParenLoc, diag::err_pointer_to_member_call_drops_quals)
- << fnType.getUnqualifiedType()
- << qualsString
- << (qualsString.find(' ') == std::string::npos ? 1 : 2);
+ if (!IsStdInvoke)
+ Diag(LParenLoc, diag::err_pointer_to_member_call_drops_quals)
+ << fnType.getUnqualifiedType() << qualsString
+ << (qualsString.find(' ') == std::string::npos ? 1 : 2);
+ else {
+ Diag(LParenLoc, diag::err_invoke_pointer_to_member_drops_qualifiers)
+ << op->getRHS() << qualsString
+ << (qualsString.find(' ') == std::string::npos ? 1 : 2);
+ return ExprError();
+ }
}
CXXMemberCallExpr *call = CXXMemberCallExpr::Create(
@@ -14432,7 +14438,8 @@
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))
@@ -14675,11 +14682,11 @@
/// type (C++ [over.call.object]), which can end up invoking an
/// overloaded function call operator (@c operator()) or performing a
/// user-defined conversion on the object argument.
-ExprResult
-Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
- SourceLocation LParenLoc,
- MultiExprArg Args,
- SourceLocation RParenLoc) {
+ExprResult Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
+ SourceLocation LParenLoc,
+ MultiExprArg Args,
+ SourceLocation RParenLoc,
+ bool IsStdInvoke) {
if (checkPlaceholderForOverload(*this, Obj))
return ExprError();
ExprResult Object = Obj;
@@ -14768,6 +14775,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:
@@ -14778,32 +14787,33 @@
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());
+ ? (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 << 1 << 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() << 2 << SR);
CandidateSet.NoteCandidates(
- PartialDiagnosticAt(Object.get()->getBeginLoc(),
- PDiag(diag::err_ovl_ambiguous_object_call)
- << Object.get()->getType()
- << Object.get()->getSourceRange()),
- *this, OCD_AmbiguousCandidates, Args);
+ 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()),
- *this, OCD_AllCandidates, Args);
+ PartialDiagnosticAt(Object.get()->getBeginLoc(), PD), *this,
+ OCD_AllCandidates, Args);
break;
}
@@ -14860,10 +14870,9 @@
DeclarationNameInfo OpLocInfo(
Context.DeclarationNames.getCXXOperatorName(OO_Call), LParenLoc);
OpLocInfo.setCXXOperatorNameRange(SourceRange(LParenLoc, RParenLoc));
- ExprResult NewFn = CreateFunctionRefExpr(*this, Method, Best->FoundDecl,
- Obj, HadMultipleCandidates,
- OpLocInfo.getLoc(),
- OpLocInfo.getInfo());
+ ExprResult NewFn = CreateFunctionRefExpr(
+ *this, Method, Best->FoundDecl, Obj, HadMultipleCandidates,
+ OpLocInfo.getLoc(), 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,7 +17,9 @@
#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"
@@ -26,9 +28,10 @@
#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/TypeTraits.h"
#include "clang/Basic/TokenKinds.h"
+#include "clang/Basic/TypeTraits.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/Initialization.h"
@@ -5750,6 +5753,182 @@
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, true);
+ 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) {
+ auto *D = dyn_cast<ClassTemplateSpecializationDecl>(
+ FirstArgType.getDesugaredType(S.Context)->getAsRecordDecl());
+ FirstArgType = S.BuiltinAddReference(D->getTemplateArgs().get(0).getAsType(),
+ Sema::UTTKind::AddLvalueReference,
+ Arg->getExprLoc());
+ return S.BuildCXXNamedCast(Arg->getBeginLoc(), tok::kw_static_cast,
+ S.Context.getTrivialTypeSourceInfo(FirstArgType),
+ Arg, {}, Arg->getEndLoc());
+}
+
+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(),
+ TheCall->getBeginLoc());
+}
+
ExprResult Sema::ActOnArrayTypeTrait(ArrayTypeTrait ATT,
SourceLocation KWLoc,
ParsedType Ty,
@@ -5881,8 +6060,8 @@
QualType Sema::CheckPointerToMemberOperands(ExprResult &LHS, ExprResult &RHS,
ExprValueKind &VK,
- SourceLocation Loc,
- bool isIndirect) {
+ SourceLocation Loc, bool isIndirect,
+ bool IsStdInvoke) {
assert(!LHS.get()->hasPlaceholderType() && !RHS.get()->hasPlaceholderType() &&
"placeholders should have been weeded out by now");
@@ -6000,16 +6179,28 @@
Diag(Loc, getLangOpts().CPlusPlus20
? diag::warn_cxx17_compat_pointer_to_const_ref_member_on_rvalue
: diag::ext_pointer_to_const_ref_member_on_rvalue);
- else
+ else if (!IsStdInvoke)
Diag(Loc, diag::err_pointer_to_member_oper_value_classify)
<< RHSType << 1 << LHS.get()->getSourceRange();
+ else {
+ Diag(Loc, diag::err_invoke_pointer_to_member_ref_qualifiers)
+ << RHS.get() << 0;
+ return QualType();
+ }
}
break;
case RQ_RValue:
- if (isIndirect || !LHS.get()->Classify(Context).isRValue())
- Diag(Loc, diag::err_pointer_to_member_oper_value_classify)
- << RHSType << 0 << LHS.get()->getSourceRange();
+ if (isIndirect || !LHS.get()->Classify(Context).isRValue()) {
+ if (!IsStdInvoke)
+ Diag(Loc, diag::err_pointer_to_member_oper_value_classify)
+ << RHSType << 0 << LHS.get()->getSourceRange();
+ else {
+ Diag(Loc, diag::err_invoke_pointer_to_member_ref_qualifiers)
+ << RHS.get() << 1;
+ return QualType();
+ }
+ }
break;
}
}
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;
}
@@ -5999,13 +6002,12 @@
/// Fn is the function expression. For a C++ member function, this
/// routine does not attempt to convert the object argument. Returns
/// 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) {
+bool Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
+ 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 +6029,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 +6077,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 +6656,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 +6707,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 +6855,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 +6931,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 +7108,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 +14503,8 @@
/// 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 +14536,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();
}
@@ -14818,8 +14844,8 @@
/// operator @p Opc at location @c TokLoc. This routine only supports
/// built-in operations; ActOnBinOp handles overloaded operators.
ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
- BinaryOperatorKind Opc,
- Expr *LHSExpr, Expr *RHSExpr) {
+ BinaryOperatorKind Opc, Expr *LHSExpr,
+ Expr *RHSExpr, bool IsStdInvoke) {
if (getLangOpts().CPlusPlus11 && isa<InitListExpr>(RHSExpr)) {
// The syntax only allows initializer lists on the RHS of assignment,
// so we don't need to worry about accepting invalid code for
@@ -14919,7 +14945,7 @@
case BO_PtrMemD:
case BO_PtrMemI:
ResultTy = CheckPointerToMemberOperands(LHS, RHS, VK, OpLoc,
- Opc == BO_PtrMemI);
+ Opc == BO_PtrMemI, IsStdInvoke);
break;
case BO_Mul:
case BO_Div:
@@ -15332,8 +15358,8 @@
/// Build an overloaded binary operator expression in the given scope.
static ExprResult BuildOverloadedBinOp(Sema &S, Scope *Sc, SourceLocation OpLoc,
- BinaryOperatorKind Opc,
- Expr *LHS, Expr *RHS) {
+ BinaryOperatorKind Opc, Expr *LHS,
+ Expr *RHS, bool IsStdInvoke = false) {
switch (Opc) {
case BO_Assign:
case BO_DivAssign:
@@ -15355,12 +15381,13 @@
// Build the (potentially-overloaded, potentially-dependent)
// binary operation.
- return S.CreateOverloadedBinOp(OpLoc, Opc, Functions, LHS, RHS);
+ return S.CreateOverloadedBinOp(OpLoc, Opc, Functions, LHS, RHS, true, true,
+ nullptr, IsStdInvoke);
}
ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc,
- BinaryOperatorKind Opc,
- Expr *LHSExpr, Expr *RHSExpr) {
+ BinaryOperatorKind Opc, Expr *LHSExpr,
+ Expr *RHSExpr, bool IsStdInvoke) {
ExprResult LHS, RHS;
std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr);
if (!LHS.isUsable() || !RHS.isUsable())
@@ -15457,7 +15484,8 @@
// overloadable type.
if (LHSExpr->getType()->isOverloadableType() ||
RHSExpr->getType()->isOverloadableType())
- return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr);
+ return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr,
+ IsStdInvoke);
}
if (getLangOpts().RecoveryAST &&
@@ -15516,8 +15544,8 @@
}
ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
- UnaryOperatorKind Opc,
- Expr *InputExpr) {
+ UnaryOperatorKind Opc, Expr *InputExpr,
+ bool IsStdInvoke) {
ExprResult Input = InputExpr;
ExprValueKind VK = VK_PRValue;
ExprObjectKind OK = OK_Ordinary;
@@ -15567,7 +15595,8 @@
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 +15814,8 @@
}
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 +15851,8 @@
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
@@ -4092,20 +4092,20 @@
ExprResult CreateOverloadedUnaryOp(SourceLocation OpLoc,
UnaryOperatorKind Opc,
- const UnresolvedSetImpl &Fns,
- Expr *input, bool RequiresADL = true);
+ const UnresolvedSetImpl &Fns, Expr *input,
+ bool RequiresADL = true,
+ bool IsStdInvoke = false);
void LookupOverloadedBinOp(OverloadCandidateSet &CandidateSet,
OverloadedOperatorKind Op,
const UnresolvedSetImpl &Fns,
ArrayRef<Expr *> Args, bool RequiresADL = true);
- ExprResult CreateOverloadedBinOp(SourceLocation OpLoc,
- BinaryOperatorKind Opc,
- const UnresolvedSetImpl &Fns,
- Expr *LHS, Expr *RHS,
- bool RequiresADL = true,
+ ExprResult CreateOverloadedBinOp(SourceLocation OpLoc, BinaryOperatorKind Opc,
+ const UnresolvedSetImpl &Fns, Expr *LHS,
+ Expr *RHS, bool RequiresADL = true,
bool AllowRewrittenCandidates = true,
- FunctionDecl *DefaultedFn = nullptr);
+ FunctionDecl *DefaultedFn = nullptr,
+ bool IsStdInvoke = false);
ExprResult BuildSynthesizedThreeWayComparison(SourceLocation OpLoc,
const UnresolvedSetImpl &Fns,
Expr *LHS, Expr *RHS,
@@ -4115,17 +4115,16 @@
SourceLocation RLoc, Expr *Base,
MultiExprArg Args);
- ExprResult BuildCallToMemberFunction(Scope *S, Expr *MemExpr,
- SourceLocation LParenLoc,
- MultiExprArg Args,
- SourceLocation RParenLoc,
- Expr *ExecConfig = nullptr,
- bool IsExecConfig = false,
- bool AllowRecovery = false);
- ExprResult
- BuildCallToObjectOfClassType(Scope *S, Expr *Object, SourceLocation LParenLoc,
- MultiExprArg Args,
- SourceLocation RParenLoc);
+ ExprResult BuildCallToMemberFunction(
+ Scope *S, Expr *MemExpr, SourceLocation LParenLoc, MultiExprArg Args,
+ SourceLocation RParenLoc, Expr *ExecConfig = nullptr,
+ bool IsExecConfig = false, bool AllowRecovery = false,
+ bool IsStdInvoke = false);
+ ExprResult BuildCallToObjectOfClassType(Scope *S, Expr *Object,
+ SourceLocation LParenLoc,
+ MultiExprArg Args,
+ SourceLocation RParenLoc,
+ bool IsStdInvoke = false);
ExprResult BuildOverloadedArrowExpr(Scope *S, Expr *Base,
SourceLocation OpLoc,
@@ -5249,7 +5248,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 +5559,9 @@
// Binary/Unary Operators. 'Tok' is the token for the operator.
ExprResult CreateBuiltinUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc,
- Expr *InputExpr);
- ExprResult BuildUnaryOp(Scope *S, SourceLocation OpLoc,
- UnaryOperatorKind Opc, Expr *Input);
+ Expr *InputExpr, bool IsStdInvoke = false);
+ ExprResult BuildUnaryOp(Scope *S, SourceLocation OpLoc, UnaryOperatorKind Opc,
+ Expr *Input, bool IsStdInvoke = false);
ExprResult ActOnUnaryOp(Scope *S, SourceLocation OpLoc,
tok::TokenKind Op, Expr *Input);
@@ -5706,16 +5706,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 +5728,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 +5742,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,
@@ -5790,10 +5794,11 @@
public:
ExprResult ActOnBinOp(Scope *S, SourceLocation TokLoc,
tok::TokenKind Kind, Expr *LHSExpr, Expr *RHSExpr);
- ExprResult BuildBinOp(Scope *S, SourceLocation OpLoc,
- BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr);
+ ExprResult BuildBinOp(Scope *S, SourceLocation OpLoc, BinaryOperatorKind Opc,
+ Expr *LHSExpr, Expr *RHSExpr, bool IsStdInvoke = false);
ExprResult CreateBuiltinBinOp(SourceLocation OpLoc, BinaryOperatorKind Opc,
- Expr *LHSExpr, Expr *RHSExpr);
+ Expr *LHSExpr, Expr *RHSExpr,
+ bool IsStdInvoke = false);
void LookupBinOp(Scope *S, SourceLocation OpLoc, BinaryOperatorKind Opc,
UnresolvedSetImpl &Functions);
@@ -12162,8 +12167,8 @@
QualType InvalidLogicalVectorOperands(SourceLocation Loc, ExprResult &LHS,
ExprResult &RHS);
QualType CheckPointerToMemberOperands( // C++ 5.5
- ExprResult &LHS, ExprResult &RHS, ExprValueKind &VK,
- SourceLocation OpLoc, bool isIndirect);
+ ExprResult &LHS, ExprResult &RHS, ExprValueKind &VK, SourceLocation OpLoc,
+ bool isIndirect, bool IsStdInvoke = false);
QualType CheckMultiplyDivideOperands( // C99 6.5.5
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, bool IsCompAssign,
bool IsDivide);
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8415,6 +8415,33 @@
"%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_pointer_to_member_drops_qualifiers : Error<
+ "can't invoke pointer-to-member function: '%0' drops '%1' qualifier%s2">;
+def err_invoke_pointer_to_member_ref_qualifiers : Error<
+ "can't invoke pointer-to-member function: '%0' can only be called on an "
+ "%select{lvalue|rvalue}1">;
+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%s2 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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits