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

<generator>: Mark strengthened noexcept (plus tiny drive-by changes) #4351

Merged
merged 2 commits into from
Jan 31, 2024

Conversation

cpplearner
Copy link
Contributor

@cpplearner cpplearner commented Jan 30, 2024

Mostly tiny adjustments to better match the standard.

Also implements LWG-3894 "generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>) should not be noexcept".

Works towards #2936.

@cpplearner cpplearner requested a review from a team as a code owner January 30, 2024 10:56
@StephanTLavavej StephanTLavavej added bug Something isn't working generator C++23 generator labels Jan 30, 2024
@StephanTLavavej StephanTLavavej self-assigned this Jan 30, 2024
@@ -362,7 +362,7 @@ public:
return *this;
}

_NODISCARD _Ref operator*() const noexcept {
_NODISCARD _Ref operator*() const noexcept(is_nothrow_copy_constructible_v<_Ref>) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is constructing, but IIRC not necessarily copy constructing, a _Ref. Should this be is_nothrow_constructible_v<_Ref, decltype(*_Coro.promise()._Top.promise()._Ptr)>?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the condition needs to be noexcept(static_cast<_Ref>(*_Coro.promise()._Top.promise()._Ptr)).

If _Ref is an rvalue reference, this is a cast from lvalue to rvalue reference. Such a cast is non-throwing IIUC, but is_nothrow_constructible_v is false.

The standard says noexcept(is_nothrow_copy_constructible_v<reference>), which also needs to be fixed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On further thought, I believe the logic here is "If _Ref is a reference type, this function doesn't throw, which is consistent with is_nothrow_copy_constructible_v<_Ref>. If it's not a reference type, it must be a (cv-unqualified?) object type, and _Ptr is a pointer to (const?) _Ref so the static_cast expression in fact does copy a _Ref."

What we've merged here is correct, but we should nonetheless change it in a followup to obviously agree with the Standard wording.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_nothrow_copy_constructible_v<_Ref> is correct if:

  • _Ref is an lvalue reference type.
    • In this case, the function does not throw, and is_nothrow_copy_constructible_v<_Ref> is true.
  • _Ref is an object type.
    • In this case, is_nothrow_copy_constructible_v<_Ref> is the right condition.

is_nothrow_copy_constructible_v<_Ref> is not correct if:

  • _Ref is an rvalue reference type.

In this case, the function does not throw, but is_nothrow_copy_constructible_v<_Ref> is false, because,

  • is_nothrow_copy_constructible_v<_Ref> is equivalent to is_nothrow_constructible_v<_Ref, const _Ref&>.
  • When _Ref is A&& for some object type A, const _Ref& is A&.
  • is_constructible_v<A&&, A&> is false, and so is is_nothrow_constructible_v<A&&, A&>.

So I believe that we should not follow the standard wording. We need to fix it.

@@ -210,7 +210,7 @@ public:
}

_NODISCARD auto yield_value(const remove_reference_t<_Yielded>& _Val) noexcept(
is_nothrow_constructible_v<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>)
is_nothrow_constructible_v<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>) /* strengthened */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stylistic nitpick: We use /* strengthened */ when it's embedded in a line (especially before an opening brace), but the following requires will always be clang-formatted on a new line, so this should ideally be a C++ comment // strengthened. Not worth resetting testing though.

Comment on lines +380 to +381
_NODISCARD_FRIEND bool operator==(const _Gen_iter& _It, default_sentinel_t) noexcept /* strengthened */
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another stylistic nitpick: There's enough room for the brace here to be attached; clang-format exhibits "sticky" behavior and will preserve it. Also not worth resetting testing for this PR.

@StephanTLavavej StephanTLavavej removed their assignment Jan 31, 2024
@StephanTLavavej StephanTLavavej merged commit fb6a4cb into microsoft:feature/generator Jan 31, 2024
35 checks passed
@StephanTLavavej
Copy link
Member

Thanks! Had a couple of formatting nitpicks that can be addressed in followup PRs, but the functional changes all look great. 😻

@cpplearner cpplearner deleted the patch-1 branch January 31, 2024 14:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working generator C++23 generator
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

3 participants