Annotated Ada Reference Manual (Ada 202y Draft 1)Legal Information
Contents   Index   References   Search   Previous   Next 

5.5.3 Procedural Iterators

1/5
{AI12-0189-1} A procedural_iterator invokes a user-defined procedure, passing in the body of the enclosing loop_statement as a parameter of an anonymous access-to-procedure type, to allow the loop body to be executed repeatedly as part of the invocation of the user-defined procedure. 

Syntax

2/5
{AI12-0189-1} {AI12-0250-1} procedural_iterator ::= 
     iterator_parameter_specification of iterator_procedure_call
       [iterator_filter]
3/5
{AI12-0189-1} {AI12-0308-1} iterator_parameter_specification ::= 
     formal_part
   | (defining_identifier{, defining_identifier})
4/5
{AI12-0189-1} iterator_procedure_call ::= 
     procedure_name
   | procedure_prefix iterator_actual_parameter_part
5/5
{AI12-0189-1} iterator_actual_parameter_part ::= 
     (iterator_parameter_association {, iterator_parameter_association})
6/5
{AI12-0189-1} iterator_parameter_association ::= 
     parameter_association
   | parameter_association_with_box
7/5
{AI12-0189-1} parameter_association_with_box ::= 
   [ formal_parameter_selector_name => ] <>
8/5
{AI12-0189-1} At most one iterator_parameter_association within an iterator_actual_parameter_part shall be a parameter_association_with_box.

Name Resolution Rules

9/5
{AI12-0189-1} {AI12-0292-1} {AI12-0326-2} The name or prefix given in an iterator_procedure_call shall resolve to denote a callable entity C (the iterating procedure) that is a procedure, or an entry renamed as (viewed as) a procedure. [When there is an iterator_actual_parameter_part, the prefix can be an implicit_dereference of an access-to-subprogram value.]
10/5
{AI12-0189-1} An iterator_procedure_call without a parameter_association_with_box is equivalent to one with an iterator_actual_parameter_part with an additional parameter_association_with_box at the end, with the formal_parameter_selector_name identifying the last formal parameter of the callable entity denoted by the name or prefix.
11/5
{AI12-0189-1} {AI12-0320-1} An iterator_procedure_call shall contain at most one iterator_parameter_association for each formal parameter of the callable entity C. Each formal parameter without an iterator_parameter_association shall have a default_expression (in the profile of the view of C denoted by the name or prefix).
12/5
{AI12-0189-1} The formal parameter of the callable entity C associated with the parameter_association_with_box shall be of an anonymous access-to-procedure type A

Legality Rules

13/5
{AI12-0189-1} {AI12-0308-1} The anonymous access-to-procedure type A shall have at least one formal parameter in its parameter profile. If the iterator_parameter_specification is a formal_part, then this formal_part shall be mode conformant with that of A. If the iterator_parameter_specification is a list of defining_identifiers, the number of formal parameters of A shall be the same as the length of this list.
14/5
{AI12-0189-1} {AI12-0292-1} [If the name or prefix given in an iterator_procedure_call denotes an abstract subprogram, the subprogram shall be a dispatching subprogram.]
14.a/5
Proof: {AI12-0320-1} This is stated normatively in 3.9.3

Static Semantics

15/5
{AI12-0189-1} {AI12-0250-1} {AI12-0308-1} {AI12-0326-2} 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 defining_identifiers given in the iterator_parameter_specification. The body of P consists of the conditionally executed sequence_of_statements. The procedure P is called the loop body procedure.
15.a/5
Implementation Note: {AI12-0250-1} For a procedural_iterator with an iterator_filter, the body of the routine would be something like:
15.b/5
procedure P ... is
begin
   if iterator_filter then
      sequence_of_statements
   end if;
