The Delphi Bug List

Compiler

Code generation


The color codes indicate in which version(s) of Delphi the bug occurs and what its status is.
Latest update: 28 December 1998
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
Code generation

You must manually Initialize() and Finalize() any old-style objects that contain any long strings or variants

Description
Reported by John Baima; checked by Chris Rankin
Consider the following code:
{$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!!!
Solution / workaround
There are two possible workarounds; the first is simply to avoid old-style objects entirely and replace them with classes. This is probably the better solution in the long-run, since old style objects are being quietly phased out. (The D3 manuals don't mention them at all.)

For those of you with large amounts of old TP/BP legacy code ported to Delphi, compile the application above with the FixBug symbol defined. Delphi will now correctly zero out both long string fields. Note that this object's destructor must also always Finalize() itself immediately before it returns to preserve the reference-counting mechanism.
Note that you must put the calls to Initialize() and Finalize() in both the ancestor and descendant objects to ensure that Delphi sees all the fields.
This is definitely a bug, since Delphi claims to do this Initialize()-ing and Finalize()-ing automatically for array, records and objects allocated using New() / Dispose(). It seems as if they just forgot about old-style objects.

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
Code generation

When code relies on initial global variable zeroing, the optimizer can get confused and produce bad code

Description
Reported by Jani Järvinen
Problem example:
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 ('').

Cause:
Because the compiler optimizes the variables to be on CPU registers and not in memory, the register for I is not cleared. If you look at the assembled code, you can see that the EBX register is not cleared and contains a garbage value. The reason for this seems to be the incorrect warning that variable I might not be initialized on statement B (shouldn't the warning occur on statement A?) and thus the optimizer gets confused producing bad code. Stack or memory based variables seem to work OK in this kind of situations.
Solution / workaround
Initialize global variables, even though that is unnecessary:
...
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
Code generation

Range Checking is not performed properly on Set expressions anymore

Description
Reported by Hovik Melikian; additional information by Reinier Sterkenburg
Take the following sample code (console app):
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
Code generation

Compiler directive {$D xxx} (Description) is ignored; and {$DESCRIPTION xxx} is not recognized!

Description
Reported by Jani Järvinen
The directive $D (module description) simply does nothing. The longer version of the same directive, $DESCRIPTION is rejected by the compiler: "Invalid compiler directive: 'DESCRIPTION'."
Problem example:
Create a plain simple program with a description:
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 encountered
So 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.
Solution / workaround
1. By Ataru Nakata

This problem's solution is very simple, only change {$DESCRIPTION xxx} to {$DESCRIPTION 'xxx'} !
(But, {$D xxx} style still does not work.)

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
Code generation

Version info not compiled into DLLs

Description
Reported by Keith Johnson; checked by Arjen Broeze
Creating a .DLL with Delphi, I've checked all the appropriate items in the Project|Options|VersionInfo, but the version info still doesn't make it into the compiled .DLL.
The build number increments when I do a full build, but even then nothing appears in the file.
Solution / workaround
By Steve Teixeira
You need to manually put the {$R *.RES} directive in the project DPR file. This is a bug that was fixed in the Delphi update.

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
Code generation

Raising an exception in an exception handler causes access violation.

Description
Reported by Stefan Hoffmeister
Consider this small program:
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.
The expected reaction would be to show a modal dialog box saying something like "Exception at .... Bug on Win95". After that, because of an exception in the exception handler, the program will be "halt"ed.
But if the program is run on a Windows 95 system *and* it is run from within the IDE, the program will crash with an unspecific access violation.
Solution / workaround
Make sure that you do not raise exceptions in exception handlers when debugging your program on Windows 95 in the Delphi 3 IDE. According to Ray Lischner, Windows NT is not affected by this problem, i.e. it will never crash with an access violation. Also, you will see the correct reaction if the program is *not* run within the IDE.

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
Code generation

The compiler does not correctly initialise (old-style) objects typed consts.

Description
Reported by Antoine Leca; checked by Duncan Murdoch
Delphi 2/3/4 do not select the correct method when calling a virtual method via a polymorphed pointer with old-style objects created as typed consts. It fall short by 2, i.e. it selects the method two previous from the correct one; in fact, it does as if the VMT for "objects" would have the same format as a one for "classes" (which is wrong).

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.
Solution / workaround
You should add a constructor to the object type, and call it in an initialization section (before calling any virtual method). The code called in the constructor (_ObjSetup) stores in the VMT field of the object a new value (offseted by +8), which is the one expected by the code when calling virtual methods.

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
Code generation

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.

Description
Reported by Marc Nijweide; checked by Greg Chapman
Code example:
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:
The bug reported here is not limited only to AnsiStrings. It will also occur with a record which has only a WideString, only an Interface reference, only a DynamicArray or only a single-element static array of String or WideString etc.. What all these things have in common is that they are implemented as pointers, so a record containing just one of them will be only 4 bytes long. According to the ObjectPascal manual, most record function results are implemented by passing the function a hidden var param holding an instance of the correct record type. However, four-byte records are returned in EAX, thus there is no hidden var param to assign to. On entry, the function creates its own copy of the record on the stack; on exit, the record is Finalized and nil is returned.

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.

Solution / workaround
The workaround is to put an extra field in the record (most record contain multiple fields anyway), any field will do.

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
Code generation

Small objects can give strange runtime errors

Description
Reported by Björn Sahlen; checked by Reinier Sterkenburg
To reproduce, compile and execute the code below.

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.
Solution / workaround
The only acceptable workaround for me is to use a record and standard functions and procedures instead of an object. An other workaround is of course to make sure that the size of your objects are at least five bytes

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
Code generation

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.

Description
Reported by Stefan Hoffmesiter; checked by Yorai Aminov
To reproduce, try and run this:
{$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 test1
This 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.

Solution / workaround
The only solution to this problem is to have the absolute declaration turned around, e.g.
  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
Code generation

Using Low(integer) and High(integer) in for loops produces bad code.

Description
Reported by Anders Melander; checked by Reinier Sterkenburg
For example:
  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...
Further testing shows that
  for i := low(integer) downto high(integer) do ;
is not removed by the optimizer.
Even though low(integer) is negative and high(integer) is positive when you display them with IntToStr(), it appears that the for loop treats the values as unsigned DWORDs (verified with CPU window).

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
Code generation

Integer expressions involving unsigned constants or variables are (unexpectedly) treated as unsigned numbers

Description
Reported by Robert Lee
Note: this problem / change of behaviour only exists in Delphi 4.0 and 4.01; it has been eliminated in Delphi 4.02 (D 4 Update Pack #2).
The description below may be somewhat incomplete; a new, clearer (not too long) description of this problem would be appreciated very much

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.
So the burden has shifted entirely to the unsigned crowd, as it should be. Note that this might actually break unsigned code written in d4.00 or d4.01.

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
Code generation

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.

Description
Reported by Primoz Gabrijelcic, on Inprise's objectpascal newsgroup; checked by Stefan Hoffmeister; Tor-Arne Riksheim
Problem: EAccessViolation exception in perfectly bug-free program.
Applies to: All versions of Delphi 3 and Delphi 4.
Workaround: none known
How to reproduce:
Compile and run following program with Delphi.
--------------- 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
Code generation

Project with no run-time packages sometimes can be compiled with run-time packages

Description
Reported by Sergey Mishkovskiy
A project that has no run-time packages is sometimes compiled as if had run-time packages. To illustrate that, open a project with run-time packages and compile it. Open a project without run-time packages and compile it too. You'll notice that last project exe file size is very small, almost if it was compiled with run-time packages (even though it didn't have them).

Step by step to reproduce:

Solution / workaround
Close and re-start Delphi

Index page
The Delphi Bug Lists are maintained by Reinier Sterkenburg, with help from the DeBug Team.
All feedback is appreciated. See also the feedback section of the Delphi Bug List home page.