[clang] c25acec - [Coroutines] Handle dependent promise types for final_suspend non-throw check

Xun Li via cfe-commits cfe-commits at lists.llvm.org
Thu Jun 25 11:27:33 PDT 2020


Author: Xun Li
Date: 2020-06-25T11:27:27-07:00
New Revision: c25acec84594ca15748553341969f8e579290e27

URL: https://github.com/llvm/llvm-project/commit/c25acec84594ca15748553341969f8e579290e27
DIFF: https://github.com/llvm/llvm-project/commit/c25acec84594ca15748553341969f8e579290e27.diff

LOG: [Coroutines] Handle dependent promise types for final_suspend non-throw check

Summary:
Check that the co_await promise.final_suspend() does not potentially throw again after we have resolved dependent types.
This takes care of the cases where promises types are templated.
Added test cases for this scenario and confirmed that the checks happen now.
Also run libcxx tests locally to make sure all tests pass.

Reviewers: Benabik, lewissbaker, junparser, modocache

Reviewed By: modocache

Subscribers: modocache, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D82332

Added: 
    

Modified: 
    clang/include/clang/Sema/Sema.h
    clang/lib/Sema/SemaCoroutine.cpp
    clang/lib/Sema/TreeTransform.h
    clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 19c68423e5da..f3024efa293c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9804,6 +9804,9 @@ class Sema final {
   void CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body);
   ClassTemplateDecl *lookupCoroutineTraits(SourceLocation KwLoc,
                                            SourceLocation FuncLoc);
+  /// Check that the expression co_await promise.final_suspend() shall not be
+  /// potentially-throwing.
+  bool checkFinalSuspendNoThrow(const Stmt *FinalSuspend);
 
   //===--------------------------------------------------------------------===//
   // OpenCL extensions.

