https://github.com/philnik777 updated 
https://github.com/llvm/llvm-project/pull/116709

>From e0bb550672326e21a556ac727f2e2a6ef65f0469 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklau...@berlin.de>
Date: Tue, 1 Oct 2024 11:08:02 +0200
Subject: [PATCH 1/5] [Clang] Add __builtin_invoke and recognize std::invoke as
 a builtin

---
 clang/include/clang/Basic/Builtins.td         |   6 +
 clang/include/clang/Sema/Sema.h               |   9 +
 clang/lib/Parse/ParseDeclCXX.cpp              |  24 +--
 clang/lib/Sema/SemaChecking.cpp               |  97 +++++++++++
 clang/lib/Sema/SemaExprCXX.cpp                | 105 ++++++------
 clang/test/CodeGenCXX/builtin-invoke.cpp      |  61 +++++++
 clang/test/SemaCXX/builtin-invoke.cpp         | 133 +++++++++++++++
 libcxx/include/__type_traits/invoke.h         | 155 ++++++++++++++----
 .../__type_traits/is_core_convertible.h       |  11 ++
 9 files changed, 499 insertions(+), 102 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/builtin-invoke.cpp
 create mode 100644 clang/test/SemaCXX/builtin-invoke.cpp

diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index 187d3b5ed24a7..58cc35088c40a 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4272,6 +4272,12 @@ def MoveIfNsoexcept : CxxLibBuiltin<"utility"> {
   let Namespace = "std";
 }
 
+def Invoke : Builtin {
+  let Spellings = ["__builtin_invoke"];
+  let Attributes = [CustomTypeChecking, Constexpr];
+  let Prototype = "void(...)";
+}
+
 def Annotation : Builtin {
   let Spellings = ["__builtin_annotation"];
   let Attributes = [NoThrow, CustomTypeChecking];
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5ec67087aeea4..22d66e8688906 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2594,6 +2594,8 @@ class Sema final : public SemaBase {
                                SourceLocation BuiltinLoc,
                                SourceLocation RParenLoc);
 
+  ExprResult BuiltinInvoke(CallExpr *TheCall);
+
   static StringRef GetFormatStringTypeName(FormatStringType FST);
   static FormatStringType GetFormatStringType(StringRef FormatFlavor);
   static FormatStringType GetFormatStringType(const FormatAttr *Format);
@@ -15220,11 +15222,18 @@ class Sema final : public SemaBase {
                                SourceLocation Loc);
   QualType BuiltinRemoveReference(QualType BaseType, UTTKind UKind,
                                   SourceLocation Loc);
+
+  QualType BuiltinRemoveCVRef(QualType BaseType, SourceLocation Loc) {
+    return BuiltinRemoveReference(BaseType, UTTKind::RemoveCVRef, Loc);
+  }
+
   QualType BuiltinChangeCVRQualifiers(QualType BaseType, UTTKind UKind,
                                       SourceLocation Loc);
   QualType BuiltinChangeSignedness(QualType BaseType, UTTKind UKind,
                                    SourceLocation Loc);
 
+  bool BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT, QualType RhsT);
+
   /// Ensure that the type T is a literal type.
   ///
   /// This routine checks whether the type @p T is a literal type. If @p T is 
an
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 316bc30edf1f0..aeb1112bad8b4 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1611,29 +1611,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind 
TagTokKind,
       Tok.isOneOf(
 #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) tok::kw___##Trait,
 #include "clang/Basic/TransformTypeTraits.def"
-          tok::kw___is_abstract,
-          tok::kw___is_aggregate,
-          tok::kw___is_arithmetic,
-          tok::kw___is_array,
-          tok::kw___is_assignable,
-          tok::kw___is_base_of,
-          tok::kw___is_bounded_array,
-          tok::kw___is_class,
-          tok::kw___is_complete_type,
-          tok::kw___is_compound,
-          tok::kw___is_const,
-          tok::kw___is_constructible,
-          tok::kw___is_convertible,
-          tok::kw___is_convertible_to,
-          tok::kw___is_destructible,
-          tok::kw___is_empty,
-          tok::kw___is_enum,
-          tok::kw___is_floating_point,
-          tok::kw___is_final,
-          tok::kw___is_function,
-          tok::kw___is_fundamental,
-          tok::kw___is_integral,
-          tok::kw___is_interface_class,
+          tok::kw___is_convertible, // Last use in libc++ was removed in 
925a11a
           tok::kw___is_literal,
           tok::kw___is_lvalue_expr,
           tok::kw___is_lvalue_reference,
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index a960b9931ddfd..26579de25bdf0 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2368,6 +2368,8 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, 
unsigned BuiltinID,
     return BuiltinShuffleVector(TheCall);
     // TheCall will be freed by the smart pointer here, but that's fine, since
     // BuiltinShuffleVector guts it, but then doesn't release it.
+  case Builtin::BI__builtin_invoke:
+    return BuiltinInvoke(TheCall);
   case Builtin::BI__builtin_prefetch:
     if (BuiltinPrefetch(TheCall))
       return ExprError();
@@ -5406,6 +5408,101 @@ ExprResult Sema::ConvertVectorExpr(Expr *E, 
TypeSourceInfo *TInfo,
                                    RParenLoc, CurFPFeatureOverrides());
 }
 
