Appendix E: Free Pascal vs. Delphi Dialect Differences

Free Pascal (FPC) and Delphi are the two major Object Pascal compilers in active use today. While they share a common heritage and a great deal of syntax, they diverge in important ways. This appendix provides a systematic comparison so you can read Delphi code, port projects between the two compilers, and understand the implications of FPC's {$mode delphi} compatibility switch.


E.1 Compiler Modes in Free Pascal

Free Pascal supports multiple syntax modes, selected by a compiler directive at the top of each source file:

Mode Directive Description
ObjFPC {$mode objfpc} Free Pascal's native mode. Recommended for new FPC projects. Supports all FPC features including its native generics syntax.
Delphi {$mode delphi} Delphi compatibility mode. Accepts most Delphi syntax including Delphi-style generics, anonymous methods (FPC 3.3+), and String as AnsiString by default.
FPC {$mode fpc} Traditional Free Pascal mode (pre-ObjFPC). Rarely used for new code.
TP {$mode tp} Turbo Pascal 7 compatibility mode. Useful for compiling vintage TP code.
MacPas {$mode macpas} Macintosh Pascal compatibility. Very rarely used.

Most code in this textbook uses {$mode objfpc}`. When porting Delphi examples, switch to `{$mode delphi} or translate the syntax differences described below.


E.2 Syntax Differences

Procedure Variable Assignment

In ObjFPC mode, assigning a procedure to a procedure variable requires the @ operator. Delphi mode does not.

{ ObjFPC mode }
type
  TProc = procedure(X: Integer);
var
  P: TProc;
begin
  P := @MyProcedure;      { @ required }
  P(42);
end;

{ Delphi mode }
type
  TProc = procedure(X: Integer);
var
  P: TProc;
begin
  P := MyProcedure;        { No @ needed }
  P(42);
end;

Method Pointers and @ Operator

The same distinction applies to method pointers. ObjFPC requires @; Delphi mode does not.

{ ObjFPC }
Button1.OnClick := @HandleClick;

{ Delphi }
Button1.OnClick := HandleClick;

Result Variable

Both modes support the Result variable in functions. ObjFPC also supports assigning to the function name (Turbo Pascal style):

function Add(A, B: Integer): Integer;
begin
  Result := A + B;     { Both modes }
  Add := A + B;        { Also valid, TP-compatible }
end;

Pointer Dereferencing

Both modes use ^ for dereferencing, but ObjFPC is stricter about requiring explicit dereferencing:

type
  PRec = ^TRec;
  TRec = record
    Value: Integer;
  end;

var
  P: PRec;
begin
  { Both modes }
  New(P);
  P^.Value := 42;

  { Delphi mode also allows (auto-dereference): }
  P.Value := 42;          { Implicit dereference in Delphi mode }

  { ObjFPC requires the explicit ^ }
  P^.Value := 42;
end;

String Type Default

Mode String resolves to Override
ObjFPC with {$H+}` | `AnsiString` | `{$H-} for ShortString
ObjFPC with {$H-}` | `ShortString` | `{$H+} for AnsiString
Delphi AnsiString (always, {$H+} assumed) String[N] for ShortString

Modern Delphi (2009+) defaults String to UnicodeString. FPC in {$mode delphi}` still defaults to `AnsiString`. You can switch with `{$ModeSwitch UnicodeStrings} in FPC trunk.


E.3 Generics

This is one of the largest syntax differences between FPC's ObjFPC mode and Delphi.

Declaration

{ ObjFPC mode }
type
  generic TList<T> = class
    procedure Add(const Item: T);
  end;

{ Delphi mode }
type
  TList<T> = class
    procedure Add(const Item: T);
  end;

Note: ObjFPC requires the generic keyword before the type name.

Implementation

{ ObjFPC mode }
procedure TList.Add(const Item: T);
begin
  { ... }
end;

{ Delphi mode }
procedure TList<T>.Add(const Item: T);
begin
  { ... }
end;

In ObjFPC mode, the implementation uses the unparameterized name (TList). In Delphi mode, it includes the type parameter (TList<T>).

Specialization / Instantiation

{ ObjFPC mode }
type
  TIntList = specialize TList<Integer>;
var
  List: specialize TList<String>;

{ Delphi mode }
type
  TIntList = TList<Integer>;
var
  List: TList<String>;

ObjFPC requires the specialize keyword whenever a generic type is instantiated. Delphi mode uses the angle brackets directly.

Generic Functions and Methods

{ ObjFPC mode }
generic function Max<T>(A, B: T): T;
begin
  if A > B then Result := A else Result := B;
end;

{ Calling: }
X := specialize Max<Integer>(3, 7);

{ Delphi mode }
function Max<T>(A, B: T): T;
begin
  if A > B then Result := A else Result := B;
end;

{ Calling: }
X := Max<Integer>(3, 7);

Generic Constraints

Both modes support constraints, with slightly different syntax in older FPC versions. Modern FPC (3.2+) supports constraints in both modes:

{ Both modes (FPC 3.2+) }
type
  generic TComparer<T: class> = class
    function Compare(A, B: T): Integer;
  end;

