Hello everyone,
I want to propose a new feature I've built for the FPC, which completely uncreatively I named "High level Variant Records". Variant records are quite interesting but the way they are implemented in FPC is very low level. Basically they are the exact same as C style unions, just with extra steps.
That said, syntactically they are quite interesting, because they allow for a selector field, and associating the branches with values the selector field can take. But while this is syntactically interesting, there is no semantic meaning behind this and the following is perfectly legal code:
TVarRec = record
case sel: Boolean of
True: (A: Integer);
True: (B: Double);
end;
vr: TVarRec;
begin
vr.Sel := False;
vr.A:=42;
Furthermore because there is no semantic, variant records do not allow for managed types in the variant branches. So take the following:
TVarRec = record
case sel: Boolean of
True: (A: String);
False: (B: Integer);
end;
This is not allowed because the FPC doesn't know if A or B is set. But from the syntax this should be obvious, if sel is True then A should be set and if it is False B should be set. But this semantic connection is not encoded in the language.
So I set out to change that. And with my
Merge Request I've added 4 new features to do so:
Variant RTTIFirst as a basis I've added RTTI information about the variants. While this was a necessary base for the management operations, it also allows the user to traverse the variant information easiely using the TypInfo unit
td:=GetTypeData(TypeInfo(TVarRec));
for i:=0 to td^.VariantInfo^.BranchCount-1 do
begin
mf:=td^.VariantInfo^.Branches[i]^.BranchStart;
while mf<td^.VariantInfo^.Branches[i]^.BranchEnd do
begin
// Iterate through all fields of a branch and print the type
WriteLn(mf^.TypeRef^.Name);
Inc(mf);
end
end;
Strict VariantsThe next introduction is strict variants, this will add compiler checks that your variant branches need to have unique values and contain a 0 index:
TVarRec = record
case Boolean of
True: (...);
True: (...); // Error because overlap with existing first branch
end;
Variant Access ChecksThis adds runtime checks to variant access to make sure the program only accesses the branch currently selected by the selector:
TVarRec = record
case sel: Boolean of
True: (A: Integer);
False: (B: Double);
end;
var
vr: TVarRec;
begin
vr.sel:=True;
vr.B:=3.14; // Runtime error because B is on false branch but true is set
end;
Managed VariantsLastly, with all these precursers, it finally enables to use managed types in variant branches:
type
TVarRec = record
case sel:Boolean of
True: (s:String); // Managed field
False: (I: Integer);
end;
var
vr: TVarRec;
begin
vr.sel:=True; // Branch switch -> will initialized managed field s
vr.s:='Hello World';
vr.sel:=False; // Branch switch -> will finalize managed field s
end.
Further information can be found in the merge request.
I would be greatful if people can give me feedback and maybe try it out a bit so see whats missing or where problems arise.