Version 1.4 of ai05s/ai05-0138-1.txt

Unformatted version of ai05s/ai05-0138-1.txt version 1.4
Other versions for file ai05s/ai05-0138-1.txt

!standard 3.10.2          09-02-13 AI05-0138-1/00
!class Amendment 09-02-13
!status work item 09-02-13
!status received 08-11-14
!priority Medium
!difficulty Hard
!subject Improving accessibility
!summary
A quote from Ed Schonberg: "Accessibility migraine setting in." Seems right to me. - RLB
!problem
A variety of issues have been noted with anonymous access types specifically and with accessibility in general. (See the !appendix for now for descriptions of some of the problems.)
!proposal
Five ideas stand out in this mass of mail:
(1) Some sort of accessibility test that a programmer can write. (An "accessibility membership" was suggested.) Presumably, this could be used to enhance the contract of a subprogram. [This is now AI05-0149-1.]
(2) Some modifications to the accessibility of stand-alone anonymous access objects. Tucker had suggested some sort of dynamic accessibility for them. (Careful, though, Randy tried to design a general dynamic accessibility check and determined that it is not implementable without serious distributed overhead. So this would have to be limited in the accessibility that could possibly be saved in it - it would be some sort of hybrid approach.) [This is now AI05-0148-1.]
(3) Some mechanism (a restriction pragma??) to ensure that no allocators of anonymous access types are used in a unit. (Anonymous access types can't have a specified storage pool, so for many applications allocation needs to be done with a named access type. If that model is used, an allocator for an anonymous access type is a bug.) Preferably, the mechanism can be turned on and off for different subsystems (which may have different requirements).
(4) Adding a new accessibility "half-level" for the accessibility of subprogram parameters and potentially results. (This would allow safer accessor functions, and might be able to eliminate most dynamic overhead for anonymous access parameters.)
(5) A mechanism for specifying the intended accessibility for an access type. This would re-enforce the fact that the accessibility of an access type is a critical piece of information, and that a type that has the same designated type isn't necessarily the same. This also can get rid of the dynamic overhead for anonymous access types, and strengthen the contract of subprograms by including accessibility as part of the contract of anonymous access types. (These currently are a ticking time-bomb if the parameter is converted to another non-local access type.)
The expectation is that there will be separate AIs for each idea that we we decide to progress; but we need some preliminary discussion to decide what to work on, and there had to be a mail file. Thus this AI.
!wording
** TBD **
!discussion
** TBD **
!examples
!ACATS test
!appendix

========= New thread: contains idea (2) toward the bottom =====
 (dynamic accessibility for stand-alone objects)
****************************************************************

From: Franco Gasperoni
Date: Monday, November 10, 2008  1:13 PM

On behalf of AdaCore we would like to submit the attached proposal to
complement Ada's access-to-object types in Ada 2010.

Thank you in advance for your feedback

--------------

===========================================================================

     A Proposal to Complement Ada's Access-to-Object Types in Ada 2010

     by AdaCore
     Reference: 2008-Nov-11

Contents
===========================================================================
1. Summary

2. OOP in Ada

3. OOP and Access Types in Ada 2005
   3.1 Pool-specific access types
   3.2 General access types
   3.3 Anonymous access types

4. Intrinsic Limitations of Anonymous Access
   4.1 Cannot instantiate Unchecked_Deallocation
   4.2 Cannot specify a storage pool for anonymous access types
   4.3 Cannot specify a storage size for anonymous access types
   4.4 Cannot have "out" or "in out" anonymous access parameters
   4.5 An entry_declaration in a task cannot have access parameters
   4.6 Cannot mix local and global anonymous access types
   4.7 Equality does not always work

5. Complementing Ada 2005 Access Types
   5.1 'Ref and 'Ref_Const types
   5.2 Chaining 'Ref[_Const]
   5.3 Accessibility level of 'Ref[_Const] types
   5.4 'Ref[_Const] and primitive operations
   5.5 'Ref[_Const] types and "not null"
   5.6 Storage pools
   5.7 T'Free
   5.8 Subtyping 'Ref[_Const] types
   5.9 Limited with and 'Ref[_Const] types
   5.10 'Ref[_Const] and "in out" and "out" parameters
   5.11 'Ref[_Const] and entry_declaration
   5.12 'Ref[_Const] and streams
   5.13 'Ref[_Const] discriminants
   5.14 'Ref[_Const] and generic formal parameter matching
   5.15 Explicit Conversions
   5.16 Implicit conversions

6. The 'Level attribute

7. Suggested Ada Compiler Approach
   7.1 Implementing 'Ref[_Const], 'Level, and 'Free
   7.2 Restricting the use of local anonymous allocators
===========================================================================



1. Summary
---------------------------------------------------------------------------

The objective of this document is to provide a convenient and
intuitive access type model to OOP developers that want to use Ada.
After surveying Ada's access types in the context of OOP, we analyze
their limitations and suggest a way to address these limitations in a
fashion that is upwardly compatible with Ada 2005.

The key idea of the proposal is to complement Ada 2005 with a new
attribute 'Ref.

For any type T, T'Ref is a general access type whose designated type
is T. Being a subtype_mark T'Ref is engineered to have the advantages
of general named access types without their limitations when it comes
to primitive operations and upcasting conversions.

This proposal adjoins to 'Ref three further attributes: 'Ref_Const,
'Level, and 'Free as well as the child package
System.Storage_Pools.Default_Pools.

For any type T, T'Ref_Const is an access-to-constant type whose
designated type is T. T'Ref_Const is the alter ego of T'Ref when we
need an access-to-constant type instead of an access-to-variable type.

For any access type A, A'Level returns the accessibility level of
A. For any access object Ptr, Ptr'Level returns the accessibility
level of its access type. The 'Level attribute is needed to safely
convert an access parameter "access T" to a T'Ref.

As a matter of convenience, for any object X of type T'Ref or T'Ref_Const,
T'Free (X) can be used instead of Ada.Unchecked_Deallocation.

Finally, if no Storage_Pool or Storage_Size clause is specified for a
T'Ref[_Const] type, then the same Default_Pool defined in
System.Storage_Pools.Default_Pools, is used.  Thus if we don't specify
pools we can do "new" with one type, convert to another type, and
finally deallocate the object without causing erroneous execution.

Our objectives, in this proposal, are as follows:

   1. 'Ref[_Const] types should make OOP by access simple and
      intuitive in Ada.

   2. It should be possible and convenient to write Ada programs using
      only 'Ref[_Const] as access types.

   3. It should be possible to mix 'Ref[_Const] types with named and
      anonymous access types for upward compatibility.

   4. Conversions among 'Ref[_Const] types that are "safe" should be
      implicit.

   5. Conversions among 'Ref[_Const] types that may raise exceptions
      or may lead to an erroneous execution should be explicit (or
      disallowed altogether).

   6. The design should be upwardly compatible with Ada 2005.

For brevity in the remainder of this document "access type" stands
for "access-to-object type".


2. OOP in Ada
---------------------------------------------------------------------------

Most languages that provide object-oriented programming (OOP) do so by
reference, either implicitly as in Java and Smalltalk, or explicitly
as in C++.

Ada provides two models to the OOP developer: OOP-by-object and
OOP-by-access-to-object. In the OOP-by-object model the developer writes

   type T is tagged ...;
   procedure P (This : T);   --  primitive operation

P is a method (a primitive operation in Ada's jargon) of type T.
In this model upcasting conversions are implicit and we can write

   type D is new T with ....;

   D_Obj     : D;
   Any_T_Obj : T'Class := D_Obj; --  implicit upcasting conversion

In the OOP-by-object model primitive operations can manipulate and
return objects. However, even though parameter "This" in operation P
above is passed by reference, an explicit pointer to "This" cannot be
safely obtained inside P. The reason for this is exemplified below:

   procedure Do_Something is
      Local_Obj : T;
   begin
      P (Local_Obj); --  or Local_Obj.P

if inside P we were allowed to save a pointer to "This" as shown below

   procedure P (This : T) is
   begin
      Save_Into_Global_Data_Structure (This'Access);
      --  This'Access is illegal here

we would have a dangling pointer saved in the global data structure
once Do_Something returns.

As a result operations such as P that rely on the OOP-by-object model
of Ada cannot save a pointer to the object in global data structures
such as linked lists.  In order to achieve this we need OOP-by-access-to-object
and use an explicit access type as the parameter of a primitive
operation of T. This is discussed below.


3. OOP and Access Types in Ada 2005
---------------------------------------------------------------------------

Ada 2005 has several access types:

   * pool-specific access types
   * general access types
     - named access types
     - anonymous access types

The purpose and history of each of these access types in the context
of OOP is described below.


3.1 Pool-specific access types
    --------------------------

Ada 83 had a single kind of access type, called pool-specific access
types in Ada 2005. A pool-specific access type can designate only
objects created dynamically by means of an allocator ("new T").

In Ada's jargon these objects are allocated from a storage pool (a
heap), which is potentially user-defined.

Pool-specific access types take the following form in Ada 2005:

   type T is tagged ...
   type Pool_Specific_Access_T is [not null] access T;

where [not null] is optional and specifies whether or not the access
type contains the null pointer.

Pool-specific access types are not very interesting from an OOP
perspective because:

   procedure Q (This_Ptr : Pool_Specific_Access_T);

cannot be a primitive operation (i.e. a method) of T. Furthermore,
given:

   type B is tagged ...;
   type PSA_Any_B is access B'Class;

   type D is new B with ...;
   type PSA_Any_D is access D'Class;

we cannot write

   P_D : PSA_Any_D := new D;
   P_B : PSA_Any_B := PSA_Any_B (P_D);  --  ILLEGAL


3.2 General access types
    --------------------

Ada 95 has extended Ada 83 pool-specific access types with general
access types which have the double ability to point to dynamically
allocated objects as well as aliased objects created (on the stack) by
declarations. General access types take the following form in Ada 2005:

   type T is tagged ...
   type General_Access_T       is [not null] access all      T;
   type General_Access_Const_T is [not null] access constant T;

and can be used as follows:

   Obj : aliased T;
   Acc : General_Access_T := new T;
   Ptr : General_Access_T := Obj'Access;
   Ptr_Const : General_Access_Const_T := Obj'Access;

The difference between General_Access_T and General_Access_Const_T is
that pointers of the latter type cannot change the content of the
pointed object, they can just reference it.

General access types are not very interesting from an OOP perspective
because:

   procedure Q (This_Ptr : General_Access_T);

cannot be a primitive operation (i.e. a method) of T. Furthermore,
given:

   type B is tagged ...;
   type GA_Any_B is access all B'Class;

   type D is new B with ...;
   type GA_Any_D is access all D'Class;

we can write

   P_D : GA_Any_D := new D;
   P_B : GA_Any_B := GA_Any_B (P_D);

but cannot write

   P_B : GA_Any_B := P_D;  --  ILLEGAL, explicit conversion needed
                           --           when doing an upcast


3.3 Anonymous access types
    ----------------------

To make OOP-by-access-to-object possible, Ada 95 has complemented general access
types with access parameters and we can write:

    type T is tagged ....;
    procedure P (This : T);
    procedure Q (This_Ptr : access T);

Both P and Q are primitive operations (methods) of T and in both cases the
actual object of type T is passed by reference.  While in the case of P we
cannot obtain a reference to the object (except via 'Unchecked_Access), in the
case of Q the pointer to the object (This_Ptr) is made explicit and can be
saved, for instance, in a linked list.

This notion of access parameters has been extended to access
discriminants and in Ada 95 we can write

   type T (D : access Some_Type) is ....

These access parameters and access discriminants have been regrouped in
Ada 95 under the notion of anonymous access types. Anonymous access types
are general access types whose (first sub)type does not have a name.

The notion of anonymous access types has being extended in Ada 2005
and now, in addition to access parameters and access discriminants, we
can have access components, access objects, generic formal access
objects, and functions can return an access result, as shown below:

   type Cell is tagged record
      Next : access Cell'Class;
   end record;

   Obj : aliased Cell;
   Ptr : access Cell'Class := Obj'Access;

   generic
      Object : in out access Cell;
   procedure Proc;

   function Create return access Cell'Class;

An anonymous access type to T is a placeholder for ALL access types to
T. As a result, such anonymous access types can leverage only on (a)
being an access type and (b) the common properties of access types to
T, namely that the designated type is T.

As a result we can implicitly convert any access type to T to an
anonymous access type to T. For instance, we can write

   type T is ...
   type PSA_T is access T;
   type GA_T is access all T;

   PSA_Ptr : PSA_T := ...;
   GA_Ptr  : GA_T := ...;

   Ptr1 : access T := PSA_Ptr;
   Ptr2 : access T := GA_Ptr;

moreover, from an OOP perspective, we can write

   type B is tagged ...;
   type D is new B with ...;

   D_Ptr     : access D := ...;
   Any_B_Ptr : access B'Class := D_Ptr;  --  upcast, implicit conversion

i.e. we can upcast anonymous access types without the need for
explicit conversions. This is particularly appealing. For instance we
can write:

   type Cell is tagged record
      Next : access Cell'Class;
   end record;
   type List is access all Cell'Class;

   function Length (H : access Cell'Class) return Natural;

   type DCell is new Cell with record
      ...
   end record;
   type DList is access all DCell'Class;

   List_1 : List  := ...;
   List_2 : DList := ...;

   L1 : Natural := Length (List_1);  --  or  List_1.Length
   L2 : Natural := Length (List_2);  --  or  List_2.Length

For someone coming from Java or C++ anonymous access types are also
the natural way to write something like

   type Cell is tagged record
      ...
      Next : access Cell'Class;
   end record;

Furthermore, when we have circularities that must be tackled with a
"limited with", anonymous access types are very convenient:

   limited with B;
   package A is
      type A_Type is tagged record
         ...
         CB : access B.B_Type'Class;
      end record;
   end A;

   limited with A;
   package B is
      type B_Type is tagged record
         ...
         CA : access A.A_Type'Class;
      end record;
   end B;

Another important use of anonymous access types is in the context of
access discriminants which allow a general form of multiple
inheritance to be simulated in Ada.

A further interesting use of access parameters is to modify the
contents of an object when passed to a function. Consider

   type T is ...
   function F (X : T) return ....;
   function G (X_Ptr : access T) return ....;

While function F cannot modify X in its body, function G can modify
the object of type T via X_Ptr.

Beyond OOP, anonymous access-to-object types are useful when
interfacing to C, where

   procedure C_Proc (X : access T);
   pragma Import (C, C_Proc, "name_of_c_proc")

allows us to import the following C routine into Ada conveniently:

   void name_of_c_proc (T *x);

and conversely

   procedure Ada_Proc (X : access T);
   pragma Export (C, Ada_Proc, "name_of_ada_proc")

allows us to export Ada_Proc in C equally conveniently:

   extern void name_of_ada_proc (T *x);


4. Intrinsic Limitations of Anonymous Access
---------------------------------------------------------------------------

Anonymous access types have not been designed to replace named general
access types but rather to supplement them.  As a result, by design,
certain "things" that can be done with general named access types
cannot be done with anonymous access types.  These "things" are
described below.


4.1 Cannot instantiate Unchecked_Deallocation
    -----------------------------------------

Because the spec of Ada.Unchecked_Deallocation reads as

   generic
      type Object (<>) is limited private;
      type Name is access Object;
   procedure Ada.Unchecked_Deallocation (X : in out Name);

there is no way to Unchecked_Deallocate an object pointed by an anonymous
access.  This is a useful safeguard since the storage provenance of
the object we want to deallocate is unknown (e.g. it could come from
outside Ada). This may be frustrating in programs where there are no
aliased objects and all objects are allocated from the default storage
pool.


4.2 Cannot specify a storage pool for anonymous access types
    --------------------------------------------------------

In Ada 2005 we cannot write:

   type T is tagged ...;
   for (access T)'Storage_Pool use ...; -- Illegal syntax!

This is not just a matter of syntax, it is fairly fundamental to the
model of anonymous allocators which, depending on the context,
allocate storage in the right place (e.g. coextensions).


4.3 Cannot specify a storage size for anonymous access types
    --------------------------------------------------------

Given a named access type NAT, in Ada 2005 we can write

   for NAT'Storage_Size use 0;

This means that the use of allocators for NAT is forbidden.
In Ada 2005 we cannot write:

   type T is tagged ...;
   for (access T)'Storage_Size use 0; -- Illegal syntax!


4.4 Cannot have "out" or "in out" anonymous access parameters
    ---------------------------------------------------------

In Ada 2005 we cannot write:

   procedure P (X : in out access T);


4.5 An entry_declaration in a task cannot have access parameters
    ------------------------------------------------------------

In Ada 2005 we cannot write:

   task TT is
      entry E (Z : access Integer); -- Illegal!
   end TT;

See AARM05 9.5.2 (13).


4.6 Cannot mix local and global anonymous access types
    --------------------------------------------------

RM05 3.10(17/2) states that

  The elaboration of an access_definition creates an anonymous access type.

where for the purpose of this discussion

  access_definition ::= [null_exclusion] access [constant] subtype_mark

This means that the anonymous access type (and its first and unique
subtype) is created at the point of the access_definition.

The above rule, combined with the accessibility rules make
it impossible to write an iterative Reverse_Links routine using
only anonymous access types, as shown in the following example.

   package Lists is
      type Cell is tagged record
         Next : access Cell'Class;
         Prev : access Cell'Class;
      end record;

      procedure Reverse_Links (Start : not null access Cell'Class);
      --
      --  Reverses the list of cells pointed by Start. As an example
      --  given
      --
      --       .-----.  .-----.  .-----.  .-----.  .-----.  .-----.
      --       |    ===>|    ===>|    ===>|    ===>|    ===>|    ===>null
      --       |  A  |  |  B  |  |  C  |  |  D  |  |  E  |  |  F  |
      -- null<===    |<===    |<===    |<===    |<===    |<===    |
      --       .-----.  .-----.  .-----.  .-----.  .-----.  .-----.
      --
      --  Calling Reverse_Links on Start pointing to cell "C" yields
      --
      --       .-----.  .-----.  .-----.  .-----.  .-----.  .-----.
      --       |    ===>|    ===>|    ===>|    ===>|    ===>|    ===>null
      --       |  A  |  |  B  |  |  C  |  |  F  |  |  E  |  |  D  |
      -- null<===    |<===    |<===    |<===    |<===    |<===    |
      --       .-----.  .-----.  .-----.  .-----.  .-----.  .-----.
      --
   end Lists;

   package body Lists is
      procedure Reverse_Links (Start : not null access Cell'Class) is
         Current    : access Cell'Class := Start.Next;
         Following  : access Cell'Class;
         Subsequent : access Cell'Class;
      begin
         if Current = null then
            return;
         else
            Following := Current.Next;
            Current.Next := null;
         end if;

         while Following /= null loop
            Subsequent := Following.Next;

            Current.Prev := Following;  --  *ILLEGAL*
            Following.Next := Current;  --  *ILLEGAL*

            Current := Following;
            Following := Subsequent;
         end loop;

         Current.Prev := Start;
         Start.Next := Current;         --  *ILLEGAL*
      end Reverse_Links;
   end Lists;


4.7 Equality does not always work
    -----------------------------

Equality does not always work as expected when using anonymous access types.
In particular:

    declare
        X : access T := ...;
        Y : aliased T := ...;
    begin
        if X = Y'Access then ...

The above call to "=" is ambiguous.


5. Complementing Ada 2005 Access Types
---------------------------------------------------------------------------

Previous sections have shown that the current access types in Ada have
limitations when it comes to OOP. This section proposes a simple model
to complement Ada's access types.

The idea is to create a subtype_mark which has all the advantages of
named access types without their limitations.


5.1 'Ref and 'Ref_Const types
    -------------------------

Given a (sub)type T (which can be a generic formal type):

T'Ref is a general access-to-object type whose designated subtype is T.
      T'Ref becomes visible at the same point where T becomes visible.
      T'Ref behaves as a general named access-to-object type except
      for certain implicit upcasting conversions and the fact that
      subprograms with a parameter of type T'Ref qualify as primitive
      operations of T when they are declared in the same list of
      declarations as T.

T'Ref_Const is a general access-to-constant type whose designated type
      is "constant T", with the rest being the same as for T'Ref.

The above access types are referred to as 'Ref[_Const] types in this
document. Here are some valid uses of 'Ref[_Const] types:

   type T is record
      X : T'Ref;  --  OK
   end record;

   type T (X : T'Ref) is limited null record; --  OK

   type T is limited record
      X : T'Ref := T'Unchecked_Access;  --  OK
   end record;

   generic
      type T is private;
   function G_Copy (Obj : T) return not null T'Ref;

   function G_Copy (Obj : T) return not null T'Ref is
      Ptr : constant T'Ref := new T'(Obj);
   begin
      return Ptr;
   end G_Copy;

When T is a tagged type:

T'Class'Ref is a general access-to-object type whose designated type is
      T'Class.

T'Class'Ref_Const is a general access-to-constant type whose designated
      type is "constant T'Class".

As an example consider:

   type Cell is tagged record
      Next : Cell'Class'Ref;
   end record;

The following sections describe and clarify the rules concerning
'Ref[_Const] types.


5.2 Chaining 'Ref[_Const]
    ---------------------

This proposal allows the chaining of 'Ref[_Const] attributes. Thus we
can, for instance, write T'Ref'Ref. This is consistent with the goal
that programmers can use only 'Ref[_Const] for access types.

Taking the GNAT implementation model as an example, it is felt that
'Ref[_Const] chaining does not add significant implementation
complexity to the model.

Finally, note that if we write

   R : Integer'Ref'Ref_Const;

then

   R := new Integer'Ref'(new Integer'(1));  --  OK
   R := new Integer'Ref'(new Integer'(2));  --  OK
   R.all.all := 3;                          --  OK
   R.all     := new Integer'(4);            --  ILLEGAL 'Ref_Const


5.3 Accessibility level of 'Ref[_Const] types
    -----------------------------------------

The static accessibility level of T'Ref[_Const] and
T'Class'Ref[_Const] (when defined) is the same as that of T. This
means that, unlike anonymous access types, we can safely mix local and
global objects of type 'Ref[_Const] (normal accessibility rules
apply).

This means, for instance, that we can now write an iterative version of
the Reverse_Links routine conveniently:

      procedure Reverse_Links (Start : not null Cell'Class'Ref) is
         Current    : Cell'Class'Ref := Start.Next;
         Following  : Cell'Class'Ref;
         Subsequent : Cell'Class'Ref;
      begin
         --  same body as in section 4.1 except
         --  that here there are no illegalities.
         ...
      end Reverse_Links;


5.4 'Ref[_Const] and primitive operations
    -------------------------------------

Given

   type T is tagged ...;
   procedure A (X : T'Ref);
   procedure B (Y : T'Ref_Const);
   procedure C (X : in out T'Ref);
   procedure D (Y : in out T'Ref_Const);
   procedure E (X : out T'Ref);
   procedure F (Y : out T'Ref_Const);

A through F are primitive operations of T.  This is unlike other named
access types. Because the accessibility level of T'Ref[_Const] is that
of T there is no need to pass to P a hidden accessibility parameter
for X and Y.

When we have:

   Ptr : T'Class'Ref;
   ...
   A (Ptr);  --  or Ptr.A

a check is made to ensure that the controlling operand, Ptr, is not null.
This check happens only in the case of controlling operands. In the
case:

   procedure R (Z : Integer'Ref);

R (null) is legal and does not raise an exception. In this respect
'Ref[_Const] parameters behave as access parameters.

It is possible to have same-named primitives that differ only with respect to
T'Ref, T'Ref_Const, and anonymous access:

  type T is tagged ...
  procedure P (X : access T);    --  primitive operation
  procedure P (X : T'Ref);       --  primitive operation
  procedure P (X : T'Ref_Const); --  primitive operation

Calls to P will tend to be ambiguous, of course.  When the expected type is
T'Ref or T'Ref_Const, the actual can be of any access type with an appropriate
designated type; the nonexistence of implicit conversion from T'Ref_Const
to T'Ref is not used during overload resolution.

Note that this is different from anonymous access [constant] types where
the following is illegal in Ada 2005:

  type T is tagged ...
  procedure P (X : access T);
  procedure P (X : access constant T); -- Illegal!

because the two P's are type conformant, but not subtype conformant.
However, the following is legal in Ada 2005:

  type T is tagged ...
  type T_Ref is access all T;
  type T_Ref_Constant is access constant T;
  procedure P (X : T_Ref);
  procedure P (X : T_Ref_Constant);

This is more similar to the T'Ref[_Const] case; T'Ref and T'Ref_Const are two
distinct types, mostly similar to named types.  We considered making the rules
more similar to the anonymous access case, but that would require complicated
special-case rules.

Similarly, if we have a primitive that takes T1'Ref, and "T2 is new T1
with...", a same-named primitive of T2 with T2'Ref_Const does not override --
it is a new operation.  As always, it is wise to use the "overriding" keyword
to say what you mean!

Note that in the case of 'in out' and 'out' controlling parameters of type
T'Ref[_Const], the Tag that controls the dispatch is that of the actual object
before the call.  The procedure might update the formal to designate an object
with some other Tag.


5.5 'Ref[_Const] types and "not null"
    ---------------------------------

It is possible to qualify a 'Ref[_Const] type as being "not null" as
follows:

   Ptr : not null T'Ref;
or
   procedure P (X : not null T'Ref);
or
   subtype Not_Null_T_Ref is not null T'Ref;


5.6 Storage pools
    -------------

Given that 'Ref[_Const] types are subtype marks it is perfectly
possible at the syntactic level to associate a storage pool with them
and our proposal allows this.

   for T'Ref'Storage_Pool use ...;       --  OK
   for T'Class'Ref'Storage_Pool use ...; --  OK when T is tagged

'Ref[_Const] types behave as any general named access type in this
respect.

It is possible to have a Storage_Size clause for 'Ref[_Const] types.
Again 'Ref[_Const] types behave as any general named access type in
this respect.

Note: These Storage_Pool and Storage_Size clauses must occur in the same
package or wherever T is declared, because the T'Ref types are declared
at the same place as T.

The following predefined package exists:

package System.Storage_Pools.Default_Pools is

   type Default_Pool_Type is <implementation defined>;

   Default_Pool : Default_Pool_Type;

private
   ... implementation defined
end System.Storage_Pools.Default_Pools;

If no Storage_Pool or Storage_Size clause is specified for a 'Ref[_Const]
type, then the Default_Pool is used.  The same applies to a named
access-to-variable type.  (Named access-to-constant types and anonymous types
are different -- their pools are as specified in the RM.)

As always, the "as if" rule applies:  There is of course no requirement that
the generated machine code actually call Allocate(Default_Pool, ...).  The
compiler can have hardwired knowledge of what this pool does, and generate the
appropriate code inline.

The point of this requirement is that if you don't specify pools, you can
do "new" with one type, convert to another type, and Unchecked_Deallocate
on that without causing erroneous execution.

Note: In the GNAT implementation, we will probably want to make the things in
System.Pool_Global be renamings of the things in
System.Storage_Pools.Default_Pools.


5.7 T'Free
    ------

For every type T, the attribute T'Free denotes a procedure:

    procedure T'Free (X : in out T'Ref_Const);

which behaves as Unchecked_Deallocation: finalizes X.all, frees the storage by
calling Deallocate on T'Ref'Storage_Pool, and sets X to null.

This presumes we allow implicit conversion from T'Ref to T'Ref_Const,
in which case we can't have another T'Free that takes T'Ref, because
calls would be ambiguous.

Note: X.all is a constant view, but it (or parts of it) end up being passed to
Finalize as an 'in out' parameter.  This is a little strange, but it's no
different from normal constant declarations, which also get finalized.
Constants in Ada can be modified during finalization -- nothing new here!

We considered calling 'Free 'Unchecked_Free because then you can
search for "Unchecked" to find all potentially-erroneous deallocations.
Because of the preceding rules on the use of the default allocators
coming from the same default storage pool, 'Free is safer than plain
Unchecked_Allocation as currently defined. Furthermore, searching for
'Free is just as easy as searching for Unchecked_Free.


5.8 Subtyping 'Ref[_Const] types
    ----------------------------

For convenience we can give a name to 'Ref[_Const] types as shown
below:

   type T is tagged record ...;
   subtype Any_T is T'Class'Ref;

Again 'Ref[_Const] types behave as any general named access type in
this respect.


5.9 Limited with and 'Ref[_Const] types
    -----------------------------------

'Ref[_Const] types can be used to replace anonymous access types in
the presence of "limited with" clauses, as illustrated in the
following example:

   limited with B;
   package A is
      type A_Type is tagged record
         CB : B.B_Type'Ref;
      end record;
   end A;

   limited with A;
   package B is
      type B_Type is tagged record
         CA : A.A_Type'Ref;
      end record;
   end B;

In RM terms, the limited view of a package includes a full view of each
'Ref[_Const] type declared in that package.  (If the 'Ref views were limited
views, then the above example would be illegal.)


5.10 'Ref[_Const] and "in out" and "out" parameters
     ----------------------------------------------

A procedure can have a 'Ref[_Const] "out" or "in out" parameter as is
the case for general named access types.


5.11 'Ref[_Const] and entry_declaration
     ----------------------------------

An entry_declaration in a task can have 'Ref[_Const] parameters as is
the case for general named access type. This is because their
accessibility level is static.


5.12 'Ref[_Const] and streams
     ------------------------

We can stream 'Ref[_Const] types as any named general access type.


5.13 'Ref[_Const] discriminants
     --------------------------

Used as discriminants 'Ref[_Const] types follow the same rules as any
named general access type.


5.14 'Ref[_Const] and generic formal parameter matching
     --------------------------------------------------

In terms of formal parameter matching 'Ref[_Const] types can match any
generic formal that can be matched by a named general access type.

A generic formal procedure with an anonymous access parameter cannot match an
actual with a T'Ref parameter, nor vice-versa.  This restriction is necessary
because the internal calling conventions will be different -- access parameters
carry an implicit accessibility level, whereas T'Ref parameters do not.


5.15 Explicit Conversions
     --------------------

When converting from named, anonymous, or 'Ref[_Const] access types to
'Ref[_Const] types and conversely, 'Ref[_Const] types are regarded as
a general named access type and the type conversion rules of RM05 4.6
are followed. For instance:

   type T is ...;
   type PSA_T is access     T;
   type GA_T  is access all T;

   PSA_Ptr : PSA_T;
   GA_Ptr  : GA_T;
   A       : access T;
   R       : T'Ref;

   GA_Ptr := GA_T (R);    -- OK explicit conversion needed
   A      := R;           -- OK implicit conversion

   R := T'Ref (PSA_Ptr);  -- OK explicit conversion needed
   R := T'Ref (GA_Ptr);   -- OK explicit conversion needed
   R := T'Ref (A);        -- OK explicit conversion needed

and

   procedure Q (X : access T) is
      AL : access T;
      RL : T'Ref;
   begin
      AL := RL;          -- OK implicit conversion
      RL := T'Ref (AL);  -- ILLEGAL, wrong accessibility level
      RL := T'Ref (X);   -- OK, dynamic accessibility check
      ...
   end Q;

Some examples of explicit conversions between 'Ref[_Const] types are

   type B is tagged ...;
   type D is new B with ...;

   BR : B'Ref;
   DR : D'Ref;

   BCR : B'Class'Ref;
   DCR : D'Class'Ref;

   BR := B'Ref (DR);
   BR := B'Ref (DCR)

   DCR := D'Class'Ref (BCR);  --  Tag check
   DCR := D'Class'Ref (BR);   --  Tag check

In the presence of chained 'Ref[_Const] we want to allow the following
explicit conversions:

   type B is tagged ...;
   type D is new B with ...;

   BRR : B'Ref'Ref;
   DRR : D'Ref'Ref;

   BRC : B'Ref'Ref_Const;
   DRC : D'Ref'Ref_Const;

   BRR := B'Ref'Ref (DRR);
   BRC := B'Ref'Ref_Const (DRR);
   BRC := B'Ref'Ref_Const (DRC);

To address these we extend the type conversion rules for general
access types (RM05 4.6 (24.11/2 - 24.17/2)) by adding the following
clause between 24.13/2 and 24.14/2:

   If both the operand and the target designated type are 'Ref[_Const]
   access types, then the operand designated type shall be convertible
   to the target designated type;

and changing 24.14/2 from

   If the target designated type is not tagged, then the designated
   types shall be the same, ...

to

   If the target designated type is not tagged, and the operand and
   the target designated are not 'Ref[_Const] access types, then the
   designated types shall be the same, ...


5.16 Implicit conversions
     --------------------

In addition to the above explicit conversion rules we allow implicit
conversions between 'Ref[_Const] types if:

   (1) the corresponding explicit conversion is allowed

and either

   (2.a) the target designated type is a class-wide type covering the
         operand designated type

or

   (2.b) the operand and target types comprise an identical sequence
         of 'Ref[_Const] types and the ultimate operand and target
         designated types satisfy rule (2.b) above.

As an example, consider

   type B is tagged ...;
   type D is new B with ...;

   BR : B'Ref;
   DR : D'Ref;

   BCR : B'Class'Ref;
   DCR : D'Class'Ref;

   BCRR : B'Class'Ref'Ref;
   DRR  : D'Ref'Ref;

   BCR := DR;    --  OK, implicit conversion
   BCR := DCR;   --  OK, implicit conversion
   DCR := DR;    --  OK, implicit conversion

   BCRR := DRR;  --  OK, implicit conversion

Note that when the programmer uses different storage pools for
D'Class'Ref and B'Class'Ref as shown below:

   for B'Class'Ref'Storage_Pool use Blah;
   for D'Class'Ref'Storage_Pool use Doh;

   DCR : D'Class'Ref := new D;
   BCR : B'Class'Ref := DCR;   -- upcasting - implicit conversion

we may deallocate an object with the wrong Deallocate routine as shown
below:

   procedure Free is new Ada.Unchecked_Deallocation (B, B'Ref);

   Free (BCR);
   --  Potential problem if BCR references an object allocated
   --  from a storage pool different from B's.

In the case of explicit conversions there is a subtype_mark to remind
us of this potential problem.  We don't feel the above is an issue
firstly because the vast majority of programs do not use storage
pools, and secondly the above implicit conversion does not add more
unsafety than what currently exists in Ada 95 and Ada 2005 when we
write:

   type T is ...;
   type T_Ref is access all T;

   procedure Free is new Ada.Unchecked_Deallocation (T, T_Ref);

   Global : aliased T;
   Ref : T_Ref := Global'Access;

   Free (Ref);

Finally, it is always possible to emit a warning (a priori) when the
storage pools of two convertible 'Ref[_Const] types differ.

Note that given our implicit conversion rules the following is safe:

   type B is tagged ...;

   procedure Proc is
      type D is new B with ...;

      DCR : D'Class'Ref := new D;
      BCR : B'Class'Ref := DCR;  -- ILLEGAL: wrong accessibility level

Note that an implicit conversion from T'Ref to T'Ref_Const is allowed.
Also from T'Class'Ref to Ancestor_Of_T'Class'Ref_Const.


6. The 'Level attribute
---------------------------------------------------------------------------

To provide a safe bridge from an anonymous access type to a
'Ref[_Const] type we have defined a new attribute 'Level, where for
any access type A:

   A'Level returns a Natural which represents the accessibility
           level of A.

The accessibility level of a library-level access type is defined to
be zero.

For any access object Ptr:

   Ptr'Level returns the accessibility level of Ptr's access type.

As an example consider the following:

   package Pack is
      G : access Integer := new Integer'(0);
      --  G'Level = 0

      procedure P (X : access Integer; Y : Integer'Ref);
   end Pack;

   package body Pack is
      procedure P (X : access Integer; Y : Integer'Ref) is
         --  X'Level = accessibility level of the type of the actual
         --  Y'Level = Integer'Ref'Level = 0

         Y : access Integer := new Integer'(1);
         --  Y'Level = 1
      begin
         null;
      end P;
   end Pack;

   with Pack; use Pack;
   procedure Main is
      W : access Integer := new Integer'(1);
      --  W'Level = 1

      Z : Integer'Ref := new Integer'(0);
      --  Z'Level = 0
   begin
      P (G, Z);
      --  Inside P, X'Level = 0

      P (W, Z);
      --  Inside P, X'Level = 1

      P (new Integer'(2), Z);   --  anonymous allocator
      --  Inside P, X'Level = 2
   end Main;

Why is 'Level useful? Let's assume we have some preexisting code like

       type Int is interface;    -- already written code
       procedure P (X : access Int);

When we write new code and want to use only 'Ref[_Const] types we
proceed as follows:

       type T is new Int with ...;
       overriding
       procedure P (X : access T);  --  wrapper forwards to Real_P

       not overriding
       procedure Real_P (X : T'Ref);

and in P we write:

       procedure P (X : access T) is
       begin
          if X'Level > T'Ref'Level then
              ...
              --  can't hop over to 'Ref
              --  some remedial action needed
          else
             Real_P (T'Class'Ref (X)); -- or T'Class'Ref (X).Real_P
             --  class-wide conversion: dispatches
          end if;
       end P;


7. Suggested Ada Compiler Approach
---------------------------------------------------------------------------

7.1 Implementing 'Ref[_Const], 'Level, and 'Free
    --------------------------------------------

The attributes 'Ref[_Const], 'Level, and 'Free should be
implemented as part of the regular Ada 95 mode of an Ada 95 compiler.
That way these useful attributes are available to all Ada 95
developers without forcing Ada 95 developers to embrace all of Ada 2005.

In terms of warnings, given

  type B is tagged ...
  procedure P (X : access B); --  primitive operation

  type D is new B with ...;
  procedure P (X : T'Ref);    --  primitive operation
  --  Warning emitted here.

the Ada compiler should emit a warning for the second P to warn the
programmer that it does not override the primitive operation of B.

A further warning should be emitted when the storage pools of two
'Ref[_Const] types that can be implicitly converted differ, as
exemplified below:


   type B is tagged ...;
   type D is new B with ...;

   for B'Class'Ref'Storage_Pool use Blah;
   for D'Class'Ref'Storage_Pool use Doh;
   --  Warning emitted here


7.2 Restricting the use of local anonymous allocators
    -------------------------------------------------

A local allocator in Ada 95 and Ada 2005 is something like

   procedure P (X : access T);

   procedure Q is
   begin
      P (new T);  --  Local anonymous allocator
   end Q;

The local anonymous allocator above is implicitly deallocated just
after the call to P.

This is very convenient when we want to allocate temporary
data-structures and storage for a phase of our application.  Local
allocators allow us to allocate storage and be assured that this
storage is automatically reclaimed on our behalf when the phase ends.

However, for inexperienced programmers the fact that local anonymous
allocators and other type of allocators behave differently can be
confusing and error prone. Consider the following example:

   package Named_Lists is
      type Cell;
      type Any_Cell is access all Cell'Class;
      type Cell is tagged record
         Next : Any_Cell;
      end record;
      Head : Any_Cell := new Cell;
      procedure Insert (Head : access Cell'Class;
                        New_Cell : access Cell'Class);
   end Named_Lists;

   package body Named_Lists is
      procedure Insert (Head : access Cell'Class;
                        New_Cell : access Cell'Class)
      is
      begin
         New_Cell.Next := Head.Next;
         Head.Next := Any_Cell (New_Cell);
      end Insert;
   end Named_Lists;

If we write

   with Named_Lists; use Named_Lists;
   procedure Main is
   begin
      Insert (Head, new Cell);
   end Main;

then we have a Program_Error, however if we write

   with Named_Lists; use named_Lists;
   procedure Main is
      N : Any_Cell := new Cell;
   begin
      Insert (Head, N);
   end Main;

or if we write

   with Named_Lists; use named_Lists;
   procedure Main is
   begin
      Insert (Head, Any_Cell'(new Cell));
   end Main;

we don't. This can be confusing and error prone to some users and it
may be important to restrict their use. To address this we propose
the following additional Restrictions pragma:

   No_Anonymous_Allocators: forbids the use of anonymous allocators.

Note that the above restricts all anonymous allocators, not just local
ones. This seems a simpler approach than have two restrictions
pragmas, one to suppress local anonymous allocators, and one to
suppress all anonymous allocators.

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

From: Randy Brukardt
Date: Monday, November 10, 2008  2:45 PM

I did not read this proposal carefully, so you may have answered this issue. We
did seriously consider a 'Ref attribute when we were looking at solutions to the
access type proliferation problem in limited withs. We ended up not using it
because it had several serious technical problems. One that I recall had to do
with the generation of such types in compilers that require the existence of all
types in the symbol table. The proposed attribute potentially required the
generation of an infinite number of such types, which did not work. I believe
that there also were problems with comparisons (perhaps solved by the later
addition of universal access "=", perhaps not). Hopefully, you went back and
studied the old discussions on this idea in order to ensure that you were not
just replowing old ground.

---

<Rant>

The main reason that I didn't read the whole proposal is that it starts "The
objective of this document is to provide a convenient and intuitive access type
model to OOP developers that want to use Ada." That seems like the wrong goal.
In fact it *is* the wrong goal. Ada OOP interfaces should not (visibly) use
access types. Period.

I say that because it is very important to have an appropriate separation of
concerns. That is, an OOP interface should manage (internally) all of the memory
it uses. However, memory management of the top-level objects should be totally
left to the client. The use of access types interferes with these goals, as it
requires adding noise ('Access and aliased) on the declaration and use of
stack-based objects. Stack-based OOP objects should *never* be second-class
citizens, as they are to be preferred to allocated objects whenever possible.

Similarly, the use of access types in an OOP interface makes it much harder to
put those objects into instances of Ada.Containers. Either you put in the access
types (which puts the burden of storage management back on the user and losing
the advantage of having the containers do that management), or you have to wrap
the objects in order to get a aliased component to refer to (along with all of
the other mechanisms needed).

We already provide anonymous access parameters, which is already one too many
ways to write OOP parameters in my view. Semantically, an In Out parameter and
an access parameter of a tagged type are identical (with the exception of the
accessibility checks; and those make the access parameter more expensive than
the In Out parameter). We effectively have 5 semantically identical ways to
write OOP parameters now (In, In Out, Out, access-to-var, access-to-const) [the
first and last being limited to constant views]. The last thing we need is more
ways to write such parameters.

I realize that programmers used to doing OOP in some other languages are used to
using access types for everything. Ada, however, provides better ways to do
those things. Why should we be adding the mistakes of other languages to Ada??

</Rant>

There are two issues that do occur with Ada OOP, and that solutions are needed
for:

(1) In Out parameters are not allowed for functions. Pascal and I finally
extracted some real technical issues from Tucker on this topic. As such, I will
soon be writing proposals to account for those technical issues (which both can
be solved without throwing out the In Out parameter baby). Hopefully, by
addressing the real issues, we can eliminate the opposition to the reasonable
change needed. (It should be noted that allowing In Out parameters on functions
will be a real earthquake in Janus/Ada. Despite that, I'm *still* in favor of
this change, because of the damage it does to the OOP model, along with other
issues.)

(2) Return-by-reference. The problem with returning an access value from an OOP
function is that prevention of dangling pointers is impossible. If the OOP
library does actions asynchronously to the Ada program (this is the case with a
GUI library, for instance, where the human user of the interface can do actions
outside of those that the program is expecting), and management of OOP objects
is left to the client of the interface (highly recommended), there is no way to
prevent or defensively program against a returned access becoming dangling. In
Claw, we mitigated this problem by returning a lock with the access value; the
lock has to be saved locally (else the entire program will freeze) and when it
finalizes it allows actions to resume. Along with that, we have a strong
recommendation not to copy the returned access value. But of course that latter
cannot be enforced by the compiler.

There are a number of ways to address this issue; one would be to provide
anonymous access types that cannot be copied (best done by giving them an
infinite accessibility level, as was done for access-to-subprogram parameters).
Another would be to provide some runtime way of either detecting the dangling
pointers and/or determining the lifetime of the pointers. (I have an unrelated
proposal in the works which could in fact be used to solve this latter problem.)

In any case, I am much more in favor of small, incremental changes to Ada to
mitigate the problems with doing things the "right" way rather than to add more
ways to do the same "wrong" thing.

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

From: Alexander E. Kopilovich
Date: Monday, November 10, 2008  10:16 PM

>5.7 T'Free
>    ------
>
>For every type T, the attribute T'Free denotes a procedure:
>
>    procedure T'Free (X : in out T'Ref_Const);
>
>which behaves as Unchecked_Deallocation: finalizes X.all, frees the
>storage by calling Deallocate on T'Ref'Storage_Pool, and sets X to null.
>...
>We considered calling 'Free 'Unchecked_Free

It looks like you did not consider calling it 'Deallocation or 'Deallocate .
If so then - why?

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

From: John Barnes
Date: Tuesday, November 11, 2008  1:49 AM

I was forewarned that your proposal was on the way.

I now believe that anonymous access types were a huge mistake. The biggest
mistake in Ada we ever made. I had great trouble in integrating them into my
book.  Personally I would never use them.

Does your list address the fact that they cannot be used for playing with
limited with?

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

From: Robert Dewar
Date: Tuesday, November 11, 2008  9:05 AM

I agree that they were a mistake, but labeling them as the biggest mistake does
not make sense when there is a trivial work around .. don't use the silly things
at all :-)

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

From: Sergey I. Rybin
Date: Tuesday, November 11, 2008  10:11 AM

That's true, but in many cases you have to deal with "the silly thing" even if
you do not want to. You have to process it in the compiler, I have to be able to
analyse it in ASIS and ASIS-based tools...  :(

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

From: Robert Dewar
Date: Tuesday, November 11, 2008  10:50 AM

of course yes, but inconvenience to book writers and tool writers is always
secondary, so once again, this misguided feature need not be advertised as a
major shortcoming of Ada 2005, it is just a feature you probably don't want to
use.

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

From: Randy Brukardt
Date: Tuesday, November 11, 2008  12:47 PM

I agree with this; and that is true in Ada 95 as well. But then why do we even
want to consider providing another form of the feature (or enhancing it for that
matter)?

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

From: Tucker Taft
Date: Tuesday, November 11, 2008  11:27 AM

Before putting up a whole new approach, I would like to try to understand what
is the real usability problem with the anonymous access types as currently
proposed for Ada 2005, and see if there is a possible "targeted" fix.  I'm not
convinced that this new proposal actually addresses the usability problems with
the existing Ada 2005 feature.

One fix that came to mind last time I discussed this with Franco was to specify
that the accessibility level of an initialized stand-alone object of an
anonymous access type would be the same as its initial value. Only if
uninitialized (including the case of a deferred constant), or if initialized by
an allocator, would its accessibility level be determined by its point of
declaration.

For stand-alone constants, it seems to make no sense for their accessibility
level to be other than that of their initial value.  Initialized stand-alone
variables seems to be the controversial one, but I think various illustrative
examples might help determine whether this change would improve usability.

I would be interested in what John has to say about the usability problems he
encountered while writing his book.  Ditto for others who have seen problems
with Ada 2005's anonymous access types.

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

From: Pascal Obry
Date: Tuesday, November 11, 2008  11:45 AM

> Before putting up a whole new approach, I would like to try to
> understand what is the real usability problem with the anonymous
> access types as currently proposed for Ada 2005, and see if there is a
> possible "targeted" fix.  I'm not convinced that this new proposal
> actually addresses the usability problems with the existing Ada 2005
> feature.

The most important usability problem I have in mind is that you cannot free
memory. Consider:

   type T is record
      C : access Integer;
   end record;

   V : T := (C => new Integer'(1));

How do you free V.C?

There is no possible way to instantiate Unchecked_Deallocation with an anonymous
type as you have no name for it.

> I would be interested in what John has to say about the usability
> problems he encountered while writing his book.  Ditto for others who
> have seen problems with Ada 2005's anonymous access types.

I'm not John, but not being able to deallocate memory is quite a shortcoming if
you ask me :)

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

From: Ben Brosgol
Date: Tuesday, November 11, 2008  12:39 PM

> I would be interested in what John has to say about the usability
> problems he encountered while writing his book.  Ditto for others who
> have seen problems with Ada 2005's anonymous access types.

My experience is limited to preparing some training material on the subject,
specifically an example of the "limited with" for interdependent package specs
modeling the Event/Listener pattern.  I found anonymous access types useful and
intuitive for this purpose.  The main issue, if it can be called an issue, was
the "viral" nature of "access constant". But this wasn't really a big problem
(and indeed forced a more thorough inspection of what needed to be constant and
what didn't).

Sorry that I can't complain about the feature; I like it, at least in the
limited way that I have used it :-)

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

From: Randy Brukardt
Date: Tuesday, November 11, 2008  1:12 PM

...
> The most important usability problem I have in mind is that you cannot
> free memory. Consider:
>
>    type T is record
>       C : access Integer;
>    end record;
>
>    V : T := (C => new Integer'(1));
>
> How do you free V.C?

You have to convert it to a named type. But the bug here is that you can write
the allocator at all. Since they don't have access to storage pools or any other
sort of management, anonymous access types shouldn't allow allocation at all.

I know the original Ada 95 idea was that such allocators would be managed by the
compiler into the smallest possible scope (for access parameters, the idea was
that they are stack allocated). This led to the abomination of coextensions, but
it also means that allowing explicit deallocators would make things much worse.

Shouldn't we be trying to move away from Unchecked_Deallocation anyway??

In any case, the best solution is that if it hurts, don't do it. Don't use
anonymous access types. And don't use any access types at all visibly in your
O-O designs.

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

From: John Barnes
Date: Wednesday, November 12, 2008  6:59 AM

I like "if it hurts don't do it". Trouble is one might be maintaining someone
else's stuff and they might have effectively done it and hurt you.

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

From: John Barnes
Date: Tuesday, November 11, 2008  1:59 PM

> I would be interested in what John has to say about the usability
> problems he encountered while writing his book.  Ditto for others who
> have seen problems with Ada 2005's anonymous access types.

I suppose my biggest problem was knowing whether to introduce them before or
after named types. I rewrote that wretched chapter several times. My first
instinct was to introduce anonymous ones first and then the named ones but I
ended up the other way around. Generally, when using the language, it's obvious
what feature to use. But here we have a choice. I know that choice is
fashionable but it can be a pain. Another trouble seems to be that you have to
use named types for some purposes anyway (such as deallocation) so you might as
well use them always.

And they didn't solve the conversion problems they were meant to. And the
conversion rules are now very tiresome.

Of course when you start and do your first baby list, introducing an incomplete
type seems a fag and such a list is perhaps ones first example. It's only later
that you find they are not the panacea.

For Ada 95 I wrote a paper entitled "Accessiblity rules OK". I recall that the
essence of the proof (actually more a comfortable felling) was that the fact
that they were named meant that the scope rules constrained one just fine. It
was suggested that I might write a similar paper on the anonymous ones but I
declined.

On reflection, one of the strengths of Ada is that we give things names so that
we can talk about them. That becomes formalized in Spark of course where
everything is named (more or less) so that the proof stuff can talk about things
explicitly.

I should read my book again to try to get back some of my concerns. But the book
has minor typos (bugs regarding conversions) in this area anyway (another sign
that I kept getting confused).

When I next revise the book, I will probably play down anonymous types and just
mention them en passant.

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

From: John Barnes
Date: Wednesday, November 12, 2008  1:39 AM

Is everybody on the Arg list on Ada comment as well? It would be helpful to know
that.

Another point about my book.

I felt when talking about these anon access types that I was using more pages
making the book bigger, heavier and more expensive (maybe more royalties for old
John though), causing the reader confusion, the problem of unnecessary choice
and so on. And at the same time providing no additional real useful
functionality whatsoever.

One of Ada's strengths is clarity. We do not confuse specific types with classes
of types. And we do not confuse things and the names of things. Anonymousness
tends to introduce a lack of clarity. I suppose the only thing to be said for it
is that it saves the programmer from thinking of a name.

The big problem is with accessibility. Where is the invisible name declared? I
found that tedious to explain and am sure that I just obfuscated over the issue.
C programmers typically just hack the code until it compiles. I like to think
that Ada programmers like to know and understand why their code has bugs and put
it right by design rather than hacking. Anon access types could induce that
hacking mentality. Grrr!

Are many real users (those with a heavy investment) and not just those exploring
and doing prototyping actually using 2005 and these anon types? Can we not just
have the courage to cut the whole lot out?

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

From: Jeffery C. Carter
Date: Tuesday, November 11, 2008  2:31 PM

IMHO, anonymous types are a bad idea. Ada 83's anonymous array types were a bad
idea, Ada 95's anonymous access parameters were a bad idea, and the current crop
of anonymous access types just compounds the problem. Adding more ways to
declare anonymous access types in Ada 19 would, therefore, be continuing a
long-standing tradition of bad ideas.

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

From: John Barnes
Date: Wednesday, November 12, 2008  1:39 AM

I am not sure that all anonymous types are a bad idea. I quite like anonymous
array types for a single array that is unrelated to any other arrays. What is
annoying is that I cannot write

protected Stack is
...
private
   Max: constant := 100;
   Top: Integer range 0 .. Max :=0;
   A: array (1 .. Max) of Float;
end Stack;

but have to introduce a type name such as Float_Array and moreover I have to
stick it ouside the spec of Stack for all the world to see.

But this is a different subject. And please don't say Ada 19. It makes me
worried about how old I will be.

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

From: Jeffery C. Carter
Date: Wednesday, November 12, 2008  12:57 PM

> but have to introduce a type name such as Float_Array and moreover I
> have to stick it ouside the spec of Stack for all the world to see.

I hope you would call the object something like Content, in which case
Content_List seems like a good type name. What is annoying is the latter part:
"I have to stick it ouside the spec of Stack for all the world to see." But
that's unrelated to anonymous types.

> But this is a different subject. And please don't say Ada 19. It makes
> me worried about how old I will be.

Sorry. I'll be retire-able then, so you're not the only one. But given
publication dates of 1983, 1995, and 2007, the only possible extrapolation is
2019.

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

From: Tucker Taft
Date: Wednesday, November 12, 2008  8:54 AM

I guess I am still looking for some more specifics.  In particular, did you
encounter the most trouble with:

    anonymous access-type parameter
    anonymous access-type function result
    anonymous access-type discriminants
    anonymous access-type components
    stand-alone anonymous access-type objects

My own sense is that the generalizations we made to access parameters and access
discriminants work quite well.  Access results for function need some work, but
I happen to like the AI I wrote up on that (surprise, surprise!).  Access-type
components seem relatively straightforward, but I'm not convinced they add a
lot.  Given that you are defining a record type anyway, it is easy enough to
define a named access type or two as needed.

It is stand-alone anonymous access-type objects that seem the biggest challenge
to me.  Clearly when the object is library-level, its accessibility level must
be as well.  The only issue is local variables of an anonymous access type.

I suggested something in a previous note that initialized stand-alone objects of
an anonymous access type would take their accessibility from the initial value.
An alternative idea, since we are only talking about local variables which
necessarily have limited scope, is to have them be completely dynamic, like
access parameters, inheriting the accessibility level of the RHS on assignment.
This sounds inefficient, but in fact is probably not a big deal.  I suspect that
most compiler optimizers could quite easily track the accessibility level of the
variable, ensuring that decent compile-time warnings are provided.  You can
always declare a named access type and use that if this level of dynamicity is
of concern.  The good thing is that by introducing a temp into an algorithm
using anonymous access types, you never affect the accessibility.  That seems
pretty desirable.

Comments?

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

From: Tucker Taft
Date: Wednesday, November 12, 2008  9:11 AM

One point to add, the dynamic accessibility level should never be allowed to
become *deeper* than that of the access object itself, or you have a sure recipe
for a dangling reference.  That is, even if we were to make the accessibility
level dynamic, it would be limited to that of the access object.  It could only
be the same as or shallower than that.  Hence you would still get a compile-time
error if you tried to assign the 'Access of a nested aliased object via an
up-level reference to an enclosing stand-alone access object.  This means that
library-level access objects are not a special case, since there is nothing
"shallower" than library-level, meaning that the "dynamic" accessibility level
can never be anything by library-level.

The nice thing about this approach is that if you assign an access parameter
into a stand-alone access object, or if you assign the value of an anonymous
access component into such an access object, you don't suddenly have a
relatively useless value of "local" accessibility.  You can always store it
*back* where it came from.  As the rules work now, you get into a funny
situation where you "poison" the value by storing it into a temp, and can't
store it back.  Hence, if you just want to swap the value of two anon-access
components, you can't do it using a temp with the current rules.  But if we make
temps carry a "dynamic" accessibility level, this all works quite
straightforwardly:

      type Tree is tagged record
          Left, Right : access Tree'Class;
          ...
      end record;

      ...
      procedure Flip(T : access Tree'Class) is
          Temp : access Tree'Class;
      begin

          ...
          Temp := T.Left;
          T.Left := T.Right;
          T.Right := Temp;
          ...
      end Flip;

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

From: Steve Baird
Date: Wednesday, November 12, 2008  1:16 PM

>      type Tree is tagged record
>          Left, Right : access Tree'Class;
>          ...
>      end record;
>
>      ...
>      procedure Flip(T : access Tree'Class) is
>          Temp : access Tree'Class;
>      begin
>
>          ...
>          Temp := T.Left;
>          T.Left := T.Right;
>          T.Right := Temp;
>          ...
>      end Flip;


I don't think this example demonstrates the need for any addition to the
language. This is analogous to the situation with arrays, where the user has the
option of introducing a named unconstrained type, or a constrained first named
subtype, or a completely anonymous type/subtype (in the case of an object
declaration).

It sometimes happens that one of the latter two options looks appealing at first
but it later turns out that there is a need to name an entity that was chosen to
be anonymous. When this happens, this only indicates that this was a case where
choosing the anonymous type/subtype shortcut was inappropriate.

The workaround in this case is to go back and choose a name for the type/subtype
that needs to be named.

There seems to be a general consensus that the utility of these shortcuts when
they are used correctly is more than enough to offset the drawbacks associated
with the language-defined-booby-trap scenario I've described.

The same analysis applies to anonymous access component types (with the possible
exception of the consensus).

Unlike the anonymous access types for discriminants, parameters, and (much to my
chagrin) function results, the use of an anonymous component access type or an
anonymous top level object access type is equivalent to using a named access
type except that any construct that would require naming the access type becomes
unavailable.

The solution for the example above is to use a named access type for the record
component. As Randy observed, "if it hurts, don't do it".

This is not to say that there are no good arguments for the changes that you
described. I'm just claiming that this particular example isn't one; it is more
of an illustration of the kind of issues one ought to consider before deciding
to make a component access type anonymous.

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

From: Tucker Taft
Date: Wednesday, November 12, 2008  1:58 PM

This example wasn't intended to indicate a problem with anonymous access
components, but rather with the accessibility rules for standalone objects of an
anonymous access type.  My sense is that the current accessibility rule for
stand-alone objects is the problem, whereas all the others are reasonable or at
least defensible.

Local variables often serve the role of temps.  The accessibility rules for
local stand-alone objects of an anonymous access type conflict with that.
Because of the problem with stand-alone objects, they tend to make almost any
use of anonymous access types more painful, because you can't store them
temporarily in the middle of an operation.  My sense is that that is why the
whole feature seems less useful.

If we solve the problem with stand-alone objects, my feeling is that all other
anonymous access entities will feel more useful as well.

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

From: Franco Gasperoni
Date: Thursday, November 13, 2008  5:17 AM

> Before putting up a whole new approach, I would like to try to
> understand what is the real usability problem with the anonymous
> access types as currently proposed for Ada 2005, and see if there is a
> possible "targeted" fix.

Thank you for your reply. That is a good idea that we should pursue first before
delving in the proposal that we sent.

I just subscribed to ada-comment and am reviewing the archive of these last few
days before providing more comments on the above which I shall do shortly

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

From: Arnaud Charlet
Date: Wednesday, November 12, 2008  1:58 AM

> Are many real users (those with a heavy investment) and not just those
> exploring and doing prototyping actually using 2005 and these anon types?

Yes, we do have real and serious customers using anon types, and starting using
Ada 2005, and they rapidly stumbled upon the limitations of anon access types.

> Can we not just have the courage to cut the whole lot out?

That would be a real step backward for those users I believe.

As for "doing OO without access types" as suggested by Randy, that's simply
impossible in practice if you want to use dynamic dispatching.

Now, I agree that introducing yet another kind of access type is not very
elegant, so I agree with Tuck that we should try to fix the existing
capabilities of anon access types rather than introducing another capability,
and I think Franco's document gives a good starting point explaining what are
the limitations of the current model that we would like to address (and
accessibility level of stand alone objects is definitely one).

The introduction of the 'Free or 'Unchecked_Free attribute is orthogonal as well
and could equally apply to the current anon access types I believe.

At AdaCore, I was also in favor of 'Unchecked_Free, but 'Free has the merit of
being less verbose, and still as clear for most programmers, and has also the
advantage of being in line with what other languages provide in this area. No
strong feeling on the name though.

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

From: Tucker Taft
Date: Wednesday, November 12, 2008  2:09 PM

I have a strong feeling that anything that is "unchecked" should have
"unchecked" in its name.  In retrospect, I think the
Address_To_Access_Conversions generic should have had "Unchecked" in its name as
well.  Any kind of potentially unsafe deallocation is certainly something that
deserves an "unchecked" marker. Modeling ourselves after "C" is hardly a good
thing in this area.

I am curious whether you can tell if making the accessibility of stand-alone
variables dynamic would reduce some of the user "stumbles."

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

From: Randy Brukardt
Date: Wednesday, November 12, 2008  1:56 PM

...
> As for "doing OO without access types" as suggested by Randy, that's
> simply impossible in practice if you want to use dynamic dispatching.

I was mainly talking about the interface of OO types, not how the users of the
OO objects implement their code.

I presume you are talking about how clients manage class-wide objects (that is,
objects without a statically known type). Other kinds of dynamic dispatching
surely don't need any access types, just type conversions.

For Ada 95, my experience has been that named access types worked just fine to
create data structures of class-wide objects. We could argue that forever. But
it is irrelevant.

For Ada 2005, you don't need to use any explicit access types to have lists or
vectors or maps or sets of class-wide objects. You just appropriately
instantiate the containers. The upcoming Amendment will add a Holder container
that essentially allows stack-based class-wide variables. It's hard to imagine
what other ways to store class-wide objects that needs to be supported. (Other
than persistence, and access types are an active hinderance to that.)

Worse, the use of anonymous access types (especially in the interface)
essentially prevents the use of these containers by forcing the use of access
types where none are needed. We *really* don't want to encourage that, it's
exactly the wrong direction.

So I think that the assumption that you need access types to get dynamic
dispatching is Ada 95 think. I hope we've moved beyond that.

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

From: Franco Gasperoni
Date: Wednesday, November 13, 2008  11:20 AM

> Before putting up a whole new approach, I would like to try to
> understand what is the real usability problem with the anonymous
> access types as currently proposed for Ada 2005, and see if there is a
> possible "targeted" fix.

That is the best way to proceed and in your series of e-mails you have provided
an elegant, exciting, and very constructive solution. Thank you Tuck.

I like anonymous access types (parameters, function results, discriminants,
components, stand-alone objects).

They would be very useful if we can fix the issue of the accessibility level of
stand-alone anonymous access objects. As Tuck puts it:

    My sense is that the current accessibility rule for stand-alone objects is
    the problem, whereas all the others are reasonable or at least defensible.

    Local variables often serve the role of temps.  The accessibility rules for
    local stand-alone objects of an anonymous access type conflict with that.
    Because of the problem with stand-alone objects, they tend to make almost
    any use of anonymous access types more painful, because you can't store them
    temporarily in the middle of an operation.  My sense is that that is why the
    whole feature seems less useful.

    If we solve the problem with stand-alone objects, my feeling is that all the
    anonymous access entities will feel more useful as well.

That is right on target and Tuck's emails provide an elegant solution to the
problem. Let me summarize Tuck's proposal as I understood it:

1. The accessibility level of a stand-alone anonymous access-type object PTR
    is completely dynamic (like access parameters), inheriting the
    accessibility level from the right-hand-side on assignment.
    [Note: we need a rule to define the accessibility level of PTR when
     PTR = Null]

2. The dynamic accessibility level of PTR cannot be *deeper* than that of
    PTR itself.  That is, the accessibility of the objects pointed to by PTR
    would be limited to that of PTR. As an example consider:

    package body P is
       Global : aliased Integer;
       G_PTR  : access Integer;
       procedure Proc is
          Local : aliased Integer;
          PTR   : access Integer;  --  default initialized to Null OK

          procedure Inner_Proc is
             Inner : aliased Integer;
          begin
             PTR := Inner'Access;  -- Compile-time *ERROR*
          end Inner_Proc;

       begin
          G_PTR : Local'Access; -- Compile-time *ERROR*

          PTR := Local'Access;  -- OK
          PTR := Global'Access; -- OK
          PTR := Null;          -- OK

          G_PTR := Global'Access; -- OK
       end Proc;
    end P;

3. Since there is nothing "shallower" than library-level, the "dynamic"
    accessibility level of a stand-alone library-level anonymous access G_PTR
    (as in the above example) must be library-level. As an optimization, such
    library-level G_PTR does not need to carry accessibility info with it.

To conclude, as Tuck puts it:

    The nice thing about this approach is that if you assign
    an access parameter into a stand-alone access object, or
    if you assign the value of an anonymous access component
    into such an access object, you don't suddenly have a
    relatively useless value of "local" accessibility.  You
    can always store it *back* where it came from.

For those who have read 'Ref proposal that solves the problem listed in 4.6.
This problem was the biggest issue to address.

That leaves one more issue to solve (7.2 in the 'Ref proposal) that we can
discuss in a separate thread.

In short thank you Tuck :)

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

From: Tucker Taft
Date: Thursday, November 13, 2008  11:58 AM

I'm glad to hear that this seems to be a fruitful direction.  Thanks for
creating a concrete example. Interestingly enough, everything you wrote in your
example would work exactly the same way as the current Ada 2005 rules.  What
would be *different* would be sequences like the following:

      PTR := Global'Access;
      G_PTR := PTR;

In Ada 2005, this would be illegal.  With these new rules, it would, at least
conceptually, involve a run-time accessibility check, which would be certain to
pass.

In fact, my expectation is that almost all of the "run-time" accessibility
checks introduced by this proposal would be determinable at compile time with
any semi-decent compiler optimizer.  The exception would be those that involve
access parameters, since that is where dynamic accessibility gets introduced.
But these would not really involve new kinds of checks, because if we just
substituted the access parameter directly in all uses of the stand-alone
variable, essentially the same sort of check as currently defined would be
needed.

As far as the accessibility level of "null", it is already defined in Ada 2005
to be library level (3.10.2(12.{1,2}/2)), which makes sense with this proposal
as well.

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

From: Arnaud Charlet
Date: Thursday, November 13, 2008  4:05 AM

> I have a strong feeling that anything that is "unchecked"
> should have "unchecked" in its name.  In retrospect, I think the
> Address_To_Access_Conversions generic should have had "Unchecked" in
> its name as well.  Any kind of potentially unsafe deallocation is
> certainly something that deserves an "unchecked" marker.
> Modeling ourselves after "C" is hardly a good thing in this area.

That's my feeling as well, but other people's mileage may vary (as mentioned by
e.g. Bob).

> I am curious whether you can tell if making the accessibility of
> stand-alone variables dynamic would reduce some of the user
> "stumbles."

That would certainly be one step in the right direction, yes.

The other big issues that Franco is trying to solve I believe (I may have
forgotten others) is the issue of converting between different access types,
which becomes more of an issue with interfaces. And having to use tricks such as
"Object.all'Access" for that as we e.g. do in Ada 95 is not very pleasant.

I am not planning to answer to Randy's message who seem to be focusing on
containers, which is really an orthogonal issue to this discussion.

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

From: Steve Baird
Date: Thursday, November 13, 2008  3:08 AM

> As far as the accessibility level of "null", it is already defined in
> Ada 2005 to be library level (3.10.2(12.{1,2}/2)), which makes sense
> with this proposal as well.

Perhaps I am diving into details prematurely, but I am curious about the case
where a top-level local variable of an anonymous access type is assigned an
allocator.

Incidentally, I agree that the general approach looks very promising.

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

From: Tucker Taft
Date: Thursday, November 13, 2008  3:19 PM

An anonymous allocator would have to take the accessibility level from the
variable's declaration.  Nothing else would make much sense, in my view.  Access
parameters pretty much work the same way.

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

From: Randy Brukardt
Date: Thursday, November 13, 2008  6:40 PM

...
> I am not planning to answer to Randy's message who seem to be focusing
> on containers, which is really an orthogonal issue to this discussion.

I don't see why. My point boils down to "why spend effort 'improving' access
types when most new software shouldn't be using them in the first place". It's
very much like spending a lot of effort on carriage design when they are getting
replaced by automobiles.

If you think this is "orthogonal to this discussion", you must understand that
an important point about any ARG discussion is whether there is sufficient need
for the proposal. And by "need", I mean "big picture" need. I don't mean "I
can't do X when I use Y"; that's like saying "I can't drive West on Johnson Ave.
(it's one-way)", sure that's true, but there's no need (University is one-way
west only a block away).

The problem with using access types is that they force a particular (and unsafe,
at least in Ada today) form of storage management on their user, whether that is
appropriate or not. And discourages or even prevents using other, possibly
better forms of storage management. So I believe access types should only be
used if no other efficient language solution exists. That's especially true in
specifications, but it exists everywhere.

Once the bounded containers definition is completed (and that is not too far
off), there will be little efficiency difference between an access type solution
and a container solution to storage management. (Nor will there be any reason to
avoid containers in safety-critical code.) But the container solution (since it
is almost fully checked) is less likely to have errors. So why use access types
for those types of data structures?

So I want to see some examples of problems that cannot be better solved in other
ways, given the features that Ada now has or are already known to be included in
the upcoming Amendment. And I think that any such use has to be in new code, as
any new features added to access types are going to only be usable in new code.
(Rewriting old code to use them is just as stupid as rewriting working old code
for any other reason.) So I don't care much about access types in existing code
(obviously they'll remain, but there is little need to make them easier to use
-- even program maintenance is unlikely to add many additional uses -- big new
chunks should be done with containers for the same reasons new code should be).
I've tried to imagine uses in safety-critical software -- but such systems
rarely use dynamic allocation (so no need for allocators or deallocators) and
rarely use OOP (so the basic underlying statement of the proposal is
unnecessary). So what is this usage??

So, especially in the case of anonymous access types (which shouldn't be used in
much existing code anyway), I want to see good reasons why anyone would want to
use them. I'm now convinced that we (and especially me) made a horrible mistake
allowing ourselves to be talked into an expansion of a barely working feature of
the language for Ada 2005. I think the whole lot belongs in Annex J.

If we were to improve access types, we need to give them more control over the
finalization and accessibility of the objects that they point at and additional
ways to make them safe. These things need names in order to refer to the types
in question, and anonymous access types cannot have that. They can only have a
guess at what the accessibility should be, or an extremely expensive run-time
accessibility model (remember that accessibilities can be incomparable).

Anyway, if you want my support for any changes to anonymous access types, you
are going to have to come up with compelling use cases that can't be done better
another way. Otherwise, I will be opposing such changes as a monumental waste of
time and energy.

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

From: Tucker Taft
Date: Thursday, November 13, 2008  7:27 PM

Tree structures are painful to work with without access types. Tagged types,
access types, and heterogeneous trees are natural bedfellows.  In this era of
XML everything, heterogeneous trees are not just for compiler writers anymore.

But of course Your Mileage May Vary.

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

From: Randy Brukardt
Date: Thursday, November 13, 2008  8:17 PM

I surely agree that tagged types and heterogeneous tress are natural bedfellows.
I'm not so certain about the access types, though. I thought that the Set
containers were supposed to cover a significant portion of the need for trees.
If they don't, then I think we ought to discuss what containers will. (Let's
work on tomorrow's ways to program, not yesterday's.)

Beyond that, why would anonymous access types be preferred for a tree rather
than a named access type? They can't be managed (no pools), so you still have to
convert back and forth, and that needs explicit conversions when going to the
named type. That's no help. And allocation without pools makes no sense; your
storage management ideas and my proposal both depend on the capabilities of
custom storage pools. And that usually means pool-specific types (another thing
that anonymous access types aren't).

So I still don't get it. (This proposal is about *anonymous* access types, after
all. I would not be as negative toward proposals for *named* access types -- I'm
working on one of those myself.)

P.S. <Rant> Your description of the need to use heterogeneous trees for XML
seems to be the best reason not to use it that I've ever heard. :-) It still
sounds like a solution in search of a problem to me. But, even ignoring that, I
don't see any reason to use giant "one-size-fits-all" parsers for something as
simple as XML. So I cannot imagine why such complex data structures are needed;
smarter parsers are what's needed. The whole thing makes me think that the world
has lost its mind -- except that use of C and other junk languages tells me it
never had one in the first place.</Rant>

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

From: Tucker Taft
Date: Thursday, November 13, 2008  8:43 PM

> I surely agree that tagged types and heterogeneous tress are natural
> bedfellows. I'm not so certain about the access types, though.
> I thought that the Set containers were supposed to cover a significant
> portion of the need for trees. If they don't, then I think we ought to
> discuss what containers will. (Let's work on tomorrow's ways to
> program, not
> yesterday's.)

You can use a tree to implement a set, but you can't really use a set to
implement a tree.  You could implement a tree using a map, but that's pretty
painful.  If I want to represent something that is tree like (e.g. an abstract
syntax tree, an XML file, a CAD drawing, etc.) I really need pointers of some
sort.

> Beyond that, why would anonymous access types be preferred for a tree
> rather than a named access type? They can't be managed (no pools), so
> you still have to convert back and forth, and that needs explicit
> conversions when going to the named type. That's no help. And
> allocation without pools makes no sense; your storage management ideas
> and my proposal both depend on the capabilities of custom storage
> pools. And that usually means pool-specific types (another thing that anonymous access types aren't).

You might use named access types for allocation, but use anonymous access types
in your structures, since you don't want the structures to be dependent on the
particular access type used to allocate them. If you do deallocation en-masse
(mark/release, subpools, etc.), then you wouldn't have to convert back to the
original named access type for deallocation.  You would just wave the magic wand
(do a "release" or equivalent), and all the storage would be reclaimed.

> So I still don't get it. (This proposal is about *anonymous* access
> types, after all. I would not be as negative toward proposals for
> *named* access types -- I'm working on one of those myself.)

I think there are definitely paradigms where anonymous access types work well. I
am coming to think that our only real boo-boo had to do with the accessibility
of stand-alone access objects, and I think the tentative proposal I have made
may resolve that.  I think making a modest fix to the accessibility rules for
stand-alone access objects is better than ripping out the whole feature at this
point.

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

From: Randy Brukardt
Date: Thursday, November 13, 2008  9:24 PM

> You can use a tree to implement a set, but you can't really use a set
> to implement a tree.  You could implement a tree using a map, but
> that's pretty painful.  If I want to represent something that is tree
> like (e.g. an abstract syntax tree, an XML file, a CAD drawing, etc.)
> I really need pointers of some sort.

But a container cursor is very much like an access type, but it is much better
checked and much less likely to be dangling (and dangling cursor detection is
fairly cheap, at least in the existing cursors). And all of the storage
management gets handled by the container. So my thought was that if the existing
containers cannot handle trees well, then probably we need some tree containers.
I really think we need to get away from raw access types most of the time.

I know you've said that "it's easier to write these yourself", but you could
make that argument about all but the ordered set containers (I know I've been
writing what amount to vectors and lists and (less often) maps from scratch for
years.) So I wonder if a traditional parent-child-sibling tree container would
not be very helpful. (Especially as it could include various iterators to
traverse the entire tree.) (Every tree structure I've ever used was a
parent-child-sibling tree -- not counting ones used when in school in the 70's.
I don't see much value to a balanced tree, for instance -- it's essentially
another form of the Ordered_Set container.)

While I don't particularly want another task, I suppose I'll have to draw
something up now. (For the observers, this is generally how the ARG assigns
action items -- whomever mentions it first has to write it up...) [Editor's
note: This became the Multiway_Tree container proposal, AI05-0136-1.]

...
> You might use named access types for allocation, but use anonymous
> access types in your structures, since you don't want the structures
> to be dependent on the particular access type used to allocate them.
> If you do deallocation en-masse (mark/release, subpools, etc.), then
> you wouldn't have to convert back to the original named access type
> for deallocation.  You would just wave the magic wand (do a "release"
> or equivalent), and all the storage would be reclaimed.

As you know, that falls over dead for anything that needs finalization (and for
tasks, too), at least in Ada up through Ada 2005. Since I want to use the
unbounded containers to do storage management, I don't think that really works
in general.

The other problem I have here is that general access types (and anonynous access
types specifically) aren't very analyzable statically. Pool-specific access
types are much better in this regard. Our optimizer, for instance, has to use
"reads all" or "writes all" whenever it encounters a dereference of a general
access value; it cannot assume anything useful. (Pool-specific access types
using the default pool get "reads heap" or "writes heap" instead, which allows a
lot more optimizations.) I'd think you would be more sensitive to this as an
issue.

...
> I think there are definitely paradigms where anonymous access types
> work well.
> I am coming to think that our only real boo-boo had to do with the
> accessibility of stand-alone access objects, and I think the tentative
> proposal I have made may resolve that.  I think making a modest fix to
> the accessibility rules for stand-alone access objects is better than
> ripping out the whole feature at this point.

I won't make too big of a stink about a "modest fix". The problem here is that
you originally proposed essentially this for variables and essentially what you
have in AI05-0051-1 for function results. The ARG said that those choices were
too complicated. (And it surely wasn't just me!) Now, a bunch of years later,
you're essentially coming back and trying to get the more expensive original
solutions. That makes me mad, although I doubt that you did it on purpose.
(OTOH, I should know better, because I know you did that to the DRs for Ada 9x,
although in that case, I was mostly on your side [except for child generics!!])

In any case, this thread is supposed to be on the AdaCore proposal, not on your
"modest fix". And there is nothing "modest" about adding 'Unchecked_Deallocation
and storage pools and a new kind of type to the language!

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

From: Tucker Taft
Date: Thursday, November 13, 2008  9:09 AM

I just went back and read part of AI-230.  One of the main goals was to allow
normal OO "substitutability" in assignment where the LHS is of type
access-to-T1'Class and the RHS is of type access-to-T2'class where T2 is derived
from T1.  With named access types, you end up having to do lots of explicit
conversions, with no visible distinction between safe, "widening" conversions,
and potentially unsafe, "narrowing" conversions.

Interestingly, the original proposal didn't have stand-alone objects of an
anonymous access type, specifically because their accessibility rules were hard
to get right.  It allowed local renaming only.  It seems in retrospect it was
the final push to include stand-alone objects that produced the set of usability
issues we now face.  But I do understand the desire to be able to have temps in
the middle of a computation, but this seems to clearly argue for having "temps"
whose accessibility level is "morphable," or else they aren't very useful (as we
have seen).

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

From: John Barnes
Date: Friday, November 14, 2008  2:07 AM

> Interestingly, the original proposal didn't have stand-alone objects
> of an anonymous access type, specifically because their accessibility
> rules were hard to get right.  It allowed local renaming only.

And I remember feeling that was very strange and confusing.

> It seems in retrospect it was the
> final push to include stand-alone objects that produced the set of
> usability issues we now face.

So maybe it was partly my fault for ranting about the need for them. Sorry
chaps.

I think the real thing this shows is that how useful it would be to have a
period of prototyping new features before they get set in ISO concrete.

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

From: Franco Gasperoni
Date: Friday, November 14, 2008  7:31 AM

> I just went back and read part of AI-230.  One of the main goals was
> to allow normal OO "substitutability"
> in assignment where the LHS is of type access-to-T1'Class and the RHS
> is of type access-to-T2'class where
> T2 is derived from T1.  With named access types, you end up having to
> do lots of explicit conversions, with no visible distinction between
> safe, "widening" conversions, and potentially unsafe, "narrowing"
> conversions.

Right, as Bob puts it you end up "crying wolf" where there is no wolf. This is a
very important benefit of anonymous access types.

A comparison (from an OO view-point) between named and anonymous access types
showing the advantages of anonymous access types is provided in section 3  (3.
OOP and Access Types in Ada 2005) of the proposal that started this thread.

> the desire to be able to have temps in the middle of a computation,
> but this seems to clearly argue for having "temps" whose accessibility
> level is "morphable," or else they aren't very useful (as we have
> seen).

precisely.

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

From: Franco Gasperoni
Date: Friday, November 14, 2008  7:32 AM

> Perhaps I am diving into details prematurely, but I am curious about
> the case where a top-level local variable of an anonymous access type
> is assigned an allocator.

That is the second problem that needs to be solved in my opinion. I'll send an
email on this topic on a new thread

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

From: Alexander E. Kopilovich
Date: Thursday, November 13, 2008  9:16 PM

By the way, is it true that anonymous access type is actually just an interface?

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

From: Randy Brukardt
Date: Thursday, November 13, 2008  9:47 PM

Huh? An anonymous access-to-object type is a general access type with a
implementation-defined pool. (3.10(12/2), 13.11(25)). (I see a bug in 13.11(17);
it should include anonymous access-to-object types. Sigh!) An anonymous
access-to-subprogram type is an access-to-subprogram type. (3.10(12)).
Interfaces are quite different (they really don't exist, they only are used as
the prefix to 'Class in access types and other places).

If you are using "interface" in some informal way, then maybe. I suppose you
could think of access parameters as an interface in a way, but they're really
implemented as access objects (as are by-reference parameters, no matter what
the mode) and they require access objects as their actual (which is what I hate
so much about them), so they're best thought of as another parameter type. And,
in any case, the sorts of uses we've mostly been talking about have not been in
parameters (they've been as components or stand-alone objects).

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

From: John Barnes
Date: Monday, November 17, 2008  10:11 AM

>> I would be interested in what John has to say about the usability
>> problems he encountered while writing his book.  Ditto for others who
>> have seen problems with Ada 2005's anonymous access types.
>>
>
> I should read my book again to try to get back some of my concerns.
> But the book has minor typos (bugs regarding conversions) in this area
> anyway (another sign that I kept getting confused).

I just flipped through my book again.  I suppose my big surprise in using
anonymous types was that I had to add even more type conversions. Page 189
contains some words of wisdom about avoiding conversions But then...

I got a surprise with the program Sylvan Sorter (page 209). The types Tree and
List are treated differently. My excuse is that it illustrates their properties.
In List the inner type is anonymous and this requires a conversion in
Take_From_List that wasn't needed in the old book - that's because I am using a
named type for the external view and an anon type internally. When it came to
the type Tree, I found that the same approach led to zillions of conversions
that were really ugly, so I gave up. Moreover List needs to be general (with
all) whereas Tree doesn't. This is confusing both to the novice and the elderly.

I ran into a similar situation in section 11.5 where on tehe one hand I had to
add a conversion in Pop but on the other hand the function "=" did not go
recursive on me! Win some, lose some.

And when I came to rewrite the deep copy on page 342, I was pretty fed up with
anon types so that I just left the tried and trusted named ones as they were.

There are more mutterings about why I didn't use anon types on page 417 when
talking about the family.

On page 524 on iterators I seem to have given the anon types another go. I am
suspicious about this section. I would have expected some type conversions but I
don't see any.

On page 544, I have plucked up courage again and used an anon type for access
Filter but then I have to whinge about why I couldn't write Next := new Filter
(N) but still had to declare Make_Filter.

I think they are the main points. If I went through my attic I might find copies
of other attempts to write certain bits. But maybe they were consigned to the
flames.

It could well be that a careful scrutiny of these bits reveals some bugs. I
would be grateful to anyone who spots such bugs (I think).

****************************************************************
================ End of thread ====================
====== New thread: Contains some justification for ideas (1) and (5). =====

From: Franco Gasperoni
Date: Friday, November 14, 2008  10:47 AM

A local allocator in Ada 95 and Ada 2005 is something like

    procedure P (X : access T);

    procedure Q is
    begin
       P (new T);  --  Local anonymous allocator
    end Q;

The local anonymous allocator above is implicitly deallocated just after the
call to P, i.e. the above is akin to something like

    procedure Q is
    begin
       declare
          new_T : aliased T;
       begin
          P (new_T'Access);
       end;
    end Q;

This is very convenient when we want to allocate temporary data-structures and
storage for a phase of our application.  Local allocators allow us to allocate
storage and be assured that this storage is automatically reclaimed on our
behalf when the phase ends.

However, for some people (including me :) the fact that local anonymous
allocators and other type of allocators behave differently can be confusing and
error prone. Consider the following example:

    package Named_Lists is
       type Cell;
       type Any_Cell is access all Cell'Class;
       type Cell is tagged record
          Next : Any_Cell;
       end record;
       Head : Any_Cell := new Cell;
       procedure Insert (Head : access Cell'Class;
                         New_Cell : access Cell'Class);
    end Named_Lists;

    package body Named_Lists is
       procedure Insert (Head : access Cell'Class;
                         New_Cell : access Cell'Class)
       is
       begin
          New_Cell.Next := Head.Next;
          Head.Next := Any_Cell (New_Cell);
       end Insert;
    end Named_Lists;

If we write

    with Named_Lists; use Named_Lists;
    procedure Main is
    begin
       Insert (Head, new Cell);
    end Main;

then we have a Program_Error, however if we write

    with Named_Lists; use named_Lists;
    procedure Main is
       N : Any_Cell := new Cell;
    begin
       Insert (Head, N);
    end Main;

or if we write

    with Named_Lists; use named_Lists;
    procedure Main is
    begin
       Insert (Head, Any_Cell'(new Cell));
    end Main;

we don't. This can be confusing and error prone to some users and it may be
important at least to be able to restrict their use (with some restrictions
pragma).

Alternatively, since we can mimic the use of local anonymous allocators with a
local aliased object we could consider giving a longer (e.g. library-level if T
is library-level) life-time to local anonymous allocators.

More generally we may want to revisit the creation and destruction of storage
from anonymous access types (see next post on this topic).

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

From: Steve Baird
Date: Friday, November 14, 2008  11:48 AM

>    package body Named_Lists is
>       procedure Insert (Head : access Cell'Class;
>                         New_Cell : access Cell'Class)
>       is
>       begin
>          New_Cell.Next := Head.Next;
>          Head.Next := Any_Cell (New_Cell);
>       end Insert;
>    end Named_Lists;
>

Is this Insert procedure any better than something like

    procedure P (X : Integer) is
       Temp : Natural := X;
       ...
?

In both cases, the implementation is depending on properties of the formal
parameter that could have been documented in the specification but were not. The
use of an anonymous access type for a parameter suggests (at least to me) that
the routine is prepared to cope with a parameter with any accessibility level.

If a more precise profile is specified for a routine like Insert, then an actual
parameter which is an allocator will do the right thing.

There may well be good reasons for changing the rules governing the
lifetime/storage_pool of an object allocated via an allocator of an anonymous
access type, but I don't think this example illustrates any of them.

However, I suppose you could construct an example involving an
access-to-subprogram type or a generic formal subprogram which requires that
your subprogram must have an anonymous access parameter. Is the combination of
this contract-abusing scenario with an actual parameter which is an allocator a
sufficiently common occurrence to warrant a language change?

As an aside, it is interesting that there isn't anything analogous to a
membership test that can be used to test whether an accessibility check would
fail without raising an exception. In the scalar case, one could write

    procedure P (X : Integer) is
    begin
       if X not in Natural then
          ... -- cope with it
       else
          declare Temp : Natural := X;
       ...

but I think that trying to do something like this for the Insert procedure would
require introducing an exception handler.

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

From: Franco Gasperoni
Date: Friday, November 14, 2008  12:02 PM

> In both cases, the implementation is depending on properties of the
> formal parameter that could have been documented in the specification
> but were not.
> The use of an anonymous access type for a parameter suggests (at least
> to me) that the routine is prepared to cope with a parameter with any
> accessibility level.

I might have used the wrong example, the problem that I would like to see solved
is that having the accessibiliy of "new T" depend on the context is very
confusing and error-prone for regular Ada developers (including me)

> As an aside, it is interesting that there isn't anything analogous to
> a membership test that can be used to test whether an accessibility
> check would fail without raising an exception.
> In the scalar case, one could write
>
>    procedure P (X : Integer) is
>    begin
>       if X not in Natural then
>          ... -- cope with it
>       else
>          declare Temp : Natural := X;
>       ...
>

I just submitted a proposal in this direction

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

From: Tucker Taft
Date: Friday, November 14, 2008  12:16 PM

> I might have used the wrong example, the problem that I would like to
> see solved is that having the accessibiliy of "new T" depend on the
> context is very confusing and error-prone for regular Ada developers
> (including me)

This statement of the problem surprises me, because the interpretation of an
allocator is *completely* context dependent, and has been since Ada 83.
Everything depends on the expected type.  And the particular problem you cite is
not an Ada 2005 feature, but rather an Ada 95 feature, so if Ada developers are
still confused about it, they apparently have not been using the feature for the
past 13 years.  I can't imagine changing this at this point.

Note that there is already an AI (AI05-111) about providing syntax for
specifying the storage pool as part of an allocator:

    X := new (storage_pool) T'(initial_value);

There is more to the proposal than just this new syntax, but it might be worth
reviewing this AI in any case.

See http://www.ada-auth.org/cgi-bin/cvsweb.cgi/AI05s/AI05-0111-1.TXT

But note, I have already rewritten this AI completely since this was posted, so
the only thing surviving is the syntax in the latest rewrite (not yet posted).

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

From: Steve Baird
Date: Friday, November 14, 2008  12:39 PM

> I might have used the wrong example, the problem that I would like to
> see solved is that having the accessibiliy of "new T" depend on the
> context is very confusing and error-prone for regular Ada developers
> (including me)
>

I wasn't trying to be a nitpicker.

I think the real problem here is that there is a common misconception that an
access type is pretty much characterized by its designated subtype (and perhaps
its storage pool). It is easy to forget about lifetime/accessibility issues.

This means that cases where an anonymous access type is used where a named
general access type would have been more appropriate are probably less obvious
to many users than cases such as my integer/natural example.

There is a legitimate question here about whether we want to try to design the
language to minimize the impact of this kind of misunderstanding. For example,
is it reasonable for me to expect that if a user requires a parameter to
designate an object having (at least) a certain lifetime, then the subprogram
spec should reflect this?

If we expect users to understand this idea, then I think that weakens the
argument for constructs which help the user cope when they find themselves in an
avoidable mess. If we don't, then such features become more attractive.

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

From: Randy Brukardt
Date: Friday, November 14, 2008  3:02 AM

> This statement of the problem surprises me, because the interpretation
> of an allocator is *completely* context dependent, and has been since
> Ada 83.  Everything depends on the expected type.  And the particular
> problem you cite is not an Ada 2005 feature, but rather an Ada 95
> feature, so if Ada developers are still confused about it, they
> apparently have not been using the feature for the past
> 13 years.  I can't imagine changing this at this point.

Compatibility is a concern, surely. However, Franco (and Tuck) are wrong about
one thing: the storage pool used by an allocator used in an access parameter is
not defined by the language. There is some Implementation Advice about it, but
there is no *requirement* for this to act as a local object. As such, depending
on the particular deallocation behavior of such an allocator is a bug.

Janus/Ada uses the global heap (because it doesn't have any local storage
pools) and flags all such allocators with a warning about a likely memory
leak:

*Warning* Memory will not be recovered for allocator

One hopes that this is enough to cause people to avoid such things.

I think that anonymous allocators were allowed because the Ada 9x team thought
of a "cool" use for them, and that mistake has led to all manner of abominations
in Ada. I don't see any reason to make the problem worse.

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

From: Franco Gasperoni
Date: Saturday, November 15, 2008  5:21 AM

> This statement of the problem surprises me, because the interpretation
> of an allocator is *completely* context dependent, and has been since
> Ada 83.

Yes, and back then we only had named (pool-specific) access types :)

> Everything depends
> on the expected type.  And the particular problem you cite is not an
> Ada 2005 feature, but rather an Ada 95 feature, so if Ada developers
> are still confused about it, they apparently have not been using the
> feature for the past
> 13 years.

This problem becomes much more visible in Ada 2005 with the generalized used of
anonymous access and you are right most Ada developers have not read the ARM
(they have read John's books :)

Also this problem disappears in Ada 95 if like the Janus and GNAT compilers you
allocate this stuff on the heap and you disable accessibility checks .... (I've
seen this)

>  I can't imagine changing this at this point.

I understand

> Note that there is already an AI (AI05-111) about providing syntax for
> specifying the storage pool as part of an allocator:
>
>    X := new (storage_pool) T'(initial_value);
>
> There is more to the proposal than just this new syntax, but it might
> be worth reviewing this AI in any case.
>
> See http://www.ada-auth.org/cgi-bin/cvsweb.cgi/AI05s/AI05-0111-1.TXT
>
> But note, I have already rewritten this AI completely since this was
> posted, so the only thing surviving is the syntax in the latest
> rewrite (not yet posted).

OK let me know, this is interesting.

Also Randy's idea about forcing static or dynamic accessibility for a family of
access types is interesting and may help us in the goal to explain/map pointers
in Ada to folks coming from C/C++.

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

From: Dmitry A. Kazakov
Date: Saturday, November 15, 2008   7:19 AM

> This statement of the problem surprises me, because the interpretation
> of an allocator is *completely* context dependent, and has been since
> Ada 83.  Everything depends on the expected type.  And the particular
> problem you cite is not an Ada 2005 feature, but rather an Ada 95
> feature, so if Ada developers are still confused about it, they
> apparently have not been using the feature for the past
> 13 years.  I can't imagine changing this at this point.

Hmm, the crucial difference to Ada 83 is anonymity of the type. Anonymous types
are matched by structure. The programmer expects "access T" be equivalent to
"access T". But they are not with respect to the accessibility and life time.
This makes it conceptually broken and inherently unsafe. We just have lost about
a half of week exactly on this issue.

All anonymous access types are bad but some of them are worse than others...

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

From: John Barnes
Date: Monday, November 17, 2008  1:40 AM

But we are used to the array type in  X: array(1..10) of Integer not to be
equivalent to that in  Y: array(1..10) of Integer in the sense that we cannot
write X := Y;

So we are used to anon types not having structural matching.

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

From: Dmitry A. Kazakov
Date: Monday, November 17, 2008   2:57 AM

> But we are used to the array type in  X: array(1..10) of Integer not
> to be equivalent to that in  Y: array(1..10) of Integer in the sense
> that we cannot write X := Y; So we are used to anon types not having
> structural matching.

Anonymous arrays are not allowed as parameters of subprograms. Even if they
were, as access types are, there would be no harm, because their values are
indeed equivalent, while access values are not.

The sequence of errors, which, I bet, will repeat itself number of times:

   type T is tagged null record;
   function Create return not null access T;

Then an implementation of Create follows:

   function Create return not null access T is
      X : not null access T := new T;
   begin
      ... -- Doing some stuff with X
      return X;
   end Create;

The compiler rejects the code. "Those damned accessibility checks," thinks the
programmer, "I have a Wunderwaffe against you!"

   function Create return not null access T is
      X : not null access T := new T;
   begin
      ...
      return X.all'Access;
   end Create;

"Still not going? Unchecked_Access must do it at least"

   function Create return not null access T is
      X : not null access T := new T;
   begin
      ...
      return X.all'Unchecked_Access; -- A perfect dangling pointer returned
   end Create;

The mental barrier of going from 'Access to 'Unchecked_Access is quite low. Some
people just start with 'Unchecked_Access, because there are too many cases where
'Unchecked_Access is required.

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

From: Jeff Cousins
Date: Wednesday, November 19, 2008  10:17 AM

> But we are used to the array type in  X: array(1..10) of Integer not
> to be equivalent to that in  Y: array(1..10) of Integer in the sense
> that we cannot write X := Y;
> So we are used to anon types not having structural matching.

WG9 may be used to it but I've just done a little straw poll of programmers and
they're divided between saying one can write X := Y, and one can't but one ought
to be able to. I think the "no surprises" rule comes into play, the behaviour
ought to be intuitive.

****************************************************************
========= End of thread ==========
====== New thread: A discussion of idea (4)  =========
(Call-level accessibility)
****************************************************************

From: Randy Brukardt
Date: Thursday, November 20, 2008  7:26 PM

I was trying to figure out the effect of AI05-0051-1 on some cases that might
(or might not) come up in some of the proposals that I've been designing in my
head for the last couple of weeks. I figured I needed to ask the oracle of
accessibility (Tucker), but then thought that the rest of you would feel left
out if you couldn't share in this fun.

Back in January, Tucker responded to a comment of mine:

> When you take 'Access of a formal parameter of a tagged type, it is
> treated like a local variable.

I don't think this can be right; it would make it impossible to return 'Access
of part of a parameter with an anonymous access type, and that can't be right:

    function Fooey (A : in A_Tagged_Type) return access A_Tagged_Type is
        Local : aliased A_Tagged_Type;
    begin
        if Clock > Morning then
            return Local'Access; -- (1)
        else
            return A'Access; -- (2)
        end if;
    end Fooey;

I would hope that the return at (1) would raise Program_Error no matter where
the function is called. But I would hope that (2) would in fact work if the call
to Fooey is used appropriately. So I'm assuming that by local variable, Tucker
means "local to the call" and not "local to the function". Right?

My interest stems from looking at container-like uses. Imagine the following
bounded container (I've left out the generics and private types to make the
interesting stuff easier to see):

     package A_Container is
         type Vector (Capacity : Natural) is tagged record
             Length : Natural := 0;
             Data : array (1..Capacity) of aliased Element;
         end record;

         function Reference (Obj : Vector; Index : Natural) return access Element;
     end A_Container;

     package body A_Container is
         function Reference (Obj : Vector; Index : Natural) return access Element is
         begin
             if Index > Obj.Length then raise Constraint_Error; end if;
             return Obj.Data(Index)'Access;
         end Reference;
     end A_Container;

Surely we want this return to work, but we also don't want the accessibility of
the access returned to be very long, because we don't know how long Obj will
live (and we surely don't want to have to pass dynamic accessibility with all
parameters!).

It's easy to see this if we look at some uses of this package:

    with A_Container;
    package Test is
        Lib_Level_Vector : A_Container.Vector;

        type Lib_Access is access Element;

    end Test;

    package body Test is
       O : Lib_Access;
       procedure Nest is
           Nested_Vector : A_Container.Vector;
           type Local_Access is access Element;
           Local : Local_Access;
       begin
           O := Nested_Vector.Reference (10); -- (1)
           O := Lib_Level_Vector.Reference (10); -- (2)
           Local := Nested_Vector.Reference (10); -- (3)
           Local := Lib_Level_Vector.Reference (10); -- (4)
           declare
               Inner : access Element := Nested_Vector.Reference (10); -- (5)
           begin
               O := Inner; -- Statically illegal currently.
               Local := Nested_Vector.Reference (10); -- (6)
               Local := Lib_Level_Vector.Reference (10); -- (7)
           end;
    begin
       O := Lib_Level_Vector.Reference (10); -- (8)
       Nest;
    end Test;

Since Reference will assume its parameter is a local variable at the point of
the call, calls (1) and (2) will raise Program_Error (for an accessibility
failure), even though (2) is actually OK. (3) and (4) will work, as will (5) and
(8). But, oddly, (6) and (7) will raise Program_Error (because the block is more
nested than the subprogram level where the access type is declared). While that
is OK for my intended purpose (which is similar to (5)), it looks like a
significant maintenance hazard -- I add and remove blocks all the time expecting
nothing about the execution of the enclosed statements to change. That property
seems to be violated here.

Do I understand this all correctly??

In general, this definition works well for containers, because we surely do not
want the returned access to be usable for very long. Keeping it to a local scope
prevents any long-lived accesses from being created, and that greatly reduces
(but does not eliminate) the danger of dangling accesses.

But this also makes clear that if a routine like Reference is added to any of
the language-defined packages, we would have to define the accessibility of the
result. Because we wouldn't want a change in the internal implementation (from
directly nested objects to heap objects for instance) to change whether or not
the usage raises an exception. That would be particularly important when moving
from unbounded to bounded implementations -- we wouldn't want a call like (6) to
fail only for bounded implementations.

That seems to imply a need to be able to force the dynamic accessibility of an
access value to a particular level. At the moment, I don't see how I could
implement the correct accessibility for an unbounded vector:

     package Another_Container is
         type Data_Access is array (Positive range <> of aliased Element;
         type Vector is tagged record
             Length : Natural := 0;
             Data : Data_Access := null;
         end record;

         function Reference (Obj : Vector; Index : Natural) return access Element;
     end A_Container;

     package body Another_Container is
         function Reference (Obj : Vector; Index : Natural) return access Element is
         begin
             if Index > Obj.Length then raise Constraint_Error; end if;
             return Obj.Data.all(Index)'Access;
         end Reference;
     end Another_Container;

The 'Access here would have library-level accessibility, and that is *exactly*
what we don't want. It would make all of the calls in the example work without
raising an exception, but of course that is nonsense given that (1) for instance
would save an access value that would outlive the container object. That would
be very, very bad. (And of course changing from the unbounded to the bounded
version would cause Program_Error to appear all over the place; that surely is
bad, too.)

But I don't see any way to write this to give the returned access the right
accessibility level (the level of the call). A local type is too nested, and I
don't see anything else that could be written. So I think adopting AI05-0051-1
would also mean that we would need an additional way to control accessibility.
But I can't quite imagine what form that should take.

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

From: Tucker Taft
Date: Thursday, November 20, 2008  8:40 PM

>> When you take 'Access of a formal parameter of a tagged type, it is
> treated like a local variable.
>
> I don't think this can be right; it would make it impossible to return
> 'Access of part of a parameter with an anonymous access type, and that
> can't be right:

Well that is that is the way it works.  You have to use 'Unchecked_Access if you
want anything better than that, or we have to start passing accessibility levels
with all tagged type parameters, not just access parameters.

>     function Fooey (A : in A_Tagged_Type) return access A_Tagged_Type is
>         Local : aliased A_Tagged_Type;
>     begin
>         if Clock > Morning then
>             return Local'Access; -- (1)
>         else
>             return A'Access; -- (2)
>         end if;
>     end Fooey;

If you want to do something like this, then make the parameter into an access
parameter:

    function Fooey(A : access A_Tagged_Type) ...

> I would hope that the return at (1) would raise Program_Error no
> matter where the function is called. But I would hope that (2) would
> in fact work if the call to Fooey is used appropriately. So I'm
> assuming that by local variable, Tucker means "local to the call" and not "local to the function".
> Right?

Sorry.  Even 'Unchecked_Access would fail unless you made the parameter an IN
OUT parameter (unless the return type was "access constant..."), and of course
that is yet another AI... ;-). Without an accessibility level passed in, there
is no way we can do anything interesting, which means an access parameter.

> My interest stems from looking at container-like uses. Imagine the
> following bounded container (I've left out the generics and private
> types to make the interesting stuff easier to see):
>
>      package A_Container is
>          type Vector (Capacity : Natural) is tagged record
>              Length : Natural := 0;
>              Data : array (1..Capacity) of aliased Element;
>          end record;
>
>          function Reference (Obj : Vector; Index : Natural) return
> access Element;
>      end A_Container;
>
>      package body A_Container is
>          function Reference (Obj : Vector; Index : Natural) return
> access Element is
>          begin
>              if Index > Obj.Length then raise Constraint_Error; end if;
>              return Obj.Data(Index)'Access;
>          end Reference;
>      end A_Container;
>
> Surely we want this return to work, but we also don't want the
> accessibility of the access returned to be very long, because we don't
> know how long Obj will live (and we surely don't want to have to pass
> dynamic accessibility with all parameters!).

You are definitely stuck unless you change the return type to "access constant
Element".  And 'Unchecked_Access will be needed. Alternatively, make Vector an
access parameter and both problems go away.

> It's easy to see this if we look at some uses of this package:
>
>     with A_Container;
>     package Test is
>         Lib_Level_Vector : A_Container.Vector;
>
>         type Lib_Access is access Element;
>
>     end Test;
>
>     package body Test is
>        O : Lib_Access;
>        procedure Nest is
>            Nested_Vector : A_Container.Vector;
>            type Local_Access is access Element;
>            Local : Local_Access;
>        begin
>            O := Nested_Vector.Reference (10); -- (1)
>            O := Lib_Level_Vector.Reference (10); -- (2)
>            Local := Nested_Vector.Reference (10); -- (3)
>            Local := Lib_Level_Vector.Reference (10); -- (4)

If you are going to use prefix notation, then making the parameter an access
parameter is pretty easy, since you get the implicit "'Access" provided for you.
But you will have to declare Nested_Vector "aliased."

>            declare
>                Inner : access Element := Nested_Vector.Reference (10);
> --
> (5)
>            begin
>                O := Inner; -- Statically illegal currently.
>                Local := Nested_Vector.Reference (10); -- (6)
>                Local := Lib_Level_Vector.Reference (10); -- (7)
>            end;
>     begin
>        O := Lib_Level_Vector.Reference (10); -- (8)
>        Nest;
>     end Test;
>
> Since Reference will assume its parameter is a local variable at the
> point of the call, calls (1) and (2) will raise Program_Error (for an
> accessibility failure), even though (2) is actually OK. (3) and (4)
> will work, as will (5) and (8).

Your assumption (that Reference assumes its parameter is a local variable at the
point of call) is wrong, so it will be hard for me to comment on your
conclusions.  I could give answers based on assuming the Obj parameter is an
access parameter.  If we make that assumption, then it carries its accessibility
level with it.  You would have to declare Lib_Level_Vector and Nested_Vector
both as "aliased," so the implicit 'Access associated with the prefixed calls
would be legal.  With these assumptions, (1) would raise P_E, (2), (3), and (4)
would be fine. (5) is also fine, but even if we change accessibility levels of
local anonymous access objects to be dynamic, the assignment "O := Inner;" would
raise P_E.  (6) and (7) would also be fine, as the point of call is irrelevant,
only the accessibility level of the actual (presuming, again, Obj is an access
parameter).  (8) is also fine.

> ... But, oddly, (6) and (7) will raise Program_Error
> (because the block is more nested than the subprogram level where the
> access type is declared). While that is OK for my intended purpose
> (which is similar to (5)), it looks like a significant maintenance
> hazard -- I add and remove blocks all the time expecting nothing about
> the execution of the enclosed statements to change. That property seems to be violated here.
>
> Do I understand this all correctly??

No, see above.

> In general, this definition works well for containers, because we
> surely do not want the returned access to be usable for very long.
> Keeping it to a local scope prevents any long-lived accesses from
> being created, and that greatly reduces (but does not eliminate) the danger of dangling accesses.

I am beginning to lose you...  Could you review the rest in the context of what
I said above, and resend that which still applies?

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

From: Randy Brukardt
Date: Thursday, November 20, 2008  10:59 PM

...
> Well that is that is the way it works.  You have to use
> 'Unchecked_Access if you want anything better than that, or we have to
> start passing accessibility levels with all tagged type parameters,
> not just access parameters.

Thanks for answering.

I don't see any need to pass accessibility with tagged type parameters (although
it surely would help). Besides, it isn't solely a tagged type problem (as my
examples show).

My point was, however, that dynamic accessibility is based on the stack, and
clearly parameters live longer (at least until the innermost master of the call)
than local objects. I think you're talking about the static accessibility of the
parameters, but that is (or should be) irrelevant - mixing of static and dynamic
accessibility gets one nowhere. (As demonstrated by AI05-0051-1 itself.)

If the current proposal doesn't do this as I suggested, I certainly suggest that
we consider changing it so that this will work as I outlined. It would provide
some justification the serious amount of work to implement the AI05-0051-1. (I
don't see any right now.)

> >     function Fooey (A : in A_Tagged_Type) return access A_Tagged_Type is
> >         Local : aliased A_Tagged_Type;
> >     begin
> >         if Clock > Morning then
> >             return Local'Access; -- (1)
> >         else
> >             return A'Access; -- (2)
> >         end if;
> >     end Fooey;
>
> If you want to do something like this, then make the parameter into an
> access parameter:
>
>     function Fooey(A : access A_Tagged_Type) ...
>
> > I would hope that the return at (1) would raise Program_Error no
> > matter where the function is called. But I would hope that (2) would
> > in fact work if the call to Fooey is used appropriately. So I'm
> > assuming that by local variable, Tucker means "local to the
> call" and not "local to the function".
> > Right?
>
> Sorry.  Even 'Unchecked_Access would fail unless you made the
> parameter an IN OUT parameter (unless the return type was "access
> constant..."), and of course that is yet another AI... ;-).
> Without an accessibility level passed in, there is no way we can do
> anything interesting, which means an access parameter.

No *&%$#ing access parameters in good code. We've discussed that plenty; I still
think it is criminal to force a storage management strategy on clients (and
writing 'Access all over the place is not something that any sane programmer is
going to do, especially as they'll be getting random Program_Errors for
mysterious reasons).

And 'Unchecked_Access is completely unacceptable here; there is no sane reason
to define an access that can dangle completely asynchronously to the rest of the
program. No one can use such a thing; anyone calling such a function simply has
an erroneous program that might work the first hundred times its tested and fail
on the 101st.

But I don't understand your contention; we surely can do plenty interesting with
the semantics I thought was intended. It's surely true that the semantics you
are suggesting are useless for any purpose at all in a good interface (such as
Ada.Containers). But then why are we bothering to try to make this work? Just
make it illegal in any case that is trouble and forget it. Why define something
that will take many man-months to implement and is completely useless in new
programs???

> > My interest stems from looking at container-like uses. Imagine the
> > following bounded container (I've left out the generics and private
> > types to make the interesting stuff easier to see):
> >
> >      package A_Container is
> >          type Vector (Capacity : Natural) is tagged record
> >              Length : Natural := 0;
> >              Data : array (1..Capacity) of aliased Element;
> >          end record;
> >
> >          function Reference (Obj : Vector; Index : Natural) return access Element;
> >      end A_Container;
> >
> >      package body A_Container is
> >          function Reference (Obj : Vector; Index : Natural) return access Element is
> >          begin
> >              if Index > Obj.Length then raise Constraint_Error; end if;
> >              return Obj.Data(Index)'Access;
> >          end Reference;
> >      end A_Container;
> >
> > Surely we want this return to work, but we also don't want the
> > accessibility of the access returned to be very long, because we
> > don't know how long Obj will live (and we surely don't want to have
> > to pass dynamic accessibility with all parameters!).
>
> You are definitely stuck unless you change the return type to "access
> constant Element".  And 'Unchecked_Access will be needed.
> Alternatively, make Vector an access parameter and both problems go
> away.

That doesn't make anything go away, because then it is trivial to keep a pointer
after the object goes away. And I don't know of any way to detect or prevent
that problem. With the very short accessibility I was expecting, the pointer
would be good so long as it was used immediately (or saved in a block). That's
exactly what's wanted in most cases (it doesn't eliminate every problem -- such
as the element being deleted -- but it comes close enough that I would have been
willing to drop my opposition to it).

To make it extremely clear, I was trying to see if there was any way for an accessor function like
    function Reference (Obj : in out Vector; Index : Natural) return access Element; or
    function Reader (Obj : Vector; Index : Natural) return access constant Element; to be used safely. That would be the easiest way to define iterators. But if it doesn't work safely, putting it into the spec of the containers is a non-starter for me. So 
obviously, I'll have to look for another solution.
(There is another problem anyway, so the whole idea might not work.)

...
> > In general, this definition works well for containers, because we
> > surely do not want the returned access to be usable for very long.
> > Keeping it to a local scope prevents any long-lived accesses from
> > being created, and that greatly reduces (but does not eliminate) the
> > danger of dangling accesses.
>
> I am beginning to lose you...  Could you review the rest in the
> context of what I said above, and resend that which still applies?

Fair enough. If accessibility continues to work as you described, then there
will never be an access-returning function in the containers (or anywhere else
that I have anything to do with), as it would be too unsafe. However, the
semantics I was thinking was intended would allow the returned access to have
exactly the right lifetime (certainly the same or shorter than any possible
object that was passed). That would be pretty safe, outside of a direct deletion
of a node from the container, or an unchecked deallocation of the whole
container. Not perfect, but perhaps good enough. The current rules mean that we
rely totally on the programmer (and in fact, any programmer that can touch the
object) to do the right thing, and that is not (IMHO) what Ada is about.

The rest of what I was writing as in consequence of that idea (that it could be
made safe-ish). Mainly that it would be necessary to control the accessibility
of the returned access even for things that live longer, so that the
accessibility of the returned object is consistent even if the implementation
changes. That seemed impossible to do for "point of call".

I note that is a significant problem with AI05-0051-1 as currently written: the
accessibility of the implementation "leaks out" of a function that returns an
anonymous access object. You can tell if the object was allocated from a global
pool, whether it was part of some other object (or nested pool), or whether it
was allocated solely for the return. That seems nasty for maintenance.

Anyway, I hope you can understand the problem I was trying to solve; perhaps
there is a good idea somewhere that I hadn't thought of. But if the rules stay
as you currently think they are, I will definitely have to adopt a "no anonymous
access-to-object types in any profile" style rule. Anything else is madness (the
only reason to pay for dynamic accessibility is if it does something useful).

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

From: Pascal Leroy
Date: Friday, November 21, 2008  12:25 AM

> No *&%$#ing access parameters in good code. We've discussed that plenty; I
> still think it is criminal to force a storage management strategy on clients

And I still don't understand this.  Storage management issues don't come from
using access parameters, they come from using allocators.  Tuck's example didn't
have any storage management problems.  I am sympathetic to the notion that
having to add "aliased" all over the place is a nuisance, but it's not like it
ruins your entire architecture.

>To make it extremely clear, I was trying to see if there was any way for an
>accessor function like
>   function Reference (Obj : in out Vector; Index : Natural) return access Element; or
>   function Reader (Obj : Vector; Index : Natural) return access constant Element;
>to be used safely. That would be the easiest way to define iterators. But if
>it doesn't work safely, putting it into the spec of the containers is a
>non-starter for me.

I don't have the context of the discussion (last time I checked the minutes of
the Portland meeting were not on the web site) but I don't see how this differs
from the current situation with Cursors.  If you retain a Cursor on a deleted
container (or element) you're hosed.  In retrospect I think we should have
forced implementation to detect invalid cursors.  That's the only added value of
having an abstraction like Cursor and not a naked pointer (and screw the
performances).

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

From: Robert I. Eachus
Date: Friday, November 21, 2008  1:29 AM

>If the current proposal doesn't do this as I suggested, I certainly
>suggest that we consider changing it so that this will work as I
>outlined. It would provide some justification the serious amount of
>work to implement the AI05-0051-1. (I don't see any right now.)

The more I see of things like this, the more I like the 'Ref type proposal.  The
containers will use 'Ref values and returning them gives you what you want, even
for limited types.

What needs to be developed further are things like the returning 'Ref of a stack
object.  I think that answer is that the programmer may need to explicitly put a
copy in the heap.  I understand the 'Ref proposal the default heap would have
the same lifetime as the (base) type, which is what you normally want.  But
there may be dynamic level checks involved when assigning a value to a T'Ref
parameter, which is not nice  (I'd much rather have all assignments of some
other access type value to a T'Ref object check that the accessibility level is
the same as for T..

But that is a detail to be resolved it the 'Ref proposal becomes part of the
language.  This is just one more case where T'Ref can simplify programming for
both the programmer and user of an abstraction.

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

From: Randy Brukardt
Date: Friday, November 21, 2008  1:33 AM

> And I still don't understand this.  Storage management issues don't come from using
> access parameters, they come from using allocators.  Tuck's example didn't have any
> storage management problems.  I am sympathetic to the notion that having to add "aliased"
> all over the place is a nuisance, but it's not like it ruins your entire architecture.

"aliased" and 'Access and usually 'Unchecked_Access (because you can never use
'Access in Ada as it is written). But the big issue these days is you can't put
an abstraction that requires access parameters into an Ada.Containers object
without taking over all of the storage management yourself. (Essentially
eliminating the largest benefit of using the containers in the first place.)
That's because Ada.Containers doesn't expose any access values; you have to
store access values in the container but then you have to use allocators to
create those objects and thus take over all of the management. So you're putting
serious constraints on the way storage management is done -- very bad. (I'd
argue that all of that "aliased" junk will cause you to quickly switch to using
allocators just to avoid the noise; that's happened in every Ada program that
I've written using access types. But YMMV on that.)

...
> I don't have the context of the discussion (last time I checked the minutes of the
> Portland meeting were not on the web site)

This *is* the whole context; AI05-0051-1 (and many other, unfortunately) were
not discussed in Portland. You haven't missed anything. This whole topic has
sprung full-blown from my imagination while brushing my teeth, driving to work,
etc. in the morning.

> ... but I don't see how this differs from the current situation with Cursors.  If you
> retain a Cursor on a deleted container (or element) you're hosed.  In retrospect I think
> we should have forced implementation to detect invalid cursors. That's the only added
> value of having an abstraction like Cursor and not a naked pointer (and screw the
> performances).

I don't know of any real Ada.Containers implementations that don't at least
attempt an invalid cursor check. (I think Matt let's you turn them off, but I
believe it's distributed with the cheap checks on.) It's quite cheap to make a
pretty decent check, and while it doesn't catch all bugs, it ought to catch the
vast majority. (Especially of deleted elements; the usual problem is that the
memory of a freed container can be reused and that could make it pass the check.
A deleted element won't match even if it is reused until after a whole lot of
operations have occurred.)

If, however, we have *required* checking, then only a controlled implementation
would do (98% detection wouldn't be good enough), and then cursor operations
would have quite a bit more expensive. So I think this is a case where we got it
exactly right (especially as a full debugging version is possible with perfect
checking when needed).

I admit that my views on this topic are somewhat colored by our experience with
Claw, where objects really do disappear asychronously (when the wetware clicks
that X in the corner, the window objects go away whether you have pointers to
them or not). Avoiding that was a real headache, and that caused us to avoid
(visible) access types in all but one case (and in that case, the access is
paired with a lock object; use the access after the lock is freed is not allowed
-- but I'd really like a way to enforce that).

In this particular case, I was looking at using such a routine as part of the
iterator abstraction, and that means that it would only be used in a tampering
context. So the only problem would occur if the object being iterated was
actually Unchecked_Deallocated while you are using it, and that has been
erroneous since the beginning of (Ada) time. Other cases are already required to
be detected by the containers libraries. So that's not worth worrying about
(whomever allocated it should be worrying about that, anyway).

The problem I am having is that I can't hide the existence of this routine. This
is something I don't want the users of the containers to use, but various Ada
rules prevent putting it into the private part of the containers. So I was
hoping to make it safe.

But instead, Tucker tells me it won't work at all. So it is back to the drawing
board (unless I can convince him to make a change somewhere).

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

From: Tucker Taft
Date: Friday, November 21, 2008   7:35 AM

I guess I don't understand your allergy to access parameters, while we are
talking about AI-51 which was all about access discriminants and access results.

Be that as it may, I agree we could interpret IN parameters as though they were
local to the call site rather than the callee, and that wouldn't be a
significant disturbance to the model.  But IN parameters are pretty limiting,
since the result is necessarily then limited to "access constant", and you
showed that it made various uses illegal that were safe. If we added IN OUT
parameters, that would address the "access constant" problem, though it would
still be limiting in accessibility, but you seem content with those limitations.

One of the features of AI-51 was that static accessibility checks weren't
affected, only dynamic ones.  Your suggestion, which I agree can fit within the
model, does imply a change in the static accessibility rules.  Essentially you
are saying that there is a new static accessibility level that corresponds to
the parameters, which is shallower than the locals, but deeper than up-level
references, and that on returning an access result, the static level of the
returned object need only be as shallow as that of the parameters, rather than
as that of the level where the function is declared.  I'll let you do the
wording... ;-)

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

From: Jean-Pierre Rosen
Date: Friday, November 21, 2008   8:14 AM

[...]
> Essentially you are saying that
> there is a new static accessibility level that corresponds to the
> parameters, which is shallower than the locals, but deeper than
> up-level references

So, are we going to have fractional (non integer) accessibility levels now?
Going this way, they might even become complex in the future (in the
mathematical sense of course, for the usual meaning they are already :-)

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

From: Randy Brukardt
Date: Friday, November 21, 2008  4:27 PM

> I guess I don't understand your allergy to access parameters, while we
> are talking about AI-51 which was all about access discriminants and
> access results.

I described that in my reply to Pascal's message. But YMMV.

> Be that as it may, I agree we could interpret IN parameters as though
> they were local to the call site rather than the callee, and that
> wouldn't be a significant disturbance to the model.  But IN parameters
> are pretty limiting, since the result is necessarily then limited to
> "access constant", and you showed that it made various uses illegal
> that were safe.

Well, that's not quite true, because a designer of an abstraction could use a
Rosen-trick or controlled initialization to get a writable access. We do exactly
this in Claw. But that clearly would not be possible for the bounded containers
(which are neither controlled nor limited).

> If we added IN OUT parameters, that would address the "access constant"
> problem, though it would still be limiting in accessibility, but you
> seem content with those limitations.

Actually, I'm looking for a way to *get* those limitations (consistently, no
matter what the actual 'Access is); I don't want these pointers to live longer
than the dereference that they're used in. That (in a tampering context)
addresses the safety issues quite well.

> One of the features of AI-51 was that static accessibility checks
> weren't affected, only dynamic ones.  Your suggestion, which I agree
> can fit within the model, does imply a change in the static
> accessibility rules.  Essentially you are saying that there is a new
> static accessibility level that corresponds to the parameters, which
> is shallower than the locals, but deeper than up-level references, and
> that on returning an access result, the static level of the returned
> object need only be as shallow as that of the parameters, rather than
> as that of the level where the function is declared.  I'll let you do
> the wording... ;-)

I'm unconvinced that static accessibility would need to change, but maybe it
does in a way that I don't see. The main issue is that dynamic accessibility and
static accessibility are so different that mixing them always seems to lead to
trouble. In this case, the only mixing needs to be at the point of the
accessibility check (if any) where the function result is used; I don't see any
reason to talk about it at all inside the function. What I would do is say that
for the purposes of determining the accessibility of 'Access with a result type
of an anonymous access type with dynamic accessibility (access parameters,
access return, possibly others coming), you ignore the static accessibility
completely and use the dynamic accessibility of the prefix, which is defined to
be the master of the call for a parameter, and the master of the declaration for
other declarations. (In the wording, "dynamic accessibility" is just written
"accessibility", so that admittedly will be confusing.) think only the wording
for 'Access needs to change, and not by very much. But the devil is in the
details here...

There needs to be some way to specify this accessibility for type conversions as
well, given the desire to make 'Access equivalent to an access conversion, and
also the need to define for language-defined libraries what the acceptable
accessibility of the result is. (Else you cannot know what safe uses of the
function are from its specification, which is madness.) That seems like the
messy part, given that we're talking about stuff that is anonymous.

I'll try wording on this line someday soon when I feel like beating my head
against a wall until bloody. :-)

****************************************************************
========= End of thread ==========
== New thread: An outline of proposals for ideas (4) and (5) ===
(Call-level accessibility and specifying accessibility)
****************************************************************

From: Randy Brukardt
Date: Tuesday, February  3, 2009  6:10 PM

I'm writing this note somewhat less formally than a full AI (although I've
organized the middle part somewhat like an AI to simplify my life in the future
should we decide to adopt any of this.)

I've spent a lot of time thinking about accessibility issues, particularly in
the context of anonymous access types, and one thing that strikes me is that we
seem to be defining how these work in an ad-hoc manner, based mostly on what
seems to work. I have started to wonder if the reason for this problem is simply
that it isn't possible to define the accessibility of access types (that is, the
accessibility of what they designate) by default; we need to know the intended
use. Thus I started investigating what would happen if we would allow the
programmer to *declare* what accessibility they want an access type to have.

I've constructed this proposal as a thought experiment in order to see what
would happen if I took that idea to its logical conclusion. Thus, I've included
everything in this proposal that seems to make sense. I don't really expect to
adopt such a large change as-is; I expect that we'll take some (many?) ideas
from it, but unlikely to keep them all.

I think the most important ideas are specifying accessibility for access types,
and the idea of call-level accessibility. In particular, I have been trying to
figure out how we can write an accessor function for the elements of a container
(and any solution has to work for bounded containers). Call-level accessibility
and the associated changes elsewhere would be one way to do this (although
personally I'd prefer a solution without any access types - I've previously
described how that could be accomplished).

P.S. You may want to read this with your favorite adult beverage. It's not light
reading. I surely am going to go have an adult beverage now... ;-)

!problem

Accessibility of access types and that of their designated objects never seems
to be quite what is needed for an application. Many projects simply use
'Unchecked_Access exclusively, because the accessibility checks do nothing other
than get in the way. Moreover, we have a hodge-podge of default rules for
anonymous access types: we have different rules for access discriminants, access
parameters, access results, and other anonymous access types. And some of these
rules require run-time overhead.

The result is that it is hard to tell what will happen with the accessibility of
a particular combination of anonymous access types. In addition, the dynamic
accessibility checks for access parameters can be a hazard: they may add an
implicit contract to a subprogram (that designated objects passed are
library-level, for instance) that may not be documented at all, and in any case
can't be checked at the call-site. But, many subprograms have no need for the
dynamic accessibility check (if they only dereference the access value, for
instance, which is common in functions since they don't have "in out"
parameters), so the overhead is wasted. Either way, more control would be
valuable.

AI05-0051-1 proposes to add dynamic accessibility checks to access results to
fix various language holes. Obviously, this is adding more overhead, yet again
the function may only be intended to provide a writable reference to an object
(and not to be save somewhere). Indeed, there are cases where we do not want to
allow saving the access value at all. In addition, this proposed rule exposes
the implementation of the function by exposing the accessibility of the returned
object - without providing any way to set the accessibility to preserve
portability.

!proposal

Add a mechanism to optionally specify the accessibility of the designated
objects of an access type.

[Note that I talk about the "accessibility of the designated objects", as that
is what we care about in accessibility checks. For other purposes (such as
finalization of allocated objects), the access type will still have the
accessibility of the location of its declaration. Note that we can only specify
an accessibility that is longer than the point of the declaration of the access
type, so that shouldn't cause a problem.]

Add an optional accessibility clause to access_definition and
access_to_object_definition:

access_definition ::=
    [null_exclusion] access [constant] subtype_mark [accessiblity_clause]

access_to_object_definition ::=
    access [general_access_modifier] subtype_mark [accessiblity_clause]

accessibility_clause ::= with *accessibility_static_string_*expression

This would look something like:

    Param : access My_Type with "call-level accessibility";

[Notes: I'm using a static string here because I don't want to define a mess of
reserved words, and I don't want to add a type to Standard for this. The latter
idea was to add an enumeration Accessibility_Type to Standard and use that in an
accessibility clause:

accessibility_clause ::= with *accessibility_*expression [of *entity_*name]

That would look something like:

    Param : access My_Type with call_level_accessibility;

The need for the extra entity clause makes this less appealing. Of course, if we
were to drop that capability, it becomes more interesting.]

If no accessibility clause is given, the rules would remain as they are now
(thus, this proposal has no compatibility issues).

The following static strings are allowed as
"accessibility_static_string_expression"s:

"library-level accessibility"
   The accessibility of the designated objects of the access type is
   library-level. Conversions into the type are statically checked to be
   library-level; this follows the current model of accessibility checking in
   Ada (note that in a few cases, such checks are actually dynamic). Note that
   whenever we talk about "converting to an access type" we're also talking
   about 'Access and the checks on the object prefix of the attribute; the
   checks are essentially the same. We'll only talk about converting to a type
   in order to simplify the presentation.

"current accessibility"
   The accessibility of the designated objects of the access type is that of the
   location of the declaration. This is the default for all named access types
   and many kinds of anonymous access types, and corresponds to the current
   rules for those cases. Accessibility checks for conversions into the type are
   statically checked.

"accessibility of <entity-name>"
   Entity-name must statically denote an entity that is visible at the point of
   the access declaration. The entity must be a subprogram, stand-alone object,
   or type. The accessibility of the designated objects is that of the specified
   entity (for an access type, if it has an accessibility_clause that is used).
   Accessibility checks for conversions into the type are statically checked.
   This allows the definition of any arbitrary static accessibility level, so
   long as it is shallower than the current point. [Note: I really wanted this
   to just match any existing access type, so <entity-name> could just be the
   name of an access type. However, since we have those dang anonymous access
   types, we have to allow other entities like subprograms and objects here.]

"call-level accessibility"
   This accessibility definition is only allowed in anonymous access parameters
   and anonymous access results. The accessibility of the designated objects of
   the access type is that of the call of the enclosing callable object for
   access parameters, (note that we have modified that accessibility in a few
   cases, see the details in the discussion section), and that of the function
   result for an access result. This is statically checked when converting into
   the type. This is the default accessibility for anonymous access results.

   [This kind of accessibility is new; it eliminates the need for dynamic
   checking (and the overhead of passing levels) for access parameters and
   mostly eliminates that overhead for access results. A detailed definition
   (but not wording yet - I'll want to see if there is any major flaws with the
   definition first) is given in the discussion section. Note that I've
   suggested to use it as the default for anonymous access results, which
   currently are broken. That is appropriate for "accessor functions" (where the
   interesting result is the designated object, and there is no further use for
   the pointer), but not when the access value is the actual intended return.
   Thus we still need to define a dynamic version as in AI05-0051-1; both
   accessibilities work for their intended purpose but solve different (and
   almost non-overlapping) problems. Also note that it isn't clear which purpose
   is more common; I've selected this as the default mainly because it has much
   less overhead -- if in doubt, choose the cheaper alternative.]

   We don't provide this accessibility for named types, as it would be very
   confusing as to which subprogram's calls are being referred to. It also could
   mean that an access type could collect objects with different accessibility,
   depending on what call they were in when the access was created. That would
   be a nightmare to implement properly and would completely defeat the purpose
   of the accessibility level (to provide fully static checking).

"dynamic accessibility"
   The accessibility of the designated objects is maintained dynamically. Each
   access object includes an indication of its accessibility. This accessibility
   definition is allowed only for access parameters (where it means what it does
   currently); for access results (where it means something like the AI05-0051-1
   definition); and for objects of an anonymous access type (where it means
   something like Tucker's dynamic object proposal). This is the default meaning
   for anonymous access parameters. (The others are pending other new
   proposals.)

   [I discuss an alternative to this definition in the discussion below.]

[These items are intended to cover all of the existing cases; did I miss any?]


Storage pools:

If an accessibility clause is given for a named access type, any storage pool
specified has to have the accessibility of the designated objects. (That is, it
cannot be a local object if the designated objects are library-level.) This is
checked in the same way that a conversion to the access type is checked. If the
type does not have a storage pool specified, the pool is chosen by the
implementation in an implementation-defined manner, but the pool chosen must
have at least the accessibility of the designated objects.

"and no pool" may be added to any of the above accessibility strings. This means
the same as specifying Storage_Size to be 0 (that can't be done explicitly for
anonymous access types) -- specifically, allocators are illegal for the type.
This should have been the default for anonymous access types, but it is too late
to change that now.


!discussion

I think that in new code, all access types should specify their intended
accessibility. That would eliminate confusion about what it the accessibility is
and avoid incorrect assumptions about capability. That is especially true for
anonymous access types where there are many different kinds of accessibility
chosen by default (and AI05-0051-1 would add another, and Tucker wants to add
another for objects). That does suggest that a simpler syntax would be
preferable; I'll leave that to others.


Detailed discussion of call-level accessibility:

For an anonymous access parameter, call-level accessibility means the
accessibility of the parameters of the call. (We modify this accessibility in
certain cases for functions with anonymous access results, see below.) This
requires no checking at the point of call; any object that exists at the point
of the call will necessarily live long enough. An allocator of such a parameter
(if allowed) would have the accessibility of the point of the call (this is
unchanged from Ada 95/2005 other than in cases with anonymous access results).

Converting an anonymous access parameter with call-level accessibility to
another access type (recall that we are including 'Access when we say "convert
to an access type") triggers an appropriate accessibility check (of course, the
type of check depends on the accessibility of the target type). If the target
type is has some form of static accessibility (other than call-level
accessibility, we'll cover that below), we'll need a static accessibility check
(along with a dynamic check as in existing Ada). The check will succeed if the
access type is local to the subprogram (or a nested subprogram) and fail
otherwise. This can almost always be checked statically (I can't think of a case
where it cannot, as a generic formal access type would necessarily be outside of
the subprogram and thus would fail the check). When making these checks, we
don't assume anything about the call site, so we don't allow converting to any
type with a longer life than the call, which is anything visible outside of the
subprogram call.

One can imagine that call-level accessibility is essentially a static level
half-way in between the level of the current subprogram and whatever the outer
level is. It could even be implemented that way if it was passed to an anonymous
access parameter with dynamic accessibility. (Since the number of nesting levels
is small, there should be plenty of bits available to represent half-levels.)
Only access parameters and results could have this half-level (and since access
parameters are *in* parameters, you can't assign into those), so this has little
impact on what is allowed.

The basic idea for anonymous access results is the same, but a number of factors
make it a bit more complex.

The accessibility of an anonymous access result is that of the result object.
For purposes of accessibility checks within the function, we assume the worst
and expect that that result has the accessibility of the innermost master
surrounding the call (and thus, no outside access types match). This leads to
the following:

For a static check (and its associated dynamic part) of an existing value E, the
following rules hold:

* The check succeeds if E is of the type of an anonymous access parameter of the
  function. (It doesn't matter whether it has call-level or dynamic
  accessibility -- unless we expand the definition of dynamic accessibility --
  see below.)
* The check succeeds if E is of the type of an anonymous access result of a
  function call (this happens if the function call is immediately within the
  return statement). (It doesn't matter whether it has call-level or dynamic
  accessibility - in the former case, a static check will have occurred within
  the called function, and in the latter case, a dynamic check will have
  occurred within the called function. We don't need to repeat it.)
* If E is of an access type with static accessibility and has deeper
  accessibility than the function result (that is, the access type of E is local
  to the function), then the check succeeds. If E is of an access type with
  static accessibility and does not have deeper accessibility (E is outside of
  the function), then the check fails.
* If E is of an access type with dynamic accessibility other than those defined
  above, I don't know: there are no such types currently and what check is
  needed depends on how they are defined.

For a static check (and its associated dynamic part) for the Object Obj of an
access attribute, the following rules hold:

* The check succeeds if Obj is (part of) a parameter of the function. (That is, returning
  'Access of a parameter or a part of a parameter is always legal for accessibility purposes.)
* The check succeeds if the master that elaborated the function is deeper than Obj. (You can
  return the 'Access of any object that is visible outside of the function.)
* Otherwise the check fails. (Can't return 'Access of local objects.)


If an allocator is used to create the designated object of an anonymous access
return that has call-level accessibility, that object needs to have the lifetime
of the return object. It cannot live longer than the return object. That means
that whatever mechanism is used for returning objects of unknown size (such as
unconstrained arrays) can be used here as well. (This is often a secondary
stack.)


As mentioned earlier, we are proposing to change the master of a call that
returns an anonymous access value (could be just calls that return call-level
anonymous access value, although that would make the dynamic version less
useful) to be the same as the return value. This is necessary so that a
parameter (such as an anonymous access parameter) or part of a parameter (such
as 'Access of a component) can be returned from such a function.

The primary reason that function results and their parameters have different
accessibility is to eliminate parameters sticking around for long periods for
build-in-place constructor functions. By-copy types like access types should not
be built in place (see aside below), so they do not require this particular
change. There is a potential for parameters of a renames of a function result to
stick around forever, but that could in fact be necessary:

      El : Element renames Accessor (Create_Vector(...)).all;

If Accessor is returning a portion of the Vector object returned by
Create_Vector, we surely do not want that vector finalized before the renames
goes out of scope. In addition, this change is limited only to anonymous access
results and has no effect when existing objects are passed (it would mainly
affect aggregates, function results, and allocators used in calls). These are
relatively rare cases when put together, and this limited negative effect seems
worth the benefit of being able to write functions like Accessor. Also note that
if the result of such a function is not renamed, its lifetime will be quite
short, so the parameters will still be finalized at almost the same point they
currently are.

I considered making this change for all pass-by-copy return types (as they don't
need the special cases for build-in-place), but that would be a serious problem
for code-shared generics. That would mean that the master of the parameters of a
call could depend on the actual type for a formal private type of the generic
(if there is a function that returns that type). That surely would be a
nightmare to implement. OTOH, since anonymous access results must syntactically
appear in the profile of the function, whether or not they are used cannot
depend on the actuals of an instance.

Changing this for all functions would not work, as it would mean that parameters
for constructors would stick around as long as the constructed object. This
could be a significant waste of space, and for types that aren't built-in-place,
a totally useless one. Also note that accessibility checks would prevent
returning part of a parameter if the function is returning a *named* access
type, so there is no need for the change in that case.

Aside:
I think that we should not allow build-in-place for functions that return
elementary types. (This would require a modification to 7.6(17.4/3)). We do not
allow "pass-in-place" (that is, pass-by-reference) for elementary parameters, so
it is odd that we would allow function results to act differently. Indeed, we
already do not allow the build-in-place permissions for elementary types in
return statements (no early exceptions); so we're already part of the way there.
(Of course, such a statement cannot prevent "as-if" optimizations; we're just
not going to allow any visible effects of such a change.) I think it would be
best to make it clear that no elementary function returns are ever
build-in-place.


Note that this change of accessibility for parameters implies that a function
call used to initialize a call-level anonymous access return must have any
temporary parameters live as long as the function result. Such parameters
presumably would have to be allocated and managed by whatever mechanism is used
to return objects of an unknown size. While this has a fairly obvious
implementation, it is likely to be a pain in the butt.

An alternative to changing the master of temporary parameters in this case would
be to adopt a legality rule banning temporary life parameters for functions with
anonymous access results. Such parameters are rare, and working double-time to
make them work may not be worth the effort - surely a legality rule would be
easier to implement. The obvious rule would make passing a composite or access
parameter with an accessibility less than that of the function result illegal
for such functions. (By-copy parameters aren't an issue.) In that case, we'd
leave the accessibility rules alone. That would ban parameters that are
aggregates, allocators (for anonymous access types), and function calls. The
latter is why I didn't pursue this idea; it seems fairly draconian to not allow
function calls. One could tighten up the rule further: for composite types, we'd
only need to ban types that are tagged, have an access discriminant, or have an
aliased component (these are the only ones that we an take a part of); of
course, we'd still ban anonymous access allocators. One could also imagine
enforcing these rules only when a function call is used to set another return
object (that is the nastiest case) and changing the master for other calls.


Alternative definition of dynamic accessibility:

One could consider trying to unify dynamic accessibility. That has some
performance incompatibilities (and a significant implementation issue), which is
why I didn't do it (well, actually, I did do it, but then abandoned it at the
11th hour...). It seems appealing on the surface, as it would allow runtime
detection of dangling pointers, while also allowing anything that makes sense.

The definition would be:
   The accessibility of the designated objects is maintained dynamically. Each
   access object includes an indication of its accessibility.

   There is no limitation on the accessibility of the storage pool for the such
   an access type. Allocated objects get the accessibility of the given storage
   pool, or, if no pool is specified, the accessibility of the access type.
   [This latter rule is simply to make the accessibility portable in the default
   case.]

   Accessibility checking occurs when converting to an access type with static
   accessibility. It also occurs when one of these access values is
   dereferenced; the accessibility of the master of the dereference must be the
   same as or deeper than that of the access value.

This brings up several implementation issues. The first is that since this can
be applied to any access type, we have to do generalized accessibility checks
(including handling incomparable objects). That probably would require checks
that were mostly handled out of line, but need not be too expensive, as many
entities will be library-level or declared in the same task (where a check is
just a compare of stack addresses).

Note that having this capability makes access parameters more expensive, as they
could be passed a dynamically accessible value that belongs to some other access
type which may have existed for a long time. We could mitigate this somewhat by
pushing the existence check to the point of the conversion to the anonymous
access parameter type. But that would be somewhat weird, as a dereference
exception could be raised for an entity which is never actually dereferenced by
the program.

However, there is a bigger problem that I didn't think of until I had written
this up. If a function is called multiple times from a single calling
subprogram, there would be no way in my intended implementation to tell between
objects created in the current call or in earlier calls. This would look
something like:

     type Global is access Integer with "dynamic accessibility";
     Obj : Global;

     procedure Called (P : Natural) is
        Local : aliased Integer := P;
     begin
        if P = 1 then
           Obj := Local'access; -- OK, will make dynamic checks.
        elsif Obj.all = 0 then -- Make check here.
           null;
        end if;
     end Called;

     Called (1);
     Called (2); -- Will use a dangling point, an object from Called (1).

My implementation suggestion was to keep a stack address (representing a master)
and a task id for each dynamic address created. But that doesn't work here,
because Called (1) and Called (2) probably will have the same stack address.

One could fix that with a per-task call counter that would get incremented any
time a call that contains a conversion into an dynamically accessible access
type. That is either a distributed overhead (if done for all subprograms) or
fairly complex. The counter value would have to be part of the dynamic
accessibility representation. Then checks would have to take that into account
as well (if it doesn't match and the addresses do, the check fails). But I'm
unconvinced that would work for an arbitrary call tree -- and at this point I
decided it wasn't worth trying to figure out how to make it work.

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

From: Bob Duff
Sent: Sunday, February 15, 2009  12:06 PM

> P.S. You may want to read this with your favorite adult beverage. It's 
> not light reading.
> I surely am going to go have an adult beverage now... ;-)

I assume you mean coffee.

> !problem
> 
> Accessibility of access types and that of their designated objects 
> never seems to be quite what is needed for an application. Many 
> projects simply use 'Unchecked_Access exclusively, because the 
> accessibility checks do nothing other than get in the way.
> Moreover, we have a hodge-podge of default rules for anonymous access types:
> we
> have different rules for access discriminants, access parameters, 
> access results, and other anonymous access types. And some of these 
> rules require run-time overhead.

> accessibility_clause ::= with *accessibility_static_string_*expression
> 
> This would look something like:
> 
>     Param : access My_Type with "call-level accessibility";
> 
> [Notes: I'm using a static string here because I don't want to define 
> a mess of reserved words, and I don't want to add a type to Standard 
> for this. The latter idea was to add an enumeration Accessibility_Type 
> to Standard and use that in an accessibility clause:

I find the use of string literals in the proposed syntax to be rather ugly.
You could add Accessibility_Type somewhere less intrusive than Standard
-- just invent a new package.

> accessibility_clause ::= with *accessibility_*expression [of 
> *entity_*name]
> 
> That would look something like:
> 
>     Param : access My_Type with call_level_accessibility;
> 
> The need for the extra entity clause makes this less appealing. Of 
> course, if we were to drop that capability, it becomes more 
> interesting.]
> 
> If no accessibility clause is given, the rules would remain as they 
> are now (thus, this proposal has no compatibility issues).
> 
> The following static strings are allowed as
> "accessibility_static_string_expression"s:
> 
> "library-level accessibility"
>    The accessibility of the designated objects of the access type is 
> library-level.
>    Conversions into the type are statically checked to be 
> library-level; this follows
>    the current model of accessibility checking in Ada (note that in a 
> few cases, such
>    checks are actually dynamic). Note that whenever we talk about 
> "converting to an
>    access type" we're also talking about 'Access and the checks on the 
> object prefix
>    of the attribute; the checks are essentially the same. We'll only 
> talk about
>    converting to a type in order to simplify the presentation.
> 
> "current accessibility"
>    The accessibility of the designated objects of the access type is 
> that of the
>    location of the declaration. This is the default for all named 
> access types
>    and many kinds of anonymous access types, and corresponds to the 
> current rules for
>    those cases. Accessibility checks for conversions into the type are 
> statically
>    checked.

The default accessibility level for an anonymous access type should be the
level of the designated subtype (normally library level).  This is a key
mistake in the current design.

> "accessibility of <entity-name>"
>    Entity-name must statically denote an entity that is visible at the 
> point of the
>    access declaration. The entity must be a subprogram, stand-alone 
> object, or type.
>    The accessibility of the designated objects is that of the 
> specified entity (for
>    an access type, if it has an accessibility_clause that is used).
> Accessibility
>    checks for conversions into the type are statically checked.  This 
> allows the
>    definition of any arbitrary static accessibility level, so long as 
> it is shallower
>    than the current point.
>    [Note: I really wanted this to just match any existing access type, 
> so <entity-name>
>    could just be the name of an access type. However, since we have 
> those dang anonymous
>    access types, we have to allow other entities like subprograms and 
> objects here.]

You could use "acessibility of Standard" instead of "library-level accessibility".
You could use "acessibility of X" to mean "current accessibility", for a suitably
placed X.

And then use syntax like:

accessibility_clause ::= at name

In other words, if there's always a name that means what you want to mean, we don't
need the string literal syntax.

> "and no pool" may be added to any of the above accessibility strings. 
> This means the same as specifying Storage_Size to be 0 (that can't be 
> done explicitly for anonymous access types) -- specifically, 
> allocators are illegal for the type. This should have been the default 
> for anonymous access types, but it is too late to change that now.

Sounds useful.

It should have been the default for ALL access types, but it's WAY too late for that
now.

You could say that if there's an accessibility_clause, it implies "no pool".

> !discussion
> 
> I think that in new code, all access types should specify their 
> intended accessibility.
> That would eliminate confusion about what it the accessibility is and 
> avoid incorrect assumptions about capability. That is especially true 
> for anonymous access types where there are many different kinds of 
> accessibility chosen by default (and
> AI05-0051-1
> would add another, and Tucker wants to add another for objects). That 
> does suggest that a simpler syntax would be preferable; I'll leave 
> that to others.

The syntax sure is getting verbose:

    procedure Mumble (X : not null access constant My_Type with "library-level
                      accessibility" and no pool);

Almost entirely specifying stuff that should have been the default.

The C programmer who says "char *x" is going to laugh at us.

> Detailed discussion of call-level accessibility:
> 
> For an anonymous access parameter, call-level accessibility means the 
> accessibility of the parameters of the call. (We modify this 
> accessibility in certain cases for functions with anonymous access 
> results, see below.) This requires no checking at the point of call; 
> any object that exists at the point of the call will necessarily live 
> long enough. An allocator of such a parameter (if allowed) would have 
> the accessibility of the point of the call (this is unchanged from Ada 
> 95/2005 other than in cases with anonymous access results).

I'm not sure I see the point of call-level accessibility.  Why not use an 'in out'
parameter?  (Presuming AI05-0143-1 is approved -- it's titled "It's BAAACCKK!!:
In Out parameters for functions".)

Maybe it's for access results, which I do not understand.

> Converting an anonymous access parameter with call-level accessibility 
> to another access type (recall that we are including 'Access when we 
> say "convert to an access
> type") triggers
> an appropriate accessibility check (of course, the type of check 
> depends on the accessibility of the target type). If the target type 
> is has some form of static accessibility (other than call-level 
> accessibility, we'll cover that below), we'll need a static 
> accessibility check (along with a dynamic check as in existing Ada). 
> The check will succeed if the access type is local to the subprogram 
> (or a nested subprogram) and fail otherwise. This can almost always be 
> checked statically (I can't think of a case where it cannot, as a 
> generic formal access type would necessarily be outside of the 
> subprogram and thus would fail the check). When making these checks, 
> we don't assume anything about the call site, so we don't allow 
> converting to any type with a longer life than the call, which is 
> anything visible outside of the subprogram call.
> 
> One can imagine that call-level accessibility is essentially a static 
> level half-way in between the level of the current subprogram and 
> whatever the outer level is. It could even be implemented that way if 
> it was passed to an anonymous access parameter with dynamic 
> accessibility. (Since the number of nesting levels is small, there 
> should be plenty of bits available to represent half-levels.)

type Accessibility_Level is delta 0.5 range 0.0 .. Max; for
   Accessibility_Level'Small use Accessibility_Level'Delta;

;-)

My overall impression: Some good ideas, but way too complicated to implement
(as is -- maybe it can be simplified, but I don't understand the details
well enough to know).

All this accessibility level stuff is to prevent dangling pointers being
created by '[Unchecked_]Access.  But the primary use of access types is to
create heap-allocated data structures.  In fact, that's all we had in Ada 83,
and we got along OK.  So the primary way of creating dangling pointer bugs is
to call Unchecked_Deallocation prematurely.  Makes me think we are inventing
large amounts of stuff that completely misses the point.

Keep in mind that we could also implement garbage collection.  (There's an open
ticket at AdaCore to do so.)  If we keep complicating the accessibility rules,
implementing a GC starts looking "simple".  I realize, of course, that there
are some embedded applications that are allergic to GC.  But for other
applications, it sure would be nice to remove one of the major advantages of
Java and C# over Ada.  I'd rather spend my time implementing GC than
implementing arcane dynamic accessibility checks.  (OK, I admit I'm exaggerating
-- an efficient GC is a LOT of work.)

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

From: Tucker Taft
Sent: Wednesday, March 18, 2009  9:38 PM

Randy made the interesting suggestion that we make the accessibility
level you get when you take 'Access of a part of a parameter be that
of the call, rather than that of a local variable of the subprogram.
It took me a while to understand the point, but I think I get it now
and I don't see any major problem with this, and it does seem to
provide additional flexibility.

This distinction didn't really make much difference in Ada 95, nor
even in Ada 2005, but in the context of AI05-51, where we propose that
the accessibility check performed against a function result of an
anonymous access type is determined by the context of the call, this
distinction would allow something like:


     type T is record
        ...
        C : aliased Comp_Type;
     end record;


     function Cee(X : T)
       return access constant Comp_Type is
     begin
        return X.C'Access;  -- currently illegal
     end Cee;


     procedure P(Y : access constant Comp_Type);

     ...

     P(Cee(X));

That is, the accessibility check on the function result would succeed,
so long as the context of the call is not expecting the result to outlive
the innermost master of the call.

This gets more interesting if functions can have in-out parameters, by
the way, since then the function result type in this example could be
"access Comp_Type" rather than "access constant Comp_Type."  Probably
that would argue for only allowing this "extra" level for parameters that
are required to be passed by reference, e.g. are of a tagged record type.

****************************************************************
========= End of thread ==========
== New thread: Comments on the AI (everything mixed together) ===
****************************************************************

From: Bob Duff
Sent: Sunday, February 15, 2009  7:20 AM

This is a comment (a rant, really ;-) ) primarily on AI05-0138, "Improving
accessibility", but I think there are several related AI's.

I think anonymous access types were a mistake in Ada 95, and a bigger mistake
in Ada 2005.  I am concerned that the ARG will add a whole new set of
complicated rules for Ada 201X, causing a whole lot of implementation
complexity for no real user benefit.

Franco proposed the 'Ref idea (see first e-mail in the appendix).
It lists seven problems with anonymous access types.  I've listed some more
problems below.

Tucker proposed dynamic accessibility for local anonymous access variables,
which largely solves _one_ of the problems.  But it seems to me if we're not
going to solve all or nearly all of the problems, we should give up.
(I'm also concerned about efficiency.  It sounds easy enough to optimize
away most dynamic accessibility, in principle, but I fear compilers might not
have the right information at the time they are doing the necessary flow
analysis.  I'm not sure whether GNAT would do it in the front end, or in gigi.)

I think it is impossible to solve all the problems without serious incompatibility.
If I'm proven wrong, that's great. But adding a lot of complexity is not a
solution -- part of the problem is that we have too much already, in this area.

I think Franco's proposal fixes nearly all of the problems, and is reasonably
simple.  AdaCore might go ahead and implement it as an experiment -- it's
based on an attribute, so it's an allowable implementation-defined extension.

We could try to fix anonymous access types.
Or we could go with Franco's proposal.
But at this point, I think perhaps the best solution is to do nothing --
Ada really doesn't need a whole new layer of complexity!

Let's not repeat the mistake, making anonymous access types more usable, but
still not usable enough to be worth the trouble.

----------------

Problems in addition to the ones Franco mentioned:

Using anonymous access types does not allow you to eliminate named access types --
you still need to declare a named access type for every interesting type (where
"interesting" = "you want to put some objects of that type in the heap").

Anonymous access types do not eliminate the need for bogus explicit type
conversions; conversion that can't fail (up the tagged type hierarchy) should be
implicit. I suppose you can use .all'Access, but that's kind of ugly.

The above two items were the main purpose of introducing anonymous access types
all over the place in Ada 2005, but they don't work.

I think anonymous access types are fundamentally confusing, because (for
parameters and discriminants, and perhaps function results) they have various
special properties.  It's just confusing that "X: access T"
means something different from "X: Access_T".  Doubly so, because the special
properties are different depending on what X is.

Access parameters have dynamic accessibility, which is never what you want.
It's just a tripping hazard, and it breaks abstraction. Also, we violate the
normal principle that if you're going to have a run-time check, there ought
to be some way to check it explicitly with an 'if' statement, as in "if X in
A'Range then A(X) := A(X) + 1; else...".

The accessibility rules are confusing.  When people say "new T", they expect
it to allocate an object in a global heap, to be explicitly reclaimed later.
But in some cases, the object gets automatically reclaimed (and finalized) early.
This is just not what people expect of "new".  It can easily cause accidental
creation of dangling pointers.  (Tucker points out that this malfeature existed
in Ada 95, so people are used to it. But it's only for parameters in Ada 95,
and you don't normally pass "new T" as an actual parameter.  The issue is much
more common in Ada 2005, and still just as confusing.)  (Actually, I now see
it's even worse than I thought -- the time of automatic deallocation, if any,
is not portable!)

There's some discussion about what "return access T" means, with the possible
introduction of yet more confusing and complex accessibility rules.  I've no
idea what this feature means, so I'm afraid to use it myself.

Coextensions have some cool properties, but the number of Ada programmers in
the world who understand them well enough to actually make use of them can be
counted on the fingers of one hand.

(This one is not specifically about anonymous types.)  If you say "new" for
one access type, convert to another type, and then Unchecked_Deallocate from
the other type, it is implementation dependent whether this is erroneous.
In most compilers, it works just fine, and in fact people do it all the time.
To me, this is just an unacceptable non-portability.

----------------

Randy is fond of pointing out that access types should always be hidden, and 
therefore it doesn't matter if they're a pain in the neck.  I don't agree with
that view -- in my opinion, if your abstraction involves reference semantics,
hiding that fact is confusing.  Anyway, I'm not trying to convince Randy that
he's wrong -- Randy can hide his access types if he likes.  I'm just trying to
point out that Randy's point of view is a minority one, so it would be wrong
to base the language design on that narrow view.

Nor do I buy Randy's argument that Ada.Containers can replace nearly all access
types.

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

From: Randy Brukardt
Sent: Monday, February 16, 2009  8:22 PM

...
> Problems in addition to the ones Franco mentioned:
> 
> Using anonymous access types does not allow you to eliminate named 
> access types -- you still need to declare a named access type for 
> every interesting type (where "interesting" = "you want to put some 
> objects of that type in the heap").
> 
> Anonymous access types do not eliminate the need for bogus explicit 
> type conversions; conversion that can't fail (up the tagged type 
> hierarchy) should be implicit.
> I suppose you can use .all'Access, but that's kind of ugly.
> 
> The above two items were the main purpose of introducing anonymous 
> access types all over the place in Ada 2005, but they don't work.

Yes, I agree. I especially worry that when we (the ARG) said that the
anonymous access was getting too complex, they were simplified. But now
it turns out that those simplifications don't work, so now we're supposed
to swallow the entire original mess. I would have voted to remove it had
I had any idea that those simplifications (especially for function results)
were not going to work.

...
> Randy is fond of pointing out that access types should always be 
> hidden, and therefore it doesn't matter if they're a pain in the neck.  
> I don't agree with that view -- in my opinion, if your abstraction 
> involves reference semantics, hiding that fact is confusing.

I don't believe that any abstraction fundementally involves reference semantics
(which is just a artifact of programming languages anyway). If it appears
to do so, it probably is not well designed.

> Anyway, I'm not trying to convince Randy that he's wrong -- Randy can 
> hide his access types if he likes.  I'm just trying to point out that 
> Randy's point of view is a minority one, so it would be wrong to base 
> the language design on that narrow view.

I would not argue that point, however.

> Nor do I buy Randy's argument that Ada.Containers can replace nearly 
> all access types.

The only time where Ada.Containers (with extensions) would not be appropriate
is when the performance of those containers is insufficient for the
application. (That should be a rare occurrence.) In that case, you might
need to use access types and some kind of hand-built storage mechanism. But
if that is the case, you surely don't want dynamic anything on those access
types, so the anonymous access types we currently have are almost certainly
not helping anything. (This is the same reason that I'm dubious about the
value of garbage collection in Ada - if you're writing a lot of code that
needs it, you are doing something wrong.)

The only other reason to avoid using the containers is because they are
hard to use in some application. That we can fix, and indeed most of my
emphasis has been on adding missing functionality to the containers and
to the language to make the containers easier to use. (If it helps make
other abstractions easier to use, that's even better.)

If you know of some other reason for using access types rather than
containers that are not covered by one of these two, I'd surely like
to know what it is so it can be addressed in *this* cycle.

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

From: Randy Brukardt
Sent: Monday, February 16, 2009  11:01 PM

...
> Randy is fond of pointing out that access types should always be 
> hidden, and therefore it doesn't matter if they're a pain in the neck.

I forgot to respond to this statement in my previous mail.

I don't recall ever saying that "it doesn't matter if access types are
a pain the neck". I think named access types work just fine, and could
be even better with some pool enhancements. Anonymous access types
don't buy anything that named ones don't, except a few less type
conversions. They isn't worth the accessibility mess. Of course, if your
access types are hidden, you don't need to write any type conversions
for them, so named access types are just fine.

Of course, looking at it another way, access types are a pain in the neck
simply because dynamic allocation and deallocation lead to all kinds of
problems. Short of mandating garbage collection, that pain isn't going
anywhere. But at least hiding them as much as possible, and using
containers as much as possible, reduces the pain to managable levels.
(And no language changes are needed.)

Please note that I agree with Bob's conclusions, even if he somewhat
misrepresents my position.
 
****************************************************************

From: Randy Brukardt
Sent: Monday, February 16, 2009  10:53 PM

...
> (This one is not specifically about anonymous types.)  If you say 
> "new" for one access type, convert to another type, and then 
> Unchecked_Deallocate from the other type, it is implementation 
> dependent whether this is erroneous.  In most compilers, it works just 
> fine, and in fact people do it all the time.  To me, this is just an 
> unacceptable non-portability.

If the designated types are the same, and the pool is the same, I would
expect it to work everywhere. (And I'm not sure why you think it wouldn't
in that case.) OTOH, if the designated types are different, that means
that the Size parameter to the Deallocate routine of a pool could be
different than was allocated. And obviously, if the pool uses that
parameter for something, havoc will occur. Indeed, the pre-Windows
versions of Janus/Ada all would scramble memory in that case, because
that parameter is the only way that they knew how much memory to put
on the free list. Obviously, if too much was put there, bad things
would happen. That's the definition of erroneous in my book.

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

From: Bob Duff
Sent: Tuesday, February 17, 2009  7:51 AM

> > Anyway, I'm not trying to convince Randy that he's wrong -- Randy 
> > can hide his access types if he likes.  I'm just trying to point out 
> > that Randy's point of view is a minority one, so it would be wrong 
> > to base the language design on that narrow view.
> 
> I would not argue that point, however.

That's all I'm asking -- we can agree to disagree about access type
philosophy, while agreeing to rein in the endless growth in complexity
of them.  ;-)

> The only other reason to avoid using the containers is because they 
> are hard to use in some application. That we can fix, and indeed most 
> of my emphasis has been on adding missing functionality to the 
> containers and to the language to make the containers easier to use. 
> (If it helps make other abstractions easier to use, that's even 
> better.)

That is a worthy goal.

> If you know of some other reason for using access types rather than 
> containers that are not covered by one of these two, I'd surely like 
> to know what it is so it can be addressed in *this* cycle.

No, please don't try to make containers applicable to _all_ current uses
of access types.  Containers are about containment.

Whenever you use an access type to "refer" to something ("reference
semantics"), I don't see how a container can be applicable.  E.g., in a
compiler, you might have a Usage_Name node, which contains a component
pointing to a "symbol table entry".  The Usage_Name does not "contain" the
symbol table entry, it points to (refers to) it.

It doesn't have to be an access type -- it could be an index or something,
but whatever it is, there's no "containment" relationship.

Similar example: remember the "doctors/patients" thing?  A doctor
conceptually contains a set of references to its patients, which might
be implemented as a "set" container, but it's a set of _references_,
not a set of patients.  (The patients don't disappear when the doctor does,
for ex.)

So I'm not asking for new container gizmos to deal with this.
The "worthy goal" above is to make containers easier to use for cases
involving "containment".

Another example is recursive (treeish) data types:

    type Binary_Op_Expression is new Expression with
        record
            Op: ...;
            Left, Right : Expression'Class; -- Illegal!
        end record;

That's illegal, so I'd use an access type.  I saw the talk about
Tree containers in some other thread, but I don't think anything like
that would apply here.  I'm not asking for any language change, here.
We already have plain old records to use as "containers" in this case,
and it's not so horrible that we have to use access types to break up the
recursive cases.

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

From: Bob Duff
Sent: Tuesday, February 17, 2009  8:01 AM

> > (This one is not specifically about anonymous types.)  If you say 
> > "new" for one access type, convert to another type, and then 
> > Unchecked_Deallocate from the other type, it is implementation 
> > dependent whether this is erroneous.  In most compilers, it works 
> > just fine, and in fact people do it all the time.  To me, this is 
> > just an unacceptable non-portability.
> 
> If the designated types are the same, and the pool is the same, I 
> would expect it to work everywhere. (And I'm not sure why you think it 
> wouldn't in that case.) OTOH, if the designated types are different, 
> that means that the Size parameter to the Deallocate routine of a pool 
> could be different than was allocated. And obviously, if the pool uses 
> that parameter for something, havoc will occur. Indeed, the 
> pre-Windows versions of Janus/Ada all would scramble memory in that 
> case, because that parameter is the only way that they knew how much 
> memory to put on the free list. Obviously, if too much was put there, 
> bad things would happen. That's the definition of erroneous in my book.

I'm talking about the case where there are no user-defined storage pools.
And we have:

    type Root is ...;
    type Root_Ref is access all Root'Class;

    type Child is new Root ...;
    type Child_Ref is access all Child'Class;

    type Grandchild is new Child...;
    ...etc

Now suppose "new" is done for Child_Ref, then the result is converted
(or .all'Access-ed) to Root_Ref.  Much later, we Unchecked_Dealloc the
Root_Ref value.

This works just fine on many compilers, including GNAT.  And people do
it all the time.  But the RM allows some compilers to make it erroneous.
That's the "unacceptable non-portability" I'm talking about.

I don't understand your concern about the size.  You need to calculate
the size to pass to Deallocate based on the Tag, using a dispatching call
(or equivalent).

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

From: Bob Duff
Sent: Tuesday, February 17, 2009  8:03 AM

> Please note that I agree with Bob's conclusions, even if he somewhat 
> misrepresents my position.

I apologize for misrepresenting your position, and I'm still not sure I
fully understand it.  But that's OK -- let's concentrate on the "conclusions",
as you say.

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

From: Randy Brukardt
Sent: Tuesday, February 17, 2009  3:11 PM

...
> > If you know of some other reason for using access types rather than 
> > containers that are not covered by one of these two, I'd surely like 
> > to know what it is so it can be addressed in *this* cycle.
> 
> No, please don't try to make containers applicable to _all_ current 
> uses of access types.  Containers are about containment.
> 
> Whenever you use an access type to "refer" to something ("reference 
> semantics"), I don't see how a container can be applicable.  E.g., in 
> a compiler, you might have a Usage_Name node, which contains a 
> component pointing to a "symbol table entry".  The Usage_Name does not 
> "contain" the symbol table entry, it points to (refers to) it.

Right, that's what a container cursor is for. When I'm talking about containers,
I'm always talking about containers + cursors. Cursors take over the role
of access types in most cases, having the advantage that they can be checked
and detect most dangling cursors. The containers handle the storage management.
No muss, no fuss. :-)

> It doesn't have to be an access type -- it could be an index or 
> something, but whatever it is, there's no "containment"
> relationship.

It's the cursor of the container, of course. I think you are thinking too
narrowly. Keep in mind that you can do pretty much anything with a container
cursor that you can do with an access type -- except conveniently dereference it
(something I'd like to fix!!).

> Similar example: remember the "doctors/patients" thing?  A doctor 
> conceptually contains a set of references to its patients, which might 
> be implemented as a "set" container, but it's a set of _references_, 
> not a set of patients.  (The patients don't disappear when the doctor 
> does, for ex.)
> 
> So I'm not asking for new container gizmos to deal with this.
> The "worthy goal" above is to make containers easier to use for cases 
> involving "containment".

We don't need one; we already have it. The thing here is to remember that the
data is somewhere (that's the container) and the relationships are the cursors
to that container.

> Another example is recursive (treeish) data types:
> 
>     type Binary_Op_Expression is new Expression with
>         record
>             Op: ...;
>             Left, Right : Expression'Class; -- Illegal!
>         end record;
> 
> That's illegal, so I'd use an access type.  I saw the talk about Tree 
> containers in some other thread, but I don't think anything like that 
> would apply here.

This is *exactly* the sort of case that the tree containers are intended to
handle. See my HTML example in that proposal (now AI05-0136-1). It's very
similar to this example. It's not quite as convenient in the sense that naming
the subcomponents takes more work (you'd probably want to write accessor functions
called "Left" and "Right"), but again you don't have to do any memory management
nor worry about dangling pointers. So I think you're usually ahead of the game.

> I'm not asking for any
> language change, here.  We already have plain old records to use as 
> "containers" in this case, and it's not so horrible that we have to 
> use access types to break up the recursive cases.

But the point is that it isn't really necessary. Lists and Trees are (or should
be) handled by the containers. I'm sure there must exist data structures that
aren't handled by the containers (some sort of network graph comes to mind), but
how often do you need one of those? I'd guess pretty much never. If we can make
95% reasonably easy and reasonably safe (and reasonably efficient), I don't
think it matters much how hard and messy the rest are. (And it is pretty good
already, so long as you stay away from those anonymous abominations.)

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

From: Randy Brukardt
Sent: Tuesday, February 17, 2009  3:19 PM

...
> I'm talking about the case where there are no user-defined storage 
> pools.
> And we have:
> 
>     type Root is ...;
>     type Root_Ref is access all Root'Class;
> 
>     type Child is new Root ...;
>     type Child_Ref is access all Child'Class;
> 
>     type Grandchild is new Child...;
>     ...etc
> 
> Now suppose "new" is done for Child_Ref, then the result is converted 
> (or .all'Access-ed) to Root_Ref.  Much later, we Unchecked_Dealloc the 
> Root_Ref value.
> 
> This works just fine on many compilers, including GNAT.  And people do 
> it all the time.  But the RM allows some compilers to make it 
> erroneous.  That's the "unacceptable non-portability" I'm talking 
> about.
> 
> I don't understand your concern about the size.  You need to calculate 
> the size to pass to Deallocate based on the Tag, using a dispatching 
> call (or equivalent).

I was speaking in general, not some rare special case! I say rare, because
I've always avoided declaring things like Child_Ref; they just make a mess.
Everything uses Root_Ref, right from the beginning. I just use conversions
on the dereference "Child(Root.all)" to get to the components of Child
(assuming I can't use dispatching). Moreover, this sort of thing illustrates
exactly why you shouldn't put the access types visibly into the O-O abstractions
in the first place. If you didn't do something stupid like that, you wouldn't
have these stupid issues.

Anyway, I was thinking more about constrained subtypes. In this case, a type like
   type Root_Only_Ref is access all Root;
If you take a reference to a child pointer and stuff it into this type, then
deallocate, you'll definitely have trouble. Similarly with constrained and
unconstrained subtypes. (I know some of these are illegal to convert, I don't
have time to check out which examples are legal but of course there is always
Unchecked_Conversion.)

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

From: Christoph Grein
Sent: Wednesday, February 18, 2009 12:53 AM

> It's the cursor of the container, of course. I think you are thinking 
> too narrowly. Keep in mind that you can do pretty much anything with a 
> container cursor that you can do with an access type -- except 
> conveniently dereference it (something I'd like to fix!!).

Yes, that's exactly what I'd need for my Safe_Pointers, too.

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


Questions? Ask the ACAA Technical Agent