Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overload resolution error with DrawText

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.

like image 321
Uli Gerhardt Avatar asked Jul 31 '14 07:07

Uli Gerhardt


1 Answers

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.
like image 110
J... Avatar answered Oct 01 '22 17:10

J...