View Issue Details

IDProjectCategoryView StatusLast Update
0036658FPCCompilerpublic2020-02-15 12:07
Reporternanobit Assigned To 
PrioritynormalSeverityminorReproducibilityalways
Status newResolutionopen 
Platformwin32OSWindows 
Summary0036658: @getArrayPtr[i] errors
Descriptiongetting an element pointer (PInteger)
via function result (which is array-pointer PIntArray):

{$mode delphi}, {$modeswitch autoderef}
type PIntArray = ^TIntArray;
type TIntArray = array[0..3] of integer;

In the test project, see:
p := @getArrayPtr^[1]; // Error: Illegal qualifier
p := @(getArrayPtr^[1]); // at runtime: wrong pointer
p := @(gate.getArrayPtr[1]); // Fatal: Syntax error, ")" expected but "[" found
p := @gate.getArrayPtr[1]; // Error: Illegal qualifier
 
For comparison, no problems with counterparts to above:
p := @gate.getArrayPtr^[1]; // ok
p := @(pints^[1]); // ok
p := @(getArrayPtr[1]); // ok
p := @getArrayPtr[1]; // ok
Additional InformationProduct Version 3.2 was not selectable in this report form.
TagsNo tags attached.
Fixed in Revision
FPCOldBugId
FPCTarget
Attached Files

Activities

nanobit

2020-02-04 19:49

reporter  

project2.lpr (1,773 bytes)   
program project2;
{$mode delphi}
//{$modeswitch autoderef}

type
TIntArray = array[0..3] of integer;
PIntArray = ^TIntArray;
var
ints: TIntArray;
pints: PIntArray;

function getArrayPtr: PIntArray;
begin result := @ints; end;

type
TArrayGate = class(Tobject)
function getArrayPtr: PIntArray;
end;

function TArrayGate.getArrayPtr: PIntArray;
begin result := @ints; end;

procedure piCheck( pi: PInteger);
begin
writeLn( (pi = @ints[1]) and (pi^ = ints[1]));
end;

procedure testArrayPtr;
var n: integer; p: PInteger;
begin
n := pints[1]; // ok
p := @(pints[1]); piCheck(p); // ok
p := @pints[1]; piCheck(p); // ok
p := @pints^[1]; piCheck(p); // ok
p := @(pints^[1]); piCheck(p); // ok
end;

procedure testFuncResult;
var n: integer; p: PInteger;
begin
n := getArrayPtr[1]; // ok
p := @(getArrayPtr[1]); piCheck(p); // ok
p := @getArrayPtr[1]; piCheck(p); // ok
//p := @getArrayPtr^[1]; piCheck(p);  // Error: Illegal qualifier
p := @(getArrayPtr^[1]); piCheck(p); // runtime: wrong pointer
p := @(getArrayPtr()[1]); piCheck(p); // ok
p := @(getArrayPtr()^[1]); piCheck(p); // ok
end;

procedure testMethodResult;
var gate: TArrayGate;
var n: integer; p: PInteger;
begin
gate := TArrayGate.create;
n := gate.getArrayPtr[1]; // ok
//p := @(gate.getArrayPtr[1]); piCheck(p);  // Fatal: Syntax error, ")" expected but "[" found
//p := @gate.getArrayPtr[1]; piCheck(p);    // Error: Illegal qualifier
p := @gate.getArrayPtr^[1]; piCheck(p); // ok
p := @(gate.getArrayPtr^[1]); piCheck(p); // ok
p := @(gate.getArrayPtr()[1]); piCheck(p); // ok
p := @(gate.getArrayPtr()^[1]); piCheck(p); // ok
gate.free;
end;

begin
pints := @ints[0];
ints[1] := 123;
testArrayPtr;
testFuncResult;
testMethodResult;
end.
project2.lpr (1,773 bytes)   

Thaddy de Koning

2020-02-06 11:04

reporter   ~0120897

