Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ FIELD(IsStandardLayout, 1, NO_MERGE)
/// member.
FIELD(IsCXX11StandardLayout, 1, NO_MERGE)

/// True when the class has a virtual base class.
FIELD(HasVBases, 1, NO_MERGE)

/// True when any base class has any declared non-static data
/// members or bit-fields.
/// This is a helper bit of state used to implement IsStandardLayout more
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,13 @@ class CXXRecordDecl : public RecordDecl {
needsOverloadResolutionForDestructor()) &&
"destructor should not be deleted");
data().DefaultedDestructorIsDeleted = true;
// C++23 [dcl.constexpr]p3.2:
// if the function is a constructor or destructor, its class does not have
// any virtual base classes.
// C++20 [dcl.constexpr]p5:
// The definition of a constexpr destructor whose function-body is
// [not = delete] shall additionally satisfy...
data().DefaultedDestructorIsConstexpr = !data().HasVBases;
}

/// Determine whether this class should get an implicit move
Expand Down
24 changes: 16 additions & 8 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,11 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
: UserDeclaredConstructor(false), UserDeclaredSpecialMembers(0),
Aggregate(true), PlainOldData(true), Empty(true), Polymorphic(false),
Abstract(false), IsStandardLayout(true), IsCXX11StandardLayout(true),
HasBasesWithFields(false), HasBasesWithNonStaticDataMembers(false),
HasPrivateFields(false), HasProtectedFields(false),
HasPublicFields(false), HasMutableFields(false), HasVariantMembers(false),
HasOnlyCMembers(true), HasInitMethod(false), HasInClassInitializer(false),
HasVBases(false), HasBasesWithFields(false),
HasBasesWithNonStaticDataMembers(false), HasPrivateFields(false),
HasProtectedFields(false), HasPublicFields(false),
HasMutableFields(false), HasVariantMembers(false), HasOnlyCMembers(true),
HasInitMethod(false), HasInClassInitializer(false),
HasUninitializedReferenceMember(false), HasUninitializedFields(false),
HasInheritedConstructor(false), HasInheritedDefaultConstructor(false),
HasInheritedAssignment(false),
Expand Down Expand Up @@ -316,6 +317,8 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
}

if (Base->isVirtual()) {
data().HasVBases = true;

// Add this base if it's not already in the list.
if (SeenVBaseTypes.insert(C.getCanonicalType(BaseType)).second)
VBases.push_back(Base);
Expand Down Expand Up @@ -547,9 +550,9 @@ void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
data().NeedOverloadResolutionForDestructor = true;
}

// C++2a [dcl.constexpr]p4:
// The definition of a constexpr destructor [shall] satisfy the
// following requirement:
// C++20 [dcl.constexpr]p5:
// The definition of a constexpr destructor whose function-body is not
// = delete [shall] additionally satisfy the following requirement:
// -- for every subobject of class type or (possibly multi-dimensional)
// array thereof, that class type shall have a constexpr destructor
if (!Subobj->hasConstexprDestructor())
Expand Down Expand Up @@ -1214,8 +1217,13 @@ void CXXRecordDecl::addedMember(Decl *D) {
data().DefaultedCopyAssignmentIsDeleted = true;
if (FieldRec->hasNonTrivialMoveAssignment())
data().DefaultedMoveAssignmentIsDeleted = true;
if (FieldRec->hasNonTrivialDestructor())
if (FieldRec->hasNonTrivialDestructor()) {
data().DefaultedDestructorIsDeleted = true;
// C++20 [dcl.constexpr]p5:
// The definition of a constexpr destructor whose function-body is
// [not = delete] shall additionally satisfy...
data().DefaultedDestructorIsConstexpr = true;
}
}

