10

The following code generates an exception (c0000005 ACCESS_VIOLATION) in Delphi 10.3.1 only when compiled to 64-bit.

However, the same code does not generate an exception in Delphi 10.3.1 when compiled to 32 bits. Also, it does not fail in Delphi 10.2.3 when compiled to either 32 bits or 64 bits.

program CrashOn64;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

type
  TMyBaseClass = class
  protected
    procedure Setup(aParams: array of const); virtual;
  public
  end;

type
  TMyWorkClass = class(TMyBaseClass)
  protected
    procedure DoSetup; virtual;
  public
    procedure Setup(aParams: array of const); override;
  end;

{ TMyBaseClass }

procedure TMyBaseClass.Setup(aParams: array of const);
begin
end;

{ TMyWorkClass }

procedure TMyWorkClass.DoSetup;
begin
  inherited;   
end;

procedure TMyWorkClass.Setup(aParams: array of const);
begin
  inherited;
  DoSetup
end;

// main

var
  myClass: TMyWorkClass;
begin
  try
    myClass:=TMyWorkClass.Create;
    try
      myClass.Setup([123]); // <-- Crash on Windows 64-bit
      writeln('OK!')
    finally
      myClass.Free
    end
  except
    on E: Exception do Writeln(E.ClassName, ': ', E.Message);
  end;

  readln; // Wait for Enter key
end.

The issue seems to be in the argument type being array of const. The code still fails for 64 bits if we change array of constto array of integer, so it seems that the new Delphi compiler has an issue with arrays with an unknown number of parameters. We found the trick to avoid the compiler bug by creating a type for array of integer but this trick is not available for what we need array of const.

This is the assembler code generated for 64 bits in Delphi 10.3.1 accoding to the CPU View:

CrashOn64.dpr.41: inherited;
0000000000428888 488B7528         mov rsi,[rbp+$28]
000000000042888C 488D7D20         lea rdi,[rbp+$20]
0000000000428890 48B9FFFFFFFFFFFFFF1F mov rcx,$1fffffffffffffff <<< What????????
000000000042889A F348A5           rep movsq                     <<< Crashes here.
000000000042889D A5               movsd
000000000042889E 66A5             movsw
00000000004288A0 A4               movsb
00000000004288A1 488B4D50         mov rcx,[rbp+$50]
00000000004288A5 488D5520         lea rdx,[rbp+$20]
00000000004288A9 448B4560         mov r8d,[rbp+$60]
00000000004288AD E8CEFEFFFF       call TMyBaseClass.Setup

And this is the code generated for 64 bits in Delphi 10.2.3 for the same function:

CrashOn64.dpr.41: inherited;
0000000000427329 488B4D50         mov rcx,[rbp+$50]
000000000042732D 488B5528         mov rdx,[rbp+$28]
0000000000427331 448B4560         mov r8d,[rbp+$60]
0000000000427335 E8E6FEFFFF       call TMyBaseClass.Setup

Is this a 64-bit compiler bug in Delphi 10.3.1 or are we missing anything? Are there any workarounds?

J...
  • 28,957
  • 5
  • 61
  • 128
Pep
  • 1,905
  • 2
  • 24
  • 39
  • 7
    Surely this is a compiler bug. I think you need to submit a report to QualityPortal. – David Heffernan Apr 15 '19 at 15:10
  • One more reason not to upgrade to 10.3.1, thank you – Nasreddine Galfout Apr 15 '19 at 15:25
  • @NasreddineGalfout, we upgraded because of some improvements in High DPI support, and first impressions were in general good but this issue seems to be a showstopper. – Pep Apr 15 '19 at 15:29
  • Must be possible to work around it. For instance, consider a typed `Integer` constant. Does that work? – David Heffernan Apr 15 '19 at 15:44
  • `array of const` is effectively `array of TVarRec` - perhaps something has changed in 10.3.1 with variant records? What if you explicitly pass a valid `TVarRec` instead of the implicit `[123]`? – J... Apr 15 '19 at 15:58
  • [This](https://stackoverflow.com/questions/55583298) is another show stopper of backward compatibility – Nasreddine Galfout Apr 15 '19 at 16:43
  • 1
    FWIW, the Tokyo code also copies the array, but that code is listed under the begin line, not under inherited. And Tokyo uses the correct size. **But this is a bug in Rio and should be reported**. – Rudy Velthuis Apr 15 '19 at 17:41
  • 5
    OH NO, please not *another* open array bug! A while back ago, I found a nasty codegen bug in Delphi 5 that corrupts the call stack when open arrays are used in certain conditions, and that bug greatly affected Indy (which still supports Delphi 5) and was a major PITA to work around, to the point where the "fix" was so ugly I haven't wanted to release it publicly. If there is a *new* open array bug, I need to check Indy all over again :-( There are quite a few open arrays used in Indy. – Remy Lebeau Apr 15 '19 at 20:29

2 Answers2

14

This is bug and should be reported1. As mentioned in the question, it fails for every type of open array.

A workaround is to define the array as a const in the method:

procedure Setup(const aParams: array of const); 

Declaring the open array as const, passes the array by reference, while without the const, it will be passed by value as a copy. In this case, the Rio version fails.


1 It was reported as: Access violation when calling an inherited function with an open array parameter in Rio

LU RD
  • 32,988
  • 5
  • 71
  • 260
  • I notice the report at the Quality Portal dates from March 8. – Pep Apr 16 '19 at 09:29
  • @pep That's not surprising. Emb aren't great at fixing issues. I have a bug that they claim was fixed in Tokyo but the test code that I created for them still fails. – Graymatter Apr 16 '19 at 20:28
1

This bug is FAR more extensive, it doesn't handle VARed shortstrings to procedures/functions.

consider this SIMPLE code...
procedure Copyit(var s: shortstring); // VAR is important, pass by value ok
begin
   debugform(s); // break point here
end;

procedure TForm1.Button1Click(Sender: TObject);
var
stuff: shortstring;
begin
   stuff:= 'This is a demo string';
   CopyIt(stuff);
end;

In 32 bit mode, it generates code you would expect. In 64 bit mode, it generates code exactly as shown above, A huge memory move with rcx set to $1FFFFFFFFFFFFFFFF !!!

Lee Davis
  • 11
  • 1