[clang] [Clang][NFC] Move the type trait logic to a separate file. (PR #141245)
via cfe-commits
cfe-commits at lists.llvm.org
Fri May 23 08:53:16 PDT 2025
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/141245
>From 02186cea131a735c29e08541c0d1f6a4bce6b6f0 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 23 May 2025 17:43:49 +0200
Subject: [PATCH 1/2] [Clang][NFC] Move the type trait logic to a separate
file.
Just to try to keep the size of SemaExprCXX.cpp in check.
As discussed in #141238
---
clang/lib/Sema/CMakeLists.txt | 1 +
clang/lib/Sema/SemaDeclCXX.cpp | 273 ----
clang/lib/Sema/SemaExprCXX.cpp | 1621 ------------------------
clang/lib/Sema/SemaTypeTraits.cpp | 1919 +++++++++++++++++++++++++++++
4 files changed, 1920 insertions(+), 1894 deletions(-)
create mode 100644 clang/lib/Sema/SemaTypeTraits.cpp
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 4b87004e4b8ea..51e0ee10b080b 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -96,6 +96,7 @@ add_clang_library(clangSema
SemaTemplateInstantiateDecl.cpp
SemaTemplateVariadic.cpp
SemaType.cpp
+ SemaTypeTraits.cpp
SemaWasm.cpp
SemaX86.cpp
TypeLocBuilder.cpp
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index fe92191b6a687..ead53a995dff1 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7368,279 +7368,6 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
CheckMismatchedTypeAwareAllocators(OO_Array_New, OO_Array_Delete);
}
-static CXXMethodDecl *LookupSpecialMemberFromXValue(Sema &SemaRef,
- const CXXRecordDecl *RD,
- bool Assign) {
- RD = RD->getDefinition();
- SourceLocation LookupLoc = RD->getLocation();
-
- CanQualType CanTy = SemaRef.getASTContext().getCanonicalType(
- SemaRef.getASTContext().getTagDeclType(RD));
- DeclarationName Name;
- Expr *Arg = nullptr;
- unsigned NumArgs;
-
- QualType ArgType = CanTy;
- ExprValueKind VK = clang::VK_XValue;
-
- if (Assign)
- Name =
- SemaRef.getASTContext().DeclarationNames.getCXXOperatorName(OO_Equal);
- else
- Name =
- SemaRef.getASTContext().DeclarationNames.getCXXConstructorName(CanTy);
-
- OpaqueValueExpr FakeArg(LookupLoc, ArgType, VK);
- NumArgs = 1;
- Arg = &FakeArg;
-
- // Create the object argument
- QualType ThisTy = CanTy;
- Expr::Classification Classification =
- OpaqueValueExpr(LookupLoc, ThisTy, VK_LValue)
- .Classify(SemaRef.getASTContext());
-
- // Now we perform lookup on the name we computed earlier and do overload
- // resolution. Lookup is only performed directly into the class since there
- // will always be a (possibly implicit) declaration to shadow any others.
- OverloadCandidateSet OCS(LookupLoc, OverloadCandidateSet::CSK_Normal);
- DeclContext::lookup_result R = RD->lookup(Name);
-
- if (R.empty())
- return nullptr;
-
- // Copy the candidates as our processing of them may load new declarations
- // from an external source and invalidate lookup_result.
- SmallVector<NamedDecl *, 8> Candidates(R.begin(), R.end());
-
- for (NamedDecl *CandDecl : Candidates) {
- if (CandDecl->isInvalidDecl())
- continue;
-
- DeclAccessPair Cand = DeclAccessPair::make(CandDecl, clang::AS_none);
- auto CtorInfo = getConstructorInfo(Cand);
- if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(Cand->getUnderlyingDecl())) {
- if (Assign)
- SemaRef.AddMethodCandidate(M, Cand, const_cast<CXXRecordDecl *>(RD),
- ThisTy, Classification,
- llvm::ArrayRef(&Arg, NumArgs), OCS, true);
- else {
- assert(CtorInfo);
- SemaRef.AddOverloadCandidate(CtorInfo.Constructor, CtorInfo.FoundDecl,
- llvm::ArrayRef(&Arg, NumArgs), OCS,
- /*SuppressUserConversions*/ true);
- }
- } else if (FunctionTemplateDecl *Tmpl =
- dyn_cast<FunctionTemplateDecl>(Cand->getUnderlyingDecl())) {
- if (Assign)
- SemaRef.AddMethodTemplateCandidate(
- Tmpl, Cand, const_cast<CXXRecordDecl *>(RD), nullptr, ThisTy,
- Classification, llvm::ArrayRef(&Arg, NumArgs), OCS, true);
- else {
- assert(CtorInfo);
- SemaRef.AddTemplateOverloadCandidate(
- CtorInfo.ConstructorTmpl, CtorInfo.FoundDecl, nullptr,
- llvm::ArrayRef(&Arg, NumArgs), OCS, true);
- }
- }
- }
-
- OverloadCandidateSet::iterator Best;
- switch (OCS.BestViableFunction(SemaRef, LookupLoc, Best)) {
- case OR_Success:
- return cast<CXXMethodDecl>(Best->Function);
- default:
- return nullptr;
- }
-}
-
-static bool hasSuitableConstructorForRelocation(Sema &SemaRef,
- const CXXRecordDecl *D,
- bool AllowUserDefined) {
- assert(D->hasDefinition() && !D->isInvalidDecl());
-
- if (D->hasSimpleMoveConstructor() || D->hasSimpleCopyConstructor())
- return true;
-
- CXXMethodDecl *Decl =
- LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/false);
- return Decl && Decl->isUserProvided() == AllowUserDefined;
-}
-
-static bool hasSuitableMoveAssignmentOperatorForRelocation(
- Sema &SemaRef, const CXXRecordDecl *D, bool AllowUserDefined) {
- assert(D->hasDefinition() && !D->isInvalidDecl());
-
- if (D->hasSimpleMoveAssignment() || D->hasSimpleCopyAssignment())
- return true;
-
- CXXMethodDecl *Decl =
- LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/true);
- if (!Decl)
- return false;
-
- return Decl && Decl->isUserProvided() == AllowUserDefined;
-}
-
-// [C++26][class.prop]
-// A class C is default-movable if
-// - overload resolution for direct-initializing an object of type C
-// from an xvalue of type C selects a constructor that is a direct member of C
-// and is neither user-provided nor deleted,
-// - overload resolution for assigning to an lvalue of type C from an xvalue of
-// type C selects an assignment operator function that is a direct member of C
-// and is neither user-provided nor deleted, and C has a destructor that is
-// neither user-provided nor deleted.
-static bool IsDefaultMovable(Sema &SemaRef, const CXXRecordDecl *D) {
- if (!hasSuitableConstructorForRelocation(SemaRef, D,
- /*AllowUserDefined=*/false))
- return false;
-
- if (!hasSuitableMoveAssignmentOperatorForRelocation(
- SemaRef, D, /*AllowUserDefined=*/false))
- return false;
-
- CXXDestructorDecl *Dtr = D->getDestructor();
-
- if (!Dtr)
- return true;
-
- if (Dtr->isUserProvided() && (!Dtr->isDefaulted() || Dtr->isDeleted()))
- return false;
-
- return !Dtr->isDeleted();
-}
-
-// [C++26][class.prop]
-// A class is eligible for trivial relocation unless it...
-static bool IsEligibleForTrivialRelocation(Sema &SemaRef,
- const CXXRecordDecl *D) {
-
- for (const CXXBaseSpecifier &B : D->bases()) {
- const auto *BaseDecl = B.getType()->getAsCXXRecordDecl();
- if (!BaseDecl)
- continue;
- // ... has any virtual base classes
- // ... has a base class that is not a trivially relocatable class
- if (B.isVirtual() || (!BaseDecl->isDependentType() &&
- !SemaRef.IsCXXTriviallyRelocatableType(B.getType())))
- return false;
- }
-
- for (const FieldDecl *Field : D->fields()) {
- if (Field->getType()->isDependentType())
- continue;
- if (Field->getType()->isReferenceType())
- continue;
- // ... has a non-static data member of an object type that is not
- // of a trivially relocatable type
- if (!SemaRef.IsCXXTriviallyRelocatableType(Field->getType()))
- return false;
- }
- return !D->hasDeletedDestructor();
-}
-
-// [C++26][class.prop]
-// A class C is eligible for replacement unless
-static bool IsEligibleForReplacement(Sema &SemaRef, const CXXRecordDecl *D) {
-
- for (const CXXBaseSpecifier &B : D->bases()) {
- const auto *BaseDecl = B.getType()->getAsCXXRecordDecl();
- if (!BaseDecl)
- continue;
- // it has a base class that is not a replaceable class
- if (!BaseDecl->isDependentType() &&
- !SemaRef.IsCXXReplaceableType(B.getType()))
- return false;
- }
-
- for (const FieldDecl *Field : D->fields()) {
- if (Field->getType()->isDependentType())
- continue;
-
- // it has a non-static data member that is not of a replaceable type,
- if (!SemaRef.IsCXXReplaceableType(Field->getType()))
- return false;
- }
- return !D->hasDeletedDestructor();
-}
-
-ASTContext::CXXRecordDeclRelocationInfo
-Sema::CheckCXX2CRelocatableAndReplaceable(const CXXRecordDecl *D) {
- ASTContext::CXXRecordDeclRelocationInfo Info{false, false};
-
- if (!getLangOpts().CPlusPlus || D->isInvalidDecl())
- return Info;
-
- assert(D->hasDefinition());
-
- // This is part of "eligible for replacement", however we defer it
- // to avoid extraneous computations.
- auto HasSuitableSMP = [&] {
- return hasSuitableConstructorForRelocation(*this, D,
- /*AllowUserDefined=*/true) &&
- hasSuitableMoveAssignmentOperatorForRelocation(
- *this, D, /*AllowUserDefined=*/true);
- };
-
- auto IsUnion = [&, Is = std::optional<bool>{}]() mutable {
- if (!Is.has_value())
- Is = D->isUnion() && !D->hasUserDeclaredCopyConstructor() &&
- !D->hasUserDeclaredCopyAssignment() &&
- !D->hasUserDeclaredMoveOperation() &&
- !D->hasUserDeclaredDestructor();
- return *Is;
- };
-
- auto IsDefaultMovable = [&, Is = std::optional<bool>{}]() mutable {
- if (!Is.has_value())
- Is = ::IsDefaultMovable(*this, D);
- return *Is;
- };
-
- Info.IsRelocatable = [&] {
- if (D->isDependentType())
- return false;
-
- // if it is eligible for trivial relocation
- if (!IsEligibleForTrivialRelocation(*this, D))
- return false;
-
- // has the trivially_relocatable_if_eligible class-property-specifier,
- if (D->hasAttr<TriviallyRelocatableAttr>())
- return true;
-
- // is a union with no user-declared special member functions, or
- if (IsUnion())
- return true;
-
- // is default-movable.
- return IsDefaultMovable();
- }();
-
- Info.IsReplaceable = [&] {
- if (D->isDependentType())
- return false;
-
- // A class C is a replaceable class if it is eligible for replacement
- if (!IsEligibleForReplacement(*this, D))
- return false;
-
- // has the replaceable_if_eligible class-property-specifier
- if (D->hasAttr<ReplaceableAttr>())
- return HasSuitableSMP();
-
- // is a union with no user-declared special member functions, or
- if (IsUnion())
- return HasSuitableSMP();
-
- // is default-movable.
- return IsDefaultMovable();
- }();
-
- return Info;
-}
-
/// Look up the special member function that would be called by a special
/// member function for a subobject of class type.
///
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index b53877c40668d..63cf324ddb1f9 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -30,7 +30,6 @@
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TokenKinds.h"
-#include "clang/Basic/TypeTraits.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
@@ -49,7 +48,6 @@
#include "clang/Sema/TemplateDeduction.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/STLForwardCompat.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/TypeSize.h"
@@ -5294,1625 +5292,6 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
return From;
}
-/// Checks that type T is not a VLA.
-///
-/// @returns @c true if @p T is VLA and a diagnostic was emitted,
-/// @c false otherwise.
-static bool DiagnoseVLAInCXXTypeTrait(Sema &S, const TypeSourceInfo *T,
- clang::tok::TokenKind TypeTraitID) {
- if (!T->getType()->isVariableArrayType())
- return false;
-
- S.Diag(T->getTypeLoc().getBeginLoc(), diag::err_vla_unsupported)
- << 1 << TypeTraitID;
- return true;
-}
-
-/// Checks that type T is not an atomic type (_Atomic).
-///
-/// @returns @c true if @p T is VLA and a diagnostic was emitted,
-/// @c false otherwise.
-static bool DiagnoseAtomicInCXXTypeTrait(Sema &S, const TypeSourceInfo *T,
- clang::tok::TokenKind TypeTraitID) {
- if (!T->getType()->isAtomicType())
- return false;
-
- S.Diag(T->getTypeLoc().getBeginLoc(), diag::err_atomic_unsupported)
- << TypeTraitID;
- return true;
-}
-
-/// Check the completeness of a type in a unary type trait.
-///
-/// If the particular type trait requires a complete type, tries to complete
-/// it. If completing the type fails, a diagnostic is emitted and false
-/// returned. If completing the type succeeds or no completion was required,
-/// returns true.
-static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
- SourceLocation Loc,
- QualType ArgTy) {
- // C++0x [meta.unary.prop]p3:
- // For all of the class templates X declared in this Clause, instantiating
- // that template with a template argument that is a class template
- // specialization may result in the implicit instantiation of the template
- // argument if and only if the semantics of X require that the argument
- // must be a complete type.
- // We apply this rule to all the type trait expressions used to implement
- // these class templates. We also try to follow any GCC documented behavior
- // in these expressions to ensure portability of standard libraries.
- switch (UTT) {
- default: llvm_unreachable("not a UTT");
- // is_complete_type somewhat obviously cannot require a complete type.
- case UTT_IsCompleteType:
- // Fall-through
-
- // These traits are modeled on the type predicates in C++0x
- // [meta.unary.cat] and [meta.unary.comp]. They are not specified as
- // requiring a complete type, as whether or not they return true cannot be
- // impacted by the completeness of the type.
- case UTT_IsVoid:
- case UTT_IsIntegral:
- case UTT_IsFloatingPoint:
- case UTT_IsArray:
- case UTT_IsBoundedArray:
- case UTT_IsPointer:
- case UTT_IsLvalueReference:
- case UTT_IsRvalueReference:
- case UTT_IsMemberFunctionPointer:
- case UTT_IsMemberObjectPointer:
- case UTT_IsEnum:
- case UTT_IsScopedEnum:
- case UTT_IsUnion:
- case UTT_IsClass:
- case UTT_IsFunction:
- case UTT_IsReference:
- case UTT_IsArithmetic:
- case UTT_IsFundamental:
- case UTT_IsObject:
- case UTT_IsScalar:
- case UTT_IsCompound:
- case UTT_IsMemberPointer:
- case UTT_IsTypedResourceElementCompatible:
- // Fall-through
-
- // These traits are modeled on type predicates in C++0x [meta.unary.prop]
- // which requires some of its traits to have the complete type. However,
- // the completeness of the type cannot impact these traits' semantics, and
- // so they don't require it. This matches the comments on these traits in
- // Table 49.
- case UTT_IsConst:
- case UTT_IsVolatile:
- case UTT_IsSigned:
- case UTT_IsUnboundedArray:
- case UTT_IsUnsigned:
-
- // This type trait always returns false, checking the type is moot.
- case UTT_IsInterfaceClass:
- return true;
-
- // We diagnose incomplete class types later.
- case UTT_StructuredBindingSize:
- return true;
-
- // C++14 [meta.unary.prop]:
- // If T is a non-union class type, T shall be a complete type.
- case UTT_IsEmpty:
- case UTT_IsPolymorphic:
- case UTT_IsAbstract:
- if (const auto *RD = ArgTy->getAsCXXRecordDecl())
- if (!RD->isUnion())
- return !S.RequireCompleteType(
- Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
- return true;
-
- // C++14 [meta.unary.prop]:
- // If T is a class type, T shall be a complete type.
- case UTT_IsFinal:
- case UTT_IsSealed:
- if (ArgTy->getAsCXXRecordDecl())
- return !S.RequireCompleteType(
- Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
- return true;
-
- // LWG3823: T shall be an array type, a complete type, or cv void.
- case UTT_IsAggregate:
- case UTT_IsImplicitLifetime:
- if (ArgTy->isArrayType() || ArgTy->isVoidType())
- return true;
-
- return !S.RequireCompleteType(
- Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
-
- // has_unique_object_representations<T>
- // remove_all_extents_t<T> shall be a complete type or cv void (LWG4113).
- case UTT_HasUniqueObjectRepresentations:
- ArgTy = QualType(ArgTy->getBaseElementTypeUnsafe(), 0);
- if (ArgTy->isVoidType())
- return true;
- return !S.RequireCompleteType(
- Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
-
- // C++1z [meta.unary.prop]:
- // remove_all_extents_t<T> shall be a complete type or cv void.
- case UTT_IsTrivial:
- case UTT_IsTriviallyCopyable:
- case UTT_IsStandardLayout:
- case UTT_IsPOD:
- case UTT_IsLiteral:
- case UTT_IsBitwiseCloneable:
- // By analogy, is_trivially_relocatable and is_trivially_equality_comparable
- // impose the same constraints.
- case UTT_IsTriviallyRelocatable:
- case UTT_IsTriviallyEqualityComparable:
- case UTT_IsCppTriviallyRelocatable:
- case UTT_IsReplaceable:
- case UTT_CanPassInRegs:
- // Per the GCC type traits documentation, T shall be a complete type, cv void,
- // or an array of unknown bound. But GCC actually imposes the same constraints
- // as above.
- case UTT_HasNothrowAssign:
- case UTT_HasNothrowMoveAssign:
- case UTT_HasNothrowConstructor:
- case UTT_HasNothrowCopy:
- case UTT_HasTrivialAssign:
- case UTT_HasTrivialMoveAssign:
- case UTT_HasTrivialDefaultConstructor:
- case UTT_HasTrivialMoveConstructor:
- case UTT_HasTrivialCopy:
- case UTT_HasTrivialDestructor:
- case UTT_HasVirtualDestructor:
- ArgTy = QualType(ArgTy->getBaseElementTypeUnsafe(), 0);
- [[fallthrough]];
- // C++1z [meta.unary.prop]:
- // T shall be a complete type, cv void, or an array of unknown bound.
- case UTT_IsDestructible:
- case UTT_IsNothrowDestructible:
- case UTT_IsTriviallyDestructible:
- case UTT_IsIntangibleType:
- if (ArgTy->isIncompleteArrayType() || ArgTy->isVoidType())
- return true;
-
- return !S.RequireCompleteType(
- Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
- }
-}
-
-static bool HasNoThrowOperator(const RecordType *RT, OverloadedOperatorKind Op,
- Sema &Self, SourceLocation KeyLoc, ASTContext &C,
- bool (CXXRecordDecl::*HasTrivial)() const,
- bool (CXXRecordDecl::*HasNonTrivial)() const,
- bool (CXXMethodDecl::*IsDesiredOp)() const)
-{
- CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
- if ((RD->*HasTrivial)() && !(RD->*HasNonTrivial)())
- return true;
-
- DeclarationName Name = C.DeclarationNames.getCXXOperatorName(Op);
- DeclarationNameInfo NameInfo(Name, KeyLoc);
- LookupResult Res(Self, NameInfo, Sema::LookupOrdinaryName);
- if (Self.LookupQualifiedName(Res, RD)) {
- bool FoundOperator = false;
- Res.suppressDiagnostics();
- for (LookupResult::iterator Op = Res.begin(), OpEnd = Res.end();
- Op != OpEnd; ++Op) {
- if (isa<FunctionTemplateDecl>(*Op))
- continue;
-
- CXXMethodDecl *Operator = cast<CXXMethodDecl>(*Op);
- if((Operator->*IsDesiredOp)()) {
- FoundOperator = true;
- auto *CPT = Operator->getType()->castAs<FunctionProtoType>();
- CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
- if (!CPT || !CPT->isNothrow())
- return false;
- }
- }
- return FoundOperator;
- }
- return false;
-}
-
-static bool HasNonDeletedDefaultedEqualityComparison(Sema &S,
- const CXXRecordDecl *Decl,
- SourceLocation KeyLoc) {
- if (Decl->isUnion())
- return false;
- if (Decl->isLambda())
- return Decl->isCapturelessLambda();
-
- {
- EnterExpressionEvaluationContext UnevaluatedContext(
- S, Sema::ExpressionEvaluationContext::Unevaluated);
- Sema::SFINAETrap SFINAE(S, /*ForValidityCheck=*/true);
- Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());
-
- // const ClassT& obj;
- OpaqueValueExpr Operand(
- KeyLoc,
- Decl->getTypeForDecl()->getCanonicalTypeUnqualified().withConst(),
- ExprValueKind::VK_LValue);
- UnresolvedSet<16> Functions;
- // obj == obj;
- S.LookupBinOp(S.TUScope, {}, BinaryOperatorKind::BO_EQ, Functions);
-
- auto Result = S.CreateOverloadedBinOp(KeyLoc, BinaryOperatorKind::BO_EQ,
- Functions, &Operand, &Operand);
- if (Result.isInvalid() || SFINAE.hasErrorOccurred())
- return false;
-
- const auto *CallExpr = dyn_cast<CXXOperatorCallExpr>(Result.get());
- if (!CallExpr)
- return false;
- const auto *Callee = CallExpr->getDirectCallee();
- auto ParamT = Callee->getParamDecl(0)->getType();
- if (!Callee->isDefaulted())
- return false;
- if (!ParamT->isReferenceType() && !Decl->isTriviallyCopyable())
- return false;
- if (ParamT.getNonReferenceType()->getUnqualifiedDesugaredType() !=
- Decl->getTypeForDecl())
- return false;
- }
-
- return llvm::all_of(Decl->bases(),
- [&](const CXXBaseSpecifier &BS) {
- if (const auto *RD = BS.getType()->getAsCXXRecordDecl())
- return HasNonDeletedDefaultedEqualityComparison(
- S, RD, KeyLoc);
- return true;
- }) &&
- llvm::all_of(Decl->fields(), [&](const FieldDecl *FD) {
- auto Type = FD->getType();
- if (Type->isArrayType())
- Type = Type->getBaseElementTypeUnsafe()
- ->getCanonicalTypeUnqualified();
-
- if (Type->isReferenceType() || Type->isEnumeralType())
- return false;
- if (const auto *RD = Type->getAsCXXRecordDecl())
- return HasNonDeletedDefaultedEqualityComparison(S, RD, KeyLoc);
- return true;
- });
-}
-
-static bool isTriviallyEqualityComparableType(Sema &S, QualType Type, SourceLocation KeyLoc) {
- QualType CanonicalType = Type.getCanonicalType();
- if (CanonicalType->isIncompleteType() || CanonicalType->isDependentType() ||
- CanonicalType->isEnumeralType() || CanonicalType->isArrayType())
- return false;
-
- if (const auto *RD = CanonicalType->getAsCXXRecordDecl()) {
- if (!HasNonDeletedDefaultedEqualityComparison(S, RD, KeyLoc))
- return false;
- }
-
- return S.getASTContext().hasUniqueObjectRepresentations(
- CanonicalType, /*CheckIfTriviallyCopyable=*/false);
-}
-
-static bool IsCXXTriviallyRelocatableType(Sema &S, const CXXRecordDecl *RD) {
- if (std::optional<ASTContext::CXXRecordDeclRelocationInfo> Info =
- S.getASTContext().getRelocationInfoForCXXRecord(RD))
- return Info->IsRelocatable;
- ASTContext::CXXRecordDeclRelocationInfo Info =
- S.CheckCXX2CRelocatableAndReplaceable(RD);
- S.getASTContext().setRelocationInfoForCXXRecord(RD, Info);
- return Info.IsRelocatable;
-}
-
-bool Sema::IsCXXTriviallyRelocatableType(QualType Type) {
-
- QualType BaseElementType = getASTContext().getBaseElementType(Type);
-
- if (Type->isVariableArrayType())
- return false;
-
- if (BaseElementType.hasNonTrivialObjCLifetime())
- return false;
-
- if (BaseElementType.hasAddressDiscriminatedPointerAuth())
- return false;
-
- if (BaseElementType->isIncompleteType())
- return false;
-
- if (BaseElementType->isScalarType() || BaseElementType->isVectorType())
- return true;
-
- if (const auto *RD = BaseElementType->getAsCXXRecordDecl())
- return ::IsCXXTriviallyRelocatableType(*this, RD);
-
- return false;
-}
-
-static bool IsCXXReplaceableType(Sema &S, const CXXRecordDecl *RD) {
- if (std::optional<ASTContext::CXXRecordDeclRelocationInfo> Info =
- S.getASTContext().getRelocationInfoForCXXRecord(RD))
- return Info->IsReplaceable;
- ASTContext::CXXRecordDeclRelocationInfo Info =
- S.CheckCXX2CRelocatableAndReplaceable(RD);
- S.getASTContext().setRelocationInfoForCXXRecord(RD, Info);
- return Info.IsReplaceable;
-}
-
-bool Sema::IsCXXReplaceableType(QualType Type) {
- if (Type.isConstQualified() || Type.isVolatileQualified())
- return false;
-
- if (Type->isVariableArrayType())
- return false;
-
- QualType BaseElementType =
- getASTContext().getBaseElementType(Type.getUnqualifiedType());
- if (BaseElementType->isIncompleteType())
- return false;
- if (BaseElementType->isScalarType())
- return true;
- if (const auto *RD = BaseElementType->getAsCXXRecordDecl())
- return ::IsCXXReplaceableType(*this, RD);
- return false;
-}
-
-static bool IsTriviallyRelocatableType(Sema &SemaRef, QualType T) {
- QualType BaseElementType = SemaRef.getASTContext().getBaseElementType(T);
-
- if (BaseElementType->isIncompleteType())
- return false;
- if (!BaseElementType->isObjectType())
- return false;
-
- if (T.hasAddressDiscriminatedPointerAuth())
- return false;
-
- if (const auto *RD = BaseElementType->getAsCXXRecordDecl();
- RD && !RD->isPolymorphic() && IsCXXTriviallyRelocatableType(SemaRef, RD))
- return true;
-
- if (const auto *RD = BaseElementType->getAsRecordDecl())
- return RD->canPassInRegisters();
-
- if (BaseElementType.isTriviallyCopyableType(SemaRef.getASTContext()))
- return true;
-
- switch (T.isNonTrivialToPrimitiveDestructiveMove()) {
- case QualType::PCK_Trivial:
- return !T.isDestructedType();
- case QualType::PCK_ARCStrong:
- return true;
- default:
- return false;
- }
-}
-
-static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
- SourceLocation KeyLoc,
- TypeSourceInfo *TInfo) {
- QualType T = TInfo->getType();
- assert(!T->isDependentType() && "Cannot evaluate traits of dependent type");
-
- ASTContext &C = Self.Context;
- switch(UTT) {
- default: llvm_unreachable("not a UTT");
- // Type trait expressions corresponding to the primary type category
- // predicates in C++0x [meta.unary.cat].
- case UTT_IsVoid:
- return T->isVoidType();
- case UTT_IsIntegral:
- return T->isIntegralType(C);
- case UTT_IsFloatingPoint:
- return T->isFloatingType();
- case UTT_IsArray:
- // Zero-sized arrays aren't considered arrays in partial specializations,
- // so __is_array shouldn't consider them arrays either.
- if (const auto *CAT = C.getAsConstantArrayType(T))
- return CAT->getSize() != 0;
- return T->isArrayType();
- case UTT_IsBoundedArray:
- if (DiagnoseVLAInCXXTypeTrait(Self, TInfo, tok::kw___is_bounded_array))
- return false;
- // Zero-sized arrays aren't considered arrays in partial specializations,
- // so __is_bounded_array shouldn't consider them arrays either.
- if (const auto *CAT = C.getAsConstantArrayType(T))
- return CAT->getSize() != 0;
- return T->isArrayType() && !T->isIncompleteArrayType();
- case UTT_IsUnboundedArray:
- if (DiagnoseVLAInCXXTypeTrait(Self, TInfo, tok::kw___is_unbounded_array))
- return false;
- return T->isIncompleteArrayType();
- case UTT_IsPointer:
- return T->isAnyPointerType();
- case UTT_IsLvalueReference:
- return T->isLValueReferenceType();
- case UTT_IsRvalueReference:
- return T->isRValueReferenceType();
- case UTT_IsMemberFunctionPointer:
- return T->isMemberFunctionPointerType();
- case UTT_IsMemberObjectPointer:
- return T->isMemberDataPointerType();
- case UTT_IsEnum:
- return T->isEnumeralType();
- case UTT_IsScopedEnum:
- return T->isScopedEnumeralType();
- case UTT_IsUnion:
- return T->isUnionType();
- case UTT_IsClass:
- return T->isClassType() || T->isStructureType() || T->isInterfaceType();
- case UTT_IsFunction:
- return T->isFunctionType();
-
- // Type trait expressions which correspond to the convenient composition
- // predicates in C++0x [meta.unary.comp].
- case UTT_IsReference:
- return T->isReferenceType();
- case UTT_IsArithmetic:
- return T->isArithmeticType() && !T->isEnumeralType();
- case UTT_IsFundamental:
- return T->isFundamentalType();
- case UTT_IsObject:
- return T->isObjectType();
- case UTT_IsScalar:
- // Note: semantic analysis depends on Objective-C lifetime types to be
- // considered scalar types. However, such types do not actually behave
- // like scalar types at run time (since they may require retain/release
- // operations), so we report them as non-scalar.
- if (T->isObjCLifetimeType()) {
- switch (T.getObjCLifetime()) {
- case Qualifiers::OCL_None:
- case Qualifiers::OCL_ExplicitNone:
- return true;
-
- case Qualifiers::OCL_Strong:
- case Qualifiers::OCL_Weak:
- case Qualifiers::OCL_Autoreleasing:
- return false;
- }
- }
-
- return T->isScalarType();
- case UTT_IsCompound:
- return T->isCompoundType();
- case UTT_IsMemberPointer:
- return T->isMemberPointerType();
-
- // Type trait expressions which correspond to the type property predicates
- // in C++0x [meta.unary.prop].
- case UTT_IsConst:
- return T.isConstQualified();
- case UTT_IsVolatile:
- return T.isVolatileQualified();
- case UTT_IsTrivial:
- return T.isTrivialType(C);
- case UTT_IsTriviallyCopyable:
- return T.isTriviallyCopyableType(C);
- case UTT_IsStandardLayout:
- return T->isStandardLayoutType();
- case UTT_IsPOD:
- return T.isPODType(C);
- case UTT_IsLiteral:
- return T->isLiteralType(C);
- case UTT_IsEmpty:
- if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
- return !RD->isUnion() && RD->isEmpty();
- return false;
- case UTT_IsPolymorphic:
- if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
- return !RD->isUnion() && RD->isPolymorphic();
- return false;
- case UTT_IsAbstract:
- if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
- return !RD->isUnion() && RD->isAbstract();
- return false;
- case UTT_IsAggregate:
- // Report vector extensions and complex types as aggregates because they
- // support aggregate initialization. GCC mirrors this behavior for vectors
- // but not _Complex.
- return T->isAggregateType() || T->isVectorType() || T->isExtVectorType() ||
- T->isAnyComplexType();
- // __is_interface_class only returns true when CL is invoked in /CLR mode and
- // even then only when it is used with the 'interface struct ...' syntax
- // Clang doesn't support /CLR which makes this type trait moot.
- case UTT_IsInterfaceClass:
- return false;
- case UTT_IsFinal:
- case UTT_IsSealed:
- if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
- return RD->hasAttr<FinalAttr>();
- return false;
- case UTT_IsSigned:
- // Enum types should always return false.
- // Floating points should always return true.
- return T->isFloatingType() ||
- (T->isSignedIntegerType() && !T->isEnumeralType());
- case UTT_IsUnsigned:
- // Enum types should always return false.
- return T->isUnsignedIntegerType() && !T->isEnumeralType();
-
- // Type trait expressions which query classes regarding their construction,
- // destruction, and copying. Rather than being based directly on the
- // related type predicates in the standard, they are specified by both
- // GCC[1] and the Embarcadero C++ compiler[2], and Clang implements those
- // specifications.
- //
- // 1: http://gcc.gnu/.org/onlinedocs/gcc/Type-Traits.html
- // 2: http://docwiki.embarcadero.com/RADStudio/XE/en/Type_Trait_Functions_(C%2B%2B0x)_Index
- //
- // Note that these builtins do not behave as documented in g++: if a class
- // has both a trivial and a non-trivial special member of a particular kind,
- // they return false! For now, we emulate this behavior.
- // FIXME: This appears to be a g++ bug: more complex cases reveal that it
- // does not correctly compute triviality in the presence of multiple special
- // members of the same kind. Revisit this once the g++ bug is fixed.
- case UTT_HasTrivialDefaultConstructor:
- // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
- // If __is_pod (type) is true then the trait is true, else if type is
- // a cv class or union type (or array thereof) with a trivial default
- // constructor ([class.ctor]) then the trait is true, else it is false.
- if (T.isPODType(C))
- return true;
- if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl())
- return RD->hasTrivialDefaultConstructor() &&
- !RD->hasNonTrivialDefaultConstructor();
- return false;
- case UTT_HasTrivialMoveConstructor:
- // This trait is implemented by MSVC 2012 and needed to parse the
- // standard library headers. Specifically this is used as the logic
- // behind std::is_trivially_move_constructible (20.9.4.3).
- if (T.isPODType(C))
- return true;
- if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl())
- return RD->hasTrivialMoveConstructor() && !RD->hasNonTrivialMoveConstructor();
- return false;
- case UTT_HasTrivialCopy:
- // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
- // If __is_pod (type) is true or type is a reference type then
- // the trait is true, else if type is a cv class or union type
- // with a trivial copy constructor ([class.copy]) then the trait
- // is true, else it is false.
- if (T.isPODType(C) || T->isReferenceType())
- return true;
- if (CXXRecordDecl *RD = T->getAsCXXRecordDecl())
- return RD->hasTrivialCopyConstructor() &&
- !RD->hasNonTrivialCopyConstructor();
- return false;
- case UTT_HasTrivialMoveAssign:
- // This trait is implemented by MSVC 2012 and needed to parse the
- // standard library headers. Specifically it is used as the logic
- // behind std::is_trivially_move_assignable (20.9.4.3)
- if (T.isPODType(C))
- return true;
- if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl())
- return RD->hasTrivialMoveAssignment() && !RD->hasNonTrivialMoveAssignment();
- return false;
- case UTT_HasTrivialAssign:
- // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
- // If type is const qualified or is a reference type then the
- // trait is false. Otherwise if __is_pod (type) is true then the
- // trait is true, else if type is a cv class or union type with
- // a trivial copy assignment ([class.copy]) then the trait is
- // true, else it is false.
- // Note: the const and reference restrictions are interesting,
- // given that const and reference members don't prevent a class
- // from having a trivial copy assignment operator (but do cause
- // errors if the copy assignment operator is actually used, q.v.
- // [class.copy]p12).
-
- if (T.isConstQualified())
- return false;
- if (T.isPODType(C))
- return true;
- if (CXXRecordDecl *RD = T->getAsCXXRecordDecl())
- return RD->hasTrivialCopyAssignment() &&
- !RD->hasNonTrivialCopyAssignment();
- return false;
- case UTT_IsDestructible:
- case UTT_IsTriviallyDestructible:
- case UTT_IsNothrowDestructible:
- // C++14 [meta.unary.prop]:
- // For reference types, is_destructible<T>::value is true.
- if (T->isReferenceType())
- return true;
-
- // Objective-C++ ARC: autorelease types don't require destruction.
- if (T->isObjCLifetimeType() &&
- T.getObjCLifetime() == Qualifiers::OCL_Autoreleasing)
- return true;
-
- // C++14 [meta.unary.prop]:
- // For incomplete types and function types, is_destructible<T>::value is
- // false.
- if (T->isIncompleteType() || T->isFunctionType())
- return false;
-
- // A type that requires destruction (via a non-trivial destructor or ARC
- // lifetime semantics) is not trivially-destructible.
- if (UTT == UTT_IsTriviallyDestructible && T.isDestructedType())
- return false;
-
- // C++14 [meta.unary.prop]:
- // For object types and given U equal to remove_all_extents_t<T>, if the
- // expression std::declval<U&>().~U() is well-formed when treated as an
- // unevaluated operand (Clause 5), then is_destructible<T>::value is true
- if (auto *RD = C.getBaseElementType(T)->getAsCXXRecordDecl()) {
- CXXDestructorDecl *Destructor = Self.LookupDestructor(RD);
- if (!Destructor)
- return false;
- // C++14 [dcl.fct.def.delete]p2:
- // A program that refers to a deleted function implicitly or
- // explicitly, other than to declare it, is ill-formed.
- if (Destructor->isDeleted())
- return false;
- if (C.getLangOpts().AccessControl && Destructor->getAccess() != AS_public)
- return false;
- if (UTT == UTT_IsNothrowDestructible) {
- auto *CPT = Destructor->getType()->castAs<FunctionProtoType>();
- CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
- if (!CPT || !CPT->isNothrow())
- return false;
- }
- }
- return true;
-
- case UTT_HasTrivialDestructor:
- // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html
- // If __is_pod (type) is true or type is a reference type
- // then the trait is true, else if type is a cv class or union
- // type (or array thereof) with a trivial destructor
- // ([class.dtor]) then the trait is true, else it is
- // false.
- if (T.isPODType(C) || T->isReferenceType())
- return true;
-
- // Objective-C++ ARC: autorelease types don't require destruction.
- if (T->isObjCLifetimeType() &&
- T.getObjCLifetime() == Qualifiers::OCL_Autoreleasing)
- return true;
-
- if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl())
- return RD->hasTrivialDestructor();
- return false;
- // TODO: Propagate nothrowness for implicitly declared special members.
- case UTT_HasNothrowAssign:
- // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
- // If type is const qualified or is a reference type then the
- // trait is false. Otherwise if __has_trivial_assign (type)
- // is true then the trait is true, else if type is a cv class
- // or union type with copy assignment operators that are known
- // not to throw an exception then the trait is true, else it is
- // false.
- if (C.getBaseElementType(T).isConstQualified())
- return false;
- if (T->isReferenceType())
- return false;
- if (T.isPODType(C) || T->isObjCLifetimeType())
- return true;
-
- if (const RecordType *RT = T->getAs<RecordType>())
- return HasNoThrowOperator(RT, OO_Equal, Self, KeyLoc, C,
- &CXXRecordDecl::hasTrivialCopyAssignment,
- &CXXRecordDecl::hasNonTrivialCopyAssignment,
- &CXXMethodDecl::isCopyAssignmentOperator);
- return false;
- case UTT_HasNothrowMoveAssign:
- // This trait is implemented by MSVC 2012 and needed to parse the
- // standard library headers. Specifically this is used as the logic
- // behind std::is_nothrow_move_assignable (20.9.4.3).
- if (T.isPODType(C))
- return true;
-
- if (const RecordType *RT = C.getBaseElementType(T)->getAs<RecordType>())
- return HasNoThrowOperator(RT, OO_Equal, Self, KeyLoc, C,
- &CXXRecordDecl::hasTrivialMoveAssignment,
- &CXXRecordDecl::hasNonTrivialMoveAssignment,
- &CXXMethodDecl::isMoveAssignmentOperator);
- return false;
- case UTT_HasNothrowCopy:
- // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
- // If __has_trivial_copy (type) is true then the trait is true, else
- // if type is a cv class or union type with copy constructors that are
- // known not to throw an exception then the trait is true, else it is
- // false.
- if (T.isPODType(C) || T->isReferenceType() || T->isObjCLifetimeType())
- return true;
- if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) {
- if (RD->hasTrivialCopyConstructor() &&
- !RD->hasNonTrivialCopyConstructor())
- return true;
-
- bool FoundConstructor = false;
- unsigned FoundTQs;
- for (const auto *ND : Self.LookupConstructors(RD)) {
- // A template constructor is never a copy constructor.
- // FIXME: However, it may actually be selected at the actual overload
- // resolution point.
- if (isa<FunctionTemplateDecl>(ND->getUnderlyingDecl()))
- continue;
- // UsingDecl itself is not a constructor
- if (isa<UsingDecl>(ND))
- continue;
- auto *Constructor = cast<CXXConstructorDecl>(ND->getUnderlyingDecl());
- if (Constructor->isCopyConstructor(FoundTQs)) {
- FoundConstructor = true;
- auto *CPT = Constructor->getType()->castAs<FunctionProtoType>();
- CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
- if (!CPT)
- return false;
- // TODO: check whether evaluating default arguments can throw.
- // For now, we'll be conservative and assume that they can throw.
- if (!CPT->isNothrow() || CPT->getNumParams() > 1)
- return false;
- }
- }
-
- return FoundConstructor;
- }
- return false;
- case UTT_HasNothrowConstructor:
- // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html
- // If __has_trivial_constructor (type) is true then the trait is
- // true, else if type is a cv class or union type (or array
- // thereof) with a default constructor that is known not to
- // throw an exception then the trait is true, else it is false.
- if (T.isPODType(C) || T->isObjCLifetimeType())
- return true;
- if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl()) {
- if (RD->hasTrivialDefaultConstructor() &&
- !RD->hasNonTrivialDefaultConstructor())
- return true;
-
- bool FoundConstructor = false;
- for (const auto *ND : Self.LookupConstructors(RD)) {
- // FIXME: In C++0x, a constructor template can be a default constructor.
- if (isa<FunctionTemplateDecl>(ND->getUnderlyingDecl()))
- continue;
- // UsingDecl itself is not a constructor
- if (isa<UsingDecl>(ND))
- continue;
- auto *Constructor = cast<CXXConstructorDecl>(ND->getUnderlyingDecl());
- if (Constructor->isDefaultConstructor()) {
- FoundConstructor = true;
- auto *CPT = Constructor->getType()->castAs<FunctionProtoType>();
- CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
- if (!CPT)
- return false;
- // FIXME: check whether evaluating default arguments can throw.
- // For now, we'll be conservative and assume that they can throw.
- if (!CPT->isNothrow() || CPT->getNumParams() > 0)
- return false;
- }
- }
- return FoundConstructor;
- }
- return false;
- case UTT_HasVirtualDestructor:
- // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
- // If type is a class type with a virtual destructor ([class.dtor])
- // then the trait is true, else it is false.
- if (CXXRecordDecl *RD = T->getAsCXXRecordDecl())
- if (CXXDestructorDecl *Destructor = Self.LookupDestructor(RD))
- return Destructor->isVirtual();
- return false;
-
- // These type trait expressions are modeled on the specifications for the
- // Embarcadero C++0x type trait functions:
- // http://docwiki.embarcadero.com/RADStudio/XE/en/Type_Trait_Functions_(C%2B%2B0x)_Index
- case UTT_IsCompleteType:
- // http://docwiki.embarcadero.com/RADStudio/XE/en/Is_complete_type_(typename_T_):
- // Returns True if and only if T is a complete type at the point of the
- // function call.
- return !T->isIncompleteType();
- case UTT_HasUniqueObjectRepresentations:
- return C.hasUniqueObjectRepresentations(T);
- case UTT_IsTriviallyRelocatable:
- return IsTriviallyRelocatableType(Self, T);
- case UTT_IsBitwiseCloneable:
- return T.isBitwiseCloneableType(C);
- case UTT_IsCppTriviallyRelocatable:
- return Self.IsCXXTriviallyRelocatableType(T);
- case UTT_IsReplaceable:
- return Self.IsCXXReplaceableType(T);
- case UTT_CanPassInRegs:
- if (CXXRecordDecl *RD = T->getAsCXXRecordDecl(); RD && !T.hasQualifiers())
- return RD->canPassInRegisters();
- Self.Diag(KeyLoc, diag::err_builtin_pass_in_regs_non_class) << T;
- return false;
- case UTT_IsTriviallyEqualityComparable:
- return isTriviallyEqualityComparableType(Self, T, KeyLoc);
- case UTT_IsImplicitLifetime: {
- DiagnoseVLAInCXXTypeTrait(Self, TInfo,
- tok::kw___builtin_is_implicit_lifetime);
- DiagnoseAtomicInCXXTypeTrait(Self, TInfo,
- tok::kw___builtin_is_implicit_lifetime);
-
- // [basic.types.general] p9
- // Scalar types, implicit-lifetime class types ([class.prop]),
- // array types, and cv-qualified versions of these types
- // are collectively called implicit-lifetime types.
- QualType UnqualT = T->getCanonicalTypeUnqualified();
- if (UnqualT->isScalarType())
- return true;
- if (UnqualT->isArrayType() || UnqualT->isVectorType())
- return true;
- const CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl();
- if (!RD)
- return false;
-
- // [class.prop] p9
- // A class S is an implicit-lifetime class if
- // - it is an aggregate whose destructor is not user-provided or
- // - it has at least one trivial eligible constructor and a trivial,
- // non-deleted destructor.
- const CXXDestructorDecl *Dtor = RD->getDestructor();
- if (UnqualT->isAggregateType())
- if (Dtor && !Dtor->isUserProvided())
- return true;
- if (RD->hasTrivialDestructor() && (!Dtor || !Dtor->isDeleted()))
- if (RD->hasTrivialDefaultConstructor() ||
- RD->hasTrivialCopyConstructor() || RD->hasTrivialMoveConstructor())
- return true;
- return false;
- }
- case UTT_IsIntangibleType:
- assert(Self.getLangOpts().HLSL && "intangible types are HLSL-only feature");
- if (!T->isVoidType() && !T->isIncompleteArrayType())
- if (Self.RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), T,
- diag::err_incomplete_type))
- return false;
- if (DiagnoseVLAInCXXTypeTrait(Self, TInfo,
- tok::kw___builtin_hlsl_is_intangible))
- return false;
- return T->isHLSLIntangibleType();
-
- case UTT_IsTypedResourceElementCompatible:
- assert(Self.getLangOpts().HLSL &&
- "typed resource element compatible types are an HLSL-only feature");
- if (T->isIncompleteType())
- return false;
-
- return Self.HLSL().IsTypedResourceElementCompatible(T);
- }
-}
-
-static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
- const TypeSourceInfo *Rhs, SourceLocation KeyLoc);
-
-static ExprResult CheckConvertibilityForTypeTraits(
- Sema &Self, const TypeSourceInfo *Lhs, const TypeSourceInfo *Rhs,
- SourceLocation KeyLoc, llvm::BumpPtrAllocator &OpaqueExprAllocator) {
-
- QualType LhsT = Lhs->getType();
- QualType RhsT = Rhs->getType();
-
- // 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 ExprError();
-
- // A function definition requires a complete, non-abstract return type.
- if (!Self.isCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT) ||
- Self.isAbstractType(Rhs->getTypeLoc().getBeginLoc(), RhsT))
- return ExprError();
-
- // 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));
- Expr *From = new (OpaqueExprAllocator.Allocate<OpaqueValueExpr>())
- OpaqueValueExpr(KeyLoc, LhsT.getNonLValueExprType(Self.Context),
- Expr::getValueKindForType(LhsT));
- 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, /*ForValidityCheck=*/true);
- Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl());
- InitializationSequence Init(Self, To, Kind, From);
- if (Init.Failed())
- return ExprError();
-
- ExprResult Result = Init.Perform(Self, To, Kind, From);
- if (Result.isInvalid() || SFINAE.hasErrorOccurred())
- return ExprError();
-
- return Result;
-}
-
-static APValue EvaluateSizeTTypeTrait(Sema &S, TypeTrait Kind,
- SourceLocation KWLoc,
- ArrayRef<TypeSourceInfo *> Args,
- SourceLocation RParenLoc,
- bool IsDependent) {
- if (IsDependent)
- return APValue();
-
- switch (Kind) {
- case TypeTrait::UTT_StructuredBindingSize: {
- QualType T = Args[0]->getType();
- SourceRange ArgRange = Args[0]->getTypeLoc().getSourceRange();
- UnsignedOrNone Size =
- S.GetDecompositionElementCount(T, ArgRange.getBegin());
- if (!Size) {
- S.Diag(KWLoc, diag::err_arg_is_not_destructurable) << T << ArgRange;
- return APValue();
- }
- return APValue(
- S.getASTContext().MakeIntValue(*Size, S.getASTContext().getSizeType()));
- break;
- }
- default:
- llvm_unreachable("Not a SizeT type trait");
- }
-}
-
-static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
- SourceLocation KWLoc,
- ArrayRef<TypeSourceInfo *> Args,
- SourceLocation RParenLoc,
- bool IsDependent) {
- if (IsDependent)
- return false;
-
- if (Kind <= UTT_Last)
- return EvaluateUnaryTypeTrait(S, Kind, KWLoc, Args[0]);
-
- // Evaluate ReferenceBindsToTemporary and ReferenceConstructsFromTemporary
- // alongside the IsConstructible traits to avoid duplication.
- if (Kind <= BTT_Last && Kind != BTT_ReferenceBindsToTemporary &&
- Kind != BTT_ReferenceConstructsFromTemporary &&
- Kind != BTT_ReferenceConvertsFromTemporary)
- return EvaluateBinaryTypeTrait(S, Kind, Args[0],
- Args[1], RParenLoc);
-
- switch (Kind) {
- case clang::BTT_ReferenceBindsToTemporary:
- case clang::BTT_ReferenceConstructsFromTemporary:
- case clang::BTT_ReferenceConvertsFromTemporary:
- case clang::TT_IsConstructible:
- case clang::TT_IsNothrowConstructible:
- case clang::TT_IsTriviallyConstructible: {
- // C++11 [meta.unary.prop]:
- // is_trivially_constructible is defined as:
- //
- // is_constructible<T, Args...>::value is true and the variable
- // definition for is_constructible, as defined below, is known to call
- // no operation that is not trivial.
- //
- // The predicate condition for a template specialization
- // is_constructible<T, Args...> shall be satisfied if and only if the
- // following variable definition would be well-formed for some invented
- // variable t:
- //
- // T t(create<Args>()...);
- assert(!Args.empty());
-
- // Precondition: T and all types in the parameter pack Args shall be
- // complete types, (possibly cv-qualified) void, or arrays of
- // unknown bound.
- 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 false;
- }
-
- // Make sure the first argument is not incomplete nor a function type.
- QualType T = Args[0]->getType();
- if (T->isIncompleteType() || T->isFunctionType())
- return false;
-
- // Make sure the first argument is not an abstract type.
- CXXRecordDecl *RD = T->getAsCXXRecordDecl();
- if (RD && RD->isAbstract())
- return false;
-
- llvm::BumpPtrAllocator OpaqueExprAllocator;
- SmallVector<Expr *, 2> ArgExprs;
- ArgExprs.reserve(Args.size() - 1);
- for (unsigned I = 1, N = Args.size(); I != N; ++I) {
- QualType ArgTy = Args[I]->getType();
- if (ArgTy->isObjectType() || ArgTy->isFunctionType())
- ArgTy = S.Context.getRValueReferenceType(ArgTy);
- ArgExprs.push_back(
- new (OpaqueExprAllocator.Allocate<OpaqueValueExpr>())
- OpaqueValueExpr(Args[I]->getTypeLoc().getBeginLoc(),
- ArgTy.getNonLValueExprType(S.Context),
- Expr::getValueKindForType(ArgTy)));
- }
-
- // Perform the initialization in an unevaluated context within a SFINAE
- // trap at translation unit scope.
- EnterExpressionEvaluationContext Unevaluated(
- S, Sema::ExpressionEvaluationContext::Unevaluated);
- Sema::SFINAETrap SFINAE(S, /*ForValidityCheck=*/true);
- Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());
- InitializedEntity To(
- InitializedEntity::InitializeTemporary(S.Context, Args[0]));
- InitializationKind InitKind(
- Kind == clang::BTT_ReferenceConvertsFromTemporary
- ? InitializationKind::CreateCopy(KWLoc, KWLoc)
- : InitializationKind::CreateDirect(KWLoc, KWLoc, RParenLoc));
- InitializationSequence Init(S, To, InitKind, ArgExprs);
- if (Init.Failed())
- return false;
-
- ExprResult Result = Init.Perform(S, To, InitKind, ArgExprs);
- if (Result.isInvalid() || SFINAE.hasErrorOccurred())
- return false;
-
- if (Kind == clang::TT_IsConstructible)
- return true;
-
- if (Kind == clang::BTT_ReferenceBindsToTemporary ||
- Kind == clang::BTT_ReferenceConstructsFromTemporary ||
- Kind == clang::BTT_ReferenceConvertsFromTemporary) {
- if (!T->isReferenceType())
- return false;
-
- if (!Init.isDirectReferenceBinding())
- return true;
-
- if (Kind == clang::BTT_ReferenceBindsToTemporary)
- return false;
-
- QualType U = Args[1]->getType();
- if (U->isReferenceType())
- return false;
-
- TypeSourceInfo *TPtr = S.Context.CreateTypeSourceInfo(
- S.Context.getPointerType(T.getNonReferenceType()));
- TypeSourceInfo *UPtr = S.Context.CreateTypeSourceInfo(
- S.Context.getPointerType(U.getNonReferenceType()));
- return !CheckConvertibilityForTypeTraits(S, UPtr, TPtr, RParenLoc,
- OpaqueExprAllocator)
- .isInvalid();
- }
-
- if (Kind == clang::TT_IsNothrowConstructible)
- return S.canThrow(Result.get()) == CT_Cannot;
-
- if (Kind == clang::TT_IsTriviallyConstructible) {
- // Under Objective-C ARC and Weak, if the destination has non-trivial
- // Objective-C lifetime, this is a non-trivial construction.
- if (T.getNonReferenceType().hasNonTrivialObjCLifetime())
- return false;
-
- // The initialization succeeded; now make sure there are no non-trivial
- // calls.
- return !Result.get()->hasNonTrivialCall(S.Context);
- }
-
- llvm_unreachable("unhandled type trait");
- return false;
- }
- default: llvm_unreachable("not a TT");
- }
-
- return false;
-}
-
-namespace {
-void DiagnoseBuiltinDeprecation(Sema& S, TypeTrait Kind,
- SourceLocation KWLoc) {
- TypeTrait Replacement;
- switch (Kind) {
- case UTT_HasNothrowAssign:
- case UTT_HasNothrowMoveAssign:
- Replacement = BTT_IsNothrowAssignable;
- break;
- case UTT_HasNothrowCopy:
- case UTT_HasNothrowConstructor:
- Replacement = TT_IsNothrowConstructible;
- break;
- case UTT_HasTrivialAssign:
- case UTT_HasTrivialMoveAssign:
- Replacement = BTT_IsTriviallyAssignable;
- break;
- case UTT_HasTrivialCopy:
- Replacement = UTT_IsTriviallyCopyable;
- break;
- case UTT_HasTrivialDefaultConstructor:
- case UTT_HasTrivialMoveConstructor:
- Replacement = TT_IsTriviallyConstructible;
- break;
- case UTT_HasTrivialDestructor:
- Replacement = UTT_IsTriviallyDestructible;
- break;
- case UTT_IsTriviallyRelocatable:
- Replacement = clang::UTT_IsCppTriviallyRelocatable;
- break;
- default:
- return;
- }
- S.Diag(KWLoc, diag::warn_deprecated_builtin)
- << getTraitSpelling(Kind) << getTraitSpelling(Replacement);
-}
-}
-
-bool Sema::CheckTypeTraitArity(unsigned Arity, SourceLocation Loc, size_t N) {
- if (Arity && N != Arity) {
- Diag(Loc, diag::err_type_trait_arity)
- << Arity << 0 << (Arity > 1) << (int)N << SourceRange(Loc);
- return false;
- }
-
- if (!Arity && N == 0) {
- Diag(Loc, diag::err_type_trait_arity)
- << 1 << 1 << 1 << (int)N << SourceRange(Loc);
- return false;
- }
- return true;
-}
-
-enum class TypeTraitReturnType {
- Bool,
- SizeT,
-};
-
-static TypeTraitReturnType GetReturnType(TypeTrait Kind) {
- if (Kind == TypeTrait::UTT_StructuredBindingSize)
- return TypeTraitReturnType::SizeT;
- return TypeTraitReturnType::Bool;
-}
-
-ExprResult Sema::BuildTypeTrait(TypeTrait Kind, SourceLocation KWLoc,
- ArrayRef<TypeSourceInfo *> Args,
- SourceLocation RParenLoc) {
- if (!CheckTypeTraitArity(getTypeTraitArity(Kind), KWLoc, Args.size()))
- return ExprError();
-
- if (Kind <= UTT_Last && !CheckUnaryTypeTraitTypeCompleteness(
- *this, Kind, KWLoc, Args[0]->getType()))
- return ExprError();
-
- DiagnoseBuiltinDeprecation(*this, Kind, KWLoc);
-
- bool Dependent = false;
- for (unsigned I = 0, N = Args.size(); I != N; ++I) {
- if (Args[I]->getType()->isDependentType()) {
- Dependent = true;
- break;
- }
- }
-
- switch (GetReturnType(Kind)) {
- case TypeTraitReturnType::Bool: {
- bool Result = EvaluateBooleanTypeTrait(*this, Kind, KWLoc, Args, RParenLoc,
- Dependent);
- return TypeTraitExpr::Create(Context, Context.getLogicalOperationType(),
- KWLoc, Kind, Args, RParenLoc, Result);
- }
- case TypeTraitReturnType::SizeT: {
- APValue Result =
- EvaluateSizeTTypeTrait(*this, Kind, KWLoc, Args, RParenLoc, Dependent);
- return TypeTraitExpr::Create(Context, Context.getSizeType(), KWLoc, Kind,
- Args, RParenLoc, Result);
- }
- }
- llvm_unreachable("unhandled type trait return type");
-}
-
-ExprResult Sema::ActOnTypeTrait(TypeTrait Kind, SourceLocation KWLoc,
- ArrayRef<ParsedType> Args,
- SourceLocation RParenLoc) {
- SmallVector<TypeSourceInfo *, 4> ConvertedArgs;
- ConvertedArgs.reserve(Args.size());
-
- for (unsigned I = 0, N = Args.size(); I != N; ++I) {
- TypeSourceInfo *TInfo;
- QualType T = GetTypeFromParser(Args[I], &TInfo);
- if (!TInfo)
- TInfo = Context.getTrivialTypeSourceInfo(T, KWLoc);
-
- ConvertedArgs.push_back(TInfo);
- }
-
- 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();
-
- assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
- "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(
- Rhs->getTypeLoc().getBeginLoc(), 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(
- Rhs->getTypeLoc().getBeginLoc(), RhsT,
- diag::err_incomplete_type_used_in_type_trait_expr))
- return false;
-
- 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>();
-
- if (!BaseRecord || !DerivedRecord) {
- DiagnoseVLAInCXXTypeTrait(Self, Lhs,
- tok::kw___builtin_is_virtual_base_of);
- DiagnoseVLAInCXXTypeTrait(Self, Rhs,
- tok::kw___builtin_is_virtual_base_of);
- return false;
- }
-
- if (BaseRecord->isUnionType() || DerivedRecord->isUnionType())
- return false;
-
- if (!BaseRecord->isStructureOrClassType() ||
- !DerivedRecord->isStructureOrClassType())
- return false;
-
- if (Self.RequireCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT,
- diag::err_incomplete_type))
- return false;
-
- return cast<CXXRecordDecl>(DerivedRecord->getDecl())
- ->isVirtuallyDerivedFrom(cast<CXXRecordDecl>(BaseRecord->getDecl()));
- }
- case BTT_IsSame:
- return Self.Context.hasSameType(LhsT, RhsT);
- case BTT_TypeCompatible: {
- // GCC ignores cv-qualifiers on arrays for this builtin.
- Qualifiers LhsQuals, RhsQuals;
- QualType Lhs = Self.getASTContext().getUnqualifiedArrayType(LhsT, LhsQuals);
- QualType Rhs = Self.getASTContext().getUnqualifiedArrayType(RhsT, RhsQuals);
- return Self.Context.typesAreCompatible(Lhs, Rhs);
- }
- case BTT_IsConvertible:
- case BTT_IsConvertibleTo:
- case BTT_IsNothrowConvertible: {
- if (RhsT->isVoidType())
- return LhsT->isVoidType();
- llvm::BumpPtrAllocator OpaqueExprAllocator;
- ExprResult Result = CheckConvertibilityForTypeTraits(Self, Lhs, Rhs, KeyLoc,
- OpaqueExprAllocator);
- if (Result.isInvalid())
- return false;
-
- if (BTT != BTT_IsNothrowConvertible)
- return true;
-
- return Self.canThrow(Result.get()) == CT_Cannot;
- }
-
- case BTT_IsAssignable:
- case BTT_IsNothrowAssignable:
- case BTT_IsTriviallyAssignable: {
- // C++11 [meta.unary.prop]p3:
- // is_trivially_assignable is defined as:
- // is_assignable<T, U>::value is true and the assignment, as defined by
- // is_assignable, is known to call no operation that is not trivial
- //
- // is_assignable is defined as:
- // The expression declval<T>() = declval<U>() is well-formed when
- // treated as an unevaluated operand (Clause 5).
- //
- // For both, T and U shall be complete types, (possibly cv-qualified)
- // void, or arrays of unknown bound.
- if (!LhsT->isVoidType() && !LhsT->isIncompleteArrayType() &&
- Self.RequireCompleteType(
- Lhs->getTypeLoc().getBeginLoc(), LhsT,
- diag::err_incomplete_type_used_in_type_trait_expr))
- return false;
- if (!RhsT->isVoidType() && !RhsT->isIncompleteArrayType() &&
- Self.RequireCompleteType(
- Rhs->getTypeLoc().getBeginLoc(), RhsT,
- diag::err_incomplete_type_used_in_type_trait_expr))
- return false;
-
- // cv void is never assignable.
- if (LhsT->isVoidType() || RhsT->isVoidType())
- return false;
-
- // Build expressions that emulate the effect of declval<T>() and
- // declval<U>().
- if (LhsT->isObjectType() || LhsT->isFunctionType())
- LhsT = Self.Context.getRValueReferenceType(LhsT);
- if (RhsT->isObjectType() || RhsT->isFunctionType())
- RhsT = Self.Context.getRValueReferenceType(RhsT);
- OpaqueValueExpr Lhs(KeyLoc, LhsT.getNonLValueExprType(Self.Context),
- Expr::getValueKindForType(LhsT));
- OpaqueValueExpr Rhs(KeyLoc, RhsT.getNonLValueExprType(Self.Context),
- Expr::getValueKindForType(RhsT));
-
- // Attempt the assignment in an unevaluated context within a SFINAE
- // trap at translation unit scope.
- EnterExpressionEvaluationContext Unevaluated(
- Self, Sema::ExpressionEvaluationContext::Unevaluated);
- Sema::SFINAETrap SFINAE(Self, /*ForValidityCheck=*/true);
- Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl());
- ExprResult Result = Self.BuildBinOp(/*S=*/nullptr, KeyLoc, BO_Assign, &Lhs,
- &Rhs);
- if (Result.isInvalid())
- return false;
-
- // Treat the assignment as unused for the purpose of -Wdeprecated-volatile.
- Self.CheckUnusedVolatileAssignment(Result.get());
-
- if (SFINAE.hasErrorOccurred())
- return false;
-
- if (BTT == BTT_IsAssignable)
- return true;
-
- if (BTT == BTT_IsNothrowAssignable)
- return Self.canThrow(Result.get()) == CT_Cannot;
-
- if (BTT == BTT_IsTriviallyAssignable) {
- // Under Objective-C ARC and Weak, if the destination has non-trivial
- // Objective-C lifetime, this is a non-trivial assignment.
- if (LhsT.getNonReferenceType().hasNonTrivialObjCLifetime())
- return false;
-
- return !Result.get()->hasNonTrivialCall(Self.Context);
- }
-
- llvm_unreachable("unhandled type trait");
- return false;
- }
- case BTT_IsLayoutCompatible: {
- if (!LhsT->isVoidType() && !LhsT->isIncompleteArrayType())
- Self.RequireCompleteType(Lhs->getTypeLoc().getBeginLoc(), LhsT,
- diag::err_incomplete_type);
- if (!RhsT->isVoidType() && !RhsT->isIncompleteArrayType())
- Self.RequireCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT,
- diag::err_incomplete_type);
-
- DiagnoseVLAInCXXTypeTrait(Self, Lhs, tok::kw___is_layout_compatible);
- DiagnoseVLAInCXXTypeTrait(Self, Rhs, tok::kw___is_layout_compatible);
-
- return Self.IsLayoutCompatible(LhsT, RhsT);
- }
- case BTT_IsPointerInterconvertibleBaseOf: {
- if (LhsT->isStructureOrClassType() && RhsT->isStructureOrClassType() &&
- !Self.getASTContext().hasSameUnqualifiedType(LhsT, RhsT)) {
- Self.RequireCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT,
- diag::err_incomplete_type);
- }
-
- DiagnoseVLAInCXXTypeTrait(Self, Lhs,
- tok::kw___is_pointer_interconvertible_base_of);
- DiagnoseVLAInCXXTypeTrait(Self, Rhs,
- tok::kw___is_pointer_interconvertible_base_of);
-
- return Self.IsPointerInterconvertibleBaseOf(Lhs, Rhs);
- }
- case BTT_IsDeducible: {
- const auto *TSTToBeDeduced = cast<DeducedTemplateSpecializationType>(LhsT);
- sema::TemplateDeductionInfo Info(KeyLoc);
- return Self.DeduceTemplateArgumentsFromType(
- TSTToBeDeduced->getTemplateName().getAsTemplateDecl(), RhsT,
- Info) == TemplateDeductionResult::Success;
- }
- case BTT_IsScalarizedLayoutCompatible: {
- if (!LhsT->isVoidType() && !LhsT->isIncompleteArrayType() &&
- Self.RequireCompleteType(Lhs->getTypeLoc().getBeginLoc(), LhsT,
- diag::err_incomplete_type))
- return true;
- if (!RhsT->isVoidType() && !RhsT->isIncompleteArrayType() &&
- Self.RequireCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT,
- diag::err_incomplete_type))
- return true;
-
- DiagnoseVLAInCXXTypeTrait(
- Self, Lhs, tok::kw___builtin_hlsl_is_scalarized_layout_compatible);
- DiagnoseVLAInCXXTypeTrait(
- Self, Rhs, tok::kw___builtin_hlsl_is_scalarized_layout_compatible);
-
- return Self.HLSL().IsScalarizedLayoutCompatible(LhsT, RhsT);
- }
- default:
- llvm_unreachable("not a BTT");
- }
- llvm_unreachable("Unknown type trait or not implemented");
-}
-
-ExprResult Sema::ActOnArrayTypeTrait(ArrayTypeTrait ATT,
- SourceLocation KWLoc,
- ParsedType Ty,
- Expr* DimExpr,
- SourceLocation RParen) {
- TypeSourceInfo *TSInfo;
- QualType T = GetTypeFromParser(Ty, &TSInfo);
- if (!TSInfo)
- TSInfo = Context.getTrivialTypeSourceInfo(T);
-
- return BuildArrayTypeTrait(ATT, KWLoc, TSInfo, DimExpr, RParen);
-}
-
-static uint64_t EvaluateArrayTypeTrait(Sema &Self, ArrayTypeTrait ATT,
- QualType T, Expr *DimExpr,
- SourceLocation KeyLoc) {
- assert(!T->isDependentType() && "Cannot evaluate traits of dependent type");
-
- switch(ATT) {
- case ATT_ArrayRank:
- if (T->isArrayType()) {
- unsigned Dim = 0;
- while (const ArrayType *AT = Self.Context.getAsArrayType(T)) {
- ++Dim;
- T = AT->getElementType();
- }
- return Dim;
- }
- return 0;
-
- case ATT_ArrayExtent: {
- llvm::APSInt Value;
- uint64_t Dim;
- if (Self.VerifyIntegerConstantExpression(
- DimExpr, &Value, diag::err_dimension_expr_not_constant_integer)
- .isInvalid())
- return 0;
- if (Value.isSigned() && Value.isNegative()) {
- Self.Diag(KeyLoc, diag::err_dimension_expr_not_constant_integer)
- << DimExpr->getSourceRange();
- return 0;
- }
- Dim = Value.getLimitedValue();
-
- if (T->isArrayType()) {
- unsigned D = 0;
- bool Matched = false;
- while (const ArrayType *AT = Self.Context.getAsArrayType(T)) {
- if (Dim == D) {
- Matched = true;
- break;
- }
- ++D;
- T = AT->getElementType();
- }
-
- if (Matched && T->isArrayType()) {
- if (const ConstantArrayType *CAT = Self.Context.getAsConstantArrayType(T))
- return CAT->getLimitedSize();
- }
- }
- return 0;
- }
- }
- llvm_unreachable("Unknown type trait or not implemented");
-}
-
-ExprResult Sema::BuildArrayTypeTrait(ArrayTypeTrait ATT,
- SourceLocation KWLoc,
- TypeSourceInfo *TSInfo,
- Expr* DimExpr,
- SourceLocation RParen) {
- QualType T = TSInfo->getType();
-
- // FIXME: This should likely be tracked as an APInt to remove any host
- // assumptions about the width of size_t on the target.
- uint64_t Value = 0;
- if (!T->isDependentType())
- Value = EvaluateArrayTypeTrait(*this, ATT, T, DimExpr, KWLoc);
-
- // While the specification for these traits from the Embarcadero C++
- // compiler's documentation says the return type is 'unsigned int', Clang
- // returns 'size_t'. On Windows, the primary platform for the Embarcadero
- // compiler, there is no difference. On several other platforms this is an
- // important distinction.
- return new (Context) ArrayTypeTraitExpr(KWLoc, ATT, TSInfo, Value, DimExpr,
- RParen, Context.getSizeType());
-}
-
-ExprResult Sema::ActOnExpressionTrait(ExpressionTrait ET,
- SourceLocation KWLoc,
- Expr *Queried,
- SourceLocation RParen) {
- // If error parsing the expression, ignore.
- if (!Queried)
- return ExprError();
-
- ExprResult Result = BuildExpressionTrait(ET, KWLoc, Queried, RParen);
-
- return Result;
-}
-
-static bool EvaluateExpressionTrait(ExpressionTrait ET, Expr *E) {
- switch (ET) {
- case ET_IsLValueExpr: return E->isLValue();
- case ET_IsRValueExpr:
- return E->isPRValue();
- }
- llvm_unreachable("Expression trait not covered by switch");
-}
-
-ExprResult Sema::BuildExpressionTrait(ExpressionTrait ET,
- SourceLocation KWLoc,
- Expr *Queried,
- SourceLocation RParen) {
- if (Queried->isTypeDependent()) {
- // Delay type-checking for type-dependent expressions.
- } else if (Queried->hasPlaceholderType()) {
- ExprResult PE = CheckPlaceholderExpr(Queried);
- if (PE.isInvalid()) return ExprError();
- return BuildExpressionTrait(ET, KWLoc, PE.get(), RParen);
- }
-
- bool Value = EvaluateExpressionTrait(ET, Queried);
-
- return new (Context)
- ExpressionTraitExpr(KWLoc, ET, Queried, Value, RParen, Context.BoolTy);
-}
QualType Sema::CheckPointerToMemberOperands(ExprResult &LHS, ExprResult &RHS,
ExprValueKind &VK,
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
new file mode 100644
index 0000000000000..763d37071b134
--- /dev/null
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -0,0 +1,1919 @@
+//===----- SemaTypeTraits.cpp - Semantic Analysis for C++ Type Traits -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements semantic analysis for C++ type traits.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/DeclCXX.h"
+#include "clang/Basic/DiagnosticParse.h"
+#include "clang/Basic/DiagnosticSema.h"
+#include "clang/Sema/EnterExpressionEvaluationContext.h"
+#include "clang/Sema/Initialization.h"
+#include "clang/Sema/Lookup.h"
+#include "clang/Sema/Overload.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaHLSL.h"
+
+using namespace clang;
+
+static CXXMethodDecl *LookupSpecialMemberFromXValue(Sema &SemaRef,
+ const CXXRecordDecl *RD,
+ bool Assign) {
+ RD = RD->getDefinition();
+ SourceLocation LookupLoc = RD->getLocation();
+
+ CanQualType CanTy = SemaRef.getASTContext().getCanonicalType(
+ SemaRef.getASTContext().getTagDeclType(RD));
+ DeclarationName Name;
+ Expr *Arg = nullptr;
+ unsigned NumArgs;
+
+ QualType ArgType = CanTy;
+ ExprValueKind VK = clang::VK_XValue;
+
+ if (Assign)
+ Name =
+ SemaRef.getASTContext().DeclarationNames.getCXXOperatorName(OO_Equal);
+ else
+ Name =
+ SemaRef.getASTContext().DeclarationNames.getCXXConstructorName(CanTy);
+
+ OpaqueValueExpr FakeArg(LookupLoc, ArgType, VK);
+ NumArgs = 1;
+ Arg = &FakeArg;
+
+ // Create the object argument
+ QualType ThisTy = CanTy;
+ Expr::Classification Classification =
+ OpaqueValueExpr(LookupLoc, ThisTy, VK_LValue)
+ .Classify(SemaRef.getASTContext());
+
+ // Now we perform lookup on the name we computed earlier and do overload
+ // resolution. Lookup is only performed directly into the class since there
+ // will always be a (possibly implicit) declaration to shadow any others.
+ OverloadCandidateSet OCS(LookupLoc, OverloadCandidateSet::CSK_Normal);
+ DeclContext::lookup_result R = RD->lookup(Name);
+
+ if (R.empty())
+ return nullptr;
+
+ // Copy the candidates as our processing of them may load new declarations
+ // from an external source and invalidate lookup_result.
+ SmallVector<NamedDecl *, 8> Candidates(R.begin(), R.end());
+
+ for (NamedDecl *CandDecl : Candidates) {
+ if (CandDecl->isInvalidDecl())
+ continue;
+
+ DeclAccessPair Cand = DeclAccessPair::make(CandDecl, clang::AS_none);
+ auto CtorInfo = getConstructorInfo(Cand);
+ if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(Cand->getUnderlyingDecl())) {
+ if (Assign)
+ SemaRef.AddMethodCandidate(M, Cand, const_cast<CXXRecordDecl *>(RD),
+ ThisTy, Classification,
+ llvm::ArrayRef(&Arg, NumArgs), OCS, true);
+ else {
+ assert(CtorInfo);
+ SemaRef.AddOverloadCandidate(CtorInfo.Constructor, CtorInfo.FoundDecl,
+ llvm::ArrayRef(&Arg, NumArgs), OCS,
+ /*SuppressUserConversions*/ true);
+ }
+ } else if (FunctionTemplateDecl *Tmpl =
+ dyn_cast<FunctionTemplateDecl>(Cand->getUnderlyingDecl())) {
+ if (Assign)
+ SemaRef.AddMethodTemplateCandidate(
+ Tmpl, Cand, const_cast<CXXRecordDecl *>(RD), nullptr, ThisTy,
+ Classification, llvm::ArrayRef(&Arg, NumArgs), OCS, true);
+ else {
+ assert(CtorInfo);
+ SemaRef.AddTemplateOverloadCandidate(
+ CtorInfo.ConstructorTmpl, CtorInfo.FoundDecl, nullptr,
+ llvm::ArrayRef(&Arg, NumArgs), OCS, true);
+ }
+ }
+ }
+
+ OverloadCandidateSet::iterator Best;
+ switch (OCS.BestViableFunction(SemaRef, LookupLoc, Best)) {
+ case OR_Success:
+ return cast<CXXMethodDecl>(Best->Function);
+ default:
+ return nullptr;
+ }
+}
+
+static bool hasSuitableConstructorForRelocation(Sema &SemaRef,
+ const CXXRecordDecl *D,
+ bool AllowUserDefined) {
+ assert(D->hasDefinition() && !D->isInvalidDecl());
+
+ if (D->hasSimpleMoveConstructor() || D->hasSimpleCopyConstructor())
+ return true;
+
+ CXXMethodDecl *Decl =
+ LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/false);
+ return Decl && Decl->isUserProvided() == AllowUserDefined;
+}
+
+static bool hasSuitableMoveAssignmentOperatorForRelocation(
+ Sema &SemaRef, const CXXRecordDecl *D, bool AllowUserDefined) {
+ assert(D->hasDefinition() && !D->isInvalidDecl());
+
+ if (D->hasSimpleMoveAssignment() || D->hasSimpleCopyAssignment())
+ return true;
+
+ CXXMethodDecl *Decl =
+ LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/true);
+ if (!Decl)
+ return false;
+
+ return Decl && Decl->isUserProvided() == AllowUserDefined;
+}
+
+// [C++26][class.prop]
+// A class C is default-movable if
+// - overload resolution for direct-initializing an object of type C
+// from an xvalue of type C selects a constructor that is a direct member of C
+// and is neither user-provided nor deleted,
+// - overload resolution for assigning to an lvalue of type C from an xvalue of
+// type C selects an assignment operator function that is a direct member of C
+// and is neither user-provided nor deleted, and C has a destructor that is
+// neither user-provided nor deleted.
+static bool IsDefaultMovable(Sema &SemaRef, const CXXRecordDecl *D) {
+ if (!hasSuitableConstructorForRelocation(SemaRef, D,
+ /*AllowUserDefined=*/false))
+ return false;
+
+ if (!hasSuitableMoveAssignmentOperatorForRelocation(
+ SemaRef, D, /*AllowUserDefined=*/false))
+ return false;
+
+ CXXDestructorDecl *Dtr = D->getDestructor();
+
+ if (!Dtr)
+ return true;
+
+ if (Dtr->isUserProvided() && (!Dtr->isDefaulted() || Dtr->isDeleted()))
+ return false;
+
+ return !Dtr->isDeleted();
+}
+
+// [C++26][class.prop]
+// A class is eligible for trivial relocation unless it...
+static bool IsEligibleForTrivialRelocation(Sema &SemaRef,
+ const CXXRecordDecl *D) {
+
+ for (const CXXBaseSpecifier &B : D->bases()) {
+ const auto *BaseDecl = B.getType()->getAsCXXRecordDecl();
+ if (!BaseDecl)
+ continue;
+ // ... has any virtual base classes
+ // ... has a base class that is not a trivially relocatable class
+ if (B.isVirtual() || (!BaseDecl->isDependentType() &&
+ !SemaRef.IsCXXTriviallyRelocatableType(B.getType())))
+ return false;
+ }
+
+ for (const FieldDecl *Field : D->fields()) {
+ if (Field->getType()->isDependentType())
+ continue;
+ if (Field->getType()->isReferenceType())
+ continue;
+ // ... has a non-static data member of an object type that is not
+ // of a trivially relocatable type
+ if (!SemaRef.IsCXXTriviallyRelocatableType(Field->getType()))
+ return false;
+ }
+ return !D->hasDeletedDestructor();
+}
+
+// [C++26][class.prop]
+// A class C is eligible for replacement unless
+static bool IsEligibleForReplacement(Sema &SemaRef, const CXXRecordDecl *D) {
+
+ for (const CXXBaseSpecifier &B : D->bases()) {
+ const auto *BaseDecl = B.getType()->getAsCXXRecordDecl();
+ if (!BaseDecl)
+ continue;
+ // it has a base class that is not a replaceable class
+ if (!BaseDecl->isDependentType() &&
+ !SemaRef.IsCXXReplaceableType(B.getType()))
+ return false;
+ }
+
+ for (const FieldDecl *Field : D->fields()) {
+ if (Field->getType()->isDependentType())
+ continue;
+
+ // it has a non-static data member that is not of a replaceable type,
+ if (!SemaRef.IsCXXReplaceableType(Field->getType()))
+ return false;
+ }
+ return !D->hasDeletedDestructor();
+}
+
+ASTContext::CXXRecordDeclRelocationInfo
+Sema::CheckCXX2CRelocatableAndReplaceable(const CXXRecordDecl *D) {
+ ASTContext::CXXRecordDeclRelocationInfo Info{false, false};
+
+ if (!getLangOpts().CPlusPlus || D->isInvalidDecl())
+ return Info;
+
+ assert(D->hasDefinition());
+
+ // This is part of "eligible for replacement", however we defer it
+ // to avoid extraneous computations.
+ auto HasSuitableSMP = [&] {
+ return hasSuitableConstructorForRelocation(*this, D,
+ /*AllowUserDefined=*/true) &&
+ hasSuitableMoveAssignmentOperatorForRelocation(
+ *this, D, /*AllowUserDefined=*/true);
+ };
+
+ auto IsUnion = [&, Is = std::optional<bool>{}]() mutable {
+ if (!Is.has_value())
+ Is = D->isUnion() && !D->hasUserDeclaredCopyConstructor() &&
+ !D->hasUserDeclaredCopyAssignment() &&
+ !D->hasUserDeclaredMoveOperation() &&
+ !D->hasUserDeclaredDestructor();
+ return *Is;
+ };
+
+ auto IsDefaultMovable = [&, Is = std::optional<bool>{}]() mutable {
+ if (!Is.has_value())
+ Is = ::IsDefaultMovable(*this, D);
+ return *Is;
+ };
+
+ Info.IsRelocatable = [&] {
+ if (D->isDependentType())
+ return false;
+
+ // if it is eligible for trivial relocation
+ if (!IsEligibleForTrivialRelocation(*this, D))
+ return false;
+
+ // has the trivially_relocatable_if_eligible class-property-specifier,
+ if (D->hasAttr<TriviallyRelocatableAttr>())
+ return true;
+
+ // is a union with no user-declared special member functions, or
+ if (IsUnion())
+ return true;
+
+ // is default-movable.
+ return IsDefaultMovable();
+ }();
+
+ Info.IsReplaceable = [&] {
+ if (D->isDependentType())
+ return false;
+
+ // A class C is a replaceable class if it is eligible for replacement
+ if (!IsEligibleForReplacement(*this, D))
+ return false;
+
+ // has the replaceable_if_eligible class-property-specifier
+ if (D->hasAttr<ReplaceableAttr>())
+ return HasSuitableSMP();
+
+ // is a union with no user-declared special member functions, or
+ if (IsUnion())
+ return HasSuitableSMP();
+
+ // is default-movable.
+ return IsDefaultMovable();
+ }();
+
+ return Info;
+}
+
+static bool IsCXXTriviallyRelocatableType(Sema &S, const CXXRecordDecl *RD) {
+ if (std::optional<ASTContext::CXXRecordDeclRelocationInfo> Info =
+ S.getASTContext().getRelocationInfoForCXXRecord(RD))
+ return Info->IsRelocatable;
+ ASTContext::CXXRecordDeclRelocationInfo Info =
+ S.CheckCXX2CRelocatableAndReplaceable(RD);
+ S.getASTContext().setRelocationInfoForCXXRecord(RD, Info);
+ return Info.IsRelocatable;
+}
+
+bool Sema::IsCXXTriviallyRelocatableType(QualType Type) {
+
+ QualType BaseElementType = getASTContext().getBaseElementType(Type);
+
+ if (Type->isVariableArrayType())
+ return false;
+
+ if (BaseElementType.hasNonTrivialObjCLifetime())
+ return false;
+
+ if (BaseElementType.hasAddressDiscriminatedPointerAuth())
+ return false;
+
+ if (BaseElementType->isIncompleteType())
+ return false;
+
+ if (BaseElementType->isScalarType() || BaseElementType->isVectorType())
+ return true;
+
+ if (const auto *RD = BaseElementType->getAsCXXRecordDecl())
+ return ::IsCXXTriviallyRelocatableType(*this, RD);
+
+ return false;
+}
+
+static bool IsCXXReplaceableType(Sema &S, const CXXRecordDecl *RD) {
+ if (std::optional<ASTContext::CXXRecordDeclRelocationInfo> Info =
+ S.getASTContext().getRelocationInfoForCXXRecord(RD))
+ return Info->IsReplaceable;
+ ASTContext::CXXRecordDeclRelocationInfo Info =
+ S.CheckCXX2CRelocatableAndReplaceable(RD);
+ S.getASTContext().setRelocationInfoForCXXRecord(RD, Info);
+ return Info.IsReplaceable;
+}
+
+bool Sema::IsCXXReplaceableType(QualType Type) {
+ if (Type.isConstQualified() || Type.isVolatileQualified())
+ return false;
+
+ if (Type->isVariableArrayType())
+ return false;
+
+ QualType BaseElementType =
+ getASTContext().getBaseElementType(Type.getUnqualifiedType());
+ if (BaseElementType->isIncompleteType())
+ return false;
+ if (BaseElementType->isScalarType())
+ return true;
+ if (const auto *RD = BaseElementType->getAsCXXRecordDecl())
+ return ::IsCXXReplaceableType(*this, RD);
+ return false;
+}
+
+
+/// Checks that type T is not a VLA.
+///
+/// @returns @c true if @p T is VLA and a diagnostic was emitted,
+/// @c false otherwise.
+static bool DiagnoseVLAInCXXTypeTrait(Sema &S, const TypeSourceInfo *T,
+ clang::tok::TokenKind TypeTraitID) {
+ if (!T->getType()->isVariableArrayType())
+ return false;
+
+ S.Diag(T->getTypeLoc().getBeginLoc(), diag::err_vla_unsupported)
+ << 1 << TypeTraitID;
+ return true;
+}
+
+/// Checks that type T is not an atomic type (_Atomic).
+///
+/// @returns @c true if @p T is VLA and a diagnostic was emitted,
+/// @c false otherwise.
+static bool DiagnoseAtomicInCXXTypeTrait(Sema &S, const TypeSourceInfo *T,
+ clang::tok::TokenKind TypeTraitID) {
+ if (!T->getType()->isAtomicType())
+ return false;
+
+ S.Diag(T->getTypeLoc().getBeginLoc(), diag::err_atomic_unsupported)
+ << TypeTraitID;
+ return true;
+}
+
+/// Check the completeness of a type in a unary type trait.
+///
+/// If the particular type trait requires a complete type, tries to complete
+/// it. If completing the type fails, a diagnostic is emitted and false
+/// returned. If completing the type succeeds or no completion was required,
+/// returns true.
+static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
+ SourceLocation Loc,
+ QualType ArgTy) {
+ // C++0x [meta.unary.prop]p3:
+ // For all of the class templates X declared in this Clause, instantiating
+ // that template with a template argument that is a class template
+ // specialization may result in the implicit instantiation of the template
+ // argument if and only if the semantics of X require that the argument
+ // must be a complete type.
+ // We apply this rule to all the type trait expressions used to implement
+ // these class templates. We also try to follow any GCC documented behavior
+ // in these expressions to ensure portability of standard libraries.
+ switch (UTT) {
+ default: llvm_unreachable("not a UTT");
+ // is_complete_type somewhat obviously cannot require a complete type.
+ case UTT_IsCompleteType:
+ // Fall-through
+
+ // These traits are modeled on the type predicates in C++0x
+ // [meta.unary.cat] and [meta.unary.comp]. They are not specified as
+ // requiring a complete type, as whether or not they return true cannot be
+ // impacted by the completeness of the type.
+ case UTT_IsVoid:
+ case UTT_IsIntegral:
+ case UTT_IsFloatingPoint:
+ case UTT_IsArray:
+ case UTT_IsBoundedArray:
+ case UTT_IsPointer:
+ case UTT_IsLvalueReference:
+ case UTT_IsRvalueReference:
+ case UTT_IsMemberFunctionPointer:
+ case UTT_IsMemberObjectPointer:
+ case UTT_IsEnum:
+ case UTT_IsScopedEnum:
+ case UTT_IsUnion:
+ case UTT_IsClass:
+ case UTT_IsFunction:
+ case UTT_IsReference:
+ case UTT_IsArithmetic:
+ case UTT_IsFundamental:
+ case UTT_IsObject:
+ case UTT_IsScalar:
+ case UTT_IsCompound:
+ case UTT_IsMemberPointer:
+ case UTT_IsTypedResourceElementCompatible:
+ // Fall-through
+
+ // These traits are modeled on type predicates in C++0x [meta.unary.prop]
+ // which requires some of its traits to have the complete type. However,
+ // the completeness of the type cannot impact these traits' semantics, and
+ // so they don't require it. This matches the comments on these traits in
+ // Table 49.
+ case UTT_IsConst:
+ case UTT_IsVolatile:
+ case UTT_IsSigned:
+ case UTT_IsUnboundedArray:
+ case UTT_IsUnsigned:
+
+ // This type trait always returns false, checking the type is moot.
+ case UTT_IsInterfaceClass:
+ return true;
+
+ // We diagnose incomplete class types later.
+ case UTT_StructuredBindingSize:
+ return true;
+
+ // C++14 [meta.unary.prop]:
+ // If T is a non-union class type, T shall be a complete type.
+ case UTT_IsEmpty:
+ case UTT_IsPolymorphic:
+ case UTT_IsAbstract:
+ if (const auto *RD = ArgTy->getAsCXXRecordDecl())
+ if (!RD->isUnion())
+ return !S.RequireCompleteType(
+ Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
+ return true;
+
+ // C++14 [meta.unary.prop]:
+ // If T is a class type, T shall be a complete type.
+ case UTT_IsFinal:
+ case UTT_IsSealed:
+ if (ArgTy->getAsCXXRecordDecl())
+ return !S.RequireCompleteType(
+ Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
+ return true;
+
+ // LWG3823: T shall be an array type, a complete type, or cv void.
+ case UTT_IsAggregate:
+ case UTT_IsImplicitLifetime:
+ if (ArgTy->isArrayType() || ArgTy->isVoidType())
+ return true;
+
+ return !S.RequireCompleteType(
+ Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
+
+ // has_unique_object_representations<T>
+ // remove_all_extents_t<T> shall be a complete type or cv void (LWG4113).
+ case UTT_HasUniqueObjectRepresentations:
+ ArgTy = QualType(ArgTy->getBaseElementTypeUnsafe(), 0);
+ if (ArgTy->isVoidType())
+ return true;
+ return !S.RequireCompleteType(
+ Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
+
+ // C++1z [meta.unary.prop]:
+ // remove_all_extents_t<T> shall be a complete type or cv void.
+ case UTT_IsTrivial:
+ case UTT_IsTriviallyCopyable:
+ case UTT_IsStandardLayout:
+ case UTT_IsPOD:
+ case UTT_IsLiteral:
+ case UTT_IsBitwiseCloneable:
+ // By analogy, is_trivially_relocatable and is_trivially_equality_comparable
+ // impose the same constraints.
+ case UTT_IsTriviallyRelocatable:
+ case UTT_IsTriviallyEqualityComparable:
+ case UTT_IsCppTriviallyRelocatable:
+ case UTT_IsReplaceable:
+ case UTT_CanPassInRegs:
+ // Per the GCC type traits documentation, T shall be a complete type, cv void,
+ // or an array of unknown bound. But GCC actually imposes the same constraints
+ // as above.
+ case UTT_HasNothrowAssign:
+ case UTT_HasNothrowMoveAssign:
+ case UTT_HasNothrowConstructor:
+ case UTT_HasNothrowCopy:
+ case UTT_HasTrivialAssign:
+ case UTT_HasTrivialMoveAssign:
+ case UTT_HasTrivialDefaultConstructor:
+ case UTT_HasTrivialMoveConstructor:
+ case UTT_HasTrivialCopy:
+ case UTT_HasTrivialDestructor:
+ case UTT_HasVirtualDestructor:
+ ArgTy = QualType(ArgTy->getBaseElementTypeUnsafe(), 0);
+ [[fallthrough]];
+ // C++1z [meta.unary.prop]:
+ // T shall be a complete type, cv void, or an array of unknown bound.
+ case UTT_IsDestructible:
+ case UTT_IsNothrowDestructible:
+ case UTT_IsTriviallyDestructible:
+ case UTT_IsIntangibleType:
+ if (ArgTy->isIncompleteArrayType() || ArgTy->isVoidType())
+ return true;
+
+ return !S.RequireCompleteType(
+ Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
+ }
+}
+
+static bool HasNoThrowOperator(const RecordType *RT, OverloadedOperatorKind Op,
+ Sema &Self, SourceLocation KeyLoc, ASTContext &C,
+ bool (CXXRecordDecl::*HasTrivial)() const,
+ bool (CXXRecordDecl::*HasNonTrivial)() const,
+ bool (CXXMethodDecl::*IsDesiredOp)() const)
+{
+ CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
+ if ((RD->*HasTrivial)() && !(RD->*HasNonTrivial)())
+ return true;
+
+ DeclarationName Name = C.DeclarationNames.getCXXOperatorName(Op);
+ DeclarationNameInfo NameInfo(Name, KeyLoc);
+ LookupResult Res(Self, NameInfo, Sema::LookupOrdinaryName);
+ if (Self.LookupQualifiedName(Res, RD)) {
+ bool FoundOperator = false;
+ Res.suppressDiagnostics();
+ for (LookupResult::iterator Op = Res.begin(), OpEnd = Res.end();
+ Op != OpEnd; ++Op) {
+ if (isa<FunctionTemplateDecl>(*Op))
+ continue;
+
+ CXXMethodDecl *Operator = cast<CXXMethodDecl>(*Op);
+ if((Operator->*IsDesiredOp)()) {
+ FoundOperator = true;
+ auto *CPT = Operator->getType()->castAs<FunctionProtoType>();
+ CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
+ if (!CPT || !CPT->isNothrow())
+ return false;
+ }
+ }
+ return FoundOperator;
+ }
+ return false;
+}
+
+static bool HasNonDeletedDefaultedEqualityComparison(Sema &S,
+ const CXXRecordDecl *Decl,
+ SourceLocation KeyLoc) {
+ if (Decl->isUnion())
+ return false;
+ if (Decl->isLambda())
+ return Decl->isCapturelessLambda();
+
+ {
+ EnterExpressionEvaluationContext UnevaluatedContext(
+ S, Sema::ExpressionEvaluationContext::Unevaluated);
+ Sema::SFINAETrap SFINAE(S, /*ForValidityCheck=*/true);
+ Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());
+
+ // const ClassT& obj;
+ OpaqueValueExpr Operand(
+ KeyLoc,
+ Decl->getTypeForDecl()->getCanonicalTypeUnqualified().withConst(),
+ ExprValueKind::VK_LValue);
+ UnresolvedSet<16> Functions;
+ // obj == obj;
+ S.LookupBinOp(S.TUScope, {}, BinaryOperatorKind::BO_EQ, Functions);
+
+ auto Result = S.CreateOverloadedBinOp(KeyLoc, BinaryOperatorKind::BO_EQ,
+ Functions, &Operand, &Operand);
+ if (Result.isInvalid() || SFINAE.hasErrorOccurred())
+ return false;
+
+ const auto *CallExpr = dyn_cast<CXXOperatorCallExpr>(Result.get());
+ if (!CallExpr)
+ return false;
+ const auto *Callee = CallExpr->getDirectCallee();
+ auto ParamT = Callee->getParamDecl(0)->getType();
+ if (!Callee->isDefaulted())
+ return false;
+ if (!ParamT->isReferenceType() && !Decl->isTriviallyCopyable())
+ return false;
+ if (ParamT.getNonReferenceType()->getUnqualifiedDesugaredType() !=
+ Decl->getTypeForDecl())
+ return false;
+ }
+
+ return llvm::all_of(Decl->bases(),
+ [&](const CXXBaseSpecifier &BS) {
+ if (const auto *RD = BS.getType()->getAsCXXRecordDecl())
+ return HasNonDeletedDefaultedEqualityComparison(
+ S, RD, KeyLoc);
+ return true;
+ }) &&
+ llvm::all_of(Decl->fields(), [&](const FieldDecl *FD) {
+ auto Type = FD->getType();
+ if (Type->isArrayType())
+ Type = Type->getBaseElementTypeUnsafe()
+ ->getCanonicalTypeUnqualified();
+
+ if (Type->isReferenceType() || Type->isEnumeralType())
+ return false;
+ if (const auto *RD = Type->getAsCXXRecordDecl())
+ return HasNonDeletedDefaultedEqualityComparison(S, RD, KeyLoc);
+ return true;
+ });
+}
+
+static bool isTriviallyEqualityComparableType(Sema &S, QualType Type, SourceLocation KeyLoc) {
+ QualType CanonicalType = Type.getCanonicalType();
+ if (CanonicalType->isIncompleteType() || CanonicalType->isDependentType() ||
+ CanonicalType->isEnumeralType() || CanonicalType->isArrayType())
+ return false;
+
+ if (const auto *RD = CanonicalType->getAsCXXRecordDecl()) {
+ if (!HasNonDeletedDefaultedEqualityComparison(S, RD, KeyLoc))
+ return false;
+ }
+
+ return S.getASTContext().hasUniqueObjectRepresentations(
+ CanonicalType, /*CheckIfTriviallyCopyable=*/false);
+}
+
+static bool IsTriviallyRelocatableType(Sema &SemaRef, QualType T) {
+ QualType BaseElementType = SemaRef.getASTContext().getBaseElementType(T);
+
+ if (BaseElementType->isIncompleteType())
+ return false;
+ if (!BaseElementType->isObjectType())
+ return false;
+
+ if (T.hasAddressDiscriminatedPointerAuth())
+ return false;
+
+ if (const auto *RD = BaseElementType->getAsCXXRecordDecl();
+ RD && !RD->isPolymorphic() && IsCXXTriviallyRelocatableType(SemaRef, RD))
+ return true;
+
+ if (const auto *RD = BaseElementType->getAsRecordDecl())
+ return RD->canPassInRegisters();
+
+ if (BaseElementType.isTriviallyCopyableType(SemaRef.getASTContext()))
+ return true;
+
+ switch (T.isNonTrivialToPrimitiveDestructiveMove()) {
+ case QualType::PCK_Trivial:
+ return !T.isDestructedType();
+ case QualType::PCK_ARCStrong:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
+ SourceLocation KeyLoc,
+ TypeSourceInfo *TInfo) {
+ QualType T = TInfo->getType();
+ assert(!T->isDependentType() && "Cannot evaluate traits of dependent type");
+
+ ASTContext &C = Self.Context;
+ switch(UTT) {
+ default: llvm_unreachable("not a UTT");
+ // Type trait expressions corresponding to the primary type category
+ // predicates in C++0x [meta.unary.cat].
+ case UTT_IsVoid:
+ return T->isVoidType();
+ case UTT_IsIntegral:
+ return T->isIntegralType(C);
+ case UTT_IsFloatingPoint:
+ return T->isFloatingType();
+ case UTT_IsArray:
+ // Zero-sized arrays aren't considered arrays in partial specializations,
+ // so __is_array shouldn't consider them arrays either.
+ if (const auto *CAT = C.getAsConstantArrayType(T))
+ return CAT->getSize() != 0;
+ return T->isArrayType();
+ case UTT_IsBoundedArray:
+ if (DiagnoseVLAInCXXTypeTrait(Self, TInfo, tok::kw___is_bounded_array))
+ return false;
+ // Zero-sized arrays aren't considered arrays in partial specializations,
+ // so __is_bounded_array shouldn't consider them arrays either.
+ if (const auto *CAT = C.getAsConstantArrayType(T))
+ return CAT->getSize() != 0;
+ return T->isArrayType() && !T->isIncompleteArrayType();
+ case UTT_IsUnboundedArray:
+ if (DiagnoseVLAInCXXTypeTrait(Self, TInfo, tok::kw___is_unbounded_array))
+ return false;
+ return T->isIncompleteArrayType();
+ case UTT_IsPointer:
+ return T->isAnyPointerType();
+ case UTT_IsLvalueReference:
+ return T->isLValueReferenceType();
+ case UTT_IsRvalueReference:
+ return T->isRValueReferenceType();
+ case UTT_IsMemberFunctionPointer:
+ return T->isMemberFunctionPointerType();
+ case UTT_IsMemberObjectPointer:
+ return T->isMemberDataPointerType();
+ case UTT_IsEnum:
+ return T->isEnumeralType();
+ case UTT_IsScopedEnum:
+ return T->isScopedEnumeralType();
+ case UTT_IsUnion:
+ return T->isUnionType();
+ case UTT_IsClass:
+ return T->isClassType() || T->isStructureType() || T->isInterfaceType();
+ case UTT_IsFunction:
+ return T->isFunctionType();
+
+ // Type trait expressions which correspond to the convenient composition
+ // predicates in C++0x [meta.unary.comp].
+ case UTT_IsReference:
+ return T->isReferenceType();
+ case UTT_IsArithmetic:
+ return T->isArithmeticType() && !T->isEnumeralType();
+ case UTT_IsFundamental:
+ return T->isFundamentalType();
+ case UTT_IsObject:
+ return T->isObjectType();
+ case UTT_IsScalar:
+ // Note: semantic analysis depends on Objective-C lifetime types to be
+ // considered scalar types. However, such types do not actually behave
+ // like scalar types at run time (since they may require retain/release
+ // operations), so we report them as non-scalar.
+ if (T->isObjCLifetimeType()) {
+ switch (T.getObjCLifetime()) {
+ case Qualifiers::OCL_None:
+ case Qualifiers::OCL_ExplicitNone:
+ return true;
+
+ case Qualifiers::OCL_Strong:
+ case Qualifiers::OCL_Weak:
+ case Qualifiers::OCL_Autoreleasing:
+ return false;
+ }
+ }
+
+ return T->isScalarType();
+ case UTT_IsCompound:
+ return T->isCompoundType();
+ case UTT_IsMemberPointer:
+ return T->isMemberPointerType();
+
+ // Type trait expressions which correspond to the type property predicates
+ // in C++0x [meta.unary.prop].
+ case UTT_IsConst:
+ return T.isConstQualified();
+ case UTT_IsVolatile:
+ return T.isVolatileQualified();
+ case UTT_IsTrivial:
+ return T.isTrivialType(C);
+ case UTT_IsTriviallyCopyable:
+ return T.isTriviallyCopyableType(C);
+ case UTT_IsStandardLayout:
+ return T->isStandardLayoutType();
+ case UTT_IsPOD:
+ return T.isPODType(C);
+ case UTT_IsLiteral:
+ return T->isLiteralType(C);
+ case UTT_IsEmpty:
+ if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
+ return !RD->isUnion() && RD->isEmpty();
+ return false;
+ case UTT_IsPolymorphic:
+ if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
+ return !RD->isUnion() && RD->isPolymorphic();
+ return false;
+ case UTT_IsAbstract:
+ if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
+ return !RD->isUnion() && RD->isAbstract();
+ return false;
+ case UTT_IsAggregate:
+ // Report vector extensions and complex types as aggregates because they
+ // support aggregate initialization. GCC mirrors this behavior for vectors
+ // but not _Complex.
+ return T->isAggregateType() || T->isVectorType() || T->isExtVectorType() ||
+ T->isAnyComplexType();
+ // __is_interface_class only returns true when CL is invoked in /CLR mode and
+ // even then only when it is used with the 'interface struct ...' syntax
+ // Clang doesn't support /CLR which makes this type trait moot.
+ case UTT_IsInterfaceClass:
+ return false;
+ case UTT_IsFinal:
+ case UTT_IsSealed:
+ if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
+ return RD->hasAttr<FinalAttr>();
+ return false;
+ case UTT_IsSigned:
+ // Enum types should always return false.
+ // Floating points should always return true.
+ return T->isFloatingType() ||
+ (T->isSignedIntegerType() && !T->isEnumeralType());
+ case UTT_IsUnsigned:
+ // Enum types should always return false.
+ return T->isUnsignedIntegerType() && !T->isEnumeralType();
+
+ // Type trait expressions which query classes regarding their construction,
+ // destruction, and copying. Rather than being based directly on the
+ // related type predicates in the standard, they are specified by both
+ // GCC[1] and the Embarcadero C++ compiler[2], and Clang implements those
+ // specifications.
+ //
+ // 1: http://gcc.gnu/.org/onlinedocs/gcc/Type-Traits.html
+ // 2: http://docwiki.embarcadero.com/RADStudio/XE/en/Type_Trait_Functions_(C%2B%2B0x)_Index
+ //
+ // Note that these builtins do not behave as documented in g++: if a class
+ // has both a trivial and a non-trivial special member of a particular kind,
+ // they return false! For now, we emulate this behavior.
+ // FIXME: This appears to be a g++ bug: more complex cases reveal that it
+ // does not correctly compute triviality in the presence of multiple special
+ // members of the same kind. Revisit this once the g++ bug is fixed.
+ case UTT_HasTrivialDefaultConstructor:
+ // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
+ // If __is_pod (type) is true then the trait is true, else if type is
+ // a cv class or union type (or array thereof) with a trivial default
+ // constructor ([class.ctor]) then the trait is true, else it is false.
+ if (T.isPODType(C))
+ return true;
+ if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl())
+ return RD->hasTrivialDefaultConstructor() &&
+ !RD->hasNonTrivialDefaultConstructor();
+ return false;
+ case UTT_HasTrivialMoveConstructor:
+ // This trait is implemented by MSVC 2012 and needed to parse the
+ // standard library headers. Specifically this is used as the logic
+ // behind std::is_trivially_move_constructible (20.9.4.3).
+ if (T.isPODType(C))
+ return true;
+ if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl())
+ return RD->hasTrivialMoveConstructor() && !RD->hasNonTrivialMoveConstructor();
+ return false;
+ case UTT_HasTrivialCopy:
+ // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
+ // If __is_pod (type) is true or type is a reference type then
+ // the trait is true, else if type is a cv class or union type
+ // with a trivial copy constructor ([class.copy]) then the trait
+ // is true, else it is false.
+ if (T.isPODType(C) || T->isReferenceType())
+ return true;
+ if (CXXRecordDecl *RD = T->getAsCXXRecordDecl())
+ return RD->hasTrivialCopyConstructor() &&
+ !RD->hasNonTrivialCopyConstructor();
+ return false;
+ case UTT_HasTrivialMoveAssign:
+ // This trait is implemented by MSVC 2012 and needed to parse the
+ // standard library headers. Specifically it is used as the logic
+ // behind std::is_trivially_move_assignable (20.9.4.3)
+ if (T.isPODType(C))
+ return true;
+ if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl())
+ return RD->hasTrivialMoveAssignment() && !RD->hasNonTrivialMoveAssignment();
+ return false;
+ case UTT_HasTrivialAssign:
+ // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
+ // If type is const qualified or is a reference type then the
+ // trait is false. Otherwise if __is_pod (type) is true then the
+ // trait is true, else if type is a cv class or union type with
+ // a trivial copy assignment ([class.copy]) then the trait is
+ // true, else it is false.
+ // Note: the const and reference restrictions are interesting,
+ // given that const and reference members don't prevent a class
+ // from having a trivial copy assignment operator (but do cause
+ // errors if the copy assignment operator is actually used, q.v.
+ // [class.copy]p12).
+
+ if (T.isConstQualified())
+ return false;
+ if (T.isPODType(C))
+ return true;
+ if (CXXRecordDecl *RD = T->getAsCXXRecordDecl())
+ return RD->hasTrivialCopyAssignment() &&
+ !RD->hasNonTrivialCopyAssignment();
+ return false;
+ case UTT_IsDestructible:
+ case UTT_IsTriviallyDestructible:
+ case UTT_IsNothrowDestructible:
+ // C++14 [meta.unary.prop]:
+ // For reference types, is_destructible<T>::value is true.
+ if (T->isReferenceType())
+ return true;
+
+ // Objective-C++ ARC: autorelease types don't require destruction.
+ if (T->isObjCLifetimeType() &&
+ T.getObjCLifetime() == Qualifiers::OCL_Autoreleasing)
+ return true;
+
+ // C++14 [meta.unary.prop]:
+ // For incomplete types and function types, is_destructible<T>::value is
+ // false.
+ if (T->isIncompleteType() || T->isFunctionType())
+ return false;
+
+ // A type that requires destruction (via a non-trivial destructor or ARC
+ // lifetime semantics) is not trivially-destructible.
+ if (UTT == UTT_IsTriviallyDestructible && T.isDestructedType())
+ return false;
+
+ // C++14 [meta.unary.prop]:
+ // For object types and given U equal to remove_all_extents_t<T>, if the
+ // expression std::declval<U&>().~U() is well-formed when treated as an
+ // unevaluated operand (Clause 5), then is_destructible<T>::value is true
+ if (auto *RD = C.getBaseElementType(T)->getAsCXXRecordDecl()) {
+ CXXDestructorDecl *Destructor = Self.LookupDestructor(RD);
+ if (!Destructor)
+ return false;
+ // C++14 [dcl.fct.def.delete]p2:
+ // A program that refers to a deleted function implicitly or
+ // explicitly, other than to declare it, is ill-formed.
+ if (Destructor->isDeleted())
+ return false;
+ if (C.getLangOpts().AccessControl && Destructor->getAccess() != AS_public)
+ return false;
+ if (UTT == UTT_IsNothrowDestructible) {
+ auto *CPT = Destructor->getType()->castAs<FunctionProtoType>();
+ CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
+ if (!CPT || !CPT->isNothrow())
+ return false;
+ }
+ }
+ return true;
+
+ case UTT_HasTrivialDestructor:
+ // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html
+ // If __is_pod (type) is true or type is a reference type
+ // then the trait is true, else if type is a cv class or union
+ // type (or array thereof) with a trivial destructor
+ // ([class.dtor]) then the trait is true, else it is
+ // false.
+ if (T.isPODType(C) || T->isReferenceType())
+ return true;
+
+ // Objective-C++ ARC: autorelease types don't require destruction.
+ if (T->isObjCLifetimeType() &&
+ T.getObjCLifetime() == Qualifiers::OCL_Autoreleasing)
+ return true;
+
+ if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl())
+ return RD->hasTrivialDestructor();
+ return false;
+ // TODO: Propagate nothrowness for implicitly declared special members.
+ case UTT_HasNothrowAssign:
+ // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
+ // If type is const qualified or is a reference type then the
+ // trait is false. Otherwise if __has_trivial_assign (type)
+ // is true then the trait is true, else if type is a cv class
+ // or union type with copy assignment operators that are known
+ // not to throw an exception then the trait is true, else it is
+ // false.
+ if (C.getBaseElementType(T).isConstQualified())
+ return false;
+ if (T->isReferenceType())
+ return false;
+ if (T.isPODType(C) || T->isObjCLifetimeType())
+ return true;
+
+ if (const RecordType *RT = T->getAs<RecordType>())
+ return HasNoThrowOperator(RT, OO_Equal, Self, KeyLoc, C,
+ &CXXRecordDecl::hasTrivialCopyAssignment,
+ &CXXRecordDecl::hasNonTrivialCopyAssignment,
+ &CXXMethodDecl::isCopyAssignmentOperator);
+ return false;
+ case UTT_HasNothrowMoveAssign:
+ // This trait is implemented by MSVC 2012 and needed to parse the
+ // standard library headers. Specifically this is used as the logic
+ // behind std::is_nothrow_move_assignable (20.9.4.3).
+ if (T.isPODType(C))
+ return true;
+
+ if (const RecordType *RT = C.getBaseElementType(T)->getAs<RecordType>())
+ return HasNoThrowOperator(RT, OO_Equal, Self, KeyLoc, C,
+ &CXXRecordDecl::hasTrivialMoveAssignment,
+ &CXXRecordDecl::hasNonTrivialMoveAssignment,
+ &CXXMethodDecl::isMoveAssignmentOperator);
+ return false;
+ case UTT_HasNothrowCopy:
+ // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
+ // If __has_trivial_copy (type) is true then the trait is true, else
+ // if type is a cv class or union type with copy constructors that are
+ // known not to throw an exception then the trait is true, else it is
+ // false.
+ if (T.isPODType(C) || T->isReferenceType() || T->isObjCLifetimeType())
+ return true;
+ if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) {
+ if (RD->hasTrivialCopyConstructor() &&
+ !RD->hasNonTrivialCopyConstructor())
+ return true;
+
+ bool FoundConstructor = false;
+ unsigned FoundTQs;
+ for (const auto *ND : Self.LookupConstructors(RD)) {
+ // A template constructor is never a copy constructor.
+ // FIXME: However, it may actually be selected at the actual overload
+ // resolution point.
+ if (isa<FunctionTemplateDecl>(ND->getUnderlyingDecl()))
+ continue;
+ // UsingDecl itself is not a constructor
+ if (isa<UsingDecl>(ND))
+ continue;
+ auto *Constructor = cast<CXXConstructorDecl>(ND->getUnderlyingDecl());
+ if (Constructor->isCopyConstructor(FoundTQs)) {
+ FoundConstructor = true;
+ auto *CPT = Constructor->getType()->castAs<FunctionProtoType>();
+ CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
+ if (!CPT)
+ return false;
+ // TODO: check whether evaluating default arguments can throw.
+ // For now, we'll be conservative and assume that they can throw.
+ if (!CPT->isNothrow() || CPT->getNumParams() > 1)
+ return false;
+ }
+ }
+
+ return FoundConstructor;
+ }
+ return false;
+ case UTT_HasNothrowConstructor:
+ // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html
+ // If __has_trivial_constructor (type) is true then the trait is
+ // true, else if type is a cv class or union type (or array
+ // thereof) with a default constructor that is known not to
+ // throw an exception then the trait is true, else it is false.
+ if (T.isPODType(C) || T->isObjCLifetimeType())
+ return true;
+ if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl()) {
+ if (RD->hasTrivialDefaultConstructor() &&
+ !RD->hasNonTrivialDefaultConstructor())
+ return true;
+
+ bool FoundConstructor = false;
+ for (const auto *ND : Self.LookupConstructors(RD)) {
+ // FIXME: In C++0x, a constructor template can be a default constructor.
+ if (isa<FunctionTemplateDecl>(ND->getUnderlyingDecl()))
+ continue;
+ // UsingDecl itself is not a constructor
+ if (isa<UsingDecl>(ND))
+ continue;
+ auto *Constructor = cast<CXXConstructorDecl>(ND->getUnderlyingDecl());
+ if (Constructor->isDefaultConstructor()) {
+ FoundConstructor = true;
+ auto *CPT = Constructor->getType()->castAs<FunctionProtoType>();
+ CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
+ if (!CPT)
+ return false;
+ // FIXME: check whether evaluating default arguments can throw.
+ // For now, we'll be conservative and assume that they can throw.
+ if (!CPT->isNothrow() || CPT->getNumParams() > 0)
+ return false;
+ }
+ }
+ return FoundConstructor;
+ }
+ return false;
+ case UTT_HasVirtualDestructor:
+ // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
+ // If type is a class type with a virtual destructor ([class.dtor])
+ // then the trait is true, else it is false.
+ if (CXXRecordDecl *RD = T->getAsCXXRecordDecl())
+ if (CXXDestructorDecl *Destructor = Self.LookupDestructor(RD))
+ return Destructor->isVirtual();
+ return false;
+
+ // These type trait expressions are modeled on the specifications for the
+ // Embarcadero C++0x type trait functions:
+ // http://docwiki.embarcadero.com/RADStudio/XE/en/Type_Trait_Functions_(C%2B%2B0x)_Index
+ case UTT_IsCompleteType:
+ // http://docwiki.embarcadero.com/RADStudio/XE/en/Is_complete_type_(typename_T_):
+ // Returns True if and only if T is a complete type at the point of the
+ // function call.
+ return !T->isIncompleteType();
+ case UTT_HasUniqueObjectRepresentations:
+ return C.hasUniqueObjectRepresentations(T);
+ case UTT_IsTriviallyRelocatable:
+ return IsTriviallyRelocatableType(Self, T);
+ case UTT_IsBitwiseCloneable:
+ return T.isBitwiseCloneableType(C);
+ case UTT_IsCppTriviallyRelocatable:
+ return Self.IsCXXTriviallyRelocatableType(T);
+ case UTT_IsReplaceable:
+ return Self.IsCXXReplaceableType(T);
+ case UTT_CanPassInRegs:
+ if (CXXRecordDecl *RD = T->getAsCXXRecordDecl(); RD && !T.hasQualifiers())
+ return RD->canPassInRegisters();
+ Self.Diag(KeyLoc, diag::err_builtin_pass_in_regs_non_class) << T;
+ return false;
+ case UTT_IsTriviallyEqualityComparable:
+ return isTriviallyEqualityComparableType(Self, T, KeyLoc);
+ case UTT_IsImplicitLifetime: {
+ DiagnoseVLAInCXXTypeTrait(Self, TInfo,
+ tok::kw___builtin_is_implicit_lifetime);
+ DiagnoseAtomicInCXXTypeTrait(Self, TInfo,
+ tok::kw___builtin_is_implicit_lifetime);
+
+ // [basic.types.general] p9
+ // Scalar types, implicit-lifetime class types ([class.prop]),
+ // array types, and cv-qualified versions of these types
+ // are collectively called implicit-lifetime types.
+ QualType UnqualT = T->getCanonicalTypeUnqualified();
+ if (UnqualT->isScalarType())
+ return true;
+ if (UnqualT->isArrayType() || UnqualT->isVectorType())
+ return true;
+ const CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl();
+ if (!RD)
+ return false;
+
+ // [class.prop] p9
+ // A class S is an implicit-lifetime class if
+ // - it is an aggregate whose destructor is not user-provided or
+ // - it has at least one trivial eligible constructor and a trivial,
+ // non-deleted destructor.
+ const CXXDestructorDecl *Dtor = RD->getDestructor();
+ if (UnqualT->isAggregateType())
+ if (Dtor && !Dtor->isUserProvided())
+ return true;
+ if (RD->hasTrivialDestructor() && (!Dtor || !Dtor->isDeleted()))
+ if (RD->hasTrivialDefaultConstructor() ||
+ RD->hasTrivialCopyConstructor() || RD->hasTrivialMoveConstructor())
+ return true;
+ return false;
+ }
+ case UTT_IsIntangibleType:
+ assert(Self.getLangOpts().HLSL && "intangible types are HLSL-only feature");
+ if (!T->isVoidType() && !T->isIncompleteArrayType())
+ if (Self.RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), T,
+ diag::err_incomplete_type))
+ return false;
+ if (DiagnoseVLAInCXXTypeTrait(Self, TInfo,
+ tok::kw___builtin_hlsl_is_intangible))
+ return false;
+ return T->isHLSLIntangibleType();
+
+ case UTT_IsTypedResourceElementCompatible:
+ assert(Self.getLangOpts().HLSL &&
+ "typed resource element compatible types are an HLSL-only feature");
+ if (T->isIncompleteType())
+ return false;
+
+ return Self.HLSL().IsTypedResourceElementCompatible(T);
+ }
+}
+
+
+
+static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
+ const TypeSourceInfo *Rhs, SourceLocation KeyLoc);
+
+static ExprResult CheckConvertibilityForTypeTraits(
+ Sema &Self, const TypeSourceInfo *Lhs, const TypeSourceInfo *Rhs,
+ SourceLocation KeyLoc, llvm::BumpPtrAllocator &OpaqueExprAllocator) {
+
+ QualType LhsT = Lhs->getType();
+ QualType RhsT = Rhs->getType();
+
+ // 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 ExprError();
+
+ // A function definition requires a complete, non-abstract return type.
+ if (!Self.isCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT) ||
+ Self.isAbstractType(Rhs->getTypeLoc().getBeginLoc(), RhsT))
+ return ExprError();
+
+ // 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));
+ Expr *From = new (OpaqueExprAllocator.Allocate<OpaqueValueExpr>())
+ OpaqueValueExpr(KeyLoc, LhsT.getNonLValueExprType(Self.Context),
+ Expr::getValueKindForType(LhsT));
+ 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, /*ForValidityCheck=*/true);
+ Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl());
+ InitializationSequence Init(Self, To, Kind, From);
+ if (Init.Failed())
+ return ExprError();
+
+ ExprResult Result = Init.Perform(Self, To, Kind, From);
+ if (Result.isInvalid() || SFINAE.hasErrorOccurred())
+ return ExprError();
+
+ return Result;
+}
+
+static APValue EvaluateSizeTTypeTrait(Sema &S, TypeTrait Kind,
+ SourceLocation KWLoc,
+ ArrayRef<TypeSourceInfo *> Args,
+ SourceLocation RParenLoc,
+ bool IsDependent) {
+ if (IsDependent)
+ return APValue();
+
+ switch (Kind) {
+ case TypeTrait::UTT_StructuredBindingSize: {
+ QualType T = Args[0]->getType();
+ SourceRange ArgRange = Args[0]->getTypeLoc().getSourceRange();
+ UnsignedOrNone Size =
+ S.GetDecompositionElementCount(T, ArgRange.getBegin());
+ if (!Size) {
+ S.Diag(KWLoc, diag::err_arg_is_not_destructurable) << T << ArgRange;
+ return APValue();
+ }
+ return APValue(
+ S.getASTContext().MakeIntValue(*Size, S.getASTContext().getSizeType()));
+ break;
+ }
+ default:
+ llvm_unreachable("Not a SizeT type trait");
+ }
+}
+
+static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
+ SourceLocation KWLoc,
+ ArrayRef<TypeSourceInfo *> Args,
+ SourceLocation RParenLoc,
+ bool IsDependent) {
+ if (IsDependent)
+ return false;
+
+ if (Kind <= UTT_Last)
+ return EvaluateUnaryTypeTrait(S, Kind, KWLoc, Args[0]);
+
+ // Evaluate ReferenceBindsToTemporary and ReferenceConstructsFromTemporary
+ // alongside the IsConstructible traits to avoid duplication.
+ if (Kind <= BTT_Last && Kind != BTT_ReferenceBindsToTemporary &&
+ Kind != BTT_ReferenceConstructsFromTemporary &&
+ Kind != BTT_ReferenceConvertsFromTemporary)
+ return EvaluateBinaryTypeTrait(S, Kind, Args[0],
+ Args[1], RParenLoc);
+
+ switch (Kind) {
+ case clang::BTT_ReferenceBindsToTemporary:
+ case clang::BTT_ReferenceConstructsFromTemporary:
+ case clang::BTT_ReferenceConvertsFromTemporary:
+ case clang::TT_IsConstructible:
+ case clang::TT_IsNothrowConstructible:
+ case clang::TT_IsTriviallyConstructible: {
+ // C++11 [meta.unary.prop]:
+ // is_trivially_constructible is defined as:
+ //
+ // is_constructible<T, Args...>::value is true and the variable
+ // definition for is_constructible, as defined below, is known to call
+ // no operation that is not trivial.
+ //
+ // The predicate condition for a template specialization
+ // is_constructible<T, Args...> shall be satisfied if and only if the
+ // following variable definition would be well-formed for some invented
+ // variable t:
+ //
+ // T t(create<Args>()...);
+ assert(!Args.empty());
+
+ // Precondition: T and all types in the parameter pack Args shall be
+ // complete types, (possibly cv-qualified) void, or arrays of
+ // unknown bound.
+ 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 false;
+ }
+
+ // Make sure the first argument is not incomplete nor a function type.
+ QualType T = Args[0]->getType();
+ if (T->isIncompleteType() || T->isFunctionType())
+ return false;
+
+ // Make sure the first argument is not an abstract type.
+ CXXRecordDecl *RD = T->getAsCXXRecordDecl();
+ if (RD && RD->isAbstract())
+ return false;
+
+ llvm::BumpPtrAllocator OpaqueExprAllocator;
+ SmallVector<Expr *, 2> ArgExprs;
+ ArgExprs.reserve(Args.size() - 1);
+ for (unsigned I = 1, N = Args.size(); I != N; ++I) {
+ QualType ArgTy = Args[I]->getType();
+ if (ArgTy->isObjectType() || ArgTy->isFunctionType())
+ ArgTy = S.Context.getRValueReferenceType(ArgTy);
+ ArgExprs.push_back(
+ new (OpaqueExprAllocator.Allocate<OpaqueValueExpr>())
+ OpaqueValueExpr(Args[I]->getTypeLoc().getBeginLoc(),
+ ArgTy.getNonLValueExprType(S.Context),
+ Expr::getValueKindForType(ArgTy)));
+ }
+
+ // Perform the initialization in an unevaluated context within a SFINAE
+ // trap at translation unit scope.
+ EnterExpressionEvaluationContext Unevaluated(
+ S, Sema::ExpressionEvaluationContext::Unevaluated);
+ Sema::SFINAETrap SFINAE(S, /*ForValidityCheck=*/true);
+ Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());
+ InitializedEntity To(
+ InitializedEntity::InitializeTemporary(S.Context, Args[0]));
+ InitializationKind InitKind(
+ Kind == clang::BTT_ReferenceConvertsFromTemporary
+ ? InitializationKind::CreateCopy(KWLoc, KWLoc)
+ : InitializationKind::CreateDirect(KWLoc, KWLoc, RParenLoc));
+ InitializationSequence Init(S, To, InitKind, ArgExprs);
+ if (Init.Failed())
+ return false;
+
+ ExprResult Result = Init.Perform(S, To, InitKind, ArgExprs);
+ if (Result.isInvalid() || SFINAE.hasErrorOccurred())
+ return false;
+
+ if (Kind == clang::TT_IsConstructible)
+ return true;
+
+ if (Kind == clang::BTT_ReferenceBindsToTemporary ||
+ Kind == clang::BTT_ReferenceConstructsFromTemporary ||
+ Kind == clang::BTT_ReferenceConvertsFromTemporary) {
+ if (!T->isReferenceType())
+ return false;
+
+ if (!Init.isDirectReferenceBinding())
+ return true;
+
+ if (Kind == clang::BTT_ReferenceBindsToTemporary)
+ return false;
+
+ QualType U = Args[1]->getType();
+ if (U->isReferenceType())
+ return false;
+
+ TypeSourceInfo *TPtr = S.Context.CreateTypeSourceInfo(
+ S.Context.getPointerType(T.getNonReferenceType()));
+ TypeSourceInfo *UPtr = S.Context.CreateTypeSourceInfo(
+ S.Context.getPointerType(U.getNonReferenceType()));
+ return !CheckConvertibilityForTypeTraits(S, UPtr, TPtr, RParenLoc,
+ OpaqueExprAllocator)
+ .isInvalid();
+ }
+
+ if (Kind == clang::TT_IsNothrowConstructible)
+ return S.canThrow(Result.get()) == CT_Cannot;
+
+ if (Kind == clang::TT_IsTriviallyConstructible) {
+ // Under Objective-C ARC and Weak, if the destination has non-trivial
+ // Objective-C lifetime, this is a non-trivial construction.
+ if (T.getNonReferenceType().hasNonTrivialObjCLifetime())
+ return false;
+
+ // The initialization succeeded; now make sure there are no non-trivial
+ // calls.
+ return !Result.get()->hasNonTrivialCall(S.Context);
+ }
+
+ llvm_unreachable("unhandled type trait");
+ return false;
+ }
+ default: llvm_unreachable("not a TT");
+ }
+
+ return false;
+}
+
+namespace {
+void DiagnoseBuiltinDeprecation(Sema& S, TypeTrait Kind,
+ SourceLocation KWLoc) {
+ TypeTrait Replacement;
+ switch (Kind) {
+ case UTT_HasNothrowAssign:
+ case UTT_HasNothrowMoveAssign:
+ Replacement = BTT_IsNothrowAssignable;
+ break;
+ case UTT_HasNothrowCopy:
+ case UTT_HasNothrowConstructor:
+ Replacement = TT_IsNothrowConstructible;
+ break;
+ case UTT_HasTrivialAssign:
+ case UTT_HasTrivialMoveAssign:
+ Replacement = BTT_IsTriviallyAssignable;
+ break;
+ case UTT_HasTrivialCopy:
+ Replacement = UTT_IsTriviallyCopyable;
+ break;
+ case UTT_HasTrivialDefaultConstructor:
+ case UTT_HasTrivialMoveConstructor:
+ Replacement = TT_IsTriviallyConstructible;
+ break;
+ case UTT_HasTrivialDestructor:
+ Replacement = UTT_IsTriviallyDestructible;
+ break;
+ case UTT_IsTriviallyRelocatable:
+ Replacement = clang::UTT_IsCppTriviallyRelocatable;
+ break;
+ default:
+ return;
+ }
+ S.Diag(KWLoc, diag::warn_deprecated_builtin)
+ << getTraitSpelling(Kind) << getTraitSpelling(Replacement);
+}
+} // namespace
+
+bool Sema::CheckTypeTraitArity(unsigned Arity, SourceLocation Loc, size_t N) {
+ if (Arity && N != Arity) {
+ Diag(Loc, diag::err_type_trait_arity)
+ << Arity << 0 << (Arity > 1) << (int)N << SourceRange(Loc);
+ return false;
+ }
+
+ if (!Arity && N == 0) {
+ Diag(Loc, diag::err_type_trait_arity)
+ << 1 << 1 << 1 << (int)N << SourceRange(Loc);
+ return false;
+ }
+ return true;
+}
+
+enum class TypeTraitReturnType {
+ Bool,
+ SizeT,
+};
+
+static TypeTraitReturnType GetReturnType(TypeTrait Kind) {
+ if (Kind == TypeTrait::UTT_StructuredBindingSize)
+ return TypeTraitReturnType::SizeT;
+ return TypeTraitReturnType::Bool;
+}
+
+ExprResult Sema::BuildTypeTrait(TypeTrait Kind, SourceLocation KWLoc,
+ ArrayRef<TypeSourceInfo *> Args,
+ SourceLocation RParenLoc) {
+ if (!CheckTypeTraitArity(getTypeTraitArity(Kind), KWLoc, Args.size()))
+ return ExprError();
+
+ if (Kind <= UTT_Last && !CheckUnaryTypeTraitTypeCompleteness(
+ *this, Kind, KWLoc, Args[0]->getType()))
+ return ExprError();
+
+ DiagnoseBuiltinDeprecation(*this, Kind, KWLoc);
+
+ bool Dependent = false;
+ for (unsigned I = 0, N = Args.size(); I != N; ++I) {
+ if (Args[I]->getType()->isDependentType()) {
+ Dependent = true;
+ break;
+ }
+ }
+
+ switch (GetReturnType(Kind)) {
+ case TypeTraitReturnType::Bool: {
+ bool Result = EvaluateBooleanTypeTrait(*this, Kind, KWLoc, Args, RParenLoc,
+ Dependent);
+ return TypeTraitExpr::Create(Context, Context.getLogicalOperationType(),
+ KWLoc, Kind, Args, RParenLoc, Result);
+ }
+ case TypeTraitReturnType::SizeT: {
+ APValue Result =
+ EvaluateSizeTTypeTrait(*this, Kind, KWLoc, Args, RParenLoc, Dependent);
+ return TypeTraitExpr::Create(Context, Context.getSizeType(), KWLoc, Kind,
+ Args, RParenLoc, Result);
+ }
+ }
+ llvm_unreachable("unhandled type trait return type");
+}
+
+ExprResult Sema::ActOnTypeTrait(TypeTrait Kind, SourceLocation KWLoc,
+ ArrayRef<ParsedType> Args,
+ SourceLocation RParenLoc) {
+ SmallVector<TypeSourceInfo *, 4> ConvertedArgs;
+ ConvertedArgs.reserve(Args.size());
+
+ for (unsigned I = 0, N = Args.size(); I != N; ++I) {
+ TypeSourceInfo *TInfo;
+ QualType T = GetTypeFromParser(Args[I], &TInfo);
+ if (!TInfo)
+ TInfo = Context.getTrivialTypeSourceInfo(T, KWLoc);
+
+ ConvertedArgs.push_back(TInfo);
+ }
+
+ 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();
+
+ assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
+ "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(
+ Rhs->getTypeLoc().getBeginLoc(), 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(
+ Rhs->getTypeLoc().getBeginLoc(), RhsT,
+ diag::err_incomplete_type_used_in_type_trait_expr))
+ return false;
+
+ 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>();
+
+ if (!BaseRecord || !DerivedRecord) {
+ DiagnoseVLAInCXXTypeTrait(Self, Lhs,
+ tok::kw___builtin_is_virtual_base_of);
+ DiagnoseVLAInCXXTypeTrait(Self, Rhs,
+ tok::kw___builtin_is_virtual_base_of);
+ return false;
+ }
+
+ if (BaseRecord->isUnionType() || DerivedRecord->isUnionType())
+ return false;
+
+ if (!BaseRecord->isStructureOrClassType() ||
+ !DerivedRecord->isStructureOrClassType())
+ return false;
+
+ if (Self.RequireCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT,
+ diag::err_incomplete_type))
+ return false;
+
+ return cast<CXXRecordDecl>(DerivedRecord->getDecl())
+ ->isVirtuallyDerivedFrom(cast<CXXRecordDecl>(BaseRecord->getDecl()));
+ }
+ case BTT_IsSame:
+ return Self.Context.hasSameType(LhsT, RhsT);
+ case BTT_TypeCompatible: {
+ // GCC ignores cv-qualifiers on arrays for this builtin.
+ Qualifiers LhsQuals, RhsQuals;
+ QualType Lhs = Self.getASTContext().getUnqualifiedArrayType(LhsT, LhsQuals);
+ QualType Rhs = Self.getASTContext().getUnqualifiedArrayType(RhsT, RhsQuals);
+ return Self.Context.typesAreCompatible(Lhs, Rhs);
+ }
+ case BTT_IsConvertible:
+ case BTT_IsConvertibleTo:
+ case BTT_IsNothrowConvertible: {
+ if (RhsT->isVoidType())
+ return LhsT->isVoidType();
+ llvm::BumpPtrAllocator OpaqueExprAllocator;
+ ExprResult Result = CheckConvertibilityForTypeTraits(Self, Lhs, Rhs, KeyLoc,
+ OpaqueExprAllocator);
+ if (Result.isInvalid())
+ return false;
+
+ if (BTT != BTT_IsNothrowConvertible)
+ return true;
+
+ return Self.canThrow(Result.get()) == CT_Cannot;
+ }
+
+ case BTT_IsAssignable:
+ case BTT_IsNothrowAssignable:
+ case BTT_IsTriviallyAssignable: {
+ // C++11 [meta.unary.prop]p3:
+ // is_trivially_assignable is defined as:
+ // is_assignable<T, U>::value is true and the assignment, as defined by
+ // is_assignable, is known to call no operation that is not trivial
+ //
+ // is_assignable is defined as:
+ // The expression declval<T>() = declval<U>() is well-formed when
+ // treated as an unevaluated operand (Clause 5).
+ //
+ // For both, T and U shall be complete types, (possibly cv-qualified)
+ // void, or arrays of unknown bound.
+ if (!LhsT->isVoidType() && !LhsT->isIncompleteArrayType() &&
+ Self.RequireCompleteType(
+ Lhs->getTypeLoc().getBeginLoc(), LhsT,
+ diag::err_incomplete_type_used_in_type_trait_expr))
+ return false;
+ if (!RhsT->isVoidType() && !RhsT->isIncompleteArrayType() &&
+ Self.RequireCompleteType(
+ Rhs->getTypeLoc().getBeginLoc(), RhsT,
+ diag::err_incomplete_type_used_in_type_trait_expr))
+ return false;
+
+ // cv void is never assignable.
+ if (LhsT->isVoidType() || RhsT->isVoidType())
+ return false;
+
+ // Build expressions that emulate the effect of declval<T>() and
+ // declval<U>().
+ if (LhsT->isObjectType() || LhsT->isFunctionType())
+ LhsT = Self.Context.getRValueReferenceType(LhsT);
+ if (RhsT->isObjectType() || RhsT->isFunctionType())
+ RhsT = Self.Context.getRValueReferenceType(RhsT);
+ OpaqueValueExpr Lhs(KeyLoc, LhsT.getNonLValueExprType(Self.Context),
+ Expr::getValueKindForType(LhsT));
+ OpaqueValueExpr Rhs(KeyLoc, RhsT.getNonLValueExprType(Self.Context),
+ Expr::getValueKindForType(RhsT));
+
+ // Attempt the assignment in an unevaluated context within a SFINAE
+ // trap at translation unit scope.
+ EnterExpressionEvaluationContext Unevaluated(
+ Self, Sema::ExpressionEvaluationContext::Unevaluated);
+ Sema::SFINAETrap SFINAE(Self, /*ForValidityCheck=*/true);
+ Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl());
+ ExprResult Result = Self.BuildBinOp(/*S=*/nullptr, KeyLoc, BO_Assign, &Lhs,
+ &Rhs);
+ if (Result.isInvalid())
+ return false;
+
+ // Treat the assignment as unused for the purpose of -Wdeprecated-volatile.
+ Self.CheckUnusedVolatileAssignment(Result.get());
+
+ if (SFINAE.hasErrorOccurred())
+ return false;
+
+ if (BTT == BTT_IsAssignable)
+ return true;
+
+ if (BTT == BTT_IsNothrowAssignable)
+ return Self.canThrow(Result.get()) == CT_Cannot;
+
+ if (BTT == BTT_IsTriviallyAssignable) {
+ // Under Objective-C ARC and Weak, if the destination has non-trivial
+ // Objective-C lifetime, this is a non-trivial assignment.
+ if (LhsT.getNonReferenceType().hasNonTrivialObjCLifetime())
+ return false;
+
+ return !Result.get()->hasNonTrivialCall(Self.Context);
+ }
+
+ llvm_unreachable("unhandled type trait");
+ return false;
+ }
+ case BTT_IsLayoutCompatible: {
+ if (!LhsT->isVoidType() && !LhsT->isIncompleteArrayType())
+ Self.RequireCompleteType(Lhs->getTypeLoc().getBeginLoc(), LhsT,
+ diag::err_incomplete_type);
+ if (!RhsT->isVoidType() && !RhsT->isIncompleteArrayType())
+ Self.RequireCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT,
+ diag::err_incomplete_type);
+
+ DiagnoseVLAInCXXTypeTrait(Self, Lhs, tok::kw___is_layout_compatible);
+ DiagnoseVLAInCXXTypeTrait(Self, Rhs, tok::kw___is_layout_compatible);
+
+ return Self.IsLayoutCompatible(LhsT, RhsT);
+ }
+ case BTT_IsPointerInterconvertibleBaseOf: {
+ if (LhsT->isStructureOrClassType() && RhsT->isStructureOrClassType() &&
+ !Self.getASTContext().hasSameUnqualifiedType(LhsT, RhsT)) {
+ Self.RequireCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT,
+ diag::err_incomplete_type);
+ }
+
+ DiagnoseVLAInCXXTypeTrait(Self, Lhs,
+ tok::kw___is_pointer_interconvertible_base_of);
+ DiagnoseVLAInCXXTypeTrait(Self, Rhs,
+ tok::kw___is_pointer_interconvertible_base_of);
+
+ return Self.IsPointerInterconvertibleBaseOf(Lhs, Rhs);
+ }
+ case BTT_IsDeducible: {
+ const auto *TSTToBeDeduced = cast<DeducedTemplateSpecializationType>(LhsT);
+ sema::TemplateDeductionInfo Info(KeyLoc);
+ return Self.DeduceTemplateArgumentsFromType(
+ TSTToBeDeduced->getTemplateName().getAsTemplateDecl(), RhsT,
+ Info) == TemplateDeductionResult::Success;
+ }
+ case BTT_IsScalarizedLayoutCompatible: {
+ if (!LhsT->isVoidType() && !LhsT->isIncompleteArrayType() &&
+ Self.RequireCompleteType(Lhs->getTypeLoc().getBeginLoc(), LhsT,
+ diag::err_incomplete_type))
+ return true;
+ if (!RhsT->isVoidType() && !RhsT->isIncompleteArrayType() &&
+ Self.RequireCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT,
+ diag::err_incomplete_type))
+ return true;
+
+ DiagnoseVLAInCXXTypeTrait(
+ Self, Lhs, tok::kw___builtin_hlsl_is_scalarized_layout_compatible);
+ DiagnoseVLAInCXXTypeTrait(
+ Self, Rhs, tok::kw___builtin_hlsl_is_scalarized_layout_compatible);
+
+ return Self.HLSL().IsScalarizedLayoutCompatible(LhsT, RhsT);
+ }
+ default:
+ llvm_unreachable("not a BTT");
+ }
+ llvm_unreachable("Unknown type trait or not implemented");
+}
+
+ExprResult Sema::ActOnArrayTypeTrait(ArrayTypeTrait ATT,
+ SourceLocation KWLoc,
+ ParsedType Ty,
+ Expr* DimExpr,
+ SourceLocation RParen) {
+ TypeSourceInfo *TSInfo;
+ QualType T = GetTypeFromParser(Ty, &TSInfo);
+ if (!TSInfo)
+ TSInfo = Context.getTrivialTypeSourceInfo(T);
+
+ return BuildArrayTypeTrait(ATT, KWLoc, TSInfo, DimExpr, RParen);
+}
+
+static uint64_t EvaluateArrayTypeTrait(Sema &Self, ArrayTypeTrait ATT,
+ QualType T, Expr *DimExpr,
+ SourceLocation KeyLoc) {
+ assert(!T->isDependentType() && "Cannot evaluate traits of dependent type");
+
+ switch(ATT) {
+ case ATT_ArrayRank:
+ if (T->isArrayType()) {
+ unsigned Dim = 0;
+ while (const ArrayType *AT = Self.Context.getAsArrayType(T)) {
+ ++Dim;
+ T = AT->getElementType();
+ }
+ return Dim;
+ }
+ return 0;
+
+ case ATT_ArrayExtent: {
+ llvm::APSInt Value;
+ uint64_t Dim;
+ if (Self.VerifyIntegerConstantExpression(
+ DimExpr, &Value, diag::err_dimension_expr_not_constant_integer)
+ .isInvalid())
+ return 0;
+ if (Value.isSigned() && Value.isNegative()) {
+ Self.Diag(KeyLoc, diag::err_dimension_expr_not_constant_integer)
+ << DimExpr->getSourceRange();
+ return 0;
+ }
+ Dim = Value.getLimitedValue();
+
+ if (T->isArrayType()) {
+ unsigned D = 0;
+ bool Matched = false;
+ while (const ArrayType *AT = Self.Context.getAsArrayType(T)) {
+ if (Dim == D) {
+ Matched = true;
+ break;
+ }
+ ++D;
+ T = AT->getElementType();
+ }
+
+ if (Matched && T->isArrayType()) {
+ if (const ConstantArrayType *CAT = Self.Context.getAsConstantArrayType(T))
+ return CAT->getLimitedSize();
+ }
+ }
+ return 0;
+ }
+ }
+ llvm_unreachable("Unknown type trait or not implemented");
+}
+
+ExprResult Sema::BuildArrayTypeTrait(ArrayTypeTrait ATT,
+ SourceLocation KWLoc,
+ TypeSourceInfo *TSInfo,
+ Expr* DimExpr,
+ SourceLocation RParen) {
+ QualType T = TSInfo->getType();
+
+ // FIXME: This should likely be tracked as an APInt to remove any host
+ // assumptions about the width of size_t on the target.
+ uint64_t Value = 0;
+ if (!T->isDependentType())
+ Value = EvaluateArrayTypeTrait(*this, ATT, T, DimExpr, KWLoc);
+
+ // While the specification for these traits from the Embarcadero C++
+ // compiler's documentation says the return type is 'unsigned int', Clang
+ // returns 'size_t'. On Windows, the primary platform for the Embarcadero
+ // compiler, there is no difference. On several other platforms this is an
+ // important distinction.
+ return new (Context) ArrayTypeTraitExpr(KWLoc, ATT, TSInfo, Value, DimExpr,
+ RParen, Context.getSizeType());
+}
+
+ExprResult Sema::ActOnExpressionTrait(ExpressionTrait ET,
+ SourceLocation KWLoc,
+ Expr *Queried,
+ SourceLocation RParen) {
+ // If error parsing the expression, ignore.
+ if (!Queried)
+ return ExprError();
+
+ ExprResult Result = BuildExpressionTrait(ET, KWLoc, Queried, RParen);
+
+ return Result;
+}
+
+static bool EvaluateExpressionTrait(ExpressionTrait ET, Expr *E) {
+ switch (ET) {
+ case ET_IsLValueExpr: return E->isLValue();
+ case ET_IsRValueExpr:
+ return E->isPRValue();
+ }
+ llvm_unreachable("Expression trait not covered by switch");
+}
+
+ExprResult Sema::BuildExpressionTrait(ExpressionTrait ET,
+ SourceLocation KWLoc,
+ Expr *Queried,
+ SourceLocation RParen) {
+ if (Queried->isTypeDependent()) {
+ // Delay type-checking for type-dependent expressions.
+ } else if (Queried->hasPlaceholderType()) {
+ ExprResult PE = CheckPlaceholderExpr(Queried);
+ if (PE.isInvalid()) return ExprError();
+ return BuildExpressionTrait(ET, KWLoc, PE.get(), RParen);
+ }
+
+ bool Value = EvaluateExpressionTrait(ET, Queried);
+
+ return new (Context)
+ ExpressionTraitExpr(KWLoc, ET, Queried, Value, RParen, Context.BoolTy);
+}
>From 64a90d29451d172d1e70481fef839800238af757 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 23 May 2025 17:53:02 +0200
Subject: [PATCH 2/2] Run clang-format to fix miss-aligned comments
---
clang/lib/Sema/SemaExprCXX.cpp | 1 -
clang/lib/Sema/SemaTypeTraits.cpp | 360 +++++++++++++++---------------
2 files changed, 180 insertions(+), 181 deletions(-)
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 63cf324ddb1f9..d8338e08dd0f7 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5292,7 +5292,6 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
return From;
}
-
QualType Sema::CheckPointerToMemberOperands(ExprResult &LHS, ExprResult &RHS,
ExprValueKind &VK,
SourceLocation Loc,
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index 763d37071b134..bac71c07d9907 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -358,7 +358,6 @@ bool Sema::IsCXXReplaceableType(QualType Type) {
return false;
}
-
/// Checks that type T is not a VLA.
///
/// @returns @c true if @p T is VLA and a diagnostic was emitted,
@@ -406,15 +405,16 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
// these class templates. We also try to follow any GCC documented behavior
// in these expressions to ensure portability of standard libraries.
switch (UTT) {
- default: llvm_unreachable("not a UTT");
+ default:
+ llvm_unreachable("not a UTT");
// is_complete_type somewhat obviously cannot require a complete type.
case UTT_IsCompleteType:
- // Fall-through
+ // Fall-through
- // These traits are modeled on the type predicates in C++0x
- // [meta.unary.cat] and [meta.unary.comp]. They are not specified as
- // requiring a complete type, as whether or not they return true cannot be
- // impacted by the completeness of the type.
+ // These traits are modeled on the type predicates in C++0x
+ // [meta.unary.cat] and [meta.unary.comp]. They are not specified as
+ // requiring a complete type, as whether or not they return true cannot be
+ // impacted by the completeness of the type.
case UTT_IsVoid:
case UTT_IsIntegral:
case UTT_IsFloatingPoint:
@@ -438,29 +438,29 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
case UTT_IsCompound:
case UTT_IsMemberPointer:
case UTT_IsTypedResourceElementCompatible:
- // Fall-through
+ // Fall-through
- // These traits are modeled on type predicates in C++0x [meta.unary.prop]
- // which requires some of its traits to have the complete type. However,
- // the completeness of the type cannot impact these traits' semantics, and
- // so they don't require it. This matches the comments on these traits in
- // Table 49.
+ // These traits are modeled on type predicates in C++0x [meta.unary.prop]
+ // which requires some of its traits to have the complete type. However,
+ // the completeness of the type cannot impact these traits' semantics, and
+ // so they don't require it. This matches the comments on these traits in
+ // Table 49.
case UTT_IsConst:
case UTT_IsVolatile:
case UTT_IsSigned:
case UTT_IsUnboundedArray:
case UTT_IsUnsigned:
- // This type trait always returns false, checking the type is moot.
+ // This type trait always returns false, checking the type is moot.
case UTT_IsInterfaceClass:
return true;
- // We diagnose incomplete class types later.
+ // We diagnose incomplete class types later.
case UTT_StructuredBindingSize:
return true;
- // C++14 [meta.unary.prop]:
- // If T is a non-union class type, T shall be a complete type.
+ // C++14 [meta.unary.prop]:
+ // If T is a non-union class type, T shall be a complete type.
case UTT_IsEmpty:
case UTT_IsPolymorphic:
case UTT_IsAbstract:
@@ -470,8 +470,8 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
return true;
- // C++14 [meta.unary.prop]:
- // If T is a class type, T shall be a complete type.
+ // C++14 [meta.unary.prop]:
+ // If T is a class type, T shall be a complete type.
case UTT_IsFinal:
case UTT_IsSealed:
if (ArgTy->getAsCXXRecordDecl())
@@ -479,7 +479,7 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
return true;
- // LWG3823: T shall be an array type, a complete type, or cv void.
+ // LWG3823: T shall be an array type, a complete type, or cv void.
case UTT_IsAggregate:
case UTT_IsImplicitLifetime:
if (ArgTy->isArrayType() || ArgTy->isVoidType())
@@ -488,8 +488,8 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
return !S.RequireCompleteType(
Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
- // has_unique_object_representations<T>
- // remove_all_extents_t<T> shall be a complete type or cv void (LWG4113).
+ // has_unique_object_representations<T>
+ // remove_all_extents_t<T> shall be a complete type or cv void (LWG4113).
case UTT_HasUniqueObjectRepresentations:
ArgTy = QualType(ArgTy->getBaseElementTypeUnsafe(), 0);
if (ArgTy->isVoidType())
@@ -497,8 +497,8 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
return !S.RequireCompleteType(
Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
- // C++1z [meta.unary.prop]:
- // remove_all_extents_t<T> shall be a complete type or cv void.
+ // C++1z [meta.unary.prop]:
+ // remove_all_extents_t<T> shall be a complete type or cv void.
case UTT_IsTrivial:
case UTT_IsTriviallyCopyable:
case UTT_IsStandardLayout:
@@ -546,8 +546,7 @@ static bool HasNoThrowOperator(const RecordType *RT, OverloadedOperatorKind Op,
Sema &Self, SourceLocation KeyLoc, ASTContext &C,
bool (CXXRecordDecl::*HasTrivial)() const,
bool (CXXRecordDecl::*HasNonTrivial)() const,
- bool (CXXMethodDecl::*IsDesiredOp)() const)
-{
+ bool (CXXMethodDecl::*IsDesiredOp)() const) {
CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
if ((RD->*HasTrivial)() && !(RD->*HasNonTrivial)())
return true;
@@ -564,7 +563,7 @@ static bool HasNoThrowOperator(const RecordType *RT, OverloadedOperatorKind Op,
continue;
CXXMethodDecl *Operator = cast<CXXMethodDecl>(*Op);
- if((Operator->*IsDesiredOp)()) {
+ if ((Operator->*IsDesiredOp)()) {
FoundOperator = true;
auto *CPT = Operator->getType()->castAs<FunctionProtoType>();
CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
@@ -591,7 +590,7 @@ static bool HasNonDeletedDefaultedEqualityComparison(Sema &S,
Sema::SFINAETrap SFINAE(S, /*ForValidityCheck=*/true);
Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());
- // const ClassT& obj;
+ // const ClassT& obj;
OpaqueValueExpr Operand(
KeyLoc,
Decl->getTypeForDecl()->getCanonicalTypeUnqualified().withConst(),
@@ -640,7 +639,8 @@ static bool HasNonDeletedDefaultedEqualityComparison(Sema &S,
});
}
-static bool isTriviallyEqualityComparableType(Sema &S, QualType Type, SourceLocation KeyLoc) {
+static bool isTriviallyEqualityComparableType(Sema &S, QualType Type,
+ SourceLocation KeyLoc) {
QualType CanonicalType = Type.getCanonicalType();
if (CanonicalType->isIncompleteType() || CanonicalType->isDependentType() ||
CanonicalType->isEnumeralType() || CanonicalType->isArrayType())
@@ -693,8 +693,9 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
assert(!T->isDependentType() && "Cannot evaluate traits of dependent type");
ASTContext &C = Self.Context;
- switch(UTT) {
- default: llvm_unreachable("not a UTT");
+ switch (UTT) {
+ default:
+ llvm_unreachable("not a UTT");
// Type trait expressions corresponding to the primary type category
// predicates in C++0x [meta.unary.cat].
case UTT_IsVoid:
@@ -742,8 +743,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
case UTT_IsFunction:
return T->isFunctionType();
- // Type trait expressions which correspond to the convenient composition
- // predicates in C++0x [meta.unary.comp].
+ // Type trait expressions which correspond to the convenient composition
+ // predicates in C++0x [meta.unary.comp].
case UTT_IsReference:
return T->isReferenceType();
case UTT_IsArithmetic:
@@ -776,8 +777,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
case UTT_IsMemberPointer:
return T->isMemberPointerType();
- // Type trait expressions which correspond to the type property predicates
- // in C++0x [meta.unary.prop].
+ // Type trait expressions which correspond to the type property predicates
+ // in C++0x [meta.unary.prop].
case UTT_IsConst:
return T.isConstQualified();
case UTT_IsVolatile:
@@ -829,21 +830,22 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
// Enum types should always return false.
return T->isUnsignedIntegerType() && !T->isEnumeralType();
- // Type trait expressions which query classes regarding their construction,
- // destruction, and copying. Rather than being based directly on the
- // related type predicates in the standard, they are specified by both
- // GCC[1] and the Embarcadero C++ compiler[2], and Clang implements those
- // specifications.
- //
- // 1: http://gcc.gnu/.org/onlinedocs/gcc/Type-Traits.html
- // 2: http://docwiki.embarcadero.com/RADStudio/XE/en/Type_Trait_Functions_(C%2B%2B0x)_Index
- //
- // Note that these builtins do not behave as documented in g++: if a class
- // has both a trivial and a non-trivial special member of a particular kind,
- // they return false! For now, we emulate this behavior.
- // FIXME: This appears to be a g++ bug: more complex cases reveal that it
- // does not correctly compute triviality in the presence of multiple special
- // members of the same kind. Revisit this once the g++ bug is fixed.
+ // Type trait expressions which query classes regarding their construction,
+ // destruction, and copying. Rather than being based directly on the
+ // related type predicates in the standard, they are specified by both
+ // GCC[1] and the Embarcadero C++ compiler[2], and Clang implements those
+ // specifications.
+ //
+ // 1: http://gcc.gnu/.org/onlinedocs/gcc/Type-Traits.html
+ // 2:
+ // http://docwiki.embarcadero.com/RADStudio/XE/en/Type_Trait_Functions_(C%2B%2B0x)_Index
+ //
+ // Note that these builtins do not behave as documented in g++: if a class
+ // has both a trivial and a non-trivial special member of a particular kind,
+ // they return false! For now, we emulate this behavior.
+ // FIXME: This appears to be a g++ bug: more complex cases reveal that it
+ // does not correctly compute triviality in the presence of multiple special
+ // members of the same kind. Revisit this once the g++ bug is fixed.
case UTT_HasTrivialDefaultConstructor:
// http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
// If __is_pod (type) is true then the trait is true, else if type is
@@ -862,7 +864,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
if (T.isPODType(C))
return true;
if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl())
- return RD->hasTrivialMoveConstructor() && !RD->hasNonTrivialMoveConstructor();
+ return RD->hasTrivialMoveConstructor() &&
+ !RD->hasNonTrivialMoveConstructor();
return false;
case UTT_HasTrivialCopy:
// http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
@@ -883,7 +886,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
if (T.isPODType(C))
return true;
if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl())
- return RD->hasTrivialMoveAssignment() && !RD->hasNonTrivialMoveAssignment();
+ return RD->hasTrivialMoveAssignment() &&
+ !RD->hasNonTrivialMoveAssignment();
return false;
case UTT_HasTrivialAssign:
// http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
@@ -914,26 +918,26 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
if (T->isReferenceType())
return true;
- // Objective-C++ ARC: autorelease types don't require destruction.
+ // Objective-C++ ARC: autorelease types don't require destruction.
if (T->isObjCLifetimeType() &&
T.getObjCLifetime() == Qualifiers::OCL_Autoreleasing)
return true;
- // C++14 [meta.unary.prop]:
- // For incomplete types and function types, is_destructible<T>::value is
- // false.
+ // C++14 [meta.unary.prop]:
+ // For incomplete types and function types, is_destructible<T>::value is
+ // false.
if (T->isIncompleteType() || T->isFunctionType())
return false;
- // A type that requires destruction (via a non-trivial destructor or ARC
- // lifetime semantics) is not trivially-destructible.
+ // A type that requires destruction (via a non-trivial destructor or ARC
+ // lifetime semantics) is not trivially-destructible.
if (UTT == UTT_IsTriviallyDestructible && T.isDestructedType())
return false;
- // C++14 [meta.unary.prop]:
- // For object types and given U equal to remove_all_extents_t<T>, if the
- // expression std::declval<U&>().~U() is well-formed when treated as an
- // unevaluated operand (Clause 5), then is_destructible<T>::value is true
+ // C++14 [meta.unary.prop]:
+ // For object types and given U equal to remove_all_extents_t<T>, if the
+ // expression std::declval<U&>().~U() is well-formed when treated as an
+ // unevaluated operand (Clause 5), then is_destructible<T>::value is true
if (auto *RD = C.getBaseElementType(T)->getAsCXXRecordDecl()) {
CXXDestructorDecl *Destructor = Self.LookupDestructor(RD);
if (!Destructor)
@@ -964,7 +968,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
if (T.isPODType(C) || T->isReferenceType())
return true;
- // Objective-C++ ARC: autorelease types don't require destruction.
+ // Objective-C++ ARC: autorelease types don't require destruction.
if (T->isObjCLifetimeType() &&
T.getObjCLifetime() == Qualifiers::OCL_Autoreleasing)
return true;
@@ -1094,9 +1098,9 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
return Destructor->isVirtual();
return false;
- // These type trait expressions are modeled on the specifications for the
- // Embarcadero C++0x type trait functions:
- // http://docwiki.embarcadero.com/RADStudio/XE/en/Type_Trait_Functions_(C%2B%2B0x)_Index
+ // These type trait expressions are modeled on the specifications for the
+ // Embarcadero C++0x type trait functions:
+ // http://docwiki.embarcadero.com/RADStudio/XE/en/Type_Trait_Functions_(C%2B%2B0x)_Index
case UTT_IsCompleteType:
// http://docwiki.embarcadero.com/RADStudio/XE/en/Is_complete_type_(typename_T_):
// Returns True if and only if T is a complete type at the point of the
@@ -1125,10 +1129,10 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
DiagnoseAtomicInCXXTypeTrait(Self, TInfo,
tok::kw___builtin_is_implicit_lifetime);
- // [basic.types.general] p9
- // Scalar types, implicit-lifetime class types ([class.prop]),
- // array types, and cv-qualified versions of these types
- // are collectively called implicit-lifetime types.
+ // [basic.types.general] p9
+ // Scalar types, implicit-lifetime class types ([class.prop]),
+ // array types, and cv-qualified versions of these types
+ // are collectively called implicit-lifetime types.
QualType UnqualT = T->getCanonicalTypeUnqualified();
if (UnqualT->isScalarType())
return true;
@@ -1138,11 +1142,11 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
if (!RD)
return false;
- // [class.prop] p9
- // A class S is an implicit-lifetime class if
- // - it is an aggregate whose destructor is not user-provided or
- // - it has at least one trivial eligible constructor and a trivial,
- // non-deleted destructor.
+ // [class.prop] p9
+ // A class S is an implicit-lifetime class if
+ // - it is an aggregate whose destructor is not user-provided or
+ // - it has at least one trivial eligible constructor and a trivial,
+ // non-deleted destructor.
const CXXDestructorDecl *Dtor = RD->getDestructor();
if (UnqualT->isAggregateType())
if (Dtor && !Dtor->isUserProvided())
@@ -1174,10 +1178,10 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
}
}
-
-
-static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
- const TypeSourceInfo *Rhs, SourceLocation KeyLoc);
+static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT,
+ const TypeSourceInfo *Lhs,
+ const TypeSourceInfo *Rhs,
+ SourceLocation KeyLoc);
static ExprResult CheckConvertibilityForTypeTraits(
Sema &Self, const TypeSourceInfo *Lhs, const TypeSourceInfo *Rhs,
@@ -1186,45 +1190,45 @@ static ExprResult CheckConvertibilityForTypeTraits(
QualType LhsT = Lhs->getType();
QualType RhsT = Rhs->getType();
- // 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.
+ // 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 ExprError();
- // A function definition requires a complete, non-abstract return type.
+ // A function definition requires a complete, non-abstract return type.
if (!Self.isCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT) ||
Self.isAbstractType(Rhs->getTypeLoc().getBeginLoc(), RhsT))
return ExprError();
- // Compute the result of add_rvalue_reference.
+ // 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.
+ // Build a fake source and destination for initialization.
InitializedEntity To(InitializedEntity::InitializeTemporary(RhsT));
Expr *From = new (OpaqueExprAllocator.Allocate<OpaqueValueExpr>())
OpaqueValueExpr(KeyLoc, LhsT.getNonLValueExprType(Self.Context),
@@ -1232,8 +1236,8 @@ static ExprResult CheckConvertibilityForTypeTraits(
InitializationKind Kind =
InitializationKind::CreateCopy(KeyLoc, SourceLocation());
- // Perform the initialization in an unevaluated context within a SFINAE
- // trap at translation unit scope.
+ // 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, /*ForValidityCheck=*/true);
@@ -1287,13 +1291,12 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
if (Kind <= UTT_Last)
return EvaluateUnaryTypeTrait(S, Kind, KWLoc, Args[0]);
- // Evaluate ReferenceBindsToTemporary and ReferenceConstructsFromTemporary
- // alongside the IsConstructible traits to avoid duplication.
+ // Evaluate ReferenceBindsToTemporary and ReferenceConstructsFromTemporary
+ // alongside the IsConstructible traits to avoid duplication.
if (Kind <= BTT_Last && Kind != BTT_ReferenceBindsToTemporary &&
Kind != BTT_ReferenceConstructsFromTemporary &&
Kind != BTT_ReferenceConvertsFromTemporary)
- return EvaluateBinaryTypeTrait(S, Kind, Args[0],
- Args[1], RParenLoc);
+ return EvaluateBinaryTypeTrait(S, Kind, Args[0], Args[1], RParenLoc);
switch (Kind) {
case clang::BTT_ReferenceBindsToTemporary:
@@ -1317,25 +1320,25 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
// T t(create<Args>()...);
assert(!Args.empty());
- // Precondition: T and all types in the parameter pack Args shall be
- // complete types, (possibly cv-qualified) void, or arrays of
- // unknown bound.
+ // Precondition: T and all types in the parameter pack Args shall be
+ // complete types, (possibly cv-qualified) void, or arrays of
+ // unknown bound.
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))
+ if (S.RequireCompleteType(
+ KWLoc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr))
return false;
}
- // Make sure the first argument is not incomplete nor a function type.
+ // Make sure the first argument is not incomplete nor a function type.
QualType T = Args[0]->getType();
if (T->isIncompleteType() || T->isFunctionType())
return false;
- // Make sure the first argument is not an abstract type.
+ // Make sure the first argument is not an abstract type.
CXXRecordDecl *RD = T->getAsCXXRecordDecl();
if (RD && RD->isAbstract())
return false;
@@ -1349,13 +1352,13 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
ArgTy = S.Context.getRValueReferenceType(ArgTy);
ArgExprs.push_back(
new (OpaqueExprAllocator.Allocate<OpaqueValueExpr>())
- OpaqueValueExpr(Args[I]->getTypeLoc().getBeginLoc(),
- ArgTy.getNonLValueExprType(S.Context),
- Expr::getValueKindForType(ArgTy)));
+ OpaqueValueExpr(Args[I]->getTypeLoc().getBeginLoc(),
+ ArgTy.getNonLValueExprType(S.Context),
+ Expr::getValueKindForType(ArgTy)));
}
- // Perform the initialization in an unevaluated context within a SFINAE
- // trap at translation unit scope.
+ // Perform the initialization in an unevaluated context within a SFINAE
+ // trap at translation unit scope.
EnterExpressionEvaluationContext Unevaluated(
S, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::SFINAETrap SFINAE(S, /*ForValidityCheck=*/true);
@@ -1411,23 +1414,23 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
if (T.getNonReferenceType().hasNonTrivialObjCLifetime())
return false;
- // The initialization succeeded; now make sure there are no non-trivial
- // calls.
+ // The initialization succeeded; now make sure there are no non-trivial
+ // calls.
return !Result.get()->hasNonTrivialCall(S.Context);
}
llvm_unreachable("unhandled type trait");
return false;
}
- default: llvm_unreachable("not a TT");
+ default:
+ llvm_unreachable("not a TT");
}
return false;
}
namespace {
-void DiagnoseBuiltinDeprecation(Sema& S, TypeTrait Kind,
- SourceLocation KWLoc) {
+void DiagnoseBuiltinDeprecation(Sema &S, TypeTrait Kind, SourceLocation KWLoc) {
TypeTrait Replacement;
switch (Kind) {
case UTT_HasNothrowAssign:
@@ -1466,13 +1469,13 @@ void DiagnoseBuiltinDeprecation(Sema& S, TypeTrait Kind,
bool Sema::CheckTypeTraitArity(unsigned Arity, SourceLocation Loc, size_t N) {
if (Arity && N != Arity) {
Diag(Loc, diag::err_type_trait_arity)
- << Arity << 0 << (Arity > 1) << (int)N << SourceRange(Loc);
+ << Arity << 0 << (Arity > 1) << (int)N << SourceRange(Loc);
return false;
}
if (!Arity && N == 0) {
Diag(Loc, diag::err_type_trait_arity)
- << 1 << 1 << 1 << (int)N << SourceRange(Loc);
+ << 1 << 1 << 1 << (int)N << SourceRange(Loc);
return false;
}
return true;
@@ -1544,15 +1547,17 @@ 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) {
+static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT,
+ const TypeSourceInfo *Lhs,
+ const TypeSourceInfo *Rhs,
+ SourceLocation KeyLoc) {
QualType LhsT = Lhs->getType();
QualType RhsT = Rhs->getType();
assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
"Cannot evaluate traits of dependent types");
- switch(BTT) {
+ switch (BTT) {
case BTT_IsBaseOf: {
// C++0x [meta.rel]p2
// Base is a base class of Derived without regard to cv-qualifiers or
@@ -1580,11 +1585,11 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
return BaseInterface->isSuperClassOf(DerivedInterface);
}
- assert(Self.Context.hasSameUnqualifiedType(LhsT, RhsT)
- == (lhsRecord == rhsRecord));
+ 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
+ // 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())
@@ -1593,10 +1598,10 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
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.
+ // 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))
@@ -1682,12 +1687,12 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
diag::err_incomplete_type_used_in_type_trait_expr))
return false;
- // cv void is never assignable.
+ // cv void is never assignable.
if (LhsT->isVoidType() || RhsT->isVoidType())
return false;
- // Build expressions that emulate the effect of declval<T>() and
- // declval<U>().
+ // Build expressions that emulate the effect of declval<T>() and
+ // declval<U>().
if (LhsT->isObjectType() || LhsT->isFunctionType())
LhsT = Self.Context.getRValueReferenceType(LhsT);
if (RhsT->isObjectType() || RhsT->isFunctionType())
@@ -1697,18 +1702,18 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
OpaqueValueExpr Rhs(KeyLoc, RhsT.getNonLValueExprType(Self.Context),
Expr::getValueKindForType(RhsT));
- // Attempt the assignment in an unevaluated context within a SFINAE
- // trap at translation unit scope.
+ // Attempt the assignment in an unevaluated context within a SFINAE
+ // trap at translation unit scope.
EnterExpressionEvaluationContext Unevaluated(
Self, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::SFINAETrap SFINAE(Self, /*ForValidityCheck=*/true);
Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl());
- ExprResult Result = Self.BuildBinOp(/*S=*/nullptr, KeyLoc, BO_Assign, &Lhs,
- &Rhs);
+ ExprResult Result =
+ Self.BuildBinOp(/*S=*/nullptr, KeyLoc, BO_Assign, &Lhs, &Rhs);
if (Result.isInvalid())
return false;
- // Treat the assignment as unused for the purpose of -Wdeprecated-volatile.
+ // Treat the assignment as unused for the purpose of -Wdeprecated-volatile.
Self.CheckUnusedVolatileAssignment(Result.get());
if (SFINAE.hasErrorOccurred())
@@ -1789,10 +1794,8 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
llvm_unreachable("Unknown type trait or not implemented");
}
-ExprResult Sema::ActOnArrayTypeTrait(ArrayTypeTrait ATT,
- SourceLocation KWLoc,
- ParsedType Ty,
- Expr* DimExpr,
+ExprResult Sema::ActOnArrayTypeTrait(ArrayTypeTrait ATT, SourceLocation KWLoc,
+ ParsedType Ty, Expr *DimExpr,
SourceLocation RParen) {
TypeSourceInfo *TSInfo;
QualType T = GetTypeFromParser(Ty, &TSInfo);
@@ -1807,7 +1810,7 @@ static uint64_t EvaluateArrayTypeTrait(Sema &Self, ArrayTypeTrait ATT,
SourceLocation KeyLoc) {
assert(!T->isDependentType() && "Cannot evaluate traits of dependent type");
- switch(ATT) {
+ switch (ATT) {
case ATT_ArrayRank:
if (T->isArrayType()) {
unsigned Dim = 0;
@@ -1828,7 +1831,7 @@ static uint64_t EvaluateArrayTypeTrait(Sema &Self, ArrayTypeTrait ATT,
return 0;
if (Value.isSigned() && Value.isNegative()) {
Self.Diag(KeyLoc, diag::err_dimension_expr_not_constant_integer)
- << DimExpr->getSourceRange();
+ << DimExpr->getSourceRange();
return 0;
}
Dim = Value.getLimitedValue();
@@ -1846,7 +1849,8 @@ static uint64_t EvaluateArrayTypeTrait(Sema &Self, ArrayTypeTrait ATT,
}
if (Matched && T->isArrayType()) {
- if (const ConstantArrayType *CAT = Self.Context.getAsConstantArrayType(T))
+ if (const ConstantArrayType *CAT =
+ Self.Context.getAsConstantArrayType(T))
return CAT->getLimitedSize();
}
}
@@ -1856,32 +1860,28 @@ static uint64_t EvaluateArrayTypeTrait(Sema &Self, ArrayTypeTrait ATT,
llvm_unreachable("Unknown type trait or not implemented");
}
-ExprResult Sema::BuildArrayTypeTrait(ArrayTypeTrait ATT,
- SourceLocation KWLoc,
- TypeSourceInfo *TSInfo,
- Expr* DimExpr,
+ExprResult Sema::BuildArrayTypeTrait(ArrayTypeTrait ATT, SourceLocation KWLoc,
+ TypeSourceInfo *TSInfo, Expr *DimExpr,
SourceLocation RParen) {
QualType T = TSInfo->getType();
- // FIXME: This should likely be tracked as an APInt to remove any host
- // assumptions about the width of size_t on the target.
+ // FIXME: This should likely be tracked as an APInt to remove any host
+ // assumptions about the width of size_t on the target.
uint64_t Value = 0;
if (!T->isDependentType())
Value = EvaluateArrayTypeTrait(*this, ATT, T, DimExpr, KWLoc);
- // While the specification for these traits from the Embarcadero C++
- // compiler's documentation says the return type is 'unsigned int', Clang
- // returns 'size_t'. On Windows, the primary platform for the Embarcadero
- // compiler, there is no difference. On several other platforms this is an
- // important distinction.
+ // While the specification for these traits from the Embarcadero C++
+ // compiler's documentation says the return type is 'unsigned int', Clang
+ // returns 'size_t'. On Windows, the primary platform for the Embarcadero
+ // compiler, there is no difference. On several other platforms this is an
+ // important distinction.
return new (Context) ArrayTypeTraitExpr(KWLoc, ATT, TSInfo, Value, DimExpr,
RParen, Context.getSizeType());
}
-ExprResult Sema::ActOnExpressionTrait(ExpressionTrait ET,
- SourceLocation KWLoc,
- Expr *Queried,
- SourceLocation RParen) {
+ExprResult Sema::ActOnExpressionTrait(ExpressionTrait ET, SourceLocation KWLoc,
+ Expr *Queried, SourceLocation RParen) {
// If error parsing the expression, ignore.
if (!Queried)
return ExprError();
@@ -1893,22 +1893,22 @@ ExprResult Sema::ActOnExpressionTrait(ExpressionTrait ET,
static bool EvaluateExpressionTrait(ExpressionTrait ET, Expr *E) {
switch (ET) {
- case ET_IsLValueExpr: return E->isLValue();
+ case ET_IsLValueExpr:
+ return E->isLValue();
case ET_IsRValueExpr:
return E->isPRValue();
}
llvm_unreachable("Expression trait not covered by switch");
}
-ExprResult Sema::BuildExpressionTrait(ExpressionTrait ET,
- SourceLocation KWLoc,
- Expr *Queried,
- SourceLocation RParen) {
+ExprResult Sema::BuildExpressionTrait(ExpressionTrait ET, SourceLocation KWLoc,
+ Expr *Queried, SourceLocation RParen) {
if (Queried->isTypeDependent()) {
// Delay type-checking for type-dependent expressions.
} else if (Queried->hasPlaceholderType()) {
ExprResult PE = CheckPlaceholderExpr(Queried);
- if (PE.isInvalid()) return ExprError();
+ if (PE.isInvalid())
+ return ExprError();
return BuildExpressionTrait(ET, KWLoc, PE.get(), RParen);
}
More information about the cfe-commits
mailing list