While porting some code from D2007 to XE2 I got a compiler error I can't understand. Please see the following sample:
procedure TForm1.FormPaint(Sender: TObject);
var
c: Char;
pc: PChar;
r: TRect;
begin
c := '1';
pc := @c;
r := Bounds(100, 100, 100, 100);
DrawText(Canvas.Handle, pc, 1, r, DT_SINGLELINE or DT_NOCLIP); //1
{$TYPEDADDRESS OFF}
DrawText(Canvas.Handle, @c, 1, r, DT_SINGLELINE or DT_NOCLIP); //2
{$TYPEDADDRESS ON}
DrawText(Canvas.Handle, @c, 1, r, DT_SINGLELINE or DT_NOCLIP); //3
DrawText(Canvas.Handle, PChar(@c), 1, r, DT_SINGLELINE or DT_NOCLIP); //4
end;
D2007 compiles that without a problem. The XE2 compiler rejects the line marked //3
with
[DCC Fehler] Unit1.pas(38): E2010 Inkompatible Typen: 'string' und 'Pointer'
I guess that is due to the newly added DrawText
overloads accepting Delphi strings.
Can you explain that error? It's no big problem as I have a workaround (explicit casting), but I'm curious. Is the error still present in later Delphi versions?
Edit: I'm asking whether there is an error in the compiler, not for an explanation why it is there. It might well be that I overlooked a valid reason for the compiler to reject my code.
This doesn't seem to be a normal overload resolution situation. DrawText
is defined twice as :
function DrawText(hDC: HDC;
lpString: PWideChar;
nCount: Integer;
var lpRect: TRect;
uFormat: UINT): Integer; external user32 name 'DrawTextW';
function DrawText(hDC: HDC;
const lpString: UnicodeString;
nCount: Integer;
var lpRect: TRect; uFormat: UINT): Integer;
begin
Result := Winapi.Windows.DrawText(hDC,
PWideChar(lpString),
nCount,
lpRect,
uFormat);
end;
With {$TYPEDADDRESS OFF}
it seems that a ^Char
is interpreted by the compiler as an untyped pointer
that is never compatible with a declared type of PChar
while @c
does seem to resolve to a PChar
ok. This seems at odds with the notion that {$TYPEDADDRESS OFF}
is meant to make all pointers type-agnostic. It seems that PChar
and ^Char
are somehow treated differently in the compiler than other pointers.
With {$TYPEDADDRESS ON}
both @c
and ^Char
become equivalent, but curiously are accepted as arguments so long as there is no overload resolution to sort out.
In both cases it seems that overload resolutions are finalized before type-compatibility is fully established. I'm not sure I'd call it a bug, though... it seems like behaviour that would be tricky to change without causing problems.
SSCCE
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
{$DEFINE OVLD}
{$IFDEF OVLD}
procedure Test(s:string); overload;
begin
end;
{$ENDIF}
procedure Test(x:PChar); {$IFDEF OVLD}overload; {$ENDIF}
begin
end;
var
c : Char;
pc : ^Char;
begin
{$TYPEDADDRESS OFF}
Test(@c);
Test(pc); //OVLD - Incompatible types : 'string'-'pointer'
//No OVLD - Incompat. types : 'PWideChar'-'pointer'
{$TYPEDADDRESS ON}
Test(@c); //OVLD - Incompatible types : 'string'-'pointer'
//No OVLD - OK
Test(pc); //OVLD - Incompatible types : 'string'-'pointer'
//No OVLD - OK
end.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With