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

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

--- ai12s/ai12-0266-1.txt	2018/06/15 01:03:32	1.4
+++ ai12s/ai12-0266-1.txt	2018/06/16 02:09:59	1.5
@@ -1,4 +1,4 @@
-!standard 5.5.1(4/3)                                  18-06-14  AI12-0266-1/02
+!standard 5.5.1(4/3)                                  18-06-15  AI12-0266-1/03
 !standard 5.5.1(6/4)
 !standard 5.5.1(11/3)
 !standard 5.5.2(4/3)
@@ -46,18 +46,20 @@
 can be used for parallel iteration, but can also be used for sequential iteration
 for loops that do not have the parallel reserved keyword.
 
-A parallel iterator provides a Split function that can be used to return a
-cursor array that defines the chunk boundaries and cursors needed for iterating
-through the container in parallel, where each cursor in the array identifies
-the first cursor of a  different chunk of iterations that can be iterated through
-in parallel with other iteration chunks. Each cursor of the array can be
-obtained by calling the To_Cursor operation passing in the index of the chunk
-as a parameter to the call. The end of a chunk can be determined by calling the
-To_Cursor operation for the next highest chunk index. Calling To_Cursor with
-an index value higher than the number of elements in the cursor array returns
-a No_Element cursor. The idea is that the iteration for a chunk is complete when
-the current cursor gets advanced enough times to give the cursor value associated
-with the next highest index.
+A parallel iterator provides a Split procedure that can be used to associate
+with the iterator a set of cursors that define the chunk boundaries for parallel
+iteration. Each cursor in the array identifies the first element of a different
+chunk of iterations where each chunk can be iterated through in parallel with
+other chunks to collectively cover all the elements of the container.
+
+Each cursor of the array can be obtained by calling the Get_Cursor operation,
+passing in the index of the split as a parameter to the call. The end Cursor of
+an iteration chunk can be determined by calling the Get_Cursor operation for the
+next highest split index. Calling Get_Cursor with a split index value higher
+than the number of splits associated with the iterator returns in a No_Element
+cursor result. The idea is that the iteration for a chunk is complete when the
+current cursor gets advanced enough times to give the cursor value associated
+with the next highest split index.
 
 In addition, the loop syntax is extended to allow the loops with iterator
 specifications to have the parallel keyword.
@@ -72,22 +74,6 @@
 
 Add after 5.5.1(4/3)
 
-   type Cursor_Array is limited interface
-     -- with Nonblocking
-     ;
-
-   function Length
-     (Object  : Cursor_Array) return Natural is abstract;
-
-   function To_Cursor
-     (Object : Cursor_Array;
-      Index  : Natural) return Cursor is abstract;
-
-   procedure Set_Cursor
-     (Object : in out Cursor_Array;
-      Index : Natural;
-      Position : Cursor) is abstract;
-
    type Parallel_Iterator is limited interface and Forward_Iterator
       -- with Nonblocking
       ;
@@ -96,9 +82,24 @@
      (Object : Parallel_Iterator)
       return Ada.Containers.Count_Type is abstract;
 
+   subtype Cursor_Count is Positive;
+   subtype Split_Index is Positive;
+   use type Ada.Containers.Count_Type;
+
    procedure Split
