]> granicus.if.org Git - clang/commitdiff
Fix missed exception spec checks and crashes
authorAlp Toker <alp@nuanti.com>
Fri, 18 Oct 2013 05:54:19 +0000 (05:54 +0000)
committerAlp Toker <alp@nuanti.com>
Fri, 18 Oct 2013 05:54:19 +0000 (05:54 +0000)
Delayed exception specification checking for defaulted members and virtual
destructors are both susceptible to mutation during iteration so we need to
swap and process the worklists.

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.

Checking can be further delayed where parent classes aren't yet fully defined.
This patch 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@192947 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Sema/Sema.h
lib/Sema/Sema.cpp
lib/Sema/SemaDeclCXX.cpp
test/SemaTemplate/exception-spec-crash.cpp [new file with mode: 0644]

index 769998012310affc90d130c7574e914d76c2f075..65f07a88763e5c7604150760a635aba445102421 100644 (file)
@@ -4790,7 +4790,7 @@ public:
   void CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD);
   void CheckExplicitlyDefaultedMemberExceptionSpec(CXXMethodDecl *MD,
                                                    const FunctionProtoType *T);
-  void CheckDelayedExplicitlyDefaultedMemberExceptionSpecs();
+  void CheckDelayedMemberExceptionSpecs();
 
   //===--------------------------------------------------------------------===//
   // C++ Derived Classes
index 59648441a6d12c83e6b8c5372840782e712f1306..41f72a68a38da72402a5f16ac50694e87d1c851e 100644 (file)
@@ -604,8 +604,14 @@ void Sema::ActOnEndOfTranslationUnit() {
     // valid, but we could do better by diagnosing if an instantiation uses a
     // name that was not visible at its first point of instantiation.
     PerformPendingInstantiations();
+    CheckDelayedMemberExceptionSpecs();
   }
 
+  // All delayed member exception specs should be checked or we end up accepting
+  // incompatible declarations.
+  assert(DelayedDefaultedMemberExceptionSpecs.empty());
+  assert(DelayedDestructorExceptionSpecChecks.empty());
+
   // Remove file scoped decls that turned out to be used.
   UnusedFileScopedDecls.erase(
       std::remove_if(UnusedFileScopedDecls.begin(0, true),
index 81283e1f7825091c7d6bcf91fa0fbf75015af714..ed5724b88fb36af4e86467c230c65c666a8f4c74 100644 (file)
@@ -4873,14 +4873,28 @@ 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() {
+  SmallVector<std::pair<const CXXDestructorDecl *, const CXXDestructorDecl *>,
+              2> Checks;
+  SmallVector<std::pair<CXXMethodDecl *, const FunctionProtoType *>, 2> Specs;
 
-  DelayedDefaultedMemberExceptionSpecs.clear();
+  std::swap(Checks, DelayedDestructorExceptionSpecChecks);
+  std::swap(Specs, DelayedDefaultedMemberExceptionSpecs);
+
+  // Perform any deferred checking of exception specifications for virtual
+  // destructors.
+  for (unsigned i = 0, e = Checks.size(); i != e; ++i) {
+    const CXXDestructorDecl *Dtor = Checks[i].first;
+    assert(!Dtor->getParent()->isDependentType() &&
+           "Should not ever add destructors of templates into the list.");
+    CheckOverridingFunctionExceptionSpec(Dtor, Checks[i].second);
+  }
+
+  // Check that any explicitly-defaulted methods have exception specifications
+  // compatible with their implicit exception specifications.
+  for (unsigned I = 0, N = Specs.size(); I != N; ++I)
+    CheckExplicitlyDefaultedMemberExceptionSpec(Specs[I].first,
+                                                Specs[I].second);
 }
 
 namespace {
@@ -8191,9 +8205,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.
@@ -8716,23 +8729,11 @@ void Sema::ActOnFinishCXXMemberDecls() {
   // If the context is an invalid C++ class, just suppress these checks.
   if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(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 (file)
index 0000000..4d93559
--- /dev/null
@@ -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 <class _Tp> struct is_nothrow_move_constructible {
+  static const bool value = false;
+};
+
+template <class _Tp>
+class allocator;
+
+template <>
+class allocator<char> {};
+
+template <class _Allocator>
+class basic_string {
+  typedef _Allocator allocator_type;
+  basic_string(basic_string &&__str)
+  noexcept(is_nothrow_move_constructible<allocator_type>::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<allocator<char> > vectorFoo_;
+};