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 above public variant readonly data R { ... }readonly struct R (+ generated equality)ValueType + [IsReadOnly]let field: Treadonly T fieldinitonly fieldexpr with { f: v }IIFE copy + assign ldobj + stfldref 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 factories ValueType + tag enum ref choice Expr { ... }abstract base + sealed subclass per case abstract 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 Point instance 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 + NewSlotabstract 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 argsinit(args) : base(b): base(b) chain.ctor with explicit call BaseType::.ctorinit(...) on data— ES3012 : use composite literal / factoryreturns T clausedefault return type for nested funcs missing -> T —
E# C# IL let x = 42var x = 42; (immutable by E#)stlocvar x = 0var x = 0;stloca ? 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 destructure field loads after tag check match x { .a(v) { } } (ref choice)is type patternisinst + field extractionlet x = f()?unwrap with early error return conditional branch let x = f() else { return }null guard, early return brfalsedefer { 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 / ldfldavar p = &xref var p = ref xByReferenceType local&(int, int -> int)delegate*<int, int, int>FunctionPointerTypex: *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.ValueTuplelet (a, b) = exprtemp + .Item1/.Item2 field extraction derive equalityEquals / GetHashCode / == / !=generated methods derive debugToString()generated method
E# C# IL await exprawait exprIAsyncStateMachine structspawn { ... }Job.Spawn(_ => { ... })Task.Runchan<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>GenericInstanceTypeusing static "System.Math"using static System.Math;static type import data Foo(x: int)positional sugar fields + 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.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
Disable the whole tier with <ImplicitUsings>disable</ImplicitUsings>, after which only explicitly
imported namespaces resolve.