diff --git a/clang/lib/AST/ExprConstantMeta.cpp b/clang/lib/AST/ExprConstantMeta.cpp index 9715682ff833de..6063d5fb5ecc37 100644 --- a/clang/lib/AST/ExprConstantMeta.cpp +++ b/clang/lib/AST/ExprConstantMeta.cpp @@ -4532,6 +4532,47 @@ bool define_aggregate(APValue &Result, ASTContext &C, MetaActions &Meta, if (!Definition) return true; + if (Definition->isUnion()) { + // union specific behaviour of "define_aggregate" + + for (CXXConstructorDecl *CD : Definition->ctors()) { + if (CD->isDefaultConstructor() && CD->isDeleted()) { + CXXConstructorDecl *CanonicalCD = CD->getCanonicalDecl(); + + CanonicalCD->setImplicit(false); + CanonicalCD->setDeletedAsWritten(false); + CanonicalCD->setDefaulted(false); + CanonicalCD->setAccess(AS_public); + + auto *EmptyBody = + CompoundStmt::CreateEmpty(CanonicalCD->getASTContext(), 0, false); + CanonicalCD->setBody(EmptyBody); + + assert(CD->isUserProvided()); + assert(CD->isDefaultConstructor()); + assert(!CD->isDeleted()); + } + } + + if (CXXDestructorDecl *DD = Definition->getDestructor()) { + if (DD->isDeleted()) { + CXXDestructorDecl *CanonicalDD = DD->getCanonicalDecl(); + + CanonicalDD->setImplicit(false); + CanonicalDD->setDeletedAsWritten(false); + CanonicalDD->setDefaulted(false); + CanonicalDD->setAccess(AS_public); + + auto *EmptyBody = + CompoundStmt::CreateEmpty(CanonicalDD->getASTContext(), 0, false); + CanonicalDD->setBody(EmptyBody); + + assert(DD->isUserProvided()); + assert(!DD->isDeleted()); + } + } + } + C.recordClassMemberSpecHash(ToComplete, MemberSpecHash); return SetAndSucceed(Result, makeReflection(ToComplete)); } diff --git a/clang/lib/Sema/SemaReflect.cpp b/clang/lib/Sema/SemaReflect.cpp index 1a30ae4776bcfd..88410c07aca0b8 100644 --- a/clang/lib/Sema/SemaReflect.cpp +++ b/clang/lib/Sema/SemaReflect.cpp @@ -515,6 +515,8 @@ class MetaActionsImpl : public MetaActions { // Iterate over member specs. unsigned AnonMemCtr = 0; + bool AtLeastOneMemberIsNonTriviallyConstr = false; + for (TagDataMemberSpec *MemberSpec : MemberSpecs) { // Build the member declaration. unsigned DiagID; @@ -574,11 +576,26 @@ class MetaActionsImpl : public MetaActions { S.Context.getSizeType(), DefinitionLoc); } + if (const CXXRecordDecl *RD = MemberSpec->Ty->getAsCXXRecordDecl()) { + if (RD->hasUserDeclaredConstructor()) { + AtLeastOneMemberIsNonTriviallyConstr = true; + } + } + VirtSpecifiers VS; S.ActOnCXXMemberDeclarator(&ClsScope, MemberAS, MemberDeclarator, MTP, BitWidthCE, VS, ICIS_NoInit); } + const bool NeedToGenerateDefaultConstr = + IncompleteDecl->getTagKind() == TagTypeKind::Union && + AtLeastOneMemberIsNonTriviallyConstr && + NewDecl->needsImplicitDefaultConstructor(); + + if (NeedToGenerateDefaultConstr) { + S.DeclareImplicitDefaultConstructor(NewDecl); + } + // Finish the member-specification and the class definition. S.ActOnFinishCXXMemberSpecification(&ClsScope, NewDecl->getBeginLoc(), NewDecl, SourceLocation{}, diff --git a/libcxx/test/std/experimental/reflection/define-aggregate.pass.cpp b/libcxx/test/std/experimental/reflection/define-aggregate.pass.cpp index 1cba81402ccc83..3503cc364b898d 100644 --- a/libcxx/test/std/experimental/reflection/define-aggregate.pass.cpp +++ b/libcxx/test/std/experimental/reflection/define-aggregate.pass.cpp @@ -20,6 +20,7 @@ // // RUN: %{exec} %t.exe > %t.stdout +#include #include #include @@ -303,4 +304,49 @@ static_assert(is_type(define_aggregate(^^S2, { } // namespace repeat_calls +namespace non_trivial_constructor_and_destructor_of_union_members { +// https://github.com/bloomberg/clang-p2996/issues/115 + +// all members of union are trivially constructible and destructible +union U; +static_assert(is_type(define_aggregate( + ^^U, + { + data_member_spec(^^int), + data_member_spec(^^char), + }))); + +static_assert(std::is_default_constructible::value == true); +static_assert(std::is_trivially_constructible::value == true); + +static_assert(std::is_destructible::value == true); +static_assert(std::is_trivially_destructible::value == true); + +// at least one member of union has non-trivial constructor or destructor +struct A { + constexpr A() { + // no-op + } + + constexpr ~A() { + // no-op + } +}; + +union UU; +static_assert(is_type(define_aggregate( + ^^UU, + { + data_member_spec(^^int), + data_member_spec(^^A), + }))); + +static_assert(std::is_default_constructible::value == true); +static_assert(std::is_trivially_constructible::value == false); + +static_assert(std::is_destructible::value == true); +static_assert(std::is_trivially_destructible::value == false); + +} // namespace non_trivial_constructor_and_destructor + int main() { }