Delphi supports additional constraint types (record, constructor) that FPC also implements in {$mode delphi}.


E.4 Anonymous Methods and Closures

Delphi

Delphi has supported anonymous methods since Delphi 2009:

{ Delphi }
type
  TFunc = reference to function(X: Integer): Integer;

var
  F: TFunc;
begin
  F := function(X: Integer): Integer
       begin
         Result := X * X;
       end;
  WriteLn(F(5));    { 25 }
end;

Free Pascal

FPC added anonymous function support in the development branch (3.3.1+). In FPC 3.2.x (stable), anonymous methods are not available. The workaround is to use nested procedures with the is nested modifier:

{ FPC 3.2.x workaround }
type
  TFunc = function(X: Integer): Integer is nested;

procedure Demo;
var
  F: TFunc;

  function Square(X: Integer): Integer;
  begin
    Result := X * X;
  end;

begin
  F := @Square;
  WriteLn(F(5));    { 25 }
end;

In FPC trunk ({$mode delphi}` or `{$ModeSwitch AnonymousFunctions}), the Delphi syntax works:

{$ModeSwitch AnonymousFunctions}
{$ModeSwitch FunctionReferences}
{$ModeSwitch AdvancedRecords}

type
  TFunc = reference to function(X: Integer): Integer;

E.5 String Handling Differences

Feature Delphi (Modern) FPC (ObjFPC, {$H+})
Default String type UnicodeString (UTF-16) AnsiString (UTF-8 capable)
String indexing 0-based optional ({$ZEROBASEDSTRINGS}) Always 1-based
Char type WideChar (2 bytes) AnsiChar (1 byte)
PChar PWideChar PAnsiChar
String helpers S.Length, S.ToUpper, etc. Available with {$ModeSwitch TypeHelpers}

String Helper Methods

Modern Delphi provides methods directly on the String type:

{ Delphi }
var
  S: String;
begin
  S := 'Hello World';
  WriteLn(S.Length);          { 11 }
  WriteLn(S.ToUpper);        { HELLO WORLD }
  WriteLn(S.Contains('World'));  { True }
  WriteLn(S.Replace('World', 'Pascal'));
end;

FPC supports type helpers that provide similar functionality. The SysUtils unit in FPC 3.2+ includes TStringHelper:

{ FPC with type helpers }
{$mode objfpc}{$H+}
{$ModeSwitch TypeHelpers}

uses
  SysUtils;

var
  S: String;
begin
  S := 'Hello World';
  WriteLn(S.Length);           { Works in FPC 3.2+ with TypeHelpers }
end;

However, the set of available methods may differ. Check the FPC documentation for the exact methods available on TStringHelper.


E.6 Unit and Package Differences

Unit Naming

Feature Delphi FPC
Unit file extension .pas .pas or .pp
Case sensitivity Case-insensitive (Windows) Case-insensitive on Windows, case-sensitive on Linux
Namespaced units System.SysUtils, Vcl.Forms Not natively supported; use flat names

Key Unit Name Differences

Delphi Unit FPC Equivalent Notes
System.SysUtils SysUtils FPC does not use dotted unit names
System.Classes Classes
System.Generics.Collections Generics.Collections or fgl FPC has its own generic containers
Vcl.Forms Forms Lazarus LCL equivalent
Vcl.Controls Controls
Vcl.StdCtrls StdCtrls
Vcl.Dialogs Dialogs
Vcl.Graphics Graphics
Data.DB DB
FireDAC.* SQLDB / SQLite3Conn Different database frameworks

Package System

Delphi uses .dpk packages. FPC/Lazarus uses .lpk packages. They are not interchangeable, but many Delphi packages have been ported to Lazarus or have Lazarus equivalents.

Resource Files

Feature Delphi FPC/Lazarus
Form files .dfm (binary or text) .lfm (text)
Resource compiler brcc32 windres (Windows) or lazres
Resource include {$R *.dfm}` | `{$R *.lfm}

Lazarus can import Delphi .dfm files, but some property names and component types differ.


E.7 Compiler Directives

Shared Directives

Most compiler directives work identically in both compilers:

Directive Meaning
{$R+}/{$R-} Range checking
{$Q+}/{$Q-} Overflow checking
{$I+}/{$I-} I/O checking
{$H+}/{$H-} Long strings (AnsiString/ShortString)
{$B+}/{$B-} Boolean evaluation
{$IFDEF}/{$ENDIF} Conditional compilation
{$WARNINGS OFF/ON} Suppress warnings
{$HINTS OFF/ON} Suppress hints

FPC-Only Directives

Directive Purpose
{$mode objfpc} Set ObjFPC syntax mode
{$mode delphi} Set Delphi compatibility mode
{$ModeSwitch X} Enable specific language features
{$INTERFACES CORBA} Use CORBA-style interfaces (no ref counting)
{$INLINE ON} Enable inline function expansion
{$OPTIMIZATION ON} Enable optimizations
{$CODEPAGE UTF8} Set source file code page
{$CALLING REGISTER} Set default calling convention

