Explorar o código

Postponing building of VMT until inheritance chain is specialized

This commit does 3 changes:
1. Introduce new option `oo_inherits_not_specialized` indicating if
   somewhere in the inheritance chain of an object there is a non
   specialized generic parameter
2. Avoid building the VMT for an object which has a generic parameter in
   the inheritance chain (fixes #40983)
3. When no vmt is build `insert_struct_hidden_paras` usually called as
   part of `build_vmt` will be called seperately to add missing
   parameters
Frederic Kehrein hai 8 meses
pai
achega
05b73f1523

+ 5 - 1
compiler/pdecl.pas

@@ -1134,7 +1134,11 @@ implementation
                     { Build VMT indexes, skip for type renaming and forward classes }
                     if not istyperenaming and
                        not(oo_is_forward in tobjectdef(hdef).objectoptions) then
-                      build_vmt(tobjectdef(hdef));
+                      if not (oo_inherits_not_specialized in tobjectdef(hdef).objectoptions) then
+                        build_vmt(tobjectdef(hdef))
+                      else
+                      { update the procdevs to add hidden self param }
+                      insert_struct_hidden_paras(tobjectdef(hdef));
 
                     { In case of an objcclass, verify that all methods have a message
                       name set. We only check this now, because message names can be set

+ 4 - 1
compiler/pgenutil.pas

@@ -2058,8 +2058,11 @@ uses
 
                       if oo_is_forward in tobjectdef(result).objectoptions then
                         add_forward_generic_def(result,context)
+                      else if not (oo_inherits_not_specialized in tobjectdef(result).objectoptions) then
+                        build_vmt(tobjectdef(result))
                       else
-                        build_vmt(tobjectdef(result));
+                        { update the procdevs to add hidden self param }
+                        insert_struct_hidden_paras(tobjectdef(result));
                     end;
                   { handle params, calling convention, etc }
                   procvardef:

+ 1 - 1
compiler/ppu.pas

@@ -48,7 +48,7 @@ const
   CurrentPPUVersion = 208;
   { for any other changes to the ppu format, increase this version number
     (it's a cardinal) }
-  CurrentPPULongVersion = 26;
+  CurrentPPULongVersion = 27;
 
 { unit flags }
   uf_big_endian          = $000004;

+ 3 - 2
compiler/symconst.pas

@@ -585,7 +585,8 @@ type
     oo_has_new_destructor,{ the object/class declares a destructor (apart from potentially inherting one from the parent) }
     oo_is_funcref,        { interface has a single Invoke method that can be directly called }
     oo_is_invokable,      { interface that is invokable like a function }
-    oo_is_capturer        { the class is the capturer for anonymous functions (or converted proc(var)s) }
+    oo_is_capturer,        { the class is the capturer for anonymous functions (or converted proc(var)s) }
+    oo_inherits_not_specialized { the class inherits from a not yet specialized type }
   );
   tobjectoptions=set of tobjectoption;
 
@@ -895,7 +896,7 @@ const
 {$ifndef jvm}
    inherited_objectoptions : tobjectoptions = [oo_has_virtual,oo_has_private,oo_has_protected,
                 oo_has_strictprotected,oo_has_strictprivate,oo_has_constructor,oo_has_destructor,
-                oo_can_have_published];
+                oo_can_have_published,oo_inherits_not_specialized];
 {$else not jvm}
 { constructors are not inherited in Java }
 inherited_objectoptions : tobjectoptions = [oo_has_virtual,oo_has_private,oo_has_protected,

+ 3 - 0
compiler/symdef.pas

@@ -8327,6 +8327,9 @@ implementation
           exit;
         { inherit options and status }
         objectoptions:=objectoptions+(c.objectoptions*inherited_objectoptions);
+        { check if parent is a generic parameter }
+        if sp_generic_para in c.typesym.symoptions then
+         objectoptions:=objectoptions+[oo_inherits_not_specialized];
         { initially has the same number of abstract methods as the parent }
         abstractcnt:=c.abstractcnt;
         { add the data of the anchestor class/object }

+ 2 - 1
compiler/utils/ppuutils/ppudump.pp

@@ -3302,7 +3302,8 @@ const
      (mask:oo_has_new_destructor; str:'HasNewDestructor'),
      (mask:oo_is_funcref;         str:'IsFuncRef'),
      (mask:oo_is_invokable;       str:'IsInvokable'),
-     (mask:oo_is_capturer;        str:'IsCapturer')
+     (mask:oo_is_capturer;        str:'IsCapturer'),
+     (mask:oo_inherits_not_specialized; str:'InheritedNotSpecialized')
   );
 var
   i      : longint;

+ 15 - 0
tests/test/tgeneric112.pp

@@ -0,0 +1,15 @@
+{$Mode ObjFPC}{$H+}
+
+type
+  generic TTest<T:class> = class(T)
+  public
+    function Foo:Integer;override;
+  end;
+
+function TTest.Foo:Integer;
+begin
+  Result:=42;
+end;
+
+begin
+end.

+ 26 - 0
tests/test/tgeneric113.pp

@@ -0,0 +1,26 @@
+{$Mode ObjFPC}{$H+}
+
+type
+  TBase = class
+  public
+    function Foo:Integer;virtual;abstract;
+  end;
+
+  generic TTest<T:class> = class(T)
+  public
+    function Foo:Integer;override;
+  end;
+
+function TTest.Foo:Integer;
+begin
+  Result:=42;
+end;
+
+var
+  b: TBase;
+begin
+  b:=specialize TTest<TBase>.Create;
+  if b.Foo<>42 then
+    Halt(1);
+  WriteLn('Ok');
+end.

+ 26 - 0
tests/test/tgeneric114.pp

@@ -0,0 +1,26 @@
+{ %FAIL }
+{$Mode ObjFPC}{$H+}
+
+type
+  TBase = class
+  public
+  end;
+
+  generic TTest<T:class> = class(T)
+  public
+    function Foo:Integer;override;
+  end;
+
+function TTest.Foo:Integer;
+begin
+  Result:=42;
+end;
+
+var
+  b: TBase;
+begin
+  b:=specialize TTest<TBase>.Create;
+  if b.Foo<>42 then
+    Halt(1);
+  WriteLn('Ok');
+end.

+ 30 - 0
tests/test/tgeneric115.pp

@@ -0,0 +1,30 @@
+{$Mode ObjFPC}{$H+}
+
+type
+  TBase = class
+  public
+    function Foo:Integer;virtual;abstract;
+  end;
+
+  generic TTest<T:class> = class(T)
+  public
+    function Foo:Integer;override;
+  end;
+
+  generic TTest2<T:class> = class(specialize TTest<T>)
+  public
+  end;
+
+function TTest.Foo:Integer;
+begin
+  Result:=42;
+end;
+
+var
+  b: TBase;
+begin
+  b:=specialize TTest2<TBase>.Create;
+  if b.Foo<>42 then
+    Halt(1);
+  WriteLn('Ok');
+end.

+ 30 - 0
tests/test/tgeneric116.pp

@@ -0,0 +1,30 @@
+{$Mode ObjFPC}{$H+}
+
+type
+  TBase = class
+  public
+    function Foo:Integer;virtual;abstract;
+  end;
+
+  generic TTest<T:class> = class(T)
+  public
+  end;
+
+  generic TTest2<T:class> = class(specialize TTest<T>)
+  public
+    function Foo:Integer;override;
+  end;
+
+function TTest2.Foo:Integer;
+begin
+  Result:=42;
+end;
+
+var
+  b: TBase;
+begin
+  b:=specialize TTest2<TBase>.Create;
+  if b.Foo<>42 then
+    Halt(1);
+  WriteLn('Ok');
+end.