// For an anonymous union member, our overload resolution will perform
Expand Down
4 changes: 2 additions & 2 deletions clang/test/AST/ByteCode/cxx23.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ namespace AnonUnionDtor {
template <class T>
struct opt
{
union { // all20-note {{is not literal}}
union {
char c;
T data;
};
Expand All @@ -279,7 +279,7 @@ namespace AnonUnionDtor {
};

consteval void foo() {
opt<A> a; // all20-error {{variable of non-literal type}}
opt<A> a;
}

void bar() { foo(); }
Expand Down
68 changes: 68 additions & 0 deletions clang/test/SemaCXX/literal-type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ static_assert(__is_literal(VectorExt), "fail");
// [...]
// -- a class type that has all of the following properties:
// -- it has a trivial destructor
// [P0784R7 changed the condition to "constexpr destructor" in C++20]
// -- every constructor call and full-expression in the
// brace-or-equal-initializers for non-static data members (if an) is
// a constant expression,
Expand Down Expand Up @@ -108,3 +109,70 @@ void test() {

}
#endif

#if __cplusplus >= 201103L
namespace GH85550 {
struct HasDefaultCtorAndNonConstexprDtor {
constexpr HasDefaultCtorAndNonConstexprDtor() = default;
~HasDefaultCtorAndNonConstexprDtor() {}
};

union UnionWithNonLiteralMember {
HasDefaultCtorAndNonConstexprDtor x;
int y;

constexpr UnionWithNonLiteralMember() : x{} {}
};
#if __cplusplus >= 202002L
static_assert(__is_literal(UnionWithNonLiteralMember), "fail");
#else
static_assert(!__is_literal(UnionWithNonLiteralMember), "fail");
#endif

union UnionWithNonLiteralMemberExplicitDtor1 {
HasDefaultCtorAndNonConstexprDtor x;
int y;
// expected-note@-2 {{destructor of 'UnionWithNonLiteralMemberExplicitDtor1' is implicitly deleted because variant field 'x' has a non-trivial destructor}}

constexpr UnionWithNonLiteralMemberExplicitDtor1() : x{} {}
~UnionWithNonLiteralMemberExplicitDtor1() = default; // expected-warning {{explicitly defaulted destructor is implicitly deleted}}
// expected-note@-1 {{replace 'default' with 'delete'}}
};
#if __cplusplus >= 202002L
static_assert(__is_literal(UnionWithNonLiteralMemberExplicitDtor1), "fail");
#else
static_assert(!__is_literal(UnionWithNonLiteralMemberExplicitDtor1), "fail");
#endif

union UnionWithNonLiteralMemberExplicitDtor2 {
HasDefaultCtorAndNonConstexprDtor x;
int y;

constexpr UnionWithNonLiteralMemberExplicitDtor2() : x{} {}
~UnionWithNonLiteralMemberExplicitDtor2() = delete;
};
static_assert(!__is_literal(UnionWithNonLiteralMemberExplicitDtor2), "fail");

#if __cplusplus >= 202002L
union UnionWithNonLiteralMemberConstexprDtor1 {
HasDefaultCtorAndNonConstexprDtor x;
int y;
// expected-note@-2 {{destructor of 'UnionWithNonLiteralMemberConstexprDtor1' is implicitly deleted because variant field 'x' has a non-trivial destructor}}

constexpr UnionWithNonLiteralMemberConstexprDtor1() : x{} {}
constexpr ~UnionWithNonLiteralMemberConstexprDtor1() = default; // expected-warning {{explicitly defaulted destructor is implicitly deleted}}
// expected-note@-1 {{replace 'default' with 'delete'}}
};
static_assert(__is_literal(UnionWithNonLiteralMemberConstexprDtor1), "fail");

union UnionWithNonLiteralMemberConstexprDtor2 {
HasDefaultCtorAndNonConstexprDtor x;
int y;

constexpr UnionWithNonLiteralMemberConstexprDtor2() : x{} {}
constexpr ~UnionWithNonLiteralMemberConstexprDtor2() = delete;
};
static_assert(__is_literal(UnionWithNonLiteralMemberConstexprDtor2), "fail");
#endif
}
#endif
Loading