Browse Source

* pdecobj.pas, object_dec:
Always define nested types of a generic as generic as well.
* symtable.pas:
+ Add a function which decides whether two defs belong to the same generic.
* Fix sym_is_owned_by as there can be corner cases where childsym becomes Nil.
* ptype.pas:
* record_dec: Always define nested types of a generic as generic as well.
* single_type & read_named_type.expr_type:
Use the newly added function to improve the checks whether a generic
symbol should be rejected as "generics not allowed for types of
variables or fields".
+ Add a test for which the improved checks are necessary.

git-svn-id: trunk@20189 -

svenbarth 13 years ago
parent
commit
a28a9a9521
5 changed files with 71 additions and 5 deletions
  1. 1 0
      .gitattributes
  2. 6 0
      compiler/pdecobj.pas
  3. 25 3
      compiler/ptype.pas
  4. 17 2
      compiler/symtable.pas
  5. 22 0
      tests/test/tgeneric73.pp

+ 1 - 0
.gitattributes

@@ -10342,6 +10342,7 @@ tests/test/tgeneric7.pp svneol=native#text/plain
 tests/test/tgeneric70.pp svneol=native#text/pascal
 tests/test/tgeneric70.pp svneol=native#text/pascal
 tests/test/tgeneric71.pp svneol=native#text/pascal
 tests/test/tgeneric71.pp svneol=native#text/pascal
 tests/test/tgeneric72.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/tgeneric8.pp svneol=native#text/plain
 tests/test/tgeneric9.pp svneol=native#text/plain
 tests/test/tgeneric9.pp svneol=native#text/plain
 tests/test/tgoto.pp svneol=native#text/plain
 tests/test/tgoto.pp svneol=native#text/plain

+ 6 - 0
compiler/pdecobj.pas

@@ -1152,6 +1152,12 @@ implementation
         if assigned(old_current_structdef) and
         if assigned(old_current_structdef) and
             (df_specialization in old_current_structdef.defoptions) then
             (df_specialization in old_current_structdef.defoptions) then
           include(current_structdef.defoptions,df_specialization);
           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
         { 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) }

+ 25 - 3
compiler/ptype.pas

@@ -386,7 +386,14 @@ implementation
                   (
                   (
                     parse_generic and
                     parse_generic and
                     (current_genericdef.typ in [recorddef,objectdef]) 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<T>"
+                        declarations inside a generic }
+                      sym_is_owned_by(srsym,tabstractrecorddef(current_genericdef).symtable)
+                    )
                   )
                   )
                 then
                 then
               begin
               begin
@@ -742,6 +749,12 @@ implementation
          if assigned(old_current_structdef) and
          if assigned(old_current_structdef) and
              (df_specialization in old_current_structdef.defoptions) then
              (df_specialization in old_current_structdef.defoptions) then
            include(current_structdef.defoptions,df_specialization);
            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);
          insert_generic_parameter_types(current_structdef,genericdef,genericlist);
          { when we are parsing a generic already then this is a generic as
          { when we are parsing a generic already then this is a generic as
@@ -921,8 +934,17 @@ implementation
                                parse_generic and
                                parse_generic and
                                (current_genericdef.typ in [recorddef,objectdef]) and
                                (current_genericdef.typ in [recorddef,objectdef]) and
                                (def.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<T>"
+                                   declarations inside a generic }
+                                 (
+                                   (ttypenode(pt1).typesym<>nil) and
+                                   sym_is_owned_by(ttypenode(pt1).typesym,tabstractrecorddef(current_genericdef).symtable)
+                                 )
+                               )
                              )
                              )
                            then
                            then
                          begin
                          begin

+ 17 - 2
compiler/symtable.pas

@@ -225,6 +225,7 @@ interface
     procedure addsymref(sym:tsym);
     procedure addsymref(sym:tsym);
     function  is_owned_by(childdef,ownerdef:tdef):boolean;
     function  is_owned_by(childdef,ownerdef:tdef):boolean;
     function  sym_is_owned_by(childsym:tsym;symtable:tsymtable):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(symst:tsymtable;symvisibility:tvisibility;contextobjdef:tabstractrecorddef):boolean;
     function  is_visible_for_object(pd:tprocdef;contextobjdef:tabstractrecorddef):boolean;
     function  is_visible_for_object(pd:tprocdef;contextobjdef:tabstractrecorddef):boolean;
     function  is_visible_for_object(sym:tsym;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;
     function sym_is_owned_by(childsym:tsym;symtable:tsymtable):boolean;
       begin
       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);
           result:=sym_is_owned_by(tabstractrecorddef(childsym.owner.defowner).typesym,symtable);
       end;
       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;
     function is_visible_for_object(symst:tsymtable;symvisibility:tvisibility;contextobjdef:tabstractrecorddef):boolean;
       var
       var
         symownerdef : tabstractrecorddef;
         symownerdef : tabstractrecorddef;

+ 22 - 0
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<T> = class
+  public type
+    TSubClass1 = class
+
+    end;
+
+    TSubClass2 = class
+      f: TSubClass1;
+    end;
+  end;
+
+begin
+
+end.