blob: 71d1d44e93a84e7a388ba98d9b5a48db23370a7c [file] [log] [blame]
//===----- 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);
}
static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) {
return llvm::StringSwitch<std::optional<TypeTrait>>(Name)
.Case("is_trivially_relocatable",
TypeTrait::UTT_IsCppTriviallyRelocatable)
.Default(std::nullopt);
}
using ExtractedTypeTraitInfo =
std::optional<std::pair<TypeTrait, llvm::SmallVector<QualType, 1>>>;
// Recognize type traits that are builting type traits, or known standard
// type traits in <type_traits>. Note that at this point we assume the
// trait evaluated to false, so we need only to recognize the shape of the
// outer-most symbol.
static ExtractedTypeTraitInfo ExtractTypeTraitFromExpression(const Expr *E) {
llvm::SmallVector<QualType, 1> Args;
std::optional<TypeTrait> Trait;
// builtins
if (const auto *TraitExpr = dyn_cast<TypeTraitExpr>(E)) {
Trait = TraitExpr->getTrait();
for (const auto *Arg : TraitExpr->getArgs())
Args.push_back(Arg->getType());
return {{Trait.value(), std::move(Args)}};
}
const auto *Ref = dyn_cast<DeclRefExpr>(E);
if (!Ref)
return std::nullopt;
// std::is_xxx_v<>
if (const auto *VD =
dyn_cast<VarTemplateSpecializationDecl>(Ref->getDecl())) {
if (!VD->isInStdNamespace())
return std::nullopt;
StringRef Name = VD->getIdentifier()->getName();
if (!Name.consume_back("_v"))
return std::nullopt;
Trait = StdNameToTypeTrait(Name);
if (!Trait)
return std::nullopt;
for (const auto &Arg : VD->getTemplateArgs().asArray())
Args.push_back(Arg.getAsType());
return {{Trait.value(), std::move(Args)}};
}
// std::is_xxx<>::value
if (const auto *VD = dyn_cast<VarDecl>(Ref->getDecl());
Ref->hasQualifier() && VD && VD->getIdentifier()->isStr("value")) {
const Type *T = Ref->getQualifier()->getAsType();
if (!T)
return std::nullopt;
const TemplateSpecializationType *Ts =
T->getAs<TemplateSpecializationType>();
if (!Ts)
return std::nullopt;
const TemplateDecl *D = Ts->getTemplateName().getAsTemplateDecl();
if (!D || !D->isInStdNamespace())
return std::nullopt;
Trait = StdNameToTypeTrait(D->getIdentifier()->getName());
if (!Trait)
return std::nullopt;
for (const auto &Arg : Ts->template_arguments())
Args.push_back(Arg.getAsType());
return {{Trait.value(), std::move(Args)}};
}
return std::nullopt;
}
static void DiagnoseNonTriviallyRelocatableReason(Sema &SemaRef,
SourceLocation Loc,
const CXXRecordDecl *D) {
for (const CXXBaseSpecifier &B : D->bases()) {
const auto *BaseDecl = B.getType()->getAsCXXRecordDecl();
assert(BaseDecl && "invalid base?");
if (B.isVirtual())
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::VBase << B.getType()
<< B.getSourceRange();
if (!SemaRef.IsCXXTriviallyRelocatableType(B.getType()))
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::NRBase << B.getType()
<< B.getSourceRange();
}
for (const FieldDecl *Field : D->fields()) {
if (!Field->getType()->isReferenceType() &&
!SemaRef.IsCXXTriviallyRelocatableType(Field->getType()))
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::NRField << Field << Field->getType()
<< Field->getSourceRange();
}
if (D->hasDeletedDestructor())
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::DeletedDtr << /*Deleted*/ 0
<< D->getDestructor()->getSourceRange();
if (D->hasAttr<TriviallyRelocatableAttr>())
return;
if (D->isUnion()) {
auto DiagSPM = [&](CXXSpecialMemberKind K, bool Has) {
if (Has)
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::UnionWithUserDeclaredSMF << K;
};
DiagSPM(CXXSpecialMemberKind::CopyConstructor,
D->hasUserDeclaredCopyConstructor());
DiagSPM(CXXSpecialMemberKind::CopyAssignment,
D->hasUserDeclaredCopyAssignment());
DiagSPM(CXXSpecialMemberKind::MoveConstructor,
D->hasUserDeclaredMoveConstructor());
DiagSPM(CXXSpecialMemberKind::MoveAssignment,
D->hasUserDeclaredMoveAssignment());
return;
}
if (!D->hasSimpleMoveConstructor() && !D->hasSimpleCopyConstructor()) {
const auto *Decl = cast<CXXConstructorDecl>(
LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/false));
if (Decl && Decl->isUserProvided())
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::UserProvidedCtr
<< Decl->isMoveConstructor() << Decl->getSourceRange();
}
if (!D->hasSimpleMoveAssignment() && !D->hasSimpleCopyAssignment()) {
CXXMethodDecl *Decl =
LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/true);
if (Decl && Decl->isUserProvided())
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::UserProvidedAssign
<< Decl->isMoveAssignmentOperator() << Decl->getSourceRange();
}
CXXDestructorDecl *Dtr = D->getDestructor();
if (Dtr && Dtr->isUserProvided() && !Dtr->isDefaulted())
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::DeletedDtr << /*User Provided*/ 1
<< Dtr->getSourceRange();
}
static void DiagnoseNonTriviallyRelocatableReason(Sema &SemaRef,
SourceLocation Loc,
QualType T) {
SemaRef.Diag(Loc, diag::note_unsatisfied_trait)
<< T << diag::TraitName::TriviallyRelocatable;
if (T->isVariablyModifiedType())
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::VLA;
if (T->isReferenceType())
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::Ref;
T = T.getNonReferenceType();
if (T.hasNonTrivialObjCLifetime())
SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason)
<< diag::TraitNotSatisfiedReason::HasArcLifetime;
const CXXRecordDecl *D = T->getAsCXXRecordDecl();
if (!D || D->isInvalidDecl())
return;
if (D->hasDefinition())
DiagnoseNonTriviallyRelocatableReason(SemaRef, Loc, D);
SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D;
}
void Sema::DiagnoseTypeTraitDetails(const Expr *E) {
E = E->IgnoreParenImpCasts();
if (E->containsErrors())
return;
ExtractedTypeTraitInfo TraitInfo = ExtractTypeTraitFromExpression(E);
if (!TraitInfo)
return;
const auto &[Trait, Args] = TraitInfo.value();
switch (Trait) {
case UTT_IsCppTriviallyRelocatable:
DiagnoseNonTriviallyRelocatableReason(*this, E->getBeginLoc(), Args[0]);
break;
default:
break;
}
}