Language - amir1387aht/MicroCS GitHub Wiki

Language reference โ€” the MicroCS subset of C#

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 ยท โš ๏ธ supported with a documented difference ยท โŒ not supported

Program structure

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 โŒ

Statements

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)

Expressions

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 @"..." โœ…

ref / out semantics (implementation note)

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 / TryDequeue on a miss store null (no static default(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 (implementation note)

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.

Types and values

  • int, uint, short, byte, sbyte, char, bool, double, float, string, object; all integer types share one representation: 32-bit by default (also in the host mcs build โ€” long.MaxValue prints 2147483647), 64-bit with MCS_INT64=1 (profiles/mcs_profile_linux.h). float and double share one representation: double by default, single with MCS_FLOAT_DOUBLE=0 (lowram profile).
  • Strings are UTF-8 and byte-indexed (s.Length counts bytes; foreach yields code points).
  • LINQ is eager and returns List<T>; GroupBy returns Dictionary<key, List<item>>. The operators work on arrays, List<T>, Dictionary (as KeyValuePairs), HashSet, Stack and Queue; the last four are enumerated from a snapshot. MCS_ENABLE_LINQ=0 removes them.
  • Value-type semantics for struct are not implemented โ€” structs are references.

Deliberate MicroCS behaviour (differs from .NET)

  • StackOverflowException is catchable; out-of-memory ends the run with MCS_ERR_MEMORY instead of killing the process.
  • Integer constant overflow wraps instead of a compile error; checked does not trap. Division is the exception: MinValue / -1 and MinValue % -1 throw OverflowException as in .NET (also for constants, which C# rejects at compile time) โ€” MinValue of the shared integer representation, so with MCS_INT64=1 int.MinValue / -1 is 2147483648.
  • foreach throws Collection was modified after a List<T> change or a Dictionary key addition in the loop body, like .NET 8; removing dictionary entries or Clear is allowed. HashSet, Stack and Queue are enumerated from a snapshot, so changing them does not throw.
  • GroupBy returns a Dictionary<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).
โš ๏ธ **GitHub.com Fallback** โš ๏ธ