Unleashed Pascal is a community-driven fork of Free Pascal that explores modern language features the official compiler chose not to adopt. Upstream is deliberately conservative - which has its value; this fork is where the experimental ideas get to run. Everything turns on with one line:
{$mode unleashed}The mode is based on objfpc; every modeswitch-gated feature can also be enabled individually in objfpc / delphi via {$modeswitch X}. Backward compatibility is the one hard rule: everything is opt-in, and existing Pascal code keeps compiling untouched. The compiler always defines UNLEASHED, so a shared source tree can {$ifdef} its way between stock FPC and Unleashed; the mode itself defines FPC_UNLEASHED (and not FPC_OBJFPC).
Already sold, don't care about the tour? Yeah yeah, just scroll me straight down to the install steps.
A complete program. Every commented line is a separate feature, and all of them ship today.
program taste;
{$mode unleashed}
uses Classes, SysUtils;
// statement expression: if as a value
function fib(n: integer): int64;
begin
result := if n < 2 then n else fib(n-1)+fib(n-2);
end;
// tuple return type
function minMax(const a: array of integer): (lo, hi: integer);
begin
result := (a[0], a[0]);
for var v in a do begin // inline loop variable
if v < result.lo then result.lo := v;
if v > result.hi then result.hi := v;
end;
end;
var hits: integer;
begin
var nums := [34, 7, 23, 32, 5, 62]; // inferred array of LongInt
var (lo, hi) := minMax(nums); // tuple destructuring
writeln($'min = {lo}, max = {hi}'); // string interpolation
var f := async fib(32); // background thread, one keyword
for parallel var i := 1 to 1000 do // loop body across all cores
if fib(15) = 610 then lock(hits) do hits += 1; // zero-setup critical section
writeln($'fib(32) = {await f}, hits = {hits}');
var log := autofree TStringList.Create; // freed at scope exit, no finally
log.Add($'finished at {Now:hh:nn:ss}');
write(log.Text);
{$ifdef WINDOWS}readln;{$endif}
end.Every feature has a full reference page in unleashed/docs/ - the complete grammar, semantics, edge cases, IDE notes, and a runnable demo for each.
Threads in stock Pascal mean TThread subclasses, hand-managed critical sections, and unit-level threadvar. Each collapses into a keyword.
| Feature | One-liner | Docs |
|---|---|---|
async / await |
var f := async work(x); ... writeln(await f); - one thread per async, one join per await |
async-await.md |
for parallel |
for parallel var i := 1 to N do ... - loop body across a worker pool, with a barrier |
parallelfor.md |
lock / trylock |
lock(counter) do inc(counter); - hidden critical section, auto init and release |
lock.md |
threadstatic |
threadstatic next := 1000; - a per-thread variable, init once per thread |
thread-static.md |
| Feature | One-liner | Docs |
|---|---|---|
| Inline Variables | var x := 42; at the point of use, with type inference and block scope |
inline-vars.md |
| Statement Expressions | var s := if x > 0 then 'pos' else 'neg'; - if / case / try yield values |
statement-expressions.md |
match |
match cmd of 'go': run; _: skip; end; - first-match dispatch over strings, tuples, conditions |
match.md |
| Tuples | function pair: (integer, integer); then var (a, b) := pair; |
tuples.md |
| String Interpolation | writeln($'Hello {name}, pi = {pi:%.2f}'); |
string-interpolation.md |
for ... step |
for var i := 1 to 10 step 2 do ... |
forstep.md |
| Multi-Var Init | var a, b, c: integer = 42; - one initializer, independent copies |
multi-var-init.md |
SwapValues() |
SwapValues(a, b); - bitwise swap, no uses, no refcount churn |
swapvalues.md |
Type() |
var tmp: Type(a[0]); - static type of an expression, operand unevaluated |
type-intrinsic.md |
| 128-bit Integers | var x: UInt128 := 340282366920938463463374607431768211455; |
int128.md |
| Feature | One-liner | Docs |
|---|---|---|
| Composable Records | embed TBase;, union ... end;, bitpacked record of byte, OffsetOf(T.field) - C-struct layout, Pascal safety |
composable-records.md |
| Flexible Array Members | data: array[] of byte; as the last field - C99-style variable tail |
flexible-arrays.md |
| Static Variables | static cnt: integer = 0; - program lifetime, block scope |
static-section.md |
| Scoped Cleanup | defer x.Free;, var x := autofree T.Create;, with var http := autofree ... |
autofree.md |
zeroinit |
procedure foo; zeroinit; - zero every local at entry |
zeroinit.md |
| Aligned Heap | GetMemAligned(size, 64) and friends in system |
introduced-functions.md |
| Feature | One-liner | Docs |
|---|---|---|
| Auto-Properties | property Name: string; synthesizes a backing field - zero overhead |
auto-properties.md |
| Nested Generic Methods | function pair<U>(...) inside TBox<T> - class and method specialize independently |
extra-improvements.md |
| Implicit Generics + Type Helpers | TList<integer> without generic / specialize; type helper for integer; multiple helpers at once |
extra-improvements.md |
| Feature | One-liner | Docs |
|---|---|---|
| Strip RTTI | {$modeswitch striprtti} empties type-name strings; expose / --rttiexpose= whitelist |
strip-rtti.md |
| Custom Binary Metadata | fpc --fpcsignature="" --linkerversion=14.39 --osversion=Win11 |
binary-metadata.md |
| File Embedding | {$embedbytes preset 'default.bin'} bakes a file into the binary |
embed.md |
| Feature | One-liner | Docs |
|---|---|---|
| Multiline Strings | backtick `...` (byte-exact) and triple-quote '''...''' (auto-trimmed) |
multiline-strings.md |
| Numeric Underscores | 1_000_000, $FFFFFFFF_FFFFFFFF |
extra-improvements.md |
| String-to-Ordinal Cast | dword('RIFF') folds to a constant in native endianness |
extra-improvements.md |
Semantic adjustments and small syntax unlocks - full catalog in tweaks.md and extra-improvements.md.
- Preserved For-Loop Counter - after
for i := 1 to N do ... break;the counter keeps its value. is not/not in-if obj is not TFoo then,if x not in [a, b] then.- Compound Assignment -
i div= 2;,flags xor= $05;in every mode;+=and friends without{$coperators on}; both work on properties, as doinc()/dec(). - Array Size Shorthand -
array[10] of Tmeansarray[0..9] of T. - Array Equality -
if a = b thencompares two arrays element by element. - Indexed and Lazy Labels -
label state[0..4]; goto state[n];compiles to a case dispatch;goto done;needs no prior declaration. goto, Macros, C-Operators without Directives - all implied by the mode.DEBUG/RELEASEDefines - set automatically from-g: react to a debug build with{$ifdef DEBUG}, no-dDEBUGneeded.
Each feature has a dedicated reference page with the full grammar, semantics, edge cases, IDE notes, and a runnable demo. Start at the index: unleashed/docs/README.md.
Self-contained GUI installer for Windows and Linux: it downloads sources, builds the compiler and the Lazarus IDE into a directory of your choice, optionally installs cross compilers, and drops a desktop shortcut. No PATH changes, no registry side effects, no overwriting of an existing FPC. Run it again any time to add a cross target or pull an update - it applies only that change and leaves the rest of the install alone.
- Repo: unleashedpascal/installer
- Downloads: installer/releases - tagged stable releases plus a rolling
nightlythat tracksmaininstaller_win64_x86_64.exe- Windows hostinstaller_linux_x86_64/installer_linux_x86_64.AppImage- Linux host
Run it, pick a directory (default C:\unleashed on Windows, $HOME/unleashed on Linux), tick the cross targets you want, click Install.
- Download fpcupdeluxe and run it once to generate
fpcup.ini. - Add to
fpcup.ini:
[ALIASfpcURL]
unleashed.git=https://github.com/unleashedpascal/compiler.git
[ALIASlazURL]
unleashed.git=https://github.com/unleashedpascal/ide.git- Reopen fpcupdeluxe, uncheck GitLab, and select
unleashed.gitas both the FPC version and the Lazarus version. - In Setup+ tick Docked Lazarus IDE (required - the default window layout is tuned for docked mode).
- Click Install/update FPC+Lazarus. Cross-compilers are on the
Crosstab.
Delete or rename the fpcsrc folder in your installation directory, then:
git clone https://github.com/unleashedpascal/compiler.git fpcsrcIn fpcupdeluxe: Setup+, check FPC/Laz rebuild only, confirm, then click Only FPC.
For the best experience, use Unleashed Pascal IDE - a fork of the Lazarus IDE that understands unleashed mode end to end. Its parser, code completion, and Code Insight know the new syntax, so inline vars, tuples, match, async / await, and the rest autocomplete and navigate like any built-in construct, with no false-positive error markers in the editor.
Stock Lazarus still builds and runs Unleashed projects fine, but its Code Insight does not recognize the new syntax and will flag it as errors. If you stay on stock Lazarus, enable the mode through -Munleashed in the project's Custom Options rather than {$mode unleashed} in the source - that keeps the editor's background parser quieter about the constructs it cannot model.
Unleashed Pascal is a home for innovation. If you have built a language feature that did not find a home upstream, this is where it belongs.
We are looking for new language ideas - propose modeswitches, syntax extensions, or compiler enhancements via GitHub Issues or Discussions; a well-described idea with clear use cases is valuable even without an implementation - and complete, high-quality implementations with production-grade code, test coverage, and documentation.
We are not looking for minor convenience patches, trivial reformats, or tweaks that only scratch a personal itch. Every change to a compiler carries weight; contributed code should be a meaningful feature or fix that benefits the broader community.
