Skip to content

CLR mapping

E# is a first-class CLR citizen: every construct lowers to ordinary metadata and IL, indistinguishable from what C# emits. This is that lowering, end to end. The IL column is what the primary backend emits; the C# column is the equivalent a reader (or the secondary transpiler) would write.

E#C#IL
namespace Foopublic static partial class Foostatic class holding namespace methods
data Point { x: int, y: int }struct Point or sealed class Point (compiler’s choice)value type or reference type — value-semantic contract preserved either way
pub data Point { ... }public variant of the abovepublic variant
readonly data R { ... }readonly struct R (+ generated equality)ValueType + [IsReadOnly]
let field: Treadonly T fieldinitonly field
expr with { f: v }IIFE copy + assignldobj + stfld
ref data Config { ... }sealed partial class Configsealed class
open ref data Fooclass Foo (not sealed)inheritable class
abstract ref data Fooabstract class Fooabstract class
choice Error { ... }tag enum + partial struct with factoriesValueType + tag enum
ref choice Expr { ... }abstract base + sealed subclass per caseabstract class + sealed subclasses
enum Direction { ... }public enum DirectionSystem.Enum (int32)
interface IDrawable { ... }public interface IDrawableinterface
delegate func BinOp(...)delegate int BinOp(int, int)sealed MulticastDelegate subclass
static func Foo { ... }public static partial class Foo (sibling to the namespace class)static class: const/static fields + static methods
T? (value type)Nullable<T>System.Nullable<T>
T? (ref type)T? annotationsame reference type
nil (value context)default(Nullable<T>)initobj Nullable<T>
[Serializable][Serializable]CLR custom attribute
E#C#IL
func add(a: int, b: int) -> intpublic static int add(int a, int b)call (direct)
func describe(p: Point) -> stringinstance method on Pointinstance call
func move(p: *Point, dx: int)static void move(ref Point p, int dx)managed pointer &
func f() -> T = exprT f() { return expr; }expression-body desugar
virtual func name(...)public virtual T name(...)Virtual + NewSlot
abstract func name(...)public abstract T name(...)Virtual + NewSlot + Abstract, no body
: func name(...) (override of virtual)public override T name(...)Virtual (ReuseSlot) — same vtable slot
: func name(...) (fulfill of abstract)public override T name(...)Virtual (ReuseSlot)
init(args) { body } on ref dataparameterized constructor.ctor with args
init(args) : base(b): base(b) chain.ctor with explicit call BaseType::.ctor
init(...) on dataES3012: use composite literal / factory
returns T clausedefault return type for nested funcs missing -> T
E#C#IL
let x = 42var x = 42; (immutable by E#)stloc
var x = 0var x = 0;stloc
a ? b : ca ? b : cconditional branch
x ?? yx ?? ynull check + branch
x?.Memberx?.Membernull-conditional access
match x { .a { } }switch (x.Tag)IL switch (jump table)
match x { .a(v1, v2) { } }multi-field destructurefield loads after tag check
match x { .a(v) { } } (ref choice)is type patternisinst + field extraction
let x = f()?unwrap with early error returnconditional branch
let x = f() else { return }null guard, early returnbrfalse
defer { f() }try { ... } finally { f(); }exception handler block
"hello {name}"$"hello {name}"String.Concat (value types boxed)
E#C#IL
&funcNamedelegate*<...>ldftn + calli
&varNameref varNameldloca / ldarga / ldflda
var p = &xref var p = ref xByReferenceType local
&(int, int -> int)delegate*<int, int, int>FunctionPointerType
x: *T paramref Tmanaged pointer
readonly *T paramin TByReferenceType + [In]
out x: T paramout T[Out] T&
[1, 2, 3]new List<int> { 1, 2, 3 }List<T> + Add
(a, b)new ValueTuple<T1, T2>(a, b)System.ValueTuple
let (a, b) = exprtemp + .Item1/.Item2field extraction
derive equalityEquals / GetHashCode / == / !=generated methods
derive debugToString()generated method
E#C#IL
await exprawait exprIAsyncStateMachine struct
spawn { ... }Job.Spawn(_ => { ... })Task.Run
chan<T>(n)new Chan<T>(n)Channel.CreateBounded<T>(n)
task func name() -> Tstatic Job<T> name()Job.Spawn<T>(__inner)Esharp.Stdlib.Job<T>
List<int>()new List<int>()newobj List1::.ctor`
Dictionary<string, T>Dictionary<string, T>GenericInstanceType
using static "System.Math"using static System.Math;static type import
data Foo(x: int)positional sugarfields + positional construction

These namespaces are searched for an unqualified type name as the last resolution tier (after exact names and explicit usings — see Names & resolution), so Dictionary<…>, StringBuilder, ObservableCollection<T>, and FormatException resolve with no using:

System System.Linq
System.Collections.Generic System.Diagnostics
System.Collections.ObjectModel System.Timers
System.Collections.Specialized System.ComponentModel
System.Text System.Threading
System.Text.Json System.Threading.Tasks
System.Text.Json.Serialization System.Threading.Channels
System.IO

Disable the whole tier with <ImplicitUsings>disable</ImplicitUsings>, after which only explicitly imported namespaces resolve.