5.5.2 Generalized Loop Iteration
Syntax
Name Resolution Rules
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
{
AI05-0139-2}
If the reserved word
reverse appears, the
iterator_specification
is a
reverse iterator;
otherwise it is a
forward iterator.
In a reverse generalized iterator, the
iterator_name
shall be of a 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.
{
AI05-0139-2}
{
AI12-0151-1}
The
subtype defined by type
of the
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
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.
{
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.
{
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.
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.
{
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.
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.
{
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.
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
{
AI05-0139-2}
{
AI05-0269-1}
{
AI05-0292-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
subtype_indication
is present, it determines the nominal subtype of the loop parameter.
In an array component iterator, if a
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
subtype_indication
is not present, the nominal subtype of the loop parameter is the default
element subtype for the type of the
iterable_name.
{
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.
Ramification: {
AI12-0093-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 container.
Dynamic Semantics
{
AI05-0139-2}
For a 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 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.
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.
{
AI05-0139-2}
{
AI05-0292-1}
For an array component iterator, 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 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 executed for each component of the array for the loop, or until
the loop is left as a consequence of a transfer of control.
{
AI05-0139-2}
{
AI05-0292-1}
For a container element iterator, the
iterable_name
is evaluated and the denoted iterable container object 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.
An object of the default cursor subtype
is created (the
loop cursor).
{
AI05-0139-2}
{
AI05-0292-1}
For a forward container element iterator, 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; 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.
{
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.
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
{
AI05-0269-1}
--
Array component iterator example:
for Element
of Board
loop --
See 3.6.1.
Element := Element * 2.0; --
Double each element of Board, a two-dimensional array.
end loop;
Extensions to Ada 2005
{
AI05-0139-2}
Generalized forms of loop iteration are new.
Incompatibilities With Ada 2012
{
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.
{
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.
{
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.
Wording Changes from Ada 2012
{
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.
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe