compiler: implement generic procedural types

- store type parameter in parasymtable, push it to the symtablestack before parsing arguments and return type
  - move procedure/function declaration parsing to procvar_dec subroutine
  - don't skip parasymtable while searching types because they store type parameters now
  - add TParaSymTable.ReadOnly field to prevent adding defs into symtable. Add defs to the parent symtable in this case (we are adding this symtable to stack to read type parameters only, add defs should go to parent in this case as it was before)

git-svn-id: trunk@16719 -
This commit is contained in:
paul 2011-01-06 11:53:51 +00:00
parent fab44804d2
commit 2599cc63bd
6 changed files with 161 additions and 58 deletions

1
.gitattributes vendored
View File

@ -9426,6 +9426,7 @@ tests/test/tgeneric3.pp svneol=native#text/plain
tests/test/tgeneric30.pp svneol=native#text/pascal tests/test/tgeneric30.pp svneol=native#text/pascal
tests/test/tgeneric31.pp svneol=native#text/pascal tests/test/tgeneric31.pp svneol=native#text/pascal
tests/test/tgeneric32.pp svneol=native#text/pascal tests/test/tgeneric32.pp svneol=native#text/pascal
tests/test/tgeneric33.pp svneol=native#text/pascal
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
tests/test/tgeneric6.pp svneol=native#text/plain tests/test/tgeneric6.pp svneol=native#text/plain

View File

@ -611,7 +611,7 @@ implementation
end; end;
end; end;
if isgeneric and not(hdef.typ in [objectdef,recorddef,arraydef]) then if isgeneric and not(hdef.typ in [objectdef,recorddef,arraydef,procvardef]) then
message(parser_e_cant_create_generics_of_this_type); message(parser_e_cant_create_generics_of_this_type);
{ Stop recording a generic template } { Stop recording a generic template }

View File

@ -602,6 +602,7 @@ implementation
case def.typ of case def.typ of
recorddef,objectdef: st:=tabstractrecorddef(def).symtable; recorddef,objectdef: st:=tabstractrecorddef(def).symtable;
arraydef: st:=tarraydef(def).symtable; arraydef: st:=tarraydef(def).symtable;
procvardef: st:=tprocvardef(def).parast;
else else
internalerror(201101020); internalerror(201101020);
end; end;

View File

