[libcxx-commits] [clang] [libcxx] [Clang] Add __is_invocable_r and __is_nothrow_invocable_r (PR #81213)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Feb 8 17:36:21 PST 2024
https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/81213
This patch also uses the new builtins in libc++.
>From 6c74eb263dd889858f3f7be328d85fe354f71835 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 11 Sep 2023 04:31:02 +0200
Subject: [PATCH] [Clang] Add __is_invocable_r and __is_nothrow_invocable_r
This patch also uses the new builtins in libc++.
---
clang/docs/LanguageExtensions.rst | 2 +
clang/docs/ReleaseNotes.rst | 4 +
clang/include/clang/AST/Type.h | 3 +
clang/include/clang/Basic/TokenKinds.def | 2 +
clang/lib/Sema/SemaExprCXX.cpp | 390 +++++++++++++------
clang/test/SemaCXX/type-traits-invocable.cpp | 288 ++++++++++++++
libcxx/include/__type_traits/invoke.h | 36 ++
7 files changed, 606 insertions(+), 119 deletions(-)
create mode 100644 clang/test/SemaCXX/type-traits-invocable.cpp
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index e91156837290f7..bf061590e5ca0b 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1582,6 +1582,7 @@ The following type trait primitives are supported by Clang. Those traits marked
* ``__is_integral`` (C++, Embarcadero)
* ``__is_interface_class`` (Microsoft):
Returns ``false``, even for types defined with ``__interface``.
+* ``__is_invocable_r`` (Clang)
* ``__is_literal`` (Clang):
Synonym for ``__is_literal_type``.
* ``__is_literal_type`` (C++, GNU, Microsoft):
@@ -1594,6 +1595,7 @@ The following type trait primitives are supported by Clang. Those traits marked
* ``__is_nothrow_assignable`` (C++, MSVC 2013)
* ``__is_nothrow_constructible`` (C++, MSVC 2013)
* ``__is_nothrow_destructible`` (C++, MSVC 2013)
+* ``__is_nothrow_invocable_r`` (Clang)
* ``__is_nullptr`` (C++, GNU, Microsoft, Embarcadero):
Returns true for ``std::nullptr_t`` and false for everything else. The
corresponding standard library feature is ``std::is_null_pointer``, but
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index cd8a82f281f52a..8a4ae646c008f6 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -78,6 +78,10 @@ here. Generic improvements to Clang as a whole or to its underlying
infrastructure are described first, followed by language-specific
sections with improvements to Clang's support for those languages.
+- The builtins `__is_invocable_r` and `__is_nothrow_invocable_r` have been added.
+ These are equivalent to the standard library builtins `std::is_invocable_r`
+ and `std::is_nothrow_invocable_r`.
+
C++ Language Changes
--------------------
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index d6a55f39a4bede..fe10391ac7057c 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -926,6 +926,9 @@ class QualType {
/// Return true if this is a trivially equality comparable type.
bool isTriviallyEqualityComparableType(const ASTContext &Context) const;
+ /// Returns true if this is an invocable type.
+ bool isInvocableType() const;
+
/// Returns true if it is a class and it might be dynamic.
bool mayBeDynamicClass() const;
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 23817cde7a9354..6467a52c82cb5b 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -532,6 +532,8 @@ TYPE_TRAIT_1(__is_unbounded_array, IsUnboundedArray, KEYCXX)
TYPE_TRAIT_1(__is_nullptr, IsNullPointer, KEYCXX)
TYPE_TRAIT_1(__is_scoped_enum, IsScopedEnum, KEYCXX)
TYPE_TRAIT_1(__is_referenceable, IsReferenceable, KEYCXX)
+TYPE_TRAIT_N(__is_invocable_r, IsInvocableR, KEYCXX)
+TYPE_TRAIT_N(__is_nothrow_invocable_r, IsNothrowInvocableR, KEYCXX)
TYPE_TRAIT_1(__can_pass_in_regs, CanPassInRegs, KEYCXX)
TYPE_TRAIT_2(__reference_binds_to_temporary, ReferenceBindsToTemporary, KEYCXX)
TYPE_TRAIT_2(__reference_constructs_from_temporary, ReferenceConstructsFromTemporary, KEYCXX)
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 246d2313e089f3..4f4b6492e58880 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5468,6 +5468,266 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT,
QualType RhsT, SourceLocation KeyLoc);
+static bool IsBaseOf(Sema &Self, QualType LhsT, QualType RhsT,
+ SourceLocation KeyLoc) {
+ // 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;
+
+ ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
+ ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
+ if (!BaseInterface || !DerivedInterface)
+ return false;
+
+ if (Self.RequireCompleteType(
+ KeyLoc, RhsT, diag::err_incomplete_type_used_in_type_trait_expr))
+ return false;
+
+ return BaseInterface->isSuperClassOf(DerivedInterface);
+ }
+
+ assert(Self.Context.hasSameUnqualifiedType(LhsT, RhsT) ==
+ (lhsRecord == rhsRecord));
+
+ // 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;
+
+ // 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(
+ KeyLoc, RhsT, diag::err_incomplete_type_used_in_type_trait_expr))
+ return false;
+
+ return cast<CXXRecordDecl>(rhsRecord->getDecl())
+ ->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
+}
+
+static bool IsConvertible(Sema& Self, QualType LhsT, QualType RhsT, SourceLocation KeyLoc, bool CheckNothrow) {
+ // C++0x [meta.rel]p4:
+ // Given the following function prototype:
+ //
+ // template <class T>
+ // typename add_rvalue_reference<T>::type create();
+ //
+ // the predicate condition for a template specialization
+ // is_convertible<From, To> shall be satisfied if and only if
+ // the return expression in the following code would be
+ // well-formed, including any implicit conversions to the return
+ // type of the function:
+ //
+ // To test() {
+ // return create<From>();
+ // }
+ //
+ // Access checking is performed as if in a context unrelated to To and
+ // From. Only the validity of the immediate context of the expression
+ // of the return-statement (including conversions to the return type)
+ // is considered.
+ //
+ // We model the initialization as a copy-initialization of a temporary
+ // of the appropriate type, which for this expression is identical to the
+ // return statement (since NRVO doesn't apply).
+
+ // Functions aren't allowed to return function or array types.
+ if (RhsT->isFunctionType() || RhsT->isArrayType())
+ return false;
+
+ // A return statement in a void function must have void type.
+ if (RhsT->isVoidType())
+ return LhsT->isVoidType();
+
+ // A function definition requires a complete, non-abstract return type.
+ if (!Self.isCompleteType(KeyLoc, RhsT) || Self.isAbstractType(KeyLoc, RhsT))
+ return false;
+
+ // Compute the result of add_rvalue_reference.
+ if (LhsT->isObjectType() || LhsT->isFunctionType())
+ LhsT = Self.Context.getRValueReferenceType(LhsT);
+
+ // Build a fake source and destination for initialization.
+ InitializedEntity To(InitializedEntity::InitializeTemporary(RhsT));
+ OpaqueValueExpr From(KeyLoc, LhsT.getNonLValueExprType(Self.Context),
+ Expr::getValueKindForType(LhsT));
+ Expr *FromPtr = &From;
+ InitializationKind Kind(
+ InitializationKind::CreateCopy(KeyLoc, SourceLocation()));
+
+ // Perform the initialization in an unevaluated context within a SFINAE
+ // trap at translation unit scope.
+ EnterExpressionEvaluationContext Unevaluated(
+ Self, Sema::ExpressionEvaluationContext::Unevaluated);
+ Sema::SFINAETrap SFINAE(Self, /*AccessCheckingSFINAE=*/true);
+ Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl());
+ InitializationSequence Init(Self, To, Kind, FromPtr);
+ if (Init.Failed())
+ return false;
+
+ ExprResult Result = Init.Perform(Self, To, Kind, FromPtr);
+ if (Result.isInvalid() || SFINAE.hasErrorOccurred())
+ return false;
+
+ if (!CheckNothrow)
+ return true;
+ return Self.canThrow(Result.get()) == CT_Cannot;
+}
+
+static bool RequireAllCompleteTypes(Sema &S, ArrayRef<TypeSourceInfo *> Args,
+ SourceLocation KWLoc) {
+ for (const auto *TSI : Args) {
+ QualType ArgTy = TSI->getType();
+ if (ArgTy->isVoidType() || ArgTy->isIncompleteArrayType())
+ continue;
+
+ if (S.RequireCompleteType(
+ KWLoc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr))
+ return true;
+ }
+ return false;
+}
+
+static bool IsInvocable(Sema &S, SourceLocation KWLoc,
+ ArrayRef<TypeSourceInfo *> Args,
+ SourceLocation RParenLoc, bool CheckNothrow) {
+ assert(!Args.empty());
+
+ if (Args.size() == 1) {
+ S.Diag(RParenLoc, diag::err_type_trait_arity)
+ << 2 << 1 << 1 << Args.size() << 1;
+ return false;
+ }
+
+ if (RequireAllCompleteTypes(S, Args, KWLoc))
+ return false;
+
+ auto ReturnType = Args[0]->getType();
+ auto FunctorType = Args[1]->getType();
+ auto NonRefFunctorType = FunctorType.getNonReferenceType();
+
+ const auto IsConvertibleToReturnType = [&](QualType InvokeResult) {
+ return ReturnType->isVoidType() ||
+ IsConvertible(S, InvokeResult, ReturnType, RParenLoc, CheckNothrow);
+ };
+
+ const auto BuildCallHelper = [&](size_t IgnoreCount, auto CallChecker) {
+ EnterExpressionEvaluationContext Unevaluated(
+ S, Sema::ExpressionEvaluationContext::Unevaluated);
+ Sema::SFINAETrap SFINAE(S, true);
+
+ const auto ArgCount = Args.size() - IgnoreCount;
+
+ auto Deleter = [&](OpaqueValueExpr *Expr) {
+ for (size_t i = 0; i != ArgCount; ++i)
+ std::destroy_at(Expr + i);
+ std::allocator<OpaqueValueExpr>{}.deallocate(Expr, ArgCount);
+ };
+
+ // FIXME: There is most likely a much better way to achieve this
+ std::unique_ptr<OpaqueValueExpr[], decltype(Deleter) &> ArgExprs(
+ std::allocator<OpaqueValueExpr>{}.allocate(ArgCount), Deleter);
+ std::unique_ptr<Expr *[]> ArgExprPtrs =
+ std::make_unique<Expr *[]>(ArgCount);
+
+ for (auto [I, Arg] : llvm::enumerate(Args.drop_front(IgnoreCount))) {
+ ::new (ArgExprs.get() + I)
+ OpaqueValueExpr(KWLoc, Arg->getType().getNonReferenceType(),
+ Expr::getValueKindForType(Arg->getType()));
+ ArgExprPtrs[I] = ArgExprs.get() + I;
+ }
+ return CallChecker(MutableArrayRef<Expr *>(ArgExprPtrs.get(), ArgCount));
+ };
+
+ // bullets 1-6
+ if (NonRefFunctorType->isMemberPointerType()) {
+ if (Args.size() < 3)
+ return false;
+
+ auto *MemberPointerT = NonRefFunctorType->getAs<MemberPointerType>();
+
+ auto Object = Args[2]->getType().getNonReferenceType();
+
+ // bullets 3, 6 - ignore pointers
+ if (Object->isPointerType())
+ Object = Object->getPointeeType();
+ // bullets 2, 5 - ignore reference_wrapper
+ else if (auto* RD = Object->getAsCXXRecordDecl()) {
+ if (auto *TS = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
+ if (TS->isInStdNamespace() && TS->getName() == "reference_wrapper")
+ Object = TS->getTemplateArgs().get(0).getAsType();
+ }
+ }
+ // Bullets 2 and 3 are now equivalent to 1 and
+ // bullets 2 and 5 are equivalent to 4.
+
+ if (!IsBaseOf(S, MemberPointerT->getClass()->getCanonicalTypeUnqualified(),
+ Object, RParenLoc))
+ return false;
+
+ // bullets 4-6
+ if (NonRefFunctorType->isMemberDataPointerType()) {
+ if (!IsConvertibleToReturnType(MemberPointerT->getPointeeType()))
+ return false;
+ return Args.size() == 3;
+ }
+
+ // bullets 1-3
+ return BuildCallHelper(3, [&](auto ArgExprPtrs) {
+ OpaqueValueExpr MemberPtr(KWLoc, NonRefFunctorType,
+ Expr::getValueKindForType(FunctorType));
+ OpaqueValueExpr Obj(KWLoc, Object, Expr::getValueKindForType(Object));
+ auto *BinOp = BinaryOperator::Create(
+ S.getASTContext(), &Obj, &MemberPtr,
+ BinaryOperator::Opcode::BO_PtrMemD, S.getASTContext().BoundMemberTy,
+ clang::VK_PRValue, OK_Ordinary, KWLoc, {});
+
+ ParenExpr Parens(KWLoc, RParenLoc, BinOp);
+
+ auto Result = S.BuildCallToMemberFunction(nullptr, &Parens, KWLoc,
+ ArgExprPtrs, RParenLoc);
+ if (Result.isInvalid())
+ return false;
+
+ if (CheckNothrow && S.canThrow(Result.get()) != CT_Cannot)
+ return false;
+
+ return IsConvertibleToReturnType(Result.get()->getType());
+ });
+ }
+
+ // bullet 7
+ return BuildCallHelper(2, [&](auto ArgExprPtrs) {
+ OpaqueValueExpr Obj(KWLoc, NonRefFunctorType,
+ Expr::getValueKindForType(FunctorType));
+
+ auto Result = S.BuildCallExpr(nullptr, &Obj, KWLoc, ArgExprPtrs, RParenLoc);
+
+ if (Result.isInvalid())
+ return false;
+
+ if (CheckNothrow && S.canThrow(Result.get()) != CT_Cannot)
+ return false;
+
+ return IsConvertibleToReturnType(Result.get()->getType());
+ });
+}
+
static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
SourceLocation KWLoc,
ArrayRef<TypeSourceInfo *> Args,
@@ -5600,7 +5860,12 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
llvm_unreachable("unhandled type trait");
return false;
}
- default: llvm_unreachable("not a TT");
+ case clang::TT_IsInvocableR:
+ case clang::TT_IsNothrowInvocableR:
+ return IsInvocable(S, KWLoc, Args, RParenLoc,
+ Kind == TT_IsNothrowInvocableR);
+
+ default: llvm_unreachable("not a TT");
}
return false;
@@ -5719,56 +5984,9 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT,
"Cannot evaluate traits of dependent types");
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;
-
- ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
- ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
- if (!BaseInterface || !DerivedInterface)
- return false;
-
- if (Self.RequireCompleteType(
- KeyLoc, RhsT, diag::err_incomplete_type_used_in_type_trait_expr))
- return false;
-
- return BaseInterface->isSuperClassOf(DerivedInterface);
- }
-
- assert(Self.Context.hasSameUnqualifiedType(LhsT, RhsT)
- == (lhsRecord == rhsRecord));
-
- // 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;
-
- // 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(KeyLoc, RhsT,
- diag::err_incomplete_type_used_in_type_trait_expr))
- return false;
+ case BTT_IsBaseOf:
+ return IsBaseOf(Self, LhsT, RhsT, KeyLoc);
- return cast<CXXRecordDecl>(rhsRecord->getDecl())
- ->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
- }
case BTT_IsSame:
return Self.Context.hasSameType(LhsT, RhsT);
case BTT_TypeCompatible: {
@@ -5780,75 +5998,9 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT,
}
case BTT_IsConvertible:
case BTT_IsConvertibleTo:
- case BTT_IsNothrowConvertible: {
- // C++0x [meta.rel]p4:
- // Given the following function prototype:
- //
- // template <class T>
- // typename add_rvalue_reference<T>::type create();
- //
- // the predicate condition for a template specialization
- // is_convertible<From, To> shall be satisfied if and only if
- // the return expression in the following code would be
- // well-formed, including any implicit conversions to the return
- // type of the function:
- //
- // To test() {
- // return create<From>();
- // }
- //
- // Access checking is performed as if in a context unrelated to To and
- // From. Only the validity of the immediate context of the expression
- // of the return-statement (including conversions to the return type)
- // is considered.
- //
- // We model the initialization as a copy-initialization of a temporary
- // of the appropriate type, which for this expression is identical to the
- // return statement (since NRVO doesn't apply).
-
- // Functions aren't allowed to return function or array types.
- if (RhsT->isFunctionType() || RhsT->isArrayType())
- return false;
-
- // A return statement in a void function must have void type.
- if (RhsT->isVoidType())
- return LhsT->isVoidType();
-
- // A function definition requires a complete, non-abstract return type.
- if (!Self.isCompleteType(KeyLoc, RhsT) || Self.isAbstractType(KeyLoc, RhsT))
- return false;
-
- // Compute the result of add_rvalue_reference.
- if (LhsT->isObjectType() || LhsT->isFunctionType())
- LhsT = Self.Context.getRValueReferenceType(LhsT);
-
- // Build a fake source and destination for initialization.
- InitializedEntity To(InitializedEntity::InitializeTemporary(RhsT));
- OpaqueValueExpr From(KeyLoc, LhsT.getNonLValueExprType(Self.Context),
- Expr::getValueKindForType(LhsT));
- Expr *FromPtr = &From;
- InitializationKind Kind(InitializationKind::CreateCopy(KeyLoc,
- SourceLocation()));
-
- // Perform the initialization in an unevaluated context within a SFINAE
- // trap at translation unit scope.
- EnterExpressionEvaluationContext Unevaluated(
- Self, Sema::ExpressionEvaluationContext::Unevaluated);
- Sema::SFINAETrap SFINAE(Self, /*AccessCheckingSFINAE=*/true);
- Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl());
- InitializationSequence Init(Self, To, Kind, FromPtr);
- if (Init.Failed())
- return false;
-
- ExprResult Result = Init.Perform(Self, To, Kind, FromPtr);
- if (Result.isInvalid() || SFINAE.hasErrorOccurred())
- return false;
-
- if (BTT != BTT_IsNothrowConvertible)
- return true;
-
- return Self.canThrow(Result.get()) == CT_Cannot;
- }
+ case BTT_IsNothrowConvertible:
+ return IsConvertible(Self, LhsT, RhsT, KeyLoc,
+ BTT == BTT_IsNothrowConvertible);
case BTT_IsAssignable:
case BTT_IsNothrowAssignable:
diff --git a/clang/test/SemaCXX/type-traits-invocable.cpp b/clang/test/SemaCXX/type-traits-invocable.cpp
new file mode 100644
index 00000000000000..2b67e1e2aa3314
--- /dev/null
+++ b/clang/test/SemaCXX/type-traits-invocable.cpp
@@ -0,0 +1,288 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify -std=c++23 -fcxx-exceptions %s
+
+namespace std::inline __1 {
+ template <class>
+ class reference_wrapper {};
+} // namespace std::__1
+
+struct S {};
+
+struct Derived : S {};
+
+struct T {};
+
+struct U {
+ U(int) noexcept {}
+};
+
+struct V {
+ V(int i) {
+ if (i)
+ throw int{};
+ }
+};
+
+static_assert(!__is_nothrow_convertible(int, V));
+
+struct convertible_to_int {
+ operator int();
+};
+
+struct explicitly_convertible_to_int {
+ explicit operator int();
+};
+
+struct InvocableT {
+ void operator()() {}
+ int operator()(int, int, T) noexcept { return 0; }
+ void operator()(T) && {}
+};
+
+struct StaticInvocableT {
+ static void operator()() noexcept {}
+};
+
+struct Incomplete; // expected-note 2 {{forward declaration of 'Incomplete'}}
+
+// __is_invocable_r
+static_assert(__is_invocable_r()); // expected-error {{expected a type}}
+static_assert(!__is_invocable_r(Incomplete, void)); // expected-error {{incomplete type 'Incomplete' used in type trait expression}}
+static_assert(!__is_invocable_r(void)); // expected-error {{type trait requires 2 or more arguments; have 1 argument}}
+static_assert(!__is_invocable_r(void, int));
+
+using member_function_ptr_t = void (S::*)() noexcept;
+using member_function_arg_ptr_t = void (S::*)(int);
+using member_function_return_t = Derived (S::*)();
+
+// bullet 1
+static_assert(__is_invocable_r(void, member_function_ptr_t, S));
+static_assert(__is_invocable_r(void, member_function_ptr_t&, S));
+static_assert(!__is_invocable_r(void, member_function_ptr_t&, T));
+static_assert(__is_invocable_r(void, member_function_ptr_t, S&));
+static_assert(!__is_invocable_r(void, member_function_ptr_t, T&));
+static_assert(__is_invocable_r(void, member_function_arg_ptr_t, S&, int));
+static_assert(__is_invocable_r(void, member_function_arg_ptr_t, S&, int&));
+static_assert(__is_invocable_r(void, member_function_arg_ptr_t, S&, int&));
+static_assert(__is_invocable_r(void, member_function_return_t, S&));
+static_assert(__is_invocable_r(Derived, member_function_return_t, S&));
+static_assert(__is_invocable_r(S, member_function_return_t, S&));
+static_assert(!__is_invocable_r(T, member_function_return_t, S&));
+
+// bullet 2
+static_assert(__is_invocable_r(void, member_function_ptr_t, std::reference_wrapper<S>));
+static_assert(__is_invocable_r(void, member_function_ptr_t&, std::reference_wrapper<S>));
+static_assert(!__is_invocable_r(void, member_function_ptr_t&, std::reference_wrapper<T>));
+static_assert(__is_invocable_r(void, member_function_ptr_t, std::reference_wrapper<S>&));
+static_assert(!__is_invocable_r(void, member_function_ptr_t, std::reference_wrapper<T>&));
+static_assert(__is_invocable_r(void, member_function_arg_ptr_t, std::reference_wrapper<S>&, int));
+static_assert(__is_invocable_r(void, member_function_arg_ptr_t, std::reference_wrapper<S>&, int&));
+static_assert(__is_invocable_r(void, member_function_arg_ptr_t, std::reference_wrapper<S>&, int&));
+static_assert(__is_invocable_r(void, member_function_return_t, std::reference_wrapper<S>&));
+static_assert(__is_invocable_r(Derived, member_function_return_t, std::reference_wrapper<S>&));
+static_assert(__is_invocable_r(S, member_function_return_t, std::reference_wrapper<S>&));
+static_assert(!__is_invocable_r(T, member_function_return_t, std::reference_wrapper<S>&));
+
+// bullet 3
+static_assert(__is_invocable_r(void, member_function_ptr_t, S*));
+static_assert(__is_invocable_r(void, member_function_ptr_t&, S*));
+static_assert(!__is_invocable_r(void, member_function_ptr_t&, T*));
+static_assert(__is_invocable_r(void, member_function_ptr_t, S*&));
+static_assert(!__is_invocable_r(void, member_function_ptr_t, T*&));
+static_assert(__is_invocable_r(void, member_function_arg_ptr_t, S*&, int));
+static_assert(__is_invocable_r(void, member_function_arg_ptr_t, S*&, int&));
+static_assert(__is_invocable_r(void, member_function_arg_ptr_t, S*&, int&));
+static_assert(__is_invocable_r(void, member_function_return_t, S*&));
+static_assert(__is_invocable_r(Derived, member_function_return_t, S*&));
+static_assert(__is_invocable_r(S, member_function_return_t, S*&));
+static_assert(!__is_invocable_r(T, member_function_return_t, S*&));
+
+using member_ptr_t = int S::*;
+// bullet 4
+static_assert(!__is_invocable_r(void, member_ptr_t));
+static_assert(!__is_invocable_r(void, member_ptr_t, S&, int));
+static_assert(__is_invocable_r(void, member_ptr_t, S&));
+static_assert(__is_invocable_r(void, member_ptr_t&, S&));
+static_assert(!__is_invocable_r(void, member_ptr_t, T&));
+static_assert(!__is_invocable_r(void, member_ptr_t, T&, int));
+static_assert(__is_invocable_r(int, member_ptr_t, S&));
+static_assert(__is_invocable_r(long, member_ptr_t, S&));
+static_assert(!__is_invocable_r(T, member_ptr_t, S&));
+static_assert(__is_invocable_r(void, member_ptr_t, Derived&));
+static_assert(!__is_invocable_r(void, member_ptr_t, Derived&, int));
+
+// bullet 5
+static_assert(!__is_invocable_r(void, member_ptr_t, int));
+static_assert(__is_invocable_r(void, member_ptr_t, std::reference_wrapper<S>));
+static_assert(!__is_invocable_r(void, member_ptr_t, std::reference_wrapper<S>, int));
+static_assert(__is_invocable_r(void, member_ptr_t, std::reference_wrapper<S>&));
+static_assert(__is_invocable_r(void, member_ptr_t&, std::reference_wrapper<S>));
+static_assert(!__is_invocable_r(void, member_ptr_t, std::reference_wrapper<T>));
+static_assert(__is_invocable_r(int, member_ptr_t, std::reference_wrapper<S>));
+static_assert(__is_invocable_r(long, member_ptr_t, std::reference_wrapper<S>));
+static_assert(!__is_invocable_r(T, member_ptr_t, std::reference_wrapper<S>));
+static_assert(__is_invocable_r(void, member_ptr_t, std::reference_wrapper<Derived>));
+
+// bullet 6
+static_assert(__is_invocable_r(void, member_ptr_t, S*));
+static_assert(__is_invocable_r(void, member_ptr_t&, S*));
+static_assert(!__is_invocable_r(void, member_ptr_t&, S*, int));
+static_assert(!__is_invocable_r(void, member_ptr_t, T*));
+static_assert(__is_invocable_r(int, member_ptr_t, S*));
+static_assert(__is_invocable_r(long, member_ptr_t, S*));
+static_assert(!__is_invocable_r(T, member_ptr_t, S*));
+static_assert(__is_invocable_r(void, member_ptr_t, Derived*));
+
+// Bullet 7
+using func_t = void(*)() noexcept;
+using func_arg_t = void(*)(int);
+using func_arg_ref_t = void(*)(int&) noexcept;
+using func_return_t = int(*)();
+static_assert(__is_invocable_r(void, func_t));
+static_assert(__is_invocable_r(void, func_t&));
+static_assert(!__is_invocable_r(void, func_arg_t));
+static_assert(__is_invocable_r(void, func_arg_t, int));
+static_assert(__is_invocable_r(void, func_arg_t, int&));
+static_assert(__is_invocable_r(void, func_arg_t, const int&));
+static_assert(__is_invocable_r(void, func_arg_t, short));
+static_assert(__is_invocable_r(void, func_arg_t, char));
+static_assert(__is_invocable_r(void, func_arg_t, long));
+static_assert(__is_invocable_r(void, func_arg_t, double));
+static_assert(__is_invocable_r(void, func_arg_t, convertible_to_int));
+static_assert(!__is_invocable_r(void, func_arg_t, explicitly_convertible_to_int));
+static_assert(__is_invocable_r(void, func_arg_ref_t, int&));
+static_assert(!__is_invocable_r(void, func_arg_ref_t, int));
+static_assert(!__is_invocable_r(void, func_arg_ref_t, int&&));
+static_assert(!__is_invocable_r(int, func_t));
+static_assert(__is_invocable_r(int, func_return_t));
+static_assert(!__is_invocable_r(void, S));
+static_assert(__is_invocable_r(void, InvocableT));
+static_assert(!__is_invocable_r(void, InvocableT, int));
+static_assert(__is_invocable_r(void, InvocableT, int, int, T));
+static_assert(__is_invocable_r(U, InvocableT, int, int, T));
+static_assert(__is_invocable_r(int, InvocableT, int, int, T));
+static_assert(__is_invocable_r(U, InvocableT, int, int, T));
+static_assert(__is_invocable_r(V, InvocableT, int, int, T));
+static_assert(__is_invocable_r(void, InvocableT&, int, int, T));
+static_assert(!__is_invocable_r(T, InvocableT, int, int, T));
+static_assert(!__is_invocable_r(T, InvocableT&, int, int, T));
+static_assert(!__is_invocable_r(T, InvocableT&, const int&, int, T));
+static_assert(__is_invocable_r(void, InvocableT&&, T));
+static_assert(__is_invocable_r(void, StaticInvocableT));
+static_assert(!__is_invocable_r(void, StaticInvocableT, int));
+
+// __is_nothrow_invocable_r
+
+static_assert(__is_nothrow_invocable_r()); // expected-error {{expected a type}}
+static_assert(!__is_nothrow_invocable_r(Incomplete, void)); // expected-error {{incomplete type 'Incomplete' used in type trait expression}}
+static_assert(!__is_nothrow_invocable_r(void)); // expected-error {{type trait requires 2 or more arguments; have 1 argument}}
+static_assert(!__is_nothrow_invocable_r(void, int));
+
+// bullet 1
+static_assert(__is_nothrow_invocable_r(void, member_function_ptr_t, S));
+static_assert(__is_nothrow_invocable_r(void, member_function_ptr_t&, S));
+static_assert(!__is_nothrow_invocable_r(void, member_function_ptr_t&, T));
+static_assert(__is_nothrow_invocable_r(void, member_function_ptr_t, S&));
+static_assert(!__is_nothrow_invocable_r(void, member_function_ptr_t, T&));
+static_assert(!__is_nothrow_invocable_r(void, member_function_arg_ptr_t, S&, int));
+static_assert(!__is_nothrow_invocable_r(void, member_function_arg_ptr_t, S&, int&));
+static_assert(!__is_nothrow_invocable_r(void, member_function_arg_ptr_t, S&, int&));
+static_assert(!__is_nothrow_invocable_r(void, member_function_return_t, S&));
+static_assert(!__is_nothrow_invocable_r(Derived, member_function_return_t, S&));
+static_assert(!__is_nothrow_invocable_r(S, member_function_return_t, S&));
+static_assert(!__is_nothrow_invocable_r(T, member_function_return_t, S&));
+
+// bullet 2
+static_assert(__is_nothrow_invocable_r(void, member_function_ptr_t, std::reference_wrapper<S>));
+static_assert(__is_nothrow_invocable_r(void, member_function_ptr_t&, std::reference_wrapper<S>));
+static_assert(!__is_nothrow_invocable_r(void, member_function_ptr_t&, std::reference_wrapper<T>));
+static_assert(__is_nothrow_invocable_r(void, member_function_ptr_t, std::reference_wrapper<S>&));
+static_assert(!__is_nothrow_invocable_r(void, member_function_ptr_t, std::reference_wrapper<T>&));
+static_assert(!__is_nothrow_invocable_r(void, member_function_arg_ptr_t, std::reference_wrapper<S>&, int));
+static_assert(!__is_nothrow_invocable_r(void, member_function_arg_ptr_t, std::reference_wrapper<S>&, int&));
+static_assert(!__is_nothrow_invocable_r(void, member_function_arg_ptr_t, std::reference_wrapper<S>&, int&));
+static_assert(!__is_nothrow_invocable_r(void, member_function_return_t, std::reference_wrapper<S>&));
+static_assert(!__is_nothrow_invocable_r(Derived, member_function_return_t, std::reference_wrapper<S>&));
+static_assert(!__is_nothrow_invocable_r(S, member_function_return_t, std::reference_wrapper<S>&));
+static_assert(!__is_nothrow_invocable_r(T, member_function_return_t, std::reference_wrapper<S>&));
+
+// bullet 3
+static_assert(__is_nothrow_invocable_r(void, member_function_ptr_t, S*));
+static_assert(__is_nothrow_invocable_r(void, member_function_ptr_t&, S*));
+static_assert(!__is_nothrow_invocable_r(void, member_function_ptr_t&, T*));
+static_assert(__is_nothrow_invocable_r(void, member_function_ptr_t, S*&));
+static_assert(!__is_nothrow_invocable_r(void, member_function_ptr_t, T*&));
+static_assert(!__is_nothrow_invocable_r(void, member_function_arg_ptr_t, S*&, int));
+static_assert(!__is_nothrow_invocable_r(void, member_function_arg_ptr_t, S*&, int&));
+static_assert(!__is_nothrow_invocable_r(void, member_function_arg_ptr_t, S*&, int&));
+static_assert(!__is_nothrow_invocable_r(void, member_function_return_t, S*&));
+static_assert(!__is_nothrow_invocable_r(Derived, member_function_return_t, S*&));
+static_assert(!__is_nothrow_invocable_r(S, member_function_return_t, S*&));
+static_assert(!__is_nothrow_invocable_r(T, member_function_return_t, S*&));
+
+// bullet 4
+static_assert(!__is_nothrow_invocable_r(void, member_ptr_t));
+static_assert(!__is_nothrow_invocable_r(void, member_ptr_t, S&, int));
+static_assert(__is_nothrow_invocable_r(void, member_ptr_t, S&));
+static_assert(__is_nothrow_invocable_r(void, member_ptr_t&, S&));
+static_assert(!__is_nothrow_invocable_r(void, member_ptr_t, T&));
+static_assert(!__is_nothrow_invocable_r(void, member_ptr_t, T&, int));
+static_assert(__is_nothrow_invocable_r(int, member_ptr_t, S&));
+static_assert(__is_nothrow_invocable_r(long, member_ptr_t, S&));
+static_assert(!__is_nothrow_invocable_r(T, member_ptr_t, S&));
+static_assert(__is_nothrow_invocable_r(void, member_ptr_t, Derived&));
+static_assert(!__is_nothrow_invocable_r(void, member_ptr_t, Derived&, int));
+
+// bullet 5
+static_assert(!__is_nothrow_invocable_r(void, member_ptr_t, int));
+static_assert(__is_nothrow_invocable_r(void, member_ptr_t, std::reference_wrapper<S>));
+static_assert(!__is_nothrow_invocable_r(void, member_ptr_t, std::reference_wrapper<S>, int));
+static_assert(__is_nothrow_invocable_r(void, member_ptr_t, std::reference_wrapper<S>&));
+static_assert(__is_nothrow_invocable_r(void, member_ptr_t&, std::reference_wrapper<S>));
+static_assert(!__is_nothrow_invocable_r(void, member_ptr_t, std::reference_wrapper<T>));
+static_assert(__is_nothrow_invocable_r(int, member_ptr_t, std::reference_wrapper<S>));
+static_assert(__is_nothrow_invocable_r(long, member_ptr_t, std::reference_wrapper<S>));
+static_assert(!__is_nothrow_invocable_r(T, member_ptr_t, std::reference_wrapper<S>));
+static_assert(__is_nothrow_invocable_r(void, member_ptr_t, std::reference_wrapper<Derived>));
+
+// bullet 6
+static_assert(__is_nothrow_invocable_r(void, member_ptr_t, S*));
+static_assert(__is_nothrow_invocable_r(void, member_ptr_t&, S*));
+static_assert(!__is_nothrow_invocable_r(void, member_ptr_t&, S*, int));
+static_assert(!__is_nothrow_invocable_r(void, member_ptr_t, T*));
+static_assert(__is_nothrow_invocable_r(int, member_ptr_t, S*));
+static_assert(__is_nothrow_invocable_r(long, member_ptr_t, S*));
+static_assert(!__is_nothrow_invocable_r(T, member_ptr_t, S*));
+static_assert(__is_nothrow_invocable_r(void, member_ptr_t, Derived*));
+
+// Bullet 7
+static_assert(__is_nothrow_invocable_r(void, func_t));
+static_assert(__is_nothrow_invocable_r(void, func_t&));
+static_assert(!__is_nothrow_invocable_r(void, func_arg_t));
+static_assert(!__is_nothrow_invocable_r(void, func_arg_t, int));
+static_assert(!__is_nothrow_invocable_r(void, func_arg_t, int&));
+static_assert(!__is_nothrow_invocable_r(void, func_arg_t, const int&));
+static_assert(!__is_nothrow_invocable_r(void, func_arg_t, short));
+static_assert(!__is_nothrow_invocable_r(void, func_arg_t, char));
+static_assert(!__is_nothrow_invocable_r(void, func_arg_t, long));
+static_assert(!__is_nothrow_invocable_r(void, func_arg_t, double));
+static_assert(!__is_nothrow_invocable_r(void, func_arg_t, convertible_to_int));
+static_assert(!__is_nothrow_invocable_r(void, func_arg_t, explicitly_convertible_to_int));
+static_assert(__is_nothrow_invocable_r(void, func_arg_ref_t, int&));
+static_assert(!__is_nothrow_invocable_r(void, func_arg_ref_t, int));
+static_assert(!__is_nothrow_invocable_r(void, func_arg_ref_t, int&&));
+static_assert(!__is_nothrow_invocable_r(int, func_t));
+static_assert(!__is_nothrow_invocable_r(int, func_return_t));
+static_assert(!__is_nothrow_invocable_r(void, S));
+static_assert(!__is_nothrow_invocable_r(void, InvocableT));
+static_assert(!__is_nothrow_invocable_r(void, InvocableT, int));
+static_assert(__is_nothrow_invocable_r(void, InvocableT, int, int, T));
+static_assert(__is_nothrow_invocable_r(int, InvocableT, int, int, T));
+static_assert(__is_nothrow_invocable_r(U, InvocableT, int, int, T));
+static_assert(!__is_nothrow_invocable_r(V, InvocableT, int, int, T));
+static_assert(__is_nothrow_invocable_r(void, InvocableT&, int, int, T));
+static_assert(!__is_nothrow_invocable_r(T, InvocableT, int, int, T));
+static_assert(!__is_nothrow_invocable_r(T, InvocableT&, int, int, T));
+static_assert(!__is_nothrow_invocable_r(T, InvocableT&, const int&, int, T));
+static_assert(!__is_nothrow_invocable_r(void, InvocableT&&, T));
+static_assert(__is_nothrow_invocable_r(void, StaticInvocableT));
+static_assert(!__is_nothrow_invocable_r(void, StaticInvocableT, int));
diff --git a/libcxx/include/__type_traits/invoke.h b/libcxx/include/__type_traits/invoke.h
index c0487e25a0d8b7..5286d78bf60b9e 100644
--- a/libcxx/include/__type_traits/invoke.h
+++ b/libcxx/include/__type_traits/invoke.h
@@ -424,6 +424,22 @@ struct __invoke_void_return_wrapper<_Ret, true> {
// is_invocable
+#if __has_builtin(__is_invocable_r)
+
+template <class _Fn, class... _Args>
+struct _LIBCPP_TEMPLATE_VIS is_invocable : bool_constant<__is_invocable_r(void, _Fn, _Args...)> {};
+
+template <class _Ret, class _Fn, class... _Args>
+struct _LIBCPP_TEMPLATE_VIS is_invocable_r : bool_constant<__is_invocable_r(_Ret, _Fn, _Args...)> {};
+
+template <class _Fn, class... _Args>
+inline constexpr bool is_invocable_v = __is_invocable_r(void, _Fn, _Args...);
+
+template <class _Ret, class _Fn, class... _Args>
+inline constexpr bool is_invocable_r_v = __is_invocable_r(_Ret, _Fn, _Args...);
+
+#else
+
template <class _Fn, class... _Args>
struct _LIBCPP_TEMPLATE_VIS is_invocable : integral_constant<bool, __invokable<_Fn, _Args...>::value> {};
@@ -436,8 +452,26 @@ inline constexpr bool is_invocable_v = is_invocable<_Fn, _Args...>::value;
template <class _Ret, class _Fn, class... _Args>
inline constexpr bool is_invocable_r_v = is_invocable_r<_Ret, _Fn, _Args...>::value;
+#endif // __has_builtin(__is_invocable_r)
+
// is_nothrow_invocable
+#if __has_builtin(__is_nothrow_invocable_r)
+
+template <class _Fn, class... _Args>
+struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable : bool_constant<__is_nothrow_invocable_r(void, _Fn, _Args...)> {};
+
+template <class _Ret, class _Fn, class... _Args>
+struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable_r : bool_constant<__is_nothrow_invocable_r(_Ret, _Fn, _Args...)> {};
+
+template <class _Fn, class... _Args>
+inline constexpr bool is_nothrow_invocable_v = __is_nothrow_invocable_r(void, _Fn, _Args...);
+
+template <class _Ret, class _Fn, class... _Args>
+inline constexpr bool is_nothrow_invocable_r_v = __is_nothrow_invocable_r(_Ret, _Fn, _Args...);
+
+#else
+
template <class _Fn, class... _Args>
struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable : integral_constant<bool, __nothrow_invokable<_Fn, _Args...>::value> {
};
@@ -452,6 +486,8 @@ inline constexpr bool is_nothrow_invocable_v = is_nothrow_invocable<_Fn, _Args..
template <class _Ret, class _Fn, class... _Args>
inline constexpr bool is_nothrow_invocable_r_v = is_nothrow_invocable_r<_Ret, _Fn, _Args...>::value;
+#endif // __has_builtin(__is_nothrow_invocable_r)
+
template <class _Fn, class... _Args>
struct _LIBCPP_TEMPLATE_VIS invoke_result : __invoke_of<_Fn, _Args...> {};
More information about the libcxx-commits
mailing list