[fpc-announce] [Feature Announcement] Statement Expressions
Frederic Kehrein
frederic at kehrein.org
Tue Sep 29 00:09:15 CEST 2026
Dear FreePascal Community,
we are pleased to announce a new feature now available on fpc main:
*Statement Expressions*.
Statement Expressions introduces new expression types based of control
flow statements, namely *if-then-else*, *case-of* and *try-except*. All
three are enabled via the `{$ModeSwitch StatementExpressions}`.
**Expressions**
If-Then-Else is the tenary if-operator that is known from other
languages and also available in Delphi now:
```
absValue := if Value < 0 then -Value else Value;
```
Besides this, this MR also extends this concept to the case-of statement:
```
ActiveDuplicates := case StringList.Duplicates of
dupIgnore: 'Does not add duplicates';
dupAccept: 'Adds duplicates as well';
dupError: 'Throws error on duplicate';
end;
```
Similar to the if-then-else the case-of can be used to return a value
based on a conditional. To ensure the return value is always well
defined the case-of must be exhaustive. In the example above this is
given because the case-of covers all 3 possible values from the enum
`TDuplicates`. If that is not the case an `else` or `otherwise` is required:
```
AgeDescription := case Age of
0..1: 'Newborn';
1..3: 'Toddler';
4..12: 'Child';
13..19: 'Teenager';
otherwise 'Adult';
{ Alternative:
else 'Adult';
}
end;
```
Note that unlike the if-then-else statement the case-of requires an
`end` after the expression.
Try-except allows to try to evaluate an expression but if an exception
is raised during the evaluation, a fallback can be returned. The most
generic version without specific exception type handling is:
```
MyValue := try StrToInt(StringInput) except -1 end;
```
As different exceptions can be thrown and it may be desired to get
different results based on the different exception types. In this case
the except on ... syntax can be used:
```
Result := try ReceiveStr(Socket) except
on EConnectionClosedException do 'Connection Closed';
on E: ESocketError do 'Socket Error %d'.Format([E.Code.ToString]);
else 'Unknown Error'
end;
```
Note that here as well an `end` is required.
**Type Promotion**
While these features are relatively self-explainatory, there is one
thing worth noting, the underlying type selection. If-then-else has to
branches with potentially two different types. Case-Of and Try-Except
can have an arbitrary number of branches and types. Requiring all
branches to have the exact same type is not feasible, as it would
already make problems when dealing with something like:
```
var AValue: Double;
...
AValue := if AValue > 0 then AValue else 0;
```
Because here the if-branch is of type Double (AValue) while the
else-branch is of type Integer.
As such fpc tries to find a common type between a process that I like to
call type promotion, as it always tries to promote to the "larger" type:
* If one branch is of type char and the other one is of type string, the
result is promoted to string
* AnsiChar or AnsiString can be promoted to WideChar or WideString
* Shortstrings of different sizes are promoted to the largest size of
any branch
* Objects and Classes are promoted to the most recent common ancestor in
their inheritance tree
* Any two other types are promoted to the type with the "easiest" conversion
Type promotion always occurs between two branches at a time, going from
top to bottom. This can lead to edge cases where the order of the
branches could change the result type of the total expression.
When in doubt, use casts to enforce specific types.
Have fun and enjoy building things :)
More information about the fpc-announce
mailing list