@ -215,6 +215,8 @@ implementation
st:=genericdef.GetSymtable(gs_record); st:=genericdef.GetSymtable(gs_record);
arraydef: arraydef:
st:=tarraydef(genericdef).symtable; st:=tarraydef(genericdef).symtable;
procvardef:
st:=genericdef.GetSymtable(gs_para);
else else
internalerror(200511182); internalerror(200511182);
end; end;
@ -327,17 +329,31 @@ implementation
read_named_type(tt,specializename,genericdef,generictypelist,false); read_named_type(tt,specializename,genericdef,generictypelist,false);
ttypesym(srsym).typedef:=tt; ttypesym(srsym).typedef:=tt;
tt.typesym:=srsym; tt.typesym:=srsym;
{ Consume the semicolon if it is also recorded }
try_to_consume(_SEMICOLON);
case tt.typ of
{ Build VMT indexes for classes } { Build VMT indexes for classes }
if (tt.typ=objectdef) then objectdef:
begin begin
vmtbuilder:=TVMTBuilder.Create(tobjectdef(tt)); vmtbuilder:=TVMTBuilder.Create(tobjectdef(tt));
vmtbuilder.generate_vmt; vmtbuilder.generate_vmt;
vmtbuilder.free; vmtbuilder.free;
end; end;
{ handle params, calling convention, etc }
procvardef:
begin
if not check_proc_directive(true) then
begin
try_consume_hintdirective(ttypesym(srsym).symoptions,ttypesym(srsym).deprecatedmsg);
consume(_SEMICOLON);
end;
parse_var_proc_directives(ttypesym(srsym));
handle_calling_convention(tprocvardef(tt));
if try_consume_hintdirective(ttypesym(srsym).symoptions,ttypesym(srsym).deprecatedmsg) then
consume(_SEMICOLON);
end;
end;
{ Consume the semicolon if it is also recorded }
try_to_consume(_SEMICOLON);
end; end;
{ Restore symtablestack } { Restore symtablestack }
@ -1247,15 +1263,87 @@ implementation
current_genericdef:=old_current_genericdef; current_genericdef:=old_current_genericdef;
current_specializedef:=old_current_specializedef; current_specializedef:=old_current_specializedef;
end; end;
function procvar_dec(genericdef:tstoreddef;genericlist:TFPObjectList):tdef;
var
is_func:boolean;
pd:tabstractprocdef;
newtype:ttypesym;
old_current_genericdef,
old_current_specializedef: tstoreddef;
old_parse_generic: boolean;
begin
old_current_genericdef:=current_genericdef;
old_current_specializedef:=current_specializedef;
old_parse_generic:=parse_generic;
current_genericdef:=nil;
current_specializedef:=nil;
is_func:=(token=_FUNCTION);
consume(token);
pd:=tprocvardef.create(normal_function_level);
{ usage of specialized type inside its generic template }
if assigned(genericdef) then
current_specializedef:=pd
{ reject declaration of generic class inside generic class }
else if assigned(genericlist) then
current_genericdef:=pd;
symtablestack.push(pd.parast);
insert_generic_parameter_types(pd,genericdef,genericlist);
parse_generic:=(df_generic in pd.defoptions);
{ don't allow to add defs to the symtable - use it for type param search only }
tparasymtable(pd.parast).readonly:=true;
if token=_LKLAMMER then
parse_parameter_dec(pd);
if is_func then
begin
consume(_COLON);
single_type(pd.returndef,[]);
end;
if try_to_consume(_OF) then
begin
consume(_OBJECT);
include(pd.procoptions,po_methodpointer);
end
else if (m_nested_procvars in current_settings.modeswitches) and
try_to_consume(_IS) then
begin
consume(_NESTED);
pd.parast.symtablelevel:=normal_function_level+1;
pd.check_mark_as_nested;
end;
symtablestack.pop(pd.parast);
tparasymtable(pd.parast).readonly:=false;
result:=pd;
{ possible proc directives }
if parseprocvardir then
begin
if check_proc_directive(true) then
begin
newtype:=ttypesym.create('unnamed',result);
parse_var_proc_directives(tsym(newtype));
newtype.typedef:=nil;
result.typesym:=nil;
newtype.free;
end;
{ Add implicit hidden parameters and function result }
handle_calling_convention(pd);
end;
{ restore old state }
parse_generic:=old_parse_generic;
current_genericdef:=old_current_genericdef;
current_specializedef:=old_current_specializedef;
end;
const const
SingleTypeOptionsInTypeBlock:array[Boolean] of TSingleTypeOptions = ([],[stoIsForwardDef]); SingleTypeOptionsInTypeBlock:array[Boolean] of TSingleTypeOptions = ([],[stoIsForwardDef]);
var var
p : tnode; p : tnode;
hdef : tdef; hdef : tdef;
pd : tabstractprocdef;
is_func,
enumdupmsg, first, is_specialize : boolean; enumdupmsg, first, is_specialize : boolean;
newtype : ttypesym;
oldlocalswitches : tlocalswitches; oldlocalswitches : tlocalswitches;
bitpacking: boolean; bitpacking: boolean;
stitem: psymtablestackitem; stitem: psymtablestackitem;
@ -1506,43 +1594,7 @@ implementation
_PROCEDURE, _PROCEDURE,
_FUNCTION: _FUNCTION:
begin begin
is_func:=(token=_FUNCTION); def:=procvar_dec(genericdef,genericlist);
consume(token);
pd:=tprocvardef.create(normal_function_level);
if token=_LKLAMMER then
parse_parameter_dec(pd);
if is_func then
begin
consume(_COLON);
single_type(pd.returndef,[]);
end;
if try_to_consume(_OF) then
begin
consume(_OBJECT);
include(pd.procoptions,po_methodpointer);
end
else if (m_nested_procvars in current_settings.modeswitches) and
try_to_consume(_IS) then
begin
consume(_NESTED);
pd.parast.symtablelevel:=normal_function_level+1;
pd.check_mark_as_nested;
end;
def:=pd;
{ possible proc directives }
if parseprocvardir then
begin
if check_proc_directive(true) then
begin
newtype:=ttypesym.create('unnamed',def);
parse_var_proc_directives(tsym(newtype));
newtype.typedef:=nil;
def.typesym:=nil;
newtype.free;
end;
{ Add implicit hidden parameters and function result }
handle_calling_convention(pd);
end;
end; end;
else else
if (token=_KLAMMERAFFE) and (m_iso in current_settings.modeswitches) then if (token=_KLAMMERAFFE) and (m_iso in current_settings.modeswitches) then

View File

@ -128,10 +128,14 @@ interface
function checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean;override; function checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean;override;
end; end;
{ tparasymtable }
tparasymtable = class(tabstractlocalsymtable) tparasymtable = class(tabstractlocalsymtable)
public public
readonly: boolean;
constructor create(adefowner:tdef;level:byte); constructor create(adefowner:tdef;level:byte);
function checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean;override; function checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean;override;
procedure insertdef(def:TDefEntry);override;
end; end;
tabstractuniTSymtable = class(tstoredsymtable) tabstractuniTSymtable = class(tstoredsymtable)
@ -1373,6 +1377,7 @@ implementation
constructor tparasymtable.create(adefowner:tdef;level:byte); constructor tparasymtable.create(adefowner:tdef;level:byte);
begin begin
inherited create(''); inherited create('');
readonly:=false;
defowner:=adefowner; defowner:=adefowner;
symtabletype:=parasymtable; symtabletype:=parasymtable;
symtablelevel:=level; symtablelevel:=level;
@ -1395,6 +1400,14 @@ implementation
result:=tprocdef(defowner).struct.symtable.checkduplicate(hashedid,sym); result:=tprocdef(defowner).struct.symtable.checkduplicate(hashedid,sym);
end; end;
procedure tparasymtable.insertdef(def: TDefEntry);
begin
if readonly then
defowner.owner.insertdef(def)
else
inherited insertdef(def);
end;
{**************************************************************************** {****************************************************************************
TAbstractUniTSymtable TAbstractUniTSymtable
@ -1965,7 +1978,6 @@ implementation
end; end;
end end
else else
if srsymtable.symtabletype<>parasymtable then
begin begin
srsym:=tsym(srsymtable.FindWithHash(hashedid)); srsym:=tsym(srsymtable.FindWithHash(hashedid));
if assigned(srsym) and if assigned(srsym) and

37
tests/test/tgeneric33.pp Normal file
View File

@ -0,0 +1,37 @@
program tgeneric33;
{$mode objfpc}{$H+}
type
// regular procdef
generic TProc1<T> = function(Value: T): T;
// object procdef
generic TProc2<T> = function(Value: T): T of object;
TFoo = class
function Test2(Value: Integer): Integer;
end;
function Test1(Value: Integer): Integer;
begin
Result := Value + 1;
end;
function TFoo.Test2(Value: Integer): Integer;
begin
Result := Value - 1;
end;
var
Foo: TFoo;
Proc1: specialize TProc1<Integer>;
Proc2: specialize TProc2<Integer>;
begin
Proc1 := @Test1;
if Proc1(1) <> 2 then
halt(1);
Foo := TFoo.Create;
Proc2 := @Foo.Test2;
if Proc2(2) <> 1 then
halt(2);
Foo.Free;
end.