Skip to content

[V4] Lift meta-functions#112

Merged
ConorWilliams merged 11 commits into
modulesfrom
v4-lift
May 17, 2026
Merged

[V4] Lift meta-functions#112
ConorWilliams merged 11 commits into
modulesfrom
v4-lift

Conversation

@ConorWilliams
Copy link
Copy Markdown
Owner

@ConorWilliams ConorWilliams commented May 17, 2026

Lift a regular function into the task monad

Summary by CodeRabbit

  • New Features
    • Added lift utility to convert synchronous functions to async operations within coroutines, automatically handling cancellation requests and exception propagation to parent scopes.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 17, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 30b07674-64b3-4d08-8dbe-b639e3a67abe

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This pull request introduces the lf::lift synchronous-to-async lifting facility for libfork, a C++20 coroutine library. The feature converts synchronous callables into async tasks with built-in cancellation and exception propagation semantics, and integrates it into the promise machinery via specialized await transformations.

Changes

Lift: Synchronous-to-Async Lifting Facility

Layer / File(s) Summary
Lift module implementation: lift_impl functor and lifted_awaitable awaitable
src/core/lift.cxx
The new libfork.core:lift module defines lift_impl, a coroutine-backed functor that asynchronously invokes callables by co_returning their results, and lifted_awaitable, a suspend_never awaitable that performs optimized lifting on await_resume: checks cancellation via stop token or parent context, invokes the stored callable with captured arguments, writes results to a return address for non-void, and stashes exceptions into the parent frame on failure.
Promise integration: await_transform specialization for lift_impl
src/core/promise.cxx
Adds :lift import and introduces a specialized await_transform overload in mixin_frame for pkg types where Fn is lift_impl, returning a lifted_awaitable wired with the current frame as parent. The generic await_transform is refactored to use deduced return type while maintaining exception-stashing and default-failure behavior.
Module registration and re-export
CMakeLists.txt, src/core/core.cxx
CMakeLists.txt adds src/core/lift.cxx to the public C++ modules file set. src/core/core.cxx re-exports the :lift module via export import :lift; to expose it as part of the public libfork.core API.
Test suite: lift validation across scenarios
test/src/lift.cpp
Comprehensive validation of lf::lift including callable test fixtures, direct scheduling tests verifying return values and side effects, scope operations tests for call/fork/join semantics in normal and child scopes, cancellation tests asserting expected outcomes when stop is requested, exception propagation tests (guarded by LF_COMPILER_EXCEPTIONS), and static concept conformance checks.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • ConorWilliams/libfork#103: Both PRs modify src/core/promise.cxx's mixin_frame await_transform implementation, with this PR adding the lift_impl-specific lifted_awaitable overload while the retrieved PR adjusts awaitable behavior and return types on the same code path.
  • ConorWilliams/libfork#76: Both PRs modify mixin_frame/await_transform logic in src/core/promise.cxx to change how awaitables are constructed and how failure/cancellation paths are handled, so this PR's lift_impllifted_awaitable integration is directly entangled with the retrieved PR's promise/transform refactors.
  • ConorWilliams/libfork#104: Both PRs modify lf::mixin_frame<Context>::await_transform overload logic in src/core/promise.cxx, with this PR adding the lift_impllifted_awaitable overload and the retrieved PR updating await_transform machinery for explicit scheduling.

Poem

🐰 A rabbit lifts the sync to async skies,
Where callables dance as coroutine sighs,
Exceptions caught, cancellation respected,
Scope operations perfectly directed!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '[V4] Lift meta-functions' directly relates to the main change: introducing a new lift facility for converting synchronous functions into coroutine-compatible async tasks, which is the primary purpose of all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch v4-lift

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ConorWilliams ConorWilliams changed the base branch from main to modules May 17, 2026 13:05
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/core/lift.cxx (1)

78-80: 💤 Low value

Consider verifying return_addr is non-null for non-void results.

The non-void path dereferences pkg.return_addr without a null check. While the forked path in await_transform_pkg uses not_null(pkg.return_addr) (line 145 in promise.cxx), this optimized path doesn't have the same guard.

If the invariant that call operations with non-void R always have a valid return address holds by construction, this is fine—but adding an assertion would make the contract explicit and catch violations in debug builds.

🛡️ Optional: Add assertion for defensive debugging
      } else {
-       std::move(pkg.args).apply([addr = pkg.return_addr](auto &&fn, auto &&...args) -> void {
+       auto *addr = pkg.return_addr;
+       LF_ASSUME(addr != nullptr);
+       std::move(pkg.args).apply([addr](auto &&fn, auto &&...args) -> void {
          *addr = std::invoke(LF_FWD(fn), LF_FWD(args)...);
        });
      }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/lift.cxx` around lines 78 - 80, The lambda in
std::move(pkg.args).apply dereferences pkg.return_addr without checking it; add
an explicit debug-time assertion that pkg.return_addr is non-null for non-void
results (e.g., before or inside the apply lambda that uses *addr). Reference
pkg.return_addr and the apply lambda in lift.cxx; mirror the invariant used in
await_transform_pkg/promise.cxx (not_null(pkg.return_addr)) by asserting the
pointer (using the project's assert/not_null macro or standard assert) so the
contract is explicit in debug builds.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/core/lift.cxx`:
- Around line 78-80: The lambda in std::move(pkg.args).apply dereferences
pkg.return_addr without checking it; add an explicit debug-time assertion that
pkg.return_addr is non-null for non-void results (e.g., before or inside the
apply lambda that uses *addr). Reference pkg.return_addr and the apply lambda in
lift.cxx; mirror the invariant used in await_transform_pkg/promise.cxx
(not_null(pkg.return_addr)) by asserting the pointer (using the project's
assert/not_null macro or standard assert) so the contract is explicit in debug
builds.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6f83700a-ff6a-42e7-8d58-3a4d9391441a

📥 Commits

Reviewing files that changed from the base of the PR and between b2a4543 and 1aa24cc.

📒 Files selected for processing (5)
  • CMakeLists.txt
  • src/core/core.cxx
  • src/core/lift.cxx
  • src/core/promise.cxx
  • test/src/lift.cpp

@ConorWilliams ConorWilliams merged commit 4204e59 into modules May 17, 2026
7 of 10 checks passed
ConorWilliams added a commit that referenced this pull request May 17, 2026
* lift

* lift optimization

* overload

* decay fn to lifted

* lifted pass 1 test

* doc

* await resume

* fix semantics

* todo

* no separate .fn in pkg

* assert non-null
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