Design decisions (ADRs)
This is a curated reference index of the Architecture Decision Records in the repository. ADRs explain design intent and tradeoffs; they are not the normative language specification. Each link points to the source ADR on GitHub. The repository currently has accepted/proposed ADRs through ADR-0146 (plus the 0000 template).
Null model, values, and primitives
| ADR | Title | Summary |
|---|---|---|
| 0001 | Absence / null model — Kotlin-style nullable types | Uses explicit nullable T?, nil, safe access, and assertion instead of a universal null. |
| 0073 | Null-conditional indexing a?[i] | Adds the ?[ token; result lifts to the nullable form of the indexer's return type; receiver evaluated exactly once; rejected as assignment LHS. |
| 0008 | Variable bindings — keep Go's var/const/:=, add let | Original decision; the := short-declaration leg was superseded by ADR-0077. |
| 0077 | Drop := short variable declaration | Removes := from the language; the parser hard-rejects it with GS0305 and a context-sensitive let/var/for … in … migration suggestion. |
| 0015 | Multi-target assignment evaluation order | Evaluates all right-hand values before observable writes, matching Go swap semantics. |
| 0016 | Slice backing storage — T[] | Represents slices with CLR single-dimensional zero-based arrays. |
| 0044 | Complete numeric primitive coverage | Defines full width-bearing numeric primitive coverage and conversion behavior. |
| 0045 | object as the universal upper bound | Makes object the top type for assignment compatibility, boxing, and object equality. |
| 0046 | 'c' character literal grammar | Specifies character literal grammar, escapes, static type, and diagnostics. |
| 0049 | Width-bearing integer keyword names | Chooses explicit names such as int32 and uint64 over ambiguous aliases. |
| 0098 | Friendly numeric type aliases | Adds int / uint / long / ulong / short / ushort / byte / sbyte / float / double as binder-resolved aliases on top of the canonical width-bearing names; diagnostics and IL keep the canonical spelling. |
Concurrency, async, and resource scope
| ADR | Title | Summary |
|---|---|---|
| 0002 | Concurrency model — Go surface, .NET runtime, Kotlin scopes | Combines Go-like go/channel syntax with .NET tasks and structured scopes. |
| 0022 | go / chan / select → .NET lowering | Defines how goroutine-like calls, channels, send/receive, and select lower to .NET. |
| 0023 | async func / await — state-machine strategy | Commits to compiler-generated async state machines for emitted async functions. |
| 0030 | defer and block-scoped cleanup convergence | Makes cleanup block-scoped and aligns defer with using/finally lowering. |
| 0040 | sequence[T] type alias and yield statement | Introduces sequence types and iterator yield statements. |
| 0041 | sequence[T] in an async context aliases IAsyncEnumerable[T] | Explores async sequence feasibility and binding shape. |
| 0042 | async sequence[T] as a type-clause spelling for IAsyncEnumerable[T] | Chooses an explicit type-clause spelling for async streams. |
| 0043 | async func(P) R as a type-clause spelling for func(P) Task[R] | Defines async function type clauses as task-returning function types. Re-spelled as async (P) -> R by ADR-0075. |
Object model, OO, and data types
| ADR | Title | Summary |
|---|---|---|
| 0003 | OO surface — data-oriented core with light OO escape hatch | Adds classes/interfaces without making OO the center of the language. |
| 0017 | Method virtuality — sealed by default, opt-in with open | Requires explicit open for inheritable classes and overridable methods. |
| 0018 | Interface default methods — not in Phase 3 | Defers default interface methods from the early interface surface. |
| 0024 | Methods with receivers vs. extension functions canonical style | Establishes receiver functions as the canonical extension/method style. |
| 0079 | Restrict receiver-clause methods to non-owned receiver types (warning) | Reserves func (r T) M() for types the package does not own; same-package owned receivers emit the soft GS0314 warning. Operators are exempt. |
| 0025 | record keyword alias for data struct | Makes record syntactic sugar for data struct. |
| 0029 | data struct synthesized members | Defines synthesized equality, copy, and ergonomic members for data structs. |
| 0032 | Data-struct ergonomics polish | Refines copying, deconstruction, and update syntax for data structs. |
| 0033 | Inline value classes / inline struct | Introduces single-field inline structs for value-class ergonomics. |
| 0051 | Property declarations — prop keyword with accessor bodies | Specifies property syntax, auto-properties, and accessors. |
| 0052 | Event declarations on user types — event keyword | Defines field-like and custom event declarations on user types. |
| 0053 | Static members on user types — shared block | Adds static fields, methods, properties, and events through a shared block. |
Generics and functions
| ADR | Title | Summary |
|---|---|---|
| 0004 | Generics — consumption and definition in a single phase, with constraints | Defines initial generic types/functions, constraints, and reified-CLR-generics commitment (now fully delivered; the original implementation-status addendum was superseded by ADR-0087 R1–R7). |
| 0020 | Generic type-parameter brackets — Go-style [T] | Chooses square brackets for type parameters and type arguments. |
| 0021 | Generic variance modifiers — in / out on interface type parameters | Adds variance markers for interface type parameters. |
| 0038 | Type-argument inference for imported open generic methods | Defines inference for imported generic method calls and associated emit fixes. |
| 0050 | -> arrow trailing-lambda syntax | Superseded by ADR-0074. The proposed ->-connector trailing-lambda was never implemented and never will be — -> is now the lambda operator, so the connector spelling collides with (params) -> … lambdas. Full arrow lambdas plus the live Phase 4.9 func(...) trailing-call form cover the need (revisited under issue #411). |
| 0087 | Reified-generics emit — open-shape erasure audit, staged elimination plan, and v1 disposition | Implementation-status addendum to ADR-0004. Catalogued every open-generic erasure site (53 across 14 source files), specified the target CLR metadata (TypeDef+GenericParam, TypeSpec/MethodSpec, Var/MVar encoding), staged the elimination R1–R7 (all implemented), and pinned the v1 disposition. Supersedes issue #484. |
Error handling and control flow
| ADR | Title | Summary |
|---|---|---|
| 0005 | Error handling — exceptions only, unchecked | Uses CLR exceptions instead of checked exceptions or Go-style error returns. |
| 0009 | switch semantics — expression + statement, patterns, exhaustive | Defines switch statements, switch expressions, patterns, and exhaustiveness. |
| 0013 | Drop Go's fallthrough | Reserves fallthrough but rejects it; cases never fall through. |
| 0031 | Canonical for x in collection | Establishes for x in collection as the preferred range iteration spelling. |
Syntax, naming, and documentation policy
| ADR | Title | Summary |
|---|---|---|
| 0006 | Visibility — explicit modifiers, public default | Replaces Go capitalization export rules with explicit visibility modifiers. |
| 0007 | String interpolation syntax — Kotlin-style | Chooses $name and ${expr} interpolation forms. |
| 0010 | Aspirational samples policy — rewrite to today's subset, re-expand per phase | Keeps samples aligned with implemented language phases. |
| 0011 | String interpolation grammar and lowering | Specifies interpolation parsing and lowering details. |
| 0012 | Raw string delimiter — backtick | Chooses backtick-delimited raw strings. |
| 0014 | Visibility defaults — public for top-level declarations | Makes unmodified top-level declarations public by default. |
| 0054 | Postfix member/index access on primary expressions | Chains ./?./[] after any primary except a bare numeric literal ((42).Member). |
| 0055 | String interpolation revamp | Delimiter-aware multiline holes, alignment/format clauses, late context-driven lowering (DefaultInterpolatedStringHandler/FormattableString), and full IDE support inside holes. |
| 0057 | Documentation comments | Markdown-authored /// documentation comments with lossless CLR XML-doc round-trip; hover renders the merged XML on imported APIs. Diagnostics GS0227–GS0231. |
| 0062 | Generalized ternary expression | Promotes cond ? a : b from the narrow ADR-0061 ref form to a normal expression; retires GS0259 in value contexts and adds GS0263. |
| 0074 | -> for lambda expressions, : for switch-expression arms | Adds the arrow-lambda expression form (x int32) -> x * x and migrates switch-expression arms from -> to : (deprecated old arm form emits GS0302). |
| 0075 | (T) -> R as the canonical function-type clause syntax | Function-type clauses are spelled (T1, T2) -> R and async (T) -> R. The legacy func(T) R and async func(T) R type-clause spellings stay valid for one release and emit GS0303. |
| 0076 | Type inference for let / var lambda bindings | When a let / var binding is initialized with a lambda whose parameter types are fully spelled, the binding's type is inferred to the lambda's (T1, ...) -> R function type. Open lambdas (untyped params and no target type) emit GS0304. |
| 0128 | Arrow-lambda / func-literal parity — statement-block arrow bodies | A block-bodied arrow lambda (p) -> { … } becomes a statement block with an optional trailing value expression: an if-without-else (and other void control-flow) is a void statement instead of being rejected with GS0276, reaching full parity with func literals. The parser classifies if as a value-producing if-expression only when it has a matching else. cs2gs reverts the #1160 workaround and emits idiomatic arrow lambdas for block-bodied C# lambdas. |
| 0129 | C#-compatible numeric literal narrowing/widening | A constant integer expression (an integer literal, or unary +/- over one) implicitly narrows to any integer target whose range contains its value (C# §10.2.11), so var x uint8 = 42 / var a int8 = -5 compile with no cast; out-of-range constants still error (GS0156). Non-constant values widen implicitly per the lattice and narrow only via the explicit T(x) conversion-call form (truncating like C#). |
CLR interop
| ADR | Title | Summary |
|---|---|---|
| 0019 | Extension function declaration syntax — func (Receiver) Name(...) ... | Defines receiver-based extension function syntax. |
| 0026 | Operator-by-name on user types — deferred | Defers user operator naming until a later design is chosen. |
| 0034 | Imported CLR interop — static members, writes, operators, conversions, overload resolution | Extends imported CLR support across static members, writes, operators, conversions, and overloads. |
| 0035 | User-defined operator keyword on GSharp types | Adds receiver-style operator declarations for G# types. |
| 0036 | CLR event subscription with += / -= | Uses assignment-like syntax for CLR event add/remove. |
| 0037 | Numeric better-conversion target tie-breaking in overload resolution | Defines numeric conversion ranking for overload resolution. |
| 0039 | By-ref pointers and CLR interop for ref / out / in parameters | Introduces *T, &, dereference, ref arguments, and related diagnostics. |
| 0047 | Attribute consumption and declaration (Kotlin-style annotations) | Defines @ annotation syntax, use-site targets, attribute arguments, and @Attribute sugar. |
| 0056 | Span consumption v1 — ref-returning members, span element access, closed generic value-type fields | Auto-dereferences ref-returning members in rvalue position, makes spans indexable (read/write), applies []T → Span[T] conversion in argument position, and gives closed generic value-type fields real layout. |
| 0058 | Ref-safe-to-escape and the scoped modifier | Adds the scoped parameter modifier and the supporting GS9004/GS9006 ref-pointer escape diagnostics. |
| 0059 | Named delegate types | type Name = delegate func(...) declares a real CLR MulticastDelegate-derived type; generic delegates supported per issue #1503; diagnostic GS0233. |
| 0060 | ref/out/in parameters | Declaration-site and call-site ref-kind modifiers with diagnostics GS0235–GS0243; ref-aliasing locals (let ref/var ref) and ref returns are follow-ups (GS0248–GS0258). |
| 0061 | Conditional ref-arguments | Narrow ref cond ? a : b form inside ref-kind argument payloads; diagnostics GS0259–GS0262. |
| 0063 | Method overloading and optional parameters | Lifts the v0 "one declaration per name" rule and adds default parameter values; diagnostics GS0264–GS0267. |
| 0080 | Deprecate name = value named-argument spelling (warning) | Reserves name: value as the canonical call-site and attribute named-argument separator (issue #343); the legacy = spelling kept for back-compat by ADR-0032 / ADR-0047 emits the soft GS0315 warning before removal. |
Emit and tooling
| ADR | Title | Summary |
|---|---|---|
| 0027 | Roslyn-fork decision for v1.0 — stay on the bespoke emitter | Keeps the direct metadata emitter instead of adopting a Roslyn fork for v1.0. |
| 0028 | Multi-package emit model — Option B, C#-faithful | Defines how multiple packages map to emitted CLR namespaces/types. |
| 0048 | Portable PDB emit | Adds portable PDB, source mapping, and debug-symbol policy to the emitter. |
0.3 ADRs (0105-0146)
The 0.3 documentation audit covers these ADRs landed after the 0.2 snapshot. The earlier curated sections above remain grouped by topic; this table keeps the current ADR range visible.
| ADR | Title |
|---|---|
| 0105 | Incremental (delta) binding for the language server |
| 0106 | Incremental LSP SemanticModel via instance-keyed memoization |
| 0107 | Cross-session cold-start cache for the language server |
| 0108 | Delegate return-type covariance and lambda target-typing on CLR method calls |
| 0109 | Top-level private maps to IL assembly (internal) |
| 0110 | Nested type declarations |
| 0111 | Completion-as-you-type triggering policy |
| 0112 | Unified member resolution |
| 0113 | Predefined type aliases as static-member-access receivers |
| 0114 | Nested (and forward-referenced) class constructor emission |
| 0115 | cs2gs — a C#→G# migration tool and gap-discovery pipeline |
| 0116 | Null-coalescing operator spelled ?? (replacing ?:) |
| 0117 | Collection initializers — List[T]{…}, HashSet[T]{…}, Dictionary[K,V]{…} |
| 0118 | User indexer-member declaration — prop this[i int32] T { get; set } |
| 0119 | Inferred-type arrow lambdas are the canonical lambda form |
| 0120 | User-defined conversion operators (operator implicit / operator explicit) |
| 0121 | Throw expressions (throw in value position) |
| 0122 | Unsafe context and unmanaged raw pointers (*T = ELEMENT_TYPE_PTR) |
| 0123 | From-end index operator (^n) for index and range bounds |
| 0124 | stackalloc [n]T stack allocation (localloc) — safe Span<T> and unsafe T* forms, with initializers |
| 0125 | fixed statement — pinning a managed buffer and binding an unmanaged *T |
| 0126 | Increment / decrement as value-producing expressions (++ / --) |
| 0127 | Standalone System.Range value (let r = 1..3) |
| 0128 | Arrow-lambda / func-literal parity — statement-block arrow bodies |
| 0129 | C#-compatible numeric literal narrowing/widening |
| 0130 | [n]T runtime/zero-initialised array allocation |
| 0131 | Expression-bodied members via the -> arrow |
| 0132 | []T? array of nullable elements vs []?T nullable array |
| 0133 | Implicit numeric promotion at call sites |
| 0134 | Static imports — import Ns.Type exposes shared members for unqualified use (C# using static) |
| 0135 | unmanaged type-parameter constraint and sizeof(T) expression |
| 0136 | Unannotated imported reference types are nullable by default |
| 0137 | Nullable function type spelling |
| 0138 | cs2gs construct-coverage program and gap-triage automation |
| 0139 | general goto / label statements |
| 0140 | shared { init { … } } static-initializer block |
| 0141 | lambda conversions to Expression[TDelegate] |
| 0142 | resx codebehind generator (Resources.Designer.gs) |
| 0143 | generated code in cs2gs migration — reproduce at build, don't freeze |
| 0144 | partial types (partial on class / struct / interface) |
| 0145 | Roslyn source-generator host for native G# projects (gsgen) |
| 0146 | Anonymous-object literal (object { ... }, Kotlin-style) |