-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[Clang] Add __is_invocable_r and __is_nothrow_invocable_r #81213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Member
|
@llvm/pr-subscribers-libcxx @llvm/pr-subscribers-clang Author: Nikolas Klauser (philnik777) ChangesThis patch also uses the new builtins in libc++. Patch is 37.90 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/81213.diff 7 Files Affected:
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 { re...
[truncated]
|
You can test this locally with the following command:git-clang-format --diff 9cc2122bf5a81f7063c2a32b2cb78c8d615578a1 a6ef9191b3a68e68e7dd225bfb1e802f7542ccd9 -- clang/test/SemaCXX/type-traits-invocable.cpp clang/lib/Sema/SemaExprCXX.cpp libcxx/include/__type_traits/invoke.hView the diff from clang-format here.diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 6fca4b0edd..23a4c3f9ee 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5520,7 +5520,8 @@ static bool IsBaseOf(Sema &Self, QualType LhsT, QualType RhsT,
->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
}
-static bool IsConvertible(Sema& Self, QualType LhsT, QualType RhsT, SourceLocation KeyLoc, bool CheckNothrow) {
+static bool IsConvertible(Sema &Self, QualType LhsT, QualType RhsT,
+ SourceLocation KeyLoc, bool CheckNothrow) {
// C++0x [meta.rel]p4:
// Given the following function prototype:
//
@@ -5667,7 +5668,7 @@ static bool IsInvocable(Sema &S, SourceLocation KWLoc,
if (Object->isPointerType())
Object = Object->getPointeeType();
// bullets 2, 5 - ignore reference_wrapper
- else if (auto* RD = Object->getAsCXXRecordDecl()) {
+ 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();
@@ -5870,7 +5871,8 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
return IsInvocable(S, KWLoc, Args, RParenLoc,
Kind == TT_IsNothrowInvocableR);
- default: llvm_unreachable("not a TT");
+ default:
+ llvm_unreachable("not a TT");
}
return false;
diff --git a/libcxx/include/__type_traits/invoke.h b/libcxx/include/__type_traits/invoke.h
index d2a73d8631..0920539c71 100644
--- a/libcxx/include/__type_traits/invoke.h
+++ b/libcxx/include/__type_traits/invoke.h
@@ -357,8 +357,8 @@ struct __invokable_r {
using _Result = decltype(__try_call<_Fp, _Args...>(0));
using type = __conditional_t<_IsNotSame<_Result, __nat>::value,
- __conditional_t<is_void<_Ret>::value, true_type, __is_core_convertible<_Result, _Ret> >,
- false_type>;
+ __conditional_t<is_void<_Ret>::value, true_type, __is_core_convertible<_Result, _Ret> >,
+ false_type>;
static const bool value = type::value;
};
template <class _Fp, class... _Args>
|
8657d0d to
b0b0072
Compare
This patch also uses the new builtins in libc++.
b0b0072 to
a6ef919
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
clang:frontend
Language frontend issues, e.g. anything involving "Sema"
clang
Clang issues not falling into any other category
libc++
libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This patch also uses the new builtins in libc++.