!standard 09.05.02 (27) 01-10-17 AI95-00278/01 !class amendment 01-10-17 !status work item 01-10-17 !status received 01-10-10 !priority Medium !difficulty Hard !subject Task Entry without Accept Statement !summary Require that all entry declaration have at least one matching accept statement. !problem Ada 95 allows the declaration of an entry with no corresponding accept. A deadlock may occur for a call to such an entry. Ada's safety requirements suggest that this case be an error. !proposal (Specific wording changes TBD.) !discussion This sort of check is similar to the existing requirement that a function have at least one return statement. That requirement also pushes run-time errors to compile-time. This change is incompatible with Ada 95, but the fix (assuming that the entry is actually needed in the program) is easy -- simply surround an accept with an if statement: if False then accept Foo; end if; Moreover, tasks which have no accept statement for one of their entries are likely to be very rare. !example !ACATS test !appendix !topic Task Entry Without Accept Statement !reference RM95-9.5.3(17 on page 168) !from Anh Vo 01-10-10 !keywords entry accept task !discussion NOTES (22) A task entry has corresponding accept_statements (zero or more), whereas a protected entry has a corresponding entry_body (exactly one). I would like to suggest to rephrase notes 22 as shown below (22) A task entry has corresponding accept_statements (one or more), whereas a protected entry has a corresponding entry_body (exactly one). The consequences of the current requirements are that a dead lock surely occurs for an entry without at least one corresponding accept statement. Has any body faced this situation before? I sure have. If you are troubleshooting a task dead lock in this situation, I am sure you will be happy to see this change. This suggestion is totally compatible with the current version. It only affects the compiler vendor. In addition, GNAT already gives a warning if no corresponding accept statement for a declare task entry. GNAT, you are great! ************************************************************* From: Randy Brukardt Sent: Monday, October 15, 2001 8:22 PM > NOTES > (22) A task entry has corresponding accept_statements (zero or more), whereas a > protected entry has a corresponding entry_body (exactly one). > > I would like to suggest to rephrase notes 22 as shown below Umm, a note has no normative meaning. The rules which lead to the note are elsewhere. You would need to change the original rules as well as the note. (BTW, the paragraph reference in your message header is wrong, it ought to be "9.5.2(27)". In this case, it is really an absence of rules that lead to this result. Task entries are not "completed", as subprograms and protected entries are. This is because there can be many accepts for a particular entry. Writing a rule with the effect you want would be rather tricky (we certainly don't want to make any useful programs to be illegal). Something similar to 6.5(5) might work, but the need to include parameter profiles would complicate the rule. Dunno if it is worth the danger. (There are those who find 6.5(5) to be limiting during prototyping.) > In addition, GNAT already gives a warning if no corresponding > accept statement for a declare task entry. GNAT, you are great! GNAT also gives a warning if there is a way to exit a function without executing a return statement. We got hundreds of those in Claw, from code like: Result := ; if Result = Error then raise Claw.Windows_Error; else return ; end if; Sometimes, such warnings *aren't* helpful! ************************************************************* From: Robert Dewar Sent: Monday, October 15, 2001 8:51 PM <> This is quite false, GNAT does not give a warning for the above code. I don't know what Randy is talking about, but this particular example (which is obviously not an exact quote) does not give any warning! ************************************************************* From: Randy Brukardt Sent: Monday, October 15, 2001 9:21 PM Well, it does for me (GNAT 3.14a1). We changed all of the many places that we got warnings to eliminate them. I just put one of them back to experiment, and I got: Compiling: claw-static.adb (source file time stamp: 2001-10-16 02:15:50) 670. Claw.Raise_Windows_Error; | >>> warning: "return" statement missing following this statement >>> warning: Program_Error may be raised at run time Ah...I see the difference. We use a subprogram to actually raise the exception, because we need to construct an appropriate exception message (and we didn't want hundreds of copies of that code in user's programs). But my original comment stands... ************************************************************* From: Robert Dewar Sent: Monday, October 15, 2001 9:26 PM If Claw.Raise_Windows_Error can never return, then pragma No_Return should be used, otherwise of course the error message is quite legitimate. By the way, pragma No_Return is a good candidate for semi-standardization. If you don't want to use pragma No_Return , then the proper way to suppress this message is to put an explicit raise Program_Error after the call, which makes it clear that you never expect control to return that way. That will also suppress the message. ************************************************************* From: Robert Duff Sent: Tuesday, October 16, 2001 10:31 AM > By the way, pragma No_Return is a good candidate for semi-standardization. I agree. It makes the warnings much more useful, by eliminating most bogus ones. ************************************************************* From: Robert Dewar Sent: Tuesday, October 16, 2001 7:06 PM And improves the code, by eliminating junk raises And allows improved diagnosing of dead code following the call ************************************************************* From: Arthur Evans Jr Sent: Tuesday, October 16, 2001 10:12 AM At 20:22 -0500 2001.10.15, Randy Brukardt wrote: >GNAT also gives a warning if there is a way to exit a function without >executing a return statement. We got hundreds of those in Claw, from code >like: > > Result := ; > if Result = Error then > raise Claw.Windows_Error; > else > return ; > end if; > >Sometimes, such warnings *aren't* helpful! There followed an instructive discussion between Randy and Robert. Let me approach the matter differently. Because the successor of the 'raise' statement is never the next statement in the listing, you can reframe the fragment like this without changing the effect: Result := ; if Result = Error then raise Claw.Windows_Error; end if; return ; Of course if you write it this way, or even if you call a procedure to raise the error and neglect to use 'pragma NoReturn' on that procedure, GNAT won't complain about leaving the function without a return. However, I write this note because this is an example of a guideline I've often thought about: Is it bad taste to have 'else' follow some statement whose execution is never followed by the next statement? Consider if then return else [lengthy sequence of statements] end if I think it reads better to write if then return end if [lengthy sequence of statements] Any opinions? ************************************************************* From: Ted Baker Sent: Thursday, October 18, 2001 7:05 AM I agree with you. The latter is better. ************************************************************* From: David Emery Sent: Thursday, October 18, 2001 12:23 PM What I prefer is adding comments to make things clear: if then return ; end if; -- -- if we get here then it's not [lengthy sequence of statements] Although I've more often done the following (notice comments) If then [bunch of statements] else -- not [lots more statements] end if; -- And as a rule of thumb, if either [bunch of statements] or [lots more statements] are more than a page, I'll consider making a nested subprogram to encapsulate the linear sequence of statements, to make the control flow more clear: if then first_bunch_of_statements; else -- not second_bunch_of_statements; end if; -- I also tend to lean on pretty-printing/indentation to make the control flow comb a little clearer. But this only works about 2-3 levels deep max on a single page or maybe 2 pages max. But good style is the opinion of the ranking person reading the code :-) ************************************************************* From: Randy Brukardt Sent: Tuesday, October 16, 2001 1:06 PM > If Claw.Raise_Windows_Error can never return, then pragma No_Return should > be used, otherwise of course the error message is quite legitimate. Ah, GNAT has a pragma for that? We never looked at the GNAT-specific pragmas when working on Claw, as it needs to work with all compilers. (Of course, a pragma can be given anyway, it should just be a warning on other compilers). > By the way, pragma No_Return is a good candidate for > semi-standardization. Sounds like it to me. > If you don't want to use pragma No_Return, then the proper way to suppress > this message is to put an explicit raise Program_Error after the call, > which makes it clear that you never expect control to return that way. > That will also suppress the message. Well, we rearranged the code so that the return came after the if statement. It was memorable because we had to do it in a hundred or so places (and it pops up again from time to time). I've concluded that the code rearrangement was for the better anyway, as slightly less code should be generated (there would otherwise be a need for the code to raise Program_Error). ************************************************************* From: Robert Dewar Sent: Tuesday, October 16, 2001 3:03 PM <> Of course no such code is generated if you use pragma No_Return in GNAT. ************************************************************* From: Anh Vo Sent: Tuesday, October 16, 2001 8:02 AM Yes, I should have pointed out the rules instead of notes. Any way, it does not make sense that declared entry could have no corresponding accept statement. Then, task entry has no value, or it is a dead code. However, dead code does not cause task deadlock. But, dead task entry code does. Therefore, it is much danger not to change it since it costs time, money and headache. I do not quite understand why it is tricky to write a rule requiring that there is at least one corresponding accept statement for each declared entry. In fact, why is it easy to write a rule requiring that there is one body subprogram for one or none spec. subprogram? ************************************************************* From: Pascal Leroy Sent: Tuesday, October 16, 2001 1:46 PM > I do not quite understand why it is tricky to write a rule requiring that > there is at least one corresponding accept statement for each declared entry. If you want to understand this, I suggest that you try to write such a rule. It's not impossible, but it's delicate. You cannot rely on the rules for completion, because there can be more than one accept statement for a given entry. And you have to be cautious with entry families. ************************************************************* From: Robert Dewar Sent: Tuesday, October 16, 2001 1:01 PM I find this not worth bothering about, at most a warning is appropriate. I don't like rules of this nature in the language (an example is the silly rule that functions must contain at least one return). If compilers consider it an issue they can generate warnings, but why introduce a non-upwards compatible change that does not add to expressive power? ************************************************************* From: Anh Vo Sent: Tuesday, October 16, 2001 11:50 AM I disagree. Can compiler vendors guaranteed to give warnings in this case? Certainly, not. In fact, some compilers do and some do not. Even warning given by the compilers, one now relies the compilers instead of the rules of the language. That is not expressive power. ************************************************************* From: Robert Dewar Sent: Tuesday, October 16, 2001 3:04 PM So if a vendor does not even consider this issue important enough to generate a warning (translation: their customers do not consider this an important issue), then why on earth would the ARG be in the business of decreeing that this be an error. As I said, rules that are basically style rules seem a bad idea to me in any case. ************************************************************* From: Jean-Pierre Rosen Sent: Tuesday, October 16, 2001 12:48 AM > Yes, I should have pointed out the rules instead of notes. Any way, > it does not make sense that declared entry could have no corresponding > accept statement. Actually, it is quite useful in at least one case. How do you wait for a task to terminate, and be sure that it works in all cases, even if the task is aborted, and without race condition? Easy, call an entry of the task that is never accepted, and catch Tasking_Error... ************************************************************* From: Ted Baker Sent: Thursday, October 18, 2001 7:02 AM | Yes, I should have pointed out the rules instead of notes. Any way, it does | not make sense that declared entry could have no corresponding accept | statement. Then, task entry has no value, or it is a dead code. However, | dead code does not cause task deadlock. But, dead task entry code does. | Therefore, it is much danger not to change it since it costs time, money and | headache. | I do not quite understand why it is tricky to write a rule requiring that | there is at least one corresponding accept statement for each declared entry. | In fact, why is it easy to write a rule requiring that there is one body | subprogram for one or none spec. subprogram? 1) Such a rule would only catch the most obvious kind of error. In real life with experienced programmers deadlocks occur for more subtle reasons. 2) There actually are situations where it is useful to have an entry with no accept, that can be used to block a task forever (or until it gets an asynchronous exception). 3) A compiler can always provide an advisory warning message about this case. *************************************************************