]> granicus.if.org Git - clang/commitdiff
[coroutines][PR41909] Don't build dependent coroutine statements for generic lambda
authorBrian Gesiak <modocache@gmail.com>
Mon, 3 Jun 2019 00:47:32 +0000 (00:47 +0000)
committerBrian Gesiak <modocache@gmail.com>
Mon, 3 Jun 2019 00:47:32 +0000 (00:47 +0000)
Summary:
https://bugs.llvm.org/show_bug.cgi?id=41909 describes an issue in which
a generic lambda that takes a dependent argument `auto set` causes the
template instantiation machinery for coroutine body statements to crash
with an ICE. The issue is two-fold:

1. The paths taken by the template instantiator contain several asserts
   that the coroutine promise must not have a dependent type.
2. The template instantiator unconditionally builds corotuine statements
   that depend on the promise type, which cannot be dependent.

To work around the issue, prevent the template instantiator from building
dependent coroutine statements if the coroutine promise type is dependent.
Since we only expect this to occur in the case of a generic lambda, limit
the workaround behavior to just that case.

Reviewers: GorNishanov, EricWF, lewissbaker, tks2103

Reviewed By: GorNishanov

Subscribers: cfe-commits

Tags: #clang

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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@362348 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/TreeTransform.h
test/SemaCXX/coroutines.cpp

index f46193502b6a6f0b7e87e853fe7e331d201acb27..592787a5870ce3726bd1d597d06933395664e737 100644 (file)
@@ -7163,13 +7163,22 @@ TreeTransform<Derived>::TransformCoroutineBodyStmt(CoroutineBodyStmt *S) {
   Builder.ReturnValue = Res.get();
 
   if (S->hasDependentPromiseType()) {
-    assert(!Promise->getType()->isDependentType() &&
-           "the promise type must no longer be dependent");
-    assert(!S->getFallthroughHandler() && !S->getExceptionHandler() &&
-           !S->getReturnStmtOnAllocFailure() && !S->getDeallocate() &&
-           "these nodes should not have been built yet");
-    if (!Builder.buildDependentStatements())
-      return StmtError();
+    // PR41909: We may find a generic coroutine lambda definition within a
+    // template function that is being instantiated. In this case, the lambda
+    // will have a dependent promise type, until it is used in an expression
+    // that creates an instantiation with a non-dependent promise type. We
+    // should not assert or build coroutine dependent statements for such a
+    // generic lambda.
+    auto *MD = dyn_cast_or_null<CXXMethodDecl>(FD);
+    if (!MD || !MD->getParent()->isGenericLambda()) {
+      assert(!Promise->getType()->isDependentType() &&
+             "the promise type must no longer be dependent");
+      assert(!S->getFallthroughHandler() && !S->getExceptionHandler() &&
+             !S->getReturnStmtOnAllocFailure() && !S->getDeallocate() &&
+             "these nodes should not have been built yet");
+      if (!Builder.buildDependentStatements())
+        return StmtError();
+    }
   } else {
     if (auto *OnFallthrough = S->getFallthroughHandler()) {
       StmtResult Res = getDerived().TransformStmt(OnFallthrough);
index 99964ef6bcb1bf90095657ba1eb16eb0c91fa72e..1286ca4628b7b6c61fa97fd7813f608e239c4c3d 100644 (file)
@@ -720,6 +720,16 @@ coro<good_promise_1> ok_static_coawait() {
   co_await 42;
 }
 
+template<typename T> void ok_generic_lambda_coawait_PR41909() {
+  [](auto& arg) -> coro<good_promise_1> { // expected-warning {{expression result unused}}
+    co_await 12;
+  };
+  [](auto &arg) -> coro<good_promise_1> {
+    co_await 24;
+  }("argument");
+}
+template void ok_generic_lambda_coawait_PR41909<int>(); // expected-note {{in instantiation of function template specialization 'ok_generic_lambda_coawait_PR41909<int>' requested here}}
+
 template<> struct std::experimental::coroutine_traits<int, int, const char**>
 { using promise_type = promise; };