!standard 6.1.1(0/3) 18-02-28 AI12-0020-1/02 !class Amendment 12-02-14 !status work item 12-02-14 !status received 11-10-13 !priority Medium !difficulty Medium !subject 'Image for all types !summary **TBD. !problem Ada only provides 'Image for scalar types. However, for debugging in particular, an easy way to view an object of any type is useful. !proposal T'Image is defined for all non-abstract types (including class-wide types). Ditto for T'Wide_Image and T'Wide_Wide_Image. T'Image and T'Wide_Image are defined in terms of T'Wide_Wide_Image just as they are already defined in the RM for scalar types. Image, Wide_Image, and Wide_Wide_Image attributes are defined for any object of a non-abstract type just as they are defined for any object of a scalar type today. Default implementation: For scalars - unchanged. For access types - Integer value displayed as a hex digits in square brackets, except null displayed as "null". Number of hex digits determined by size of access type. For example, "[FF0012AC]". For task types - Undiscriminated: "()" Discriminated: "(D1 => 123, D2 => 456, )" For protected types - Nothing special. Same as any record type. Protected_Obj'Image is a protected function (this matters, for example, for locking). For type extensions - By default we treat a type extension the same as any other record type. This may involve (in corner cases) having multiple components with the same component name, but that's ok because the order disambiguates. But suppose an ancestor type has a user-defined Image attribute (actually a user-defined Put attribute, but we haven't gotten to that detail yet; for purposes of this discussion, we will incorrectly assume that Image is a user-specifiable attribute). In that case we use extension aggregate syntax, as in "( with C1 => 123, C2 => 4.5)" . It might be more regular to always use extension-aggregate syntax but the result would be less readable. For other composite specific types: Named aggregate syntax is used; only white space is a single blank after "," and both before and after "=>". This can result in double blanks in some cases, as in "(Field => 123)" where the second blank comes from the image of the component value. Record component names in upper case (same as existing rule for 'Image of enumeration values). TBD: Do we want to allow Discard_Names to be specified for a record type, meaning that implementation component names would be used in image functions? Record component ordering follows positional aggregate rules. Array index names generated via image. Null arrays displayed as "(1 .. 0 => <>)" or "(1 .. 0 => (1 .. 10 => <>))" Null records displayed as "(null record)". [For an array with contiguous elements with the same image, there is no collapsing down to a range. We get "( 1 => False, 2 => False, 3 => True)" instead of something like "( 1 .. 2 => False, 3 => True)] [No special treatment for string types.] For class-wide, the image obtained from the specific type is prefixed with the Wide_Wide_Expanded_Name of the tag and a "'", yielding qualified expression syntax, as in "My_Pkg.My_Tagged_Type'(ABC => True, DEF => 123.45)" Inherited by untagged derived types like streaming operators (although no "availability" rules are needed - Image is always available). TBD: Do we want to do anything special for predefined String types so that, for example, String'Image ("cat") returns something other than "( 1 => 'c', 2 => 'a', 3 => 't')"? Note that bounds information would be missing if it returns "cat"; is that acceptable? Perhaps it should return something like "1 .. 3 => ""cat""" (using usual Ada double-quoting here for quotes-in-quotes). We could punt and make Put implementation dependent for these types, but that seems like sacrificing portability for no good reason. TBD: Do we want to require anything about the images of predefined types which are private (e.g., the variable length string types)? Portability seems like less of an issue for these guys, but it seems worth mentioning. ==== Default implementation of T'Image for a type T is defined in terms of another subprogram-valued attribute, T'Put. T'Image cannot be overridden with a user-defined subprogram but T'Put can (which is how one gets the effect of a user-defined Image function). In other words, Put is a specifiable attribute and Image is not. [Attribute name "Put" is, of course, TBD. Perhaps "Put_Image"?] The idea here is that we want to avoid excessive copying as we build up the image value of a composite type. Instead, we do all the "real" work in "Put" procedures which pass around a stream parameter which is used as a buffer. A typical Put procedure contains calls to Wide_Wide_String'Write and, in the case of a composite type, a call to each component's Put procedure. Before going into further detail about Image and Put attributes, we first introduce some new predefined child units (TBD: names are tentative). package Ada.Streams.Counted_Streams is type Counted_Stream is abstract new Root_Stream_Type with private; function Element_Count (Stream : Buffered_Stream) return Stream_Element_Count is abstract; end; package Ada.Streams.Counted_Streams.Unbounded is -- intended implementation uses allocators, controlled types, etc. type Unbounded_Stream is new Counted_Stream with private; overriding procedure Read (...); overriding procedure Write (...); overriding function Element_Count ...; end; package Ada.Streams.Counted_Streams.Bounded is -- same as unbounded except for the discriminant type Bounded_Stream (Max_Elements : Stream_Element_Count) is new Counted_Stream with private; overriding procedure Read (...); overriding procedure Write (...); overriding function Element_Count ...; end; The Bounded variant isn't strictly needed for this AI, but it is provided for use in calls to T'Put when compilation restrictions would preclude use of the Unbounded package (and therefore uses of 'Image for non-scalar types). For each non-abstract type T, T'Put is a procedure with the profile ( : T; Stream : access Counted_Stream'Class); For every type T such that T'Wide_Wide_Image is defined, that function is defined to be equivalent to the following implementation: function T'Wide_Wide_Image ( : T) return Wide_Wide_String is Local_Stream : aliased Ada.Streams.Counted_Streams.Unbounded.Unbounded_Stream; Elems_Per_Char : constant := Wide_Wide_Character'Stream_Size / Stream_Element'Size; -- typically 4 begin T'Put (, Local_Stream'Access); return Result : Wide_Wide_String (1 .. (Element_Count (Local_Stream) + Elems_Per_Char - 1) / Elems_Per_Char) do Wide_Wide_String'Read (Result, Local_Stream'Access); end return; end; Wide_Wide_Image is not a specifiable attribute (nor are Wide_Image or Image). However, Put is a specifiable attribute. Given X of type T, X'Put () is equivalent to T'Put (X, ). Note that the usual "as if" optimization rules apply, so that this definition does not require any change in the existing default implementation of a scalar type's image function. It probably needs to be stated explicitly that a use of T'Image for a scalar type T doesn't violate any restrictions pertaining to dynamic storage allocation, controlled types, dispatching, unless the default implementation of T'Put has been overridden ... details TBD. In situations where dynamic storage allocation and/or controlled types are disallowed, one could imagine a compiler option which causes the local variable declared by the compiler inside each Wide_Wide_Image function to be of type Bounded_Stream instead of Unbounded_Stream, with the discriminant value determined via a (user-specified?) aspect associated with the type. The default implementation of Put for a Scalar type is equivalent to procedure Put ( : Scalar_Type; Stream : access Counted_Stream'Class) is Wide_Wide_Image : constant Wide_String := ; begin Wide_Wide_String'Write (Wide_Wide_Image, Stream) end; For a record type having components C1, C2, and C3 (in that order, as given by the component ordering rules for a positional record aggregate), the default implementation of Put is equivalent to procedure Put ( : Record_Type; Stream : access Counted_Stream'Class) is begin Wide_Wide_String'Write ("(", Stream); Wide_Wide_String'Write ("C1 => ", Stream); .C1'Put (Stream) Wide_Wide_String'Write (", C2 => ", Stream); .C1'Put (Stream) Wide_Wide_String'Write (", C3 => ", Stream); .C1'Put (Stream) Wide_Wide_String'Write (")", Stream); end; A variant part introduces a corresponding case statement. The default implementation of Put for a one-dimensional array type is equivalent to procedure Put ( : Array_Type; Stream : access Counted_Stream'Class) is begin Wide_Wide_String'Write ("(", Stream); if 'Length = 0 then 'First'Put (Stream); Wide_Wide_String'Write (" .. ", Stream); 'Last'Put (Stream); Wide_Wide_String'Write (" => <>", Stream); else for Idx in 'Range loop Idx'Put (Stream); Wide_Wide_String'Write (" => ", Stream); (Idx)'Put (Stream); if Idx /= 'Last then Wide_Wide_String'Write (", ", Stream); end if; end if; end loop; Wide_Wide_String'Write (")", Stream); end; Other types are handled similarly; just calls to Wide_Wide_String'Write mixed with Put calls for components. Note that the implementation of T'Class'Put will involve a dispatching call to the Put of the corresponding specific type (after writing out the Wide_Wide_Expanded_Name of the tag and a "'"), so that requires a new entry in the dispatch table for each tagged type. !wording ** TBD. !discussion Ada has a way to automatically create stream attributes and equality, so there is some sense to supporting a way to do that for 'Image as well. But care would be needed: some representation would be needed for access types, and do we want to support 'Value for complex records? !ACATS test ** TBD. !appendix From: Gregory D Moncreaff Sent: Thursday, October 13, 2011 6:59 PM Having worked with other language technologies, specifically C# and Java, I was wondering if any consideration was being given to the idea of providing some form of default rendering of record types via an attribute? Typically one would expect it to report all components (recursively) of record using the type primitive existing image attributes. Access types would not be traversed to avoid recursion. Only the valid components of a discriminated record would be reported. Ideal format would be of something that could itself be used as a record assignment by components. Optionally providing the ability of overriding this attribute would be nice, but not necessary. Special care would need to be taken if String's had special ASCII characters within them by concatenating the quotable portions of the string with the specials. Does this seem reasonable? **************************************************************** From: Randy Brukardt Sent: Thursday, October 13, 2011 9:16 PM ... >I was wondering if any consideration was being given to the idea of >providing some form of default rendering of record types via an >attribute? Yes, of course. We've considered a number of additional forms of 'Image and 'Value. We never got any consensus on whether they are important enough. >Typically one would expect it to report all components (recursively) of >record using the type primitive existing image attributes. In what format?? (Aggregates, presumably.) Why only records, why not arrays? >Access types would not be traversed to avoid recursion. Only the valid >components of a discriminated record would be reported. What gets output for a non-null access type parameter. Whatever it is, it wouldn't mean the same thing in an aggregate, which is bad. >Ideal format would be of something that could itself be used as a >record assignment by components. What is this? An aggregate I can imagine, I don't even know what "record assignment by components" means (I imagine a series of assignment statements when I read that, but I can't imagine how you could use that as an 'Image). >Optionally providing the ability of overriding this attribute would be >nice, but not necessary. We've discussed this idea in the past, too. We also considered *only* providing this (see below for why). >Special care would need to be taken if String's had special ASCII >characters within them by concatenating the quotable portions of the >string with the specials. A String is just an array in Ada, there is nothing special about it. And what about components that are other arrays (arrays of integer, arrays of float, arrays of record)? Task components? Protected type components? >Does this seem reasonable? Not particularly, for some of the reasons given above. As a built-in attribute, the compiler would have to generate it for all types (whether it is used or not, since it can't know whether some other package will use it). And given the recursive definition you are giving, it would become very, very large for a lot of types (even stopping on access types). Imagine a record containing a matrix component, for one example. For a compiler (like Janus/Ada) that is designed to make small size code, this would be a problem. (At least it would not have any runtime impact unless used.) I do think there might be something worth doing for 'Image (I don't think we rejected it for technical reasons as much as importance). But an automatically recursing 'Image isn't it, IMHO. **************************************************************** From: Simon Wright Sent: Friday, October 14, 2011 2:23 AM > I was wondering if any consideration was being given to the idea of providing > some form of default rendering of record types via an attribute? Gregory might find the ASIS-based Auto_Text_IO useful - http://www.sigada.org/WG/asiswg/ASIS_Clients.html#Leake **************************************************************** From: Steve Baird Sent: Thursday, January 25, 2018 7:24 PM I'd like to get a consensus on intent before attempting wording. For statements in the list below, I'd like confirmation. For questions, I'd like answers. 1) Image is to be defined for all types (including limited types) and can be overridden in pretty much the same way that streaming attributes can be overridden (but see #8 below). Unlike streaming attributes, there are no availability issues - 'Image is always available. 2) For scalar types, behavior is unchanged (except in the user-defined case). In all (non-user-defined) cases, you get no line-breaking or any sort of formatting. [We are avoiding pretty-printer wars as much as possible.] 3) For task and access types, the string is implementation dependent. For example, the image for any value of such a type might be "<>". Or it might be something more useful. As mentioned earlier, users can override. 3) For untagged records, we use named-notation aggregate syntax (order of fields follows positional aggregate rules) type T is record Xx, Yy, Zz : Some_Type; end record; Obj : T := ... ; pragma Assert (Obj'Image = "(Xx => " & Obj.Xx'Image & ", Yy => " & Obj.Yy'Image & ", Zz => " & Obj.Zz'Image & ")" Ditto for specific tagged records; in particular, the tag value is not mentioned at all in that case. Nothing special for protected records. In the no-components case, image is "(null record)". 4) What do we do for class-wide? It seems like we need to mention the tag or the name of the specific type or something like that. Perhaps using qualified expression syntax, with Ada.Tags.External_Name (Obj'Tag) as the qualifier? Record aggregate syntax is used (as opposed to extension aggregate syntax). [It is ok if this displays two components with the same name; ordering resolves the ambiguity in this corner case.] There is also the issue of distributed overhead, similar to the case of a user who declares a tagged type and never uses its streaming operations; can we ignore this concern? 5) What about arrays? It would be very verbose to use named notation syntax, as in "(1 => 123, 2 => 456, 3 => 789, ..., 9999 => 0)" but we have to get the bounds in there somehow. Perhaps display it sort of like a record, as in "(First(1) => 1, Last(1) => 3, First(2) => 1, Last(2) => 2, " & "((1, 2), (3, 4), (5, 6)))" Do we want to do anything different in the case where the first named subtype is constrained? One might argue that there is no need to display bounds in that case. But then what happens with an aggregate or a slice which doesn't have the bounds of the first named subtype? There is also a question about null arrays, both single- and multi-dimensional. Perhaps these might be displayed as "(First => 1, Last => 0, ())" "(First(1) => 1, Last(1) => 3, First(2) => 1, Last(2) => 0," & " ((), (), ())" "(First(1) => 1, Last(1) => 0, First(2) => 1, Last(2) => 3," & " ()" Do we want to do anything special for the image of a string type and use some variant of string-literal syntax? Probably, although string-type is the wrong thing to test for because that would allow something like type Element is ('A', 'B', None_Of_The_Above); type I_Am_A_String_Type is array (1 .. 10) of Element; and we don't want to use string-literal syntax for some values of a given type and not for others. A big advantage of using verbose named-notation syntax is that it would then be more natural to use range notation in the case where consecutive elements have the same image (with the usual permissions to avoid having to invoke the Image function twice if arguments are known to be equal). So we might see "(1 => 123, 2 .. 999 => 0, 1000 => 456)" Named-notation syntax is probably the way to go, even though (as noted earlier) it can lead to a lot of redundant text in some cases. If we do that, then we'd need consistent syntax for the null array case (single- and multi-dimensional). 6) Some images begin with leading blanks and some don't (that's already true, so we have to deal with both cases). Do we want to take any steps to avoid ugly double-blanks? For example, would the image of a record with two integers look like "(X => 123, Y => 456)" or like "(X => 123, Y => 456)" -- note the two additional spaces ? See the Assert pragma in item #3 above. 7) What about compatibility issues? Given a bunch of overloaded parameterless functions named Foo, could there be a situation where we currently accept Foo'Image but it would become ambiguous with the new rules? I think the answer is no, and there is no problem here. Nonetheless, it seems worth mentioning. 8) The same relationship between 'Image, 'Wide_Image, and 'Wide_Wide_Image holds for any type as holds today. Only 'Wide_Wide_Image is user-specifiable and the others follow suit as usual. If a mention of "user-specified Image attribute" or somesuch in this discussion appears to violate this rule, that is a case of preferring clarity over precision - to repeat: Image cannot be specified, Wide_Wide_Image can be specified, and specifying Wide_Wide_Image changes the behavior of Wide_Image and Image. 9) Inheritance of user-specified Image attributes follows the same rules as for streaming attributes. 10) Evaluation of an Image attribute never yields a static string if the attribute is user-defined. For a scalar type, at least, this means that we probably want to prohibit package Pkg is type T is range 1 .. 10; private function WWI (X : T) return Wide_Wide_String; for T'Wide_Wide_Image use WWI; end Pkg; because clients outside would have to look into the private part to see that T'Image(5) is not a static string. **************************************************************** From: Randy Brukardt Sent: Thursday, January 25, 2018 8:02 PM ... > 3) For task and access types, the string is implementation > dependent. For example, the image for any value of such > a type might be "<>". Or it might be something more useful. > As mentioned earlier, users can override. You have two #3s here. :-) For a task, one could imagine using the task id somehow. (At least, that was my first thought.) Not completely useful (especially for 'Value), but it would clearly allow telling tasks apart and would be useful for debugging. For access values, one could mandate using the image of the associated Integer_Address in, say square brackets. (That would be familiar to anyone used to an Intel assembler.) One of the main purposes of the predefined version is debugging, and allowing the image to be nothing is not useful for debugging. So perhaps it is better to specify something, even if that something isn't enough to use in 'Value. > 3) For untagged records, we use named-notation aggregate syntax > (order of fields follows positional aggregate rules) > > type T is record Xx, Yy, Zz : Some_Type; end record; > Obj : T := ... ; > > pragma Assert (Obj'Image = > "(Xx => " & Obj.Xx'Image > & ", Yy => " & Obj.Yy'Image > & ", Zz => " & Obj.Zz'Image > & ")" > > Ditto for specific tagged records; in particular, the > tag value is not mentioned at all in that case. Nothing > special for protected records. > > In the no-components case, image is "(null record)". One needs to specify the case of the component names in this aggregate. I'd expect the assertion to fail, because those names will probably be all UPPER CASE or all lower case -- but never mixed case. (Most likely, copy the enumeration rule.) For the purposes of 'Value, some types would have to be disallowed as there may not be unique UPPER CASE versions of the ids. But leave that worry to the author of 'Value. > 4) What do we do for class-wide? It seems like we need to > mention the tag or the name of the specific type or > something like that. Perhaps using qualified expression > syntax, with Ada.Tags.External_Name (Obj'Tag) as the > qualifier? Record aggregate syntax is used (as opposed to > extension aggregate syntax). [It is ok if this displays > two components with the same name; ordering resolves > the ambiguity in this corner case.] The qualified expression syntax seems fine. One would want to be able to have a 'Value for it, at least if the components are all distinct. (That obviously doesn't matter for 'Image). > There is also the issue of distributed overhead, similar > to the case of a user who declares a tagged type and never > uses its streaming operations; can we ignore this concern? Yes. All good compilers ... sorry Tuck. Janus/Ada removes unused subprograms at link-time, even if they occur in tags. (Yes, there is a way to write a shared generic such that we have to turn off this optimization, but I've never seen such a generic written by anyone -- including me -- to date.) So this is possible, and this "dead code" overhead can be made zero if it is an actual concern. This can be a real problem for tagged types (we implemented this to make the "hello world" CLAW program more reasonably sized; I believe it changes a 1.5M executable into a 0.5M executable, so this isn't trivial). > 5) What about arrays? It would be very verbose to use > named notation syntax, as in > > "(1 => 123, 2 => 456, 3 => 789, ..., 9999 => 0)" > > but we have to get the bounds in there somehow. > Perhaps display it sort of like a record, as in > > "(First(1) => 1, Last(1) => 3, First(2) => 1, Last(2) => 2, " > & "((1, 2), (3, 4), (5, 6)))" > > Do we want to do anything different in the case where the > first named subtype is constrained? One might argue > that there is no need to display bounds in that case. > But then what happens with an aggregate or a slice which > doesn't have the bounds of the first named subtype? > > There is also a question about null arrays, both > single- and multi-dimensional. Perhaps these might > be displayed as > > "(First => 1, Last => 0, ())" > > "(First(1) => 1, Last(1) => 3, First(2) => 1, Last(2) => 0," > & " ((), (), ())" > > "(First(1) => 1, Last(1) => 0, First(2) => 1, Last(2) => 3," > & " ()" > > > Do we want to do anything special for the image of > a string type and use some variant of string-literal > syntax? Probably, although string-type is the wrong > thing to test for because that would allow something > like > type Element is ('A', 'B', None_Of_The_Above); > type I_Am_A_String_Type is array (1 .. 10) of Element; > and we don't want to use string-literal syntax for some > values of a given type and not for others. > > A big advantage of using verbose named-notation > syntax is that it would then be more natural to use range > notation in the case where consecutive elements have > the same image (with the usual permissions to avoid > having to invoke the Image function twice if arguments > are known to be equal). > > So we might see > "(1 => 123, 2 .. 999 => 0, 1000 => 456)" > > Named-notation syntax is probably the way to go, even though > (as noted earlier) it can lead to a lot of redundant text in > some cases. If we do that, then we'd need consistent syntax > for the null array case (single- and multi-dimensional). You seem to have answered your own question here. And I tend to agree. 'Image of large data structures is never going to be very useful (what do you do with 20 pages of output), so it probably makes sense to just use the syntax that provides the most information and least chance of getting it wrong. > 6) Some images begin with leading blanks and some don't (that's > already true, so we have to deal with both cases). Do we want > to take any steps to avoid ugly double-blanks? For example, > would the image of a record with two integers look like > "(X => 123, Y => 456)" > or like > "(X => 123, Y => 456)" -- note the two additional spaces > ? See the Assert pragma in item #3 above. Not sure here. It would be a pain to try to eliminate the leading blanks for numeric 'Image, so I probably would say leave the extra blanks. (Or maybe, skip putting a blank after "=>", as it will still be syntactically correct either way.) One can always user-define it if you need it perfect. > 7) What about compatibility issues? > > Given a bunch of overloaded parameterless functions named > Foo, could there be a situation where we currently accept > Foo'Image > but it would become ambiguous with the new rules? > I think the answer is no, and there is no problem here. > Nonetheless, it seems worth mentioning. Attribute prefixes have to resolved sans context (with the exception of 'Access and any others with special rules). So it would have to be ambiguous now if Foo is overloaded with no parameters. ... > 10) Evaluation of an Image attribute never yields a > static string if the attribute is user-defined. > For a scalar type, at least, this means that we > probably want to prohibit > > package Pkg is > type T is range 1 .. 10; > private > function WWI (X : T) return Wide_Wide_String; > for T'Wide_Wide_Image use WWI; > end Pkg; > > because clients outside would have to look into the > private part to see that T'Image(5) is not a static string. Luckily, string attributes are never static -- see 4.9(7) -- and we didn't change that in part because we knew this AI was coming -- so this is a lot of worry about nothing. **************************************************************** From: Raphaël Amiard Sent: Friday, January 25, 2018 8:23 AM I agree wholeheartedly with this proposal, and Randy's suggestions, FWIW. >... > >3) For task and access types, the string is implementation > dependent. For example, the image for any value of such > a type might be "<>". Or it might be something more useful. > As mentioned earlier, users can override. I wonder: In my opinion we want to make a lot of that implementation dependent (I mean it's good if there are some guide lines, but that's it). I don't think we want users to start relying on the output of 'Image as some kind of interchange format, so we should be clear on that in any way. I'm also wondering, and I know we discussed it live but I don't see any mention of that in your mail Steve: What about 'Value ? I think we said that 'Value won't be implemented for complex types, but it would be better to state it clearly I guess. If we don't implement 'Value, I would lean forward making most of the rules you mention implementation dependent (for example the one about named parameters in arrays), with the ARM just giving advice/guidelines/how do we call that again **************************************************************** From: Steve Baird Sent: Friday, January 25, 2018 11:49 AM > ... >> 3) For task and access types, the string is implementation >> dependent. For example, the image for any value of such >> a type might be "<>". Or it might be something more useful. >> As mentioned earlier, users can override. > > You have two #3s here. :-) > > For a task, one could imagine using the task id somehow. (At least, > that was my first thought.) Not completely useful (especially for > 'Value), but it would clearly allow telling tasks apart and would be useful > for debugging. I agree, Task_Identification.Image of the task id is what we want for tasks. Note that no changes involving 'Value are proposed in this AI. As Raphael points out, it would probably be good to mention this explicitly (i.e., to talk about what we are not talking about). I've omitted in this reply all of your subsequent 'Value-related comments. > For access values, one could mandate using the image of the associated > Integer_Address in, say square brackets. (That would be familiar to > anyone used to an Intel assembler.) Fair enough, although if we are going to require something then I'd rather see the address displayed in hex. >> 3) For untagged records, we use named-notation aggregate syntax >> (order of fields follows positional aggregate rules) >> >> type T is record Xx, Yy, Zz : Some_Type; end record; >> Obj : T := ... ; >> >> pragma Assert (Obj'Image = >> "(Xx => " & Obj.Xx'Image >> & ", Yy => " & Obj.Yy'Image >> & ", Zz => " & Obj.Zz'Image >> & ")" >> >> Ditto for specific tagged records; in particular, the >> tag value is not mentioned at all in that case. Nothing >> special for protected records. >> >> In the no-components case, image is "(null record)". > > One needs to specify the case of the component names in this > aggregate. I'd expect the assertion to fail, because those names will > probably be all UPPER CASE or all lower case -- but never mixed case. > (Most likely, copy the enumeration rule.) Good point. I agree - upper case. We certainly don't want the case that was used in the declaration(s) of the type to have any effect on the runtime behavior of a program. >> 5) What about arrays? It would be very verbose to use ... >> Named-notation syntax is probably the way to go, even though >> (as noted earlier) it can lead to a lot of redundant text in >> some cases. If we do that, then we'd need consistent syntax >> for the null array case (single- and multi-dimensional). > > You seem to have answered your own question here. And I tend to agree. > 'Image of large data structures is never going to be very useful (what > do you do with 20 pages of output), so it probably makes sense to just > use the syntax that provides the most information and least chance of > getting it wrong. It's worth noting that when we display an index value, we of course use the Image attribute of the index type. >> 6) Some images begin with leading blanks and some don't (that's >> already true, so we have to deal with both cases). Do we want >> to take any steps to avoid ugly double-blanks? For example, >> would the image of a record with two integers look like >> "(X => 123, Y => 456)" >> or like >> "(X => 123, Y => 456)" -- note the two additional spaces >> ? See the Assert pragma in item #3 above. > > Not sure here. It would be a pain to try to eliminate the leading > blanks for numeric 'Image, so I probably would say leave the extra > blanks. (Or maybe, skip putting a blank after "=>", as it will still > be syntactically correct either way.) One can always user-define it if you > need it perfect. Changing the behavior of numeric 'Image would be grossly incompatible; that seems out of the question to me. I was thinking more about looking for a leading space on a component image and conditionally preceding it with either a "=> " or a "=>" when generating the image of an enclosing composite object. === I was thinking some more about the Image/Wide_Image/Wide_Wide_Image rules. 90% of the time, all that anyone is interested in is the Image attribute and it is somewhat clunky to have to specify the Wide_Wide_Image aspect (or even mention Wide_Wide_String at all). But we want to retain consistency between the 3 image attributes; we don't want, for example, to allow users to explicitly specify Image and Wide_Image aspects for one type. I originally proposed one solution: only Wide_Wide_Image is specifiable. How about, instead, allowing any *one* of the three aspects Image, Wide_Image, Wide_Wide_Image to be specified? Whatever aspect is specified determines the behavior of all three attributes. [Alternatively, we could say that only Image is specifiable but the specified value can be a function returning any of the three predefined string types; there are advantages to this approach - we don't have to worry about defining what happens in a case like type T1 is ... with Wide_Image => ... ; type T2 is new T1 with Image => ... ; ]. **************************************************************** From: Steve Baird Sent: Friday, January 25, 2018 2:11 PM More thoughts on this. > It's worth noting that when we display an index value, we of course > use the Image attribute of the index type. "the Image attribute" => "the appropriate image attribute". If we are in the middle of generating, say, a Wide_Wide_String as part of evaluating Some_Array'Wide_Wide_Image, then we want to use Index_Type'Wide_Wide_Image (as opposed to Index_Type'Image). > I agree, Task_Identification.Image of the task id is what we want for > tasks. Discriminated task types should display the discriminant values somehow. Perhaps extension-aggregate-ish syntax? Something like (task with D1 => This, D2 => that) and then, for consistency, undiscriminated image is (task ) . Or perhaps we drop the word "task", but then the parens look odd in the undiscriminated task case. Do we drop them? > Nothing special for protected records. Presumably Some_Protected_Object'Image behaves like a call to a protected function with respect to locking. > Named-notation syntax is probably the way to go... > If we do that, then we'd need consistent syntax > for the null array case (single- and multi-dimensional). If we define syntax for null array aggregates, then we should use whatever is agreed upon. Otherwise, how about "(1 .. 0 => <>)" "(1 .. 3 => (1 .. 0 => <>))" "(1 .. 0 => (1 .. 3 => <>))" ? Or perhaps "()" instead of "<>". ==== It needs to be explicitly stated that Some_Object'Image is defined when the object is of a non-numeric type. **************************************************************** From: Randy Brukardt Sent: Friday, January 25, 2018 2:56 PM > I'm also wondering, and I know we discussed it live but I don't see any > mention of that in your mail Steve: What about 'Value ? I think we said that >'Value won't >be implemented for complex types, but it would be better to > state it clearly I guess. We decided only to consider 'Value separately. (And assigned that AI to Tucker.) We didn't decide yes or no on that. > If we don't implement 'Value, I would lean forward making most of the rules > you mention implementation dependent (for example the one about named > parameters in arrays), with the ARM just giving advice/guidelines/how do we > call that again That's undecided at this time. But my preference would be to leave the possibility open for the future, even if we decide not to do it this time. Therefore, I would prefer to lock down the formats for as much as possible. If we made them implementation-dependent, then we could never implement 'Value (nor could a user write their own portable 'Value). **************************************************************** From: Randy Brukardt Sent: Friday, January 25, 2018 3:06 PM ... > Note that no changes involving 'Value are proposed in this AI. > As Raphael points out, it would probably be good to mention this > explicitly (i.e., to talk about what we are not talking about). > > I've omitted in this reply all of your subsequent 'Value-related > comments. My point of mentioning 'Value in my comments is that I want the possibility of implementing that preserved as much as possible. So, if there is a syntax that would work for 'Value, and one that would not work for 'Value, I strongly lean toward the one that would allow 'Value. Note that "allowing 'Value" is the same as "allowing users to write a portable 'Value-like subprogram". I agree that whether or not we support 'Value this time (or ever) is not relevant to this AI. I think that is fair game to talk about whether or not the suggested format could support 'Value (or similar user-written routines). > > For access values, one could mandate using the image of the > > associated Integer_Address in, say square brackets. (That would be > > familiar to anyone used to an Intel assembler.) > > Fair enough, although if we are going to require something then I'd > rather see the address displayed in hex. The only issue with that is that we don't have a hex image routine anywhere. Integer_Address'Image is well-defined. ... > >> 6) Some images begin with leading blanks and some don't (that's > >> already true, so we have to deal with both cases). Do we want > >> to take any steps to avoid ugly double-blanks? For example, > >> would the image of a record with two integers look like > >> "(X => 123, Y => 456)" > >> or like > >> "(X => 123, Y => 456)" -- note the two additional spaces > >> ? See the Assert pragma in item #3 above. > > > > Not sure here. It would be a pain to try to eliminate the leading > > blanks for numeric 'Image, so I probably would say leave the extra > > blanks. (Or maybe, skip putting a blank after "=>", as it will still > > be syntactically correct either way.) One can always user-define it > > if you need it perfect. > > Changing the behavior of numeric 'Image would be grossly incompatible; > that seems out of the question to me. I agree, but I was not suggesting that. > I was thinking more about looking for a leading space on a component > image and conditionally preceding it with either a "=> " or a "=>" > when generating the image of an enclosing composite object. I think that is too much work, especially as it is value-dependent for Integer'Image. My suggestion was simply to always generate "=>" (with no trailing space), as that is lexically and syntactically correct, and it avoids the double spacing. Otherwise, just don't worry about that -- this is mainly for debugging anyway, perfection not required. > === > > I was thinking some more about the > Image/Wide_Image/Wide_Wide_Image rules. > > 90% of the time, all that anyone is interested in is the Image > attribute and it is somewhat clunky to have to specify the > Wide_Wide_Image aspect (or even mention Wide_Wide_String at all). > > But we want to retain consistency between the 3 image attributes; we > don't want, for example, to allow users to explicitly specify Image > and Wide_Image aspects for one type. > > I originally proposed one solution: only Wide_Wide_Image is > specifiable. > > How about, instead, allowing any *one* of the three aspects Image, > Wide_Image, Wide_Wide_Image to be specified? > Whatever aspect is specified determines the behavior of all three > attributes. [Alternatively, we could say that only Image is > specifiable but the specified value can be a function returning any of > the three predefined string types; there are advantages to this > approach - we don't have to worry about defining what happens in a > case like > type T1 is ... with Wide_Image => ... ; > type T2 is new T1 with Image => ... ; ]. Whatever the rule is, it can allow specifying only one. In this latter Bairdian case, I'd expect the second one to totally override all three. I'll leave the exact rule to you. **************************************************************** From: Steve Baird Sent: Friday, January 25, 2018 3:29 PM > So, if there is a syntax > that would work for 'Value, and one that would not work for 'Value, I > strongly lean toward the one that would allow 'Value. I agree. >>> For access values, one could mandate using the image of the >>> associated Integer_Address in, say square brackets. (That would be >>> familiar to anyone used to an Intel assembler.) >>> >> >> Fair enough, although if we are going to require something then I'd >> rather see the address displayed in hex. > > The only issue with that is that we don't have a hex image routine anywhere. > Integer_Address'Image is well-defined. Requiring (as opposed to allowing) something that isn't very useful would be bad. I'd rather leave it implementation dependent than to require decimal-format display for addresses. My first choice is requiring hex. > My suggestion was simply to always generate "=>" (with no trailing > space), as that is lexically and syntactically correct, and it avoids > the double spacing. Otherwise, just don't worry about that -- this is > mainly for debugging anyway, perfection not required. In a situation where one space would be ideal, I'd rather see two spaces than none. So I'd rather unconditionally generate "=> " instead of "=>" if those are the only two choices. Note that there is a similar issue with index values in array aggregates (and these can appear in at least 3 different contexts - after a left paren, a comma, or a ".."). > Whatever the rule is, it can allow specifying only one. In this latter > Bairdian case, I'd expect the second one to totally override all three. Agreed. **************************************************************** From: Randy Brukardt Sent: Friday, January 25, 2018 9:51 PM ... > >>> For access values, one could mandate using the image of the > >>> associated Integer_Address in, say square brackets. (That would be > >>> familiar to anyone used to an Intel assembler.) > >>> > >> > >> Fair enough, although if we are going to require something then I'd > >> rather see the address displayed in hex. > > > > The only issue with that is that we don't have a hex image routine > > anywhere. Integer_Address'Image is well-defined. > > > > Requiring (as opposed to allowing) something that isn't very useful > would be bad. On modern 32-bit machines, neither is very useful. Data point: When Janus/Ada got ported to 32-bit machines, no 32-bit hex value routine was available and someone took the lazy way out when programming the symbol table dump. The decimal numbers, as you say, aren't very useful. No one can memorize a ten-digit number, so looking for the values by hand is impossible, especially as they all look similar. And searching is not fun, simply because there is a lot of chance of error in typing a 10 digit number. So we changed to hex. And guess what? The effect is the same: looking for values by hand isn't possible in general, because memorizing an 8 digit number isn't substantially easier than 10. And typing an 8-digit hex number for searching is nearly as hard as a 10 digit decimal number (needing to use both numbers and letters rather than just the number pad). This gets even worse on modern OSes using address-space randomization, because the first few hex digits vary more than they used to. And also for 64-bit machines, where the number of digits is way too many to do anything with when reading (in either format). I gave up on both last winter and reprogrammed the routine to show the symbol record unit/id pair instead. This is usually a pair of 2 digit numbers (in test programs, anyway, they'd be longer in a full-sized program but luckily one rarely has to debug those without a smaller reproducer). That's much easier to look for manually (plus it gives some information about where to look), and easier to search for with a program (as it's shorter). The downside is that it requires dereferencing the pointer to be displayed, so a corrupt pointer can crash the dump -- but a corrupt pointer is a critical bug and the failing dump shows precisely where to look (wherever the dump stops), so that's hardly an issue for this use. Ergo, I'd expect most serious use of access Image to replace it with an user-defined version. For one-off debugging, what it is hardly matters (so long as values that are different can be recognized). Aside: should the image of a null access value be "null" or "[00000000]" (if hex) or "[ 0]" (if 'Image)?? **************************************************************** From: Jean-Pierre Rosen Sent: Saturday, January 27, 2018 1:28 AM > The only issue with that is that we don't have a hex image routine > anywhere. Integer_Address'Image is well-defined. If the (hex) image uses based notation, it can be fed back to 'Value **************************************************************** From: Tucker Taft Sent: Saturday, January 27, 2018 11:29 AM > ... > Discriminated task types should display the discriminant values somehow. > Perhaps extension-aggregate-ish syntax? Something like > > (task with D1 => This, D2 => that) > > and then, for consistency, undiscriminated image is > > (task ) . > > Or perhaps we drop the word "task", but then the parens look odd in > the undiscriminated task case. Do we drop them? I like the word "task" being there, and always using parentheses for composite types. > Nothing special for protected records. I would think you would have "(protected with disc1 => X, disc2 => Y, comp1 => Z, ...)" to be consistent with tasks, and to make it clear that this is not your normal record. > Presumably Some_Protected_Object'Image behaves like a call to a > protected function with respect to locking. > >> Named-notation syntax is probably the way to go... >> If we do that, then we'd need consistent syntax >> for the null array case (single- and multi-dimensional). > > If we define syntax for null array aggregates, then we should use > whatever is agreed upon. Otherwise, how about > > "(1 .. 0 => <>)" > > "(1 .. 3 => (1 .. 0 => <>))" > > "(1 .. 0 => (1 .. 3 => <>))" > ? Or perhaps "()" instead of "<>". "<>" seems better than "()" given the general Ada allergy to empty parentheses. **************************************************************** From: Steve Baird Sent: Wednesday, February 28, 2018 8:03 PM I still don't have real RM wording for this one, but here is a more detailed outline than what we had before for this AI, incorporating feedback from the last meeting and subsequent discussions with Bob and Tuck. [This is the !proposal section of version /02 of the AI - Editor.] First, a summary of what we plan for the default (as opposed to user-specified) implementations of T'Image for a possibly-nonscalar type T. Much of this has already been discussed, but there is some new stuff (e.g., the treatment of type extensions). The completely new stuff that has not previously been discussed on the ARG list is the discussion of the Put attribute in the second part of this message. Feedback, as always, is most welcome. **************************************************************** From: Tucker Taft Sent: Thursday, March 1, 2018 6:52 AM I preferred the proposal to include the words "task ..." or "protected ..." in the 'Image for such types. You wrote: For task types - Undiscriminated: "()" Discriminated: "(D1 => 123, D2 => 456, )" For protected types - Nothing special. Same as any record type. Protected_Obj'Image is a protected function (this matters, for example, for locking). Earlier I had seen a proposal of approximately: (task ) -- no discriminants (task with D1 => 123, D2 => 456) and for protected types, the analogous thing: (protected ) -- no discriminants or private components (protected with D1 => ... , C1 => ...) -- discriminants and/or components I think these have a nice symmetry, and will reduce confusion with other aggregates. Clearly protected objects have some kind of identity (typically some sort of internal semaphore ID), and that is something that would be of interest. Just dumping the value of the private components would be losing significant information. **************************************************************** From: Randy Brukardt Sent: Thursday, March 1, 2018 9:37 PM > Earlier I had seen a proposal of approximately: > > (task ) -- no discriminants > > (task with D1 => 123, D2 => 456) I think you made that proposal, so I'm pretty sure you saw it. ;-) But I agree with this, especially as a task id might just be a small integer which would make no sense without context. > and for protected types, the analogous thing: > > (protected ) -- no discriminants or private components > (protected with D1 => ... , C1 => ...) -- discriminants and/or components I agree with the "protected" part, but there is no such thing as a ; protected types are just records with a predefined blob used by the task supervisor. One could use the address if someone insisted on an , but this doesn't seem different than any other record type in that in some cases you might need to display the identity. In that case, you should dump the address as well as the image. So I don't think there should be any here. In the case of tasks, most of them have no other components so the task id is needed to even get an idea of the type. That's unlikely for protected objects. If we'd prefer consistency, I'd rather drop the id from the tasks rather than force a non-existent thing into the protected case. > I think these have a nice symmetry, and will reduce confusion with > other aggregates. > > Clearly protected objects have some kind of identity (typically some > sort of internal semaphore ID), and that is something that would be of > interest. Just dumping the value of the private components would be > losing significant information. I strongly disagree with this: Janus/Ada exposes none of this stuff even to the rest of the compiler. The compiler just sees a private type of a particular size. We'd have to define new task supervisor operations specifically for this purpose if we needed to expose something else -- but the "something else" doesn't even exist. The address of the protected object serves to identify it in any case that is needed. I'd be surprised if other compilers differed on this. These guys - particularly because they have discriminants - are much more like a record with some special operations when accessed than a task (which is an active structure). Perhaps there is some mutex that the underlying OS gives an Id to, but that has nothing to do with Ada. The idea of a Task_Id is well-defined in Ada; there's an entire section in the Standard talking about it. And that defines an Image function. That's what would appear in 'Image. Imagining a whole new mechanism (one that is nonexistent in at least some compilers) just for Image is waaaay over the top. **************************************************************** From: Tucker Taft Sent: Thursday, March 1, 2018 10:06 PM Perhaps we should always just include its address. Clearly two otherwise identical protected objects are still fundamentally distinct, since they each have their own specific lock. That is all I was trying to accomplish with the protected ID. *************************************************************** From: Randy Brukardt Sent: Thursday, March 1, 2018 11:24 PM OK, I guess, but isn't that fundamentally true of any limited type? You've often made the point that the object identity of a limited object is an important part of its abstraction. I was just wondering why protected objects ought to be special in this way. *************************************************************** From: Tucker Taft Sent: Friday, March 2, 2018 2:55 AM I agree this is true of all "immutably limited" types. But the large majority of immutably limited types are that because they contain a task or a protected object, so adding such an identifier to a protected object seemed to solve most of the problem. I wouldn't be averse to a more general solution, but I don't have a proposal for that. *************************************************************** From: Steve Baird Sent: Wednesday, March 28, 2018 6:57 PM Still no wording. Here are the results of some discussions I've had with Randy and Tuck since the summary I sent out a month ago. This includes both decisions (tentatively) made and questions raised. ==== 1) For a task type T, T'Image yields "(task )" or, if discriminants are involved, "(task with D1 => 123, D2 => 456)" 2) For a protected type, there is nothing analogous to the task stuff (because there is nothing analogous to a task_id for protected types). Protected types just get displayed like any other record. 3) The default implementation of Put for a protected type would be implemented as something like procedure Put (X : T; S : access Counted_Stream'Class) is Ignored : constant Dummy_Type := X. (S); begin null; end; where is an anonymous compiler-generated protected function. The idea here is to write out to the stream a consistent snapshot of the state of the protected record, using only one protected action to access all components. The definition given in the RM only needs to say that the default implementation of T'Image where T is a protected type involves calling a protected function. One could imagine making this protected function visible (perhaps via some new attribute), but let's wait until there is some demand for this. 4) Recall that in this new scheme [Wide_[Wide_]]Image is not a user-specifiable attribute. Instead, a new attribute (tentatively named Put) can be specified and Image and friends are defined in terms of Put. Do we want to name this new attribute "Put" or "Put_Image" ? 5) In the absence of a user-specified Put operation, should new discriminants for a derived untagged type have an effect on the image? Presumably yes (at least that's how the streaming attributes work). So the assertion in this example should succeed: type T1 (D1, D2 : Positive) is record ... end record; -- untagged type T2 (D : Positive) is new T1 (D1 => D, D2 => D); X : T2 (D => 123) := ... ; pragma Assert (X'Image /= T1(X)'Image); ***************************************************************