28. Reference C# lowering
28.1 Purpose and conformance
CS-LOWERING-001. DirectSharp semantics define a program's observable behavior. A reference C# lowering illustrates a behavior-preserving implementation; its incidental temporary names, containing helper types and formatting are not language rules.
The C# reference implementation guide connects the chapter-level patterns with evaluation, failure, lifetime and concurrency cases. The standard-library plan distinguishes public CLR contracts from compiler-selected support and unselected API declarations.
A lowering must preserve:
- Evaluation count and source evaluation order.
- Binding/type/result identity and permitted absence.
- Short-circuiting and branch-local captures.
- Deferred versus immediate work.
- Successful-result publication versus exceptional exits.
- Resource acquisition/cleanup order and borrowed ownership.
- Structured child lifetime, limits and result ordering.
- Public CLR contracts and source-correlated diagnostics.
28.2 Backend patterns
| Source construct | Representative backend pattern | Critical requirement |
|---|---|---|
| Ordinary awaited call | await plus appropriate async implementation |
Sequential intent; imported name/ABI preserved |
| Explicit async action | Task/Task<T> and standard token contract |
Contract stays async independently of body inference |
| Inferred async ordinary action | Task/Task<T> without an inferred cancellation parameter |
Public inference permitted; fixed interface/base contracts preserved |
| Iterator / async iterator | Iterator / async-iterator state machine | Deferred execution and disposal |
Independent concurrent all requests |
Start tasks, then join, then publish | Logical values unavailable before publication |
Inline run ... end run |
Generated local async function or async delegate, registered as an owned producer | Branch-local return; cleanup before settlement; no implicit Task.Run |
concurrent first |
Observe completions until a successful producer wins | A failed first completion cannot win |
| Concurrent collection | Source-indexed result storage plus owned joins | Source order, whole-iteration limits |
| Gate region | Async acquisition plus try/finally release |
No permit leak on control transfer |
| Atomic Integer increment | Interlocked.Increment |
Explicit atomicity |
| Atomic multi-field region | Shared synchronous synchronization domain | No suspension and actual cross-region coordination |
| Language lazy value | Hidden once-only publication state | Cached absence/failure and nonforcing probes |
| Event operations | CLR subscription/removal/invocation | Signature, identity and declaring-type restrictions |
| Need | Constructor/storage plus metadata/invariant validation | Required input, stability and borrowed ownership |
| Feature | Ordinary bound adapter calls | No unrestricted type generation |
| Wrapper | Expanded DirectSharp, then ordinary compilation | Restricted deterministic transformation and source maps |
28.3 Cleanup and failure
Lowering must not erase semantic distinctions. Action postconditions are successful-exit checks, not finally checks. Scoped take captures a value before required disposal, then publishes it only after cleanup succeeds. A Gate acquisition failure must not release a permit it never acquired. A borrowed service cannot become an owned using variable merely to fit a backend pattern.
For example, structured Gate protection has this reference shape:
await semaphore.WaitAsync(cancellationToken);
try
{
await File.WriteAllTextAsync(path, contents, cancellationToken);
}
finally
{
semaphore.Release();
}
The snippet assumes the Gate's permit primitive and configured acquisition policy have been resolved. Timeout handling must distinguish acquisition timeout from whole-scope timeout and cancellation. Backend helpers must preserve original producer propagation and source-order aggregation, suppress observed cancellation-request errors after joins, and coordinate recorded external causes with publication under CS-CONCURRENT-017–020. A successful winner does not end the whole-scope deadline while owned work remains unsettled.
28.4 Source maps and diagnostics
Generated code retains enough source correlation to explain errors in DirectSharp terms. Wrapper/feature expansions retain originating declaration/application context. A failed binding or unsupported construct is reported explicitly; generation must not silently reinterpret the program to make the output compile.
Specification contents · Previous: 27. .NET interoperability · Next: Appendices