Delphi Tokyo exception prevents setting function result

The Tokyo behaviour is correct. A function that raises an exception does not return a value. You have hitherto been relying on implementation detail.

Consider this code:

Result:=FuncTest;

This executes as follows:

  1. FuncTest is called.
  2. Result is assigned.

Now, because step 1 raises an exception, step 2 does not execute.

If anything, I would say that the behaviour you report from earlier versions is dubious. In this function:

function Test:integer;
begin
  Result:=0;
  try
    Result:=FuncTest;
  finally
    ShowMessage(Result.ToString);
  end;
end;

The statement Result:=FuncTest raises an exception and so Result should not be modified by that statement. Another way to think of it is that the function is called but the assignment is not executed.


One of the problems with the Delphi ABI is that function return values are sometimes implemented as implicit var parameters. Which means that the assignment may or may not happen. To demonstrate:

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

type
  TRec1 = record
    X1: NativeInt;
  end;

  TRec2 = record
    X1: NativeInt;
    X2: NativeInt;
  end;

function GetRec1: TRec1;
begin
  Result.X1 := 1;
  raise Exception.Create('');
end;

function GetRec2: TRec2;
begin
  Result.X1 := 1;
  raise Exception.Create('');
end;

procedure Main;
var
  Rec1: TRec1;
  Rec2: TRec2;
begin
  Rec1 := Default(TRec1);
  Writeln(Rec1.X1);
  try
    Rec1 := GetRec1;
  except
  end;
  Writeln(Rec1.X1);

  Rec2 := Default(TRec2);
  Writeln(Rec2.X1);
  try
    Rec2 := GetRec2;
  except
  end;
  Writeln(Rec2.X1);
end;

begin
  Main;
  Readln;
end.

This outputs:

0
0
0
1

Which is rather disappointing. It should not be possible to modify the caller's variable, but the use of an implicit var parameter rather than a value return allows this leakage. In my view this is a serious flaw in the design of the Delphi ABI, a flaw that you will not find in most other languages.


In your code, there is no var parameter because the return type is transferred in a register. In which case any Delphi version that outputs 2 is broken.

Fundamentally though, your code is mistaken in its expectations. If a function raises an exception then you must assume that the return value is ill-defined.

Finally, your code outputs 0 in XE3 and XE7, so I wonder how far back you need to go to see a value of 2.