Version 1.25 of ais/ai-00195.txt

Unformatted version of ais/ai-00195.txt version 1.25
Other versions for file ais/ai-00195.txt

!standard 8.3 (23)          02-05-09 AI95-00195/11
!standard 13.13.1 (09)
!standard 13.13.1 (27)
!standard 13.13.1 (35)
!standard 13.13.1 (36)
!standard 13.13.1 (36.1)
!class binding interpretation 98-03-27
!status work item 02-05-31
!status ARG Approved 6-0-2 01-10-07
!status work item 98-04-04
!status received 98-03-27
!reference AI95-00108
!reference AI95-00145
!priority High
!difficulty Hard
!subject Streams
!summary
1 - When the predefined Input attribute creates an object, this object undergoes default initialization and finalization.
2 - For the purposes of checking legality rules, it is necessary to determine whether a stream-oriented attribute has been specified for a limited type (13.13.2(9/1) and 13.13.2(36/1)). This is done by applying the normal visibility rules to the attribute_definition_clause.
3 - For a limited tagged type T, if Read is available then T'Input is available, even if it is not specified. Similarly, if Write is available then T'Output is available. T'Class'Read, T'Class'Write, T'Class'Input and T'Class'Output are available only if they are specified or the corresponding specific attribute is available everywhere within the immediate scope of T.
4 - In the profiles of the stream-oriented attributes, the notation "italicized T" refers to the base subtype for a scalar type, and to the first subtype otherwise.
5 - In an attribute_definition_clause for a stream-oriented attribute, the name shall not denote an abstract subprogram.
6 - The predefined Read attribute for composite types with defaulted discriminants must ensure that, if exceptions are raised by the Read attribute for some discriminant, the discriminants of the actual object passed to Read are not modified. This may require the creation of an anonymous object, which undergoes initialization and finalization.
7 - The predefined Read attribute for composite types with defaulted discriminants must raise Constraint_Error if the discriminants found in the stream differ from those of the actual parameter to Read, and this parameter is constrained.
8 - If S is a subtype of an abstract type, an attribute_reference for S'Input is illegal unless this attribute has been specified by an attribute_definition_clause.
9 - The number of calls performed by the predefined implementation of the stream-oriented attributes on the Read and Write operations of the stream type is unspecified. An implementation may take advantage of this permission to perform internal buffering. However, all the calls on the Read and Write operations of the stream type needed to implement an explicit invocation of a stream-oriented attribute must take place before this invocation returns.
!question
1 - 13.13.2(27) states that S'Input "creates an object (with the bounds or discriminants, if any, taken from the stream), initializes it with S'Read, and returns the value of the object."
Does the verb "initialize" in this sentence refer to the entire initialization process mentioned in 3.3.1(18) and 7.6(10)? (Yes.) In particular, if S is a controlled subtype, or if it contains controlled components, is the Initialize subprogram called? (Yes.) Is the Finalize subprogram called when the intermediate object is finalized? (Yes.) For a record type whose components have initial values, are these values evaluated? (Yes.)
2 - 13.13.2(36) states that "an attribute_reference for one of these attributes is illegal if the type is limited, unless the attribute has been specified by an attribute definition clause."
If some stream-oriented attribute has been specified in a private part, and we are at a point where declarations in that private part are not visible, is a reference to the attribute legal? (No.)
3 - Let T be a limited type with a visible attribute_definition_clause for attribute Read. Is a usage of T'Class'Read legal? (Yes.)
4 - The definition of the profiles of S'Read, S'Write, S'Input and S'Output given in 13.13.2 uses the notation "italicized T" for the type of the Item parameter. What is the meaning of "italicized T" in this context? (T'Base for scalars, first subtype otherwise.)
5 - In an attribute_definition_clause for a stream attribute, is it legal to give a name that denotes an abstract subprogram? (No.)
6 - 13.13.2(9) states that for a record type, the predefined S'Read reads the components in positional aggregate order. However, the language doesn't seem to specify what happens when exceptions are raised by the calls to the Read attribute for the components. Consider for example the following type declarations:
type T1 is range ...; type T2 is range ...;
type R (D1 : T1 := ...; D2 : T2 := ...) is record ... end record;
Say that attribute_definition_clauses have been given for T1'Read and T2'Read, and consider a call to R'Read. Assume that, during this call, an exception is raised by T2'Read. Is the discriminant X.D1 modified? (No.)
7 - Consider a call to T'Read where T is a type with defaulted discriminants. If the discriminants found in the stream have values different from those of the discriminants of the object passed to T'Read for the Item parameter, and that object is constrained, is Constraint_Error raised? (Yes.)
8 - If T is an abstract type, is the function T'Input abstract? (No, but it cannot be called.)
9 - 13.13.1(1) states that "T'Read and T'Write make dispatching calls on the Read and Write procedures of the type Root_Stream_Type." Is the number of those calls specified? (No.)
!recommendation
(See Summary.)
!wording
(See Corrigendum.)
!discussion
1 - It seems logical to make Input follow as closely as possible the semantics of code that could be written by a user, so the implementation of S'Input should obtain the discriminants or bounds from the stream, and then declare an object with appropriate discriminants or bounds, as in:
declare Anon : S (<discriminants or bounds taken from the stream>); begin S'Read (..., Anon); end;
Accordingly, the initialization described in 3.3.1(8-9) and 7.6(10) takes place when Anon is declared, and the finalization described in 7.6.1 takes place when Anon disappears (and before returning from the call to S'Input).
Note that as part of initialization, compiler-specific fields are initialized as required by the implementation (and as permitted by AARM 3.3.1(14.a)).
2 - Consider for example:
package P is type T is limited private; private type T is new Boolean; procedure Read (Stream : ...; Item : out T); for T'Read use Read; end P;
with P; procedure Q is X : P.T; begin P.T'Read (..., X); -- Illegal? end Q;
The call to P.T'Read is illegal, because the declarations and clauses (including the attribute_definition_clause for Read) in the private part of P are not visible in Q. On the other hand, at a place where the declarations and clauses in the private part of P are visible (and that comes after the attribute_definition_clause) a reference to T'Read is legal. This rule is necessary to preserve the privacy of private types.
Note that it is the location of the attribute_definition_clause that counts, not that of the subprogram specified in that clause. Thus, if the procedure Read above were moved to the visible part of P, a reference to P.T'Read would still be illegal (but a reference to P.Read wouldn't).
Because the stream attributes are operational attributes (13.13.2(1/1)), an attribute_definition_clause may be given before the type is fully defined.
3 - In order to simplify the discussion, we first define a new term: a stream- oriented attribute is "available" at some place in the program text if an attribute_reference for that attribute is (or would be) legal at that place.
The rules given in 13.13.2(9/1) try to insure that if the Read attribute is available for a limited tagged ancestor type, then it is also available for all of the types derived from that ancestor. However, this is not true because this rule fails to take into account the visibility issues mentioned in item #2 above. Consider for example:
package P is type T is tagged limited private; private type T is tagged record ...; for T'Read use ... end P;
with P; package Q is protected type Prot is ... end Prot; type NT is new T with record C : Prot; end record; -- No need to define Read (13.13.2(9/1)) end Q;
with Q; package body P is type NNT is new NT with null record; ... NNT'Read (...); -- Legal? end P;
Because the attribute_definition_clause for P.T'Read is not visible at the place where NT is declared, 13.13.2(9/1) does not apply, and it is therefore not necessary to define the Read attribute for Q.NT. However, when we come to the declaration of NNT, we want to ensure that NNT'Read is not callable, because we have no mechanism for reading the component C. Instead of depending on whether the attribute has been specified for some ancestor type, 13.13.2(9/1 and 36/1) must depend on whether the attribute is available for the parent type (and for the types of the extension components).
Moreover, dispatching to the Read attribute does not work in the above case: there is no guarantee that all the types derived from T will have an available Read attribute. So the attribute Read is not available for T'Class. The only case where NT'Read is guaranteed to be available for every NT descended from T is if T'Read is available in the visible part of P. In this case, T'Class'Read is available everywhere.
The default implementation of T'Input is defined in terms of T'Read. If T'Read is available, then the implementation of T'Input is well-defined as well, and there is no reason to prohibit calls. A similar argument applies to T'Output when T'Write is available. But T'Class'Input and T'Class'Output are only available if T'Read and T'Write are available in the visible part of P.
The above model is unfortunately not what is stated by the rules in 13.13.2(36/1). These rules seem to imply that a reference to Input (resp. Output) is legal for the type extension if Input (resp. Output) is available for the parent type. This is incorrect because the default implementation of Input and Output calls Read or Write for the same type, not Input or Output for the parent type. It should say that a reference to Input (resp. Output) is legal for the type extension if Read (resp. Write) is available for that type.
4 - AI95-00145 specifies the meaning of the notation "italicized T" for operators as follows:
"The italicized T shown in the definitions of predefined operators means:
- T'Base, for scalars - the first subtype, for tagged types - the type without any constraint, in other cases"
In the case of stream-oriented attributes the notation "italicized T" must be consistent with the parameter subtype required for attribute_definition_clauses. If we chose the same rule as for operators, we would have a discrepancy in the case of constrained untagged types, and this would unnecessarily complicate the static and dynamic semantics.
When one of the stream-oriented attributes is specified by an attribute definition clause, 13.13.2(36) states that "the subtype of the Item parameter shall be the base subtype if scalar, and the first subtype otherwise."
Therefore, in the parameter profiles for the stream-oriented attributes in section 13.13, the notation "italicized T" means:
- T'Base, for scalars - the first subtype, in other cases
5 - Obviously it should not be possible to perform a non-dispatching call to an abstract subprogram (stream-oriented attributes are always called in a non-dispatching manner). Therefore, we have two options:
- Make the attribute_definition_clause illegal. - Make the calls (explicit or implicit) illegal.
The second option is a significant implementation burden, and allowing the attribute_definition_clause only to reject all calls doesn't seem to do any good. That's why the first option was preferred.
6 - The problem mentioned in the question only exists for a type R that is passed by reference to R'Read: obviously if the type is passed by copy, an exception raised by R'Read cannot affect the original object.
In the case of a type which is passed by reference, we must consider two cases:
- If the exception in T2'Read is due to the failure of some
language-defined check, 11.6(6) explains that the object or its parts may become abnormal, so we don't have to specify what happens to the discriminants.
- If the exception in T2'Read is due to an explicit raise, the object
obviously doesn't become abnormal, and therefore we must preserve the integrity of its discriminants. In other words, either all discriminants are updated (if the calls to the Read attributes for the discriminants were successful) or none (if any call to a Read attribute for a discriminant raised an exception).
This model requires an implementation to read the discriminants, create an anonymous object using the given discriminants and assign that object to the formal parameter Item. The normal initialization and finalization take place for this anonymous object.
Strictly speaking, the use of an anonymous object is only required for a type with defaulted discriminants which is passed by reference, if the actual parameter of Read is not constrained. However, an implementation is free to use anonymous objects in other cases.
Use of an anonymous object is only required for discriminants. An implementation is free to read the other components directly in the Item parameter. For example, if we change the original example as follows:
type R is record C1 : T1; C2 : T2; end record;
then if T2'Read raises an exception when reading component C2, it is unspecified if C1 is modified or not.
7 - When the type has defaulted discriminants, the predefined Read attribute must read them from the stream. It can be the case that the actual object passed to Read is constrained. In this case, the discriminants found in the stream may or may not match those of the actual. If they don't match, Constraint_Error is raised, and this is a Discriminant_Check.
It is unspecified whether this effect is achieved by assigning a temporary object, as explained in #6 above, or by other means.
8 - If T is an abstract type, calling the function T'Input would effectively create an object whose tag designates T, which is absurd. We could decide that T'Input is abstract, but it seems simpler to say that any attribute_reference for this attribute is illegal, by analogy with the rule stated in 13.13.2(36) for limited types.
9 - Surely the user could count the calls to the Read and Write operations for the stream type by writing a perverse implementation of these operations, but it seems that performance should have precedence: for the predefined implementation of String'Write, requiring a call to Ada.Streams.Write for each character would be a big performance hit, with no real benefits.
Therefore, the number of calls to the Read and Write operations is unspecified, and implementations are free (and in fact advised) to do internal buffering. However, we don't want to allow an implementation to buffer all stream output and do only one call to Write when the program terminates: the user must be able to assume reasonable properties regarding the underlying buffering mechanism. That's why we require that all the calls to Read or Write take place before the top-level call to a stream-oriented attribute completes. In other words, an implementation may combine several consecutive calls to Write into a single one, provided these calls all pertain to a single top-level call to the attribute Write (or Output).
!corrigendum 8.3(23)
Insert after the paragraph:
the new paragraph:
An attribute_definition_clause is visible at a place if a declaration at the point of the attribute_definition_clause would be immediately visible at the place.
!corrigendum 13.13.2(9/1)
Replace the paragraph:
For elementary types, the representation in terms of stream elements is implementation defined. For composite types, the Write or Read attribute for each component is called in canonical order, which is last dimension varying fastest for an array, and positional aggregate order for a record. Bounds are not included in the stream if T is an array type. If T is a discriminated type, discriminants are included only if they have defaults. If T is a tagged type, the tag is not included. 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 any ancestor type of T has been directly specified and the attribute of any ancestor type of the type of any of the extension components which are of a limited type has not been specified, the attribute of T shall be directly specified.
by:
For elementary types, the representation in terms of stream elements is implementation defined. For composite types, the Write or Read attribute for each component is called in canonical order, which is last dimension varying fastest for an array, and positional aggregate order for a record. Bounds are not included in the stream if T is an array type. If T is a discriminated type, discriminants are included only if they have defaults. If T is a tagged type, the tag is not included. 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 of T is available anywhere within the immediate scope of T, and the attribute of the type of any of the extension components which are of a limited type, L, is not available at the freezing point of T, then the attribute of T shall be directly specified.
!corrigendum 13.13.2(27)
Replace the paragraph:
S'Output then calls S'Write to write the value of Item to the stream. S'Input then creates an object (with the bounds or discriminants, if any, taken from the stream), initializes it with S'Read, and returns the value of the object.
by:
S'Output then calls S'Write to write the value of Item to the stream. S'Input then creates an object (with the bounds or discriminants, if any, taken from the stream), initializes it with S'Read, and returns the value of the object. Normal initialization and finalization take place for this object (see 3.3.1, 7.6, 7.6.1).
!corrigendum 13.13.2(35)
Replace the paragraph:
In the default implementation of Read and Input for a composite type, for each scalar component that is a discriminant or whose component_declaration includes a default_expression, a check is made that the value returned by Read for the component belongs to its subtype. Constraint_Error is raised if this check fails. For other scalar components, no check is made. For each component that is of an access type, if the implementation can detect that the value returned by Read for the component is not a value of its subtype, Constraint_Error is raised. If the value is not a value of its subtype and this error is not detected, the component has an abnormal value, and erroneous execution can result (see 13.9.1).
by:
In the default implementation of Read and Input for a composite type, for each scalar component that is a discriminant or whose component_declaration includes a default_expression, a check is made that the value returned by Read for the component belongs to its subtype. Constraint_Error is raised if this check fails. For other scalar components, no check is made. For each component that is of an access type, if the implementation can detect that the value returned by Read for the component is not a value of its subtype, Constraint_Error is raised. If the value is not a value of its subtype and this error is not detected, the component has an abnormal value, and erroneous execution can result (see 13.9.1). In the default implementation of Read for a composite type with defaulted discriminants, if the actual parameter of Read is constrained, a check is made that the discriminants read from the stream are equal to those of the actual parameter. Constraint_Error is raised if this check fails.
!corrigendum 13.13.2(36/1)
Replace the paragraph:
The stream-oriented attributes may be specified for any type via an attribute_definition_clause. All nonlimited types have default implementations for these operations. An attribute_reference for one of these attributes is illegal if the type is limited, unless the attribute has been specified by an attribute_definition_clause or (for a type extension) the attribute has been specified for an ancestor type. For an attribute_definition_clause specifying one of these attributes, the subtype of the Item parameter shall be the base subtype if scalar, and the first subtype otherwise. The same rule applies to the result of the Input function.
by:
The stream-oriented attributes may be specified for any type via an attribute_definition_clause. The subprogram name given in such a clause shall not denote an abstract subprogram.
A stream-oriented attribute for a subtype of a specific type T is available at places where one of the following conditions is true:
A stream-oriented attribute for a subtype of a class-wide type T'Class is available at places where one of the following conditions is true:
!corrigendum 13.13.2(36.1/1)
Insert after the paragraph:
For every subtype S of a language-defined nonlimited specific type T, the output generated by S'Output or S'Write shall be readable by S'Input or S'Read, respectively. This rule applies across partitions if the implementation conforms to the Distributed Systems Annex.
the new paragraphs:
If Constraint_Error is raised during a call on Read because of failure of one the above checks, the implementation must ensure that the discriminants of the actual parameter of Read are not modified.
Implementation Permissions
The number of calls performed by the predefined implementation of the stream- oriented attributes on the Read and Write operations of the stream type is unspecified. An implementation may take advantage of this permission to perform internal buffering. However, all the calls on the Read and Write operations of the stream type needed to implement an explicit invocation of a stream-oriented attribute must take place before this invocation returns. An explicit invocation is one appearing explicitly in the program text, possibly through a generic instantiation (see 12.3).
!appendix

!section 13.13.1
!subject Various issues with the stream-oriented attributes
!reference RM95 13.13.1
!reference RM95 13.13.2
!reference AI95-00108
!from Pascal Leroy 97-09-02
!reference 1997-15783.a Pascal Leroy 1997-9-2>>
!discussion

1 - RM95 13.13.2(27) states that S'Input "creates an object (with the bounds
or discriminants, if any, taken from the stream), initializes it with S'Read,
and returns the value of the object.

