# C# Code Generation

This document defines the C# code generation target.

## Kind

The target kind identifier is `csharp`.

## Options

The C# target reads the following options from its `options` mapping in the project configuration:

- `namespace: STRING` is the C# namespace used in every generated file. Required. The value is used verbatim as the file-scoped `namespace` directive and must be a valid C# qualified identifier (one or more identifier segments separated by `.`).
- `class: STRING` is the name of the shared `public static partial class` every module contributes to. Optional; defaults to `Masterbelt`. The value must be a valid C# identifier (one segment, no `.`).
- `storage: STRING` selects the master-data backend baked into the generated package. Optional; defaults to `memory`. Accepted values are `memory` (the in-memory executor that consumes records supplied through the `MasterData` constructor / `LoadJson`) and `sql` (a SQL-backed executor that translates queries into ADO.NET commands against a host-supplied `DbConnection` — see [Master Data](#master-data)).

A missing or empty `namespace` is a configuration error. An invalid `class` value is a configuration error. A `storage` value other than `memory` or `sql` is a configuration error.

The C# target does not consume any other options at this stage; unknown options are silently ignored.

## File Set

For a project whose lowered IR modules are `m1.mst`, `m2.mst`, ..., the C# target produces:

- One generated file per Masterbelt module. The file name is the module file stem in PascalCase followed by `.cs`. Each file declares one segment of the shared `public static partial class` (named by the `class` option, default `Masterbelt`) containing every reachable constant from that module, plus any product-type classes declared by the module as siblings at file scope.
- A `MasterbeltUnions.cs` file when any constant has a union type. The file declares one sealed-abstract-class union per union type encountered across the project.
- A `MasterbeltMasterData.cs` file when the project declares at least one master. The file declares the `MasterData` class described in [Master Data](#master-data). The file is omitted when the project declares no master.
- A `MasterbeltQuery.cs` file when the project declares at least one master. The file declares `MasterSource`, `FieldRef`, the `IPredicate<R>` / `IOrdering<R>` interfaces, the exported concrete predicate / ordering node classes that back the inspectable AST, the static `Predicates` helper with `And` / `Or` / `Not`, the generic field-handle classes (`OrderedField<R, V>`, `BoolField<R>`), the `QueryPlan<R>` value type, and the `QueryRuntime.Execute` helper used by every generated relation terminal; see [Master Data](#master-data). The file is omitted when the project declares no master.

All files share the same C# namespace declared by the `namespace` option and live under the configured output root.

### Reserved file name prefix

Files invented by the C# target itself, rather than derived from a Masterbelt source file name, use the reserved `Masterbelt` PascalCase prefix. New generator-managed files added to this target MUST use the same prefix.

## Shared Partial Class

C# does not have free-standing top-level constants. Every Masterbelt module contributes its reachable constants and re-exports to one shared `public static partial class` declared in the configured namespace. The class name is taken from the `class` option (default `Masterbelt`); each generated module file repeats the same `public static partial class` declaration with that name and adds its members.

The class is always `public static partial`. Its visibility does not depend on whether any of its members are public: it is the container, not a participating symbol. Because every module shares one class, every top-level identifier across the project must be unique under its C# mapping; collisions are reported as a generation diagnostic.

Product-type classes emitted from a module are declared as siblings of the partial class, at file scope rather than as nested types of the shared class.

## Reachability

The C# target follows the default reachability policy defined in `codegen/model`: only constants reachable from at least one `pub`-declared constant are emitted. Identifier references resolve against the surviving set, so a `pub const A = helper` declaration keeps `helper` even though `helper` itself is not public.

## Visibility

A Masterbelt constant declared with `pub` is emitted with the `public` access modifier on its class member; a non-public constant is emitted with `internal`. C# identifier names are preserved verbatim because C# expresses visibility through modifiers, not through identifier case.

## Constants and Static Fields

Each Masterbelt const item maps to one C# class member:

- A `const` field is used when the constant's Masterbelt type is `bool`, `string`, or any built-in fixed-width numeric type and the lowered expression is a corresponding literal. C# `const` fields are compile-time constants and require literal initializers. Native-width numerics (`int`, `uint`) cannot be C# `const` because `nint`/`nuint` are disallowed there, so they always fall back to the next bullet.
- A `static readonly` field is used for every other case: `null`-typed constants, list and map literals, union-typed constants, and references to other constants.

Doc comment lines are emitted as `/// <summary>` and `/// <para>` lines forming a single XML documentation comment block immediately before the member.

## Type Mapping

| Masterbelt        | C#                                                                                 |
|-------------------|------------------------------------------------------------------------------------|
| `null`            | `object?` (the value `null` lowers to the C# literal `null`)                       |
| `bool`            | `bool`                                                                             |
| `int` / `uint`    | `nint` / `nuint` (C#'s native-width integers)                                      |
| `int8` / `uint8`  | `sbyte` / `byte`                                                                   |
| `int16` / `uint16`| `short` / `ushort`                                                                 |
| `int32` / `uint32`| `int` / `uint`                                                                     |
| `int64` / `uint64`| `long` / `ulong`                                                                   |
| `string`          | `string`                                                                           |
| `list<T>`         | `IReadOnlyList<T'>` where `T'` is the C# mapping of `T`                            |
| `map<K, V>`       | `IReadOnlyDictionary<K', V'>` where `K'` and `V'` are the C# mappings              |
| `T1 \| T2 \| ...` | A sealed abstract class generated into `MasterbeltUnions.cs` (see Unions below)    |
| `{f: T, ...}`     | `public class Name { public required T' Field { get; init; } ... }` emitted at file scope next to the shared partial class; see Product Types below |
| `fn(p: T, ...): R`| `public delegate R Name(T p, ...)` at file scope when reached as a named type; `System.Func<...>` when used inline. See Function Types below |
| `enum Name { ... }`| `public enum Name : Storage { Variant = value, ... }` at file scope (see Enums below) |

Type declarations are resolved before mapping; declared types do not appear in generated code.

## Literal Mapping

- `null` lowers to the C# literal `null`.
- `true` and `false` lower to the C# literals `true` and `false`.
- An integer literal lowers to its decoded value rendered in base 10.
- A string literal lowers to a C# double-quoted string with C# escape sequences.
- A list literal `[e1, ..., eN]` lowers to a C# 12 collection expression `[e1', ..., eN']`. The surrounding declared list type fixes the runtime collection type, so the explicit element type is omitted to keep the output free of the `IDE0300: Collection initialization can be simplified` warning. An empty list literal lowers to `[]`.
- A map literal of type `map<K, V>` lowers to `new Dictionary<K', V'> { [k1] = v1, ..., [kN] = vN }`, with entries emitted in lowering order after last-wins deduplication.
- An identifier reference lowers to the referent constant's identifier, qualified by its declaring class when necessary.
- An integer literal lowers to its decoded value rendered in base 10, with the suffix C# requires to bind the literal to the resolved numeric type: `UL` for `uint64`, `L` for `int64`, and `U` for `uint32`. Narrower fixed-width types (`int8`/`uint8`/`int16`/`uint16`/`int32`) and the native widths (`int`/`uint`) emit the bare digits because implicit conversion or target inference covers them.
- A product literal lowers to a C# target-typed object initializer `new() { Field = ..., ... }`. The surrounding declared type (a product-type declaration) supplies the runtime type, so the type name is omitted at the call site to keep the output free of the `IDE0090: 'new' expression can be simplified` warning. Initializers carry PascalCase property names and preserve the source order of the literal.

## Product Types

A Masterbelt type declaration whose target is a product type emits a `public class` at file scope, alongside the shared partial class:

```csharp
public class Item
{
    public required string Name { get; init; }

    public required int Count { get; init; }
}
```

Field names are converted to PascalCase to match the C# naming convention. Field order matches the field order written in source. The `required` modifier ensures every field must be supplied at construction. Each property's setter accessor reflects the Masterbelt field modifier: a `readonly` field emits `{ get; init; }` so it cannot be reassigned after object initialization, while a field without the modifier (or with the explicit `writable` modifier) emits `{ get; set; }` and remains assignable. Anonymous nested product types written inline in a Masterbelt declaration body are normalized to named declarations by the lowering pass (see [ir.md](../language/ir.md#anonymous-product-hoisting)), so the C# target emits each one as a sibling class with the synthesized name.

## Unions

A union type lowers to a C# sealed abstract class. For a canonical union `T1 | T2 | ... | TN`:

- The class type is named by concatenating the PascalCase member type names with `Or`. For primitive members, the names are `Null`, `Bool`, `Int`, `String`. The order matches the canonical order recorded in the IR.
- The class has a private parameterless constructor so external code cannot construct arbitrary subclasses.
- For each non-null member type, a nested `public sealed class` wrapper is declared that inherits from the abstract base. The wrapper exposes a `public required` property named `Value` of the member's mapped C# type. Inheritance lets `is`-pattern checks on a union variable identify the matched member (used by match statement lowering), and a nested class has access to the base class's private parameterless constructor so external assemblies still cannot construct arbitrary subclasses.
- The `null` member does not get a wrapper. The C# `null` literal directly satisfies a nullable reference to the abstract base, so a union variable of static type `T1Or...?` holding `null` represents the null case.

A value of a union type lowers as `new <Interface>.<Member> { Value = <expression> }` for non-null members and `null` for null.

At this stage, only unions of primitive types are supported. A union containing a generic member is a generation error.

## Type Declarations

Each Masterbelt type declaration whose target is not a product type emits one C# file-scoped using-alias directive placed immediately after the regular `using` directives and before the `namespace` declaration:

```csharp
using LocalName<Params> = MappedTargetType;
```

When the declaration carries type parameters, the using directive carries the same parameters as a generic using-alias parameter list (`<T, ...>`). Generic using-aliases require C# 12 or later. C# does not distinguish public and non-public for file-scoped aliases; the directive is visible everywhere in the file.

A type declaration whose target is a product type does not emit a using-alias directive; it emits a `public class Name<Params> { ... }` at file scope. See the Product Types section above for the class shape; the parameter list is the same parameter list as the declaration's. At a use site, a generic class is rendered with its type arguments: `LocalName<T1, ...>`.

## Enums

A Masterbelt enum lowers to a `public enum Name : Storage { ... }` declaration at file scope alongside the shared partial class:

```csharp
public enum Status : sbyte
{
    Active = 0,
    Inactive = 1,
}
```

The storage clause uses the C# spelling defined in [Type Mapping](#type-mapping) (`int8` → `sbyte`, `int32` → `int`, and so on). Variant names appear in source declaration order with their resolved integer values.

A member access expression `Enum.Variant` lowers to the C# member access `Enum.Variant`. When the variant is used as an initializer for a shared-class member, the variant is emitted with its enum-type qualifier.

## Functions and Methods

A top-level function declaration emits a `[public|internal] static Result Name(params) { ... }` method on the shared partial class. Asyncable functions are declared `async` and return `Task<R>`; cancellable functions append a `CancellationToken cancellationToken` parameter.

A method declared inside a product type emits a `[public] Result Name(params) { ... }` instance method on the owning class. Overloaded methods rely on C#'s native overloading by signature — the emitted methods all share the same name; no numeric suffix is needed. The Masterbelt implicit-receiver keyword `self` is rewritten to C#'s `this` keyword inside method bodies so the surface form maps onto C#'s idiom.

A call expression `target(args)` emits `Target(args)` (PascalCase identifier resolution for functions). A method call `value.method(args)` emits `value.Method(args)`.

A function literal `fn(params): R { ... }` is reserved for a follow-up; the current target does not synthesize C# lambdas at expression positions.

## For Statements

A Masterbelt for statement lowers to a C# `foreach` or `for` statement. The IR subject shape selects the form:

- `list<T>` subject — `foreach (var name in subject) { ... }`. A skipped binding (`_` in source) is rendered as the C# discard pattern `_`.
- `map<K, V>` subject — `foreach (var (k, v) in subject) { ... }`. Either binding renders as `_` when skipped. The C# target's map type (`IReadOnlyDictionary<K, V>`) yields `KeyValuePair<K, V>` entries; the foreach pattern deconstructs them via the language's built-in tuple deconstruction support on `KeyValuePair`.
- `range(start, end)` subject — `for (var i = start; i < end; i++) { ... }`. A `_` binding synthesizes `__mbI` used only to advance the counter.

A `break` statement lowers to C#'s `break`; a `continue` statement lowers to `continue`.

The lowered subject expression is evaluated exactly once. Iteration over a master's `toList()` lowers through the [Master Data](#master-data) rewrite (`await this.ToList(data, cancellationToken)` inside the owning master's own static body, `await Masterbelt.<Other>.ToList(data, cancellationToken)` for a cross-master subject) and walks the returned `IReadOnlyList<Record>`. The `await` placement comes from the regular asyncable inheritance machinery: the surrounding static method becomes `async Task<R>` silently and the for-loop subject lifts into `await ...` at the C# call site.

## Master Data

A `master Foo { record { ... } static { ... } }` declaration follows the runtime model defined in [../masterdata/schema.md](../masterdata/schema.md#runtime-model). The C# target emits per master:

- `<Master>Record` — a C# class that backs one row. Field naming, modifiers, and constructor / property generation follow the regular [Product Types](#product-types) rules.
- `<Master>Relation` — a C# `public sealed class` declared at file scope alongside the record. The class is data-less: it carries a single `private readonly QueryPlan<<Master>Record> plan` field plus a default constructor that initialises a fresh plan and an `internal` constructor that accepts a pre-built plan for copy-on-write chaining. Stage methods (`Where`, `OrderBy`, `ThenBy`, `Skip`, `Take`) return a freshly-allocated `<Master>Relation` whose plan extends the receiver's plan; the receiver is never mutated. Terminals (`ToList`, `AsAsyncEnumerable`, `FindBy`, `FirstOrDefault`, `Count`, `Any`) take `(MasterData data, CancellationToken cancellationToken)` (plus primary-key arguments for `FindBy`) and resolve records through the master's `IExecutor` obtained from `data.GetExecutor<Master>()`. Every terminal carries the `asyncable`, `cancellable`, and `failable` effects per [../masterdata/schema.md](../masterdata/schema.md#runtime-model) — the C# target renders the `async`/`Task<R>` wrap (asyncable) and the trailing `CancellationToken cancellationToken` parameter (cancellable); failure surfaces through a thrown exception (failable). `AsAsyncEnumerable` is the streaming counterpart of `ToList`: it returns `IAsyncEnumerable<<Master>Record>` and uses `[EnumeratorCancellation]` on its `cancellationToken` parameter so cancellation flows through `await foreach`. Each user-declared static method becomes an instance method on the relation; it always takes `(MasterData data, ...declared args, CancellationToken cancellationToken)`.
- Package-level entrypoint: `public static partial class Masterbelt { public static readonly <Master>Relation <Master> = new(); }`. Users write `Items.Where(...).ToList(data, cancellationToken)` after adding `using static <Namespace>.Masterbelt;` (or `Masterbelt.Items.Where(...)` with no using directive).

Both declarations live as siblings at file scope.

Nested masters follow the same naming scheme on the flattened identifier — `master User { master Friendships { ... } }` emits `UserFriendshipsRecord`, `UserFriendshipsRelation`, and a `Masterbelt.UserFriendships` static field as siblings of the parent. See [Nested Masters](../masterdata/schema.md#nested-masters).

### MasterData Entry

A project that declares at least one master emits one `MasterData` class declaration. The class lives in a generator-managed file named `MasterbeltMasterData.cs`. It declares:

- One `private readonly IReadOnlyList<<Master>Record> <master>Records` field per master (lowerCamelCase), storing the per-master record set.
- A constructor `public MasterData(IReadOnlyList<<Items>Record> items, IReadOnlyList<<Users>Record> users, ...)` that takes one read-only list per master in master-declaration order and assigns each to the matching field.
- One `internal IExecutor<<Master>Record> GetExecutor<Master>()` accessor per master. Every generated relation terminal calls `data.GetExecutor<Master>()` to reach the active backend, then `await`s `Execute(plan, cancellationToken)` / `FindByPK(plan, keys, cancellationToken)` on the returned executor. Under `storage: memory` the accessor returns a `MemoryExecutor<<Master>Record>` wrapping the master's record list (and the `<Master>Relation.Matches<Master>PK` closure when the master declares a primary key).

The C# target never writes `MasterData` in code emitted from a Masterbelt source program: the identifier exists for the host application to construct and inject the dataset. MasterData stores records — not relations — because every generated relation is a data-less value-typed plan; the dataset is supplied at every terminal call site.

When `storage: sql` is configured, the layout above changes:

- `MasterData` carries a single `private readonly DbConnection connection` (from `System.Data.Common`) shared by every per-master executor; the per-master record lists are not emitted.
- The public positional constructor is replaced by a factory `public static Task<MasterData> NewSqliteMasterData(DbConnection connection, CancellationToken cancellationToken)`. The host application opens the connection with its preferred SQLite provider (`Microsoft.Data.Sqlite`, `System.Data.SQLite`, a Unity binding, ...) and owns its open/close lifetime; `MasterData` never closes it.
- `GetExecutor<Master>()` returns a per-master `<Master>SqlExecutor` (emitted alongside the relation) that translates the relation's `QueryPlan` into a parameterised `DbCommand` via `SqlTranslator.TranslatePlan` / `TranslatePKLookup`, runs it with `ExecuteReaderAsync`, and materialises rows with the generated `ScanRow`.
- `LoadJson` is not emitted under `storage: sql`. Hosts that want to seed a SQL backend from the JSON exporter should round-trip the data through the SQLite exporter (see [../masterdata/export-sqlite.md](../masterdata/export-sqlite.md)) and open the resulting database.

The `IExecutor<R>` / `MemoryExecutor<R>` seam, the `ISqlEmittable` / `ISqlOrderable` interfaces, the `SqlFragment` carrier, the `SqlTranslator` helpers, and the `SqlSupport` query runner all live in `MasterbeltQuery.cs`. Generated code depends only on `System.Data.Common`; it never references a concrete SQLite provider package.

Under `storage: sql`, a `ref<T>` join and a `select` projection are pushed into SQL when the source relation's plan is trivial: the join becomes one `INNER JOIN` statement (rather than a per-row `FindBy`), and the projection becomes a column-narrowed `SELECT`. Each derived relation captures the source plan's triviality at the accessor (`SqlTranslator.IsTrivial`); a non-trivial source plan (a source-side `Where`/`OrderBy`/`Skip`/`Take`) or the memory backend falls back to the materialise-and-evaluate path. Both paths return identical results.

Because the factory accepts a `DbConnection` directly, no adapter is needed: a provider's connection type already derives from `DbConnection`. With `Microsoft.Data.Sqlite`:

```csharp
using Microsoft.Data.Sqlite;

await using var connection = new SqliteConnection("Data Source=masterdata.db");
await connection.OpenAsync(cancellationToken);
var data = await MasterData.NewSqliteMasterData(connection, cancellationToken);
// var items = await Masterbelt.Items.Where(f => f.Count.Ge(20)).ToList(data, cancellationToken);
```

The host owns the connection's open/close lifetime; `MasterData` never disposes it. Any `System.Data.Common`-compatible provider (`Microsoft.Data.Sqlite`, `System.Data.SQLite`, a Unity SQLite binding) works without changes to the generated code.

The same file also declares `public static MasterData LoadJson(string json)`, a helper that consumes the JSON document produced by the JSON exporter (see [../masterdata/export-json.md](../masterdata/export-json.md)) and returns a freshly wired `MasterData`. The helper uses `System.Text.Json` only:

```csharp
public static MasterData LoadJson(string json) {
    var raw = JsonSerializer.Deserialize<JsonShape>(json, JsonOptions);
    return new MasterData(raw.items, raw.userFriendships /* ... */);
}

private static readonly JsonSerializerOptions JsonOptions =
    new JsonSerializerOptions { PropertyNameCaseInsensitive = true };

private sealed class JsonShape {
    [JsonPropertyName("items")]
    public IReadOnlyList<ItemsRecord> items { get; init; }
    [JsonPropertyName("userFriendships")]
    public IReadOnlyList<UserFriendshipsRecord> userFriendships { get; init; }
    // ... one per master in declaration order
}
```

Each generated `<Master>Record` property carries a `[JsonPropertyName("<surfaceName>")]` attribute so the C# `Id` property maps to JSON key `id`, `Name` maps to `name`, and so on. The surface name is the master's source-level field identifier verbatim. A `ref<T>` field expands to the underlying primary-key fields under the surrounding field's name joined with `_` (`field_pk1`, `field_pk2`, ...); the `[JsonPropertyName(...)]` on each expanded leaf carries the joined source name. Other product types not declared inside a `master` block do not receive JSON attributes.

### Static Body Rewrites

A user-declared static method's body is rewritten so the planner-side master references resolve against the package-level relation values and the threaded dataset:

- `Master.toList()` inside the owning master's own static body lowers to `this.ToList(data, cancellationToken)`. The receiver is the data-less relation value the static method was invoked on, so a caller that chained stages before invoking the static observes those stages.
- `Master.X` (any user-declared constant or method) inside the same owning master's body lowers to `this.X(data, cancellationToken, ...args)`.
- `OtherMaster.toList()` lowers to `Masterbelt.<OtherMaster>.ToList(data, cancellationToken)` against the static field on the partial class.
- `OtherMaster.X` (any other cross-master reference) lowers to `Masterbelt.<OtherMaster>.X(data, cancellationToken, ...args)` against the static field.

Every relation method, including user-declared statics and constants, accepts `(MasterData data, ...declared args, CancellationToken cancellationToken)` uniformly so the call-site rewrite stays straightforward; methods that do not declare a body-level need for either argument still take both.

### Top-Level Dataset Threading

A top-level function or constant that transitively reaches any master static member (constant or method, including the built-in `toList()`) acquires the dataset as an explicit parameter: it receives `MasterData data` as its first positional parameter and `CancellationToken cancellationToken` as a trailing parameter, and forwards both to every call that also requires them. The Masterbelt source program never writes the parameters.

The dataset-threading parameter is added before any other parameters declared by the function. It composes with the effect-driven signature transforms (`cancellable` ensures `CancellationToken cancellationToken` is appended, `asyncable` wraps the return in `Task<R>`) without further interaction.

### Query API

Every master emits the chainable surface directly on `<Master>Relation`. The C# target uses a callback style: `Where`, `OrderBy`, and `ThenBy` accept a callback that receives a typed `<Master>Fields` instance and returns a predicate or ordering AST. Per the design doc, the predicate is a generated AST type and **not** an `Expression<Func<...>>` for this phase; future SQL-translating backends may revisit the choice. See [../masterdata/query.md](../masterdata/query.md) for the cross-target contract.

The runtime types live in the generator-managed `MasterbeltQuery.cs` file (one file per project, emitted whenever the project declares at least one master):

- `MasterSource` — a `public sealed record(string Name)` identifying a master by its source-level name.
- `FieldRef` — a `public sealed record(string Name)` identifying a record field by its source-level name.
- `IPredicate<R>` — `interface { bool Evaluate(R record); }`. Parametrised by the record type so a predicate built for one master cannot be passed to another's `Where` callback; the compiler rejects the mismatch on `R`.
- `IOrdering<R>` — `interface { int Compare(R a, R b); }`.
- Concrete predicate / ordering classes (`EqPredicate<R, V>`, `NePredicate`, `LtPredicate`, `LePredicate`, `GtPredicate`, `GePredicate`, `InPredicate`, `BetweenPredicate`, `BoolEqPredicate<R>`, `BoolNePredicate<R>`, `BoolInPredicate<R>`, `AndPredicate<R>`, `OrPredicate<R>`, `NotPredicate<R>`, `AscOrdering<R, V>`, `DescOrdering<R, V>`) that carry the operator-relevant metadata (`Field`, `Value`, `Low` / `High`, `Operands`) as public properties so a backend can translate the node to SQL without invoking the per-record accessor.
- `Predicates.And<R>(params IPredicate<R>[])`, `Predicates.Or<R>(...)`, `Predicates.Not<R>(IPredicate<R>)` — static combinators that compose predicates over a single record type. A mixed-record composition is a compile-time error.
- `OrderedField<R, V> where V : IComparable<V>` and `BoolField<R>` — generic field-handle classes whose comparison methods return concrete predicate / ordering nodes specialised to the same record type. The constructors take `(string name, Func<R, V> accessor)` so each node embeds the source-level field name into its `FieldRef`.
- `QueryPlan<R>` — the inspectable AST value type wrapping `Source`, `Predicates`, `Orderings`, `Skip`, and `Take`. Stage helpers (`WithWhere`, `WithOrderBy`, `WithThenBy`, `WithSkip`, `WithTake`) return a new plan; the original is never mutated.
- `QueryRuntime.Execute<R>(records, plan)` — the shared in-memory evaluator used by every generated `<Master>Relation` terminal.

The per-master user-facing declarations are:

- `<Master>Fields` — a `public sealed class` exposing one `public readonly` field-handle per supported record field plus a `public static readonly <Master>Fields Instance` singleton. Field handles for ordered values (numeric, string) expose `Eq`, `Ne`, `Lt`, `Le`, `Gt`, `Ge`, `In`, `Between`, `Asc`, and `Desc`; field handles for bool values expose `Eq`, `Ne`, and `In` only. Each handle's comparison method returns the concrete predicate / ordering class specialised to the owning master, statically typed as `IPredicate<<Master>Record>` / `IOrdering<<Master>Record>` for the callback's return type.
- `<Master>Relation` — see [Master Data](#master-data) for the relation class itself. Its `Where` / `OrderBy` / `ThenBy` callbacks are typed `Func<<Master>Fields, IPredicate<<Master>Record>>` / `Func<<Master>Fields, IOrdering<<Master>Record>>`, so the compiler rejects a predicate built against a different master at the call site.

```csharp
using static Example.Masters.Masterbelt;
// ...
var weapons = await Items
    .Where(item => Predicates.And(item.Category.Eq("weapon"), item.Level.Ge(10)))
    .OrderBy(item => item.SortOrder.Asc())
    .Take(10)
    .ToList(data, cancellationToken);

await foreach (var item in Items
    .Where(item => item.Category.Eq("weapon"))
    .AsAsyncEnumerable(data, cancellationToken))
{
    // ...
}
```

### Scope Methods

Each `pub` [scope](../masterdata/schema.md#scope-section) on a master emits an instance method on the `<Master>Relation` class; a non-`pub` scope is internal to Masterbelt source and is not emitted. The method name is the source scope name in PascalCase (`genderedAdult` → `GenderedAdult`). Its parameters are the scope's declared parameters mapped through the regular [Type Mapping](#type-mapping); it takes no `MasterData` and no `CancellationToken` because a scope is effect-free and synchronous.

```csharp
public <Master>Relation <Scope>(<params>)
```

The method returns a freshly-allocated `<Master>Relation` whose plan extends the receiver's plan with the scope body's stages (chained scopes inlined), exactly like the built-in stage methods, so a scope composes with `Where` / `OrderBy` and with other scopes (`Records.Adult().Gendered(1)`). The method builds a plan only, so it behaves identically under `storage: memory` and `storage: sql`. A call to a non-`pub` sibling scope is inlined into the method body (with its parameters substituted), because only `pub` scopes are emitted as methods; a call to a `pub` sibling scope is a method call. An `indexed` scope adds no C# surface beyond its `pub` flag; it only influences SQLite index generation.

### Select Projections

Each `select Name { ... }` section on a master ([../masterdata/schema.md](../masterdata/schema.md#select-section)) emits a parallel set of C# declarations alongside the source relation:

- `<Master><Name>Record` — a `public sealed class` carrying the projected fields. Field order matches the source order written in the select body.
- `<Master><Name>Fields` — a `public sealed class` exposing typed field handles for the projected record, with the same singleton `Instance` shape as `<Master>Fields`.
- `<Master><Name>Relation` — a `public sealed class` carrying the source relation by value plus its own pair-level `QueryPlan<<Master><Name>Record>`. Stage methods are copy-on-write; terminals mirror the base relation's surface, parametrised on the projected record type.
- `public <Master><Name>Relation Select<Name>()` — a method on `<Master>Relation` that returns a fresh projected relation capturing the receiver's source-side plan.

Terminals on the projected relation first apply the source-side plan, then project each surviving record into a `<Master><Name>Record` by copying the named fields, and finally apply the projected plan to the projected slice. Projected relations do not expose `FindBy`.

```csharp
var summaries = await Items
    .Where(item => item.Count.Ge(10))
    .SelectSummary()
    .OrderBy(summary => summary.Name.Asc())
    .ToList(data, cancellationToken);
```

### Join Operator

Each `ref<Target>` field on a master's record ([../masterdata/relations.md](../masterdata/relations.md)) emits a parallel set of C# declarations alongside the source relation:

- `<Master>Join<Field>Pair` — a `public sealed class` with `public required <Master>Record Left { get; init; }` and `public required <Target>Record Right { get; init; }` properties that aggregate the joined pair.
- `<Master>Join<Field>LeftFields` / `<Master>Join<Field>RightFields` — `public sealed class`es each exposing typed field handles for the corresponding side's record, plus a singleton `Instance` static field. Each handle's accessor reads `pair.Left.<Field>` or `pair.Right.<Field>`.
- `<Master>Join<Field>Fields` — a `public sealed class` whose `Left` and `Right` instance fields point at the per-side singletons above, plus its own singleton `Instance` callback target.
- `<Master>Join<Field>Relation` — a `public sealed class` carrying the source relation by value, the right relation supplied at the call site, and its own pair-level plan. Stage and terminal methods mirror the source relation's surface, parametrised on the pair record.
- `public <Master>Join<Field>Relation Join<Field>(<Target>Relation right)` — a method on `<Master>Relation` that returns a fresh joined relation capturing the receiver as the left source, the supplied relation as the right source, and a fresh pair-level plan.

Terminals on the joined relation first `await source.ToList(data, cancellationToken)`, then iterate the surviving left records and `await right.FindBy(data, cancellationToken, leftRecord.<Field>_<Pk1>, ...)` for each, calling `pairs.Add(new <Master>Join<Field>Pair { Left = ..., Right = ... })` on a successful match and dropping the row on `null` (INNER JOIN; `LEFT` / `RIGHT` / `FULL OUTER` deferred). Pair-level state (predicates, orderings, skip, take) then applies through `QueryRuntime.Execute` before the terminal returns. Joined relations do not expose `FindBy`.

```csharp
var pairs = await B
    .JoinARecord(A)
    .Where(fields => fields.Right.Name.Eq("alpha"))
    .OrderBy(fields => fields.Left.Id.Asc())
    .ToList(data, cancellationToken);
```

## Match Statements

A Masterbelt match statement lowers to a C# `switch` statement on the lowered subject expression. The C# target relies on the language's pattern matching syntax to express every arm without synthesizing helper methods.

- A **type pattern** `T as name` lowers to a C# type pattern `case T name:`. A union type pattern matches against the union's nested wrapper class (`case IntOrString.Int_ wrapper:` followed by `var name = wrapper.Value;` for the user binding). A type pattern against the union's null member lowers to `case null:`.
- An **enum pattern** lowers to a value pattern `case Status.Active:`.
- A **literal pattern** lowers to a constant pattern `case 1:`, `case "a":`, `case true:`, or `case null:`.
- A **product pattern** lowers to a recursive pattern `case T { Field: pattern, ... } prefix:`. Field sub-patterns reuse the same pattern lowering rules. Short field bindings emit `var` patterns (`{ Field: var name }`) so the field value is captured directly.
- A **wildcard pattern** lowers to `default:`.

A `|`-separated alternative list emits one `case` label per alternative on the same arm body. When alternatives introduce a binding, the binding is rendered with C#'s `or` pattern combinator inside a single case label.

Bindings introduced by a pattern are emitted as local variables declared by the pattern itself (C# pattern matching binds directly), so the arm body uses the bound names without extra declarations. When the subject expression is a plain identifier without an explicit pattern binding, the C# target renders the narrowed binding by introducing `var name = <pattern variable>;` at the top of the arm body.

A guard is rendered as a `when` clause on the case label (`case T name when condition:`). A guarded arm whose guard evaluates to `false` is skipped per C#'s normal switch semantics; matching continues with the next case.

A wildcard arm becomes the `default:` label. When the match is statically exhaustive without a wildcard, the C# target omits any `default:` label; C#'s compiler does not require one for switch statements (it only requires exhaustiveness on switch expressions). The Masterbelt checker has already proven exhaustiveness, so no fallback `throw` is synthesized.

A match statement is otherwise emitted at its source position inside the surrounding C# method body and shares the method's local scope. The subject expression is evaluated exactly once at the head of the switch.

## Operator Expressions

The unary and binary operator expressions defined in [language/syntax.md](../language/syntax.md) reach the C# target as method calls on the operand's type (see [language/builtins.md](../language/builtins.md)). When the receiver is a built-in primitive type (or an alias whose target resolves to a primitive), the C# target emits the call as the corresponding native operator:

- Numeric operands emit `+ - * / % == != < <= > >= & | ^ << >>` directly.
- `bool` operands emit `&& || == !=`; `and`/`or`/`xor` lower to `&& || !=` respectively. Unary `not` emits `!`.
- `string` operands emit `+` for `add`, the `==` / `!=` operators for `eql`/`neq`, and `String.Compare` ordinal comparison results for `lt`/`lteq`/`gt`/`gteq`.
- Unary `plus`, `minus`, and `not` emit `+`, `-`, and `!`.

A user product type that declares one of the operator method names continues to lower through the regular method call path: the call site emits `receiver.Method(args)` (C# native overloading handles overload disambiguation). The native-operator rewrite applies only when the receiver's resolved type is a primitive.

### Built-in Field Accesses

The fields defined in [language/builtins.md](../language/builtins.md) on built-in primitive and generic types lower to native C# expressions:

- `string.length` lowers to a call into the runtime helper `MasterbeltStringLength(receiver)` written into `MasterbeltRuntime.cs`. The helper returns the number of Unicode codepoints in the string by iterating `string.EnumerateRunes()`, matching the spec's codepoint count. The naive `receiver.Length` property is **not** used because it returns the UTF-16 code unit count, which diverges from the spec for any non-BMP codepoint.
- `list<T>.size` lowers to `receiver.Count` (the `IReadOnlyList<T>.Count` property).
- `map<K, V>.size` lowers to `receiver.Count` (the `IReadOnlyDictionary<K, V>.Count` property).

Only the `string.length` access pulls in the runtime file; the list and map size accesses inline directly.

### Built-in Generic Operator Methods

`list<T>.add(other)` and `map<K, V>.add(other)` defined in [language/builtins.md](../language/builtins.md) lower to a call into a runtime helper that the C# target writes alongside the generated module files. The helper file is named `MasterbeltRuntime.cs` (the reserved `Masterbelt` PascalCase prefix used by any generator-managed file) and is emitted only when at least one generated module references a helper from it.

The helper file contributes to the shared partial class declared in the configured namespace, so call sites reach the helpers as bare identifiers without any class qualifier:

- `MasterbeltListAdd<T>(IReadOnlyList<T> a, IReadOnlyList<T> b): IReadOnlyList<T>` returns a fresh list containing the elements of `a` followed by the elements of `b`.
- `MasterbeltMapAdd<K, V>(IReadOnlyDictionary<K, V> a, IReadOnlyDictionary<K, V> b): IReadOnlyDictionary<K, V>` returns a fresh dictionary containing every entry of `a` and every entry of `b`, with keys present in both taking the value from `b`. The `K` parameter is constrained `where K : notnull` to satisfy `Dictionary<,>`'s key constraint.

Call sites for `list.add` emit `MasterbeltListAdd(a, b)`; call sites for `map.add` emit `MasterbeltMapAdd(a, b)`. C#'s type inference resolves the generic type parameters from the operand types at the call site.

## Function Types

A Masterbelt function type lowers to one of two C# forms depending on where it appears.

A type declaration whose body is a function type emits a `public delegate` at file scope alongside the shared partial class:

```csharp
public delegate int BinaryOp(int left, int right);
public delegate U Mapper<T, U>(T value);
public delegate int Summer(int initial, params int[] values);
```

Parameter names are preserved from source and rendered after the parameter type, matching C#'s `Type name` parameter form. A variadic parameter prefixed with `*` in source lowers to a C# `params T[]` parameter; the element type is the parameter's declared type. The variadic-must-be-last rule defined in [language/types.md](../language/types.md) is enforced before generation.

When a function type appears inline as part of another type expression (a product field's type, a generic argument, a union member, and so on), it lowers to a closed `System.Func<...>` constructed by listing each parameter type followed by the return type. Variadic inline function types have no `Func<>` representation and are rejected with the diagnostic defined for unsupported types; named-delegate form must be used instead.

Effects defined in [Effects](#effects) shape the rendered signature:

- `cancellable` appends a `CancellationToken cancellationToken` parameter to the parameter list.
- `failable` does not change the declared return type. The C# signature renders the success type `R`; the failure path is plumbed by exception propagation at the call site (see [Failable Handling](#failable-handling)).
- `asyncable` wraps the declared return type `R` in `System.Threading.Tasks.Task<R>`. When the named delegate or `Func<...>` form is used, the wrapping applies to the return type only; the C# `async` keyword is not synthesized at the type level (it belongs on the implementation, not the delegate signature).

A function type that combines multiple effects applies every transformation listed above.

## Cross-Module References

A reference to a symbol declared in another Masterbelt module emits the bare C# identifier of the foreign symbol. No class qualifier is added: every module's reachable symbols are members of one shared partial class, so the foreign symbol is already visible in scope at the reference site.

## Re-exports

A `pub { ForeignName as LocalName } from "./other.mst"` declaration that renames the foreign symbol emits a forwarding member on the shared partial class. The forwarding member is a `public static readonly` field whose type matches the foreign symbol's checked type and whose initializer references the foreign symbol by its bare name:

```csharp
public static readonly int LocalName = ForeignName;
```

A re-export that keeps the foreign name (no `as` rename) is a no-op in C#: the foreign symbol is already a member of the shared partial class under the same identifier, so emitting `public static readonly T Foo = Foo;` would be a self-reference. Such re-exports emit no declaration.

Re-exports keep their declaration's doc comment as an XML documentation block immediately before the field.

## Effects

Each effect defined in `codegen/model` maps to C# as follows:

- `cancellable` adds a `CancellationToken cancellationToken` parameter as the last parameter of the callable.
- `failable` does not change the return type. Failure is surfaced through a thrown exception (see [Failable Handling](#failable-handling)); call sites do not catch it so the exception bubbles through the surrounding callable.
- `asyncable` wraps the result in `Task<T>` (or `Task` for `void`) and the callable is declared `async`. A call site whose callee is asyncable is wrapped in `await`.

Every effect is inferred along the call graph: the C# target computes an effective effect set per callable by walking from the declared effects and propagating through every transitive call site to a fixed point. A function whose effective set differs from its declared set still renders with the inferred shape — a non-asyncable surface declaration whose body calls an asyncable method still becomes an `async Task<R>` method, and a non-cancellable declaration that calls a cancellable method still receives and forwards the `CancellationToken`.

A callable that carries multiple effective effects combines all applicable transformations on its signature.

The target reports an explicit diagnostic when a program requires an effect that has no defined mapping rather than emitting code that silently drops the obligation.

## Failable Handling

A `failable` function in C# reports failure through a thrown exception. The Masterbelt surface treats `failable` as transparent (see [language/semantics.md](../language/semantics.md#failable-handling)); the C# target uses native exception propagation so neither the method signature nor the call site needs to surface the failure path:

- `fail "message"` lowers to `throw new System.InvalidOperationException("message");`.
- `fail value` where `value: Error` lowers to `throw new System.InvalidOperationException(value.Message);`.
- A call to a `failable` callable lowers to a plain method invocation; a thrown exception propagates through the surrounding `failable` method because the surrounding method does not catch it.

No `Error` class is emitted by the C# target. Match expressions cannot observe the failure path of a `failable` call subject because the surface type of the call is `R`.

## Using Directives

The C# target's emitter assembles the file's `using` directives from the symbols referenced during rendering. A symbol carries the C# namespace it lives in and the unqualified identifier name. The emitter:

- Aggregates the set of referenced namespaces across every declaration written into a file.
- Emits one `using <namespace>;` directive per distinct referenced namespace, in lexicographic order, before the file's `namespace` directive.
- When two distinct namespaces export the same unqualified name, the second occurrence is emitted as a `using` alias such as `using <Alias> = <Namespace>.<Name>;` so each local identifier is unique.
- Rewrites identifier renderings to use either the unqualified name (when only one namespace exports it) or the chosen alias.

The default local name of an imported symbol is its original name in the namespace. The emitter only assigns aliases when collisions require it.

Targets MUST express external references through this emitter mechanism. Inlining raw namespaces into source bypasses collision handling and is forbidden.

## Determinism

Generated files are deterministic with respect to the input modules and options. Constants appear in source order within each class. Union and wrapper class declarations appear sorted by interface name in `MasterbeltUnions.cs`. Map literal entries appear in lowering order (first-occurrence position, last-wins value). Using directives are emitted in lexicographic order.
