@@ -7221,8 +7221,7 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
72217221 checkClassLevelDLLAttribute(Record);
72227222 checkClassLevelCodeSegAttribute(Record);
72237223
7224- CheckCXX2CTriviallyRelocatable(Record);
7225- CheckCXX2CReplaceable(Record);
7224+ CheckCXX2CRelocatableAndReplaceable(Record);
72267225
72277226 bool ClangABICompat4 =
72287227 Context.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver4;
@@ -7261,82 +7260,116 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
72617260 }
72627261}
72637262
7264- static bool hasSuitableConstructorForRelocation(CXXRecordDecl *D,
7265- bool AllowUserDefined) {
7266- assert(D->hasDefinition() && !D->isInvalidDecl());
7263+ static CXXMethodDecl *
7264+ LookupSpecialMemberFromXValue(Sema &SemaRef, CXXRecordDecl *RD, bool Assign) {
7265+ RD = RD->getDefinition();
7266+ SourceLocation LookupLoc = RD->getLocation();
7267+
7268+ CanQualType CanTy = SemaRef.getASTContext().getCanonicalType(
7269+ SemaRef.getASTContext().getTagDeclType(RD));
7270+ DeclarationName Name;
7271+ Expr *Arg = nullptr;
7272+ unsigned NumArgs;
7273+
7274+ QualType ArgType = CanTy;
7275+ ExprValueKind VK = clang::VK_XValue;
7276+
7277+ if (Assign)
7278+ Name =
7279+ SemaRef.getASTContext().DeclarationNames.getCXXOperatorName(OO_Equal);
7280+ else
7281+ Name =
7282+ SemaRef.getASTContext().DeclarationNames.getCXXConstructorName(CanTy);
7283+
7284+ OpaqueValueExpr FakeArg(LookupLoc, ArgType, VK);
7285+ NumArgs = 1;
7286+ Arg = &FakeArg;
7287+
7288+ // Create the object argument
7289+ QualType ThisTy = CanTy;
7290+ Expr::Classification Classification =
7291+ OpaqueValueExpr(LookupLoc, ThisTy, VK_LValue)
7292+ .Classify(SemaRef.getASTContext());
7293+
7294+ // Now we perform lookup on the name we computed earlier and do overload
7295+ // resolution. Lookup is only performed directly into the class since there
7296+ // will always be a (possibly implicit) declaration to shadow any others.
7297+ OverloadCandidateSet OCS(LookupLoc, OverloadCandidateSet::CSK_Normal);
7298+ DeclContext::lookup_result R = RD->lookup(Name);
7299+
7300+ if (R.empty())
7301+ return nullptr;
72677302
7268- bool HasDeletedMoveConstructor = false;
7269- bool HasDeletedCopyConstructor = false;
7270- bool HasMoveConstructor = D->needsImplicitMoveConstructor();
7271- bool HasCopyConstructor = D->needsImplicitCopyConstructor();
7272- bool HasDefaultedMoveConstructor = D->needsImplicitMoveConstructor();
7273- bool HasDefaultedCopyConstructor = D->needsImplicitCopyConstructor();
7303+ // Copy the candidates as our processing of them may load new declarations
7304+ // from an external source and invalidate lookup_result.
7305+ SmallVector<NamedDecl *, 8> Candidates(R.begin(), R.end());
72747306
7275- for (const Decl *D : D->decls()) {
7276- auto *MD = dyn_cast<CXXConstructorDecl>(D);
7277- if (!MD || MD->isIneligibleOrNotSelected())
7307+ for (NamedDecl *CandDecl : Candidates) {
7308+ if (CandDecl->isInvalidDecl())
72787309 continue;
72797310
7280- if (MD->isMoveConstructor()) {
7281- HasMoveConstructor = true;
7282- if (MD->isDefaulted())
7283- HasDefaultedMoveConstructor = true;
7284- if (MD->isDeleted())
7285- HasDeletedMoveConstructor = true;
7286- } else if (MD->isCopyConstructor()) {
7287- HasCopyConstructor = true;
7288- if (MD->isDefaulted())
7289- HasDefaultedCopyConstructor = true;
7290- if (MD->isDeleted())
7291- HasDeletedCopyConstructor = true;
7311+ DeclAccessPair Cand = DeclAccessPair::make(CandDecl, clang::AS_none);
7312+ auto CtorInfo = getConstructorInfo(Cand);
7313+ if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(Cand->getUnderlyingDecl())) {
7314+ if (Assign)
7315+ SemaRef.AddMethodCandidate(M, Cand, RD, ThisTy, Classification,
7316+ llvm::ArrayRef(&Arg, NumArgs), OCS, true);
7317+ else {
7318+ assert(CtorInfo);
7319+ SemaRef.AddOverloadCandidate(CtorInfo.Constructor, CtorInfo.FoundDecl,
7320+ llvm::ArrayRef(&Arg, NumArgs), OCS,
7321+ /*SuppressUserConversions*/ true);
7322+ }
7323+ } else if (FunctionTemplateDecl *Tmpl =
7324+ dyn_cast<FunctionTemplateDecl>(Cand->getUnderlyingDecl())) {
7325+ if (Assign)
7326+ SemaRef.AddMethodTemplateCandidate(
7327+ Tmpl, Cand, RD, nullptr, ThisTy, Classification,
7328+ llvm::ArrayRef(&Arg, NumArgs), OCS, true);
7329+ else {
7330+ assert(CtorInfo);
7331+ SemaRef.AddTemplateOverloadCandidate(
7332+ CtorInfo.ConstructorTmpl, CtorInfo.FoundDecl, nullptr,
7333+ llvm::ArrayRef(&Arg, NumArgs), OCS, true);
7334+ }
72927335 }
72937336 }
72947337
7295- if (HasMoveConstructor)
7296- return !HasDeletedMoveConstructor &&
7297- (AllowUserDefined ? true : HasDefaultedMoveConstructor);
7298- return HasCopyConstructor && !HasDeletedCopyConstructor &&
7299- (AllowUserDefined ? true : HasDefaultedCopyConstructor);
7338+ OverloadCandidateSet::iterator Best;
7339+ switch (OCS.BestViableFunction(SemaRef, LookupLoc, Best)) {
7340+ case OR_Success:
7341+ return cast<CXXMethodDecl>(Best->Function);
7342+ default:
7343+ return nullptr;
7344+ }
73007345}
73017346
7302- static bool
7303- hasSuitableMoveAssignmentOperatorForRelocation(CXXRecordDecl *D,
7304- bool AllowUserDefined) {
7347+ static bool hasSuitableConstructorForRelocation(Sema &SemaRef, CXXRecordDecl *D,
7348+ bool AllowUserDefined) {
73057349 assert(D->hasDefinition() && !D->isInvalidDecl());
73067350
7307- if (D->hasExplicitlyDeletedMoveAssignment ())
7308- return false ;
7351+ if (D->hasSimpleMoveConstructor() || D->hasSimpleCopyConstructor ())
7352+ return true ;
73097353
7310- bool HasDeletedMoveAssignment = false;
7311- bool HasDeletedCopyAssignment = false;
7312- bool HasMoveAssignment = D->needsImplicitMoveAssignment();
7313- bool HasDefaultedMoveAssignment = D->needsImplicitMoveAssignment();
7314- bool HasDefaultedCopyAssignment = D->needsImplicitCopyAssignment();
7354+ CXXMethodDecl *Decl =
7355+ LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/false);
7356+ return Decl && Decl->isUserProvided() == AllowUserDefined;
7357+ }
73157358
7316- for (const Decl *D : D->decls()) {
7317- auto *MD = dyn_cast<CXXMethodDecl>(D);
7318- if (!MD || MD->isIneligibleOrNotSelected())
7319- continue ;
7359+ static bool
7360+ hasSuitableMoveAssignmentOperatorForRelocation(Sema &SemaRef, CXXRecordDecl *D,
7361+ bool AllowUserDefined) {
7362+ assert(D->hasDefinition() && !D->isInvalidDecl()) ;
73207363
7321- if (MD->isMoveAssignmentOperator()) {
7322- HasMoveAssignment = true;
7323- if (MD->isDefaulted())
7324- HasDefaultedMoveAssignment = true;
7325- if (MD->isDeleted())
7326- HasDeletedMoveAssignment = true;
7327- } else if (MD->isCopyAssignmentOperator()) {
7328- if (MD->isDefaulted())
7329- HasDefaultedCopyAssignment = true;
7330- if (MD->isDeleted())
7331- HasDeletedCopyAssignment = true;
7332- }
7333- }
7364+ if (D->hasSimpleMoveAssignment() || D->hasSimpleCopyAssignment())
7365+ return true;
73347366
7335- if (HasMoveAssignment)
7336- return !HasDeletedMoveAssignment &&
7337- (AllowUserDefined ? true : HasDefaultedMoveAssignment);
7338- return !HasDeletedCopyAssignment &&
7339- (AllowUserDefined ? true : HasDefaultedCopyAssignment);
7367+ CXXMethodDecl *Decl =
7368+ LookupSpecialMemberFromXValue(SemaRef, D, /*Assign=*/true);
7369+ if (!Decl)
7370+ return false;
7371+
7372+ return Decl && Decl->isUserProvided() == AllowUserDefined;
73407373}
73417374
73427375// [C++26][class.prop]
@@ -7348,12 +7381,13 @@ hasSuitableMoveAssignmentOperatorForRelocation(CXXRecordDecl *D,
73487381// type C selects an assignment operator function that is a direct member of C
73497382// and is neither user-provided nor deleted, and C has a destructor that is
73507383// neither user-provided nor deleted.
7351- static bool isDefaultMovable(CXXRecordDecl *D) {
7352- if (!hasSuitableConstructorForRelocation(D, /*AllowUserDefined=*/false))
7384+ static bool isDefaultMovable(Sema &SemaRef, CXXRecordDecl *D) {
7385+ if (!hasSuitableConstructorForRelocation(SemaRef, D,
7386+ /*AllowUserDefined=*/false))
73537387 return false;
73547388
73557389 if (!hasSuitableMoveAssignmentOperatorForRelocation(
7356- D, /*AllowUserDefined=*/false))
7390+ SemaRef, D, /*AllowUserDefined=*/false))
73577391 return false;
73587392
73597393 const auto *Dtr = D->getDestructor();
@@ -7395,53 +7429,15 @@ static bool isEligibleForTrivialRelocation(Sema &SemaRef, CXXRecordDecl *D) {
73957429 continue;
73967430 // ... has a non-static data member of an object type that is not
73977431 // of a trivially relocatable type
7398- QualType T = SemaRef.getASTContext().getBaseElementType(
7399- Field->getType().getUnqualifiedType());
7400- if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) {
7401- if (!RD->isTriviallyRelocatable())
7402- return false;
7403- }
7432+ if (!Field->getType().isCppTriviallyRelocatableType(
7433+ SemaRef.getASTContext()))
7434+ return false;
74047435 }
74057436
74067437 // ...has a deleted destructor
74077438 return !hasDeletedDestructor(D);
74087439}
74097440
7410- // [C++26][class.prop]
7411- // A class C is a trivially relocatable class if
7412- void Sema::CheckCXX2CTriviallyRelocatable(CXXRecordDecl *D) {
7413- if (!getLangOpts().CPlusPlus || D->isInvalidDecl())
7414- return;
7415-
7416- assert(D->hasDefinition());
7417-
7418- bool MarkedTriviallyRelocatable =
7419- D->getTriviallyRelocatableSpecifier().isSet();
7420-
7421- bool IsTriviallyRelocatable = [&] {
7422- // if it is eligible for trivial relocation
7423-
7424- if (!isEligibleForTrivialRelocation(*this, D))
7425- return false;
7426-
7427- // has the trivially_relocatable_if_eligible class-property-specifier,
7428- if (D->isDependentType() || MarkedTriviallyRelocatable)
7429- return true;
7430-
7431- // is a union with no user-declared special member functions, or
7432- if (D->isUnion() && !D->hasUserDeclaredCopyConstructor() &&
7433- !D->hasUserDeclaredCopyAssignment() &&
7434- !D->hasUserDeclaredMoveOperation() && !D->hasUserDeclaredDestructor()) {
7435- return true;
7436- }
7437-
7438- // is default-movable.
7439- return isDefaultMovable(D);
7440- }();
7441-
7442- D->setIsTriviallyRelocatable(IsTriviallyRelocatable);
7443- }
7444-
74457441// [C++26][class.prop]
74467442// A class C is eligible for replacement unless
74477443static bool isEligibleForReplacement(Sema &SemaRef, CXXRecordDecl *D) {
@@ -7468,41 +7464,81 @@ static bool isEligibleForReplacement(Sema &SemaRef, CXXRecordDecl *D) {
74687464 return !hasDeletedDestructor(D);
74697465}
74707466
7471- // [C++26][class.prop] A class C is a replaceable class if...
7472- void Sema::CheckCXX2CReplaceable(CXXRecordDecl *D) {
7467+ void Sema::CheckCXX2CRelocatableAndReplaceable(CXXRecordDecl *D) {
74737468 if (!getLangOpts().CPlusPlus || D->isInvalidDecl())
74747469 return;
74757470
74767471 assert(D->hasDefinition());
74777472
74787473 bool MarkedCXX2CReplaceable = D->getReplaceableSpecifier().isSet();
7474+ bool MarkedTriviallyRelocatable =
7475+ D->getTriviallyRelocatableSpecifier().isSet();
74797476
74807477 // This is part of "eligible for replacement", however we defer it
74817478 // to avoid extraneous computations.
74827479 auto HasSuitableSMP = [&] {
7483- return hasSuitableConstructorForRelocation(D, /*AllowUserDefined=*/true) &&
7480+ return hasSuitableConstructorForRelocation(*this, D,
7481+ /*AllowUserDefined=*/true) &&
74847482 hasSuitableMoveAssignmentOperatorForRelocation(
7485- D, /*AllowUserDefined=*/true);
7483+ *this, D, /*AllowUserDefined=*/true);
7484+ };
7485+
7486+ auto IsUnion = [&, Is = std::optional<bool>{}]() mutable {
7487+ if (!Is.has_value())
7488+ Is = D->isUnion() && !D->hasUserDeclaredCopyConstructor() &&
7489+ !D->hasUserDeclaredCopyAssignment() &&
7490+ !D->hasUserDeclaredMoveOperation() &&
7491+ !D->hasUserDeclaredDestructor();
7492+ return *Is;
74867493 };
74877494
7495+ auto IsDefaultMovable = [&, Is = std::optional<bool>{}]() mutable {
7496+ if (!Is.has_value())
7497+ Is = isDefaultMovable(*this, D);
7498+ return *Is;
7499+ };
7500+
7501+ bool IsTriviallyRelocatable = [&] {
7502+ if (D->isDependentType())
7503+ return false;
7504+
7505+ // if it is eligible for trivial relocation
7506+ if (!isEligibleForTrivialRelocation(*this, D))
7507+ return false;
7508+
7509+ // has the trivially_relocatable_if_eligible class-property-specifier,
7510+ if (MarkedTriviallyRelocatable)
7511+ return true;
7512+
7513+ // is a union with no user-declared special member functions, or
7514+ if (IsUnion()) {
7515+ return true;
7516+ }
7517+ // is default-movable.
7518+ return IsDefaultMovable();
7519+ }();
7520+
7521+ D->setIsTriviallyRelocatable(IsTriviallyRelocatable);
7522+
74887523 bool IsReplaceable = [&] {
7524+ if (D->isDependentType())
7525+ return false;
7526+
74897527 // A class C is a replaceable class if it is eligible for replacement
74907528 if (!isEligibleForReplacement(*this, D))
74917529 return false;
74927530
74937531 // has the replaceable_if_eligible class-property-specifier
7494- if (D->isDependentType() || MarkedCXX2CReplaceable)
7532+ if (MarkedCXX2CReplaceable)
74957533 return HasSuitableSMP();
74967534
74977535 // is a union with no user-declared special member functions, or
7498- if (D->isUnion() && !D->hasUserDeclaredCopyConstructor() &&
7499- !D->hasUserDeclaredCopyAssignment() &&
7500- !D->hasUserDeclaredMoveOperation() && !D->hasUserDeclaredDestructor()) {
7536+ if (IsUnion()) {
75017537 return HasSuitableSMP();
75027538 }
75037539
75047540 // is default-movable.
7505- return isDefaultMovable(D );
7541+ return IsDefaultMovable( );
75067542 }();
75077543
75087544 D->setIsReplaceable(IsReplaceable);
0 commit comments