Does the verb "initialize" in this sentence refer to the entire initialization
process mentioned in RM95 3.3.1(18) and 7.6(10)? In particular, if S is a
controlled subtype, or if it contains controlled components, is the Initialize
subprogram called?  Is the Finalize subprogram called when the intermediate
object is finalized?  For a record type whose components have initial values,
are these values evaluated?

It seems that the simplest model is that S'Input declares a local object,
which is then passed to S'Read, as in:

                X : S (<discriminants taken from the stream>);
        begin
                S'Read (..., X);

If we don't do that, the object passed to S'Read might have uninitialized
access values, or uninitialized compiler-specific fields (dopes, offsets,
tag, etc.).

2 - RM95 13.13.2(36) states that "an attribute_reference for one of these
attributes is illegal if the type is limited, unless the attribute has been
specified by an attribute definition clause."

If some stream-oriented attribute has been specified in a private part, and we
are at a point that doesn't have visibility over that private part, is a
reference to the attribute legal? For example:

        package P is
                type T is limited private;
        private
                procedure Read (Stream : ...; Item : out T);
                for T'Read use Read;
        end P;

        with P;
        procedure Q is
                X : T;
        begin
                T'Read (..., X);        -- Legal?
        end Q;

One hopes that the call to T'Read is illegal, overwise it would break the
privacy of private types.

3 - Let T a limited type with an attribute_definition_clause for attribute
Read, and D a type derived from T, and assume that there is no attribute
definition clause for D'Read:

        type T is limited record ... end record;
        for T'Read use ...;

        type D is new T;

Is a usage of D'Read legal or not?  In other words, shall we consider that,
for the purpose of checking RM95 13.13.2(36), "an attribute has been
specified" for D?

If the answer is "yes", how do the inheritance rules stated in AI95-00108
apply in the case where T is tagged, and D has a component of a task type in
its record extension part?  How is an implementation expected to read that
component?

4 - The definition of the profiles of the predefined S'Read, S'Write, S'Input
and S'Read given in RM95 13.13.2 uses the notation "T italic" for the type of
the Item parameter. AI95-00145 explains that:

"The italicized T shown in the definitions of predefined operators means:

     - T'Base, for scalars
     - the first subtype, for tagged types
     - the type without any constraint, in other cases"

(Note that the above sentence says "operators", so strictly speaking it
doesn't apply to attributes; it seems that the wording of this AI should be
broadened.)

When one of these attributes is specified by an attribute definition clause,
RM95 13.13.2(36) states that "the subtype of the Item parameter shall be the
base subtype if scalar, and the first subtype otherwise."

There is a problem because these definitions don't coincide in the case of
constrained array types. Consider:

        type T is new String (1 .. 10);

It appears that the type of the Item parameter of the predefined
stream-oriented attributes is "T without any constraint" (an anonymous
unconstrained array type ), while the type of the Item parameter for a
user-defined stream-oriented subprogram is T.

In particular, does the presence or absence of an attribute definition clause
have any bearing on the legality of the calls?  For example, consider the
legality of the 'others' choice in an array aggregate; it depends on the
existence on an applicable index constraint, and therefore on whether the
type of the parameter is constrained or not:

        package P is
                type T is new String (1 .. 10);
                procedure Write (...; Item : in T);
        private
                for T'Write use Write;
        end P;

        with P;
        procedure Q is
        begin
                T'Write (..., (others => 'a')); -- Legal?
        end;

Is the above call legal? If yes, would it become illegal if the
attribute_definition_clause were removed?

A possible model is to say that an attribute_definition_clause changes the
body of the Read attribute, but not its profile. It's as if the predefined
Read was just a wrapper calling the user-specified subprogram.

5 - AI95-00108 states (in the !discussion section) that "for untagged derived
types, there is no problem for the derived type inheriting the stream
attributes."

This doesn't seem clear if the derived type includes a known discriminant
part. Consider:

        type Parent (D1, D2 : Integer := 1) is ...;
        type Child (D : Integer := 2) is new Parent (D1 => D, D2 => D);

Clearly Parent'Write writes two discriminant values. It would seem that
Child'Write should only write one discriminant value, which contradicts the
simple inheritance rule given in the AI.

6 - RM95 13.13.2(9) states that for a record type, the predefined S'Read reads
the components in positional aggregate order. However, the RM95 doesn't seem
to specify what happens when exceptions are raised by the calls to the Read
attribute for the components. Consider for example the following type
declarations:

        type T1 is range ...;
        type T2 is range ...;

        type R (D1 : T1 := ...; D2 : T2 := ...) is
                record
                        ...
                end record;

Say that attributes_definition_clauses have been given for T1'Read and
T2'Read, and consider the call:

        X : R;
        ...
        R'Read (..., X);

Assume that an exception is raised by T2'Read. Is the discriminant X.D1
modified?  That would be unpleasant if there were components depending on
this discriminant!

Consider a different example, where there are no discriminants:

        type T1 is ...;
        type T2 is ...;
        type R is
                record
                        C1 : T1;
                        C2 : T2;
                end record;

Assume that an exception is raised by T2'Read. Is the component X.C1
modified?

It would seem that we should stick to the notion that discriminants are only
modified by an assignment of entire objects. This probably requires an
intermediate object in the case of discriminated types. However, it would
seem quite expensive to require the creation of such an intermediate object
for types that don't have discriminants.

Note that if we adopt the idea that an intermediate object is needed (at least
in some cases) then we must define if initialization and finalization of this
object take place.

7 - Consider a call to T'Read where T is a type with defaulted discriminants.
If the discriminants found in the stream have values different from those of
the discriminants of the object passed to T'Read for the Item parameter, and
that object is constrained, is Constraint_Error raised?

Note that if we adopt the model that an intermediate object is needed in this
case, then the constraint check performed when assigning the intermediate
object to the actual parameter will raise Constraint_Error.

8 - If T is an abstract type, is the function T'Input abstract?  One hopes
so,
otherwise it makes it possible to create objects of an abstract type.

9 - RM95 13.13.1(1) states that "T'Read and T'Write make dispatching calls on
the Read and Write procedures of the type Root_Stream_Type."

Is the number of those calls specified?  For example, for the predefined
implementation of String'Write, is an implementation required to call
Ada.Streams.Write for each character? or is it allowed to perform internal
buffering and to call Ada.Streams.Write only once for the entire string?
Obviously the user could tell the difference by writing a pervert
implementation of the Write subprogram, but it would seem that performance
considerations should have the priority in this case.

_____________________________________________________________________
Pascal Leroy                                    +33.1.30.12.09.68
pleroy@rational.com                             +33.1.30.12.09.66 FAX

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

From: 	Tucker Taft
Sent: 	Friday, February 26, 1999 9:47 AM
Subject: 	Re: Updated AIs

AI 195 does not address an issue that we have confronted
recently relating to stream attributes. It is very difficult
to specify stream attributes for a limited private type, if
you require them to be specified in the visible part. In general,
you may not specify a "representation" attribute until after
a type is fully defined. But if you also have to specify stream
attributes in the visible part to make them usable, then you
have a contradiction.

There seem to be two solutions. One
is to allow stream attributes to be specified before the
type is fully defined. The other is to allow them to be specified
in the private part.

I suppose another option is to have some
kind of "incomplete" stream attribute specification,
such as "for Lim_Type'Read use <>;" in the visible part,
and then complete the definition in the private part. This is
rampant invention, of course, but it solves another problem.
Types like Exception_Occurrence are required to have stream attributes,
but not have any other primitive operations declared in the
visible part. However stream attributes can only be defined
in terms of some "normal" subprogram, which must necessarily also
be visible at the point of the stream attribute definition.
Another contradiction...

> The update to AI
> 195 is partial: I believe that Tucker owns the part of this AI that has to do
> with the number of stream elements written/read for a scalar type.

Okay, I will handle that part.

-Tuck

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

From: 	Pascal Leroy
Sent: 	Monday, March 01, 1999 4:14 AM

> There seem to be two solutions. One
> is to allow stream attributes to be specified before the
> type is fully defined. The other is to allow them to be specified
> in the private part.

I hate to open private parts. In our compiler, this is a real nightmare (we
have to do that for pragma Convention, and that's already a big pain in the
neck).

So my preference would be to allow the specification of stream attributes
before the type is fully defined. Remember that we already have an AI that
says that 13.1(10) does not apply to stream attributes. This would just be
another oddity with stream-oriented attributes. (In fact I believe the
stream-oriented attributes should not be considered a representation item,
since they do not affect the physical representation of the type; they should
be "something else", with much more relaxed rules; but we are not going to
rewrite chapter 13...)

> I suppose another option is to have some
> kind of "incomplete" stream attribute specification,
> such as "for Lim_Type'Read use <>;" in the visible part,
> and then complete the definition in the private part. This is
> rampant invention, of course, but it solves another problem.

This would have been a nice idea in '93, but at this point I think the
language is insufficiently broken to invent new syntax.

> Types like Exception_Occurrence are required to have stream attributes,
> but not have any other primitive operations declared in the
> visible part. However stream attributes can only be defined
> in terms of some "normal" subprogram, which must necessarily also
> be visible at the point of the stream attribute definition.
> Another contradiction...

If there is a problem with this package, then I'd rather see the ARG modify
the specification of Ada.Exceptions to include the additional stuff needed for
streaming, rather than adding new syntax for this cornercase.

Pascal

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

From: 	Tucker Taft
Sent: 	Monday, March 01, 1999 8:18 AM

> > There seem to be two solutions. One
> > is to allow stream attributes to be specified before the
> > type is fully defined. The other is to allow them to be specified
> > in the private part.
>
> I hate to open private parts. In our compiler, this is a real nightmare (we
> have to do that for pragma Convention, and that's already a big pain in the
> neck).
>
> So my preference would be to allow the specification of stream attributes
> before the type is fully defined. Remember that we already have an AI that
> says that 13.1(10) does not apply to stream attributes. This would just be
> another oddity with stream-oriented attributes. (In fact I believe the
> stream-oriented attributes should not be considered a representation item,
> since they do not affect the physical representation of the type; they should
> be "something else", with much more relaxed rules; but we are not going to
> rewrite chapter 13...)

Yes, I agree, since 13.1(10) does not apply to stream attributes, we
should view them as a special category of attribute, and allow them
to be specified on private types and other not-yet-completely-defined
types.

> > I suppose another option is to have some
> > kind of "incomplete" stream attribute specification,
> > such as "for Lim_Type'Read use <>;" in the visible part,
> > and then complete the definition in the private part. This is
> > rampant invention, of course, but it solves another problem.
>
> This would have been a nice idea in '93, but at this point I think the
> language is insufficiently broken to invent new syntax.
>
> > Types like Exception_Occurrence are required to have stream attributes,
> > but not have any other primitive operations declared in the
> > visible part. However stream attributes can only be defined
> > in terms of some "normal" subprogram, which must necessarily also
> > be visible at the point of the stream attribute definition.
> > Another contradiction...
>
> If there is a problem with this package, then I'd rather see the ARG modify
> the specification of Ada.Exceptions to include the additional stuff needed for
> streaming, rather than adding new syntax for this cornercase.

If we allow specification of stream attributes on private types, that
will probably simplify the solution enough to not justify adding any
declarations to Ada.Exceptions.

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

From: Tucker Taft
Sent: Thursday, June 01, 2000 9:25 PM

...
I would suggest that Output and Input piggy back automatically
on Write and Read, even for limited types:
If the Read attribute of a type can legally be called, then the Input
attribute has a non-abstract default implementation; similarly,
if the Write attribute of a type can legally be called, then
the Output attribute has a non-abstract default implementation.

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

From: Randy Brukardt
Sent: Friday, June 02, 2000 5:12 PM

This seems like a good idea, but it's going well beyond the AI, or even what is
currently in AI-00195. I don't want to bog the corrigendum down at the last
minute with good ideas... I suggest that we add that to AI-195.

