Skip to content

docs: freeze goroutine launch translation - #52

Open
4lmighty wants to merge 4 commits into
mainfrom
docs/goroutine-launch-translation
Open

4lmighty wants to merge 4 commits into
mainfrom
docs/goroutine-launch-translation

Conversation

@4lmighty

Copy link
Copy Markdown
Collaborator

Summary

  • define the canonical translation shape for Go go statements
  • require function values, receivers, and arguments to be evaluated in the launching goroutine before launch
  • preserve source evaluation order with deterministic adjacent temporaries
  • preserve Go value- vs pointer-receiver binding for method launches
  • add focused conformance coverage that launch expressions complete before the goroutine body begins

Motivation

A naive translation such as go([&] { f(x()); }) evaluates x() after launch, which changes Go semantics. Go evaluates the function value and call arguments in the launching goroutine before the new goroutine begins executing the function body.

This PR freezes that source-shape rule without adding runtime magic: go(...) remains the launch primitive, while translators make the pre-launch evaluation boundary explicit.

Scope

This PR freezes goroutine call evaluation and launch shape. General Go closure lifetime extension remains governed by the existing translation lifetime boundaries and is not expanded here.

@4lmighty 4lmighty left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The source-order/eager-evaluation direction is right, and Build/Style are green, but I don't think this launch mapping is safe to freeze yet. I see two semantic blockers.

First, the canonical shape go(f(prepared_args...)) still calls the C++ function f before go(...). This happens to delay the function body only if every translated callee is guaranteed to be a true lazy C++ coroutine whose body cannot execute during invocation. That invariant is not stated in this contract, and a non-coroutine wrapper that returns an eo::func<> can execute side effects synchronously before launch. Either make the translated-function coroutine invariant explicit and enforce/test it, or make the launch boundary robust by registering an already-prepared thunk/factory whose actual function invocation occurs after launch.

Second, the pointer-receiver rule (auto* receiver = &x) and, more generally, prepared pointer/reference arguments can outlive their C++ local storage once the goroutine runs asynchronously. Go escape semantics keep such storage alive; the current raw-pointer mapping does not. The function-literal paragraph only mentions closure lifetime extension, but this issue also applies to method receivers and ordinary pointer arguments. Until general escaping-local lowering is frozen, this contract should either scope these cases out explicitly or define the required lifetime-preserving storage rule.

I would keep the current adjacent-temp/source-order rule. The remaining issue is making the actual launch boundary and escaping-storage boundary explicit enough that the frozen mapping cannot silently depend on C++ lifetime/coroutine accidents.

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