Language Atlas (mini-courses for more languages) Module 4 – Object Pascal with Free Pascal (Delphi-compatible)
Object Pascal setup: FPC, modes and Delphi
Install the FPC 3.2.2 compiler, build a first Object Pascal program, choose objfpc or delphi mode, switch on range checks and see where Delphi fits.
What you will learn
- Install FPC 3.2.2 in a folder you own and compile and run a program from the command line
- Choose a compiler mode for a file, objfpc or delphi, and know what each makes of Integer and string
- Predict which setting wins when fpc.cfg, the command line and the file disagree
- Switch on range and overflow checks and explain what {$Q+} alone misses on a 64-bit CPU
- Explain how Free Pascal, Lazarus and Delphi relate and what the licence asks of your programs
Before you start
What you’ll learn · Before you start
What you will learn
- Install FPC 3.2.2 in a folder you own and compile and run a program from the command line
- Choose a compiler mode for a file, objfpc or delphi, and know what each makes of Integer and string
- Predict which setting wins when fpc.cfg, the command line and the file disagree
- Switch on range and overflow checks and explain what {$Q+} alone misses on a 64-bit CPU
- Explain how Free Pascal, Lazarus and Delphi relate and what the licence asks of your programs
Before you start
On this page
A Free Pascal program that adds 1 to 2,147,483,647 prints −2,147,483,648 without a word, because the checks that would stop it are off until you switch them on, and on a 64-bit Mac the overflow check alone still lets this one through. Object Pascal is the language of Delphi and Lazarus, and Free Pascal 3.2.2 is the open-source compiler that runs every example of this mini-course, here on a Mac with Apple silicon; it is released for Windows and Linux too.
Here you install the compiler in a folder of your own and read what its first build prints. You choose between its two main dialects, objfpc and delphi, predict which setting wins when the configuration file, the command line and the file itself disagree, switch on the checks that stop a mark of 101, and see how Free Pascal, Lazarus and Delphi relate.
One fpc command compiles and links a program
For macOS the Free Pascal download page offers an installer disk image, fpc-3.2.2.intelarm64-macosx.dmg, and the
release’s own folder for Apple silicon, aarch64-darwin, also holds a plain tarball, fpc-3.2.2.aarch64-darwin.tar,
which this mini-course installs. The project’s home page calls 3.2.2 the latest stable version; development versions
are numbered 3.3.x. The tarball holds an installer script, install.sh, which asks where to install. The User’s Guide
points out that the tar file lets you install below your home folder when you have no root rights, and the macOS
download notes add that the compiler needs Apple’s Command Line Tools, whose linker it calls. Step through the whole
path, from the download to a running program, with the two checks that can send you back:
The drawing shows the whole scene; its 9 steps are listed below.
Every step as text
- Download the release. For macOS the download page offers a disk image, and the release folder for Apple silicon also holds the tarball fpc-3.2.2.aarch64-darwin.tar, about 100 MB, with install.sh and the compiler packages inside. This mini-course installs the tarball.
- Check the file. shasum -a 256 prints the SHA-256 of the file. The tarball this lesson's runs used has one that starts 70e93eef; a different hash means a different or damaged file, so download it again before you run anything from it.
- Hash differs. A mismatch sends you back to the download. Run install.sh only from a file whose hash matches.
- Install into a prefix. Unpack the tarball and run ./install.sh from its folder. It asks where to install; a folder you own, such as ~/fpc-3.2.2, needs no administrator rights. Without write access to /etc it writes its configuration to ~/.fpc.cfg.
- Ask for the version. Put the prefix's bin folder on your PATH and ask fpc -iV. It must answer 3.2.2, as it did in the recorded session; fpc -iTP answered aarch64 and fpc -iTO darwin.
- Another fpc first. Any other answer means another Free Pascal comes first on your PATH. Fix the PATH, or call the one in your prefix by its full path, before you compare any output.
- Compile. fpc hello.pas calls the real compiler, ppca64, which reads its options from fpc.cfg and compiles the 13 lines into an object file. The banner, Compiling and Assembling lines are its progress.
- Link. On macOS the compiler hands the object files to Apple's linker from the Command Line Tools. It prints two warnings about options that Free Pascal 3.2.2 still passes; the program is not affected.
- Run. ./hello prints its three lines, including the compiler version and target that the compiler filled in. The sample configuration strips debug information, so the program is 1,053,720 bytes.
The recorded session starts at the fourth stage, with the compiler installed. Here is the program it builds. The
three values between % signs are filled in by the compiler, so the program reports what built it:
program Hello;
{ A first Object Pascal program. The compiler fills in the three %FPC...% values
while it compiles, so the program reports the compiler and the target it was built for. }
{$mode objfpc}{$H+}
uses
SysUtils;
begin
WriteLn(Format('Hello from Free Pascal %s', [{$I %FPCVERSION%}]));
WriteLn(Format('Built for %s on %s', [{$I %FPCTARGETCPU%}, {$I %FPCTARGETOS%}]));
WriteLn(Format('%d + %d = %d', [40, 2, 40 + 2]));
end. Each command of the session follows a $, and the lines below it are what the command printed:
from session import run
# The first session of the Object Pascal mini-course: ask the compiler for its version and target, then build and
# run a first program with the default output, read the size of what was built, see which configuration file the
# compiler read, build once more without the banner and the progress lines, and once with smart linking (-XX).
run("""
fpc -iV
fpc -iTP
fpc -iTO
fpc hello.pas
./hello
wc -c < hello
fpc -vt hello.pas 2>&1 | grep -e 'Configfile search' -e 'Reading options'
fpc -l- -vi- hello.pas
fpc -l- -vi- -XX hello.pas -ohello_small
./hello_small
wc -c < hello_small
""") Output
$ fpc -iV 3.2.2 $ fpc -iTP aarch64 $ fpc -iTO darwin $ fpc hello.pas Free Pascal Compiler version 3.2.2 [2022/10/19] for aarch64 Copyright (c) 1993-2021 by Florian Klaempfl and others Target OS: Darwin for AArch64 Compiling hello.pas Assembling hello Linking hello -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete 13 lines compiled, … sec $ ./hello Hello from Free Pascal 3.2.2 Built for aarch64 on Darwin 40 + 2 = 42 $ wc -c < hello 1053720 $ fpc -vt hello.pas 2>&1 | grep -e 'Configfile search' -e 'Reading options' Configfile search: ~/.fpc.cfg Configfile search: …/etc/fpc.cfg Reading options from file …/etc/fpc.cfg $ fpc -l- -vi- hello.pas -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ fpc -l- -vi- -XX hello.pas -ohello_small ld: warning: option '-no_dead_strip_inits_and_terms' is obsolete, use '-dead_strip' instead -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./hello_small Hello from Free Pascal 3.2.2 Built for aarch64 on Darwin 40 + 2 = 42 $ wc -c < hello_small 223496
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 setup_session.py
Read it from the top. fpc -iV prints the short version, 3.2.2, the toolchain pin: The exact version of the compiler, interpreter or SDK that an example was built and run with, such as Tcl 9.0.4 or the .NET SDK 10.0.401. Another version may print something else for the same file. More in “How the Language Atlas works” of every
example in this mini-course, and -iTP and -iTO print the target processor and system, aarch64 and darwin. The
first build shows the compiler’s banner, the target, one line per step (Compiling, Assembling, Linking) and the
number of lines compiled, 13. The two lines before that count come from Apple’s linker, which objects to two options
that Free Pascal 3.2.2 still passes to it. The program is not affected, and the same two lines follow every build in
this mini-course.
The compiler took most of its options from a configuration file, fpc.cfg. With -vt it lists where it looked:
first ~/.fpc.cfg in your home folder, then …/etc/fpc.cfg, the etc folder above the compiler’s bin folder (… is
the folder you installed into), and it read the second. The User’s Guide gives
the full order on Unix-like systems: the current folder, ~/.fpc.cfg, the folder named by PPC_CONFIG_PATH, the etc
folder above the compiler, then /etc. The first file found is the one used. install.sh writes ~/.fpc.cfg when
it cannot write to /etc.
The program is 1,053,720 bytes, already without debug information: the sample configuration adds -Xs, which strips
it. Units are linked in whole by default, and with -XX, smart linking, only the parts the program calls are kept;
the same program came to 223,496 bytes, about a fifth, and the linker added a third warning of the same kind. The
last builds pass -l- and -vi-, which leave out the banner and the progress lines, so only the linker’s lines
remain. The later lessons of this mini-course build that way.
Repeat the first build
With Free Pascal 3.2.2 installed and its bin folder on your PATH, save the program as hello.pas and type
fpc -iV, then fpc hello.pas, then ./hello. On another version or another platform the banner and the target
lines differ, and the three lines the program prints name what built it.
Checkpoint
Check: Installing Free Pascal and reading a first build
The mode decides which Pascal the compiler reads
Compile a short program with a class in it and no mode directive, and Free Pascal stops at the first line that uses
the word: Identifier not found "class". The same file compiles with -Mobjfpc. Free Pascal reads several dialects
of Pascal, and the compiler mode: The dialect a Free Pascal file is compiled in, chosen with {$mode …} in the file or -M on the command line: fpc (the default), objfpc, delphi, delphiunicode, tp and others. The mode decides, among other things, whether class is a keyword and how big an Integer is. chooses one. In the default, fpc mode, class is not
a keyword, an Integer has 16 bits and a string is a ShortString: Pascal's original string type: at most 255 characters, kept with a length byte in a fixed block of 256 bytes. In Free Pascal the word string means ShortString unless {$H+} is in force. of at most 255
characters; objfpc mode switches on classes, exceptions and interfaces. The session shows the class failing, then one
program in four modes, and one generic record written the objfpc way and the delphi way:
from session import run
# A class without a mode directive, in the default mode and in objfpc mode; one program compiled in four modes;
# the same generic record in objfpc and in delphi syntax; and delphi syntax under {$mode objfpc}, which does not
# compile.
run("""
cd modes
!fpc -l- -vi- no_mode.pas
fpc -l- -vi- -Mobjfpc no_mode.pas
./no_mode
fpc -l- -vi- sizes.pas -osizes_fpc
./sizes_fpc
fpc -l- -vi- -Mobjfpc sizes.pas -osizes_objfpc
./sizes_objfpc
fpc -l- -vi- -Mdelphi sizes.pas -osizes_delphi
./sizes_delphi
fpc -l- -vi- -Mdelphiunicode sizes.pas -osizes_unicode
./sizes_unicode
fpc -l- -vi- pair_objfpc.pas
./pair_objfpc
fpc -l- -vi- pair_delphi.pas
./pair_delphi
!fpc -l- -vi- pair_wrong.pas
""") Output
$ cd modes $ fpc -l- -vi- no_mode.pas no_mode.pas(4,14) Error: Identifier not found "class" no_mode.pas(5,5) Error: Error in type definition no_mode.pas(5,5) Fatal: Syntax error, ";" expected but "identifier NAME" found Fatal: Compilation aborted Error: …/bin/ppca64 returned an error exitcode [exit status 1] $ fpc -l- -vi- -Mobjfpc no_mode.pas -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./no_mode a class, compiled in objfpc mode: Asha $ fpc -l- -vi- sizes.pas -osizes_fpc -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./sizes_fpc SizeOf(Integer) = 2, High(Integer) = 32767 SizeOf(String) = 256, SizeOf(Char) = 1 $ fpc -l- -vi- -Mobjfpc sizes.pas -osizes_objfpc -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./sizes_objfpc SizeOf(Integer) = 4, High(Integer) = 2147483647 SizeOf(String) = 256, SizeOf(Char) = 1 $ fpc -l- -vi- -Mdelphi sizes.pas -osizes_delphi -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./sizes_delphi SizeOf(Integer) = 4, High(Integer) = 2147483647 SizeOf(String) = 8, SizeOf(Char) = 1 $ fpc -l- -vi- -Mdelphiunicode sizes.pas -osizes_unicode -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./sizes_unicode SizeOf(Integer) = 4, High(Integer) = 2147483647 SizeOf(String) = 8, SizeOf(Char) = 2 $ fpc -l- -vi- pair_objfpc.pas -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./pair_objfpc objfpc: Asha 72, Ravi 58 $ fpc -l- -vi- pair_delphi.pas -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./pair_delphi delphi: Asha 72, Ravi 58 $ fpc -l- -vi- pair_wrong.pas pair_wrong.pas(6,8) Fatal: Syntax error, "=" expected but "<" found Fatal: Compilation aborted Error: …/bin/ppca64 returned an error exitcode [exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 modes_session.py
SizeOf(Integer) is 2 in fpc mode and 4 in the others, so High(Integer) jumps from 32,767 to 2,147,483,647.
SizeOf(String) is 256 bytes for a ShortString and 8, the size of a pointer, for the strings the Delphi modes use.
In delphiunicode mode a Char takes 2 bytes, and string is a UnicodeString. The four modes this mini-course
meets:
| Mode | Integer | string |
A generic type |
|---|---|---|---|
| fpc, the default | 16 bits | ShortString | written as in objfpc |
| objfpc | 32 bits | ShortString until {$H+} |
generic and specialize |
| delphi | 32 bits | AnsiString | TPair<T>, no keywords |
| delphiunicode | 32 bits | UnicodeString | TPair<T>, no keywords |
Generics show the difference best. In objfpc mode a generic type is declared with generic and every use names a
type with specialize; the Reference Guide says delphi mode must not use either keyword. The two modes also treat
procedure variables differently: objfpc needs the address operator, @, to assign one, while delphi mode takes the
routine’s plain name, as Delphi code writes it.
objfpc is the default mode of Lazarus source files, and delphi mode is the one for code written for Delphi.
Delphi syntax under {$mode objfpc}
A file that starts with {$mode objfpc} and declares TPair<T> = record does not compile: Free Pascal stopped
with Syntax error, "=" expected but "<" found. Write generic TPair<T> = record and
specialize TPair<Integer>, or start the file with {$mode delphi} if it was written for Delphi.
Which mode a file should start with depends on where its code comes from and where it will live. Answer the three questions and the table marks what fits:
Answer the questions above: the table then marks what fits your case and says why the rest does not.
| Mode | Code written for Delphi | Integer | What string means | New Lazarus units |
|---|---|---|---|---|
| objfpc Selected with: {$mode objfpc}{$H+} | Mixed generics need generic and specialize, and procedure variables need @ | Good 32 bits, as in delphi mode | Mixed ShortString unless {$H+} follows the mode directive | Good the default mode of Lazarus source files |
| delphi Selected with: {$mode delphi} | Good emulates Delphi: TPair<T> without generic or specialize | Good 32 bits | Good AnsiString: the mode implies {$H+} | Mixed compiles in Lazarus, but its units start in objfpc mode |
| delphiunicode Selected with: {$mode delphiunicode} | Good delphi mode with string as a UnicodeString | Good 32 bits | Mixed UnicodeString: SizeOf(Char) is 2 in the recorded run | Mixed compiles, but string differs from the objfpc units around it |
| fpc, the default Selected with: no mode directive | Poor class is not a keyword: no_mode.pas did not compile | Poor 16 bits: High(Integer) is 32767 | Poor ShortString: at most 255 characters | Poor no classes, so no forms or components |
How the answers decide
- “Where does the code come from?” It was written for Delphi: rules out fpc, the default and adds a caveat to objfpc. Delphi code needs classes and 32-bit Integers; under objfpc its generics and procedure variables need edits first.
- “Where does the code come from?” It is new, or written for Free Pascal: rules out fpc, the default. The default fpc mode has no classes and a 16-bit Integer, so new Object Pascal code starts with a mode directive.
- “Must string hold 2-byte UTF-16 characters, as in a Delphi whose string is a UnicodeString?” Yes, the code expects UnicodeString: adds a caveat to objfpc and delphi. Here string is an AnsiString of 1-byte characters; only delphiunicode makes string a UnicodeString.
- “Must string hold 2-byte UTF-16 characters, as in a Delphi whose string is a UnicodeString?” No, 1-byte characters are fine: adds a caveat to delphiunicode. Every string becomes a UnicodeString of 2-byte characters, which code that expects bytes does not want.
- “Will the file live in a Lazarus project next to the units Lazarus creates?” Yes, in a Lazarus project: adds a caveat to delphi and delphiunicode. Lazarus source files use objfpc mode by default; another mode works, but differs from the units around it.
In short: {$mode objfpc}{$H+} for new code and for Lazarus, {$mode delphi} for code written for Delphi, and
{$mode delphiunicode} only when that code expects string to hold 2-byte characters.
Checkpoint
Check: What a compiler mode changes
The last setting wins, and a mode resets {$H}
The program mode_and_r.pas starts with {$mode objfpc} and {$R-}, and it prints the settings it was compiled
with. Built with fpc -Mdelphi -Sh -Cr, it printed objfpc mode, a ShortString and range checks off: none of the three
options survived. The compiler reads its settings in a fixed order, the configuration file first, then the command
line from left to right, then the file’s own compiler directive: An instruction to the compiler written in the source, in braces that start with a dollar sign, such as {$mode objfpc} or {$R+}. It applies from the point where it appears, and it replaces the same setting given in fpc.cfg or on the command line. from top to bottom, and a
later setting replaces an earlier one. Step through that build:
The drawing shows the whole scene; its 8 steps are listed below.
Every step as text
- Start. Before it reads anything, the compiler is in fpc mode, where string is a ShortString, and range checks are off.
- The configuration file. fpc.cfg is read first. The sample configuration sets search paths, the banner and the messages, but no mode and no checks, so nothing changes here.
- -Mdelphi. The command line comes next, left to right. -Mdelphi selects delphi mode, and selecting a mode also sets {$H}: delphi mode switches it on.
- -Sh. -Sh switches {$H} on. It already was, so string is still an AnsiString.
- -Cr. -Cr switches range checks on. After the command line the build would be delphi mode, AnsiString, range checks on.
- The file's mode. Then the file, top to bottom. {$mode objfpc} replaces delphi mode, and like every mode it resets {$H}: objfpc switches it off, so the -Sh from the command line is gone.
- {$R-}. {$R-} switches range checks off, replacing -Cr. A setting in the file always comes after the command line, so the file wins.
- The result. The program printed mode: objfpc, string: ShortString and range checks: off. None of the three command-line options survived.
The surprising step is the sixth: a mode directive also sets {$H}. The Programmer’s Guide says a delphi mode
directive implies {$H+} and all other modes switch it off (delphiunicode, which that page does not name, switched it
on too in sizes.pas), and it draws the conclusion itself:
As a result, you should always put
{$H+}after a mode directive.
The same two lines in the other order show what happens when you do not:
from session import run
# The order of the settings: {$H+} before and after the mode directive, then options on the command line that the
# file's own directives override.
run("""
cd order
fpc -l- -vi- h_before_mode.pas
./h_before_mode
fpc -l- -vi- h_after_mode.pas
./h_after_mode
fpc -l- -vi- -Mdelphi -Sh -Cr mode_and_r.pas
./mode_and_r
""") Output
$ cd order $ fpc -l- -vi- h_before_mode.pas -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./h_before_mode mode: objfpc string: ShortString, at most 255 characters range checks: off $ fpc -l- -vi- h_after_mode.pas -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./h_after_mode mode: objfpc string: AnsiString range checks: off $ fpc -l- -vi- -Mdelphi -Sh -Cr mode_and_r.pas -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./mode_and_r mode: objfpc string: ShortString, at most 255 characters range checks: off
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 order_session.py
h_before_mode.pas asked for {$H+} and got a ShortString, because the mode directive on the next line switched it
off again. h_after_mode.pas got its AnsiString. A mode does not reset range or overflow checks: in the next
section -Cr -Co stay on through the {$mode objfpc} of the file they build. So write the pair in this order,
{$mode objfpc}{$H+}, at the top of every unit and program for objfpc mode.
Key idea
Settings are read in order, fpc.cfg, then the command line, then the file, and the last one wins; a mode directive
resets {$H}, so {$H+} goes after it.
Checkpoint
Check: The order of settings and the {$H} reset
Range and overflow checks are off until you switch them on
marks.pas declares a mark as the subrange 0..100 and stores 101 in it. Built as it is, it printed
stored mark: 101: nothing checks a subrange unless range checking: Code the compiler adds, with {$R+} or -Cr, that stops a program when a value leaves its declared range, such as 101 stored in a 0..100 subrange or an array index past the end. Free Pascal leaves it off unless you switch it on. is on, and the
Programmer’s Guide says it is off by default. Built with -Cr, the command-line form of {$R+}, the same program
stopped with Runtime error 201 and exit status 201, as the guide describes. The overflow program then tries the two
classic overflows under each combination of checks:
from session import run
# Range and overflow checks, off by default: a subrange that accepts 101, then the same overflow program built
# without checks, with -Co ({$Q+}), with -Cr ({$R+}) and with both.
run("""
cd checks
fpc -l- -vi- marks.pas
./marks
fpc -l- -vi- -Cr marks.pas
!./marks
fpc -l- -vi- overflow.pas -ooverflow_none
./overflow_none
fpc -l- -vi- -Co overflow.pas -ooverflow_q
./overflow_q
fpc -l- -vi- -Cr overflow.pas -ooverflow_r
./overflow_r
fpc -l- -vi- -Cr -Co overflow.pas -ooverflow_rq
./overflow_rq
""") Output
$ cd checks $ fpc -l- -vi- marks.pas -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./marks stored mark: 101 $ fpc -l- -vi- -Cr marks.pas -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./marks Runtime error 201 at $… $… [exit status 201] $ fpc -l- -vi- overflow.pas -ooverflow_none -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./overflow_none Integer: 2147483647 + 1 = -2147483648 Int64: High(Int64) + 1 = -9223372036854775808 $ fpc -l- -vi- -Co overflow.pas -ooverflow_q -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./overflow_q Integer: 2147483647 + 1 = -2147483648 Int64: EIntOverflow: Arithmetic overflow $ fpc -l- -vi- -Cr overflow.pas -ooverflow_r -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./overflow_r Integer: ERangeError: Range check error Int64: High(Int64) + 1 = -9223372036854775808 $ fpc -l- -vi- -Cr -Co overflow.pas -ooverflow_rq -macosx_version_min has been renamed to -macos_version_min ld: warning: -multiply_defined is obsolete $ ./overflow_rq Integer: ERangeError: Range check error Int64: EIntOverflow: Arithmetic overflow
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 checks_session.py
The four builds of overflow.pas, side by side:
| Build | High(Integer) + 1 in an Integer |
High(Int64) + 1 |
|---|---|---|
| no checks | −2,147,483,648 | −9,223,372,036,854,775,808 |
-Co, overflow checks |
−2,147,483,648 | EIntOverflow |
-Cr, range checks |
ERangeError | −9,223,372,036,854,775,808 |
-Cr -Co, both |
ERangeError | EIntOverflow |
The second row is the surprise: overflow checking: Code the compiler adds, with {$Q+} or -Co, that stops a program when integer arithmetic overflows. On a 64-bit CPU Free Pascal computes 32-bit expressions in 64 bits, so a 32-bit overflow is caught by range checking instead. alone let the 32-bit overflow
through. The Programmer’s Guide explains why. On 64-bit CPUs integer expressions are computed with 64-bit integers, so
the sum of 2,147,483,647 and 1 is no overflow at all; it only goes wrong when the result is stored in a 32-bit
variable, and that is a range error, which -Cr catches. The Int64 sum overflows the 64 bits themselves, so only
-Co sees it. Programs that use SysUtils turn these run-time errors into exceptions, such as ERangeError and
EIntOverflow, which a handler can catch. A program without SysUtils, like marks.pas, stops with the error’s
number.
Switch both checks on while you learn and wherever a wrong number costs more than a check: put {$R+}{$Q+} after the
mode directive, or build with -Cr -Co. The sample configuration does part of this for you: building with -dDEBUG
selects its debug section, which adds -Crtoi (range, overflow, stack and input-output checks).
Checkpoint
Check: Range and overflow checks on a 64-bit CPU
Free Pascal, Lazarus and Delphi share one language
A unit written in Object Pascal may have been written for one of three tools, and each reads it a little differently. Delphi is Embarcadero’s commercial IDE and compiler; its maker describes it as a way to build native applications for Windows, macOS, iOS, Android and Linux, with the VCL and FireMonkey component libraries. Free Pascal is the open-source compiler; the Lazarus team describes it as designed to understand and compile Delphi syntax, and it runs on many processors and systems. Lazarus is an open-source IDE for Free Pascal: its project page calls it a Delphi compatible cross-platform IDE, and its component library, the LCL, is more or less compatible with Delphi’s VCL. Lazarus 4.8, the release its download button offers, was built with Free Pascal 3.2.2, and with the 3.2.4 release candidate on macOS.
One language, three tools
Text description of the diagram
The diagram is read from top to bottom.
- Object Pascal is the language, with units, classes, exceptions, generics and interfaces.
- Two compilers read it. Delphi, by Embarcadero, is a commercial IDE and compiler with the VCL and FireMonkey component libraries. Free Pascal 3.2.2 is open source: the compiler is under the GPL, and its run-time library (RTL) is under a modified LGPL.
- Lazarus is an open-source IDE for Free Pascal: Free Pascal builds every Lazarus project, and Lazarus adds the LCL, a component library close to Delphi's VCL.
The licences decide what you may do with the programs you build. The Free Pascal FAQ says the compiler is under the GPL, while programs that use the run-time library come under a modified LGPL with an exception that lets you distribute the program on terms of your choice: the licence imposes nothing on the programs you create, closed source included. What it still asks is about the library itself: the RTL’s source must be available to anyone who receives your program, which pointing to the Free Pascal website satisfies if you did not change the RTL, and changes you make to the RTL must be shared.
Where is the language used? Embarcadero sells Delphi for native applications on five systems, and the Lazarus site lists what people build with Lazarus, from file browsers and image viewers to database applications, games and medical analysis software. In the Stack Overflow Developer Survey 2025, 2.5 % of the people who answered its question on programming languages had done extensive development work in Delphi over the past year. Whether you meet the language in a Delphi codebase or a Lazarus project, it is the same language in two dialects, which is why this mini-course writes both.
Organisations named on this page
MySmartCoPilot has no link with Embarcadero, the Free Pascal and Lazarus teams or Stack Overflow. Each is named only as the maker or publisher of a tool, a document or a survey. Every example on this page was built with Free Pascal.
Checkpoint
Check: Free Pascal, Lazarus, Delphi and the licence
Practise: work out which settings a build used
The exercise turns the order of the third section into code: given the options of a build and the directives at the
top of its file, return the mode, what string means, the size of Integer and both checks. Its sample tests replay
this lesson’s own builds, from sizes.pas in four modes to mode_and_r.pas built with -Mdelphi -Sh -Cr.
Exercise · Easy · Python
Work out which settings a Free Pascal build uses
A build script passes options to fpc, and the file starts with directives of its own. Before you trust a build, you want to know which settings it really used. Write effective(options, directives) in settings.py.
options is the list of command-line options in the order they were typed, such as ["-Mdelphi", "-Cr"]. directives is the list of directives at the top of the file, in the order they appear, such as ["{$mode objfpc}", "{$H+}"]. The function returns a dictionary with five keys:
"mode":"fpc","objfpc","delphi","delphiunicode"or"tp", in lower case."string": what the wordstringmeans:"ShortString","AnsiString"or"UnicodeString"."integer_bits":16in the fpc and tp modes,32in the other three."range_checks"and"overflow_checks":TrueorFalse.
The rules, as the lesson's recorded builds showed them:
- Before anything is read, the mode is fpc and
{$H},{$R}and{$Q}are all off. - The options are applied first, from left to right, then the directives, from top to bottom. A later setting replaces an earlier one, so the file's directives win over the command line.
-M<name>and{$mode <name>}set the mode. Setting a mode also sets{$H}: on for delphi and delphiunicode, off for every other mode. Names may be written in any case:-MOBJFPCand{$MODE DELPHI}work.-Shand{$H+}switch{$H}on;-Sh-and{$H-}switch it off. With{$H}off,stringis a ShortString; with it on,stringis a UnicodeString in delphiunicode mode and an AnsiString in every other mode.-Crand{$R+}switch range checks on,-Cr-and{$R-}off;-Coand{$Q+}switch overflow checks on,-Co-and{$Q-}off. A mode leaves both checks as they were.- Any other option, such as
-O2or-l-, changes none of the five settings. The letters of a directive may be in either case:{$h+}is{$H+}.
The sample tests replay the builds of the lesson, from sizes.pas in four modes to mode_and_r.pas built with -Mdelphi -Sh -Cr.
Starter code · settings.py
def effective(options, directives):
"""Return the mode, the meaning of string, the size of Integer and the two checks that a build uses."""
# Replace these lines with your code: apply the options, then the directives.
return {
"mode": "fpc",
"string": "ShortString",
"integer_bits": 16,
"range_checks": False,
"overflow_checks": False,
} The sample tests · test_settings.py
from settings import effective
def test_default_mode():
"""sizes.pas built with no option: fpc mode, a 16-bit Integer and a ShortString"""
assert effective([], []) == {"mode": "fpc", "string": "ShortString", "integer_bits": 16, "range_checks": False, "overflow_checks": False}
def test_modes_on_the_command_line():
"""sizes.pas built with -Mobjfpc, -Mdelphi and -Mdelphiunicode"""
assert effective(["-Mobjfpc"], []) == {"mode": "objfpc", "string": "ShortString", "integer_bits": 32, "range_checks": False, "overflow_checks": False}
assert effective(["-Mdelphi"], [])["string"] == "AnsiString"
assert effective(["-Mdelphiunicode"], [])["string"] == "UnicodeString"
assert effective(["-Mtp"], [])["integer_bits"] == 16
def test_h_before_the_mode_is_lost():
"""h_before_mode.pas: the mode directive switches {$H} off again"""
assert effective([], ["{$H+}", "{$mode objfpc}"])["string"] == "ShortString"
def test_h_after_the_mode_stays():
"""h_after_mode.pas: {$H+} after the mode gives an AnsiString"""
assert effective([], ["{$mode objfpc}", "{$H+}"])["string"] == "AnsiString"
def test_the_file_wins():
"""mode_and_r.pas built with -Mdelphi -Sh -Cr: the file's mode and {$R-} decide"""
assert effective(["-Mdelphi", "-Sh", "-Cr"], ["{$mode objfpc}", "{$R-}"]) == {"mode": "objfpc", "string": "ShortString", "integer_bits": 32, "range_checks": False, "overflow_checks": False}
def test_a_mode_keeps_the_checks():
"""overflow.pas built with -Cr -Co: its {$mode objfpc}{$H+} leaves both checks on"""
assert effective(["-Cr", "-Co"], ["{$mode objfpc}", "{$H+}"]) == {"mode": "objfpc", "string": "AnsiString", "integer_bits": 32, "range_checks": True, "overflow_checks": True}
def test_order_on_the_command_line():
"""-Sh before -Mobjfpc is undone by the mode; after it, it stays; -Sh- after -Mdelphi switches it off"""
assert effective(["-Sh", "-Mobjfpc"], [])["string"] == "ShortString"
assert effective(["-Mobjfpc", "-Sh"], [])["string"] == "AnsiString"
assert effective(["-Mdelphi", "-Sh-"], [])["string"] == "ShortString"
def test_switching_off_and_any_case():
"""-Cr- and {$Q-} switch checks off; names and letters in any case; other options change nothing"""
assert effective(["-Cr", "-Cr-", "-Co"], ["{$Q-}"])["range_checks"] is False
assert effective(["-Co"], ["{$Q-}"])["overflow_checks"] is False
assert effective(["-O2", "-l-", "-MOBJFPC"], ["{$h+}"]) == {"mode": "objfpc", "string": "AnsiString", "integer_bits": 32, "range_checks": False, "overflow_checks": False}
assert effective([], ["{$MODE DELPHI}", "{$H-}"])["string"] == "ShortString" A hint
Keep the four things that change in four variables (mode, {$H}, {$R}, {$Q}) and write one small function that sets a mode, because both -M<name> and {$mode <name>} must also reset {$H}. Read every option, then every directive, and only at the end turn the variables into the five answers.
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Then work through the self-check, five ideas from fpc -iV to the licence. Ideas where your answers have slipped come
up first, and every pick is explained the moment you make it:
Practice that adapts to you
Practise this lesson
16 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher to mid-level): What does {$mode objfpc}{$H+} at the top of a file do, and why in that order?
The first directive selects objfpc mode, the Free Pascal dialect with classes, exceptions and interfaces, in which an
Integer has 32 bits; without a mode directive the compiler is in fpc mode, where class is not even a keyword. The
second makes string an AnsiString, a reference-counted string of any length, instead of a ShortString of at most 255
characters. The order matters because setting a mode also sets {$H}: objfpc switches it off, so a {$H+} written
before {$mode objfpc} is undone, and the file quietly gets ShortStrings. Directives in the file are read after
fpc.cfg and the command line, so they also override -Sh or -M options given there. Write the pair in this order
at the top of every unit and program for objfpc mode.
Two questions of the kind a team asks someone who will maintain Object Pascal code:
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Fresher to senior
A unit that has compiled in Delphi for years fails with Free Pascal 3.2.2 at its first class declaration. How do you get it building with Free Pascal, and how do you keep it building with both compilers afterwards?
-
Mid to senior
A Pascal service stored a percentage of 101 and printed a negative total in production. Its release build had range and overflow checks off. What do you change, how do you show the change works, and what does it cost?
Flashcards · 8 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
A Free Pascal program adds 1 to 2,147,483,647 with its checks left at their defaults. What does it print?
−2,147,483,648, and nothing stops it: range and overflow checks stay off until you switch them on.
-
Which fpc.cfg does the compiler read, and how can you see where it looked?
The first one it finds, on a Mac ~/.fpc.cfg in the home folder before the etc folder above the compiler's bin folder. A build with -vt lists every place it tried.
-
In Free Pascal's default fpc mode, how big is an Integer and what is a string?
An Integer has 16 bits, so High(Integer) is 32,767, and string means ShortString, which holds up to 255 characters. class is not a keyword there: objfpc mode brings classes, exceptions and interfaces.
-
How is a generic record written in objfpc mode, and in delphi mode?
objfpc: generic TPair<T> = record, used as specialize TPair<Integer>. delphi: TPair<T> = record, with neither keyword, as Delphi code writes it.
-
Why must {$H+} come after {$mode objfpc} and not before it?
A mode directive sets {$H} as well, and objfpc turns it off. A {$H+} written first is undone, and string quietly stays a ShortString.
-
A file starts with {$mode objfpc} and {$R-} and is built with fpc -Mdelphi -Sh -Cr. Which settings win?
The file's: objfpc mode, ShortStrings and range checks off. The compiler reads fpc.cfg, then the command line, then the directives in the file, and a later setting replaces an earlier one.
-
On a 64-bit CPU, which check stops High(Integer) + 1 from being stored in an Integer?
Range checking, {$R+} or -Cr. The sum is worked out in 64 bits, where it does not overflow, and only storing it in 32 bits goes wrong, so {$Q+} alone lets it through.
-
How do Delphi, Free Pascal and Lazarus relate?
Delphi is Embarcadero's commercial IDE and compiler. Free Pascal is an open-source compiler that also reads Delphi syntax, and Lazarus is an open-source IDE built on Free Pascal. All three use Object Pascal.
Key takeaways
- Free Pascal 3.2.2 installs from one release tarball into a folder you own, and
fpc -iVmust answer 3.2.2 before any output is compared; on a Mac it links with Apple’s linker, whose two warnings follow every build. - The compiler reads its options from the first configuration file it finds,
~/.fpc.cfgbefore the etc folder above its bin folder, and smart linking with-XXcut the first program from 1,053,720 to 223,496 bytes. - The mode decides the dialect: the default fpc mode has no classes and a 16-bit Integer, objfpc is the mode of new code and Lazarus, and delphi mode accepts Delphi syntax, generics included.
- Settings are read as fpc.cfg, then the command line, then the file, and the last one wins; a mode directive resets
{$H}, so{$H+}goes after it. - Range and overflow checks are off by default; on a 64-bit CPU a 32-bit overflow is caught by
{$R+}, not by{$Q+}alone, so switch on both. - Delphi and Free Pascal both compile Object Pascal, Lazarus is an IDE built on Free Pascal, and the run-time library’s licence leaves your program’s licence to you.
Terms in this lesson
- compiler directive
- An instruction to the compiler written in the source, in braces that start with a dollar sign, such as {$mode objfpc} or {$R+}. It applies from the point where it appears, and it replaces the same setting given in fpc.cfg or on the command line.
- compiler mode
- The dialect a Free Pascal file is compiled in, chosen with {$mode …} in the file or -M on the command line: fpc (the default), objfpc, delphi, delphiunicode, tp and others. The mode decides, among other things, whether class is a keyword and how big an Integer is.
- overflow checking
- Code the compiler adds, with {$Q+} or -Co, that stops a program when integer arithmetic overflows. On a 64-bit CPU Free Pascal computes 32-bit expressions in 64 bits, so a 32-bit overflow is caught by range checking instead.
- range checking
- Code the compiler adds, with {$R+} or -Cr, that stops a program when a value leaves its declared range, such as 101 stored in a 0..100 subrange or an array index past the end. Free Pascal leaves it off unless you switch it on.
- ShortString
- Pascal's original string type: at most 255 characters, kept with a length byte in a fixed block of 256 bytes. In Free Pascal the word string means ShortString unless {$H+} is in force.
References
- Free Pascal: Download (Free Pascal team)
- Free Pascal 3.2.2 for macOS: files and installation notes (Free Pascal team)
- Free Pascal 3.2.2 release files for aarch64-darwin (Free Pascal team)
- Free Pascal home page (current version) (Free Pascal team)
- User's Guide 2.2.3: Installing under Linux (the tar file and install.sh) (Free Pascal team)
- User's Guide 3.1.5: Configuration file (Free Pascal team)
- User's Guide 3.5: Reducing the size of your program (Free Pascal team)
- User's Guide 5.1.1: General options (-i) (Free Pascal team)
- User's Guide 5.1.4: Options controlling the kind of output (-Cr, -Co) (Free Pascal team)
- User's Guide 5.1.5: Options concerning the sources (-M, -Sh) (Free Pascal team)
- Programmer's Guide, appendix D: Compiler modes (Free Pascal team)
- Programmer's Guide D.1: FPC mode (Free Pascal team)
- Programmer's Guide D.3: Delphi mode (Free Pascal team)
- Programmer's Guide D.4: DelphiUnicode mode (Free Pascal team)
- Programmer's Guide D.5: OBJFPC mode (Free Pascal team)
- Programmer's Guide 1.2.25: $H or $LONGSTRINGS (Free Pascal team)
- Programmer's Guide 1.2.64: $Q or $OVERFLOWCHECKS (Free Pascal team)
- Programmer's Guide 1.2.65: $R or $RANGECHECKS (Free Pascal team)
- Reference Guide 8.5.1: Generics in Delphi mode, syntax elements (Free Pascal team)
- Free Pascal FAQ: License and copyright information (Free Pascal team)
- Lazarus home page (releases and their compilers) (Lazarus team)
- About Lazarus (Lazarus team)
- Free Pascal wiki: Mode ObjFPC (Free Pascal team)
- Delphi: product overview (Embarcadero)
- Stack Overflow Developer Survey 2025: Technology (Stack Overflow)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress