View Issue Details

IDProjectCategoryView StatusLast Update
0038678FPCCompilerpublic2021-04-28 07:54
ReporterZeljko Avramovic Assigned To 
PrioritynormalSeverityminorReproducibilityalways
Status newResolutionopen 
PlatformallOSall 
Product Version3.3.1 
Summary0038678: Type helpers do not work properly for subrange types
DescriptionWriting to a type helper property does not work for subrange types. Attached snippet demonstrates this behavior.
Steps To Reproduceprogram subrange-type-helper-test;
 
{$mode delphi} {$H+}
{$modeswitch typehelpers}
 
uses
  cthreads;
 
type
  TNewJ1939PGN = 0..(1 shl 18)-1; // does not work with subrange, but works if type is changed to dword
 
  TNewJ1939CanID = bitpacked record
    SA: byte; // if PGN was a record, bit packing would not work and
    PGN: TNewJ1939PGN; // Priority bits would not be put immediately after PGN bits
    Priority: 0..7; // since PGN record would then be byte aligned by pascal
  end; // and there is no solution for that, so let's try type helpers instead
 
  TNewJ1939PGNHelper = type helper for TNewJ1939PGN
  private
    function GetPS: byte;
    procedure SetPS(const ANewPS: byte);
  public
    property PS: byte read GetPS write SetPS;
  end;
 
function TNewJ1939PGNHelper.GetPS: byte;
begin
  Result := Self and $FF; // and %00 00000000 11111111 (PS is in lowest 8 bits)
end;
 
procedure TNewJ1939PGNHelper.SetPS(const ANewPS: byte);
begin // debug says that Self was 100 and becomes 255, but PGN value is not changed
  Self := (Self and $3FF00) or ANewPS; // and %11 11111111 00000000
end; // If I change type TNewJ1939PGN to dword, although debug says that Self becomes 255 it does not propagate further
 
var
  NewJ1939CanID: TNewJ1939CanID;
begin
  NewJ1939CanID.PGN := 100;
  NewJ1939CanID.PGN.PS := 255;
  WriteLn('BitSizeOf(NewJ1939CanID) = ', BitSizeOf(NewJ1939CanID));
  WriteLn('BitSizeOf(NewJ1939CanID.SA) = ', BitSizeOf(NewJ1939CanID.SA));
  WriteLn('BitSizeOf(NewJ1939CanID.PGN) = ', BitSizeOf(NewJ1939CanID.PGN));
  WriteLn('BitSizeOf(NewJ1939CanID.Priority) = ', BitSizeOf(NewJ1939CanID.Priority));
  WriteLn('NewJ1939CanID.PGN = ', NewJ1939CanID.PGN); // this should show 255 but it shows 100 instead
  WriteLn('NewJ1939CanID.PGN.PS = ', NewJ1939CanID.PGN.PS); // this should show 255 but it shows 100 instead
end.
TagsNo tags attached.
Fixed in Revision
FPCOldBugId
FPCTarget
Attached Files

Activities

Zeljko Avramovic

2021-04-07 09:28

reporter   ~0130150

Reported helper bug for subrange types is still there, but I have found a work around for my use case:
https://forum.lazarus.freepascal.org/index.php/topic,53879.msg400796.html#msg400796

Zeljko Avramovic

2021-04-28 07:44

reporter   ~0130634

Here is a much better example. It shows that type helper does not work as expected when type is used in a record, and works well when the same type is used without a record:

program testtypehelperbug;
{$mode delphi} {$H+}
{$modeswitch typehelpers}

type
  TNewJ1939PGN = 0..(1 shl 18)-1;

  TNewJ1939CanID = bitpacked record
    SA: byte;
    PGN: TNewJ1939PGN;
    Priority: 0..7;
  end;

  TNewJ1939PGNHelper = type helper for TNewJ1939PGN
  private
    function GetPS: byte;
    procedure SetPS(const ANewPS: byte);
  public
    property PS: byte read GetPS write SetPS;
  end;

function TNewJ1939PGNHelper.GetPS: byte;
begin
  Result := Self and $FF; // and %00 00000000 11111111 (PS is in lowest 8 bits)
end;

procedure TNewJ1939PGNHelper.SetPS(const ANewPS: byte); // works well only when type is not used in a record
begin // debug says that Self was 100 and becomes 255, but PGN value is not changed
  Self := (Self and $3FF00) or ANewPS; // and %11 11111111 00000000
end; // If I change type TNewJ1939PGN to dword, although debug says that Self becomes 255 it does not propagate further

var
  NewJ1939CanID: TNewJ1939CanID;
  PGN: TNewJ1939PGN;
begin
  PGN := 100;
  PGN.PS := 255;
  WriteLn('PGN = ', PGN); // PGN is 255 as it should be
  WriteLn('PGN.PS = ', PGN.PS); // PGN.PS is 255 as it should be

  NewJ1939CanID.PGN := 100;
  NewJ1939CanID.PGN.PS := 255;
  WriteLn('NewJ1939CanID.PGN = ', NewJ1939CanID.PGN); // PGN is 100 (WRONG!!!)
  WriteLn('NewJ1939CanID.PGN.PS = ', NewJ1939CanID.PGN.PS); // PGN.PS is 100 (WRONG!!!)
end.

Zeljko Avramovic

2021-04-28 07:54

reporter   ~0130635

Ah yes. If you change TNewJ1939CanID from bitpacked record to plain record, it works without a bug. So, it seams that bitpacked records are only affected.

Issue History

Date Modified Username Field Change
2021-03-29 06:45 Zeljko Avramovic New Issue
2021-04-07 09:28 Zeljko Avramovic Note Added: 0130150
2021-04-28 07:44 Zeljko Avramovic Note Added: 0130634
2021-04-28 07:54 Zeljko Avramovic Note Added: 0130635