+ExprResult Sema::BuiltinInvoke(CallExpr *TheCall) {
+  auto Loc = TheCall->getBeginLoc();
+  auto Args = MutableArrayRef(TheCall->getArgs(), TheCall->getNumArgs());
+  assert(llvm::none_of(Args,
+                       [](Expr *Arg) { return Arg->isTypeDependent(); }));
+
+  if (Args.size() == 0) {
+    Diag(TheCall->getBeginLoc(), 
diag::err_typecheck_call_too_few_args_at_least)
+        << 0 << 1 << 0 << 0 << TheCall->getSourceRange();
+    return ExprError();
+  }
+
+  auto FuncT = Args[0]->getType();
+
+  if (auto *MPT = FuncT->getAs<MemberPointerType>()) {
+    if (Args.size() < 2) {
+      Diag(TheCall->getBeginLoc(),
+            diag::err_typecheck_call_too_few_args_at_least)
+          << 0 << 2 << 1 << 0 << TheCall->getSourceRange();
+      return ExprError();
+    }
+
+    auto *MemPtrClass = MPT->getQualifier()->getAsType();
+    auto ObjectT = Args[1]->getType();
+
+
+    if (MPT->isMemberDataPointer() && Args.size() != 2) {
+      Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_many_args)
+          << 0 << 2 << Args.size() << 0 << TheCall->getSourceRange();
+      return ExprError();
+    }
+
+    ExprResult ObjectArg = [&]() -> ExprResult {
+      // (1.1): (t1.*f)(t2, …, tN) when f is a pointer to a member function of 
a
+      // class T and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
+      // is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
+      // (1.4): t1.*f when N=1 and f is a pointer to data member of a class T
+      // and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
+      // is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
+      if (Context.hasSameType(QualType(MemPtrClass, 0),
+                              BuiltinRemoveCVRef(ObjectT, Loc)) ||
+          BuiltinIsBaseOf(Args[1]->getBeginLoc(), QualType(MemPtrClass, 0),
+                          BuiltinRemoveCVRef(ObjectT, Loc))) {
+        return Args[1];
+      }
+
+      // (t1.get().*f)(t2, …, tN) when f is a pointer to a member function of
+      // a class T and remove_cvref_t<decltype(t1)> is a specialization of
+      // reference_wrapper;
+      if (auto *RD = ObjectT->getAsCXXRecordDecl()) {
+        if (RD->isInStdNamespace() &&
+            RD->getDeclName().getAsString() == "reference_wrapper") {
+          CXXScopeSpec SS;
+          IdentifierInfo *GetName = &Context.Idents.get("get");
+          UnqualifiedId GetID;
+          GetID.setIdentifier(GetName, Loc);
+
+          auto MemExpr = ActOnMemberAccessExpr(
+              getCurScope(), Args[1], Loc, tok::period, SS,
+              /*TemplateKWLoc=*/SourceLocation(), GetID, nullptr);
+
+          if (MemExpr.isInvalid())
+            return ExprError();
+
+          return ActOnCallExpr(getCurScope(), MemExpr.get(), Loc, {}, Loc);
+        }
+      }
+
+      // ((*t1).*f)(t2, …, tN) when f is a pointer to a member function of a
+      // class T and t1 does not satisfy the previous two items;
+
+      return ActOnUnaryOp(getCurScope(), Loc, tok::star, Args[1]);
+    }();
+
+    if (ObjectArg.isInvalid())
+      return ExprError();
+
+    auto BinOp = ActOnBinOp(getCurScope(), TheCall->getBeginLoc(),
+                            tok::periodstar, ObjectArg.get(), Args[0]);
+    if (BinOp.isInvalid())
+      return ExprError();
+
+    if (MPT->isMemberDataPointer())
+      return BinOp;
+
+    auto *MemCall = new (Context)
+        ParenExpr(SourceLocation(), SourceLocation(), BinOp.get());
+
+    return ActOnCallExpr(getCurScope(), MemCall, TheCall->getBeginLoc(),
+                         Args.drop_front(2), TheCall->getRParenLoc());
+  }
+  return ActOnCallExpr(getCurScope(), Args.front(), TheCall->getBeginLoc(),
+                       Args.drop_front(), TheCall->getRParenLoc());
+}
+
 bool Sema::BuiltinPrefetch(CallExpr *TheCall) {
   unsigned NumArgs = TheCall->getNumArgs();
 
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index b071c98051bbe..e945b446953c7 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -6540,67 +6540,70 @@ ExprResult Sema::ActOnTypeTrait(TypeTrait Kind, 
SourceLocation KWLoc,
   return BuildTypeTrait(Kind, KWLoc, ConvertedArgs, RParenLoc);
 }
 
-static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const 
TypeSourceInfo *Lhs,
-                                    const TypeSourceInfo *Rhs, SourceLocation 
KeyLoc) {
-  QualType LhsT = Lhs->getType();
-  QualType RhsT = Rhs->getType();
+bool Sema::BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT,
+                           QualType RhsT) {
+  // C++0x [meta.rel]p2
+  // Base is a base class of Derived without regard to cv-qualifiers or
+  // Base and Derived are not unions and name the same class type without
+  // regard to cv-qualifiers.
+
+  const RecordType *lhsRecord = LhsT->getAs<RecordType>();
+  const RecordType *rhsRecord = RhsT->getAs<RecordType>();
+  if (!rhsRecord || !lhsRecord) {
+    const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>();
+    const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>();
+    if (!LHSObjTy || !RHSObjTy)
+      return false;
 
-  assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
-         "Cannot evaluate traits of dependent types");
+    ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
+    ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
+    if (!BaseInterface || !DerivedInterface)
+      return false;
 
-  switch(BTT) {
-  case BTT_IsBaseOf: {
-    // C++0x [meta.rel]p2
-    // Base is a base class of Derived without regard to cv-qualifiers or
-    // Base and Derived are not unions and name the same class type without
-    // regard to cv-qualifiers.
-
-    const RecordType *lhsRecord = LhsT->getAs<RecordType>();
-    const RecordType *rhsRecord = RhsT->getAs<RecordType>();
-    if (!rhsRecord || !lhsRecord) {
-      const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>();
-      const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>();
-      if (!LHSObjTy || !RHSObjTy)
-        return false;
+    if (RequireCompleteType(RhsTLoc, RhsT,
+                            diag::err_incomplete_type_used_in_type_trait_expr))
+      return false;
 
-      ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
-      ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
-      if (!BaseInterface || !DerivedInterface)
-        return false;
+    return BaseInterface->isSuperClassOf(DerivedInterface);
+  }
 
-      if (Self.RequireCompleteType(
-              Rhs->getTypeLoc().getBeginLoc(), RhsT,
-              diag::err_incomplete_type_used_in_type_trait_expr))
-        return false;
+  assert(Context.hasSameUnqualifiedType(LhsT, RhsT) ==
+         (lhsRecord == rhsRecord));
 
-      return BaseInterface->isSuperClassOf(DerivedInterface);
-    }
+  // Unions are never base classes, and never have base classes.
+  // It doesn't matter if they are complete or not. See PR#41843
+  if (lhsRecord && lhsRecord->getDecl()->isUnion())
+    return false;
+  if (rhsRecord && rhsRecord->getDecl()->isUnion())
+    return false;
+
+  if (lhsRecord == rhsRecord)
+    return true;
 
-    assert(Self.Context.hasSameUnqualifiedType(LhsT, RhsT)
-             == (lhsRecord == rhsRecord));
+  // C++0x [meta.rel]p2:
+  //   If Base and Derived are class types and are different types
+  //   (ignoring possible cv-qualifiers) then Derived shall be a
+  //   complete type.
+  if (RequireCompleteType(RhsTLoc, RhsT,
+                          diag::err_incomplete_type_used_in_type_trait_expr))
+    return false;
 
-    // Unions are never base classes, and never have base classes.
-    // It doesn't matter if they are complete or not. See PR#41843
-    if (lhsRecord && lhsRecord->getDecl()->isUnion())
-      return false;
-    if (rhsRecord && rhsRecord->getDecl()->isUnion())
-      return false;
+  return cast<CXXRecordDecl>(rhsRecord->getDecl())
+    ->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
+}
 
-    if (lhsRecord == rhsRecord)
-      return true;
+static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const 
TypeSourceInfo *Lhs,
+                                    const TypeSourceInfo *Rhs, SourceLocation 
KeyLoc) {
+  QualType LhsT = Lhs->getType();
+  QualType RhsT = Rhs->getType();
 
-    // C++0x [meta.rel]p2:
-    //   If Base and Derived are class types and are different types
-    //   (ignoring possible cv-qualifiers) then Derived shall be a
-    //   complete type.
-    if (Self.RequireCompleteType(
-            Rhs->getTypeLoc().getBeginLoc(), RhsT,
-            diag::err_incomplete_type_used_in_type_trait_expr))
-      return false;
+  assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
+         "Cannot evaluate traits of dependent types");
+
+  switch(BTT) {
+  case BTT_IsBaseOf:
+    return Self.BuiltinIsBaseOf(Rhs->getTypeLoc().getBeginLoc(), LhsT, RhsT);
 
-    return cast<CXXRecordDecl>(rhsRecord->getDecl())
-      ->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
-  }
   case BTT_IsVirtualBaseOf: {
     const RecordType *BaseRecord = LhsT->getAs<RecordType>();
     const RecordType *DerivedRecord = RhsT->getAs<RecordType>();
diff --git a/clang/test/CodeGenCXX/builtin-invoke.cpp 
b/clang/test/CodeGenCXX/builtin-invoke.cpp
new file mode 100644
index 0000000000000..af66dfd4dae30
--- /dev/null
+++ b/clang/test/CodeGenCXX/builtin-invoke.cpp
@@ -0,0 +1,61 @@
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+extern "C" void* memcpy(void*, const void*, decltype(sizeof(int)));
+void func();
+
+namespace std {
+  template <class T>
+  class reference_wrapper {
+    T* ptr;
+
+  public:
+    T& get() { return *ptr; }
+  };
+} // namespace std
+
+struct Callable {
+  void operator()() {}
+
+  void func();
+};
+
+extern "C" void call1() {
+  __builtin_invoke(func);
+  __builtin_invoke(Callable{});
+  __builtin_invoke(memcpy, nullptr, nullptr, 0);
+
+  // CHECK:      define dso_local void @call1
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT:   %ref.tmp = alloca %struct.Callable, align 1
+  // CHECK-NEXT:   call void @_Z4funcv()
+  // CHECK-NEXT:   call void @_ZN8CallableclEv(ptr noundef nonnull align 1 
dereferenceable(1) %ref.tmp)
+  // CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 1 null, ptr 
align 1 null, i64 0, i1 false)
+  // CHECK-NEXT:   ret void
+}
+
+extern "C" void call_memptr(std::reference_wrapper<Callable> wrapper) {
+  __builtin_invoke(&Callable::func, wrapper);
+
+  // CHECK:      define dso_local void @call_memptr
+  // CHECK-NEXT: entry:
+  // CHECK-NEXT:   %wrapper = alloca %"class.std::reference_wrapper", align 8
+  // CHECK-NEXT:   %coerce.dive = getelementptr inbounds nuw 
%"class.std::reference_wrapper", ptr %wrapper, i32 0, i32 0
+  // CHECK-NEXT:   store ptr %wrapper.coerce, ptr %coerce.dive, align 8
+  // CHECK-NEXT:   %call = call noundef nonnull align 1 dereferenceable(1) ptr 
@_ZNSt17reference_wrapperI8CallableE3getEv(ptr noundef nonnull align 8 
dereferenceable(8) %wrapper)
+  // CHECK-NEXT:   %0 = getelementptr inbounds i8, ptr %call, i64 0
+  // CHECK-NEXT:   br i1 false, label %memptr.virtual, label %memptr.nonvirtual
+  // CHECK-EMPTY:
+  // CHECK-NEXT: memptr.virtual:
+  // CHECK-NEXT:   %vtable = load ptr, ptr %0, align 8
+  // CHECK-NEXT:   %1 = getelementptr i8, ptr %vtable, i64 sub (i64 ptrtoint 
(ptr @_ZN8Callable4funcEv to i64), i64 1), !nosanitize !2
+  // CHECK-NEXT:   %memptr.virtualfn = load ptr, ptr %1, align 8, !nosanitize 
!2
+  // CHECK-NEXT:   br label %memptr.end
+  // CHECK-EMPTY:
+  // CHECK-NEXT: memptr.nonvirtual:
+  // CHECK-NEXT:   br label %memptr.end
+  // CHECK-EMPTY:
+  // CHECK-NEXT: memptr.end:
+  // CHECK-NEXT:   %2 = phi ptr [ %memptr.virtualfn, %memptr.virtual ], [ 
@_ZN8Callable4funcEv, %memptr.nonvirtual ]
+  // CHECK-NEXT:   call void %2(ptr noundef nonnull align 1 dereferenceable(1) 
%0)
+  // CHECK-NEXT:   ret void
+}
diff --git a/clang/test/SemaCXX/builtin-invoke.cpp 
b/clang/test/SemaCXX/builtin-invoke.cpp
new file mode 100644
index 0000000000000..5b156b5ff75c4
--- /dev/null
+++ b/clang/test/SemaCXX/builtin-invoke.cpp
@@ -0,0 +1,133 @@
+// RUN: %clang_cc1 -verify -fsyntax-only %s
+
+void func() { // expected-note {{'func' declared here}}
+  __builtin_invoke(); // expected-error {{too few arguments to function call, 
expected at least 1, have 0}}
+}
+
+void nfunc() noexcept {}
+
+struct S {};
+void argfunc(int, S) {} // expected-note {{'argfunc' declared here}}
+
+struct Callable {
+  void operator()() {}
+
+  void func() {}
+
+  int var;
+};
+
+void* malloc(decltype(sizeof(int)));
+
+template <class T>
+struct pointer_wrapper {
+  T* v;
+
+  T& operator*() {
+    return *v;
+  }
+};
+
+namespace std {
+  template <class T>
+  class reference_wrapper {
+    T* ptr;
+
+  public:
+    reference_wrapper(T& ref) : ptr(&ref) {}
+
+    T& get() { return *ptr; }
+  };
+
+  template <class T>
+  reference_wrapper<T> ref(T& v) {
+    return reference_wrapper<T>(v);
+  }
+} // namespace std
+
+struct InvalidSpecialization1 {
+  void func() {}
+
+  int var;
+};
+
+template <>
+class std::reference_wrapper<InvalidSpecialization1> {
+public:
+  reference_wrapper(InvalidSpecialization1&) {}
+};
+
+struct InvalidSpecialization2 {
+  void func() {}
+
+  int var;
+};
+
+template <>
+class std::reference_wrapper<InvalidSpecialization2> {
+public:
+  reference_wrapper(InvalidSpecialization2&) {}
+
+private:
+  InvalidSpecialization2& get(); // expected-note 2 {{declared private here}}
+};
+
+void call() {
+  __builtin_invoke(func);
+  __builtin_invoke(nfunc);
+  static_assert(!noexcept(__builtin_invoke(func)));
+  static_assert(noexcept(__builtin_invoke(nfunc)));
+  __builtin_invoke(func, 1); // expected-error {{too many arguments to 
function call, expected 0, have 1}}
+  __builtin_invoke(argfunc, 1); // expected-error {{too few arguments to 
function call, expected 2, have 1}}
+  __builtin_invoke(Callable{});
+  __builtin_invoke(malloc, 0);
+  __builtin_invoke(__builtin_malloc, 0); // expected-error {{builtin functions 
must be directly called}}
+
+  // Member functiom pointer
+  __builtin_invoke(&Callable::func); // expected-error {{too few arguments to 
function call, expected at least 2, have 1}}
+  __builtin_invoke(&Callable::func, 1); // expected-error {{indirection 
requires pointer operand ('int' invalid)}}
+  __builtin_invoke(&Callable::func, Callable{});
+  __builtin_invoke(&Callable::func, Callable{}, 1); // expected-error {{too 
many arguments to function call, expected 0, have 1}}
+
+  Callable c;
+  __builtin_invoke(&Callable::func, &c);
+  __builtin_invoke(&Callable::func, std::ref(c));
+  __builtin_invoke(&Callable::func, &c);
+  __builtin_invoke(&Callable::func, &c, 2); // expected-error {{too many 
arguments to function call, expected 0, have 1}}
+  __builtin_invoke(&Callable::func, pointer_wrapper<Callable>{&c});
+  __builtin_invoke(&Callable::func, pointer_wrapper<Callable>{&c}, 2); // 
expected-error {{too many arguments to function call, expected 0, have 1}}
+
+  InvalidSpecialization1 is1;
+  InvalidSpecialization2 is2;
+  __builtin_invoke(&InvalidSpecialization1::func, std::ref(is1)); // 
expected-error {{no member named 'get' in 
'std::reference_wrapper<InvalidSpecialization1>'}}
+  __builtin_invoke(&InvalidSpecialization2::func, std::ref(is2)); // 
expected-error {{'get' is a private member of 
'std::reference_wrapper<InvalidSpecialization2>'}}
+
+  // Member data pointer
+  __builtin_invoke(&Callable::var); // expected-error {{too few arguments to 
function call, expected at least 2, have 1}}
+  __builtin_invoke(&Callable::var, 1); // expected-error {{indirection 
requires pointer operand ('int' invalid)}}
+  (void)__builtin_invoke(&Callable::var, Callable{});
+  __builtin_invoke(&Callable::var, Callable{}, 1); // expected-error {{too 
many arguments to function call, expected 2, have 3}}
+
+  (void)__builtin_invoke(&Callable::var, &c);
+  (void)__builtin_invoke(&Callable::var, std::ref(c));
+  (void)__builtin_invoke(&Callable::var, &c);
+  __builtin_invoke(&Callable::var, &c, 2); // expected-error {{too many 
arguments to function call, expected 2, have 3}}
+  (void)__builtin_invoke(&Callable::var, pointer_wrapper<Callable>{&c});
+  __builtin_invoke(&Callable::var, pointer_wrapper<Callable>{&c}, 2); // 
expected-error {{too many arguments to function call, expected 2, have 3}}
+
+  __builtin_invoke(&InvalidSpecialization1::var, std::ref(is1)); // 
expected-error {{no member named 'get' in 
'std::reference_wrapper<InvalidSpecialization1>'}}
+  (void)__builtin_invoke(&InvalidSpecialization2::var, std::ref(is2)); // 
expected-error {{'get' is a private member of 
'std::reference_wrapper<InvalidSpecialization2>'}}
+}
+
+[[nodiscard]] int diagnose_discard();
+int no_diagnose_discard();
+
+namespace std {
+  template <class... Args>
+  auto invoke(Args&&... args) -> decltype(__builtin_invoke(args...));
+} // namespace std
+
+void test3() {
+  __builtin_invoke(diagnose_discard); // expected-warning {{ignoring return 
value of function declared with 'nodiscard' attribute}}
+  __builtin_invoke(no_diagnose_discard);
+}
diff --git a/libcxx/include/__type_traits/invoke.h 
b/libcxx/include/__type_traits/invoke.h
index ccf86209d2295..531b3d02bbb4d 100644
--- a/libcxx/include/__type_traits/invoke.h
+++ b/libcxx/include/__type_traits/invoke.h
@@ -22,6 +22,7 @@
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
 #include <__type_traits/nat.h>
