Version 1.8 of ais/ai-00404.txt

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

!standard 3.10(6)          05-08-15 AI95-00404/06
!standard 3.9.2(11)
!class amendment 05-01-28
!status Amendment 200Y 05-03-14
!status WG9 Approved 06-06-09
!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 have a subtype that excludes null.
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 Inner.Not_Disp_1; -- illegal procedure Ren2(X : access T) renames Inner.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)
Insert after the paragraph:
The default_expression 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 default_expression.
the new paragraph:
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 have a subtype that excludes null.
!corrigendum 3.10(6)
Replace the paragraph:
access_definition ::= access subtype_mark
by:
null_exclusion ::= not null access_definition ::= [null_exclusion] access [constant] subtype_mark | [null_exclusion] access [protected] procedure parameter_profile | [null_exclusion] access [protected] function 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.

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

!topic Bad example in AI-404
!reference AI95-00404
!from Adam Beneschan 05-04-25
!discussion


The example in AI-404 to demonstrate the new rule about dispatching
operations created by renaming and generic instantiations has a
problem.


   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;


Besides the obvious problem that Not_Disp_1 and Not_Disp_2 are not
visible at the point of the renaming declarations, the more subtle
problem is that the generic instantiations cannot be legal, because
the occurrence of the instantiation causes T to be frozen (13.14(11)),
and thus no new dispatching operations on T could be defined by the
instantiation, with or without the new rule.  (Or have I read the
rules wrong?)

This is really a minor nitpick, since this example hasn't found its
way into the Ada 2006 RM.

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


Questions? Ask the ACAA Technical Agent