Pointers & by-ref
*T is E#‘s pointer: nullable, aliasing, and first-class (storable, returnable, shareable). It applies to
value data (any size) and primitives (*int); *RefData is ill-formed
(ES2003) — a ref data is already a reference. Member access through a *T
auto-dereferences.
Representation is implementation-defined. A whole-module escape analysis chooses per binding:
| Condition | Representation |
|---|---|
| escapes the frame (returned, stored, captured) or is nullable | __Ptr_T — a heap reference cell shared by every holder |
| provably neither | a managed pointer ref T — zero allocation, aliases the caller’s storage |
Conversions between the two forms are inserted automatically where they meet at a call. Neither is observable from source.
new vs. &
Section titled “new vs. &”NewExpr = "new" TypeName ( "{" [ FieldInitList ] "}" | "(" [ ArgList ] ")" ) .new T { … } heap-allocates a value data and yields a fresh *T — the only allocation expression, and
the only way to mint a fresh pointer. new on a non-data is ES2144. The
deprecated &T { … } spelling warns ES2143. & otherwise only takes
addresses: new allocates something that does not yet exist; & takes the address of something that
does.
The managed-pointer foundation
Section titled “The managed-pointer foundation”The CLR has one underlying by-ref primitive, the managed pointer T& (ByReferenceType). ref,
out, and in are the same IL type, distinguished only by parameter metadata. E#‘s by-ref family maps
onto it:
| Form | CLR emission | Direction | Deref | Call site |
|---|---|---|---|---|
x: T | T | in (by value) | — | f(x) |
x: *T | ref T or __Ptr_T | in / out | explicit | f(&x) / f(*x) |
x: readonly *T | in T ([In]) | in (zero-copy) | explicit, read-only | f(&x) |
out x: T | [Out] T& | out | implicit | f(out x) |
A *T parameter emits ref T; both &expr and *expr at the call site pass by reference. readonly *T
is a zero-copy read-only borrow (in T) — assigning through it is a binder error, and such a first
parameter is not promoted. out x: T is the C# out shape ([Out] T&): assignment writes through the
slot, reads load through it, and it interoperates one-to-one with C# out. Definite assignment of out is
a documented obligation, not yet a hard error.
Address-of and ref locals
Section titled “Address-of and ref locals”&name yields a managed pointer to a local, parameter, or field (ldloca/ldarga/ldflda); if name
resolves to a function it is instead a function pointer. A local
initialized from &expr becomes a ref local (a ByReferenceType variable); reads and writes through
it transparently dereference. Managed pointers are GC-tracked, cannot dangle, and cannot escape the frame.
Method sets and interface conformance
Section titled “Method sets and interface conformance”*T has its own method set: value-receiver functions (func f(x: T)) are in both T’s and *T’s sets;
pointer-receiver functions (func f(x: *T)) are in *T’s set only. When a declared interface is satisfied
only by the pointer method set, the generated __Ptr_T wrapper implements the interface and forwards to
the underlying functions — so *T flows through interface-typed parameters without repeatedly boxing the
value. The wrapper is generated only when a *T is actually used as an interface.