mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-09-02 12:50:27 +02:00
* when determining the best candidates for overloaded method calls, apply
the scope penalty relative to the nearest symtable that contains one of the applicable overloads, rather than relative to the nearest symtable that simply contains a method with this name (based on patch by Maciej Izak, mantis #25607) git-svn-id: trunk@35089 -
This commit is contained in:
parent
04f7e47df7
commit
18077d9530
6
.gitattributes
vendored
6
.gitattributes
vendored
@ -14953,6 +14953,12 @@ tests/webtbs/tw25603.pp svneol=native#text/pascal
|
||||
tests/webtbs/tw25604.pp svneol=native#text/pascal
|
||||
tests/webtbs/tw25605.pp svneol=native#text/pascal
|
||||
tests/webtbs/tw25606.pp svneol=native#text/pascal
|
||||
tests/webtbs/tw25607a.pp -text svneol=native#text/plain
|
||||
tests/webtbs/tw25607b.pp -text svneol=native#text/plain
|
||||
tests/webtbs/tw25607c.pp -text svneol=native#text/plain
|
||||
tests/webtbs/tw25607d.pp -text svneol=native#text/plain
|
||||
tests/webtbs/tw25607e.pp -text svneol=native#text/plain
|
||||
tests/webtbs/tw25607f.pp -text svneol=native#text/plain
|
||||
tests/webtbs/tw2561.pp svneol=native#text/plain
|
||||
tests/webtbs/tw25610.pp -text svneol=native#text/plain
|
||||
tests/webtbs/tw25685.pp svneol=native#text/pascal
|
||||
|
@ -73,6 +73,7 @@ interface
|
||||
procedure collect_overloads_in_struct(structdef:tabstractrecorddef;ProcdefOverloadList:TFPObjectList;searchhelpers,anoninherited:boolean;spezcontext:tspecializationcontext);
|
||||
procedure collect_overloads_in_units(ProcdefOverloadList:TFPObjectList; objcidcall,explicitunit: boolean;spezcontext:tspecializationcontext);
|
||||
procedure create_candidate_list(ignorevisibility,allowdefaultparas,objcidcall,explicitunit,searchhelpers,anoninherited:boolean;spezcontext:tspecializationcontext);
|
||||
procedure calc_distance(st_root:tsymtable;objcidcall: boolean);
|
||||
function proc_add(st:tsymtable;pd:tprocdef;objcidcall: boolean):pcandidate;
|
||||
function maybe_specialize(var pd:tprocdef;spezcontext:tspecializationcontext):boolean;
|
||||
public
|
||||
@ -2549,10 +2550,93 @@ implementation
|
||||
end;
|
||||
end;
|
||||
|
||||
calc_distance(st,objcidcall);
|
||||
|
||||
ProcdefOverloadList.Free;
|
||||
end;
|
||||
|
||||
|
||||
procedure tcallcandidates.calc_distance(st_root: tsymtable; objcidcall: boolean);
|
||||
var
|
||||
pd:tprocdef;
|
||||
candidate:pcandidate;
|
||||
objdef: tobjectdef;
|
||||
st: tsymtable;
|
||||
begin
|
||||
{ Give a small penalty for overloaded methods not defined in the
|
||||
current class/unit }
|
||||
st:=nil;
|
||||
if objcidcall or
|
||||
not assigned(st_root) or
|
||||
not assigned(st_root.defowner) or
|
||||
(st_root.defowner.typ<>objectdef) then
|
||||
st:=st_root
|
||||
else
|
||||
repeat
|
||||
{ In case of a method, st_root is the symtable of the first found
|
||||
procsym with the called method's name, but this procsym may not
|
||||
contain any of the overloads that match the used parameters (which
|
||||
are the procdefs that have been collected as candidates) -> walk
|
||||
up the class hierarchy and look for the first class that actually
|
||||
defines at least one of the candidate procdefs.
|
||||
|
||||
The reason is that we will penalise methods in other classes/
|
||||
symtables, so if we pick a symtable that does not contain any of
|
||||
the candidates, this won't help with picking the best/
|
||||
most-inner-scoped one (since all of them will be penalised) }
|
||||
candidate:=FCandidateProcs;
|
||||
|
||||
{ the current class contains one of the candidates? }
|
||||
while assigned(candidate) do
|
||||
begin
|
||||
pd:=candidate^.data;
|
||||
if pd.owner=st_root then
|
||||
begin
|
||||
{ yes -> choose this class }
|
||||
st:=st_root;
|
||||
break;
|
||||
end;
|
||||
candidate:=candidate^.next;
|
||||
end;
|
||||
|
||||
{ None found -> go to parent class }
|
||||
if not assigned(st) then
|
||||
begin
|
||||
if not assigned(st_root.defowner) then
|
||||
internalerror(201605301);
|
||||
|
||||
{ no more parent class -> take current class as root anyway
|
||||
(could maybe happen in case of a class helper?) }
|
||||
if not assigned(tobjectdef(st_root.defowner).childof) then
|
||||
begin
|
||||
st:=st_root;
|
||||
break;
|
||||
end;
|
||||
|
||||
st_root:=tobjectdef(st_root.defowner).childof.symtable;
|
||||
end;
|
||||
until assigned(st);
|
||||
|
||||
candidate:=FCandidateProcs;
|
||||
{ when calling Objective-C methods via id.method, then the found
|
||||
procsym will be inside an arbitrary ObjectSymtable, and we don't
|
||||
want to give the methods of that particular objcclass precedence
|
||||
over other methods, so instead check against the symtable in
|
||||
which this objcclass is defined }
|
||||
if objcidcall then
|
||||
st:=st.defowner.owner;
|
||||
while assigned(candidate) do
|
||||
begin
|
||||
pd:=candidate^.data;
|
||||
|
||||
if st<>pd.owner then
|
||||
candidate^.ordinal_distance:=candidate^.ordinal_distance+1.0;
|
||||
|
||||
candidate:=candidate^.next;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function tcallcandidates.proc_add(st:tsymtable;pd:tprocdef;objcidcall: boolean):pcandidate;
|
||||
var
|
||||
defaultparacnt : integer;
|
||||
@ -2580,17 +2664,6 @@ implementation
|
||||
dec(result^.firstparaidx,defaultparacnt);
|
||||
end;
|
||||
end;
|
||||
{ Give a small penalty for overloaded methods not in
|
||||
defined the current class/unit }
|
||||
{ when calling Objective-C methods via id.method, then the found
|
||||
procsym will be inside an arbitrary ObjectSymtable, and we don't
|
||||
want togive the methods of that particular objcclass precedence over
|
||||
other methods, so instead check against the symtable in which this
|
||||
objcclass is defined }
|
||||
if objcidcall then
|
||||
st:=st.defowner.owner;
|
||||
if (st<>pd.owner) then
|
||||
result^.ordinal_distance:=result^.ordinal_distance+1.0;
|
||||
end;
|
||||
|
||||
|
||||
|
64
tests/webtbs/tw25607a.pp
Executable file
64
tests/webtbs/tw25607a.pp
Executable file
@ -0,0 +1,64 @@
|
||||
program E01;
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$MODE DELPHI}
|
||||
{$ENDIF}
|
||||
{$APPTYPE CONSOLE}
|
||||
|
||||
type
|
||||
TA = class
|
||||
constructor Create(A: Integer = 0); overload; virtual;
|
||||
end;
|
||||
|
||||
TB = class(TA)
|
||||
constructor Create(A: Integer); overload; override;
|
||||
end;
|
||||
|
||||
TClassB = class of TB;
|
||||
|
||||
var
|
||||
tacalled,
|
||||
tbcalled: boolean;
|
||||
|
||||
constructor TA.Create(A: Integer = 0);
|
||||
begin
|
||||
WriteLn('TA.Create');
|
||||
tacalled:=true;
|
||||
end;
|
||||
|
||||
constructor TB.Create(A: Integer);
|
||||
begin
|
||||
WriteLn('TB.Create');
|
||||
tbcalled:=true;
|
||||
end;
|
||||
|
||||
var
|
||||
B: TB;
|
||||
ClassB: TClassB;
|
||||
begin
|
||||
B := TB.Create; // TA.Create (VMT is not used
|
||||
// compiler can determine) -- in Delphi;
|
||||
// In FPC, because TB.Create is used, we
|
||||
// call TB.Create
|
||||
if tacalled then
|
||||
halt(1);
|
||||
if not tbcalled then
|
||||
halt(2);
|
||||
tbcalled:=false;
|
||||
|
||||
B.Create; // call TB.Create because of VMT rules
|
||||
B.Free;
|
||||
if tacalled then
|
||||
halt(3);
|
||||
if not tbcalled then
|
||||
halt(4);
|
||||
tbcalled:=false;
|
||||
|
||||
ClassB := TB;
|
||||
B := ClassB.Create; // call TB.Create because of VMT rules
|
||||
B.Free;
|
||||
if tacalled then
|
||||
halt(5);
|
||||
if not tbcalled then
|
||||
halt(6);
|
||||
end.
|
63
tests/webtbs/tw25607b.pp
Executable file
63
tests/webtbs/tw25607b.pp
Executable file
@ -0,0 +1,63 @@
|
||||
program E02;
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$MODE DELPHI}
|
||||
{$ENDIF}
|
||||
{$APPTYPE CONSOLE}
|
||||
|
||||
type
|
||||
TA = class
|
||||
constructor Create(A: Integer = 0); overload;
|
||||
end;
|
||||
|
||||
TB = class(TA)
|
||||
constructor Create(A: Integer); overload;
|
||||
end;
|
||||
|
||||
TClassB = class of TB;
|
||||
|
||||
var
|
||||
tacalled,
|
||||
tbcalled: boolean;
|
||||
|
||||
constructor TA.Create(A: Integer = 0);
|
||||
begin
|
||||
WriteLn('TA.Create');
|
||||
tacalled:=true;
|
||||
end;
|
||||
|
||||
constructor TB.Create(A: Integer);
|
||||
begin
|
||||
WriteLn('TB.Create');
|
||||
tbcalled:=true;
|
||||
end;
|
||||
|
||||
var
|
||||
B: TB;
|
||||
ClassB: TClassB;
|
||||
begin
|
||||
B := TB.Create; // TA.Create (VMT is not used
|
||||
// compiler can determine)
|
||||
if not tacalled then
|
||||
halt(1);
|
||||
if tbcalled then
|
||||
halt(2);
|
||||
tacalled:=false;
|
||||
|
||||
B.Create; // call TA.Create because of VMT rules
|
||||
B.Free;
|
||||
if not tacalled then
|
||||
halt(3);
|
||||
if tbcalled then
|
||||
halt(4);
|
||||
tacalled:=false;
|
||||
|
||||
ClassB := TB;
|
||||
B := ClassB.Create; // call TA.Create because of VMT rules
|
||||
B.Free;
|
||||
if not tacalled then
|
||||
halt(5);
|
||||
if tbcalled then
|
||||
halt(6);
|
||||
tacalled:=false;
|
||||
end.
|
80
tests/webtbs/tw25607c.pp
Executable file
80
tests/webtbs/tw25607c.pp
Executable file
@ -0,0 +1,80 @@
|
||||
program E03;
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$MODE DELPHI}
|
||||
{$ENDIF}
|
||||
{$APPTYPE CONSOLE}
|
||||
|
||||
type
|
||||
T0 = class
|
||||
class procedure Foo;
|
||||
end;
|
||||
|
||||
TA = class(T0)
|
||||
class procedure Foo(A: Integer = 0); overload; virtual;
|
||||
end;
|
||||
|
||||
TB = class(TA)
|
||||
class procedure Foo(A: Integer); overload; override;
|
||||
end;
|
||||
|
||||
TClassB = class of TB;
|
||||
|
||||
var
|
||||
t0called,
|
||||
tacalled,
|
||||
tbcalled: boolean;
|
||||
|
||||
class procedure T0.Foo();
|
||||
begin
|
||||
WriteLn('T0.Foo');
|
||||
t0called:=true;
|
||||
end;
|
||||
|
||||
class procedure TA.Foo(A: Integer = 0);
|
||||
begin
|
||||
WriteLn('TA.Foo');
|
||||
tacalled:=true;
|
||||
end;
|
||||
|
||||
class procedure TB.Foo(A: Integer);
|
||||
begin
|
||||
WriteLn('TB.Foo');
|
||||
tbcalled:=true;
|
||||
end;
|
||||
|
||||
var
|
||||
B: TB;
|
||||
ClassB: TClassB;
|
||||
begin
|
||||
TB.Foo; // call TA.Foo (VMT is not used, compiler can determine) -- on Delphi
|
||||
// on FPC: call TB.Foo because virtual method and VMT specified
|
||||
if t0called then
|
||||
halt(1);
|
||||
if tacalled then
|
||||
halt(2);
|
||||
if not tbcalled then
|
||||
halt(3);
|
||||
tbcalled:=false;
|
||||
|
||||
B := TB.Create;
|
||||
B.Foo; // call TB.Foo because of VMT rules
|
||||
B.Free;
|
||||
if t0called then
|
||||
halt(4);
|
||||
if tacalled then
|
||||
halt(5);
|
||||
if not tbcalled then
|
||||
halt(6);
|
||||
tbcalled:=false;
|
||||
|
||||
ClassB := TB;
|
||||
ClassB.Foo; // call TB.Foo because of VMT rules
|
||||
if t0called then
|
||||
halt(7);
|
||||
if tacalled then
|
||||
halt(8);
|
||||
if not tbcalled then
|
||||
halt(9);
|
||||
tbcalled:=false;
|
||||
end.
|
78
tests/webtbs/tw25607d.pp
Executable file
78
tests/webtbs/tw25607d.pp
Executable file
@ -0,0 +1,78 @@
|
||||
program E04;
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$MODE DELPHI}
|
||||
{$ENDIF}
|
||||
{$APPTYPE CONSOLE}
|
||||
|
||||
type
|
||||
T0 = class
|
||||
class procedure Foo;
|
||||
end;
|
||||
|
||||
TA = class(T0)
|
||||
class procedure Foo(A: Integer = 0); overload;
|
||||
end;
|
||||
|
||||
TB = class(TA)
|
||||
class procedure Foo(A: Integer); overload;
|
||||
end;
|
||||
|
||||
TClassB = class of TB;
|
||||
|
||||
var
|
||||
t0called,
|
||||
tacalled,
|
||||
tbcalled: boolean;
|
||||
|
||||
class procedure T0.Foo();
|
||||
begin
|
||||
WriteLn('T0.Foo');
|
||||
t0called:=true;
|
||||
end;
|
||||
|
||||
class procedure TA.Foo(A: Integer = 0);
|
||||
begin
|
||||
WriteLn('TA.Foo');
|
||||
tacalled:=true;
|
||||
end;
|
||||
|
||||
class procedure TB.Foo(A: Integer);
|
||||
begin
|
||||
WriteLn('TB.Foo');
|
||||
tbcalled:=true;
|
||||
end;
|
||||
|
||||
var
|
||||
B: TB;
|
||||
ClassB: TClassB;
|
||||
begin
|
||||
TB.Foo; // call TA.Foo (VMT is not used, compiler can determine)
|
||||
if t0called then
|
||||
halt(1);
|
||||
if not tacalled then
|
||||
halt(2);
|
||||
if tbcalled then
|
||||
halt(3);
|
||||
tacalled:=false;
|
||||
|
||||
B := TB.Create;
|
||||
B.Foo; // call TA.Foo because of VMT rules
|
||||
B.Free;
|
||||
if t0called then
|
||||
halt(4);
|
||||
if not tacalled then
|
||||
halt(5);
|
||||
if tbcalled then
|
||||
halt(6);
|
||||
tacalled:=false;
|
||||
|
||||
ClassB := TB;
|
||||
ClassB.Foo; // call TA.Foo because of VMT rules
|
||||
if t0called then
|
||||
halt(7);
|
||||
if not tacalled then
|
||||
halt(8);
|
||||
if tbcalled then
|
||||
halt(9);
|
||||
end.
|
59
tests/webtbs/tw25607e.pp
Executable file
59
tests/webtbs/tw25607e.pp
Executable file
@ -0,0 +1,59 @@
|
||||
program E05;
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$MODE DELPHI}
|
||||
{$ENDIF}
|
||||
{$APPTYPE CONSOLE}
|
||||
|
||||
type
|
||||
T0 = class
|
||||
procedure Foo;
|
||||
end;
|
||||
|
||||
TA = class(T0)
|
||||
procedure Foo(A: Integer = 0); overload; virtual;
|
||||
end;
|
||||
|
||||
TB = class(TA)
|
||||
procedure Foo(A: Integer); overload; override;
|
||||
end;
|
||||
|
||||
TClassB = class of TB;
|
||||
|
||||
var
|
||||
t0called,
|
||||
tacalled,
|
||||
tbcalled: boolean;
|
||||
|
||||
procedure T0.Foo();
|
||||
begin
|
||||
WriteLn('T0.Foo');
|
||||
t0called:=true;
|
||||
end;
|
||||
|
||||
procedure TA.Foo(A: Integer = 0);
|
||||
begin
|
||||
WriteLn('TA.Foo');
|
||||
tacalled:=true;
|
||||
end;
|
||||
|
||||
procedure TB.Foo(A: Integer);
|
||||
begin
|
||||
WriteLn('TB.Foo');
|
||||
tbcalled:=true;
|
||||
end;
|
||||
|
||||
var
|
||||
B: TB;
|
||||
ClassB: TClassB;
|
||||
begin
|
||||
B := TB.Create;
|
||||
B.Foo; // call TB.Foo because of VMT rules
|
||||
B.Free;
|
||||
if t0called then
|
||||
halt(1);
|
||||
if tacalled then
|
||||
halt(2);
|
||||
if not tbcalled then
|
||||
halt(3);
|
||||
end.
|
59
tests/webtbs/tw25607f.pp
Executable file
59
tests/webtbs/tw25607f.pp
Executable file
@ -0,0 +1,59 @@
|
||||
program E06;
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$MODE DELPHI}
|
||||
{$ENDIF}
|
||||
{$APPTYPE CONSOLE}
|
||||
|
||||
type
|
||||
T0 = class
|
||||
procedure Foo;
|
||||
end;
|
||||
|
||||
TA = class(T0)
|
||||
procedure Foo(A: Integer = 0); overload;
|
||||
end;
|
||||
|
||||
TB = class(TA)
|
||||
procedure Foo(A: Integer); overload;
|
||||
end;
|
||||
|
||||
TClassB = class of TB;
|
||||
|
||||
var
|
||||
t0called,
|
||||
tacalled,
|
||||
tbcalled: boolean;
|
||||
|
||||
procedure T0.Foo();
|
||||
begin
|
||||
WriteLn('T0.Foo');
|
||||
t0called:=true;
|
||||
end;
|
||||
|
||||
procedure TA.Foo(A: Integer = 0);
|
||||
begin
|
||||
WriteLn('TA.Foo');
|
||||
tacalled:=true;
|
||||
end;
|
||||
|
||||
procedure TB.Foo(A: Integer);
|
||||
begin
|
||||
WriteLn('TB.Foo');
|
||||
tbcalled:=true;
|
||||
end;
|
||||
|
||||
var
|
||||
B: TB;
|
||||
ClassB: TClassB;
|
||||
begin
|
||||
B := TB.Create;
|
||||
B.Foo; // call TA.Foo because of VMT rules
|
||||
B.Free;
|
||||
if t0called then
|
||||
halt(1);
|
||||
if not tacalled then
|
||||
halt(2);
|
||||
if tbcalled then
|
||||
halt(3);
|
||||
end.
|
Loading…
Reference in New Issue
Block a user