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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits