6
votes

Consider this program:

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

procedure Foo;
begin
end;

type
  TProcedure = procedure;

const
  FooConst: TProcedure = Foo;

var
  FooVar: TProcedure = Foo;
  P: Pointer;

{$TYPEDADDRESS ON}

begin
  P := @Foo;
  Writeln(Format('%p', [P]));
  Writeln(Format('%p', [@FooConst]));
  Writeln(Format('%p', [@FooVar]));
  Writeln(Format('%p', [@Foo]));
  Readln;
end.

This program compiles and runs on XE3 and produces the following output:

00419FB8
00419FB8
00419FB8
00419FB8

On XE4 and later the program fails to compile, with error messages on both of these lines:

Writeln(Format('%p', [@FooConst]));
Writeln(Format('%p', [@FooVar]));
[dcc32 Error] E2250 There is no overloaded version of 'Format' that can be called
with these arguments

On XE4, XE5 and XE6, the program compiles when $TYPEDADDRESS is switched off. On XE7, the program fails to compile irrespective of the setting of $TYPEDADDRESS.

Is this a compiler bug? Or am I using incorrect syntax to obtain the address of a procedure?

2
Addr(FooConst) and Addr(FooVar)works in XE6. System.Addr is unaffected by the $T directive. Don't have XE7 at hand right now.LU RD
@LURD Addr(...) compiles and run correctly in XE7 too. Which is odd and points finger at compiler bug. Thanks.David Heffernan
FWIW, Pointer(@FooConst) works as well in XE6 and XE7.LU RD
@LURD Yes, I've observed that tooDavid Heffernan

2 Answers

4
votes

I believe that this is a compiler bug and have submitted a QC report: QC#127814.

As a work around you can use either of the following:

  1. Use addr() rather than the @ operator.
  2. Cast @FooVar or @FooConst to Pointer, e.g. Pointer(@FooVar).
1
votes

I think that the behaviour of the new compiler XE7 is more consistent with the specification and the error need to be shown in this case, since the {$TYPEDADDRESS ON} enforce the @ operator to return a typed pointer and the Format function instead gets as input an untyped generic pointer.

Since the the purpose of the {$TYPEDADDRESS ON} is encouraging careful use of pointers, catching at compile time unsafe pointer assignments, it is right that if the function expects a generic untyped pointer(and in this case make sense because the function purpose is to print the address of it - so no need to have typed pointer to retrieve its address), the compiler will catch an error in the case of a typed pointer is passed, the behaviour is consistent with the specification.

I think that in this case the right way to go(based on the documentation) would be :

  Writeln(Format('%p', [Addr(FooConst)]));
  Writeln(Format('%p', [Addr(FooVar)]));

since the Addr function always returns an untyped Pointer that is what the Format with %p expects and needs.

What I assume is that in previous versions the compiler, in a case like this one, used to perform an automatic cast : Pointer(@FooConst) , but it doesn't make too much sense because of the {$TYPEDADDRESS ON} directive .