elsteveogrande created this revision.
elsteveogrande added reviewers: rsmith, dblaikie.
Herald added a subscriber: cfe-commits.

(PR38627)

After deserializing, the `PendingExceptionSpecUpdates` table is iterated over 
to apply exception specs to functions.  There may be `EST_None`-type in this 
table.  However if there is a method in the redecl chain already having some 
other non-`EST_None` type, this should not be clobbered with none.

If we see `EST_None` avoid setting the exception spec.  (This is the default 
anyway, so it's safe to skip in the legit case.)

There may be *better* ways to actually fix this, rather than a simple 1-line 
defensive check like I did here.  It would be more complicated and quite 
outside my area of experience though.

Test Plan: Passes `ninja check-clang`, including a new test case.

Andrew Gallagher provided the test case (distilling it from a much more complex 
instance in our code base).


Repository:
  rC Clang

https://reviews.llvm.org/D51608

Files:
  lib/Serialization/ASTReader.cpp
  test/Modules/Inputs/PR38627/a.h
  test/Modules/Inputs/PR38627/module.modulemap
  test/Modules/pr38627.cpp

Index: test/Modules/pr38627.cpp
===================================================================
--- /dev/null
+++ test/Modules/pr38627.cpp
@@ -0,0 +1,47 @@
+// RUN: rm -rf %t
+// RUN: %clang -std=c++17 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/PR38627 -c %s
+// expected-no-diagnostics
+
+// PR38627: Exception specifications sometimes go missing / are incorrect
+// after deserializing from a module.
+// https://bugs.llvm.org/show_bug.cgi?id=38627
+
+namespace test_with_unserialized_decls {
+
+// X/Y/Z are the same as A/B/C from: Inputs/PR38627/a.h
+class X {
+ public:
+  virtual ~X() = default;
+};
+class Y {
+ public:
+  virtual ~Y() {
+    z.func();
+  }
+  struct Z {
+    void func() {}
+    friend Y::~Y();
+  };
+  Z z;
+};
+
+// ~Y's exception spec should be calculated to be noexcept.
+static_assert(noexcept(Y().~Y()));
+
+} // end test_with_unserialized_decls
+
+// Same definitions of A and B (but without the namespace) in this module
+#include "a.h"
+
+namespace test_with_deserialized_decls {
+
+static_assert(noexcept(B().~B()), "PR38627");
+
+class D : public A, public B {
+ public:
+  // Error should be gone with bug fix:
+  // "exception specification of overriding function is more lax than base version"
+  virtual ~D() override = default;
+};
+
+} // end test_with_deserialized_decls
Index: test/Modules/Inputs/PR38627/module.modulemap
===================================================================
--- /dev/null
+++ test/Modules/Inputs/PR38627/module.modulemap
@@ -0,0 +1,3 @@
+module a {
+  header "a.h"
+}
Index: test/Modules/Inputs/PR38627/a.h
===================================================================
--- /dev/null
+++ test/Modules/Inputs/PR38627/a.h
@@ -0,0 +1,17 @@
+#pragma once
+
+class A {
+ public:
+  virtual ~A() = default;
+};
+class B {
+ public:
+  virtual ~B() {
+    c.func();
+  }
+  struct C {
+    void func() {}
+    friend B::~B();
+  };
+  C c;
+};
Index: lib/Serialization/ASTReader.cpp
===================================================================
--- lib/Serialization/ASTReader.cpp
+++ lib/Serialization/ASTReader.cpp
@@ -11551,10 +11551,17 @@
         ProcessingUpdatesRAIIObj ProcessingUpdates(*this);
         auto *FPT = Update.second->getType()->castAs<FunctionProtoType>();
         auto ESI = FPT->getExtProtoInfo().ExceptionSpec;
-        if (auto *Listener = getContext().getASTMutationListener())
-          Listener->ResolvedExceptionSpec(cast<FunctionDecl>(Update.second));
-        for (auto *Redecl : Update.second->redecls())
-          getContext().adjustExceptionSpec(cast<FunctionDecl>(Redecl), ESI);
+        // Ignore updates with EST_None sorts of exception specifications.
+        // That is the default anyway; also, in the event a FunctionDecl does
+        // have a non-None type, we'll avoid clobbering it.
+        if (ESI.Type != ExceptionSpecificationType::EST_None) {
+          if (auto *Listener = getContext().getASTMutationListener())
+            Listener->ResolvedExceptionSpec(cast<FunctionDecl>(Update.second));
+          for (auto *Redecl : Update.second->redecls()) {
+            // FIXME: If the exception spec is already set, ensure it matches.
+            getContext().adjustExceptionSpec(cast<FunctionDecl>(Redecl), ESI);
+          }
+        }
       }
 
       auto DTUpdates = std::move(PendingDeducedTypeUpdates);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D51608: [... Steve O'Brien via Phabricator via cfe-commits

Reply via email to