Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Design history of libfn, newest first. The living documents — [README.md](README.md), [CONTRIBUTING.md](CONTRIBUTING.md), [docs/](docs/) — describe only the present state of the design; when a decision makes an earlier idea obsolete, this file is where the transition is recorded and explained.

## `apply` traits reject an incomplete `pack` or `copack` operand — 27 September 2026

`apply`, `apply_r` and their traits reject incomplete `pack` or `copack` operands. Previously, trait queries could cache `false` (or `void` for `apply_result`) even after the type definition became available. Include `<fn/pack.hpp>` or `<fn/copack.hpp>` before querying. `typelist_applicable` still works from template arguments alone.

## `pack` and `copack` operations move to `<fn/algebra.hpp>` — 27 September 2026

`operator&` over `pack` and `copack`, `conjoin`, and `disjoin` move to `<fn/algebra.hpp>`. Include this header when using these operations without a carrier header. The `expected`, `optional`, and `just` headers include it.
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ Match the assertion to the tested property:
* **Conditional Specifications**: Pair negative assertions with their positive converses (e.g., pair `static_assert(not noexcept(expr))` with a witness for which it is `noexcept`). This proves that the specification is conditional rather than unconditionally false. Apply the same positive-control discipline to negative constraint or viability probes.
* **Substitution Contexts**: Ensure expressions in negative viability probes are dependent on a template parameter. A `requires`-expression over concrete types is not a substitution context, meaning an invalid requirement triggers a hard error rather than a SFINAE failure. Enclose the dependent probe in a generic lambda or a type-keyed concept to yield `false` on invalid substitution.
* **Exception Fixtures**: Select exception-proving fixtures carefully. The library's `helper_t` implements a separate constructor for each value category, and its non-const-lvalue copy constructor is always `noexcept`. A `helper_t` configured to throw is therefore not throwing for every value category, which can hide incorrect `noexcept` specifications. Use a simple, local type if the test requires every relevant constructor to be potentially throwing.
* **Expected Failures**: The suite cannot test runtime crashes or compilation failures. Record these untested cases beside their positive controls.

## Client code

Expand Down
12 changes: 12 additions & 0 deletions include/fn/detail/functional.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,13 +300,23 @@ template <typename Ret, typename Fn, typename Arg, typename Arg0, typename... Ar
}
} // namespace _apply_detail

// Reject incomplete operands before a false trait result can be cached.
template <typename A> constexpr bool _complete_operand() noexcept
{
if constexpr (_some_pack<A> || _some_copack<A>)
return sizeof(::std::remove_cvref_t<A>) > 0; // incomplete: include <fn/pack.hpp> or <fn/copack.hpp> first
else
return true;
}

// apply_result
template <typename Fn, typename... Args>
constexpr auto _apply_result_result(Fn &&, Args &&...)
-> ::std::type_identity<decltype(_apply_detail::apply(::std::declval<Fn>(), ::std::declval<Args>()...))>;
template <typename Fn, typename... Args> constexpr auto _apply_result_result(auto &&...) -> ::std::type_identity<void>;

template <typename Fn, typename... Args> struct _apply_result {
static_assert((_complete_operand<Args>() && ...));
using type = decltype(_apply_result_result<Fn, Args...>(::std::declval<Fn>(), ::std::declval<Args>()...))::type;
};

Expand All @@ -318,6 +328,7 @@ constexpr auto _is_applicable_result(Fn &&, Args &&...,
template <typename Fn, typename... Args> constexpr auto _is_applicable_result(auto &&...) -> ::std::false_type;

template <typename Fn, typename... Args> struct _is_applicable {
static_assert((_complete_operand<Args>() && ...));
static constexpr bool value
= decltype(_is_applicable_result<Fn, Args...>(::std::declval<Fn>(), ::std::declval<Args>()...))::value;
};
Expand All @@ -338,6 +349,7 @@ constexpr auto _is_applicable_r_result(
template <typename Ret, typename Fn, typename... Args>
constexpr auto _is_applicable_r_result(auto &&...) -> ::std::false_type;
template <typename Ret, typename Fn, typename... Args> struct _is_applicable_r {
static_assert((_complete_operand<Args>() && ...));
static constexpr bool value
= decltype(_is_applicable_r_result<Ret, Fn, Args...>(::std::declval<Fn>(), ::std::declval<Args>()...))::value;
};
Expand Down
2 changes: 2 additions & 0 deletions tests/fn/detail/functional.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ constexpr auto sum_two = [](int i, double d) { return i + d; };
constexpr auto sum_three = [](int i, double d, char c) { return i + d + c; };
} // namespace

// Incomplete pack/copack operands must fail compilation; the suite cannot test this.

TEST_CASE("_apply_result", "[functional][apply_result]")
{
using fn::detail::_apply_result;
Expand Down
Loading