Version 1.1 of ai12s/ai12-0435-1.txt

Unformatted version of ai12s/ai12-0435-1.txt version 1.1
Other versions for file ai12s/ai12-0435-1.txt

!standard 4.10(6/5)          21-05-29 AI12-0435-1/01
!standard 4.10(15/5)
!standard 4.10(26/5)
!standard 13.13.2(9.1/5)
!class Amendment 21-05-29
!status work item 21-05-29
!status received 21-05-29
!priority Low
!difficulty Easy
!subject Fixups from WG 9 Issue #143 - Image and streams
!summary
(1) Exclude null extensions from the Implementation Advice about type extensions.
(2) Define the subtype of the parameters to the default Put_Image implementations similarly to that for the stream-oriented attributes.
(3) The subprogram specified for Put_Image cannot be abstract.
(4) Stream-oriented attributes are implicitly composed.
!problem
(1) It seems desirable that if we have a tagged specific noninterface type T1 and a specific type T2 which is a proper descendant of T1 (i.e., a descendant of T1 other than T1 itself), and if T2 is "the same" as T1 ("the same" meaning that no new components are defined nor Put_Image redefined for T2), then for any value X of T2 it should be the case that T2'Image (X) = T1'Image (T1 (X)).
This is not true when T2 is a null extension of T1. If T1'Image (X) = "(Some Image Text)", then (according to the implementation advice) T2'Image (T2 (X)) should equal something like "((Some Image Text) with NULL)".
(2) Are rules for determining the parameter subtypes for T'Put_Image procedures sufficiently well-defined?
For example, is the "others" aggregate in the following code legal?
type T is array (1 .. 10) of Integer; -- first subtype is constrained
procedure Foo (Buff : in out Ada.Strings.Text_Buffers.Root_Buffer_Type'Class) is begin
T'Put_Image (Buff, (others => 123)); -- legal?
end Foo;
(3) For streams, we have a rule The subprogram name given in such an attribute_definition_clause or aspect_specification shall statically denote a subprogram that is not an abstract subprogram.
We don't have an similar rule for Put_Image. Since we want the image of any type (including abstract types) to be well-defined, this seems to be an oversight.
(4) we have rules in 13.1.1 and 12.5.1 that apply to aspects that are "implicitly composed", but we have no explicit statements anywhere in the manual that any particular aspects are "implicitly composed". This should be stated explicitly for Put_Image and for the aspects corresponding to the stream-oriented attributes.
!proposal
(See Summary.)
!wording
(1) Modify 4.10(6/5):
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.
Modify 4.10(15/5):
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.
(2) and (3) Add after 4.10(26/5):
In the parameter_and_result_profile for the default implementation of Put_Image, the subtype of the Arg parameter is the base subtype of T if T is a scalar type, and the first subtype otherwise. For an aspect_specification or attribute_definition_clause specifying Put_Image, the subprogram name shall denote a nonabstract procedure whose second parameter is either of the first subtype of T, or as an option when T is scalar, the base subtype of T.
(4) Modify 13.13.2(9.1/5):
{For derived types, the default implementations of the Write and Read attributes are not inherited, but are rather implicitly composed.} For type extensions, the Write or Read attribute for the parent type is called, followed by the Write or Read attribute of each component of the extension part, in canonical order. For a limited type extension, if the attribute of the parent type or any progenitor type of T is available anywhere within the immediate scope of T, and the attribute of the parent type or the type of any of the extension components is not available at the freezing point of T, then the attribute of T shall be directly specified. 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.
[Editor's complaint: This only covers Read and Write. What about Input and Output, and the class-wide versions of all of these?? I would have expected a general statement at the top of 13.13.2 instead of this.]
!discussion
(1) Its unclear to the Editor whether this is really a principle that we want to support. OTOH, he has consistently objected to treating null extensions specially and has lost that argument consistently.
(2) We copy the definition of stream-oriented attributes for Put_Image.
(3) We never want Put_Image to be uncallable, so abstract routines should be banned. There's no problem with null procedures (even though they don't seem very useful in this context), so we allow them.
(4) 4.10(4/5) states that Put_Image is implicitly composed. We don't have such wording for stream-oriented attributes, though.
!ASIS
No ASIS effect.
!ACATS test
ACATS B-Tests should be constructed to test the subtypes of the parameters of Put_Image and to check that abstract subprograms cannot be specified.
ACATS C-Tests should be constructed to check that null extensions do not affect the image.
!appendix

From WG 9 review issue #143 - Steve Baird

It seems desirable that if we have a tagged specific noninterface type
T1 and a specific type T2 which is a proper descendant of T1 (i.e., a
descendant of T1 other than T1 itself), and if T2 is "the same" as T1
("the same" in a sense I'll describe in a moment), then for any value
X of T2 it should be the case that T2'Image (X) = T1'Image (T1 (X)).

When I say that T1 and T2 are "the same", I mean that neither T2 nor any
other proper descendant of T1 that is also an ancestor of T2 either

    defines any new components (including discriminants); or
    has an explicitly-specified Put_Image aspect.

This seems to generally be true, but (unfortunately) not always.
Consider the case where a type T1 has a user-defined Put_Image aspect
and a type T2 is a null extension of T1, declared as
type T2 is new T1 with null record;

The implementation advice about how to handle this case does not have
a special case for null extension (or a null extension of a null extension,
etc.). So if T1'Image (X) = "(Some Image Text)", then (according to that
advice) T2'Image (T2 (X)) should equal something like
"((Some Image Text) with NULL)".
That's not what we want.

===

Are rules for determining the parameter subtypes for T'Put_Image
procedures sufficiently well-defined? [Tuck raised this question.]

For example, is the "others" aggregate in the following code legal?

type T is array (1 .. 10) of Integer; -- first subtype is constrained

procedure Foo
(Buff : in out Ada.Strings.Text_Buffers.Root_Buffer_Type'Class) is
begin
T'Put_Image (Buff, (others => 123)); -- legal?
end Foo;

===

For streams, we have a rule
The subprogram name given in such an attribute_definition_clause or
aspect_specification shall statically denote a subprogram that is not
an abstract subprogram.

The word "abstract" does not appear anywhere in 4.10; that seems like a
problem. We want to prohibit something like

type T is tagged record ... end record with Put_Image => Foo;
procedure Foo () is abstract;

Assuming such a rule, would a (redundant) note be useful pointing out
that Some_Abstract_Type'Put_Image is never an abstract procedure?

===

On a related note, we have rules in 8.6 that apply to aspects that are
"implicitly composed", but we have no explicit statements anywhere in
the manual that any particular aspects are "implicitly composed".
This should be stated explicitly for Put_Image and for the
aspects corresponding to the stream-oriented attributes.

[Tucker Taft replied:]

As far as null extensions, I would agree with Steve we should suppress them.
So I would suggest:

Modify 4.10(6/5):

   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.

Modify 4.10(15/5):

   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.

---

As far as the subtype of the parameters, for stream attributes we have:
13.13.2(50/3-51/3):

   In the parameter_and_result_profiles for the default implementations of the 
   stream-oriented attributes, the subtype of the Item parameter is the base 
   subtype of T if T is a scalar type, and the first subtype otherwise. The
   same rule applies to the result of the Input attribute.

   For an attribute_definition_clause specifying one of these attributes, the 
   subtype of the Item parameter shall be the first subtype or the base
   subtype if scalar, and the first subtype if not scalar. The same rule
   applies to the result of the Input function.

We presumably need something analogous for Put_Image. As far as the use of 
"abstract" procedures for Put_Image, I agree we want to disallow them. All
types have Put_Image, including all abstract types, and we want to be able to
presume it is a non-abstract procedure in generics, etc. And the implicitly
composed Put_Image for a type extension of an abstract type will invoke the
parent's operation, so again, we want to be sure it is nonabstract. Hence,
I recommend we add a paragraph to specify both the parameter subtype, and the
requirement to be nonabstract:

Add after 4.10(26/5):

  In the parameter_and_result_profile for the default implementation of 
  Put_Image, the subtype of the Arg parameter is the base subtype of T if T
  is a scalar type, and the first subtype otherwise. For an 
  aspect_specification or attribute_definition_clause specifying Put_Image,
  the subprogram name shall denote a nonabstract procedure whose second
  parameter is either of the first subtype of T, or as an option when T
  is scalar, the base subtype of T.

---

As far as "implicitly composed" we do say something in 4.10(4/5) about 
Put_Image, but there is nothing about how the stream attributes are
implicitly composed. So I would suggest:

Modify 13.13.2(9.1/5):

  {For derived types, the default implementations of the Write and Read 
  attributes are not inherited, but are rather implicitly composed.} For
  type extensions, the Write or Read attribute for the parent type is
  called, followed by the Write or Read attribute of each component of the
  extension part, in canonical order. For a limited type extension, if the
  attribute of the parent type or any progenitor type of T is available
  anywhere within the immediate scope of T, and the attribute of the parent
  type or the type of any of the extension components is not available at
  the freezing point of T, then the attribute of T shall be directly
  specified. 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.

****************************************************************


Questions? Ask the ACAA Technical Agent