Version 1.8 of ais/ai-00290.txt

Unformatted version of ais/ai-00290.txt version 1.8
Other versions for file ais/ai-00290.txt

!standard 10.2.1 (00)          04-02-29 AI95-00290/01
!class amendment 02-03-13
!status No Action (9-0-1) 04-03-05
!status work item 04-02-29
!status received 02-03-13
!priority Medium
!difficulty Medium
!subject Declaring functions Pure
!summary
A pragma Pure_Function may be used to declare that a particular function within an impure package is to be considered "pure" by the implementation. This means that a second call with identical inputs to a first call may be omitted, reusing the result of the first call instead of performing the second call.
!problem
Making an entire package "pure" can be difficult, because it may necessarily depend on an impure package. Nevertheless, there are benefits to declaring that a given function may be treated as "pure" by the implementation, meaning that the outputs are a function of the inputs, without dependence on global variables, etc.
!proposal
(See wording.)
!wording
Add a new section:
6.3.3 Pragma Pure_Function
A function may be declared pure to indicate that it has no significant side-effects, and that the results it produces are a pure function of its inputs, without any dependence on global variables.
Syntax
The form of pragma Pure_Function, which is a program unit pragma (see 10.1.5), is as follows:
pragma Pure_Function([Entity => ] function_local_name);
Legality Rules
The pragma shall apply to one or more functions or generic functions.
Implementation Permission
If a function is declared pure, then the implementation is permitted to omit a call on the function if its results are not needed after the call. In addition, the implementation may omit a call on such a function and simply reuse the results produced by an earlier call on the same function, provided that none of the parameters nor any object accessible via access values from the parameters are of a limited type, and the addresses and values of all by-reference actual parameters, the values of all by-copy-in actual parameters, and the values of all objects accessible via access values from the parameters, are the same as they were at the earlier call. This permission applies even if the subprogram produces other side effects when called.
!discussion
The existing ability to declare a library unit "pure" is useful, but is limiting given some of the stringent elaboration dependence requirements on such units. This pragma provides the benefits of declaring a function pure, without imposing such requirements. In fact, it imposes no requirements, though it is anticipated that implementations may provide warnings when the result of such a fuction clearly depends on the state of a global variable (for example).
Pure functions are particularly useful for assertions (see AI-286). Although AI-286 does not require the use of pure functions in assertions, an implementation-defined assertion policy might impose such a requirement, or an implementation might warn about use of impure functions in assertions.
Note that we do not permit implementations to assume a declared-pure function is truly "pure." Instead, we give permission to omit certain calls, even if in fact they are not "truly" redundant. This approach avoids the significant danger associated with allowing implementations to assume propositions to be true which in reality are not true. Assuming falsehoods can be used to prove essentially any considered proposition.
!example
!ACATS test
An ACATS test should be a created for this pragma.
!appendix

From: Tucker Taft
Sent: Tuesday, March 12, 2002  4:47 PM

Thanks, Robert.  I hereby nominate this as the basis for an Amendment AI.
-Tuck

> As Tuck requested:
>
> @item pragma Pure_Function
> @noindent
> Syntax:
>
> @smallexample
> pragma Pure_Function ([Entity =>] function_LOCAL_NAME);
> @end smallexample
>
> This pragma appears in the same declarative part as a function
> declaration (or a set of function declarations if more than one
> overloaded declaration exists, in which case the pragma applies
> to all entities).  If specifies that the function @code{Entity} is
> to be considered pure for the purposes of code generation.  This means
> that the compiler can assume that there are no side effects, and
> in particular that two calls with identical arguments produce the
> same result.  It also means that the function can be used in an
> address clause.
>
> Note that, quite deliberately, there are no static checks to try
> to ensure that this promise is met, so @code{Pure_Function} can be used
> with functions that are conceptually pure, even if they do modify
> global variables.  For example, a square root function that is
> instrumented to count the number of times it is called is still
> conceptually pure, and can still be optimized, even though it
> modifies a global variable (the count).  Memo functions are another
> example (where a table of previous calls is kept and consulted to
> avoid re-computation).
>
> @findex Pure
> Note: Most functions in a @code{Pure} package are automatically pure, and
> there is no need to use pragma @code{Pure_Function} for such functions.  An
> exception is any function that has at least one formal of type
> @code{System.Address} or a type derived from it.  Such functions are not
> considered pure by default, since the compiler assumes that the
> @code{Address} parameter may be functioning as a pointer and that the
> referenced data may change even if the address value does not.  The use
> of pragma @code{Pure_Function} for such a function will override this default
> assumption, and cause the compiler to treat such a function as pure.
>
> Note: If pragma @code{Pure_Function} is applied to a renamed function, it
> applies to the underlying renamed function.  This can be used to
> disambiguate cases of overloading where some but not all functions
> in a set of overloaded functions are to be designated as pure.

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

