[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:
cor3ntin
2025-05-23 18:22:49 +02:00
committed by GitHub
parent cd7104eb87
commit 9502d1bcb2
4 changed files with 1920 additions and 1895 deletions

View File

@@ -96,6 +96,7 @@ add_clang_library(clangSema
SemaTemplateInstantiateDecl.cpp
SemaTemplateVariadic.cpp
SemaType.cpp
SemaTypeTraits.cpp
SemaWasm.cpp
SemaX86.cpp
TypeLocBuilder.cpp

View File

@@ -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

File diff suppressed because it is too large Load Diff