@@ -7339,12 +7339,21 @@ hasSuitableMoveAssignmentOperatorForRelocation(CXXRecordDecl *D,
73397339 (AllowUserDefined ? true : HasDefaultedCopyAssignment);
73407340}
73417341
7342+ // [C++26][class.prop]
7343+ // A class C is default-movable if
7344+ // - overload resolution for direct-initializing an object of type C
7345+ // from an xvalue of type C selects a constructor that is a direct member of C
7346+ // and is neither user-provided nor deleted,
7347+ // - overload resolution for assigning to an lvalue of type C from an xvalue of
7348+ // type C selects an assignment operator function that is a direct member of C
7349+ // and is neither user-provided nor deleted, and C has a destructor that is
7350+ // neither user-provided nor deleted.
73427351static bool isDefaultMovable(CXXRecordDecl *D) {
7343- if (!hasSuitableConstructorForRelocation(D, /*AllowUserDefined*/ false))
7352+ if (!hasSuitableConstructorForRelocation(D, /*AllowUserDefined=*/ false))
73447353 return false;
73457354
73467355 if (!hasSuitableMoveAssignmentOperatorForRelocation(
7347- D, /*AllowUserDefined*/ false))
7356+ D, /*AllowUserDefined=*/ false))
73487357 return false;
73497358
73507359 const auto *Dtr = D->getDestructor();
@@ -7364,12 +7373,16 @@ static bool hasDeletedDestructor(CXXRecordDecl *D) {
73647373 return false;
73657374}
73667375
7376+ // [C++26][class.prop]
7377+ // A class is eligible for trivial relocation unless it...
73677378static bool isEligibleForTrivialRelocation(Sema &SemaRef, CXXRecordDecl *D) {
73687379
73697380 for (const CXXBaseSpecifier &B : D->bases()) {
73707381 const auto *BaseDecl = B.getType()->getAsCXXRecordDecl();
73717382 if (!BaseDecl)
73727383 continue;
7384+ // ... has any virtual base classes
7385+ // ... has a base class that is not a trivially relocatable class
73737386 if (B.isVirtual() ||
73747387 (!BaseDecl->isDependentType() && !BaseDecl->isTriviallyRelocatable()))
73757388 return false;
@@ -7380,6 +7393,8 @@ static bool isEligibleForTrivialRelocation(Sema &SemaRef, CXXRecordDecl *D) {
73807393 continue;
73817394 if (Field->getType()->isReferenceType())
73827395 continue;
7396+ // ... has a non-static data member of an object type that is not
7397+ // of a trivially relocatable type
73837398 QualType T = SemaRef.getASTContext().getBaseElementType(
73847399 Field->getType().getUnqualifiedType());
73857400 if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) {
@@ -7388,9 +7403,12 @@ static bool isEligibleForTrivialRelocation(Sema &SemaRef, CXXRecordDecl *D) {
73887403 }
73897404 }
73907405
7406+ // ...has a deleted destructor
73917407 return !hasDeletedDestructor(D);
73927408}
73937409
7410+ // [C++26][class.prop]
7411+ // A class C is a trivially relocatable class if
73947412void Sema::CheckCXX2CTriviallyRelocatable(CXXRecordDecl *D) {
73957413 if (!getLangOpts().CPlusPlus || D->isInvalidDecl())
73967414 return;
@@ -7401,30 +7419,38 @@ void Sema::CheckCXX2CTriviallyRelocatable(CXXRecordDecl *D) {
74017419 D->getTriviallyRelocatableSpecifier().isSet();
74027420
74037421 bool IsTriviallyRelocatable = [&] {
7422+ // if it is eligible for trivial relocation
7423+
74047424 if (!isEligibleForTrivialRelocation(*this, D))
74057425 return false;
74067426
7427+ // has the trivially_relocatable_if_eligible class-property-specifier,
74077428 if (D->isDependentType() || MarkedTriviallyRelocatable)
74087429 return true;
74097430
7431+ // is a union with no user-declared special member functions, or
74107432 if (D->isUnion() && !D->hasUserDeclaredCopyConstructor() &&
74117433 !D->hasUserDeclaredCopyAssignment() &&
74127434 !D->hasUserDeclaredMoveOperation() && !D->hasUserDeclaredDestructor()) {
74137435 return true;
74147436 }
74157437
7438+ // is default-movable.
74167439 return isDefaultMovable(D);
74177440 }();
74187441
74197442 D->setIsTriviallyRelocatable(IsTriviallyRelocatable);
74207443}
74217444
7445+ // [C++26][class.prop]
7446+ // A class C is eligible for replacement unless
74227447static bool isEligibleForReplacement(Sema &SemaRef, CXXRecordDecl *D) {
74237448
74247449 for (const CXXBaseSpecifier &B : D->bases()) {
74257450 const auto *BaseDecl = B.getType()->getAsCXXRecordDecl();
74267451 if (!BaseDecl)
74277452 continue;
7453+ // it has a base class that is not a replaceable class
74287454 if ((!BaseDecl->isDependentType() && !BaseDecl->isReplaceable()))
74297455 return false;
74307456 }
@@ -7433,23 +7459,16 @@ static bool isEligibleForReplacement(Sema &SemaRef, CXXRecordDecl *D) {
74337459 if (Field->getType()->isDependentType())
74347460 continue;
74357461
7436- if (Field->getType()->isReferenceType())
7462+ // it has a non-static data member that is not of a replaceable type,
7463+ if (Field->getType().isReplaceableType(SemaRef.getASTContext()))
74377464 return false;
7438-
7439- if (Field->getType().isConstQualified())
7440- return false;
7441-
7442- QualType T = SemaRef.getASTContext().getBaseElementType(
7443- Field->getType().getUnqualifiedType());
7444- if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) {
7445- if (!RD->isReplaceable())
7446- return false;
7447- }
74487465 }
74497466
7467+ // it has a deleted destructor.
74507468 return !hasDeletedDestructor(D);
74517469}
74527470
7471+ // [C++26][class.prop] A class C is a replaceable class if...
74537472void Sema::CheckCXX2CReplaceable(CXXRecordDecl *D) {
74547473 if (!getLangOpts().CPlusPlus || D->isInvalidDecl())
74557474 return;
@@ -7458,25 +7477,31 @@ void Sema::CheckCXX2CReplaceable(CXXRecordDecl *D) {
74587477
74597478 bool MarkedCXX2CReplaceable = D->getReplaceableSpecifier().isSet();
74607479
7480+ // This is part of "eligible for replacement", however we defer it
7481+ // to avoid extraneous computations.
74617482 auto HasSuitableSMP = [&] {
74627483 return hasSuitableConstructorForRelocation(D, /*AllowUserDefined=*/true) &&
74637484 hasSuitableMoveAssignmentOperatorForRelocation(
74647485 D, /*AllowUserDefined=*/true);
74657486 };
74667487
74677488 bool IsReplaceable = [&] {
7489+ // A class C is a replaceable class if it is eligible for replacement
74687490 if (!isEligibleForReplacement(*this, D))
74697491 return false;
74707492
7493+ // has the replaceable_if_eligible class-property-specifier
74717494 if (D->isDependentType() || MarkedCXX2CReplaceable)
74727495 return HasSuitableSMP();
74737496
7497+ // is a union with no user-declared special member functions, or
74747498 if (D->isUnion() && !D->hasUserDeclaredCopyConstructor() &&
74757499 !D->hasUserDeclaredCopyAssignment() &&
74767500 !D->hasUserDeclaredMoveOperation() && !D->hasUserDeclaredDestructor()) {
74777501 return HasSuitableSMP();
74787502 }
74797503
7504+ // is default-movable.
74807505 return isDefaultMovable(D);
74817506 }();
74827507
0 commit comments