From jwagener@amoco.com Mon Oct  3 07:34:51 1994
Received: from interlock.amoco.com by dkuug.dk with SMTP id AA27102
  (5.65c8/IDA-1.4.4j for <sc22wg5@dkuug.dk>); Mon, 3 Oct 1994 18:36:12 +0100
Received: by interlock.amoco.com id AA09658
  (InterLock SMTP Gateway 1.1 for sc22wg5@dkuug.dk);
  Mon, 3 Oct 1994 12:35:59 -0500
Received: by interlock.amoco.com (Internal Mail Agent-3);
  Mon, 3 Oct 1994 12:35:59 -0500
Received: by interlock.amoco.com (Internal Mail Agent-2);
  Mon, 3 Oct 1994 12:35:59 -0500
Received: by interlock.amoco.com (Internal Mail Agent-1);
  Mon, 3 Oct 1994 12:35:59 -0500
From: jwagener@amoco.com
X-Openmail-Hops: 1
Date: Mon, 3 Oct 94 12:34:51 -0500
Message-Id: <H00001500115ae8b@MHS>
Subject: Technical comments on the ENABLE ballot
To: sc22wg5@dkuug.dk
X-Charset: ASCII
X-Char-Esc: 29

Item Subject: Message text
Attached are the technical comments associated with the recent ENABLE letter
ballot.
It's too big to copy directly into my mailer, so I am attaching an ASCII version
of the document containing the comments.  Let me know if for some reason you
can't access it and I'll try some other way of getting it to you.

Jerry

======================================


.......................................................................

Item Subject: technical comments - ascii
Following is a complete set of the technical comments on the ENABLE ballot. The comments are in sections, separated by dashed horizontal lines, by individual commenter; however, the commenters are not identifed, and all references to specific individuals have been, or at least intended to be, removed. A number of nontechnical comments have also been removed - the intent was to keep all, and only, comments of a technical nature.

When distributed in hardcopy at the next X3J3 meeting these comments will take 35 pages. Only the first 11 are ballot comments. The next 11 are post-ballot email exchanges of a technical nature between the commenter and the prinicpal author of the proposal. The final 13 pages comprise comments by a non-X3J3 WG5 member on the specific edits of the proposal.

--------------------------------------------------------------------------------------------------------

(1) INSUFFICIENT_STORAGE

       The requirement that this signal outside enable blocks may lead to less efficient procedure entry sequences. Some implementations do not have an explicit stack overflow test when a procedure's stack frame is allocated. Rather they simply update one or more registers that address the stack and rely on the stack being bounded by a read only area to detect access beyond the end of the stack area. If such a privilege violation or addressing error condition (presumably a SYSTEM_ERROR in the enable model) can be interpreted by the implementation 	as INSUFFICIENT_STORAGE fine. If not then an explicit test for stack overflow must be added to the procedure entry sequence.

       Maybe the the signalling of INSUFFICIENT_STORAGE outside enable blocks should be made processor dependent. Alternatively it could be made clear that SYSTEM_ERROR might be returned for out of store in some cases.

(2) Order of handling signals

      For good reasons, the model allows an implementation to defer the testing of conditions possibly until the end of the enable block is reached. By contrast, a signal that is not enabled by the enable block causes a return or stop. Consider an enable block with a handler for OVERFLOW say, in which first OVERFLOW and then INSUFFICIENT_STORAGE are signalled. In an implementation that defers testing of the OVERFLOW condition until the end of the enable block, the handler the user has specified for this condition will not be entered since the subsequent INSUFFICIENT_STORAGE condition will have caused a return or stop. Although ENABLE,IMMEDIATE would overcome this I do not see why the user should be required to specify IMMEDIATE just to get signals processed in the order they occur. Are there good reasons why this requirement can't be added to the model?

--------------------------------------------------------------------------------------------------------

(1) ... re turning previously illegal programs (subscript out of range etc.) into legal programs; the fix for this is to remove the facility (i.e. the conditions for subscript error, shape error, many-to-one assignment, etc.).  The functionality of catching these errors within a small range of code is already present in the language in the form of IF statements; adding this feature to an ENABLE block merely complicates implementation.

(2) "ENABLE;END ENABLE" and permutations thereof to achieve RESIGNAL and CONDITION_CHECK are too opaque and error-prone - they look like no-ops and therefore should be no-ops.

(3) The scoping rules are not properly spelt out; text is required for chapter 14.

(4) All ALLOCATION_ERROR, DEALLOCATION_ERROR, IO_ERROR, END_OF_FILE or END_OF_RECORD conditions should signal synchronously.  It is not useful to the user for processing to continue for some random number of statements after such an error occurs; imprecise interrupts should only be allowed for arithmetic conditions - these can be efficiently implemented by hardware flags and at least under IEEE rules the continued processing occurs in a controlled and systematic manner.

(5) User-defined conditions with properly determined scoping rules should be included in the proposal.

--------------------------------------------------------------------------------------------------------

1)  ... I do not believe it is necessary or adviseable to allow transfers out of an ENABLE block.  It is unlikely that a person would put an ENABLE around a GOTO since the GOTO would undoubtable be conditional and a major thrust of the ENABLE construct is to allow the processor to be pretty fuzzy about when things are detected within a block.  In effect, there could be some sort of dangling signal left in the enable block.  Just what should the result of something like:
           ENABLE (floating)
             A = B/C
             IF ( test )  GO TO 10
           HANDLE
             ...
           END ENABLE
  10       print *,A
be?

2)  ...

3)  I do not think it is well integrated into the automatic deallocation proposal.  Given a subroutine like:
         Subroutine XXX
         allocatable :: A(:)
         allocate (A(10))
         call YYY
         end
we apparently have the result that YYY can signal an error and it is processor dependent on whether or not A gets deallocated.  I believe one of the allowed or intended implementations is that YYY could magically return up-the-call-tree without actually returning "through" XXX.  Thus a subsequent call to XXX is an error condition.

4)  I think the proposal does not match well with a normal users understanding of how it should work and with normal implementations.  It's well and good to say that implementors can implement it any way they want, but they can't.  Given a sequence like:
          program main
          enable (all_conditions)
          call BBB
          end enable
          end
          subroutine BBB
          call CCC
          call DDD
          end
If CCC signals a fault then nobody will expect that DDD is executed.  The problem will be magnified when BBB is a third party vendor supplied routine with a name like "INTEGRATE" and CCC and DDD are passed in functions.

5)  I think the proposal ignores or confuses the distinction between compile time and run time.  I don't see any advantage for allowing compile-time illegal constructs to be OK if they are imbedded in an ENABLE block. Also,... I think much more needs to be written to make clear that a previously non-standard conforming run-time action is OK if it is trapped by an ENABLE.  I think this is mostly a writing exercise, not a concept problem. 

