Product / versions
- Remoting SDK + Data Abstract for Delphi 10.0.0.1627 — RAD Studio 13.1 (Win32/Win64) → fails
- Same project with Remoting SDK + Data Abstract for Delphi 10.0.0.1553 — RAD Studio 11.2 → compiled fine
Summary
When a RODL struct declares an Ancestor that comes from a used RODL marked DontCodeGen=“1” (the classic MyUserInfo : UserInfo case for SimpleLoginService), the generated _Intf.pas assigns directly to the backing fields of the ancestor. Those fields are declared private in another unit (DataAbstract4_Intf.pas), so the unit cannot compile.
RODL (minimal repro)
<Structs>
<Struct Name="LeContManagementUserInfo" UID="{576C3CF8-...}" AutoCreateParams="1" Ancestor="UserInfo">
<Elements>
<Element Name="IDRole" DataType="Integer" />
<Element Name="RoleName" DataType="WideString" />
<Element Name="Name" DataType="WideString" />
<Element Name="Surname" DataType="WideString" />
</Elements>
</Struct>
</Structs>
<Uses>
<Use Name="DataAbstract" UID="{EBEAC9B9-...}"
Rodl="$(Data Abstract for Delphi)\Source\DataAbstract4.RODL"
DontCodeGen="1" UsedRodlUID="{DC8B7BE2-...}">
<Includes Delphi="DataAbstract4" ... />
</Use>
</Uses>
Generated code (broken) — LeContManagementDA_Intf.pas, non-RecordStrictOrder branch of ReadComplex / WriteComplex:
LeContManagementUserInfo = class(DataAbstract4_Intf.UserInfo) // ancestor lives in ANOTHER unit
...
procedure LeContManagementUserInfo.ReadComplex(aSerializer: TObject);
begin
...
else begin
__Serializer.ReadArrayWithErrorHandling('Attributes', DataAbstract4_Intf.VariantArray, l_Attributes);
if Self.int_Attributes <> l_Attributes then FreeOrDisposeOf(Self.int_Attributes);
Self.fAttributes := l_Attributes; // <-- E2361
...
Self.fPrivileges := l_Privileges; // <-- E2361
Self.fSessionID := l_SessionID; // <-- E2361
Self.fUserData := l_UserData; // <-- E2361
Self.fUserID := l_UserID; // <-- E2361
end;
end;
procedure LeContManagementUserInfo.WriteComplex(aSerializer: TObject);
begin
...
else begin
...
l_SessionID := Self.fSessionID; // <-- E2361
l_UserID := Self.fUserID; // <-- E2361
end;
end;
Ancestor declaration — DataAbstract4_Intf.pas (shipped, unmodified):
UserInfo = class(TROComplexType)
private
fSessionID: ROUTF8String;
fUserID: ROUTF8String;
fPrivileges: StringArray;
fAttributes: VariantArray;
fUserData: Binary;
...
protected
property int_Privileges: StringArray read fPrivileges;
property int_Attributes: VariantArray read fAttributes;
property int_UserData: Binary read fUserData;
published
property SessionID: ROUTF8String read fSessionID write fSessionID;
property UserID: ROUTF8String read fUserID write fUserID;
property Privileges: StringArray read GetPrivileges write fPrivileges;
property Attributes: VariantArray read GetAttributes write fAttributes;
property UserData: Binary read GetUserData write fUserData;
end;
Compiler output
[dcc32 Error] LeContManagementDA_Intf.pas(353): E2361 Cannot access private symbol UserInfo.fAttributes
[dcc32 Error] LeContManagementDA_Intf.pas(362): E2361 Cannot access private symbol UserInfo.fPrivileges
[dcc32 Error] LeContManagementDA_Intf.pas(366): E2361 Cannot access private symbol UserInfo.fSessionID
[dcc32 Error] LeContManagementDA_Intf.pas(373): E2361 Cannot access private symbol UserInfo.fUserData
[dcc32 Error] LeContManagementDA_Intf.pas(375): E2361 Cannot access private symbol UserInfo.fUserID
[dcc32 Error] LeContManagementDA_Intf.pas(416): E2361 Cannot access private symbol UserInfo.fSessionID
[dcc32 Error] LeContManagementDA_Intf.pas(422): E2361 Cannot access private symbol UserInfo.fUserID
Analysis
The generator is clearly aware that the ancestor is external — for arrays and binary it correctly reads through the protected accessors (Self.int_Attributes, Self.int_Privileges, Self.int_UserData). It is only the assignments (and the two scalar reads of SessionID/UserID) that go through Self.f, which is legal only when the ancestor is generated into the same unit. The RecordStrictOrder branch is fine, since it delegates inherited members to inherited ReadComplex / inherited WriteComplex; only the flattened (non-strict-order) branch is affected.
An older generated version of the very same unit, still present in our repository and used by the client application, emitted the published properties instead and compiles without changes:
Self.Attributes := l_Attributes;
Self.SessionID := l_SessionID;
l_UserID := Self.UserID;
This looks like the Delphi counterpart of bug #77561 (“C++ compiling error when FreeAndNil of private properties in an inherited structure”), fixed for the C++Builder generator by switching this->f… to this->int_…
Workaround currently in use
Hand-editing the 7 lines in the generated _Intf.pas (Self.f → Self.) and disabling the {#ROGEN:…} directive in the .dpr, so the IDE compile notifier does not regenerate — and thus re-break — the unit on every build.