CVS difference for ai12s/ai12-0020-1.txt

Differences between 1.4 and version 1.5
Log of other versions for file ai12s/ai12-0020-1.txt

--- ai12s/ai12-0020-1.txt	2018/02/22 07:06:14	1.4
+++ ai12s/ai12-0020-1.txt	2018/03/02 04:06:49	1.5
@@ -1,4 +1,4 @@
-!standard 6.1.1(0/3)                                 12-02-14    AI12-0020-1/01
+!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
@@ -9,9 +9,230 @@
 
 **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: "(<task_id_image>)"
+      Discriminated: "(D1 => 123, D2 => 456, <task_id_image>)"
+   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
+         "(<parent part image> 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.
+
+====
 
-Ada should provide 'Image for record and array types, using an aggregate format.
+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
+  (<arg> : 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 (<arg> : 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 (<arg>, 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 (<stream arg>) is equivalent to T'Put (X, <stream arg>).
+
+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
+      (<arg> : Scalar_Type; Stream : access Counted_Stream'Class) is
+       Wide_Wide_Image : constant Wide_String := <as described in RM>;
+    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 (<arg> : Record_Type; Stream : access Counted_Stream'Class) is
+   begin
+      Wide_Wide_String'Write ("(", Stream);
+      Wide_Wide_String'Write ("C1 => ", Stream);
+      <arg>.C1'Put (Stream)
+      Wide_Wide_String'Write (", C2 => ", Stream);
+      <arg>.C1'Put (Stream)
+      Wide_Wide_String'Write (", C3 => ", Stream);
+      <arg>.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 (<arg> : Array_Type; Stream : access Counted_Stream'Class) is
+   begin
+      Wide_Wide_String'Write ("(", Stream);
+      if <arg>'Length = 0 then
+         <arg>'First'Put (Stream);
+         Wide_Wide_String'Write (" .. ", Stream);
+         <arg>'Last'Put (Stream);
+         Wide_Wide_String'Write (" => <>", Stream);
+      else
+         for Idx in <arg>'Range loop
+           Idx'Put (Stream);
+           Wide_Wide_String'Write (" => ", Stream);
+           <arg>(Idx)'Put (Stream);
+           if Idx /= <arg>'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
 
@@ -963,3 +1184,120 @@
 
 ****************************************************************
 
+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: "(<task_id_image>)"
+      Discriminated: "(D1 => 123, D2 => 456, <task_id_image>)"
+   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 <task_id_image>) -- no discriminants
+
+   (task <task_id_image> with D1 => 123, D2 => 456)
+
+and for protected types, the analogous thing:
+
+   (protected <protected_id_image>)  -- no discriminants or private components
+   (protected <pid> 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 <task_id_image>) -- no discriminants
+> 
+>    (task <task_id_image> 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 <protected_id_image>)  -- no discriminants or private components
+>    (protected <pid> with D1 => ... , C1 => ...) -- discriminants and/or components
+
+I agree with the "protected" part, but there is no such thing as a
+<protected_id>; protected types are just records with a predefined blob used 
+by the task supervisor. One could use the address if someone insisted on an 
+<id>, 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 
+<protected_id> 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.
+
+***************************************************************

Questions? Ask the ACAA Technical Agent