Skip to content

Compute a real noexcept for the sum, pack and choice surface#300

Merged
Bronek merged 1 commit into
mainfrom
bronek/P6/nothrow_invocable_traits
Jul 13, 2026
Merged

Compute a real noexcept for the sum, pack and choice surface#300
Bronek merged 1 commit into
mainfrom
bronek/P6/nothrow_invocable_traits

Conversation

@Bronek

@Bronek Bronek commented Jul 13, 2026

Copy link
Copy Markdown
Member

The dispatch machinery promised noexcept unconditionally while invoking user callbacks and constructing user types, so a throwing callback or a throwing copy called std::terminate - and since the specification also read noexcept == true, callers could not even detect the hazard by asking. The constructors were wrong the other way: carrying no specifier at all, they under-promised, so sum<int>{42} was noexcept(false).

is_nothrow_invocable / is_nothrow_invocable_r (stubbed false until now) are the foundation, and are computed by asking the invoke chain itself, which grows the specification it was missing. That composes: a pack answers for the call over its elements, a sum for the call over every alternative - one throwing alternative makes the whole dispatch throwing, since which one runs is a run-time choice. The recursion terminates because each step strictly reduces the number of pack/sum operands.

On top of that: the constructors weigh what they construct and relocate, append weighs the element built and every element moved into the new pack, operator== weighs the alternatives' own comparisons, and choice stops over-promising where optional and expected never did.

Two traps worth naming. A noexcept-specifier is an ordinary constant expression, not a requires-clause: both operands of its || must be well-formed, so "this alternative is not compared, and need not be comparable" has to be a guarded trait rather than a disjunction. And an explicit noexcept on a defaulted copy/move that disagrees with the implicit specification deletes it - choice's had to drop theirs once sum's became conditional.

Closes #45
Closes #280


Stack: P6 of 13 — the release-0.1 bugfix queue, merging bottom-up into main. Based on #299 and to be retargeted to main once it merges; the diff shows only this PR's commits. Every stack boundary was validated with a gcc build and the full test suite on top of main, and the aggregate branch ran the full CI matrix green (#289).

@augmentcode

augmentcode Bot commented Jul 13, 2026

Copy link
Copy Markdown
🤖 Augment PR Summary

Summary: This PR makes the public noexcept surface for sum, pack, and choice match real behavior, avoiding both over-promising (terminate on throw) and under-promising (missing nothrow guarantees).

Changes:

  • Propagates conditional noexcept through the internal invoke chain and typelist dispatch helpers
  • Implements is_nothrow_invocable/is_nothrow_invocable_r by querying the invoke chain itself (instead of a stubbed false)
  • Adds a brace-init-aware _nothrow_initializable trait and applies it to sum/choice constructors and related operations
  • Refines pack::append, pack::invoke(_r), and relocation accounting to reflect real throwing behavior
  • Updates/extends tests to pin the new noexcept contracts across monads and dispatch paths

Technical Notes: The nothrow computation composes across pack/sum dispatch (runtime choice means “all alternatives must be nothrow”). It also accounts for brace-vs-paren initialization differences and guarded traits where noexcept operands must remain well-formed.

🤖 Was this summary useful? React with 👍 or 👎

@augmentcode augmentcode Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review completed. 2 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Comment thread include/fn/sum.hpp
Comment thread tests/fn/sum.cpp
@Bronek Bronek force-pushed the bronek/P5/sum_operator_not_equal branch from 54e67e6 to dbd8898 Compare July 13, 2026 14:29
@Bronek Bronek changed the base branch from bronek/P5/sum_operator_not_equal to main July 13, 2026 14:46
The dispatch machinery promised `noexcept` unconditionally while invoking user
callbacks and constructing user types, so a throwing callback or a throwing copy
called `std::terminate` - and since the specification also read `noexcept == true`,
callers could not even detect the hazard by asking. The constructors were wrong the
other way: carrying no specifier at all, they under-promised, so `sum<int>{42}` was
`noexcept(false)`.

`is_nothrow_invocable` / `is_nothrow_invocable_r` (stubbed `false` until now) are the
foundation, and are computed by asking the invoke chain itself, which grows the
specification it was missing. That composes: a pack answers for the call over its
elements, a sum for the call over every alternative - one throwing alternative makes
the whole dispatch throwing, since which one runs is a run-time choice. The recursion
terminates because each step strictly reduces the number of pack/sum operands.

On top of that: the constructors weigh what they construct and relocate, `append`
weighs the element built and every element moved into the new pack, `operator==`
weighs the alternatives' own comparisons, and `choice` stops over-promising where
`optional` and `expected` never did.

Two traps worth naming. A `noexcept`-specifier is an ordinary constant expression,
not a requires-clause: both operands of its `||` must be well-formed, so "this
alternative is not compared, and need not be comparable" has to be a guarded trait
rather than a disjunction. And an explicit `noexcept` on a defaulted copy/move that
disagrees with the implicit specification deletes it - `choice`'s had to drop theirs
once `sum`'s became conditional.

Closes #45
Closes #280

Assisted-by: Claude:claude-opus-4-8[1m]
@Bronek Bronek force-pushed the bronek/P6/nothrow_invocable_traits branch from 7dcd970 to 48fcd2f Compare July 13, 2026 14:47
@Bronek

Bronek commented Jul 13, 2026

Copy link
Copy Markdown
Member Author

augment review

@sonarqubecloud

Copy link
Copy Markdown

@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@augmentcode augmentcode Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review completed. 1 suggestion posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

Comment thread include/fn/pack.hpp
@Bronek Bronek merged commit fa25592 into main Jul 13, 2026
64 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant