Version 1.7 of ai12s/ai12-0269-1.txt

Unformatted version of ai12s/ai12-0269-1.txt version 1.7
Other versions for file ai12s/ai12-0269-1.txt

!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)
Replace the paragraph:
Nonreturning Procedures
by:
Nonreturning Subprograms
!corrigendum 6.5.1(1/3)
Replace the paragraph:
Specifying aspect No_Return to have the value True indicates that a procedure cannot return normally; it may propagate an exception or loop forever.
by:
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)
Replace the paragraph:
For a procedure or generic procedure, the following language-defined representation aspect may be specified:
by:
For a subprogram or generic subprogram, the following language-defined representation aspect may be specified:
!corrigendum 6.5.1(3.4/3)
Replace the paragraph:
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.
by:
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)
Insert after the paragraph:
A return statement shall not apply to a nonreturning procedure or generic procedure.
the new paragraph:
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.
!corrigendum 6.5.1(6/2)
Replace the paragraph:
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.
by:
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)
Replace the paragraph:
If a renaming-as-body completes a nonreturning procedure declaration, then the renamed procedure shall be nonreturning.
by:
If a renaming-as-body completes a nonreturning subprogram declaration, then the renamed subprogram shall be nonreturning.
!corrigendum J.15.2(2/3)
Replace the paragraph:
pragma No_Return (procedure_local_name{, procedure_local_name});
by:
pragma No_Return (subprogram_local_name{, subprogram_local_name});
!corrigendum J.15.2(3/3)
Replace the paragraph:
Each procedure_local_name shall denote one or more procedures or generic procedures. The procedure_local_name shall not denote a null procedure nor an instance of a generic unit.
by:
Each subprogram_local_name shall denote one or more subprograms or generic subprograms. The subprogram_local_name shall not denote a null procedure nor an instance of a generic unit.
!corrigendum J.15.2(4/3)
Replace the paragraph:
Pragma
No_Return specifies that the No_Return aspect (see 6.5.1) for each procedure denoted by each local_name given in the pragma has the value True.
by:
Pragma
No_Return specifies that the No_Return aspect (see 6.5.1) for each subprogram denoted by each local_name given in the pragma 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 <raise_expression> 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);

****************************************************************

Questions? Ask the ACAA Technical Agent