Ada Conformity Assessment Authority      Home Conformity Assessment   Test Suite ARGAda Standard
 
Annotated Ada Reference Manual (Ada 202x Draft 24)Legal Information
Contents   Index   References   Search   Previous   Next 

5.5.2 Generalized Loop Iteration

1/3
{AI05-0139-2} Generalized forms of loop iteration are provided by an iterator_specification.

Syntax

2/5
{AI05-0139-2} {AI05-0292-1} {AI12-0156-1} {AI12-0250-1} iterator_specification ::= 
    defining_identifier [: loop_parameter_subtype_indicationin [reverseiterator_name
      [iterator_filter]

  | defining_identifier [: loop_parameter_subtype_indication subtype_indicationof [reverseiterable_name
      [iterator_filter]
2.1/5
{AI12-0156-1} loop_parameter_subtype_indication ::= subtype_indication | access_definition
2.2/5
{AI12-0266-1} If an iterator_specification is for a parallel construct, the reserved word reverse shall not appear in the iterator_specification.

Name Resolution Rules

3/3
{AI05-0139-2} {AI05-0292-1} For the first form of iterator_specification, called a generalized iterator, the expected type for the iterator_name is any iterator type. For the second form of iterator_specification, the expected type for the iterable_name is any array or iterable container type. If the iterable_name denotes an array object, the iterator_specification is called an array component iterator; otherwise it is called a container element iterator.
3.a.1/3
Glossary entry: An iterator is a construct that is used to loop over the elements of an array or container. Iterators may be user defined, and may perform arbitrary computations to access elements from a container.

Legality Rules

4/5
{AI05-0139-2} {AI12-0266-1} If the reserved word reverse appears, the iterator_specification is a reverse iterator. If the iterator_specification is for a parallel construct, the iterator_specification is a parallel iterator. Otherwise,; otherwise it is a forward iterator. Forward and reverse iterators are collectively called sequential iterators. 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.
5/5
{AI05-0139-2} {AI12-0151-1} {AI12-0156-1} {AI12-0183-1} The subtype defined by the loop_parameter_subtype_indication, if any, of a generalized iterator shall statically match the iteration cursor subtype. The subtype defined by type of the loop_parameter_subtype_indication subtype_indication, if any, of an array component iterator shall statically match cover the component subtype type of the type of the iterable_name. The subtype defined by type of the loop_parameter_subtype_indication subtype_indication, if any, of a container element iterator shall statically match cover the default element subtype type for the type of the iterable_name.
6/3
{AI05-0139-2} In a container element iterator whose iterable_name has type T, if the iterable_name denotes a constant or the Variable_Indexing aspect is not specified for T, then the Constant_Indexing aspect shall be specified for T.
6.1/4
 {AI12-0047-1} The iterator_name or iterable_name of an iterator_specification shall not denote a subcomponent that depends on discriminants of an object whose nominal subtype is unconstrained, unless the object is known to be constrained.
6.a/4
Reason: This is the same rule that applies to renames; it serves the same purpose of preventing the object from disappearing while the iterator is still using it. 
6.2/4
 {AI12-0120-1} A container element iterator is illegal if the call of the default iterator function that creates the loop iterator (see below) is illegal.
6.b/4
Ramification: This can happen if the parameter to the default iterator function is in out and the iterable_name is a constant. The wording applies to any reason that the call would be illegal, as it's possible that one of the default parameters would be illegal, or that some accessibility check would fail. 
6.3/4
 {AI12-0120-1} A generalized iterator is illegal if the iteration cursor subtype of the iterator_name is a limited type at the point of the generalized iterator. A container element iterator is illegal if the default cursor subtype of the type of the iterable_name is a limited type at the point of the container element iterator.
6.c/4
Reason: If the cursor type is limited, the assignment to the loop parameter for a generalized iterator would be illegal. The same is true for a container element iterator. We have to say "at the point of the iterator" as the limitedness of a type can change due to visibility. 

Static Semantics

7/5
{AI05-0139-2} {AI05-0269-1} {AI05-0292-1} {AI12-0156-1} An iterator_specification declares a loop parameter. In a generalized iterator, the nominal subtype of the loop parameter is the iteration cursor subtype. In an array component iterator, or a container element iterator, if a loop_parameter_subtype_indication subtype_indication is present, it determines the nominal subtype of the loop parameter. In a generalized iterator, if a loop_parameter_subtype_indication is not present, the nominal subtype of the loop parameter is the iteration cursor subtype. In an array component iterator, if a loop_parameter_subtype_indication subtype_indication is not present, the nominal subtype of the loop parameter is the component subtype of the type of the iterable_name. In a container element iterator, if a loop_parameter_subtype_indication subtype_indication is not present, the nominal subtype of the loop parameter is the default element subtype for the type of the iterable_name.
8/3
{AI05-0139-2} {AI05-0292-1} In a generalized iterator, the loop parameter is a constant. In an array component iterator, the loop parameter is a constant if the iterable_name denotes a constant; otherwise it denotes a variable. In a container element iterator, the loop parameter 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.
8.a/5
Ramification: {AI12-0093-1} {AI12-0156-1} The loop parameter of a generalized iterator has the same accessibility as the loop statement. This means that the loop parameter object is finalized when the loop statement is left. (It also may be finalized as part of assigning a new value to the loop parameter.) For array component iterators and container element iterators, the loop parameter directly denotes an element of the array or container and has the accessibility of the associated array or containerFor container element iterators, the loop parameter denotes the result of the indexing function call (in the case of a constant indexing) or a generalized reference thereof (in the case of a variable indexing). Roughly speaking, the loop parameter has the accessibility level of a single iteration of the loop. More precisely, the function result (or the generalized reference thereof) is considered to be renamed in the declarative part of a notional block statement which immediately encloses the loop's sequence_of_statements; the accessibility of the loop parameter is that of the block statement.

Dynamic Semantics

9/3
{AI05-0139-2} For the execution of a loop_statement with an iterator_specification, the iterator_specification is first elaborated. This elaboration elaborates the subtype_indication, if any.
10/5
{AI05-0139-2} {AI12-0250-1} {AI12-0266-1} For a sequential generalized iterator, the loop parameter is created, 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 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. This repeats until the result of calling Has_Element on the loop parameter is False, or the loop is left as a consequence of a transfer of control. For a reverse generalized iterator, the operations Last and Previous are called rather than First and Next.
10.a/4
Ramification: {AI12-0093-1} The loop parameter of a generalized iterator is a variable of which the user only has a constant view. It follows the normal rules for a variable of its nominal subtype. In particular, if the nominal subtype is indefinite, the variable is constrained by its initial value. Similarly, if the nominal subtype is class-wide, the variable (like all variables) has the tag of the initial value. Constraint_Error may be raised by a subsequent iteration if Next or Previous return an object with a different tag or constraint. 
10.1/5
  {AI12-0266-1} For a parallel generalized iterator, the chunk_specification, if any, of the associated parallel construct, is first elaborated, to determine the maximum number of chunks (see 5.5), and then the operation Split_Into_Chunks of the iterator type is called, with the determined maximum passed as the Max_Chunks parameter, specifying the upper bound for the number of loop parameter objects (and the number of logical threads of control) to be associated with the iterator. In the absence of a chunk_specification, the maximum number of chunks is determined in an implementation-defined manner.
10.b/5
Implementation defined: The maximum number of chunks for a parallel generalized iterator without a chunk_specification.
10.2/5
  {AI12-0250-1} {AI12-0266-1} Upon return from Split_Into_Chunks, the actual number of chunks for the loop is determined by calling the Chunk_Count operation of the iterator, at which point one logical thread of control is initiated for each chunk, with an associated chunk index in the range from one to the actual number of chunks. Within each logical thread of control, a loop parameter is created. If a chunk_specification with a discrete_subtype_definition is present in the associated parallel construct, then a chunk parameter is created, and initialized with a value from the discrete subtype defined by the discrete_subtype_definition, so that the order of the chosen chunk parameter values correspond to the order of the chunk indices associated with the logical threads of control. The operation First of the iterator type having a Chunk parameter is called on the loop iterator, with Chunk initialized from the corresponding chunk index, to produce the initial value for the loop parameter. If the result of calling Has_Element on this initial value is False, then the execution of the logical thread of control is complete. Otherwise, the sequence_of_statements is conditionally executed and then the Next operation of the iterator type having a Chunk parameter is called, with the loop iterator, the current value of the loop parameter, and the corresponding chunk index, to produce the next value to be assigned to the loop parameter. This repeats until the result of calling Has_Element on the loop parameter is False, or the associated parallel construct is left as a consequence of a transfer of control. In the absence of a transfer of control, the associated parallel construct of a parallel generalized iterator is complete when all of its logical threads of control are complete.
10.b.1/5
Discussion: The Max_Chunks parameter of the Split_Into_Chunks procedure is an upper bound for the number of chunks to be associated with a loop. A container implementation may opt for a lower value for the number of chunks 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.
11/5
{AI05-0139-2} {AI05-0292-1} {AI12-0250-1} {AI12-0266-1} For an array component iterator, the chunk_specification of the associated parallel construct, if any, is first elaborated to determine the maximum number of chunks (see 5.5), and then the iterable_name is evaluated and the denoted array object becomes the array for the loop. If the array for the loop is a null array, then the execution of the loop_statement is complete. 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. For a parallel array component iterator, the iteration is broken up into contiguous chunks of the canonical order, such that all components are covered with no overlaps; each chunk has its own logical thread of control with its own loop parameter and iteration within each chunk is in the canonical order. The number of chunks is implementation defined, but is limited in the presence of a chunk_specification to the determined maximum. 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.
11.a/5
Implementation defined: The number of chunks for an array component iterator.
11.1/5
  {AI12-0266-1} If a chunk_specification with a discrete_subtype_definition is present in the associated parallel construct, then the logical thread of control associated with a given chunk has a chunk parameter initialized with a distinct value from the discrete subtype defined by the discrete_subtype_definition. The values of the chunk parameters are assigned such that they increase in the canonical order of the starting array components for the chunks.
12/5
{AI05-0139-2} {AI05-0292-1} {AI12-0111-1} {AI12-0266-1} For a container element iterator, the chunk_specification of the associated parallel construct, if any, is first elaborated to determine the maximum number of chunks (see 5.5), and then the iterable_name is evaluated. If the container type has Iterator_View specified, an object of the Iterator_View type is created with the discriminant referencing the iterable container object denoted by the iterable_name. This is the iterable container object for the loop. Otherwise, and the iterable container object denoted by the iterable_name becomes the iterable container object for the loop becomes the iterable container object for the loop. The default iterator function for the type of the iterable container object for the loop is called on the iterable container object and the result is the loop iterator. For a sequential container element iterator, an An object of the default cursor subtype is created (the loop cursor). For a parallel container element iterator, each chunk of iterations will have its own loop cursor, again of the default cursor subtype.
12.a/5
Reason: {AI12-0111-1} If Iterator_View is specified, we add an extra object and use that object for this iteration. This allows these iterators to automatically use the stable view (defined in each of the language-defined containers) to do the iteration. That eliminates the need to set and clear the tampering with elements indication each time Reference is called; that eliminates substantial overhead as finalization is typically used to implement the tampering reset. 
13/5
{AI05-0139-2} {AI05-0292-1} {AI12-0266-1} A For a container element iterator then proceeds as described above for a generalized iterator, except that each reference to a loop parameter is replaced by a reference to the corresponding loop cursor. For a container element iterator, the loop parameter for each iteration instead denotes, the operation First of the iterator type is called on the loop iterator, to produce the initial value for the loop cursor. If the result of calling Has_Element on the initial value is False, then the execution of the loop_statement is complete. Otherwise, the sequence_of_statements is 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 for the given iteration; 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. This repeats until the result of calling Has_Element on the loop cursor is False, or until the loop is left as a consequence of a transfer of control. For a reverse container element iterator, the operations Last and Previous are called rather than First and Next. 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.
14/4
{AI12-0120-1} Any exception propagated by the execution of a generalized iterator or container element iterator is propagated by the immediately enclosing loop statement.
14.a/4
Ramification: This text covers exceptions raised by called functions that make up the execution of the iterator as well as exceptions raised by the assignment to the loop parameter or cursor. 

Examples

15/5
{AI05-0269-1} {AI12-0266-1} -- Array component iterator example:
parallel
for Element of Board loop  -- See 3.6.1.
   Element := Element * 2.0; -- Double each element of Board, a two-dimensional array.
end loop;
16/3
{AI05-0268-1} For examples of use of generalized iterators, see A.18.33 and the corresponding container packages in A.18.2 and A.18.3

Extensions to Ada 2005

16.a/3
{AI05-0139-2} Generalized forms of loop iteration are new. 

Incompatibilities With Ada 2012

16.b/4
{AI12-0047-1} Corrigendum: Added a rule to ensure that the object being iterated cannot be a component that could disappear before the loop completes. This could be incompatible by making a loop that was legal (and worked correctly, so long as the enclosing object is not modified during the loop) from the original Ada 2012 illegal in corrected Ada 2012. Such loops should be pretty rare, especially as these iterator forms are new to Ada 2012.
16.c/4
{AI12-0120-1} Corrigendum: Added rules to reject loops if the call to the default iterator function for a container element iterator is illegal, or if the cursor type of an iterator is limited. These are formally incompatible with original Ada 2012, but as it's unlikely that any Ada 2012 compiler ever allowed the illegal usages in an expansion of a loop (it's much more likely that they would have just caused an internal error in the compiler), this should have no effect in practice.
16.d/4
{AI12-0151-1} Corrigendum: Added a requirement that the given subtype statically match the subtype of the element or component for a component element iterator or array component iterator. Original Ada 2012 text allowed any type that covered the subtype of the element or component, but that led to questions of what the meaning was if they are different. In this case, the element is essentially a renaming of the container element, and it doesn't make sense for the constraints to be different. Ignoring explicitly defined constraints in renames is a mistake that we don't want to continue, thus we require static matching. This means that some programs might be illegal, but those programs were misleading at best, and potentially would raise unexpected exceptions because the element values might have been invalid or abnormal with respect to the declared constraint. 

Extensions to Ada 2012

16.e/5
{AI12-0156-1} For consistency, we now allow a subtype_indication on a generalized iterator, and anonymous access types on all forms of iterator. We introduced a new syntax non-terminal, loop_parameter_subtype_indication to simplfy the wording.
16.f/5
{AI12-0250-1} An iterator_filter is now allowed on iterator_specifications. This is mainly for consistency with aggregate and reduction iterators, where it eliminates the need for temporary objects. 

Wording Changes from Ada 2012

16.g/4
{AI12-0120-1} Corrigendum: Added wording to specify that a loop propagates any exceptions propagated by the execution of an iterator. Since that's what naturally would happen from a macro-style expansion of the parts of an iterator, and no other interpretation makes sense given the way the rest of Ada works, we consider it so unlikely that any Ada 2012 implementation ever did anything else that we don't document this as a possible inconsistency.
16.h/5
{AI12-0111-1} Added wording to include the use of the iterator view in a container element iterator.
16.i/5
{AI12-0266-1} Added wording to describe the execution of parallel iterators. 

Contents   Index   References   Search   Previous   Next 
Ada-Europe Ada 2005 and 2012 Editions sponsored in part by Ada-Europe