Skip to content

Match pfn monadic declarations and constraints to the C++ draft - #459

Merged
Bronek merged 5 commits into
mainfrom
bronek/remove_pfn_noncompliant_sfinae
Oct 17, 2026
Merged

Bronek merged 5 commits into
mainfrom
bronek/remove_pfn_noncompliant_sfinae

Conversation

@Bronek

@Bronek Bronek commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Helper-based trailing return types made some invalid monadic callbacks disappear through substitution failure. This could select a different cv/ref overload, including one that lets a callback borrow from a temporary owning optional despite deleting its rvalue overload.

For example, this callback explicitly rejects borrowing from an rvalue Person. Before this change, the rvalue and_then overloads disappear through substitution failure, allowing the const& member overload to invoke the callback with an lvalue instead:

#include <pfn/optional.hpp>

struct Person {
  int age;
};

struct BorrowAge {
  pfn::optional<int const&> operator()(Person const& person) const {
    return person.age;
  }
  pfn::optional<int const&> operator()(Person const&&) const = delete;
};

int main() {
  auto age = pfn::optional<Person>{Person{42}}.and_then(BorrowAge{});
  // The temporary Person is gone; before this fix, age still refers to its member.
  return *age; // Undefined behavior: dangling reference.
}

This compiles before the fix on GCC and Clang, and AddressSanitizer reports a stack-use-after-scope at *age. With the fix, both compilers reject the and_then call. Calling the same callback through a named optional that remains alive is still valid.

Match the current C++ draft's public declarations for owning and reference pfn::optional and both pfn::expected specializations. Use body-deduced returns where invalid callbacks must produce hard errors, preserve reference optional's explicit transform return type, and retain the specified invocability and constructibility constraints. Expected's permitted constructibility-based copy fallback remains available.

Add compile-failure tests with successful positive controls and separate fixture checks, plus runtime and constexpr preservation tests. Update TYPE_ALGEBRA, CHANGELOG, and the contributor guidance for rejection tests.

Assisted-by: Codex:gpt-6

Deduce owning optional transform and and_then return types in their
bodies, as specified by the standard. Trailing-return substitution could
remove the selected overload and bypass a callback's deleted cv/ref
overload, allowing a borrow from a temporary through const&.

Keep conditional noexcept and valid reference results. Add constexpr and
runtime controls for callable categories, lvalue-only callbacks, external
references, and temporary reference wrappers; document the hard errors.

GCC and Clang full builds and 48/48 tests pass on each. Separate compiler
probes reject all 32 previously accepted invalid calls, matching the
standard-library controls; mandate errors cannot run in the unit suite.
The shared std optional tests compile in C++23 and C++26 on both compilers.

Assisted-by: Codex:gpt-6
Keep reference and_then's return deduction in its body so invalid
invocations are hard errors. Spell reference transform's result and both
or_else result types as specified, preserving their substitution rules,
constraints and conditional noexcept.

Add paired compile-failure controls for reference and_then and owning
monadic cv/ref fallback. Guard extension-only noexcept assertions in the
standard-library validation tests.

Assisted-by: Codex:gpt-6
Compile each negative callback fixture without its rejected call so an
incorrect invocability assertion fails a normal build instead of passing
a compile-must-fail test.

Assisted-by: Codex:gpt-6
Expose only the unchanged-side constructibility constraints on the four
monadic families. Deduce returns in the body so an invalid callback cannot
remove the selected overload or become false in a requires-expression.

Preserve valid copy fallback and cover rejection with paired compile-fail
controls and independently checked callback fixtures. Leave conditional
noexcept unchanged.

Assisted-by: Codex:gpt-6
Keep error constructibility constraints on and_then and transform while
leaving or_else and transform_error unconstrained. Body-deduced returns
make invalid callbacks hard errors without removing valid copy fallback.

Cover nullary callback rejection, error callback cv/ref fallback, and
operations on immovable errors. Leave conditional noexcept unchanged.

Assisted-by: Codex:gpt-6
@codecov

codecov Bot commented Oct 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@sonarqubecloud

Copy link
Copy Markdown

@augmentcode

augmentcode Bot commented Oct 17, 2026

Copy link
Copy Markdown

augment review

@augmentcode

augmentcode Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
🤖 Augment PR Summary

Summary: Aligns pfn monadic declarations and constraints with the C++ draft to prevent unintended cv/ref fallback.

  • Uses body-deduced returns for owning optional's `and_then` and `transform`.
  • Makes invalid selected callbacks hard errors, preventing the demonstrated dangling-reference case.
  • Preserves reference optional's explicit `transform` return type and substitution behavior.
  • Declares both optional specializations' `or_else` with a fixed optional return type.
  • Uses body-deduced returns for all monadic operations in both expected specializations.
  • Exposes specified constructibility constraints while preserving expected's permitted copy fallback.
  • Retains conditional `noexcept` behavior for valid calls.
  • Adds compile-failure tests with successful positive controls and separate fixture checks.
  • Adds runtime/constexpr preservation tests and updates TYPE_ALGEBRA, CHANGELOG, and contributor guidance.

🤖 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. No suggestions at this time.

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

@Bronek
Bronek merged commit 03d9e51 into main Oct 17, 2026
59 checks passed

@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. No suggestions at this time.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant