From 4fbaa62b60b872654a5e2dbfd68337dc34b5f8cc Mon Sep 17 00:00:00 2001 From: Volodymyr Sapsai Date: Thu, 21 Sep 2017 19:54:12 +0000 Subject: [PATCH] [Sema] Prevent InstantiateClass from checking unrelated exception specs. Sema::InstantiateClass should check only exception specs added during class instantiation and ignore already present delayed specs. This fixes a case where we instantiate a class before parsing member initializers, check exceptions for a different class and fail to find a member initializer. Which is required for comparing exception specs for explicitly-defaulted and implicit default constructor. With the fix we are still checking exception specs but only after member initializers are present. Removing errors in crash-unparsed-exception.cpp is acceptable according to discussion in PR24000 because other compilers accept code in crash-unparsed-exception.cpp as valid. rdar://problem/34167492 Reviewers: davide, rsmith Reviewed By: rsmith Subscribers: dim, cfe-commits Differential Revision: https://reviews.llvm.org/D37881 llvm-svn: 313906 --- clang/include/clang/Sema/Sema.h | 30 +++++++++++++++++++ clang/lib/Sema/SemaTemplateInstantiate.cpp | 14 ++++----- .../SemaTemplate/crash-unparsed-exception.cpp | 5 ++-- .../SemaTemplate/default-arguments-cxx0x.cpp | 27 +++++++++++++++++ 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 9420407acb886..0f2729eb87fff 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10503,6 +10503,36 @@ class Sema { SmallVector DelayedDllExportClasses; private: + class SavePendingParsedClassStateRAII { + public: + SavePendingParsedClassStateRAII(Sema &S) : S(S) { swapSavedState(); } + + ~SavePendingParsedClassStateRAII() { + assert(S.DelayedExceptionSpecChecks.empty() && + "there shouldn't be any pending delayed exception spec checks"); + assert(S.DelayedDefaultedMemberExceptionSpecs.empty() && + "there shouldn't be any pending delayed defaulted member " + "exception specs"); + assert(S.DelayedDllExportClasses.empty() && + "there shouldn't be any pending delayed DLL export classes"); + swapSavedState(); + } + + private: + Sema &S; + decltype(DelayedExceptionSpecChecks) SavedExceptionSpecChecks; + decltype(DelayedDefaultedMemberExceptionSpecs) + SavedDefaultedMemberExceptionSpecs; + decltype(DelayedDllExportClasses) SavedDllExportClasses; + + void swapSavedState() { + SavedExceptionSpecChecks.swap(S.DelayedExceptionSpecChecks); + SavedDefaultedMemberExceptionSpecs.swap( + S.DelayedDefaultedMemberExceptionSpecs); + SavedDllExportClasses.swap(S.DelayedDllExportClasses); + } + }; + /// \brief Helper class that collects misaligned member designations and /// their location info for delayed diagnostics. struct MisalignedMember { diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 1a06f9a2f85a3..16f7381330807 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -2026,12 +2026,11 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, bool MergeWithParentScope = !Instantiation->isDefinedOutsideFunctionOrMethod(); LocalInstantiationScope Scope(*this, MergeWithParentScope); - // All dllexported classes created during instantiation should be fully - // emitted after instantiation completes. We may not be ready to emit any - // delayed classes already on the stack, so save them away and put them back - // later. - decltype(DelayedDllExportClasses) ExportedClasses; - std::swap(ExportedClasses, DelayedDllExportClasses); + // Some class state isn't processed immediately but delayed till class + // instantiation completes. We may not be ready to handle any delayed state + // already on the stack as it might correspond to a different class, so save + // it now and put it back later. + SavePendingParsedClassStateRAII SavedPendingParsedClassState(*this); // Pull attributes from the pattern onto the instantiation. InstantiateAttrs(TemplateArgs, Pattern, Instantiation); @@ -2118,9 +2117,6 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, // default arg exprs for default constructors if necessary now. ActOnFinishCXXNonNestedClass(Instantiation); - // Put back the delayed exported classes that we moved out of the way. - std::swap(ExportedClasses, DelayedDllExportClasses); - // Instantiate late parsed attributes, and attach them to their decls. // See Sema::InstantiateAttrs for (LateInstantiatedAttrVec::iterator I = LateAttrs.begin(), diff --git a/clang/test/SemaTemplate/crash-unparsed-exception.cpp b/clang/test/SemaTemplate/crash-unparsed-exception.cpp index 1226b35deb701..3137d3fa197c5 100644 --- a/clang/test/SemaTemplate/crash-unparsed-exception.cpp +++ b/clang/test/SemaTemplate/crash-unparsed-exception.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify -fcxx-exceptions -fexceptions %s +// expected-no-diagnostics struct A { virtual ~A(); @@ -11,7 +12,7 @@ struct C { ~D() throw(); }; struct E : A { - D d; //expected-error{{exception specification is not available until end of class definition}} + D d; }; - B b; //expected-note{{in instantiation of template class 'B' requested here}} + B b; }; diff --git a/clang/test/SemaTemplate/default-arguments-cxx0x.cpp b/clang/test/SemaTemplate/default-arguments-cxx0x.cpp index d9fa2b4a825ec..c24ed12a0248c 100644 --- a/clang/test/SemaTemplate/default-arguments-cxx0x.cpp +++ b/clang/test/SemaTemplate/default-arguments-cxx0x.cpp @@ -87,3 +87,30 @@ namespace PR13986 { A<1> m_target; }; } + +// rdar://problem/34167492 +// Template B is instantiated during checking if defaulted A copy constructor +// is constexpr. For this we check if S copy constructor is constexpr. And +// for this we check S constructor template with default argument that mentions +// template B. In turn, template instantiation triggers checking defaulted +// members exception spec. The problem is that it checks defaulted members not +// for instantiated class only, but all defaulted members so far. In this case +// we try to check exception spec for A default constructor which requires +// initializer for the field _a. But initializers are added after constexpr +// check so we reject the code because cannot find _a initializer. +namespace rdar34167492 { + template struct B { using type = bool; }; + + template struct S { + S() noexcept; + + template ::type = true> + S(const S&) noexcept; + }; + + class A { + A() noexcept = default; + A(const A&) noexcept = default; + S _a{}; + }; +}