!standard 13.13.1 (00) 98-03-27 AI95-00195/01 !class binding interpretation 98-03-27 !status received 98-03-27 !priority High !difficulty Hard !subject Streams !summary 98-03-27 !question 98-03-27 !recommendation 98-03-27 !wording 98-03-27 !discussion 98-03-27 !appendix 98-03-27 !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 (); 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 **************************************************************** !section 13.13.2(17) !subject Stream size of 16 bit integers !reference RM95 13.13.2 (17) !reference 1998-15826.a Stephen Leake 98-03-14 !reference 1998-15827.a Tucker Taft 1998-3-17 !from Randy Brukardt 98-03-18 !keywords base type, size clause, stream representation !reference 1998-15828.a Randy Brukardt 1998-3-19>> !discussion We just ran into this problem. We were trying to use Streams to read Microsoft Windows bitmap files. Since the file format is defined external to Ada, and is not very friendly to Ada, it is fairly difficult. We tried various stearm solutions that worked on GNAT, but not on ObjectAda, because of the 16-bit vs. 32-bit problem. Since we can't control the stream representation, we can't use Streams to portably read this externally defined file format. We ended up reading an array of Storage_Elements, then converting that with Unchecked_Conversion. Yuk! This problem should be solved; otherwise, Ada 95 will not be able to portably read files with a definition outside of Ada. Randy. ****************************************************************