From: Alp Toker Date: Thu, 17 Oct 2013 19:12:03 +0000 (+0000) Subject: Fix missed exception spec checks and crashes X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=bb81f880aa77cfd09dedfc72a9b87262889ae75c;p=clang Fix missed exception spec checks and crashes Delayed exception specification checking for defaulted members and virtual destructors are both susceptible to mutation during iteration so we need to process the worklists fully. This resolves both accepts-invalid and rejects-valid issues and moreover fixes potential invalid memory access as the contents of the vectors change during iteration and recursive template instantiation. This patch also adds two assertions at end of TU to ensure no specs are left unchecked as was happenning before the fix, plus a test case from Marshall Clow for the defaulted member crash extracted from the libcxx headers. Reviewed by Richard Smith. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@192914 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index f5a93d442c..4ac00b2fed 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -4789,7 +4789,7 @@ public: void CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD); void CheckExplicitlyDefaultedMemberExceptionSpec(CXXMethodDecl *MD, const FunctionProtoType *T); - void CheckDelayedExplicitlyDefaultedMemberExceptionSpecs(); + void CheckDelayedMemberExceptionSpecs(); //===--------------------------------------------------------------------===// // C++ Derived Classes diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 59648441a6..31de6524a4 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -792,6 +792,11 @@ void Sema::ActOnEndOfTranslationUnit() { } } + // All delayed member exception specs should be checked or we end up accepting + // incompatible declarations. + assert(DelayedDefaultedMemberExceptionSpecs.empty()); + assert(DelayedDestructorExceptionSpecChecks.empty()); + // Check we've noticed that we're no longer parsing the initializer for every // variable. If we miss cases, then at best we have a performance issue and // at worst a rejects-valid bug. diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index bd864b3adb..0355a981b2 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -4864,14 +4864,27 @@ void Sema::CheckExplicitlyDefaultedMemberExceptionSpec( SpecifiedType, MD->getLocation()); } -void Sema::CheckDelayedExplicitlyDefaultedMemberExceptionSpecs() { - for (unsigned I = 0, N = DelayedDefaultedMemberExceptionSpecs.size(); - I != N; ++I) - CheckExplicitlyDefaultedMemberExceptionSpec( - DelayedDefaultedMemberExceptionSpecs[I].first, - DelayedDefaultedMemberExceptionSpecs[I].second); +void Sema::CheckDelayedMemberExceptionSpecs() { + // Perform any deferred checking of exception specifications for virtual + // destructors. + while (!DelayedDestructorExceptionSpecChecks.empty()) { + std::pair Check = + DelayedDestructorExceptionSpecChecks.pop_back_val(); + const CXXDestructorDecl *Dtor = Check.first; + assert(!Dtor->getParent()->isDependentType() && + "Should not ever add destructors of templates into the list."); + CheckOverridingFunctionExceptionSpec(Dtor, Check.second); + } + + // Check that any explicitly-defaulted methods have exception specifications + // compatible with their implicit exception specifications. + while (!DelayedDefaultedMemberExceptionSpecs.empty()) { + std::pair Spec = + DelayedDefaultedMemberExceptionSpecs.pop_back_val(); + CheckExplicitlyDefaultedMemberExceptionSpec(Spec.first, Spec.second); + } - DelayedDefaultedMemberExceptionSpecs.clear(); + assert(DelayedDestructorExceptionSpecChecks.empty()); } namespace { @@ -8182,9 +8195,8 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation, } void Sema::ActOnFinishDelayedMemberInitializers(Decl *D) { - // Check that any explicitly-defaulted methods have exception specifications - // compatible with their implicit exception specifications. - CheckDelayedExplicitlyDefaultedMemberExceptionSpecs(); + // Perform any delayed checks on exception specifications. + CheckDelayedMemberExceptionSpecs(); // Once all the member initializers are processed, perform checks to see if // any unintialized use is happeneing. @@ -8707,23 +8719,11 @@ void Sema::ActOnFinishCXXMemberDecls() { // If the context is an invalid C++ class, just suppress these checks. if (CXXRecordDecl *Record = dyn_cast(CurContext)) { if (Record->isInvalidDecl()) { + DelayedDefaultedMemberExceptionSpecs.clear(); DelayedDestructorExceptionSpecChecks.clear(); return; } } - - // Perform any deferred checking of exception specifications for virtual - // destructors. - for (unsigned i = 0, e = DelayedDestructorExceptionSpecChecks.size(); - i != e; ++i) { - const CXXDestructorDecl *Dtor = - DelayedDestructorExceptionSpecChecks[i].first; - assert(!Dtor->getParent()->isDependentType() && - "Should not ever add destructors of templates into the list."); - CheckOverridingFunctionExceptionSpec(Dtor, - DelayedDestructorExceptionSpecChecks[i].second); - } - DelayedDestructorExceptionSpecChecks.clear(); } void Sema::AdjustDestructorExceptionSpec(CXXRecordDecl *ClassDecl, diff --git a/test/SemaTemplate/exception-spec-crash.cpp b/test/SemaTemplate/exception-spec-crash.cpp new file mode 100644 index 0000000000..4d9355974c --- /dev/null +++ b/test/SemaTemplate/exception-spec-crash.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -DCXX_EXCEPTIONS -fsyntax-only -verify %s + +template struct is_nothrow_move_constructible { + static const bool value = false; +}; + +template +class allocator; + +template <> +class allocator {}; + +template +class basic_string { + typedef _Allocator allocator_type; + basic_string(basic_string &&__str) + noexcept(is_nothrow_move_constructible::value); +}; + +class Foo { + Foo(Foo &&) noexcept = default; +#ifdef CXX_EXCEPTIONS +// expected-error@-2 {{does not match the calculated}} +#else +// expected-no-diagnostics +#endif + Foo &operator=(Foo &&) noexcept = default; + basic_string > vectorFoo_; +};