[fpc-pascal] Statement Expressions

Mattias Gaertner nc-gaertnma at netcologne.de
Mon Sep 28 20:46:08 CEST 2026


Hi all,

FPC trunk (3.3.1) now supports statement expressions. You can use if, 
case and try..except as expressions that return a value.
Big thanks to Frederic Kehrein who implemented most parts.

Enabling

{$modeswitch StatementExpressions}

The modeswitch is on by default in {$mode delphi} and {$mode 
delphiunicode}. All other modes, including objfpc, need the modeswitch.


if-expression

s := if Count > 0 then 'some' else 'none';

s := if i < 10 then 'small' else
      if i < 100 then 'medium' else
      'large';

DoSomething(if Verbose then LogLevelDebug else LogLevelInfo);

- The else part is required.
- Only the selected branch is evaluated.
- The else branch extends as far to the right as possible, so if b then 
'A' else 'B' + 'C' means if b then 'A' else ('B' + 'C'). To use the 
result in a larger expression, put the if-expression in brackets:

i := (if b then 10 else 20) + 1;


case-expression

s := case Color of
   clRed:   'red';
   clGreen: 'green';
   else     'other'
end;

- It must end with end.
- It must cover every possible value. Either the labels cover the whole 
ordinal range, e.g. all enum values or False/True, or there is an 
else/otherwise branch. If neither holds, you get "Statement expression 
does not handle all cases".
- Ranges (1..9:) and string case labels work as they do in case statements.
- The semicolon before else and before end is optional.


try..except-expression

n := try StrToInt(s) except -1 end;

s := try LoadName(Id)
      except
        on E: EFileNotFound do '<missing>';
        else '<error>'
      end;

- If there are on-do handlers, an else branch is required so that every 
exception produces a value.
- try..finally cannot be used as an expression.


Result type

The branches must be type-compatible. The result has their common type, 
e.g.:

- ShortString + UnicodeString gives UnicodeString.
- Two sibling classes give their common ancestor.
- A class + nil gives that class.
- A string + Variant gives Variant.

Overload resolution works on the result type. For example, Fly(if b then 
1.5 else 2.5) selects the same overload as Fly(1.5).

Incompatible branches give an error, e.g. if b then 'Foo' else 32.


Nesting and anonymous functions

Statement expressions can be nested, and a branch can be an anonymous 
function. The target must be a procedure variable or a reference to type:

{$mode delphi}
var
   Handler: TProc;
begin
   Handler :=
     if Debug then
       procedure begin WriteLn('debug'); end
     else
       procedure begin end;


Enjoy!

Mattias



More information about the fpc-pascal mailing list