Types
Builtin types, aliases, choices, arrays, pointers, sets, routine types, C++ types, records and overlays. Objects, interfaces and owners have their own page: Objects.
Builtin types
| Type | Size |
|---|---|
int8 int16 int32 int64 | 1, 2, 4, 8 bytes |
uint8 uint16 uint32 uint64 | 1, 2, 4, 8 bytes |
float32 float64 | 4, 8 bytes |
bool char | 1 byte |
wchar | the target's wchar_t: 2 bytes on x86_64-windows-gnu, 4 on x86_64-linux-gnu |
string wstring | text |
ptr | the untyped pointer, 8 bytes; ptr to T points to a T |
varargs | only the type of a variadic routine's local holding varargs.copy() |
bool and char are also C++ fundamentals, and both spellings are one C++ type, so plain C++ functions take them as they are. pointer and boolean are not keywords: a C typedef such as Windows' boolean is an ordinary name.
A builtin takes no C++ suffix or qualifier: int32*, int32&, string&&, int32 const, const int32 and volatile float64 are PR0027 (for int32* the message says write ptr to int32). ptr to T, a var / const parameter or the C++ type (int32_t) says it instead.
Aliases
TName = <type expr>; gives a type a second name, and it IS that type: values move between the two spellings freely, across var parameters too. Every type form may be aliased, and an alias works as a cast (TByte(300) with TByte = uint8 gives 44), a parameter type and a return type.
- A type that names itself (
TA = TA,ptr to TListwith no forward declaration,array of TGrid) is SM0003. - An alias of a variable, a constant, a routine, a module or a choice value is SM0013 (
'X' is not a type). - A name PaskalCPP does not know, in a type position, is taken as a C/C++ type, so clang reports it.
Choices
TColor = choices(cRed, cGreen, cBlue); names a list of values. Each is reached through its type, TColor.cRed; a bare value is SM0015 (write TColor.cRed), a name the list lacks SM0010.
module exe types_choices;
type
TColor = choices(cRed, cGreen, cBlue);
// An explicit value; the implicit ones after it count on
TLevel = choices(lLow = -1, lMid, lHigh = 10, lTop);
var
LC: TColor = TColor.cGreen;
begin
println("{} {} {}", TColor.cRed, LC, TColor.cBlue);
println("numbers {} {} {}", int32(TColor.cRed), int32(LC), int32(TColor.cBlue));
println("lLow = {}, lHigh = {}, lTop = {}", int32(TLevel.lLow), int32(TLevel.lHigh), int32(TLevel.lTop));
// (T)N makes a value from a number; one outside the list prints as TColor(N)
println("cast {} {}", (TColor)2, (TColor)7);
println("cGreen is after cRed: {}", LC > TColor.cRed);
end.
cRed cGreen cBlue
numbers 0 1 2
lLow = -1, lHigh = 10, lTop = 11
cast cBlue TColor(7)
cGreen is after cRed: true
[Heap] Allocs: 0, Frees: 0, Leaked: 0
- Values are numbered from 0. An explicit
= valuesets one and the implicit ones after it count on; inside its own list an earlier value is named bare and is its number (fB = fA + 1). Two values may share a number (the first one prints); a value past int32 makes the type 8 bytes. - An explicit value is a PaskalCPP integer constant: a variable or C++ (
cpp("5"), a C macro) is SM0202, a float SM0200. An empty list is PR0035, a name used twice SM0004. - print shows a value by its name, and a number outside the list as
TColor(7). - A value is no number and a number no value: both directions are SM0200.
int32(V)/int64(V)give the number and(TColor)Nmakes a value, both constants when N is. - A value has no arithmetic: only a comparison and
intake one;+,+=,++and the like are SM0250. - A choices bitfield must hold every number of the type as a signed value (SM0316), and holds its values exactly on every target.
Arrays
array[lo..hi] of T is a static array; array of T (or array[] of T) is dynamic, sized by setlength, and new elements start zeroed.
module exe types_arrays;
type
TQuad = array[0..3] of int32;
var
LA: TQuad;
LB: TQuad;
LL: array['A'..'C'] of int32;
LO: array[-2..2] of int32;
LD: array of int32;
begin
LA[0] := 1;
LA[1] := 2;
LA[2] := 3;
LA[3] := 4;
// A static array is a value: := copies it
LB := LA;
LB[1] := 9;
println("LA {} LB {}", LA, LB);
println("equal {} len {} size {}", LA = LB, len(LA), size(TQuad));
// A char-bounded array is indexed by a char
LL['B'] := 5;
println("letters {} {}", LL['B'], LL);
LO[-2] := 7;
println("offset {} len {}", LO[-2], len(LO));
// New elements of a dynamic array start zeroed
setlength(LD, 3);
LD[2] := 8;
println("dynamic {} len {}", LD, len(LD));
end.
LA [1, 2, 3, 4] LB [1, 9, 3, 4]
equal false len 4 size 16
letters 5 [0, 5, 0]
offset 7 len 5
dynamic [0, 0, 8] len 3
[Heap] Allocs: 0, Frees: 0, Leaked: 0
- Static bounds are constants (a variable is SM0202) of one family, integer, char or one choices type (otherwise SM0322), low not above high (SM0328). A static array names both bounds:
array[5]is PR0001. - A low bound may be negative or other than 0. An index takes the bounds' family: an integer, a char for a char-bounded array, a value of the type for a choices-bounded one (else SM0261). A constant index outside the bounds is SM0276.
lengives the element count,sizethe bytes, and print shows every element ([1, 2, 3]).- A static array is a value:
:=copies it, a value parameter and a return value are copies, and=compares element by element. Avarparameter writes the caller's array, aconstone reads it. - Two static arrays of other bounds or other element types are other types, and a dynamic array is no static one (SM0200).
- Arrays nest (
array[0..1] of array[0..2] of int32, indexedLG[1][2]) and may be record fields. - No type holds more than 1152921504606846975 bytes on any target: a static array past it is SM0277.
Pointers
ptr to T, ptr to const T (what it points to is read only through it: a write is SM0255) and the untyped ptr.
module exe types_pointers;
type
TPoint = record
X: int32;
Y: int32;
end;
var
GN: int32 = 10;
GPoint: TPoint;
GList: array[0..2] of int32;
LP: ptr to int32;
LC: ptr to const int32;
LU: ptr;
LR: ptr to TPoint;
LQ: ptr to int32;
begin
LP := address of GN;
print("deref {}", LP^);
LP^ := LP^ + 1;
println(", after write {}", GN);
LR := address of GPoint;
LR^.X := 5;
LR->Y := 6;
println("record {} {}", GPoint.X, GPoint.Y);
// A ptr to T converts to a ptr to const T and to the untyped ptr
LC := LP;
println("const read {}", LC^);
LU := LP;
LQ := ptr to int32(LU);
println("round trip {} same {}", LQ^, LQ = LP);
GList[0] := 10;
GList[1] := 20;
GList[2] := 30;
LQ := address of GList[0];
print("index {}", LQ[2]);
LQ := LQ + 1;
println(", moved {}", LQ^);
LQ := nil;
println("nil {}", LQ = nil);
end.
deref 10, after write 11
record 5 6
const read 11
round trip 11 same true
index 30, moved 20
nil true
[Heap] Allocs: 0, Frees: 0, Leaked: 0
address of Xgives a pointer to X;P^reads and writes what P points to;P^.FandP->Freach a field;P[i]indexes a typed pointer andP + nmoves it; pointers nest (LPP^^).^and->need a pointer (SM0250 on a value), and.on a pointer reaches no field (SM0250: writeP^.XorP->X). The untypedptrpoints at no type:P^andP[i]on it are SM0253 (cast it to a typed pointer first).- A
ptr to Tconverts to aptr to const Tand to the untypedptr; nil converts to every pointer. Nothing else converts implicitly (SM0200, with the cast named as the hint): not to a pointer to another type, not const away, not 0 (write nil), not to an integer. - The pointer cast is
ptr to T(x): it takes a pointer that keeps const, or an integer of pointer width (uint64);uint64(P)goes the other way. A cast that drops const, a float, a narrower integer or nil is SM0265. - A
ptr to const charholds a string literal, and print shows its text.
Sets
set of lo..hi (a range of constants of one family: integer, char or one choices type) or set of T (an element type: a choices type, char or bool). A set type is declared in a type block and used by its name; a bare set is PR0053, an inline one PR0026.
module exe types_sets;
type
TColor = choices(cRed, cGreen, cBlue);
TSmall = set of 0..15;
TColors = set of TColor;
var
LS: TSmall;
LT: TSmall;
LC: TColors;
LN: int32 = 9;
begin
// A literal lists values and ranges
LS := [1, 3..5, LN];
println("literal {}", LS);
println("in {} {}", 4 in LS, 2 in LS);
LT := [2, 3..5];
println("union {} difference {} intersection {}", LS + LT, LS - LT, LS * LT);
// <= is subset, >= superset
println("subset {} equal {}", [3, 4] <= LS, LS = LT);
LC := [TColor.cRed];
LC += [TColor.cBlue];
println("colors {}", LC);
end.
literal [1, 3, 4, 5, 9]
in true false
union [1, 2, 3, 4, 5, 9] difference [1, 9] intersection [3, 4, 5]
subset true equal false
colors [cRed, cBlue]
[Heap] Allocs: 0, Frees: 0, Leaked: 0
- A range's bounds are of one family (SM0320), low not above high (SM0328), with at most 65536 members (SM0321). An integer type as the element names its range instead (SM0318:
set of 0..255);set of wcharis SM0319, since its size differs by target (writeset of 0..65535). - A set literal lists values and ranges (
[1, 3..5, LN]);[]is empty. Its elements are of the range's family, and a constant one lies inside the range (SM0200). Two different set types do not convert. intests a member;+ - *and+= -= *=are union, difference and intersection;= <>compare,>= <=are superset and subset;/and/=are SM0250.- A set prints its members in order; the widest set (
set of 0..65535) is 8192 bytes.
Routine types
routine[(params)][: T] is a type expression, written wherever a type goes, named or inline: a field, an array element, a variable, a parameter, a return type, ptr to. routine clink(...) is the C linkage type, a type of its own (a cpplink routine never fills it); no linkage word means cpplink.
module exe types_routines;
type
TUnary = routine(AN: int32): int32;
TOps = record
Named: TUnary;
// A routine type written inline, where a type goes
Square: routine(AN: int32): int32;
end;
routine Twice(AN: int32): int32;
begin
return AN * 2;
end;
routine Sqr(AN: int32): int32;
begin
return AN * AN;
end;
routine Apply(const AF: TUnary; AN: int32): int32;
begin
return AF(AN);
end;
routine Thrice(AN: int32): int32;
begin
return AN * 3;
end;
routine Thrice(AS: string): int32;
begin
return int32(len(AS) * 3);
end;
var
LOps: TOps;
LTable: array[0..1] of TUnary;
LF: TUnary;
begin
LOps.Named := Twice;
LOps.Square := Sqr;
println("field {} inline {}", LOps.Named(5), LOps.Square(5));
LTable[0] := Twice;
LTable[1] := Sqr;
println("table {} {}", LTable[0](4), LTable[1](4));
println("apply {}", Apply(Twice, 7));
// An overload set gives its member of the type's signature
LF := Thrice;
println("overload {}", LF(10));
LF := nil;
println("nil {}", LF = nil);
end.
field 10 inline 25
table 8 16
apply 14
overload 30
nil true
[Heap] Allocs: 0, Frees: 0, Leaked: 0
- A routine value assigned, passed or returned must match the type's parameters, modes, result and linkage exactly (SM0200, SM0404 for an argument); an overload set gives its member of that signature.
- A routine value is called like a routine, with exactly its parameters, at least the fixed ones when it ends in
...(SM0401); one with no return type gives no value (SM0406); nil compares. - The
:after a routine type's parameters is always its return type, so a routine-typed field takes no bitfield width.
C++ types
Any C or C++ type is a type expression, written as C++ spells it: fundamentals and their combinations (unsigned int, long long, auto), cv qualifiers before or after, * with its own cv, one & or && last, qualified names (a leading :: too), template arguments holding a type, a constant or a C++ type id (std::function<int(int)>), an elaborated key (struct tm) and decltype(...).
PaskalCPP values and routines move into C++ types by C++'s rules (a routine into a std::function, nil into a pointer). A C++ mistake inside one is clang's; a mistake in its shape is the parser's: a repeated cv qualifier PR0029, & not last PR0030, template arguments never closed PR0031, struct with no name PR0036, decltype with no parentheses PR0037. See C++ mixing.
Records
A record is a plain aggregate of fields. It may also hold anonymous overlays and operators (see Operators), but no methods (PR0034).
module exe types_records;
type
TBase = record
A: int32;
end;
// An extension holds every field of its chain
TExt = record(TBase)
B: int32;
end;
TPk = record packed
X: int8;
Y: int32;
end;
TBits = record
S: int32 : 3;
F: bool : 1;
end;
routine ShowBase(const AB: TBase);
begin
println("base {}", AB.A);
end;
var
LE: TExt;
LZ: TExt;
LP: ptr to TBase;
LB: TBits;
begin
println("zero {} {}", LZ.A, LZ.B);
// A record literal names the inherited fields too
LE := TExt(A: 1, B: 2);
println("literal {} {} size {}", LE.A, LE.B, size(TExt));
ShowBase(LE);
LP := address of LE;
println("through base {}", LP^.A);
println("packed {}", size(TPk));
LB.S := -4;
LB.F := true;
println("bits {} {}", LB.S, LB.F);
end.
zero 0 0
literal 1 2 size 8
base 1
through base 1
packed 5
bits -4 true
[Heap] Allocs: 0, Frees: 0, Leaked: 0
- The prefixes come in the order
record(TParent) packed align(n); out of order or twice is PR0032. - A record is plain data: a variable, a field,
create's object and asetlengthelement start zeroed. - A record literal names its fields,
TR(B: 7); on an extension it names the inherited fields too. - Records, overlays and arrays of them do not compare:
=,<>,<and the like are SM0252, unless the type declares that operator. Arrays of comparable elements compare element by element. create(P)/destroy(P)take a pointer to a record or an overlay (aptr to int32is SM0409);destroyfrees it and sets the pointer to nil.
Parents and extensions
record(TBase) extends a record and holds every field of its chain; a field reuses no name of the chain (SM0309), and an object, a choices type or an alias of int32 as a parent is SM0308.
- An extension stands in for its base: a pointer to it converts to a pointer to the base, a value converts to the base (the base part is copied), a variable passes to a
varparameter of the base. Never the other way (SM0200; avarparameter SM0405); a downcast is writtenptr to TExt(P). - A record with the same fields is no extension: records are named types.
- A base holding a routine-typed field gives dispatch through a record: an extension sets it, and a call through a pointer to the base runs the extension's routine.
- A C++ struct may be a record's parent, declared as plain C++ in the same file; its members are C++'s, and the record converts to a pointer to that struct.
packed and align
packed packs every field as written, with no padding, nested records, static arrays, sets and the parent included: int8 + int32 is 5 bytes. A string, wstring or dynamic array field, or a record holding one, is SM0331.
A field of a packed record may be misaligned. It is read, copied to a const parameter, printed, indexed, tested with in and assigned whole; but a var argument, address of and a compound assignment (+=) on it are SM0270.
align(n) takes a constant expression: a power of two (SM0310), at most 8192 (SM0312), not below the record's natural alignment (SM0311, lower it with packed). The same limits and codes hold for an align value written in C++ and for a record holding C/C++ fields.
Bitfields
F: T : width; declares a bitfield of an integer, bool, char, wchar or choices type (another type is SM0314); the width is 1 up to the type's bits, wchar 1..16 on every target (else SM0315).
A constant stored into a bitfield (:=, a record literal's field) must fit its width, signed for a signed integer, char and choices, unsigned for an unsigned integer and wchar: 100 does not fit in 'int32 : 3' (-4..3) (SM0200).
Overlays
An overlay holds its fields in the same storage: each field is a view of the same bytes, and its size is its largest field's. An anonymous record inside it groups fields that are the overlay's own (LW.Lo).
module exe types_overlays;
type
TWord = overlay
Whole: uint32;
record
Lo: uint16;
Hi: uint16;
end;
end;
var
LW: TWord;
begin
// Every byte starts zeroed
println("fresh {} {} size {}", LW.Whole, LW.Hi, size(TWord));
// 0x12345678: both official targets are little-endian
LW.Whole := 305419896;
println("lo {} hi {}", LW.Lo, LW.Hi);
end.
fresh 0 0 size 4
lo 22136 hi 4660
[Heap] Allocs: 0, Frees: 0, Leaked: 0
- An overlay starts zeroed in every byte wherever it is created (a variable, a field, an array element, a
setlengthelement,create's object): a field read before any write reads 0. - Its fields may be bitfields, choices, named records, C structs (plain data), an anonymous packed record, or an anonymous record holding an anonymous overlay. An overlay is a field, an array element, a pointer target and a parameter like any type.
- Each field is plain data: a
string,wstringor dynamic array, or a record holding one at any depth, is SM0313, as is a C++ type with a constructor or destructor. An object or an own / ref / weak is SM0357. - An overlay holds no methods,
hiddenmembers, embeds, init, properties or operators (PR0034); two fields of one name are SM0004.