!standard 3.10(6) 05-03-14 AI95-00404/04 !standard 3.9.2(11) !class amendment 05-01-28 !status Amendment 200Y 05-03-14 !status ARG Approved 6-0-2 05-02-14 !status work item 05-01-28 !status received 05-01-28 !status received !priority High !difficulty Easy !subject Not null and all in access parameters and types !summary Not null is implicit, but may be given explicitly, for controlling access parameters. When defined by a renaming or a generic instantiation, the controlling access parameters must be null excluding. Note that controlling access results are not automatically null excluding. All is not permitted as a general access modifier for anonymous access types. !problem The current proposal for Ada 2005 permits the specification of not null for controlling access parameters even though they have to be not null anyway. Similarly it permits the specification of all in anonymous access types even though they never are pool specific and so all is superfluous. This provides the user with several different ways of saying the same thing which is confusing. However, specifying not null for controlling access parameters provides helpful documentation, and which parameters are controlling might change, or might not be obvious, so this sort of specification should be permitted, despite being redundant. !proposal (See summary.) !wording Modify 3.10(6) [as previously modified by AI-231 and AI-254] to ... access_definition ::= [null exclusion] access [constant] subtype_mark | | [null exclusion] access [protected] procedure parameter_profile | [null exclusion] access [protected] function parameter_and_result_profile Add after 3.9.2(11): If a dispatching operation is defined by a subprogram_renaming_declaration or the instantiation of a generic subprogram, any access parameter of the renamed subprogram or the generic subprogram that corresponds to a controlling access parameter of the dispatching operation, shall be null excluding. AARM Note on incompatibility: This rule will require the addition of an explicit "not null" on non-dispatching operations that are later renamed to be dispatching, or on a generic that is used to define a dispatching operation. !discussion The draft rationale says: For uniformity, Ada 2005 permits all three forms with anonymous access types. And we can also add not null so we can write all the following in Ada 2005: procedure P1(X: access T) procedure P2(X: access constant T); procedure P3(X: access all T); procedure P4(X: not null access T); procedure P5(X: not null access constant T); procedure P6(X: not null access all T); Note that two anomalies remain. One is that access T is deemed to be short for access all T when it occurs in an anonymous access type in order to permit compatibility between Ada 95 and Ada 2005. And so P1 and P3 are the same. To have required all existing users to insert all in their access parameters would have been too much of a burden. Similarly P4 and P6 are the same as well. Moreover, as mentioned above, there is also the anomaly that controlling parameters are still always null excluding and so in that case P1, P3, P4, and P6 are all the same. It is very confusing to have unnecessary ways of saying the same thing. We are stuck with access T as a parameter as being general because of backwards compatibility with Ada 95. It is therefore proposed that anonymous access types should just take the form AV: access T; AC: access constant T; and that AV: access all T; should not be permitted. This makes them behave like normal object declarations where we either have constant or nothing. The anomaly is with named access types having to distinguish between pool-specific and general access types. There is no reason why this should be extended to anonymous access types. Similarly, controlling access parameters have to be null excluding but for compatibility with Ada 95 we cannot require the programmer to insert not null everywhere. We considered disallowing an explicit not null where they are implicit, but it was anticipated that after a transition period, explicit not null would be used everywhere it applies, and disallowing its use for controlling access parameters would defeat this. For a dispatching operation defined by renaming or instantiation, we require that the renamed subprogram or generic subprogram be null excluding for each access parameter or access result that ends up controlling in the renaming or the instance. For example: generic type GT is private; procedure Gen_Subp_1(Y : access GT); generic type GT is private; procedure Gen_Subp_2(Y : not null access GT); with Gen_Subp_1; with Gen_Subp_2; package P is type T is tagged .... package Inner is procedure Not_Disp_1(X : access T); procedure Not_Disp_2(X : not null access T); end Inner; procedure Ren1(X : access T) renames Not_Disp_1; -- illegal procedure Ren2(X : access T) renames Not_Disp_2; -- legal procedure Inst1 is new Gen_Subp_1(T); -- illegal procedure Inst2 is new Gen_Subp_2(T); -- legal end P; Note that we considered imposing a similar implicit null exclusion for controlling access results, but chose not to do that, because there is no Ada95 compatibility issue, and there is no automatic null check inherent in the use of a controlling access result. If a null check is necessary, it is because there is a dereference of the result. If there is no dereference of the result, a null return value is perfectly acceptable, and can be a useful indication of a particular status of the call. !example (See discussion.) !corrigendum 3.9.2(11) @dinsa The @fa for a controlling formal parameter of a dispatching operation shall be tag indeterminate. A controlling formal parameter that is an access parameter shall not have a @fa. @dinst If a dispatching operation is defined by a @fa or the instantiation of a generic subprogram, any access parameter of the renamed subprogram or the generic subprogram that corresponds to a controlling access parameter of the dispatching operation, shall be null excluding. !corrigendum 3.10(6) @drepl @xcode<@fa@ft<@b>@fa< subtype_mark>> @dby @xcode<@fa@ft<@b> @fa@ft<@b>@fa< [>@ft<@b>@fa<] subtype_mark | [null_exclusion] >@ft<@b>@fa< [>@ft<@b>@fa<] >@ft<@b>@fa< parameter_profile | [null_exclusion] >@ft<@b>@fa< [>@ft<@b>@fa<] >@ft<@b>@fa< parameter_and_result_profile>> !ACATS test Create B-Tests to check that the renames and instance rule is checked. Create a B-Test to insure that "all" is not allowed in anonymous access types. !appendix From: Tucker Taft Sent: Friday, February 18, 2005 6:18 AM The other "morning after" issue that I remember has to do with access results. Erhard made the point that by associating non-nullness with controlling access results, we will make it that much harder to use them as a replacement for named access types. Although I argued against his point in the meeting, afterward I realized there really isn't much justification for requiring that controlling access results be non-null. There are several ways in which controlling access results are different from controlling access parameters: 1) We don't have any "legacy"/"compatibility" to worry about here. Access results didn't exist in Ada 95, so we don't have to inherit their rules. We can easily say that with controlling access results, you get what you ask for, either null-excluding or null-allowing, whichever is more appropriate. 2) With controlling access parameters, there is a necessary dereference on every dispatching call to find out which body to execute, or in the case of multiple parameters, to make sure they all agree on which body. With controlling access results, there is no such requirement, except when the controlling access result is passed to a null-excluding parameter or assigned to a null-excluding target, and then the check is really associated with the target, not the result. 3) Given "function Empty_Acc return access Set:" and "X : access Set'Class;" I had leapt to the conclusion that "X := Empty_Acc;" was dispatching on result. But that's wrong. We are doing an access value assignment, and there is certainly no requirement that X be non-null to begin with. A dispatching on result would only occur with: "X.all := Empty_Acc.all;" in which case we have the ".all" on Empty_Acc which clearly inserts the appropriate null check, so again, there is no need to associate it with Empty_Acc itself. 4) Unlike with normal results, with access results, "null" is a well-defined value that could be returned from a primitive function. For example, consider the C++ streaming approach, where the streaming "get" returns the input stream: function Get(S : access Stream; Val : access Integer) return access Stream; function Get(S : access Stream; Val : access Float) return access Stream; We could also have other operations that produce streams or add characters into a stream for later "Get"s, which would look more like constructors: function Open(Name : String) return access Stream; function Init_Contents(Contents : String) return access Stream; function Unget_String(S : access Stream; Put_Back : String) return access Stream; -- "Put_Back" inserted -- back into stream for Get -- to re-read A "null" value could be used as an indication of an empty stream. One could then write: Str : access Stream := Open("My_File"); A, B : aliased Integer; C : aliased Float; begin while Str /= null loop Str := Str.Get(A'Access).Get(B'Access).Get(C'Access); -- Do something with A, B, C end loop; Of course some people don't like using "null" as an indicator, but it is pretty common (certainly when iterating over a linked list, for example). But in any case, it seems a conceivable thing to do, and there seems no reason why we need to disallow it or make it more difficult than necessary. ---------------------- So for all the above reasons, and perhaps more, I would suggest we drop the connection between non-nullness and controlling access results, and adopt a "what you see is what you get" model for them. I believe it might have been an overactive attempt to unify, otherwise known as "confuse," controlling access parameters and controlling access results, which led us to believe they were necessarily non-null. In other words, I think Erhard had the right instinct here... ************************************************************* From: Pascal Leroy Sent: Tuesday, February 22, 2005 4:11 AM I agree with your arguments: there doesn't seem to be any good reason to force access results to be null-excluding. *************************************************************