title: Lambda lifetime explanation for C++20 coroutines
body: <p><a href="https://github.com/facebook/folly/tree/master/folly/experimental/coro" rel="noreferrer">Folly</a> has a useable library for C++20 style coroutines.</p>
  
  <p>In the Readme it claims:</p>
  
  <blockquote>
    <p>IMPORTANT: You need to be very careful about the lifetimes of temporary lambda objects. Invoking a lambda coroutine returns a folly::coro::Task that captures a reference to the lambda and so if the returned Task is not immediately co_awaited then the task will be left with a dangling reference when the temporary lambda goes out of scope.</p>
  </blockquote>
  
  <p>I tried to make a MCVE for the example they provided, and was confused about the results.
  Assume the following boilerplate for all the following examples:</p>
  
  <pre class="lang-cpp prettyprint-override"><code>#include &lt;folly/experimental/coro/Task.h&gt;
  #include &lt;folly/experimental/coro/BlockingWait.h&gt;
  #include &lt;folly/futures/Future.h&gt;
  using namespace folly;
  using namespace folly::coro;
  
  int main() {
      fmt::print("Result: {}\n", blockingWait(foo()));
  }
  </code></pre>
  
  <p>I compiled the following with address sanitizer to see if there would be any dangling references.</p>
  
  <p><strong>EDIT: clarified question</strong></p>
  
  <h2><strong>Question</strong>: Why does the second example not trigger an ASAN warning?</h2>
  
  <p>According to <a href="https://en.cppreference.com/w/cpp/language/coroutines" rel="noreferrer">cppreference</a>:</p>
  
  <blockquote>
    <p>When a coroutine reaches the co_return statement, it performs the following:</p>
    
    <p>...</p>
    
    <ul>
    <li>or calls promise.return_value(expr) for co_return expr where expr has non-void type</li>
    <li>destroys all variables with automatic storage duration in reverse order they were created.</li>
    <li>calls promise.final_suspend() and co_await's the result.</li>
    </ul>
  </blockquote>
  
  <p>Thus perhaps the temporary lambda's state is not actually destroyed until the result is returned, because <code>foo</code> itself is a coroutine?</p>
  
  <hr>
  
  <p><strong>ASAN ERROR</strong>: I assume 'i' doesn't exist when the coroutine is waited on</p>
  
  <pre class="lang-cpp prettyprint-override"><code>auto foo() -&gt; Task&lt;int&gt; {
      auto task = [i=1]() -&gt; folly::coro::Task&lt;int&gt; {
          co_return i;
      }(); // lambda is destroyed after this semicolon
      return task;
  }
  </code></pre>
  
  <hr>
  
  <p><strong>NO ERROR</strong> -- why?</p>
  
  <pre class="lang-cpp prettyprint-override"><code>auto foo() -&gt; Task&lt;int&gt; {
    auto task = [i=1]() -&gt; folly::coro::Task&lt;int&gt; {
        co_return i;
    }();
    co_return co_await std::move(task);
  }
  </code></pre>
  
  <hr>
  
  <p><strong>ASAN ERROR</strong>: Same problem as first example?</p>
  
  <pre class="lang-cpp prettyprint-override"><code>auto foo() -&gt; folly::SemiFuture&lt;int&gt; {
      auto task = [i=1]() -&gt; folly::coro::Task&lt;int&gt; {
          co_return i;
      }();
      return std::move(task).semi();
  }
  </code></pre>
  
  <hr>
  
  <p><strong>NO ERROR</strong> ...and for good measure, just returning a constant (no lambda state captured) works fine. Compare to first example:</p>
  
  <pre class="lang-cpp prettyprint-override"><code>auto foo() -&gt; Task&lt;int&gt; {
      auto task = []() -&gt; folly::coro::Task&lt;int&gt; {
          co_return 1;
      }();
      return task;
  }
  </code></pre>