Skip to content

convertible_to_optional and convertible_to_choice hard-error (instead of yielding false) for void #293

Description

@Bronek

The exact mechanism of #290, one concept down: convertible_to_optional and convertible_to_choice (include/fn/concepts.hpp) instantiate optional<void> / choice<void> to answer for T = void, and both classes refuse the instantiation with a class-body mandate — which fires outside any immediate context, so no enclosing requires-expression can absorb it.

convertible_to_optional is user-reachable. The optional arm of invocable_transform asks it about the callback's result:

#include <fn/transform.hpp>

struct Error final {};
constexpr auto fnVoid = [](auto &&...) -> void {};

static_assert(not fn::invocable_transform<decltype(fnVoid), fn::optional<Error>>);

hard-errors on g++ and clang, and so does o | transform([](Error) {}) — where the honest answer is false: std::optional::transform mandates a non-void result. (Contrast expected, where a void return is legitimate — transform yields expected<void, E> — and convertible_to_expected carries an explicit void arm for exactly that reason.)

convertible_to_choice is not reached by any verb, but it is a public concept, and asking must answer:

#include <fn/concepts.hpp>
static_assert(not fn::convertible_to_optional<void>);
static_assert(not fn::convertible_to_choice<void>);

Both assertions are hard errors today.

Fix direction: the void conjunct #290 places on convertible_to_unexpected, applied to both — for neither concept can void ever be valid, so each just needs to say so.

Reproduced at 58d131c with gcc 16.1.1 and clang 22.1.6 (x86-64 Linux).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingrelease-0.1Planned for release 0.1

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions