mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-12 10:29:36 +02:00
2653 lines
100 KiB
ObjectPascal
2653 lines
100 KiB
ObjectPascal
{
|
|
Copyright (c) 1998-2002 by Florian Klaempfl
|
|
|
|
This unit exports some help routines for the type checking
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
****************************************************************************
|
|
}
|
|
unit htypechk;
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
cclasses,tokens,cpuinfo,
|
|
node,globtype,
|
|
symconst,symtype,symdef,symsym,symbase;
|
|
|
|
type
|
|
Ttok2nodeRec=record
|
|
tok : ttoken;
|
|
nod : tnodetype;
|
|
op_overloading_supported : boolean;
|
|
end;
|
|
|
|
pcandidate = ^tcandidate;
|
|
tcandidate = record
|
|
next : pcandidate;
|
|
data : tprocdef;
|
|
wrongparaidx,
|
|
firstparaidx : integer;
|
|
exact_count,
|
|
equal_count,
|
|
cl1_count,
|
|
cl2_count,
|
|
cl3_count,
|
|
cl4_count,
|
|
cl5_count,
|
|
coper_count : integer; { should be signed }
|
|
ordinal_distance : double;
|
|
invalid : boolean;
|
|
wrongparanr : byte;
|
|
end;
|
|
|
|
tcallcandidates = class
|
|
private
|
|
FProcsym : tprocsym;
|
|
FProcsymtable : tsymtable;
|
|
FOperator : ttoken;
|
|
FCandidateProcs : pcandidate;
|
|
FProcCnt : integer;
|
|
FParaNode : tnode;
|
|
FParaLength : smallint;
|
|
FAllowVariant : boolean;
|
|
procedure collect_overloads_in_class(ProcdefOverloadList:TFPObjectList);
|
|
procedure collect_overloads_in_units(ProcdefOverloadList:TFPObjectList);
|
|
procedure create_candidate_list(ignorevisibility,allowdefaultparas:boolean);
|
|
function proc_add(ps:tprocsym;pd:tprocdef):pcandidate;
|
|
public
|
|
constructor create(sym:tprocsym;st:TSymtable;ppn:tnode;ignorevisibility,allowdefaultparas:boolean);
|
|
constructor create_operator(op:ttoken;ppn:tnode);
|
|
destructor destroy;override;
|
|
procedure list(all:boolean);
|
|
{$ifdef EXTDEBUG}
|
|
procedure dump_info(lvl:longint);
|
|
{$endif EXTDEBUG}
|
|
procedure get_information;
|
|
function choose_best(var bestpd:tabstractprocdef; singlevariant: boolean):integer;
|
|
procedure find_wrong_para;
|
|
property Count:integer read FProcCnt;
|
|
end;
|
|
|
|
type
|
|
tregableinfoflag = (
|
|
// can be put in a register if it's the address of a var/out/const parameter
|
|
ra_addr_regable,
|
|
// orthogonal to above flag: the address of the node is taken and may
|
|
// possibly escape the block in which this node is declared (e.g. a
|
|
// local variable is passed as var parameter to another procedure)
|
|
ra_addr_taken);
|
|
tregableinfoflags = set of tregableinfoflag;
|
|
|
|
const
|
|
tok2nodes=24;
|
|
tok2node:array[1..tok2nodes] of ttok2noderec=(
|
|
(tok:_PLUS ;nod:addn;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_MINUS ;nod:subn;op_overloading_supported:true), { binary and unary overloading supported }
|
|
(tok:_STAR ;nod:muln;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_SLASH ;nod:slashn;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_EQUAL ;nod:equaln;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_GT ;nod:gtn;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_LT ;nod:ltn;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_GTE ;nod:gten;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_LTE ;nod:lten;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_SYMDIF ;nod:symdifn;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_STARSTAR;nod:starstarn;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_OP_AS ;nod:asn;op_overloading_supported:false), { binary overloading NOT supported }
|
|
(tok:_OP_IN ;nod:inn;op_overloading_supported:false), { binary overloading NOT supported }
|
|
(tok:_OP_IS ;nod:isn;op_overloading_supported:false), { binary overloading NOT supported }
|
|
(tok:_OP_OR ;nod:orn;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_OP_AND ;nod:andn;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_OP_DIV ;nod:divn;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_OP_NOT ;nod:notn;op_overloading_supported:true), { unary overloading supported }
|
|
(tok:_OP_MOD ;nod:modn;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_OP_SHL ;nod:shln;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_OP_SHR ;nod:shrn;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_OP_XOR ;nod:xorn;op_overloading_supported:true), { binary overloading supported }
|
|
(tok:_ASSIGNMENT;nod:assignn;op_overloading_supported:true), { unary overloading supported }
|
|
(tok:_UNEQUAL ;nod:unequaln;op_overloading_supported:false) { binary overloading NOT supported overload = instead }
|
|
);
|
|
|
|
{ true, if we are parsing stuff which allows array constructors }
|
|
allow_array_constructor : boolean = false;
|
|
|
|
function node2opstr(nt:tnodetype):string;
|
|
|
|
{ check operator args and result type }
|
|
function isbinaryoperatoroverloadable(treetyp:tnodetype;ld:tdef;lt:tnodetype;rd:tdef;rt:tnodetype) : boolean;
|
|
function isoperatoracceptable(pf : tprocdef; optoken : ttoken) : boolean;
|
|
function isunaryoverloaded(var t : tnode) : boolean;
|
|
function isbinaryoverloaded(var t : tnode) : boolean;
|
|
|
|
{ Register Allocation }
|
|
procedure make_not_regable(p : tnode; how: tregableinfoflags);
|
|
|
|
{ procvar handling }
|
|
function is_procvar_load(p:tnode):boolean;
|
|
procedure test_local_to_procvar(from_def:tprocvardef;to_def:tdef);
|
|
|
|
{ sets varsym varstate field correctly }
|
|
type
|
|
tvarstateflag = (vsf_must_be_valid,vsf_use_hints);
|
|
tvarstateflags = set of tvarstateflag;
|
|
procedure set_varstate(p:tnode;newstate:tvarstate;varstateflags:tvarstateflags);
|
|
|
|
{ sets the callunique flag, if the node is a vecn, }
|
|
{ takes care of type casts etc. }
|
|
procedure set_unique(p : tnode);
|
|
|
|
function valid_for_formal_var(p : tnode; report_errors: boolean) : boolean;
|
|
function valid_for_formal_const(p : tnode; report_errors: boolean) : boolean;
|
|
function valid_for_var(p:tnode; report_errors: boolean):boolean;
|
|
function valid_for_assignment(p:tnode; report_errors: boolean):boolean;
|
|
function valid_for_loopvar(p:tnode; report_errors: boolean):boolean;
|
|
function valid_for_addr(p : tnode; report_errors: boolean) : boolean;
|
|
|
|
function allowenumop(nt:tnodetype):boolean;
|
|
|
|
procedure check_hints(const srsym: tsym; const symoptions: tsymoptions);
|
|
|
|
procedure check_ranges(const location: tfileposinfo; source: tnode; destdef: tdef);
|
|
|
|
implementation
|
|
|
|
uses
|
|
sysutils,
|
|
systems,constexp,globals,
|
|
cutils,verbose,
|
|
symtable,
|
|
defutil,defcmp,
|
|
nbas,ncnv,nld,nmem,ncal,nmat,ninl,nutils,ncon,
|
|
cgbase,procinfo
|
|
;
|
|
|
|
type
|
|
TValidAssign=(Valid_Property,Valid_Void,Valid_Const,Valid_Addr,Valid_Packed);
|
|
TValidAssigns=set of TValidAssign;
|
|
|
|
|
|
function node2opstr(nt:tnodetype):string;
|
|
var
|
|
i : integer;
|
|
begin
|
|
result:='<unknown>';
|
|
for i:=1 to tok2nodes do
|
|
if tok2node[i].nod=nt then
|
|
begin
|
|
result:=tokeninfo^[tok2node[i].tok].str;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
|
|
function isbinaryoperatoroverloadable(treetyp:tnodetype;ld:tdef;lt:tnodetype;rd:tdef;rt:tnodetype) : boolean;
|
|
|
|
function internal_check(treetyp:tnodetype;ld:tdef;lt:tnodetype;rd:tdef;rt:tnodetype;var allowed:boolean):boolean;
|
|
begin
|
|
internal_check:=true;
|
|
case ld.typ of
|
|
formaldef,
|
|
recorddef,
|
|
variantdef :
|
|
begin
|
|
allowed:=true;
|
|
end;
|
|
procvardef :
|
|
begin
|
|
if (rd.typ in [pointerdef,procdef,procvardef]) then
|
|
begin
|
|
allowed:=false;
|
|
exit;
|
|
end;
|
|
allowed:=true;
|
|
end;
|
|
pointerdef :
|
|
begin
|
|
if ((rd.typ in [orddef,enumdef,pointerdef,classrefdef,procvardef]) or
|
|
is_class_or_interface(rd)) then
|
|
begin
|
|
allowed:=false;
|
|
exit;
|
|
end;
|
|
|
|
{ don't allow pchar+string }
|
|
if (is_pchar(ld) or is_pwidechar(ld)) and
|
|
((rd.typ=stringdef) or
|
|
is_pchar(rd) or
|
|
is_pwidechar(rd) or
|
|
is_chararray(rd) or
|
|
is_widechararray(rd)) then
|
|
begin
|
|
allowed:=false;
|
|
exit;
|
|
end;
|
|
allowed:=true;
|
|
end;
|
|
arraydef :
|
|
begin
|
|
{ not vector/mmx }
|
|
if ((cs_mmx in current_settings.localswitches) and
|
|
is_mmx_able_array(ld)) or
|
|
((cs_support_vectors in current_settings.globalswitches) and
|
|
is_vector(ld)) then
|
|
begin
|
|
allowed:=false;
|
|
exit;
|
|
end;
|
|
{ not chararray+[(wide)char,(wide)string,(wide)chararray] }
|
|
if (is_chararray(ld) or is_widechararray(ld) or
|
|
is_open_chararray(ld) or is_open_widechararray(ld))
|
|
and
|
|
((rd.typ in [stringdef,orddef,enumdef]) or
|
|
is_pchar(rd) or
|
|
is_pwidechar(rd) or
|
|
is_chararray(rd) or
|
|
is_widechararray(rd) or
|
|
is_open_chararray(rd) or
|
|
is_open_widechararray(rd) or
|
|
(rt=niln)) then
|
|
begin
|
|
allowed:=false;
|
|
exit;
|
|
end;
|
|
{ dynamic array compare with niln }
|
|
if ((is_dynamic_array(ld) and
|
|
(rt=niln)) or
|
|
(is_dynamic_array(ld) and is_dynamic_array(rd)))
|
|
and
|
|
(treetyp in [equaln,unequaln]) then
|
|
begin
|
|
allowed:=false;
|
|
exit;
|
|
end;
|
|
allowed:=true;
|
|
end;
|
|
objectdef :
|
|
begin
|
|
{ <> and = are defined for classes }
|
|
if (treetyp in [equaln,unequaln]) and
|
|
is_class_or_interface(ld) then
|
|
begin
|
|
allowed:=false;
|
|
exit;
|
|
end;
|
|
allowed:=true;
|
|
end;
|
|
stringdef :
|
|
begin
|
|
if (rd.typ in [orddef,enumdef,stringdef]) or
|
|
is_pchar(rd) or
|
|
is_pwidechar(rd) or
|
|
is_chararray(rd) or
|
|
is_widechararray(rd) or
|
|
is_open_chararray(rd) or
|
|
is_open_widechararray(rd) then
|
|
begin
|
|
allowed:=false;
|
|
exit;
|
|
end;
|
|
allowed:=true;
|
|
end;
|
|
else
|
|
internal_check:=false;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
allowed : boolean;
|
|
begin
|
|
{ power ** is always possible }
|
|
if (treetyp=starstarn) then
|
|
begin
|
|
isbinaryoperatoroverloadable:=true;
|
|
exit;
|
|
end;
|
|
{ order of arguments does not matter so we have to check also
|
|
the reversed order }
|
|
allowed:=false;
|
|
if not internal_check(treetyp,ld,lt,rd,rt,allowed) then
|
|
internal_check(treetyp,rd,rt,ld,lt,allowed);
|
|
isbinaryoperatoroverloadable:=allowed;
|
|
end;
|
|
|
|
|
|
function isunaryoperatoroverloadable(treetyp : tnodetype;ld : tdef) : boolean;
|
|
begin
|
|
result:=false;
|
|
case treetyp of
|
|
subn,
|
|
unaryminusn :
|
|
begin
|
|
if (ld.typ in [orddef,enumdef,floatdef]) then
|
|
exit;
|
|
|
|
{$ifdef SUPPORT_MMX}
|
|
if (cs_mmx in current_settings.localswitches) and
|
|
is_mmx_able_array(ld) then
|
|
exit;
|
|
{$endif SUPPORT_MMX}
|
|
|
|
result:=true;
|
|
end;
|
|
|
|
notn :
|
|
begin
|
|
if (ld.typ in [orddef,enumdef,floatdef]) then
|
|
exit;
|
|
|
|
{$ifdef SUPPORT_MMX}
|
|
if (cs_mmx in current_settings.localswitches) and
|
|
is_mmx_able_array(ld) then
|
|
exit;
|
|
{$endif SUPPORT_MMX}
|
|
|
|
result:=true;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function isoperatoracceptable(pf : tprocdef; optoken : ttoken) : boolean;
|
|
var
|
|
ld,rd : tdef;
|
|
i : longint;
|
|
eq : tequaltype;
|
|
conv : tconverttype;
|
|
pd : tprocdef;
|
|
oldcount,
|
|
count: longint;
|
|
parasym : tparavarsym;
|
|
begin
|
|
result:=false;
|
|
count := pf.parast.SymList.count;
|
|
|
|
oldcount:=count;
|
|
while count > 0 do
|
|
begin
|
|
parasym:=tparavarsym(pf.parast.SymList[count-1]);
|
|
if is_boolean(parasym.vardef) then
|
|
begin
|
|
if parasym.name='RANGECHECK' then
|
|
begin
|
|
Include(parasym.varoptions, vo_is_hidden_para);
|
|
Include(parasym.varoptions, vo_is_range_check);
|
|
Dec(count);
|
|
end
|
|
else if parasym.name='OVERFLOWCHECK' then
|
|
begin
|
|
Include(parasym.varoptions, vo_is_hidden_para);
|
|
Include(parasym.varoptions, vo_is_overflow_check);
|
|
Dec(count);
|
|
end
|
|
else
|
|
break;
|
|
end
|
|
else
|
|
break;
|
|
end;
|
|
if count<>oldcount then
|
|
pf.calcparas;
|
|
|
|
case count of
|
|
1 : begin
|
|
ld:=tparavarsym(pf.parast.SymList[0]).vardef;
|
|
{ assignment is a special case }
|
|
if optoken=_ASSIGNMENT then
|
|
begin
|
|
eq:=compare_defs_ext(ld,pf.returndef,nothingn,conv,pd,[cdo_explicit]);
|
|
result:=
|
|
(eq=te_incompatible) and
|
|
{ don't allow overloading assigning to custom shortstring
|
|
types, because we also don't want to differentiate based
|
|
on different shortstring types (e.g.,
|
|
"operator :=(const v: variant) res: shorstring" also
|
|
has to work for assigning a variant to a string[80])
|
|
}
|
|
(not is_shortstring(pf.returndef) or
|
|
(tstringdef(pf.returndef).len=255));
|
|
end
|
|
else
|
|
begin
|
|
for i:=1 to tok2nodes do
|
|
if tok2node[i].tok=optoken then
|
|
begin
|
|
result:=
|
|
tok2node[i].op_overloading_supported and
|
|
isunaryoperatoroverloadable(tok2node[i].nod,ld);
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
2 : begin
|
|
for i:=1 to tok2nodes do
|
|
if tok2node[i].tok=optoken then
|
|
begin
|
|
ld:=tparavarsym(pf.parast.SymList[0]).vardef;
|
|
rd:=tparavarsym(pf.parast.SymList[1]).vardef;
|
|
result:=
|
|
tok2node[i].op_overloading_supported and
|
|
isbinaryoperatoroverloadable(tok2node[i].nod,ld,nothingn,rd,nothingn);
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function isunaryoverloaded(var t : tnode) : boolean;
|
|
var
|
|
ld : tdef;
|
|
optoken : ttoken;
|
|
operpd : tprocdef;
|
|
ppn : tcallparanode;
|
|
candidates : tcallcandidates;
|
|
cand_cnt : integer;
|
|
begin
|
|
result:=false;
|
|
operpd:=nil;
|
|
|
|
{ load easier access variables }
|
|
ld:=tunarynode(t).left.resultdef;
|
|
if not isunaryoperatoroverloadable(t.nodetype,ld) then
|
|
exit;
|
|
|
|
{ operator overload is possible }
|
|
result:=true;
|
|
|
|
case t.nodetype of
|
|
notn:
|
|
optoken:=_OP_NOT;
|
|
unaryminusn:
|
|
optoken:=_MINUS;
|
|
else
|
|
begin
|
|
CGMessage(parser_e_operator_not_overloaded);
|
|
t:=cnothingnode.create;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
{ generate parameter nodes }
|
|
ppn:=ccallparanode.create(tunarynode(t).left.getcopy,nil);
|
|
ppn.get_paratype;
|
|
candidates:=tcallcandidates.create_operator(optoken,ppn);
|
|
|
|
{ stop when there are no operators found }
|
|
if candidates.count=0 then
|
|
begin
|
|
CGMessage(parser_e_operator_not_overloaded);
|
|
candidates.free;
|
|
ppn.free;
|
|
t:=cnothingnode.create;
|
|
exit;
|
|
end;
|
|
|
|
{ Retrieve information about the candidates }
|
|
candidates.get_information;
|
|
{$ifdef EXTDEBUG}
|
|
{ Display info when multiple candidates are found }
|
|
candidates.dump_info(V_Debug);
|
|
{$endif EXTDEBUG}
|
|
cand_cnt:=candidates.choose_best(tabstractprocdef(operpd),false);
|
|
|
|
{ exit when no overloads are found }
|
|
if cand_cnt=0 then
|
|
begin
|
|
CGMessage(parser_e_operator_not_overloaded);
|
|
candidates.free;
|
|
ppn.free;
|
|
t:=cnothingnode.create;
|
|
exit;
|
|
end;
|
|
|
|
{ Multiple candidates left? }
|
|
if cand_cnt>1 then
|
|
begin
|
|
CGMessage(type_e_cant_choose_overload_function);
|
|
{$ifdef EXTDEBUG}
|
|
candidates.dump_info(V_Hint);
|
|
{$else EXTDEBUG}
|
|
candidates.list(false);
|
|
{$endif EXTDEBUG}
|
|
{ we'll just use the first candidate to make the
|
|
call }
|
|
end;
|
|
candidates.free;
|
|
|
|
addsymref(operpd.procsym);
|
|
|
|
{ the nil as symtable signs firstcalln that this is
|
|
an overloaded operator }
|
|
t:=ccallnode.create(ppn,Tprocsym(operpd.procsym),nil,nil,[]);
|
|
|
|
{ we already know the procdef to use, so it can
|
|
skip the overload choosing in callnode.pass_typecheck }
|
|
tcallnode(t).procdefinition:=operpd;
|
|
end;
|
|
|
|
|
|
function isbinaryoverloaded(var t : tnode) : boolean;
|
|
var
|
|
rd,ld : tdef;
|
|
optoken : ttoken;
|
|
operpd : tprocdef;
|
|
ht : tnode;
|
|
ppn : tcallparanode;
|
|
candidates : tcallcandidates;
|
|
cand_cnt : integer;
|
|
begin
|
|
isbinaryoverloaded:=false;
|
|
operpd:=nil;
|
|
{ load easier access variables }
|
|
ld:=tbinarynode(t).left.resultdef;
|
|
rd:=tbinarynode(t).right.resultdef;
|
|
if not isbinaryoperatoroverloadable(t.nodetype,ld,tbinarynode(t).left.nodetype,rd,tbinarynode(t).right.nodetype) then
|
|
exit;
|
|
|
|
{ operator overload is possible }
|
|
result:=true;
|
|
|
|
case t.nodetype of
|
|
equaln,
|
|
unequaln :
|
|
optoken:=_EQUAL;
|
|
addn:
|
|
optoken:=_PLUS;
|
|
subn:
|
|
optoken:=_MINUS;
|
|
muln:
|
|
optoken:=_STAR;
|
|
starstarn:
|
|
optoken:=_STARSTAR;
|
|
slashn:
|
|
optoken:=_SLASH;
|
|
ltn:
|
|
optoken:=_LT;
|
|
gtn:
|
|
optoken:=_GT;
|
|
lten:
|
|
optoken:=_LTE;
|
|
gten:
|
|
optoken:=_GTE;
|
|
symdifn :
|
|
optoken:=_SYMDIF;
|
|
modn :
|
|
optoken:=_OP_MOD;
|
|
orn :
|
|
optoken:=_OP_OR;
|
|
xorn :
|
|
optoken:=_OP_XOR;
|
|
andn :
|
|
optoken:=_OP_AND;
|
|
divn :
|
|
optoken:=_OP_DIV;
|
|
shln :
|
|
optoken:=_OP_SHL;
|
|
shrn :
|
|
optoken:=_OP_SHR;
|
|
else
|
|
begin
|
|
CGMessage(parser_e_operator_not_overloaded);
|
|
t:=cnothingnode.create;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
{ generate parameter nodes }
|
|
ppn:=ccallparanode.create(tbinarynode(t).right.getcopy,ccallparanode.create(tbinarynode(t).left.getcopy,nil));
|
|
ppn.get_paratype;
|
|
candidates:=tcallcandidates.create_operator(optoken,ppn);
|
|
|
|
{ for commutative operators we can swap arguments and try again }
|
|
if (candidates.count=0) and
|
|
not(optoken in [_OP_SHL,_OP_SHR,_OP_DIV,_OP_MOD,_STARSTAR,_SLASH,_MINUS]) then
|
|
begin
|
|
candidates.free;
|
|
reverseparameters(ppn);
|
|
{ reverse compare operators }
|
|
case optoken of
|
|
_LT:
|
|
optoken:=_GTE;
|
|
_GT:
|
|
optoken:=_LTE;
|
|
_LTE:
|
|
optoken:=_GT;
|
|
_GTE:
|
|
optoken:=_LT;
|
|
end;
|
|
candidates:=tcallcandidates.create_operator(optoken,ppn);
|
|
end;
|
|
|
|
{ stop when there are no operators found }
|
|
if candidates.count=0 then
|
|
begin
|
|
CGMessage(parser_e_operator_not_overloaded);
|
|
candidates.free;
|
|
ppn.free;
|
|
t:=cnothingnode.create;
|
|
exit;
|
|
end;
|
|
|
|
{ Retrieve information about the candidates }
|
|
candidates.get_information;
|
|
{$ifdef EXTDEBUG}
|
|
{ Display info when multiple candidates are found }
|
|
candidates.dump_info(V_Debug);
|
|
{$endif EXTDEBUG}
|
|
cand_cnt:=candidates.choose_best(tabstractprocdef(operpd),false);
|
|
|
|
{ exit when no overloads are found }
|
|
if cand_cnt=0 then
|
|
begin
|
|
CGMessage(parser_e_operator_not_overloaded);
|
|
candidates.free;
|
|
ppn.free;
|
|
t:=cnothingnode.create;
|
|
exit;
|
|
end;
|
|
|
|
{ Multiple candidates left? }
|
|
if cand_cnt>1 then
|
|
begin
|
|
CGMessage(type_e_cant_choose_overload_function);
|
|
{$ifdef EXTDEBUG}
|
|
candidates.dump_info(V_Hint);
|
|
{$else EXTDEBUG}
|
|
candidates.list(false);
|
|
{$endif EXTDEBUG}
|
|
{ we'll just use the first candidate to make the
|
|
call }
|
|
end;
|
|
candidates.free;
|
|
|
|
addsymref(operpd.procsym);
|
|
|
|
{ the nil as symtable signs firstcalln that this is
|
|
an overloaded operator }
|
|
ht:=ccallnode.create(ppn,Tprocsym(operpd.procsym),nil,nil,[]);
|
|
|
|
{ we already know the procdef to use, so it can
|
|
skip the overload choosing in callnode.pass_typecheck }
|
|
tcallnode(ht).procdefinition:=operpd;
|
|
|
|
if t.nodetype=unequaln then
|
|
ht:=cnotnode.create(ht);
|
|
t:=ht;
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
Register Calculation
|
|
****************************************************************************}
|
|
|
|
{ marks an lvalue as "unregable" }
|
|
procedure make_not_regable_intern(p : tnode; how: tregableinfoflags; records_only: boolean);
|
|
var
|
|
update_regable: boolean;
|
|
begin
|
|
update_regable:=true;
|
|
repeat
|
|
case p.nodetype of
|
|
subscriptn:
|
|
begin
|
|
records_only:=true;
|
|
p:=tsubscriptnode(p).left;
|
|
end;
|
|
vecn:
|
|
begin
|
|
{ arrays are currently never regable and pointers indexed like }
|
|
{ arrays do not have be made unregable, but we do need to }
|
|
{ propagate the ra_addr_taken info }
|
|
update_regable:=false;
|
|
p:=tvecnode(p).left;
|
|
end;
|
|
typeconvn :
|
|
begin
|
|
if (ttypeconvnode(p).resultdef.typ = recorddef) then
|
|
records_only:=false;
|
|
p:=ttypeconvnode(p).left;
|
|
end;
|
|
loadn :
|
|
begin
|
|
if (tloadnode(p).symtableentry.typ in [staticvarsym,localvarsym,paravarsym]) then
|
|
begin
|
|
if (ra_addr_taken in how) then
|
|
tabstractvarsym(tloadnode(p).symtableentry).addr_taken:=true;
|
|
if update_regable and
|
|
(tabstractvarsym(tloadnode(p).symtableentry).varregable <> vr_none) and
|
|
((not records_only) or
|
|
(tabstractvarsym(tloadnode(p).symtableentry).vardef.typ = recorddef)) then
|
|
if (tloadnode(p).symtableentry.typ = paravarsym) and
|
|
(ra_addr_regable in how) then
|
|
tabstractvarsym(tloadnode(p).symtableentry).varregable:=vr_addr
|
|
else
|
|
tabstractvarsym(tloadnode(p).symtableentry).varregable:=vr_none;
|
|
end;
|
|
break;
|
|
end;
|
|
temprefn :
|
|
begin
|
|
if (ra_addr_taken in how) then
|
|
include(ttemprefnode(p).tempinfo^.flags,ti_addr_taken);
|
|
if update_regable and
|
|
(ti_may_be_in_reg in ttemprefnode(p).tempinfo^.flags) and
|
|
((not records_only) or
|
|
(ttemprefnode(p).tempinfo^.typedef.typ = recorddef)) then
|
|
exclude(ttemprefnode(p).tempinfo^.flags,ti_may_be_in_reg);
|
|
break;
|
|
end;
|
|
else
|
|
break;
|
|
end;
|
|
until false;
|
|
end;
|
|
|
|
procedure make_not_regable(p : tnode; how: tregableinfoflags);
|
|
begin
|
|
make_not_regable_intern(p,how,false);
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
Subroutine Handling
|
|
****************************************************************************}
|
|
|
|
function is_procvar_load(p:tnode):boolean;
|
|
begin
|
|
result:=false;
|
|
{ remove voidpointer typecast for tp procvars }
|
|
if ((m_tp_procvar in current_settings.modeswitches) or
|
|
(m_mac_procvar in current_settings.modeswitches)) and
|
|
(p.nodetype=typeconvn) and
|
|
is_voidpointer(p.resultdef) then
|
|
p:=tunarynode(p).left;
|
|
result:=(p.nodetype=typeconvn) and
|
|
(ttypeconvnode(p).convtype=tc_proc_2_procvar);
|
|
end;
|
|
|
|
|
|
{ local routines can't be assigned to procvars }
|
|
procedure test_local_to_procvar(from_def:tprocvardef;to_def:tdef);
|
|
begin
|
|
if (from_def.parast.symtablelevel>normal_function_level) and
|
|
(to_def.typ=procvardef) then
|
|
CGMessage(type_e_cannot_local_proc_to_procvar);
|
|
end;
|
|
|
|
|
|
procedure set_varstate(p:tnode;newstate:tvarstate;varstateflags:tvarstateflags);
|
|
const
|
|
vstrans: array[tvarstate,tvarstate] of tvarstate = (
|
|
{ vs_none -> ... }
|
|
(vs_none,vs_declared,vs_initialised,vs_read,vs_read_not_warned,vs_referred_not_inited,vs_written,vs_readwritten),
|
|
{ vs_declared -> ... }
|
|
(vs_none,vs_declared,vs_initialised,vs_read,vs_read_not_warned,vs_referred_not_inited,vs_written,vs_readwritten),
|
|
{ vs_initialised -> ... }
|
|
(vs_none,vs_initialised,vs_initialised,vs_read,vs_read,vs_read,vs_written,vs_readwritten),
|
|
{ vs_read -> ... }
|
|
(vs_none,vs_read,vs_read,vs_read,vs_read,vs_read,vs_readwritten,vs_readwritten),
|
|
{ vs_read_not_warned -> ... }
|
|
(vs_none,vs_read_not_warned,vs_read,vs_read,vs_read_not_warned,vs_read_not_warned,vs_readwritten,vs_readwritten),
|
|
{ vs_referred_not_inited }
|
|
(vs_none,vs_referred_not_inited,vs_read,vs_read,vs_read_not_warned,vs_referred_not_inited,vs_written,vs_readwritten),
|
|
{ vs_written -> ... }
|
|
(vs_none,vs_written,vs_written,vs_readwritten,vs_readwritten,vs_written,vs_written,vs_readwritten),
|
|
{ vs_readwritten -> ... }
|
|
(vs_none,vs_readwritten,vs_readwritten,vs_readwritten,vs_readwritten,vs_readwritten,vs_readwritten,vs_readwritten));
|
|
var
|
|
hsym : tabstractvarsym;
|
|
begin
|
|
{ make sure we can still warn about uninitialised use after high(v), @v etc }
|
|
if (newstate = vs_read) and
|
|
not(vsf_must_be_valid in varstateflags) then
|
|
newstate := vs_referred_not_inited;
|
|
|
|
while assigned(p) do
|
|
begin
|
|
case p.nodetype of
|
|
derefn:
|
|
begin
|
|
if (tderefnode(p).left.nodetype=temprefn) and
|
|
assigned(ttemprefnode(tderefnode(p).left).tempinfo^.withnode) then
|
|
p:=ttemprefnode(tderefnode(p).left).tempinfo^.withnode
|
|
else
|
|
break;
|
|
end;
|
|
typeconvn :
|
|
begin
|
|
case ttypeconvnode(p).convtype of
|
|
tc_cchar_2_pchar,
|
|
tc_cstring_2_pchar,
|
|
tc_array_2_pointer :
|
|
exclude(varstateflags,vsf_must_be_valid);
|
|
tc_pchar_2_string,
|
|
tc_pointer_2_array :
|
|
include(varstateflags,vsf_must_be_valid);
|
|
end;
|
|
p:=tunarynode(p).left;
|
|
end;
|
|
subscriptn :
|
|
begin
|
|
if is_class_or_interface(tunarynode(p).left.resultdef) then
|
|
newstate := vs_read;
|
|
p:=tunarynode(p).left;
|
|
end;
|
|
vecn:
|
|
begin
|
|
set_varstate(tbinarynode(p).right,vs_read,[vsf_must_be_valid]);
|
|
if (newstate in [vs_read,vs_readwritten]) or
|
|
not(tunarynode(p).left.resultdef.typ in [stringdef,arraydef]) then
|
|
include(varstateflags,vsf_must_be_valid)
|
|
else if (newstate = vs_written) then
|
|
exclude(varstateflags,vsf_must_be_valid);
|
|
p:=tunarynode(p).left;
|
|
end;
|
|
{ do not parse calln }
|
|
calln :
|
|
break;
|
|
loadn :
|
|
begin
|
|
if (tloadnode(p).symtableentry.typ in [localvarsym,paravarsym,staticvarsym]) then
|
|
begin
|
|
hsym:=tabstractvarsym(tloadnode(p).symtableentry);
|
|
if (vsf_must_be_valid in varstateflags) and
|
|
(hsym.varstate in [vs_declared,vs_read_not_warned,vs_referred_not_inited]) then
|
|
begin
|
|
{ Give warning/note for uninitialized locals }
|
|
if assigned(hsym.owner) and
|
|
not(cs_opt_nodedfa in current_settings.optimizerswitches) and
|
|
not(vo_is_external in hsym.varoptions) and
|
|
(hsym.owner.symtabletype in [parasymtable,localsymtable,staticsymtable]) and
|
|
((hsym.owner=current_procinfo.procdef.localst) or
|
|
(hsym.owner=current_procinfo.procdef.parast)) then
|
|
begin
|
|
if (vo_is_funcret in hsym.varoptions) then
|
|
begin
|
|
if (vsf_use_hints in varstateflags) then
|
|
CGMessagePos(p.fileinfo,sym_h_function_result_uninitialized)
|
|
else
|
|
CGMessagePos(p.fileinfo,sym_w_function_result_uninitialized)
|
|
end
|
|
else
|
|
begin
|
|
if tloadnode(p).symtable.symtabletype=localsymtable then
|
|
begin
|
|
if (vsf_use_hints in varstateflags) then
|
|
CGMessagePos1(p.fileinfo,sym_h_uninitialized_local_variable,hsym.realname)
|
|
else
|
|
CGMessagePos1(p.fileinfo,sym_w_uninitialized_local_variable,hsym.realname);
|
|
end
|
|
else
|
|
begin
|
|
if (vsf_use_hints in varstateflags) then
|
|
CGMessagePos1(p.fileinfo,sym_h_uninitialized_variable,hsym.realname)
|
|
else
|
|
CGMessagePos1(p.fileinfo,sym_w_uninitialized_variable,hsym.realname);
|
|
end;
|
|
end;
|
|
end
|
|
else if (newstate = vs_read) then
|
|
newstate := vs_read_not_warned;
|
|
end;
|
|
hsym.varstate := vstrans[hsym.varstate,newstate];
|
|
end;
|
|
case newstate of
|
|
vs_written:
|
|
include(tloadnode(p).flags,nf_write);
|
|
vs_readwritten:
|
|
if not(nf_write in tloadnode(p).flags) then
|
|
include(tloadnode(p).flags,nf_modify);
|
|
end;
|
|
break;
|
|
end;
|
|
callparan :
|
|
internalerror(200310081);
|
|
else
|
|
break;
|
|
end;{case }
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure set_unique(p : tnode);
|
|
begin
|
|
while assigned(p) do
|
|
begin
|
|
case p.nodetype of
|
|
vecn:
|
|
begin
|
|
include(p.flags,nf_callunique);
|
|
break;
|
|
end;
|
|
typeconvn,
|
|
subscriptn,
|
|
derefn:
|
|
p:=tunarynode(p).left;
|
|
else
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function valid_for_assign(p:tnode;opts:TValidAssigns; report_errors: boolean):boolean;
|
|
var
|
|
hp2,
|
|
hp : tnode;
|
|
gotstring,
|
|
gotsubscript,
|
|
gotrecord,
|
|
gotpointer,
|
|
gotvec,
|
|
gotclass,
|
|
gotdynarray,
|
|
gotderef,
|
|
gottypeconv : boolean;
|
|
fromdef,
|
|
todef : tdef;
|
|
errmsg,
|
|
temp : longint;
|
|
begin
|
|
if valid_const in opts then
|
|
errmsg:=type_e_variable_id_expected
|
|
else if valid_property in opts then
|
|
errmsg:=type_e_argument_cant_be_assigned
|
|
else
|
|
errmsg:=type_e_no_addr_of_constant;
|
|
result:=false;
|
|
gotsubscript:=false;
|
|
gotvec:=false;
|
|
gotderef:=false;
|
|
gotrecord:=false;
|
|
gotclass:=false;
|
|
gotpointer:=false;
|
|
gotdynarray:=false;
|
|
gotstring:=false;
|
|
gottypeconv:=false;
|
|
hp:=p;
|
|
if not(valid_void in opts) and
|
|
is_void(hp.resultdef) then
|
|
begin
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,errmsg);
|
|
exit;
|
|
end;
|
|
while assigned(hp) do
|
|
begin
|
|
{ property allowed? calln has a property check itself }
|
|
if (nf_isproperty in hp.flags) then
|
|
begin
|
|
{ check return type }
|
|
case hp.resultdef.typ of
|
|
pointerdef :
|
|
gotpointer:=true;
|
|
objectdef :
|
|
gotclass:=is_class_or_interface(hp.resultdef);
|
|
recorddef :
|
|
gotrecord:=true;
|
|
classrefdef :
|
|
gotclass:=true;
|
|
stringdef :
|
|
gotstring:=true;
|
|
end;
|
|
if (valid_property in opts) then
|
|
begin
|
|
{ don't allow writing to calls that will create
|
|
temps like calls that return a structure and we
|
|
are assigning to a member }
|
|
if (valid_const in opts) or
|
|
{ if we got a deref, we won't modify the property itself }
|
|
(gotderef) or
|
|
{ same when we got a class and subscript (= deref) }
|
|
(gotclass and gotsubscript) or
|
|
(
|
|
{ allowing assignments to typecasted properties
|
|
a) is Delphi-incompatible
|
|
b) causes problems in case the getter is a function
|
|
(because then the result of the getter is
|
|
typecasted to this type, and then we "assign" to
|
|
this typecasted function result) -> always
|
|
disallow, since property accessors should be
|
|
transparantly changeable to functions at all
|
|
times
|
|
}
|
|
not(gottypeconv) and
|
|
not(gotsubscript and gotrecord) and
|
|
not(gotstring and gotvec)
|
|
) then
|
|
result:=true
|
|
else
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,errmsg);
|
|
end
|
|
else
|
|
begin
|
|
{ 1. if it returns a pointer and we've found a deref,
|
|
2. if it returns a class or record and a subscription or with is found
|
|
3. if the address is needed of a field (subscriptn, vecn) }
|
|
if (gotpointer and gotderef) or
|
|
(gotstring and gotvec) or
|
|
(
|
|
(gotclass or gotrecord) and
|
|
(gotsubscript)
|
|
) or
|
|
(
|
|
(gotvec and gotdynarray)
|
|
) or
|
|
(
|
|
(Valid_Addr in opts) and
|
|
(hp.nodetype in [subscriptn,vecn])
|
|
) then
|
|
result:=true
|
|
else
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,errmsg);
|
|
end;
|
|
exit;
|
|
end;
|
|
case hp.nodetype of
|
|
temprefn :
|
|
begin
|
|
valid_for_assign := true;
|
|
exit;
|
|
end;
|
|
derefn :
|
|
begin
|
|
gotderef:=true;
|
|
hp:=tderefnode(hp).left;
|
|
end;
|
|
typeconvn :
|
|
begin
|
|
gottypeconv:=true;
|
|
{ typecast sizes must match, exceptions:
|
|
- implicit typecast made by absolute
|
|
- from formaldef
|
|
- from void
|
|
- from/to open array
|
|
- typecast from pointer to array }
|
|
fromdef:=ttypeconvnode(hp).left.resultdef;
|
|
todef:=hp.resultdef;
|
|
if not((nf_absolute in ttypeconvnode(hp).flags) or
|
|
(fromdef.typ=formaldef) or
|
|
is_void(fromdef) or
|
|
is_open_array(fromdef) or
|
|
is_open_array(todef) or
|
|
((fromdef.typ=pointerdef) and (todef.typ=arraydef)) or
|
|
((fromdef.typ = objectdef) and (todef.typ = objectdef) and
|
|
(tobjectdef(fromdef).is_related(tobjectdef(todef))))) and
|
|
(fromdef.size<>todef.size) then
|
|
begin
|
|
{ in TP it is allowed to typecast to smaller types. But the variable can't
|
|
be in a register }
|
|
if (m_tp7 in current_settings.modeswitches) or
|
|
(todef.size<fromdef.size) then
|
|
make_not_regable(hp,[ra_addr_regable])
|
|
else
|
|
if report_errors then
|
|
CGMessagePos2(hp.fileinfo,type_e_typecast_wrong_size_for_assignment,tostr(fromdef.size),tostr(todef.size));
|
|
end;
|
|
|
|
{ don't allow assignments to typeconvs that need special code }
|
|
if not(gotsubscript or gotvec or gotderef) and
|
|
not(ttypeconvnode(hp).assign_allowed) then
|
|
begin
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,errmsg);
|
|
exit;
|
|
end;
|
|
case hp.resultdef.typ of
|
|
pointerdef :
|
|
gotpointer:=true;
|
|
objectdef :
|
|
gotclass:=is_class_or_interface(hp.resultdef);
|
|
classrefdef :
|
|
gotclass:=true;
|
|
arraydef :
|
|
begin
|
|
{ pointer -> array conversion is done then we need to see it
|
|
as a deref, because a ^ is then not required anymore }
|
|
if (ttypeconvnode(hp).left.resultdef.typ=pointerdef) then
|
|
gotderef:=true;
|
|
end;
|
|
end;
|
|
hp:=ttypeconvnode(hp).left;
|
|
end;
|
|
vecn :
|
|
begin
|
|
if { only check for first (= outermost) vec node }
|
|
not gotvec and
|
|
not(valid_packed in opts) and
|
|
(tvecnode(hp).left.resultdef.typ = arraydef) and
|
|
(ado_IsBitPacked in tarraydef(tvecnode(hp).left.resultdef).arrayoptions) and
|
|
((tarraydef(tvecnode(hp).left.resultdef).elepackedbitsize mod 8 <> 0) or
|
|
(is_ordinal(tarraydef(tvecnode(hp).left.resultdef).elementdef) and
|
|
not ispowerof2(tarraydef(tvecnode(hp).left.resultdef).elepackedbitsize div 8,temp))) then
|
|
begin
|
|
if report_errors then
|
|
if (valid_property in opts) then
|
|
CGMessagePos(hp.fileinfo,parser_e_packed_element_no_loop)
|
|
else
|
|
CGMessagePos(hp.fileinfo,parser_e_packed_element_no_var_addr);
|
|
exit;
|
|
end;
|
|
gotvec:=true;
|
|
{ accesses to dyn. arrays override read only access in delphi }
|
|
if (m_delphi in current_settings.modeswitches) and is_dynamic_array(tunarynode(hp).left.resultdef) then
|
|
gotdynarray:=true;
|
|
hp:=tunarynode(hp).left;
|
|
end;
|
|
blockn :
|
|
begin
|
|
hp2:=tblocknode(hp).statements;
|
|
if assigned(hp2) then
|
|
begin
|
|
if hp2.nodetype<>statementn then
|
|
internalerror(2006110801);
|
|
while assigned(tstatementnode(hp2).next) do
|
|
hp2:=tstatementnode(hp2).next;
|
|
hp:=tstatementnode(hp2).statement;
|
|
end
|
|
else
|
|
begin
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,type_e_variable_id_expected);
|
|
exit;
|
|
end;
|
|
end;
|
|
asn :
|
|
begin
|
|
{ asn can't be assigned directly, it returns the value in a register instead
|
|
of reference. }
|
|
if not(gotsubscript or gotderef or gotvec) then
|
|
begin
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,errmsg);
|
|
exit;
|
|
end;
|
|
hp:=tunarynode(hp).left;
|
|
end;
|
|
subscriptn :
|
|
begin
|
|
{ only check first (= outermost) subscriptn }
|
|
if not gotsubscript and
|
|
not(valid_packed in opts) and
|
|
is_packed_record_or_object(tsubscriptnode(hp).left.resultdef) and
|
|
((tsubscriptnode(hp).vs.fieldoffset mod 8 <> 0) or
|
|
(is_ordinal(tsubscriptnode(hp).resultdef) and
|
|
not ispowerof2(tsubscriptnode(hp).resultdef.packedbitsize div 8,temp))) then
|
|
begin
|
|
if report_errors then
|
|
if (valid_property in opts) then
|
|
CGMessagePos(hp.fileinfo,parser_e_packed_element_no_loop)
|
|
else
|
|
CGMessagePos(hp.fileinfo,parser_e_packed_element_no_var_addr);
|
|
exit;
|
|
end;
|
|
gotsubscript:=true;
|
|
{ loop counter? }
|
|
if not(Valid_Const in opts) and
|
|
(vo_is_loop_counter in tsubscriptnode(hp).vs.varoptions) then
|
|
begin
|
|
if report_errors then
|
|
CGMessage1(parser_e_illegal_assignment_to_count_var,tsubscriptnode(hp).vs.realname)
|
|
else
|
|
exit;
|
|
end;
|
|
{ a class/interface access is an implicit }
|
|
{ dereferencing }
|
|
hp:=tsubscriptnode(hp).left;
|
|
if is_class_or_interface(hp.resultdef) then
|
|
gotderef:=true;
|
|
end;
|
|
muln,
|
|
divn,
|
|
andn,
|
|
xorn,
|
|
orn,
|
|
notn,
|
|
subn,
|
|
addn :
|
|
begin
|
|
{ Allow operators on a pointer, or an integer
|
|
and a pointer typecast and deref has been found }
|
|
if ((hp.resultdef.typ=pointerdef) or
|
|
(is_integer(hp.resultdef) and gotpointer)) and
|
|
gotderef then
|
|
result:=true
|
|
else
|
|
{ Temp strings are stored in memory, for compatibility with
|
|
delphi only }
|
|
if (m_delphi in current_settings.modeswitches) and
|
|
((valid_addr in opts) or
|
|
(valid_const in opts)) and
|
|
(hp.resultdef.typ=stringdef) then
|
|
result:=true
|
|
else
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,type_e_variable_id_expected);
|
|
exit;
|
|
end;
|
|
niln,
|
|
pointerconstn :
|
|
begin
|
|
{ to support e.g. @tmypointer(0)^.data; see tests/tbs/tb0481 }
|
|
if gotderef then
|
|
result:=true
|
|
else
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,type_e_no_assign_to_addr);
|
|
exit;
|
|
end;
|
|
ordconstn,
|
|
realconstn :
|
|
begin
|
|
{ these constants will be passed by value }
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,type_e_variable_id_expected);
|
|
exit;
|
|
end;
|
|
setconstn,
|
|
stringconstn,
|
|
guidconstn :
|
|
begin
|
|
{ these constants will be passed by reference }
|
|
if valid_const in opts then
|
|
result:=true
|
|
else
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,type_e_variable_id_expected);
|
|
exit;
|
|
end;
|
|
addrn :
|
|
begin
|
|
if gotderef then
|
|
result:=true
|
|
else
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,type_e_no_assign_to_addr);
|
|
exit;
|
|
end;
|
|
calln :
|
|
begin
|
|
{ check return type }
|
|
case hp.resultdef.typ of
|
|
arraydef :
|
|
begin
|
|
{ dynamic arrays are allowed when there is also a
|
|
vec node }
|
|
if is_dynamic_array(hp.resultdef) and
|
|
gotvec then
|
|
begin
|
|
gotderef:=true;
|
|
gotpointer:=true;
|
|
end;
|
|
end;
|
|
pointerdef :
|
|
gotpointer:=true;
|
|
objectdef :
|
|
gotclass:=is_class_or_interface(hp.resultdef);
|
|
recorddef, { handle record like class it needs a subscription }
|
|
classrefdef :
|
|
gotclass:=true;
|
|
stringdef :
|
|
gotstring:=true;
|
|
end;
|
|
{ 1. if it returns a pointer and we've found a deref,
|
|
2. if it returns a class or record and a subscription or with is found
|
|
3. string is returned }
|
|
if (gotstring and gotvec) or
|
|
(gotpointer and gotderef) or
|
|
(gotclass and gotsubscript) then
|
|
result:=true
|
|
else
|
|
{ Temp strings are stored in memory, for compatibility with
|
|
delphi only }
|
|
if (m_delphi in current_settings.modeswitches) and
|
|
(valid_addr in opts) and
|
|
(hp.resultdef.typ=stringdef) then
|
|
result:=true
|
|
else
|
|
if ([valid_const,valid_addr] * opts = [valid_const]) then
|
|
result:=true
|
|
else
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,errmsg);
|
|
exit;
|
|
end;
|
|
inlinen :
|
|
begin
|
|
if ((valid_const in opts) and
|
|
(tinlinenode(hp).inlinenumber in [in_typeof_x]))
|
|
{$ifdef SUPPORT_UNALIGNED}
|
|
or (tinlinenode(hp).inlinenumber in [in_unaligned_x])
|
|
{$endif SUPPORT_UNALIGNED}
|
|
then
|
|
result:=true
|
|
else
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,type_e_variable_id_expected);
|
|
exit;
|
|
end;
|
|
dataconstn:
|
|
begin
|
|
{ only created internally, so no additional checks necessary }
|
|
result:=true;
|
|
exit;
|
|
end;
|
|
loadn :
|
|
begin
|
|
case tloadnode(hp).symtableentry.typ of
|
|
absolutevarsym,
|
|
staticvarsym,
|
|
localvarsym,
|
|
paravarsym :
|
|
begin
|
|
{ loop counter? }
|
|
if not(Valid_Const in opts) and
|
|
not gotderef and
|
|
(vo_is_loop_counter in tabstractvarsym(tloadnode(hp).symtableentry).varoptions) then
|
|
if report_errors then
|
|
CGMessage1(parser_e_illegal_assignment_to_count_var,tloadnode(hp).symtableentry.realname)
|
|
else
|
|
exit;
|
|
{ read-only variable? }
|
|
if (tabstractvarsym(tloadnode(hp).symtableentry).varspez=vs_const) then
|
|
begin
|
|
{ allow p^:= constructions with p is const parameter }
|
|
if gotderef or gotdynarray or (Valid_Const in opts) or
|
|
(nf_isinternal_ignoreconst in tloadnode(hp).flags) then
|
|
result:=true
|
|
else
|
|
if report_errors then
|
|
CGMessagePos(tloadnode(hp).fileinfo,type_e_no_assign_to_const);
|
|
exit;
|
|
end;
|
|
result:=true;
|
|
exit;
|
|
end;
|
|
procsym :
|
|
begin
|
|
if (Valid_Const in opts) then
|
|
result:=true
|
|
else
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,type_e_variable_id_expected);
|
|
exit;
|
|
end;
|
|
labelsym :
|
|
begin
|
|
if (Valid_Addr in opts) then
|
|
result:=true
|
|
else
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,type_e_variable_id_expected);
|
|
exit;
|
|
end;
|
|
constsym:
|
|
begin
|
|
if (tconstsym(tloadnode(hp).symtableentry).consttyp=constresourcestring) and
|
|
(valid_addr in opts) then
|
|
result:=true
|
|
else
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,type_e_variable_id_expected);
|
|
exit;
|
|
end;
|
|
else
|
|
begin
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,type_e_variable_id_expected);
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
else
|
|
begin
|
|
if report_errors then
|
|
CGMessagePos(hp.fileinfo,type_e_variable_id_expected);
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function valid_for_var(p:tnode; report_errors: boolean):boolean;
|
|
begin
|
|
valid_for_var:=valid_for_assign(p,[],report_errors);
|
|
end;
|
|
|
|
|
|
function valid_for_formal_var(p : tnode; report_errors: boolean) : boolean;
|
|
begin
|
|
valid_for_formal_var:=valid_for_assign(p,[valid_void],report_errors);
|
|
end;
|
|
|
|
|
|
function valid_for_formal_const(p : tnode; report_errors: boolean) : boolean;
|
|
begin
|
|
valid_for_formal_const:=(p.resultdef.typ=formaldef) or
|
|
valid_for_assign(p,[valid_void,valid_const,valid_property],report_errors);
|
|
end;
|
|
|
|
|
|
function valid_for_assignment(p:tnode; report_errors: boolean):boolean;
|
|
begin
|
|
valid_for_assignment:=valid_for_assign(p,[valid_property,valid_packed],report_errors);
|
|
end;
|
|
|
|
|
|
function valid_for_loopvar(p:tnode; report_errors: boolean):boolean;
|
|
begin
|
|
valid_for_loopvar:=valid_for_assign(p,[valid_property],report_errors);
|
|
end;
|
|
|
|
|
|
function valid_for_addr(p : tnode; report_errors: boolean) : boolean;
|
|
begin
|
|
result:=valid_for_assign(p,[valid_const,valid_addr,valid_void],report_errors);
|
|
end;
|
|
|
|
|
|
procedure var_para_allowed(var eq:tequaltype;def_from,def_to:Tdef; fromnode: tnode);
|
|
begin
|
|
{ Note: eq must be already valid, it will only be updated! }
|
|
case def_to.typ of
|
|
formaldef :
|
|
begin
|
|
{ all types can be passed to a formaldef,
|
|
but it is not the prefered way }
|
|
if not is_constnode(fromnode) then
|
|
eq:=te_convert_l2
|
|
else
|
|
eq:=te_incompatible;
|
|
end;
|
|
orddef :
|
|
begin
|
|
{ allows conversion from word to integer and
|
|
byte to shortint, but only for TP7 compatibility }
|
|
if (m_tp7 in current_settings.modeswitches) and
|
|
(def_from.typ=orddef) and
|
|
(def_from.size=def_to.size) then
|
|
eq:=te_convert_l1;
|
|
end;
|
|
arraydef :
|
|
begin
|
|
if is_open_array(def_to) then
|
|
begin
|
|
if is_dynamic_array(def_from) and
|
|
equal_defs(tarraydef(def_from).elementdef,tarraydef(def_to).elementdef) then
|
|
eq:=te_convert_l2
|
|
else
|
|
if equal_defs(def_from,tarraydef(def_to).elementdef) then
|
|
eq:=te_convert_l2;
|
|
end;
|
|
end;
|
|
pointerdef :
|
|
begin
|
|
{ an implicit pointer conversion is allowed }
|
|
if (def_from.typ=pointerdef) then
|
|
eq:=te_convert_l1;
|
|
end;
|
|
stringdef :
|
|
begin
|
|
{ all shortstrings are allowed, size is not important }
|
|
if is_shortstring(def_from) and
|
|
is_shortstring(def_to) then
|
|
eq:=te_equal;
|
|
end;
|
|
objectdef :
|
|
begin
|
|
{ child objects can be also passed }
|
|
{ in non-delphi mode, otherwise }
|
|
{ they must match exactly, except }
|
|
{ if they are objects }
|
|
if (def_from.typ=objectdef) and
|
|
(
|
|
(tobjectdef(def_from).objecttype=odt_object) and
|
|
(tobjectdef(def_to).objecttype=odt_object)
|
|
) and
|
|
(tobjectdef(def_from).is_related(tobjectdef(def_to))) then
|
|
eq:=te_convert_l1;
|
|
end;
|
|
filedef :
|
|
begin
|
|
{ an implicit file conversion is also allowed }
|
|
{ from a typed file to an untyped one }
|
|
if (def_from.typ=filedef) and
|
|
(tfiledef(def_from).filetyp = ft_typed) and
|
|
(tfiledef(def_to).filetyp = ft_untyped) then
|
|
eq:=te_convert_l1;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure para_allowed(var eq:tequaltype;p:tcallparanode;def_to:tdef);
|
|
begin
|
|
{ Note: eq must be already valid, it will only be updated! }
|
|
case def_to.typ of
|
|
formaldef :
|
|
begin
|
|
{ all types can be passed to a formaldef }
|
|
eq:=te_equal;
|
|
end;
|
|
stringdef :
|
|
begin
|
|
{ to support ansi/long/wide strings in a proper way }
|
|
{ string and string[10] are assumed as equal }
|
|
{ when searching the correct overloaded procedure }
|
|
if (p.resultdef.typ=stringdef) and
|
|
(tstringdef(def_to).stringtype=tstringdef(p.resultdef).stringtype) then
|
|
eq:=te_equal
|
|
else
|
|
{ Passing a constant char to ansistring or shortstring or
|
|
a widechar to widestring then handle it as equal. }
|
|
if (p.left.nodetype=ordconstn) and
|
|
(
|
|
is_char(p.resultdef) and
|
|
(is_shortstring(def_to) or is_ansistring(def_to))
|
|
) or
|
|
(
|
|
is_widechar(p.resultdef) and
|
|
(is_widestring(def_to) or is_unicodestring(def_to))
|
|
) then
|
|
eq:=te_equal
|
|
end;
|
|
setdef :
|
|
begin
|
|
{ set can also be a not yet converted array constructor }
|
|
if (p.resultdef.typ=arraydef) and
|
|
is_array_constructor(p.resultdef) and
|
|
not is_variant_array(p.resultdef) then
|
|
eq:=te_equal;
|
|
end;
|
|
procvardef :
|
|
begin
|
|
{ in tp7 mode proc -> procvar is allowed }
|
|
if ((m_tp_procvar in current_settings.modeswitches) or
|
|
(m_mac_procvar in current_settings.modeswitches)) and
|
|
(p.left.nodetype=calln) and
|
|
(proc_to_procvar_equal(tprocdef(tcallnode(p.left).procdefinition),tprocvardef(def_to))>=te_equal) then
|
|
eq:=te_equal
|
|
else
|
|
if (m_mac_procvar in current_settings.modeswitches) and
|
|
is_procvar_load(p.left) then
|
|
eq:=te_convert_l2;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function allowenumop(nt:tnodetype):boolean;
|
|
begin
|
|
result:=(nt in [equaln,unequaln,ltn,lten,gtn,gten]) or
|
|
((cs_allow_enum_calc in current_settings.localswitches) and
|
|
(nt in [addn,subn]));
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
TCallCandidates
|
|
****************************************************************************}
|
|
|
|
constructor tcallcandidates.create(sym:tprocsym;st:TSymtable;ppn:tnode;ignorevisibility,allowdefaultparas:boolean);
|
|
begin
|
|
if not assigned(sym) then
|
|
internalerror(200411015);
|
|
FOperator:=NOTOKEN;
|
|
FProcsym:=sym;
|
|
FProcsymtable:=st;
|
|
FParanode:=ppn;
|
|
create_candidate_list(ignorevisibility,allowdefaultparas);
|
|
end;
|
|
|
|
|
|
constructor tcallcandidates.create_operator(op:ttoken;ppn:tnode);
|
|
begin
|
|
FOperator:=op;
|
|
FProcsym:=nil;
|
|
FProcsymtable:=nil;
|
|
FParanode:=ppn;
|
|
create_candidate_list(false,false);
|
|
end;
|
|
|
|
|
|
destructor tcallcandidates.destroy;
|
|
var
|
|
hpnext,
|
|
hp : pcandidate;
|
|
begin
|
|
hp:=FCandidateProcs;
|
|
while assigned(hp) do
|
|
begin
|
|
hpnext:=hp^.next;
|
|
dispose(hp);
|
|
hp:=hpnext;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tcallcandidates.collect_overloads_in_class(ProcdefOverloadList:TFPObjectList);
|
|
var
|
|
j : integer;
|
|
pd : tprocdef;
|
|
srsym : tsym;
|
|
objdef : tobjectdef;
|
|
hashedid : THashedIDString;
|
|
hasoverload : boolean;
|
|
begin
|
|
objdef:=tobjectdef(fprocsym.owner.defowner);
|
|
hashedid.id:=fprocsym.name;
|
|
hasoverload:=false;
|
|
while assigned(objdef) do
|
|
begin
|
|
srsym:=tprocsym(objdef.symtable.FindWithHash(hashedid));
|
|
if assigned(srsym) then
|
|
begin
|
|
if (srsym.typ<>procsym) then
|
|
internalerror(200111022);
|
|
{ add all definitions }
|
|
hasoverload:=false;
|
|
for j:=0 to tprocsym(srsym).ProcdefList.Count-1 do
|
|
begin
|
|
pd:=tprocdef(tprocsym(srsym).ProcdefList[j]);
|
|
if po_overload in pd.procoptions then
|
|
hasoverload:=true;
|
|
ProcdefOverloadList.Add(tprocsym(srsym).ProcdefList[j]);
|
|
end;
|
|
{ when there is no explicit overload we stop searching }
|
|
if not hasoverload then
|
|
break;
|
|
end;
|
|
{ next parent }
|
|
objdef:=objdef.childof;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tcallcandidates.collect_overloads_in_units(ProcdefOverloadList:TFPObjectList);
|
|
var
|
|
j : integer;
|
|
pd : tprocdef;
|
|
srsymtable : TSymtable;
|
|
srsym : tsym;
|
|
checkstack : psymtablestackitem;
|
|
hashedid : THashedIDString;
|
|
hasoverload : boolean;
|
|
begin
|
|
{ we search all overloaded operator definitions in the symtablestack. The found
|
|
entries are only added to the procs list and not the procsym, because
|
|
the list can change in every situation }
|
|
if FOperator<>NOTOKEN then
|
|
hashedid.id:=overloaded_names[FOperator]
|
|
else
|
|
hashedid.id:=FProcsym.name;
|
|
|
|
checkstack:=symtablestack.stack;
|
|
if assigned(FProcsymtable) then
|
|
begin
|
|
while assigned(checkstack) and
|
|
(checkstack^.symtable<>FProcsymtable) do
|
|
checkstack:=checkstack^.next;
|
|
end;
|
|
while assigned(checkstack) do
|
|
begin
|
|
srsymtable:=checkstack^.symtable;
|
|
if srsymtable.symtabletype in [localsymtable,staticsymtable,globalsymtable] then
|
|
begin
|
|
srsym:=tprocsym(srsymtable.FindWithHash(hashedid));
|
|
if assigned(srsym) and
|
|
(srsym.typ=procsym) then
|
|
begin
|
|
{ Store first procsym found }
|
|
if not assigned(FProcsym) then
|
|
FProcsym:=tprocsym(srsym);
|
|
{ add all definitions }
|
|
hasoverload:=false;
|
|
for j:=0 to tprocsym(srsym).ProcdefList.Count-1 do
|
|
begin
|
|
pd:=tprocdef(tprocsym(srsym).ProcdefList[j]);
|
|
if po_overload in pd.procoptions then
|
|
hasoverload:=true;
|
|
ProcdefOverloadList.Add(tprocsym(srsym).ProcdefList[j]);
|
|
end;
|
|
{ when there is no explicit overload we stop searching }
|
|
if not hasoverload then
|
|
break;
|
|
end;
|
|
end;
|
|
checkstack:=checkstack^.next;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tcallcandidates.create_candidate_list(ignorevisibility,allowdefaultparas:boolean);
|
|
var
|
|
j : integer;
|
|
pd : tprocdef;
|
|
hp : pcandidate;
|
|
pt : tcallparanode;
|
|
found : boolean;
|
|
contextobjdef : tobjectdef;
|
|
ProcdefOverloadList : TFPObjectList;
|
|
begin
|
|
FCandidateProcs:=nil;
|
|
|
|
{ Find all available overloads for this procsym }
|
|
ProcdefOverloadList:=TFPObjectList.Create(false);
|
|
if (FOperator=NOTOKEN) and
|
|
(FProcsym.owner.symtabletype=objectsymtable) then
|
|
collect_overloads_in_class(ProcdefOverloadList)
|
|
else
|
|
collect_overloads_in_units(ProcdefOverloadList);
|
|
|
|
{ determine length of parameter list.
|
|
for operators also enable the variant-operators if
|
|
a variant parameter is passed }
|
|
FParalength:=0;
|
|
FAllowVariant:=(FOperator=NOTOKEN);
|
|
pt:=tcallparanode(FParaNode);
|
|
while assigned(pt) do
|
|
begin
|
|
if (pt.resultdef.typ=variantdef) then
|
|
FAllowVariant:=true;
|
|
inc(FParalength);
|
|
pt:=tcallparanode(pt.right);
|
|
end;
|
|
|
|
{ when the class passed is defined in this unit we
|
|
need to use the scope of that class. This is a trick
|
|
that can be used to access protected members in other
|
|
units. At least kylix supports it this way (PFV) }
|
|
if assigned(FProcSymtable) and
|
|
(
|
|
(FProcSymtable.symtabletype=ObjectSymtable) or
|
|
((FProcSymtable.symtabletype=withsymtable) and
|
|
(FProcSymtable.defowner.typ=objectdef))
|
|
) and
|
|
(FProcSymtable.defowner.owner.symtabletype in [globalsymtable,staticsymtable]) and
|
|
FProcSymtable.defowner.owner.iscurrentunit then
|
|
contextobjdef:=tobjectdef(FProcSymtable.defowner)
|
|
else
|
|
contextobjdef:=current_objectdef;
|
|
|
|
{ Process all found overloads }
|
|
for j:=0 to ProcdefOverloadList.Count-1 do
|
|
begin
|
|
pd:=tprocdef(ProcdefOverloadList[j]);
|
|
|
|
{ only when the # of parameter are supported by the procedure and
|
|
it is visible }
|
|
if (FParalength>=pd.minparacount) and
|
|
(
|
|
(
|
|
allowdefaultparas and
|
|
(
|
|
(FParalength<=pd.maxparacount) or
|
|
(po_varargs in pd.procoptions)
|
|
)
|
|
) or
|
|
(
|
|
not allowdefaultparas and
|
|
(FParalength=pd.maxparacount)
|
|
)
|
|
) and
|
|
(
|
|
ignorevisibility or
|
|
(pd.owner.symtabletype<>objectsymtable) or
|
|
is_visible_for_object(pd,contextobjdef)
|
|
) then
|
|
begin
|
|
{ don't add duplicates, only compare visible parameters for the user }
|
|
found:=false;
|
|
hp:=FCandidateProcs;
|
|
while assigned(hp) do
|
|
begin
|
|
if compare_paras(hp^.data.paras,pd.paras,cp_value_equal_const,[cpo_ignorehidden])>=te_equal then
|
|
begin
|
|
found:=true;
|
|
break;
|
|
end;
|
|
hp:=hp^.next;
|
|
end;
|
|
if not found then
|
|
proc_add(fprocsym,pd);
|
|
end;
|
|
end;
|
|
|
|
ProcdefOverloadList.Free;
|
|
end;
|
|
|
|
|
|
function tcallcandidates.proc_add(ps:tprocsym;pd:tprocdef):pcandidate;
|
|
var
|
|
defaultparacnt : integer;
|
|
begin
|
|
{ generate new candidate entry }
|
|
new(result);
|
|
fillchar(result^,sizeof(tcandidate),0);
|
|
result^.data:=pd;
|
|
result^.next:=FCandidateProcs;
|
|
FCandidateProcs:=result;
|
|
inc(FProccnt);
|
|
{ Find last parameter, skip all default parameters
|
|
that are not passed. Ignore this skipping for varargs }
|
|
result^.firstparaidx:=pd.paras.count-1;
|
|
if not(po_varargs in pd.procoptions) then
|
|
begin
|
|
{ ignore hidden parameters }
|
|
while (result^.firstparaidx>=0) and (vo_is_hidden_para in tparavarsym(pd.paras[result^.firstparaidx]).varoptions) do
|
|
dec(result^.firstparaidx);
|
|
defaultparacnt:=pd.maxparacount-FParalength;
|
|
if defaultparacnt>0 then
|
|
begin
|
|
if defaultparacnt>result^.firstparaidx+1 then
|
|
internalerror(200401141);
|
|
dec(result^.firstparaidx,defaultparacnt);
|
|
end;
|
|
end;
|
|
{ Give a small penalty for overloaded methods not in
|
|
defined the current class/unit }
|
|
if ps.owner<>pd.owner then
|
|
result^.ordinal_distance:=result^.ordinal_distance+1.0;
|
|
end;
|
|
|
|
|
|
procedure tcallcandidates.list(all:boolean);
|
|
var
|
|
hp : pcandidate;
|
|
begin
|
|
hp:=FCandidateProcs;
|
|
while assigned(hp) do
|
|
begin
|
|
if all or
|
|
(not hp^.invalid) then
|
|
MessagePos1(hp^.data.fileinfo,sym_h_param_list,hp^.data.fullprocname(false));
|
|
hp:=hp^.next;
|
|
end;
|
|
end;
|
|
|
|
|
|
{$ifdef EXTDEBUG}
|
|
procedure tcallcandidates.dump_info(lvl:longint);
|
|
|
|
function ParaTreeStr(p:tcallparanode):string;
|
|
begin
|
|
result:='';
|
|
while assigned(p) do
|
|
begin
|
|
if result<>'' then
|
|
result:=','+result;
|
|
result:=p.resultdef.typename+result;
|
|
p:=tcallparanode(p.right);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
hp : pcandidate;
|
|
i : integer;
|
|
currpara : tparavarsym;
|
|
begin
|
|
if not CheckVerbosity(lvl) then
|
|
exit;
|
|
Comment(lvl+V_LineInfo,'Overloaded callnode: '+FProcsym.name+'('+ParaTreeStr(tcallparanode(FParaNode))+')');
|
|
hp:=FCandidateProcs;
|
|
while assigned(hp) do
|
|
begin
|
|
Comment(lvl,' '+hp^.data.fullprocname(false));
|
|
if (hp^.invalid) then
|
|
Comment(lvl,' invalid')
|
|
else
|
|
begin
|
|
Comment(lvl,' ex: '+tostr(hp^.exact_count)+
|
|
' eq: '+tostr(hp^.equal_count)+
|
|
' l1: '+tostr(hp^.cl1_count)+
|
|
' l2: '+tostr(hp^.cl2_count)+
|
|
' l3: '+tostr(hp^.cl3_count)+
|
|
' l4: '+tostr(hp^.cl4_count)+
|
|
' l5: '+tostr(hp^.cl5_count)+
|
|
' oper: '+tostr(hp^.coper_count)+
|
|
' ord: '+realtostr(hp^.ordinal_distance));
|
|
{ Print parameters in left-right order }
|
|
for i:=0 to hp^.data.paras.count-1 do
|
|
begin
|
|
currpara:=tparavarsym(hp^.data.paras[i]);
|
|
if not(vo_is_hidden_para in currpara.varoptions) then
|
|
Comment(lvl,' - '+currpara.vardef.typename+' : '+EqualTypeName[currpara.eqval]);
|
|
end;
|
|
end;
|
|
hp:=hp^.next;
|
|
end;
|
|
end;
|
|
{$endif EXTDEBUG}
|
|
|
|
|
|
procedure tcallcandidates.get_information;
|
|
var
|
|
hp : pcandidate;
|
|
currpara : tparavarsym;
|
|
paraidx : integer;
|
|
currparanr : byte;
|
|
rfh,rth : double;
|
|
objdef : tobjectdef;
|
|
def_from,
|
|
def_to : tdef;
|
|
currpt,
|
|
pt : tcallparanode;
|
|
eq : tequaltype;
|
|
convtype : tconverttype;
|
|
pdtemp,
|
|
pdoper : tprocdef;
|
|
releasecurrpt : boolean;
|
|
cdoptions : tcompare_defs_options;
|
|
n : tnode;
|
|
|
|
{$ifopt r+}{$define ena_r}{$r-}{$endif}
|
|
{$ifopt q+}{$define ena_q}{$q-}{$endif}
|
|
const
|
|
inf=1.0/0.0;
|
|
{$ifdef ena_r}{$r+}{$endif}
|
|
{$ifdef ena_q}{$q+}{$endif}
|
|
|
|
begin
|
|
cdoptions:=[cdo_check_operator];
|
|
if FAllowVariant then
|
|
include(cdoptions,cdo_allow_variant);
|
|
{ process all procs }
|
|
hp:=FCandidateProcs;
|
|
while assigned(hp) do
|
|
begin
|
|
{ We compare parameters in reverse order (right to left),
|
|
the firstpara is already pointing to the last parameter
|
|
were we need to start comparing }
|
|
currparanr:=FParalength;
|
|
paraidx:=hp^.firstparaidx;
|
|
while (paraidx>=0) and (vo_is_hidden_para in tparavarsym(hp^.data.paras[paraidx]).varoptions) do
|
|
dec(paraidx);
|
|
pt:=tcallparanode(FParaNode);
|
|
while assigned(pt) and (paraidx>=0) do
|
|
begin
|
|
currpara:=tparavarsym(hp^.data.paras[paraidx]);
|
|
{ currpt can be changed from loadn to calln when a procvar
|
|
is passed. This is to prevent that the change is permanent }
|
|
currpt:=pt;
|
|
releasecurrpt:=false;
|
|
{ retrieve current parameter definitions to compares }
|
|
eq:=te_incompatible;
|
|
def_from:=currpt.resultdef;
|
|
def_to:=currpara.vardef;
|
|
if not(assigned(def_from)) then
|
|
internalerror(200212091);
|
|
if not(
|
|
assigned(def_to) or
|
|
((po_varargs in hp^.data.procoptions) and
|
|
(currparanr>hp^.data.minparacount))
|
|
) then
|
|
internalerror(200212092);
|
|
|
|
{ Convert tp procvars when not expecting a procvar }
|
|
if (currpt.left.resultdef.typ=procvardef) and
|
|
not(def_to.typ in [procvardef,formaldef]) and
|
|
{ Only convert to call when there is no overload or the return type
|
|
is equal to the expected type. }
|
|
(
|
|
(count=1) or
|
|
equal_defs(tprocvardef(currpt.left.resultdef).returndef,def_to)
|
|
) then
|
|
begin
|
|
releasecurrpt:=true;
|
|
currpt:=tcallparanode(pt.getcopy);
|
|
if maybe_call_procvar(currpt.left,true) then
|
|
begin
|
|
currpt.resultdef:=currpt.left.resultdef;
|
|
def_from:=currpt.left.resultdef;
|
|
end;
|
|
end;
|
|
|
|
{ If we expect a procvar and the left is loadnode that
|
|
returns a procdef we need to find the correct overloaded
|
|
procdef that matches the expected procvar. The loadnode
|
|
temporary returned the first procdef (PFV) }
|
|
if (def_to.typ=procvardef) and
|
|
(currpt.left.nodetype=loadn) and
|
|
(currpt.left.resultdef.typ=procdef) then
|
|
begin
|
|
pdtemp:=tprocsym(Tloadnode(currpt.left).symtableentry).Find_procdef_byprocvardef(Tprocvardef(def_to));
|
|
if assigned(pdtemp) then
|
|
begin
|
|
tloadnode(currpt.left).setprocdef(pdtemp);
|
|
currpt.resultdef:=currpt.left.resultdef;
|
|
def_from:=currpt.left.resultdef;
|
|
end;
|
|
end;
|
|
|
|
{ varargs are always equal, but not exact }
|
|
if (po_varargs in hp^.data.procoptions) and
|
|
(currparanr>hp^.data.minparacount) and
|
|
not is_array_of_const(def_from) and
|
|
not is_array_constructor(def_from) then
|
|
eq:=te_equal
|
|
else
|
|
{ same definition -> exact }
|
|
if (def_from=def_to) then
|
|
eq:=te_exact
|
|
else
|
|
{ for value and const parameters check if a integer is constant or
|
|
included in other integer -> equal and calc ordinal_distance }
|
|
if not(currpara.varspez in [vs_var,vs_out]) and
|
|
is_integer(def_from) and
|
|
is_integer(def_to) and
|
|
is_in_limit(def_from,def_to) then
|
|
begin
|
|
eq:=te_equal;
|
|
hp^.ordinal_distance:=hp^.ordinal_distance+
|
|
abs(bestreal(torddef(def_from).low)-bestreal(torddef(def_to).low));
|
|
rth:=bestreal(torddef(def_to).high);
|
|
rfh:=bestreal(torddef(def_from).high);
|
|
hp^.ordinal_distance:=hp^.ordinal_distance+abs(rth-rfh);
|
|
{ Give wrong sign a small penalty, this is need to get a diffrence
|
|
from word->[longword,longint] }
|
|
if is_signed(def_from)<>is_signed(def_to) then
|
|
{$ifopt r+}{$define ena_rq}{$q-}{$r-}{$endif}
|
|
hp^.ordinal_distance:=nextafter(hp^.ordinal_distance,inf);
|
|
{$ifdef ena_rq}{$r+}{$q+}{$endif}
|
|
end
|
|
else
|
|
{ for value and const parameters check precision of real, give
|
|
penalty for loosing of precision. var and out parameters must match exactly }
|
|
if not(currpara.varspez in [vs_var,vs_out]) and
|
|
is_real(def_from) and
|
|
is_real(def_to) then
|
|
begin
|
|
eq:=te_equal;
|
|
if is_extended(def_to) then
|
|
rth:=4
|
|
else
|
|
if is_double (def_to) then
|
|
rth:=2
|
|
else
|
|
rth:=1;
|
|
if is_extended(def_from) then
|
|
rfh:=4
|
|
else
|
|
if is_double (def_from) then
|
|
rfh:=2
|
|
else
|
|
rfh:=1;
|
|
{ penalty for shrinking of precision }
|
|
if rth<rfh then
|
|
rfh:=(rfh-rth)*16
|
|
else
|
|
rfh:=rth-rfh;
|
|
hp^.ordinal_distance:=hp^.ordinal_distance+rfh;
|
|
end
|
|
else
|
|
{ related object parameters also need to determine the distance between the current
|
|
object and the object we are comparing with. var and out parameters must match exactly }
|
|
if not(currpara.varspez in [vs_var,vs_out]) and
|
|
(def_from.typ=objectdef) and
|
|
(def_to.typ=objectdef) and
|
|
(tobjectdef(def_from).objecttype=tobjectdef(def_to).objecttype) and
|
|
tobjectdef(def_from).is_related(tobjectdef(def_to)) then
|
|
begin
|
|
eq:=te_convert_l1;
|
|
objdef:=tobjectdef(def_from);
|
|
while assigned(objdef) do
|
|
begin
|
|
if objdef=def_to then
|
|
break;
|
|
hp^.ordinal_distance:=hp^.ordinal_distance+1;
|
|
objdef:=objdef.childof;
|
|
end;
|
|
end
|
|
{ compare_defs_ext compares sets and array constructors very poorly because
|
|
it has too little information. So we do explicitly a detailed comparisation,
|
|
see also bug #11288 (FK)
|
|
}
|
|
else if (def_to.typ=setdef) and is_array_constructor(currpt.left.resultdef) then
|
|
begin
|
|
n:=currpt.left.getcopy;
|
|
arrayconstructor_to_set(n);
|
|
eq:=compare_defs_ext(n.resultdef,def_to,n.nodetype,convtype,pdoper,cdoptions);
|
|
n.free;
|
|
end
|
|
else
|
|
{ generic type comparision }
|
|
begin
|
|
eq:=compare_defs_ext(def_from,def_to,currpt.left.nodetype,convtype,pdoper,cdoptions);
|
|
|
|
{ when the types are not equal we need to check
|
|
some special case for parameter passing }
|
|
if (eq<te_equal) then
|
|
begin
|
|
if currpara.varspez in [vs_var,vs_out] then
|
|
begin
|
|
{ para requires an equal type so the previous found
|
|
match was not good enough, reset to incompatible }
|
|
eq:=te_incompatible;
|
|
{ var_para_allowed will return te_equal and te_convert_l1 to
|
|
make a difference for best matching }
|
|
var_para_allowed(eq,currpt.resultdef,currpara.vardef,currpt.left)
|
|
end
|
|
else
|
|
para_allowed(eq,currpt,def_to);
|
|
end;
|
|
end;
|
|
|
|
{ when a procvar was changed to a call an exact much is
|
|
downgraded to equal. This way an overload call with the
|
|
procvar is choosen. See tb0471 (PFV) }
|
|
if (pt<>currpt) and (eq=te_exact) then
|
|
eq:=te_equal;
|
|
|
|
{ increase correct counter }
|
|
case eq of
|
|
te_exact :
|
|
inc(hp^.exact_count);
|
|
te_equal :
|
|
inc(hp^.equal_count);
|
|
te_convert_l1 :
|
|
inc(hp^.cl1_count);
|
|
te_convert_l2 :
|
|
inc(hp^.cl2_count);
|
|
te_convert_l3 :
|
|
inc(hp^.cl3_count);
|
|
te_convert_l4 :
|
|
inc(hp^.cl4_count);
|
|
te_convert_l5 :
|
|
inc(hp^.cl5_count);
|
|
te_convert_operator :
|
|
inc(hp^.coper_count);
|
|
te_incompatible :
|
|
hp^.invalid:=true;
|
|
else
|
|
internalerror(200212072);
|
|
end;
|
|
|
|
{ stop checking when an incompatible parameter is found }
|
|
if hp^.invalid then
|
|
begin
|
|
{ store the current parameter info for
|
|
a nice error message when no procedure is found }
|
|
hp^.wrongparaidx:=paraidx;
|
|
hp^.wrongparanr:=currparanr;
|
|
break;
|
|
end;
|
|
|
|
{$ifdef EXTDEBUG}
|
|
{ store equal in node tree for dump }
|
|
currpara.eqval:=eq;
|
|
{$endif EXTDEBUG}
|
|
|
|
{ maybe release temp currpt }
|
|
if releasecurrpt then
|
|
currpt.free;
|
|
|
|
{ next parameter in the call tree }
|
|
pt:=tcallparanode(pt.right);
|
|
|
|
{ next parameter for definition, only goto next para
|
|
if we're out of the varargs }
|
|
if not(po_varargs in hp^.data.procoptions) or
|
|
(currparanr<=hp^.data.maxparacount) then
|
|
begin
|
|
{ Ignore vs_hidden parameters }
|
|
repeat
|
|
dec(paraidx);
|
|
until (paraidx<0) or not(vo_is_hidden_para in tparavarsym(hp^.data.paras[paraidx]).varoptions);
|
|
end;
|
|
dec(currparanr);
|
|
end;
|
|
if not(hp^.invalid) and
|
|
(assigned(pt) or (paraidx>=0) or (currparanr<>0)) then
|
|
internalerror(200212141);
|
|
{ next candidate }
|
|
hp:=hp^.next;
|
|
end;
|
|
end;
|
|
|
|
|
|
function get_variantequaltype(def: tdef): tvariantequaltype;
|
|
const
|
|
variantorddef_cl: array[tordtype] of tvariantequaltype =
|
|
(tve_incompatible,tve_byte,tve_word,tve_cardinal,tve_chari64,
|
|
tve_shortint,tve_smallint,tve_longint,tve_chari64,
|
|
tve_boolformal,tve_boolformal,tve_boolformal,tve_boolformal,tve_boolformal,
|
|
tve_chari64,tve_chari64,tve_dblcurrency);
|
|
{ TODO: fixme for 128 bit floats }
|
|
variantfloatdef_cl: array[tfloattype] of tvariantequaltype =
|
|
(tve_single,tve_dblcurrency,tve_extended,
|
|
tve_dblcurrency,tve_dblcurrency,tve_extended);
|
|
variantstringdef_cl: array[tstringtype] of tvariantequaltype =
|
|
(tve_sstring,tve_astring,tve_astring,tve_wstring,tve_ustring);
|
|
begin
|
|
case def.typ of
|
|
orddef:
|
|
begin
|
|
result:=variantorddef_cl[torddef(def).ordtype];
|
|
end;
|
|
floatdef:
|
|
begin
|
|
result:=variantfloatdef_cl[tfloatdef(def).floattype];
|
|
end;
|
|
stringdef:
|
|
begin
|
|
result:=variantstringdef_cl[tstringdef(def).stringtype];
|
|
end;
|
|
formaldef:
|
|
begin
|
|
result:=tve_boolformal;
|
|
end;
|
|
else
|
|
begin
|
|
result:=tve_incompatible;
|
|
end;
|
|
end
|
|
end;
|
|
|
|
|
|
function is_better_candidate(currpd,bestpd:pcandidate):integer;
|
|
var
|
|
res : integer;
|
|
begin
|
|
{
|
|
Return values:
|
|
> 0 when currpd is better than bestpd
|
|
< 0 when bestpd is better than currpd
|
|
= 0 when both are equal
|
|
|
|
To choose the best candidate we use the following order:
|
|
- Incompatible flag
|
|
- (Smaller) Number of convert operator parameters.
|
|
- (Smaller) Number of convertlevel 2 parameters.
|
|
- (Smaller) Number of convertlevel 1 parameters.
|
|
- (Bigger) Number of exact parameters.
|
|
- (Smaller) Number of equal parameters.
|
|
- (Smaller) Total of ordinal distance. For example, the distance of a word
|
|
to a byte is 65535-255=65280.
|
|
}
|
|
if bestpd^.invalid then
|
|
begin
|
|
if currpd^.invalid then
|
|
res:=0
|
|
else
|
|
res:=1;
|
|
end
|
|
else
|
|
if currpd^.invalid then
|
|
res:=-1
|
|
else
|
|
begin
|
|
{ less operator parameters? }
|
|
res:=(bestpd^.coper_count-currpd^.coper_count);
|
|
if (res=0) then
|
|
begin
|
|
{ less cl5 parameters? }
|
|
res:=(bestpd^.cl5_count-currpd^.cl5_count);
|
|
if (res=0) then
|
|
begin
|
|
{ less cl4 parameters? }
|
|
res:=(bestpd^.cl4_count-currpd^.cl4_count);
|
|
if (res=0) then
|
|
begin
|
|
{ less cl3 parameters? }
|
|
res:=(bestpd^.cl3_count-currpd^.cl3_count);
|
|
if (res=0) then
|
|
begin
|
|
{ less cl2 parameters? }
|
|
res:=(bestpd^.cl2_count-currpd^.cl2_count);
|
|
if (res=0) then
|
|
begin
|
|
{ less cl1 parameters? }
|
|
res:=(bestpd^.cl1_count-currpd^.cl1_count);
|
|
if (res=0) then
|
|
begin
|
|
{ more exact parameters? }
|
|
res:=(currpd^.exact_count-bestpd^.exact_count);
|
|
if (res=0) then
|
|
begin
|
|
{ less equal parameters? }
|
|
res:=(bestpd^.equal_count-currpd^.equal_count);
|
|
if (res=0) then
|
|
begin
|
|
{ smaller ordinal distance? }
|
|
if (currpd^.ordinal_distance<bestpd^.ordinal_distance) then
|
|
res:=1
|
|
else
|
|
if (currpd^.ordinal_distance>bestpd^.ordinal_distance) then
|
|
res:=-1
|
|
else
|
|
res:=0;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
is_better_candidate:=res;
|
|
end;
|
|
|
|
|
|
{ Delphi precedence rules extracted from test programs. Only valid if passing
|
|
a variant parameter to overloaded procedures expecting exactly one parameter.
|
|
|
|
single > (char, currency, int64, shortstring, ansistring, widestring, extended, double)
|
|
double/currency > (char, int64, shortstring, ansistring, widestring, extended)
|
|
extended > (char, int64, shortstring, ansistring, widestring)
|
|
longint/cardinal > (int64, shortstring, ansistring, widestring, extended, double, single, char, currency)
|
|
smallint > (longint, int64, shortstring, ansistring, widestring, extended, double single, char, currency);
|
|
word > (longint, cardinal, int64, shortstring, ansistring, widestring, extended, double single, char, currency);
|
|
shortint > (longint, smallint, int64, shortstring, ansistring, widestring, extended, double, single, char, currency)
|
|
byte > (longint, cardinal, word, smallint, int64, shortstring, ansistring, widestring, extended, double, single, char, currency);
|
|
boolean/formal > (char, int64, shortstring, ansistring, widestring)
|
|
shortstring > (char, int64, ansistring, widestring)
|
|
ansistring > (char, int64, widestring)
|
|
widestring > (char, int64)
|
|
|
|
Relations not mentioned mean that they conflict: no decision possible }
|
|
|
|
function is_better_candidate_single_variant(currpd,bestpd:pcandidate):integer;
|
|
|
|
function calculate_relation(const currvcl, bestvcl, testvcl:
|
|
tvariantequaltype; const conflictvcls: tvariantequaltypes):integer;
|
|
begin
|
|
{ if (bestvcl=conflictvcl) or
|
|
(currvcl=conflictvcl) then
|
|
result:=0
|
|
else if (bestvcl=testvcl) then
|
|
result:=-1
|
|
else result:=1 }
|
|
result:=1-2*ord(bestvcl=testvcl)+
|
|
ord(currvcl in conflictvcls)-ord(bestvcl in conflictvcls);
|
|
end;
|
|
|
|
|
|
function getfirstrealparaidx(pd: pcandidate): integer;
|
|
begin
|
|
{ can be different for currpd and bestpd in case of overloaded }
|
|
{ functions, e.g. lowercase():char and lowercase():shortstring }
|
|
{ (depending on the calling convention and parameter order) }
|
|
result:=pd^.firstparaidx;
|
|
while (result>=0) and (vo_is_hidden_para in tparavarsym(pd^.data.paras[result]).varoptions) do
|
|
dec(result);
|
|
if (vo_is_hidden_para in tparavarsym(pd^.data.paras[result]).varoptions) then
|
|
internalerror(2006122803);
|
|
end;
|
|
|
|
var
|
|
currpara, bestpara: tparavarsym;
|
|
currvcl, bestvcl: tvariantequaltype;
|
|
begin
|
|
{
|
|
Return values:
|
|
> 0 when currpd is better than bestpd
|
|
< 0 when bestpd is better than currpd
|
|
= 0 when both are equal
|
|
}
|
|
currpara:=tparavarsym(currpd^.data.paras[getfirstrealparaidx(currpd)]);
|
|
bestpara:=tparavarsym(bestpd^.data.paras[getfirstrealparaidx(bestpd)]);
|
|
|
|
{ if one of the parameters is a regular variant, fall back to the }
|
|
{ default algorithm }
|
|
if (currpara.vardef.typ = variantdef) or
|
|
(bestpara.vardef.typ = variantdef) then
|
|
begin
|
|
result:=is_better_candidate(currpd,bestpd);
|
|
exit;
|
|
end;
|
|
|
|
currvcl:=get_variantequaltype(currpara.vardef);
|
|
bestvcl:=get_variantequaltype(bestpara.vardef);
|
|
|
|
{ sanity check }
|
|
result:=-5;
|
|
|
|
{ if both are the same, there is a conflict }
|
|
if (currvcl=bestvcl) then
|
|
result:=0
|
|
{ if one of the two cannot be used as variant, the other is better }
|
|
else if (bestvcl=tve_incompatible) then
|
|
result:=1
|
|
else if (currvcl=tve_incompatible) then
|
|
result:=-1
|
|
{ boolean and formal are better than chari64str, but conflict with }
|
|
{ everything else }
|
|
else if (currvcl=tve_boolformal) or
|
|
(bestvcl=tve_boolformal) then
|
|
if (currvcl=tve_boolformal) then
|
|
result:=ord(bestvcl in [tve_chari64,tve_sstring,tve_astring,tve_wstring,tve_ustring])
|
|
else
|
|
result:=-ord(currvcl in [tve_chari64,tve_sstring,tve_astring,tve_wstring,tve_ustring])
|
|
{ byte is better than everything else (we assume both aren't byte, }
|
|
{ since there's only one parameter and that one can't be the same) }
|
|
else if (currvcl=tve_byte) or
|
|
(bestvcl=tve_byte) then
|
|
result:=calculate_relation(currvcl,bestvcl,tve_byte,[tve_shortint])
|
|
{ shortint conflicts with word and cardinal, but is better than }
|
|
{ everything else but byte (which has already been handled) }
|
|
else if (currvcl=tve_shortint) or
|
|
(bestvcl=tve_shortint) then
|
|
result:=calculate_relation(currvcl,bestvcl,tve_shortint,[tve_word, tve_cardinal])
|
|
{ word conflicts with smallint, but is better than everything else }
|
|
{ but shortint and byte (which has already been handled) }
|
|
else if (currvcl=tve_word) or
|
|
(bestvcl=tve_word) then
|
|
result:=calculate_relation(currvcl,bestvcl,tve_word,[tve_smallint])
|
|
{ smallint conflicts with cardinal, but is better than everything }
|
|
{ which has not yet been tested }
|
|
else if (currvcl=tve_smallint) or
|
|
(bestvcl=tve_smallint) then
|
|
result:=calculate_relation(currvcl,bestvcl,tve_smallint,[tve_cardinal])
|
|
{ cardinal conflicts with each longint and is better than everything }
|
|
{ which has not yet been tested }
|
|
else if (currvcl=tve_cardinal) or
|
|
(bestvcl=tve_cardinal) then
|
|
result:=calculate_relation(currvcl,bestvcl,tve_cardinal,[tve_longint])
|
|
{ longint is better than everything which has not yet been tested }
|
|
else if (currvcl=tve_longint) or
|
|
(bestvcl=tve_longint) then
|
|
{ if bestvcl=tve_longint then
|
|
result:=-1
|
|
else
|
|
result:=1 }
|
|
result:=1-2*ord(bestvcl=tve_longint)
|
|
{ single is better than everything left }
|
|
else if (currvcl=tve_single) or
|
|
(bestvcl=tve_single) then
|
|
result:=1-2*ord(bestvcl=tve_single)
|
|
{ double/comp/currency are better than everything left, and conflict }
|
|
{ with each other (but that's already tested) }
|
|
else if (currvcl=tve_dblcurrency) or
|
|
(bestvcl=tve_dblcurrency) then
|
|
result:=1-2*ord(bestvcl=tve_dblcurrency)
|
|
{ extended is better than everything left }
|
|
else if (currvcl=tve_extended) or
|
|
(bestvcl=tve_extended) then
|
|
result:=1-2*ord(bestvcl=tve_extended)
|
|
{ shortstring is better than everything left }
|
|
else if (currvcl=tve_sstring) or
|
|
(bestvcl=tve_sstring) then
|
|
result:=1-2*ord(bestvcl=tve_sstring)
|
|
{ ansistring is better than everything left }
|
|
else if (currvcl=tve_astring) or
|
|
(bestvcl=tve_astring) then
|
|
result:=1-2*ord(bestvcl=tve_astring)
|
|
{ widestring is better than everything left }
|
|
else if (currvcl=tve_wstring) or
|
|
(bestvcl=tve_wstring) then
|
|
result:=1-2*ord(bestvcl=tve_wstring)
|
|
{ unicodestring is better than everything left }
|
|
else if (currvcl=tve_ustring) or
|
|
(bestvcl=tve_ustring) then
|
|
result:=1-2*ord(bestvcl=tve_ustring);
|
|
|
|
{ all possibilities should have been checked now }
|
|
if (result=-5) then
|
|
internalerror(2006122805);
|
|
end;
|
|
|
|
|
|
function tcallcandidates.choose_best(var bestpd:tabstractprocdef; singlevariant: boolean):integer;
|
|
var
|
|
besthpstart,
|
|
hp : pcandidate;
|
|
cntpd,
|
|
res : integer;
|
|
begin
|
|
{
|
|
Returns the number of candidates left and the
|
|
first candidate is returned in pdbest
|
|
}
|
|
{ Setup the first procdef as best, only count it as a result
|
|
when it is valid }
|
|
bestpd:=FCandidateProcs^.data;
|
|
if FCandidateProcs^.invalid then
|
|
cntpd:=0
|
|
else
|
|
cntpd:=1;
|
|
if assigned(FCandidateProcs^.next) then
|
|
begin
|
|
besthpstart:=FCandidateProcs;
|
|
hp:=FCandidateProcs^.next;
|
|
while assigned(hp) do
|
|
begin
|
|
if not singlevariant then
|
|
res:=is_better_candidate(hp,besthpstart)
|
|
else
|
|
res:=is_better_candidate_single_variant(hp,besthpstart);
|
|
if (res>0) then
|
|
begin
|
|
{ hp is better, flag all procs to be incompatible }
|
|
while (besthpstart<>hp) do
|
|
begin
|
|
besthpstart^.invalid:=true;
|
|
besthpstart:=besthpstart^.next;
|
|
end;
|
|
{ besthpstart is already set to hp }
|
|
bestpd:=besthpstart^.data;
|
|
cntpd:=1;
|
|
end
|
|
else
|
|
if (res<0) then
|
|
begin
|
|
{ besthpstart is better, flag current hp to be incompatible }
|
|
hp^.invalid:=true;
|
|
end
|
|
else
|
|
begin
|
|
{ res=0, both are valid }
|
|
if not hp^.invalid then
|
|
inc(cntpd);
|
|
end;
|
|
hp:=hp^.next;
|
|
end;
|
|
end;
|
|
|
|
result:=cntpd;
|
|
end;
|
|
|
|
|
|
procedure tcallcandidates.find_wrong_para;
|
|
var
|
|
currparanr : smallint;
|
|
hp : pcandidate;
|
|
pt : tcallparanode;
|
|
wrongpara : tparavarsym;
|
|
begin
|
|
{ Only process the first overloaded procdef }
|
|
hp:=FCandidateProcs;
|
|
{ Find callparanode corresponding to the argument }
|
|
pt:=tcallparanode(FParanode);
|
|
currparanr:=FParalength;
|
|
while assigned(pt) and
|
|
(currparanr>hp^.wrongparanr) do
|
|
begin
|
|
pt:=tcallparanode(pt.right);
|
|
dec(currparanr);
|
|
end;
|
|
if (currparanr<>hp^.wrongparanr) or
|
|
not assigned(pt) then
|
|
internalerror(200212094);
|
|
{ Show error message, when it was a var or out parameter
|
|
guess that it is a missing typeconv }
|
|
wrongpara:=tparavarsym(hp^.data.paras[hp^.wrongparaidx]);
|
|
if wrongpara.varspez in [vs_var,vs_out] then
|
|
begin
|
|
{ Maybe passing the correct type but passing a const to var parameter }
|
|
if (compare_defs(pt.resultdef,wrongpara.vardef,pt.nodetype)<>te_incompatible) and
|
|
not valid_for_var(pt.left,true) then
|
|
CGMessagePos(pt.left.fileinfo,type_e_variable_id_expected)
|
|
else
|
|
CGMessagePos3(pt.left.fileinfo,parser_e_call_by_ref_without_typeconv,tostr(hp^.wrongparanr),
|
|
FullTypeName(pt.left.resultdef,wrongpara.vardef),
|
|
FullTypeName(wrongpara.vardef,pt.left.resultdef))
|
|
end
|
|
else
|
|
CGMessagePos3(pt.left.fileinfo,type_e_wrong_parameter_type,tostr(hp^.wrongparanr),
|
|
FullTypeName(pt.left.resultdef,wrongpara.vardef),
|
|
FullTypeName(wrongpara.vardef,pt.left.resultdef));
|
|
end;
|
|
|
|
|
|
procedure check_hints(const srsym: tsym; const symoptions: tsymoptions);
|
|
begin
|
|
if not assigned(srsym) then
|
|
internalerror(200602051);
|
|
if sp_hint_deprecated in symoptions then
|
|
Message1(sym_w_deprecated_symbol,srsym.realname);
|
|
if sp_hint_experimental in symoptions then
|
|
Message1(sym_w_experimental_symbol,srsym.realname);
|
|
if sp_hint_platform in symoptions then
|
|
Message1(sym_w_non_portable_symbol,srsym.realname);
|
|
if sp_hint_unimplemented in symoptions then
|
|
Message1(sym_w_non_implemented_symbol,srsym.realname);
|
|
end;
|
|
|
|
|
|
procedure check_ranges(const location: tfileposinfo; source: tnode; destdef: tdef);
|
|
begin
|
|
if not(cs_check_ordinal_size in current_settings.localswitches) then
|
|
exit;
|
|
{ check if the assignment may cause a range check error }
|
|
{ if its not explicit, and only if the values are }
|
|
{ ordinals, enumdef and floatdef }
|
|
if assigned(destdef) and
|
|
(destdef.typ in [enumdef,orddef,floatdef]) and
|
|
not is_boolean(destdef) and
|
|
assigned(source.resultdef) and
|
|
(source.resultdef.typ in [enumdef,orddef,floatdef]) and
|
|
not is_boolean(source.resultdef) and
|
|
not is_constrealnode(source) then
|
|
begin
|
|
if (destdef.size < source.resultdef.size) then
|
|
begin
|
|
if (cs_check_range in current_settings.localswitches) then
|
|
MessagePos(location,type_w_smaller_possible_range_check)
|
|
else
|
|
MessagePos(location,type_h_smaller_possible_range_check);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
end.
|