Version 1.5 of ai12s/ai12-0120-1.txt

Unformatted version of ai12s/ai12-0120-1.txt version 1.5
Other versions for file ai12s/ai12-0120-1.txt

!standard 5.5.2(6.1/4)          14-10-02 AI05-0120-1/04
!standard 5.5.2(10/3)
!standard 5.5.2(13/3)
!class binding interpretation 14-06-20
!status Corrigendum 2015 14-07-14
!status WG9 Approved 14-10-20
!status ARG Approved 7-0-0 14-06-29
!status work item 14-06-20
!status received 14-06-04
!priority Low
!difficulty Easy
!qualifier Omission
!subject Legality and exceptions of generalized loop iteration
!summary
If the call to the default iterator function would be illegal, a container element iterator is illegal. If the cursor type for a generalized iterator or container element iterator is limited, the iterator is illegal. If any of the calls to functions in a generalized iterator or container element iterator propagate an exception, the enclosing loop statement propagates the exception.
!question
The original version of a proposed ACATS test used a default iterator function with the specification:
function Iterate
(Container : aliased in out Sparse_Array)
return Sparse_Array_Iterator_Interfaces.Reversible_Iterator'Class;
This caused an internal error in a compiler when the test tried iterating on a function call (that is, a constant):
for Item of Get_Constant_Sparse_Data loop
We eventually realized that this expands to
for Cur in Get_Constant_Sparse_Data.Iterate loop
which is of course illegal (as we're passing a function call to an in out parameter).
There is no rule which disallows this, but clearly we don't want to allow making calls that would otherwise be illegal. Do we need a new rule? (Yes.)
!recommendation
(See Summary.)
!wording
Add after 5.5.2(6.1/4): [A legality rule added by AI12-0047-1]
A container element iterator is illegal if the call of the default iterator function that creates the loop iterator (see below) is illegal.
AARM 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. [Editor's note: I can't come up with an example of an accessibility check failure, but that area is so complex that it's easy to believe that there is a way for it to fail.]
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.
AARM 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.
[Editor's Note: We can't use "inherently limited" here as we usually do in legality rules as the assignment rule applies to all limited types, including those that become nonlimited at some point in their scope. Yes, the assignment rule is just "the target shall be a nonlimited variable". Here I think we need more clarification; in particular this does not depend on the limitedness of the cursor at the point of the instantiation of iterator interfaces; if the cursor later becomes nonlimited, iterators are then legal.]
Delete AARM 5.5.2(10.b/4) (it's covered by the above).
Add after 5.5.2(13/3):
Any exception propagated by the execution of a generalized iterator or container element iterator is propagated by the immediately enclosing loop statement.
AARM 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.
!discussion
We want this to work just like a macro-expansion into the appropriate code, so we define that it works that way.
The rules for the interfaces defined in Iterator_Interfaces prevents any of the calls to those functions from being illegal, so we don't need any rules for them.
We specify that an exception raised by one of the calls that are used to implement an iterator, or by an assignment used to implement an iterator, is propagated by the loop statement. This means that those exceptions cannot be handled inside of the loop; in particular, they cannot be handled by the sequence_of_statements.
!corrigendum 5.5.2(6/3)
Insert after the paragraph:
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.
the new paragraphs:
A container element iterator is illegal if the call of the default iterator function that creates the loop iterator (see below) is illegal.
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.
!corrigendum 5.5.2(13/3)
Insert after the paragraph:
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.
the new paragraph:
Any exception propagated by the execution of a generalized iterator or container element iterator is propagated by the immediately enclosing loop statement.
!ASIS
No ASIS effect.
!ACATS test
ACATS B-Test(s) are needed for the Legality Rules. A low priority C-Test is needed to ensure that any exceptions are propagated properly.
!appendix

From: Randy Brukardt
Sent: Wednesday, June  4, 2014  1:44 PM

Brad wrote an ACATS foundation to test default container iterators by copying
some of the interesting parts of the design of the Ada containers.

In particular, he put an access to the container into the cursor.

In order to do that, he had to make the Iterate function have the
specification:

   function Iterate
     (Container : aliased in out Sparse_Array)
      return Sparse_Array_Iterator_Interfaces.Reversible_Iterator'Class;

so that Container'Unchecked_Access would work.

GNAT threw up on the part of the test that tried iterating on constants:

    for Item of Get_Constant_Sparse_Data loop
                |
         >>> actual for "Container" must be a variable

This message didn't make much sense, so I reported it as a bug in GNAT. But it
actually does make sense if you expand the loop into the equivalent generalized
iterator form:

    for Cur in Get_Constant_Sparse_Data.Iterate loop

Here we're passing a function result (constant) to an aliased in out parameter
(requires variable), which is clearly wrong.

5.5.1(8/3) requires various things of the default iterator function. I have to
wonder if one of them ought to be that it doesn't require a variable (is not "in
out" or access-to-variable). Alternatively, 5.5.2 needs a rule to require the
object for a container element iterator is a variable if the T parameter to the
default iterator function is "in out" or "access-to-variable". This certainly
should not be left to a compiler internal error!

****************************************************************

From: Tucker Taft
Sent: Wednesday, June  4, 2014  2:19 PM

I agree this is a language bug (though if you read it just right the right rule
is implied ;-).  These new features are supposed to be "syntactic sugar" and the
same legality rules should apply (and so should the same dynamic semantics).  In
particular, in 5.5.2(12) it specifies:

   "The default iterator function for the type of the iterable container object
    for the loop is called on the iterable container object ..."

Somewhere in 5.5.2 we should require that it be legal to make such a function
call, as far as parameter modes, accessibility, or whatever.  Furthermore, if
this function propagates an exception, then presumably the loop statement
propagates an exception.

****************************************************************

Questions? Ask the ACAA Technical Agent