!standard 6.01 (18) 09-02-15 AI05-0143-1/01 !standard 6.06 (03) !standard 9.5.1 (02) !class Amendment 09-02-15 !status work item 09-02-15 !status received 09-02-15 !priority High !difficulty Medium !subject It's BAAACCKK!!: In Out parameters for functions !summary Parameters of all modes are allowed in functions. !problem The restriction on parameter modes for functions is an embarassing wart on the language. Ada functions can have arbitrary side-effects, but are not allowed to announce that in their specifications. When constructing reusable object-oriented abstractions, it is important to allow future extensions to have the maximum flexibility. This means that parameters of object-oriented types should generally be declared as in out, so that future extensions can modify the parameter's contents if necessary. However, this prevents using functions for most operations. If there is any conceptual chance of an extension needing to modify its argument, then a function cannot be used. Even for operations that are conceptually pure, caching of results may require modification of extensions. If an unconstrained type (such as String) needs to be returned, there is no real alternative to using a function. When this problem occurs, only three solutions are possible: -- Abandon functions; -- Use the functions, leaving the onus of solving the problem on extensions. -- Use an access parameter. For the second solution, the author of an extension would have to allocate (probably from the heap) a separate, writable object, and include an access to it in the extension. This means the author now has to handle memory management (with all of its possibilities for errors, like storage leaks and double freeing) which they previously did not have to do. Access parameters are a very flawed solution as well. Access parameters require altering the calling syntax of calls to the function (to include an approriate 'Access, or to remove a dereference). They also require altering the declaration of the object to be passed (to include "aliased"). Finally, access parameters are more expensive at run-time than regular parameter passing, as they are required to include a run-tine indication of their accessibility level. The run-time check can even be dangerous, as the check may fail only with arguments declared in nested scopes, and that may not happen in unit testing. For all of these reasons, the restriction on parameter modes for functions has been removed. !proposal (See summary.) !wording The last sentence of 6.1(18) is deleted. Add "of mode in" to 6.6(3): The subprogram_specification of a unary or binary operator shall have one or two parameters of mode in, respectively. A generic function instantiation whose designator is an operator_symbol is only allowed if the specification of the generic function has the corresponding number of parameters of mode in and no other parameters. Replace 9.5.1(2) by: Within the body of a protected function (or a function declared immediately within a protected_body), all of whose parameters have mode in, the current instance of the enclosing protected unit is defined to be a constant [(that is, its subcomponents may be read but not updated)]. Within the body of a protected subprogram other than a function with parameters of mode in (or a such a subprogram declared immediately within a protected_body), and within an entry_body, the current instance is defined to be a variable [(updating is permitted)]. !discussion The primary technical argument against allowing "in out" and "out" parameters in function calls is that there is not much visibility of the side-effects, and thus they make it more likely that order-dependent expressions are created. However, the existing features of the language (especially the combination of access parameters and prefix notation) make it possible to write the majority of examples of incorrect order dependence in Ada as it exists today. Specifically, access parameters are very similar semantically to "in out" by-reference parameters, and they are allowed on functions already. They just provide a less convenient syntax and are less efficient (because of the possibility of dynamic accessibility checks) than "in out" parameters would be. As one commenter put described our current situation with incorrect order dependence, we're already in a deep pit, and if we're not at the bottom already, we're only a few centimeters above it. Thus, the problem of incorrect order dependence is one that should be addressed irrespective of "in out" parameters for functions, and surely should not be used as an excuse to "throw out the baby with the bathwater". (The problem of incorrect order dependence is discussed in AI05-0144-1, including plenty of examples.) Two additional issues are addressed by this change. Operator symbols are conceptually functions with one or two in parameters. We do not want infix expressions to have side effects on their arguments. Thus, we add wording to restrict operator symbols to parameters of mode in. The unfortunate tying of read-only vs. read-write access to a protected object to the use of a function or procedure also needs to be changed. We've used the minimal fix here, which is to say that functions with in out or out parameters have read-write access to the protected object. A better solution would be to add a modifier similar to that used for overriding to determine whether a subprogram has read-only versus read-write access. However, that was rejected as being too heavy of a solution, particularly as Ada compilers rarely take advantage of read-only protected object access. !example In the Claw Builder, we have a function that can return an access to the actual Claw object for simulation. This function is usually used to directly call a dispatching Claw operation: Claw.Move (Get_Claw_Object (My_Button).all, ); Because the access returned is never used for more than a single subprogram, we implemented the function with Unchecked_Access: function Get_Claw_Object (Window : in Button_Type) return Claw.Any_Window_Access_Type is begin return Window.My_Button'Unchecked_Access; end Get_Claw_Object; This code is illegal, of course, as Window.My_Button is a constant, and Any_Window_Access_Type is an access-to-variable. Since Claw.Move takes an in out parameter, changing Any_Window_Access_Type is out of the question. Changing this to the "obvious" implementation: function Get_Claw_Object (Window : access Button_Type) return Claw.Any_Window_Access_Type is begin return Window.My_Button'Unchecked_Access; end Get_Claw_Object; means that calling objects have to be declared as aliased, and an additional 'Access used. Moreover, there is the run-time overhead of passing an accessibility level, even though it will never be used in this case. What we really want, of course, is to write: function Get_Claw_Object (Window : in out Button_Type) return Claw.Any_Window_Access_Type is begin return Window.My_Button'Unchecked_Access; end Get_Claw_Object; which we can now do. Conceptually, of course, the parameter to this function isn't even modified. We just need to use in out here to prevent someone from passing a constant, which could cause all manner of trouble. !ACATS test ACATS tests are needed to test that this is allowed, and that the restrictions on operator symbols are enforced. Moreover, existing ACATS B-Test tests prohibiting in out parameters on function needs to be withdrawn. !appendix From: Randy Brukardt, February 15, 2009 Version /01 of this AI is almost identical to the final state of AI95-0323 (when it was voted no action). The only changes were to incorporate one wording comment and to add a bit of discussion about the technical issues that were mentioned by Tucker (see the e-mail toward the end of AI95-0323). I distinctly remember someone saying "thank goodness that we don't allow "in out" parameters in functions" during some ARG discussion. I was unable to find that in the minutes of any ARG meeting or in any AIs. I also spent quite a bit of time reading old AIs about freezing and incomplete views to see if adding "in out" parameters would break anything; I was unable to find any rules that would not work in that context. It's quite possible that the rule under discussion was later modified eliminating the problem, or perhaps it was never used at all. Or maybe I just failed to find it. ****************************************************************