================
@@ -10054,6 +10060,102 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl
*MD,
SpecialMemberDeletionInfo SMI(*this, MD, CSM, ICI, Diagnose);
+ // C++26 [class.dtor]p7:
+ // A defaulted destructor for a union X is defined as deleted if
+ // - X has a default constructor and overload resolution to select a
+ // constructor to default-initialize an object of type X either fails
+ // or selects a constructor that is either deleted or not trivial, or
+ // - X has a variant member of class type M (or possibly
+ // multi-dimensional array thereof) with a default member initializer
+ // and M has a non-trivial destructor.
+ //
+ // The pre-existing p7.2 (deleted/inaccessible member dtor) still applies.
+ // We check the two union-specific bullets here and fall through to the
+ // normal per-member visit for p7.2. shouldDeleteForSubobjectCall already
+ // returns false for non-trivial-but-not-deleted dtors in C++26 unions,
+ // so only truly deleted/inaccessible dtors will cause deletion through
+ // the member visit.
+ if (getLangOpts().CPlusPlus26 && RD->isUnion() &&
+ CSM == CXXSpecialMemberKind::Destructor) {
+ // Check p7 bullet 1: overload resolution for default initialization.
+ SpecialMemberOverloadResult SMOR = LookupSpecialMember(
+ RD, CXXSpecialMemberKind::DefaultConstructor,
+ /*ConstArg=*/false, /*VolatileArg=*/false, /*RValueThis=*/false,
+ /*ConstThis=*/false, /*VolatileThis=*/false);
+ bool CtorOK = false;
+ if (SMOR.getKind() == SpecialMemberOverloadResult::Success) {
+ auto *Ctor = cast<CXXConstructorDecl>(SMOR.getMethod());
+ // In C++26, union default ctors are trivial unless user-provided
+ // (see C++26 [class.default.ctor]p3). We use !isUserProvided()
+ // rather than isTrivial() because the triviality flag may not be set
+ // yet for
+ // explicitly defaulted ctors at the point DeclareImplicitDestructor
+ // runs during class completion.
+ CtorOK = !Ctor->isDeleted() && !Ctor->isUserProvided();
+ } else if (SMOR.getKind() ==
+ SpecialMemberOverloadResult::NoMemberOrDeleted) {
+ if (!SMOR.getMethod()) {
+ // No default constructor exists (e.g. suppressed by user-declared
+ // constructors). The union cannot be default-initialized, so
+ // p7 bullet 1 does not apply.
+ CtorOK = true;
+ }
+ // else: a deleted default ctor was selected → CtorOK stays false.
+ }
+ // Ambiguous → CtorOK stays false.
+ if (!CtorOK) {
+ // p7 bullet 1: overload resolution is ambiguous, selects a deleted
+ // ctor, or selects a non-trivial (user-provided) ctor.
+ if (Diagnose) {
+ unsigned Reason;
+ if (SMOR.getKind() == SpecialMemberOverloadResult::Ambiguous)
+ Reason = 2; // ambiguous
+ else if (SMOR.getKind() ==
+ SpecialMemberOverloadResult::NoMemberOrDeleted) {
+ auto *Ctor = SMOR.getMethod();
+ Reason = (Ctor && Ctor->isDeleted()) ? 1 : 0;
+ } else {
+ Reason = 3; // not trivial
+ }
+ Diag(RD->getLocation(), diag::note_deleted_dtor_default_ctor)
+ << RD << Reason;
+ }
+ return true;
+ }
+ // Ctor is OK. Check p7 bullet 2: walk variant members (including
+ // through anonymous structs) for DMI + non-trivial dtor.
+ std::function<bool(const CXXRecordDecl *)> HasDMINonTrivDtor =
+ [&](const CXXRecordDecl *Record) -> bool {
+ for (const auto *FD : Record->fields()) {
+ if (FD->hasInClassInitializer()) {
+ QualType FT = Context.getBaseElementType(FD->getType());
+ if (const auto *FR = FT->getAsCXXRecordDecl()) {
+ if (FR->hasNonTrivialDestructor()) {
+ if (Diagnose)
+ Diag(FD->getLocation(),
+ diag::note_deleted_special_member_class_subobject)
+ << getSpecialMember(MD) << RD << /*IsField*/ true << FD
+ << /*NonTrivialDecl*/ 4 << /*IsDtorCallInCtor*/ false
+ << /*IsObjCPtr*/ false;
+ return true;
+ }
+ }
+ }
+ if (FD->isAnonymousStructOrUnion()) {
+ if (const auto *SubRD = FD->getType()->getAsCXXRecordDecl()) {
+ if (HasDMINonTrivDtor(SubRD))
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+ if (HasDMINonTrivDtor(RD))
+ return true;
+ // Neither p7 bullet 1 nor bullet 2 triggered. Fall through to the
+ // normal per-member visit for p7.2 (deleted/inaccessible dtor).
+ }
+
----------------
cor3ntin wrote:
It might be better to collect this info as we build the union (by adding data
to `CXXRecordDeclDefinitionBits.def`
In fact, we already have `HasInClassInitializer`. If `HasInClassInitializer`
is false, we can bypass the whole thing. but maybe also adding
`HasInClassInitializerWithNonTrivialDestructor` or something along these lines
would be beneficial.
(We want to do as little work as possible twice!)
https://github.com/llvm/llvm-project/pull/185886
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits