View Issue Details

IDProjectCategoryView StatusLast Update
0037175FPCCompilerpublic2020-06-04 17:39
ReporterKostas Michalopoulos Assigned To 
PrioritynormalSeverityminorReproducibilityalways
Status newResolutionopen 
Product Version3.3.1 
Summary0037175: Allow property getters to expose object fields as one of its base/parent types
DescriptionThis patch adds a new feature that enables property getters to expose objects as one of its base/parent types. As an example:

===
program ExposeDerivedAsBase;
{$mode objfpc}
type
  TBase = class
  end;
  TDerived = class(TBase)
  end;
  TSomething = class
  private
    FProp: TDerived;
  public
    property Prop: TBase read FProp;
  end;
begin
end.
===

This can be useful when you want to access the property varfield directly to avoid any additional overhead while hiding the actual internal object type that the object uses. See the message in fpc-devel list for details.
TagsNo tags attached.
Fixed in Revision
FPCOldBugId
FPCTarget
Attached Files

Activities

Kostas Michalopoulos

2020-06-04 03:42

reporter  

expose-derived-objects-as-base.diff (994 bytes)   
Index: compiler/symsym.pas
===================================================================
--- compiler/symsym.pas	(revision 45563)
+++ compiler/symsym.pas	(working copy)
@@ -1580,7 +1580,16 @@
                    finalize_getter_or_setter_for_sym(getset,sym,fielddef,accessordef);
                end
               else
-               IncompatibleTypes(fielddef,propdef);
+               begin
+                 { allow getters to present a derived object type as a base type }
+                 if (getset=palt_read) and
+                    (fielddef.typ=objectdef) and
+                    (propdef.typ=objectdef) and
+                    def_is_related(tobjectdef(fielddef),tobjectdef(propdef)) then
+                   finalize_getter_or_setter_for_sym(getset,sym,fielddef,accessordef)
+                 else
+                   IncompatibleTypes(fielddef,propdef);
+               end;
             end;
           else
             Message(parser_e_ill_property_access_sym);

Serge Anvarov

2020-06-04 16:58

reporter   ~0123222

It's not Delphi compatible. At least two solutions that do not affect the language:
1.
TSomething = class
private
  FProp: record
    case Byte of
      0: (D: TDerived);
      1: (B: TBase);
  end;
public
  property Prop: TBase read FProp.D;
end;
2.
TSomething = class
private
  FProp: TDerived;
  function GetProp: TBase; inline;
public
  property Prop: TBase read GetProp;
end;

function TSomething.GetProp: TBase;
begin
  Result := FProp;
end;

Kostas Michalopoulos

2020-06-04 17:39

reporter   ~0123224

Delphi doesn't have the feature at all and this is objfpc so it shouldn't matter. Existing Delphi code should not be affected and will compile under delphi mode and if it is really necessary, a check to disallow this under delphi mode can be added.

The other solutions are not really equivalent and only work around the issue in the posted example (the example was meant to help understand what this change is about, not a real use case): the second solution changes the property from referencing a variable field to a procedure (which changes the RTTI data and can introduce additional overhead when the compiler fails to inline it - which seems to be the case often when an inlined method is called by another inlined method) and the first solution only works with pointerlike objects. It will not work properly with objects that contain managed types, so if the base or the derived object contain -say- an ansistring or a dynamic array this will fail.

For example this fails:

===
A = object
  A1: string;
end;
B = object(A)
  A2: string;
end;
C = class
public
  FProp: record case Byte of
    0: (M: A);
    1: (V: B);
  end;
public
  property Prop: A read FProp.M;
end;
===

However, this (with my patch applied) works as expected:

===
A = object
  A1: string;
end;
B = object(A)
  A2: string;
end;
C = class
public
  FProp: B;
public
  property Prop: A read FProp;
end;
===

Note that both (RTTI data not being affected and being able to use managed types) are issues with the code i'm using this for (and FWIW the first one actually crashes the compiler, though that might be unrelated since i'm using those with generics).

Also IMO the code i posted is much easier to read while using ".D" and ".B" is basically working around language limitations that do not really need to be there in the first place.

Issue History

Date Modified Username Field Change
2020-06-04 03:42 Kostas Michalopoulos New Issue
2020-06-04 03:42 Kostas Michalopoulos File Added: expose-derived-objects-as-base.diff
2020-06-04 16:58 Serge Anvarov Note Added: 0123222
2020-06-04 17:39 Kostas Michalopoulos Note Added: 0123224