diff --git a/.gitattributes b/.gitattributes index 372184c018..ab3803aa46 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10342,6 +10342,7 @@ tests/test/tgeneric7.pp svneol=native#text/plain tests/test/tgeneric70.pp svneol=native#text/pascal tests/test/tgeneric71.pp svneol=native#text/pascal tests/test/tgeneric72.pp svneol=native#text/pascal +tests/test/tgeneric73.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 diff --git a/compiler/pdecobj.pas b/compiler/pdecobj.pas index 1c29df2437..4c270a43ed 100644 --- a/compiler/pdecobj.pas +++ b/compiler/pdecobj.pas @@ -1152,6 +1152,12 @@ implementation if assigned(old_current_structdef) and (df_specialization in old_current_structdef.defoptions) then include(current_structdef.defoptions,df_specialization); + if assigned(old_current_structdef) and + (df_generic in old_current_structdef.defoptions) then + begin + include(current_structdef.defoptions,df_generic); + current_genericdef:=current_structdef; + end; { 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) } diff --git a/compiler/ptype.pas b/compiler/ptype.pas index da096f17b8..8be3bb6e7b 100644 --- a/compiler/ptype.pas +++ b/compiler/ptype.pas @@ -386,7 +386,14 @@ implementation ( parse_generic and (current_genericdef.typ in [recorddef,objectdef]) and - sym_is_owned_by(srsym,tabstractrecorddef(current_genericdef).symtable) + ( + { if both defs belong to the same generic (e.g. both are + subtypes) then we must allow the usage } + defs_belong_to_same_generic(def,current_genericdef) or + { this is needed to correctly resolve "type Foo=SomeGeneric" + declarations inside a generic } + sym_is_owned_by(srsym,tabstractrecorddef(current_genericdef).symtable) + ) ) then begin @@ -742,6 +749,12 @@ implementation if assigned(old_current_structdef) and (df_specialization in old_current_structdef.defoptions) then include(current_structdef.defoptions,df_specialization); + if assigned(old_current_structdef) and + (df_generic in old_current_structdef.defoptions) then + begin + include(current_structdef.defoptions,df_generic); + current_genericdef:=current_structdef; + end; insert_generic_parameter_types(current_structdef,genericdef,genericlist); { when we are parsing a generic already then this is a generic as @@ -921,8 +934,17 @@ implementation parse_generic and (current_genericdef.typ in [recorddef,objectdef]) and (def.typ in [recorddef,objectdef]) and - (ttypenode(pt1).typesym<>nil) and - sym_is_owned_by(ttypenode(pt1).typesym,tabstractrecorddef(current_genericdef).symtable) + ( + { if both defs belong to the same generic (e.g. both are + subtypes) then we must allow the usage } + defs_belong_to_same_generic(def,current_genericdef) or + { this is needed to correctly resolve "type Foo=SomeGeneric" + declarations inside a generic } + ( + (ttypenode(pt1).typesym<>nil) and + sym_is_owned_by(ttypenode(pt1).typesym,tabstractrecorddef(current_genericdef).symtable) + ) + ) ) then begin diff --git a/compiler/symtable.pas b/compiler/symtable.pas index 9f382ae279..2a927bdb69 100644 --- a/compiler/symtable.pas +++ b/compiler/symtable.pas @@ -225,6 +225,7 @@ interface procedure addsymref(sym:tsym); function is_owned_by(childdef,ownerdef:tdef):boolean; function sym_is_owned_by(childsym:tsym;symtable:tsymtable):boolean; + function defs_belong_to_same_generic(def1,def2:tdef):boolean; function is_visible_for_object(symst:tsymtable;symvisibility:tvisibility;contextobjdef:tabstractrecorddef):boolean; function is_visible_for_object(pd:tprocdef;contextobjdef:tabstractrecorddef):boolean; function is_visible_for_object(sym:tsym;contextobjdef:tabstractrecorddef):boolean; @@ -1880,11 +1881,25 @@ implementation function sym_is_owned_by(childsym:tsym;symtable:tsymtable):boolean; begin - result:=childsym.owner=symtable; - if not result and (childsym.owner.symtabletype in [objectsymtable,recordsymtable]) then + result:=assigned(childsym) and (childsym.owner=symtable); + if not result and assigned(childsym) and + (childsym.owner.symtabletype in [objectsymtable,recordsymtable]) then result:=sym_is_owned_by(tabstractrecorddef(childsym.owner.defowner).typesym,symtable); end; + function defs_belong_to_same_generic(def1, def2: tdef): boolean; + begin + result:=false; + if not assigned(def1) or not assigned(def2) then + exit; + { for both defs walk to the topmost generic } + while assigned(def1.owner.defowner) and (df_generic in tstoreddef(def1.owner.defowner).defoptions) do + def1:=tdef(def1.owner.defowner); + while assigned(def2.owner.defowner) and (df_generic in tstoreddef(def2.owner.defowner).defoptions) do + def2:=tdef(def2.owner.defowner); + result:=def1=def2; + end; + function is_visible_for_object(symst:tsymtable;symvisibility:tvisibility;contextobjdef:tabstractrecorddef):boolean; var symownerdef : tabstractrecorddef; diff --git a/tests/test/tgeneric73.pp b/tests/test/tgeneric73.pp new file mode 100644 index 0000000000..0ccbdac00e --- /dev/null +++ b/tests/test/tgeneric73.pp @@ -0,0 +1,22 @@ +{ %NORUN } + +{ This tests that nested types can reference each other inside a generic } +program tgeneric73; + +{$mode objfpc} + +type + generic TTest = class + public type + TSubClass1 = class + + end; + + TSubClass2 = class + f: TSubClass1; + end; + end; + +begin + +end.