!standard 6.5.1(0) 18-04-26 AI12-0269-1/04 !standard 6.5.1(1/3) !standard 6.5.1(3.1/3) !standard 6.5.1(3.4/3) !standard 6.5.1(5/2) !standard 6.5.1(6/2) !standard 6.5.1(7/2) !standard J.15.2(2/3) !standard J.15.2(3/3) !standard J.15.2(4/3) !class Amendment 18-03-29 !status Amendment 1-2012 18-04-04 !status WG9 Approved 16-06-22 !status ARG Approved 7-0-2 18-04-02 !status work item 18-03-29 !status received 18-03-24 !priority Low !difficulty Medium !subject Aspect No_Return for functions reprise !summary A function can be defined as No_Return. !problem For purposes of static analysis, it is helpful to be able to explicitly indicate that a function never returns normally, typically because it always propagates an exception. This is quite important for a static analysis tool that is focused on identifying the conditions under which an exception might be raised. For some critical systems, they go so far as to completely suppress exception raising and handling in the final system; they use exceptions for finding and logging bugs during development and testing. For such systems, proving that exceptions will never be raised in the final system is particularly important, and a number of static analysis tools are focused very much on aiding that proof. The No_Return annotation is very useful in that context, to indicate that any call on such a function or procedure should be considered a serious error. A "No_Return" function might exist because it is inherited from a progenitor, but should not be called (admittedly violating the Liskov Substitution Principle, but sometimes LSP is not a perfect fit to the problem). Alternatively, it might represent a potential future capability that has been deactivated for a particular version of the software. And perhaps the biggest reason is that Ada users find it confusing that No_Return is available for procedures, but not for functions. !proposal Allow aspect No_Return to be specified for functions. Such a function must raise an exception. !wording Change the title of 6.5.1 to "Nonreturning Subprograms". In 6.5.1(1/3), 6.5.1(3.1/3), 6.5.1(3.4/3), 6.5.1(6/2), 6.5.1(7/2), change "procedure" to "subprogram". Add after 6.5.1(5/2): Any return statement that applies to a nonreturning function or generic function shall be a simple_return_statement with an expression that is a raise_expression, a call on a nonreturning function, or a parenthesized expression of one of these. AARM Ramification: We still require at least one return statement in a function; we just require that all such return statements don't actually return a value. [Editor's note: We don't need an analog of 6.5.1(9/2) for functions; all functions have this semantics.] Change "procedure" to "subprogram" in J.15.2(2/3), J.15.2(3/3) (except "null procedure"), and J.15.2(4/3). !discussion This is mainly for uniformity, so that projects or tools that rely on an explicit indication of No_Return can use this same aspect for procedures and functions. We don't require that all return statements be removed from a "No_Return" function, because that would have the effect of making an illegal program (a return-less function) into a legal program, something we try to avoid doing with a pragma or aspect specification. Because of the availability of the raise expression, it is now convenient to bury the raise statement "inside" the return statement. We considered going further to accommodate legacy code, and relax the requirement that all return statements return a raise expression or a non-returning call. In particular, we could have allowed an arbitrary return statement so long as it immediately follows a raise statement or a call on a No_Return procedure. This was judged to be an unnecessarily complication for a feature that is not expected to be used often. !example function Copy (Obj : in T) return T with No_Return => True is begin return raise Program_Error with "type T not copyable"; end Copy; !corrigendum 6.5.1(0) @drepl Nonreturning Procedures @dby Nonreturning Subprograms !corrigendum 6.5.1(1/3) @drepl Specifying aspect No_Return to have the value True indicates that a procedure cannot return normally; it may propagate an exception or loop forever. @dby Specifying aspect No_Return to have the value True indicates that a subprogram cannot return normally; it may propagate an exception or loop forever. !corrigendum 6.5.1(3.1/3) @drepl For a procedure or generic procedure, the following language-defined representation aspect may be specified: @dby For a subprogram or generic subprogram, the following language-defined representation aspect may be specified: !corrigendum 6.5.1(3.4/3) @drepl If a generic procedure is nonreturning, then so are its instances. If a procedure declared within a generic unit is nonreturning, then so are the corresponding copies of that procedure in instances. @dby If a generic subprogram is nonreturning, then so are its instances. If a subprogram declared within a generic unit is nonreturning, then so are the corresponding copies of that subprogram in instances. !corrigendum 6.5.1(5/2) @dinsa A return statement shall not apply to a nonreturning procedure or generic procedure. @dinst Any return statement that applies to a nonreturning function or generic function shall be a @fa with an @fa that is a @fa, a call on a nonreturning function, or a parenthesized @fa of one of these. !corrigendum 6.5.1(6/2) @drepl A procedure shall be nonreturning if it overrides a dispatching nonreturning procedure. In addition to the places where Legality Rules normally apply (see 12.3), this rule applies also in the private part of an instance of a generic unit. @dby A subprogram shall be nonreturning if it overrides a dispatching nonreturning subprogram. In addition to the places where Legality Rules normally apply (see 12.3), this rule applies also in the private part of an instance of a generic unit. !corrigendum 6.5.1(7/2) @drepl If a renaming-as-body completes a nonreturning procedure declaration, then the renamed procedure shall be nonreturning. @dby If a renaming-as-body completes a nonreturning subprogram declaration, then the renamed subprogram shall be nonreturning. !corrigendum J.15.2(2/3) @drepl @xindent<@ @ @b No_Return (@i@fa{, @i@fa});> @dby @xindent<@ @ @b No_Return (@i@fa{, @i@fa});> !corrigendum J.15.2(3/3) @drepl Each @i@fa shall denote one or more procedures or generic procedures. The @i@fa shall not denote a null procedure nor an instance of a generic unit. @dby Each @i@fa shall denote one or more subprograms or generic subprograms. The @i@fa shall not denote a null procedure nor an instance of a generic unit. !corrigendum J.15.2(4/3) @drepl @fa No_Return specifies that the No_Return aspect (see 6.5.1) for each procedure denoted by each @fa given in the @fa has the value True. @dby @fa No_Return specifies that the No_Return aspect (see 6.5.1) for each subprogram denoted by each @fa given in the @fa has the value True. !ASIS Probably won't need an interface change, this is an existing aspect. !ACATS test ACATS B- and C-Tests are needed to check that the new capabilities are supported. !appendix From: Tucker Taft Sent: Saturday, March 24, 2018 3:14 PM We voted "no action" on AI12-0063 back in 2013, but I think we should re-examine it. At the time we were struggling with the problem that predicates were using "raise exception" to indicate what exception should be raised on failure, and ultimately we eliminated that problem by defining the Predicate_Failure aspect. Given that, I think all of the controversy associated with this AI is gone, and it becomes a simple equivalence, namely calling a function with No_Return is essentially equivalent to a raise expression from the flow analysis point of view. We have a number of customers who have naturally tried to use the No_Return aspect on functions, and have been surprised it is not permitted. In this case, I think allowing it on functions would simplify things for users. **************************************************************** From: Randy Brukardt Sent: Monday, March 26, 2018 8:10 PM I don't share your memory on this one. My recollection was that we considered defining it early on before we defined raise_expressions, when raising an exception in an expression required a function with a body (since a raise_statement was required). Once we invented raise_expression, the need to write functions that only raised exceptions went away, and thus the utility of this aspect went with it. Since Ada doesn't have exception variables, it's hard to imagine what advantage writing a function to raise an exception might have over just raising the exception explicitly. There's no abstraction to be gained here (unlike most other uses of functions). Could you explain the use case the customers had for using No_Return on a function? Note that the original use case for No_Return on procedures was to construct an exception message, but the need for that went away when we added "with message" to the syntax in Ada 2005. It works just as well to construct the message in a function and use a usual raise. So if this aspect was invented today, it probably wouldn't have enough use cases to get added to the language. **************************************************************** From: Tucker Taft Sent: Monday, March 26, 2018 8:34 PM > I don't share your memory on this one. My recollection was that we > considered defining it early on before we defined raise_expressions, > when raising an exception in an expression required a function with a > body (since a raise_statement was required). Once we invented > raise_expression, the need to write functions that only raised > exceptions went away, and thus the utility of this aspect went with it. You should read the AI's appendix. We had raise expressions, but we were giving them special properties relative to a membership test (returned False rather than propagating an exception), and we thought No_Return functions should have the same special property. That made the whole feature much more complex. > Since Ada doesn't have exception variables, it's hard to imagine what > advantage writing a function to raise an exception might have over > just raising the exception explicitly. There's no abstraction to be > gained here (unlike most other uses of functions). > > Could you explain the use case the customers had for using No_Return > on a function? The problem is when you have certain operations of a tagged type that should not be called, but you cannot make the whole type abstract. Or it is simply a stub to be filled in later. > Note that the original use case for No_Return on procedures was to > construct an exception message, but the need for that went away when > we added "with message" to the syntax in Ada 2005. It works just as > well to construct the message in a function and use a usual raise. So > if this aspect was invented today, it probably wouldn't have enough > use cases to get added to the language. Similar reasoning for procedures, or for stubs that might be implemented eventually, but are initially not defined. In any case, given that we have No_Return for procedures, it seems practically free to extend it to functions, so why not remove this somewhat arbitrary restriction? **************************************************************** From: Randy Brukardt Sent: Monday, March 26, 2018 9:16 PM > You should read the AI's appendix. We had raise expressions, but we > were giving them special properties relative to a membership test > (returned False rather than propagating an exception), and we thought > No_Return functions should have the same special property. That made > the whole feature much more complex. Yes, but that issue was long gone when we voted it No Action. (AI12-0054-2 was approved Jun 2013, this was voted N/A Oct 2015). The minutes of that meeting say: "For most such uses, using a raise_expression directly is sufficient. And the AI12-0054-1 proposal never ended up in the language, so the primary reason for this AI never happened." > > Since Ada doesn't have exception variables, it's hard to imagine > > what advantage writing a function to raise an exception might have > > over just raising the exception explicitly. There's no abstraction > > to be gained here (unlike most other uses of functions). > > > > Could you explain the use case the customers had for using No_Return > > on a function? > > The problem is when you have certain operations of a tagged type that > should not be called, but you cannot make the whole type abstract. Or > it is simply a stub to be filled in later. It seems like a mistake to put into the contract (and No_Return is clearly part of the contract) that some operation is TBD. And you're not allowed to later change that contract via derivation, so it seems to "poison" operations of a tagged type. Ergo, it seems inappropriate for the uses described above. Probably people using No_Return on procedures in such cases have already found that out, but maybe not. Anyway, thanks for the explanation. > > Note that the original use case for No_Return on procedures was to > > construct an exception message, but the need for that went away when > > we added "with message" to the syntax in Ada 2005. It works just as > > well to construct the message in a function and use a usual raise. > > So if this aspect was invented today, it probably wouldn't have > > enough use cases to get added to the language. > > Similar reasoning for procedures, or for stubs that might be > implemented eventually, but are initially not defined. As noted above, that's a dubious use-case. You don't want to be modifying the specification of routines when they change from TBD to implemented. > In any case, given that we have No_Return for procedures, it seems > practically free to extend it to functions, so why not remove this > somewhat arbitrary restriction? I don't think it is that free. There's more than a dozen "procedure"s in 6.5.1, and many, but not all of them need to be changed to something else. I also recall that there were several cases that only come up for functions that were not handled - someone would have to spent some careful thought time to ensure that everything needed is covered. (It's possible that the introduction of raise expressions uncovered some of those cases already.) And it surely would complicate existing implementations (all of which have switches for various language versions). Certainly, as editor it is far from free (probably two+ hours of work, depending on which wording changes are made and how good of an AI is proposed -- AI12-0063-1 does not have wording). I don't feel that strongly on this, but again we have an AI that has been decided and seems to be of fairly low value (YMMV). Reanimating it means that much less time to work on things that people are interested in. You do have some "new information" (the statement that "some users" have bumped into this), but that's a common thing and rarely causes much change in interest. What do the rest of you think??? **************************************************************** From: Tucker Taft Sent: Monday, March 26, 2018 9:29 PM For what it is worth, GNAT will probably support something like this. Our static analysis tool complains if you have a procedure that inevitably raises an exception unless you mark it as No_Return. We would like to do the same thing for a function that inevitably raises an exception. Having to invent another aspect seems pretty silly. One issue is that we are dealing with customers who have legacy code. Adding a pragma is one thing when it comes to reducing number of complaints from the static analysis tool, but removing a subprogram is much more disruptive, especially if it is part of a tagged type hierarchy. **************************************************************** From: Arnaud Charlet Sent: Tuesday, March 27, 2018 2:11 AM We should allow it for consistency and because we have real users needing it, whether we like it or not and whether we would personally write things differently is not really relevant. In addition it will make it easier for static analysis tools to take advantage of this information. **************************************************************** From: Tullio Vardanega Sent: Tuesday, March 27, 2018 3:39 AM I think this is a case where "reanimation" looks justified. **************************************************************** From: Jeff Cousins Sent: Tuesday, March 27, 2018 6:51 AM > What do the rest of you think??? Sorry to be boring, but I have no strong feelings either way. **************************************************************** From: Erhard Ploedereder Sent: Tuesday, March 27, 2018 5:23 PM I would like to see a coherent set of pros and cons. Right now, the discussion is too specific to write-ysrounds for the exception case in assertions. Adding random thoughts: - uniformity argues for it, as long as the rules are the same for functions and procedures. - having No-Return and still requiring a return stmt? Surely not. - Is there a way to distinguish "really no return due to an infinite loop inside" from "returns anyway, but with exceptions only"? (A big difference for flow analysis. This question is already open in 6.5.) **************************************************************** From: Randy Brukardt Sent: Tuesday, March 27, 2018 5:48 PM Can't help you with anything coherent. ;-) However, it struck me that the current rules don't work well with functions. Consider: function Foo (I : Natural) return Natural with No_Return is begin return (raise My_Exception with "Bad value " & I'Image); end Foo; Legal? The current rules say no, no return statement is allowed in a No_Return subprogram. However, not allowing the above also means that one cannot write a No_Return expression function (as those all have an implicit return statement). That also seems bad. This is just an illustration that this idea might seem obvious, but there is quite a bit of work lurking in there, as the procedure rules (which assume that they can't appear in specifications and declarative parts, among other things) might need quite a bit of enhancement. (Or not, point being that someone has to check everything carefully.) So, to me, that's the biggest con: this is a significant amount (not giant, but much more than trivial amount) of work. And probably nearly all of the 42 AIs we just voted on are more generally useful. So should we spend part of our very limited time on this topic? Would any of you have voted for this over BigNums (arbitrarily picking a somewhat popular AI)? Would you have even put this into 10th place in that recent vote? (I have to leave off 6 AIs that I wanted to vote for, because I could only vote for 10, and this one wouldn't have been one of the 16.) **************************************************************** From: Randy Brukardt Sent: Tuesday, March 27, 2018 6:08 PM ... > We should allow it for consistency and because we have real users > needing it, ... The ARG's charge is to ensure that the problems of "real users" are solved, not necessarily that they can write whatever construct that they *think* is the way to solve the problems. Otherwise, we'd have a hodge-podge of unrelated features that interact is bizarre ways. After all, users (and implementers) have a long history of proposing features without much consideration of how they fit into the full system nor without any realistic explanation of the problem being solved. So all ARG discussions are supposed to start from the problem (not the proposed solution). I was trying to understand the problem that the users were trying to solve -- it apparently has nothing to do with the problem as described in AI12-0063-1 (which we previously decided did not need to be solved). Without understanding the problem, it's impossible to determine either the importance or whether the solution is sufficient. I did say that the issue Tucker described didn't seem very convincing, but it's possible I didn't understand it very well. More detail (especially an example) is always welcome. The problem we have right now is we have far more plausible ideas than we have time, and thus we need to focus our energies on the most compelling problems. This doesn't (yet) seem to be a compelling problem. **************************************************************** From: Tucker Taft Sent: Tuesday, March 27, 2018 9:20 PM > I would like to see a coherent set of pros and cons. > Right now, the discussion is too specific to write-ysrounds for the > exception case in assertions. I think the only "con" is that it is a certain number of edits to the manual. The "pro" is that it is something that some customers have already bumped into, because they want to explicitly mark functions and procedures in their spec to indicate that any call will propagate an exception. > Adding random thoughts: > - uniformity argues for it, as long as the rules are the same for > functions and procedures. They would be the same, as far as I can imagine. I have included an update to AI12-0063 below, based on the earlier writeup, simplified by eliminating some special handling in membership tests, which is no longer necessary. > - having No-Return and still requiring a return stmt? Surely not. But with the availability of raise expression, there is no particular hardship in having to write a return statement. So I see no critical need to change this syntax rule as a side effect of specifying the aspect. > - Is there a way to distinguish "really no return due to an infinite > loop inside" from "returns anyway, but with exceptions only"? > (A big difference for flow analysis. This question is already open in > 6.5.) The issue seems no different for functions and procedures. I would consider the infinite loop case a corner case, and that is certainly not the way No_Return is currently used in 99% of the cases. See below for the proposed update. [This is version /01 of the AI - ED]. **************************************************************** From: Arnaud Charlet Sent: Wednesday, March 28, 2018 2:30 AM > > We should allow it for consistency and because we have real users > > needing it, ... > > The ARG's charge is to ensure that the problems of "real users" are > solved, not necessarily that they can write whatever construct that > they *think* is the way to solve the problems. Otherwise, we'd have a > hodge-podge of unrelated features that interact is bizarre ways. After > all, users (and > implementers) have a long history of proposing features without much > consideration of how they fit into the full system nor without any > realistic explanation of the problem being solved. Fully agreed. Which is why arguments of the form "I (Randy or someone else) would never do it this way and would always do it this other way" are not relevant for the ARG. **************************************************************** From: Erhard Ploedereder Sent: Wednesday, March 28, 2018 7:35 AM >> - having No-Return and still requiring a return stmt? Surely not. > But with the availability of raise expression, there is no particular >hardship in having to write a return statement. So I see no critical >need to change this syntax rule as a side effect of specifying the aspect. But this is then a non-uniformity. Returns in No-Return procedures are illegal. Returns in No-Return functions would (still) be mandatory, but useless. (In fact, should be caught by dead-code warnings.) I would not call this "uniform", quite the contrary. And to spare the implementer from making a explicit check conditional in exchange for such an obvious language design wart, seems entirely wrong. **************************************************************** From: Randy Brukardt Sent: Wednesday, March 28, 2018 1:45 PM > Fully agreed. Which is why arguments of the form "I (Randy or someone > else) would never do it this way and would always do it this other > way" are not relevant for the ARG. Surely. But if you reread my message, I made no such argument. Essentially acting as if I did rather than commenting on the substance of my discussion is not advancing the discourse in any way -- it's essentially a personal attack (Randy's technical arguments are irrelevant as they are always personal opinion). What I said (which is only two short paragraphs) can be summarized in two sentences: No_Return, being a permanent contractual element, is not well-suited to be used for temporary purposes (which TBDs are by their nature). Moreover, as an inherited contractual element, it can "poison" operations of a derived type, not allowing them to be fully implemented. Because of these factors, usage of No_Return in TBD situations does not seem compelling. Indeed, if this is the use case, I'd quickly expect a request to get rid of the inheritance of the aspect, as it's certain to cause issues in some cases. I could see that the aspect might have some use in NPTI situations (No Plan To Implement), but I'd expect those to be rare because such things violate LSP and prevent useful dispatching. (And I never said anything about them at all in my original message - surely nothing like "I'd never use them for that".) I don't see any personal opinion in the first statement; the closest thing is the bedrock Ada principle that specifications should change infrequently. I wouldn't expect anyone here to disagree with that, or for that matter the meaning of contractual elements; the entire design of Ada 2012 revolves around that principle. I suppose this is personal opinion in the sense that "Ada is good" is a personal opinion, but really these are part of the basic assumptions of discussion around here -- without them all discussion devolves to personal opinions and there is essentially no facts to talk about. **************************************************************** From: Tucker Taft Sent: Wednesday, March 28, 2018 1:56 PM > But this is then a non-uniformity. Returns in No-Return procedures are > illegal. Returns in No-Return functions would (still) be mandatory, > but useless. (In fact, should be caught by dead-code warnings.) Well, they are not useless if they determine what exception is raised. > I would not call this "uniform", quite the contrary. And to spare the > implementer from making a explicit check conditional in exchange for > such an obvious language design wart, seems entirely wrong. But this makes the pragma violate one of our "good taste in pragmas" rule because it now makes an illegal program legal. We have very few such pragmas (pragma Import is one of the few), and I don't see the need to make "pragma No_Return" be such a pragma. Furthermore, if users have already been compiling these exception-raising functions (which is true for legacy users), you are forcing them to *remove* the return statements to be allowed to add the pragma. **************************************************************** From: Tucker Taft Sent: Wednesday, March 28, 2018 2:09 PM >Well, they are not useless if they determine what exception is raised. As in: return (raise Foobar with "String"); **************************************************************** From: Tucker Taft Sent: Wednesday, March 28, 2018 1:59 PM I believe this discussion has gone sideways. Please look at the new version of the AI I proposed, and comment on that rather than on comments on comments on some earlier discussion before the AI was proposed. In particular, if more needs to be added to the !problem of this AI, let me know. **************************************************************** From: Randy Brukardt Sent: Wednesday, March 28, 2018 2:16 PM ... > But this is then a non-uniformity. Returns in No-Return procedures are > illegal. Returns in No-Return functions would > (still) be mandatory, ... Yes, that's what Tucker's AI says. > ...but useless. (In fact, should be caught by dead-code warnings.) No, because one uses raise expressions rather than raise statements in such functions. Tucker's rule requires all returns in the function be such returns. One would use "return (raise Something);" instead of "raise Something" in such functions (and probably in most new functions). When we discussed again eliminating the return statement requirement, we decided that the presence of raise expressions means that writing return (raise Program_Error); is always legal and easy, so there was not serious need to eliminate the protections that the rule provides. Clearly the same is true here. It's mildly annoying that an expression function like: function Raise_It (Is_Valid : in Boolean) return Natural is (if Is_Valid then raise Constraint_Error else raise Program_Error); can't be No_Return, but it's probably not worth the complication to support that. > I would not call this "uniform", quite the contrary. And to spare the > implementer from making a explicit check conditional in exchange for > such an obvious language design wart, seems entirely wrong. The alternative of not allowing returns only in No_Return functions means that adding or removing the aspect would require potentially extensive changes to the body, even if the semantics didn't change. That doesn't seem like a good idea, and it seems worse than the non-uniformity of the legality rules for a little-used aspect like No_Return. (I just don't find No_Return worthwhile enough to take time away from other, more valuable AIs, which is a different discussion altogether. If we had 2 more years before lockdown, rather than 3 days, I wouldn't care about that at all.) **************************************************************** From: Randy Brukardt Sent: Wednesday, March 28, 2018 2:31 PM ... > > I would not call this "uniform", quite the contrary. And to spare > > the implementer from making a explicit check conditional in exchange > > for such an obvious language design wart, seems entirely wrong. > > But this makes the pragma violate one of our "good taste in pragmas" > rule because it now makes an illegal program legal. Considering that the pragma is obsolescent, using it at all violates "good taste", so what it does isn't particularly relevant. I'd rather not define it at all, but the naming restrictions on implementation-defined pragmas would require that we do so (an implementation can't add functionality to a language-defined pragma). ... > Furthermore, if users have already been compiling these > exception-raising functions (which is true for legacy users), you are > forcing them to *remove* the return statements to be allowed to add > the pragma. I had independently noted this effect. It works both ways, too; having to change raise statements into returning raise expressions when removing the aspect makes users much more "locked-in" to the aspect. That seems bad for the TBD use case (whether or not it is compelling). **************************************************************** From: Tucker Taft Sent: Wednesday, March 28, 2018 2:54 PM > Considering that the pragma is obsolescent, using it at all violates > "good taste", so what it does isn't particularly relevant. I'd rather > not define it at all, but the naming restrictions on > implementation-defined pragmas would require that we do so (an > implementation can't add functionality to a language-defined pragma). Well pragma or aspect, it still seems in "bad taste" to have it make an illegal program legal, unless its name happens to be "Import." For a pragma it is particularly obviously in bad taste, because older compilers will ignore the pragma if they don't recognize it, but then discover the illegality. > ... >> Furthermore, if users have already been compiling these >> exception-raising functions (which is true for legacy users), you are >> forcing them to *remove* the return statements to be allowed to add >> the pragma. > > I had independently noted this effect. It works both ways, too; having > to change raise statements into returning raise expressions when > removing the aspect makes users much more "locked-in" to the aspect. > That seems bad for the TBD use case (whether or not it is compelling). It would be possible to adjust the AI so No_Return would allow plain old return statements if they immediately follow a "raise" statement, for those who haven't graduated to the use of raise expressions. There is a separate incentive to switch to using return because your static analysis tool might be complaining about dead code otherwise. But we could allow No_Return to accept the obviously dead return without complaint. **************************************************************** From: Arnaud Charlet Sent: Wednesday, March 28, 2018 3:02 PM > > Fully agreed. Which is why arguments of the form "I (Randy or > > someone else) would never do it this way and would always do it this > > other way" are not relevant for the ARG. > > Surely. But if you reread my message, I made no such argument. > Essentially Quoting: << It seems like a mistake to put into the contract (and No_Return is clearly part of the contract) that some operation is TBD. And you're not allowed to later change that contract via derivation, so it seems to "poison" operations of a tagged type. Ergo, it seems inappropriate for the uses described above. Probably people using No_Return on procedures in such cases have already found that out, but maybe not. >> You are above stating your opinion that you wouldn't do things this way because (guessing from your various emails) you have a pure OO design in mind. Similarly: << As noted above, that's a dubious use-case. You don't want to be modifying the specification of routines when they change from TBD to implemented. >> is also what I described as "I wouldn't do it this way". Changing the specification when going from TBD to implemented is routine for some people (and trivial for some IDEs). I understand that "you wouldn't do it this way", but this isn't shared by everyone. In particular, it is frequent that you need to refine your specification to e.g. add more precise contracts because most people don't "get it right the first time", so while it's a nice goal to ensure that (quoting you) "specifications (...) change infrequently" things are not so simple in pratice and even when specifications only change infrequently, you are acknowledging that they do need to change from time to time. > acting as if I did rather than commenting on the substance of my > discussion is not advancing the discourse in any way -- it's > essentially a personal attack (Randy's technical arguments are > irrelevant as they are always personal opinion). > > What I said (which is only two short paragraphs) can be summarized in > two sentences: > No_Return, being a permanent contractual element, is not well-suited > to be used for temporary purposes (which TBDs are by their nature). > Moreover, as an inherited contractual element, it can "poison" > operations of a derived type, not allowing them to be fully implemented. People use functions (and subprograms) without using derived types all the time, so I don't see the above as being a strong enough justification to reject the feature. > Because of these factors, usage of No_Return in TBD situations does > not seem compelling. Indeed, if this is the use case, I'd quickly > expect a request to get rid of the inheritance of the aspect, as it's > certain to cause issues in some cases. > > I could see that the aspect might have some use in NPTI situations (No > Plan To Implement), but I'd expect those to be rare because such > things violate LSP and prevent useful dispatching. (And I never said > anything about them at The majority of Ada programmers do not do OO and wouldn't know what LSP stand for. **************************************************************** From: Randy Brukardt Sent: Wednesday, March 28, 2018 3:08 PM >I believe this discussion has gone sideways. Please look at the new >version of the AI I proposed, and comment on that rather than on >comments on comments on some earlier discussion before the AI was >proposed. In particular, if more needs to be added to the !problem of >this AI, let me know. I don't think you can come up with a very strong !problem -- the use cases posed to date are dubious for reasons I discussed in my previous e-mails. Specifically: >For purposes of static analysis, it is helpful to be able to explicitly >indicate that a function never returns normally, typically because it >always propagates an exception. I'm not sure what value this has for static analysis; typical analysis ignore the possibility of exceptions because they leave the flow of control being considered, never to return. There is a bit of value from identifying dead code (which typically represents a bug if it is supposed to have some effect), but generally such code doesn't exist (if the programmer did in fact understand the contract). Is there any other value for static analysis, or is it purely identification of dead code (that probably wasn't intended to be dead)? >Such a function might exist because it is inherited from a progenitor, >but should not be called. This violates LSP, so one wants to minimize such cases. Depending of your view of OOP, this might be considered bad practice as well. (Arno: Not my view!) Still, this is the strongest of a bad lot of use cases. :-) This does occasionally happen in practice, most of us aren't OOP purists. There is, however, the problem that this is then required for future derivations of the type; one might want to implement the function in a descendant, but it is required to be no-returning. As I've noted previously, if this is the use case, then the definition of No_Return is wrong for that use case. (Insisting on preserving LSP in a situation when we know LSP has been previously violated seems silly.) >It might represent a potential future capability that has been >deactivated for a particular version of the software. This seems weak as well. One generally wants to minimize change in specifications, and defining the future capability at all suggests that is the intent (otherwise, leave the future stuff out altogether). Then using a contract on the specification that requires a required future change to the specification which defeats the purpose of defining it in the first place. Writing a contractual promise that a subprogram will always raise an exception is something that clients ought to be able to depend upon, now and in the foreseeable future. If one can foresee that the contract is not going to be true for long, one ought to refrain from writing it (in order to avoid modifying contracts more than necessary). If you don't expect to implement the functionality in the foreseeable future, why define it at all? It's of no use for clients in that case. >And perhaps the biggest reason is that Ada users find it confusing that >No_Return is available for procedures, but not for functions. Since all the issues noted above apply to procedures as well, using No_Return on procedures for the above reasons also is questionable. Therefore, the above issue boils down to wondering why some questionable uses are allowed, and others aren't. Hard to justify spending some of our limited time on this rather than (say) Bignums. **************************************************************** From: Tucker Taft Sent: Wednesday, March 28, 2018 3:27 PM >> For purposes of static analysis, it is helpful to be able to >> explicitly indicate that a function never returns normally, typically >> because it always propagates an exception. > > I'm not sure what value this has for static analysis; typical analysis > ignore the possibility of exceptions because they leave the flow of > control being considered, never to return. Not true in the static analyzers we sell. There is a lot of energy spent on worrying about what sorts of inputs will lead to exceptions, and if calling a given function is known to result in raising an exception, then the static analyzer will attempt to identify exactly what inputs to a subprogram that includes a call on such a function might allow such a call to be reached. > There is a bit of value from identifying dead code (which typically > represents a bug if it is supposed to have some effect), but generally > such code doesn't exist (if the programmer did in fact understand the > contract). Is there any other value for static analysis, or is it > purely identification of dead code (that probably wasn't intended to > be dead)? As indicated, this is not about dead code. This is about identifying what input values might result in a run-time exception. > >> Such a function might exist because it is inherited from a >> progenitor, but should not be called. > > This violates LSP, so one wants to minimize such cases. Depending of > your view of OOP, this might be considered bad practice as well. > (Arno: Not my > view!) True, but not everyone lives and dies by LSP. It imposes limitations that sometimes just don't work out very well in a given context. And again, we are not legislating project coding patterns. > ... > > Since all the issues noted above apply to procedures as well, using > No_Return on procedures for the above reasons also is questionable. > Therefore, the above issue boils down to wondering why some > questionable uses are allowed, and others aren't. > > Hard to justify spending some of our limited time on this rather than > (say) Bignums. But we also should weigh the cost/benefit equation. This is trivially low cost compared to any of our other AIs, both in terms of specification and implementation, and this one will benefit existing users of Ada. **************************************************************** From: Randy Brukardt Sent: Wednesday, March 28, 2018 5:05 PM > << > It seems like a mistake to put into the contract (and No_Return is > clearly part of the contract) that some operation is TBD. And you're > not allowed to later change that contract via derivation, so it seems > to "poison" > operations of a tagged type. Ergo, it seems inappropriate for the uses > described above. Probably people using No_Return on procedures in such > cases have already found that out, but maybe not. > >> > > You are above stating your opinion that you wouldn't do things this > way because (guessing from your various emails) you have a pure OO > design in mind. You're stretching. I said specifically that it wouldn't work well in OO designs (and gave two reasons). And it is only OO designs that would have this problem of inheriting an operation that shouldn't be called (one can declare such operations "abstract" for untagged derivations (rare anyway) without any other penalty -- a compile-time check is always preferred to a runtime check). To the extent that I wouldn't do it, that's only because it wouldn't work well. *I* am not an OOP purist! It seems that you want to discount my opinion without having to actually worry about such things as actual use-cases. I suppose that is your right, but it does not lead to useful discourse. > Similarly: > << > As noted above, that's a dubious use-case. You don't want to be > modifying the specification of routines when they change from TBD to > implemented. > >> > > is also what I described as "I wouldn't do it this way". > Changing the specification when going from TBD to implemented is > routine for some people (and trivial for some IDEs). I understand that > "you wouldn't do it this way", but this isn't shared by everyone. That's news to me. I've never dealt with any system where the build time was so short that one could change specs frequently, regardless of the design. Now, of course, if it is the *spec* that is TBD, then of course it is changing rapidly. That isn't the case we're talking about here -- one has to be able to compile/analyze clients in order for No_Return to have any useful effect. We're talking about cases where the body of the operation is TBD, but the specification is (thought to be) finished. > In particular, it is frequent that you need to refine your > specification to e.g. add more precise contracts because most people > don't "get it right the first time", so while it's a nice goal to > ensure that (quoting you) "specifications (...) change infrequently" > things are not so simple in pratice and even when specifications only > change infrequently, you are acknowledging that they do need to change > from time to time. Surely the specification will have to change periodically for unplanned reasons. But one does not want to change the specification unnecessarily, and as such, one does not want to change it intentionally. Certainly not a commandment, but it makes the use of No_Return this way less appealing as it has to be done. (Note that my argument all along is that this usage is a "weak case", not that it is "no case".) I'm trying to discuss "standard" Ada usage here; changing specifications as little as possible is a basic principle of pretty much all Ada-related design: package-based, object-based, and OOP included. There might be an early churn on a new package, but after that there is little change. Ada as a whole was designed assuming this sort of usage, as such I view it as fair game to include in discussions. If we didn't include usage discussions, we'd have almost no way to differentiate between the dozens of proposals that we get. You're essentially saying that the whims of (a small number) of AdaCore customers trumps any usage-based argument. If that was really true, we wouldn't need a standard at all --- the whole purpose of a standard is to keep sanity checks on vendor and user suggested proposals. > > acting as if I did rather than commenting on the substance of my > > discussion is not advancing the discourse in any way -- it's > > essentially a personal attack (Randy's technical arguments are > > irrelevant as they are always personal opinion). > > > > What I said (which is only two short paragraphs) can be summarized > > in two sentences: > > > No_Return, being a permanent contractual element, is not well-suited > > to be used for temporary purposes (which TBDs are by their nature). > > Moreover, as an inherited contractual element, it can "poison" > > operations of a derived type, not allowing them to be fully implemented. > > People use functions (and subprograms) without using derived types all > the time, so I don't see the above as being a strong enough > justification to reject the feature. (1) The first sentence (which is by far the more important one) has nothing to do with derived types. I don't use derived types hardly at all, but I'd still have an issue with the first sentence. (2) I never said anything about rejecting the feature. I said things about having insufficient time to spend on yet another proposal (especially one of minimal value), given the very tight deadlines for Ada 2020 (we're supposed to provide a list of all of the features that will be in Ada 2020 effectively immediately after our meeting on Monday). Propose it for Ada 2028 (or whatever the next language version is) and you'll get no argument from me. > > Because of these factors, usage of No_Return in TBD situations does > > not seem compelling. Indeed, if this is the use case, I'd quickly > > expect a request to get rid of the inheritance of the aspect, as > > it's certain to cause issues in some cases. > > > > I could see that the aspect might have some use in NPTI situations > > (No Plan To Implement), but I'd expect those to be rare because such > > things violate LSP and prevent useful dispatching. (And I never said > > anything about them at > > The majority of Ada programmers do not do OO and wouldn't know what > LSP stand for. Neither do I. But in that case, there is no inherited operation in the first place, so there is no reason for a subprogram that is never going to be implemented to be in the specification. [Why have an unimplemented subprogram sitting around? Just delete or comment out the thing and get a compile-time check. The only time you can't do that is in an inheritance situation.] This seems like a legitimate use case, but *only* within OO designs. Keep in mind, Ada is really designed around expected use-cases. Users can and do go outside of those use-cases, but the Standard has no obligation to (and - opinion alert - should not) make other use-cases work well. If you don't separate your specifications well from your bodies, using Ada is going to be harder than necessary. Not the standard's job. **************************************************************** From: Randy Brukardt Sent: Wednesday, March 28, 2018 5:18 PM > Not true in the static analyzers we sell. There is a lot of energy > spent on worrying about what sorts of inputs will lead to exceptions, > and if calling a given function is known to result in raising an > exception, then the static analyzer will attempt to identify exactly > what inputs to a subprogram that includes a call on such a function > might allow such a call to be reached. OK, thanks. It might help to add a line about that to the !proposal, since the only use I know of for No_Return is dead code. ... > > Hard to justify spending some of our limited time on this rather > > than (say) Bignums. > > But we also should weigh the cost/benefit equation. This is trivially > low cost compared to any of our other AIs, both in terms of > specification and implementation, and this one will benefit existing > users of Ada. Surely not *any* of our other AIs. We could take cost into account; I sort of did that with the *s and ?s in the report I sent the other day. This one would have been a ? (too much wording change to be a *). Note that all of the easy AIs ended up toward the bottom of the vote. Here are all of the AIs that I'd consider easier than this one (I expect much less wording change with each of these, YMMV): 18)*10 pts, 0 first place votes, 3 total ballots: AI12-0248-1 Null array aggregates 27)* 7 pts, 0 first place votes, 2 total ballots: AI12-0???-1 Omit subtype name from object renaming 29)* 7 pts, 0 first place votes, 1 total ballots: AI12-0256-1 Aspect No_Controlled_Subcomponents 34)* 3 pts, 0 first place votes, 1 total ballots: AI12-0213-1 Ids in record syntax 38)* 0 pts, 0 first place votes, 0 total ballots: AI12-0237-1 Reading the representation of an enumeration value 39)* 0 pts, 0 first place votes, 0 total ballots: AI12-0235-1 Root_Storage_Pool should be Pure I'd say several of these are more important than this one, again YMMV. In any case, I'd rather spend time on alternatives to AI-240, or any of Brad's AIs, or even 'Reduce, then spending any time on any of these. Maybe if we have a few extra minutes to kill at the end of a session. Otherwise, we'd be totally ignoring the results of the priority vote (to date, no one has said that they would vote for this AI rather than some other AI they did vote for). **************************************************************** From: Tucker Taft Sent: Wednesday, March 28, 2018 7:45 PM >OK, thanks. It might help to add a line about that to the !proposal, since >the only use I know of for No_Return is dead code. OK, I will add some words to this effect. ... >>>Hard to justify spending some of our limited time on this >>>rather than (say) Bignums. >> >>But we also should weigh the cost/benefit equation. This is >>trivially low cost compared to any of our other AIs, both in >>terms of specification and implementation, and this one will >>benefit existing users of Ada. > > >.... We could take cost into account; I sort >of did that with the *s and ?s in the report I sent the other day. This one >would have been a ? (too much wording change to be a *). Note that all of >the easy AIs ended up toward the bottom of the vote. Certainly I used my votes to rank in terms of total perceived benefit, ignoring cost. I figured the very low cost, low to medium benefit AIs would take care of themselves. I think we should probably have a separate vote for very low cost AIs, to decide their relative perceived benefit. Clearly when you are doing a revision, you want to make the simple fixes, along with some number of big items. To me this is a simple fix worth doing. Clearly not everyone agrees. ... >I'd say several of these are more important than this one, again YMMV. So perhaps we should have a separate vote for the "low hanging" fruit. **************************************************************** From: Tucker Taft Sent: Wednesday, March 28, 2018 7:58 PM Here is a further update. I updated the !problem and the !discussion to incorporate some of the back-and-forth e-mails. [This is version /02 of the AI - ED]. **************************************************************** From: Arnaud Charlet Sent: Thursday, March 29, 2018 3:39 AM > That's news to me. I've never dealt with any system where the build > time was so short that one could change specs frequently, regardless of the > design. In these days of multi-core and fast network of multi-core machines, this is actually becoming common that build time isn't an issue with GNAT, whatever the change. **************************************************************** From: Jeff Cousins Sent: Thursday, March 29, 2018 5:08 AM Maybe a small team hacking away at the code would have rapidly changing specs, but large multiple team projects spread over multiple sites? I doubt it. If the implementation of a body changed such that a parameter was no longer needed, we would stick in a pragma Unreferenced for the unused parameter rather than change a spec that was on an inter-team or inter-site boundary. Or if the design evolved such that an additional parameter was desired, there would be a very long overlap period where both the old and new forms were in use, with the old form having pragma Obsolescent added. I’m not against resurrecting this as a new AI, I just don’t see it as having any great priority, e.g. compared with the parallelism stuff. *************************************************************** From: Erhard Ploedereder Sent: Thursday, March 29, 2018 6:54 AM >> Well, they are not useless if they determine what exception is raised. > > As in:  return (raise Foobar with "String"); Isn't that semantically the same as raise Foobar with "String"; ? I.e., you don't get to the point of "return"ing anyway. So, "return(...)" is just a bloody nuisance code. So, for the sake of legacy code where users want to "merely" add the No-Return aspect, the wart will affect all future users. Incidentally, what happened when No-Return was introduced for procedures and users "merely" added that to their code? The "return"s became illegal, and that was apparently acceptable at the time. And now, for functions, it is not? No, the legacy argument does not hold water at all. Nor does the "different exceptions" argument, if return is forbidden in the first place. The argument about "no semantic differences" by pragmas has already been violated, when the No-Return aspect was introduced for procedures. It is hardly surprising that the same violation continues for functions as well. So, no water there, either. **************************************************************** From: Tucker Taft Sent: Thursday, March 29, 2018 7:35 AM > ... > > The argument about "no semantic differences" by pragmas has already > been violated, when the No-Return aspect was introduced for > procedures. It is hardly surprising that the same violation continues for > functions as well. > So, no water there, either. Not the case for procedures, because return-less procedures are legal. **************************************************************** From: Tucker Taft Sent: Thursday, March 29, 2018 7:43 AM > ... > > So, for the sake of legacy code where users want to "merely" add the > No-Return aspect, the wart will affect all future users.... Not sure what "wart" you are talking about. In any case, I could imagine an intermediary position, namely that the presence of the No_Return pragma/aspect would *allow* the function to omit return statements, but if there are return statements, they must return a raise-expression or a non-returning function call, or be immediately preceded by a raise statement or a non-returning procedure call. The goal we have for our static-analysis tool users is that they can merely *add* this pragma, and not have to make any other change, to a function that already inevitably raises an exception. Currently, such a function (like all Ada functions) must have a return statement, and so it is likely that they already satisfy the rules described above, namely the return statement returns a raise expression, or is immediately preceded by a raise statement. Would this intermediary position be satisfactory? **************************************************************** From: Jean-Pierre Rosen Sent: Thursday, March 29, 2018 9:42 AM Thinking about it.... If you put a No_Return in a function, and there is no return statement nor raise statement... then Program_Error will be raised, so it's still OK! **************************************************************** From: Tucker Taft Sent: Thursday, March 29, 2018 11:03 AM It is illegal to have a return-less function in Ada, at the moment (see RM 6.5(5)). That is what we are debating... **************************************************************** From: Jean-Pierre Rosen Sent: Thursday, March 29, 2018 12:01 PM I was talking about the case where you have a no_return in the function, this allows not having a return statement, and you don't have a raise statement either... Noting that (I assume) P_E would be raised, and therefore the No_Return still holds! **************************************************************** From: Randy Brukardt Sent: Thursday, March 29, 2018 1:24 PM Note that this has nothing whatsoever to do with whether or not we require a return statement. function Foo (N : in Natural) return Natural is begin if N > 12 then return (raise Constraint_Error); end if; end Foo; The above is a non-returning function by any sensible definition, and certainly by the one Tucker proposed. There's no requirement that the returns be last. That's necessary to allow something like: function Foo2 (N : in Natural) return Natural is begin if N > 12 then return (raise Constraint_Error); else return (raise Program_Error); end if; end Foo2; Neither of these returns are "last", but we'd still want this considered a non-returning function. **************************************************************** From: Bob Duff Sent: Thursday, March 29, 2018 2:00 PM What do you mean by "last", and why do you point this out? They both look "last" to me. And by the way, why do you parenthesize the raise exprs? **************************************************************** From: Randy Brukardt Sent: Thursday, March 29, 2018 9:03 PM > What do you mean by "last", and why do you point this out? > They both look "last" to me. I was talking about statically last, which they would have to be if mentioned in a compile-time rule (Ada doesn't depend on flow for Legality Rules). They're of course dynamically last, presuming you are certain that "end if" doesn't have an effect itself. The first return can't possibly be statically last, because there's another return textually after it. > And by the way, why do you parenthesize the raise exprs? It's required in some cases, and since I'm not a compiler and don't use them often, I don't remember what those cases are. So I'm parenthesizing them to be safe. I really did not want to recommend something syntactically illegal! **************************************************************** From: Randy Brukardt Sent: Thursday, March 29, 2018 2:02 PM > Isn't that semantically the same as > raise Foobar with "String"; > ? I.e., you don't get to the point of "return"ing anyway. So, > "return(...)" is just a bloody nuisance code. No, because we need to allow that in order to allow expression functions to be non-returning. (The "return" is implicit in that case, but it's surely there.) Just banning returns in functions would require a major restructuring if someone needed to add No_Return to an expression function. (That is, the expression function [in the spec] would have to be reimplemented with a real body [in the body].) Given the plausible use cases that Tucker is suggesting, that is fairly likely, and thus would be a significant annoyance to use of the feature. Ada has enough annoyances without adding more out of some misguided adherence to uniformity. > So, for the sake of legacy code where users want to "merely" > add the No-Return aspect, the wart will affect all future users. That's by far the most common use case. To my mind, adding and removing of these aspects would be fairly frequent, as they are being added to mark a temporary state of code, and most likely after the fact as analysis needs dictate. (The original intended use case as a fixed contract seems much less likely since raise expressions have been introduced.) It has nothing to do with "legacy" code per-se, but rather with code that already exists (possibly only for a short time) when the need to add the aspect is discovered. And at least some of those additions will be temporary (in part because of the effect on OOP). > Incidentally, what happened when No-Return was introduced for > procedures and users "merely" added that to their code? The "return"s > became illegal, and that was apparently acceptable at the time. And > now, for functions, it is not? > No, the legacy argument does not hold water at all. The use of return statements in procedures is not that common. There are various reasons for avoiding their use (possible unintentionally skipping clean-up code, possible return from the wrong subprogram when code refactoring), and they simply aren't needed in many cases. Moreover, for a candidate procedure to be No_Return, any returns would have to be preceded by "raise" anyway, and my compiler (and probably many others) would complain the "return" is dead code. So it probably would have been removed long before adding the aspect. In any case, it is completely impossible (because of the expression functions case above) to have procedures and functions completely consistent. There is going to be a "bump under the rug" as Tuck puts it. We have to have the inconsistency of not allowing no-returning expression functions (boo!) or the inconsistency of allowing "return" only in function bodies with a particular form. There's no perfect solution, but I know I'd rather keep the rest of the language consistent and put the only warts in this little used aspect. Saving 9 characters (and 9 characters that are a commonly used idiom at that) to *add* an inconsistency in the language as a whole seems silly. > Nor does the "different exceptions" argument, if return is forbidden > in the first place. That's not a viable option, IMHO. > The argument about "no semantic differences" by pragmas has already > been violated, when the No-Return aspect was introduced for > procedures. Only if the user has been ignoring the compiler's nagging about dead code. Any returns in a procedure that could have No_Return are necessarily dead code and certainly will be flagged as such. (If they're not dead code, then obviously the routine CAN return.) > It is hardly surprising that the > same violation continues for functions as well. > So, no water there, either. Fine then vote against the AI. I certainly would vote against yours (no expression functions = No vote). We've spent altogether too much time on this relatively useless feature already, and this is what I feared would happen. **************************************************************** From: Erhard Ploedereder Sent: Thursday, March 29, 2018 8:35 PM >> ... >> >> So, for the sake of legacy code where users want to "merely" add the >> No-Return aspect, the wart will affect all future users.... > > Not sure what "wart" you are talking about. The wart is that you assert by aspect that the function will not return, while another rule mandates that it shall have a return statement. > In any case, I could imagine an intermediary position, namely that the > presence of the No_Return pragma/aspect would *allow* the function to omit > return statements, but if there are return statements, they must return a > raise-expression or a non-returning function call, or be immediately > preceded by a raise statement or a non-returning procedure call. Better, yes, but you see how you need to amend return rules by non-trivial constraints, merely to keep a few folks with very rare code completely happy. Prohibit returns and these rules go away. And, look at the example below. > The goal we have for our static-analysis tool users is that they can merely > *add* this pragma, and not have to make any other change, to a function that > already inevitably raises an exception. Currently, such a function (like > all Ada functions) must have a return statement, and so it is likely that > they already satisfy the rules described above, namely the return statement > returns a raise expression, or is immediately preceded by a raise statement. I would expect existing function code of the form: begin if cond1 then last_wishes1(...); raise this_except; elsif cond2 then last_wishes2(...); raise that_except; end if; return language_rule = nuiscance; -- dead code to keep compiler happy end >I have seen code like that to centralize last-wishes handling (admittedly in procedures, not functions). This does not match your rules. => Unhappy customer by your position, since you do make his code illegal. Your added constraints on returns cater to one specific style, and not what I would argue to be a more common style. I would not have expected any of our Ada programmers to have discovered the idea that the return expression could be a raise. And there is no unhacky way to tie in last wishes into a raise expression, so your pattern is quite limited in its application, while the above is not. **************************************************************** From: Randy Brukardt Sent: Thursday, March 29, 2018 8:58 PM ... > Better, yes, but you see how you need to amend return rules > by non-trivial constraints, merely to keep a few folks with > very rare code completely happy. Prohibit returns and these > rules go away. ...along with the ability to use expression functions. Which is introducing a much worse wart. ... > I would expect existing function code of the form: > begin > if cond1 then last_wishes1(...); raise this_except; > elsif cond2 then last_wishes2(...); raise that_except; > end if; > return language_rule = nuiscance; -- dead code to keep compiler happy end The Ada 2012 idiom is return (raise Program_Error); -- dead code to keep compiler happy which doesn't require any confusing declarations or expressions and doesn't change anything about the semantics of the function. > >I have seen code like that to centralize last-wishes handling > (admittedly in procedures, not functions). > This does not match your rules. => Unhappy customer by your > position, since you do make his code illegal. Your added > constraints on returns cater to one specific style, and not > what I would argue to be a more common style. What this actually does is gives the customer a teachable moment, which allows the mentor to show the customer the much preferred idiom above. > I would not have expected any of our Ada programmers to have > discovered the idea that the return expression could be a > raise. You have to tell them! > And there is no unhacky way to tie in last wishes into > a raise expression, so your pattern is quite limited in its > application, while the above is not. And your pattern prevents any use at all with expression functions (or we have adopt a bunch of truly special cases for expression functions to essentially say that it is a return statement always except when Erhard P. won't let it be. How does that simplify the rules??? **************************************************************** From: Erhard Ploedereder Sent: Thursday, March 29, 2018 10:21 PM The expression function according to 6.8. reads function foo is (raise FooBar with "String") with No_Return => True; Not a return in sight here The "return" comes in as an "as if"-rule in 6.8., surely easily changed with no impact on users whatsoever. I.e., make the equivalence read X := expression; return X; -- to deal with raise expression vs statement and then No_Return allows/mandates omission of "return" in real bodies as well as in the "as if" rule. **************************************************************** From: Randy Brukardt Sent: Thursday, March 29, 2018 11:44 PM > The "return" comes in as an "as if"-rule in 6.8., surely easily > changed with no impact on users whatsoever. I.e., make the equivalence > read X := expression; return X; -- to deal with raise expression vs > statement and then No_Return allows/mandates omission of "return" in > real bodies as well as in the "as if" rule. This doesn't work, at least without further changes in 7.6. The above makes an extra object, causing an extra Finalize/Adjust pair (which a user could discover). A compiler could optimize that away, but it wouldn't have to (and my experience is that compilers are bad at that sort of optimization), so users potentially would be affected. Moveover, it is illegal for build-in-place types. (You cannot return/assign a limited stand-alone object.) You could try to change the rules for that, but it makes no sense to do so (spreading a cancer even further). There's also the further problem that the object here is not necessarily constrained, so declaring an object is expensive; there are checks based on the accessibility of the expression being returned (which would be lost with the intermediate object); the tag of the return type is determined by different rules for return and a stand-alone object. Etc. Remember all of the trouble we had with the 'Old attribute? -- it tries to do this, and it doesn't work very well without a pile of special case rules. The only way to do what you want without breaking lots of return statement rules is a special case rule specifically about No_Return, which is massive wart of its own. What you are suggesting is taking a feature of minimal value (surely some people will use it, but not that often), and spreading its warts all over the Standard. If it has to have warts (and this does -- procedures and functions are different beasts and almost never should be considered together), at least keep them in one place. On top of which, we've repeatedly looked at lifting the "must have return rule" and have always concluded to leave it alone. It seems bizarre to lift it for a tiny corner case. And so on. I'm happy with Tucker's current version; it keeps the mess where it belongs (a little-used aspect) and not all over the standard (and compilers). **************************************************************** From: Randy Brukardt Sent: Friday, March 30, 2018 12:11 AM Tucker Taft wrote at the start of this discussion: ... > In any case, given that we have No_Return for procedures, it seems > practically free to extend it to functions, ... I'm approaching 5 hours spent on reading/responding/filing e-mail on this topic. I realize I've been part of the issue, but still, if this is "practically free", I'm not looking forward to the topics that take real work!! :-) **************************************************************** From: Randy Brukardt Sent: Friday, March 30, 2018 1:24 AM > In these days of multi-core and fast network of multi-core machines, > this is actually becoming common that build time isn't an issue with > GNAT, whatever the change. Build time surely are faster these days than back in the day, but the only build time that is not an issue is a build that finishes before you can get your hands back to type the next command. :-) GNAT is far from that today, even on relatively small programs. Same for Janus/Ada. They build about the same speed, at least on the smallish programs that I've used both. **************************************************************** From a private discussion about the Editorial Review comments of Randy Brukardt: Randy Brukardt: "procedure" needs to be changed in J.15.2(4/3) as well, it shouldn't be changed in "null procedure" in J.15.2(3/3). The return statement Legality Rule is in part: "... an expression that is a raise_expression or a call on a nonreturning function" Humm...actually, we have a problem with this wording. I tend to write: return (raise Program_Error); because I can't remember if the parens are required or not (they're required for conditional expressions and the like, but raise expressions are different). However, the above rule makes that illegal, as the above is a parenthesized expression, not either of the things allowed. We need at least to allow parenthesized raise_expressions and function calls lest the rule be ridiculous. One could imagine going further and also allowing qualified expressions and conditional expressions where all of the dependent expressions qualify -- that would be best for expression functions. [But that would require reopening the AI.] Thus, we need at least: "... an expression that is a raise_expression, a call on a nonreturning function, or a parenthesized expression of one of these." Steve Baird: ... like Randy, I always put parens around a raise expression if the enclosing construct doesn't already provide them. If a compiler rejected return (raise Program_Error); then I would probably be annoyed (assuming that for some reason I was writing a No_Return function; I don't know why I would be doing that). ... I think that opening the door wide enough to allow a parenthesized expression is pushing the bounds of editorial review (justified in part by Randy's observation that folks forget that the syntax for these guys does not include parens), and anything more (e.g., allowing conditional expressions) is well outside of those bounds. Tucker Taft: This [the wording Randy proposed above] seems more than adequate. Anything more seems silly. Jeff Cousins: I'm OK with [considering] it as editorial review. **************************************************************** From: Bob Duff Sent: Friday, March 30, 2018 7:15 AM > ... > > In any case, given that we have No_Return for procedures, it seems > > practically free to extend it to functions, ... > > I'm approaching 5 hours spent on reading/responding/filing e-mail on > this topic. I realize I've been part of the issue, but still, if this > is "practically free", I'm not looking forward to the topics that take > real work!! :-) This is what is known as bikeshedding. **************************************************************** From: Tucker Taft Sent: Friday, March 30, 2018 8:20 AM > This is what is known as bikeshedding. Agreed. Simple issue, easy to talk about forever. **************************************************************** From: Ben Brosgol Sent: Tuesday, April 3, 2018 2:53 PM This proposal is two days too late, but one way to satisfy all parties in this discussion is to require a return statement in a function (even one with the No_Return aspect) but to allow a special syntax in such cases: return not; [Please notice the date minus two days - Editor.] **************************************************************** From: Tucker Taft Sent: Tuesday, April 3, 2018 3:14 PM LS (Laughing silently... ;-) **************************************************************** From: Randy Brukardt Sent: Tuesday, April 3, 2018 6:29 PM That seems to be a fitting end to this discussion. (At least, I hope it's the end. ;-) **************************************************************** From: Erhard Ploedereder Sent: Tuesday, April 3, 2018 8:01 PM Ah, I see ... Looks good in all versions and particularly in the brevity of an expression function, alas extra syntay required ... function AI12-0063 return Black_Hole with No_Return is begin approach_to_closely; return not; -- wise Yoda return trick end AI12-0063; function AI12-0063 return Black_Hole with No_Return is (not); ****************************************************************