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

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

!standard 6.5.1(0)          18-03-27 AI12-0269-1/01
!standard 6.5.1(5/2)
!class Amendment 18-03-29
!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. Such a function might exist because it is inherited from a progenitor, but should not be called. 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 in a nonreturning function or generic function shall have an expression that is solely a raise_expression or a call on a nonreturning function.
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) and J.15.2(3/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.
!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;
!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: 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.

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

Questions? Ask the ACAA Technical Agent