Repository navigation
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
🤖 Augment PR SummarySummary: Preserve borrowed lvalues throughout libfn's type algebra.
🤖 Was this summary useful? React with 👍 or 👎 |
| [[nodiscard]] constexpr auto _collapsing_apply(Data &&data, ::std::size_t index, Fn &&fn, Args &&...args) // | ||
| noexcept(_is_nothrow_rts_applicable<To, _copack_injector<To, Fn &&>, Data &&, Args &&...>) -> To | ||
| { | ||
| static_assert(_admissible_results<Fn &&, Data &&, ::std::remove_cvref_t<Data>, Args &&...>, |
There was a problem hiding this comment.
Could you check the const& overload fallback at include/fn/copack.hpp:240? A projector with operator()(int const&) -> int const& and deleted operator()(int const&&) makes both rvalue transform overloads non-viable, so copack<int>{1}.transform(projector) falls back to const&, passes this assertion as an lvalue, and returns a reference to the destroyed temporary instead of rejecting it.
Severity: high
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
…ment by value A single argument was forwarded as a reference, which dangled when bound to a temporary and gave a result whose shape depended on the number of arguments. Assisted-by: Claude:claude-opus-5-5
Assisted-by: Claude:claude-opus-5-5
MSVC instantiates the deduced-return body of a non-viable rvalue transform overload while forming its declaration, which fired the reference-result guard for a const lvalue call. Assisted-by: Claude:claude-opus-5-5
Assisted-by: Claude:claude-opus-5-5
Assisted-by: Claude:claude-opus-5-5
Assisted-by: Claude:claude-opus-5-5
…the headers Declare get_ptr with a trailing return, which doxygen reads as constexpr, and list the single-carrier conjoin overload first, so that each listing has the shape the signature check derives from the headers. Assisted-by: Claude:claude-opus-5-5
…erence Assisted-by: Claude:claude-opus-5-5
|



Five commits:
1. Allow lvalue-reference alternatives in
copack. An alternative may beT&, held as a pointer to its referent;T,T&andT const&are distinct alternatives. As withoptional<T&>, assignment rebinds and comparison compares referents. As through apack<T&>, a copack hands outT&, orT const&whenconst, whatever its value category. A value selects among the alternatives of its own decayed type by overload resolution, socopack_for<int, int&>{x}is ambiguous;std::in_place_type<int&>names the alternative.choice<T&>andexpected<copack<T&>, E>now work, too.2. Key
|and&on the payload type. Anoptional<T&>operand contributesT&rather thanT:optional<int&> | optional<long&>isoptional<copack_for<int&, long&>>, andoptional<int&> & optional<long>isoptional<pack<int&, long>>. Aconstreferent no longer leaks into&or breaks|. This absorbs the|fix of #437, which rebases onto it.Breaking: result types change wherever an
optional<T&>operand meets|or&, includingoptional<int&> & just<void>, nowoptional<pack<int&>>— this brings the intended behaviour of the type algebra.3. Keep lvalue-reference results of
transformover acopack. A callback returningT&yields a reference alternative, asoptional'stransformyieldsoptional<T&>; other reference results still enter as values.Breaking: a reference result that could refer into an argument expiring with the call, such as an alternative of an rvalue copack, is refused where it was copied; return by value instead.
4. Deduce values and lift references. CTAD for
pack,copack,justandchoiceremoves only references and cv-qualifiers: for an lvaluex,pack{x}ispack<int>, andpack{"abc"}ispack<char[4]>. The liftsas_pack,as_copackandas_choicekeep an lvalue as a reference, and refuse one they cannot refer to rather than copy it. Pass an rvalue, e.g.std::move(x), to lift a value instead; an array or a function cannot be lifted by value.conjoinholds a leading scalar by value, as&holds the others.Breaking:
pack{x}copies where it referred;as_pack(x)still refers.as_copack(x)andas_choice(x)refer where they copied; move the source, or use CTAD, to copy.arrand a functionf,just{arr},just{f}andpack{f}fail to compile, as doas_copackof an lvaluepackor array andas_choiceof an lvalue copack, array or function, such asas_choice("hi").int x, y;,conjoin(x, y)ispack<int, int>, where it waspack<int&, int>.5. Normalize a single
conjoinargument; return a singledisjoinargument by value.conjoinis the fold of&from its unit, so a single argument meets the unit alone:conjoin(x)ispack<int>, acopack_for<A, B>becomescopack_for<pack<A>, pack<B>>, and anoptional<int>becomesoptional<pack<int>>, while avoidvalue staysvoid.disjoin(m)returnsmby value.This fixes a dangling reference. With one argument, both returned the argument itself as a reference, so binding the result of a call on a temporary left a reference to a destroyed object. Both now return a value, which the binding keeps alive:
Breaking: the result of a single-argument
conjoinchanges type, e.g.conjoin(o).value()is apack<int>where it was anint; callbacks still receive the unpacked elements.TYPE_ALGEBRA.md and CHANGELOG.md document each change.
Each commit was tested locally with gcc 16 and clang 22, and the final tree in the CI images for gcc 12–15 and clang 19 and 20; MSVC and AppleClang are covered by CI only.
Found along the way: #450,
&on aconst pack<int&>fails to compile.Closes #448
Closes #449
Assisted-by: Claude:claude-opus-5-5