Version 1.13 of ai12s/ai12-0030-1.txt

Unformatted version of ai12s/ai12-0030-1.txt version 1.13
Other versions for file ai12s/ai12-0030-1.txt

!standard 12.5.1(21/3)          13-07-19 AI12-0030-1/06
!standard 13.13.2(49/2)
!class binding interpretation 12-07-15
!status Corrigendum 1-2012 13-07-16
!status WG9 Approved 13-11-15
!status ARG Approved 8-0-0 13-06-15
!status work item 12-06-06
!status received 12-05-11
!priority Low
!difficulty Medium
!subject Formal derived types and stream attribute availability
!summary
For an untagged formal derived type, stream attributes (at runtime, in an instance) are those of the actual type. Those of the ancestor type do not reemerge.
!question
Consider this example:
procedure Foo
(S : not null access Ada.Streams.Root_Stream_Type'Class) is
type Lim is limited null record;
type Dlim is new Lim; -- does not inherit Lim'Read
procedure Read_Lim (Stream : not null access Ada.Streams.Root_Stream_Type'Class; Item : out Lim) is null;
for Lim'Read use Read_Lim; Lim_Var : Lim; Dlim_Var : Dlim;
generic type Flim is new Lim; procedure G; procedure G is Flim_Var : Flim; begin Flim'Read (S, Flim_Var); -- Legal? (Yes.) end G; procedure I is new G (Dlim);
begin Lim'Read (S, Lim_Var); Dlim'Read (S, Dlim_Var); -- Illegal. I; -- Legal, but raises Program_Error when called.
end;
Is the use of Flim'Read legal? (Yes.) If it is legal, are the dynamic semantics well defined? (Yes; when the procedure I invokes Dlim'Read, Program_Error is raised).
Assuming that stream attribute availability is a "characteristic", it seems that 12.5.1(20/3) applies and the use within the generic body is legal. This means we need to define the meaning of the corresponding construct in the instance.
The "even if it is never declared" wording in 12.5.1(21/3) handles a similar case, the case where a generic formal type promises a primitive operation that the corresponding actual type lacks. Do we need to add some analogous wording to handle this case? (No, but wording is added to ensure that the dynamic behavior of streaming attributes is defined in all cases including, for example, a task type with no user-defined streaming operations).
!recommendation
(See summary.)
!wording
Add after AARM 12.5.1(21.a/3):
AARM To Be Honest: The availability of stream attributes is not formally a characteristic of a type, but it is still determined by the ancestor type for a formal derived type in the same way as the characteristics are. Availability is rechecked in the instance specification.
Append to the end of 13.13.2(49/2):
In addition to the places where Legality Rules normally apply (see 12.3), these rules also apply in the private part of an instance of a generic unit.
[Editor's note: We have to use the plural version of this boilerplate text as there are two rules in this paragraph, and we want this to apply to both.]
Unless inherited from a parent type, if any, for an untagged type having a task, protected, or explicitly limited record part, the default implementation of each of the Read, Write, Input, and Output attributes raises Program_Error and performs no other action.
AARM note: It might seem that there is no need to specify the behavior of the default implementation of a streaming attribute of, for example, a task type because there is no way that it can be invoked. It is possible, however, to construct an example where such a stream attribute can be invoked. This involves using a formal untagged limited derived type for which some streaming attribute is available (because it was explicitly specified for the ancestor type) and a corresponding actual type for which the attribute is unspecified (because the derivation occurred before the aspect was specified for the ancestor type and the specification was therefore not inherited).
!discussion
The answer to the legality question is clear: characteristics of all sorts come from the ancestor type, not the actual type, so of course the attribute is legal and it will call the attribute of the ancestor type.
However, the wording of the Standard is definitely not clear on this point. "Availability of stream attributes" is not a characteristic of a type (at least it is not listed in 3.4, which is the definition of a "characteristic"). So the existing rules don't cover this case.
We could try to define "availability" as a "characteristic" (by adding a new bullet at 3.4(15), but that most likely would lead to conflicts as 7.3.1 defines how characteristics are inherited for private types, while 13.13.2 does so for "availability". These are similar, but probably not precisely the same, and we'd have to reword 13.13.2 in order to avoid confusion.
Alternatively, we could just throw some text and add a new rule after 12.5.1(20/3) specifically to say that "availability of stream attributes" is the same as that of the ancestor type, and the stream attribute called is that of the ancestor type. But that just adds a lot of text to say something obvious.
It's rare to see a derived untagged formal type, as such types can only match derived untagged types -- which is very limiting, especially given that untagged derivations themselves aren't that common (it's usually better to declare a new type). As such, it's not critical to have the Standard wording perfect in this area. So we simply add a To Be Honest note so there is no doubt of the intent.
However, that leaves the dynamic semantics. It's clear from 13.1(11/3) that the stream attributes (which are operational aspects) used in the instance are those of the actual type. But in this case, the actual type has no stream attributes defined. Thus we have to define what the attributes do in this case.
We've chosen to have them raise Program_Error. This means that the example given in the question compiles successfully and raises Program_Error at runtime. Raising Program_Error is fine - we just want the construct to be well defined and reasonably easy to implement. For an implemention which macro-expands instantiations, the Program_Error case is identifiable at compile-time.
The "In addition" boilerplate isn't really necessary, but consider modifying the example so that the generic is a generic package, not a generic procedure, and the problematic call to Flim'Read is replaced with a call to Flim'Input in the private part of the generic. Without the boilerplate (and with the rest of the change) this raises Program_Error. With the boilerplate, this gets caught at compile time, which is preferable.
!corrigendum 13.13.2(49/2)
Replace the paragraph:
An attribute_reference for one of the stream-oriented attributes is illegal unless the attribute is available at the place of the attribute_reference. Furthermore, an attribute_reference for T'Input is illegal if T is an abstract type.
by:
An attribute_reference for one of the stream-oriented attributes is illegal unless the attribute is available at the place of the attribute_reference. Furthermore, an attribute_reference for T'Input is illegal if T is an abstract type. In addition to the places where Legality Rules normally apply (see 12.3), these rules also apply in the private part of an instance of a generic unit.
Unless inherited from a parent type, if any, for an untagged type having a task, protected, or explicitly limited record part, the default implementation of each of the Read, Write, Input, and Output attributes raises Program_Error and performs no other action.
!ACATS test
An ACATS C-test could be written that is similar to the example in the question.
!appendix

From: Steve Baird
Sent: Friday, May 11, 2012  5:27 PM

Consider this example:

   procedure Foo
     (S : not null access Ada.Streams.Root_Stream_Type'Class) is
      type Lim is limited null record;

      type Dlim is new Lim; -- does not inherit Lim'Read

      procedure Read_Lim
        (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
         Item   : out Lim) is null;

      for Lim'Read use Read_Lim;
      Lim_Var : Lim;
      Dlim_Var : Dlim;

      generic
         type Flim is new Lim;
      procedure G;
      procedure G is
         Flim_Var : Flim;
      begin
         Flim'Read (S, Flim_Var); -- legal?
      end G;
      procedure I is new G (Dlim);
   begin
      Lim'Read (S, Lim_Var);
      Dlim'Read (S, Dlim_Var); -- illegal
      I;
   end;

Is the use of Flim'Read legal? Should it be?
If it is legal, are the dynamic semantics well defined?

Assuming that attribute availability is a "characteristic", it seems that
12.5.1(20/3) applies and the use within the generic body is legal. This means we
need to define the meaning of the corresponding construct in the instance.

The "even if it is never declared" wording in 12.5.1(21/3) handles a similar
case, the case where a generic formal type promises a primitive operation that
the corresponding actual type lacks. We may need to add some analogous wording
to handle this case (or at least a TBH note if this seems like too much of a
corner case).

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

From: Tucker Taft
Sent: Sunday, May 13, 2012  10:08 PM

> generic
> type Flim is new Lim;
> procedure G;
> procedure G is
> Flim_Var : Flim;
> begin
> Flim'Read (S, Flim_Var); -- legal?

Yes.

> end G;
> procedure I is new G (Dlim);
> begin
> Lim'Read (S, Lim_Var);
> Dlim'Read (S, Dlim_Var); -- illegal

True.

> I;
> end;
>
> Is the use of Flim'Read legal? Should it be?

Yes, just like all operations for non-tagged types.

> If it is legal, are the dynamic semantics well defined?

Yes, since the semantics are always to use the operation of the specified
ancestor type if non-tagged.  We have now made "=" a special case, but all other
operations come from the ancestor type.

> Assuming that attribute availability is a "characteristic", it seems
> that 12.5.1(20/3) applies and the use within the generic body is
> legal. This means we need to define the meaning of the corresponding
> construct in the instance.
>
> The "even if it is never declared" wording in 12.5.1(21/3) handles a
> similar case, the case where a generic formal type promises a
> primitive operation that the corresponding actual type lacks. We may
> need to add some analogous wording to handle this case (or at least a
> TBH note if this seems like too much of a corner case).

I don't really see this as a very special case.
For non-tagged types, unless you explicitly import the operation (or it is
equality), you always use the operations of the specified ancestor, even if they
are overridden in the actual type.

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

From: Steve Baird
Sent: Monday, May 14, 2012  12:31 PM

> I don't really see this as a very special case.
> For non-tagged types, unless you explicitly import the operation (or
> it is equality), you always use the operations of the specified
> ancestor, even if they are overridden in the actual type.

If I understand you correctly, you are talking about the following 12.5.1(21/3)
rule:

   In an instance, the copy of such an implicit declaration of a
   primitive subprogram of a formal derived type declares a view of the
   corresponding primitive subprogram of the ancestor or progenitor of
   the formal derived type, even if this primitive has been overridden
   for the actual type and even if it is never declared for the actual
   type.

and (in passing) the 3.4(17/2) rule that begins
   For each user-defined primitive subprogram (other than a user-defined
   equality operator - see below) of the parent type ...

Note that these rules talk about primitive subprograms, not primitive
operations. I think that means that they don't apply to the question I raised.

Note that it wouldn't change the example in any essential way if we introduced a
nested package so that the procedure Read_Lim is not a primitive operation of
type Lim, as in

      type Lim is limited null record;

      type Dlim is new Lim; -- does not inherit Lim'Read

      package Prevent_Primitiveness is
        procedure Read_Lim
          (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
           Item   : out Lim) is null;
      end Prevent_Primitiveness;

      for Lim'Read use Prevent_Primitiveness.Read_Lim;

This is not a question about primitive subprograms.

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

From: Tucker Taft
Sent: Monday, May 14, 2012  12:56 PM

> ...
> Note that these rules talk about primitive subprograms, not primitive
> operations. I think that means that they don't apply to the question I
> raised.

You are right in the letter of the law, but I think the intent was to include
all overrideable operations. Note that the streaming operations of tagged types
are considered dispatching operations, so that would mean that we are already
treating them pretty similarly to primitives.

In general, we have not presumed any significant "descriptor" needed to
accompany formal derived non-tagged types (presuming you have some amount of
sharing), since essentially all of the properties inside the generic are
determined by the specified ancestor type.  I agree we should clarify this, but
I have no doubt which way it should be clarified. Your example is a good
indication of why for non-tagged types we should carry this same principle
through to the streaming operations, and use the operations of the specified
ancestor type inside the generic.

> ... This is not a question about primitive subprograms.

Agreed, but it is a question about something which is the "moral equivalent" of
a primitive, and I believe we should clarify the RM that they should follow the
same rules.

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

From: Steve Baird
Sent: Monday, May 14, 2012  1:24 PM

Sounds good to me.

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

From: Steve Baird
Sent: Friday, June 29, 2012  6:51 PM

Randy Brukardt wrote (in private communication):
> The model Tucker gave for untagged derived types is crystal-clear: all
> non-representation properties come from the ancestor, period, and
> nothing comes from the actual. That includes the routines that are
> called at runtime.

Randy sent me a proposed writeup for this AI which captured the above idea.

Recall that the example that led to this AI involves a formal untagged derived
type whose ancestor type has streaming attributes available and a corresponding
actual type which does not have streaming attributes available. In the generic
body, we perform streaming operations. At runtime, what is supposed to happen?

The model Randy wrote up answers this question by saying that we use the
streaming attributes of the ancestor type unconditionally, and the streaming
attributes of the actual are ignored.

Tuck described this model in a mail message of May 14, to which my reply was
"Sounds good to me".

Now I'm not so sure. Consider the following:

       type T1 is record ... ; -- untagged

       type T2 is new T;
       for T2'Read use ...;
       for T2'Write use ...;

       generic
          type Formal_Private is private;
       package G1 is
          ...;
       end;
       package body G1 is
       begin
           -- do some Formal_Private streaming stuff
       end;

       generic
          type Formal_Derived is new T1;
       package G2 is
          ...;
       end;

       package body G2 is
       begin
           -- do some Formal_Derived streaming stuff
       end;

       package I1 is new G1 (T2);
       package I2 is new G2 (T2);

Do the two instances execute the same streaming ops?
It would seem odd to me if they didn't.
What streaming ops do we get if G2 instantiates G1 with
    Formal_Private => Formal_Derived
?

I don't remember the details of the discussion in Stockholm, but neither Randy
nor I remember any discussion of this aspect of the question.

Other alternatives for resolving the original question that led to this AI
include:

   1) A legality rule - if streaming attributes are available for a
      formal derived type, then they must be available for the actual.

   2) A conditional rule - use the streaming attributes of the actual,
      unless they don't exist in which case use the streaming
      attributes of the ancestor type (or perhaps raise P_E).

I'm leaning towards a "use the streaming attributes of the actual" model
combined with rule #1, which guarantees that those streaming attributes exist.

The best argument for the model Tuck proposed, I think, is that this is a
completely unimportant corner case and therefore any choice is ok as long as it
is well-defined and we should go with whatever is simplest. I could continue to
support Tuck's model on these grounds.

Opinions?

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

From: Tucker Taft
Sent: Saturday, June 30, 2012  6:19 AM

> package I1 is new G1 (T2);
> package I2 is new G2 (T2);
>
> Do the two instances execute the same streaming ops?

G1 uses the predefined streaming attributes.
Predefined operations reemerge in generics for untagged (non-derived) formal
types (except for record equality as of Ada 2012).

G2 uses T1's streaming attributes.  The ancestor type's operations reemerge in
generics for untagged formal derived types (again, except for record equality,
as of Ada 2012).

So long as T1's streaming attributes are predefined, then G1 and G2 use the same
attributes.

> It would seem odd to me if they didn't.

I agree, presuming T1's streaming attributes are predefined.

> What streaming ops do we get if G2 instantiates G1 with Formal_Private
> => Formal_Derived

The predefined attributes, so if T1 has user-defined streaming attributes, these
are not used inside the nested instantiation of G1.

> ?
>
> I don't remember the details of the discussion in Stockholm, but
> neither Randy nor I remember any discussion of this aspect of the
> question.

Formal private is essentially equivalent to formal derived where the ancestor is
a type with only predefined operations (again, modulo user-defined equality on
records).

I also think you need to consider this same question for all untagged formal
generic types (formal integer, formal array, etc.). I believe in all these cases
the predefined streaming attributes should reemerge, just like the predefined
"+", etc.

>
> Other alternatives for resolving the original question that led to
> this AI include:
>
> 1) A legality rule - if streaming attributes are available for a
> formal derived type, then they must be available for the actual.
>
> 2) A conditional rule - use the streaming attributes of the actual,
> unless they don't exist in which case use the streaming attributes of
> the ancestor type (or perhaps raise P_E).
>
> I'm leaning towards a "use the streaming attributes of the actual"
> model combined with rule #1, which guarantees that those streaming
> attributes exist.

It seems odd to me that predefined "+" reemerges on a formal integer type but
predefined streaming does *not* reemerge.

> The best argument for the model Tuck proposed, I think, is that this
> is a completely unimportant corner case and therefore any choice is ok
> as long as it is well-defined and we should go with whatever is
> simplest. I could continue to support Tuck's model on these grounds.

I don't believe it is a "corner case" argument.  I believe it is for consistency
with all of the other predefined operations, which *do* reemerge in a generic
unless the formal type is tagged.

> Opinions?

See above.

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

From: Tucker Taft
Sent: Saturday, June 30, 2012  7:08 AM

I can also construct a counter-argument (here comes TT-2):

Because streaming attributes "compose" even for untagged types, that is, the
"predefined" streaming attribute for a record or array type is produced by
composing the user-defined streaming attributes of its component types, the
user-defined streaming attributes should be carried through into generics.

This seems pretty clear if the generic spec declares a record type whose
components are formal types.  One would certainly expect that outside the
generic, this newly constructed record type's streaming attribute would be
composed out of the user-defined streaming attributes of its component types. It
would be pretty weird if this record type had different behavior than an
otherwise identical record type declared outside of the generic instance.

Given this automatic composability, it seems that we need to carry streaming
attributes into generics.

So now we are back to needing rules to deal with formal limited derived types.
Groan...

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

From: Steve Baird
Sent: Monday, July  2, 2012  12:51 PM

> I don't believe it is a "corner case" argument.  I believe it is for
> consistency with all of the other predefined operations, which
> *do* reemerge in a generic unless the formal type is tagged.

I agree with you here (and with TT-2 later).

> I can also construct a counter-argument (here comes TT-2):

As you point out, if  we are going to be consistent then this affects more than
just the formal untagged derived case.

I think this leads to a fairly strong
compatibility-with-existing-practice argument for TT-2's position.

For at least the GNAT implementation (and, I believe, the IBM/Rational
compiler), predefined streaming attributes do not currently reemerge. My guess
is that this is the case for most any implementation which does not use generic
code sharing, since this is what naturally falls out of the macro-expansion
model.

The xample below, compiled with the Gnat compiler, prints
   User-defined stream attrs were invoked

IMO, this is a good thing and we want a language definition that allows/requires
this behavior (which the proposed model does not).

Incidentally, package Simple_Streams is just a simpler alternative to
Ada.Streams.Stream_IO that I tend to use when investigating streaming issues.
Nothing special about it.

with Text_IO;
with Ada.Streams;
with Simple_Streams;
procedure formal_type_streaming is
    type Vector is array (Positive range <>) of Integer;

    procedure Output
     (S : not null access Ada.Streams.Root_Stream_Type'Class;
      V : Vector);
    function Input
      (S : not null access Ada.Streams.Root_Stream_Type'Class)
      return Vector;

    for Vector'Output use Output;
    for Vector'Input use Input;

    procedure Output
      (S : not null access Ada.Streams.Root_Stream_Type'Class;
       V : Vector) is
    begin
       -- only write out length, not both bounds
       Integer'Output (S, V'Length);
       for I in V'Range loop
          Integer'Output (S, V(I));
       end loop;
    end;

    function Input
       (S : not null access Ada.Streams.Root_Stream_Type'Class)
       return Vector is
        -- low bound of result is always 1
        Result : Vector (1 .. Integer'Input (S));
    begin
       Vector'Read (S, Result);
       return Result;
    end;

    generic
       type Formal_Vector is array (Positive range <>) of Integer;
    function Ident (V : Formal_Vector) return Formal_Vector;
    function Ident (V : Formal_Vector) return Formal_Vector is
       S: aliased Simple_Streams.Stream_Type;
    begin
       Formal_Vector'Output (S'access, V);
       return  Formal_Vector'Input (S'access);
    end;

    function Ident_Inst is new Ident (Formal_Vector => Vector);

    X : constant Vector (2 .. 4) := (2, 3, 4);
    Y : constant Vector := Ident_Inst (X); begin
    pragma Assert (X = Y);
    if Y'First = 1 then
       Text_IO.Put_Line ("User-defined stream attrs were invoked");
    else
       Text_IO.Put_Line ("Predefined stream attrs were invoked");
    end if;
end;


with Ada.Streams;
use Ada.Streams;
package Simple_Streams is
    type Stream_Type is new Root_Stream_Type with private; private
    subtype Buffer_Index is
      Stream_Element_Offset range 1 .. 1024;
    subtype Buffer_Length is
      Stream_Element_Count range 0 .. Buffer_Index'Last;

    type Stream_Type is new Root_Stream_Type with
       record
         Buffer : Stream_Element_Array (Buffer_Index);
         Length : Buffer_Length := 0;
       end record;

    overriding
    procedure Read(
      Stream : in out Stream_Type;
      Item   : out Stream_Element_Array;
      Last   : out Stream_Element_Offset);

    overriding
    procedure Write(
      Stream : in out Stream_Type;
      Item   : in Stream_Element_Array);

end Simple_Streams;

package body Simple_Streams is

    procedure Read(
      Stream : in out Stream_Type;
      Item   : out Stream_Element_Array;
      Last   : out Stream_Element_Offset) is
    begin
      if Item'Length <= Stream.Length then
          Item := Stream.Buffer (1 .. Item'Length);
          Last := Item'Last;
          Stream.Length := Stream.Length - Item'Length;
          Stream.Buffer (1 .. Stream.Length)
            := Stream.Buffer
                 (1 + Item'Length .. Stream.Length + Item'Length);
      else
          Item (Item'First .. Item'First + Stream.Length - 1) :=
            Stream.Buffer (1 .. Stream.Length);
          Last := Item'First + Stream.Length - 1;
          Stream.Length := 0;
      end if;
    end Read;

    procedure Write(
      Stream : in out Stream_Type;
      Item   : in Stream_Element_Array) is
    begin
      Stream.Length := Stream.Length + Item'Length;
      Stream.Buffer
        (Stream.Length + 1 - Item'Length .. Stream.Length) := Item;
    end Write;

end Simple_Streams;

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

From: Randy Brukardt
Sent: Monday, July  2, 2012  3:19 PM

...
> I think this leads to a fairly strong
> compatibility-with-existing-practice argument for TT-2's position.
>
> For at least the GNAT implementation (and, I believe, the IBM/Rational
> compiler), predefined streaming attributes do not currently reemerge.
> My guess is that this is the case for most any implementation which
> does not use generic code sharing, since this is what naturally falls
> out of the macro-expansion model.

I haven't encountered any compilers for which stream attributes did not
dynamically call the user-defined attributes. I certainly thought that was the
model for Ada.

It's also a usability issue. You wouldn't be able to usefully stream anything
from inside a generic unit if the predefined attributes reemerged. (You'd get
useless results if any access types are involved.)

More generally, reemergence is almost always a problem (given that it means that
a generic unit works differently than the same unit created by hand using
textual substitution). We ought to use it only where it is necessary for
compatibility, and nowhere else.

In any case, the fact that Tucker is even willing to make an argument for
reemergence suggests that the entire model of stream attributes in generics is
ill-defined. So just using a Ramification to solve this is a bad idea - we need
to correct the wording so it is crystal-clear as to what happens.

...
> Incidentally, package Simple_Streams is just a simpler alternative to
> Ada.Streams.Stream_IO that I tend to use when investigating streaming
> issues.
> Nothing special about it.

Actually, this looks very similar to Claw.Marshalling (which we use to allow
items to be streamed into and out of the Windows clipboard and registry). It's
something that is often needed and probably ought to be language-defined. (The
topic has informally come up before, I believe, but never formally.)

[The only significant difference here (other than naming) is that
Claw.Marshalling uses a discriminant to specify the buffer size, since this is
definitely a case where one size does not fit all. Well, and Claw.Marshalling
also has Length and Clear operations. The implementation also uses buffer chunks
to allow the buffer to grow beyond the initial size, but that's probably
overkill.]

...
> with Ada.Streams;
> use Ada.Streams;
> package Simple_Streams is
>     type Stream_Type is new Root_Stream_Type with private; private
>     subtype Buffer_Index is
>       Stream_Element_Offset range 1 .. 1024;
>     subtype Buffer_Length is
>       Stream_Element_Count range 0 .. Buffer_Index'Last;
>
>     type Stream_Type is new Root_Stream_Type with
>        record
>          Buffer : Stream_Element_Array (Buffer_Index);
>          Length : Buffer_Length := 0;
>        end record;
>
>     overriding
>     procedure Read(
>       Stream : in out Stream_Type;
>       Item   : out Stream_Element_Array;
>       Last   : out Stream_Element_Offset);
>
>     overriding
>     procedure Write(
>       Stream : in out Stream_Type;
>       Item   : in Stream_Element_Array);
>
> end Simple_Streams;

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

From: Jeff Cousins
Sent: Wednesday, July  4, 2012  6:24 AM

Wearing my user's hat, re-emergence is abhorrent and breaks the language design
principle of "no surprises".

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

From: Randy Brukardt
Sent: Tuesday, July 17, 2012  6:17 PM

FYI:

> I can also construct a counter-argument (here comes TT-2):
>
> Because streaming attributes "compose" even for untagged types, that
> is, the "predefined" streaming attribute for a record or array type is
> produced by composing the user-defined streaming attributes of its
> component types, the user-defined streaming attributes should be
> carried through into generics.
>
> This seems pretty clear if the generic spec declares a record type
> whose components are formal types.  One would certainly expect that
> outside the generic, this newly constructed record type's streaming
> attribute would be composed out of the user-defined streaming
> attributes of its component types.
> It would be pretty weird if this record type had different behavior
> than an otherwise identical record type declared outside of the
> generic instance.
>
> Given this automatic composability, it seems that we need to carry
> streaming attributes into generics.

Steve privately pointed out that we all had missed the first sentence of
13.1(11/3):

Operational and representation aspects of a generic formal parameter are the
same as those of the actual.

Stream attributes are operational aspects, thus the actual's values are always
used. QED.

This matches with existing practice, as well.

Steve has already written this AI up, so we don't need to discuss it further, I
just wanted to put on the record that TT-1 was not supported by the Standard.

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

Summary of private mail thread, July 19th, 2013

Randy Brukardt asks:

The wording we added in AI12-0030-1 (will be 13.13.2(49.1/4) starts:

"For an untagged nonderived type having a task, protected, or explicitly
limited record part ..."

The example of the question of the AI starts:

      type Lim is limited null record; 

      type Dlim is new Lim; -- does not inherit Lim'Read

      procedure Read_Lim
        (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
         Item   : out Lim) is null;

      for Lim'Read use Read_Lim;

Note that type Dlim, for which we need this rule to define the stream
attributes runtime behavior, is a derived type. Thus the new rule does not
apply to Dlim.

OK, perhaps inheritance will fix us. Let's look at 13.13.2(8.1/3):

"For an untagged derived type, the Write (resp. Read) attribute is inherited
according to the rules given in 13.1 if the attribute is specified and
available for the parent type at the point where T is declared." (T here
refers to the type of the attribute.)

So this doesn't apply, either, as the attribute is not specified at the point
where T is declared.

So precisely how does this wording make the example in the question raise
Program_Error? I'd say that it does not.

Steve Baird replies:

> ... Thus the new rule does not apply to Dlim.

Ok, no problem so far. This is as I would expect.

...
> So this doesn't apply, either, as the attribute is not specified at 
> the point where T is declared.

Oh ugh. I thought the "raise P_E" streaming attributes would be inherited.
That's how this case is supposed to work.

[Followed a by a long diversion into a solution that doesn't work as proposed.]

Randy:

We could patch up the original rule by modifying the new 13.13.2(49.1/4) rule
as follows:

"{Unless inherited from a parent type, if any, for}[For] an untagged
[nonderived] type having a task, protected, or explicitly limited record part,
the default implementation of each of the Read, Write, Input, and Output
attributes raises Program_Error and performs no other action."

I would have preferred that you have simply followed the existing pattern and
somehow worked your new rule in as what the default implementation is defined
to do (that is, as new bullets following 13.13.2(9.1/3) and 13.13.2(27/3)),
but that would be more than an Editorial Review fix.

Tucker Taft:

Seems fine to me.

Steve:

Ditto.

Jeff Cousins:

I'm not sure about this one, but changing something in 13.3.2 seems preferable
to changing 13.1.

[So this was done in version /06 of the AI - Editor.]

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

Questions? Ask the ACAA Technical Agent