* patch by Aleksa Todorovic which adds the ability to reference current generic class. Each reference to current generic class is "translated" to appropriate specialization of that generic class. There are two tests as part of the patch to test that it works. The patch, also, rejects declaring generic inside another generic. 0010479 is easy to implement with this patch applied, resolves #11777

git-svn-id: trunk@16423 -
This commit is contained in:
florian 2010-11-24 21:57:41 +00:00
parent 1f61b5b30c
commit d30952b408
14 changed files with 549 additions and 377 deletions

2
.gitattributes vendored
View File

@ -9279,6 +9279,8 @@ tests/test/tgeneric18.pp svneol=native#text/pascal
tests/test/tgeneric19.pp svneol=native#text/pascal tests/test/tgeneric19.pp svneol=native#text/pascal
tests/test/tgeneric2.pp svneol=native#text/plain tests/test/tgeneric2.pp svneol=native#text/plain
tests/test/tgeneric20.pp svneol=native#text/pascal tests/test/tgeneric20.pp svneol=native#text/pascal
tests/test/tgeneric21.pp svneol=native#text/pascal
tests/test/tgeneric22.pp svneol=native#text/pascal
tests/test/tgeneric3.pp svneol=native#text/plain tests/test/tgeneric3.pp svneol=native#text/plain
tests/test/tgeneric4.pp svneol=native#text/plain tests/test/tgeneric4.pp svneol=native#text/plain
tests/test/tgeneric5.pp svneol=native#text/plain tests/test/tgeneric5.pp svneol=native#text/plain

View File

@ -579,6 +579,8 @@ implementation
begin begin
current_procinfo:=nil; current_procinfo:=nil;
current_objectdef:=nil; current_objectdef:=nil;
current_genericdef:=nil;
current_specializedef:=nil;
end; end;
{ release procinfo tree } { release procinfo tree }
while assigned(procinfo) do while assigned(procinfo) do
@ -659,6 +661,8 @@ implementation
begin begin
current_procinfo:=nil; current_procinfo:=nil;
current_objectdef:=nil; current_objectdef:=nil;
current_genericdef:=nil;
current_specializedef:=nil;
end; end;
{ release procinfo tree } { release procinfo tree }
while assigned(procinfo) do while assigned(procinfo) do

View File

@ -1336,6 +1336,11 @@ parser_e_no_procvarnested_const=03296_E_Typed constants of the type 'procedure i
% procedural variable contains a reference to a nested procedure/function. % procedural variable contains a reference to a nested procedure/function.
% Therefore such typed constants can only be initialized with global % Therefore such typed constants can only be initialized with global
% functions/procedures since these do not require a parent frame pointer. % functions/procedures since these do not require a parent frame pointer.
parser_f_no_generic_inside_generic=03297_F_Declaration of generic class inside another generic class is not allowed
% At the moment, scanner supports recording of only one token buffer at the time
% (guarded by internal error 200511173 in tscannerfile.startrecordtokens).
% Since generics are implemented by recording tokens, it is not possible to
% have declaration of generic class inside another generic class.
% \end{description} % \end{description}
# #
# Type Checking # Type Checking

View File

@ -385,6 +385,7 @@ const
parser_e_objc_enumerator_2_0=03294; parser_e_objc_enumerator_2_0=03294;
parser_e_objc_missing_enumeration_defs=03295; parser_e_objc_missing_enumeration_defs=03295;
parser_e_no_procvarnested_const=03296; parser_e_no_procvarnested_const=03296;
parser_f_no_generic_inside_generic=03297;
type_e_mismatch=04000; type_e_mismatch=04000;
type_e_incompatible_types=04001; type_e_incompatible_types=04001;
type_e_not_equal_types=04002; type_e_not_equal_types=04002;
@ -870,9 +871,9 @@ const
option_info=11024; option_info=11024;
option_help_pages=11025; option_help_pages=11025;
MsgTxtSize = 57808; MsgTxtSize = 57889;
MsgIdxMax : array[1..20] of longint=( MsgIdxMax : array[1..20] of longint=(
24,88,297,97,82,54,111,22,202,63, 24,88,298,97,82,54,111,22,202,63,
49,20,1,1,1,1,1,1,1,1 49,20,1,1,1,1,1,1,1,1
); );

File diff suppressed because it is too large Load Diff

View File

@ -65,6 +65,8 @@ implementation
current_asmdata:=nil; current_asmdata:=nil;
current_procinfo:=nil; current_procinfo:=nil;
current_objectdef:=nil; current_objectdef:=nil;
current_genericdef:=nil;
current_specializedef:=nil;
loaded_units:=TLinkedList.Create; loaded_units:=TLinkedList.Create;
@ -137,6 +139,8 @@ implementation
current_procinfo:=nil; current_procinfo:=nil;
current_asmdata:=nil; current_asmdata:=nil;
current_objectdef:=nil; current_objectdef:=nil;
current_genericdef:=nil;
current_specializedef:=nil;
{ unload units } { unload units }
if assigned(loaded_units) then if assigned(loaded_units) then

View File

@ -443,6 +443,9 @@ implementation
{ Generic type declaration? } { Generic type declaration? }
if isgeneric then if isgeneric then
begin begin
if assigned(current_genericdef) then
Message(parser_f_no_generic_inside_generic);
consume(_LSHARPBRACKET); consume(_LSHARPBRACKET);
generictypelist:=parse_generic_parameters; generictypelist:=parse_generic_parameters;
consume(_RSHARPBRACKET); consume(_RSHARPBRACKET);

View File

@ -945,10 +945,16 @@ implementation
function object_dec(objecttype:tobjecttyp;const n:tidstring;genericdef:tstoreddef;genericlist:TFPObjectList;fd : tobjectdef) : tobjectdef; function object_dec(objecttype:tobjecttyp;const n:tidstring;genericdef:tstoreddef;genericlist:TFPObjectList;fd : tobjectdef) : tobjectdef;
var var
old_current_objectdef : tobjectdef; old_current_objectdef : tobjectdef;
old_current_genericdef : tobjectdef;
old_current_specializedef : tobjectdef;
begin begin
old_current_objectdef:=current_objectdef; old_current_objectdef:=current_objectdef;
old_current_genericdef:=current_genericdef;
old_current_specializedef:=current_specializedef;
current_objectdef:=nil; current_objectdef:=nil;
current_genericdef:=nil;
current_specializedef:=nil;
{ objects and class types can't be declared local } { objects and class types can't be declared local }
if not(symtablestack.top.symtabletype in [globalsymtable,staticsymtable,objectsymtable]) and if not(symtablestack.top.symtabletype in [globalsymtable,staticsymtable,objectsymtable]) and
@ -1002,6 +1008,13 @@ implementation
end; end;
end; end;
{ usage of specialized type inside its generic template }
if assigned(genericdef) then
current_specializedef:=current_objectdef
{ reject declaration of generic class inside generic class }
else if assigned(genericlist) then
current_genericdef:=current_objectdef;
{ set published flag in $M+ mode, it can also be inherited and will { set published flag in $M+ mode, it can also be inherited and will
be added when the parent class set with tobjectdef.set_parent (PFV) } be added when the parent class set with tobjectdef.set_parent (PFV) }
if (cs_generate_rtti in current_settings.localswitches) and if (cs_generate_rtti in current_settings.localswitches) and
@ -1068,6 +1081,8 @@ implementation
{ restore old state } { restore old state }
current_objectdef:=old_current_objectdef; current_objectdef:=old_current_objectdef;
current_genericdef:=old_current_genericdef;
current_specializedef:=old_current_specializedef;
end; end;
end. end.

View File

@ -735,7 +735,9 @@ implementation
popclass : boolean; popclass : boolean;
ImplIntf : TImplementedInterface; ImplIntf : TImplementedInterface;
old_parse_generic : boolean; old_parse_generic : boolean;
old_current_objectdef: tobjectdef; old_current_objectdef,
old_current_genericdef,
old_current_specializedef : tobjectdef;
begin begin
{ Save the position where this procedure really starts } { Save the position where this procedure really starts }
procstartfilepos:=current_tokenpos; procstartfilepos:=current_tokenpos;
@ -1003,7 +1005,13 @@ implementation
begin begin
symtablestack.push(pd._class.symtable); symtablestack.push(pd._class.symtable);
old_current_objectdef:=current_objectdef; old_current_objectdef:=current_objectdef;
old_current_genericdef:=current_genericdef;
old_current_specializedef:=current_specializedef;
current_objectdef:=pd._class; current_objectdef:=pd._class;
if assigned(current_objectdef) and (df_generic in current_objectdef.defoptions) then
current_genericdef:=current_objectdef;
if assigned(current_objectdef) and (df_specialization in current_objectdef.defoptions) then
current_specializedef:=current_objectdef;
popclass:=true; popclass:=true;
end; end;
{ Add parameter symtable } { Add parameter symtable }
@ -1015,6 +1023,8 @@ implementation
if popclass then if popclass then
begin begin
current_objectdef:=old_current_objectdef; current_objectdef:=old_current_objectdef;
current_genericdef:=old_current_genericdef;
current_specializedef:=old_current_specializedef;
symtablestack.pop(pd._class.symtable); symtablestack.pop(pd._class.symtable);
end; end;
end; end;
@ -1030,7 +1040,9 @@ implementation
locationstr: string; locationstr: string;
old_parse_generic, old_parse_generic,
popclass: boolean; popclass: boolean;
old_current_objectdef: tobjectdef; old_current_objectdef,
old_current_genericdef,
old_current_specializedef: tobjectdef;
begin begin
locationstr:=''; locationstr:='';
pd:=nil; pd:=nil;
@ -1057,7 +1069,13 @@ implementation
popclass:=true; popclass:=true;
parse_generic:=(df_generic in pd._class.defoptions); parse_generic:=(df_generic in pd._class.defoptions);
old_current_objectdef:=current_objectdef; old_current_objectdef:=current_objectdef;
old_current_genericdef:=current_genericdef;
old_current_specializedef:=current_specializedef;
current_objectdef:=pd._class; current_objectdef:=pd._class;
if assigned(current_objectdef) and (df_generic in current_objectdef.defoptions) then
current_genericdef:=current_objectdef;
if assigned(current_objectdef) and (df_specialization in current_objectdef.defoptions) then
current_specializedef:=current_objectdef;
end; end;
single_type(pd.returndef,false,false); single_type(pd.returndef,false,false);
@ -1067,6 +1085,8 @@ implementation
if popclass then if popclass then
begin begin
current_objectdef:=old_current_objectdef; current_objectdef:=old_current_objectdef;
current_genericdef:=old_current_genericdef;
current_specializedef:=old_current_specializedef;
symtablestack.pop(pd._class.symtable); symtablestack.pop(pd._class.symtable);
end; end;
dec(testcurobject); dec(testcurobject);

View File

@ -806,7 +806,9 @@ implementation
old_current_procinfo : tprocinfo; old_current_procinfo : tprocinfo;
oldmaxfpuregisters : longint; oldmaxfpuregisters : longint;
oldfilepos : tfileposinfo; oldfilepos : tfileposinfo;
old_current_objectdef : tobjectdef; old_current_objectdef,
old_current_genericdef,
old_current_specializedef : tobjectdef;
templist : TAsmList; templist : TAsmList;
headertai : tai; headertai : tai;
i : integer; i : integer;
@ -1400,14 +1402,22 @@ implementation
old_current_procinfo : tprocinfo; old_current_procinfo : tprocinfo;
old_block_type : tblock_type; old_block_type : tblock_type;
st : TSymtable; st : TSymtable;
old_current_objectdef : tobjectdef; old_current_objectdef,
old_current_genericdef,
old_current_specializedef : tobjectdef;
begin begin
old_current_procinfo:=current_procinfo; old_current_procinfo:=current_procinfo;
old_block_type:=block_type; old_block_type:=block_type;
old_current_objectdef:=current_objectdef; old_current_objectdef:=current_objectdef;
old_current_genericdef:=current_genericdef;
old_current_specializedef:=current_specializedef;
current_procinfo:=self; current_procinfo:=self;
current_objectdef:=procdef._class; current_objectdef:=procdef._class;
if assigned(current_objectdef) and (df_generic in current_objectdef.defoptions) then
current_genericdef:=current_objectdef;
if assigned(current_objectdef) and (df_specialization in current_objectdef.defoptions) then
current_specializedef:=current_objectdef;
{ calculate the lexical level } { calculate the lexical level }
if procdef.parast.symtablelevel>maxnesting then if procdef.parast.symtablelevel>maxnesting then
@ -1514,6 +1524,8 @@ implementation
{$endif state_tracking} {$endif state_tracking}
current_objectdef:=old_current_objectdef; current_objectdef:=old_current_objectdef;
current_genericdef:=old_current_genericdef;
current_specializedef:=old_current_specializedef;
current_procinfo:=old_current_procinfo; current_procinfo:=old_current_procinfo;
{ Restore old state } { Restore old state }
@ -1657,7 +1669,9 @@ implementation
var var
old_current_procinfo : tprocinfo; old_current_procinfo : tprocinfo;
old_current_objectdef : tobjectdef; old_current_objectdef,
old_current_genericdef,
old_current_specializedef : tobjectdef;
pdflags : tpdflags; pdflags : tpdflags;
pd,firstpd : tprocdef; pd,firstpd : tprocdef;
s : string; s : string;
@ -1665,11 +1679,15 @@ implementation
{ save old state } { save old state }
old_current_procinfo:=current_procinfo; old_current_procinfo:=current_procinfo;
old_current_objectdef:=current_objectdef; old_current_objectdef:=current_objectdef;
old_current_genericdef:=current_genericdef;
old_current_specializedef:=current_specializedef;
{ reset current_procinfo.procdef to nil to be sure that nothing is writing { reset current_procinfo.procdef to nil to be sure that nothing is writing
to another procdef } to another procdef }
current_procinfo:=nil; current_procinfo:=nil;
current_objectdef:=nil; current_objectdef:=nil;
current_genericdef:=nil;
current_specializedef:=nil;
{ parse procedure declaration } { parse procedure declaration }
pd:=parse_proc_dec(isclassmethod, old_current_objectdef); pd:=parse_proc_dec(isclassmethod, old_current_objectdef);
@ -1798,6 +1816,8 @@ implementation
end; end;
current_objectdef:=old_current_objectdef; current_objectdef:=old_current_objectdef;
current_genericdef:=old_current_genericdef;
current_specializedef:=old_current_specializedef;
current_procinfo:=old_current_procinfo; current_procinfo:=old_current_procinfo;
end; end;

