Can Identifiers be duplicated across cases of a variant record in FreePascal?

junius picture junius · Apr 21, 2016 · Viewed 8k times · Source

Here's my problem: I want to create a record type where among the cases of a variant record, some, but not all, will have a certain field. According to the wiki, this is perfectly legal. And yet, when I tried to compile the following code:

program example;

{$mode objfpc}{$H+}

uses sysutils;

type
  maritalStates = (single, married, widowed, divorced);

  TPerson = record
    name: record
      first, middle, last: string;
    end;
    sex: (male, female);
    dob: TDateTime;
    case maritalStatus: maritalStates of
      single: ( );
      married, widowed: (marriageDate: TDateTime);
      divorced: (marriageDate, divorceDate: TDateTime;
        isFirstDivorce: boolean)      
  end;

var
  ExPerson: TPerson;

begin
ExPerson.name.first := 'John';
ExPerson.name.middle := 'Bob';
ExPerson.name.last := 'Smith';
ExPerson.sex := male;
ExPerson.dob := StrToDate('05/05/1990');
ExPerson.maritalStatus := married;
ExPerson.marriageDate := StrToDate('04/01/2015');

end.

the compilation fails with the following error:

$ fpc ex.pas
Free Pascal Compiler version 3.0.0 [2016/02/14] for x86_64
Copyright (c) 1993-2015 by Florian Klaempfl and others
Target OS: Win64 for x64
Compiling ex.pas
ex.pas(19,18) Error: Duplicate identifier "marriageDate"
ex.pas(21,3) Error: Duplicate identifier "marriageDate"
ex.pas(35,4) Fatal: There were 2 errors compiling module, stopping
Fatal: Compilation aborted
Error: C:\lazarus\fpc\3.0.0\bin\x86_64-win64\ppcx64.exe returned an error exitcode

Is the wiki simply wrong, or am I missing something here? Is there any way to achieve this effect I want?

Answer

Alexander Baltasar picture Alexander Baltasar · Apr 21, 2016

Very interesting question. I was sure this is possible. If You modify Your code to:

..
married, widowed, divorced: (marriageDate: TDateTime);
divorced: (divorceDate: TDateTime; isFirstDivorce: boolean)
..

it works, but it is not the result you intend to have. Since marriageDate and the divorceDate overlay each other (as mentioned in the comments!)

enter image description here

This picture is taken from "Pascal users manual (4th edition)" and as you can see the variant parts have the same memory location.

According to Pascal users manual (4th edition) and to the book "Turbo Pascal ISBN 3-89011-060-6 the described record declaration on your quoted wiki is not valid!

  1. All field names must be distinct - even if they occur in different variants.
  2. If a variant is empty (i.e., has no fields), the form is: C:()
  3. A field list can have only one variant part and it must follow the fixed part of the record.
  4. A variant may itself contain a variant part; hence variant parts can be nested.
  5. The scope of enumerated type constant identifiers that are introduced in a record type extends over the enclosing block.

Point 1 is the relevant one here! The book "Turbo Pascal" the suggested solution is to use a unique prefix for field names that occur multiple times.

In Your case Your could would look like:

TPerson = record
    name: record
      first, middle, last: string;
    end;
    sex: (male, female);
    dob: TDateTime;
    case maritalStatus: maritalStates of
      single: ( );
      married, widowed: (marMarriageDate: TDateTime);
      divorced: (divMarriageDate, divorceDate: TDateTime;
        isFirstDivorce: boolean)      
  end;

The other solution would be to define married, devorced ... as a record type.

..
married       : (m: TMarried);
divorced      : (d: TDivorced);
..