Ada-Rapporteur-Group / User-Community-Input

Ada User Community Input Working Group - Github Mirror Prototype
26 stars 1 forks source link

Put_Image when discriminants are replaced in untagged derived types #85

Open sttaft opened 5 months ago

sttaft commented 5 months ago

This issue was identified by Christoph Grein on 2023-12-22:

Discriminants can be effectively replaced or renamed in a derived type. See for example RM 3.7(34-35):

type Matrix_Rec(Rows, Columns : Integer) is
   record
      Mat : Matrix(1 .. Rows, 1 .. Columns);    -- see 3.6
   end record;

type Square(Side : Integer) is new
   Matrix_Rec(Rows => Side, Columns => Side);

The current rules for Put_Image do not seem to properly handle this case for untagged derived types.

For tagged type extensions RM 4.10(16/5) specifies:

For a nonnull type extension, the default implementation of T'Put_Image depends on whether there exists a noninterface ancestor of T (other than T itself) for which the Put_Image aspect has been directly specified. If so, then T'Put_Image will generate an image based on extension aggregate syntax where the ancestor type of the extension aggregate is the nearest ancestor type whose Put_Image aspect has been specified. If no such ancestor exists, then the default implementation of T'Put_Image is the same as described below for a nonderived record type.

But for an untagged derived type RM 4.10(7/5) specifies:

For an untagged derived type, or a null extension, 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.

This would produce an image for the Square type above displaying Rows and Columns rather than Size for the discriminants. To avoid this, there should be a rule similar to that for tagged type extensions, and in fact is probably most simply handled by simplifying paragraph (7/5) to only apply to elementary types, and updating paragraph (16/5) as follows:

For a [nonnull type extension]{type derived from a composite type}, the default implementation of T'Put_Image depends on whether there exists a noninterface ancestor of T (other than T itself) for which the Put_Image aspect has been directly specified. If so, then: {

  • if T is a nonnull type extension,} T'Put_Image will generate an image based on extension aggregate syntax where the ancestor type of the extension aggregate is the nearest ancestor type whose Put_Image aspect has been specified{;
  • if T is an untagged derived type or a null extension, T'Put_Image invokes the Put_Image for its parent type on a conversion of the parameter of type T to the parent type.

} If no such ancestor exists, then the default implementation of T'Put_Image is the same as described below for a nonderived record type.

Paragraph (21/5) would also need some minor rewording to cover the new cases when the default image is used for derived composite types.

CKWG commented 5 months ago

This looks like a perfect solution. It disentangles the different categories of types. The entwinement was the prime reason for my confused message on Ada Comment.

CKWG commented 5 months ago

However, I cannot follow the path of thought of (18/5). Why the reference to T'Write? 13.13.2(9/5) says: "If T is a discriminated type, discriminants are only included if they have defaults." There are none here. An image without discriminants seems utter nonsense to me - which is what GNAT creates for Image with tagged derived types under certain conditions! Is this really the intention? See below:

with Ada.Text_IO;
use  Ada.Text_IO;

with Interfaces;
use  Interfaces;

procedure Sequence is

  type T0 (D0: Integer_8) is tagged record
    K0: Integer_8;
  end record;

  type T1 is new T0 (D0 => -1) with record
    K1: Integer_8;
  end record;

  procedure P0 is
    --        D0  K0
    X: T0 := ( 1, -1);
   begin
    Put_Line ("P0");
    Put_Line ("==");
    Put_Line (X'Image);
    New_Line;
  end P0;

  procedure P1 is
    --        D0  K0 K1
    X: T1 := (-1, -2, 8);  -- D0 must be -1
  begin
    Put_Line ("P1");
    Put_Line ("==");
    Put_Line (X'Image);
    New_Line;
  end P1;

begin

  P0;
  P1;

end Sequence;
-----------------------
P0
==

(D0 =>  1,
 K0 => -1)

P1
==

(K0 => -2,
 K1 =>  8)
sttaft commented 5 months ago

The reference to T'Write is admittedly confusing here. The intent was to avoid having to repeat the special case for Fortran-convention multi-dimensional arrays given in 13.13.2(9/5), and perhaps other parts of the definition of "canonical order," but because of the special case about discriminants, mentioning T'Write just further confuses things. Also, for multi-dimensional arrays, 'Image is expected to produce multiple levels of brackets, which again makes the mention of T'Write confusing. It sounds like we should simply repeat the relevant rules here, rather than trying to piggy-back on the T'Write description. Or perhaps better, separate out the definition of "canonical order" somewhere, and refer to that.

CKWG commented 5 months ago

I'm confused. Canonical order should have been defined where positional aggregates are defined, somewhere in RM 4.3.

sttaft commented 5 months ago

Currently canonical order for array components is defined in RM 5.5.2(11/5), which is a bit of a weird place for it. In any case, I agree with you that we should move the definition of canonical order of components to some reasonable place, and then refer to that.

CKWG commented 5 months ago

For records, isn't this it? 4.3.1(11) For a positional association, the component (including possibly a discriminant) in the corresponding relative position (in the declarative region of the type), counting only the needed components;

sttaft commented 5 months ago

For records, isn't this it? 4.3.1(11) For a positional association, the component (including possibly a discriminant) in the corresponding relative position (in the declarative region of the type), counting only the needed components;

Yes, that is the correct order. So we merely need to gather these various bits into a single place and use them to define the phrase "canonical order of components".

sttaft commented 4 months ago

I will create an AI for this issue so we can review it at the next ARG meeting.

CKWG commented 4 months ago

There are more clients for canonical orders:

I think it does not make sense to collect all those definitions at one place.