On this page

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