Exhaustive Enumeration - related topic

Another more out-of-the-box example that came up in parsing Delphi (and ‘New Delphi’) code that hits near to the idea of exhaustive cases … that I didn’t predict … is compiler directives.

Claude mentioned the cases, in the process of working on a parser for the syntax I want to be able to use. (Work in progress). And then I got it to gather the data for this post.
(Currently only targeting Delphi 13+ .. and might be making some Windows-only assumptions atm.)


A quick note that a ‘total function’ is one that is defined for all inputs.
ie inc( byte ) if values wrap around without error, or writeln( string ) …
vs (x div y) using integers - which is undefined for y=0


We surveyed about 2,000 .pas/.dpr files:

  • the Delphi 13 RTL and VCL (with DUnit and DUnitX),
  • Spring4D
  • DPM
  • Devcpp
  • fluent4d
  • Rudy’s BigNumbers.

They contain 19,443 conditional groups.
About 7,700 of those are for platforms and compilers outside of our consideration (CLR, FPC, GPC, NEXTGEN, TMT Pascal, older Delphi versions) and asm blocks.
That leaves 11,755 live ones.

About half of those are really “if X then A else B”, but more are implied than stated:

  • 20% say so with {$ELSE}.
  • 28–36% are two separate conditionals that the writer means as one choice.
    Some are a flag and its negation ({$IFDEF S} … {$ENDIF} {$IFNDEF S} … {$ENDIF}).
    Others are a pair of platforms side by side:
   {$IFDEF MSWINDOWS} … {$ENDIF}  {$IFDEF POSIX} … {$ENDIF}

That second shape (13% of live conditionals) is assumed to be total, but is not even necessarily mutually exclusive.
Nothing visible enforces that one of them must hold, or that only one can.

An {$ELSEIF} chain without an {$ELSE} is at least exclusive, but may not be total.

The total form exists, and it’s the compile-time twin of the else raise … GetName(EnumVar) described here: Exhaustive Enumeration Case

{$IF Defined(MSWINDOWS)}stdcall{$ELSEIF Defined(POSIX)}cdecl{$ELSE}{$MESSAGE FATAL '…'}{$IFEND};

It’s rare: 139 groups in 18 files.

The rest depend on hidden assumptions about which flags cover the whole design space.
285 two-way choices test a single CPU or bitness symbol and let {$ELSE} mean “the other one”:

  • 74 - {$IFDEF CPUX86} … {$ELSE} …
  • 45 - {$IFDEF CPUX64} … {$ELSE} …
  • 75 - {$IFDEF WIN32} and {$IFDEF WIN64} with an {$ELSE}
  • 91 - CPU64BITS and CPU32BITS

The bitness ones still succeed with Windows on ARM64.
It seems the other nearly 200 send it into an {$ELSE} that was written for x86 or x64 - without any warning.

There are also redundant definitions.
In System.SysUtils, LINUX defines USE_LIBICU, and the chains then test USE_LIBICU before LINUX, so 24 {$ELSEIF defined(LINUX)} arms are redundant.

This is what makes me mention total functions above.
But not all conditional cases are a ‘must decide’ choice. Some are single possible inclusions.

But in other cases, a conditional is a function eg from the platform to code, and it should cover every platform.
And maybe we could do with some new syntax for this.

For example:

  • Declared bounds instead of {$DEFINE} chains:
  {$DEFINE Platform} = (
    Win32,        // WIN32
    Win64,        // WIN64 and CPUX64
    WinARM64,     // MSWINDOWS and CPUARM64
    macOS64,      // MACOS and CPUX64 (not IOS)
    macOSARM64,   // MACOS and CPUARM64 (not IOS)
    iOS64,        // IOS and CPUARM64
    Android32,    // ANDROID and CPUARM32
    Android64,    // ANDROID and CPUARM64
    Linux64       // LINUX and CPUX64 (not ANDROID)
  )

  {$DEFINE UseLibICU} = Platform in [Linux64, Android32, Android64]
  • New syntax for choices. (Just throwing mud at the wall)
