Version 1.1 of ai12s/ai12-0326-1.txt

Unformatted version of ai12s/ai12-0326-1.txt version 1.1
Other versions for file ai12s/ai12-0326-1.txt

!standard 5.5.3(17/5)          19-04-05 AI12-0326-1/01
!standard 5.5.3(21/5)
!class Amendment 19-04-05
!status work item 19-04-05
!status received 19-03-19
!priority Low
!difficulty Easy
!subject Consequence for incorrect Allows_Exit
!summary
Execution is erroneous if an Allows_Exit subprogram handles all exceptions, or calls the access-to-subprogram parameter from a parallel construct.
!problem
AI12-0189-1 introduced the Allows_Exit aspect to specify that a subprogram is designed to allow use in a parallel iterator.
However, that AI did not provide any consequence if the subprogram violates the assumptions of the aspect. This is bad, since it requires the implementation to work properly regardless of what the subprogram with the Allows_Exit aspect does.
For instance, if an implementation choses to use exceptions to implement exits from procedural iterators, a routine that specifies Allows_Exit but still "eats" all exceptions propagated from calls to the access-to-subprogram parameter could cause problems.
!proposal
(See Summary.)
!wording
[Author's note: Paragraph numbers in this AI are from draft 19 of the Ada 2020 RM.]
Add after 5.5.3(21/5):
Erroneous Execution
If a subprogram S has a True Allows_Exit aspect and is used in an iterator_procedure call, execution is erroneous:
* if S does not propagate any exception propagated from a call to any access-to-subprogram parameter of S, if the exception was handled by a "when others" exception handler.
AARM Reason: We want the implementation of a procedural iterator to be able to use exceptions to implement exits and other control flow without having to worry about the Allows_Exit subprogram. We allow "eating" of specific exceptions so that the iterator itself can use those as it sees fit; any exception that the implementation would use should be implementation-defined and thus would never appear explicitly in portable code.
* if a call is made to any access-to-subprogram parameter of S from within a parallel construct.
AARM Reason: A parallel construct can cause exceptions to disappear as one transfer-of-control is "chosen arbitrarily" when threads are canceled (see 5.1). In addition, a parallel construct could cause data races with the control information needed to determine the target of a transfer of control. An implementation could avoid that by making all control information atomic, but we don't want to make implementations slower just to guard against an unlikely case.
AARM Ramification: These cases can happen directly in S, or in any subprogram called by S.
!discussion
Another concern that was identified during (private) discussion on this topic was the use of parallel constructs in Allows_Exit routines. Procedural iterators by their nature cannot be safely executed in parallel (it's not possible to have a contract on the access parameter, so it could have natural data races; moreover, the usual uses of such routines include some sort of up-level access which necessarily would be a data race).
Therefore, executing the subprogram (which is really the body of the loop) in parallel without appropriate precautions is a step backwards in the safety of parallel operations.
As such, we do not want "hidden" parallel operations involved with the actual execution of the loop. (We note that such operations could cause an exit exception to be lost, as there is no guarantee of which exception is propagated from a parallel construct.)
---
We're trying to ensure that a canonical implementation that looks like the following would be possible without complication from a
declare Exit_Code : Integer range 1 .. 4; ... other stuff as needed ...
procedure Loop_Body is begin ...; if ... then Exit_Code := 3; raise System.Procedural_Iterator_Transfer; end if; ...; end; begin My_Container.Iterate_Procedure (Loop_Body'access); exception when System.Procedural_Iterator_Transfer => case Exit_Code is ... when 3 => null; -- Exit. ... end case; end;
For this to work, Iterate_Procedure must propagate System.Procedural_Iterator_Transfer, and must not cause a data race with Exit_Code.
Of course, an implementation can use any implementation technique that it wants; there's no requirement to use an implementation-defined exception.
!ASIS
None needed (anything needed would be part of the original mechanism).
!ACATS test
Erroneous execution cannot be tested by definition.
!appendix

From: Randy Brukardt
Sent: Tuesday, March 19, 2019  12:52 AM

AI12-0189-1 defined the Allows_Exit aspect to assert that a particular 
subprogram is prepared to allow arbitrary transfers of control from the
subprogram represented by one of its parameters. In particular, it is not
supposed to use exception handling to do cleanup. (I'm not sure why that's
specifically a problem; it's more that it shouldn't be allowed to "eat" any
exceptions coming from the subprogram -- although using finalization for 
cleanup is probably better.)

Anyway, there doesn't seem to be any consequence if the assertion is violated,
especially when used in a procedural_iterator. That seems wrong; it undermines
the point of having the aspect in the first place.

For instance, imagine an implementation that uses a special unnamable (but
handle-able) exception to implement transfers of control for a 
procedural_iterator. And imagine that we have a procedure defined thus:

     procedure Bad_Guy (A : access procedure (...))
        with Allows_Exit is
     begin
         ...
         A (...);
         ...
     exception
         when others => null;
     end Bad_Guy;

If we have a procedural_iterator thus:

     for (Q) of Bad_Guy loop
        if Match(Q) then
           Save_It;
           goto Skip;
        end if;
     end loop;
     Not_Found;
   <<Skip>> null;

This loop is legal, but it probably will execute Not_Found even when Match is 
True. It defeats the purpose to have a declaration for this property if a 
routine that violates it has no consequence -- an implementation would still 
have to work overtime to make this work, even though Bad_Guy ate the special 
exception despite its promise not to do that.

So I think we need an Erroneous Execution rule for this case. Perhaps 
something like:

   If a subprogram S has a True Allows_Exit aspect, and it is used in an 
   iterator_procedure_call, execution is erroneous if S handles any exceptions 
   propagated from a call of any access-to-subprogram parameter of S.

[or, if we wanted to just have a guarantee that S doesn't eat any propagated
exceptions:

   If a subprogram S has a True Allows_Exit aspect, and it is used in an 
   iterator_procedure_call, execution is erroneous if S does not propagate any 
   exceptions propagated from a call of any access-to-subprogram parameter of S.

but the wording of Allows_Exit seems pretty clear that this is not the intent.]

Do we need an AI for this?? (Or possibly put it into a clean-up AI?)

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


Questions? Ask the ACAA Technical Agent