!standard 10.2.1 (00) 04-02-29 AI95-00290/01 !class amendment 02-03-13 !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... ****************************************************************