Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/IDE/Structure Pane/Error Insight    [ Add a report in this area ]  
Report #:  101168   Status: Open
Syntax parser gets broken after "absolute" keyword
Project:  Delphi Build #:  16.0.4316.44803
Version:    16.2 Submitted By:   Dmitry Burov
Report Type:  Basic functionality failure Date Reported:  11/20/2011 10:46:30 AM
Severity:    Commonly encountered problem Last Updated: 3/20/2012 2:24:39 AM
Platform:    All versions Internal Tracking #:   288930
Resolution: None (Resolution Comments) Resolved in Build: : None
Duplicate of:  None
Voting and Rating
Overall Rating: No Ratings Yet
0.00 out of 5
Total Votes: None
Description
see screenshot.

Not only locally broken - but even unusable to the end of the unit file !
Steps to Reproduce:
see screenshot

-------------------------
unit Unit1;

interface

implementation

uses System.Classes;

type
  TPtr = record
    someData: Boolean;
    x:Pointer;
    someMoreData: string;
end;

procedure BugTest(const pt: TPtr; const sl:TStrings);
var
  x_as_obj: TObject absolute pt.X;
begin
  // such variable definitions come handy
  // when need to work with TList.AddObject
  // or TStrings.AddObject and such

  sl.AddObject('bug test', x_as_obj);
end;

end.
-------------------------
Workarounds
One may ask how is at better than direct typecast used many time within VCL code or any code floating over the internets.

Why not this way?
procedure BugTest(const pt: TPtr; const sl:TStrings);
begin
  sl.AddObject('bug test', TObject(pt.X));
end;

Let's make the procedure a bit more complex and use the variable more than once.

There is a common pattern, frequently used in Windows GDI API for example. And in TurboPascal ExitProc chain, if i remeber good old DOS times.
We change some value using provided function, and all value is returmned by function, so caller can save it for laiter chaining or gracefully dispose of.

Code is schematic, slow but easy.

procedure UpdateCache(const pt: TPtr; const sl:TStrings; const value: string);
var idx: integer; temp: TObject;
begin
  idx := sl.IndexOf(value);
  
  if idx >=0 then begin
      temp := sl.Objects[idx];
      sl.Objects[idx] := TObject(pt.X);
      TObject(pt.X) := temp;
  end els begin
      sl.AddObject('bug test', TObject(pt.X));
      TObject(pt.X) := nil;
  end;
end;


procedure UpdateCache(const pt: TPtr; const sl:TStrings; const value: string);
var idx: integer; temp: TObject; pt_obj: TObject absolute pt.X;
begin
  idx := sl.IndexOf(value);
  
  if idx >=0 then begin
      temp := sl.Objects[idx];
      sl.Objects[idx] := pt_obj;
      pt_obj := temp;
  end els begin
      sl.AddObject('bug test', pt_obj);
      pt_obj := nil;
  end;
end;

So we used that variable more than once.


1. Type Safety. None in both cases. Compiler protection fully overriden.
2. Coding speed - we have to key in reasonable less code.
3. Future changes: if we change container type from TStrings so some another, and it will have other type than TObject for by-side values, we only would need change type in one easy to find declaration, rather than peek through the whole procedure body. Given that we overriden type safety, if we would forget to change some place, compiler would not be able to warn us with error.
4. Mistyping: There are frequently smilar-named types, like TColor and TColors. Single mis-typed letter may change the meaning. When we have one type definition it is easier to key it in with attention and check it rigidly, than when intermixed through all the body.
5. Side Effects: providing all this, absolute-using pattern make code looks like regular variable used. This may lead unattentive programmer to wrong things like re-using variable for temporary values and such. Direct typecast way frankly marks tricky place and alerts any later reader.

Given this, i consider 2-4 more important than 5, though of cause mileage varies.
Attachment
absolute_record_bug.zip
Comments

Tomohiro Takahashi at 11/20/2011 4:36:34 PM -
Could you please provide simple sample code to reproduce/verify your issue?

Dmitry Burov at 11/22/2011 2:21:23 AM -
unit Unit1;

interface

implementation
uses System.Classes;
type TPtr = record someData: Boolean; x:Pointer; someMoreData: string; end;

procedure BugTest(const pt: TPtr; const sl:TStrings);
var  x_as_obj: TObject absolute pt.X;
begin
  // such variable definitions come handy
  // when need to work with TList.AddObject
  // or TStrings.AddObject and such

  sl.AddObject('bug test', x_as_obj);
end;

end.

Dmitry Burov at 11/25/2011 1:54:37 AM -
ok, since u updated sample i'd add workaround and few arguments against it.

Dmitry Burov at 11/25/2011 1:58:59 AM -
And speaking nostalgically, TurboPAscal mad e it all wrong.

Soviet PDP-11 Pascal had address specification bound to var, rather than to type. MAybe they taken it from some other Western implementation of Pascal, dunno.

"var Name origin Adress: Type;" instead of Borland's "var Name: Type absolute Address;"

What's the differnce ? Simple. It eliminated overlook errors, like when hastily adding two counter variables.

Var SomeData: integer absolute DataStructure.Member;

Now coder has few seconds to add looping into procedure, He hastily inserts loop counter vars and if overlook he gets...

Var i,j,SomeData: integer absolute DataStructure.Member;

He messed things up :-)

Server Response from: ETNACODE01