*!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