-     (Object        : Parallel_Iterator;
-      Into          : out Cursor_Array'Class) is abstract;
+     (Object        : in out Parallel_Iterator;
+      Advised_Split : Cursor_Count) is abstract
+     with Pre'Class =>
+       Ada.Containers.Count_Type (Advised_Split) <= Object.Iterations,
+          Post'Class => Object.Split_Count <= Advised_Split;
+
+   function Split_Count
+     (Object  : Parallel_Iterator) return Cursor_Count is abstract;
+
+   function Get_Cursor
+     (Object : Parallel_Iterator;
+      Index  : Split_Index) return Cursor is abstract
+     with Pre'Class => Index <= Object.Split_Count + 1;
 
    type Parallel_Reversible_Iterator is limited interface
      and Parallel_Iterator and Reversible_Iterator
@@ -122,26 +123,15 @@
 associated instance of Ada.Iterator_Interfaces is the iteration cursor subtype
 for the iterator type.
 
-Add after 5.5.1(9.a/3)
-
-Iteraetor_Cursor_Array
-
-This aspect is specified by a name that denotes a subtype. This is the default
-cursor array subtype for T.
-
-Aspect Description for Iterator_Cursor_Array: Cursor array type to be used for
-parallel iteration.
-
 Modify 5.5.1(11/3)
 
 An iterable container type is an indexable container type with specified
 Default_Iterator and Iterator_Element aspects. A reversible iterable container
 type is an iterable container type with the default iterator type being a
 reversible iterator type. {A parallel iterable container type is an iterable
-container type with a specified Iterator_Cursor_Array aspect with the default
-iterator type being a parallel iterator type. A parallel reversible iterable
-container type is an iterable container type with a specified Iterator_Cursor_Array
-aspect with the default iterator type being a parallel reversible iterator type.}
+container type with the default iterator type being a parallel iterator type.
+A parallel reversible iterable container type is a reversible iterable container
+type with the default iterator type being a parallel reversible iterator type.}
 An iterable container object is an object of an iterable container type. A
 reversible iterable container object is an object of a reversible iterable
 container type. {A parallel iterable container object is an object of a parallel
@@ -155,10 +145,11 @@
 is a parallel iterator;} otherwise it is a forward iterator. In a reverse
 generalized iterator, the iterator_name shall be of a reversible iterator type.
 {In a parallel generalized iterator, the iterator_name shall be of a parallel
-iterator type.} In a reverse container element iterator, the default
-iterator type for the type of the iterable_name shall be a reversible iterator
-type. {In a parallel container element iterator, the default iterator type for
-the type of the iterable_name shall be of a parallel iterator type.}
+iterator type or a parallel reversible iterator type.} In a reverse container
+element iterator, the default iterator type for the type of the iterable_name
+shall be a reversible iterator type. {In a parallel container element iterator,
+the default iterator type for the type of the iterable_name shall be of a
+parallel iterator type or a parallel reversible iterator type.}
 
 Modify 5.5.2(7/5)
 
@@ -184,10 +175,11 @@
 Modify 5.5.2(8/3)
 In a generalized iterator, the loop parameter{s are} [is a] constant. In an
 array component iterator, the loop parameter{s are} [is a] constant if the
-iterable_name denotes a constant; otherwise [it]{they} denote[s] a variable.
+iterable_name denotes a constant; otherwise [it]{they} denote[s a] variable{s}.
 In a container element iterator, the loop parameter{s are}[is a] constant if the
 iterable_name denotes a constant, or if the Variable_Indexing aspect is not
-specified for the type of the iterable_name; otherwise it is a variable.
+specified for the type of the iterable_name; otherwise [it is a]{they are}
+variable.
 
 Modify AARM 5.5.2(8.a/5)
 
@@ -210,8 +202,8 @@
 Modify 5.5.2(10/3)
 
 For a generalized iterator, the loop parameter{s are} [is] created, the
-iterator_name is evaluated, and the denoted iterator object{s} become[s] the
-loop iterator{s}. In a forward generalized iterator, the operation First of the
+iterator_name is evaluated, and the denoted iterator object becomes the
+loop iterator. In a forward generalized iterator, the operation First of the
 iterator type is called on the loop iterator, to produce the initial value for
 the loop parameter. If the result of calling Has_Element on the initial value is
 False, then the execution of the loop_statement is complete. Otherwise, the
@@ -225,39 +217,32 @@
 
 Add after 5.5.2(10/3)
 
-For a parallel generalized iterator, the operation Iterations
-of the iterator type is called first to determine the number of iterations
-associated with the iterator. If the result of calling Iterations is 0, then the
-execution of the loop_statement is complete. Otherwise, a Cursor array object of
-the type specified by the Iterator_Cursor_Array aspect is declared, with the
-number of elements associated with the Cursor array object being set to a value
-determined by the implementation that indicates a recommendation for the number
-of loop parameter objects that should be created. The operation Split of
-the iterator type is then called.
-The result of the call to Split is the Cursor_Array
-object is assigned a set of cursor values indicating the initial cursor values
-to be uniquely associated with each loop parameter object. The number of loop
-parameters to be created is determined by calling Length operation of the
-Cursor_Array. The Initial cursor values to be associated with each loop parameter
-is determined by calling the To_Cursor operation of the Cursor_Array object using
-an ordinal index value of the loop parameter object as the Index parameter for the
-call. The sequence_of_statements is executed for each loop parameter object and
-then the Next operation of the iterator type is called with the loop iterator and
-the current value of the loop parameter to produce the next value to be assigned
-to a given loop parameter. This repeats until the value of the loop parameter is
-equal to the initial cursor value associated with the next highest next loop parameter,
-or if the Has_Element operation associated with the Iterator object returns False,
-or if the loop is left as a consequence of a transfer of control.
+For a parallel generalized iterator, the operation Iterations of the iterator
+type is called first to determine the number of iterations associated with the
+iterator. If the result of calling Iterations is 0, then the execution of the
+loop_statement is complete. Otherwise, the operation Split of the iterator type is then called,
+with the Advised_Split parameter specified as the upper bound for the number of
+loop parameter objects to be associated with the iterator. The number of loop
+parameters that were created is determined by calling the Split_Count operation
+of the iterator. The Initial cursor values to be associated with each loop
+parameter is determined by calling the Get_Cursor operation of the iterator
+using an ordinal split index value of the loop parameter object as the Index
+parameter for the call. The sequence_of_statements is executed for each loop
+parameter object and then the Next operation of the iterator type is called with
+the loop iterator and the current value of the loop parameter to produce the
+next value to be assigned to a given loop parameter. This repeats until the
+value of the loop parameter is equal to the initial cursor value associated with
+the next highest split index, or if the Has_Element operation associated with
+the Iterator object returns False, or if the loop is left as a consequence of a
+transfer of control.
 
 AARM Note
 
-The number of elements specified for the Cursor_Array object is only a
-recommendation by the implementation. A container implementation may choose to ignore that
-recommendation if a more optimal split can be determined. For instance, a tree
-based container might create the split based on the number of branches at the
-top levels of the tree. Any resulting elements of a Cursor_Array object that are
-not to be used should be to be set to the No_Element value associated with
-the Cursor type.
+The number of loop parameters specified for the Advised_Split parameter of the
+Split call is only a recommendation by the implementation. A container
+implementation may choose a lower value if a more optimal split can be
+determined. For instance, a tree based container might create the split based on
+the number of branches at the top levels of the tree.
 
 Modify AARM 5.5.2(10.a/4)
 
@@ -318,37 +303,24 @@
 For a parallel container element iterator, the operation Iterations is first
 called to determine the number of iterations associated with the iterator. If
 the result of calling Iterations is 0, then the execution of the loop_statement
-is complete. Otherwise, a Cursor array object of the type specified by the
-Iterator_Cursor_Array aspect is declared, with the number of elements
-associated with the Cursor array object being set to a value
-determined by the implementation that indicates a recommendation for the number
-of loop parameter objects that should be created. The operation Split of
-the iterator type is then called.
-
-The result of the call to Split is the Cursor_Array
-object is assigned a set of cursor values indicating the initial cursor values
-to be uniquely associated with each loop parameter object. The number of loop
-parameters to be created is determined by calling Length operation of the
-Cursor_Array. The Initial cursor values to be associated with each loop parameter
-is determined by calling the To_Cursor operation of the Cursor_Array object using
-an ordinal index value of the loop parameter object as the Index parameter for the
-call.
-The Initial cursor values to be associated with each loop parameter
-is determined by calling the To_Cursor operation of the Cursor_Array object using
-an ordinal index value of the loop parameter object as the Index parameter for the
-call.
-The sequence_of_statements is executed for each loop parameter object and then the
-Next operation of the iterator type is called with the loop iterator and the
-current value of the loop parameter to produce the next value to be assigned to
-a given loop parameter.
-This repeats until the value of the loop parameter is
-equal to the initial cursor value associated with the next highest next loop parameter,
-or if the Has_Element operation associated with the Iterator object returns False,
-or if the loop is left as a consequence of a transfer of control.
-
-}If the loop parameter is a constant  (see above), then the indexing uses the
-default constant indexing function for the type of the iterable container object
-for the loop; otherwise it uses the default variable indexing function.
+is complete. Otherwise, the operation Split of the iterator type is then called,
+with the Advised_Split parameter specified as the upper bound for the number of
+loop parameter objects to be associated with the iterator. The number of loop
+parameters that were created is determined by calling the Split_Count operation
+of the iterator. The Initial cursor values to be associated with each loop
+parameter is determined by calling the Get_Cursor operation of the iterator
+using an ordinal split index value of the loop parameter object as the Index
+parameter for the call. The sequence_of_statements is executed for each loop
+parameter object and then the Next operation of the iterator type is called with
+the loop iterator and the current value of the loop parameter to produce the
+next value to be assigned to a given loop parameter. This repeats until the
+value of the loop parameter is equal to the initial cursor value associated with
+the next highest next loop parameter, or if the Has_Element operation associated
+with the Iterator object returns False, or if the loop is left as a consequence
+of a transfer of control.} If the loop parameter is a constant  (see above),
+then the indexing uses the default constant indexing function for the type of
+the iterable container object for the loop; otherwise it uses the default
+variable indexing function.
 
 Modify 5.5.2(15/3)
 
@@ -664,17 +636,37 @@
 once through the container to obtain the cursors, but can still be benefit from
 parallel processing if the amount of time to process each node in the loop is
 significantly greater than the amount of time needed to iterate through the list.
+
+he container writer has the ability to implement the container to define
+an optimal number of loop parameters to be setup by the Split call, so long as
+the number of splits is not greater than the advised_split parameter.
 
-Originally, we had the Split operation as a function rather than a procedure.
-It was felt that returning a class-wide type as a function result was undesirable,
-and possibly inefficient. So the Split call was modified to be a procedure.
-However, that means the real cursor array type needs to be declared before calling
-split, and since the compiler is to be making this call, there needed to be a
-way to tell the implementation which type should be used to create the cursor array
-object. The solution to this problem was to create a Interator_Cursor_Array
-aspect, similar to the Iterator_Element aspect, that allows the container implementer
-to specify the cursor array type to be passed to the Split call for parallel iteration.
+It would have been nice to specify a post condition for the Split call something
+like;
 
+with Post => (for all I in 1 .. Object.Split_Count =>
+                 Has_Element (Object.Get_Cursor (I)))
+
+However, this is not allowed because Has_Element cannot be called during
+a post condition in this package because the Cursor formal type is an
+untagged incomplete type.
+
+Similarly, it would have been nice to write a Post condition for Get_Cursor
+where calling Has_Element on the result would return true, however this is also
+not allowed for the same reason.
+
+Another Post condition that would have been nice would be to add to Get_Cursor,
+
+ with Post => (if Index = Object.Split_Count + 1 then Has_Element (Get_Cursor'Result) = False,
+
+ but Has_Element cannot be called here, and we would need to add No_Element as a
+ formal object to the package, which would break compatibility.
+
+ We could consider adding these as Pre and Post contracts in the derived types
+ in each of the containers, but the iterator types are not declared in the
+ public part of the package, so it seems to be not worthwhile to do so.
+
+
 !ASIS
 
 ** TBD.
@@ -840,3 +832,361 @@
 Do you need anything else?
 
 ****************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, June 14, 2018   8:10 PM
+
+> > A question I have about this approach, is it enough to use the
+> > Iterator_Cursor_Array aspect to tell the compiler how to set up the
+> > cursor array object?
+>
+> Let's start further back. Why do we need a Iterator_Cursor_Array
+> object at all?
+
+Sorry, I meant Cursor_Array. I couldn't convince Outlook to let me view your
+AI update in anything other than Notepad (which just shows a giant block of
+text; it doesn't recognize Linux line endings) so I had to go from a quick
+scan of your AI when I wrote this this afternoon (hoping for quick
+turnaround). Now that I've posted it, I see what I did wrong here.
+
+...
+> So, what is the separate object gaining over just putting the
+> Get_Cursor call directly on the iterator object (the implementation of
+> which is totally user-controlled, so it could have an array of cursors
+> internally if it wants)?
+
+Answering my own question based on your most recent wording: I don't see
+any. If we let the iterator implementor create a (possibly virtual) array
+inside their implementation, we don't need any explicit object. It seems to
+make *everything* simpler (no strange new aspect, no questions about
+where/when the object gets declared, less to talk about in how the iterator
+executes, etc.)
+
+> The "user" of this structure is the parallel loop, and it obviously
+> has an iterator object it is going to use to iterate. Why does it need
+> anything more??
+
+The actual implementation probably has to give a start/end cursor to each
+thread when it is given work, but the format of that should be 100% up to the
+implementation. The less we specify, the more likely it is that an implementer
+can optimize it and create the fastest possible code.
+
+> So I would suggest that Split is just a call to tell the iterator how
+> to organize it's internal data structure. Still need the Advised_Split
+> parameter, something like:
+>
+>    procedure Split
+>      (Object        : in out Parallel_Iterator;
+>       Advised_Split : in Natural) is abstract;
+>       -- Tell Object to create split data with the advised number of splits.
+
+Note that the internal cursor array (inside of a Parallel_Iterator'Class
+object) could be fixed length (meaning that no more splits than the max number
+is supported) or it could be dynamically allocated. I don't see much reason to
+support massive numbers of splits anyway, certainly not to require it. (Perhaps
+we might give a minimum number in Implementation Advice?)
+
+>    function Split_Length (Object : in Parallel_Iterator) return
+> Natural is abstract;
+>       -- Determine the actual split length.
+
+We'd need say what happens here if procedure Split hasn't been called. I'd
+suggest saying that it returns 1.
+
+>    function Split_Cursor (Object : in Parallel_Iterator;
+>                           Split_Index : in Natural) return Cursor;
+>
+> or, since it would save an assignment in many implementations:
+>
+>    procedure Split_Cursor (Object : in Parallel_Iterator;
+>                            Split_Index : in Natural;
+>                            Result : out Cursor);
+>
+> Either of these would have the semantics you suggested yesterday
+> (chunk ends with the 'Succ index, get No_Element if Split_Index >=
+> Length).
+
+Again, if Split hasn't been called, the first cursor is Object.First, and the
+rest are No_Element.
+
+> Do you need anything else?
+
+I don't think so, based on my reading of your current text.
+
+Assuming you concur, if you could update the AI this way before the deadline,
+it would help progress it in Lisbon.
+
+****************************************************************
+
+From: Brad Moore
+Sent: Thursday, June 14, 2018   10:54 PM
+
+Thanks Randy, the idea of hiding the cursor array inside the iterator hadn't
+occurred to me, but it looks like it does simplify things a lot, as well as
+gives more flexibility to the implementation.
+
+I will attempt to update the AI to include these ideas.
+
+****************************************************************
+
+From: Brad Moore
+Sent: Friday, June 15, 2018   11:22 AM
+
+>      -- Determine the actual split length.
+>
+>   function Split_Cursor (Object : in Parallel_Iterator;
+>                          Split_Index : in Natural) return Cursor;
+>
+> or, since it would save an assignment in many implementations:
+>
+>   procedure Split_Cursor (Object : in Parallel_Iterator;
+>                           Split_Index : in Natural;
+>                           Result : out Cursor);
+>
+
+
+I am thinking of going with the function version, because
+
+a) It is easier to work with, as it possibly saves having to declare two
+objects, since the client needs both a start and end cursor, and
+
+b) Because I suspect compilers would typically apply a transformation of
+the users code to a procedure call accepting two cursors. The function results
+can be inline expressions for the parameters to the call, so I dont think we'd
+be saving an assignment in that case. Otherwise you have to assign to the
+declared cursor variables, and then assign again to the parameters of the
+call.
+
+eg.
+
+      --  2% Raise to everyone!
+      parallel
+      for Employee of List loop
+          Employee.Salary := @ + @ * 0.02;
+      end loop;
+
+might get transformed to something like;
+
+   declare
+      procedure Give_Raise
+        (From, To    : Employee_Lists.Cursor)
+      is
+         Position : Employee_Lists.Cursor := From;
+         use type Employee_Lists.Cursor;
+      begin --  Get_Payroll
+
+         while Position /= To loop
+
+            --  2% Raise to everyone!
+            List (Position).Salary := @ + @ * 0.02;
+            Employee_Lists.Next (Position);
+         end loop;
+
+      end Give_Raise;
+
+      Iterator : Emp_List_Iterators.Parallel_Reversible_Iterator'Class :=
+        List.Iterate;
+
+   begin
+
+      Iterator.Split (Advised_Split => 4);
+
+      parallel
+      for Chunk in 1 .. Iterator.Split_Count loop
+         Give_Raise (From => Iterator.Get_Cursor (Chunk),
+                     To   => Iterator.Get_Cursor (Chunk + 1));
+      end loop;
+
+   end;
+
+****************************************************************
+
+From: Brad Moore
+Sent: Friday, June 15, 2018   11:43 AM
+
+> Note that the internal cursor array (inside of a
+> Parallel_Iterator'Class
+> object) could be fixed length (meaning that no more splits than the
+> max number is supported) or it could be dynamically allocated. I don't
+> see much reason to support massive numbers of splits anyway, certainly
+> not to require it. (Perhaps we might give a minimum number in
+> Implementation Advice?)
+
+The number of splits should typically be at least as many as the number of
+available cores. For some platforms that number might be unknown.
+For example, for IRTAW, I was interacting with a couple of people from the
+Barcelona supercomputing center, where they were experimenting with
+parallelism on 50 core computers. They managed to get my Paraffin code
+running on that system, so I think it was a Linux based system.
+
+I think storing the cursor array in the iterator for such a system would
+suggest that the cursor array would needs to be dynamically allocated, (by
+the Split call). As otherwise, the iterator object could become a very large
+object to accomodate the largest possible number of cores, and that might
+cause troubles if declared on a stack. If you know your target platform only
+supports a small number of cores, then a fixed size array could be
+appropriate. Another advantage of dynamically allocating is that if the Split
+function is not called, (for example for a non-parallel loop), then nothing
+needs to be freed upon finalization of the iterator, and no storage is needed
+for the cursor array during the lifetime of the iterator object.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, June 15, 2018   2:37 PM
+
+I mentioned using a statically sized array because this has to work in the
+bounded forms, where dynamic allocation and finalization is strongly
+discouraged. Perhaps there is some way to use the secondary stack in GNAT
+for this allocation, but otherwise the size needs to be known early.
+
+I thought that a 64 cursor array probably would be enough for most existing
+targets, and the size of that (128 words) is not going to be so large as to
+cause stack problems. There's probably going to be memory use issues if
+someone has 10,000 cores, but that's mostly theoretical (and I've read that
+the number of cores is likely to be limited by memory contention issues).
+
+For the unbounded forms, it's probably better to use dynamic allocation, but
+obviously that's up to the implementation.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, June 15, 2018   2:50 PM
+
+> I am thinking of going with the function version, because
+>
+> a) It is easier to work with, as it possibly saves having to declare
+> two objects, since the client needs both a start and end cursor, and
+
+I thought the compiler would declare them somewhere anyway. In any case,
+having thought on it more, I doubt that there would be any difference since
+the objects in question are compiler-generated and thus it is not necessary
+to preserve Ada semantics for their assignments.
+
+> b) Because I suspect compilers would typically apply a transformation
+> of the users code to a procedure call accepting two cursors. The
+> function results can be inline expressions for the parameters to the
+> call, so I dont think we'd be saving an assignment in that case.
+> Otherwise you have to assign to the declared cursor variables, and
+> then assign again to the parameters of the call.
+
+I expected that the "manager" (the main thread) would set of an array of "job"
+records, and the procedure call would simply pass one of the job records. For
+this sort of iterator, the job would contain the two cursors.
+
+Note that you're not really saving anything by directly passing cursors this
+way. Cursors don't fit in a parameter on most targets, so they're probably
+getting passed by reference. So there has to be a temporary object declared
+somewhere to allow that.
+
+If you're generating this expansion at the intermediate code level (as we
+would do in Janus/Ada), you have declare all of these things explicitly anyway
+(nothing implicit in intermediate code!). So while there might be a small
+convinience when using a library (like Paraffin), it's irrelevant to an
+implementation. (At least some compilers implement functions like this as
+procedure calls anyway, so there isn't any difference at the implementation
+level - unless an extra temporary is needed to meet Ada semantics, as in an
+assignment.)
+
+****************************************************************
+
+From: Brad Moore
+Sent: Friday, June 15, 2018   2:14 PM
+
+Here is an update to AI12-266, Container Iterators [This is version /03 of the
+AI - Editor.]
+
+The main changes are to eliminate the cursor_array interface, and instead
+imply that a cursor array or set of cursors is stored in the iterator object,
+when Split is called. This approach overall is a lot simpler than the previous
+version. There is no need for the Iterator_Cursor_Array aspect that the '
+previous proposal had.
+
+This gives the container implementor more freedom in designing data structures
+for storing the loop parameters needed for parallel iteration.
+
+I also changed the name of the calls, from To_Cursor to Get_Cursor, and from
+Length to Split_Count, which I think adds clarity.
+
+Also, instead of using Natural as the subtype for split indexes and cursor
+counts, I defined
+
+   subtype Cursor_Count is Positive;
+   subtype Split_Index is Positive;
+
+Which I think also adds clarity and ties the calls Split, Split_Count,
+Get_Cursor, together more meaningfully.
+
+Finally, I also added some pre and post conditions which help to make it
+clearer how these calls work.
+
+I would have liked to specify post conditions that check that
+Has_Element(Get_Cursor (I)) for all I in 1 .. Object.Split_Count is true
+after calling Split, or a post condition for Get_Cursor such as Post =>
+(if Index = Object.Split_Count + 1 then Has_Element(Get_Cursor'Result) = False
+
+But the compiler tells me that one cannot use Has_Element in a Pre or Post
+condition in this package because the call involves a non tagged incomplete
+type (Cursor)
+
+I suppose we could add these as Pre and Post contracts to each of the
+containers but the iterator types are not visible in the public part of the
+package, so it seems not worthwhile to do so.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, June 15, 2018   9:07 PM
+
+> Here is an update to AI12-266, Container Iterators
+> 
+> The main changes are to eliminate the cursor_array interface, and 
+> instead imply that a cursor array or set of cursors is stored in the 
+> iterator object, when Split is called. This approach overall is a lot 
+> simpler than the previous version.
+> There is no need for the Iterator_Cursor_Array aspect that the '
+> previous proposal had.
+
+Thanks, this seems a lot simpler.
+
+For the record, I one editorial fix.
+
+Part of the interface referenced the package "Ada202x". I presume this is
+something you used in your testing, since there is no such thing proposed
+here or anywhere else (nor did it appear in the previous version of the AI).
+So I replaced all of those with "Ada".
+
+
+I'd probably add a bit of discussion similar to our recent back-and-forth on 
+how the cursor array could get implemented in the iterator object. Some people
+might be concerned about the apparent need for dynamic allocation, and that 
+should explicitly addressed.
+
+...
+> I would have liked to specify post conditions that check that 
+> Has_Element(Get_Cursor (I)) for all I in 1 ..
+> Object.Split_Count is true after calling Split, or a post condition 
+> for Get_Cursor such as Post => (if Index = Object.Split_Count + 1 then 
+> Has_Element(Get_Cursor'Result) = False
+> 
+> But the compiler tells me that one cannot use Has_Element in a Pre or 
+> Post condition in this package because the call involves a non tagged 
+> incomplete type
+> (Cursor)
+
+That sounds correct. Perhaps the rule is a bit more restrictive than it needs 
+to be (one could allow the calls and then require a recheck for an instance - 
+an assume-the-best rule), but changing that doesn't sound pleasant.
+
+> I suppose we could add these as Pre and Post contracts to each of the 
+> containers but the iterator types are not visible in the public part 
+> of the package, so it seems not worthwhile to do so.
+
+Don't see the point. The most important use of such Pre/Post is to eliminate 
+the need to describe these restrictions and results in English. And they 
+wouldn't apply to user-defined iterators if you only put them into the 
+container.
+
+****************************************************************
+

Questions? Ask the ACAA Technical Agent