mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-12-16 06:10:34 +01:00
Fix for Mantis #21064.
* pgenutil.pas: factor out the reading of generic specialization parameters (parse_generic_specialization_types) and the generation of a generic type name (generate_generic_name)
* pdecsub.pas, parse_proc_head:
* also allow an interface alias declaration if an identifier is followed by a "<" (which starts a specialization)
+ add a procedure "consume_generic_interface" which parses such a specialization (by using "parse_generic_specialization_types") - this is needed, because "consume_generic_type_parameter" can (and should not!) handle "ISomeIntf<Integer, T>" or (somewhen in the future) "ISomeIntf<TSomeOtherGeneric<T>>" - and finds the correct symbol for the interface (by utilizing the "generate_generic_name" function)
* generate the correct mapping entry (for the generic it's only needed for checking (if any), but for a specialization it's essential that we reference the correct specialization)
+ add tests which were included with the issue and also two additional ones
Note: In non-Delphi modes an interface alias can be done like in Delphi mode; "specialization" is not necessary and furthermore not even allowed!
git-svn-id: trunk@21656 -
This commit is contained in:
parent
ecd0f53488
commit
75bf094e3f
4
.gitattributes
vendored
4
.gitattributes
vendored
@ -10717,6 +10717,8 @@ tests/test/tgeneric74.pp svneol=native#text/pascal
|
||||
tests/test/tgeneric75.pp svneol=native#text/pascal
|
||||
tests/test/tgeneric76.pp svneol=native#text/pascal
|
||||
tests/test/tgeneric77.pp svneol=native#text/pascal
|
||||
tests/test/tgeneric78.pp svneol=native#text/pascal
|
||||
tests/test/tgeneric79.pp svneol=native#text/pascal
|
||||
tests/test/tgeneric8.pp svneol=native#text/plain
|
||||
tests/test/tgeneric9.pp svneol=native#text/plain
|
||||
tests/test/tgoto.pp svneol=native#text/plain
|
||||
@ -12591,6 +12593,8 @@ tests/webtbs/tw20995b.pp svneol=native#text/pascal
|
||||
tests/webtbs/tw20998.pp svneol=native#text/pascal
|
||||
tests/webtbs/tw21029.pp svneol=native#text/plain
|
||||
tests/webtbs/tw21044.pp svneol=native#text/pascal
|
||||
tests/webtbs/tw21064a.pp svneol=native#text/pascal
|
||||
tests/webtbs/tw21064b.pp svneol=native#text/pascal
|
||||
tests/webtbs/tw21073.pp svneol=native#text/plain
|
||||
tests/webtbs/tw2109.pp svneol=native#text/plain
|
||||
tests/webtbs/tw21091.pp svneol=native#text/pascal
|
||||
|
||||
@ -106,7 +106,7 @@ implementation
|
||||
objcutil,
|
||||
{ parser }
|
||||
scanner,
|
||||
pbase,pexpr,ptype,pdecl,pparautl
|
||||
pbase,pexpr,ptype,pdecl,pparautl,pgenutil
|
||||
{$ifdef jvm}
|
||||
,pjvm
|
||||
{$endif}
|
||||
@ -680,8 +680,44 @@ implementation
|
||||
Message1(type_e_generic_declaration_does_not_match,genname);
|
||||
srsym:=nil;
|
||||
exit;
|
||||
end
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure consume_generic_interface;
|
||||
var
|
||||
genparalist : tfpobjectlist;
|
||||
prettyname,
|
||||
specializename : ansistring;
|
||||
genname,
|
||||
ugenname : tidstring;
|
||||
gencount : string;
|
||||
begin
|
||||
consume(_LSHARPBRACKET);
|
||||
genparalist:=tfpobjectlist.create(false);
|
||||
|
||||
if not parse_generic_specialization_types(genparalist,prettyname,specializename,nil) then
|
||||
srsym:=generrorsym
|
||||
else
|
||||
begin
|
||||
str(genparalist.count,gencount);
|
||||
genname:=sp+'$'+gencount;
|
||||
if not parse_generic then
|
||||
genname:=generate_generic_name(genname,specializename);
|
||||
ugenname:=upper(genname);
|
||||
|
||||
srsym:=search_object_name(ugenname,false);
|
||||
|
||||
if not assigned(srsym) then
|
||||
begin
|
||||
Message1(type_e_generic_declaration_does_not_match,sp+'<'+prettyname+'>');
|
||||
srsym:=nil;
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
genparalist.free;
|
||||
consume(_RSHARPBRACKET);
|
||||
end;
|
||||
|
||||
begin
|
||||
@ -700,16 +736,35 @@ implementation
|
||||
(astruct.typ=objectdef) and
|
||||
assigned(tobjectdef(astruct).ImplementedInterfaces) and
|
||||
(tobjectdef(astruct).ImplementedInterfaces.count>0) and
|
||||
try_to_consume(_POINT) then
|
||||
(
|
||||
(token = _POINT) or
|
||||
(token = _LSHARPBRACKET)
|
||||
) then
|
||||
begin
|
||||
srsym:=search_object_name(sp,true);
|
||||
if token = _POINT then
|
||||
begin
|
||||
consume(_POINT);
|
||||
srsym:=search_object_name(sp,true);
|
||||
end
|
||||
else
|
||||
begin
|
||||
consume_generic_interface;
|
||||
consume(_POINT);
|
||||
{ srsym is now either an interface def or generrordef }
|
||||
end;
|
||||
{ qualifier is interface? }
|
||||
ImplIntf:=nil;
|
||||
if (srsym.typ=typesym) and
|
||||
(ttypesym(srsym).typedef.typ=objectdef) then
|
||||
ImplIntf:=tobjectdef(astruct).find_implemented_interface(tobjectdef(ttypesym(srsym).typedef));
|
||||
if ImplIntf=nil then
|
||||
Message(parser_e_interface_id_expected);
|
||||
Message(parser_e_interface_id_expected)
|
||||
else
|
||||
{ in case of a generic or specialized interface we need to use the
|
||||
name of the def instead of the symbol, so that always the correct
|
||||
name is used }
|
||||
if [df_generic,df_specialization]*ttypesym(srsym).typedef.defoptions<>[] then
|
||||
sp:=tobjectdef(ttypesym(srsym).typedef).objname^;
|
||||
{ must be a directly implemented interface }
|
||||
if Assigned(ImplIntf.ImplementsGetter) then
|
||||
Message2(parser_e_implements_no_mapping,ImplIntf.IntfDef.typename,astruct.objrealname^);
|
||||
|
||||
@ -36,8 +36,10 @@ uses
|
||||
|
||||
procedure generate_specialization(var tt:tdef;parse_class_parent:boolean;_prettyname:string;parsedtype:tdef;symname:string);
|
||||
function parse_generic_parameters:TFPObjectList;
|
||||
function parse_generic_specialization_types(genericdeflist:tfpobjectlist;out prettyname,specializename:ansistring;parsedtype:tdef):boolean;
|
||||
procedure insert_generic_parameter_types(def:tstoreddef;genericdef:tstoreddef;genericlist:TFPObjectList);
|
||||
procedure maybe_insert_generic_rename_symbol(const name:tidstring;genericlist:tfpobjectlist);
|
||||
function generate_generic_name(const name:tidstring;specializename:ansistring):tidstring;
|
||||
|
||||
type
|
||||
tspecializationstate = record
|
||||
@ -190,59 +192,7 @@ uses
|
||||
genericdeflist:=TFPObjectList.Create(false);
|
||||
|
||||
{ Parse type parameters }
|
||||
err:=false;
|
||||
{ set the block type to type, so that the parsed type are returned as
|
||||
ttypenode (e.g. classes are in non type-compatible blocks returned as
|
||||
tloadvmtaddrnode) }
|
||||
old_block_type:=block_type;
|
||||
{ if parsedtype is set, then the first type identifer was already parsed
|
||||
(happens in inline specializations) and thus we only need to parse
|
||||
the remaining types and do as if the first one was already given }
|
||||
first:=not assigned(parsedtype);
|
||||
if assigned(parsedtype) then
|
||||
begin
|
||||
genericdeflist.Add(parsedtype);
|
||||
specializename:='$'+parsedtype.typename;
|
||||
prettyname:=parsedtype.typesym.prettyname;
|
||||
end
|
||||
else
|
||||
begin
|
||||
specializename:='';
|
||||
prettyname:='';
|
||||
end;
|
||||
while not (token in [_GT,_RSHARPBRACKET]) do
|
||||
begin
|
||||
{ "first" is set to false at the end of the loop! }
|
||||
if not first then
|
||||
consume(_COMMA);
|
||||
block_type:=bt_type;
|
||||
pt2:=factor(false,true);
|
||||
if pt2.nodetype=typen then
|
||||
begin
|
||||
if df_generic in pt2.resultdef.defoptions then
|
||||
Message(parser_e_no_generics_as_params);
|
||||
genericdeflist.Add(pt2.resultdef);
|
||||
if not assigned(pt2.resultdef.typesym) then
|
||||
message(type_e_generics_cannot_reference_itself)
|
||||
else
|
||||
begin
|
||||
specializename:=specializename+'$'+pt2.resultdef.typename;
|
||||
if first then
|
||||
prettyname:=prettyname+pt2.resultdef.typesym.prettyname
|
||||
else
|
||||
prettyname:=prettyname+','+pt2.resultdef.typesym.prettyname;
|
||||
end;
|
||||
end
|
||||
else
|
||||
begin
|
||||
Message(type_e_type_id_expected);
|
||||
err:=true;
|
||||
end;
|
||||
pt2.free;
|
||||
first:=false;
|
||||
end;
|
||||
block_type:=old_block_type;
|
||||
|
||||
err:=not parse_generic_specialization_types(genericdeflist,prettyname,specializename,parsedtype);
|
||||
if err then
|
||||
begin
|
||||
try_to_consume(_RSHARPBRACKET);
|
||||
@ -305,8 +255,7 @@ uses
|
||||
genericdef:=tstoreddef(ttypesym(srsym).typedef);
|
||||
|
||||
{ build the new type's name }
|
||||
crc:=UpdateCrc32(0,specializename[1],length(specializename));
|
||||
finalspecializename:=genname+'$crc'+hexstr(crc,8);
|
||||
finalspecializename:=generate_generic_name(genname,specializename);
|
||||
ufinalspecializename:=upper(finalspecializename);
|
||||
prettyname:=genericdef.typesym.prettyname+'<'+prettyname+'>';
|
||||
|
||||
@ -570,6 +519,67 @@ uses
|
||||
until not try_to_consume(_COMMA) ;
|
||||
end;
|
||||
|
||||
function parse_generic_specialization_types(genericdeflist:tfpobjectlist;out prettyname,specializename:ansistring;parsedtype:tdef):boolean;
|
||||
var
|
||||
old_block_type : tblock_type;
|
||||
first : boolean;
|
||||
typeparam : tnode;
|
||||
begin
|
||||
result:=true;
|
||||
if genericdeflist=nil then
|
||||
internalerror(2012061401);
|
||||
{ set the block type to type, so that the parsed type are returned as
|
||||
ttypenode (e.g. classes are in non type-compatible blocks returned as
|
||||
tloadvmtaddrnode) }
|
||||
old_block_type:=block_type;
|
||||
{ if parsedtype is set, then the first type identifer was already parsed
|
||||
(happens in inline specializations) and thus we only need to parse
|
||||
the remaining types and do as if the first one was already given }
|
||||
first:=not assigned(parsedtype);
|
||||
if assigned(parsedtype) then
|
||||
begin
|
||||
genericdeflist.Add(parsedtype);
|
||||
specializename:='$'+parsedtype.typename;
|
||||
prettyname:=parsedtype.typesym.prettyname;
|
||||
end
|
||||
else
|
||||
begin
|
||||
specializename:='';
|
||||
prettyname:='';
|
||||
end;
|
||||
while not (token in [_GT,_RSHARPBRACKET]) do
|
||||
begin
|
||||
{ "first" is set to false at the end of the loop! }
|
||||
if not first then
|
||||
consume(_COMMA);
|
||||
block_type:=bt_type;
|
||||
typeparam:=factor(false,true);
|
||||
if typeparam.nodetype=typen then
|
||||
begin
|
||||
if df_generic in typeparam.resultdef.defoptions then
|
||||
Message(parser_e_no_generics_as_params);
|
||||
genericdeflist.Add(typeparam.resultdef);
|
||||
if not assigned(typeparam.resultdef.typesym) then
|
||||
message(type_e_generics_cannot_reference_itself)
|
||||
else
|
||||
begin
|
||||
specializename:=specializename+'$'+typeparam.resultdef.typename;
|
||||
if first then
|
||||
prettyname:=prettyname+typeparam.resultdef.typesym.prettyname
|
||||
else
|
||||
prettyname:=prettyname+','+typeparam.resultdef.typesym.prettyname;
|
||||
end;
|
||||
end
|
||||
else
|
||||
begin
|
||||
Message(type_e_type_id_expected);
|
||||
result:=false;
|
||||
end;
|
||||
typeparam.free;
|
||||
first:=false;
|
||||
end;
|
||||
block_type:=old_block_type;
|
||||
end;
|
||||
|
||||
procedure insert_generic_parameter_types(def:tstoreddef;genericdef:tstoreddef;genericlist:TFPObjectList);
|
||||
var
|
||||
@ -634,6 +644,17 @@ uses
|
||||
end;
|
||||
end;
|
||||
|
||||
function generate_generic_name(const name:tidstring;specializename:ansistring):tidstring;
|
||||
var
|
||||
crc : cardinal;
|
||||
begin
|
||||
if specializename='' then
|
||||
internalerror(2012061901);
|
||||
{ build the new type's name }
|
||||
crc:=UpdateCrc32(0,specializename[1],length(specializename));
|
||||
result:=name+'$crc'+hexstr(crc,8);
|
||||
end;
|
||||
|
||||
procedure specialization_init(genericdef:tdef;var state: tspecializationstate);
|
||||
var
|
||||
pu : tused_unit;
|
||||
|
||||
27
tests/test/tgeneric78.pp
Normal file
27
tests/test/tgeneric78.pp
Normal file
@ -0,0 +1,27 @@
|
||||
{ %NORUN }
|
||||
|
||||
{ additional test based on 21064 }
|
||||
program tgeneric78;
|
||||
|
||||
{$mode delphi}
|
||||
|
||||
type
|
||||
IGenericIntf<T> = interface
|
||||
function SomeMethod: T;
|
||||
end;
|
||||
|
||||
TGenericClass<T> = class(TInterfacedObject, IGenericIntf<LongInt>)
|
||||
private
|
||||
protected
|
||||
function GenericIntf_SomeMethod: LongInt;
|
||||
function IGenericIntf<LongInt>.SomeMethod = GenericIntf_SomeMethod;
|
||||
end;
|
||||
|
||||
function TGenericClass<T>.GenericIntf_SomeMethod: LongInt;
|
||||
begin
|
||||
end;
|
||||
|
||||
type
|
||||
TGenericClassLongInt = TGenericClass<String>;
|
||||
begin
|
||||
end.
|
||||
27
tests/test/tgeneric79.pp
Normal file
27
tests/test/tgeneric79.pp
Normal file
@ -0,0 +1,27 @@
|
||||
{ %NORUN }
|
||||
|
||||
{ additional test based on 21064 }
|
||||
program tgeneric79;
|
||||
|
||||
{$mode objfpc}
|
||||
|
||||
type
|
||||
generic IGenericIntf<T> = interface
|
||||
function SomeMethod: T;
|
||||
end;
|
||||
|
||||
generic TGenericClass<T> = class(TInterfacedObject, specialize IGenericIntf<LongInt>)
|
||||
private
|
||||
protected
|
||||
function GenericIntf_SomeMethod: LongInt;
|
||||
function IGenericIntf<LongInt>.SomeMethod = GenericIntf_SomeMethod;
|
||||
end;
|
||||
|
||||
function TGenericClass.GenericIntf_SomeMethod: LongInt;
|
||||
begin
|
||||
end;
|
||||
|
||||
type
|
||||
TGenericClassLongInt = specialize TGenericClass<String>;
|
||||
begin
|
||||
end.
|
||||
26
tests/webtbs/tw21064a.pp
Normal file
26
tests/webtbs/tw21064a.pp
Normal file
@ -0,0 +1,26 @@
|
||||
{ %NORUN }
|
||||
|
||||
program tw21064a;
|
||||
|
||||
{$mode delphi}
|
||||
|
||||
type
|
||||
IGenericIntf<T> = interface
|
||||
function SomeMethod: T;
|
||||
end;
|
||||
|
||||
TGenericClass<T> = class(TInterfacedObject, IGenericIntf<T>)
|
||||
private
|
||||
protected
|
||||
function GenericIntf_SomeMethod: T;
|
||||
function IGenericIntf<T>.SomeMethod = GenericIntf_SomeMethod;
|
||||
end;
|
||||
|
||||
function TGenericClass<T>.GenericIntf_SomeMethod: T;
|
||||
begin
|
||||
end;
|
||||
|
||||
type
|
||||
TGenericClassLongInt = TGenericClass<LongInt>;
|
||||
begin
|
||||
end.
|
||||
28
tests/webtbs/tw21064b.pp
Normal file
28
tests/webtbs/tw21064b.pp
Normal file
@ -0,0 +1,28 @@
|
||||
{ %NORUN }
|
||||
|
||||
program tw21064b;
|
||||
|
||||
{$mode delphi}
|
||||
|
||||
type
|
||||
IGenericIntf<T> = interface
|
||||
function SomeMethod: T;
|
||||
end;
|
||||
|
||||
TGenericClass<T> = class(TInterfacedObject, IGenericIntf<T>)
|
||||
private
|
||||
type
|
||||
IntfType = IGenericIntf<T>;
|
||||
protected
|
||||
function GenericIntf_SomeMethod: T;
|
||||
function IntfType.SomeMethod = GenericIntf_SomeMethod;
|
||||
end;
|
||||
|
||||
function TGenericClass<T>.GenericIntf_SomeMethod: T;
|
||||
begin
|
||||
end;
|
||||
|
||||
type
|
||||
TGenericClassLongInt = TGenericClass<LongInt>;
|
||||
begin
|
||||
end.
|
||||
Loading…
Reference in New Issue
Block a user