Delphi-Only Directives

Directive Purpose
{$LEGACYIFEND ON}` | Allow `{$IFEND} without matching {$IF}
{$ZEROBASEDSTRINGS ON} 0-based string indexing
{$STRONGLINKTYPES ON} Force linking of RTTI types
{$LIBPREFIX}`, `{$LIBSUFFIX} Library naming

Predefined Symbols for Conditional Compilation

Symbol Defined When
FPC Compiling with Free Pascal
FPC_VERSION FPC major version number
DELPHI Compiling with Delphi (not defined in FPC even in {$mode delphi})
MSWINDOWS / WINDOWS Target is Windows
LINUX Target is Linux
DARWIN Target is macOS
UNIX Target is any Unix-like OS
CPU64 64-bit target
CPU32 32-bit target
CPUX86_64 / CPUAMD64 x86-64 target
CPUAARCH64 ARM64 target
LCL Lazarus component library is available

Use these to write code that compiles on both FPC and Delphi:

{$IFDEF FPC}
  {$mode delphi}{$H+}
{$ENDIF}

uses
  {$IFDEF FPC}
  LCLType,
  {$ENDIF}
  {$IFDEF MSWINDOWS}
  Windows,
  {$ENDIF}
  SysUtils, Classes;

E.8 Feature Availability Comparison

Feature Delphi FPC (ObjFPC) FPC (Delphi mode)
Classes and inheritance Yes Yes Yes
Interfaces (COM) Yes Yes Yes
Interfaces (CORBA) No Yes Yes
Generics Yes Yes (different syntax) Yes (Delphi syntax)
Anonymous methods Yes (2009+) Trunk only Trunk only
Advanced records Yes (2006+) Yes (FPC 2.6+) Yes
Operator overloading Yes Yes (different syntax) Yes
Inline functions Yes Yes Yes
Class helpers Yes (2005+) Yes Yes
Attributes (RTTI annotations) Yes (2010+) Partial (FPC 3.2+) Partial
Dynamic arrays Yes Yes Yes
for..in loops Yes (2005+) Yes Yes
Multi-platform targets Windows, macOS, Linux, iOS, Android 20+ OS targets, 15+ CPU architectures Same
GUI framework VCL (Windows), FMX (cross-platform) LCL (Lazarus, cross-platform) LCL
64-bit support Yes Yes Yes
WebAssembly target Via FMX (limited) FPC trunk FPC trunk

E.9 Operator Overloading

Operator overloading syntax differs between the two modes:

{ ObjFPC mode — uses 'operator' keyword at unit level }
type
  TVector = record
    X, Y: Double;
  end;

operator + (A, B: TVector): TVector;
begin
  Result.X := A.X + B.X;
  Result.Y := A.Y + B.Y;
end;

{ Delphi mode — uses 'class operator' inside the record }
type
  TVector = record
    X, Y: Double;
    class operator Add(A, B: TVector): TVector;
  end;

class operator TVector.Add(A, B: TVector): TVector;
begin
  Result.X := A.X + B.X;
  Result.Y := A.Y + B.Y;
end;

E.10 Porting Delphi Code to Free Pascal

When you encounter Delphi code that you want to compile with FPC, follow this checklist:

  1. Add {$mode delphi}{$H+} at the top of each unit. This is the fastest way to get Delphi code compiling in FPC.

  2. Replace VCL unit names with LCL equivalents (see the unit name table above). Remove the Vcl., System., Data. prefixes.

  3. Check for anonymous methods. If the code uses reference to function types, you may need to refactor to regular function types or wait for FPC trunk support.

  4. Check for String assumptions. If the Delphi code assumes String is UnicodeString (2-byte characters), you may need to adjust for FPC's 1-byte AnsiString.

  5. Replace {$IFDEF DELPHI}` with `{$IFDEF FPC}. Delphi defines DELPHI but not FPC; FPC defines FPC but not DELPHI.

  6. Check database components. Replace FireDAC with SQLDB or ZeosLib.

  7. Check third-party libraries. Many popular Delphi libraries (Indy, Synapse, mORMot, Brook) have FPC support. Some (DevExpress, TMS) are Delphi-only.

  8. Test on the target platform. Delphi code often has Windows-specific assumptions (Windows unit, registry access, COM objects). Replace with cross-platform equivalents.


E.11 Porting Free Pascal Code to Delphi

Going the other direction is usually easier because FPC code tends to be more portable. Key changes:

  1. Remove {$mode objfpc} and FPC-specific directives. Delphi ignores unknown directives with a warning, but it is cleaner to remove them.

  2. Remove @ from procedure variable assignments.

  3. Translate generics: Remove generic and specialize keywords, use Delphi's angle-bracket syntax.

  4. Replace {$IFDEF FPC}` with `{$IFDEF DELPHI} or use {$IFNDEF FPC}.

  5. Replace LCL components with VCL/FMX equivalents.

  6. Replace SQLDB with FireDAC or your preferred Delphi database library.


Understanding these differences allows you to work confidently with code from either ecosystem and to choose the right mode and syntax for your projects.