Skip to content

[clang][Sema] Track trivial-relocatability as a type trait #84621

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ FIELD(DeclaredNonTrivialSpecialMembers, 6, MERGE_OR)
/// SMF_MoveConstructor, and SMF_Destructor are meaningful here.
FIELD(DeclaredNonTrivialSpecialMembersForCall, 6, MERGE_OR)

/// True when this class's bases and fields are all trivially relocatable
/// or references, and the class itself has no user-provided special
/// member functions.
FIELD(IsNaturallyTriviallyRelocatable, 1, NO_MERGE)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you are trying to distinguish from the abi_tag but naturally is not saying much.

Either IsTriviallyRelocatable or, IsImplicitlyTriviallyRelocatable

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would still like to see a rename here


/// True when this class has a destructor with no semantic effect.
FIELD(HasIrrelevantDestructor, 1, NO_MERGE)

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -1398,6 +1398,9 @@ class CXXRecordDecl : public RecordDecl {
(SMF_CopyConstructor | SMF_MoveConstructor | SMF_Destructor);
}

/// Determine whether this class is trivially relocatable
bool isTriviallyRelocatable() const;

/// Determine whether declaring a const variable with this type is ok
/// per core issue 253.
bool allowConstDefaultInit() const {
Expand Down
38 changes: 36 additions & 2 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
DefaultedDestructorIsDeleted(false), HasTrivialSpecialMembers(SMF_All),
HasTrivialSpecialMembersForCall(SMF_All),
DeclaredNonTrivialSpecialMembers(0),
DeclaredNonTrivialSpecialMembersForCall(0), HasIrrelevantDestructor(true),
DeclaredNonTrivialSpecialMembersForCall(0),
IsNaturallyTriviallyRelocatable(true), HasIrrelevantDestructor(true),
HasConstexprNonCopyMoveConstructor(false),
HasDefaultedDefaultConstructor(false),
DefaultedDefaultConstructorIsConstexpr(true),
Expand Down Expand Up @@ -279,6 +280,10 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,

// An aggregate is a class with [...] no virtual functions.
data().Aggregate = false;

// A trivially relocatable class is a class:
// -- which has no virtual member functions or virtual base classes
data().IsNaturallyTriviallyRelocatable = false;
}

// C++0x [class]p7:
Expand All @@ -293,6 +298,9 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
if (!hasNonLiteralTypeFieldsOrBases() && !BaseType->isLiteralType(C))
data().HasNonLiteralTypeFieldsOrBases = true;

if (Base->isVirtual() || !BaseClassDecl->isTriviallyRelocatable())
data().IsNaturallyTriviallyRelocatable = false;

// Now go through all virtual bases of this base and add them.
for (const auto &VBase : BaseClassDecl->vbases()) {
// Add this base if it's not already in the list.
Expand Down Expand Up @@ -611,6 +619,10 @@ bool CXXRecordDecl::hasAnyDependentBases() const {
return !forallBases([](const CXXRecordDecl *) { return true; });
}

bool CXXRecordDecl::isTriviallyRelocatable() const {
return (data().IsNaturallyTriviallyRelocatable || hasAttr<TrivialABIAttr>());
}

bool CXXRecordDecl::isTriviallyCopyable() const {
// C++0x [class]p5:
// A trivially copyable class is a class that:
Expand Down Expand Up @@ -802,6 +814,10 @@ void CXXRecordDecl::addedMember(Decl *D) {
// -- has no virtual functions
data().IsStandardLayout = false;
data().IsCXX11StandardLayout = false;

// A trivially relocatable class is a class:
// -- which has no virtual member functions or virtual base classes
data().IsNaturallyTriviallyRelocatable = false;
}
}

Expand Down Expand Up @@ -1113,6 +1129,12 @@ void CXXRecordDecl::addedMember(Decl *D) {
} else if (!T.isCXX98PODType(Context))
data().PlainOldData = false;

// A trivially relocatable class is a class:
// -- all of whose members are either of reference type or of trivially
// relocatable type
if (!T->isReferenceType() && !T.isTriviallyRelocatableType(Context))
data().IsNaturallyTriviallyRelocatable = false;

if (T->isReferenceType()) {
if (!Field->hasInClassInitializer())
data().HasUninitializedReferenceMember = true;
Expand Down Expand Up @@ -1489,8 +1511,9 @@ void CXXRecordDecl::addedEligibleSpecialMemberFunction(const CXXMethodDecl *MD,
// See https://github.com/llvm/llvm-project/issues/59206

if (const auto *DD = dyn_cast<CXXDestructorDecl>(MD)) {
if (DD->isUserProvided())
if (DD->isUserProvided()) {
data().HasIrrelevantDestructor = false;
}
// If the destructor is explicitly defaulted and not trivial or not public
// or if the destructor is deleted, we clear HasIrrelevantDestructor in
// finishedDefaultedOrDeletedMember.
Expand All @@ -1506,6 +1529,17 @@ void CXXRecordDecl::addedEligibleSpecialMemberFunction(const CXXMethodDecl *MD,
data().IsAnyDestructorNoReturn = true;
}

// A trivially relocatable class is a class:
// -- where no eligible copy constructor, move constructor, copy
// assignment operator, move assignment operator, or destructor is
// user-provided,
if (MD->isUserProvided() &&
(SMKind & (SMF_CopyConstructor | SMF_MoveConstructor |
SMF_CopyAssignment | SMF_MoveAssignment | SMF_Destructor)) !=
0u) {
data().IsNaturallyTriviallyRelocatable = false;
}

if (!MD->isImplicit() && !MD->isUserProvided()) {
// This method is user-declared but not user-provided. We can't work
// out whether it's trivial yet (not until we get to the end of the
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2848,8 +2848,8 @@ bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const {
return false;
} else if (!BaseElementType->isObjectType()) {
return false;
} else if (const auto *RD = BaseElementType->getAsRecordDecl()) {
return RD->canPassInRegisters();
} else if (const auto *RD = BaseElementType->getAsCXXRecordDecl()) {
return RD->isTriviallyRelocatable();
} else if (BaseElementType.isTriviallyCopyableType(Context)) {
return true;
} else {
Expand Down
35 changes: 0 additions & 35 deletions clang/test/SemaCXX/attr-trivial-abi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,7 @@ void __attribute__((trivial_abi)) foo(); // expected-warning {{'trivial_abi' att
// Should not crash.
template <class>
class __attribute__((trivial_abi)) a { a(a &&); };
#if defined(_WIN64) && !defined(__MINGW32__)
// On Windows/MSVC, to be trivial-for-calls, an object must be trivially copyable.
// (And it is only trivially relocatable, currently, if it is trivial for calls.)
// In this case, it is suppressed by an explicitly defined move constructor.
// Similar concerns apply to later tests that have #if defined(_WIN64) && !defined(__MINGW32__)
static_assert(!__is_trivially_relocatable(a<int>), "");
#else
static_assert(__is_trivially_relocatable(a<int>), "");
#endif

struct [[clang::trivial_abi]] S0 {
int a;
Expand All @@ -39,14 +31,7 @@ struct __attribute__((trivial_abi)) S3_3 { // expected-warning {{'trivial_abi' c
S3_3(S3_3 &&);
S3_2 s32;
};
#ifdef __ORBIS__
// The ClangABI4OrPS4 calling convention kind passes classes in registers if the
// copy constructor is trivial for calls *or deleted*, while other platforms do
// not accept deleted constructors.
static_assert(__is_trivially_relocatable(S3_3), "");
#else
static_assert(!__is_trivially_relocatable(S3_3), "");
#endif

// Diagnose invalid trivial_abi even when the type is templated because it has a non-trivial field.
template <class T>
Expand Down Expand Up @@ -118,30 +103,18 @@ struct __attribute__((trivial_abi)) CopyMoveDeleted { // expected-warning {{'tri
CopyMoveDeleted(const CopyMoveDeleted &) = delete;
CopyMoveDeleted(CopyMoveDeleted &&) = delete;
};
#ifdef __ORBIS__
static_assert(__is_trivially_relocatable(CopyMoveDeleted), "");
#else
static_assert(!__is_trivially_relocatable(CopyMoveDeleted), "");
#endif

struct __attribute__((trivial_abi)) S18 { // expected-warning {{'trivial_abi' cannot be applied to 'S18'}} expected-note {{copy constructors and move constructors are all deleted}}
CopyMoveDeleted a;
};
#ifdef __ORBIS__
static_assert(__is_trivially_relocatable(S18), "");
#else
static_assert(!__is_trivially_relocatable(S18), "");
#endif

struct __attribute__((trivial_abi)) CopyDeleted {
CopyDeleted(const CopyDeleted &) = delete;
CopyDeleted(CopyDeleted &&) = default;
};
#if defined(_WIN64) && !defined(__MINGW32__)
static_assert(!__is_trivially_relocatable(CopyDeleted), "");
#else
static_assert(__is_trivially_relocatable(CopyDeleted), "");
#endif

struct __attribute__((trivial_abi)) MoveDeleted {
MoveDeleted(const MoveDeleted &) = default;
Expand All @@ -153,19 +126,11 @@ struct __attribute__((trivial_abi)) S19 { // expected-warning {{'trivial_abi' ca
CopyDeleted a;
MoveDeleted b;
};
#ifdef __ORBIS__
static_assert(__is_trivially_relocatable(S19), "");
#else
static_assert(!__is_trivially_relocatable(S19), "");
#endif

// This is fine since the move constructor isn't deleted.
struct __attribute__((trivial_abi)) S20 {
int &&a; // a member of rvalue reference type deletes the copy constructor.
};
#if defined(_WIN64) && !defined(__MINGW32__)
static_assert(!__is_trivially_relocatable(S20), "");
#else
static_assert(__is_trivially_relocatable(S20), "");
#endif
} // namespace deletedCopyMoveConstructor
Loading
Loading