*!topic* Components of derived untagged discriminated record types
*!reference* Ada 2022 RM3.7(18-25), RM4.10(7/5,17/5,18/5), RM13.13.2(8.2/2-9.1/5)
*!from* Christoph Grein 2023-12-22
*!keywords* discriminants, record components, derived types
*!discussion*

*First of all: Merry Christmas to all of you and Happy New Year*

Which record components of One_Horse_Carriage do exist? Code see at end. This is my attempt of an RM exegesis.

RM 3.7(18) The discriminant Number_Passengers of the parent type Carriage is constrained to equal the new discriminant of the derived type One_Horse_Carriage; they correspond (and may share storage). The discriminant Number_Horses of the parent type Carriage is constrained to the value of the expression 1; it is specified.

(19) The component_definition Persons depends on the discriminant Number_Passengers.
The component_definition Horses does not depend on a discriminant.

(23) The component Passengers depends on the discriminant Number_Passengers.
The component Team does not depend on a discriminant.

(25) Each value of a discriminated type includes a value for each component of the type that does not depend on a discriminant.
Thus, Number_Passengers, Coachman and Team are included.
I'm tempted to say, also the component Number_Horses is included with the fixed value 1 - it does not depend on a discriminant. The values of discriminants determine which other component values (only Passengers here) are present in the value of the discriminated type. Where RMss.ss(pp)? According to 3.7.1(11), I would expect something like: Each component whose constraint is satisfied.

Obviously, I'm misreading the RM, since Number_Horses allegedly is not included, no value may be given in the aggregate. Barnes's book Ada 2012 also says this in a different example (Boxer) on chapter 18.4.

I'm really astonished that the new attribute 'Image for a record includes a value for a nonexistent component.

Image attribute
===============
RM 4.10(7/5) For an untagged derived type, ..., the default implementation of T'Put_Image invokes the Put_Image for its parent type on a conversion of the parameter of type T to the parent type.

Does this make sense if the parent type has more components than the derived type?

(17/5) For a specific, nonderived composite type:
(18/5) If the default implementation of Put_Image writes components, the order in which components are written is the same canonical order in which components of a composite type T are written out by the default implementation of T'Write. [This is also the order that is used in determining the meaning of a positional aggregate of type T.]

Stream attributes
=================
RM 13.13.2(8.2/2) The default implementations of the Write and Read attributes, where available, execute as follows: (9/5) ... For nonderived composite types, the Write or Read attribute for each component (excluding those, if any, that are not components of the nominal type of the object) is called in ... positional aggregate order for a record. ... If T is a discriminated type, discriminants are included only if they have defaults. ...

This is funny - discriminants are output although there are no defaults.

(9.1/5) ... For untagged derived types, the Write (resp. Read) attribute invokes the corresponding attribute of the parent type, if the attribute is available for the parent type.

This last sentence does not make sense for cases like Sulky where a certain discriminant is no longer part of the record.

I admit, a lot of ranting. And GNAT has perhaps some bugs (Ada 2022 is not yet fully implemented as you can see because it does not accept [] for array aggregates, but uses them for output).

This is my sample program:
---------------------------
with Ada.Text_IO;use  Ada.Text_IO;

procedure Coaches is

  subtype Number is Natural range 0 .. 8;

  subtype Person  is Positive;
  type    Persons is array (Number range <>) of Person;

  subtype Horse  is Positive;
  type    Horses is array (Number range <>) of Horse;

  type Carriage (Number_Passengers, Number_Horses: Number) is record
    Coachman  : Person;
    Passengers: Persons (1 .. Number_Passengers);
    Team      : Horses  (1 .. Number_Horses);
  end record;

  type One_Horse_Carriage (Number_Passengers: Number) is
    new Carriage (Number_Passengers => Number_Passengers, Number_Horses => 1);

  Sulky: One_Horse_Carriage (Number_Passengers => 0) :=
    (Number_Passengers => 0,
     Coachman          => 1850,
     Passengers        => (-5 .. -6 => 1),  -- []  Ada 2022
     Team              => (1 => 2023));

  Coach      : Carriage           := Carriage (Sulky);            -- Does type conversion work ...   Sulky_again: One_Horse_Carriage := One_Horse_Carriage (Coach);  -- ... in both directions?

begin

  Put_Line ("Sulky");
  Put_Line ("=====");
  Put_Line (Sulky.Number_Passengers'Image);
  --Put_Line (Sulky.Number_Horses'Image);  -- does allegedly not exist, yet ...
  Put_Line (Sulky.Coachman'Image);
  Put_Line (Sulky.Passengers'Image);
  Put_Line (Sulky.Team'Image);
  Put_Line (Sulky'Image);                  -- GNAT prints it out here
  New_Line;

  Put_Line ("Coach");
  Put_Line ("=====");
  Put_Line (Coach.Number_Passengers'Image & Coach.Number_Horses'Image);
  Put_Line (Coach.Coachman'Image);
  Put_Line (Coach.Passengers'Image);
  Put_Line (Coach.Team'Image);
  Put_Line (Coach'Image);
  New_Line;

  Put_Line ("Sulky = Sulky_again ?");
  Put_Line ("=====================");
  Put_Line (Boolean'Image (Sulky = Sulky_again));

end Coaches;
-------------------------------------------------
This is the result:
-------------------------------------------------
Sulky
=====
 0
 1850

[]

[ 2023]

(NUMBER_PASSENGERS =>  0,
 NUMBER_HORSES =>  1,
 COACHMAN =>  1850,
 PASSENGERS =>
 [],
 TEAM =>
 [ 2023])

Coach
=====
 0 1
 1850

[]

[ 2023]

(NUMBER_PASSENGERS =>  0,
 NUMBER_HORSES =>  1,
 COACHMAN =>  1850,
 PASSENGERS =>
 [],
 TEAM =>
 [ 2023])

Sulky = Sulky_again ?
=====================
TRUE

Reply via email to