Language - amir1387aht/MicroCS GitHub Wiki
MicroCS compiles a subset of C# with C#-compatible semantics wherever it supports a
feature. Programs that stay inside the subset print the same output as .NET:
tools/verify_dotnet.sh checks t02, t05, t10, t11, t13 and examples/tour.cs
against .NET 8 byte-for-byte.
Legend: โ
supported ยท
| Feature | Notes | |
|---|---|---|
Top-level statements, static void Main()
|
โ |
args via the host |
namespace, using directives |
โ | accepted and ignored (one global namespace) |
| Classes, static classes, nested types | โ | |
struct |
reference semantics (assignment does not copy) | |
Interfaces, abstract/virtual/override/sealed/base
|
โ | default interface methods โ |
Constructors, chaining (: this(..), : base(..)), static constructors |
โ | |
Properties (auto, init, expression-bodied, get/set bodies), indexers |
โ | |
| Operator overloading | โ | user-defined conversion operators (implicit/explicit operator) โ |
| Enums (incl. explicit values, flags arithmetic) | print as numbers | |
| Generics (classes, methods, constraints) |
erased: constraints parsed and ignored, default(T) is null
|
|
Events, delegates, multicast, Action/Func
|
โ | |
Records, primary constructors, required, file-scoped types |
โ |
| Feature | Notes | |
|---|---|---|
if while do for foreach break continue return
|
โ |
foreach over arrays, lists, strings, dictionaries, sets, GetEnumerator()
|
switch statements: constants, type patterns, relational, and/or, when
|
โ |
case int n when n < 0:, case > 5 and < 8:, case null:
|
try / catch / finally, exception filters when
|
โ | |
throw, rethrow throw;, throw expressions |
โ | |
using (...), using var
|
โ | calls Dispose()
|
Local functions (incl. static), recursion |
โ | |
goto, labeled statements, yield, async/await, lock, fixed, unsafe
|
โ | |
Declarations as embedded statements (if (c) int x = 1;) |
โ | compile error, as in C# (CS1023) |
| Feature | Notes | |
|---|---|---|
Arithmetic, bitwise, shifts, >>>, compound assignment, ++/--
|
โ |
int is 32-bit; long is 64-bit only with MCS_INT64=1
|
checked(...) / unchecked(...)
|
accepted; arithmetic always wraps | |
?:, ??, ??=, ?., ?[], null-forgiving !
|
โ | |
is T x, is null, is not null, as, casts |
โ | |
is with relational / and / or / not patterns |
โ |
t is < 0 or > 100, c is >= 'a' and <= 'z'
|
Switch expressions with all pattern forms above, _
|
โ | |
| Lambdas, closures (by-reference capture), method groups | โ | |
| String interpolation with alignment and format specifiers | โ |
$"{x,8:F2}", X4, D3, N, P, E
|
Object & collection initializers, target-typed new()
|
โ | |
Arrays, jagged arrays int[][], array initializers |
โ | multi-dimensional int[,] โ |
nameof, typeof, sizeof(builtin), default, default(T)
|
โ | |
Nullable value types int?: .HasValue, .Value, .GetValueOrDefault()
|
โ |
null-able at run time (values are boxed uniformly) |
Tuples (1, "a"), named (x: 1, y: 2), tuple types (int x, int y)
|
โ | up to 7 elements; structural ==, usable as dictionary keys |
Tuple element name projection var t = (a, b); t.a
|
โ | C# 7.1 rules (duplicates / reserved names dropped) |
Deconstruction var (a, b) = t;, (a, b) = (b, a);, _ discards |
โ | tuples, KeyValuePair, foreach (var (k, v) in dict)
|
Index from end x[^1], ranges x[1..^1], x[..3], x[2..]
|
โ | strings, arrays; on List<T> as a MicroCS extension |
ref / out / in parameters and arguments, out var, out _
|
copy-in/copy-out, see below | |
Named arguments F(x: 1)
|
โ | |
ref locals and returns, stackalloc, pointers, dynamic, reflection |
โ | |
Raw string literals """...""", UTF-8 literals "x"u8
|
โ | verbatim @"..." โ
|
Arguments are passed as a one-element cell; the callee works on a local copy that is
initialised from the cell (ref) or to the default (out) and is written back to the
cell on every return; the caller then stores the cell into the target variable.
This copy-in/copy-out is indistinguishable from .NET by-reference passing except:
- if the callee throws, assignments it made to the parameter are not visible to the caller;
- if the same variable is passed twice (
Swap(ref a, ref a)) or the callee also reads the variable through another path (closure, field) while it runs, it sees the old value; - for
ref obj.F/ref a[i]the target expression is evaluated again for the write-back; -
TryGetValue/TryPop/TryDequeueon a miss storenull(no staticdefault(TValue)).
out var declared in a top-level statement becomes a global, as in C#. out/ref
arguments are supported in expression statements, declarations, if/while conditions,
return and expression-bodied members/lambdas; other positions report a compile error.
Tuples are immutable-by-convention instances of a hidden ValueTuple class per
(arity, names) pair; element names are aliases of Item1..Item7. Because types are erased,
names travel with the value: the compiler re-labels a tuple when it is stored into a
variable, field, property, parameter or out parameter, used in an object initializer, or
returned from a method/property whose declared type has element names. Member names are
found by a pre-scan of the source, so declaration order does not matter; the lookup is by
member name, so two unrelated members with the same name and different tuple element
names in one program may get the wrong labels (positional ItemN access is always correct). Two tuples are equal when their elements are equal, regardless of names.
(a, b) = (b, a) with a tuple literal on the right compiles to temporaries โ no allocation.
-
int,uint,short,byte,sbyte,char,bool,double,float,string,object; all integer types share one representation: 32-bit by default (also in the hostmcsbuild โlong.MaxValueprints 2147483647), 64-bit withMCS_INT64=1(profiles/mcs_profile_linux.h).floatanddoubleshare one representation: double by default, single withMCS_FLOAT_DOUBLE=0(lowram profile). - Strings are UTF-8 and byte-indexed (
s.Lengthcounts bytes;foreachyields code points). - LINQ is eager and returns
List<T>;GroupByreturnsDictionary<key, List<item>>. The operators work on arrays,List<T>,Dictionary(asKeyValuePairs),HashSet,StackandQueue; the last four are enumerated from a snapshot.MCS_ENABLE_LINQ=0removes them. - Value-type semantics for
structare not implemented โ structs are references.
-
StackOverflowExceptionis catchable; out-of-memory ends the run withMCS_ERR_MEMORYinstead of killing the process. - Integer constant overflow wraps instead of a compile error;
checkeddoes not trap. Division is the exception:MinValue / -1andMinValue % -1throwOverflowExceptionas in .NET (also for constants, which C# rejects at compile time) โMinValueof the shared integer representation, so withMCS_INT64=1int.MinValue / -1is2147483648. -
foreachthrows Collection was modified after aList<T>change or aDictionarykey addition in the loop body, like .NET 8; removing dictionary entries orClearis allowed.HashSet,StackandQueueare enumerated from a snapshot, so changing them does not throw. -
GroupByreturns aDictionary<key, List<item>>. - Index-out-of-range messages include the index and length.
- Enums print as their numeric value.
- A top-level variable may share its name with a pattern variable declared later (C# reports CS0128).