From 5786130f48520f80c14529cdbdf48968168b9a41 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 5 Sep 2018 22:30:37 +0000 Subject: [PATCH] PR38627: Fix handling of exception specification adjustment for destructors. We previously tried to patch up the exception specification after completing the class, which went wrong when the exception specification was needed within the class body (in particular, by a friend redeclaration of the destructor in a nested class). We now mark the destructor as having a not-yet-computed exception specification immediately after creating it. This requires delaying various checks against the exception specification (where we'd previously have just got the wrong exception specification, and now find we have an exception specification that we can't compute yet) when those checks fire while the class is being defined. This also exposed an issue that we were missing a CodeSynthesisContext for computation of exception specifications (otherwise we'd fail to make the module containing the definition of the class visible when computing its members' exception specs). Adding that incidentally also gives us a diagnostic quality improvement. This has also exposed an pre-existing problem: making the exception specification evaluation context a non-SFINAE context (as it should be) results in a bootstrap failure; PR38850 filed for this. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@341499 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 8 ++- include/clang/Sema/Sema.h | 31 ++++++++-- lib/Frontend/FrontendActions.cpp | 2 + lib/Sema/Sema.cpp | 5 +- lib/Sema/SemaDecl.cpp | 20 ++----- lib/Sema/SemaDeclCXX.cpp | 59 +++++++++++++++---- lib/Sema/SemaExceptionSpec.cpp | 47 +++++++++------ lib/Sema/SemaTemplateInstantiate.cpp | 13 ++++ lib/Sema/SemaTemplateInstantiateDecl.cpp | 3 + test/CXX/drs/dr13xx.cpp | 6 +- .../expr.prim/expr.prim.lambda/templates.cpp | 4 +- test/Modules/cxx-templates.cpp | 2 + test/Modules/exception-spec.cpp | 36 +++++++++++ test/SemaCXX/constant-expression-cxx11.cpp | 4 +- test/SemaCXX/cxx0x-defaulted-functions.cpp | 8 +-- test/SemaCXX/exception-spec.cpp | 47 +++++++++++++++ test/SemaCXX/implicit-exception-spec.cpp | 22 ++++--- test/SemaCXX/member-init.cpp | 20 +++---- test/SemaTemplate/instantiate-init.cpp | 6 +- 19 files changed, 254 insertions(+), 89 deletions(-) create mode 100644 test/Modules/exception-spec.cpp diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 6db9bbea27..c1f0774d63 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1467,6 +1467,10 @@ def err_noexcept_needs_constant_expression : Error< "argument to noexcept specifier must be a constant expression">; def err_exception_spec_not_parsed : Error< "exception specification is not available until end of class definition">; +def err_exception_spec_cycle : Error< + "exception specification of %0 uses itself">; +def err_exception_spec_incomplete_type : Error< + "exception specification needed for member of incomplete class %0">; // C++ access checking def err_class_redeclared_with_different_access : Error< @@ -4270,6 +4274,8 @@ def note_forward_template_decl : Note< def note_inst_declaration_hint : Note<"add an explicit instantiation " "declaration to suppress this warning if %q0 is explicitly instantiated in " "another translation unit">; +def note_evaluating_exception_spec_here : Note< + "in evaluation of exception specification for %q0 needed here">; def note_default_arg_instantiation_here : Note< "in instantiation of default argument for '%0' required here">; @@ -7471,8 +7477,6 @@ def note_in_class_initializer_not_yet_parsed : Note< "default member initializer declared here">; def err_in_class_initializer_cycle : Error<"default member initializer for %0 uses itself">; -def err_exception_spec_cycle - : Error<"exception specification of %0 uses itself">; def ext_in_class_initializer_non_constant : Extension< "in-class initializer for static data member is not a constant expression; " diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 5f6cc51569..465e8cddb5 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -608,7 +608,15 @@ public: /// that had their exception spec checks delayed, plus the overridden /// function. SmallVector, 2> - DelayedExceptionSpecChecks; + DelayedOverridingExceptionSpecChecks; + + /// All the function redeclarations seen during a class definition that had + /// their exception spec checks delayed, plus the prior declaration they + /// should be checked against. Except during error recovery, the new decl + /// should always be a friend declaration, as that's the only valid way to + /// redeclare a special member before its class is complete. + SmallVector, 2> + DelayedEquivalentExceptionSpecChecks; /// All the members seen during a class definition which were both /// explicitly defaulted and had explicitly-specified exception @@ -4872,8 +4880,7 @@ public: /// /// C++11 says that user-defined destructors with no exception spec get one /// that looks as if the destructor was implicitly declared. - void AdjustDestructorExceptionSpec(CXXRecordDecl *ClassDecl, - CXXDestructorDecl *Destructor); + void AdjustDestructorExceptionSpec(CXXDestructorDecl *Destructor); /// Define the specified inheriting constructor. void DefineInheritingConstructor(SourceLocation UseLoc, @@ -7142,6 +7149,10 @@ public: /// has been used when naming a template-id. DefaultTemplateArgumentChecking, + /// We are computing the exception specification for a defaulted special + /// member function. + ExceptionSpecEvaluation, + /// We are instantiating the exception specification for a function /// template which was deferred until it was needed. ExceptionSpecInstantiation, @@ -10635,7 +10646,9 @@ private: SavePendingParsedClassStateRAII(Sema &S) : S(S) { swapSavedState(); } ~SavePendingParsedClassStateRAII() { - assert(S.DelayedExceptionSpecChecks.empty() && + assert(S.DelayedOverridingExceptionSpecChecks.empty() && + "there shouldn't be any pending delayed exception spec checks"); + assert(S.DelayedEquivalentExceptionSpecChecks.empty() && "there shouldn't be any pending delayed exception spec checks"); assert(S.DelayedDefaultedMemberExceptionSpecs.empty() && "there shouldn't be any pending delayed defaulted member " @@ -10647,13 +10660,19 @@ private: private: Sema &S; - decltype(DelayedExceptionSpecChecks) SavedExceptionSpecChecks; + decltype(DelayedOverridingExceptionSpecChecks) + SavedOverridingExceptionSpecChecks; + decltype(DelayedEquivalentExceptionSpecChecks) + SavedEquivalentExceptionSpecChecks; decltype(DelayedDefaultedMemberExceptionSpecs) SavedDefaultedMemberExceptionSpecs; decltype(DelayedDllExportClasses) SavedDllExportClasses; void swapSavedState() { - SavedExceptionSpecChecks.swap(S.DelayedExceptionSpecChecks); + SavedOverridingExceptionSpecChecks.swap( + S.DelayedOverridingExceptionSpecChecks); + SavedEquivalentExceptionSpecChecks.swap( + S.DelayedEquivalentExceptionSpecChecks); SavedDefaultedMemberExceptionSpecs.swap( S.DelayedDefaultedMemberExceptionSpecs); SavedDllExportClasses.swap(S.DelayedDllExportClasses); diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp index 8a8354c7d4..342081eba6 100644 --- a/lib/Frontend/FrontendActions.cpp +++ b/lib/Frontend/FrontendActions.cpp @@ -341,6 +341,8 @@ private: return "PriorTemplateArgumentSubstitution"; case CodeSynthesisContext::DefaultTemplateArgumentChecking: return "DefaultTemplateArgumentChecking"; + case CodeSynthesisContext::ExceptionSpecEvaluation: + return "ExceptionSpecEvaluation"; case CodeSynthesisContext::ExceptionSpecInstantiation: return "ExceptionSpecInstantiation"; case CodeSynthesisContext::DeclaringSpecialMember: diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index bd2637a72e..82a04e1070 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -922,10 +922,9 @@ void Sema::ActOnEndOfTranslationUnit() { // All delayed member exception specs should be checked or we end up accepting // incompatible declarations. - // FIXME: This is wrong for TUKind == TU_Prefix. In that case, we need to - // write out the lists to the AST file (if any). + assert(DelayedOverridingExceptionSpecChecks.empty()); + assert(DelayedEquivalentExceptionSpecChecks.empty()); assert(DelayedDefaultedMemberExceptionSpecs.empty()); - assert(DelayedExceptionSpecChecks.empty()); // All dllexport classes should have been processed already. assert(DelayedDllExportClasses.empty()); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index e62728c124..c705b4920e 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -7956,14 +7956,11 @@ static FunctionDecl* CreateNewFunctionDecl(Sema &SemaRef, Declarator &D, NameInfo, R, TInfo, isInline, /*isImplicitlyDeclared=*/false); - // If the class is complete, then we now create the implicit exception - // specification. If the class is incomplete or dependent, we can't do - // it yet. - if (SemaRef.getLangOpts().CPlusPlus11 && !Record->isDependentType() && - Record->getDefinition() && !Record->isBeingDefined() && - R->getAs()->getExceptionSpecType() == EST_None) { - SemaRef.AdjustDestructorExceptionSpec(Record, NewDD); - } + // If the destructor needs an implicit exception specification, set it + // now. FIXME: It'd be nice to be able to create the right type to start + // with, but the type needs to reference the destructor declaration. + if (SemaRef.getLangOpts().CPlusPlus11) + SemaRef.AdjustDestructorExceptionSpec(NewDD); IsVirtualOkay = true; return NewDD; @@ -15810,13 +15807,6 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, } if (!CXXRecord->isDependentType()) { - if (CXXRecord->hasUserDeclaredDestructor()) { - // Adjust user-defined destructor exception spec. - if (getLangOpts().CPlusPlus11) - AdjustDestructorExceptionSpec(CXXRecord, - CXXRecord->getDestructor()); - } - // Add any implicitly-declared members to this class. AddImplicitlyDeclaredMembersToClass(CXXRecord); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 61a7730d9b..d3f7af2529 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -6635,20 +6635,27 @@ void Sema::CheckExplicitlyDefaultedMemberExceptionSpec( } void Sema::CheckDelayedMemberExceptionSpecs() { - decltype(DelayedExceptionSpecChecks) Checks; - decltype(DelayedDefaultedMemberExceptionSpecs) Specs; + decltype(DelayedOverridingExceptionSpecChecks) Overriding; + decltype(DelayedEquivalentExceptionSpecChecks) Equivalent; + decltype(DelayedDefaultedMemberExceptionSpecs) Defaulted; - std::swap(Checks, DelayedExceptionSpecChecks); - std::swap(Specs, DelayedDefaultedMemberExceptionSpecs); + std::swap(Overriding, DelayedOverridingExceptionSpecChecks); + std::swap(Equivalent, DelayedEquivalentExceptionSpecChecks); + std::swap(Defaulted, DelayedDefaultedMemberExceptionSpecs); // Perform any deferred checking of exception specifications for virtual // destructors. - for (auto &Check : Checks) + for (auto &Check : Overriding) CheckOverridingFunctionExceptionSpec(Check.first, Check.second); + // Perform any deferred checking of exception specifications for befriended + // special members. + for (auto &Check : Equivalent) + CheckEquivalentExceptionSpec(Check.second, Check.first); + // Check that any explicitly-defaulted methods have exception specifications // compatible with their implicit exception specifications. - for (auto &Spec : Specs) + for (auto &Spec : Defaulted) CheckExplicitlyDefaultedMemberExceptionSpec(Spec.first, Spec.second); } @@ -10685,19 +10692,48 @@ void SpecialMemberExceptionSpecInfo::visitSubobjectCall( ExceptSpec.CalledDecl(getSubobjectLoc(Subobj), MD); } +namespace { +/// RAII object to register a special member as being currently declared. +struct ComputingExceptionSpec { + Sema &S; + + ComputingExceptionSpec(Sema &S, CXXMethodDecl *MD, SourceLocation Loc) + : S(S) { + Sema::CodeSynthesisContext Ctx; + Ctx.Kind = Sema::CodeSynthesisContext::ExceptionSpecEvaluation; + Ctx.PointOfInstantiation = Loc; + Ctx.Entity = MD; + S.pushCodeSynthesisContext(Ctx); + } + ~ComputingExceptionSpec() { + S.popCodeSynthesisContext(); + } +}; +} + static Sema::ImplicitExceptionSpecification ComputeDefaultedSpecialMemberExceptionSpec( Sema &S, SourceLocation Loc, CXXMethodDecl *MD, Sema::CXXSpecialMember CSM, Sema::InheritedConstructorInfo *ICI) { + ComputingExceptionSpec CES(S, MD, Loc); + CXXRecordDecl *ClassDecl = MD->getParent(); // C++ [except.spec]p14: // An implicitly declared special member function (Clause 12) shall have an // exception-specification. [...] - SpecialMemberExceptionSpecInfo Info(S, MD, CSM, ICI, Loc); + SpecialMemberExceptionSpecInfo Info(S, MD, CSM, ICI, MD->getLocation()); if (ClassDecl->isInvalidDecl()) return Info.ExceptSpec; + // FIXME: If this diagnostic fires, we're probably missing a check for + // attempting to resolve an exception specification before it's known + // at a higher level. + if (S.RequireCompleteType(MD->getLocation(), + S.Context.getRecordType(ClassDecl), + diag::err_exception_spec_incomplete_type)) + return Info.ExceptSpec; + // C++1z [except.spec]p7: // [Look for exceptions thrown by] a constructor selected [...] to // initialize a potentially constructed subobject, @@ -11172,8 +11208,9 @@ void Sema::ActOnFinishCXXMemberDecls() { // If the context is an invalid C++ class, just suppress these checks. if (CXXRecordDecl *Record = dyn_cast(CurContext)) { if (Record->isInvalidDecl()) { + DelayedOverridingExceptionSpecChecks.clear(); + DelayedEquivalentExceptionSpecChecks.clear(); DelayedDefaultedMemberExceptionSpecs.clear(); - DelayedExceptionSpecChecks.clear(); return; } checkForMultipleExportedDefaultConstructors(*this, Record); @@ -11195,11 +11232,13 @@ void Sema::referenceDLLExportedClassMethods() { } } -void Sema::AdjustDestructorExceptionSpec(CXXRecordDecl *ClassDecl, - CXXDestructorDecl *Destructor) { +void Sema::AdjustDestructorExceptionSpec(CXXDestructorDecl *Destructor) { assert(getLangOpts().CPlusPlus11 && "adjusting dtor exception specs was introduced in c++11"); + if (Destructor->isDependentContext()) + return; + // C++11 [class.dtor]p3: // A declaration of a destructor that does not have an exception- // specification is implicitly considered to have the same exception- diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp index ec9dab8f53..f3e1563c3a 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/lib/Sema/SemaExceptionSpec.cpp @@ -230,6 +230,16 @@ Sema::UpdateExceptionSpec(FunctionDecl *FD, Context.adjustExceptionSpec(Redecl, ESI); } +static bool exceptionSpecNotKnownYet(const FunctionDecl *FD) { + auto *MD = dyn_cast(FD); + if (!MD) + return false; + + auto EST = MD->getType()->castAs()->getExceptionSpecType(); + return EST == EST_Unparsed || + (EST == EST_Unevaluated && MD->getParent()->isBeingDefined()); +}; + static bool CheckEquivalentExceptionSpecImpl( Sema &S, const PartialDiagnostic &DiagID, const PartialDiagnostic &NoteID, const FunctionProtoType *Old, SourceLocation OldLoc, @@ -278,6 +288,14 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { ReturnValueOnError = false; } + // If we're befriending a member function of a class that's currently being + // defined, we might not be able to work out its exception specification yet. + // If not, defer the check until later. + if (exceptionSpecNotKnownYet(Old) || exceptionSpecNotKnownYet(New)) { + DelayedEquivalentExceptionSpecChecks.push_back({New, Old}); + return false; + } + // Check the types as written: they must match before any exception // specification adjustment is applied. if (!CheckEquivalentExceptionSpecImpl( @@ -904,26 +922,21 @@ bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New, if (New->getType()->castAs()->getExceptionSpecType() == EST_Unparsed) return false; - if (getLangOpts().CPlusPlus11 && isa(New)) { - // Don't check uninstantiated template destructors at all. We can only - // synthesize correct specs after the template is instantiated. - if (New->getParent()->isDependentType()) - return false; - if (New->getParent()->isBeingDefined()) { - // The destructor might be updated once the definition is finished. So - // remember it and check later. - DelayedExceptionSpecChecks.push_back(std::make_pair(New, Old)); - return false; - } - } - // If the old exception specification hasn't been parsed yet, remember that - // we need to perform this check when we get to the end of the outermost + + // Don't check uninstantiated template destructors at all. We can only + // synthesize correct specs after the template is instantiated. + if (isa(New) && New->getParent()->isDependentType()) + return false; + + // If the old exception specification hasn't been parsed yet, or the new + // exception specification can't be computed yet, remember that we need to + // perform this check when we get to the end of the outermost // lexically-surrounding class. - if (Old->getType()->castAs()->getExceptionSpecType() == - EST_Unparsed) { - DelayedExceptionSpecChecks.push_back(std::make_pair(New, Old)); + if (exceptionSpecNotKnownYet(Old) || exceptionSpecNotKnownYet(New)) { + DelayedOverridingExceptionSpecChecks.push_back({New, Old}); return false; } + unsigned DiagID = diag::err_override_exception_spec; if (getLangOpts().MicrosoftExt) DiagID = diag::ext_override_exception_spec; diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 9a5879e675..5666cf04a2 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -199,6 +199,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const { case DefaultTemplateArgumentChecking: case DeclaringSpecialMember: case DefiningSynthesizedFunction: + case ExceptionSpecEvaluation: return false; // This function should never be called when Kind's value is Memoization. @@ -621,6 +622,12 @@ void Sema::PrintInstantiationStack() { break; } + case CodeSynthesisContext::ExceptionSpecEvaluation: + Diags.Report(Active->PointOfInstantiation, + diag::note_evaluating_exception_spec_here) + << cast(Active->Entity); + break; + case CodeSynthesisContext::ExceptionSpecInstantiation: Diags.Report(Active->PointOfInstantiation, diag::note_template_exception_spec_instantiation_here) @@ -695,6 +702,12 @@ Optional Sema::isSFINAEContext() const { // there is no SFINAE. return None; + case CodeSynthesisContext::ExceptionSpecEvaluation: + // FIXME: This should not be treated as a SFINAE context, because + // we will cache an incorrect exception specification. However, clang + // bootstrap relies this! See PR31692. + break; + case CodeSynthesisContext::Memoization: break; } diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 80980106c4..3ed9eeed16 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3690,6 +3690,9 @@ TemplateDeclInstantiator::InitMethodInstantiation(CXXMethodDecl *New, if (InitFunctionInstantiation(New, Tmpl)) return true; + if (isa(New) && SemaRef.getLangOpts().CPlusPlus11) + SemaRef.AdjustDestructorExceptionSpec(cast(New)); + New->setAccess(Tmpl->getAccess()); if (Tmpl->isVirtualAsWritten()) New->setVirtualAsWritten(true); diff --git a/test/CXX/drs/dr13xx.cpp b/test/CXX/drs/dr13xx.cpp index f193c8e024..208ab8a03b 100644 --- a/test/CXX/drs/dr13xx.cpp +++ b/test/CXX/drs/dr13xx.cpp @@ -226,10 +226,10 @@ namespace dr1330 { // dr1330: 4 c++11 #endif void f(D &d) { d = d; } // ok - // FIXME: In C++11 onwards, we should also note the declaration of 'e' as the - // line that triggers the use of E::E()'s exception specification. struct E : C {}; // expected-note {{in instantiation of}} - E e; +#if __cplusplus >= 201103L + E e; // expected-note {{needed here}} +#endif } namespace dr1346 { // dr1346: 3.5 diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/templates.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/templates.cpp index 31213c9ebc..57e555e8d9 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/templates.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/templates.cpp @@ -139,11 +139,11 @@ namespace NonLocalLambdaInstantation { } template - struct X2 { + struct X2 { // expected-note{{in instantiation of default member initializer 'NonLocalLambdaInstantation::X2::x'}} int x = []{ return T(); }(); // expected-error{{cannot initialize a member subobject of type 'int' with an rvalue of type 'int *'}} }; X2 x2i; X2 x2f; - X2 x2ip; // expected-note{{in instantiation of default member initializer 'NonLocalLambdaInstantation::X2::x'}} + X2 x2ip; // expected-note {{in evaluation of exception spec}} } diff --git a/test/Modules/cxx-templates.cpp b/test/Modules/cxx-templates.cpp index 25f8a99925..c085eb5f67 100644 --- a/test/Modules/cxx-templates.cpp +++ b/test/Modules/cxx-templates.cpp @@ -199,6 +199,8 @@ namespace hidden_specializations { cls uk4; // expected-error 1+{{partial specialization of 'cls' must be imported}} expected-error 1+{{definition of}} cls::nested_cls unk1; // expected-error 1+{{explicit specialization of 'nested_cls' must be imported}} expected-error 1+{{definition of}} cls::nested_cls_t unk2; // expected-error 1+{{explicit specialization of 'nested_cls_t' must be imported}} expected-error 1+{{definition of}} + // expected-error@cxx-templates-unimported.h:29 {{explicit specialization of 'nested_cls_t' must be imported}} + // expected-note@-2 {{in evaluation of exception specification}} cls::nested_cls_t unk3; // expected-error 1+{{explicit specialization of 'nested_cls_t' must be imported}} // For enums, uses that would trigger instantiations of definitions are not diff --git a/test/Modules/exception-spec.cpp b/test/Modules/exception-spec.cpp new file mode 100644 index 0000000000..083cd950f7 --- /dev/null +++ b/test/Modules/exception-spec.cpp @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -x c++ -std=c++17 -fmodules -fmodules-local-submodule-visibility -fmodules-cache-path=%t %s -verify + +// expected-no-diagnostics + +#pragma clang module build PR38627 +module PR38627 {} +#pragma clang module contents +#pragma clang module begin PR38627 +namespace PR38627 { +struct X { + virtual ~X() {} + struct C { + friend X::~X(); + } c; +}; +} +#pragma clang module end +#pragma clang module endbuild + +#pragma clang module import PR38627 + +namespace PR38627 { +struct Y { + virtual ~Y() {} + struct C { + friend Y::~Y(); + } c; +}; +static_assert(noexcept(X().~X())); +static_assert(noexcept(Y().~Y())); + +struct A { virtual ~A() = default; }; +struct B : public A, public X { + virtual ~B() override = default; +}; +} // PR38627 diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index a64207583d..c9bfd84641 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -1972,9 +1972,9 @@ namespace ZeroSizeTypes { namespace BadDefaultInit { template struct X { static const int n = N; }; - struct A { + struct A { // expected-error {{default member initializer for 'k' needed within definition of enclosing class}} int k = // expected-note {{default member initializer declared here}} - X::n; // expected-error {{default member initializer for 'k' needed within definition of enclosing class}} + X::n; // expected-note {{in evaluation of exception specification for 'BadDefaultInit::A::A' needed here}} }; // FIXME: The "constexpr constructor must initialize all members" diagnostic diff --git a/test/SemaCXX/cxx0x-defaulted-functions.cpp b/test/SemaCXX/cxx0x-defaulted-functions.cpp index f623dc765f..6346e1c235 100644 --- a/test/SemaCXX/cxx0x-defaulted-functions.cpp +++ b/test/SemaCXX/cxx0x-defaulted-functions.cpp @@ -99,8 +99,6 @@ namespace DefaultedFnExceptionSpec { Error c; // expected-note 2{{instantiation of}} struct DelayImplicit { - // FIXME: The location of this note is terrible. The instantiation was - // triggered by the uses of the functions in the decltype expressions below. Error e; // expected-note 6{{instantiation of}} }; Error *e; @@ -109,9 +107,9 @@ namespace DefaultedFnExceptionSpec { // a defaulted special member function that calls the function is needed. // Use in an unevaluated operand still results in the exception spec being // needed. - void test1(decltype(declval() = DelayImplicit(DelayImplicit()))); - void test2(decltype(declval() = declval())); - void test3(decltype(DelayImplicit(declval()))); + void test1(decltype(declval() = DelayImplicit(DelayImplicit()))); // expected-note 4{{in evaluation of exception specification}} + void test2(decltype(declval() = declval())); // expected-note {{in evaluation of exception specification}} + void test3(decltype(DelayImplicit(declval()))); // expected-note {{in evaluation of exception specification}} // Any odr-use needs the exception specification. void f(Error *p) { diff --git a/test/SemaCXX/exception-spec.cpp b/test/SemaCXX/exception-spec.cpp index f301a63503..6ad19aab39 100644 --- a/test/SemaCXX/exception-spec.cpp +++ b/test/SemaCXX/exception-spec.cpp @@ -5,3 +5,50 @@ namespace MissingOnTemplate { template void foo(T); // expected-error {{missing exception specification 'noexcept(true)'}} void test() { foo(0); } } + +struct UseBeforeComplete1 { + ~UseBeforeComplete1(); // expected-note {{previous}} + struct X { + friend UseBeforeComplete1::~UseBeforeComplete1() noexcept; // expected-warning {{previously declared with an implicit}} + }; +}; + +struct ThrowingDtor { ~ThrowingDtor() noexcept(false); }; +struct UseBeforeComplete2 { + ~UseBeforeComplete2(); // expected-note {{previous}} + struct X { + friend UseBeforeComplete2::~UseBeforeComplete2() noexcept; // expected-error {{does not match previous}} + }; + ThrowingDtor td; +}; + +struct UseBeforeComplete3 { + ~UseBeforeComplete3(); + struct X { + friend UseBeforeComplete3::~UseBeforeComplete3(); // ok, implicitly noexcept(true) + }; +}; +static_assert(noexcept(UseBeforeComplete3()), ""); + +struct UseBeforeComplete4 { + ~UseBeforeComplete4(); + struct X { + friend UseBeforeComplete4::~UseBeforeComplete4(); // ok, implicitly noexcept(false) + }; + ThrowingDtor td; +}; +static_assert(!noexcept(UseBeforeComplete4()), ""); + +namespace AssignmentOp { + struct D1; + struct D2; + struct B { + B &operator=(const B&); + virtual D1 &operator=(const D1&) noexcept; // expected-note {{overridden}} + virtual D2 &operator=(const D2&) noexcept; // expected-note {{overridden}} + }; + struct D1 : B {}; // expected-error {{more lax}} + struct D2 : B { + D2 &operator=(const D2&); // expected-error {{more lax}} + }; +} diff --git a/test/SemaCXX/implicit-exception-spec.cpp b/test/SemaCXX/implicit-exception-spec.cpp index c21f773e94..19d645d6d3 100644 --- a/test/SemaCXX/implicit-exception-spec.cpp +++ b/test/SemaCXX/implicit-exception-spec.cpp @@ -16,41 +16,39 @@ namespace InClassInitializers { // Noexcept::Noexcept is not declared constexpr, therefore noexcept(Noexcept()) // is false. bool ThrowSomething() noexcept(false); - struct ConstExpr { + struct ConstExpr { // expected-error {{default member initializer for 'b' needed}} bool b = // expected-note {{declared here}} - noexcept(ConstExpr()) && ThrowSomething(); // expected-error {{default member initializer for 'b' needed}} + noexcept(ConstExpr()) && ThrowSomething(); // expected-note {{in evaluation of exception spec}} }; // Much more obviously broken: we can't parse the initializer without already // knowing whether it produces a noexcept expression. - struct TemplateArg { + struct TemplateArg { // expected-error {{default member initializer for 'n' needed}} int n = // expected-note {{declared here}} - ExceptionIf::f(); // expected-error {{default member initializer for 'n' needed}} + ExceptionIf::f(); // expected-note {{in evaluation of exception spec}} }; // And within a nested class. struct Nested { - struct Inner { + struct Inner { // expected-error {{default member initializer for 'n' needed}} int n = // expected-note {{declared here}} - ExceptionIf::f(); - } inner; // expected-error {{default member initializer for 'n' needed}} + ExceptionIf::f(); // expected-note {{in evaluation of exception spec}} + } inner; // expected-note {{in evaluation of exception spec}} }; struct Nested2 { struct Inner; - int n = Inner().n; // expected-error {{initializer for 'n' needed}} - struct Inner { + int n = Inner().n; // expected-note {{in evaluation of exception spec}} + struct Inner { // expected-error {{initializer for 'n' needed}} int n = ExceptionIf::f(); // expected-note {{declared here}} } inner; }; } namespace ExceptionSpecification { - // FIXME: This diagnostic is quite useless; we should indicate whose - // exception specification we were looking for and why. struct Nested { struct T { - T() noexcept(!noexcept(Nested())); + T() noexcept(!noexcept(Nested())); // expected-note {{in evaluation of exception spec}} } t; // expected-error{{exception specification is not available until end of class definition}} }; } diff --git a/test/SemaCXX/member-init.cpp b/test/SemaCXX/member-init.cpp index 8a13eca2f1..2c4659afa3 100644 --- a/test/SemaCXX/member-init.cpp +++ b/test/SemaCXX/member-init.cpp @@ -13,10 +13,10 @@ public: bool b(); int k; -struct Recurse { +struct Recurse { // expected-error {{initializer for 'n' needed}} int &n = // expected-note {{declared here}} b() ? - Recurse().n : // expected-error {{initializer for 'n' needed}} + Recurse().n : // expected-note {{in evaluation of exception spec}} k; }; @@ -127,19 +127,19 @@ A::A() {} namespace template_default_ctor { struct A { template - struct B { + struct B { // expected-error {{initializer for 'm1' needed}} int m1 = 0; // expected-note {{declared here}} }; - enum { NOE = noexcept(B()) }; // expected-error {{initializer for 'm1' needed}} + enum { NOE = noexcept(B()) }; // expected-note {{in evaluation of exception spec}} }; } namespace default_ctor { struct A { - struct B { + struct B { // expected-error {{initializer for 'm1' needed}} int m1 = 0; // expected-note {{declared here}} }; - enum { NOE = noexcept(B()) }; // expected-error {{initializer for 'm1' needed}} + enum { NOE = noexcept(B()) }; // expected-note {{in evaluation of exception spec}} }; } @@ -147,17 +147,17 @@ namespace member_template { struct A { template struct B { - struct C { + struct C { // expected-error {{initializer for 'm1' needed}} int m1 = 0; // expected-note {{declared here}} }; template - struct D { + struct D { // expected-error {{initializer for 'm1' needed}} int m1 = 0; // expected-note {{declared here}} }; }; enum { - NOE1 = noexcept(B::C()), // expected-error {{initializer for 'm1' needed}} - NOE2 = noexcept(B::D()) // expected-error {{initializer for 'm1' needed}} + NOE1 = noexcept(B::C()), // expected-note {{in evaluation of exception spec}} + NOE2 = noexcept(B::D()) // expected-note {{in evaluation of exception spec}} }; }; } diff --git a/test/SemaTemplate/instantiate-init.cpp b/test/SemaTemplate/instantiate-init.cpp index 51fa6955d0..b58ad3a157 100644 --- a/test/SemaTemplate/instantiate-init.cpp +++ b/test/SemaTemplate/instantiate-init.cpp @@ -115,8 +115,10 @@ namespace PR13064 { struct A { explicit A(int); }; // expected-note{{here}} template struct B { T a { 0 }; }; B b; - template struct C { T a = { 0 }; }; // expected-error{{explicit}} - C c; // expected-note {{in instantiation of default member initializer}} + template struct C { // expected-note {{in instantiation of default member initializer}} + T a = {0}; // expected-error{{explicit}} + }; + C c; // expected-note {{in evaluation of exception spec}} } namespace PR16903 { -- 2.40.0