diff  --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 6262f4b117d3..70b8fd282056 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -642,7 +642,6 @@ static void checkNoThrow(Sema &S, const Stmt *E,
   } else if (SC == Expr::CallExprClass || SC == Expr::CXXMemberCallExprClass ||
              SC == Expr::CXXOperatorCallExprClass) {
     if (!cast<CallExpr>(E)->isTypeDependent()) {
-      // FIXME: Handle dependent types.
       checkDeclNoexcept(cast<CallExpr>(E)->getCalleeDecl());
       auto ReturnType = cast<CallExpr>(E)->getCallReturnType(S.getASTContext());
       // Check the destructor of the call return type, if any.
@@ -662,22 +661,20 @@ static void checkNoThrow(Sema &S, const Stmt *E,
   }
 }
 
-/// Check that the expression co_await promise.final_suspend() shall not be
-/// potentially-throwing.
-static bool checkNoThrow(Sema &S, const Stmt *FinalSuspend) {
+bool Sema::checkFinalSuspendNoThrow(const Stmt *FinalSuspend) {
   llvm::SmallPtrSet<const Decl *, 4> ThrowingDecls;
   // We first collect all declarations that should not throw but not declared
   // with noexcept. We then sort them based on the location before printing.
   // This is to avoid emitting the same note multiple times on the same
   // declaration, and also provide a deterministic order for the messages.
-  checkNoThrow(S, FinalSuspend, ThrowingDecls);
+  checkNoThrow(*this, FinalSuspend, ThrowingDecls);
   auto SortedDecls = llvm::SmallVector<const Decl *, 4>{ThrowingDecls.begin(),
                                                         ThrowingDecls.end()};
   sort(SortedDecls, [](const Decl *A, const Decl *B) {
     return A->getEndLoc() < B->getEndLoc();
   });
   for (const auto *D : SortedDecls) {
-    S.Diag(D->getEndLoc(), diag::note_coroutine_function_declare_noexcept);
+    Diag(D->getEndLoc(), diag::note_coroutine_function_declare_noexcept);
   }
   return ThrowingDecls.empty();
 }
@@ -724,7 +721,7 @@ bool Sema::ActOnCoroutineBodyStart(Scope *SC, SourceLocation KWLoc,
     return true;
 
   StmtResult FinalSuspend = buildSuspends("final_suspend");
-  if (FinalSuspend.isInvalid() || !checkNoThrow(*this, FinalSuspend.get()))
+  if (FinalSuspend.isInvalid() || !checkFinalSuspendNoThrow(FinalSuspend.get()))
     return true;
 
   ScopeInfo->setCoroutineSuspends(InitSuspend.get(), FinalSuspend.get());

diff  --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index ff9d4d610660..ce9c30339617 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -7630,7 +7630,8 @@ TreeTransform<Derived>::TransformCoroutineBodyStmt(CoroutineBodyStmt *S) {
     return StmtError();
   StmtResult FinalSuspend =
       getDerived().TransformStmt(S->getFinalSuspendStmt());
-  if (FinalSuspend.isInvalid())
+  if (FinalSuspend.isInvalid() ||
+      !SemaRef.checkFinalSuspendNoThrow(FinalSuspend.get()))
     return StmtError();
   ScopeInfo->setCoroutineSuspends(InitSuspend.get(), FinalSuspend.get());
   assert(isa<Expr>(InitSuspend.get()) && isa<Expr>(FinalSuspend.get()));

diff  --git a/clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp b/clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp
index d234d8adf7c9..48c65f8afb95 100644
--- a/clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp
+++ b/clang/test/SemaCXX/coroutine-final-suspend-noexcept.cpp
@@ -11,27 +11,27 @@ struct coroutine_traits { using promise_type = typename Ret::promise_type; };
 
 template <class Promise = void>
 struct coroutine_handle {
-  static coroutine_handle from_address(void *); // expected-note {{must be declared with 'noexcept'}}
+  static coroutine_handle from_address(void *); // expected-note 2 {{must be declared with 'noexcept'}}
 };
 template <>
 struct coroutine_handle<void> {
   template <class PromiseType>
-  coroutine_handle(coroutine_handle<PromiseType>); // expected-note {{must be declared with 'noexcept'}}
+  coroutine_handle(coroutine_handle<PromiseType>); // expected-note 2 {{must be declared with 'noexcept'}}
 };
 
 struct suspend_never {
-  bool await_ready() { return true; }       // expected-note {{must be declared with 'noexcept'}}
-  void await_suspend(coroutine_handle<>) {} // expected-note {{must be declared with 'noexcept'}}
-  void await_resume() {}                    // expected-note {{must be declared with 'noexcept'}}
-  ~suspend_never() noexcept(false);         // expected-note {{must be declared with 'noexcept'}}
+  bool await_ready() { return true; }       // expected-note 2 {{must be declared with 'noexcept'}}
+  void await_suspend(coroutine_handle<>) {} // expected-note 2 {{must be declared with 'noexcept'}}
+  void await_resume() {}                    // expected-note 2 {{must be declared with 'noexcept'}}
+  ~suspend_never() noexcept(false);         // expected-note 2 {{must be declared with 'noexcept'}}
 };
 
 struct suspend_always {
   bool await_ready() { return false; }
   void await_suspend(coroutine_handle<>) {}
   void await_resume() {}
-  suspend_never operator co_await(); // expected-note {{must be declared with 'noexcept'}}
-  ~suspend_always() noexcept(false); // expected-note {{must be declared with 'noexcept'}}
+  suspend_never operator co_await(); // expected-note 2 {{must be declared with 'noexcept'}}
+  ~suspend_always() noexcept(false); // expected-note 2 {{must be declared with 'noexcept'}}
 };
 
 } // namespace experimental
@@ -50,7 +50,7 @@ struct coro_t {
   struct promise_type {
     coro_t get_return_object();
     suspend_never initial_suspend();
-    suspend_always final_suspend(); // expected-note {{must be declared with 'noexcept'}}
+    suspend_always final_suspend(); // expected-note 2 {{must be declared with 'noexcept'}}
     void return_void();
     static void unhandled_exception();
   };
@@ -60,3 +60,13 @@ coro_t f(int n) { // expected-error {{the expression 'co_await __promise.final_s
   A a{};
   co_await a;
 }
+
+template <typename T>
+coro_t f_dep(T n) { // expected-error {{the expression 'co_await __promise.final_suspend()' is required to be non-throwing}}
+  A a{};
+  co_await a;
+}
+
+void foo() {
+  f_dep<int>(5); // expected-note {{in instantiation of function template specialization 'f_dep<int>' requested here}}
+}


        


More information about the cfe-commits mailing list