Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow functions to be conditionally compiled as coroutines through use of 'if constexpr' #29

Open
lewissbaker opened this issue Feb 8, 2018 · 1 comment

Comments

@lewissbaker
Copy link

Should we be supporting the ability for template functions to be conditionally compiled as coroutines in cases where one branch of an if constexpr branch contains the co_await keyword and the other branch does not?

For example: See https://godbolt.org/g/23jB2U

template<typename T>
auto foo(T value) -> std::conditional<is_awaitable_v<T>, task<void>, void>
{
  // Lots of common setup code here.

  if constexpr (is_awaitable_v<T>)
  {
    // Only usage of co_await is here.
    bar(co_await value);
  }
  else
  {
    bar(value);
  }
}

The current wording of N4680 8.4.4(1) says:

A function is a coroutine if it contains a coroutine-return-statement (6.6.3.1), an await-expression (5.3.8), a yield-expression (5.20), or a range-based for (6.5.4) with co_await. The parameter-declaration-clause of the coroutine shall not terminate with an ellipsis that is not part of a parameter-declaration.

And N4713 9.4.1(2) says:

... If the value of the converted condition is false, the first substatement is a discarded statement, otherwise the second substatement, if present, is a discarded statement. During the instantiation of an enclosing templated entity (Clause 17), if the condition is not value-dependent after its instantiation, the discarded substatement (if any) is not instantiated. ...

There seems to be some ambiguity of wording between these two sections as to whether or not a co_await/co_return/co_yield keyword that occurs within a statement discarded due to an if constexpr is still 'contained' within the function.

@modocache Also found a potentially relevant precedence of wording from N4713 10.1.7.4(7):

If a function with a declared return type that contains a placeholder type has multiple non-discarded return statements, the return type is deduced for each such return statement. If the type deduced is not the same in each deduction, the program is ill-formed.

This section is also using the term 'contains' but directly addresses the issue of whether or not statements discarded due to if constexpr should be considered by restricting it to consider only non-discarded return statements.

Should the wording of N4680 8.4.4(1) be updated to also use the term non-discarded when referring to coroutine-return-statement, await-expression and yield-expression to allow interaction with if constexpr?

Note that the current state of the Clang compiler (as of 2018/02/08) still triggers compilation of a function as a coroutine even if the only occurrences of the co_xxx keywords are within a discarded statement.

@GorNishanov
Copy link
Owner

Cool question.

The design intent is for "coroutine-ness" to be stable in the presence of 'constexpr-if'.
I'll check with CWG if we need to improve the wording to make it more explicit.

Rationale is partially flows from motivation and objections to if constexpr. For example, see: http://open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0128r0.html

Objections to 'constexpr if' were, essentially, that it is as bad as using the preprocessor to arbitrary cut and slice the body of the function. That objection was resolved with updated design which required compilers to look at discarded side as well.

Alterning the design will lead to code like:

template<typename T>
auto foo(T value) -> std::conditional<is_awaitable_v<T>, task<int>, int>
{
  // Lots of common setup code here.
 
  int x;

  if constexpr (is_awaitable_v<T>)
  {
    // Only usage of co_await is here.
    x = bar(co_await value);
  }
  else
  {
    x = bar(value);
  }

  if constexpr (is_awaitable_v<T>)
  {
    co_return x; 
  }
  else
  {
    return x;
  }
}

Which does not look like something we should encourage

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants