!standard 6.05.01 (01) 05-05-19 AI95-00414/03 !class amendment 05-02-02 !status Amendment 200Y 05-03-09 !status ARG Approved 8-0-2 05-02-12 !status work item 05-02-02 !status received 05-02-02 !priority Medium !difficulty Easy !subject pragma No_Return for overriding procedures !summary This AI corrects some problems with AI-00329/06, which defines pragma No_Return. !problem The goal of pragma No_Return is to ensure that a call to a No_Return procedure will not return normally. The main problem with AI-00329 is that there is no such assurance in the case of a dispatching call. The other problems are nitpicks; they are mentioned in the discussion. !proposal We ensure that dispatching calls work properly by requiring pragma No_Return on a procedure that overrides a dispatching No_Return procedure. !wording AI-00329 adds a new clause, as follows: 6.5.1 Pragma No_Return A pragma No_Return indicates that a procedure can return only by propagating an exception. Syntax The form of a pragma No_Return, which is a program unit pragma (see 10.1.5), is as follows: pragma No_Return(local_name {, local_name}); Legality Rules The pragma shall apply to one or more procedures or generic procedures. If a pragma No_Return applies to a procedure or a generic procedure, there shall be no return_statements that apply to that procedure. Static Semantics If a pragma No_Return applies to a generic procedure, pragma No_Return applies to all instances of that generic procedure. Dynamic Semantics If a pragma No_Return applies to a procedure, then the exception Program_Error is raised at the point of the call of the procedure if the procedure body completes normally. [End of old wording from AI-00329.] ---------------- This AI replaces 6.5.1 with the following: 6.5.1 Pragma No_Return A pragma No_Return indicates that a procedure cannot return normally[; it may propagate an exception or loop forever]. Syntax The form of a pragma No_Return, which is a representation pragma (see 13.1), is as follows: pragma No_Return(procedure_local_name {, procedure_local_name}); Legality Rules Each procedure_local_name shall denote one or more procedures or generic procedures; the denoted entities are "non-returning". The procedure_local_name shall not denote a null procedure nor an instance of a generic unit. AARM Reason: A null procedure cannot have the appropriate non-returning semantics, as it does not raise an exception or loop forever. AARM Ramification: The procedure can be abstract. The denoted declaration can be a renaming_declaration if it obeys the usual rules for representation pragmas: the renaming has to occur immediately within the same declarative_region as the renamed subprogram. If non-returning procedure is renamed (anywhere) calls through the new name still have the non-returning semantics. A return statement shall not apply to a non-returning procedure or generic procedure. A procedure shall be non-returning if it overrides a dispatching non-returning 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. AARM Reason: This ensures that dispatching calls to non-returning procedures will, in fact, not return. If a renaming-as-body completes a non-returning procedure declaration, then the renamed procedure shall be non-returning. Static Semantics If a generic procedure is non-returning, then so are its instances. If a procedure declared within a generic unit is non-returning, then so are the corresponding copies of that procedure in instances. Dynamic Semantics If the body of a non-returning procedure completes normally, Program_Error is raised at the point of the call. Note: AI-00329 also modifies 11.4.1; this AI retains those modifications as is. AARM Discussion: The primary goal of pragma No_Return is to document the fact that calls will not return normally. An example is a procedure that logs an error and then unconditionally raises an exception. Another example is a procedure that deliberately enters an infinite loop. Non-returning procedures are rare, so it is useful to document this unusual case. However, they are called often. This capability is useful for three reasons: 1. Programmers can better understand programs that have this unusual control-flow structure. 2. Compilers can use this control-flow information to generate better warnings. The pragma essentially removes certain control-flow arcs from the compiler's control-flow graph. A compiler can warn if it is unable to prove that the end of the procedure body is unreachable. A compiler can warn about code following a call to a non-returning procedure; it's dead code. Most importantly, a compiler can *avoid* warning in cases like this: if ... then Fatal_Error("..."); -- non-returning else X := ...; -- initialize X end if; ... X ... -- no warning Without pragma No_Return, the compiler would spuriously warn that X is uninitialized when it is referenced after the if_statement. Warnings are most useful if there are few false alarms. The compiler can avoid the false alarm if "Fatal_Error" were "raise ..."; pragma No_Return allows the same capability in the case where the programmer has chosen to encapsulate the raise statement. 3. Compilers can use this control-flow information for optimizations. For example: Y := F(...); if ... then Fatal_Error("..."); -- non-returning else Y := 3; end if; ... Y ... -- Y = 3. A compiler can prove that Y = 3 above, and take advantage of that fact. In order to gain the above advantages, we must ensure that the property asserted by the pragma is actually true; that is, calls to non-returning procedures must not return. In order to make this work for dispatching calls, we need to ensure that if a procedure is non-returning, then all overridings of it are also non-returning. We do this by requiring an explicit pragma on the overriding. An alternative would be to say that the non-returning property is inherited (implicitly) by the overriding procedure. We didn't choose that alternative, because it seems less safe. The programmer of the overriding might not notice the pragma No_Return on the parent procedure, and be surprised by a run-time failure; better to require the pragma to be repeated locally. Of course, a good compiler can usually warn about the potential run-time failure, but this is not required. This issue is like any other part of the contract for procedure calls. For example, if you call a procedure with an 'in' parameter, you know that the actual will not be modified. To make this work for dispatching calls, we need to disallow overridings from changing 'in' to 'in out'. If a non-returning procedure tries to return, we raise Program_Error. This is stated as happening at the call site, because we do not wish to allow the procedure to handle the exception (and then, perhaps, try to return again!). However, the expected run-time model is that the compiler will generate "raise Program_Error" at the end of the procedure body (but not handleable by the procedure itself), as opposed to doing it at the call site. (This is just like the typical run-time model for functions that fall off the end without returning a value). The reason is indirect calls: in "P.all(...);", the compiler cannot know whether P designates a non-returning procedure or a normal one. Putting the "raise Program_Error" in the procedure's generated code solves this problem neatly. Similarly, if one passes a non-returning procedure to a generic formal parameter, the compiler cannot know this at call sites; the raise-in-body solution deals with this neatly. There is one exception to the principle that "we must ensure that the property asserted by the pragma is actually true". For a pragma-Import procedure, it is the responsibility of the foreign-language code to obey the stated contract. This is the normal rule for interfacing pragmas, so we don't need to state this explicitly. Note: procedure "exit", imported from C, is a reasonable example of an imported procedure that deserves a pragma No_Return. The rule about renamings-as-body is also there to avoid an implementation burden, given the above intended run-time model. AARM-8.5.4(5.a/1) notes that implementations should be allowed to implement renaming-as-body using jumps; that implementation would not work if the renaming-as-body has to raise Program_Error. Given the above rule, the renaming-as-body can rely on the renamed procedure to raise Program_Error if appropriate. Anyway, a renaming-as-body that violates the rule is almost certainly a bug. It would make sense to allow pragma No_Return on generic formal procedures, access-to-procedure types, entries, and protected procedures. We choose not to allow these cases, in order to avoid implementation burden and additional language rules for little benefit. If access-to-procedure types allowed pragma No_Return, we would need a contract model to ensure that the designated procedure is always non-returning; we would forbid 'Access and type conversions that allow a non-returning access-to-procedure to designate a normal (returning) procedure. On the other hand, it would be OK for a normal access-to-procedure value to designate a non-returning procedure. Similarly, generic formal procedures would need a contract: it should be illegal to pass a normal procedure to a non-returning formal, but the other way around is OK. Pragma No_Return is like pragma Convention in some ways. It could actually affect the calling convention at the generated code level; for example, a call could use a JUMP instruction instead of a CALL instruction in some environments, although that's hardly an important optimization. However, pragma Convention is different in one way: it doesn't cause an automatic Program_Error. It has been suggested that No_Return be automatically inherited by overridings, just like Convention. But given that No_Return raises Program_Error, we choose to require an *explicit* No_Return on overridings. [End AARM Discussion.] !example Example of a dispatching call to a non-returning procedure: package Error_Handling is type Error_Log is abstract tagged null record; procedure Fatal_Error(Log: Error_Log; Message: String) is abstract; pragma No_Return(Fatal_Error); -- Send a message to the log and raise an exception. end Error_Handling; -- A client of Error_Handling: Log: Error_Log'Class := Log_Factory.Create(...); if ... then Fatal_Error(Log, "The sky is falling!"); -- dispatching call -- Can't get here. else X := ...; -- initialize X end if; ... X ... -- no warning; if we get here, X is well-defined. The idea is that there are different kinds of Error_Logs, which do what they like with the Message, and then raise an exception. It is natural to represent these log types using a hierarchy of tagged types, and to use dispatching. The above dispatching call to Fatal_Error is guaranteed not to return. The various extensions of Error_Log are required to override Fatal_Error with a non-returning procedure. Clearly, if we are going to allow dispatching calls to non-returning procedures, then they need to work right -- the call must not return. !discussion The primary change to AI-00329 is to require dispatching calls to work properly. The other wording changes are just minor fixes to nitpicking problems: Miscellaneous wording changes. The introductory paragraph is changed; I think the part bracketed in the AARM clarifies the two main purposes of the pragma. (A non-returning procedure could also presumably call a never-triggered entry, or delay until the year 2099; we don't mention these oddities). We can no longer use the "apply to" definition for program unit pragmas, since No_Return is no longer a program unit pragma; we define the term "non-returning procedure" instead. The pragma is not a program unit pragma. It is now a representation pragma. This better matches the intended semantics. The "procedure_local_name shall denote" wording better specifies which entities can have the pragma. AI-00329 forbade abstract procedures, but there's no reason to forbid them. AI-00329 apparently intended to allow renamings (in the usual "local_name" way), but it wasn't clear; now it's clear that they're allowed. "The procedure_local_name shall not denote an instance of a generic unit." is added, because the legality rule "A return_statement shall not apply to a non-returning procedure or generic procedure." cannot be reasonably checked on instances. AI-00329 did not consider renamings-as-body. The rule "If a procedure declared within a generic unit is non-returning, then so are the corresponding copies of that procedure in instances." was apparently missing from AI-00329. The rule, "A return_statement shall not apply to a non-returning procedure or generic procedure." does not strictly need the "or generic procedure" part, because inside the procedure body, it's a procedure (the current instance). Nonetheless, the rule seems clearer with the meaningless "or generic procedure" part. !corrigendum 6.5.1(1) @dinsc A @fa No_Return indicates that a procedure cannot return normally; it may propagate an exception or loop forever. @i<@s8> The form of a @fa No_Return, which is a representation pragma (see 13.1), is as follows: @xindent<@b No_Return(@i@fa{, @i@fa});> @i<@s8> Each @i@fa shall denote one or more procedures or generic procedures; the denoted entities are @i. The @i@fa shall not denote a null procedure nor an instance of a generic unit. A return statement shall not apply to a non-returning procedure or generic procedure. A procedure shall be non-returning if it overrides a dispatching non-returning 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. If a renaming-as-body completes a non-returning procedure declaration, then the renamed procedure shall be non-returning. @i<@s8> If a generic procedure is non-returning, then so are its instances. If a procedure declared within a generic unit is non-returning, then so are the corresponding copies of that procedure in instances. @i<@s8> If the body of a non-returning procedure completes normally, Program_Error is raised at the point of the call. !ACATS test In addition to the tests described in AI-329, create a B-Test to check that null procedures cannot have No_Return applied. Create a B-Test to check that No_Return is inherited. Create a B-Test to check that the rules of a representation pragma are properly checked for No_Return. !appendix From: Jean-Pierre Rosen Sent: Tuesday, January 25, 2005 2:31 AM When you give a pragma No_Return to a subprogram, does it apply to subprograms derived from it? If you think of No_Return as being some kind of contract, that would certainly make sense, especially for dispatching calls. Hoping not to open YACOW (Yet Another Can Of Worms) ************************************************************* From: Gary Dismukes Sent: Tuesday, January 25, 2005 11:34 AM Yes, No_Return should certainly apply to derived subprograms. This should behave the same way as other program-unit pragmas such as Inline (though I'm not sure where the rule is that implies inheritance of pragmas like this). ************************************************************* From: Tucker Taft Sent: Tuesday, January 25, 2005 12:26 PM I'm not sure what you mean "derived subprograms." Certainly the implicitly declared inherited subprogram retains the No_Return, since it is just the same code. But I don't see any rule, nor would I expect there to be one, that says a subprogram that overrides such an implicitly declared inherited subprogram automatically inherits its program unit pragmas. Inline being a good example where the subprogram in the parent type might be a good candidate for inlining, while the overriding would not. So I believe we need a special rule for No_Return, if we want it. It does make sense to me that if a primitive procedure is declared No_Return, then so must be all of its overridings. But I am a bit reluctant for it to be simply inherited. I might rather require that the No_Return be explicit on each overriding. It has a pretty dramatic effect, and I wouldn't want to have someone add it during maintenance to the top of a hierarchy, and suddenly discover that code deep down still compiled, but started raising Program_Error all over the place. **************************************************************** From: Jean-Pierre Rosen Sent: Tuesday, January 25, 2005 12:42 PM Tucker Taft a ‚crit : > I'm not sure what you mean "derived subprograms." > Certainly the implicitly declared inherited subprogram > retains the No_Return, since it is just the same code. > But I don't see any rule, nor would I expect there to > be one, that says a subprogram that overrides such > an implicitly declared inherited subprogram automatically > inherits its program unit pragmas. That's the point. > So I believe we need a special rule for No_Return, if > we want it. It does make sense to me that if a primitive > procedure is declared No_Return, then so must be all > of its overridings. But I am a bit reluctant for it > to be simply inherited. I might rather require that the > No_Return be explicit on each overriding. It has > a pretty dramatic effect, and I wouldn't want to have > someone add it during maintenance to the top of a hierarchy, > and suddenly discover that code deep down still compiled, > but started raising Program_Error all over the place. So the rule should be that the pragma is required for every overriding of a "no_return" procedure, thus making a compilation error in the case you mention. That's a good idea. But now, what about the opposite way? Should we require that an overriding subprogram specifies No_Return *only if* the overridden subprogram has it? Actually, I can imagine that some primitive operation is not applicable to some derived type, and that you override the operation with one that always raises Program_Error... ************************************************************* From: Gary Dismukes Sent: Tuesday, January 25, 2005 12:46 AM > I'm not sure what you mean "derived subprograms." I'm not sure who the "you" you're referring to is. :) I interpreted that phrase as meaning inherited subprograms, not overridings. However I suppose that J-P was probably referring to overridings since the answer seems obvious for inherited ones (though I'm still not sure there's a rule that actually implies that the pragma is inherited). > ... Inline being a good > example where the subprogram in the parent type might > be a good candidate for inlining, while the overriding > would not. Sure, there's certainly no rule for carrying pragmas over to overridings. > So I believe we need a special rule for No_Return, if > we want it. It does make sense to me that if a primitive > procedure is declared No_Return, then so must be all > of its overridings. But I am a bit reluctant for it > to be simply inherited. I might rather require that the > No_Return be explicit on each overriding. It has > a pretty dramatic effect, and I wouldn't want to have > someone add it during maintenance to the top of a hierarchy, > and suddenly discover that code deep down still compiled, > but started raising Program_Error all over the place. I agree that it shouldn't be inherited. But I'm not convinced that it makes sense for it to be required to be given on overridings (or am I misunderstanding you?). It seems that it should be fine for an overriding not to have No_Return semantics. Why should we impose that restriction on extensions? ************************************************************* From: Bob Duff Sent: Tuesday, January 25, 2005 12:30 AM I agree, although it might be nice to also have a way to say "this procedure never returns, but overridings can be different". This is the Eiffel problem that Tuck is fond of pointing out: there's no way to make a contract that applies to a particular procedure, as opposed to a whole tree of overridings. One probably wants No_Return to apply to access-to-subprogram types as well, but I suppose it's too late to bother with that for Ada 2005. > This should behave the same way as other program-unit pragmas > such as Inline (though I'm not sure where the rule is that > implies inheritance of pragmas like this). I don't remember any such general rule. As to Inline, I don't see why you would want it inherited. It's not a contract about the logical behavior of the thing -- it's just a statement that this thing is small enough (or whatever) to be inlined, which might be true or false independently for overridings. And for dispatching calls, I don't think you're normally going to get inlining (although it's certainly possible in some cases). There's no pragma Out_Of_Line, so it would be somewhat questionable to have cases where inlining is requested implicitly (e.g. by inheritance). ************************************************************* From: Bob Duff Sent: Tuesday, January 25, 2005 12:32 AM > I might rather require that the > No_Return be explicit on each overriding. Yeah, that's an even better idea. Either way, the point is that the compiler can know about it at call sites -- including dispatching calls. ************************************************************* From: Tucker Taft Sent: Tuesday, January 25, 2005 1:21 PM > ... > One probably wants No_Return to apply to access-to-subprogram types as > well, but I suppose it's too late to bother with that for Ada 2005. > ... This seems certainly in the category of a desirable "tweak." If we are adding a new pragma, we should make sure it applies to the appropriate kinds of entities. No_Return is almost like a new calling convention, and those apply to access-to-subp as well (and are also required to be the same in overridings of dispatching ops, by the way). So I would say we should have this apply to access-to-subp. A No_Return acc-to-subp may only designated No_Return subps. On the other hand, there is no problem for a "normal" (non-no-return) acc-to-subp designating a No_Return subp. Similarly, there should be no harm in overriding a non-no-return subp with a no-return one, but once you override with a no-return, then further overridings should also be no-return, for a tagged primitive. I suppose a formal subp should also allow a No_Return pragma, with corresponding rules (i.e. actual must be No_Return). ************************************************************* From: Robert A. Duff Sent: Tuesday, January 25, 2005 2:32 PM > But now, what about the opposite way? Should we require that an > overriding subprogram specifies No_Return *only if* the overridden > subprogram has it? No, because lack of No_Return does not promise that the thing *will* return. (And of course we can't very well say "raise P_E if so-and-so loops forever"!) >... Actually, I can imagine that some primitive operation > is not applicable to some derived type, and that you override the > operation with one that always raises Program_Error... No_Return promises no *normal* return; a No_Return procedure can raise whatever exceptions it likes (and, in fact, if it "tries" to return, it *will* raise an exception). **************************************************************** From: Robert A. Duff Sent: Tuesday, January 25, 2005 2:39 PM > I agree that it shouldn't be inherited. But I'm not convinced > that it makes sense for it to be required to be given on overridings > (or am I misunderstanding you?). It seems that it should be fine > for an overriding not to have No_Return semantics. Why should > we impose that restriction on extensions? Because if you have a dispatching call to a No_Return procedure, we would like the compiler to know for sure that it won't return, so it can give appropriate warnings: X: Integer; if ... then Fatal_Error(...); -- dispatch on message stream type or whatever Put_Line("Hello, world!"); ^ Warning: can't get here. else X := 1; ... end if; ... X ... ^ Do *not* warn here, about possibily-uninitialized X. I believe your compiler is already that smart -- at least for non-dispatching calls. **************************************************************** From: Tucker Taft Sent: Tuesday, January 25, 2005 7:14 PM > I agree that it shouldn't be inherited. But I'm not convinced > that it makes sense for it to be required to be given on overridings > (or am I misunderstanding you?). It seems that it should be fine > for an overriding not to have No_Return semantics. Why should > we impose that restriction on extensions? I think the issue is with class-wide calls. My feeling is that if a dispatching operation says it is No_Return, then that should probably be true whether it is called using statically or with run-time dispatch. Hence, I think the overridings will need to honor that pledge. If you can think of a good example where you have a routine that is something like "Fatal_Error" and you would want overridings to change the meaning enough so that it starts returning rather than raising an exception, I might be convinced. **************************************************************** From: Randy Brukardt Sent: Tuesday, January 25, 2005 4:39 PM This seems like overkill to me. It takes a relatively simple idea and complicates it to the point where I would be against even having it in the language. Moreover, this is a feature that is used rarely (maybe one out of a thousand subprograms) - it simply doesn't pay to complicate it. I would simply say that it applies to subprograms when given explicitly. Implicitly inherited subprograms inherit it, of course, but that's it. You wouldn't be able to give a warning on a dispatching call, but that's typical anyway -- you generally can assume very little about class-wide types and about dispatching calls. I think the likelyhood of a useful dispatching call for No_Return is just about zero; probably it would only happen by accident (that is, routines that were declared to be primitive but are never intended to be overridden). I could imagine a pragma or keyword to declare *that* property (it would be a lot more generally useful, especially as things need to be primitive to make an object prefix call - since in the case of a primitive routine that will never be overridden, there would be no need to allocate a slot or do subprogram lookup in a tag for a dispatching call), but not a lot of complication for No_Return. **************************************************************** From: Tucker Taft Sent: Tuesday, January 25, 2005 8:55 PM The four phases of an AI 1) Ok, sounds simple, but you have to write it up 2) Ok, we have a write-up, and still no major harm, so fine 3) Oops, we forgot something in the write-up, so let's discuss it to death on the ARG list 4) My god, with all this discussion, the feature must be incredibly complicated. Forget the whole thing. It often seems the amount of discussion about something is inversely proportional to both how complicated it really is, and to how important it really is. ;-) In this case, I think Jean-Pierre pointed out a legitimate hole. We have discussed it a while, and seem to have come to an approximate consensus on how to properly deal with the pragma w.r.t. inheritance, generics, and acc-to-subp. If we don't discuss these things now, you can be sure they will end up turning into 2005 AIs. The rules seem fairly straightforward, and are analogous to those for calling conventions. **************************************************************** From: Pascal Leroy Sent: Wednesday, January 26, 2005 5:17 AM > This seems like overkill to me. It takes a relatively simple > idea and complicates it to the point where I would be against > even having it in the language. Moreover, this is a feature > that is used rarely (maybe one out of a thousand subprograms) > - it simply doesn't pay to complicate it. > > I would simply say that it applies to subprograms when given > explicitly. Implicitly inherited subprograms inherit it, of > course, but that's it. I couldn't agree more. **************************************************************** From: Tucker Taft Sent: Wednesday, January 26, 2005 9:23 AM I don't want to complicate the feature, but it seems pretty clear that if you declare a primitive of a tagged type called something like "Fatal_Errror," and mark it as No_Return, then you would expect all overridings to be the same. Dispatching calls are more important than non-dispatching calls for many tagged types (certainly for Interfaces!), and for these, No_Return should probably apply to dispatching calls, and then the non-dispatching call requirements are implied by the requirements of the dispatching call. I agree these are not that common, but for something like a window system, it is fairly typical to have some way to pop up an "alert" box, including a "fatal alert" (especially on Windows ;-). I could imagine that something like this might be something you would want to be able to override, but if so, you wouldn't want to allow it to become a "non-fatal" alert as part of overriding it. In any case, I agree this isn't that critical, but it does seem important whenever we define a new pragma, to think carefully about how it interacts with inheritance, generics, etc. If we don't, we will just end up with more AIs in the future. In this case, it seems pretty clear that you want all overridings to be No_Return, if the original one is. **************************************************************** From: Pascal Leroy Sent: Wednesday, January 26, 2005 10:46 AM > I agree these are not that common, but for something > like a window system, it is fairly typical to have > some way to pop up an "alert" box, including a "fatal alert" > (especially on Windows ;-). I could imagine that something > like this might be something you would want to be able to > override, but if so, you wouldn't want to allow it to become > a "non-fatal" alert as part of overriding it. This is hardly a convincing example, because (other than the BSOD ;-) I don't buy the notion that this would be a No_Return. These things typically have an OK button that returns you to the main event loop. > In any case, I agree this isn't that critical, but it > does seem important whenever we define a new pragma, to > think carefully about how it interacts with inheritance, > generics, etc. If we don't, we will just end up with more > AIs in the future. I tend to agree with this, although I'd say that if it has this kind of interaction with generics and inheritance, it violates the Rule of Good Taste... > In this case, it seems pretty clear that > you want all overridings to be No_Return, if the original one is. This is not at all clear to me. I inherit from this base class and it does all sorts of wonderful things for me, but it insists on calling the No_Return procedure Fatal_Error when it detects an error. Now in the derived class there are situations where Fatal_Error is called, but which are not really errors (this doesn't seem to violate the substitution principle: I am extending the applicability of some operations). I would like to redefine Fatal_Error, maybe to look at the state of the object and see if I really have a problem or not. Ah but I see, if I returned normally from Fatal_Error, there might be code in the base class that would be really surprised. Yuck. So No_Return is really exposing a completely different control flow structure, and it would seem that it is really part of the contract. Of course, you would have to recheck that in the private part of instantiations, too, just in case you have a subprogram there that accidentally overrides a No_Return procedure inherited from the formal. PS: I never liked this pragma. I like it less and less. **************************************************************** From: Robert A. Duff Sent: Wednesday, January 26, 2005 1:31 PM It seems to me that if we're not going to go to the trouble of getting the semantics right in the dispatching case, then we should forbid it. (I.e. forbid this pragma on primitive ops of tagged types.) At least that leaves the door open to future language revisions. ("Right" = what Tucker said.) If we allow the pragma to be overridden, then we can't change our mind due to compatibility. The pragma is rarely useful. So if it's disallowed in generic/dispatching cases, well that's a wart, but not *so* bad. Better than having it do the wrong thing! > I tend to agree with this, although I'd say that if it has this kind of > interaction with generics and inheritance, it violates the Rule of Good > Taste... You mean Good Taste in Pragmas? It seems to me that a pragma that allows a programmer to place restrictions on the code (to allow optimizations, to allow better error detection, etc) are in Good Taste. Bad taste are things like pragma Elaborate, which have a fundamental effect on the semantics. > Ah but I see, if I returned normally from Fatal_Error, there might be code > in the base class that would be really surprised. Exactly. >... Yuck. So No_Return is > really exposing a completely different control flow structure,... Well, not really: infinite loops and always-raise are already legal! The pragma merely documents this control flow structure. Note that if you erase the pragma, the program behaves the same (presuming it was correctly obeying the pragma). >... and it > would seem that it is really part of the contract. Yes, it definitely should be part of the contract for dispatching calls; otherwise, it's useless for dispatching calls. Look at the examples in the AI -- if you substitute dispatching calls, the benefits claimed by the AI evaporate (without a contract model). > Of course, you would have to recheck that in the private part of > instantiations, too, just in case you have a subprogram there that > accidentally overrides a No_Return procedure inherited from the formal. Yes. **************************************************************** From: Randy Brukardt Sent: Wednesday, January 26, 2005 3:16 PM > You mean Good Taste in Pragmas? It seems to me that a pragma that > allows a programmer to place restrictions on the code (to allow > optimizations, to allow better error detection, etc) are in Good Taste. > Bad taste are things like pragma Elaborate, which have a fundamental > effect on the semantics. I disagree; pragmas that effect inheritance are in bad taste. You're going to say something about convention, and I would be happy to say that those are in bad taste. Convention should be part of the syntax, and so should this pragma. At this point, I would just forget it; it isn't worth these complications. (It all about warnings, anyway, which Pascal is usually very opposed to.) As a pragma that affects inheritance, it is very complicated to implement. (I agree that is it similar to convention, but *that* is very complicated to implement. I would be surprised if we have convention right in any significant number of inheritance cases -- unless we simply don't allow it on primitive operations of tagged types [I don't remember precisely what we do with that]). **************************************************************** From: Tucker Taft Sent: Wednesday, January 26, 2005 3:26 PM > This is hardly a convincing example, because (other than the BSOD ;-) I > don't buy the notion that this would be a No_Return. These things > typically have an OK button that returns you to the main event loop. True, though frequently the way you get back to the main event loop is by raising an exception. > ... > >>In this case, it seems pretty clear that >>you want all overridings to be No_Return, if the original one is. > > > This is not at all clear to me. I inherit from this base class and it > does all sorts of wonderful things for me, but it insists on calling the > No_Return procedure Fatal_Error when it detects an error. > ... > Ah but I see, if I returned normally from Fatal_Error, there might be code > in the base class that would be really surprised. Yuck. Yes, and this might be code you inherit without overriding it. > ... So No_Return is > really exposing a completely different control flow structure, and it > would seem that it is really part of the contract. Calling a No_Return procedure is the moral equivalent of raising an exception. It is not "completely different" but it is distinct from a procedure which is expected to not raise an exception in the "normal" case. > Of course, you would have to recheck that in the private part of > instantiations, too, just in case you have a subprogram there that > accidentally overrides a No_Return procedure inherited from the formal. Yes. > PS: I never liked this pragma. I like it less and less. Give it a chance. It will grow on you. Honestly, it really is a fundamental property of a procedure (perhaps *the* fundamental property of a procedure), and it makes a pretty big difference when you see code like: if not blah then Never_Return(xxyyzz); end if; -- assuming "blah" at this point It is fundamental to understanding what is going on in code like the above. We would understand it clearly if there were a "return", "goto", "exit", or "raise" there, but without some clear specification that Never_Return is a No_Return procedure, the above is a mystery, to both the human reader and to any sort of tool. By making it a standard pragma, portable code can be written where the compiler will ensure that such a routine definitely does *not* return. A commenting convention can't accomplish that. Note that this pragma was not invented by some pointy-headed language lawyer. This arose in two implementations in response to needs involving real user code. I have seen a number of coding conventions that pretty much require that all fatal errors be reported via calling a particular procedure, rather than just raising an exception in-line. The idea of such an annotation goes at least as far back as the original "lint" program -- it's not very radical. Why is it important now? Because more and more effort is being put into finding bugs at compile-time, or at least before run-time. Ada should be a leader here, but without a standard annotation like this, there is a huge hole in doing any sort of modular analysis of the code. **************************************************************** From: Randy Brukardt Sent: Wednesday, January 26, 2005 3:50 PM > It is fundamental to understanding what is going on in code like > the above. We would understand it clearly if there > were a "return", "goto", "exit", or "raise" there, but > without some clear specification that Never_Return is > a No_Return procedure, the above is a mystery, to both > the human reader and to any sort of tool. By making > it a standard pragma, portable code can be written > where the compiler will ensure that such a routine > definitely does *not* return. A commenting convention > can't accomplish that. Absolutely. But if it is that fundamental, then it *must not* be a pragma; pragmas are meant to be ignored when reading code. This is something that should be clearly stated in the specification of the procedure. It's very much like the overriding indicators. We tried them as pragmas, but they are too fundamental to be such. It is inappropriate for this to be a pragma if it starts applying all over the place. > Note that this pragma was not invented by some pointy-headed > language lawyer. This arose in two implementations in > response to needs involving real user code. Because those implementations couldn't invent syntax. And so far as I know, neither of those implementations make the requirement that this pragma is inherited. > Ada should be a leader here, but without > a standard annotation like this, there is a huge hole in > doing any sort of modular analysis of the code. I'm happy with the pragma as currently specified (no magic, no muss). But if you want to go further, it has to be a syntactic annotation. It has to matter when matching for formal subprograms (shouldn't it be able to apply to them too?) It has to be checked on 'Access. This is a substantial job (not like the current pragma, which only changes the drop-out return to raise Program_Error, and has no other effect). I'm willing to consider the whole boat of syntax for this (although it is awfully late), but extending the simple pragma into this complex beast is a non-starter for me. I'd rather forget the whole thing and let the implementers do what makes sense for their implementations. **************************************************************** From: Robert I. Eachus Sent: Wednesday, January 26, 2005 11:53 PM Tucker Taft wrote: > Note that this pragma was not invented by some pointy-headed > language lawyer. This arose in two implementations in > response to needs involving real user code. I have seen > a number of coding conventions that pretty much require > that all fatal errors be reported via calling a particular > procedure, rather than just raising an exception in-line. I agree with Tucker here and in the rest of his message. I think the point that is getting lost is that there are many, many real-time programs where it is a fundamental property of the main processing procedure that it never returns. There are many optimizations that can be made if the compiler knows that a procedure does not return which are important not because they significantly speed up the code--which they often do--but because they prevent accidental Hayflick limits from being created. The Hayflick limit is the built-in feature of most cell reproduction that limits the number of times a cell can divide. In cells, Hayflick limits help prevent cancer. In code they are among the worst kinds of bugs to trace down. If a radar has an accidental limit of this kind, the testers may never discover it, and the angst that will be produced once the error does cause a service failure is very real. I have been involved in the heroic efforts to find this type of bug on occasion and it is not pretty. In most cases they are unusual code paths that result in something getting pushed on the stack and left there. If a normal return occurs, the current stack frame and any extensions are popped off, and subtle memory losses get fixed. Procedures that will never return, will never clean the stack this way, so they can't afford to call subroutines which are poorly behaved. In the OS realm, this usually means calling the user visible entry point, rather than the (faster) alternative that does not do error checking, and often does not create a stack frame of its own. This is what I think of when considering pragma No_Return. Yes, it can make some compiler diagnostics better and so on in the case where the subroutine always raises an exception. But the diagnostics I care about are program verifier related. I want the verifier to check both the property that the code cannot raise an exception (or sometimes only cannot raise a predefined exception) and subprograms marked as never exiting never exit. How does this apply to the discussion so far? I think Tuck's proposal that subprograms that inherit an interface declared as No_Return must also be declared to be No_Return is fine. Even if the attribute was inherited, I'd probably put such a requirement in the SDP. Why? Well you want the property to compose correctly: procedure Main; pragma No_Return(Main); .... procedure Main_Execution_Loop; procedure Test_Execution_Loop; pragma No_Return(Main_Execution_Loop, Test_Execution_Loop); ... procedure Main_Execution_Loop is begin loop ... -- no returns or loop exits allowed here. end loop; end Main_Execution_Loop; ... procedure Main is begin if Test_Mode then Test_Execution_Loop; else Main_Execution_Loop; end if; end Main; Obviously I want tools, one of which is the compiler, to be able to reason that if No_Return is correct for Main_Execution_Loop and Test_Execution_Loop, then it is correct for Main. But more important to me is that the compiler should realize that the evaluation of Test_Mode should not leave cruft on the stack until the end of the if, and definitely any resources acquired or locked during the evaluation must be returned before the then or else parts. Anyone who used Multics prior to 8.0? will recognize the issue. The Multics PL/I compiler did have cases where complex expressions in the if clause could cause memory to be used, and it wouldn't be released until the end of the if statement. Actually early on, it only released the memory at the end of the else part, but that got fixed quickly. However if you did a goto out of the if statement, it still could result in a memory leak. Obviously it was fixed, but the full fix required two compiler releases and a flag day. (http://jargon.watson-net.com/jargon.asp?w=flag+day) **************************************************************** From: Pascal Leroy Sent: Thursday, January 27, 2005 1:36 AM > But the diagnostics I care about > are program verifier related. I want the verifier to check both the > property that the code cannot raise an exception (or sometimes only > cannot raise a predefined exception) and subprograms marked as never > exiting never exit. Two comments here. One is that the pragma doesn't ensure that the subprogram in question "never exits". It ensures that it doesn't return normally. So it can very well exit by raising an exception. You seem to assume that the main usage of this pragma is for infinite loops, but it is really the case of procedures that raise an exception that was mostly discussed. Moreover, I am skeptical about adding in the language pragmas that are intended for program verifiers. It seems quite hard to know in general which pragmas are going to be useful for a particular verifier: I suspect that it depends heavily on what kind of analysis the verifier is capable of doing. Of course the compiler is one particular kind of program verifier, but then we are mostly talking warnings (yawn) and optimization (re-yawn). **************************************************************** From: Jean-Pierre Rosen Sent: Thursday, January 27, 2005 3:15 AM > At this point, I would just forget it; it isn't worth these complications. > (It all about warnings, anyway, which Pascal is usually very opposed to.) I think this the crux of the argument: You (and Pascal I think) view this pragma as simply a mean of removing useless warning messages, while others view it as a contract model element, that is useful for the human reader (as well as tools). I have some sympathy with your idea that the second option requires true syntax, not a pragma, and the comparison with [not] overriding is quite convincing. Are we ready to go all the way to: [not return] [[not] overriding] procedure Blah is.... or (not really serious): all return with exception procedure Blah is... **************************************************************** From: Tucker Taft Sent: Thursday, January 27, 2005 8:27 AM Note that No_Return is an existing pragma in several implementations, and it has all the right characteristics of a pragma, namely that adding it has no effect on correct programs that continue to compile. **************************************************************** From: Jean-Pierre Rosen Sent: Thursday, January 27, 2005 9:34 AM Of course, and nothing would prevent existing implementations to continue supporting the pragma, with the current (absence of) semantics for derived subprograms. **************************************************************** From: Pascal Leroy Sent: Thursday, January 27, 2005 9:43 AM Bob wrote: > >... Yuck. So No_Return is > > really exposing a completely different control flow structure,... > > Well, not really: infinite loops and always-raise are already > legal! The pragma merely documents this control flow > structure. Note that if you erase the pragma, the program > behaves the same (presuming it was correctly obeying the pragma). So far so good, but then we don't require an always-raise procedure to only be overridden by always-raise procedures. So the pragma is not merely documenting the control flow. It is promising a contract that is enforced across type derivation, and presumably generic instantiations. I am not saying that this is necessarily a bad idea -- I would need to give it more thought. But I surely never realized that this pragma had such a far-reaching semantic effect. And surely a pragma that dramatically affects the legality of a program falls into the Bad Taste category. So I would be enclined to agree with Randy, syntax would be better here (and it's awfully late). **************************************************************** From: Robert Dewar Sent: Friday, January 28, 2005 6:10 AM >>I would simply say that it applies to subprograms when given >>explicitly. Implicitly inherited subprograms inherit it, of >>course, but that's it. > > > I couldn't agree more. I also agree with this, why do we have to complexify everything? One of the hardest things for us to implement was the absurdly over-decorated version of Unchecked_Union, including all kinds of bells and whistles that none of our users have even suggested in the many years we have implemented this. Let's not kill No_Return by similar overzealous attention :-) **************************************************************** From: Robert Dewar Sent: Friday, January 28, 2005 6:12 AM > The pragma is rarely useful. The main use in practice is to suppress spurious warnings about missing return paths in functions. It really has no semantics to speak of :-) **************************************************************** From: Robert Dewar Sent: Friday, January 28, 2005 6:11 AM Tucker Taft wrote: > I don't want to complicate the feature, but it seems > pretty clear that if you declare a primitive of > a tagged type called something like "Fatal_Errror," > and mark it as No_Return, then you would expect > all overridings to be the same. Dispatching calls > are more important than non-dispatching calls for > many tagged types (certainly for Interfaces!), > and for these, No_Return should probably apply to dispatching > calls, and then the non-dispatching call requirements > are implied by the requirements of the dispatching call. It does not seem clear to me! > I agree these are not that common, but for something > like a window system, it is fairly typical to have > some way to pop up an "alert" box, including a "fatal alert" > (especially on Windows ;-). I could imagine that something > like this might be something you would want to be able > to override, but if so, you wouldn't want to allow it > to become a "non-fatal" alert as part of overriding it. I discourage imagination, and suggest relying on actual user experience instead. > In any case, I agree this isn't that critical, but it > does seem important whenever we define a new pragma, to > think carefully about how it interacts with inheritance, > generics, etc. If we don't, we will just end up with > more AIs in the future. In this case, it seems pretty > clear that you want all overridings to be No_Return, > if the original one is. I don't think this is critical **************************************************************** From: Robert Dewar Sent: Friday, January 28, 2005 6:16 AM Randy Brukardt wrote: > Because those implementations couldn't invent syntax. And so far as I know, > neither of those implementations make the requirement that this pragma is > inherited. As I said earlier, the main motivation for introducing this in GNAT was to suppress junk warnings. Yes, it is used a bit for optimization purposes, but that may have been a mistake, since the gain is minimal and it has caused over time some (now fixed) problems. A pragma was definitely more appropriate, since it has no detectable semantics (i.e. you cannot write a test program to make sure it has been properly implemented). > I'm happy with the pragma as currently specified (no magic, no muss). But if > you want to go further, it has to be a syntactic annotation. It has to > matter when matching for formal subprograms (shouldn't it be able to apply > to them too?) It has to be checked on 'Access. This is a substantial job > (not like the current pragma, which only changes the drop-out return to > raise Program_Error, and has no other effect). Ada is not in the business of these kind of annotations, leave that to SPARK :-) > I'm willing to consider the whole boat of syntax for this (although it is > awfully late), but extending the simple pragma into this complex beast is a > non-starter for me. I'd rather forget the whole thing and let the > implementers do what makes sense for their implementations. I am not (willing to consider syntax boats). **************************************************************** From: Robert A. Duff Sent: Friday, January 28, 2005 9:13 AM > > If we allow the pragma to be overridden, then we can't change our mind > > due to compatibility. > > > > The pragma is rarely useful. ...in the sense that very few procedures will be no-return procedures. But such procedures are typically called all over the place (and the corresponding benefit in warning generation/suppression and optimization occurs all over). > The main use in practice is to suppress spurious warnings about > missing return paths in functions. Well, I don't know what particular compilers do with it, but it is potentially much more general that just that particular warning. Lots of warnings could be suppressed by knowing that a particular control-flow path can't happen. (Tucker gave an example of an uninit variable warning.) And misc optimizations can benefit from the same knowledge. Warnings are useless if there are too many spurious ones, so it's a Good Thing to give the compiler knowledge that can suppress the spurious ones. >... It really has no semantics to > speak of :-) Heh? According to the AI, the semantics (for nondispatching calls only) is that the call will not return normally. This is enforced, and the compiler can therefore believe it. The fact that this property is broken for dispatching calls seems obviously wrong to me. I feel quite strongly that if we're not going to do dispatching calls right, we should forbid the pragma on dispatching procedures! Otherwise, the pragma has negative benefit (it's an outright lie). **************************************************************** From: Robert A. Duff Sent: Friday, January 28, 2005 9:55 AM I wrote: > This is enforced, and the > compiler can therefore believe it. Just to clarify: I mean that the no-returning property is enforced ON THE BODY, and the compiler (and programmer) can therefore believe it (AT THE CALL SITE). It's like any other part of the contract. For example, if you call a procedure with an 'in' parameter, you know that it won't modify the actual. To make this work for dispatching calls, we obviously have to disallow overriding that 'in' parameter with an 'in out' parameter. **************************************************************** From: Robert Dewar Sent: Saturday, January 29, 2005 5:12 PM That sounds like a convincing argument to me. **************************************************************** From: Erhard Ploedereder Sent: Friday, January 28, 2005 8:07 AM Let's do a bit of code review.... type T is tagged .... procedure P(X: T); pragma No_Return(P); ----- if somepred then P(O); notify_the_police("help, we're dying"); -- (1) else .... As I code reviewer, I would find the statement at --(1) extremely suspicious, since it has the strong smell of dead code. Absent the pragma, I would not (easily) find out that the statement is dead code, if P is indeed a non-returning procedure. In that sense, the pragma makes a contribution to code safety, not just to suppress annoying warnings by compilers. In that sense, too, the pragma must be "inherited", i.e., observed by redefinitions. (Whether one insists on an explicit confirmation as part of the redefinition is a secondary question. I am mildly opposed to require this.) It just isn't correct, if a call on a routine amended by a comment, a pragma, syntax, or whatever that says that the routine will not return, does return after all. If you really insist that the pragma be merely a warning suppressing pragma, it belongs in the body of the respective routine, and only there. Actually, the pragma smells a lot more like a calling convention pragma (with all the right rules already in place) than a pragma akin to Inline. **************************************************************** From: Robert Dewar Sent: Saturday, January 29, 2005 5:11 AM > If you really insist that the pragma be merely a warning suppressing > pragma, it belongs in the body of the respective routine, and only > there. No, that won't do, it is the client that must suppress the warning and hence needs to know that this is going on. > Actually, the pragma smells a lot more like a calling convention pragma > (with all the right rules already in place) than a pragma akin to Inline. I absolutely agree that this is anaologous to a calling convention. In fact it really is part of the calling convention (which covers call and return). For example, in some environments, you could imagine that pragma No_Return causes the compiler to generate a jump instead of a call instruction. **************************************************************** From: Bob Duff Sent: Wednesday, February 2, 2005 11:35 AM Pascal has asked me to write an AI that corrects AI-329 -- pragma No_Return. But I've gotten myself confused. Pragma No_Return is a program unit pragma. It is modeled after pragma Inline, which is also a program unit pragma. 10.1.5 says: Name Resolution Rules 2 {program unit pragma [distributed]} {pragma, program unit [distributed]} Certain pragmas are defined to be program unit pragmas. {apply (to a program unit by a program unit pragma) [partial]} A name given as the argument of a program unit pragma shall resolve to denote the declarations or renamings of one or more program units that occur immediately within the declarative region or compilation in which the pragma immediately occurs, or it shall resolve to denote the declaration of the immediately enclosing program unit (if any); the pragma applies to the denoted program unit(s). If there are no names given as arguments, the pragma applies to the immediately enclosing program unit. Legality Rules 3 A program unit pragma shall appear in one of these places: 4 At the place of a compilation_unit, in which case the pragma shall immediately follow in the same compilation (except for other pragmas) a library_unit_declaration that is a subprogram_declaration, generic_- subprogram_declaration, or generic_instantiation, and the pragma shall have an argument that is a name denoting that declaration. 5/1 {8652/0033} Immediately within the declaration of a program unit and before any nested declaration{ (but not within a generic formal part)}, in which case the argument, if any, shall be a direct_name that denotes the immediately enclosing program unit declaration. 6 At the place of a declaration other than the first, of a declarative_part or program unit declaration, in which case the pragma shall have an argument, which shall be a direct_name that denotes one or more of the following (and nothing else): a subprogram_declaration, a generic_subprogram_declaration, or a generic_instantiation, of the same declarative_part or program unit declaration. Para 2 seems to contemplate program unit pragmas that refer to renaming_declarations. But paras 4 and 6 seem to forbid renamings. A subprogram_renaming_declaration is not a subprogram_declaration. 6.3.2 says: 5.a Ramification: Note that inline expansion is desired no matter what name is used in the call. This allows one to request inlining for only one of several overloaded subprograms as follows: 5.b package IO is procedure Put(X : in Integer); procedure Put(X : in String); procedure Put(X : in Character); private procedure Character_Put(X : in Character) renames Put; pragma Inline(Character_Put); end IO; But the above pragma Inline seems illegal by 10.1.5(6). Is this a bug in the RM, or am I missing something? Regardless of the pragma Inline issue, I'm thinking pragma No_Return should *not* be a program unit pragma. It should be a representation pragma, modeled after pragma Convention. Does that make sense? An implementation could use a JUMP instruction instead of a CALL instruction to call a No_Return procedure. This isn't the primary purpose of the pragma, but it seems reasonable to allow this optimization. Making No_Return a representation pragma makes sense here, because that implies some freezing rules -- the compiler needs to know it's a No_Return procedure before any calls. Should No_Return be allowed on a renaming_decl? If so, should it have the same weird semantics as for pragma Inline? **************************************************************** From: Gary Dismukes Sent: Wednesday, February 2, 2005 12:55 PM > But the above pragma Inline seems illegal by 10.1.5(6). > > Is this a bug in the RM, or am I missing something? Hmm, this does seem like a bug, given that the name in the pragma is required to denote one of those specific forms of declaration, rather than, say, denoting the entity declared by one of those (which would capture the renaming case I think). > Regardless of the pragma Inline issue, I'm thinking pragma No_Return > should *not* be a program unit pragma. It should be a representation > pragma, modeled after pragma Convention. Does that make sense? Yes, that seems reasonable. In any case, it probably doesn't that much sense to allow this pragma to be nested within the body of the subprogram that it applies to, as program unit pragmas allow. > An implementation could use a JUMP instruction instead of a CALL > instruction to call a No_Return procedure. This isn't the primary > purpose of the pragma, but it seems reasonable to allow this > optimization. Making No_Return a representation pragma makes sense > here, because that implies some freezing rules -- the compiler needs to > know it's a No_Return procedure before any calls. > > Should No_Return be allowed on a renaming_decl? If so, should it have > the same weird semantics as for pragma Inline? If we make it a representation pragma then it would seem to be allowed for renamings (that occur immediately within the same set of declarations), since the wording for such pragmas talks about denoting an entity. I don't see any particular problem with allowing those, and it would allow disambiguation in the same manner as Inline. **************************************************************** From: Tucker Taft Sent: Wednesday, February 2, 2005 12:27 PM > Para 2 seems to contemplate program unit pragmas that refer to > renaming_declarations. But paras 4 and 6 seem to forbid renamings. > A subprogram_renaming_declaration is not a subprogram_declaration. This sounds like a bug. Para 6 should allow subprogram_renaming_declaration as well. No need for that in para 4, because library units can't be overloaded. Note that in Ada 83, the wording talks about denoting a subprogram, as opposed to a subprogram_declaration. As suggested below, para 6 wording should probably be similar to 13.1(6) which allows denoting a local subprogram via a local renaming. > > ... > > Is this a bug in the RM, or am I missing something? I think it is a bug. > > Regardless of the pragma Inline issue, I'm thinking pragma No_Return > should *not* be a program unit pragma. It should be a representation > pragma, modeled after pragma Convention. Does that make sense? Not quite. Pragma Convention, when applied to a program unit, *is* a program unit pragma. It is also a representation pragma. It's both (I can almost hear the TV advertisement for this ;-). See B.1(29). > Should No_Return be allowed on a renaming_decl? If so, should it have > the same weird semantics as for pragma Inline? If it is a representation pragma, then it should use the "local_name" semantics, which allows denoting an overloadable entity via a rename, to resolve ambiguity (see 13.1(6)). Pragma Convention allows this, for example. It might be nice if para 10.1.5(6) and 13.1(6) shared more wording. Right now I think they are trying to say about the same thing, but 13.1(6) got it right, and 10.1.5(6) seems to have muffed it. **************************************************************** From: Randy Brukardt Sent: Wednesday, February 2, 2005 2:30 PM > But the above pragma Inline seems illegal by 10.1.5(6). > > Is this a bug in the RM, or am I missing something? I was sure that we'd already discussed this in the past, but I can't find anything in the AIs. Must be a deja vu experience. (Yes, if you want inheritance and the like of No_Return, then it should be treated like a Convention pragma. Indeed, it *is* a convention; perhaps we should just define this as a convention name?) **************************************************************** From: Gary Dismukes Sent: Wednesday, February 2, 2005 2:38 PM > I was sure that we'd already discussed this in the past, but I can't find > anything in the AIs. Must be a deja vu experience. Interesting, I had a similar deja vu feeling. > (Yes, if you want inheritance and the like of No_Return, then it should be > treated like a Convention pragma. Indeed, it *is* a convention; perhaps we > should just define this as a convention name?) This property seems orthogonal to convention. It seems that making it a convention would prevent exporting such a procedure to foreign code. **************************************************************** From: Randy Brukardt Sent: Wednesday, February 2, 2005 3:02 PM > This property seems orthogonal to convention. It seems that making it a > convention would prevent exporting such a procedure to foreign code. Which seems like a good thing if it actually *does* change the convention, as Bob suggested. Moreover, I can't imagine what use it would have in foreign code, which isn't going to be able to do anything with an Ada exception. In any case, pragmas are inappropriate for this. (That includes Convention; it would be much better if we had done that syntactically: procedure Something (A : Integer) as Fortran is ... But it's too late for that.) We're stuck with Convention (and Pack), but I'm strongly opposed to adding more. At least reusuing Convention would be something that I couldn't really object to... **************************************************************** From: Gary Dismukes Sent: Wednesday, February 2, 2005 3:22 PM Some implementations support importing and exporting of exceptions. For example, JGNAT allows importing Java exceptions, which can be raised and propogated to Java code. **************************************************************** From: Robert Dewar Sent: Wednesday, February 2, 2005 3:54 PM Perhaps I miss something, but a standard use of pragma No_Return is for the imported exit function. **************************************************************** From: Erhard Ploedereder Sent: Thursday, February 3, 2005 6:45 AM I agree with Gary. It is an orthogonal convention aspect and, if tied to convention semantics, should be an aspect that can be additional to the calling cenvention and one that is immutable by overriding. (In fact, it is not at all unlikely that calling convention C and the No-Return convention should both apply to a subprogram.) **************************************************************** From: Robert Dewar Sent: Wednesday, February 2, 2005 4:38 PM > > Is this a bug in the RM, or am I missing something? > > I was sure that we'd already discussed this in the past, but I can't find > anything in the AIs. Must be a deja vu experience. Strange. ;-) > (Yes, if you want inheritance and the like of No_Return, then it should be > treated like a Convention pragma. Indeed, it *is* a convention; perhaps we > should just define this as a convention name?) I just sent out my AI on the topic of No_Return. I think I answered this in the AI -- No_Return is somewhat like Convention, but not entirely. I agree with Gary that it doesn't work to simply make it another Convention name. I have some sympathy for Randy's idea that No_Return should be syntax rather than a pragma. That's probably how I'd design a from-scratch language. But it *is* a pragma, approved by WG9, and I'm just trying to get the details right. At this late date (for Ada 2005), we don't need "It should be syntax. So propose some syntax. So nobody likes the syntax. So squelch the whole idea." Let's focus on getting the details right, and ignore the fact that we might like a totally different approach. **************************************************************** From: Robert Dewar Sent: Wednesday, February 2, 2005 3:51 PM > In any case, pragmas are inappropriate for this. (That includes Convention; > it would be much better if we had done that syntactically: > > procedure Something (A : Integer) as Fortran is ... BIG UGH! Uninterpreted identifiers outside pragmas, please no. I think pragma Convention is just fine. **************************************************************** From: Randy Brukardt Sent: Wednesday, February 2, 2005 4:44 PM I don't see the issue; it's no different than First and Last, which are also magic identifiers. But it's academic in any case. **************************************************************** From: Robert Dewar Sent: Wednesday, February 2, 2005 5:17 PM Pragma names and attribute names are indeed handled specially. Please don't add more special cases, they would be a real pain to implement! **************************************************************** From: Randy Brukardt Sent: Wednesday, February 9, 2005 9:57 PM > Uninterpreted identifiers outside pragmas, please no. I think > pragma Convention is just fine. Why would they be uninterpreted? The AARM suggests that the convention names could have been an enumeration declared in package Interfaces (with implementation-defined contents); that would prevent problems. Obviously, the expression used would have to be static; but it would allow giving alternative names to these things simply -- it certainly would have helped us when we decided to follow others lead and rename our "Windows" convention to "StdCall". **************************************************************** From: Robert A. Duff Sent: Wednesday, February 2, 2005 4:28 PM OK, here's a new AI to replace AI-00329, as requested by Pascal. [This is version /01 - ED.] The only major change is to make dispatching calls work right (they don't return if so-promised by the pragma). I noticed a few other minor problems with AI-00329, which I patched up in the wording. None of this is a big deal, and none of it causes any big implementation burdens. I think the dispatching calls issue needs to be fixed *now*, because fixing it in AI2005-00001 would raise compatibility problems. Various extensions have been suggested (e.g. access-to-procedure types); these need not be fixed now. They can be easily fixed compatibly in 2010 or 2015, or never. Therefore, this AI does not propose to add those capabilities. For simplicity's sake, I have only fixed bugs, and not added gratuitous features. There is, apparently, a bug in the rules about "program unit pragmas". I did not fix that. If somebody wants an AI on that, I'm willing to write it, but it's irrelevant to *this* AI. **************************************************************** From: Robert I. Eachus Sent: Wednesday, February 2, 2005 9:06 PM I will state this as a bit of missing justification from the AI: There is no mention of why No_Return is limited to procedures and cannot be applied to functions. At first thought, No_Return for functions may seem completely silly, but one of the examples renames the C function exit. (It is not a procedure, C doesn't have such things.) The only reason for allowing No_Return to apply to functions I can see is completeness. Certainly if a function calls a procedure marked No_Return, you would like to apply the pragma to a function as well. **************************************************************** From: Robert Dewar Sent: Wednesday, February 2, 2005 9:45 PM > At first thought, No_Return for functions may > seem completely silly, but one of the examples renames the C function > exit. (It is not a procedure, C doesn't have such things.) C has void functions like exit, which properly map into Ada procedures, so this concern is pedantic and irrelevant in my view. > The only reason for allowing No_Return to apply to functions I can see > is completeness. Certainly if a function calls a procedure marked > No_Return, you would like to apply the pragma to a function as well. This seems to be orthogonality gone berserk. **************************************************************** From: Bob Duff Sent: Thursday, February 3, 2005 7:07 AM AI-329 has some rationale for that. I didn't repeat the content of AI-329 in the new AI. The new AI is about fixing a fairly important bug, and fixing a few minor nits. The only rationale in the new AI is rationale for these bug fixes, and I think that's as it should be. **************************************************************** From: Robert I. Eachus Sent: Sunday, February 6, 2005 12:43 PM My only intention was to suggest that *if* this AI is a replacement for AI-329, it should keep the rationale from AI-329... Robert Dewar wrote: > C has void functions like exit, which properly map into Ada procedures, > so this concern is pedantic and irrelevant in my view. Which concern? I thought I was saying that the AI should provide a justification for, in effect, requiring that C functions which don't return should be declared as Ada procedures. I said: >> The only reason for allowing No_Return to apply to functions I can >> see is completeness. Certainly if a function calls a procedure >> marked No_Return, you would like to apply the pragma to a function as >> well. Robert Dewar responded... > This seems to be orthogonality gone berserk. Or a vivid imagination. My intent was not to argue in favor of pragma No_Return applying to functions. I was just saying that the only possible argument I could see in favor of it was orthogonality. Maybe someone could imagine some other strawman? In any case, the current version of AI-329 (1.8) says: Originally we considered allowing No_Return on functions, but that seemed of little benefit, and outlawing return statements in functions would clearly conflict with the existing (albeit a bit weird) rule that currently requires at least one. I also won't complain if more of the discussion section of AI-329 is carried over into the new AI. Since AI-329 is already WG-9 approved, and this AI references it, that may be sufficient. If the intent is for the new AI to supplement AI-329 instead of replacing it, that is fine. **************************************************************** From: Bob Duff Sent: Sunday, February 6, 2005 2:28 PM OK, now I see what you mean. I should not have said "replace" -- sorry. The wording of the new AI replaces *most* of the wording of AI-329, but it explicitly mentions that a couple of paragraphs are carried over from AI-329. Also, I didn't want to Rationalize the existence of the feature -- AI-329 already does that, and I think referring to it is enough. The new AI should Rationalize the changes, I think. ****************************************************************