Switching from overloaded type symbol to unique symbol per generic.

Reasons for the "unique symbol" approach:
- no special search operations for cross unit search needed (which is supported by Delphi) => less performance impact
- no special care needed to really find the correct generic => less increase of parser complexity

Currently all generic tests except tgeneric29.pp compile and inline specializations work as well.

The changes in detail:
* pdecl.pas/types_dec:
- The variables used to hold the final name of the symbol are now prefixed with "gen". In case of non-generics the prefixed ones are equal to the non-prefixed ones (e.g. orgtypename=genorgtypename). In case of a generic symbol the "gen"-variants contain the type parameter count suffix (e.g. '$1' in case of 'TTest<T>') as well.
- The unmodified pattern is used to insert and detect a dummy symbol with that name, so that type declarations and - more important - inline specializations can find that symbol.
- In non-Delphi modes this symbol is also used to detect whether we have a type redefinition which is not allowed currently; its typedef points to the generic def.
- In mode Delphi the def of that dummy symbol (which contains an undefineddef) is modified when a corresponding non-generic type is parsed, so that it contains the def of the real type.

* pdecsub.pas/parse_proc_head
- consume_generic_type_parameter now only parses the type parameters and picks the generic with the correct amount of parameters. The verification of the order and names of the parameters needs to be added again.
- it also does not use "def" anymore, but it sets "srsym"
- in parse_proc_head the symbol (srsym) is only searched if the symbol isn't assigned already; in case of a generic in mode FPC it will find the dummy symbol that points to the generic def

* pexpr.pas
- in factor_read_id there are three cases to handle:
 + the symbol is not assigned => error
 + a possible generic symbol (either an undefined def or the non-generic variant) => no error and no hints
 + a non-generic symbol => hints
 Point 1 is handled correctly, point 2 and 3 aren't currently and also they might be needed to be moved somewhere else
- sub_expr:
 + a node can be a tloadvmtaddrnode as well if the non-generic variant of a generic symbol is a class
 + we can only check afterwards whether the specialization was successful

* pgenutil.pas/generate_specialization
using the count of the parsed types the correct symbol can be found easily

git-svn-id: branches/svenbarth/generics@17535 -
This commit is contained in:
svenbarth 2011-05-23 19:12:50 +00:00
parent bba6e0e73f
commit 8f0583ffb2
4 changed files with 213 additions and 242 deletions

View File

@ -345,7 +345,8 @@ implementation
end;
var
typename,orgtypename : TIDString;
typename,orgtypename,
gentypename,genorgtypename : TIDString;
newtype : ttypesym;
sym : tsym;
hdef : tdef;
@ -360,6 +361,7 @@ implementation
generictokenbuf : tdynamicarray;
vmtbuilder : TVMTBuilder;
i : integer;
s : shortstring;
begin
old_block_type:=block_type;
{ save unit container of forward declarations -
@ -393,8 +395,18 @@ implementation
consume(_LSHARPBRACKET);
generictypelist:=parse_generic_parameters;
consume(_RSHARPBRACKET);
str(generictypelist.Count,s);
gentypename:=typename+'$'+s;
genorgtypename:=orgtypename+'$'+s;
end
else
begin
gentypename:=typename;
genorgtypename:=orgtypename;
end;
consume(_EQ);
{ support 'ttype=type word' syntax }
@ -416,25 +428,19 @@ implementation
{ is the type already defined? -- must be in the current symtable,
not in a nested symtable or one higher up the stack -> don't
use searchsym & frinds! }
sym:=tsym(symtablestack.top.find(typename));
sym:=tsym(symtablestack.top.find(gentypename));
newtype:=nil;
{ found a symbol with this name? }
if assigned(sym) then
begin
if (sym.typ=typesym) then
if (sym.typ=typesym) and
{ this should not be a symbol that was created by a generic
that was declared earlier }
not (
(ttypesym(sym).typedef.typ=undefineddef) and
not (sp_generic_para in sym.symoptions)
) then
begin
if isgeneric then
begin
{ overloading types is only allowed in mode Delphi }
if not (m_delphi in current_settings.modeswitches) then
Message1(sym_e_duplicate_id,orgtypename);
for i:=0 to ttypesym(sym).gendeflist.Count-1 do
{ TODO : check whether the count of one of the defs is
the same as that of the current declaration and
print an error if so }
;
end
else
if ((token=_CLASS) or
(token=_INTERFACE) or
(token=_DISPINTERFACE) or
@ -465,17 +471,12 @@ implementation
end;
consume(token);
{ we can ignore the result, the definition is modified }
object_dec(objecttype,orgtypename,nil,nil,tobjectdef(ttypesym(sym).typedef),ht_none);
object_dec(objecttype,genorgtypename,nil,nil,tobjectdef(ttypesym(sym).typedef),ht_none);
newtype:=ttypesym(sym);
hdef:=newtype.typedef;
end
else
if not (m_delphi in current_settings.modeswitches) and
(ttypesym(sym).typedef.typ=undefineddef) and
(ttypesym(sym).gendeflist.Count>0) then
message1(sym_e_duplicate_id,orgtypename)
else
message1(parser_h_type_redef,orgtypename);
message1(parser_h_type_redef,genorgtypename);
end;
end;
{ no old type reused ? Then insert this new type }
@ -488,32 +489,45 @@ implementation
storetokenpos:=current_tokenpos;
if isgeneric then
begin
if assigned(sym) then
newtype:=ttypesym(sym)
else
{ for generics we need to check whether a non-generic type
already exists and if not we need to insert a symbol with
the non-generic name (available in (org)typename) that is a
undefineddef, so that inline specializations can be used }
sym:=tsym(symtablestack.top.Find(typename));
if not assigned(sym) then
begin
{ add the symbol with a undefineddef, so typesym can point
to this symbol }
newtype:=ttypesym.create(orgtypename,tundefineddef.create);
newtype.typedef.typesym:=newtype;
newtype.visibility:=symtablestack.top.currentvisibility;
symtablestack.top.insert(newtype);
newtype.typedef.owner:=newtype.owner;
end;
sym:=ttypesym.create(orgtypename,tundefineddef.create);
ttypesym(sym).typedef.typesym:=sym;
sym.visibility:=symtablestack.top.currentvisibility;
symtablestack.top.insert(sym);
ttypesym(sym).typedef.owner:=sym.owner;
end
else
{ this is not allowed in non-Delphi modes }
if not (m_delphi in current_settings.modeswitches) then
Message1(sym_e_duplicate_id,genorgtypename);
end
else
if assigned(sym) then
newtype:=ttypesym(sym)
else
if assigned(sym) and (sym.typ=typesym) and
(ttypesym(sym).typedef.typ=undefineddef) and
not (sp_generic_para in sym.symoptions) then
begin
newtype:=ttypesym.create(orgtypename,hdef);
newtype.visibility:=symtablestack.top.currentvisibility;
symtablestack.top.insert(newtype);
{ this is a symbol that was added by an earlier generic
declaration, reuse it }
newtype:=ttypesym(sym);
newtype.typedef:=hdef;
end;
{ insert a newtype if we don't reuse an existing symbol }
if not assigned(newtype) then
begin
newtype:=ttypesym.create(genorgtypename,hdef);
newtype.visibility:=symtablestack.top.currentvisibility;
symtablestack.top.insert(newtype);
end;
current_tokenpos:=defpos;
current_tokenpos:=storetokenpos;
{ read the type definition }
read_named_type(hdef,orgtypename,nil,generictypelist,false);
read_named_type(hdef,genorgtypename,nil,generictypelist,false);
{ update the definition of the type }
if assigned(hdef) then
begin
@ -532,8 +546,8 @@ implementation
begin
stringdispose(objname);
stringdispose(objrealname);
objrealname:=stringdup(orgtypename);
objname:=stringdup(upper(orgtypename));
objrealname:=stringdup(genorgtypename);
objname:=stringdup(upper(genorgtypename));
end;
include(hdef.defoptions,df_unique);
@ -544,14 +558,18 @@ implementation
if not assigned(hdef.typesym) then
hdef.typesym:=newtype;
end;
if isgeneric then begin
newtype.Owner.includeoption(sto_has_generic);
newtype.gendeflist.Add(hdef)
end else
newtype.typedef:=hdef;
{ in non-Delphi modes we need a reference to the generic def
without the generic suffix, so it can be found easily when
parsing method implementations }
if isgeneric and assigned(sym) and
not (m_delphi in current_settings.modeswitches) and
(ttypesym(sym).typedef.typ=undefineddef) then
{ TODO : check whether the undefined def needs to be freed }
ttypesym(sym).typedef:=hdef;
newtype.typedef:=hdef;
{ KAZ: handle TGUID declaration in system unit }
if (cs_compilesystem in current_settings.moduleswitches) and not assigned(rec_tguid) and
(typename='TGUID') and { name: TGUID and size=16 bytes that is 128 bits }
(gentypename='TGUID') and { name: TGUID and size=16 bytes that is 128 bits }
assigned(hdef) and (hdef.typ=recorddef) and (hdef.size=16) then
rec_tguid:=trecorddef(hdef);
end;

View File

@ -812,7 +812,6 @@ implementation
old_current_genericdef,
old_current_specializedef: tstoreddef;
lasttoken,lastidtoken: ttoken;
def : tdef;
procedure parse_operator_name;
begin
@ -911,108 +910,60 @@ implementation
function consume_generic_type_parameter:boolean;
var
i,
j,
declidx,
idx : integer;
found : boolean;
sym:tsym;
genparalistdecl : TFPHashList;
genname : tidstring;
s : shortstring;
begin
result:=not assigned(astruct)and(m_delphi in current_settings.modeswitches);
if result then
begin
{ is this an overloaded typesym? }
srsym:=search_object_name(sp,false);
if (srsym.typ=typesym) and
(ttypesym(srsym).gendeflist.Count>0) then
{ parse all parameters first so we can check whether we have
the correct generic def available }
genparalistdecl:=TFPHashList.Create;
if try_to_consume(_LT) then
begin
{ parse all parameters first so we can check whether we have
the correct generic def available }
genparalistdecl:=TFPHashList.Create;
if try_to_consume(_LT) then
begin
{ start with 1, so Find can return Nil (= 0) }
idx:=1;
repeat
if token=_ID then
begin
genparalistdecl.Add(pattern, Pointer(PtrInt(idx)));
consume(_ID);
inc(idx);
end
else
begin
message2(scan_f_syn_expected,arraytokeninfo[_ID].str,arraytokeninfo[token].str);
if token<>_COMMA then
consume(token);
end;
until not try_to_consume(_COMMA);
if not try_to_consume(_GT) then
consume(_RSHARPBRACKET);
end
else
begin
{ no generic }
srsym:=nil;
exit;
end;
{ start with 1, so Find can return Nil (= 0) }
idx:=1;
repeat
if token=_ID then
begin
genparalistdecl.Add(pattern, Pointer(PtrInt(idx)));
consume(_ID);
inc(idx);
end
else
begin
message2(scan_f_syn_expected,arraytokeninfo[_ID].str,arraytokeninfo[token].str);
if token<>_COMMA then
consume(token);
end;
until not try_to_consume(_COMMA);
if not try_to_consume(_GT) then
consume(_RSHARPBRACKET);
end
else
begin
{ no generic }
srsym:=nil;
exit;
end;
{ now search the matching generic definition }
found:=false;
for i:=0 to ttypesym(srsym).gendeflist.Count-1 do
begin
def:=tdef(ttypesym(srsym).gendeflist[i]);
{ for now generic overloads are only allowed for records
and objects; later they'll also be allowed for
procedures and functions }
if not (def.typ in [objectdef,recorddef]) then
continue;
st:=def.getsymtable(gs_record);
if not assigned(st) then
InternalError(2011042901);
idx:=1;
{ check whether the generic parameters of the def have the
same count and order as the ones just scanned, if so
"found" is true }
for j:=0 to st.SymList.Count-1 do
begin
sym:=tsym(st.SymList[j]);
if sp_generic_para in sym.symoptions then
begin
if not (sym.typ=typesym) then
internalerror(2011290402);
{ too many parameters }
if idx>genparalistdecl.Count then
break;
declidx:=PtrInt(genparalistdecl.Find(sym.prettyname));
{ does the parameters' index match the current
index value? }
if (declidx=0) or (declidx<>idx) then
break;
inc(idx);
end;
if (j=st.SymList.Count-1) and (idx=genparalistdecl.Count+1) then
found:=true;
end;
s:='';
str(genparalistdecl.count,s);
genname:=sp+'$'+s;
{ the first found matching generic def wins }
if found then
break;
end;
genparalistdecl.free;
genparalistdecl.free;
if not found then
begin
{ TODO : print a nicer typename that contains the parsed
generic types }
Message1(type_e_generic_declaration_does_not_match,sp);
srsym:=nil;
def:=nil;
exit;
end;
srsym:=search_object_name(genname,false);
if not assigned(srsym) then
begin
{ TODO : print a nicer typename that contains the parsed
generic types }
Message1(type_e_generic_declaration_does_not_match,genname);
srsym:=nil;
exit;
end;
end;
end;
@ -1056,40 +1007,23 @@ implementation
end;
{ method ? }
def:=nil;
srsym:=nil;
if (consume_generic_type_parameter or not assigned(astruct)) and
(symtablestack.top.symtablelevel=main_program_level) and
try_to_consume(_POINT) then
begin
repeat
searchagain:=false;
if not assigned(astruct) then
begin
if not assigned(def) then
begin
srsym:=search_object_name(sp,true);
{ in non-Delphi modes we can directly use the generic def if one
exists }
if (srsym.typ=typesym) then
if (ttypesym(srsym).gendeflist.Count>0) and
not (m_delphi in current_settings.modeswitches) then
def:=tdef(ttypesym(srsym).gendeflist[0])
else
def:=ttypesym(srsym).typedef
else
def:=nil;
end;
end
else
def:=astruct;
if not assigned(astruct) and not assigned(srsym) then
srsym:=search_object_name(sp,true);
{ consume proc name }
procstartfilepos:=current_tokenpos;
consume_proc_name;
{ qualifier is class name ? }
if (srsym.typ=typesym) and
(def.typ in [objectdef,recorddef]) then
(ttypesym(srsym).typedef.typ in [objectdef,recorddef]) then
begin
astruct:=tabstractrecorddef(def);
astruct:=tabstractrecorddef(ttypesym(srsym).typedef);
if (token<>_POINT) then
if (potype in [potype_class_constructor,potype_class_destructor]) then
sp:=lower(sp)

View File

@ -1912,20 +1912,21 @@ implementation
consume(_ASSIGNMENT);
exit;
end;
{ if nothing found give error and return errorsym }
{ TODO : adjust this check for inline specializations }
{ check hints if it is the final symbol that is used (e.g. in
case of generics only the dummy symbol or the non-generic one
is found here }
if assigned(srsym) and
not (
{ in case of an overloaded generic symbol we need to
generate an error if the non-generic symbol is still
undefined and we're not doing a specialization }
typeonly and (srsym.typ=typesym) and
(ttypesym(srsym).typedef.typ=undefineddef) and
(ttypesym(srsym).gendeflist.Count>0) and
not (token in [_LT, _LSHARPBRACKET])
not (sp_generic_para in srsym.symoptions) and
(token in [_LT, _LSHARPBRACKET])
) then
check_hints(srsym,srsym.symoptions,srsym.deprecatedmsg)
else
check_hints(srsym,srsym.symoptions,srsym.deprecatedmsg);
{ if nothing found give error and return errorsym }
if not assigned(srsym) then
begin
identifier_not_found(orgstoredpattern);
srsym:=generrorsym;
@ -2842,7 +2843,7 @@ implementation
filepos : tfileposinfo;
again,
isgeneric : boolean;
def : tdef;
gendef,parseddef : tdef;
begin
if pred_level=highest_precedence then
p1:=factor(false,typeonly)
@ -2876,24 +2877,47 @@ implementation
_LT :
begin
isgeneric:=false;
if (p1.nodetype=typen) and (p2.nodetype=typen) and
if (
{ the left node needs to be a type node }
(p1.nodetype=typen) or
(
(p1.nodetype=loadvmtaddrn) and
(tloadvmtaddrnode(p1).left.nodetype=typen)
)
) and
(
{ the right node needs to be a type node }
(p2.nodetype=typen) or
(
(p2.nodetype=loadvmtaddrn) and
(tloadvmtaddrnode(p2).left.nodetype=typen)
)
) and
(m_delphi in current_settings.modeswitches) and
(token in [_GT,_RSHARPBRACKET,_COMMA]) then
begin
{ this is an inline specialization }
if ttypenode(p1).typedef.typesym.typ<>typesym then
Internalerror(2011050301);
{ TODO : search for other generics with the same name
in other units (if no unit identifier is
given...) }
if ttypesym(ttypenode(p1).typedef.typesym).gendeflist.Count>0 then
begin
def:=ttypenode(p1).typedef;
generate_specialization(def,false,ttypenode(p2).typedef);
isgeneric:=def<>generrordef;
end
{ retrive the def of the left node }
if p1.nodetype=typen then
gendef:=ttypenode(p1).typedef
else
;
gendef:=ttypenode(tloadvmtaddrnode(p1).left).typedef;
{ retrieve the right node }
if p2.nodetype=typen then
parseddef:=ttypenode(p2).typedef
else
parseddef:=ttypenode(tloadvmtaddrnode(p2).left).typedef;
if gendef.typesym.typ<>typesym then
Internalerror(2011050301);
if parseddef.typesym.typ<>typesym then
Internalerror(2011051001);
{ generate the specialization }
generate_specialization(gendef,false,parseddef);
isgeneric:=gendef<>generrordef;
end;
if not isgeneric then
@ -2904,9 +2928,10 @@ implementation
p1.Free;
p2.Free;
{ in case of a class this is always a classrefdef }
if is_class_or_interface_or_object(def) or is_record(def) then
def:=tclassrefdef.create(def);
p1:=ctypenode.create(def);
if is_class_or_interface_or_object(gendef) or
is_record(gendef) then
gendef:=tclassrefdef.create(gendef);
p1:=ctypenode.create(gendef);
again:=true;
{ parse postfix operators }
if postfixoperators(p1,again,false) then

View File

@ -59,13 +59,10 @@ uses
st : TSymtable;
srsym : tsym;
pt2 : tnode;
found,
first,
err : boolean;
i,
j,
gencount : longint;
sym : tsym;
genericdef : tstoreddef;
genericsym,
generictype : ttypesym;
@ -76,12 +73,11 @@ uses
hmodule : tmodule;
pu : tused_unit;
uspecializename,
specializename : string;
countstr,genname,ugenname,specializename : string;
vmtbuilder : TVMTBuilder;
onlyparsepara : boolean;
specializest : tsymtable;
item: psymtablestackitem;
def : tdef;
begin
{ retrieve generic def that we are going to replace }
genericdef:=tstoreddef(tt);
@ -93,13 +89,6 @@ uses
internalerror(2011042701);
genericsym:=ttypesym(genericdef.typesym);
if genericsym.gendeflist.Count=0 then
begin
{ TODO : search for other generics with the same name }
Message(parser_e_special_onlygenerics);
tt:=generrordef;
onlyparsepara:=true;
end;
{ only need to record the tokens, then we don't know the type yet ... }
if parse_generic then
@ -178,63 +167,68 @@ uses
exit;
end;
{ check whether we have a generic with the correct amount of params }
found:=false;
for i:=0 to genericsym.gendeflist.Count-1 do begin
def:=tdef(genericsym.gendeflist[i]);
{ select the symtable containing the params }
case def.typ of
procdef:
st:=def.GetSymtable(gs_para);
objectdef,
recorddef:
st:=def.GetSymtable(gs_record);
arraydef:
st:=tarraydef(def).symtable;
procvardef:
st:=def.GetSymtable(gs_para);
else
internalerror(200511182);
end;
gencount:=0;
for j:=0 to st.SymList.Count-1 do
begin
if sp_generic_para in tsym(st.SymList[j]).symoptions then
inc(gencount);
end;
if gencount=genericdeflist.count then
begin
found:=true;
break;
end;
end;
if not found then
{ search a generic with the given count of params }
countstr:='';
str(genericdeflist.Count,countstr);
{ use the name of the symbol as procvars return a user friendly version
of the name }
genname:=ttypesym(genericdef.typesym).realname;
{ in case of non-Delphi mode the type name could already be a generic
def (but maybe the wrong one) }
if df_generic in genericdef.defoptions then
begin
identifier_not_found(genericdef.typename);
tt:=generrordef;
{ remove the type count suffix from the generic's name }
for i:=Length(genname) downto 1 do
if genname[i]='$' then
begin
genname:=copy(genname,1,i-1);
break;
end;
end;
genname:=genname+'$'+countstr;
ugenname:=upper(genname);
if not searchsym(ugenname,srsym,st)
or (srsym.typ<>typesym) then
begin
identifier_not_found(genname);
genericdeflist.Free;
generictypelist.Free;
exit;
end;
{ we've found the correct def, so use it }
genericdef:=tstoreddef(def);
{ we've found the correct def }
genericdef:=tstoreddef(ttypesym(srsym).typedef);
{ build the new type's name }
specializename:=genericdef.typesym.realname+specializename;
specializename:=genname+specializename;
uspecializename:=upper(specializename);
{ select the symtable containing the params }
case genericdef.typ of
procdef:
st:=genericdef.GetSymtable(gs_para);
objectdef,
recorddef:
st:=genericdef.GetSymtable(gs_record);
arraydef:
st:=tarraydef(genericdef).symtable;
procvardef:
st:=genericdef.GetSymtable(gs_para);
else
internalerror(200511182);
end;
{ build the list containing the types for the generic params }
gencount:=0;
for i:=0 to st.SymList.Count-1 do
begin
sym:=tsym(st.SymList[i]);
if sp_generic_para in sym.symoptions then
srsym:=tsym(st.SymList[i]);
if sp_generic_para in srsym.symoptions then
begin
if gencount=genericdeflist.Count then
internalerror(2011042702);
generictype:=ttypesym.create(sym.realname,tdef(genericdeflist[gencount]));
generictype:=ttypesym.create(srsym.realname,tdef(genericdeflist[gencount]));
generictypelist.add(generictype);
inc(gencount);
end;