Skip to content

Commit

Permalink
[Sema] Prevent InstantiateClass from checking unrelated exception specs.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
vsapsai committed Sep 21, 2017
1 parent 0fe506b commit 4fbaa62
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 11 deletions.
30 changes: 30 additions & 0 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -10503,6 +10503,36 @@ class Sema {
SmallVector<CXXRecordDecl*, 4> 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 {
Expand Down
14 changes: 5 additions & 9 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Expand Up @@ -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);
Expand Down Expand Up @@ -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(),
Expand Down
5 changes: 3 additions & 2 deletions 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();
Expand All @@ -11,7 +12,7 @@ struct C {
~D() throw();
};
struct E : A {
D<int> d; //expected-error{{exception specification is not available until end of class definition}}
D<int> d;
};
B<int> b; //expected-note{{in instantiation of template class 'B<int>' requested here}}
B<int> b;
};
27 changes: 27 additions & 0 deletions clang/test/SemaTemplate/default-arguments-cxx0x.cpp
Expand Up @@ -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<int> 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 <typename T> struct B { using type = bool; };

template <typename T> struct S {
S() noexcept;

template <typename U, typename B<U>::type = true>
S(const S<U>&) noexcept;
};

class A {
A() noexcept = default;
A(const A&) noexcept = default;
S<int> _a{};
};
}

0 comments on commit 4fbaa62

Please sign in to comment.