| Bug # | Delphi versions | Description |
| 124 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
You must manually Initialize() and Finalize() any old-style objects that contain any long strings or variants |
| 128 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
The built-in assembler generates bad code for CMPXCHG; namely $0F, $A7, ... instead of $0F, $B1 or $0F, $B0. Actually, this is Intel's fault since they changed the opcode for CMPXCHG between step A and step B of the 486. D2 uses the obselete opcode, making later 486s, Pentiums and presumably Pros generate "Illegal instruction" exceptions. |
| 129 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
When code relies on initial global variable zeroing, the optimizer can get confused and produce bad code |
| 130 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Range Checking is not performed properly on Set expressions anymore |
| 136 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Compiler directive {$D xxx} (Description) is ignored; and {$DESCRIPTION xxx} is not recognized! |
| 143 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Version info not compiled into DLLs |
| 152 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Raising an exception in an exception handler causes access violation. |
| 416 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
The compiler does not correctly initialise (old-style) objects typed consts. |
| 417 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
A record containing only 1 string (or WideString, Interface reference, DynamicArray or one-element array of string) is not handled properly when it is returned as a result value of a function or method. |
| 452 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Small objects can give strange runtime errors |
| 461 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
The Delphi 3.02 linker is a bit too clever when removing symbols: If one tries to FLD (BASM) data that has been declared using the absolute directive, bad code is generated. This will make the application crash at runtime with access violations. |
| 494 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Using Low(integer) and High(integer) in for loops produces bad code. |
| 500 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Integer expressions involving unsigned constants or variables are (unexpectedly) treated as unsigned numbers |
| 502 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Delphi generated code have an error in its address (pointer) arithmetic which can cause Acces Violations.The error was found when using an array of Strings. |
| 518 | 1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 |
Project with no run-time packages sometimes can be compiled with run-time packages |
Bug #124; last modified: 28-Oct-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| N/A | Exists | Exists | Unknown | Fixed | Fixed | Fixed | Fixed |
{$APPTYPE Console}
program ObjTest;
{$UNDEF FixBug}
{$H+}
uses
{
Install a special memory manager that fills all dynamic memory allocations
with well-defined garbage. This lets us SEE that Delphi does not zero-out
the long-string fields ...
}
DebugMem,
SysUtils;
type
TStringRec = record
Int: Integer;
Msg: string
end;
POldObject = ^TOldObject;
TOldObject = object
Field1: string;
Field2: TStringRec;
constructor Init(Str1, Str2: string; i: Integer);
destructor Done; virtual;
end;
PChildObject = ^TChildObject;
TChildObject = object(TOldObject)
Field3: string;
constructor Init(Str1, Str2, Str3: string; i: Integer);
{$IFDEF FixBug}
destructor Done; virtual;
{$ENDIF}
end;
constructor TOldObject.Init(Str1, Str2: string; i: Integer);
begin
{$IFDEF FixBug}
Initialize(Self); // Initializes Field1 and Field2 ONLY!
{$ENDIF}
Field1 := Str1;
Field2.Msg := Str2;
Field2.Int := i
end;
destructor TOldObject.Done;
begin
{$IFDEF FixBug}
Finalize(Self); // Finalizes Field1 and Field2 ONLY!
{$ENDIF}
end;
constructor TChildObject.Init(Str1, Str2, Str3: string; i: Integer);
begin
{$IFDEF FixBug}
Initialize(Self); // Initializes Field3 ONLY!
{$ENDIF}
inherited Init(Str1, Str2, i);
Field3 := Str3
end;
{$IFDEF FixBug}
destructor TChildObject.Done;
begin
try
inherited Done;
finally
Finalize(Self); // Finalizes Field3 ONLY!
end;
end;
{$ENDIF}
var
Obj: PChildObject;
begin
New(Obj,Init('Hello','World','Baby!',10));
try
with Obj^ do
begin
Writeln( 'Field1 = ', Field1 );
Writeln( 'Field2.Msg = ', Field2.Msg );
Writeln( 'Field2.Int = ', Field2.Int );
Writeln( 'Field3 = ', Field3 );
end;
finally
Dispose(Obj,Done);
end;
end.
First, compile and run the code without defining the FixBug symbol. Set
a breakpoint in the constructor at the line
Field1 := Str1;and then run the program. If you examine the initial contents of Field1 and Field2.Msg (having first typecast them both to pointers) then you will see that Delphi has not set them both to Nil. Reset the program using Ctrl-F2, since continuing to execute will cause unpredicatable behaviour!!!
Bug #129; last modified: 28-Oct-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| N/A | Exists | Exists | Exists | Exists | Exists | Exists | Exists |
program Project1;
{$OPTIMIZATION ON}
Uses Dialogs,SysUtils;
Var I, J: Integer;
begin
J := 123;
A: I := I+J;
B: I := J-I; { compiler warning here: "I" not initialized }
ShowMessage(IntToStr(I));
end.
Above, this simple program should display zero as its result. On statement
A, the program relies on the fact that global variables initially have
a zero value (I, J both equal zero) and thus the addition is unnecessary
and could be an assignment instead (I := J). On B, the code does a substract,
and the result should be zero. However, if optimization is on, a garbage
result (usually something like -5636096) will occur.
People have questioned where it says that global variables are initialized
to zero. Well, on page 47, fourth paragraph of the Language
Guide (Delphi 2?) it says:
"If a global variable declaration does not explicitly specify an
initial value, the memory occupied by the variable will initially be set
to zero".
This has been a well-known language feature since BP 7.0, and it is still
mentioned in the Delphi 4 on-line help (topic "global variables"). This is also
known as "data segment clearing" and will make all non-initialized
variables to be set to zero. For example, pointers will be set to nil and
strings to empty ('').
...
begin
I := 0; { initialize even if not needed }
J := 123;
...
(Alternative: modify statement A to not to use I: "I := J") Also rearranging
or rewriting portions of code seems to help. But until this kind of bugs
have been fully documented, the only way to be really sure is to turn optimization
off. However, this bug seems to be a special case and should not haunt
"normal" Delphi programs.
Bug #130; last modified: 11-Aug-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Absent | Exists | Exists | Exists | Exists | Fixed | Fixed | Fixed |
var
I, J, K: Integer;
begin
I := 257;
J := 357;
K := 457;
if I in [I, J, K] then
WriteLn('Miracle!')
else
WriteLn('Bug found...');
end.
Delphi 2 reports 'Bug found'. I tried to understand what D2 generates here:
it seems like the binary code is optimized for use with TRUE sets, that
is set of 0..255. At the same time (with Range Checking off, RS),
TP7 and D1 allow to use such statements and simply generate integer comparisons.
With Range Checking on, TP7 and D1 give a Range Check error on the if
statement because the set element values are illegal, see also the documentation.
The real bug here is the fact that D2 and D3 do not raise an exception on
evaluation of the if statement.
Bug #136; last modified: 4-Nov-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Unknown | Exists | Unknown | Unknown | Unknown | Unknown | Unknown | Fixed |
program Test;
{$D My Program v2.6b}
begin
{ do nothing }
end.
(Try the longer version of $D - it won't work) Now create a similar program
with no descrption:
program Test;
begin
{ do nothing }
end.
Both compiles result in a 7,168 byte EXE file. The problem is that the
first program doesn't include the module description as it should. Even
TDUMP won't display it. Doing a file compare results:
[Win95]E:\delphi32\exefiles>fc test.exe test2.exe /b Comparing files test.exe and test2.exe FC: no differences encounteredSo the compiler simply ignored the directive. Using the other comment marks "(*" and "*)" doesn't help. I also tried the other "long" compiler directives, but they appear to work as expected. Next I checked the compiler modules (DCC.DLL and DCC32.EXE) for misspellings, to no avail. Command line compiling doesn't help either. Also, linking resources with $R directive or using other units in the code doesn't help.
2. By GTABSoft@aol.com
The EXE description will be correctly written if you set the EXE Description
on the Linker tab of Project Options instead of using the $D or $DESCRITPION
directive.
Bug #143; last modified: before April 1998| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| N/A | N/A | Exists | Fixed | Fixed | Fixed | Fixed | Fixed |
Bug #152; last modified: 4-Nov-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Unknown | Unknown | Exists | Unknown | Unknown | Unknown | Unknown | Fixed |
procedure TForm1.Button1Click(Sender: TObject);
begin
try
raise Exception.Create('Help');
except
raise Exception.Create('Bug on Win95');
end;
end;
Clicking on the button will raise an exception "Help", which will be handled
in the exception clause. This will raise another exception - here by design,
but in real life this may happen because of a bug, an oversight or anything
else.
Bug #416; last modified: 28-Oct-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Fixed | Exists | Exists | Exists | Exists | Exists | Exists | Exists |
These objects are not required (see the BP 7 manual) to have a constructor. It's a rule of the language that a constructor must be called to initialize any object with virtual methods. However, the compiler is forgiving about this in BP 7 and allows constant object declarations to avoid the constructor call (this is documented behaviour); but the Delphi 32 bits compilers aren't so forgiving.
This is a bug of the compiler, because, while undocumented, all the stuff for handling old-style objects is present. Unfortunately, the compiler initialises the VMT field of the typed-const object with a wrong value.
Below is a source file to see the problem. It works OK under BP7 and Delphi 1.02, where it outputs:
TObj.c elem=1
TObj.c elem=1
but fails under Delphi 2/3, where it outputs:
TObj.c elem=1
TObj2.a elem=1
The source:
{$ifdef WINDOWS}
uses WinCRT;
{$endif}
type TObj=object
elem:Byte;
procedure a;virtual;
procedure b;virtual;
procedure c;virtual;
end;
type
PObj=^TObj;
type
TObj2=object(TObj)
procedure a; virtual;
end;
procedure TObj.a;
begin
WriteLn('TObj.a elem=',elem);
end;
procedure TObj.b;
begin
WriteLn('TObj.b elem=',elem);
end;
procedure TObj.c;
begin
WriteLn('TObj.c elem=',elem);
end;
procedure TObj2.a;
begin
WriteLn('TObj2.a elem=',elem);
end;
const
Obj:TObj2 = (elem:1);
const
Ptr:PObj = @Obj;
begin
Obj.c;
Ptr^.c;
end.
So the above code becomes (this code works correctly under BP7, Delphi 1.02, 2.0x and 3.02.):
type TObj=object
elem: Byte;
constructor init;
procedure a; virtual;
procedure b; virtual;
procedure c;virtual;
end;
type
PObj=^TObj;
type
TObj2=object(TObj)
constructor init;
procedure a;virtual;
end;
constructor TObj.init;
begin
end;
procedure TObj.a;
begin
WriteLn('TObj.a elem=',elem);
end;
procedure TObj.b;
begin
WriteLn('TObj.b elem=',elem);
end;
procedure TObj.c;
begin
WriteLn('TObj.c elem=',elem);
end;
constructor TObj2.init;
begin
inherited init
end;
procedure TObj2.a;
begin
WriteLn('TObj2.a elem=',elem);
end;
const
Obj:TObj2 = (elem:1);
const
Ptr:PObj = @Obj;
begin
Obj.init;
Obj.c;
Ptr^.c;
end.
Bug #417; last modified: 28-Oct-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Fixed | Exists | Exists | Exists | Exists | Exists | Exists | Fixed |
type
TMyRecord = record
MyString : string;
end;
// This function will always return a record containing
// an empty(!) string
function GetMyRecord : TMyRecord;
begin
Result.MyString := 'You will not see this';
end;
Comment by Greg Chapman:I'm not sure that is as clear as it should be. Anyway, what the compiler should be doing is treating such records the same way it treats AnsiStrings, etc.: it should pass the function an implicit var param to hold the result.
Bug #452; last modified: 28-Oct-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Unknown | Unknown | Unknown | Unknown | Exists | Exists | Exists | Exists |
This is a bug because:
If there is something wrong with this code (which I don't think it is),
the compiler should give an error. Now you will get this message
on the screen at runtime:
Project ... raised exeption class EAccessViolation with message
'Access violation at adress ...'
The code:
unit DelphiErr2U;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
TYPE
IObj= Object
// FieldThatMakeTheTotalFieldSizeGreaterThenFourBytes: Byte;
I: LongInt;
FUNCTION Func: LongInt;
PROCEDURE Proc (X: LongInt);
PROPERTY Prop: LongInt Read Func Write Proc;
END;
FUNCTION IObj.Func: LongInt;
BEGIN
Result:= 5;
END;
PROCEDURE IObj.Proc(X: LongInt);
BEGIN
I:= X + 1;
END;
procedure TForm1.Button1Click(Sender: TObject);
VAR AObj: IObj; Z: LongInt;
begin
AObj.Prop:= 3;
Z:= AObj.Prop;
Caption:= IntToStr(Z);
end; // Access violation when leaving this procedure.
end.
Bug #461; last modified: 4-Nov-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Unknown | Unknown | Exists | Exists | Exists | Exists | Exists | Fixed |
{$A+,B-,C+,D+,E-,F-,G+,H+,I+,J+,K-,L+,M-,N+,O+,P+,Q+,R+,S-,T+,U-,V+,W-,X+,Y+,Z1}
unit TestMe;
interface
implementation
var
Constant: array[1..8] of byte;
Test1: double absolute Constant;
Test2: double = 0;
procedure RunMe;
asm
fld test1
fld test2
fstp st(0)
fstp st(0)
end;
initialization
RunMe;
end.
You will get an access violation (in this case a Runtime Error 216,
otherwise an exception EAccessViolation) on execution of
fld test1This is because the first few lines of assembler code look like this
fld test1 0052768C: DD0502504A30 FLD QWORD PTR [+02504A30] fld test2 00527692: DD0500528884 FLD QWORD PTR [+00528884][my applications are always based at $500000]
First result:
Clearly [+02504A30] looks a bit like garbage (and it is plain
wrong, of course)... This explains the crash, since $2504A30
is way beyond reach for this application.
Second result:
Run the code as shown above up to the first FLD (breakpoint
or single-step).
Have tooltip expression or "Evaluate" tell you that:
test1 --> "Symbol was eliminated by linker"
"Touching" the Constant variable, e.g. Constant[1] := 0;, does apparently make the linker sort of include "test1", but does _NOT_ remove the crash (!).
Even worse, touching the Test1 variable, e.g. Test1 := 0;, does not change this behaviour, either.
Test3: double = 0; Constant2: array[1..8] of byte absolute Test3;
Bug #494; last modified: 28-Oct-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Unknown | Exists | Exists | Exists | Exists | Exists | Exists | Exists |
for i := low(integer) to high(integer) do
Label1.Caption := IntToStr(i);
When compiling this, D4.x gives a hint that says that the for
loop executes 0 times and that it has been deleted...for i := low(integer) downto high(integer) do ;is not removed by the optimizer.
Bug #500; last modified: 30-Oct-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| N/A | N/A | Absent | Absent | Absent | Exists | Exists | Fixed |
The treatment of integer expressions made up of (unsigned) constants in Delphi 4 is different from previous Delphi versions. Example code to illustrate this:
var a,b:byte; begin a:=9; b:=11; c:=(a-b) div 2; d:=cardinal(a-b) div 2; end;c will have a hex value of $FFFFFFFF and d will have a hex value of $7FFFFFFF. How those are interpreted depends upon the type of c and d.
Bug #502; last modified: 30-Oct-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| Unknown | Unknown | Exists | Exists | Exists | Exists | Exists | Exists |
--------------- cut here ---------------
{$A+,B-,C-,D+,E-,F-,G+,H+,I+,J+,K-,L+,M-,N+,O-,P+,Q-,R+,S-,T-,U-,V+,W+,X+,Y-,Z1}
{$APPTYPE CONSOLE}
program genlnkts;
uses
Sysutils;
const
MAX = 3300;
var
f: text;
i: integer;
begin
Assign(f,'test.dpr');
Rewrite(f);
Writeln(f,'{$A+,B-,C-,D+,E-,F-,G+,H+,I+,J+,K-,L+,M-,N+,O-,P+,Q-,R+,S-,T-,U-,
V+,W+,X+,Y-,Z1}');
Writeln(f,'program test;');
Writeln(f,'uses Forms;');
Writeln(f,'var s: array [1..',MAX,'] of string;');
Writeln(f,'begin');
for i := 1 to MAX do
Writeln(f,' s[',i,'] := ''line ',i,''';');
Writeln(f,'end.');
Close(f);
end.
--------------- cut here ---------------
It will generate a test program, test.dpr, with a bunch of string assignment
statements in the form of
s[3282] := 'line 3282'; s[3283] := 'line 3283'; s[3284] := 'line 3284'; s[3285] := 'line 3285'; s[3286] := 'line 3286'; s[3287] := 'line 3287'; s[3288] := 'line 3288'; s[3289] := 'line 3289';Open test.dpr in Delphi, compile and run. You'll get an Access Violation exception. In Delphi 3.02 exception occurs on line "s[3298] := 'line 3298';", in Delphi 4.0 + Update 1 it occures on the next line (s[3299] := ...). A quick glance at code generated shows why (this example is from D 3.02):
s[3297] := 'line 3297';
mov eax,$0045cb64
mov edx,$00457830
call @LStrAsg
s[3298] := 'line 3298';
mov eax,$0045cb68
mov edx,$00447833 ; Here lies the bug!
call @LStrAsg
The address, moved into edx should be $00457833, which is exactly $10000
(64 kb!) off. Looks like in the depth of the Delphi compiler/linker there is still
some win16-pointer-related arithmetic which is not completely bulletproof.
Comment from Tor-Arne Riksheim:
I ran the test, and found that the error doesn't happen if the stringtable is
made of ShortStrings.
Bug #518; last modified: 28-Dec-98| 1.02 | 2.01 | 3.0 | 3.01 | 3.02 | 4.0 | 4.01 | 4.02 |
| N/A | N/A | Absent | Absent | Absent | Unknown | Unknown | Exists |
Step by step to reproduce: