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

Differences between 1.2 and version 1.3
Log of other versions for file ai12s/ai12-0250-1.txt

--- ai12s/ai12-0250-1.txt	2018/10/19 05:59:55	1.2
+++ ai12s/ai12-0250-1.txt	2018/12/07 04:39:57	1.3
@@ -1,4 +1,4 @@
-!standard 5.5(4)                                     18-01-25  AI12-0250-1/01
+!standard 5.5(4)                                     18-12-06  AI12-0250-1/02
 !class Amendment 18-01-25
 !status work item 18-01-25
 !status received 18-01-24
@@ -7,7 +7,8 @@
 !subject Iterator Filters
 !summary
 
-** TBD.
+The syntax "when <condition>" may be used in an iterator to filter out elements
+which do not satisfy the specified condition.
 
 !problem
 
@@ -21,14 +22,30 @@
 semantics.  For example, if you want a set made of all of the odd elements of
 another container, you would want to write something like the following:
 
-  S : constant Set := (for E of C when E mod 2 = 1 => E);
+  S : constant Set := [for E of C when E mod 2 = 1 => E];
 
 That is, you want to *filter* out the elements for which the predicate "E mod 2
 = 1" is False.  This is useful in other contexts as well, for example in a
 parallel loop, it clarifies that the elements that don't pass the filter
-probably won't take any significant amount of computing, so can be ignored for
+probably won't take any significant amount of computing, so could be ignored for
 the purposes of spreading the loop across multiple threads.
 
+Here are further examples:
+
+  Odd_Elements_Are_Purple : constant Boolean :=
+     (for E of C when E mod 2 = 1 => Is_Purple(E));
+
+  type Pair is record
+     Key : Integer;
+     Val : Integer;
+  end record;
+
+  A : array (Positive range <>) of Rec :=
+         [(Key => 55, Val => 77), (Key => 1, Val => 88)];
+
+  M : constant Map := [for E of A when E.Val mod 2 = 1 use E.Key => E.Val];
+  --  M = [55 => 77];
+
 Note that we already check against the predicates of the discrete_subtype in a
 loop_parameter_specification or iterated_component_association, so this would
 merely "and" the filter condition with the predicates, if any, so we already
@@ -37,25 +54,238 @@
 
 !proposal
 
-Allow an iterator to include a filter of the form:  "when <condition>" which
+Allow an iterator to include a filter of the form: "when <condition>" which
 would cause the iterator to bypass the "body" of the loop or the "expression" of
 the iterated construct when the filter evaluates to False for the given value of
 the loop parameter.
 
-We would define a "static filter" as one that obeys essentially the same rules
-as a static predicate, roughly that it would be static if the loop parameter
-were replaced with a static expression.  For filters that accompany
-loop_parameter_specifications or iterated_component_associations, non-static
-filters would be permitted only in places where discrete subtypes with dynamic
-predicates are permitted.
+Note that this AI affects AI12-0212-1, and it is relative to rev 1.19 of that
+AI, which in the header is described as "18-11-19  AI12-0212-1/08."
 
 !wording
+
+In AI12-0212-1, change the !wording section as follows (as indicated by "[...]"
+and "{...}"):
+
+Add before 4.3.3(21):
+  For an array_aggregate that contains only
+  array_component_associations that are iterated_component_associations
+  with iterator_specifications, evaluation proceeds in two steps:
+
+  1. Each iterator_specification is elaborated (in an arbitrary order)
+     and an iteration is performed solely to determine a {maximum} count
+     {for} [of] the number of values produced by the iteration; all of these
+     counts are combined to determine the {maximum} overall length of the
+     array, and ultimately {the limits on} the bounds of the array
+     (defined below);
+
+  2. A second iteration is performed for each of the iterator_specifications,
+     in the order given in the aggregate, and for each value produced,
+     the associated expression is evaluated, its value is converted to
+     the component subtype of the array type, and used to define the
+     value of the next component of the array starting at the low bound
+     and proceeding sequentially toward the high bound. A check is made
+     that the second iteration results in [the same} {an} array length
+     {no greater than the maximum determined by the first iteration};
+     Constraint_Error is raised if this check fails.
+
+     AARM To Be Honest: Constraint_Error should be raised no later than
+     when the iterations exceed the [expected] {maximum} array length;
+     memory that doesn't belong to the aggregate temporary should not be
+     overwritten.
+
+.....
+
+Add after 4.3.3(26):
+   * For a named_array_aggregate containing only
+     iterated_component_associations with an iterator_specification, the
+     lower bound is determined as for a positional_array_aggregate without
+     an OTHERS choice, and the upper bound is determined from the lower
+     bound and the total number of values produced by the {second} iteration(s);
+
+...
+
+Add after 4.3.3(31):
+
+  Implementation Permissions
 
-** TBD.
+  When evaluating iterated_component_associations for an array_aggregate
+  that contains only iterated_component_associations with
+  iterator_specifications, the first step of evaluating a
+  iterated_component_association can be omitted if the implementation can
+  determine the {maximum} number of values by some other means.
+
+  AARM Discussion: For instance, if the type of the aggregate is constrained,
+  the implementation can (but does not have to) calculate the expected length
+  from the constraint.
+
+  ...
+
+ 4.3.5 Legality Rules
+
+  ...
+
+   For a named_container_aggregate that is an indexed aggregate, all
+   container_element_associations shall contain either a
+   key_choice_list, or a loop_parameter_specification without a
+   /key_/expression {or an iterator_filter}. Furthermore, for such an
+   /aggregate, either:
+
+[End of changes to !wording of AI12-0212-1]
+
+Modify RM 5.5(4):
+
+ loop_parameter_specification ::=
+   defining_identifier in [reverse] discrete_subtype_definition
+     [iterator_filter]
+
+Add after RM 5.5(4):
+ iterator_filter ::= when condition
+
+Add before RM 5.5(7/5):
+  An iterator_filter is defined to be /satisfied/ when the condition
+  evaluates to True for a given iteration of a
+  loop_parameter_specification, iterator_specification, or
+  procedural_iterator.  The term /conditionally executed/ when referring
+  to the sequence_of_statements of a loop_statement means that the
+  statements are executed only when there is no iterator_filter, or the
+  iterator_filter is satisfied.  In contexts where a
+  loop_parameter_specification or iterator_specification is used to
+  produce a sequence of values (see 4.3.3 and 4.3.5), if an
+  iterator_filter is present, the sequence of values will contain only
+  the values for which the iterator_filter is satisfied.
+
+Modify RM 5.5(9/5):
+  For the execution of a loop_statement with the iteration_scheme being
+  for loop_parameter_specification, the loop_parameter_specification is
+  first elaborated. This elaboration creates the loop parameter and
+  elaborates the discrete_subtype_definition. If the
+  discrete_subtype_definition defines a subtype with a null range, the
+  execution of the loop_statement is complete. Otherwise, the
+  sequence_of_statements is {conditionally} executed once for each value
+  of the discrete subtype defined by the discrete_subtype_definition
+  that satisfies the predicates of the subtype ...
+
+Modify RM 5.5(9.1/3):
+  [Redundant: For details about the execution of a loop_statement with
+  the iteration_scheme being for iterator_specification, see 5.5.2.
+  {For details relating to a procedural_iterator, see 5.5.3.}]
+
+Modify RM 5.5(10):
+  5  A loop parameter {declared by a loop_parameter_specification} is a
+     constant; it cannot be updated within the sequence_of_statements of
+     the loop (see 3.3).
+
+Replace RM 5.5.2(2/5) with:
+  iterator_specification ::=
+    defining_identifier [: loop_parameter_subtype_indication]
+      in [reverse] iterator_name
+        [iterator_filter]
+  | defining_identifier [: loop_parameter_subtype_indication]
+      of [reverse] iterable_name
+        [iterator_filter]
+
+Modify RM 5.5.2(10/3):
+  ... Otherwise, the sequence_of_statements is {conditionally} executed
+  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 the loop parameter. ...
+
+Modify RM 5.5.2(11/3):
+  ...  Otherwise, the sequence_of_statements is {conditionally} executed
+  with the loop parameter denoting each component of the array for the
+  loop, using a canonical order of components, which is last dimension
+  varying fastest (unless the array has convention Fortran, in which
+  case it is first dimension varying fastest). For a forward array
+  component iterator, the iteration starts with the component whose
+  index values are each the first in their index range, and continues in
+  the canonical order. For a reverse array component iterator, the
+  iteration starts with the component whose index values are each the
+  last in their index range, and continues in the reverse of the
+  canonical order. The loop iteration proceeds until the
+  sequence_of_statements has been {conditionally} executed for each
+  component of the array for the loop, or until the loop is left as a
+  consequence of a transfer of control.
+
+Modify RM 5.5.2(13/3):
+  ... Otherwise, the sequence_of_statements is {conditionally} executed
+  with the loop parameter denoting an indexing (see 4.1.6) into the
+  iterable container object for the loop, with the only parameter to the
+  indexing being the current value of the loop cursor; then the Next
+  operation of the iterator type is called with the loop iterator and
+  the loop cursor to produce the next value to be assigned to the loop
+  cursor. ...
+
+Modify RM 5.5.3(2/5):
+  procedural_iterator ::=
+     iterator_parameter_specification of iterator_procedure_call
+       [iterator_filter]
+
+Modify RM 5.5.3(14/5):
+  A loop_statement with an iteration_scheme that has a
+  procedural_iterator is equivalent to a local declaration of a
+  procedure P followed by a procedure_call_statement that is formed from
+  the iterator_procedure_call by replacing the <> of the
+  parameter_association_with_box with P'Access. The formal_part of the
+  locally declared procedure P is formed from the formal_part of the
+  anonymous access-to-procedure type A, by replacing the identifier of
+  each formal parameter of this formal_part with the identifier of the
+  corresponding formal parameter or element of the list of identifiers
+  given in the iterator_parameter_specification.  {The body of the locally
+  declared procedure P follows this structure:
+
+      procedure P ... is
+      begin
+         if (iterator_filter is absent or satisfied) then
+            sequence_of_statements
+         end if;
+      end P;
+
+  AARM Implementation Note: Any transfers of control out of the
+  sequence_of_statements, presuming the callable entity "C" has Allows_Exit
+  True (see below), would be transformed to some kind of special exception
+  that only the implementation can handle and can use to implement the
+  desired transfer of control.
+}
 
 !discussion
 
-** TBD.
+Some uses of filters are merely "nice to have," but when constructing an
+aggregate or performing a reduction, it is more important, in that trying to
+construct a new aggregate (or perform a reduction) from a subset of the elements
+of an existing container cannot be easily accomplished without something like a
+filter. For the reduction, it would be possible to use an "if" expression and
+substitute in the identity of the reduction, but the resulting construct is
+almost certainly harder to read and understand.  But for an aggregate, an "if"
+expression does not have the right semantics (as mentioned in the !problem
+above).
+
+Note that we do not propose to allow a filter on an array aggregate with a
+"simple" discrete range such as:
+
+     [for I in 1..10 => I * 2]
+
+This is because the values of the index parameter ("I" in this case) are taken
+directly as the array indices, and a filter would not make sense -- no holey
+arrays please! By contrast, when a container iterator is used within an array
+aggregate, the iterator just produces a sequence of values and the index within
+the array is determined by position within this sequence of values. For a
+container aggregate, we have similar restrictions on "indexed" aggregates --
+these are effectively user-defined array aggregates and have nearly identical
+semantics (and restrictions). For non-indexed container aggregates, we always
+allow filters, since these do not require full coverage over a contiguous key
+range.
+
+Note that we do not define the notion of a "static" filter. There seems less
+reason to introduce the distinction between "static" and "dynamic" for a filter,
+analogous to what we have done for predicates, because the filter is explicit
+syntax in the iterator, and there is no promise that the overall iteration will
+somehow magically and statically "skip over" the filtered-out elements. The
+presumption with a filter is that you generate all of the elements of the
+iteration, and then ignore those filtered out. With a loop over a predicated
+subtype, it was felt that a loop over a dynamically-predicated subtype might
+have misleading performance characteristics if there were an assumption we might
+magically "visit" only the valid elements of the subtype.
 
 !ASIS
 
@@ -196,19 +426,42 @@
 From: Randy Brukardt
 Sent: Thursday, October 18, 2018  2:40 PM
 
-We didn't discuss it last time, so there was no guidance or obvious need for 
-an update. It had a fairly low priority, if I recall correctly (recall our 
-priority votes of last spring). (Checking on this.) Yup, it's behind "Image 
-for all types", "Marshalling streams (split from the previous), "Declare 
-expressions", "Anonymous functions", "User-defined literals", "Access 
+We didn't discuss it last time, so there was no guidance or obvious need for
+an update. It had a fairly low priority, if I recall correctly (recall our
+priority votes of last spring). (Checking on this.) Yup, it's behind "Image
+for all types", "Marshalling streams (split from the previous), "Declare
+expressions", "Anonymous functions", "User-defined literals", "Access
 ownership", and "Bignums" (as well as all of the AIs that are directly
 associated with the instructions, and practically the "simple" AIs as well).
-I know that we didn't talk about access ownership or bignums in Lisbon, so 
+I know that we didn't talk about access ownership or bignums in Lisbon, so
 we couldn't have discussed 250, either.
 
-Of these, I would hope to complete all of them (with the possible exception 
-of "Access ownership"), so I'd expect it to get the top eventually, but 
+Of these, I would hope to complete all of them (with the possible exception
+of "Access ownership"), so I'd expect it to get the top eventually, but
 whether that will happen in time for it to make it into Ada 2020 is TBD.
 
 ****************************************************************
 
+From: Tucker Taft
+Sent: Thursday, December 6, 2018  4:10 PM
+
+Here is a version of this AI on iterator filters that includes !wording.
+[This is version /02 of the AI. - Editor.]
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, December 6, 2018  10:32 PM
+
+> !standard 5.5(4)                               18-12-06  AI12-0250-1/03
+
+I'm not sure what happened to version /02; I don't have one, so that's the 
+number this draft was given. I know that will confuse everyone, sorry.
+
+I didn't see any issues, other than removing a few double blanks, before I 
+filed this. But I'm too far behind to read this carefully (and I will 
+continue my voting against - I do not want to have to add 24 new kinds of 
+iteration code generation to my compiler -- nor do I want to make any other
+implementers do that either).
+
+****************************************************************

Questions? Ask the ACAA Technical Agent