[Clang][NFC] Move the type trait logic to a separate file. (#141245)
Just to try to keep the size of SemaExprCXX.cpp in check. As discussed in #141238
This commit is contained in:
@@ -96,6 +96,7 @@ add_clang_library(clangSema
|
||||
SemaTemplateInstantiateDecl.cpp
|
||||
SemaTemplateVariadic.cpp
|
||||
SemaType.cpp
|
||||
SemaTypeTraits.cpp
|
||||
SemaWasm.cpp
|
||||
SemaX86.cpp
|
||||
TypeLocBuilder.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.
|
||||
///
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1919
clang/lib/Sema/SemaTypeTraits.cpp
Normal file
1919
clang/lib/Sema/SemaTypeTraits.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user