View File

@ -492,7 +492,15 @@ implementation
generate_specialization(def) generate_specialization(def)
else else
begin begin
if (df_generic in def.defoptions) then if assigned(current_specializedef) and (def=current_specializedef.genericdef) then
begin
def:=current_specializedef
end
else if (def=current_genericdef) then
begin
def:=current_genericdef
end
else if (df_generic in def.defoptions) then
begin begin
Message(parser_e_no_generics_as_types); Message(parser_e_no_generics_as_types);
def:=generrordef; def:=generrordef;
@ -633,7 +641,15 @@ implementation
generate_specialization(def) generate_specialization(def)
else else
begin begin
if (df_generic in def.defoptions) then if assigned(current_specializedef) and (def=current_specializedef.genericdef) then
begin
def:=current_specializedef
end
else if (def=current_genericdef) then
begin
def:=current_genericdef
end
else if (df_generic in def.defoptions) then
begin begin
Message(parser_e_no_generics_as_types); Message(parser_e_no_generics_as_types);
def:=generrordef; def:=generrordef;

View File

@ -632,6 +632,8 @@ interface
var var
current_objectdef : tobjectdef; { used for private functions check !! } current_objectdef : tobjectdef; { used for private functions check !! }
current_genericdef : tobjectdef; { used to reject declaration of generic class inside generic class }
current_specializedef : tobjectdef; { used to implement usage of generic class in itself }
{ default types } { default types }
generrordef, { error in definition } generrordef, { error in definition }

12
tests/test/tgeneric21.pp Normal file
View File

@ -0,0 +1,12 @@
{ %fail }
{$mode objfpc}{$H+}
type
generic TOuter<T> = class(TObject)
public type
generic TInner<U> = class(TObject)
end;
end;
begin
end.

66
tests/test/tgeneric22.pp Normal file
View File

@ -0,0 +1,66 @@
{$mode objfpc}{$H+}
type
generic TGListItem<T> = class(TObject)
public var
FValue: T;
FNext: TGListItem;
procedure SetValue(Value: T);
function GetValue: T;
procedure Assign(Source: TGListItem);
function Merge(Other: TGListItem): TGListItem;
end;
procedure TGListItem.SetValue(Value: T);
begin
FValue := Value;
end;
function TGListItem.GetValue: T;
begin
Result := FValue;
end;
procedure TGListItem.Assign(Source: TGListItem);
begin
FNext := Source;
end;
function TGListItem.Merge(Other: TGListItem): TGListItem;
var
Temp: TGListItem;
begin
Temp := TGListItem.Create;
Temp.SetValue(FNext.GetValue + Other.FNext.GetValue);
Result := Temp;
end;
type
TIntListItem = specialize TGListItem<Integer>;
var
A, A2, B, B2: TIntListItem;
begin
A := TIntListItem.Create;
A2 := TIntListItem.Create;
A.Assign(A2);
if A.FNext <> A2 then
halt(1);
B := TIntListItem.Create;
B2 := TIntListItem.Create;
B.Assign(B2);
if B.FNext <> B2 then
halt(1);
A2.SetValue(5);
if A2.GetValue <> 5 then
halt(1);
B2.SetValue(7);
if B2.GetValue <> 7 then
halt(1);
if A.Merge(B).GetValue <> 12 then
halt(1);
end.