!standard 6.5(5/3) 12-06-05 AI12-0029-1/01 !class Amendment 12-06-13 !status work item 12-06-25 !status received 12-04-27 !priority Medium !difficulty Medium !subject Relax requirement for functions to have return statements !summary **TBD. !problem When a function is just a stub that raises an exception (as often happens during early development, as in "raise Not_Implemented_Error;"), it's annoying to have to write a dummy return statement. That can be especially annoying when a dummy value to return needs to be constructed (as in functions that return indefinite types). !proposal Add a specific exception to 6.5(5/3) to allow a function that only raises an exception. !wording Change the first sentence of 6.5(5/3) from A function body shall contain at least one return statement that applies to the function body, unless the function contains code_statements. to A function body shall contain at least one return statement that applies to the function body, unless the function contains code_statements, or unless the last statement of the "sequence_of_statements" of the "handled_sequence_of_statements" of the function's body is a "raise_statement" or a call to a nonreturning procedure and the "sequence_of_statements" does not end with a "label". !discussion This change is compatible with Ada 2012 in that it only removes errors (never adding them), so any legal program remains legal. One could imagine more complex rules that seek to reduce both false positives and false negatives for this check. However, any new false positives would represent a serious incompatibility (rendering currently legal programs illegal). (New compile-time detections of programs that actually raise Program_Error are also technically incompatible, but as that is just changing a run-time error into a compile-time one, we don't care). In addition, it is excessive false positives that make this rule so reviled. It much less clear that additional problem detections really are very important, especially if those result in additional false positives. The primary value of the rule, after all, is catching gross errors like forgetting to put in the return statement to return a calculated result. The simple proposed rule makes no changes other than to reduce false positives, which clearly is a good thing. More complex rules are not so clear. --- We include calls to nonreturning procedures (which must raise an exception), mainly so that we can easily cover calls to the procedure Raise_Exception. But it also includes user-defined exception-raising procedures, which seems like a benefit. --- It was suggested that we consider introducing a "soft error" concept into the language, which would allow errors that could be ignored with the use of an appropriate aspect. This would allow somewhat more aggressive rules as the error could be suppressed in a false positive case. This idea also could be used to improve the proposals in AI12-0024-1. [Editor's note: The following is my concept of Bob's idea. It's a bit "harder" than his original idea; but I'm strongly opposed to any sort of required warning in the standard, as such things are untestable and there is no evidence that implementers need encouragement in this area (every compiler has plenty of warnings). I find the following more acceptable because it emphasizes safety over ease-of-ignoring and that makes it testable in the same way that existing Legality Rules are.] Following is an outline of the "soft error" idea (it may need a different name): The language defines the concept of a "soft error". For each soft error that is defined, the language needs to specify to which program unit the soft error apply. (Regular Legality Rules only reject the entire compilation unit; while most compilers are far more specific, the language itself never talks about that. We need more precise location for "soft errors" so that they can be ignored reliably.) There is an aspect Ignore_Soft_Error. If this aspect is true for a program unit, then no soft errors are detected that apply to that unit. (An implementation might want to convert them to warnings or some other item.) This aspect is inherited from the containing unit if it is not specified, and it defaults to False. If Ignore_Soft_Error if False for a program unit, then if a soft error applies to the unit, the unit is illegal. --- Some thoughts on this design: 1) Ada tends to emphasize safety and correctness over ease-of-use. Moreover, many users run GNAT in warnings are errors mode. As such, I made that the default for "soft errors"; the program is rejected when a soft error occurs. It is likely that compilers would have a "soft error is a warning" mode; but the default should be to be safe (and reject anything questionable). 2) Location of "soft errors" is defined to be the innermost enclosing program unit. The granularity of ignoring them is the same. This is pragmatic; the more locality that we give the errors, the harder they are to define, the harder it is to define when they are ignored, and the more likely it is that the errors would be difficult to implement in some existing implementation. 3) It might make sense to have a more complex scheme for ignoring "soft errors", such as a scheme for ignoring specific errors, for turning soft errors back on, and the like. That would obscure the basic idea, so I didn't try to do anything beyond the basics. 4) This scheme would not do much to improve compatibility. Needed to add "Ignore_Soft_Errors" somewhere in order to get the code to compile is still incompatible -- it just has a better workaround. Having the default be ignore would allow more much more aggressive rules (as by default they'd still be compatible), but as false positives would still be a problem (especially amongst those that insist on treating soft errors and hard errors the same), this gain does not really exist. !ACATS test ** TBD. !appendix !topic Relax requirement for functions to have return statements !reference 6.5(5) !from Adam Beneschan 12-04-27 !discussion I realize that it's been suggested before to remove the requirement for function bodies to contain return statements, but this was not done because the rule is useful for catching some errors. Still, it's somewhat annoying to have to write a dummy return statement when a function is a stub that just raises an exception. (Ada 2005 made this a little easier because one can usually write "return X : T;" where T is the return type, without having to provide a value. But, as discussed in a recent comp.lang.ada thread, this still doesn't work if T is an indefinite type.) I'd like to propose a compromise: Change the first sentence of 6.5(5) from A function body shall contain at least one return statement that applies to the function body, unless the function contains code_statements. to A function body shall contain at least one return statement that applies to the function body, unless the function contains code_statements, or unless the last statement of the "sequence_of_statements" of the "handled_sequence_of_statements" of the function's body is a "raise_statement" and the "sequence_of_statements" does not end with a "label". This should be as good at preventing errors as the current rules, since of course a function that ends in a raise statement cannot "fall through". Note that I've tried to be careful to refer to the syntactic categories here. In this case: function Factorial (X : Integer) return Integer is begin if X < 0 then raise Constraint_Error with "Factorial of negative number not defined"; end if; end Factorial; this would still be illegal; the last statement of the function body is a raise statement, in an informal sense, but the last statement of the sequence_of_statements is an if_statement, not a raise_statement. Only the last statement of the actual sequence_of_statements in the syntax description of 11.2(2) would matter. **************************************************************** From: Bob Duff Sent: Saturday, April 28, 2012 8:31 AM > !topic Relax requirement for functions to have return statements I agree that the current requirement is annoying. It is both too liberal and too conservative (doesn't catch all such errors, yet raises false alarms). If we're going to change it, the "right" rule is that every path through the function body should end with return or raise. It's not hard to implement. GNAT already does, with a warning. And it catches all fall-off-end errors. **************************************************************** From: Adam Beneschan Sent: Monday, April 30, 2012 10:19 AM Perhaps. But since this would be a Legality Rule, there would have to be a rigorous definition of "every path through the function body"..., and that could get complicated. The current rule is simple--a bit too simple in my opinion, but I was trying to come up with something at approximately the same level of simplicity. A refinement would be to define an "ends-with-return-or-raise" (EWROR) property of a or a defined as follows: A sequence_of_statements has the EWROR property if it has at least one statement, does not end with a