[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:47:06 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: cor3ntin (cor3ntin)
<details>
<summary>Changes</summary>
Just to try to keep the size of SemaExprCXX.cpp in check.
As discussed in #<!-- -->141238
---
Patch is 147.71 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/141245.diff
4 Files Affected:
- (modified) clang/lib/Sema/CMakeLists.txt (+1)
- (modified) clang/lib/Sema/SemaDeclCXX.cpp (-273)
- (modified) clang/lib/Sema/SemaExprCXX.cpp (-1621)
- (added) clang/lib/Sema/SemaTypeTraits.cpp (+1919)
``````````diff
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...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/141245
More information about the cfe-commits
mailing list