From: Tucker Taft
Sent: Friday, March 15, 2002  4:23 PM

Robert Dewar wrote:
>
> As Tuck requested:
>
> @item pragma Pure_Function
> @noindent
> Syntax:
>
> @smallexample
> pragma Pure_Function ([Entity =>] function_LOCAL_NAME);
> @end smallexample
>
> This pragma appears in the same declarative part as a function
> declaration (or a set of function declarations if more than one
> overloaded declaration exists, in which case the pragma applies
> to all entities).  If specifies that the function @code{Entity} is
> to be considered pure for the purposes of code generation.  This means
> that the compiler can assume that there are no side effects, and
> in particular that two calls with identical arguments produce the
> same result.  It also means that the function can be used in an
> address clause.

For the RM description, I might recommend a different approach.  Probably start
with the existing rule in 10.2.1(18), where it allows the
compiler to omit a call if the results are not needed, and to reuse
the results of a prior call if the parameters are the "same."
But it would also be useful to go further, and say that the compiler
may reuse the results of a prior call even if some of the parameters
might have changed, so long as the only way they could have changed
is by side-effects of calls on intervening pure subprograms.
This means that the compiler doesn't have to look inside
pure subprograms to see what they might have changed, for the purposes
of eliminating calls on other pure subprograms.

An important feature of the current 10.2.1(18) wording, in my view,
is that it doesn't allow the compiler to "assume" anything which might
turn out to be false, and thereby lead to erroneousness.  It simply
allows the compiler to omit certain calls, and reuse earlier results.
For example, it can't call the same pure function twice, and omit a check
on the second call just because the result of the first call passed
the check.  It has to reuse the result of the first call (which it
checked directly) if it wants to omit the check that might normally
be associated with the second call.

> ...

> @findex Pure
> Note: Most functions in a @code{Pure} package are automatically pure, and
> there is no need to use pragma @code{Pure_Function} for such functions.  An
> exception is any function that has at least one formal of type
> @code{System.Address} or a type derived from it.  Such functions are not
> considered pure by default, since the compiler assumes that the
> @code{Address} parameter may be functioning as a pointer and that the
> referenced data may change even if the address value does not.  The use
> of pragma @code{Pure_Function} for such a function will override this default
> assumption, and cause the compiler to treat such a function as pure.

This discussion seems to be inappropriate for the normative semantics
of the potential amendment.  It might be appropriate to implementation
advice, or perhaps simply to a particular implementation's documentation.

I would hope that every function in a declared Pure package is at
least "officially" Pure, independent of whether it has an Address parameter.
GNAT might choose to not take advantage of the permissions given by
10.2.1(18) in cases where there is an Address parameter, but clearly
some compilers may already be eliminating calls on Pure functions
with Address parameters, and it seems inappropriate to change that
now.

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

From: Robert Dewar
Sent: Friday, March 15, 2002  5:15 PM

Just to be clear, I am offering the current documentation of the pragma
in the GNAT documentation, but certainly not suggesting that this is the
right language for an RM description.

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

From: Tucker Taft
Sent: Tuesday, November 19, 2002  7:50 PM

> Note also that we have in GNAT pragma Pure_Function, so we can get the
> advantages of making functions pure without the burden of making the
> whole package Pure.

And there is an "amendment AI" to add the Pure_Function
pragma to the "standard" set of pragmas, as
it seems useful and of low implementation
burden.

The "defining" semantics for a pure function
is that if you give it the "same" parameters,
you get back the same result.  This is patently
false for a random number generator, so it makes
no sense to make "random" itself pure.

As Robert points out, it is useful to declare
that a function has the "pure" property, even
though it might be implemented in terms of "impure"
units.  That would be the point of the Pure_Function
pragma.

Note that the Pure_Function pragma will not enforce
any restrictions.  It simply informs the reader and
the compiler that it may omit subsequent calls on
the function if the inputs are the same, and simply
reuse the original result.  Note that there is no
guarantee that if you *had* called the function, it would
return the same value.  That would be a bug if it
didn't, presumably, but not erroneous.

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

From: Tucker Taft
Sent: Tuesday, November 19, 2002  7:55 PM

As mentioned in the earlier response, the important
thing to remember is that the compiler may omit
calls on pure functions under certain circumstances,
and reuse the prior returned value.  It is normally a bug
to write a pure function that doesn't return the
same value given the same inputs, but not erroneous.

Another purpose of "Pure" is to support preelaboration.
A final purpose is to allow packages to be
freely replicated in a distributed application without
affecting the semantics.

In retrospect, it was probably a mistake to try to
make the single concept of "Purity" serve all three
roles.  They sometimes conflict with one another.

The Pure_Function pragma is a way to loosen the connection a
bit between these three roles.

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

From: Jean-Pierre Rosen
Sent: Wednesday, November 20, 2002  10:46 AM

Bounded_Error presumably ?

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

From: Robert Dewar
Sent: Wednesday, November 20, 2002  2:38 PM

> As mentioned in the earlier response, the important
> thing to remember is that the compiler may omit
> calls on pure functions under certain circumstances,
> and reuse the prior returned value.  It is normally a bug
> to write a pure function that doesn't return the
> same value given the same inputs, but not erroneous.

It often makes perfectly good sense to return values that are different
from a technical point of view, but the same from a conceptual point of
view. A good example is when an access value is returned, and of course
different pointers might be returned but the conceptual value (the value
pointed to) is always the same. Simple minded string concatenation for
example is in this category if you are dealing with say unbounded strings.

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

From: Tucker Taft
Sent: Tuesday, November 19, 2002  5:51 PM

No, it is not even a bounded error.  The
compiler is not allowed to complain if
it returns a different value.  See
Robert's example of why it might return
a different value, even though it is
"conceptually" pure.

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

!topic implications of pragma pure for Ada.Assertions.Assert
!reference RM95-10.2.1(18)
!from Dan Eilers 03-10-16
!keywords pragma pure assert

The proposed assertions package in AI95-00286 uses pragma pure
(apparently so that procedure Ada.Assertions.Assert can be called
from pure packages).

But this seems to imply an unintended implementation permission to omit
all calls on this procedure, per RM 10.2.1(18), since Assert has only IN
parameters.  I am assuming that an exception being raised is considered
a "side effect" rather than a "result" of the subprogram, although the
RM doesn't seem to explicit say that.

Perhaps there needs to be a mechanism to indicate that a subprogram might
raise an exception, and calls should therefore not be omitted, unless
perhaps they are successive calls with identical parameters.

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

From: Tucker Taft
Sent: Thursday, October 16, 2003  3:00 PM

Good point.  I would instead suggest that we change the wording
of 10.2.1(18) to only apply to functions and to procedures with
OUT parameters.  The permission would not apply to calls on pure
procedures with only IN parameters.

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

From: Robert A. Duff
Sent: Thursday, October 16, 2003  3:04 PM

> But this seems to imply an unintended implementation permission to omit
> all calls on this procedure, per RM 10.2.1(18), since Assert has only IN
> parameters.  I am assuming that an exception being raised is considered
> a "side effect" rather than a "result" of the subprogram, although the
> RM doesn't seem to explicit say that.

Good point.  I've never been very comfortable with the wording of this
paragraph, by the way.

> Perhaps there needs to be a mechanism to indicate that a subprogram might
> raise an exception, and calls should therefore not be omitted, unless
> perhaps they are successive calls with identical parameters.

But surely a user-defined subp call cannot be silently omitted,
just because it is known to raise an exception!?  The AARM annotation
talks about machine-code insertions and the like -- not well-defined
standard stuff like exceptions.

And I think the same about Assert -- if there aren't any pragmas or
command-line switches turning it off, it ought to raise an exception
when violated, just like array-index-out-of-bounds raises C_E.

Or was this intended to be like 11.6?.  I hope not...

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


Questions? Ask the ACAA Technical Agent