[clang] [Clang] Implement the core language parts of P2786 - Trivial relocation (PR #127636)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 21 06:34:43 PST 2025
================
@@ -7258,6 +7261,221 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
}
}
+static bool hasSuitableConstructorForReplaceability(CXXRecordDecl *D,
+ bool Implicit) {
+ assert(D->hasDefinition() && !D->isInvalidDecl());
+
+ bool HasDeletedMoveConstructor = false;
+ bool HasDeletedCopyConstructor = false;
+ bool HasMoveConstructor = D->needsImplicitMoveConstructor();
+ bool HasDefaultedMoveConstructor = D->needsImplicitMoveConstructor();
+ bool HasDefaultedCopyConstructor = D->needsImplicitMoveConstructor();
+
+ for (const Decl *D : D->decls()) {
+ auto *MD = dyn_cast<CXXConstructorDecl>(D);
+ if (!MD || MD->isIneligibleOrNotSelected())
+ continue;
+
+ if (MD->isMoveConstructor()) {
+ HasMoveConstructor = true;
+ if (MD->isDefaulted())
+ HasDefaultedMoveConstructor = true;
+ if (MD->isDeleted())
+ HasDeletedMoveConstructor = true;
+ }
+ if (MD->isCopyConstructor()) {
+ if (MD->isDefaulted())
+ HasDefaultedCopyConstructor = true;
+ if (MD->isDeleted())
+ HasDeletedCopyConstructor = true;
+ }
+ }
+
+ if (HasMoveConstructor)
+ return !HasDeletedMoveConstructor &&
+ (Implicit ? HasDefaultedMoveConstructor : true);
+ return !HasDeletedCopyConstructor &&
+ (Implicit ? HasDefaultedCopyConstructor : true);
+ ;
+}
+
+static bool hasSuitableMoveAssignmentOperatorForReplaceability(CXXRecordDecl *D,
+ bool Implicit) {
+ assert(D->hasDefinition() && !D->isInvalidDecl());
+
+ if (D->hasExplicitlyDeletedMoveAssignment())
+ return false;
+
+ bool HasDeletedMoveAssignment = false;
+ bool HasDeletedCopyAssignment = false;
+ bool HasMoveAssignment = D->needsImplicitMoveAssignment();
+ bool HasDefaultedMoveAssignment = D->needsImplicitMoveAssignment();
+ bool HasDefaultedCopyAssignment = D->needsImplicitCopyAssignment();
+
+ for (const Decl *D : D->decls()) {
+ auto *MD = dyn_cast<CXXMethodDecl>(D);
+ if (!MD || MD->isIneligibleOrNotSelected())
+ continue;
+
+ if (MD->isMoveAssignmentOperator()) {
+ HasMoveAssignment = true;
+ if (MD->isDefaulted())
+ HasDefaultedMoveAssignment = true;
+ if (MD->isDeleted())
+ HasDeletedMoveAssignment = true;
+ }
+ if (MD->isCopyAssignmentOperator()) {
+ if (MD->isDefaulted())
+ HasDefaultedCopyAssignment = true;
+ if (MD->isDeleted())
+ HasDeletedCopyAssignment = true;
+ }
+ }
+
+ if (HasMoveAssignment)
+ return !HasDeletedMoveAssignment &&
+ (Implicit ? HasDefaultedMoveAssignment : true);
+ return !HasDeletedCopyAssignment &&
+ (Implicit ? HasDefaultedCopyAssignment : true);
+}
+
+void Sema::CheckCXX2CTriviallyRelocatable(CXXRecordDecl *D) {
+ if (D->isInvalidDecl())
+ return;
+
+ assert(D->hasDefinition());
+
+ bool MarkedTriviallyRelocatable =
+ D->getTriviallyRelocatableSpecifier().isSet();
+
+ bool IsTriviallyRelocatable = true;
+ for (const CXXBaseSpecifier &B : D->bases()) {
+ const auto *BaseDecl = B.getType()->getAsCXXRecordDecl();
+ if (!BaseDecl)
+ continue;
+ if (B.isVirtual() ||
+ (!BaseDecl->isDependentType() && !BaseDecl->isTriviallyRelocatable())) {
+ IsTriviallyRelocatable = false;
+ }
+ }
+
+ for (const FieldDecl *Field : D->fields()) {
+ if (Field->getType()->isDependentType())
+ continue;
+ if (Field->getType()->isReferenceType())
+ continue;
+ QualType T = getASTContext().getBaseElementType(
+ Field->getType().getUnqualifiedType());
+ if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) {
+ if (RD->isTriviallyRelocatable())
+ continue;
+ IsTriviallyRelocatable = false;
+ break;
+ }
+ }
+
+ if (!D->isDependentType() && !MarkedTriviallyRelocatable) {
+ bool HasSuitableMoveCtr = D->needsImplicitMoveConstructor();
+ bool HasSuitableCopyCtr = false;
+ if (D->hasUserDeclaredDestructor()) {
+ const auto *Dtr = D->getDestructor();
+ if (Dtr && (!Dtr->isDefaulted() || Dtr->isDeleted()))
+ IsTriviallyRelocatable = false;
+ }
+ if (IsTriviallyRelocatable && !HasSuitableMoveCtr) {
+ for (const CXXConstructorDecl *CD : D->ctors()) {
+ if (CD->isMoveConstructor() && CD->isDefaulted() &&
+ !CD->isIneligibleOrNotSelected()) {
+ HasSuitableMoveCtr = true;
+ break;
+ }
+ }
+ }
+ if (!HasSuitableMoveCtr && !D->hasMoveConstructor()) {
+ HasSuitableCopyCtr = D->needsImplicitCopyConstructor();
+ if (!HasSuitableCopyCtr) {
+ for (const CXXConstructorDecl *CD : D->ctors()) {
+ if (CD->isCopyConstructor() && CD->isDefaulted() &&
+ !CD->isIneligibleOrNotSelected()) {
+ HasSuitableCopyCtr = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (D->isUnion() && !D->hasUserDeclaredCopyConstructor() &&
+ !D->hasUserDeclaredCopyAssignment() &&
+ !D->hasUserDeclaredMoveOperation() && !D->hasUserDeclaredDestructor()) {
+ // Do nothing
+ }
+
+ else if (!HasSuitableMoveCtr && !HasSuitableCopyCtr)
+ IsTriviallyRelocatable = false;
+
+ else if (IsTriviallyRelocatable &&
+ ((!D->needsImplicitMoveAssignment() &&
+ (D->hasUserProvidedMoveAssignment() ||
+ D->hasExplicitlyDeletedMoveAssignment())) ||
+ (!D->hasMoveAssignment() &&
+ D->hasUserProvidedCopyAssignment()))) {
+ IsTriviallyRelocatable = false;
+ }
+ }
+
+ D->setIsTriviallyRelocatable(IsTriviallyRelocatable);
+}
+
+void Sema::CheckCXX2CReplaceable(CXXRecordDecl *D) {
----------------
Sirraide wrote:
This one is far less complicated but I’d also prefer if we could do `return false` instead of ` IsReplaceable = false` here (maybe just an immediately-invoked lambda in the initialiser of `IsReplaceable` is enough for this function?)
https://github.com/llvm/llvm-project/pull/127636
More information about the cfe-commits
mailing list