end P;
16/5
{AI12-0344-1} In a procedural iterator, the Parallel_Calls aspect (see 9.10.1) of the loop body procedure is True if the reserved word parallel occurs in the corresponding loop statement, and False otherwise.
17/5
{AI12-0189-1} {AI12-0326-2} {AI12-0344-1} The following aspects may be specified for a callable entity S that has exactly one formal parameter of an anonymous access-to-subprogram type:
18/5
Allows_Exit
The Allows_Exit aspect is of type Boolean. The specified value shall be static. The Allows_Exit aspect of an inherited primitive subprogram is True if Allows_Exit is True either for the corresponding subprogram of the progenitor type or for any other inherited subprogram that it overrides. If not specified or inherited as True, the Allows_Exit aspect of a callable entity is False. For an entry, only a confirming specification of False is permitted for the Allows_Exit aspect.
18.a/5
Reason: An entry does not allow exit, because implementing a transfer of control out of a task or protected entry creates unnecessarily complex dynamic semantics. 
19/5
Specifying the Allows_Exit aspect to be True for a subprogram indicates that the subprogram allows exit, meaning that it is prepared to be completed by arbitrary transfers of control from the loop body procedure[, including propagation of exceptions. A subprogram for which Allows_Exit is True should use finalization as appropriate rather than exception handling to recover resources and make any necessary final updates to data structures].
19.a/5
Aspect Description for Allows_Exit: An indication of whether a subprogram will operate correctly for arbitrary transfers of control.
19.b/5
Ramification: A subprogram that does not need cleanup satisfies the requirements, and thus can specify Allows_Exit as True. If a subprogram S allows exit, it cannot expect to get control other than via finalization if the loop body procedure initiates a transfer of control as part of a procedural_iterator. In particular, exception handlers in S, even when others handlers, will not be executed when a transfer of control occurs. The mechanism that the implementation uses to implement such transfers of control needs to avoid triggering exception handlers. 
20/5
Parallel_Iterator

The Parallel_Iterator aspect is of type Boolean. The specified value shall be static. The Parallel_Iterator aspect of an inherited primitive subprogram is True if Parallel_Iterator is True either for the corresponding subprogram of the progenitor type or for any other inherited subprogram that it overrides. If not specified or inherited as True, the Parallel_Iterator aspect of a callable entity is False.
21/5
{AI12-0189-1} {AI12-0442-1} Specifying the Parallel_Iterator aspect to be True for a callable entity indicates that the entity is allowed to invoke the loop body procedure from multiple distinct logical threads of control. The Parallel_Iterator aspect for a subprogram shall be statically False if the subprogram allows exit.
21.a/5
Aspect Description for Parallel_Iterator: An indication of whether a subprogram may use multiple threads of control to invoke a loop body procedure.
21.b/5
Reason: Permitting exit from a parallel procedural iterator introduces additional semantic and implementation complexity. 

Legality Rules

22/5
{AI12-0189-1} {AI12-0326-2} If a callable entity overrides an inherited dispatching subprogram that allows exit, the overriding callable entity also shall allow exit. If a callable entity overrides an inherited dispatching subprogram that has a True Parallel_Iterator aspect, the overriding callable entity also shall have a True Parallel_Iterator aspect.
22.a/5
Ramification: Since an entry never allows exit, attempting to implement an allows exit subprogram with a task or protected entry is always illegal. However, the Parallel_Iterator aspect can be applied to an entry, so a subprogram with the Parallel_Iterator aspect True can be implemented by an entry. 
23/5
{AI12-0326-2} A loop_statement with a procedural_iterator as its iteration_scheme shall begin with the reserved word parallel if and only if the callable entity identified in the iterator_procedure_call has a Parallel_iterator aspect of True.
24/5
{AI12-0326-2} If the actual parameter of an anonymous access-to-subprogram type, passed in an explicit call of a subprogram for which the Parallel_Iterator aspect is True, is of the form P'Access, the designated subprogram P shall have a Parallel_Calls aspect True (see 9.10.1).
25/5
{AI12-0189-1} {AI12-0326-2} The sequence_of_statements of a loop_statement with a procedural_iterator as its iteration_scheme shall contain an exit_statement, return statement, goto_statement, or requeue_statement that leaves the loop only if the callable entity associated with the procedural_iterator allows exit.
26/5
{AI12-0294-1} The sequence_of_statements of a loop_statement with a procedural_iterator as its iteration_scheme shall not contain an accept_statement whose entry_declaration occurs outside the loop_statement.
26.a/5
Reason: An accept_statement is not allowed in a procedure (see 9.5.2), it has to be directly in a task_body. Since the loop body here is implemented as a procedure, we can't allow accept_statements there, either, even if the loop itself is directly in a task_body.
26.b/5
Ramification: This includes cases where the accept_statement is part of another construct, for instance, a select_statement.

Dynamic Semantics

27/5
{AI12-0326-2} {AI12-0355-2} {AI12-0445-1} [For the execution of a loop_statement with an iteration_scheme that has a procedural_iterator, the procedure denoted by the name or prefix of the iterator_procedure_call (the iterating procedure) is invoked, passing an access value designating the loop body procedure as a parameter. The iterating procedure then calls the loop body procedure zero or more times and returns, whereupon the loop_statement is complete. If the parallel reserved word is present, the iterating procedure is allowed to invoke the loop body procedure from multiple distinct logical threads of control.] The aspect_specification, if any, is elaborated prior to the invocation of the iterating procedure.
27.a/5
Proof: The stated dynamic semantics are implied by the static semantics given above and the bounded errors given below. 

Bounded (Run-Time) Errors

28/5
{AI12-0326-2} {AI12-0445-1} If the callable entity identified in the iterator_procedure_call allows exit, then it is a bounded error for a call of the loop body procedure to be performed from within an abort-deferred operation (see 9.8), unless the entire loop_statement was within the same abort-deferred operation. If detected, Program_Error is raised at the point of the call; otherwise, a transfer of control from the sequence_of_statements of the loop_statement will not necessarily terminate the loop_statement, and the loop body procedure can be called again.
29/5
{AI12-0326-2} {AI12-0445-1} If a loop_statement with the procedural_iterator as its iteration_scheme (see 5.5) does not begin with the reserved word parallel, it is a bounded error if the loop body procedure is invoked from a different logical thread of control than the one that initiates the loop_statement. If detected, Program_Error is raised; otherwise, conflicts associated with concurrent executions of the loop body procedure can occur without being detected by the applicable conflict check policy (see 9.10.1). Furthermore, propagating an exception or making an attempt to exit in the presence of multiple threads of control will not necessarily terminate the loop_statement, deadlock can occur, or the loop body procedure can be called again.
29.a/5
Discussion: {AI12-0326-2} Other Ada rules are still in effect for the allows exit subprogram A, of course. For instance, if a transfer of control causes finalization which raises an exception, Program_Error will be propagated by A (rather than the transfer of control). In such a case, the bounded error above would still apply. Another example is the case where an unrelated task is waiting on the normal completion of the loop body procedure call in A. Such a task might end up waiting forever if a transfer of control happens (this is a deadlock situation). This case does not require additional wording, as the same thing would happen if an exception is propagated from the loop body procedure or if A executed a transfer of control (such as a return statement). 

Examples

30/5
{AI12-0189-1} {AI12-0379-1} {AI12-0429-1} Example of iterating over a map from My_Key_Type to My_Element_Type (see A.18.4):
31/5
for (C : Cursor) of My_Map.Iterate loop
   Put_Line (My_Key_Type'Image (Key (C)) & " => " &
      My_Element_Type'Image (Element (C)));
end loop;
32/5
-- The above is equivalent to:
33/5
declare
   procedure P (C : Cursor) is
   begin
      Put_Line (My_Key_Type'Image (Key (c)) & " => " &
         My_Element_Type'Image (Element (C)));
   end P;
begin
   My_Map.Iterate (P'Access);
end;
34/5
{AI12-0189-1} {AI12-0429-1} Example of iterating over the environment variables (see A.17):
35/5
for (Name, Val) of Ada.Environment_Variables.Iterate(<>) loop
   --  "(<>)" is optional because it is the last parameter
   Put_Line (Name & " => " & Val);
end loop;
36/5
-- The above is equivalent to:
37/5
declare
   procedure P (Name : String; Val : String) is
   begin
      Put_Line (Name & " => " & Val);
   end P;
begin
   Ada.Environment_Variables.Iterate (P'Access);
end;

Extensions to Ada 2012

37.a/5
{AI12-0189-1} {AI12-0292-1} {AI12-0294-1} {AI12-0326-2} {AI12-0344-1} Procedural iterators, and the Allows_Exit and Parallel_Iterator aspects are new in Ada 2022. 

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