+#include <__type_traits/void_t.h>
 #include <__utility/declval.h>
 #include <__utility/forward.h>
 
@@ -61,6 +62,102 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#if __has_builtin(__builtin_invoke)
+
+template <class... _Args>
+using __invoke_result_t = decltype(__builtin_invoke(std::declval<_Args>()...));
+
+template <class... _Args>
+__invoke_result_t<_Args...> __invoke(_Args&&... __args)
+    _NOEXCEPT_(noexcept(__builtin_invoke(std::forward<_Args>(__args)...))) {
+  return __builtin_invoke(std::forward<_Args>(__args)...);
+}
+
+template <class _Void, class... _Args>
+inline const bool __is_invocable_impl = false;
+
+template <class... _Args>
+inline const bool __is_invocable_impl<__void_t<__invoke_result_t<_Args...> >, 
_Args...> = true;
+
+template <class... _Args>
+inline const bool __is_invocable_v = __is_invocable_impl<void, _Args...>;
+
+template <class... _Args>
+struct __is_invocable : integral_constant<bool, __is_invocable_v<_Args...> > 
{};
+
+template <class _Ret, bool, class... _Args>
+inline const bool __is_invocable_r_impl = false;
+
+template <class _Ret, class... _Args>
+inline const bool __is_invocable_r_impl<_Ret, true, _Args...> =
+    __is_core_convertible<__invoke_result_t<_Args...>, _Ret>::value || 
is_void<_Ret>::value;
+
+template <class _Ret, class... _Args>
+inline const bool __is_invocable_r_v = __is_invocable_r_impl<_Ret, 
__is_invocable_v<_Args...>, _Args...>;
+
+template <bool __is_invocable, class... _Args>
+inline const bool __is_nothrow_invocable_impl = false;
+
+template <class... _Args>
+inline const bool __is_nothrow_invocable_impl<true, _Args...> = 
noexcept(__builtin_invoke(std::declval<_Args>()...));
+
+template <class... _Args>
+inline const bool __is_nothrow_invocable_v = 
__is_nothrow_invocable_impl<__is_invocable_v<_Args...>, _Args...>;
+
+template <bool __is_invocable, class _Ret, class... _Args>
+inline const bool __is_nothrow_invocable_r_impl = false;
+
+template <class _Ret, class... _Args>
+inline const bool __is_nothrow_invocable_r_impl<true, _Ret, _Args...> =
+    __is_nothrow_core_convertible_v<__invoke_result_t<_Args...>, _Ret> || 
is_void<_Ret>::value;
+
+template <class _Ret, class... _Args>
+inline const bool __is_nothrow_invocable_r_v =
+    __is_nothrow_invocable_r_impl<__is_nothrow_invocable_v<_Args...>, _Ret, 
_Args...>;
+
+#  if _LIBCPP_STD_VER >= 17
+
+// is_invocable
+
+template <class _Fn, class... _Args>
+struct _LIBCPP_TEMPLATE_VIS is_invocable : bool_constant<__is_invocable_v<_Fn, 
_Args...> > {};
+
+template <class _Ret, class _Fn, class... _Args>
+struct _LIBCPP_TEMPLATE_VIS is_invocable_r : 
bool_constant<__is_invocable_r_v<_Ret, _Fn, _Args...>> {};
+
+template <class _Fn, class... _Args>
+inline constexpr bool is_invocable_v = __is_invocable_v<_Fn, _Args...>;
+
+template <class _Ret, class _Fn, class... _Args>
+inline constexpr bool is_invocable_r_v = is_invocable_r<_Ret, _Fn, 
_Args...>::value;
+
+// is_nothrow_invocable
+
+template <class _Fn, class... _Args>
+struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable : 
bool_constant<__is_nothrow_invocable_v<_Fn, _Args...> > {};
+
+template <class _Ret, class _Fn, class... _Args>
+struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable_r
+    : integral_constant<bool, __is_nothrow_invocable_r_v<_Ret, _Fn, _Args...>> 
{};
+
+template <class _Fn, class... _Args>
+inline constexpr bool is_nothrow_invocable_v = __is_nothrow_invocable_v<_Fn, 
_Args...>;
+
+template <class _Ret, class _Fn, class... _Args>
+inline constexpr bool is_nothrow_invocable_r_v = 
__is_nothrow_invocable_r_v<_Ret, _Fn, _Args...>;
+
+template <class _Fn, class... _Args>
+struct _LIBCPP_TEMPLATE_VIS invoke_result {
+  using type = __invoke_result_t<_Fn, _Args...>;
+};
+
+template <class _Fn, class... _Args>
+using invoke_result_t = __invoke_result_t<_Fn, _Args...>;
+
+#  endif // _LIBCPP_STD_VER >= 17
+
+#else // __has_builtin(__builtin_invoke)
+
 template <class _DecayedFp>
 struct __member_pointer_class_type {};
 
@@ -211,21 +308,21 @@ struct __nothrow_invokable_r_imp<true, false, _Ret, _Fp, 
_Args...> {
   template <class _Tp>
   static void __test_noexcept(_Tp) _NOEXCEPT;
 
-#ifdef _LIBCPP_CXX03_LANG
+#  ifdef _LIBCPP_CXX03_LANG
   static const bool value = false;
-#else
+#  else
   static const bool value =
       
noexcept(_ThisT::__test_noexcept<_Ret>(std::__invoke(std::declval<_Fp>(), 
std::declval<_Args>()...)));
-#endif
+#  endif
 };
 
 template <class _Ret, class _Fp, class... _Args>
 struct __nothrow_invokable_r_imp<true, true, _Ret, _Fp, _Args...> {
-#ifdef _LIBCPP_CXX03_LANG
+#  ifdef _LIBCPP_CXX03_LANG
   static const bool value = false;
-#else
+#  else
   static const bool value = noexcept(std::__invoke(std::declval<_Fp>(), 
std::declval<_Args>()...));
-#endif
+#  endif
 };
 
 template <class _Ret, class _Fp, class... _Args>
@@ -236,22 +333,6 @@ template <class _Fp, class... _Args>
 using __nothrow_invokable _LIBCPP_NODEBUG =
     __nothrow_invokable_r_imp<__is_invocable<_Fp, _Args...>::value, true, 
void, _Fp, _Args...>;
 
-template <class _Ret, bool = is_void<_Ret>::value>
-struct __invoke_void_return_wrapper {
-  template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static _Ret 
__call(_Args&&... __args) {
-    return std::__invoke(std::forward<_Args>(__args)...);
-  }
-};
-
-template <class _Ret>
-struct __invoke_void_return_wrapper<_Ret, true> {
-  template <class... _Args>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static void 
__call(_Args&&... __args) {
-    std::__invoke(std::forward<_Args>(__args)...);
-  }
-};
-
 template <class _Func, class... _Args>
 inline const bool __is_invocable_v = __is_invocable<_Func, _Args...>::value;
 
@@ -268,11 +349,6 @@ struct __invoke_result
 template <class _Func, class... _Args>
 using __invoke_result_t _LIBCPP_NODEBUG = typename __invoke_result<_Func, 
_Args...>::type;
 
-template <class _Ret, class... _Args>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Ret __invoke_r(_Args&&... 
__args) {
-  return 
__invoke_void_return_wrapper<_Ret>::__call(std::forward<_Args>(__args)...);
-}
-
 #if _LIBCPP_STD_VER >= 17
 
 // is_invocable
@@ -311,7 +387,30 @@ struct _LIBCPP_NO_SPECIALIZATIONS invoke_result : 
__invoke_result<_Fn, _Args...>
 template <class _Fn, class... _Args>
 using invoke_result_t = typename invoke_result<_Fn, _Args...>::type;
 
-#endif // _LIBCPP_STD_VER >= 17
+#  endif // _LIBCPP_STD_VER >= 17
+
+#endif // __has_builtin(__builtin_invoke_r)
+
+template <class _Ret, bool = is_void<_Ret>::value>
+struct __invoke_void_return_wrapper {
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static _Ret 
__call(_Args&&... __args) {
+    return std::__invoke(std::forward<_Args>(__args)...);
+  }
+};
+
+template <class _Ret>
+struct __invoke_void_return_wrapper<_Ret, true> {
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static void 
__call(_Args&&... __args) {
+    std::__invoke(std::forward<_Args>(__args)...);
+  }
+};
+
+template <class _Ret, class... _Args>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Ret __invoke_r(_Args&&... 
__args) {
+  return 
__invoke_void_return_wrapper<_Ret>::__call(std::forward<_Args>(__args)...);
+}
 
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__type_traits/is_core_convertible.h 
b/libcxx/include/__type_traits/is_core_convertible.h
index ca3a346c17cd7..36f09b049e965 100644
--- a/libcxx/include/__type_traits/is_core_convertible.h
+++ b/libcxx/include/__type_traits/is_core_convertible.h
@@ -37,6 +37,17 @@ concept __core_convertible_to = __is_core_convertible<_Tp, 
_Up>::value;
 
 #endif // _LIBCPP_STD_VER >= 20
 
+template <class _Tp, class _Up, bool = true, bool = __is_core_convertible<_Tp, 
_Up>::value>
+inline const bool __is_nothrow_core_convertible_v = false;
+
+#ifndef _LIBCPP_CXX03_LANG
+template <class _Tp, class _Up>
+inline const bool __is_nothrow_core_convertible_v<_Tp,
+                                                  _Up,
+                                                  noexcept(static_cast<void 
(*)(_Up)>(0)(static_cast<_Tp (*)()>(0)())),
+                                                  true> = true;
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___TYPE_TRAITS_IS_CORE_CONVERTIBLE_H

>From 2849e456e8b6ace4c4cd8ff971e745fed80e1bb4 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklau...@berlin.de>
Date: Tue, 20 May 2025 16:10:44 +0200
Subject: [PATCH 2/5] Address comments

