From: Richard Smith Date: Fri, 31 May 2019 01:25:16 +0000 (+0000) Subject: PR39728: When completing a class, complete the destructor first. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=aa455897d0206d4d64782433a7d70eeb3d93256e;p=clang PR39728: When completing a class, complete the destructor first. We need to know whether the destructor is trivial in order to tell whether other parts of the class are valid (in particular, this affects whether the type is a literal type, which affects whether defaulted special members can be declared constexpr or are implicitly constexpr). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@362184 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 35863a3266..0956aff21e 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -6125,9 +6125,60 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { if (HasTrivialABI) Record->setHasTrivialSpecialMemberForCall(); + auto CompleteMemberFunction = [&](CXXMethodDecl *M) { + // Check whether the explicitly-defaulted special members are valid. + if (!M->isInvalidDecl() && M->isExplicitlyDefaulted()) + CheckExplicitlyDefaultedSpecialMember(M); + + // For an explicitly defaulted or deleted special member, we defer + // determining triviality until the class is complete. That time is now! + CXXSpecialMember CSM = getSpecialMember(M); + if (!M->isImplicit() && !M->isUserProvided()) { + if (CSM != CXXInvalid) { + M->setTrivial(SpecialMemberIsTrivial(M, CSM)); + // Inform the class that we've finished declaring this member. + Record->finishedDefaultedOrDeletedMember(M); + M->setTrivialForCall( + HasTrivialABI || + SpecialMemberIsTrivial(M, CSM, TAH_ConsiderTrivialABI)); + Record->setTrivialForCallFlags(M); + } + } + + // Set triviality for the purpose of calls if this is a user-provided + // copy/move constructor or destructor. + if ((CSM == CXXCopyConstructor || CSM == CXXMoveConstructor || + CSM == CXXDestructor) && M->isUserProvided()) { + M->setTrivialForCall(HasTrivialABI); + Record->setTrivialForCallFlags(M); + } + + if (!M->isInvalidDecl() && M->isExplicitlyDefaulted() && + M->hasAttr()) { + if (getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) && + M->isTrivial() && + (CSM == CXXDefaultConstructor || CSM == CXXCopyConstructor || + CSM == CXXDestructor)) + M->dropAttr(); + + if (M->hasAttr()) { + DefineImplicitSpecialMember(*this, M, M->getLocation()); + ActOnFinishInlineFunctionDef(M); + } + } + }; + bool HasMethodWithOverrideControl = false, HasOverridingMethodWithoutOverrideControl = false; if (!Record->isDependentType()) { + // Check the destructor before any other member function. We need to + // determine whether it's trivial in order to determine whether the claas + // type is a literal type, which is a prerequisite for determining whether + // other special member functions are valid and whether they're implicitly + // 'constexpr'. + if (CXXDestructorDecl *Dtor = Record->getDestructor()) + CompleteMemberFunction(Dtor); + for (auto *M : Record->methods()) { // See if a method overloads virtual methods in a base // class without overriding any. @@ -6137,46 +6188,9 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { HasMethodWithOverrideControl = true; else if (M->size_overridden_methods() > 0) HasOverridingMethodWithoutOverrideControl = true; - // Check whether the explicitly-defaulted special members are valid. - if (!M->isInvalidDecl() && M->isExplicitlyDefaulted()) - CheckExplicitlyDefaultedSpecialMember(M); - - // For an explicitly defaulted or deleted special member, we defer - // determining triviality until the class is complete. That time is now! - CXXSpecialMember CSM = getSpecialMember(M); - if (!M->isImplicit() && !M->isUserProvided()) { - if (CSM != CXXInvalid) { - M->setTrivial(SpecialMemberIsTrivial(M, CSM)); - // Inform the class that we've finished declaring this member. - Record->finishedDefaultedOrDeletedMember(M); - M->setTrivialForCall( - HasTrivialABI || - SpecialMemberIsTrivial(M, CSM, TAH_ConsiderTrivialABI)); - Record->setTrivialForCallFlags(M); - } - } - - // Set triviality for the purpose of calls if this is a user-provided - // copy/move constructor or destructor. - if ((CSM == CXXCopyConstructor || CSM == CXXMoveConstructor || - CSM == CXXDestructor) && M->isUserProvided()) { - M->setTrivialForCall(HasTrivialABI); - Record->setTrivialForCallFlags(M); - } - if (!M->isInvalidDecl() && M->isExplicitlyDefaulted() && - M->hasAttr()) { - if (getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) && - M->isTrivial() && - (CSM == CXXDefaultConstructor || CSM == CXXCopyConstructor || - CSM == CXXDestructor)) - M->dropAttr(); - - if (M->hasAttr()) { - DefineImplicitSpecialMember(*this, M, M->getLocation()); - ActOnFinishInlineFunctionDef(M); - } - } + if (!isa(M)) + CompleteMemberFunction(M); } } diff --git a/test/SemaCXX/constant-expression-cxx1y.cpp b/test/SemaCXX/constant-expression-cxx1y.cpp index fe69d10502..6a344c8d4a 100644 --- a/test/SemaCXX/constant-expression-cxx1y.cpp +++ b/test/SemaCXX/constant-expression-cxx1y.cpp @@ -1205,3 +1205,19 @@ namespace ObjectsUnderConstruction { // The lifetime of 'n' begins at the initialization, not before. constexpr int n = ++const_cast(n); // expected-error {{constant expression}} expected-note {{modification}} } + +namespace PR39728 { + struct Comment0 { + Comment0 &operator=(const Comment0 &) = default; + ~Comment0() = default; + }; + constexpr void f() { + Comment0 a; + a = a; + } + static_assert((f(), true), ""); + struct Comment1 { + constexpr Comment1 &operator=(const Comment1 &) = default; // OK + ~Comment1() = default; + }; +}