--------------------------------------------------------------------------------------------------------

 - The MANY_ONE condition seems out of place.  The run-time cost for this check looks to be prohibitively high.  Will this be of value? (More likely, wouldn't a sophisticated user rather make localized, special checks, relying on his/her knowledge of the program?)  We could provide a new intrinsic to test whether a vector is unique, if that's an important concern.

 - The handling of the propagation of an error up the call chain is still not right.  Casting it as a "return" provides an elegant solution to the formal definition (simply do what you'd do when the procedure returns.)  Unfortunately, when a subprocess is aborted mid-stream, it's not (in the general case) appropriate to let the caller continue.  Not only is the function return value in danger, but any affects on OUT parameters or COMMON or module data are left in an incomplete and unknown state.  The caller must not be allowed to continue at all...

 - An ENABLE statement can appear with a null list of conditions. Is this the same as listing an explicit "ALL_CONDITIONS"?  This seems to be the case when the block is empty.

 - One can provide a list of exceptions on both the ENABLE and HANDLE statements.  How do these correlate?

 - The notion of "handling a condition" is central to the proposal.  I find myself too easily confused in trying to relate the two aspects of "handling".  By listing a condition on the ENABLE statement, a block presumably "handles" a condition.  Yet, since "handle" intuitively means "do something about", the condition is only handled insofar as there is code -- in the handle block -- to do something with it. Consider the following:

   ENABLE (OVERFLOW)
      ....
   HANDLE
      print *, "Overflow has occurred"
   END ENABLE

  This works fine in a simple case, and the code has "handled" overflow. In an interesting program, other things may be going on.  Say I've enabled something else, divide_by_zero, at a higher level, and that condition signals.  Now I get a wrong message about overflow.  I will still find out about the other condition because divide_by_zero is not formally "handled" by this handler and thus is not automatically reset. (Note that that would remain true even if I inserted code to detect and report on divide_by_zero into the handle block.)

 - A handler, in the general case, has the task of determining which of several conditions is signalling and performing appropriate actions. The natural control structure for such a task is a CASE construct, or at the very least IF-THEN-ELSE.  Due to the way conditions are represented, however, those structures are not easily usable.  The code is thus bulky and prone to error under maintenance.   Here's the way I understand one must do things.

  ENABLE (OVERFLOW, DIVIDE_BY_ZERO)
      ....
   HANDLE
	CONDITION_INQUIRE (Overflow, overflow_stat)
	IF (overflow_stat .ne. 0) 
	      print *, "Overflow has occurred"
	ENDIF
	CONDITION_INQUIRE (Divide_by_Zero, zdiv_stat)
	IF (zdiv_stat .ne. 0) THEN
	      print *, "Divide by Zero has occurred"
	ENDIF
 	 !! ELSE !!  (don't forget to list all the above conditions)
	IF (overflow_stat .eq. 0 .and. zdiv_stat .eq. 0) THEN 
			! none of the above; resignal
		ENABLE
		END ENABLE
	END IF

   END ENABLE
 

 - We need even more flexibilty in the linking of specific events to specific conditions. ... the problem of a processor which performs integer operations in the floating-point unit, and thus ends up with floating-point exceptions.  Other examples arise from time to time, often with special instructions.  Few processors have a "negative argument" interrupt, for instance.  It's important that the Standard allow that behavior, in the interest of efficient implementation.

Some Additional Philosophical Thoughts:

Complexity:  Two Different Paradigms
  I believe that there are two substantially different paradigms involved here.  In trying to address both, simultaneously, we are led to an overly complex solution.  The two paradigms are nicely captured by the second paragraph of the proposal: "The handle block may contain very carefully written code that ...  circumvents the problem, or may arrange for a graceful termination".
 
  From an implementation point of view, there is potentially a vast difference between the two.  In the first, we need only to abort the offending part of the program and deliver control to a designated point. That point is typically fairly distant, and the handling is usually fairly coarse-grained. (This kind of support is, in fact, what we generally find as existing practice.)  From a formal point of view, the exact behavior may be hard to define formally, but the user can generally cope quite easily on an operational basis.  This paradigm also fits well with what the underlying hardware can offer. Many platforms can trap when erroneous arithmetic operations occur; all I need is a way to enable the traps and a way to specify where to go when it happens.  I, the programmer, can then return to use my usual debugging tools to analyze the problem.  This kind of handling can easily be fitted on top of an existing program with minimal modification.

  The second paradigm reaches deeply into the very design of the program. The algorithms are carefully crafted to balance speed and generality, with trouble spots and recovery mechanisms finely matched to one another.  This is an attractive idea, but it's also really new to the wider community.  Because it requires substantial modification of the program, it seems unlikely to be heavily used by any but a few specialized projects.

  The current proposal, as noted, purports to handle both paradigms. Precise detection however, is by and large not relevant in the first paradigm.  By definition, the unexpected events are unanticipated: If the programmer knew they were going to occur, s/he would have provided for them.  Most of the complexity stems from support of the second. Perhaps we could have a much smaller, less complex, less expensive solution by setting our sights lower.

--------------------------------------------------------------------------------------------------------

A major integration issue is the wording in the standard of all the things that are "forbidden" in f90, but should presumably generate exceptions under this proposal.  there appears to be agreement in the email discussion that "a few more edits are needed." I'm not at all convinced that this is just a few small edits. I'd like to see the whole package to evaluate that.

I don't like the mismatch between signal names being specified as bnf names, but condition_inquire returning character values. I also find it a bit presumptuous to claim that an array size of 20 will *always* be sufficient to return all the conditions defined by the standard.

Speaking of condition_inquire, I would think this would more naturally be spelled inquire_condition.  It is an executable statement, and executable statements are generally named using imperative verb forms (such as handle, signal, etc.)  This is, of course, not a major issue, just a nit.

Another small nit.  I agree with the objections expressed to requiring the signal value to be a literal 0 constant.  A named constant should certainly be allowed, and once you do that, you might about as well forget the arbitrary restrictions and say that it can be any expression that evaluates to 0.

--------------------------------------------------------------------------------------------------------

... I am also concerned about the interaction with automatic deallocation.  ...

--------------------------------------------------------------------------------------------------------

I believe that the "quiet" condition should be specified with a symbolic constant (or a user specified constant) rather than requiring 0. I also prefer the RESIGNAL statement to the NULL ENABLE; END ENABLE construct (or as a redundent form).  Both the "0" and the NULL construct are difficult to find by grepping source.

--------------------------------------------------------------------------------------------------------

The interaction of INTEGER_OVERFLOW and run-time libraries is a still a significant problem.  The approach using two version of all library routines is a significant burden to vendors, considering that many of the exceptions signaled may be "ok."

The interaction of automatic deallocation with a transfer of control far up the call chain is an appalling amount of implementation for a CCI revision of the standard.

<<X>> said:
>
>Extra words are needed to describe the interaction of the automatic deallocation proposal and >this proposal. Presumably if a transfer of control to a handler results in one or more routines >being dropped from the call stack then the appropriate deallocations must take place. This must >be explicit.
>

<<Y>> said:
I do not see why. The proposal currently makes clear that the transfer is as for a return: " If there is no handler for a signaling condition (for example, if a condition signals outside any enable construct for the condition), a transfer of control as for a return statement takes place in a procedure or as for a stop statement takes place in a main program."

I say:
I think the extra words <<X>> wanted is that that "the transfer is almost as for a return (except automatic deallocation does not have to be done)."  I'd like them too.  This is a major implementation issue. It's not too big a deal to do a setjmp/longjmp kind of stack unwind. Most vendors will have some C library code they can snatch and stick into the Fortran library.  Basically, what you're doing is just poppin stack frames till you get you get to the stack frame you saved in a magic variable.  When you suddenly add automatic deallocation to this process, before you can pop the stack frame, you have to check some magic stack frame location that points you to the automatic deallocation code that has to be executed.

Ok, now what happens when the stack frames between the handler and the signaling procedure are not Fortran routines.  These non-Fortan routines are not going to have magic stack frame info.  You won't even be able to tell that these stack frames aren't from a Fortran procedure.  I am sure that considering interoperability is a major issue these days, no one would be trite enough to say that it's not a standard-conforming program, so we don't care what happens.

So, now we've the few little words: "a transfer of control as for a return statement" involving all language products; all runtime libraries; tools, such as the linker; and maybe the OS for good measure.

--------------------------------------------------------------------------------------------------------

The utility of the nested enable without a handler is not clear to me. The comment in example 4, noting that none of the code before the inner enable block could cause an overflow seems a bit of a special case - for many conditions or more general conditions, or more complicated code, one couldn't be sure that code wouldn't raise exceptions in the outer enable, hence the handler couldn't assume that variables defined within the outer enable were defined and so exceptions from the inner and outer enables couldn't be distinguished. (because state variables couldn't be assumed to be defined). I think it might be a source of many errors. On the other hand, it could be convenient to establish a handler for the whole routine. 

  I would like to think that constraints could restrict the handler's use of variables defined within the enable blocks, but plainly this isn't feasible.

  If a handler, called for a condition, raises an exception then completion of the handle-block seems to be processor dependent. (eg: handler uses an undefined variable and UNDEFINED may or may nor raise a condition). Does ignoring the new exception count as completing the handle-block and quieting the original condition? I'd guess so, or is it a non-standard conforming program? If the new exception was handled elsewhere, then the original condition would still signal?

  In Example 5, the INEXACT condition is tested, presumably assuming ZERO_SOLVER may set it. If the proposal was extended to user-defined conditions, would conditions that might be raised be part of the interface of a function?

  It seems hard to allocate a conditions-array without knowing how many conditions are signalling. Is another statement CONDITION_COUNT(<scalar-default-integer.) required? It would be if user-defined conditions were allowed.

  Should there be READ_WRITE error for INTENT(IN) or INTENT(OUT) arguments?

  8.1.5 uses 'associated' in an everyday sense. Could we use another word?

  Can 'signalling condition' and 'handled condition' be given definitions?

--------------------------------------------------------------------------------------------------------

When a signaling condition causes a transfer of control the destination of the transfer should be limited to a handler or as in a stop statement. The concept of the transfer being as a return in a procedure will be quite unusable and unreliable. It should be eliminated from the proposal.  It is fine to permit the processors the freedom to be inexact as to how soon a condition is recognized to be signaling (there are means provided by the proposal such as IMMEDIATE and SIGNAL that can help limit the duration of the inexactness), but once a signal has been recognized by the processor and the processor starts to alter the "normal" flow of the program then the user should be able to expect the handler code they've carefully supplied will be executed without much delay.  And more importantly without allowing too much further damage to occur.  If a user has coded an explicit SIGNAL within a function that has no ENABLE in it, then the expectation will be that a handler for the condition further up the call chain





ll be the next thing that is executed.  The semantics given in the paper should match this expectation.

The requirement that conditions be detected within intrinsic procedures invoked within ENABLE blocks has much larger impact on implementations then previously understood.  We had discussed at meeting 130 the idea of providing two versions of each intrinsic; one with all conditions enabled (referred to as the "slow" intrinsic implementation) and the other with no conditions enabled (ie the "fast" intrinsic).  But that is not sufficient.  If a user has enabled just the condition OVERFLOW, then they will not expect that INTEGER_OVERFLOW will be detected for them within intrinsics that they invoke.  With 2s-complement arithmetic an intermediate value can overflow and the final result will still be "ok". Users will not want their "ok" intrinsic routine results to be causing INTEGER_OVERFLOW exceptions to be signaled (and causing early termination of their programs as they don't have a handler for INTEGER_OVERFLOW).  So the implementations must either provide multiple "slow" intrinsics, or pass to the "slow" in





nsic an array indicating which conditions should be allowed to percolate back up to the user with the remainder handled within the intrinsic (even then I suspect the code would be hard to write).

There is a further problem with handling conditions such as INTEGER_OVERFLOW within intrinsics. For performance reasons it is sometimes valuable to take advantage of the way 2s-complement arithmetic works and force an overflow to occur. For example

"if ( min <= i .and. i <= max ) then ... "

is equivalent to

"if ( huge(i) - max + i >= huge(i) - max + min ) then ... "

thus allowing one comparison to be used instead of two.  But overflow occurs in the intermediate values.  This type of performance code occurs within intrinsics.  To provide a version of such intrinsics with INTEGER_OVERFLOW enabled, would mean recoding these intrinsics to either use the two comparison expression or somehow indicate that this overflow should not raise the exception.  Either way this is quite a bit of work. Part of the argument for dropping user defined conditions was to allow the ENABLE proposal to be included in the Fortran 95 definition so that implementations would soon be available.  If INTEGER_OVERFLOW is to be enabled within intrinsics then the time needed to rewrite intrinsics will necessarily delay the availability of implementations.

These two concerns about intrinsic routines also apply to library routines used to perform some intrinsic operations out of line. For example exponentiation is sometimes performed by some processors with library calls. Reworking such library routines may take quite a bit of effort.

non-conforming program.  In the ENABLE proposal it appears that previously illegal programs are now legal if an ENABLE is present.  Does that mean that if a program such as the following exists that the processor can not detect the error at compile-time?  There needs to be words to address this inconsistency.

    Program M
      integer a(10)
      ENABLE MANY_ONE
        a( (/ 1,3,1 /) ) = 5
      HANDLE
        ...
      END ENABLE
    END PROGRAM
As written a user may expect that this is a valid program and that the processor must NOT detect the error at compile-time. Whether the processor can or cannot detect the error at compile-time needs to be explicitly indicated.

Extra words are needed to describe the interaction of the automatic deallocation proposal and this proposal. Presumably if a transfer of control to a handler results in one or more routines being dropped from the call stack then the appropriate deallocations must take place. This must be explicit.

In the last part of 8.1.5 there is the sentence: "A condition must not signal if the signal could arise only during execution of a process not required by the standard." While the purpose of this statement may be to limit the examples as shown in the proposal, it appears to also effect the following example:

        ENABLE  (OVERFLOW)

           IF (( A > B ) .OR. ( expression_that_overflows )) THEN
            ...
        HANDLE
          ...
        END ENABLE

The evaluation of "expression_that_overflows" is not required by the standard as indicated by 7.1.7.1 if 'A' is greater than 'B'. So the sentence in 8.1.5 indicates that a processor must not allow the "expression_that_overflows" to cause a signal. This sounds like an unintentional side effect of the sentence.

In the 3rd footnote of 8.1.5.1 the phrase '(or a stop or return)' should be deleted as ENABLE; END ENABLE within another ENABLE that handles a condition, will only transfer to the handler.

The discussion in section 8.1.5.2 on which variables become undefined needs to be readdressed.  It should apply to all variables defined or redefined within the ENABLE (including those updated by procedures called from the ENABLE).  Attempting to exclude those variables updated within a nested ENABLE just adds to the complexity of the text without adding significant benefit.  It makes the text hard to understand ... for example many might believe that example 4 shows how a loop can be written so that the handler can determine how many valid interactions of the loop have taken place.  But in that example, if and when the handler is executed 'K' is undefined and must not be referenced within the handler.  This error in the example is hard to spot given the complexity of the text describing what variables become undefined.  (and example 4 is a relatively easy example to understand).

The effect of IMMEDIATE is that each <executable-construct> that can signal one or more of the conditions is treated as if it were followed by an empty enable block. This may not be as immediate as some people will expect for the following example:

   ENABLE (BOUND_ERROR)
     ...
     ENABLE IMMEDIATE( INTEGER_OVERFLOW )
10     DO I = HUGE(0)-3, HUGE(0), 2
20       A(I) = ....     ! A has bounds (HUGE(0)-10:HUGE(0))
         ...
30       CONTINUE   ! last statement of loop
40     END DO
     HANDLE  ! INTEGER_OVERFLOW
      ....
     END ENABLE
   HANDLE   ! BOUND_ERROR
     ....
   END ENABLE

in this case the iteration count is 2, but then on the 2nd loop through the value of I will have overflowed. Note that the IMMEDIATE is as if the loop were coded as:

     ENABLE IMMEDIATE( INTEGER_OVERFLOW )
10     DO I = HUGE(0)-3, HUGE(0), 2
20       A(I) = ....     ! A has bounds (HUGE(0)-10:HUGE(0))
         ENABLE; END ENABLE
         ...
30       CONTINUE   ! last statement of loop
         ENABLE; END ENABLE
40     END DO
       ENABLE; END ENABLE
     HANDLE  ! INTEGER_OVERFLOW
     ...

Note that there is no 'ENABLE; END ENABLE;' pair between statements 10 and 20. The setting of 'I' is done as part of the 'END DO'. The next forced check is after statement 20 has executed. By then a BOUND_ERROR has occurred and the compiler has just as much right to transfer control to the BOUND_ERROR handler as to the INTEGER_OVERFLOW handler. So IMMEDIATE may have surprising results. The term itself 'IMMEDIATE' has makes it sound like exceptions will be detected earlier then what is actually required. It puts a model in the user's mind that is not reflected by the semantics. The interaction of IMMEDIATE and optimization being permitted is not clear. Text earlier in the document indicated that given 
    X = 1.0/Y
    X = 3.0
that the signaling of DIVIDE_BY_ZERO is processor dependent. Will readers misunderstand IMMEDIATE and think IMMEDIATE overrides this earlier statement?

Example 1. - There should be further text that explains that if INTRINSIC or OVERFLOW conditions occur, that condition will continue to signal as well as the INEXACT condition. Isn't that part of why the example is being shown?

In the section on CONDITION_INQUIRE the last footnote describes how to use the CONDITION_INQUIRE more than once to get all the names of the signalling conditions. If the last element of the character array is not blank then the signaling conditions must be set to quiet and another use of CONDITION_INQUIRE is executed.  This appears to be unnecessarily awkward.  It would be better to simplify this mechanism.  We should not be knowingly adding a feature that is awkward to use.  Alternate approaches to consider are: (1) provide a variation of the statement that allows the user to ask how many conditions are signaling, then they can allocate an array of that size and invoke CONDITION_INQUIRE just once. or (2) require that the argument be a pointer to a character array of rank one. The processor then allocates that array to be the appropriate size.

The MANY_ONE condition needs further description. It is too easy for a reader to think that the following invalid program will be detected at run-time

   PROGRAM M
     INTEGER IARRAY(10)
     CALL FRED( IARRAY( (/1,4,3,1/))
   END
   SUBROUTINE FRED( IARG)
     INTEGER IARG(4)

     ENABLE MANY_ONE
       ....
       IARG = 3    ! illegal
     HANDLE
       ...
     END ENABLE
   END SUBROUTINE
It is very easy to think that the assignment of IARG = 3 will somehow cause the exception to be raised as the associated actual argument is a many-one array; ie it is really the many-one array that is being assigned the value 3.

Floating-point conditions versus integer conditions. There are processors that for various reasons implement integer arithmetic using floating point instructions. There needs to be an allowance for such implementations - perhaps indicating that enabling an integer condition may actually result in a floating point condition being signaled.

Nits:
 Example 1. The line REAL MULT ... is missing a closing ')'

 Example 9. In the paragraph after the example "in one of the procedure invoked" should be "in one of the procedures invoked" (an 's' was missing).

 Section 15. last footnote (just before 15.1) - the phrase "call CONDITION_INQUIRE again" is not appropriate as CONDITION_INQUIRE is not a routine but a statement.

Three questions about the ENABLE proposal.

1. I'm a bit confused by the following paragraphs from the ENABLE proposal: (This is from the section 8.1.5.1)

>Execution of an enable statement causes a transfer of control if a signaling
>condition is handled by the enable construct or any enable construct within
>which it is nested.  If the enable statement is nested in an enable block that
>has a handler for a signaling condition, the transfer is to the handler of the
>innermost such enable block.  Otherwise, it is as for a return if in a
>subprogram, or a stop if in a main program.  The values of the conditions are
>not altered.

>[Footnote: In an enable block, the pair of statements
>       ENABLE
>       END ENABLE
>has a checking effect.  If any handled condition is signaling, there will be a
>transfer of control to an outer handler (or a stop or return).The values of
>the conditions are not altered.]

I'm not sure when it is possible to write "ENABLE; END ENABLE" that will cause a subprogram to return or a main program to stop. Let me give some more details.

If I write  "ENABLE; END ENABLE" and the code is not statically nested within another enable construct, will it have any affect even if some of the known conditions are signaling?  The first sentence that I quoted above, indicates that the transfer of control occurs if the signaling condition is amongst the conditions handled by this enable construct. Well, in the case of "ENABLE; END ENABLE" there are no conditions that are handled. The first sentence also indicates that the transfer of control occurs if the signaling condition is handled be an outer handler. But in that case the transfer is to the appropriate handler. So when does an ENABLE; END ENABLE cause a transfer of control that is equivalent to a stop or a return?

2. In the first paragraph of 8.1.5.2, there is the sentence

"Any variable that might be defined or redefined by execution of a statement of the enable block outside any enable construct that handles the condition and is nested within the enable block is undefined,..." 

Doesn't that mean in the example 4, if a signal occurs that transfers control to the handler that the first K-1 steps can not be assumed to be saved?  Note that all the variables updated within the outermost ENABLE are within the construct but not within any nested ENABLE that handles the condition.  Any use of any of the variables after the transfer to the handler is invalid as it is considered a use of an undefined variable.

>   ENABLE (OVERFLOW)
>   ! First try a fast algorithm for inverting a matrix.
>   : ! Code that cannot signal overflow
>   DO K = 1, N
>      ENABLE
>      :
>      END ENABLE
>   END DO
>      ENABLE
>      :
>      END ENABLE
>   HANDLE
>   ! Alternative code which knows that K-1 steps have executed normally.
>   :
>   END ENABLE

>Here the code for matrix inversion is in line and the transfer is made more
>precise by adding to the enable block two enable constructs without handlers.

--------------------------------------------------------------------------------------------------------

We think X3J3 should try to do the overflow, underflow, break, etc., exceptions, and not try to do the integration required for I/O and allocation exceptions.

--------------------------------------------------------------------------------------------------------

The restriction against transfers of control out of enable constructs is too onerous and would make the facility unusable for the average user.  Moreover, this restriction would encourage the misuse of the facility in certain cases to simulate a transfer of control.  Here is a modified version of Example B from the proposal illustrating the kinds of abuses I have in mind:

   ! Example B-prime:
   !    This example is similar to Example B, but (mis)uses two
   !    additional conditions, ALLOCATION_ERROR and DEALLOCATION_ERROR,
   !    to simulate the effect of CYCLE and EXIT.  It is assumed that
   !    the fast algorithm does not make use of the ALLOCATE statement.
   ENABLE (OVERFLOW)
   ! First try a fast algorithm for inverting a matrix.
   : ! Code that cannot signal overflow
      ENABLE (DEALLOCATION_ERROR) ! Set up to simulate EXIT
         DO K = 1, N
            ENABLE (ALLOCATION_ERROR) ! Set up to simulate CYCLE
               :
               IF ... SIGNAL (ALLOCATION_ERROR,-1) ! Simulate CYCLE
               :
               IF ... SIGNAL (DEALLOCATION_ERROR,-1) ! Simulate Exit
               :
            HANDLE ! Receives control on simulated CYCLEs
            END ENABLE ! for simulated CYCLEs
         END DO
      HANDLE ! Receive control on simulated EXITs
      END ENABLE ! for simulated EXITs
      ENABLE
      :
      END ENABLE
   HANDLE
   ! Alternative code which knows that K-1 steps have executed normally.
   :
   END ENABLE

If necessary, the rules about which variables become undefined when a condition is signalled will have to be broadened in the presence of transfers of control (even conditional transfers) inside an enable construct.

The lack of user-defined conditions is also unacceptable.  It will inevitably lead to the misuse of intrinsic conditions.  Furthermore, I no longer think that previous solutions to this problem, namely, a single catch-all USER condition or global scope for all condition names is acceptable.  User condition names should be declared and should follow the same scoping rules as for variables.

The interaction of this proposal with the automatic deallocation proposal is not spelled out and appears to be tricky: the latter eliminates the allocation status of "undefined", but this appears to be what is required if an ALLOCATE statement occurs somewhere in the innermost ENABLE block when a condition is signalled.

This proposal give no guidance to an IEEE implementer as to whether an ENABLE statement is an appropriate vehicle to control whether numeric exceptions signal or propagate.  At first glance, it would seem to be a good choice, but I suspect there are a variety of pitfalls to such an approach.  There should be some discussion of this issue in a footnote.

In the past, we have always interpreted a program that violates an absolute prohibition in the text as non-conforming and permitted the processor to deal with such programs any way it sees fit; the exception handling facility would limit the freedom of the processor to deal with many violations of the existing rules, but the edits in this proposal do not reflect this.  Here are just a few examples of conditions and conflicting absolute prohibitions:

   BOUND_ERROR
     64/31, "The value of a subscript in an array element must be       within the bounds for that dimension."
     65/33-34, "Each subscript [in a sequence of subscripts by an array section] must be within the bounds for its dimension unless the sequence is empty."

   SHAPE
     90/5, "For an intrinsic assignment statement, <variable> and <expr> must conform in shape..."

   MANY_ONE
     89/31, "The <variable> [in an array assignment statement] must not be a many-one array section."

   PRESENT
     179/31, "A dummy argument that is not optional must be present."

   UNDEFINED
     61/4, "A reference is permitted only if the data object is defined."

>In a handler, if it is desired to resignal the signaling conditions, this can
>be achieved with the pair of statements
>   ENABLE
>   END ENABLE

An empty ENABLE/END ENABLE may be redundant with a RESIGNAL statement, but the latter is much more intuitive.

>A transfer of control to the next outer handler for a signaling condition (or
>a return or stop) occurs without the values of the conditions changing.

In a handler, is it possible to first set some conditions quiet, then resignal any conditions that are still signalling?  If so, what happens if a condition is raised _within_ such a handler -- does the set of signalling conditions itself become imprecise?  Note that it is not possible to avoid such imprecision by nesting additional enable blocks since the first ENABLE encountered inside the handler causes the resignal.
>
>Neither a handle statement nor an end-enable statement is permitted to be a
>branch target.  A handle-block is intended for execution only following the
>signaling of a condition that it handles, and an end-enable statement is not a
>sensible target because it would permit skipping the handling of a condition.
>
>Branching out of an enable construct is not permitted.  This limits the extent
>of uncertainty over which statements have been executed when a handler is
>entered.

This is so restrictive as to make the enable facility almost useless; for example B, above, it would preclude the use of CYCLE or EXIT in the "fast loop".

>A processor may signal a condition while executing a statement that is not in
>an enable block for the condition.  If in a subprogram, a return is executed
>without alteration of the values of the conditions.  If in a main program, a
>stop is executed and the processor must issue a warning on the default output
>unit.

There should be a requirement that the warning indicate which conditions are signalling.

--------------------------------------------------------------------------------------------------------


>4. He prefers the explicit CHECK and RESIGNAL statements to the use of
>   ENABLE; END ENABLE
>     This is a decision that was made by WG5. It was felt to be
>     inconsistent to have a RESIGNAL statement and not have a CHECK
>     statement.

It seems to me that the statements we used to call CHECK and RESIGNAL really do exactly the same thing and therefore should be a single statement.  The latest proposal apparently agrees, and suggests that statement should be spelled, "ENABLE; END ENABLE" -- I'm merely suggesting an alternative spelling.

>
>5. He wants IMMEDIATE to separate the statements within a where construct.
>     It would be possible to define IMMEDIATE to do what he wants. We
>     would have to say that where constructs are replaced by the
>     equivalent set of where statements. I prefer to leave such an
>     extension to 2000, or even to defer the whole of IMMEDIATE to
>     2000.

I thought I was merely correcting an example to conform to your own specification.  The third (non-footnote) paragraph of section 8.1.5.2 of the proposal states,

   Any <executable-construct> of the enable block that might
   signal one or more of the conditions in the immediate list on
   the enable statement is treated as if it were followed by an
   <enable-construct> with an empty enable block and no handler.

A where construct is, indeed, an executable construct, but so is each assignment statement within the where construct.  I understand the above words to apply recursively to all constructs nested within other constructs, and that was my intent when I originally suggested some functionality like the IMMEDIATE clause.


>> I don't understand this -- does it mean that an enable statement
>> _inside_ a PURE procedure forces a merge of condition values across
>> all concurrently executing instances of the procedure? I suspect
>> not, but I don't see anything in the edits below that spells out
>> exactly what it does mean.
>It was not. A pure procedure on a processor may signal a condition and
>handle it locally without any reference to other processors. How about
>adding to the front of the last sentence:  "If an enable construct
>contains statements that spawn tasks,"?

That would definitely help.

>> >Any <executable-construct> of the enable block that might signal one or more
>> >of the conditions in the immediate list on the enable statement is treated as
>> >if it were followed by an <enable-construct> with an empty enable block and
>> > no handler.
>>
>> If a condition in the immediate list is signalled while evaluating the
>> <scalar-logical-expr> part of an IF statement, must the transfer of
>> control occur before execution of the <action-stmt> part of the IF
>> statement?
>No.

If my interpretation above about the effect of the IMMEDIATE clause on nested constructs is accepted, I'd prefer that the two parts of IF and WHERE statements be handled (for the purposes of the IMMEDIATE clause) as two separate statements, just as they are for the purposes of function side effects (7.1.7).  This is consistent with a mental model in which the IF and WHERE statements are simply degenerate cases of the corresponding constructs.

>> Why must a literal 0 be used to set combination conditions quiet?  A
>> symbolic constant (e.g., "QUIET") or even an initialization
>> expression with a value of zero would seem to work equally well and
>> be more regular.
>I see not point in the extra generality. 0 is the only permitted value.

One obvious point would be the mnemonic value of defining a parameter named QUIET to be used to turn conditions off.  In any case, I thought one of the goals of the "language evolution" proposal was to get rid of unnecessary restrictions in order to make the language more regular.  Requiring a literal 0 is akin to the strange requirements on the operands of STOP and PAUSE, recently debated yet again on the interpretations list.

>> >Example 5:
>> >
>> This example illustrates my point about the need for user-defined
>> condition names.  INEXACT is not really an appropriate condition
>> name for a time-out situation, and is a particularly poor choice
>> for this example, since there is a very real likelihood that the
>> function F could, on an IEEE machine, itself raise the INEXACT
>> condition.
>Yes, it would be better to use SYSTEM_ERROR in this example

Yes, SYSTEM_ERROR is better than INEXACT for this example, but _only_ because it's less likely to actually occur in this particular problem, not because it's any more appropriate for the use to which it's being coerced.  My point was that, since there is no intrinsic condition that really signifies "timeout", and no way to define such a condition, one is forced to misuse some other intrinsic condition for this purpose.  There will be a significant long-term cost to the lack of user-defined condition names in terms of code libraries littered with inappropriate uses of intrinsic conditions.  These will be difficult to maintain and will also make it difficult for implementers to make even minor changes in condition signalling.

I'd suggest cutting this example entirely.  If the proposal does not support user-defined exceptions, we should not suggest ways of perverting it to do so.

>> >Note that the statements of a WHERE construct are not tested separately.
>>
>> What?!  The statements of a WHERE construct are executed in sequence so
>> they should be tested separately.  The equivalent construct should be:
>>
>>       ENABLE (OVERFLOW)
>>           A = B*C
>>           ENABLE
>>           END ENABLE
>>           WHERE(RAINING)
>>                X(:) = X(:)*A
>>                ENABLE
>>                END ENABLE
>>           ELSEWHERE
>>                Y(:) = Y(:)*A
>>                ENABLE
>>                END ENABLE
>>           END WHERE
>>           ENABLE
>>           END ENABLE
>>     HANDLE
>>        .....
>>     END ENABLE
>>
>> Moreover, if there were multiple assignments within one of the WHERE
>> blocks (at least, multiple assignments that could conceivably cause
>> an overflow), each such assignment should, in the equivalent
>> construct, be followed by an ENABLE/END ENABLE pair.
>The code you have constructed is not legal Fortran. Where constructs
>may contain only assignment statements. See 5 above.

And, in F95 presumably, nested WHEREs and FORALLs.  This is part of the integration problem -- for the IMMEDIATE clause to be useful, the _effect_ has to be to check for signalling conditions after every lowest-level executable construct.  If you choose to describe this in terms of equivalent explicit code, then you have to make sure that equivalent code is legal.  I don't think there would be a serious problem with permitting a CHECK or SIGNAL * statement in WHERE blocks, but permitting an empty enable block opens up a big can of worms.  You then have to go back and review a number of previous decisions (whether to add an IMMEDIATE clause at all, what it's effect should be if you do add it, how you describe it, whether to drop the CHECK statement, etc.) and make some adjustments.  This is an iterative process, and you don't have much time (2 meetings!) for the iterations to converge and the text to get written.

>> >122/17-18. Replace sentence by
>> >   If an error condition (9.4.3) occurs during execution of an input/output
>> >   statement that lies in an enable block for the IO_ERROR condition or
>> >   contains an ERR= specifier:
>>
>> Poor wording -- it sounds like the enable block contains an ERR= specifier.
>I do not think it parses with this interpretation, but how about transposing
>the clauses:
>   If an error condition (9.4.3) occurs during execution of an input/output
>   statement that contains an ERR= specifier or lies in an enable block
>   for the IO_ERROR condition:

Much better.

>> >122/25. After 'continues with' add 'the handle block or'
>>
>> This text does not spell out whether the ERR= statement or the handler
>> takes precedence when both are present.
>Yes, an edit is desirable. The definition of IO_ERROR makes it clear that the
>condition does not signal in a statement with an END=.

Yes I know, but this should be spelled out here as well.

>> >Footnote: An array size 20 will always be adequate to return the names of
>al
>   l
>> >the conditions defined by the standard.  If the final element of the
>characte
>   r
>> >array has the value blank, the names of all signaling conditions will have
>> >been returned.  If it is not blank, the user may set the conditions named
>> >quiet with SIGNAL statements and call CONDITION_INQUIRE again.?AA
>>
>> How?  The returned condition names are character values, but the
>> SIGNAL statement requires a <name> in the BNF sense, not a character
>> value.  One could write a big SELECT to handle the standard
>> intrinsic conditions, but there's no portable way to handle
>> conditions added by the implementation.
>Yes, we discussed this point in Edinburgh. The words have been chosen
>not to promise always to find all processor-defined conditions. We felt
>that this was a bit academic. Do we really expect that there are likely
>to be more than 20 processor-defined conditions? And even if there are,
>how often will more than 20 be signaling?

In that case it's better to say nothing, or to suggest that the user look at the processors documentation to decide how big to make the array.  By the way, I read the following sentence,

   If there are more signaling conditions than the size of the
   array, all elements are defined with condition names and which
   are chosen is processor dependent.

To meant that if the array was too small to hold all signalling conditions, then there was no requirement to put the standard ones first.  Was that the intent? If so, why? It would seem that a guarantee that the standard conditions would always come first would pretty much render this whole question moot.  If that was not the intent of the above sentence, then what _does_ it mean?

--------------------------------------------------------------------------------------------------------

> When a signaling condition causes a transfer of control the
> destination of the transfer should be limited to a handler or
> as in a stop statement. The concept of the transfer being as
> a return in a procedure will be quite unusable and unreliable.
> It should be eliminated from the proposal.  It is fine to permit the
> processors the freedom to be inexact as to how soon a condition is
> recognized to be signaling (there are means provided by the proposal
> such as IMMEDIATE and SIGNAL that can help limit the duration of the
> inexactness), but once a signal has been recognized by the processor and
> the processor starts to alter the "normal" flow of the program then the
> user should be able to expect the handler code they've carefully
> supplied will be executed without much delay.  And more importantly
> without allowing too much further damage to occur.  If a user has coded
> an explicit SIGNAL within a function that has no ENABLE in it, then the
> expectation will be that a handler for the condition further up the call
> chain will be the next thing that is executed.  The semantics given in
> the paper should match this expectation.

Well, there is nothing to stop an implementor doing this. It is just that we are not requiring it currently in case it would be hard on some systems. 

> 
> The requirement that conditions be detected within intrinsic procedures
> invoked within ENABLE blocks has much larger impact on implementations
> then previously understood.  We had discussed at meeting 130 the idea
> of providing two versions of each intrinsic; one with all conditions
> enabled (referred to as the "slow" intrinsic implementation) and the
> other with no conditions enabled (ie the "fast" intrinsic).  But that is
> not sufficient.  If a user has enabled just the condition OVERFLOW, then
> they will not expect that INTEGER_OVERFLOW will be detected for them
> within intrinsics that they invoke.  With 2s-complement arithmetic an
> intermediate value can overflow and the final result will still be "ok".
> Users will not want their "ok" intrinsic routine results to be causing
> INTEGER_OVERFLOW exceptions to be signaled (and causing early
> termination of their programs as they don't have a handler for
> INTEGER_OVERFLOW).  So the implementations must either provide multiple
> "slow" intrinsics, or pass to the "slow" intrinsic an array indicating
> which conditions should be allowed to percolate back up to the user with
> the remainder handled within the intrinsic (even then I suspect the code
> would be hard to write).

This is not the intention. A library code for matrix inversion might have a fast algorithm and a handler that deals with overflows by using a slower algorithm. The 'local' signaling of overflow is of no concern to the caller, provided everything is ok when the slow algorithm executes. Similarly, if an intrinsic is written to be robust even if some conditions signal during its execution, this is of no concern to the caller.  The important thing is that when INTRINSIC is enabled, the slow code is invoked that ensure that a condition signals if the procedure is unsuccessful. I believe the current wording is OK - a code could have been constructed that avoids integer overflows and always produces exactly the same results and we allow implementors to use short cuts that produce identical results. However, a footnote might help.

> 
> There is a further problem with handling conditions such as
> INTEGER_OVERFLOW within intrinsics. For performance reasons it is
> sometimes valuable to take advantage of the way 2s-complement arithmetic
> works and force an overflow to occur. For example
> 
> "if ( min <= i .and. i <= max ) then ... "
> 
> is equivalent to
> 
> "if ( huge(i) - max + i >= huge(i) - max + min ) then ... "
> 
> thus allowing one comparison to be used instead of two.  But overflow
> occurs in the intermediate values.  This type of performance code occurs
> within intrinsics.  To provide a version of such intrinsics with
> INTEGER_OVERFLOW enabled, would mean recoding these intrinsics to either
> use the two comparison expression or somehow indicate that this overflow
> should not raise the exception.  Either way this is quite a bit of work.
> Part of the argument for dropping user defined conditions was to allow
> the ENABLE proposal to be included in the Fortran 95 definition so that
> implementations would soon be available.  If INTEGER_OVERFLOW is to be
> enabled within intrinsics then the time needed to rewrite intrinsics
> will necessarily delay the availability of implementations.

See comments following the previous paragraph.

> 
> These two concerns about intrinsic routines also apply to library
> routines used to perform some intrinsic operations out of line.
> For example exponentiation is sometimes performed by some processors
> with library calls. Reworking such library routines may take quite
> a bit of effort.
> 
> ... there is an inconsistency with what is a
> non-conforming program.  In the ENABLE proposal it appears that previously
> illegal programs are now legal if an ENABLE is present.  Does that mean
> that if a program such as the following exists that the processor can
> not detect the error at compile-time?  There needs to be words to
> address this inconsistency.
> 
>     Program M
>       integer a(10)
>       ENABLE MANY_ONE
>         a( (/ 1,3,1 /) ) = 5
>       HANDLE
>         ...
>       END ENABLE
>     END PROGRAM
> As written a user may expect that this is a valid program and that
> the processor must NOT detect the error at compile-time. Whether
> the processor can or cannot detect the error at compile-time
> needs to be explicitly indicated.

This was a concern ... and early versions of the proposal had some words that tried to say that anthing normally caught at compile time should still be so caught. However, the subgroup felt that this was wrong. The programmer has explicitly coded an action to take place when the condition occurs and this intention should not be frustrated just because the event is detected (on some processors) at compile time.

> 
> Extra words are needed to describe the interaction of the
> automatic deallocation proposal and this proposal. Presumably
> if a transfer of control to a handler results in one or
> more routines being dropped from the call stack then the
> appropriate deallocations must take place. This must be explicit.
> 
I do not see why. The proposal currently makes clear that the transfer is as for a return: " If there is no handler for a signaling condition (for example, if a condition signals outside any enable construct for the condition), a transfer of control as for a return statement takes place in a procedure or as for a stop statement takes place in a main program."


> In the last part of 8.1.5 there is the sentence:
> "A condition must not signal if the signal could arise only during
> execution of a process not required by the standard."
> While the purpose of this statement may be to limit the examples as
> shown in the proposal, it appears to also effect the following example:
> 
>         ENABLE  (OVERFLOW)
> 
>            IF (( A > B ) .OR. ( expression_that_overflows )) THEN
>             ...
>         HANDLE
>           ...
>         END ENABLE
> 
> The evaluation of "expression_that_overflows" is not required by
> the standard as indicated by 7.1.7.1 if 'A' is greater than 'B'.
> So the sentence in 8.1.5 indicates that a processor must not
> allow the "expression_that_overflows" to cause a signal. This
> sounds like an unintentional side effect of the sentence.

Agreed. A word change is needed.

> 
> In the 3rd footnote of 8.1.5.1 the phrase '(or a stop or return)'
> should be deleted as ENABLE; END ENABLE within another ENABLE that
> handles a condition, will only transfer to the handler.
> 
Agreed.


> The discussion in section 8.1.5.2 on which variables become undefined
> needs to be readdressed.  It should apply to all variables defined or
> redefined within the ENABLE (including those updated by procedures
> called from the ENABLE).  Attempting to exclude those variables updated
> within a nested ENABLE just adds to the complexity of the text without
> adding significant benefit.  It makes the text hard to understand ...
> for example many might believe that example 4 shows how a loop can be
> written so that the handler can determine how many valid interactions of
> the loop have taken place.  But in that example, if and when the handler
> is executed 'K' is undefined and must not be referenced within the
> handler.  This error in the example is hard to spot given the complexity
> of the text describing what variables become undefined.  (and example 4
> is a relatively easy example to understand).

Yes, the wording in 8.1.5.2 is not quite right yet, but to repair it by saying that all variables defined or redefined within the ENABLE becode undefined is overkill. The intention for Example 4 is that a transfer to the handler can only occur from within one of the nested enables and the set of variables that is undefined consists of those defined or redefined within it. 

> 
> The effect of IMMEDIATE is that each <executable-construct> that
> can signal one or more of the conditions is treated as if it
> were followed by an empty enable block. This may not be
> as immediate as some people will expect for the following example:
> 
>    ENABLE (BOUND_ERROR)
>      ...
>      ENABLE IMMEDIATE( INTEGER_OVERFLOW )
> 10     DO I = HUGE(0)-3, HUGE(0), 2
> 20       A(I) = ....     ! A has bounds (HUGE(0)-10:HUGE(0))
>          ...
> 30       CONTINUE   ! last statement of loop
> 40     END DO
>      HANDLE  ! INTEGER_OVERFLOW
>       ....
>      END ENABLE
>    HANDLE   ! BOUND_ERROR
>      ....
>    END ENABLE
> 
> in this case the iteration count is 2, but then on the 2nd loop
> through the value of I will have overflowed. Note that the
> IMMEDIATE is as if the loop were coded as:
> 
>      ENABLE IMMEDIATE( INTEGER_OVERFLOW )
> 10     DO I = HUGE(0)-3, HUGE(0), 2
> 20       A(I) = ....     ! A has bounds (HUGE(0)-10:HUGE(0))
>          ENABLE; END ENABLE
>          ...
> 30       CONTINUE   ! last statement of loop
>          ENABLE; END ENABLE
> 40     END DO
>        ENABLE; END ENABLE
>      HANDLE  ! INTEGER_OVERFLOW
>      ...
> 
> Note that there is no 'ENABLE; END ENABLE;' pair between statements
> 10 and 20. The setting of 'I' is done as part of the 'END DO'.
> The next forced check is after statement 20 has executed. By then
> a BOUND_ERROR has occurred and the compiler has just as much
> right to transfer control to the BOUND_ERROR handler as to the
> INTEGER_OVERFLOW handler. So IMMEDIATE may have surprising
> results. The term itself 'IMMEDIATE' has makes it sound like
> exceptions will be detected earlier then what is actually
> required. It puts a model in the user's mind that is not reflected
> by the semantics. The interaction of IMMEDIATE and optimization
> being permitted is not clear. Text earlier in the document indicated
> that given
>     X = 1.0/Y
>     X = 3.0
> that the signaling of DIVIDE_BY_ZERO is processor dependent. Will
> readers misunderstand IMMEDIATE and think IMMEDIATE overrides this
> earlier statement?

I do not understand this at all. On the second iteration I has the value HUGE(0)-1. It has not overflowed. There is no third iteration.

> Example 1. - There should be further text that explains that
> if INTRINSIC or OVERFLOW conditions occur, that condition will
> continue to signal as well as the INEXACT condition. Isn't that
> part of why the example is being shown?

No. INTRINSIC and OVERFLOW are handled so are set quiet on completion of the handle block (see last para. of 8.1.5.1).

> In the section on CONDITION_INQUIRE the last footnote describes
> how to use the CONDITION_INQUIRE more than once to get all the
> names of the signalling conditions. If the last element of the
> character array is not blank then the signaling conditions must be
> set to quiet and another use of CONDITION_INQUIRE is executed.  This
> appears to be unnecessarily awkward.  It would be better to simplify
> this mechanism.  We should not be knowingly adding a feature that is
> awkward to use.  Alternate approaches to consider are:
>  (1) provide a variation of the statement that
> allows the user to ask how many conditions are signaling, then
> they can allocate an array of that size and invoke CONDITION_INQUIRE
> just once.
> or (2) require that the argument be a pointer to a character
> array of rank one. The processor then allocates that array to be
> the appropriate size.

Agreed. I prefer (2).

> The MANY_ONE condition needs further description. It is too easy
> for a reader to think that the following invalid program will be
> detected at run-time
> 
>    PROGRAM M
>      INTEGER IARRAY(10)
>      CALL FRED( IARRAY( (/1,4,3,1/))
>    END
>    SUBROUTINE FRED( IARG)
>      INTEGER IARG(4)
> 
>      ENABLE MANY_ONE
>        ....
>        IARG = 3    ! illegal
>      HANDLE
>        ...
>      END ENABLE
>    END SUBROUTINE
> It is very easy to think that the assignment of IARG = 3 will
> somehow cause the exception to be raised as the associated actual
> argument is a many-one array; ie it is really the many-one array
> that is being assigned the value 3.

No. IARG is a dummy array, not a many-one section. It is not even associated with a many-one section since actual arguments with a vector subscript are treated as expressions.

> Floating-point conditions versus integer conditions.
> There are processors that for various reasons implement integer
> arithmetic using floating point instructions. There needs to be
> an allowance for such implementations - perhaps indicating that
> enabling an integer condition may actually result in a floating
> point condition being signaled.
> 
No comment.

> Nits:
>  Example 1. The line REAL MULT ... is missing a closing ')'
> 
>  Example 9. In the paragraph after the example "in one of the
>  procedure invoked" should be "in one of the procedures invoked"
>  (an 's' was missing).
> 
>  Section 15. last footnote (just before 15.1) - the phrase
>  "call CONDITION_INQUIRE again" is not appropriate as CONDITION_INQUIRE
>  is not a routine but a statement.
> 
Agreed.

--------------------------------------------------------------------------------------------------------


> 2. I do not think the cutdown proposal removing user defined conditions is 
> acceptable. The "simplifications" it produces are neither real 
> simplifications nor is the functionality left adequate. The refusal to 
> properly deal with the scoping questions related to conditions user or 
> intrinsic leads to far too much ad hoc irregualar language design to be 
> acceptable. Not to have user defined conditions in the context of a 
> semantically extensible language like Fortran 90/95 simple adds to the 
> general irregularity which will cripple Fortran relative to C++. The user 
> can extend the set of types, procedures (functions and subroutines), and 
> operators. To add conditions without adding the facility for extending the 
> set of conditions from the intrinsic is simply an unacceptable 
> irregularity and an unacceptable constraint on the functionality. The 
> problems involved in adding this functionality are central to and 
> fundamental for a well designed simple regular language extension. Ducking 
> the issues raised by this requirement produces complication because it 
> introduces irregularity. ...
> 

> 3. ... disallowing transfer of control out of 
> ENABLE/HANDLE blocks is unacceptable. This is another ad hoc irregularity. 
> No other Fortran block has this restriction. The condition handling 
> extension should be properly designed to preserve this general feature of 
> block control constructs. The consequences of transfer out if used might 
> be reduced performance but the choice should be left to the user and the 
> efffect properly defined.

Strictly speaking, the decision was made in Edinburgh and we should stick with it. One merit of the present rule is that it is restrictive and could be broadened in 2000. However, I do not see it as a big issue. The wording about what is undefined would need to be altered if we allowed these transfers, which would not be very difficult. What do other people think?

> 4. I think spelling RESIGNAL as ENABLE;ENDENABLE seriously unfriendly. I 
> cant help feeling that the coincidence that the empty block has the effect 
> of a resignal is a further sign of a possible design problem. 

This is a decision that was made consciously by wg5 in Edinburgh and we should not change it.

> 5. Conditions should not be defined to have default integer values as 
> such. The standard should merely define that there must exist a mapping 
> between condition values and the default integers. This mapping should be 
> constrained so that "quiet" conditions map to zero, processor initiated 
> signals map to positive integers and user initiated signals map to 
> negative. The internal representation and hence actual value of a 
> condition should not be defined by the standard but left up to the 
> processor.
> 
Exactly such a mapping is how most implementors will treat this. The point is already covered by item (6) in 1.3.2.


> 6. The imprecision of where conditions signal and where they dont is 
> unacceptable. To say that for some conditions you have to say
> ENABLE (condition) to cause them to signal and others that signal whether 
> enabled or not. If a condition is not enabled it should be exactly F90 
> rules, entirely processor dependent.

Actually we are very near this now. Only INSUFFICIENT_STORAGE always signals outside enables. This is because we need to cover the important case of a failure while executing the specification statements.

> All currently unenabled conditions 
> included on an ENABLE statement should be set to quiet on entry to an 
> enable block, rather than as currently defined where such a condition 
> would cause a transfer of control rather than entry to the enable block.

This idea has not been discussed and I find it quite attractive. What do others think? 

> I think this is related to the refusal to properly handle the scoping of 
> conditions and therefore the domain of program where this extension might 
> effect the execution of the program. A program and program unit which does 
> not include any condition handling (a F90 program for instance should 
> behave in exactly the same way as it did under F90). Adding an ENABLE 
> block to such a program should only have effects on the code included in 
> the enable block. By making intrinsic conditions global rather than 
> allowing the user any control over the scope of the conditions themselves 
> means that it is only the ENABLE blocking that can control the domain of 
> program code that is effected. The overall concern is that the effect on 
> program execution of the inclusion of condition handling statements should 
> be confined to a well defined domain of program code, namely, the code 
> where the statements are included.

I believe that the present proposal meets these aims.

> 7. The inclusion of conditions on HANDLE statements appears a little 
> strange. The concern may be related to those of the above topic, but it 
> appears counter intuitive that a Handler could suddenly appear for a 
> condition not enabled by its corresponding ENABLE. It appears to allow
> 
> ENABLE(cond1)
> ...
>  ENABLE(cond2)
>  ...
>  HANDLE(cond1)
>  ...
>  ENDENABLE
> 
> HANDLE
> ...
> ENDENABLE
> 
> This sort of transfer pattern looks strange.

Agreed, but I see no need to ban it. Changing the inner enable stmt to 
      ENABLE(cond1,cond2)
would give equivalent code. Allowing extra conditions on handle stmts is useful to cover their being raised during a procedure call when the overheads of enabling them in line are not wanted. See Example 9. 

> It might be desirable to include condition names on Handlers and to allow 
> multiple handle blocks, cf. ELSEIF blocks.
> ENABLE(cond1,cond2,...)
> ....
> HANDLE(cond1)
> ! handler for condition 1 only
> HANDLE(cond2)
> ! handler for cond2 only
> HANDLE
> ! handler for any other conditions signalling
> ENDENABLE


> 
> 8. ... exception handling requires a much more 
> systematic way of handling the description of prohibitions in the 
> language. With exception handling it must be made clear in what contexts 
> the processor is permitted to do its own thing and where it is required to 
> raise a signal. This must be done carefully to preserve backwards 
> compatibility. This does not appear to have been achieved nor is it 
> clear that as currently designed it is possible in a simple regular way to 
> achieve it.

Yes, a few more edits are needed. 

--------------------------------------------------------------------------------------------------------

6/18+. Add  
      IEC 559:1989, <Binary floating-point arithmetic for microprocessor systems>
      (also ANSI/IEEE 754-1985, IEEE standard for binary floating-point arithmetic). 

8/45+. Add  
              <<or>>  <enable-construct>

9/4-24. Add to R216 (in alphabetic positions) the lines
              <<or>>  <condition-inquire-stmt>
              <<or>>  <signal-stmt>

12/50. After 'CASE constructs,', add 'ENABLE constructs,'.

12/53+. Add:
     (4) Execution of a signal statement (8.1.5.4) may change the execution sequence. 
     (5) Execution of an enable statement (8.1.5.1) may change the execution sequence. 

15/33+ Add

<<2.4.8 Condition>>

A <<condition>> is a default integer flag associated with the occurrence of an exceptional event.  The value 0 corresponds to the quiet state and this is its initial value.  Processor dependent nonzero values correspond to signaling states.  Negative values can occur only through execution of the SIGNAL 
>COMMENT:  change 'the SIGNAL' to 'a SIGNAL' to ensure a user doesn't believe there
>COMMENT:  can be only one SIGNAL statement in an enable block which 'the' might imply.
statement.  The value may be found by execution of a CONDITION_INQUIRE statement. 
>COMMENT:  I don't like the use of the word flag, which could be taken to mean a
>COMMENT:  two-valued (on and off). Could we find another word, like item or value
>COMMENT:  or something. I don't feel very strongly on this though.

[Footnote: The reason for specifying that conditions have integer values is that this leaves open the possibility of providing detailed information about the condition.  This will be useful when a procedure (for example, in a library) signals a condition so that it can indicate the cause of the problem. The intrinsic values are forced to be positive so that a negative value can be seen to be created by source code and not by the system.]

[Footnote: Although multitasking is not part of Fortran 90, the interaction of this proposal with multitasking extensions has been considered.  A model is that each virtual processor has a flag for each condition.  For example, condition handling is permissible within a pure procedure.  Enable, handle, and end-enable statements act as barriers at which the condition values are merged.]
>COMMENT:  Here we say that each virtual processor has a 'flag' and that the
>COMMENT:  'values' are merged.

22/23+ Add to the Blanks Optional column:
            END ENABLE

67/39.  After 'terminated', add 'unless the ALLOCATION_ERROR condition is enabled'. 

68/40.  After 'terminated', add 'unless the DEALLOCATION_ERROR condition is enabled'. 

80/2.  After 'program', add ', except in an enable block for a suitable condition'. 

>COMMENT:  There are very many places in the standard (particularly in chapter
>COMMENT:  13, but also in other places) where this sort of change should be made.
>COMMENT:  We should either change them all or change none and put a paragraph
>COMMENT:  somewhere general that says that all these sort of restrictions hold
>COMMENT:  only when not in an enable block for a suitable condition.
>COMMENT:  I prefer the former (i.e. change them all, but someone needs to read
>COMMENT:  the whole standard to be sure of finding all of the places.

95/10+ Add
(4) ENABLE construct

95/19. Delete 'three'.

107/0+. Add

<<8.1.5 Condition handling>>

A condition has a name with the same scoping rules as for intrinsic procedures
>COMMENT:  change 'procedures' to 'procedure names'. It is the names that have
>COMMENT:  scoping rules.
and a value of type default integer.  The value zero corresponds to the normal or 'quiet' state and nonzero values correspond to exceptional circumstances. All conditions have initial value zero.  The processor is required to signal a condition if the associated circumstance occurs during execution of an intrinsic operation or an intrinsic procedure call specified in the scope of an enable block for the condition.  Some conditions are also required to signal when the circumstance occurs outside an enable block, but whether other conditions signal outside an enable block is processor dependent.  For the detailed specification, see Section 15.  When the processor signals a condition, it has a positive value.  The SIGNAL statement (8.1.5.4) may be used to give it a negative value. 

[Footnote: For a condition whose signaling outside enable blocks is processor dependent, the control of whether the condition so signals is also processor dependent.  There might be an option on the command line or there might be an intrinsic procedure that provides dynamic control.  It is expected that by default the conditions UNDERFLOW and INEXACT will not signal except inside enable blocks.]

[Footnote: The proposal allows the in-lining of procedures with no change to the enable constructs.  On some processors, this may cause a condition that does not signal outside enable blocks to signal.]

[Footnote: On many processors, it is expected that some conditions will cause no alteration to the flow of control when they signal and that they will be tested only when the enable block completes or another enable statement is encountered.  Thus the overheads of testing the condition are confined precisely to the places where the programmer has requested a test.  On other processors, this may be very expensive.  They may instead cause a transfer of control to the handler (or a return or stop) as soon as the condition signals or soon thereafter.]

[Footnote: If additional code is needed (for example, to diagnose integer overflow), this is required only within the scope of the enable block.] 

In a sequence of statements that contains no condition handling statements, if the execution of a process would cause a condition to signal but after execution of the sequence no value of a variable depends on the process, whether the condition signals is processor dependent.  For example, when Y has the value zero, whether the code
           X = 1.0/Y
           X = 3.0
signals DIVIDE_BY_ZERO is processor dependent. 

A condition must not signal if the signal could arise only during execution of a process not required by the standard.  For example, the intrinsic LOG in the statement
           IF (F(X)>0.) Y = LOG(Z)
must not signal a condition when both F(X) and Z are negative and for the statement
           WHERE(A>0.) A = LOG (A)
negative elements of A must not cause signaling. 

[Footnote: In general, it is intended that implementations be free within enable constructs to use the code motion techniques that they use outside enable constructs.]

<<8.1.5.1. The enable construct>>

The ENABLE construct specifies a (possibly empty) set of conditions, an enable block, and (optionally) a handle block with (optionally) a further set of conditions.  The handle block is executed only if execution of the enable block leads to the signaling of one or more of the conditions. 

R835a <enable-construct>    <<is>> <enable-stmt>
                                     [<enable-block>]
                                  [<handle-stmt>
                                      <handle-block>]
                                   <end-enable-stmt>
 
R835b <enable-stmt>         <<is>> [<enable-construct-name>:]         #
                                   # ENABLE [(<condition-name-list>)]   #
                                   # [,IMMEDIATE (<condition-name-list>)] 

R835c <enable-block>        <<is>> <block>
  
R835d <handle-stmt>         <<is>> HANDLE [(<condition-name-list>)] #
                                   #  [<enable-construct-name>]

R835e <handle-block>        <<is>> <block> 

R835f <end-enable-stmt>     <<is>> END ENABLE [<enable-construct-name>]
 
Constraint: If the <enable-stmt> of an <enable-construct> is identified 
	    by an <enable-construct-name>, the corresponding
	    <end-enable-stmt> must specify the same
	    <enable-construct-name>. If the <enable-stmt> of an
	    <enable-construct> is not identified by an
	    <enable-construct-name>, the corresponding
	    <end-enable-stmt> must not specify an
	    <enable-construct-name>. If the <handle-stmt> is identified
	    by an <enable-construct-name>, the corresponding
	    <enable-stmt> must specify the same <enable-construct-name>.
 
Constraint: A condition name must not appear more than once in an <enable-stmt>. 

Constraint: A condition name must not appear more than once in a <handle-stmt>. 
 
The conditions named on the enable statement are enabled during execution of the enable block.  The set of conditions handled by the handle block consists of all those named on the enable statement or on the handle statement.  If the enable construct is nested within an enable block, the conditions enabled for the outer block are also enabled for the inner block. 

An <enable-stmt> may be a branch target statement (8.2). 

[Footnote: Neither a handle statement nor an end-enable statement is permitted to be a branch target.  A handle-block is intended for execution only following the signaling of a condition that it handles, and an end-enable statement is not a sensible target because it would permit skipping the handling of a condition.]

[Footnote: Nesting of enable constructs is permitted.  An enable or handle block may itself contain an enable-construct.  Also, nesting with other constructs is permitted, subject to the usual rules for proper nesting of constructs.]

Execution of an enable statement causes a transfer of control if a signaling condition is handled by the enable construct or any enable construct within which it is nested.  If the enable statement is nested in an enable block that has a handler for a signaling condition, the transfer is to the handler of the innermost such enable block.  Otherwise, it is as for a return if in a subprogram, or a stop if in a main program.  The values of the conditions are not altered. 

[Footnote: In an enable block, the pair of statements 
       ENABLE             
       END ENABLE     
has a checking effect.  If any handled condition is signaling, there will be a transfer of control to an outer handler (or a stop or return).The values of the conditions are not altered.]
 
[Footnote: Note that in a function subprogram it is very desirable to ensure that the function value is defined even if an error condition has been diagnosed and is expected to be handled in the calling subprogram.  If the function value is not defined, further conditions will probably be signaled during the evaluation of the expression that gave rise to the function call, which may mask the condition that was the root cause.]

[Footnote: If a condition handled by a handler signals again during execution of the handler, this second signal will be indistinguishable from the first. If it is desired to handle it separately, it must be set to the quiet value and a nested enable must be provided.]

The value of each condition handled by the enable construct is set to the quiet value upon completion of execution of the <handle-block>. 
>COMMENT:  change 'completion' to 'normal completion'. A signal statement can
>COMMENT:  terminate the execution of a handle block, but does not set the handled
>COMMENT:  conditions to quiet.

<<8.1.5.2 Execution of an enable construct>>

Execution of an <enable-construct> begins with the first executable construct of the <enable- block>, and continues to the end of the block unless a handled condition is signaled.  If a condition handled by the <enable-construct> signals outside any enable construct that handles the condition and is nested within the enable block, control is transferred to the <handle-block>. Transfer of control to the <handle-block> may take place on completion of execution of the enable-block or may take place sooner after the signaling of the condition.  Any variable that might be defined or redefined by execution of a statement of the enable block outside any enable construct that handles the condition and is nested within the enable block is undefined, any pointer whose pointer association might be altered has undefined pointer association status, any allocatable array that might be allocated or deallocated may have been allocated or become unallocated, and the file position of any file specified in an input/output s





ement that might be executed is processor dependent. 

[Footnote: The transfer to the handle block is imprecise in order to allow for optimizations such as vectorization.  As a consequence, some variables become undefined.  In Example 3 of 8.1.5.6, a copy of the matrix itself would need to be available for the slow algorithm.]

Branching out of an enable construct is not permitted.  A CYCLE or EXIT statement is not permitted in an enable construct unless the do construct to which it belongs is nested within the enable construct.  An alternate return specifier in an enable construct must not specify the label of a statement outside the construct.  An ERR=, END=, or EOR= specifier in a statement in an enable construct must not be the label of a statement outside the construct. A RETURN or STOP statement is permitted in an enable construct.  Conditions retain their values on execution of a RETURN or STOP statement. 

[Footnote: The ban on branching out of an enable construct limits the extent of uncertainty over which statements have been executed when a handler is entered.]

Any <executable-construct> of the enable block that might signal one or more of the conditions in the immediate list on the enable statement is treated as if it were followed by an <enable-construct> with an empty enable block and no handler. 

Execution of the <handle-block> completes the execution of the <enable-construct>. 

If no condition handled by the enable construct is signaling on completion of execution of the <enable-block>, the execution of the entire construct is complete. 

[Footnote: Nested enable constructs without handlers can be employed to reduce the imprecision of an interrupt.  Note that enable, handle, and end-enable statements provide effective barriers to code migration by an optimizing compiler.]

<<8.1.5.3 Signaling conditions that are not enabled>>

A processor may signal a condition while executing a statement that is not in an enable block for the condition.  If in a subprogram, a return is executed without alteration of the values of the conditions.  If in a main program, a stop is executed and the processor must issue a warning on the default output unit. 
>COMMENT:  The is no mention of 'default output unit' in the index to the standard.
>COMMENT:  At the very least, some words should be added to define this so that an
>COMMENT:  index entry is created for it.

[Footnote: On return to the caller, the condition will be signaling.  If the invocation is within an enable block that has a handler for the condition, there will be a transfer to the handler (or a return or stop), but not 
>COMMENT:  Delete '(or a return or a stop)'. If the invocation is within an enable
>COMMENT:  block that has a handler for the condition there WILL be a transfer
>COMMENT:  to the handler. There will never be a return or a stop.
necessarily until the execution of the block is complete.  If the invocation is not within an enable block that has a handler for the condition, there may be a return (or stop) at once, or the processor may continue executing.]


<<8.1.5.4 Signal statement>>

R835g  <signal-stmt> <<is>> SIGNAL (<condition-name>,<scalar-int-expr>)

Constraint: The <scalar-int-expr> must be of type default integer.

Constraint: If the condition name is that of a combination condition (15.7), the <scalar-int- expr> must be the literal constant 0. 

The SIGNAL statement changes the value of the condition it names to that of the expression it contains.  If the value is nonzero, it causes a transfer of control.  If the statement is in an enable block of an enable construct that has a handler for the condition, the transfer is to the handler of the innermost such enable construct.  Otherwise, it is as for a return if in a subprogram, or a stop if in a main program. 

[Footnote: In a handler, the pair of statements 
       ENABLE             
       END ENABLE     
has a resignaling effect.  If any handled condition is signaling, there will be a transfer of control to an outer handler (or a stop or return).The values of the conditions are not altered.]
>COMMENT:  delete '(or a stop or return)'. If the condition is handled, there WILL
>COMMENT:  be a transfer to an outer handler.

<<8.1.5.5 Examples of ENABLE constructs>>

Example 1:

   MODULE MATRIX
! Module for matrix multiplication of real arrays of rank 2.
      INTERFACE OPERATOR(.mul.)                                            
         MODULE PROCEDURE MULT
      END INTERFACE
   CONTAINS
      FUNCTION MULT(A,B)
         REAL, INTENT(IN) :: A(:,:),B(:,:)
         REAL MULT(SIZE(A,1),SIZE(B,2)
>COMMENT:  Change '2)' to ''2))' missing right paren
         ENABLE (INTRINSIC, OVERFLOW)
            MULT = MATMUL(A, B)
         HANDLE
            SIGNAL(INEXACT, -1)
         END ENABLE
      END FUNCTION MULT
   END MODULE MATRIX

This module provides matrix multiplication for real arrays of rank 2.  Since the condition INSUFFICIENT_STORAGE signals outside enable blocks (see Section 15.1), if there is insufficient storage for the necessary temporary array, the module will signal the condition INSUFFICIENT_STORAGE.  If an INTRINSIC or OVERFLOW condition occurs, the module will signal the condition INEXACT with value -1. 

Example 2:
 
IO_CHECK: ENABLE (IO_ERROR, END_OF_FILE)
             :
             READ (*, '(I5)') I
             READ (*, '(I5)', END = 90) J
             :
       90    J = 0
          HANDLE 
             CONDITION_INQUIRE(END_OF_FILE,K)
             IF (K/=0) THEN
                WRITE (*, *) 'Unexpected END-OF-FILE when reading ', & 
                             'the real data for a finite element'
             ELSE 
                CONDITION_INQUIRE(IO_ERROR,K)
                IF (K /= 0) WRITE (*, *) 'I/O error when reading ', & 
                             'the real data for a finite element'
             END IF
             STOP
          END ENABLE IO_CHECK
 
In this example, if an input/output error occurs in either of the READ statements or if an end- of-file is encountered in the first READ statement, the appropriate condition will be signaled and the handler will receive control, print a message, and terminate the program.  However, if an end-of-file is encountered in the second READ statement, no condition will be signaled and control will be transferred to the statement indicated in the END= specifier. 

Example 3:

   ENABLE (USUAL)
    ! First try the "fast" algorithm for inverting a matrix:
      MATRIX1 = FAST_INV (MATRIX) 
                        ! MATRIX is not altered during execution of FAST_INV.
   HANDLE 
    ! "Fast" algorithm failed; try "slow" one:
      SIGNAL (USUAL, 0)
      ENABLE (USUAL)
         MATRIX1 = SLOW_INV (MATRIX)
      HANDLE 
         WRITE (*, *) 'Cannot invert matrix'
         STOP
      END ENABLE
   END ENABLE
 
In this example, the function FAST_INV may cause a condition to signal.  If it does, another try is made with SLOW_INV.  If this still fails, a message is printed and the program stops.  Note the use of nested enable constructs. Note, also, that it is important to set the signals to 'quiet' before the inner enable.  If this is not done, a condition will still be signaling when the inner ENABLE is encountered, which will cause an immediate transfer to an outer handler (or a stop or return). 

Example 4:

   ENABLE (OVERFLOW)
   ! First try a fast algorithm for inverting a matrix.
   : ! Code that cannot signal overflow
   DO K = 1, N
      ENABLE
      :
      END ENABLE
   END DO
      ENABLE
      :
      END ENABLE
   HANDLE 
   ! Alternative code which knows that K-1 steps have executed normally.
   :
   END ENABLE

Here the code for matrix inversion is in line and the transfer is made more precise by adding to the enable block two enable constructs without handlers. 

Example 5:

The following subroutine finds a zero of <f(x)> on an interval [<a,b>].  It is limited to take one second of real time as measured by the system clock.  If it fails to obtain the requested accuracy after this time, the condition INEXACT signals with the value -1. 

   SUBROUTINE ZERO_SOLVER (A, B, X, TOLERANCE, F)
      REAL A, B, X, TOLERANCE
      INTERFACE; REAL FUNCTION F(X); REAL X; END INTERFACE

      INTEGER  COUNT, RATE, START ! Local variables
      CALL SYSTEM_CLOCK(START, RATE)
      :
   ! The following code is executed every iteration
      CALL SYSTEM_CLOCK(COUNT)
   ! If time has run out, return, signaling condition INEXACT.
      IF (COUNT > START+RATE) SIGNAL (INEXACT,-1)
      :
   END SUBROUTINE ZERO_SOLVER 

The application code handles the exception in a way that only it knows.  An example is:

   ENABLE
       CALL ZERO_SOLVER (A, B, X, TOLERANCE, F)
   HANDLE (INEXACT)

   ! Exceeded time limit. Fix up and go on.
      :
   END ENABLE 
   : 

Example 6:
 
      REAL FUNCTION CABS (Z)
        COMPLEX Z
! Calculate the complex absolute value, using a scaled algorithm 
!   if  the straightforward calculation underflows or overflows. Set the
!   overflow condition to the value -1 if the result is too large to
!   be representable.

        REAL S, ZI, ZR
        INTRINSIC REAL, AIMAG, SQRT, ABS, MAX

        ZR = REAL(Z)
        ZI = AIMAG(Z)

quick: ENABLE(OVERFLOW, UNDERFLOW)

!         This is the quick and usual calculation.
          CABS = SQRT(ZR**2 + ZI**2) 

        HANDLE quick

!         Will try again using a scaled equivalent method.
          S = MAX(ABS(ZR),ABS(ZI))
          SIGNAL (OVERFLOW,0) ; SIGNAL (UNDERFLOW,0) 
  slow: ENABLE(OVERFLOW, UNDERFLOW)
            CABS = S*SQRT( (ZR/S)**2 + (ZI/S)**2 ) 
          HANDLE slow
            CONDITION_INQUIRE(OVERFLOW,K)
            IF (K/= 0) THEN
!             The result is too large to be representable.
              SIGNAL(OVERFLOW, -1)
            ELSE 
              CONDITION_INQUIRE(UNDERFLOW,K)
              IF (K/= 0)  CABS = S
>COMMENT:  I think that this code is misleading. How could K possibly be zero.
>COMMENT:  The handle only hanndles overflow and underflow, and we have already
>COMMENT:  checked that overflow is not signalling, so underflow must be.
            END IF
          END ENABLE slow
  
        END ENABLE quick

      END FUNCTION CABS

This illustrates the setting of a special condition value when the problemreally has a result that overflows. 

Example 7:

      MODULE LIBRARY
      ...
      CONTAINS
         SUBROUTINE B
            ...
            X = Y*Z(I) ! No condition enabled.
            IF(X>10.)SIGNAL(OVERFLOW, 1)
            ...
         END SUBROUTINE B
      END MODULE LIBRARY

      SUBROUTINE A
         USE LIBRARY
         ENABLE 
            CALL B
         HANDLE (OVERFLOW)
            ...
         END ENABLE
      END SUBROUTINE A

This illustrates the use of a library module that may signal the condition OVERFLOW.  The signal statement causes a transfer to the handler in the calling subroutine A. 

This also illustrates the effect of an intrinsic condition that is not enabled.  An overflow in Y*Z(I) would cause OVERFLOW to signal and hence a
>COMMENT:  change 'would' to 'may'. It is processor dependent whether the overflow
>COMMENT:  signals outside of an enable block
transfer to the handler in the calling subroutine A.  An out-of-range subscript value I might or might not signal BOUND_ERROR, but it would not be handled by subroutine A. 

Example 8:

   ENABLE, IMMEDIATE (OVERFLOW)
          A = B*C
          WHERE(RAINING)
               X(:) = X(:)*A
          ELSEWHERE 
               Y(:) = Y(:)*A
          END WHERE
    HANDLE
       .....
    END ENABLE

This illustrates the use of IMMEDIATE. The enable construct is equivalent to

      ENABLE (OVERFLOW)
          A = B*C
          ENABLE
          END ENABLE
          WHERE(RAINING)
               X(:) = X(:)*A
          ELSEWHERE 
               Y(:) = Y(:)*A
          END WHERE
          ENABLE 
          END ENABLE
    HANDLE
       .....
    END ENABLE

Note that the statements of a WHERE construct are not tested separately.

Example 9:

   SUBROUTINE LONG
      REAL, ALLOCATABLE A(:), B(:,:)
>COMMENT:  Add '::' to the above line to make it
>COMMENT:       REAL, ALLOCATABLE :: A(:), B(:,:)
      : ! Other specifications
      ENABLE
          :
          ! Lots of code, including many procedure calls
          :
      HANDLE (ALL_CONDITIONS)
          ! Fix-up, including deallocation of any allocated arrays
          IF(ALLOCATED(A)) DEALLOCATE (A)
          IF(ALLOCATED(B)) DEALLOCATE (B)
          :
      END ENABLE
   END SUBROUTINE LONG

This illustrates the use of a handle statement with additional conditions. 
Here the enable block enables no conditions because fast execution is desired,
but if anything goes wrong (for example, in one of the procedure invoked),
>COMMENT:  change 'procedure' to 'procedures' - typo
fix-ups are performed, including deallocation of any local allocated arrays. 


107/5. After '<end-do-stmt,>' add 'an <enable-stmt>,'.

122/17-18. Replace sentence by 
   If an error condition (9.4.3) occurs during execution of an input/output statement that lies in an enable block for the IO_ERROR condition or contains an ERR= specifier:

122/25. After 'continues with' add 'the handle block or'

122/27-28. Replace sentence by 
   If an end-of-file condition (9.4.3) occurs and no error condition (9.4.3) occurs during execution of an input/output statement that lies in an enable block for the END_OF_FILE condition or contains an  END= specifier.

122/34. After 'continues with' add 'the handle block or'

122/37-38. Replace sentence by   

If an end-of-record condition (9.4.3) occurs and no error condition (9.4.3)occurs during condition of an input/output statement that lies in an enableblock for the END_OF_RECORD condition or contains an EOR= specifier:
 ..................................................................

123/6. After 'continues with' add 'the handle block or'

125/10. Before 'contains' add 'is not in a enable block for the IO_ERROR condition and '.

125/11.  Before 'contains' add 'is not in a enable block for the END_OF_FILE condition and '. 

125/13.  Before 'contains' add 'is not in a enable block for the END_OF_RECORD condition and '. 

241/25. After 'procedures,' add 'intrinsic conditions,'.

241/35. After 'procedure,' add 'or condition'.

<<15. CONDITIONS>>

In this section, the conditions supported by the standard and a statement for
>COMMENT:  Change 'conditions supported' to 'conditions and corresponding condition
>COMMENT:  names supported'
>COMMENT:  The changes refer to condition names and we want to make it clear that
>COMMENT:  we are defining a set of condition names as well as a set of conditions.
obtaining the value of a condition are specified. 

The CONDITION_INQUIRE statement returns the value of a condition.

R835i <condition-inquire-stmt> <<is>> CONDITION_INQUIRE (<condition-name>, #
                                     # [STAT=]<scalar-default-int-variable>)
                               <<or>> CONDITION_INQUIRE (<conditions-array>)  

835j <conditions-array>          <<is>> <default-char-variable>

Constraint: The condition name must not be that of a combination condition (Section 15.7). 

Constraint: The <conditions-array> must be a rank-one array that is not of assumed size. 

The STAT= variable is defined with the value 0 if the condition named is quiet and a nonzero value otherwise.  Negative values can occur only following execution of a SIGNAL statement. 

The <conditions-array> is defined with the names of signaling conditions and blanks according to the rules of default assignment.  If there are <s> conditions signaling, the first <s> elements are defined with the names of these conditions and the remaining elements are given the value blank.  If the processor provides additional conditions, the names of the conditions defined by the standard must precede the names of any such additional intrinsics.  If there are more signaling conditions than the size of the array, all elements are defined with condition names and which are chosen is processor dependent. 

[Footnote: An array size 20 will always be adequate to return the names of all the conditions defined by the standard.  If the final element of the character array has the value blank, the names of all signaling conditions will have been returned.  If it is not blank, the user may set the conditions named quiet with SIGNAL statements and call CONDITION_INQUIRE again.]

<<15.1 Storage and addressing conditions>>

ALLOCATION_ERROR
This occurs when the processor is unable to perform an allocation requested by an ALLOCATE statement (6.3.1) containing no STAT= specifier.  It is not signaled by an ALLOCATE statement containing a STAT= specifier.  The signaling values are the same as the STAT values.  Whether it signals outside enable blocks is processor dependent. 

DEALLOCATION_ERROR
This occurs when the processor detects an error when executing a DEALLOCATE statement (6.3.1) containing no STAT= specifier.  It is not signaled when executing a DEALLOCATE statement containing a STAT= specifier.  The signaling values are the same as the STAT values.  Whether it signals outside enable blocks is processor dependent. 

INSUFFICIENT_STORAGE
This indicates that the processor is unable to find sufficient storage to continue execution.  It may occur prior to the execution of the first executable statement of a main program or procedure and it may occur during the execution of an executable statement.  It need not signal if ALLOCATION_ERROR signals.  It signals outside enable blocks. 

BOUND_ERROR
This occurs when an array subscript, array section subscript, or substring range violates its bounds.  This does not include violations of the requirements derived from the size of an assumed-size array.  Whether it signals outside enable blocks is processor dependent. 

SHAPE
This occurs when an array operation or assignment does not conform in shape. Whether it signals outside enable blocks is processor dependent. 

MANY_ONE
This occurs when a many-one array section (6.2.2.3.2) appears on the left of the equals in an assignment statement or as an input item in a READ statement. Whether it signals outside enable blocks is processor dependent. 

NOT_PRESENT
This occurs when a dummy argument that is not present is accessed as if it were present; that is, when one of the restrictions of 12.5.2.8 is violated. Whether it signals outside enable blocks is processor dependent. 

UNDEFINED
This occurs when a value that is required for an operation is detected by the processor to be undefined.  Whether it signals outside enable blocks is processor dependent. 

[Footnote: This wording is intended to allow the processor to be as thorough as it chooses with respect to the detection of undefined values.]

<<15.2 Input/output conditions>>

IO_ERROR
This occurs when an input/output error (9.4.3) is encountered in an input/output statement containing no IOSTAT= or ERR= specifier.  It is not signaled when executing an input/output statement containing an IOSTAT= or ERR= specifier.  The signaling values are the same as the IOSTAT values. Whether it signals outside enable blocks is processor dependent. 

END_OF_FILE
This occurs when an end-of-file condition (9.4.3) is encountered in an input statement containing no IOSTAT= or END= specifier.  It is not signaled when executing an input statement containing an IOSTAT= or END= specifier.  Whether it signals outside enable blocks is processor dependent. 

END_OF_RECORD
This occurs when an end-of-record condition (9.4.3) is encountered in an input statement containing no IOSTAT= or EOR= specifier.  It is not signaled when executing an input statement containing an IOSTAT= or EOR= specifier.  Whether it signals outside enable blocks is processor dependent. 

<<15.3 Floating-point conditions>>
 
OVERFLOW
This condition occurs when the result for an intrinsic real or complex operation has a very large processor-dependent absolute value.  Whether it signals outside enable blocks is processor dependent. 
 
UNDERFLOW
This condition occurs when the result for an intrinsic real or complex operation has a very small processor-dependent absolute value.  A processor that does not conform to IEC 559:1989 is required to set this condition when requested to do so by a SIGNAL statement, but is not required to set it otherwise.  Whether it signals outside enable blocks is processor dependent. 

DIVIDE_BY_ZERO
This condition occurs when a real or complex division has a nonzero numerator and a zero denominator.  Whether it signals outside enable blocks is processor dependent. 

INEXACT
This condition occurs when the result of a real or complex operation is not exact.  A processor that does not conform to IEC 559:1989 is required to set this condition when requested to do so by a SIGNAL statement, but is not required to set it otherwise.  Whether it signals outside enable blocks is processor dependent. 
 
INVALID
This condition occurs when a real or complex operation is invalid.  A processor that does not conform to IEC 559:1989 is required to set this condition for real or complex division of zero by zero and when requested to do so by a SIGNAL statement, but is not required to set it otherwise.  Whether it signals outside enable blocks is processor dependent. 

[Footnote: It is expected that by default the conditions UNDERFLOW and INEXACT will not signal except inside enable blocks.]

<<15.4 Integer conditions>>

INTEGER_OVERFLOW
This condition occurs when the result for an intrinsic integer operation has a very large processor-dependent absolute value.  Whether it signals outside enable blocks is processor dependent. 

INTEGER_DIVIDE_BY_ZERO
This condition occurs when an integer division has a zero denominator. Whether it signals outside enable blocks is processor dependent. 

<<15.5 Intrinsic procedure condition>>

INTRINSIC
This condition indicates that an intrinsic procedure or operation has been unsuccessful.  An unsuccessful intrinsic procedure may signal other conditions instead of INTRINSIC.  Whether it signals outside enable blocks is processor dependent.  If an intrinsic procedure is an actual argument in a procedure call within an enable block for the INTRINSIC condition, the condition must signal if the procedure is invoked through the argument association. 

<<15.6 System error conditions>>

SYSTEM_ERROR
This condition occurs as a result of a system error.  Whether it signals outside enable blocks is processor dependent. 

<<15.7 Combination conditions>>

Each of the following conditions may be specified on an enable, handle, or signal statement and is equivalent to specifying a list of conditions. 

STORAGE
This condition is equivalent to the list: ALLOCATION_ERROR, DEALLOCATION_ERROR, and INSUFFICIENT_STORAGE. 

IO
This condition is equivalent to listing all the input/output conditions.

FLOATING
This condition is equivalent to the list: OVERFLOW, INVALID, and DIVIDE_BY_ZERO.

INTEGER
This condition is equivalent to listing the two integer conditions.

USUAL
This condition is equivalent to the list: STORAGE, IO, FLOATING, and INTRINSIC.

ALL_CONDITIONS
This condition is equivalent to listing all the conditions.
