The Delphi Bug List

Entry No.
416
Compiler - Code generation
The compiler does not correctly initialise (old-style) objects typed consts.
1.02 2.01 3.0 3.01 3.02 4.0 4.01 4.02 4.03 5.0 5.01 6.0 6.01 6.02 Kylix 1.0
AbsentExistsExistsExistsExistsExistsExistsExistsExistsExistsExistsExistsExistsExistsExists
Description
Reported by Antoine Leca; checked by Duncan Murdoch, Reinier Sterkenburg
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.01, where it outputs:
        TObj.c elem=1
        TObj2.a elem=1
Under Delphi 3.01, 4.02, 5.01 and 6.02 it produces an Access Violation on execution of the second line.
Under Kylix (1.0) it gives: "Program Test416 received signal SIGSEGV(11). Process stopped. Use Step or Run to continue."

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.
Latest update of this entry: 2002-04-03

Post a comment on this bug


Index page
Delphi Bug List home page
The Delphi Bug Lists are presently maintained by Jordan Russell, who has taken over this task from Reinier Sterkenburg since August 2000.
All feedback is appreciated. See also the feedback section of the Delphi Bug List home page.