---
 clang/lib/Parse/ParseDeclCXX.cpp      | 24 +++++++++++++++++++++++-
 clang/lib/Sema/SemaChecking.cpp       | 10 ++++++----
 clang/test/SemaCXX/builtin-invoke.cpp |  9 +++++++++
 3 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index aeb1112bad8b4..316bc30edf1f0 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1611,7 +1611,29 @@ void Parser::ParseClassSpecifier(tok::TokenKind 
TagTokKind,
       Tok.isOneOf(
 #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) tok::kw___##Trait,
 #include "clang/Basic/TransformTypeTraits.def"
-          tok::kw___is_convertible, // Last use in libc++ was removed in 
925a11a
+          tok::kw___is_abstract,
+          tok::kw___is_aggregate,
+          tok::kw___is_arithmetic,
+          tok::kw___is_array,
+          tok::kw___is_assignable,
+          tok::kw___is_base_of,
+          tok::kw___is_bounded_array,
+          tok::kw___is_class,
+          tok::kw___is_complete_type,
+          tok::kw___is_compound,
+          tok::kw___is_const,
+          tok::kw___is_constructible,
+          tok::kw___is_convertible,
+          tok::kw___is_convertible_to,
+          tok::kw___is_destructible,
+          tok::kw___is_empty,
+          tok::kw___is_enum,
+          tok::kw___is_floating_point,
+          tok::kw___is_final,
+          tok::kw___is_function,
+          tok::kw___is_fundamental,
+          tok::kw___is_integral,
+          tok::kw___is_interface_class,
           tok::kw___is_literal,
           tok::kw___is_lvalue_expr,
           tok::kw___is_lvalue_reference,
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 26579de25bdf0..8b9f5d402b76f 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -5409,14 +5409,15 @@ ExprResult Sema::ConvertVectorExpr(Expr *E, 
TypeSourceInfo *TInfo,
 }
 
 ExprResult Sema::BuiltinInvoke(CallExpr *TheCall) {
-  auto Loc = TheCall->getBeginLoc();
+  SourceLocation Loc = TheCall->getBeginLoc();
   auto Args = MutableArrayRef(TheCall->getArgs(), TheCall->getNumArgs());
   assert(llvm::none_of(Args,
                        [](Expr *Arg) { return Arg->isTypeDependent(); }));
 
   if (Args.size() == 0) {
     Diag(TheCall->getBeginLoc(), 
diag::err_typecheck_call_too_few_args_at_least)
-        << 0 << 1 << 0 << 0 << TheCall->getSourceRange();
+        << /*callee_type=*/0 << /*min_arg_count=*/1 << /*actual_arg_count=*/0
+        << /*is_non_object=*/0 << TheCall->getSourceRange();
     return ExprError();
   }
 
@@ -5425,8 +5426,9 @@ ExprResult Sema::BuiltinInvoke(CallExpr *TheCall) {
   if (auto *MPT = FuncT->getAs<MemberPointerType>()) {
     if (Args.size() < 2) {
       Diag(TheCall->getBeginLoc(),
-            diag::err_typecheck_call_too_few_args_at_least)
-          << 0 << 2 << 1 << 0 << TheCall->getSourceRange();
+           diag::err_typecheck_call_too_few_args_at_least)
+          << /*callee_type=*/0 << /*min_arg_count=*/2 << /*actual_arg_count=*/1
+          << /*is_non_object=*/0 << TheCall->getSourceRange();
       return ExprError();
     }
 
diff --git a/clang/test/SemaCXX/builtin-invoke.cpp 
b/clang/test/SemaCXX/builtin-invoke.cpp
index 5b156b5ff75c4..489d20bcd3dcd 100644
--- a/clang/test/SemaCXX/builtin-invoke.cpp
+++ b/clang/test/SemaCXX/builtin-invoke.cpp
@@ -131,3 +131,12 @@ void test3() {
   __builtin_invoke(diagnose_discard); // expected-warning {{ignoring return 
value of function declared with 'nodiscard' attribute}}
   __builtin_invoke(no_diagnose_discard);
 }
+
+template <class T>
+auto test(T v) {
+  return __builtin_invoke(v);
+}
+
+auto call2() {
+  test(call);
+}

>From fd17bcca02ce0cd26928c442a006f3f4d3a7965f Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklau...@berlin.de>
Date: Tue, 20 May 2025 17:00:21 +0200
Subject: [PATCH 3/5] Address comments

---
 clang/lib/Sema/SemaChecking.cpp | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 8b9f5d402b76f..5b423841dbbcb 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -5436,11 +5436,8 @@ ExprResult Sema::BuiltinInvoke(CallExpr *TheCall) {
     auto ObjectT = Args[1]->getType();
 
 
-    if (MPT->isMemberDataPointer() && Args.size() != 2) {
-      Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_many_args)
-          << 0 << 2 << Args.size() << 0 << TheCall->getSourceRange();
+    if (MPT->isMemberDataPointer() && checkArgCount(TheCall, 2))
       return ExprError();
-    }
 
     ExprResult ObjectArg = [&]() -> ExprResult {
       // (1.1): (t1.*f)(t2, …, tN) when f is a pointer to a member function of 
a

>From e66f72837d012dc762546805e44a6a466ddf2aee Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklau...@berlin.de>
Date: Tue, 20 May 2025 17:00:50 +0200
Subject: [PATCH 4/5] Commit everything

---
 clang/lib/Sema/SemaChecking.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 5b423841dbbcb..ae2a5fcba3074 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -5435,7 +5435,6 @@ ExprResult Sema::BuiltinInvoke(CallExpr *TheCall) {
     auto *MemPtrClass = MPT->getQualifier()->getAsType();
     auto ObjectT = Args[1]->getType();
 
-
     if (MPT->isMemberDataPointer() && checkArgCount(TheCall, 2))
       return ExprError();
 

>From 24427c9d0dd0252353282b1a0d3cd7e1f562616b Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklau...@berlin.de>
Date: Wed, 21 May 2025 10:37:34 +0200
Subject: [PATCH 5/5] Address comments

---
 clang/docs/LanguageExtensions.rst     |  11 ++
 clang/docs/ReleaseNotes.rst           |   1 +
 clang/include/clang/Sema/Sema.h       |   2 -
 clang/lib/Sema/SemaChecking.cpp       | 188 +++++++++++++-------------
 clang/test/SemaCXX/builtin-invoke.cpp |   8 +-
 libcxx/include/__type_traits/invoke.h |  11 +-
 6 files changed, 118 insertions(+), 103 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst 
b/clang/docs/LanguageExtensions.rst
index a40dd4d1a1673..726329d570d9d 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -3750,6 +3750,17 @@ Trivially relocates ``count`` objects of relocatable, 
complete type ``T``
 from ``src`` to ``dest`` and returns ``dest``.
 This builtin is used to implement ``std::trivially_relocate``.
 
+``__builtin_invoke``
+--------------------
+
+**Syntax**:
+
+.. code-block:: c++
+
+  template <class Callee, class... Args>
+  decltype(auto) __builtin_invoke(Callee&& callee, Args&&... args);
+
+``__builtin_invoke`` is equivalent to ``std::invoke``.
 
 ``__builtin_preserve_access_index``
 -----------------------------------
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4a3c1bee82831..a0779daeecc9e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -301,6 +301,7 @@ Non-comprehensive list of changes in this release
   different than before.
 - Fixed a crash when a VLA with an invalid size expression was used within a
   ``sizeof`` or ``typeof`` expression. (#GH138444)
+- ``__builtin_invoke`` has been added to improve the compile time of 
``std::invoke``.
 
 New Compiler Flags
 ------------------
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 22d66e8688906..de9cf1994ffe4 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2594,8 +2594,6 @@ class Sema final : public SemaBase {
                                SourceLocation BuiltinLoc,
                                SourceLocation RParenLoc);
 
-  ExprResult BuiltinInvoke(CallExpr *TheCall);
-
   static StringRef GetFormatStringTypeName(FormatStringType FST);
   static FormatStringType GetFormatStringType(StringRef FormatFlavor);
   static FormatStringType GetFormatStringType(const FormatAttr *Format);
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index ae2a5fcba3074..c49e2e1df879e 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2220,6 +2220,99 @@ static bool BuiltinCountZeroBitsGeneric(Sema &S, 
CallExpr *TheCall) {
   return false;
 }
 
+static ExprResult BuiltinInvoke(Sema &S, CallExpr *TheCall) {
+  SourceLocation Loc = TheCall->getBeginLoc();
+  MutableArrayRef Args(TheCall->getArgs(), TheCall->getNumArgs());
+  assert(llvm::none_of(Args, [](Expr *Arg) { return Arg->isTypeDependent(); 
}));
+
+  if (Args.size() == 0) {
+    S.Diag(TheCall->getBeginLoc(),
+           diag::err_typecheck_call_too_few_args_at_least)
+        << /*callee_type=*/0 << /*min_arg_count=*/1 << /*actual_arg_count=*/0
+        << /*is_non_object=*/0 << TheCall->getSourceRange();
+    return ExprError();
+  }
+
+  QualType FuncT = Args[0]->getType();
+
+  if (const auto *MPT = FuncT->getAs<MemberPointerType>()) {
+    if (Args.size() < 2) {
+      S.Diag(TheCall->getBeginLoc(),
+             diag::err_typecheck_call_too_few_args_at_least)
+          << /*callee_type=*/0 << /*min_arg_count=*/2 << /*actual_arg_count=*/1
+          << /*is_non_object=*/0 << TheCall->getSourceRange();
+      return ExprError();
+    }
+
+    const Type *MemPtrClass = MPT->getQualifier()->getAsType();
+    QualType ObjectT = Args[1]->getType();
+
+    if (MPT->isMemberDataPointer() && S.checkArgCount(TheCall, 2))
+      return ExprError();
+
+    ExprResult ObjectArg = [&]() -> ExprResult {
+      // (1.1): (t1.*f)(t2, ..., tN) when f is a pointer to a member function 
of
+      // a class T and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
+      // is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
+      // (1.4): t1.*f when N=1 and f is a pointer to data member of a class T
+      // and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
+      // is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
+      if (S.Context.hasSameType(QualType(MemPtrClass, 0),
+                                S.BuiltinRemoveCVRef(ObjectT, Loc)) ||
+          S.BuiltinIsBaseOf(Args[1]->getBeginLoc(), QualType(MemPtrClass, 0),
+                            S.BuiltinRemoveCVRef(ObjectT, Loc))) {
+        return Args[1];
+      }
+
+      // (t1.get().*f)(t2, ..., tN) when f is a pointer to a member function of
+      // a class T and remove_cvref_t<decltype(t1)> is a specialization of
+      // reference_wrapper;
+      if (const auto *RD = ObjectT->getAsCXXRecordDecl()) {
+        if (RD->isInStdNamespace() &&
+            RD->getDeclName().getAsString() == "reference_wrapper") {
+          CXXScopeSpec SS;
+          IdentifierInfo *GetName = &S.Context.Idents.get("get");
+          UnqualifiedId GetID;
+          GetID.setIdentifier(GetName, Loc);
+
+          ExprResult MemExpr = S.ActOnMemberAccessExpr(
+              S.getCurScope(), Args[1], Loc, tok::period, SS,
+              /*TemplateKWLoc=*/SourceLocation(), GetID, nullptr);
+
+          if (MemExpr.isInvalid())
+            return ExprError();
+
+          return S.ActOnCallExpr(S.getCurScope(), MemExpr.get(), Loc, {}, Loc);
+        }
+      }
+
+      // ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a
+      // class T and t1 does not satisfy the previous two items;
+
+      return S.ActOnUnaryOp(S.getCurScope(), Loc, tok::star, Args[1]);
+    }();
+
+    if (ObjectArg.isInvalid())
+      return ExprError();
+
+    ExprResult BinOp = S.ActOnBinOp(S.getCurScope(), TheCall->getBeginLoc(),
+                                    tok::periodstar, ObjectArg.get(), Args[0]);
+    if (BinOp.isInvalid())
+      return ExprError();
+
+    if (MPT->isMemberDataPointer())
+      return BinOp;
+
+    auto *MemCall = new (S.Context)
+        ParenExpr(SourceLocation(), SourceLocation(), BinOp.get());
+
+    return S.ActOnCallExpr(S.getCurScope(), MemCall, TheCall->getBeginLoc(),
+                           Args.drop_front(2), TheCall->getRParenLoc());
+  }
+  return S.ActOnCallExpr(S.getCurScope(), Args.front(), TheCall->getBeginLoc(),
+                         Args.drop_front(), TheCall->getRParenLoc());
+}
+
 ExprResult
 Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
                                CallExpr *TheCall) {
@@ -2369,7 +2462,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, 
unsigned BuiltinID,
     // TheCall will be freed by the smart pointer here, but that's fine, since
     // BuiltinShuffleVector guts it, but then doesn't release it.
   case Builtin::BI__builtin_invoke:
-    return BuiltinInvoke(TheCall);
+    return BuiltinInvoke(*this, TheCall);
   case Builtin::BI__builtin_prefetch:
     if (BuiltinPrefetch(TheCall))
       return ExprError();
@@ -5408,99 +5501,6 @@ ExprResult Sema::ConvertVectorExpr(Expr *E, 
TypeSourceInfo *TInfo,
                                    RParenLoc, CurFPFeatureOverrides());
 }
 
-ExprResult Sema::BuiltinInvoke(CallExpr *TheCall) {
-  SourceLocation Loc = TheCall->getBeginLoc();
-  auto Args = MutableArrayRef(TheCall->getArgs(), TheCall->getNumArgs());
-  assert(llvm::none_of(Args,
-                       [](Expr *Arg) { return Arg->isTypeDependent(); }));
-
-  if (Args.size() == 0) {
-    Diag(TheCall->getBeginLoc(), 
diag::err_typecheck_call_too_few_args_at_least)
-        << /*callee_type=*/0 << /*min_arg_count=*/1 << /*actual_arg_count=*/0
-        << /*is_non_object=*/0 << TheCall->getSourceRange();
-    return ExprError();
-  }
-
-  auto FuncT = Args[0]->getType();
-
-  if (auto *MPT = FuncT->getAs<MemberPointerType>()) {
-    if (Args.size() < 2) {
-      Diag(TheCall->getBeginLoc(),
-           diag::err_typecheck_call_too_few_args_at_least)
-          << /*callee_type=*/0 << /*min_arg_count=*/2 << /*actual_arg_count=*/1
-          << /*is_non_object=*/0 << TheCall->getSourceRange();
-      return ExprError();
-    }
-
-    auto *MemPtrClass = MPT->getQualifier()->getAsType();
-    auto ObjectT = Args[1]->getType();
-
-    if (MPT->isMemberDataPointer() && checkArgCount(TheCall, 2))
-      return ExprError();
-
-    ExprResult ObjectArg = [&]() -> ExprResult {
-      // (1.1): (t1.*f)(t2, …, tN) when f is a pointer to a member function of 
a
-      // class T and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
-      // is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
-      // (1.4): t1.*f when N=1 and f is a pointer to data member of a class T
-      // and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
-      // is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
-      if (Context.hasSameType(QualType(MemPtrClass, 0),
-                              BuiltinRemoveCVRef(ObjectT, Loc)) ||
-          BuiltinIsBaseOf(Args[1]->getBeginLoc(), QualType(MemPtrClass, 0),
-                          BuiltinRemoveCVRef(ObjectT, Loc))) {
-        return Args[1];
-      }
-
-      // (t1.get().*f)(t2, …, tN) when f is a pointer to a member function of
-      // a class T and remove_cvref_t<decltype(t1)> is a specialization of
-      // reference_wrapper;
-      if (auto *RD = ObjectT->getAsCXXRecordDecl()) {
-        if (RD->isInStdNamespace() &&
-            RD->getDeclName().getAsString() == "reference_wrapper") {
-          CXXScopeSpec SS;
-          IdentifierInfo *GetName = &Context.Idents.get("get");
-          UnqualifiedId GetID;
-          GetID.setIdentifier(GetName, Loc);
-
-          auto MemExpr = ActOnMemberAccessExpr(
-              getCurScope(), Args[1], Loc, tok::period, SS,
-              /*TemplateKWLoc=*/SourceLocation(), GetID, nullptr);
-
-          if (MemExpr.isInvalid())
-            return ExprError();
-
-          return ActOnCallExpr(getCurScope(), MemExpr.get(), Loc, {}, Loc);
-        }
-      }
-
-      // ((*t1).*f)(t2, …, tN) when f is a pointer to a member function of a
-      // class T and t1 does not satisfy the previous two items;
-
-      return ActOnUnaryOp(getCurScope(), Loc, tok::star, Args[1]);
-    }();
-
-    if (ObjectArg.isInvalid())
-      return ExprError();
-
-    auto BinOp = ActOnBinOp(getCurScope(), TheCall->getBeginLoc(),
-                            tok::periodstar, ObjectArg.get(), Args[0]);
-    if (BinOp.isInvalid())
-      return ExprError();
-
-    if (MPT->isMemberDataPointer())
-      return BinOp;
-
-    auto *MemCall = new (Context)
-        ParenExpr(SourceLocation(), SourceLocation(), BinOp.get());
-
-    return ActOnCallExpr(getCurScope(), MemCall, TheCall->getBeginLoc(),
-                         Args.drop_front(2), TheCall->getRParenLoc());
-  }
-  return ActOnCallExpr(getCurScope(), Args.front(), TheCall->getBeginLoc(),
-                       Args.drop_front(), TheCall->getRParenLoc());
-}
-
 bool Sema::BuiltinPrefetch(CallExpr *TheCall) {
   unsigned NumArgs = TheCall->getNumArgs();
 
diff --git a/clang/test/SemaCXX/builtin-invoke.cpp 
b/clang/test/SemaCXX/builtin-invoke.cpp
index 489d20bcd3dcd..95d34ea26dfe3 100644
--- a/clang/test/SemaCXX/builtin-invoke.cpp
+++ b/clang/test/SemaCXX/builtin-invoke.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -verify -fsyntax-only %s
+// RUN: %clang_cc1 -verify -fsyntax-only %s -std=c++20
 
 void func() { // expected-note {{'func' declared here}}
   __builtin_invoke(); // expected-error {{too few arguments to function call, 
expected at least 1, have 0}}
@@ -127,6 +127,12 @@ namespace std {
   auto invoke(Args&&... args) -> decltype(__builtin_invoke(args...));
 } // namespace std
 
+template <class... Args>
+concept invocable = requires(Args... args) { __builtin_invoke(args...); };
+
+static_assert(!invocable<std::reference_wrapper<InvalidSpecialization1>>);
+static_assert(!invocable<std::reference_wrapper<InvalidSpecialization2>>);
+
 void test3() {
   __builtin_invoke(diagnose_discard); // expected-warning {{ignoring return 
value of function declared with 'nodiscard' attribute}}
   __builtin_invoke(no_diagnose_discard);
diff --git a/libcxx/include/__type_traits/invoke.h 
b/libcxx/include/__type_traits/invoke.h
index 531b3d02bbb4d..c08b075d0a26c 100644
--- a/libcxx/include/__type_traits/invoke.h
+++ b/libcxx/include/__type_traits/invoke.h
@@ -120,10 +120,10 @@ inline const bool __is_nothrow_invocable_r_v =
 // is_invocable
 
 template <class _Fn, class... _Args>
-struct _LIBCPP_TEMPLATE_VIS is_invocable : bool_constant<__is_invocable_v<_Fn, 
_Args...> > {};
+struct is_invocable : bool_constant<__is_invocable_v<_Fn, _Args...> > {};
 
 template <class _Ret, class _Fn, class... _Args>
-struct _LIBCPP_TEMPLATE_VIS is_invocable_r : 
bool_constant<__is_invocable_r_v<_Ret, _Fn, _Args...>> {};
+struct is_invocable_r : bool_constant<__is_invocable_r_v<_Ret, _Fn, _Args...>> 
{};
 
 template <class _Fn, class... _Args>
 inline constexpr bool is_invocable_v = __is_invocable_v<_Fn, _Args...>;
@@ -134,11 +134,10 @@ inline constexpr bool is_invocable_r_v = 
is_invocable_r<_Ret, _Fn, _Args...>::va
 // is_nothrow_invocable
 
 template <class _Fn, class... _Args>
-struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable : 
bool_constant<__is_nothrow_invocable_v<_Fn, _Args...> > {};
+struct is_nothrow_invocable : bool_constant<__is_nothrow_invocable_v<_Fn, 
_Args...> > {};
 
 template <class _Ret, class _Fn, class... _Args>
-struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable_r
-    : integral_constant<bool, __is_nothrow_invocable_r_v<_Ret, _Fn, _Args...>> 
{};
+struct is_nothrow_invocable_r : integral_constant<bool, 
__is_nothrow_invocable_r_v<_Ret, _Fn, _Args...>> {};
 
 template <class _Fn, class... _Args>
 inline constexpr bool is_nothrow_invocable_v = __is_nothrow_invocable_v<_Fn, 
_Args...>;
@@ -147,7 +146,7 @@ template <class _Ret, class _Fn, class... _Args>
 inline constexpr bool is_nothrow_invocable_r_v = 
__is_nothrow_invocable_r_v<_Ret, _Fn, _Args...>;
 
 template <class _Fn, class... _Args>
-struct _LIBCPP_TEMPLATE_VIS invoke_result {
+struct invoke_result {
   using type = __invoke_result_t<_Fn, _Args...>;
 };
 

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to