Clearly, this wouldn't conflict with the current wording (which defines that
these always use the default implementation, but leaves them uncallable for
limited types). But I'm not sure it is useful for the implementation burden --
Input is a function after all, and limited functions aren't very useful.

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

Editor's note, 7/7/2000

Original questions 3 & 5 were deleted, as they have been handled in the
corrigendum wording for AI-108.

I also added Tucker's suggestion to the AI as question 3, and moved up
question 11 to take the space of question 5.

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

Editor's note, 5/25/2001

The stream size stuff was moved to a new AI (AI-270), as suggested by the ARG
at the recent meeting in Leuven.

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

From: Pascal Leory
Sent: Friday, August  3, 2001 3:47 AM

I am looking at the rules for streaming of limited types in TC1 (specifically in
DR 0040), and it seems to me that they are not quite correct. (We never seem to
get streams right, no matter how hard or how often we try.)

In 13.13.2(9/1), a section pertaining to Read and Write, we have:

"For a limited type extension, if the attribute of any ancestor type of T has
been directly specified and the attribute of any ancestor type of the type of
any of the extension components which are of a limited type has not been
specified, the attribute of T shall be directly specified."

In 13.13.2(36/1), a section pertaining to all stream attributes:

"An attribute_reference for one of these attributes is illegal if the type is
limited, unless the attribute has been specified by an
attribute_definition_clause or [(for a type extension)] the attribute has been
specified for an ancestor type."

I understand that for Read and Write, these rules ensure that the attribute is
either (1) not callable or (2) complete in the sense that it deals with all the
components, including the limited ones. However, the rules don't seem to work
well for Input and Output.

Consider the following example:

    type T1 is tagged limited record ... end record;
    for T1'Output use ... -- Note: no user-defined T1'Write

    protected type P is ... end P;

    type T2 is new T1 with
        record
            C : P;
        end record;

    T2'Output (...); -- Legal?

13.13.2(36/1) seems to say that the reference to T2'Output is legal because
T1'Output was specified. 13.13.2(9/1) is not applicable because the attribute
in question is not Read or Write, so apparently the type extension is legal.

One is left wondering what T2'Output does at execution. Its default
implementation is supposed to write the discriminants and then call T2'Write and
P'Write. But in this instance there is no T2'Write and no P'Write, so we have a
problem. At any rate, the existence of T1'Output is totally irrelevant, since
T2'Output is never defined in terms of T1'Output.

It seems that 13.13.2(36/1) needs revision. For Input and Output, a reference
should be legal iff Read or Write, resp., have been specified for some ancestor.

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

From: Randy Brukardt
Sent: Thursday, August 16, 2001 4:09 PM

Tucker had proposed such a rule only a few days before the Potsdam meeting to
finalize the Corrigendum. I made an executive decision to postpone that (lest we
introduce still more errors), in part because I was not aware of a technical
problem with the proposed rules. (Tuck's proposal was to increase consistency,
not to fix a bug.)

Anyway, I put this proposal as question 3 in the giant stream AI-195. So I think
we can handle the correction there. Conveniently, Pascal is the author of that
AI (now that the stream size stuff is out of it, we might even be able to finish
it). So I recommend that he add this problem to the AI, then put it on the
agenda for the meeting. (How's that for passing the buck? :-)

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

From: Pascal Leroy
Sent: Friday, May 31, 2002  6:45 AM

Fair enough.  Let me try again (I believe that the following addresses your two
questions):

A stream-oriented attribute for a subtype of a class-wide type T'Class is
available at places where one of the following conditions is true:

* T is nonlimited.

* T is a limited private type or a limited private extension, and the
corresponding attribute of T is available somewhere within the package
specification where its partial view is declared.

* T doesn't have a partial view, and the corresponding attribute of T is
available somewhere within the immediate scope of T.

* The attribute has been specified via an @fa<attribute_definition_clause>, and
the @fa<attribute_definition_clause> is visible.

What do you think?

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

From: Tucker Taft
Sent: Friday, May 31, 2002  12:44 PM

Including within the private part or body?

I have now lost track of why these two bullets can't be reduced to:

  * T is a limited type and the corresponding attribute of T is available.

(i.e. T'Class'Blah is available where T'Blah is available)

Is the problem that within the immediate scope of T itself, we want
T'Class'Blah to be available *before* T'Blah is available.
I presume *outside* the immediate scope of T, the above simple rule
works.  Or is the problem something else?

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

From: Pascal Leroy
Sent: Friday, May 31, 2002  2:52 PM

> Or is the problem something else?

I suggest rereading item 3 of the !discussion of the AI.

The problem is that we must ensure that T'Class'Blah is only available if
NT'Blah is available for all possible types NT derived from T (including T
itself), because the 'Class'Blah attribute will dispatch, and if it tries to
dispatch to a non-available attribute, things will go downhill.

Ignoring private types, the other rules in this AI ensure that, if Blah is
available for T, it is available for NT.  However, in the presence of
private types (or extensions) things get nasty because Blah may only be
available in the private part, and NT doesn't know about that attribute.  I
originally wrote the AI in a way that made T'Class'Blah unavailable in this
case, but Randy didn't like it, so I tried to be more clever, and failed
miserably.

Anyway it is clear that the discussion we are having is not an editorial
one, so I am going to send this AI back to the ARG once more.

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

From: Randy Brukardt
Sent: Thursday, June 13, 2002  12:32 PM

Pascal proposed:

A stream-oriented attribute for a subtype of a class-wide type T'Class is
available at places where one of the following conditions is true:

* T is nonlimited.

* T is a limited private type or a limited private extension, and the
corresponding attribute of T is available somewhere within the package
specification where its partial view is declared.

* T doesn't have a partial view, and the corresponding attribute of T is
available somewhere within the immediate scope of T.

* The attribute has been specified via an @fa<attribute_definition_clause>,
and
the @fa<attribute_definition_clause> is visible.


Tucker wrote:

> > I have now lost track of why these two bullets can't be reduced to:
> >
> >   * T is a limited type and the corresponding attribute of T is
available.
> >
> > (i.e. T'Class'Blah is available where T'Blah is available)
> >
> > Or is the problem something else?

Pascal responded:

> I suggest rereading item 3 of the !discussion of the AI.
>
> The problem is that we must ensure that T'Class'Blah is only available if
> NT'Blah is available for all possible types NT derived from T (including T
> itself), because the 'Class'Blah attribute will dispatch, and if it tries
to
> dispatch to a non-available attribute, things will go downhill.
>
> Ignoring private types, the other rules in this AI ensure that, if Blah is
> available for T, it is available for NT.  However, in the presence of
> private types (or extensions) things get nasty because Blah may only be
> available in the private part, and NT doesn't know about that attribute.
I
> originally wrote the AI in a way that made T'Class'Blah unavailable in
this
> case, but Randy didn't like it, so I tried to be more clever, and failed
> miserably.

The original AI made T'Class'Blah illegal for all limited private types.
That's the vast majority of types, and that essentially denies OOP streaming
to limited types. I didn't (and still don't) think that is a good idea.

> Anyway it is clear that the discussion we are having is not an editorial
> one, so I am going to send this AI back to the ARG once more.

The primary problem here is the proposed revised wording has the wrong
meaning, so it doesn't make sense. The substitution of "somewhere" for
"anywhere" is wrong.

That's because the point of the rule is that the attribute needs to be
available anywhere that the type is visible, or we run into the problem that
Pascal noted. Thus "somewhere" is very wrong, because it allows the
attribute to be available somewhere in its scope.

But of course "anywhere" alone isn't quite right, either, since it is a
condition that could never be met:

    type LPriv is tagged limited private;
    -- LPriv'Write is not available here!
    procedure Write (AS : Stream_Access; Obj : in out LPRiv);
    for LPriv'Write use Write;
    -- LPriv'Write is available here.

So the rule was written as:

@xbullet<The corresponding attribute of @i<T> is available everywhere within
the part of the immediate scope of @i<T> where @i<T> is frozen.>

This might be confusing, but it is (roughly) right. It's not possible to
call the attributes before they are frozen, so that is the part of the scope
that we are interested in.

If there is some part of the scope where the attribute T'Blah is not
available, then it is possible that there is a derviation NT where NT'Blah
is not available, and we therefore cannot allow the use of T'Class'Blah
(otherwise it could dispatch to an unavailable attribute).

The only problem I see with the original wording is that the freezing point
could be (and probably is) in the private part. That would still allow the
attribute to be unavailable in some derivations.

Returning to Pascal's proposed wording, I see two problems:

* T is a limited private type or a limited private extension, and the
corresponding attribute of T is available somewhere within the package
specification where its partial view is declared.

This would include the private part. It has to be available in the visible
part, or T'Class'Blah has to be unavailable.

* T doesn't have a partial view, and the corresponding attribute of T is
available somewhere within the immediate scope of T.

Tuck asks if this could include the body. I don't think so, as you can't
redefine the attribute after the type is frozen. And it has to be frozen in
the body. But again, it could include the private part (since the type isn't
necessarily frozen), and that is no good.

Indeed, I don't think whether or not the type has a partial view is
particularly relevant. What matters more is whether it is declared in a
package spec.

So my suggestion is:

A stream-oriented attribute for a subtype of a class-wide type T'Class is
available at places where one of the following conditions is true:

* T is nonlimited; or

* T or its partial view is declared in the visible part of a (generic)
package, and the corresponding attribute of T is available somewhere within
the visible part of the package; or

* T or its partial view is declared somewhere other than the visible part of
a (generic) package, and the corresponding attribute of T is
available somewhere within the immediate scope of T; or

* The attribute has been specified via an @fa<attribute_definition_clause>,
and
the @fa<attribute_definition_clause> is visible.


It's still a bit more confusing than I'd like. Comments?

Sorry about writing all of this, but it seems necessary for me to figure out
what this all means. And I hate the throw it away afterwards, hoping it will
help someone else figure it out.

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


Questions? Ask the ACAA Technical Agent