You should use the correct type (PInteger) instead of an opaque pointer.
Anyway, there is more wrong, because when I compile your code as-is with trunk, it throws an internal error and not a runtime error:
fpc -vwhnql -Fu/home/asta/synapse:/usr/local/lib/fpc/3.3.1/units/arm-linux/*:/home/asta/fpc331/packages/graph/* -Fl/usr/lib/gcc/arm-linux-gnueabihf/6.3.0 -B -CX -XXs "project2.lpr" (in directory: /home/asta/Downloads)
Hint: (11030) Start of reading config file /etc/fpc.cfg
Hint: (11031) End of reading config file /etc/fpc.cfg
Free Pascal Compiler version 3.3.1-r44120 [2020/02/06] for arm
Copyright (c) 1993-2020 by Florian Klaempfl and others
(1002) Target OS: Linux for ARMHF
(3104) Compiling project2.lpr
2 228/992 Kb Used
project2.lpr(29,5) Note: (5027) Local variable "n" is assigned but never used
project2.lpr(45,23) Fatal: Internal error 2019050529 <<< this is much worse than a runtime error
Fatal: (1018) Compilation aborted
Error: /usr/local/bin/ppcarm returned an error exitcode
Compilation failed.

When I cast the offending line with PInteger instead of @, the code works, though.

Thaddy de Koning

2020-02-06 11:37

reporter   ~0120898

Last edited: 2020-02-06 11:38

View 2 revisions

I used trunk-r44120
This code at least works, but the internal error on your original code should be fixed anyway:
procedure testFuncResult;
var n: integer; p: PInteger;
begin
n := getArrayPtr[1]; // ok
p := @(getArrayPtr[1]); piCheck(p); // ok
p := @getArrayPtr[1]; piCheck(p); // ok
p := PInteger(getArrayPtr^[1]); piCheck(p); // Ok <<----
p := PInteger(getArrayPtr^[1]); piCheck(p); // Ok <<----
p := @(getArrayPtr()[1]); piCheck(p); // ok
p := @(getArrayPtr()^[1]); piCheck(p); // ok
end;

nanobit

2020-02-06 12:46

reporter   ~0120899

Thaddy, your wrong typecast (PInteger(getArrayPtr^[1])),
from integer to pointer, will produce wrong addresses.
(And please don't use this as beginner thread here)

And with "at runtime" is meant: a wrong address is returned (silently).
This can be overseen until end-users see a consequence.

Thanks for testing FPC trunk, good to know (internal error).

nanobit

2020-02-09 13:58

reporter   ~0120966

Just extended the test to include non-delphi mode.
It can be noticed that the whole test would work
if the parser would treat "getArrayPtr" like "getArrayPtr()".

Note: combination (autoderefOn and pointermathOn) is not in the test,
because this needs the 0035745 bugfix.
project3.lpr (3,316 bytes)   
program project3;

{$define modeDelphi}

{$ifdef modeDelphi}
    {$mode delphi} // {$modeswitch autoderef},  {$PointerMath off}
{$else}
    // is already default, but anyway:
    {$modeswitch autoderef-}
    {$PointerMath on}
{$endif}

type
TIntArray = array[0..3] of integer;
PIntArray = ^TIntArray;
var
//ints: TIntArray;
ints: array[0..1] of TIntArray; // consecutive arrays to test pointerMath
pints: PIntArray;

function getArrayPtr: PIntArray;
begin result := @ints[0]; end;

type TArrayGate = class(Tobject)
function getArrayPtr: PIntArray;
end;

function TArrayGate.getArrayPtr: PIntArray;
begin result := @ints[0]; end;

procedure piCheck( pi: PInteger);
begin
writeLn( (pi = @ints[0,1]) and (pi^ = ints[0,1]));
end;

procedure paCheck( pa: PIntArray);
begin
writeLn( (pa = @ints[1]));
end;

procedure testArrayPtr;
var i: integer; pi: PInteger;
a: TIntArray; pa: PIntArray;
begin
i := pints^[1]; // ok
pi := @pints^[1]; piCheck(pi); // ok
pi := @(pints^[1]); piCheck(pi); // ok

{$ifdef modeDelphi}
i := pints[1]; // ok
pi := @pints[1]; piCheck(pi); // ok
pi := @(pints[1]); piCheck(pi); // ok

{$else}
a := pints[1]; // ok
pa := @pints[1]; paCheck(pa); // ok
pa := @(pints[1]); paCheck(pa); // ok
{$endif}
end;

procedure testFuncResult;
var i: integer; pi: PInteger;
a: TIntArray; pa: PIntArray;
begin
i := getArrayPtr^[1]; // ok
pi := @getArrayPtr^[1]; piCheck(pi); // modeDelphi: Error: Illegal qualifier
pi := @getArrayPtr()^[1]; piCheck(pi); // ok
pi := @(getArrayPtr^[1]); piCheck(pi); // modeDelphi: at runtime: pi gets wrong address
pi := @(getArrayPtr()^[1]); piCheck(pi); // ok

{$ifdef modeDelphi}
i := getArrayPtr[1]; // ok
pi := @getArrayPtr[1]; piCheck(pi); // ok
pi := @getArrayPtr()[1]; piCheck(pi); // ok
pi := @(getArrayPtr[1]); piCheck(pi); // ok
pi := @(getArrayPtr()[1]); piCheck(pi); // ok

{$else}
a := getArrayPtr[1]; // ok
pa := @getArrayPtr[1]; paCheck(pa); // ok
pa := @getArrayPtr()[1]; paCheck(pa); // ok
pa := @(getArrayPtr[1]); paCheck(pa); // ok
pa := @(getArrayPtr()[1]); paCheck(pa); // ok
{$endif}
end;

procedure testMethodResult;
var gate: TArrayGate;
i: integer; pi: PInteger;
a: TIntArray; pa: PIntArray;
begin
gate := TArrayGate.create;
i := gate.getArrayPtr^[1]; // ok
pi := @gate.getArrayPtr^[1]; piCheck(pi); // ok
pi := @gate.getArrayPtr()^[1]; piCheck(pi); // ok
pi := @(gate.getArrayPtr^[1]); piCheck(pi); // ok
pi := @(gate.getArrayPtr()^[1]); piCheck(pi); // ok

{$ifdef modeDelphi}
i := gate.getArrayPtr[1]; // ok
pi := @gate.getArrayPtr[1]; piCheck(pi); // Error: Illegal qualifier
pi := @gate.getArrayPtr()[1]; piCheck(pi); // ok
pi := @(gate.getArrayPtr[1]); piCheck(pi);  // Fatal: Syntax error, ")" expected but "[" found
pi := @(gate.getArrayPtr()[1]); piCheck(pi); // ok

{$else}
a := gate.getArrayPtr[1]; // ok
pa := @gate.getArrayPtr[1]; paCheck(pa); // Error: Illegal qualifier
pa := @gate.getArrayPtr()[1]; paCheck(pa); // ok
pa := @(gate.getArrayPtr[1]); paCheck(pa); // Fatal: Syntax error, ")" expected but "[" found
pa := @(gate.getArrayPtr()[1]); paCheck(pa); // ok
{$endif}

gate.free;
end;

begin
pints := @ints[0];
ints[0,1] := 123;
ints[1,1] := 456;
testArrayPtr;
testFuncResult;
testMethodResult;
readLn;
end.
project3.lpr (3,316 bytes)   

nanobit

2020-02-11 08:13

reporter   ~0121010

Added two defines to allow testing more mode-combinations.
project4.lpr (3,891 bytes)   
program project4;

// ---- select defines ----
{$define modeDelphi}

{$ifdef modeDelphi}
    {$mode delphi}
    {$define autoderefOn} // defaultOn
    {.$define pointerMathOn} // optionalOn
{$else}
    {$define pointerMathOn} // defaultOn
    {.$define autoderefOn} // optionalOn
{$endif}

{$if defined( pointerMathOn) and defined(autoderefOn)}
    {$warning Testable only after #0035745 bugfix}
{$endif}

// ---- apply defines ----
{$ifdef autoderefOn}
    {$modeswitch autoderef}
{$else}
    {$modeswitch autoderef-}
{$endif}

{$ifdef pointerMathOn}
    {$PointerMath on}
{$else}
    {$PointerMath off}
{$endif}

type
TIntArray = array[0..3] of integer;
PIntArray = ^TIntArray;
var
//ints: TIntArray;
ints: array[0..1] of TIntArray; // consecutive arrays to test pointerMath
pints: PIntArray;

function getArrayPtr: PIntArray;
begin result := @ints[0]; end;

type TArrayGate = class(Tobject)
function getArrayPtr: PIntArray;
end;

function TArrayGate.getArrayPtr: PIntArray;
begin result := @ints[0]; end;

procedure piCheck( pi: PInteger);
begin
writeLn( (pi = @ints[0,1]) and (pi^ = ints[0,1]));
end;

procedure paCheck( pa: PIntArray);
begin
writeLn( (pa = @ints[1]));
end;

procedure testArrayPtr;
var i: integer; pi: PInteger;
a: TIntArray; pa: PIntArray;
begin
i := pints^[1]; // ok
pi := @pints^[1]; piCheck(pi); // ok
pi := @(pints^[1]); piCheck(pi); // ok

{$if defined( pointerMathOn)}
// pointerMathIndex has precedence over autoderefIndex
a := pints[1]; // ok
pa := @pints[1]; paCheck(pa); // ok
pa := @(pints[1]); paCheck(pa); // ok

{$elseif defined( autoderefOn)}
i := pints[1]; // ok
pi := @pints[1]; piCheck(pi); // ok
pi := @(pints[1]); piCheck(pi); // ok
{$endif}
end;

procedure testFuncResult;
var i: integer; pi: PInteger;
a: TIntArray; pa: PIntArray;
begin
i := getArrayPtr^[1]; // ok
pi := @getArrayPtr^[1]; piCheck(pi); // modeDelphi: Error: Illegal qualifier
pi := @getArrayPtr()^[1]; piCheck(pi); // ok
pi := @(getArrayPtr^[1]); piCheck(pi); // modeDelphi: at runtime: pi gets wrong address
pi := @(getArrayPtr()^[1]); piCheck(pi); // ok

{$if defined( pointerMathOn)}
a := getArrayPtr[1]; // ok
pa := @getArrayPtr[1]; paCheck(pa); // ok
pa := @getArrayPtr()[1]; paCheck(pa); // ok
pa := @(getArrayPtr[1]); paCheck(pa); // ok
pa := @(getArrayPtr()[1]); paCheck(pa); // ok

{$elseif defined( autoderefOn)}
i := getArrayPtr[1]; // ok
pi := @getArrayPtr[1]; piCheck(pi); // ok
pi := @getArrayPtr()[1]; piCheck(pi); // ok
pi := @(getArrayPtr[1]); piCheck(pi); // ok
pi := @(getArrayPtr()[1]); piCheck(pi); // ok
{$endif}
end;

procedure testMethodResult;
var gate: TArrayGate;
i: integer; pi: PInteger;
a: TIntArray; pa: PIntArray;
begin
gate := TArrayGate.create;
i := gate.getArrayPtr^[1]; // ok
pi := @gate.getArrayPtr^[1]; piCheck(pi); // ok
pi := @gate.getArrayPtr()^[1]; piCheck(pi); // ok
pi := @(gate.getArrayPtr^[1]); piCheck(pi); // ok
pi := @(gate.getArrayPtr()^[1]); piCheck(pi); // ok

{$if defined( pointerMathOn)}
a := gate.getArrayPtr[1]; // ok
pa := @gate.getArrayPtr[1]; paCheck(pa); // Error: Illegal qualifier
pa := @gate.getArrayPtr()[1]; paCheck(pa); // ok
pa := @(gate.getArrayPtr[1]); paCheck(pa); // Fatal: Syntax error, ")" expected but "[" found
pa := @(gate.getArrayPtr()[1]); paCheck(pa); // ok

{$elseif defined( autoderefOn)}
i := gate.getArrayPtr[1]; // ok
pi := @gate.getArrayPtr[1]; piCheck(pi); // Error: Illegal qualifier
pi := @gate.getArrayPtr()[1]; piCheck(pi); // ok
pi := @(gate.getArrayPtr[1]); piCheck(pi);  // Fatal: Syntax error, ")" expected but "[" found
pi := @(gate.getArrayPtr()[1]); piCheck(pi); // ok
{$endif}

gate.free;
end;

begin
pints := @ints[0];
ints[0,1] := 123;
ints[1,1] := 456;
testArrayPtr;
testFuncResult;
testMethodResult;

writeLn('End');
readLn;
end.
project4.lpr (3,891 bytes)   

nanobit

2020-02-15 12:07

reporter   ~0121105

Having replaced TIntArray with TIntRecord; shows similar problems:
pi := @getRecordPtr^.y; // modeDelphi: Error: Illegal qualifier
pi := @(getRecordPtr^.y); // modeDelphi: Error: Illegal qualifier
pr := @gate.getRecordPtr[1]; // pointermath: Error: Illegal qualifier
pr := @(gate.getRecordPtr[1]); // pointermath: Fatal: Syntax error, ")" expected but "[" found
project4r.lpr (3,697 bytes)   
program project4r;

// ---- select defines ----
{$define modeDelphi}

{$ifdef modeDelphi}
    {$mode delphi}
    {$define autoderefOn} // defaultOn
    {.$define pointerMathOn} // optionalOn
{$else}
    {$mode objFpc}
    {$define pointerMathOn} // defaultOn
    {.$define autoderefOn} // optionalOn
{$endif}

// ---- apply defines ----
{$ifdef autoderefOn}
    {$modeswitch autoderef}
{$else}
    {$modeswitch autoderef-}
{$endif}

{$ifdef pointerMathOn}
    {$PointerMath on}
{$else}
    {$PointerMath off}
{$endif}

type
TIntRecord = record x, y: integer; end;
PIntRecord = ^TIntRecord;
var
records: array[0..1] of TIntRecord; // consecutive records to test pointerMath
pRecord: PIntRecord;

function getRecordPtr: PIntRecord;
begin result := @records[0]; end;

type TRecordGate = class(Tobject)
function getRecordPtr: PIntRecord;
end;

function TRecordGate.getRecordPtr: PIntRecord;
begin result := @records[0]; end;

procedure piCheck( pi: PInteger);
begin
writeLn( (pi = @records[0].y) and (pi^ = records[0].y));
end;

procedure prCheck( pr: PIntRecord);
begin
writeLn( (pr = @records[1]));
end;

procedure testRecordPtr;
var i: integer; pi: PInteger;
r: TIntRecord; pr: PIntRecord;
begin
i := pRecord^.y; // ok
pi := @pRecord^.y; piCheck(pi); // ok
pi := @(pRecord^.y); piCheck(pi); // ok

{$ifdef pointerMathOn}
r := pRecord[1]; // ok
pr := @pRecord[1]; prCheck(pr); // ok
pr := @(pRecord[1]); prCheck(pr); // ok
{$endif}

{$ifdef autoderefOn}
i := pRecord.y; // ok
pi := @pRecord.y; piCheck(pi); // ok
pi := @(pRecord.y); piCheck(pi); // ok
{$endif}
end;

procedure testFuncResult;
var i: integer; pi: PInteger;
r: TIntRecord; pr: PIntRecord;
begin
i := getRecordPtr^.y; // ok
pi := @getRecordPtr^.y; piCheck(pi); // modeDelphi: Error: Illegal qualifier
pi := @getRecordPtr()^.y; piCheck(pi); // ok
pi := @(getRecordPtr^.y); piCheck(pi); // modeDelphi: Error: Illegal qualifier
pi := @(getRecordPtr()^.y); piCheck(pi); // ok

{$ifdef pointerMathOn}
r := getRecordPtr[1]; // ok
pr := @getRecordPtr[1]; prCheck(pr); // ok
pr := @getRecordPtr()[1]; prCheck(pr); // ok
pr := @(getRecordPtr[1]); prCheck(pr); // ok
pr := @(getRecordPtr()[1]); prCheck(pr); // ok
{$endif}

{$ifdef autoderefOn}
i := getRecordPtr.y; // ok
pi := @getRecordPtr.y; piCheck(pi); // ok
pi := @getRecordPtr().y; piCheck(pi); // ok
pi := @(getRecordPtr.y); piCheck(pi); // ok
pi := @(getRecordPtr().y); piCheck(pi); // ok
{$endif}
end;

procedure testMethodResult;
var gate: TRecordGate;
i: integer; pi: PInteger;
r: TIntRecord; pr: PIntRecord;
begin
gate := TRecordGate.create;
i := gate.getRecordPtr^.y; // ok
pi := @gate.getRecordPtr^.y; piCheck(pi); // ok
pi := @gate.getRecordPtr()^.y; piCheck(pi); // ok
pi := @(gate.getRecordPtr^.y); piCheck(pi); // ok
pi := @(gate.getRecordPtr()^.y); piCheck(pi); // ok

{$ifdef pointerMathOn}
r := gate.getRecordPtr[1]; // ok
pr := @gate.getRecordPtr[1]; prCheck(pr); // Error: Illegal qualifier
pr := @gate.getRecordPtr()[1]; prCheck(pr); // ok
pr := @(gate.getRecordPtr[1]); prCheck(pr); // Fatal: Syntax error, ")" expected but "[" found
pr := @(gate.getRecordPtr()[1]); prCheck(pr); // ok
{$endif}

{$ifdef autoderefOn}
i := gate.getRecordPtr.y; // ok
pi := @gate.getRecordPtr.y; piCheck(pi); // ok
pi := @gate.getRecordPtr().y; piCheck(pi); // ok
pi := @(gate.getRecordPtr.y); piCheck(pi);  // ok
pi := @(gate.getRecordPtr().y); piCheck(pi); // ok
{$endif}

gate.free;
end;

begin
pRecord := @records[0];
records[0].y := 123;
records[1].y := 456;
testRecordPtr;
testFuncResult;
testMethodResult;

writeLn('End');
readLn;
end.
project4r.lpr (3,697 bytes)   

Issue History

Date Modified Username Field Change
2020-02-04 19:49 nanobit New Issue
2020-02-04 19:49 nanobit File Added: project2.lpr
2020-02-06 11:04 Thaddy de Koning Note Added: 0120897
2020-02-06 11:37 Thaddy de Koning Note Added: 0120898
2020-02-06 11:38 Thaddy de Koning Note Edited: 0120898 View Revisions
2020-02-06 12:46 nanobit Note Added: 0120899
2020-02-09 13:58 nanobit File Added: project3.lpr
2020-02-09 13:58 nanobit Note Added: 0120966
2020-02-11 08:13 nanobit File Added: project4.lpr
2020-02-11 08:13 nanobit Note Added: 0121010
2020-02-15 12:07 nanobit File Added: project4r.lpr
2020-02-15 12:07 nanobit Note Added: 0121105