function SQLAllocConnect(…): SQLRETURN {$CASE Platform of} {Windows} stdcall {Posix} cdecl {$ENDCASE};


{$DEFINE OSName} = {$CASE Platform of}
                     {Win32, Win64, WinARM64}  'Windows'
                     {macOS64, macOSARM64}     'macOS'
                     {iOS64}                   'iOS'
                     {Android32, Android64}    'Android'
                     {Linux64}                 'Linux'
                   {$ENDCASE};

System.pas currently contains the following, which is pretty thorough but not perfect.

{$IFNDEF PC_MAPPED_EXCEPTIONS}
  {$IF defined(ANDROID)}
    {$DEFINE ZCX_BASED_EXCEPTIONS}
  {$ELSEIF defined(MACOS)}
    {$IF defined(CPUARM32)}
      {$DEFINE SJLJ_BASED_EXCEPTIONS}
    {$ELSEIF defined(CPUARM64)}
      {$DEFINE ZCX_BASED_EXCEPTIONS_MACOS}
    {$ELSEIF defined(CPUX64)}
      {$DEFINE ZCX_BASED_EXCEPTIONS_MACOS}
    {$ENDIF}
  {$ELSEIF defined(MSWINDOWS)}
    {$IFDEF LLVM}
      {$IFDEF MSWINDOWS}
        {$DEFINE ZCX_BASED_EXCEPTIONS_WIN}
      {$ELSE}
        {$DEFINE ZCX_BASED_EXCEPTIONS}
      {$ENDIF}
    {$ELSE}
      {$IF defined(CPUX86)}
        {$DEFINE STACK_BASED_EXCEPTIONS}
      {$ELSEIF defined(CPUX64)}
        {$DEFINE TABLE_BASED_EXCEPTIONS}
      {$ELSE}
        {$MESSAGE ERROR 'Unknown platform'}
      {$ENDIF}
    {$ENDIF !LLVM}
  {$ELSEIF defined(LINUX)}
    {$IFDEF LLVM}
      {$DEFINE ZCX_BASED_EXCEPTIONS}
    {$ELSE}
      {$DEFINE PC_MAPPED_EXCEPTIONS}
    {$ENDIF}
  {$ELSE}
    {$MESSAGE ERROR 'Unknown platform'}
  {$ENDIF}
{$ENDIF !PC_MAPPED_EXCEPTIONS}

From Claude …

  • Inside it, the MACOS chain has no {$ELSE}. A new Mac CPU would silently define no exception model.
  • One branch is impossible. The {$IFDEF MSWINDOWS} inside {$ELSEIF defined(MSWINDOWS)} can never take its {$ELSE} branch.

This eg would be concrete : (dunno if correct 'tho)

{$DEFINE Windows} = Platform in [Win32, Win64, WinARM64]
{$DEFINE Posix}   = not Windows

This is actually pretty nice!

I have always hated those nested ifdef blocks.

Trying to pin down correctness …

//in Abbrevia's AbDfInW.pas, as vendored in GExperts:

function TAbDfInputWindow.FindLongestMatch(…): boolean;
{Note: … There are two versions, one written in Pascal for understanding,
 one in assembler for speed. Activate one and only one of the following
 compiler defines.}
{$IFDEF CPU386}
  {$DEFINE UseGreedyAsm}
{$ELSE}
  {$DEFINE UseGreedyPascal}
{$ENDIF}

{Check to see that all is correct}
{$IFDEF UseGreedyAsm}
  {$IFDEF UseGreedyPascal}
    !! Compile Error: only one of the greedy compiler defines can be used
  {$ENDIF}
{$ELSE}
  {$IFNDEF UseGreedyPascal}
    !! Compile Error: one of the greedy compiler defines must be used
  {$ENDIF}
{$ENDIF}