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

Unformatted version of ai05s/ai05-0138-1.txt version 1.6
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.

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

========= New thread: a whole bunch of ideas discussed =====

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

From: Bob Duff
Sent: Thursday, March 5, 2009  7:49 AM

We've had some internal discussions at AdaCore about anonymous access types. In
preparation for the ARG subcommittee meeting on this subject, here's a summary
of my thinking, which is mostly agreed by others at AdaCore (perhaps not 100%,
but close).

We've already discussed various problems with anonymous access types.
I am currently recommending that people don't use them, except in certain
special circumstances, because they are pretty much useless, and are highly
confusing.

To make them useful, it must be possible for most new programs to entirely avoid
named access types.  And avoid the annoying explicit type conversions that
should be implicit.  And keep the rules simple enough that normal programmers
can understand.  To accomplish this, we need the following:

 1. The accessibility level of all anonymous access
    types (including access parameters and access
    discriminants) should be static.
    The accessibility level should be that of the designated subtype.
    Same for "...return access Blah".  Dynamic accessibility
    is just too confusing, and breaks abstraction.

 2. It must be possible to deallocate, portably/correctly.
    Automatic ("premature") deallocation (and finalization)
    is not wanted.  The X'Free idea could solve this,
    along with eliminating the implementation-definedness
    of storage pools.  The default storage pool for all
    access types (named and anon) should be "the global heap".

 3. Anon access should be allowed for '[in] out' parameters.

 4. Functions need to allow '[in] out' parameters.  This eliminates the primary
    purpose of passing 'Access of local variables to access parameters.

If we don't do ALL of the above, then I am opposed to doing any part of it,
because it makes a lot of extra work for implementers, but leaves anon access in
a state where I will continute to recommend against its use.

For example, Tucker's idea of using dynamic accessibility for local variables of
anon acc type is a good idea.  A real improvement.  But it doesn't solve the
whole problem, so I would continue to recommend Ada programmers avoid this
feature.  So I am opposed to that idea.

Unfortunately, it's difficult to do all this while remaining compatible. One
idea is to have a pragma or something (configuration pragma
Static_Accessibility?) that requests the above static accessibility for access
parameters.  Kind of ugly.

The above is a minimal set of requirements.  There is also a long list of "nice
to haves", which I won't repeat here.

The 'Ref proposal has the advantage of solving all of the above, with full
compatibility.  In addition, it solves most (all?) of the nice-to-haves.
However, it leaves the language in an odd state -- an Ada textbook could tell
people to use 'Ref always, and by the way, there are two useless features (named
and anonymous access types), which should be avoided.  Also kind of ugly.

Franco and Quentin have taught over 100 students object-oriented programming in
Ada, and they say that it is extremely important to fix these problems for Ada
2012.

We may have to tolerate some ugliness in order to do so.  Franco is even willing
to tolerate some incompatibility.  After all, people can simply stick with Ada
95 or Ada 2005 if they like.

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

From: Robert Dewar
Sent: Thursday, March 5, 2009  10:28 AM

> To make them useful, it must be possible for most new programs to
> entirely avoid named access types.

Not sure i agree with that, I suppose as a technical criterion it is of
interest, but I don't like the implication that this would be a reasonable way
to program. In particular, if a private type is an access type that's very
common and just fine.


>  2. It must be possible to deallocate, portably/correctly.
>     Automatic ("premature") deallocation (and finalization)
>     is not wanted.  The X'Free idea could solve this,
>     along with eliminating the implementation-definedness
>     of storage pools.  The default storage pool for all
>     access types (named and anon) should be "the global heap".

I definitely agree
>
>  3. Anon access should be allowed for '[in] out' parameters.
>
>  4. Functions need to allow '[in] out' parameters.  This eliminates the primary
>     purpose of passing 'Access of local variables to access parameters.

yes please please

> If we don't do ALL of the above, then I am opposed to doing any part
> of it, because it makes a lot of extra work for implementers, but
> leaves anon access in a state where I will continute to recommend against its use.

I agree

> Unfortunately, it's difficult to do all this while remaining compatible.
> One idea is to have a pragma or something (configuration pragma
> Static_Accessibility?) that requests the above static accessibility
> for access parameters.  Kind of ugly.

I think it is OK to be incompatible here if we do some investigation of the
extent of problems this incompatibility will cause.

> The 'Ref proposal has the advantage of solving all of the above, with
> full compatibility.  In addition, it solves most (all?) of the
> nice-to-haves.  However, it leaves the language in an odd state -- an
> Ada textbook could tell people to use 'Ref always, and by the way,
> there are two useless features (named and anonymous access types),
> which should be avoided.  Also kind of ugly.

I very much disagree that named access types are useless, I find this very very
odd.

    type Set is private;

    type Set is access Set_Structure;

seems absolutely fine to me, and I don't see how anonymous access types could
possibly help here.

> Franco and Quentin have taught over 100 students object-oriented
> programming in Ada, and they say that it is extremely important to fix
> these problems for Ada 2012.

Well I disagree, anything that does not happen till 2012 has really no influence
as far as I am concerned. Unless this can be done immediately as an addendum and
implemented immediately in GNAT, it will have no effect.

That's why I prefer the 'Ref proposal, we can just do it today, and then it is
available, and the ARG can take its time figuring out what we really should have
done in the first place :-)

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

From: Bob Duff
Sent: Thursday, March 5, 2009  11:09 AM

> I very much disagree that named access types are useless, I find this
> very very odd.
>
>     type Set is private;
>
>     type Set is access Set_Structure;
>
> seems absolutely fine to me, and I don't see how anonymous access
> types could possibly help here.

Good point.  So I'll make an exception for this case.  It's not all that common
-- probably > 95% of all private types are completed with records.  Here, you
might well want Set to be controlled -- otherwise, you have storage leaks, or
you force clients to do memory management.

The real point I'm trying to make is to avoid the dilemma -- should I create a
named access type for pretty-much every type, just in case I might need it later
(because, for example "in out access T" is illegal).  There's no good answer.

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

From: Robert Dewar
Sent: Thursday, March 5, 2009  1:06 PM

I really prefer named access types in almost all cases, e.g. types like

type Task_Id is access all Ada_Task_Control_Block

type Big_String_Ptr is access all String (Positive)

seem fine to me, and in a case like Big_String_Ptr, I think it's good for the
name of the access type to lead back to the following valuable comment:

>    for Big_String_Ptr'Storage_Size use 0;
>    --  A non-fat pointer type for null terminated strings

So I am not sympathetic to the point of view that the goal of anonymous access
types is to eliminate named access types.

On the contrary I plain dislike anonymous access types and would never want to
use them, I accept that the OO folks need them to prevent unnecessary
conversions (I don't understand enough to appreicate this first hand, but it's
fine if people tell me that), but for the code I write, I prefer Ada 83 style.

(and I must say I don't understand how you get pool specific pointers using
anonymous access types, but ...)

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

From: Robert Dewar
Sent: Thursday, March 5, 2009  3:09 AM

To add a little, I had always wanted to add to Ada 83, an attribute, which i
called Access

    Integer'Access

would essentially be a named access type, entirely equivalent to what would be
the case if right after the declaration of Integer, we had

    type Integer'Access is access Integer;

Very easy semantics no strange problems with accessibility or anything else. We
nearly added this to GNAT early on :-)

But I find the anonymous access types in Ada 95 too complex to use :-(

It is definitely wrong to have type String_Access declared all over the place
multiple times (we finally got down to one case in the gnat run time, all except
for the misplaced declaration in the spec of unbounded string, which is a good
example of the horrible use of named access types :-)

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

From: Randy Brukardt
Sent: Thursday, March 5, 2009  3:25 PM

> To add a little, I had always wanted to add to Ada 83, an attribute,
> which i called Access
>
>     Integer'Access
>
> would essentially be a named access type, entirely equivalent to what
> would be the case if right after the declaration of Integer, we had
>
>     type Integer'Access is access Integer;

We actually considered something something like this during the Ada 2005 work (I
forget the name it had). But we ran into the infinite regress problem:

    Integer'Access'Access

and so on. That makes a lot of trouble for compilers (like Rational's) that
materialize everything in their symboltable, since it simply is not possible to
do that. One could try to adopt some sort of rules as to the allowed name prefix
that you can use (say only subtype_marks), but that runs into generic contract
issues:

    generic
       type Priv is private;
    package G is
       A : Priv'Access;
    end G;

    package P is new G (Integer'Access);

One could make that illegal as well, but then you have to use assume-the-worst
in the generic body, making it essentially impossible to use in a generic. So we
pretty much gave up.

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

From: Steve Baird
Sent: Thursday, March 5, 2009  3:52 PM

>  1. The accessibility level of all anonymous access
>     types (including access parameters and access
>     discriminants) should be static.
>     The accessibility level should be that of the designated subtype.

This is just a detail, not a fundamental objection to the proposal, but I think
that people expect that a syntactically constraintless subtype declaration, as
in
    subtype S is T;
should behave like a rename with no interesting dynamic semantics.

Breaking this rule would require changes in the definition of statically
matching subtypes and perhaps statically compatible subtypes. The semantic model
for generic expansions where the binding between a formal and actual type
parameter is thought of as being equivalent to a subtype declaration might also
need revisiting.

In the case of the example above, do you want
    "access S"
to mean something different than
    "access T"
if S happens to be declared in a more nested scope than T?

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

From: Bob Duff
Sent: Thursday, March 5, 2009  5:11 PM

Probably not.

I agree that the rule as I stated is probably not exactly right.
Deserves more thought.

In addition to the issue you mention, what about type extensions that are deeper
than their parent, as in:

    type Nested is new Non_Nested with ...;
    overriding function Blah (...) return access Non_Nested;

Should the accessibility level of the access Non_Nested be that of the root
type?

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

From: Steve Baird
Sent: Thursday, March 5, 2009  7:18 PM

>     type Nested is new Non_Nested with ...;
>     overriding function Blah (...) return access Non_Nested;
>
> Should the accessibility level of the access Non_Nested be that of the
>root  type?

Should this example even be legal?

If you don't want dynamic checks, then don't you have to statically prohibit
the various problematic scenarios which AI05-0051 resolves with dynamic checks?

Or am I being too hasty in jumping to the conclusion that the elimination of
dynamic access checks is one of the consequences of this proposal?

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

From: Randy Brukardt
Sent: Thursday, March 5, 2009  7:34 PM

I was just going to mention the same thing: Bob needs to read and understand as
best he can the problems noted in AI05-0051-1. That's because fixing those
problems is *not* optional; we can't leave the language broken! All of the
other changes that have been discussed *are* optional.

I did propose a way to convert AI05-0051-1 to all static checks, but I didn't
think it was appropriate in all cases. It essentially depended on thinking
similar to Bob's: the accessibility has to be that of a root type.
The problem with that is that if you are actually returning an access type
(as opposed to something that is just "return-by-reference", that pretty
much prevents using any nested extensions. I don't see how that could fly
in general (even though I personally would be happy to limit anonymous
access types as much as possible).

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

From: Bob Duff
Sent: Thursday, March 5, 2009  7:39 PM

> Should this example even be legal?

I don't know.

Note that with the 'Ref proposal, we have more freedom to make troublesome
things illegal.

> If you don't want dynamic checks, then don't you have to statically
> prohibit the various problematic scenarios which AI05-0051 resolves
> with dynamic checks?

I don't understand AI05-0051.  That's why I don't like it -- I fear programmers
are already overwhelmed by the complexity in this area, and AI05-0051
exacerbates the problem.

> Or am I being too hasty in jumping to the conclusion that the
> elimination of dynamic access checks is one of the consequences of
> this proposal?

I think elimination of dynamic accessibility checks is one of the consequences
of this proposal.  I realize that's a radical notion.

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

From: Randy Brukardt
Sent: Thursday, March 5, 2009  8:24 PM

...
> I don't understand AI05-0051.  That's why I don't like it -- I fear
> programmers are already overwhelmed by the complexity in this area,
> and AI05-0051 exacerbates the problem.

Actually, it is very simple: the accessibility of the ultimate use of the
function result determines its accessibility. It's actually a rather cool model,
in that I've had a hard time punching holes in it. OTOH, I really don't want to
see more dynamic accessibility if there is a sane alternative.

But that isn't particularly relevant. You *have to* understand the problems
noted in AI05-0051-1 so that you can propose a solution to them. If you can't
propose such a solution, we really can't go any further with your idea, because
the *language is broken* as it currently stands -- functions can return objects
that don't exist at the call site. That can't remain true (unless we're willing
to just declare anonymous access returns as erroneous - which sounds like
abandoning all hope).

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

From: Bob Duff
Sent: Thursday, March 5, 2009  5:21 PM

> > To add a little, I had always wanted to add to Ada 83, an attribute,
> > which i called Access
> >
> >     Integer'Access
> >
> > would essentially be a named access type, entirely equivalent to
> > what would be the case if right after the declaration of Integer, we
> > had
> >
> >     type Integer'Access is access Integer;

Note that this is basically the same as the 'Ref proposal.

> We actually considered something something like this during the Ada
> 2005 work (I forget the name it had). But we ran into the infinite
> regress
> problem:
>
>     Integer'Access'Access
>
> and so on. That makes a lot of trouble for compilers (like Rational's)
> that materialize everything in their symboltable, since it simply is
> not possible to do that.

Implementation difficulty is important, but I think usefulness for the user, and
simplicitly of the language, should trump that, at least in this case. I find it
hard to believe that implementing T'Ref'Ref'Ref... is _impossible_ in any
compiler.  In GNAT, I don't even think it's all that hard.

Certainly if AdaCore implements the 'Ref proposal as an allowed
implementation-defined extension (which we are still considering), our plan is
to allow this.

>... One could try to adopt some sort of rules as to the allowed name
>prefix that you can use (say only subtype_marks), but that runs into
>generic  contract issues:
>
>     generic
>        type Priv is private;
>     package G is
>        A : Priv'Access;
>     end G;
>
>     package P is new G (Integer'Access);
>
> One could make that illegal as well, but then you have to use
> assume-the-worst in the generic body, making it essentially impossible
> to use in a generic. So we pretty much gave up.

Right, this is a can of worms.  Much simpler to just allow any number of 'Refs,
and force compilers to deal with it on the fly (since it's obviously impossible
to create them all in the symbol table ahead of time).

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

From: Robert Dewar
Sent: Thursday, March 5, 2009  7:10 PM

>> ... One could try to adopt some sort of rules as to the allowed name
>> prefix that you can use (say only subtype_marks), but that runs into
>> generic contract issues:
>>
>>     generic
>>        type Priv is private;
>>     package G is
>>        A : Priv'Access;
>>     end G;
>>
>>     package P is new G (Integer'Access);

I must be dense, but I can't see any possible advantage here in using
Priv'Access instread of defining a local access type to Priv.

>> One could make that illegal as well, but then you have to use
>> assume-the-worst in the generic body, making it essentially
>> impossible to use in a generic. So we pretty much gave up.

Why would you ever use this in a generic

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

From: Randy Brukardt
Sent: Thursday, March 5, 2009  7:19 PM

Good question. Let me rephrase it as "why would you ever use this anywhere?".
Surely the answer to that is the same as in the generic (I don't find anything
all that special about generic bodies).

Personally, I think we should spend our efforts on making the existing (named)
access types safer (dangling pointer detection/prevention) and more functional
(more storage management options). I don't see any compelling reason to use any
form of anonymous access types, especially as modern programs should be using
less of access types in general (as they make it harder to write multitask
programs and harder to prove all programs).

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

From: Robert Dewar
Sent: Thursday, March 5, 2009  7:31 PM

> Good question. Let me rephrase it as "why would you ever use this
> anywhere?". Surely the answer to that is the same as in the generic (I
> don't find anything all that special about generic bodies).

No, I don't think so in practice ...

> Personally, I think we should spend our efforts on making the existing
> (named) access types safer (dangling pointer detection/prevention) and
> more functional (more storage management options). I don't see any
> compelling reason to use any form of anonymous access types,
> especially as modern programs should be using less of access types in
> general (as they make it harder to write multitask programs and harder to prove all programs).

The one case I find useful is where you have a very general access type and you
want different people to share it.

Consider the String_Access in Unbounded_String, it is really ugly that this is
declared in the Unbounded_String spec to me. In the GNAT runtime we have two
declarations of String_Access, one in System.Strings that everyone shares
*except* for the spec of Unbounded_String which has a separate one. I would
write String'Access in this spec.

And I would not need System.Strings.

I don't see ever needing 'Access on a generic formal

(of course it is confusing we are calling this 'Access in this discussion :-)
:-))

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

From: Randy Brukardt
Sent: Thursday, March 5, 2009  8:19 PM

...
> > We actually considered something something like this during the Ada
> > 2005 work (I forget the name it had). But we ran into the infinite
> > regress
> > problem:
> >
> >     Integer'Access'Access
> >
> > and so on. That makes a lot of trouble for compilers (like
> > Rational's) that materialize everything in their symboltable, since
> > it simply is not possible to do that.
>
> Implementation difficulty is important, but I think usefulness for the
> user, and simplicitly of the language, should trump that, at least in
> this case.

<rant> The *user* shouldn't be using any access types in new code (of any kind),
unless they're implementing a new container or ADT. And even then, they should
only use them in the private part/body.</rant>

> I find it hard to believe that implementing T'Ref'Ref'Ref...
> is _impossible_ in any compiler.  In GNAT, I don't even think it's all
> that hard.

I don't think it would be *impossible* in Janus/Ada, but it would mean
abandoning much of our philosophy and some invariants. In particular, the type
portion of the symbol table would balloon to at least 4 times its current size
(at least on programs that used this feature). That would directly contradict
our stated #1 goal of minimizing space usage, both at compile-time and run-time.

I realize that in these days of more plentiful memory, that is not a popular
goal (maybe even pointless). However, if we abandoned that goal completely,
there would no longer be any justification for keeping Janus/Ada around: it can
never be as good at creating *fast* programs as GCC, nor could it ever have as
many features/tools/etc as GNAT. (This is the same reason that I continue to
stick to generic code sharing, despite the pain it causes -- we need at least
someone showing alternative things that are possible.)

Thus, this would be a serious pain to implement in Janus/Ada -- except I can't
imagine doing it absent a lot of customer demand. It just seems completely
pointless.

Bob had said long ago:

> Franco and Quentin have taught over 100 students object-oriented
> programming in Ada, and they say that it is extremely important to fix
> these problems for Ada 2012.

And Robert said:

> I accept that the OO folks need them to prevent unnecessary
> conversions (I don't understand enough to appreicate this first hand,
> but it's fine if people tell me that)

I don't understand this well enough either. But I am not willing to put features
into Ada simply because a few people make a claim that no one seems to be able
to understand!

I would really like to understand *exactly* what problems that they are running
into that cannot be better solved by eliminating the access types from the O-O
programs. I know I asked Franco this explicitly back in November, and his answer
was not responsive. A quote: "As for "doing OO without access types" as
suggested by Randy, that's simply impossible in practice if you want to use
dynamic dispatching." The problem is that that statement is not remotely true,
you can do all of the dynamic dispatching you want using indefinite containers
to hold class-wide objects. Moreover, Claw is totally about dynamic dispatching
(for callbacks), but there are very few interfaces that use access types, and
many of our example programs use statically allocated objects. Yes, there is a
single named access type internally to Claw, but never any allocators,
deallocators, conversions, or any of the other issues that are claimed to be
important.

(If Ada allowed stand-alone classwide objects and components, the situation
would be even more clear-cut. I'd prefer doing that to messing anymore with
access types. It would be fairly easy to do in Janus/Ada, but I realize that
allocate-the-max compilers might have a tough time. Anyway, you can use a Holder
container for those things, but I understand that is not completely ideal. But
we could potentially change the language so that use of containers is easier
rather than access types.)

So I continue to see that people are trying to force the bad habits of other
languages into Ada. A better choice would be see how best to do it in Ada. That
means showing the *problem* (in the abstract), and letting me and others see
what the best solutions are. All I've been hearing is "it's too hard to write
our programs in Ada 2005", without any explanation as to why that is. That makes
it impossible to see if there is a real problem or just bad methodology.

I'm perfectly willing to believe that I'm wrong. But no one wants to explain
why.

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

From: Robert Dewar
Sent: Thursday, March 5, 2009  9:23 PM

> <rant> The *user* shouldn't be using any access types in new code (of
> any kind), unless they're implementing a new container or ADT. And
> even then, they should only use them in the private part/body.</rant>

I find that a very peculiar rant indeed, I strongly disagree, I don't particular
want to argue the point, just to register that the above is not received fact,
but rather personal opinion that is definitely NOT shared by all.

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

From: Randy Brukardt
Sent: Thursday, March 5, 2009  10:12 PM

I *did* mark it with <rant>!

The quickie explanation: Exporting access types from an ADT (abstract data type)
greatly constrains the storage management choices of the client. They can't let
a container manage the storage at all (even if they put the access type into the
container, they'd still have to manage the storage). And it makes using
stack-based allocation (that is, ordinary variables) much less convenient.

If the ADT wants to manage storage itself, it has to use some sort of controlled
handles in order to ensure that the storage is freed appropriately and the like.
Again, no access types exported.

If the client wants to use an access type and dynamic storage management, let
then declare the access type themselves in an appropriate place.

For a package that is an AST (abstract state machine), objects usually aren't
exported at all. So again there is no reason to export access types.

I suppose you might want to export access types in packages that are neither
ADTs nor ASTs (that is, don't export private types of some sort), but these are
likely to be highly coupled and thus are generally considered bad.

Returning an access to an existing object in an ADT or AST is unsafe. There is
no way in Ada to ensure that the object does not get destroyed while someone is
holding an access to it - no matter who (the client or the ADT itself) is doing
the storage management, there is no way to prevent copying such an access into a
long-lived place (which makes the safety problem much worse), and there is no
way to inform the ADT that the access is no longer needed. [I'd like to find a
solution to some of these issues, whether it uses access types or not, but all
of the proposals I've seen makes this problem worse.]

You could use access types in an ADT *iff* you have guaranteed garbage
collection and thus simply could completely forget about storage management. But
that's some other language, not Ada.

The net is that I don't see much good reason for visible access types in new
code. (Under the hood in the implementation is a different story.) And thus I
don't see much reason for making them easier to use (as opposed to safer).

I realize that I hold a minority view here, and I would like to have a better
understanding of why. But no one seems able to come up with a counter-argument
to the above points, they just say "I disagree" and stop the conversation. It's
almost like everyone has given up on allowing the client any flexibility in
structuring programs. (I saw the same sort of thing in the old MFC libraries,
but I blamed that on C-think. Perhaps it is something deeper?)

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

From: Robert Dewar
Sent: Friday, March 6, 2009  12:34 PM

> The quickie explanation: Exporting access types from an ADT (abstract data
> type) greatly constrains the storage management choices of the client.
> They can't let a container manage the storage at all (even if they put
> the access type into the container, they'd still have to manage the
> storage). And it makes using stack-based allocation (that is, ordinary
> variables) much less convenient.

This is a reasonable viewpoint for high level ADT's with approrpiate high level
interfaces, but Ada is about low level code too. As reasonable examples of
access type use, consider Interfaces.C.Strings. char_array_access. Sure you
could make this a high level ADT, but that would be bad design in this case.
Another example is indeed String_Access in unbounded strings, no point in
dressing that one up in some fancy interface.

Not all the world is containers!

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

From: Ed Schonberg
Sent: Friday, March 6, 2009  12:44 PM

No, but it is a reasonable goal to raise the level of the language by having
containers that take care of more storage management. I'm in sympathy with
Randy's goals : it is a good thing to make pointers less visible where possible.
After all, high-level languages don't have them!

The problem is that high-level languages have garbage collection, and in their
absence containers must depend on controlled types, and this is relatively heavy
machinery. The new bounded containers might help,  but then more of the storage
management burden is on the programmer.   As usual, no free lunch.

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

From: Robert Dewar
Sent: Friday, March 6, 2009  1:02 PM

Sure, I agree with that, but Randy was being much too absolute. I dislike it
when programming language folks tell you "I have this great new
feature/language, but to use it you must abandon using xxx", it's as though a
hardware store tried to sell you a great new plumbing tool that would solve all
your problems, and then tell you that if you buy it you must abandon all your
other tools :-)

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

From: Randy Brukardt
Sent: Friday, March 6, 2009  2:17 PM

...
> Not all the world is containers!

Of course. I was speaking in terms of the current discussion. Franco made the
claim that we need to "fix" anonymous access types because of issuing using
access types in OOP programming. Now, I can't speak for everybody, but the next
low-level code that I see that is written using OOP will be the first! So I
don't think the initial claim holds much water - I still think high-level ADTs
should be done with a minimum of access types, and using OOP means that you are
building high-level ADTs.

Later you said:
> Sure, I agree with that, but Randy was being much too absolute. I
> dislike it > when programming language folks tell you "I have this
> great new feature/language,
> but to use it you must abandon using xxx", it's as though a hardware
> store tried
> to sell you a great new plumbing tool that would solve all your
> problems, and then tell you that if you buy it you must abandon all
> your other tools
:-)

I'm sure that's true; qualifying a statement like that properly means it loses
all impact (great sound bites aren't qualified!). I did except bodies from it
(one hopes that most lower-level code is encapsulated). And I surely was not
talking about low-level packages that aren't designed for reuse (I like to call
them "sloppy" packages, or "just throwing code"); those whatever you need to do
is what you need to do. And that surely includes access types.

So don't abandon your access types, but do try to keep them tightly under
control (limited to a single package, ideally).

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

From: Robert I. Eachus
Sent: Saturday, March 7, 2009  10:11 AM

>I realize that I hold a minority view here, and I would like to have a
>better understanding of why. But no one seems able to come up with a
>counter-argument to the above points, they just say "I disagree" and
>stop the conversation. It's almost like everyone has given up on
>allowing the client any flexibility in structuring programs. (I saw the
>same sort of thing in the old MFC libraries, but I blamed that on
>C-think. Perhaps it is something deeper?)

I am strongly on Randy's side here. Many years ago I named Unchecked_Conversions
in package specifications, Unchecked_Perversion.  The reason?  It means that
throughout any program that uses the package, the two types mentioned in the
Unchecked_Conversion are Siamese twins that can't be separated.

The same thing happens when a package declares a visible access type. The
package can't manage storage for the designated type, and users of the package
can't either.  So unless the access type explicitly uses a garbage collected
storage pool, storage for the type will be unmanaged.  I certainly think that
Robert Dewar's model of exporting a private type which is actually an access
type is a big improvement, but I think better is to make the exported type a
tagged record or better yet unconstrained array type. Now the user of the
package knows he is being at least a little sinful if he sequesters a pointer to
an object of the private type by using an explicit or anonymous access type.  If
users may need to beat on an object, provide a (child or nested) generic package
that provides a cursor.  You can also provide a much lighter weight (from a
programming, not implementation point of view) function that returns the current
object.  That it can only be used as input to another function or procedure is
an advantage not a limitation.  For example:

package Foo_Service is

  type Connection is limited private;
  ...
  procedure Open(...);
  function Current return Connection;
  ...
  generic
  ...
  package Additional is
      procedure Open(...);
      procedure Close(...);
      function Current return Connection;
  end Additional;
  ...
private
  pragma Inline(Current);
end Foo_Service;

Of course, any call to either version of Current for a closed (or not yet
opened) service will raise some appropriately named error.  What if users need
to manage multiple services, say Ethernet connections?  Then use the model of
the Ada IO packages, The fact that in rev 0.1  type Connection is an access type
can be changed later, and users of the package should not need to be aware of
the implementation change.  Much, much more work if you export an access type in
the prototype implementation.

What if I want to generalize the service using tagged types?  Change type
Connection to tagged limited private, and have specialized child types for
Internet, USB, Fire Wire, and so on. (You might even export an iterator over all
open services in some future version, but I find the need for that suspect.)

Better still is to use the model of generic package instance as an ADT:

generic
...
package Service is
  ...
  function Read return Packet;
  procedure Send(...);
  ...
end Service;

If necessary you can have a Controlled object declared in the package body to do
finalization, but usually that is not necessary.  If a user of the abstraction
wants to open a dozen different instances simultaneously, he does have to go to
the effort of naming them differently.  (Or create different threads. ;-)  Much
more usual is for a user to create an instance in a nested scope that gets
called many times.  Sometimes the information needed to open an instance is not
going to be available when the instance is created, and you will need to provide
an explicit open procedure in the package.  Shrug.  I usually run into this when
the service is actually reading from a database a row at a time. (Each generic
instance is a new query.) It still seems nicer to me than having to provide an
explicit cursor and a way to iterate using it.  And if a user just reads part of
the data then exits?  Fine, exiting the scope that created the instance does all
the cleanup necessary, and there is o chance of dangling pointers.

If setting up and tearing down a service is costly, the exporter of the
abstraction can cache opened (system) services and, for example, create a task
that closes inactive channels after 5 seconds or some such.  When the
abstraction implementor does this he knows how many threads still have a channel
open--the user has no way to keep an unauthorized pointer to the data
structures.  To go back to the database example though, you usually want to put
opening the database in the parent package, not in the generics.

As for generalization, you don't even need tagged types  Just add generic child
packages to either the non-generic parent (best) or as generic children of the
generic package.

To get back to the initial topic, I usually find that when OO programmers are
screaming that they need this or that, the problem is often that they have not
learned how to use the full generality of packages and generic packages in Ada.
I do know that when teaching Ada, this dynamic view of packages, especially
nested instances is hard to teach people who learned to program in some other
language first.  (Fortran is the worst, but C and C++ can be just as bad.)
Creating a (generic) package instance to hold the results of a database query is
literally a foreign language.  (Thinking back, nested generics, where it takes
two successive generic instantiations to create the abstraction wanted, is
probably the hardest to teach.  It always seems like you should be able to
collapse the two instantiations into one, but sometimes you just can't.)

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

From: Tucker Taft
Sent: Sunday, March 8, 2009  10:03 PM

I don't find the argument convincing that "if we can't solve every problem that
anyone can identify, then we shouldn't solve any of them."  Incremental progress
is still progress.  To me there are priorities, and some things are badly
broken, while other "problems" seem to be more a matter of personal aesthetics
of philosophy.  So I would start with prioritization, and discourage statements
involving "absolutes." Ada is a wide-spectrum language, and it is dangerous to
generalize too much from one's own experience with the language to start
imposing limitations and requirements on all other users of the language.

Personally I think we should focus on the features added in Ada 2005, in
particular stand-alone local variables of an anonymous access type, and
functions that return an anonymous access type, because clearly there is very
little code and there are very few programmers that have become dependent on
these features.

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

From: Brad Moore
Sent: Sunday, March 9, 2009  11:30 PM

> > The 'Ref proposal has the advantage of solving all of the above,
> > with full compatibility.  In addition, it solves most (all?) of the
> > nice-to-haves.  However, it leaves the language in an odd state --
> > an Ada textbook could tell people to use 'Ref always, and by the
> > way, there are two useless features (named and anonymous access
> > types), which should be avoided.  Also kind of ugly.
>
> I very much disagree that named access types are useless, I find this
> very very odd.

It also seems worth mentioning named access types currently play a role with
Annex E and Remote Access Types. Would the 'Ref proposal come into play with
RACW types? Also, in a Remote_Types unit, currently if you don't want an access
to be treated as a remote access type you need to use an anonymous access type.

eg.

package Ada.Containers.Doubly_Linked_Lists is
   pragma Preelaborate;
   pragma Remote_Types;

...

   procedure Query_Element
     (Position : Cursor;
      Process  : not null access procedure (Element : Element_Type));

Would the 'Ref proposal still allow this usage of anonymous access types? I
haven't read the proposal yet, nor do I recall having come across it, but I am
still catching up in my email...

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

From: Gary Dismukes
Sent: Monday, March 9, 2009  1:35 PM

> Would the 'Ref proposal still allow this usage of anonymous access
> types?

There may be some interaction there, but presumably a 'Ref type would be treated
like a named access type in such a unit.

> I haven't read the proposal yet, nor do I recall having come across
> it, but I am still catching up in my email...

See the first part of the !appendix section in AI05-00138.  Franco sent that out
back in November, so you probably just missed or forgot about it.

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

From: Bob Duff
Sent: Monday, March 9, 2009  7:16 AM

> I don't find the argument convincing that "if we can't solve every
> problem that anyone can identify, then we shouldn't solve any of
> them."

You are misrepresenting my position.  Or exagerating for effect.  ;-)

Here's my position:  There are a dozen or so problems with anon access types.
Several of them are essential to fix, if anon access is to be useful.  The rest
are merely "nice to have".  I am strongly opposed to doing anything, unless we
fix ALL of the "essential" ones.  Contrary to what you said above, I do not
insist that we fix the "nice to haves" -- but it would be nice.

>...Incremental progress
> is still progress.  To me there are priorities, and  some things are
>badly broken, while other "problems"
> seem to be more a matter of personal aesthetics of  philosophy.  So I
>would start with prioritization,  and discourage statements involving
>"absolutes."

I disagree.  We can make a prioritized list, if you like, but I think we need to
draw a line, and say "everything above this line needs to be fixed -- otherwise,
we write off anon access as a failed attempt."

I'm too lazy to repeat all the issues here, but I'll just give two examples:

Above the line: Need to be able to "new" and free objects portably.

Below the line: It would be nice to have a way to specify Storage_Pool for anon
access types.  (The 'Ref proposal solves this, but I do not insist that we go
with the 'Ref proposal purely for this nice-to-have feature.)

> Ada is a wide-spectrum language, and it is dangerous to generalize too
> much from one's own experience with the language to start imposing
> limitations and requirements on all other users of the language.

I'm not imposing limitations on anybody.  It's just that I haven't seen a single
realistic example of this anon access feature.  Not for lack of trying, either.

> Personally I think we should focus on the features added in Ada 2005,
> in particular stand-alone local variables of an anonymous access type,
> and functions that return an anonymous access type, because clearly
> there is very little code and there are very few programmers that have
> become dependent on these features.

We can, of course, argue about which problems need to be fixed.  But so far, you
have refused to engage in that argument.  Sorry, but fixing stand-alone locals
and returns is not enough, in my opinion.

Sorry to be so strident on this issue, but we made the mistake once before: Ada
95 had some problems with access types.  We designed an extremely complicated
set of features to fix those problems.  Unfortunately, we failed to solve the
problems sufficiently.  End result: huge implementation burden, but zero benefit
to users.

The reason I'm figuratively "pounding my shoe on the table" is I desperately
want to avoid repeating that mistake.  Please don't take it personally!

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

From: Bob Duff
Sent: Monday, March 9, 2009  7:17 AM

> I don't find the argument convincing that "if we can't solve every
> problem that anyone can identify, then we shouldn't solve any of
> them."

Let me try an analogy:

You have a vegetable garden.  The problem is, rabbits eat the vegetables before
you can harvest them.  So let's put up a fence.

We NEED a 4-sided fence that surrounds the whole garden.  A 3-sided fence is no
good.  It costs a lot of effort to put up a 3-sided fence, but it doesn't solve
the problem.  If you can't afford a complete solution, you might as well forget
about it, and let the rabbits eat your food.  It's silly to talk about
priorities for the North, South, East and West sides -- priority = "essential"
for all.

Do you see where I'm coming from?

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

From: Tucker Taft
Sent: Monday, March 9, 2009  2:25 PM

I don't really get the analogy, perhaps because I don't feel the
"essential"-ness of some of the things you do.  I found the Ada 95 situation
where anonymous access types were only used for parameters and discriminants was
reasonable, as those are two places where you actually care about by-value vs.
by-reference semantics, and where you want to be able to point to local objects
safely.

The main problem we were trying to solve for Ada 2005, in my mind, was to
reduce, at least to some extent, the number of explicit conversions associated
with typical OO paradigms.  I think others decided that it was important to go
"all the way" and enable the elimination of all use of named access types. We
seemed to run into trouble in getting the accessibility rules right when we
started dealing with local variables and function results with anonymous access
types.  I think we can fix those. And I don't see why we should leave them
broken, even if there are other problems we don't know how to fix cleanly.

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

From: Bob Duff
Sent: Monday, March 9, 2009  1:44 PM

> It also seems worth mentioning named access types currently play a
> role with Annex E and Remote Access Types. Would the 'Ref proposal
> come into play with RACW types?

I don't know how 'Ref should interact with Annex E.  I haven't thought about it
(and I don't intend to, unless we decide to move forward in that direction).

>...Also, in a Remote_Types unit, currently if you  don't want an access
>to be treated as a remote access type you need to  use an anonymous
>access type.
>
> eg.
>
> package Ada.Containers.Doubly_Linked_Lists is
>    pragma Preelaborate;
>    pragma Remote_Types;
>
> ...
>
>    procedure Query_Element
>      (Position : Cursor;
>       Process  : not null access procedure (Element : Element_Type));
>
> Would the 'Ref proposal still allow this usage of anonymous access
> types?

You're referring to Process?

First, the 'Ref proposal would not disallow any anonymous access types currently
allowed, nor change their semantics.  The whole point of 'Ref is to leave anon
access alone, for compatibility, which makes us free to use different rules for
'Ref.

Second, 'Ref is not intended to address access-to-subprogram, only
access-to-object.  We could discuss changing that in some way, but it seems
premature at this point.

> I haven't read the proposal yet, nor do I recall having come across
> it, but I am still catching up in my email...

It is discussed in AI05-0138-1.

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

From: Randy Brukardt
Sent: Monday, March 9, 2009  3:26 PM

> Sorry to be so strident on this issue, but we made the mistake once
> before:
> Ada 95 had some problems with access types.  We designed an extremely
> complicated set of features to fix those problems.
> Unfortunately, we failed to solve the problems sufficiently.
> End result: huge implementation burden, but zero benefit to users.
>
> The reason I'm figuratively "pounding my shoe on the table"
> is I desperately want to avoid repeating that mistake.
> Please don't take it personally!

I strongly agree with Bob here. We need to focus on the problems (and by
problems, I mean the *high-level* problems designing Ada programs, ADT, etc.).
If those problems need anonymous access types to be solved, then we will know
what to work on. If not, then we surely should not waste the effort on them.

This is very much an issue of throwing good money after bad (sort of like
investing in the stock market these days!!) Anonymous access types look like a
mistake in the language to me, and they'll probably remain a mistake no matter
how much effort is put into them. We'll need to do some fixes to make the
semantics correct, but beyond that I need to be convinced that they actually
solve some problem.

After all, the expansion of anonymous access types in Ada 2005 was supposed to
cut down on unnecessary type conversions for programs involving limited with.
But they don't do that unless you get rid of all named access types from your
program. But that was never, ever, the intent (which is why there is no way to
do [sane] allocation or [any] deallocation of them). That makes the expansion a
complete failure; and the only reasonable fix would be to get rid of the
requirement for explicit conversions *from* anonymous access types to named
access types. If we don't do that, other changes are just lipstick on a pig.
(And I'm strongly opposed to trying to get storage pools on anonymous access
types!).

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

From: Tucker Taft
Sent: Monday, March 9, 2009  3:43 PM

> You are misrepresenting my position.  Or exaggerating for effect.  ;-)
>
> Here's my position:  There are a dozen or so problems with anon access types.
> Several of them are essential to fix, if anon access is to be useful.
> The rest are merely "nice to have".  I am strongly opposed to doing
> anything, unless we fix ALL of the "essential" ones.  Contrary to what
> you said above, I do not insist that we fix the "nice to haves" -- but it would be nice.

When I look back at your original note on this topic, you identify a handful of
problems, and then list a series of changes that you feel are required.  You may
have felt that the solutions implied the problems they were fixing, but not
everyone can make that leap.

It might be helpful for us to first only identify and prioritize the problems,
and try to avoid slipping into prematurely specifying solutions. You indicate
you think there are a dozen or so such problems, but that only some of them are
essential to fix.  Let's see if we can get a cohesive list of what are the real
problems.

I believe Franco's notes suffered from the same issue.
That is, he identified a few problems, and then immediately launched into a
rather elaborate set of solutions.  I personally believe we could solve almost
all of the problems Franco identified, with a significantly simpler set of
solutions. But my fear is that it is easy to fall in love with a particular
solution, and then become blinded to what are the real problems, and anything
that doesn't match the envisioned solution, even if it happens to solve pretty
much the same problems, will be discounted.

 From Franco's note on 'Ref, I extracted the following problems:

    * Can't use unchecked deallocation

    * Can't specify storage pool or storage size

    * Can't have [in] out access parameters

    * Can't use them with task entries

    * Can't reverse a list represented using anonymous
      access type pointers (underlying problem -- accessibility
      level of local variables of an anonymous access type).

    * Equality operator is ambiguous with anonymous access types.
      But I'm not sure the example given is correct:
       declare
         X : access T := ...;
         Y : aliased T := ...;
       begin
         if X = Y'Access then ... -- Ambiguous?

      The only "=" operator I can imagine being used for this
      is the unique one in package Standard for univ-access.
      Is there some other one that is getting in the way?
      There is no implicit conversion *from* an anonymous
      access type.

 From the note about allocators, I extracted the following problems:

    * anonymous-type allocators are generally short-lived, while named-type
      allocators have a lifetime determined by their type.

 From the second note about allocators, the problems seem to be the same ones
 identified earlier, namely a lack of unchecked deallocation, and ability to
 specify storage pool or storage size.

I'd be curious of the above problems identified by Franco, which ones make it
onto the "essential to solve" list for some of us.  For me, if you want control
over storage, then by definition you will want the fine level of control
provided by named access types.  You can still use anonymous access types for
manipulating values, but you need to use a named access type when allocating and
freeing if you want user-specified storage pools.  It seems very limiting to
presume that all users of access types with the same designated type would have
the same storage-management requirements.

Task entries seems pretty low on the totem pole.

I'm not sure there really is a problem with the equality operator.

The reversing-a-list example was pretty compelling to me, but I believe that can
be solved relatively simply.

I had more trouble extracting Bob's problem list.  Here is my attempt.
Corrections/refinements appreciated.

   * Dynamic accessibility is too confusing;

   * Automatic deallocation is undesirable for objects
     created by anonymous allocators;

   * IN OUT access parameters are needed;

   * IN OUT function parameters are needed.

Hopefully we could identify at least a few problems we all think are "essential"
to solve, before we start debating specific solutions.

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

From: Randy Brukardt
Sent: Monday, March 9, 2009  5:03 PM

...
> It might be helpful for us to first only identify and prioritize the
> problems, and try to avoid slipping into prematurely specifying
> solutions. You indicate you think there are a dozen or so such
> problems, but that only some of them are essential to fix.  Let's see
> if we can get a cohesive list of what are the real problems.

This makes sense to me.

> I believe Franco's notes suffered from the same issue.

Even worse, he didn't clarify the issues when asked (at least the ones I asked
about).

...
> I'd be curious of the above problems identified by Franco, which ones
> make it onto the "essential to solve" list for some of us.  For me, if
> you want control over storage, then by definition you will want the
> fine level of control provided by named access types.  You can still
> use anonymous access types for manipulating values, but you need to
> use a named access type when allocating and freeing if you want
> user-specified storage pools.  It seems very limiting to presume that
> all users of access types with the same designated type would have the
> same storage-management requirements.

I agree with this, but note that means that allocators of anonymous access types
are probably a mistake.

...
> I had more trouble extracting Bob's problem list.  Here is my attempt.
> Corrections/refinements appreciated.
>
>    * Dynamic accessibility is too confusing;
>
>    * Automatic deallocation is undesirable for objects
>      created by anonymous allocators;
>
>    * IN OUT access parameters are needed;
>
>    * IN OUT function parameters are needed.
>
> Hopefully we could identify at least a few problems we all think are
> "essential" to solve, before we start debating specific solutions.

You didn't even try to extract mine, even though I've written a whole bunch of
proposals on various topics. I'm starting to feel neglected. :-)

Anyway, trying the same form as you did for Bob:

Randy's problem list (in rough order of necessity):

    * In Out parameters for functions;

      [I don't think this one falls into this particular category, although I
      understand that having it decreases the need to use anonynous access
      types, and thus reduces the priority of other fixes.]

    * Safe accessors for containers;

      [That is, the ability to return an access to part of a parameter of a function, and have it have an appropriate (very-short) lifetime so that only a dereference is a possibility. But there are other possibilities as well for this, such as a general r
eturn-by-reference issue.]

    * Storage pools need the possibility of detecting dangling pointers;

      [GNAT has a solution for that.]

    * Restriction/other setting to prohibit allocators for anonymous access
      types (assuming we follow Tucker's allocate with named access only
      strategy);

      [Compatibility issues prevent us from making this a requirement, even
      though that would be best.]

All of the above have practical use-cases that make them important.

    -- Nice-to-haves:

    * Decouple accessibility from location of declaration for access types;

    * Mechanism of testing accessibility of an access value so that dynamic
      accessibility failures can be prevented/handled;

    * Decouple "access values" from System.Address in Storage_Pools;

    * Actually solve the problem of too many type conversions for access to
      limited views.

      [I suspect this is what Franco's problems *really* boil down to. I'm
       personally dubious of the need for visible accesses in the first place,
       but I don't want to dismiss anything out of hand.]

More food for thought.

BTW, what's your personal list, Tucker??

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

From: Tucker Taft
Sent: Monday, March 9, 2009  5:40 PM

I didn't mean to neglect you.  I just didn't find a separate recent proposal
from you, so it wasn't as easy to find your list.

For my list, I think the two things that are currently "broken" are 1) the
accessibility rules related to standalone objects of an anonymous access (as
illustrated by Franco's reversal use, but also because a constant initialized
from an access parameter and a renaming of an access parameter produce such
different results), and 2) the accessibility rules related to anonymous access
return (as illustrated by the problems with dispatching operations, and by the
inevitable storage leaks mentioned in AI-51).

I am quite used to having separate access types for allocation/deallocation and
for manipulation, because the code for manipulating in general doesn't need to
worry about storage management, and so can be largely storage-pool independent.
So for me, if the type for manipulation ends up anonymous because that works
more naturally, while the type(s) for allocation/deallocation remain named,
that's just fine.

I think it is unfortunate that you only get one global storage pool for a named
access type, and AI-111 is an attempt to address that problem, but it is largely
independent of the current discussion about anonymous access types (though not
completely).

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

From: Randy Brukardt
Sent: Monday, March 9, 2009  6:06 PM

> I didn't mean to neglect you.  I just didn't find a separate recent
> proposal from you, so it wasn't as easy to find your list.

Did you ever read the proposals that I sent that are in the !appendix of
AI05-0138-1 (specifying accessibility), the storage pool proposal now in
AI05-0141-1, and the accessor problem in AI05-0142-1? The latter two aren't
directly associated with anonymous access types, but one could use anonymous
access types as an alternative solution to AI05-0142-1's problem (so they're
related in that way). Not to mention AI05-0143-1 ("in out parameters for
functions") and the partially written AI05-0144-1 ("detecting order
dependencies"), both of which use your name in vain (as you are the most visible
opponent).

Admittedly, all of these are a month old or so, but until Bob started sending
notes, they covered pretty much everything. (And I don't think anything that Bob
or you have said has changed my opinions much to date.) It's would be nice if
you've read them before we have the conference call (I know Bob has). I wish I
had time to finish these other proposals before then, but that seems unlikely.

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

From: Bob Duff
Sent: Tuesday, March 10, 2009  5:57 PM

I can't find the e-mail I want to reply to right now, but I want to make this
point:

Tuck said that the Ada 95 anon access features work pretty well.

Well, I partly agree: access parameters and access discriminants work pretty
well as special-purpose gizmos, with various special purpose rules.  One can
memorize the rules, and deal with it.

But Ada 2005 adds anon access all over, and makes it seem like a general purpose
feature, and that makes the special purpose rules far more confusing.

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

From: Bob Duff
Sent: Tuesday, March 10, 2009  5:51 PM

> It might be helpful for us to first only identify and prioritize the
> problems, and try to avoid slipping into prematurely specifying
> solutions. You indicate you think there are a dozen or so such
> problems, but that only some of them are essential to fix.  Let's see
> if we can get a cohesive list of what are the real problems.

OK, you've made a good list of issues based on Franco's and my e-mails, below. I
replied "Above the line." to each issue below, to indicate that it really has to
be fixed, or "Below the line." to indicate that it's merely nice to have. I
think there are some missing "below" issues, but you've got the "above" ones,
which is the important thing.

I'm imagining a "line" drawn across a prioritized listing, highest prio first.

First let me mention some "meta issues", which are really covered by the issues
you list below, but may serve as rationale for where to draw the "line".

Whenever I declare a type T, I have to decide whether to put "type Access_T is
access all T" (or maybe T'Class) immediately after, just in case it's needed. If
yes, I clutter the program with useless access types.  If no, then I have to
create named access types on an as-needed basis, scattered about, and this
exacerbates the "bogus type conversion" issue.  Or else I have to go back and
modify existing code, which can range from "minor annoyance" to "impossible". I
don't like that dilemma.  This explains my point below about 'out' parameters --
I don't like a coding rule that says, "Declare Access_T if and only if you need
to have 'out' parameters somewhere else".

If I have to have Access_T anyway, then "X: access T" is not a big win over "X:
Access_T".  Unless of course it eliminates bogus conversions.  In my experience,
they aren't eliminated, but moved around.  And it's confusing.

Another meta issue is just general complexity.  I can't point to one particular
"problem" that causes the complexity, but taken together, all the anon access
rules feel too complicated.

In the end, we need some realistic examples using anon access, that somehow are
"better" than using named access (e.g. by removing bogus conversions, while
remaining "simple" for the typical programmer).  If we can't do that, then it's
a waste of time to "fix" anon access.

[End meta issues.]

> I believe Franco's notes suffered from the same issue.

I am guilty as accused (sorry), but Franco, not so much.
He did list "problems" separately from "solutions".

> That is, he identified a few problems, and then immediately launched
> into a rather elaborate set of solutions.  I personally believe we
> could solve almost all of the problems Franco identified, with a
> significantly simpler set of solutions.
> But my fear is that it is easy to fall in love with a particular
> solution, and then become blinded to what are the real problems, and
> anything that doesn't match the envisioned solution, even if it
> happens to solve pretty much the same problems, will be discounted.
>
>  From Franco's note on 'Ref, I extracted the following problems:
>
>     * Can't use unchecked deallocation

Above the line.

>     * Can't specify storage pool or storage size

Below the line.

>     * Can't have [in] out access parameters

Above the line.

>     * Can't use them with task entries

Below the line.  This is really the same issue as the previous "[in] out" one,
but entries are much more rare than [in] out params (and will be more so once we
allow [in] out params on functions ;-)).

>     * Can't reverse a list represented using anonymous
>       access type pointers (underlying problem -- accessibility
>       level of local variables of an anonymous access type).

Above the line.

>     * Equality operator is ambiguous with anonymous access types.
>       But I'm not sure the example given is correct:
>        declare
>          X : access T := ...;
>          Y : aliased T := ...;
>        begin
>          if X = Y'Access then ... -- Ambiguous?
>
>       The only "=" operator I can imagine being used for this
>       is the unique one in package Standard for univ-access.
>       Is there some other one that is getting in the way?

I don't remember the details of this issue.  It might be above the line.

>       There is no implicit conversion *from* an anonymous
>       access type.

I've always wondered why that is.  Some obscure incompatibility issue involving
overload resolution, I suppose.

>  From the note about allocators, I extracted the following problems:
>
>     * anonymous-type allocators are generally short-lived, while named-type
>       allocators have a lifetime determined by their type.

Above the line.

>  From the second note about allocators, the problems seem to be the
> same ones identified earlier, namely a lack of unchecked deallocation,
> and ability to specify storage pool or storage size.
>
> I'd be curious of the above problems identified by Franco, which ones
> make it onto the "essential to solve" list for some of us.  For me, if
> you want control over storage, then by definition you will want the
> fine level of control provided by named access types.  You can still
> use anonymous access types for manipulating values, but you need to
> use a named access type when allocating and freeing if you want
> user-specified storage pools.  It seems very limiting to presume that
> all users of access types with the same designated type would have the
> same storage-management requirements.
>
> Task entries seems pretty low on the totem pole.

Agreed.

> I'm not sure there really is a problem with the equality operator.

I'm not sure.

> The reversing-a-list example was pretty compelling to me, but I
> believe that can be solved relatively simply.
>
> I had more trouble extracting Bob's problem list.  Here is my attempt.
> Corrections/refinements appreciated.

Sorry for causing confusion.  Anyway, I think you got it.  Not surprisingly, all
of these are above the line, since in the message you're referring to, I
deliberately left out the ones that Franco and I had decided were mere "nice to
haves".

>    * Dynamic accessibility is too confusing;

Above the line.  Not just confusing, but it breaks abstraction. I'd actually
broaden this: Accessibility level of any anon access type is confusing, unless
it matches the accessibility level of a named access type.  And I don't mean a
named type declared in some weird place, I mean a named type declared where a
normal programmer would put it -- immediately after the declaration of type T.
Coextensions are a cool feature, but they are deeply confusing, so I doubt most
programmers can make use of them.

>    * Automatic deallocation is undesirable for objects
>      created by anonymous allocators;

Above the line.  Basically this is the same issue as "* Can't use unchecked
deallocation" under Franco's list.

>    * IN OUT access parameters are needed;

Above the line.

>    * IN OUT function parameters are needed.

Above the line.  But maybe this is a "meta issue".  The primary reason to pass
Local'Access to an anon access parameter is because you wanted 'in out', but you
weren't allowed, because it's a function.  In other words, allowing 'in out'
params on functions gives us freedom elsewhere in the language design.

> Hopefully we could identify at least a few problems we all think are
> "essential" to solve, before we start debating specific solutions.

OK.

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

From: Tucker Taft
Sent: Wednesday, March 11, 2009  7:57 AM

Note that "in out" access parameters are not trivial, because with "in"
parameters you can generally use static approximations of the true dynamic
accessibility level, but with "out" parameters you might open yourself up to
dangling references if you make certain approximations. I don't have a ready
example to show the problem, but I could probably come up with one if given
enough time. I believe this was one of the reasons we didn't allow OUT access
parameters.

I might also add that the number of times I have seen an OUT parameter of a
named access type is pretty small, so I don't feel the same critical need for
OUT parameters of an anonymous access type, especially if they make the
accessibility rules for access parameters even more complex.  Note that
languages like Java don't have any OUT parameters of a pointer type, and somehow
manage to muddle through.

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

From: Tucker Taft
Sent: Wednesday, March 11, 2009  9:22 PM

[To Randy] Thanks for the pointer to these AIs.  An immediate reaction to the
accessibility specification suggestion -- the proposed general syntax for
specifying aspects might help here:

    X : access T
      with
        Accessibility => System.Library_Level;

(presuming we add an appropriate enumeration type to package System)

I'll make sure I review all of your proposals before the phone call.

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

From: Bob Duff
Sent: Wednesday, March 11, 2009  1:18 PM

> Note that "in out" access parameters are not trivial,

They would be trivial if accessibility level of all anon access types is static,
and is that of the designated subtype.  (Or maybe the root type of hierarchy,
for tagged.)

I realize that's incompatible with Ada 95.  We've got two suggestions to avoid
compatility problems, 'Ref, and pragma Static_Accessibility, both of which seem
to be rejected by most people so far.  So we seem kind of stuck...

> I don't have a ready example to show the problem, but I could probably
> come up with one if given enough time.

Oh, I believe that!

> I might also add that the number of times I have seen an OUT parameter
> of a named access type is pretty small, so I don't feel the same
> critical need for OUT parameters of an anonymous access type, ...

OK, I'm half convinced.  Only half.  It really seems an annoying non-uniformity,
and gets you into the dilemma I mentioned.

>...especially if they make
> the accessibility rules for access parameters even more  complex.
>Note that languages like Java don't have any  OUT parameters of a
>pointer type, and somehow manage  to muddle through.

That's not my favorite "feature" of Java, FWIW.

Speaking of Java, it manages to muddle through without any accessibility rules,
using a technology I won't name, but whose initials are GC.  ;-)

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

From: Tucker Taft
Sent: Wednesday, March 11, 2009  3:21 PM

I'm not personally interested in reinventing Ada 95's anonymous-access-type
rules.  I think we can fix some of the mistakes we made in the Ada 2005
additions.  I could also imagine some new pragma-restriction identifiers
associated with anonymous access types, such as No_Anonymous_Allocators.  But
starting with a completely different model for anonymous access types is not
good for the language, in my view.

I think IN OUT access parameters could work if they are essentially fully
dynamic, with a dynamic accessibility check on copy-out.  This pretty much means
implementations would have to abandon using static accessibility levels for
access parameters, and instead pass a pointer to something like a "master"
record, which indicates the lifetime of the designated object.

The master record/storage pool might have a static nesting level stored which
could be used in many cases, as well as a dynamic nesting level, and presumably
a pointer to the immediately enclosing master record.

Note that this is the direction that AI-111 was heading to support "subpools".

FWIW, overall, I am tending toward the following model:

   - stand-alone objects/parameters of an anonymous
     access type are fully dynamic (membership can
     be used for checking before converting to a
     named type);

   - access results (of a function) take their (dynamic)
     accessibility from the caller context (per AI-51);

   - discriminants work as they do in Ada 95,
     where accessibility is determined by the enclosing
     object.

(The first two are what I would call "chameleon" semantics,
  where the object takes on the accessibility of its
  current value or context.)

The open question, at least for me, is whether our model for non-discriminant
components is right, where they have the accessibility of the enclosing
composite *type*.  Other alternatives are to make them fully dynamic, or to make
them like discriminants, where they take their accessibility from the enclosing
*object*, or to allow some kind of accessibility/storage-pool specification,
similar to what Randy has proposed.

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

From: Randy Brukardt
Sent: Wednesday, March 11, 2009  3:39 PM

> > Note that "in out" access parameters are not trivial,
>
> They would be trivial if accessibility level of all anon access types
> is static, and is that of the designated subtype.  (Or maybe the root
> type of hierarchy, for tagged.)
>
> I realize that's incompatible with Ada 95.  We've got two suggestions
> to avoid compatility problems, 'Ref, and pragma Static_Accessibility,
> both of which seem to be rejected by most people so far.  So we seem
> kind of stuck...

Sheesh, I'm being ignored again! We have *three* suggestions for avoiding
compatibility problems and still getting static accessibility on anonymous
access types: 'Ref, pragma Static_Accessibility (which BTW, I never saw any
proposal for other than a mention in passing -- where did this name come
from???), and specifying accessibility (see the detailed proposal in AI05-0138's
appendix). Of course, your conclusion still seems pretty much valid: no one
seems excited by any of these ideas.

> > I don't have a ready example to show the problem, but I could
> > probably come up with one if given enough time.
>
> Oh, I believe that!
>
> > I might also add that the number of times I have seen an OUT
> > parameter of a named access type is pretty small, so I don't feel
> > the same critical need for OUT parameters of an anonymous access type, ...
>
> OK, I'm half convinced.  Only half.  It really seems an annoying
> non-uniformity, and gets you into the dilemma I mentioned.

I'm not sure I am. I would agree with Tucker's statement taken literally, but it
ignores "in out" parameters, which are far more common than "out" parameters (at
least in my code). I would guess that he is including "in out" parameters in it,
and then I would disagree (although it may be true that many of those cases
aren't really modifying the parameter.

I could make a similar statement that is just as true:

"I might also add that the number of times I have seen an anonymous access
parameter that couldn't be better written as an "in out" or "out" parameter or
as a parameter of a named access type is pretty small, so I don't feel the same
critical need for other modes for parameters of an anonymous access type, ..."

But I don't find this particularly compelling, either, because YMMV. If we're
going to bother to have the things, they should be consistent as to how they are
used.

> >...especially if they make
> > the accessibility rules for access parameters even more  complex.
> >Note that languages like Java don't have any  OUT parameters of a
> >pointer type, and somehow manage  to muddle through.
>
> That's not my favorite "feature" of Java, FWIW.
>
> Speaking of Java, it manages to muddle through without any
> accessibility rules, using a technology I won't name, but whose
> initials are GC.  ;-)

Of course, they give up the benefits of stack allocation to get there.

An alternative way for Ada to "muddle through" would be to make all
accessibility dynamic (optionally, I think; that was the original idea behind my
specified accessibility proposal); then the only accessibility failures would be
when you are dereferencing a pointer that is actually dangling (or converting to
a type with static accessibility). The problem with that is that I wasn't able
to figure out a way to implement the accessibility check without a distributed
overhead (simply checking the relationship of stack frames isn't enough, even
within a single task). Maybe someone is cleverer than I on that.

With fully dynamic accessibility checks, the only failures occur when there are
real bugs. The main problem with static accessibility as we have it is that it
prevents many things that are perfectly reasonable and that happens a lot more
often than it detects actual bugs.

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

From: Randy Brukardt
Sent: Wednesday, March 11, 2009  3:55 PM

...
> The open question, at least for me, is whether our model for
> non-discriminant components is right, where they have the
> accessibility of the enclosing composite *type*.  Other alternatives
> are to make them fully dynamic, or to make them like discriminants,
> where they take their accessibility from the enclosing *object*, or to
> allow some kind of accessibility/storage-pool specification, similar
> to what Randy has proposed.

Unfortunately, I've convinced that fully dynamic accessibility in general is not
implementable without distributed overhead, because comparison of stack frames
is not enough (even within a single task). Access parameters get away with it
because the stack relationship is not fully general due to the relationship
implied by calls. (There is also the issue of incomparable accessibility, but I
think that a check could be engineered to minimize that overhead, as the number
of checks between two different tasks is likely to be very low).

I could show the problem on a whiteboard in 30 seconds, but I don't know of a
good way to describe it in text.

I'm concerned that the problem could show up in anonymous access objects,
although I think that would depend on how dynamic the results of functions
really are and how dynamic the accessibility is (if it is only dynamic when
initialized I think we are OK).

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

From: Randy Brukardt
Sent: Wednesday, March 11, 2009  4:08 PM

...
> I'm concerned that the problem could show up in anonymous access
> objects, although I think that would depend on how dynamic the results
> of functions really are and how dynamic the accessibility is (if it is
> only dynamic when initialized I think we are OK).

Here's a case that worries me for "stand-alone objects/parameters of an
anonymous access type are fully dynamic (membership can be used for checking
before converting to a named type)":

    declare
        Obj : access Integer;

        procedure Do_It (Value : in Integer) is
            Local : aliased Integer := Value;
        begin
            if Value mod 3 = 0 then
                Obj := Local'Access; -- (1)
            else
                Obj.All := Value + Obj.All; -- (2)
            end if;
        end Do_It;
    begin
        Do_It (3);
        Do_It (4);
    end;

The first call to Do_It saves an access to an object; as the object is has fully
dynamic accessibility, this is OK. The second call to Do_It uses the object
saved by the first call at (2) -- but it no longer exists. So (a) there is a
requirement to make accessibility checks on dereferences, and (b) a master stack
address comparision doesn't work (they're both the same in this case, which
normally should pass the check).

You could fix this case by including a master serial number with each one (in
this case, the frames are the same but the serial number is not). But if Do_It
was a recursive routine much a like a fibinacci routine, that check plus a stack
address check would not be enough. You would have to walk from the current
master to see if one with the correct address and serial number appeared. On
*every* dereference (and conversion). Plus the distributed overhead of the
serial number. That all seems too much to me (if I thought that was reasonable,
I would have proposed a fully dynamic modifier for all access type
declarations).

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

From: Tucker Taft
Sent: Wednesday, March 11, 2009  4:30 PM

...
>     declare
>         Obj : access Integer;
>
>         procedure Do_It (Value : in Integer) is
>             Local : aliased Integer := Value;
>         begin
>             if Value mod 3 = 0 then
>                 Obj := Local'Access; -- (1)

This would be statically illegal.  In the "fully dynamic" model I have suggested
in the past for stand-alone variables, it is still that case that an access
object would never be allowed to point to an object that has a shorter lifetime
than the access object.

>             else
>                 Obj.All := Value + Obj.All; -- (2)

This would not require a dynamic check if you disallow access objects pointing
to things shorter-lived than themselves.

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

From: Randy Brukardt
Sent: Wednesday, March 11, 2009  4:55 PM

> This would be statically illegal.  In the "fully dynamic"
> model I have suggested in the past for stand-alone variables, it is
> still that case that an access object would never be allowed to point
> to an object that has a shorter lifetime than the access object.

OK, but that's not by any stretch of the imagination a "fully dynamic" model.
That's a mixed static/dynamic model, because you clearly have to have some
static checks to enforce "shorter lifetime" rule. (Why the heck didn't we call
this "lifetime" in the first place? People would understand it better. And yes,
I made that suggestion way back in the Ada 9X days, but even then you were too
entrenched with the awful terminology of "accessibility" to consider the
change.)

I don't recall you ever describing in any sort of detail what this model is, and
I think you need to do that so we can evaluate it fairly. (You have a track
record of morphing accessibility rules to whatever will get approval by the ARG,
and then later discovering problems that make it necessary to adopt the overly
complex rules you originally had in mind. That probably wasn't intentional, but
still it implies insufficient detail or understanding or something. I for one do
not want to get fooled again in this regard, I will oppose all accessibility
changes unless the model is clearly laid out and vetted, especially as
accessibility is a nearly worthless set of checks that simply has to be worked
around - and even the Ada 9x designers recognized that by including a
first-class way to get around the checking - 'Unchecked_Access.)

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

From: Bob Duff
Sent: Wednesday, March 11, 2009  4:33 PM

> Here's a case that worries me for "stand-alone objects/parameters of
> an anonymous access type are fully dynamic (membership can be used for
> checking before converting to a named type)":

Hmm.  I never thought "fully dynamic" was THAT dynamic!  I thought it meant that
you'd get an exception at (1) below.

But even my not-so-fully dynamic notion has problems with tasks.

>     declare
>         Obj : access Integer;
>
>         procedure Do_It (Value : in Integer) is
>             Local : aliased Integer := Value;
>         begin
>             if Value mod 3 = 0 then
>                 Obj := Local'Access; -- (1)
>             else
>                 Obj.All := Value + Obj.All; -- (2)
>             end if;
>         end Do_It;
>     begin
>         Do_It (3);
>         Do_It (4);
>     end;
>
> The first call to Do_It saves an access to an object; as the object is
> has fully dynamic accessibility, this is OK. The second call to Do_It
> uses the object saved by the first call at (2) -- but it no longer
> exists. So (a) there is a requirement to make accessibility checks on
> dereferences, and (b) a master stack address comparision doesn't work
> (they're both the same in this case, which normally should pass the check).
>
> You could fix this case by including a master serial number with each
> one (in this case, the frames are the same but the serial number is
> not). But if Do_It was a recursive routine much a like a fibinacci
> routine, that check plus a stack address check would not be enough.
> You would have to walk from the current master to see if one with the
> correct address and serial number appeared. On *every* dereference
> (and conversion). Plus the distributed overhead of the serial number.
> That all seems too much to me (if I thought that was reasonable, I
> would have proposed a fully dynamic modifier for all access type declarations).

I agree the implementation model here is just nuts.

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

From: Bob Duff
Sent: Wednesday, March 11, 2009  4:44 PM

> > I realize that's incompatible with Ada 95.  We've got two
> > suggestions to avoid compatility problems, 'Ref, and pragma
> > Static_Accessibility, both of which seem to be rejected by most
> > people so far.  So we seem kind of stuck...
>
> Sheesh, I'm being ignored again!

Sorry!

>...We have *three* suggestions for avoiding  compatibility problems and
>still getting static accessibility on anonymous  access types: 'Ref,
>pragma Static_Accessibility (which BTW, I never saw any  proposal for
>other than a mention in passing -- where did this name come  from???),

I think Franco invented it, more-or-less on the fly.  There's no actual
"proposal", just the idea that we want to change the rules, and that's
incompatible, so we invent a pragma or some other sort of option, which invokes
the rules we like, and then programs without that pragma can function as in Ada
95/2005.

>... and specifying accessibility (see the detailed proposal in
>AI05-0138's appendix).

Yes, I remember that one.  I don't much like any idea that requires some extra
verbiage on every type.  If you can come up with a global way to say it, then
I'd be happier.  (And the string-literal-based syntax is pretty horrible, IMHO,
but that's a detail.)

>...Of course, your conclusion still seems pretty much
> valid: no one seems excited by any of these ideas.
...

> I'm not sure I am. I would agree with Tucker's statement taken
> literally, but it ignores "in out" parameters, which are far more common than "out"
> parameters (at least in my code). I would guess that he is including
> "in out" parameters in it, and then I would disagree (although it may
> be true that many of those cases aren't really modifying the parameter.
>
> I could make a similar statement that is just as true:
>
> "I might also add that the number of times I have seen an anonymous
> access parameter that couldn't be better written as an "in out" or
> "out" parameter or as a parameter of a named access type is pretty
> small, so I don't feel the same critical need for other modes for
> parameters of an anonymous access type, ..."

Right, except that 'in out' is disallowed for functions.

> But I don't find this particularly compelling, either, because YMMV.
> If we're going to bother to have the things, they should be consistent
> as to how they are used.
>
> > >...especially if they make
> > > the accessibility rules for access parameters even more  complex.
> > >Note that languages like Java don't have any  OUT parameters of a
> > >pointer type, and somehow manage  to muddle through.
> >
> > That's not my favorite "feature" of Java, FWIW.
> >
> > Speaking of Java, it manages to muddle through without any
> > accessibility rules, using a technology I won't name, but whose
> > initials are GC.  ;-)
>
> Of course, they give up the benefits of stack allocation to get there.

Right.  Java makes the mistake of saying "now that we have GC, we might as well
put everything on the heap".  But it doesn't have to be that way -- e.g. C#
doesn't make that mistake.

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

From: Randy Brukardt
Sent: Wednesday, March 11, 2009  5:08 PM

...
> > Here's a case that worries me for "stand-alone objects/parameters of
> > an anonymous access type are fully dynamic (membership can be used
> > for checking before converting to a named type)":
>
> Hmm.  I never thought "fully dynamic" was THAT dynamic!  I thought it
> meant that you'd get an exception at (1) below.

So you had a different idea of what Tucker was proposing than me or Tucker:
three people, three different ideas of what was being proposed. This is not
good! Tucker needs to write a much more detailed proposal, so we can at least
discuss a single idea...

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

From: Tucker Taft
Sent: Wednesday, March 11, 2009  5:13 PM

> I don't recall you ever describing in any sort of detail what this
> model is, and I think you need to do that so we can evaluate it fairly. ...

Here is a paragraph from an e-mail I sent on 11/12/2008:

   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.

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

From: Randy Brukardt
Sent: Wednesday, March 11, 2009  5:34 PM

OK, I don't remember seeing this at all (no surprise there, there have been
something like 250 messages on this topic). In any case, please do not call this
a "fully dynamic" model, because such a model can't have any compile-time
checks.

Is that the whole proposal, or is there more to it?? I'm not sure I see the
point in this case, because the interesting cases are all about putting
shorter-lived things in longer-lived things (not necessarily directly, but
safely). But I've probably forgotten that, too.

(I'm really dreading this conference call, because I don't see anything remotely
resembling convergence on anything that has been discussed -- we all have our
pet ideas and there doesn't seem to be much interest in considering anyone
else's very seriously. That's not intended to be a gripe or attack so much as a
statement of fact -- that's surely how *I* feel on this topic, and I don't see
much evidence that anyone else is much more open-minded. It is a struggle to
even try to analyze these ideas without revulsion...)

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

From: Bob Duff
Sent: Wednesday, March 11, 2009  5:16 PM

> I'm not personally interested in reinventing Ada 95's
> anonymous-access-type rules.

Fair enough.

>...I think we can fix
> some of the mistakes we made in the Ada 2005  additions.

But I'm not interested in fixing "some of the mistakes".
I say we should do one of:

(1) Accept the status quo, which is a couple of special-purpose Ada 95 gizmos
    (access parameters and access discriminants), plus a recommendation to
    avoid using the broken Ada 2005 stuff.

or:

(2) Fix the Ada 2005 stuff so it is simple and useful.

I don't really see how to do (2) without one of the various "ugly" things that
have been mentioned (and rejected).  I will be convinced only if I can see some
examples of typical programs that use access types to create heap-based data
structures, and are in some way "better" than what we currently have.

Replacing "X : Access_Blah;" with "X : access Blah;" doesn't do much, unless it
avoids bogus conversions, or has some other benefit.

>...I could also imagine some new pragma-restriction  identifiers
>associated with anonymous access types, such as
>No_Anonymous_Allocators.  But starting with a  completely different
>model for anonymous access types  is not good for the language, in my
>view.

Agreed.  But neither is it good for the language to invent a whole new layer of
complexity that nobody can understand, causes implementation headaches, and
still leaves the Ada 2005 stuff in a not-so-useful state.

I really need to go read AI-51...

By the way, note that there is some confusion about what "fully dynamic model"
means.  Somebody needs to clarify that.

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

From: Bob Duff
Sent: Wednesday, March 11, 2009  5:27 PM

> So you had a different idea of what Tucker was proposing than me or Tucker:
> three people, three different ideas of what was being proposed. This
> is not good! Tucker needs to write a much more detailed proposal, so
> we can at least discuss a single idea...

I think there are only two.  That is, my notion agrees with Tucker's notion,
except I said "exception" where Tucker said "compile-time error" -- but I think
the truth is, "exception at run-time, but it's always detectable at compile time
in this sort of case, so you get at least a compile-time warning".

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

From: Bob Duff
Sent: Wednesday, March 11, 2009  5:39 PM

> (Why the heck didn't
> we call this "lifetime" in the first place? People would understand it
> better. And yes, I made that suggestion way back in the Ada 9X days,
> but even then you were too entrenched with the awful terminology of
> "accessibility" to consider the change.)

During 9X, we made several terminology changes in this area, and (I think) at
least one version of the documents used "lifetime".  We went round and round in
circles, and it was always confusing to folks, so I don't think it's a mere
terminology issue.  It's just hard.

The terms "accessibility" and "deeper than" were invented fairly late in the
game, as I recall, so I don't think it's fair to say "entrenched".

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

From: Randy Brukardt
Sent: Wednesday, March 11, 2009  6:24 PM

> I think there are only two.  That is, my notion agrees with Tucker's
> notion, except I said "exception" where Tucker said "compile-time
> error" -- but I think the truth is, "exception at run-time, but it's
> always detectable at compile time in this sort of case, so you get at
> least a compile-time warning".

Maybe, but what you describe could fairly be called a "fully dynamic model"
(since warnings don't count), but Tucker is describing static checks, which
surely aren't included in "fully dynamic". That is, you and Tucker are closer
together than I was to either of you, but still significantly different.

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

From: Randy Brukardt
Sent: Wednesday, March 11, 2009  6:51 PM

> I'm not personally interested in reinventing Ada 95's
anonymous-access-type rules.

I happen to think that this focus on "anonymous access" is misguided.
Accessibility is a mess for all access types, and we ought to consider ways to
improve that situation for all types. Little patches on the edges of anonymous
access really don't do much (especially if you don't use them).

So let me throw some additional gasoline on this fire. :-)

For me, the crux of the problem is that 99% of the time, you have to end-run the
accessibility rules. That is, you have to use 'Unchecked_Access rather than
'Access for objects almost always. I used to like to say that I had never found
a case in my actual code where I could actually use 'Access. Unfortunately for
that statement, I actually found one last month: I needed to put library-level
objects into a library-level sorting structure. 'Access actually worked. So I
need a new sound bite.

It sticks me that all of the rigmarole about accessibility is virtually always
wasted effort. There are only three cases of general interest:

(1) Putting library-level stuff into a library-level structure. (This includes
    everything allocated from library-level storage pools).
(2) Creating an access with a very short-lifetime (only as long as the current
    call or shorter).
(3) Putting shorter-lived stuff into a library-level data
    structure (presumably with some outside mechanism, such as finalization, to
    make it safe).

The current accessibility rules work fine for the first. They sometimes work for
the second, but not for short lifetime returns. In the third case, no static
rules have a chance.

Most of my work has involved cases of (3). Thus I never can use 'Access -
accessibility rules simply get in the way. Indeed, I believe all good ADTs
should be written to allow the client's objects to be managed by the client,
including allowing them to be stack allocated, but Ada makes that hard and
safety is has to be provided by the programmer because the language is actively
resisting.

Note that putting data structures inside of a main subprogram is technically a
case of (2), even though in this case, "very short" isn't that short. Probably a
better description is "no longer than the current call".

If an access type is intended to be used for (1), then it ought to have
library-level accessibility no matter where it is declared. If an access type is
intended to be used for (2), then it ought to have call-level accessibility (or
local accessibility if we find some other way to deal with accessors). If an
access type is intended to be used for (3), then it ought to have fully dynamic
accessibility.

This applies equally to anonymous and named access types.

The fact that the accessibility depends mostly on the intended use is the reason
that I suggested a mechanism for specifying accessibility (with some syntax;
Tucker's suggestion surely is better than mine).

Unfortunately, I don't think we can make an efficient fully dynamic
accessibility, so (3) isn't practical. That's more than too bad, it means that
Ada is doomed for high-level programming (unless we begin to mandate garbage
collection as an option for some types -- but I don't see how that could be any
more efficient than proper dangling pointer checks).

One option would be to define an "unchecked" accessibility (meaning that you're
on your own). But that doesn't help making "good" ADTs easily.

This of course would be easier if we were starting from scratch: in that case,
every access type would default to library-level accessibility and you would
have ways to declare something else. But of course that is incompatible, and
probably too incompatible to do. So I think we're stuck with providing a way to
declare accessibility on all access types and leaving the current rules alone as
much as possible.

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

From: Tucker Taft
Sent: Wednesday, March 11, 2009  7:24 PM

> ...
> It sticks me that all of the rigmarole about accessibility is
> virtually always wasted effort. There are only three cases of general interest:
>
> (1) Putting library-level stuff into a library-level structure. (This
> includes everything allocated from library-level storage pools).
> (2) Creating an access with a very short-lifetime (only as long as the
> current call or shorter).
> (3) Putting shorter-lived stuff into a library-level data structure
> (presumably with some outside mechanism, such as finalization, to make
> it safe)...

I'm surprised you don't have cases where you have things with modest-length
lifetime (e.g. for an entire pass of some algorithm).  We certainly have
situations like that, and 'Access can be useful in that case.  For example, you
declare a local object of some sort in a procedure, pass 'Access of it to
something that does a lot of processing, and then on return, the object is
cleaned up automatically.

I find significant comfort when I can use 'Access rather than 'Unchecked_Access,
because I know there is one less possible dangling reference to worry about.
One reason I find the 'Ref proposal so unappealing is that it is essentially
giving you only what you have in C, with no ability to point to objects on the
stack without throwing away all checking.

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

From: Randy Brukardt
Sent: Wednesday, March 11, 2009  8:00 PM

I don't think that has ever happened to me, for two^H^H^Hthree reasons:
(A) I'd rather pass the object itself than an access to it. So if a subsystem needs an access to an object, it has to make the access itself from the parameter (there is a convinient rule that makes all tagged parameters aliased). That falls under case (3)
, because the lifetime of the parameter cannot be assumed to be anything in particular.
(B) In all of the programs that I've written with multiple phases, either each phase is a separate program (Janus/Ada, Jasm), the phases work on a global data structure and don't take any parameters (Jlink, COrder), or the objects that are processed are al
located from a (global) storage pool (Trash-Finder, AdaServer). Most of these are older designs; I'm not sure exactly what I'd do today.
(C) Phases often include multiple calls; there's not necessarily one long-lived processing routine (that's have the Claw Builder is structured).
The subsystem that processes a phase is at library level.

Even so, this case seems to be exactly the description of case (2): the object
lives as long as the call of some (single) subprogram. It's the same as
structures created in the main subprogram, for instance. It's this case that
justifies some of the complexity of accessibility, I suppose. But in the case
you described, the access parameter could just as well have call-level
accessibility, because there is no need to put the object into a longer-lived
data structure. (If there is such a need, you are again at case (3), where no
amount of accessibility will help.)

The big problem with access parameters in the case you've described is that if
you do find (somewhere during that processing) that you need to put the object
into a global data structure, there is no way to do so. You could abandon the
execution if you catch the exception or we add the accessibility membership, but
there is no way to retroactively apply 'Unchecked_Access to the access
parameter. About all you can do is resort to Unchecked_Conversion, which means
abandoning type safety as well as dangling pointer safety. That doesn't seem to
be helping at all. (This is what happened to me when I tried to use access
parameters in the Claw Builder; I finally gave up and changed the routines in
question to procedures from functions so I could use "in out" parameters and
'Unchecked_Access on them when needed.)

> I find significant comfort when I can use 'Access rather than
> 'Unchecked_Access, because I know there is one less possible dangling
> reference to worry about.

I agree with that, the problem is that I've been able to use 'Access precisely
once in the 15 or so years that I've been programming with general access types.
That's not exactly a lot of comfort. :-) In most of the cases where you are
getting comfort, I'm using an "in out" parameter so I don't even *need* comfort
because there is no risk in the first place.

> One reason I find the
> 'Ref proposal so unappealing is that it is essentially giving you only
> what you have in C, with no ability to point to objects on the stack
> without throwing away all checking.

There might be some benefit to that if we can't find a way to do full dynamic
checking (forcing the use of 'Unchecked_Access everywhere doesn't really help
anything; a lot of programmers have given up even trying to use 'Access). But
I'd really like to find a way to handle cases like (3) safely without massive
overhead. (Unfortunately, everything that has been tried has exactly that.)

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

From: Tucker Taft
Sent: Wednesday, March 11, 2009  8:18 PM

> ...
> The big problem with access parameters in the case you've described is
> that if you do find (somewhere during that processing) that you need
> to put the object into a global data structure, there is no way to do
> so. You could abandon the execution if you catch the exception or we
> add the accessibility membership, but there is no way to retroactively
> apply 'Unchecked_Access to the access parameter. ..

Actually, "A.all'Unchecked_Access" works in most cases.

> ... About all you can do is resort to
> Unchecked_Conversion, which means abandoning type safety as well as
> dangling pointer safety.

At least A.all'Unchecked_Access doesn't abandon type safety.

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

From: Bob Duff
Sent: Wednesday, March 11, 2009  7:17 PM

> (I'm really dreading this conference call, because I don't see
> anything remotely resembling convergence on anything that has been
> discussed -- we all have our pet ideas and there doesn't seem to be
> much interest in considering anyone else's very seriously.

Well, for what it's worth, I like your idea about allowing safe read/write
access to components of containers.  Whether or not that involves (anon?) access
types, I don't know.  At least, I agree that the goal is worthy.

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

From: Randy Brukardt
Sent: Tuesday, March 17, 2009  3:57 PM

...
> Whenever I declare a type T, I have to decide whether to put "type
> Access_T is access all T" (or maybe T'Class) immediately after, just
> in case it's needed.
> If yes, I clutter the program with useless access types.  If no, then
> I have to create named access types on an as-needed basis, scattered
> about, and this exacerbates the "bogus type conversion" issue.  Or
> else I have to go back and modify existing code, which can range from
> "minor annoyance" to "impossible".
> I don't like that dilemma.  This explains my point below about 'out'
> parameters
> -- I don't like a coding rule that says, "Declare Access_T if and only
> if you need to have 'out' parameters somewhere else".

I don't understand this at all. (Note to group: yes, I'm violating my own
request of last night. This paragraph started to bother me this morning and I
have to say something now.....)

It seems to me that a single named access type should be defined for each
"big-picture" data structure in a system. (That's presuming that you can't use a
container for it, which is always the first choice.) By "big-picture" data
structure, I mean some collection of objects that interrelate; it might be made
up of several smaller data structures. For instance, in the Trash-Finder spam
filter, there are a number of queues of messages waiting to be processed in
different phases. These all share a single access type, because they all belong
to a single collection of messages being processed.

That is especially important if you want to use a pool (or set of pools in
Tucker's model) to manage the memory of that big-picture data structure. This
structuring seems more likely when you have an open package of types (as opposed
to a "closed" ADT), but I suspect it can happen other ways as well.

In this case, having to explicitly convert between access types seems like an
advantage: it means that you are converting from one big-picture data structure
to another -- ideally, you wouldn't do that at all, but in any case it is
something that should be rare and clearly marked. Anonymous access types are
actively harmful in this model.

So I don't understand declaring an access type "just in case it is needed".
It is either clearly needed (because you are going to be defining a collection
of these objects) or it is something that the client of this type should decide
(because it is going to decide what data structures will be used). Similarly, I
don't understand "creating named access types, scattered about"; you'll create
them where you are creating a big-picture data structure; if there are several
such structures, then of course there will be several access types. But they'll
only be as "scattered about" as the data structures; presuming the locations of
the data structures make sense, so will the locations of the access types.

I guess my main concern is that I'm not the least bit interested in making it
easier in Ada to create crappy designs. Obviously, we can't outlaw crappy
designs, and I surely don't want to try, but I don't see any reason to spend
effort on making it easier.

Now, the original issue that led to the expansion of anonymous access types was
the use in limited withs. The idea was that anonymous access types could be used
to build an explicit-conversion-free bridge between a named access type, across
some code where the named access type is not available, and then back to the
named access type. The problem is that this does not work in Ada 2005, you must
use explicit conversions to get back to the named type. I know that the *only*
reason I did not object to these changes was that I was misled about this
capability. Had I known that explicit conversions would still be required, I
would have fought this expansion tooth-and-nail (because then you might as well
just declare some extra named access types and do the conversions from them -
you'll have a lot fewer accessibility issues to worry about).

So to me, if we are going to do anything at all, we need to make this original
promise a reality. Otherwise, let's move the whole lot to Annex J where they
belong and forget they ever existed.

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

========= New thread: Ada-comment discussion of various accessibility ideas =====

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

From: Randy Brukardt
Sent: Tuesday, March 17, 2009  12:54 AM

This message is primarily aimed at Franco Gasperoni, although I have posted it
here so the rest of you can comment on the ensuing discussion. I'd appreciate it
if you would refrain from commenting until Franco has had a chance to respond.

Franco, you've on several occasions noted what you consider problems in
anonymous access types. Bob Duff recently remarked: "Franco and Quentin have
taught over 100 students object-oriented programming in Ada, and they say that
it is extremely important to fix these problems".

Just looking at the detailed list of problems you posted in November, it surely
seems like this is true. But this seems like a very low-level analysis to me.
I'd like to know more about the object-oriented problems that you are solving
and how and why you are using anonymous access types in this context. On
comp.lang.ada, it has nearly become a mantra that a poster needs to explain the
*problem* they are trying to solve and not necessarily the technique that they
want to solve it with. And it seems to be a good mantra: Ada really does
eliminate a lot of the need to use access types that you find in other
languages.

My point, of course, is that there may be better ways to reach the same goals
than using anonymous access types. Candidates include (but surely aren't limited
to) "in out" parameters, containers, and named access types. And I'd like to see
the actual problems in action, perhaps there is more limited versions of the
fixes available.

It would be appreciated if you could respond ASAP, as we have a telecon
scheduled for these issues on Thursday.

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

From: Franco Gasperoni
Sent: Tuesday, March 17, 2009  12:55 PM

Named access types in Ada are great for abstraction, i.e. to implement a data
type while hiding the fact that the data type is implemented with a pointer.

When coming to Ada students/developers/... that have been doing OO in other
languages such as Java or C++ are used to have references/pointers.

The Ada model

    Object : Tagged_Type'Class

is very elegant, works nicely when it comes to OO and behaves elegantly when it
comes to upcasting.

Unfortunately, in this model we can't dynamically allocate Object, we have to
initialize Object, and once we've done that we can't change the tag, we can't
chain these objects in sophisticated data structures. So we need to resort to
access types.

These access types do not serve the same purpose as what named access types in
Ada were intended for (i.e. implement a data type by hiding the details). Here
we really need a "handle" on the actual object and we need to have all the
semantics associated with the fact that we have a handle.

The handle can point to null, it can point to an object X in D'Class or an
object Y in B'Class (where D is derived from B). The handle can point to
dynamically allocated objects.

When we try to do that in Ada 95 or Ada 2005 with the current rules we hit a
number of traps. That's when we scratch our heads and stare the student in her
yes when she asks: "Look I am trying to do something very safe here and I can do
that easily in Java or C++, how do I do the same set of things safely and
conveniently in Ada".

That's our challenge.

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

From: Randy Brukardt
Sent: Tuesday, March 17, 2009  3:55 PM

(BTW, thanks for answering quickly.)

> Named access types in Ada are great for abstraction, i.e. to implement
> a data type while hiding the fact that the data type is implemented
> with a pointer.

I don't buy this; there is no hiding implicit in named access types. Sure, you
can complete a private type with one, but that's a totally different scenario.
Otherwise you can still see the designated type so they're just as transparent
as an anonymous access type. There might be a slight advantage in fewer
conversions, but I think that can be avoided with some care in the usage.

> When coming to Ada students/developers/... that have been doing OO in
> other languages such as Java or C++ are used to have
> references/pointers.

OK, but not relevant in building good Ada designs.

> The Ada model
>
>     Object : Tagged_Type'Class
>
> is very elegant, works nicely when it comes to OO and behaves
> elegantly when it comes to upcasting.

Right.

> Unfortunately, in this model we can't dynamically allocate Object, we
> have to initialize Object, and once we've done that we can't change
> the tag, we can't chain these objects in sophisticated data
> structures. So we need to resort to access types.

Resort is right. Most of these problems can be dealt with by using one of the
forms of indefinite containers. (But not always, I admit.) There is some
annoyances in using the containers, but those areas are weaknesses in the
abstraction model of Ada generally, and thus are a high priority to be addressed
in this coming language update. So I see the containers getting easier to use
with this next version of the language.

> These access types do not serve the same purpose as what named access
> types in Ada were intended for (i.e. implement a data type by hiding
> the details). Here we really need a "handle" on the actual object and
> we need to have all the semantics associated with the fact that we
> have a handle.

Sorry, that's exactly what an access type (any flavor) is for. I don't see why
you think there is some abstraction involved in a named access type. Private
types are something different altogether.

> The handle can point to null, it can point to an object X in D'Class
> or an object Y in B'Class (where D is derived from B). The handle can
> point to dynamically allocated objects.

Yes, a named general access type can do all of that.

> When we try to do that in Ada 95 or Ada 2005 with the current rules we
> hit a number of traps. That's when we scratch our heads and stare the
> student in her yes when she asks: "Look I am trying to do something
> very safe here and I can do that easily in Java or C++, how do I do
> the same set of things safely and conveniently in Ada".
>
> That's our challenge.

OK, but why don't named access types work in this context? You seem to think
they involve some abstraction, but they surely do not necessarily do that. But
even if they do, shouldn't that be a help rather than a hinderance? Don't you
want all of the "handles" for a particular data structure (or set of data
structures) to have the same type, and be notified when you try to convert to
some other data structure??

Presuming that you can't use a container for some reason, you would then declare
a named access type for each set of related data structures. You would need
explicit conversions to go between the sets, but that should be rare.

The one place where this model doesn't work is when you are using limited with.
In that case, I could see using anonymous access types as a "bridge" until you
reach a point where the named access type is fully visible. Then you would
convert back to the named access type. (And, yes, I'm well aware this model
doesn't work right because it takes an explicit conversion to come back. That is
a serious bug in Ada 2005, because the only reason I and several other people
even agreed to expanding anonymous access types was so that this specific case
would work. That fact that it does not at all work is very, very disturbing to
me.)

So, what have I missed? I don't think it is ever good practice to scatter types
about a program, and that applies to any anonymous types as well as any others.
So I think that good designs should minimize their usage, and I have no interest
in making writing bad designs in Ada easier. But perhaps there are usage cases I
have not seen. (I think some examples are going to be needed to be convincing.)

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

From: Franco Gasperoni
Sent: Tuesday, March 17, 2009  4:31 PM

>> When coming to Ada students/developers/... that have been doing OO in
>> other languages such as Java or C++ are used to have
>> references/pointers.
>
> OK, but not relevant in building good Ada designs.

But that's today's programmer base and that's the pragmatic reality we must live
with and nurture.

Today Ada has 3 incomplete access type models, you add a 4th incomplete one
containers (that in addition it isn't that easy to explain to students).

Is Ada intended for a few experts or for the average intelligence like most of
us?

Named access types aren't complete. For instance for circular data structures
(Type A contains a pointer to B and conversely) you need anonymous access. When
you upcast you need explicit conversions, why?

Named access types are awkward when interfacing to C/C++ and interfacing with
C/C++ is a reality we must embrace not despise.

All I am asking is a _single_ elegant and safe gizmo (access types named or
anonymous, containes, whatever you want, but a single one) that can do
everything that Java references can and as elegantly they can (i.e. I should be
able to do circular data types, not have to resort to explicit conversions when
upcasting, ....).

That's all our students are asking for. And it has to be simple to explain.

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

From: Franco Gasperoni
Sent: Tuesday, March 17, 2009  4:49 PM

There is another crowd I was almost forgetting: those that write UML and that
generate specs from that. With named access types things are again very
cumbersome. What answer do we give them (and it has to be the same model we
explain to students)

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

From: Franco Gasperoni
Sent: Tuesday, March 17, 2009  4:52 PM

and another one are those that want to reuse C/C++ libraries from Ada. What
language gizmos do we give to the automatic API bindings builder when it comes
to pointers?

And how do we export an Ada API with access types to C/C++ folks?

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

From: Randy Brukardt
Sent: Tuesday, March 17, 2009  5:21 PM

> >> When coming to Ada students/developers/... that have been doing OO
> >> in other languages such as Java or C++ are used to have
references/pointers.
> >
> > OK, but not relevant in building good Ada designs.
>
> But that's today's programmer base and that's the pragmatic reality we
> must live with and nurture.

It's still not relevant. If there is a better way to do something in Ada, they
ought to learn to do that. If they are unwilling/unable to do that, they are
unlikely to ever be a good Ada programmer.

For me, the argument comes down to whether there is an equivalent (preferably
better) way to do something in Ada. If there is not, then that is the point at
which we have a deficiency that needs to be addressed.

> Today Ada has 3 incomplete access type models, you add a 4th
> incomplete one containers (that in addition it isn't that easy to
> explain to students).

Containers already exist in Ada 2005; I'm not adding anything. I'm saying you
need to consider the facilities that are already there before asking for new
ones.

Containers always come first, IMHO, with new students. I would expect the same
to be true for C++. You don't start out by reinventing the wheel -- you learn
how to *use* the wheel first. Once you've done that, then you learn how to
create new wheels.

As far as ease of use goes, I think it should be a primary goal of this update
to increase Ada's abstraction facilities (which should make the containers
easier to use). If we can't do that, we ought not bother doing anything.

In any case, your initial response to this multiplicity of models was to ask for
another access type model! That makes no sense at all based on this complaint.

> Is Ada intended for a few experts or for the average intelligence like
> most of us?

If your students already can program successfully in C++, they already a heck of
a lot smarter than me. ;-)

I think this is at least somewhat a case of wanting to use the same old patterns
that don't work that well (being very unsafe) and expecting Ada to somehow
produce better results.

> Named access types aren't complete. For instance for circular data
> structures (Type A contains a pointer to B and
> conversely) you need anonymous access. When you upcast you need
> explicit conversions, why?

I don't follow. The Janus/Ada compiler contains such types and it was built in
Ada 83. Surely we didn't use any anonymous access types!

Perhaps you are talking about limited with. I specifically mentioned that the
model in that one specific case is badly broken; but I'm not sure there is any
will to do anything about it.

> Named access types are awkward when interfacing to C/C++ and
> interfacing with C/C++ is a reality we must embrace not despise.

Why? Since C/C++ doesn't have any real nesting, library-level accesses are good
enough for most uses. (Named accesses are easier to ensure have convention C, as
well.) And finally, the vast majority of *T parameters should be mapped to in
out T (indeed, that is explicitly supported by the RM). Claw's bindings to Win32
use few anonymous access parameters (less than 1 out of 10, probably far less, I
didn't do a formal count). In hindsight, I think we could have used fewer still
anonymous access parameters, but we were not clear on which parameters were real
accesses and which are just "in out". (That's really a problem on the C side,
not the Ada side.)

> All I am asking is a _single_ elegant and safe gizmo (access types
> named or anonymous, containes, whatever you want, but a single one)
> that can do everything that Java references can and as elegantly they
> can (i.e. I should be able to do circular data types, not have to
> resort to explicit conversions when upcasting, ....).

I would like to see some examples of the problems that you are having, so I can
show you how I would write them. And then we'll both have a better idea where
the other is coming from.

The problem here is that you assert these things, and I don't doubt that you
believe them, but I continue to think that there is a better way that you are
not seeing. But if I'm wrong, I'd like to know it, and that takes
counter-examples.

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

From: Randy Brukardt
Sent: Tuesday, March 17, 2009  5:32 PM

> and another one are those that want to reuse C/C++ libraries from Ada.
> What language gizmos do we give to the automatic API bindings builder
> when it comes to pointers?

Doesn't much matter, because the only value of such a binding builder is to
reduce the errors in creating a thin binding (that is, writing the actual pragma
Imports). Under no circumstances that I've ever seen would you use such a thing
without building another abstraction on top of it to make it Ada-like. And that
has to be done by hand, and properly designed. If you don't do that, you lose
90% of the benefits of Ada and might even be worse off than just having written
the whole thing in C/C++.

> And how do we export an Ada API with access types to C/C++ folks?

I don't think this is a particularly important problem; most new code that's not
in Ada is likely to be in a web language these days. Moreover, I don't see any
problem with doing this (at least for C) -- we did plenty of interfacing for
Claw in straight Ada 95.

As far as interfacing tagged types to C++, we have pretty much given up on
finding any model that would work in Ada for that. A particular implementation
may be able to pull it off by sticking restrictions on the Ada side, but those
are not appropriate for the Ada language. So I think this will remain
implementation-specific at best.

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

From: Randy Brukardt
Sent: Tuesday, March 17, 2009  5:47 PM

...
> I would like to see some examples of the problems that you are having,
> so I can show you how I would write them. And then we'll both have a
> better idea where the other is coming from.
>
> The problem here is that you assert these things, and I don't doubt
> that you believe them, but I continue to think that there is a better
> way that you are not seeing. But if I'm wrong, I'd like to know it,
> and that takes counter-examples.

Given that time is rather short for this round, I'm most interested in the
problems that you think can't be solved by any Ada access types (named or
anonymous). I find that claim rather curious and would like to see what problems
you are having (preferably in a larger context). I'm less interested in the
anonymous access type, because by definition it is a less powerful cousin to
named access type (and I would need very strong reasons to support any expansion
in capabilities).

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

From: Franco Gasperoni
Sent: Wednesday, March 18, 2009  1:36 AM

>> and another one are those that want to reuse C/C++ libraries from
>> Ada. What language gizmos do we give to the automatic API bindings
>> builder when it comes to pointers?
>
> Doesn't much matter, because the only value of such a binding builder
> is to reduce the errors in creating a thin binding (that is, writing
> the actual pragma Imports). Under no circumstances that I've ever seen
> would you use such a thing without building another abstraction on top
> of it to make it Ada-like. And that has to be done by hand, and
> properly designed. If you don't do that, you lose 90% of the benefits
> of Ada and might even be worse off than just having written the whole thing in C/C++.

That is not my experience, for instance when you are doing a binding to .net or
to java libraries. That can be completely automated and you get a very nice and
elegant thick binding that today cannot work because of todays' limitations of
anonymous access.

>> And how do we export an Ada API with access types to C/C++ folks?
>
> As far as interfacing tagged types to C++, we have pretty much given up on
> finding any model that would work in Ada for that.

We haven't and it isn't that hard if the C++ ABI is used on the C++ side. The
problem again is that there isn't a single access type model that works in Ada.
Anonymous access are the ones to come closer but as we all discovered they are
broken.

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

From: Franco Gasperoni
Sent: Wednesday, March 18, 2009  11:01 AM

> My point, of course, is that there may be better ways to reach the same
> goals than using anonymous access types. Candidates include (but surely
> aren't limited to) "in out" parameters, containers, and named access types.


Randy, let me try another route in trying to explain the problem we are facing.

As a side note I agree with you that in certain situations containers or
wrappers in general can hide the mechanics of Ada access types.

Unfortunately, containers cannot always be used, for instance when creating
general graph-like data structures, when access time and memory management must
be addressed very finely for performance reasons, when developing a
safety-critical application where only simple data structures (e.g. a few linked
lists) are needed and the developer does not want to certify a whole containers
library.

In the following I would like to focus on those cases where you cannot use
containers.

For "in out" parameters I also agree that using access types to simulate "in
out" in functions is an abnormal situation and we should just have "in out" in
functions.

So I'd like to focus on why named general access parameters aren't enough.

Why Named Access isn't enough
-----------------------------
Let's assume we decided not to use anonymous access ever and just decided to use
general named access.

First hurtle:
------------
   type Access_B is access all B;
   type B is record
      ...
      XC: Access_C
   end record;

   type Access_C is access all C;
   type C is record
      ...
      XB : Access_B;
   end record;

for the above to work we must put them in the same package. To put them in
separate packages "limited with" does not help us, we would have to use
anonymous access. When doing a binding to C/C++/Java that is a deal breaker. So
in this case (when doing a C/C++/Java) binding we have to use "limited with" and
anonymous access.

You mentioned this problem in one of your emails.


Second Hurtle:
-------------
Ada *forces* us to mix anonymous and named access types when it comes to OO.

    type B is tagged record ...
    type Access_B is access all B;
    procedure P (XB : Access_B);

P is not a primitive operation: we *have* to use anonymous access arghhhhh.
But anonymous access types are broken :( :(

One could say, why don't you write

     procedure P (XB : access B) is
        Local : Access_B : Access_B (XB);

and use local inside P. That is an ackward work-around that does not help us
against the call to

     P (new B)

which depending on the context where P is called will raise an exception when
assigning to Local inside P and if no assignment is make does not guarantee that
we can free the allocation safely.

The workaround here would be to have

    Another_Local : Access_B := new B;
    P (Another_Local);

admittedly the code starts to be sprinkled with all the spurious variables whose
sole purpose is work around Ada's double general access type model :(


Third Hurtle
------------
Burdening upcast conversions in OO.

    type B is tagged record ...
    type Any_B is access all B'Class;

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

Quentin faces this issue very often. Assume we have a container of Any_B and in
the code we have

    D_Ref : Any_D := new D;
    ...

when we add D_ref to the container we need to write

    Add (Container, Any_B (D_Ref));

This upcasting conversion is what Bob calls crying wolf and is very annoying
because Ada teaches us that conversions are potentially dangerous places and
should be reviewed carefully and in the above example it is simply not true.


Way Out?
-------
(a) Fix anonymous access. Have Tuck's generalized dynamic accessibility rule
complemented with the guarantee that "new" (allocators) allocate from the heap
irrespective of the context and that these allocations can be safely deallocated.

(b) Get rid of anonymous access alltogether and give the ability to named
general access to create primitive ops, create circular data types with limited
with and avoid useless upcasting conversions (perhaps by allowing a subtype of
a named like

    type B is tagged record ...
    type Any_B is access all B'Class;

    type D is new B with record ...
    subtype Any_D is Any_B access D'Class;

(c) Change the semantics of

           Object : B'Class

     to be by reference rather than by value (ie Object would be a reference to
a B'Class object and it would be initialized to null if no initialization is
provided etc etc)

(d) Use Array indeces as we do in SPARK.

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

From: Simon Wright
Sent: Wednesday, March 18, 2009  12:29 PM

> There is another crowd I was almost forgetting: those that write UML
> and that generate specs from that. With named access types things are
> again very cumbersome. What answer do we give them (and it has to be
> the same model we explain to students)

As one of that crowd -- albeit to Ada95 -- I don't see the problem. A class in
UML will be translated using an appropriate mapping rule, which people just have
to learn. Ours uses Instances and Handles;

package Instances is
type Instance_Base is abstract tagged limited private; type Handle is access all Instance_Base'Class; for Handle'Storage_Size use 0;

with Instances;
package Gen_Class is
type Instance (<>) is new Instances.Instance_Base with private; type Handle is access all Instance;

Conversions to/from Instances.Handle aren't that common, when they do occur it's
an idiom that people (have to) get used to. Some of the need for user-written
conversions goes away because code for associations between classes is
automatically generated, and does the necessary conversions behind the scenes.

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

From: Randy Brukardt
Sent: Wednesday, March 18, 2009  2:15 PM

> > Doesn't much matter, because the only value of such a binding
> > builder is to reduce the errors in creating a thin binding (that is,
> > writing the actual pragma Imports). Under no circumstances that I've
> > ever seen would you use such a thing without building another
> > abstraction on top of it to make it Ada-like. And that has to be
> > done by hand, and properly designed. If you don't do that, you lose
> > 90% of the benefits of Ada and might even be worse off than just
> > having written
> the whole thing in C/C++.
>
> That is not my experience, for instance when you are doing a binding
> to .net or to java libraries. That can be completely automated and you
> get a very nice and elegant thick binding that today cannot work
> because of todays' limitations of anonymous access.

OK, but I still disagree. Ada is not a reference language! You have to get rid
of access types that aren't managed by Ada and any special conventions from any
interface before it is a "good" interface in my opinion. (A good interface does
not dictate the storage management to its clients.) It's completely impossible
to do that automatically for Java (which *is* a reference language), and dubious
for C++.

> >> And how do we export an Ada API with access types to C/C++ folks?
> >
> > As far as interfacing tagged types to C++, we have pretty much given
> > up on
> > finding any model that would work in Ada for that.
>
> We haven't and it isn't that hard if the C++ ABI is used on the C++ side. The
> problem again is that there isn't a single access type model that
> works in Ada.
> Anonymous access are the ones to come closer but as we all discovered
> they are broken.

Well, as I said, a single Ada compiler might be able to pull it off, but such
things are not possible for Ada (that is, the Ada Standard):

(1) It would be inappropriate for the Ada Standard to require that all tagged
types (for instance) be implemented as in C++ (or any other single language).
Obviously, there might not even be a C++ available for the target, and in any
case, such requirements would doom Ada to mediocrity: it would be impossible to
be *better* than C++ on various issues. One example: Janus/Ada does tag trimming
(that is, removing primitive subprograms that cannot be called, even through a
tag). That wouldn't be possible if we used C++ tags, as we would not be able to
tell statically which dispatching C++ calls are made.

(2) In general, there is no such thing as a C++ ABI. Compilers rarely document
the interfaces used for tags; using undocumented facilities is fraught with
problems. And documents that do exist are often wrong or misleading (defining
capabilities that don't actually work). We had to reverse-engineer the C
compilers on various targets to find out what they *really* do before trying to
interface to them. (Originally, we believed the documents and spent a lot of
effort building tools that didn't actually work.) Again, a particular
implementation may not have this problem, for instance an open source
implementation such as GNAT interfacing to an open source C++ probably can just
examine the appropriate source code in the worst case. But surely the Ada
Standard should not practically prevent the usage of proprietary compilers.

These issues mean it doesn't make sense for the Ada Standard to try to define
such interfaces.

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

From: Randy Brukardt
Sent: Wednesday, March 18, 2009  3:33 PM

> Unfortunately, containers cannot always be used, for instance when
> creating general graph-like data structures, when access time and
> memory management must be addressed very finely for performance
> reasons, when developing a safety-critical application where only
> simple data structures (e.g. a few linked lists) are needed and the
> developer does not want to certify a whole containers library.

I agree in general, although the last point seems irrelevant to your concerns,
as such a project would never use tagged types or dispatching or dynamic
allocation either. So they couldn't run into any of the issues you are having.

> In the following I would like to focus on those cases where you cannot
> use containers.
>
> For "in out" parameters I also agree that using access types to
> simulate "in out" in functions is an abnormal situation and we should
> just have "in out" in functions.
>
> So I'd like to focus on why named general access parameters aren't
> enough.
>
> Why Named Access isn't enough
> -----------------------------
> Let's assume we decided not to use anonymous access ever and just
> decided to use general named access.

Sounds like a good general plan.

> First hurtle:
> ------------
>    type Access_B is access all B;
>    type B is record
>       ...
>       XC: Access_C
>    end record;
>
>    type Access_C is access all C;
>    type C is record
>       ...
>       XB : Access_B;
>    end record;
>
> for the above to work we must put them in the same package.
> To put them in separate packages "limited with" does not help us, we
> would have to use anonymous access. When doing a binding to C/C++/Java
> that is a deal breaker.
> So in this case (when doing a C/C++/Java) binding we have to use
> "limited with"
> and anonymous access.
>
> You mentioned this problem in one of your emails.

Yes, this is the exception where you need some other mechanism. Preferably one
that works!

One option of course is to return to the original model and require all access
types to have a single representation. In that case, limited with of named
access types can work. But it means abandoning pools and stream attributes, so
it doesn't seem very promising as an approach. One could imagine a keyword to
allow such an export (it would have to be syntactic in order to work), and allow
the full generality otherwise. Still seems limiting, but definitely an
alternative.

> Second Hurtle:
> -------------
> Ada *forces* us to mix anonymous and named access types when it comes
> to OO.
>
>     type B is tagged record ...
>     type Access_B is access all B;
>     procedure P (XB : Access_B);
>
> P is not a primitive operation: we *have* to use anonymous access
> arghhhhh.
> But anonymous access types are broken :( :(

Repeat after me: Ada is not a reference language. Ada is not a reference
language. :-) Ada is an object language (where I'm using "object" in it's Ada
sense; O-O dogma gives this concept a different name which I forget at the
moment. Ada logically operates directly on objects and does not show the
"under-the-hood" references very often. It's this nature that allows Ada O-O
objects to be stack allocated, directly placed in containers, etc.

Thus, dispatching on access values is fundamentally not Ada. I know that a
number of us opposing having any solution to this problem in Ada 95, but were
squashed because of the lack of "in out" parameters on functions. Just because
other languages are designed this way does not mean that Ada should be. (The
fact that other people smoke dope does not mean that we should also be smoking
dope!!)

Therefore, procedure P ought to properly be written as:

   procedure P (XB : in out B);

And .all passed when needed. (This has the appropriate side effect of making the
fact that XB cannot be null obvious, although at least you can do that
optionally in Ada 2005.)

If you need an access of XB inside of the body of P, you can write
XB'Unchecked_Access.

> One could say, why don't you write
>
>      procedure P (XB : access B) is
>         Local : Access_B : Access_B (XB);
>
> and use local inside P.

That's almost exactly what you should do:

      procedure P (XB : in out B) is
         Local : Access_B : Access_B (XB'Unchecked_Access);

You are going to complain about the lose of safety (because of the potential for
dangling accesses). I agree; I've been searching for a way to make this safe
automatically for years, but such ways are pretty expensive. (Both the runtime
accessibility check and the parameter passing of tagged objects would require
runtime overhead whether or not the feature is used.

The real issue here is that we need to be able to manage the lifetime of
accesses dynamically, and no such way exists automatically. The best I can do is
make B controlled and have its finalization routine clean up any lingering
messes (such as global lists that it is saved on). This (when done right) works
perfectly (Claw uses this approach, for instance), but it isn't easy and
requires overhead on the design of objects.

But the only practical alternative to this design is to turn Ada into a
reference language, not only requiring your original code to work but also to
require garbage collection (because storage management is impossible if you are
passing references through) and ban the use of stack allocation and container
allocation (using references in their places). This seems like a massive change
to the Ada model, and at odds with the needs of the avionics folks among others.
I don't see how we can do that.

> That is an ackward work-around that
> does not help us against the call to
>
>      P (new B)
>
> which depending on the context where P is called will raise an
> exception when assigning to Local inside P and if no assignment is
> make does not guarantee that we can free the allocation safely.

Allocators of anonymous access types should never have been allowed in the first
place. Now that we have them, they can't be fixed. (See below.)

> The workaround here would be to have
>
>     Another_Local : Access_B := new B;
>     P (Another_Local);
>
> admittedly the code starts to be sprinkled with all the spurious
> variables whose sole purpose is work around Ada's double general
> access type model :(

If allocations are "sprinkled all over", there is something very wrong with the
design of your program. Allocations ought to belong to a particular "collection"
of objects (potentially shared amongst a set of data structures), and for that
purpose, a named access type makes sense. (I'm using "collection" informally
here, not in the Ada sense.) Allocations that are not clearly part of some
collection are impossible to manage and can't be deallocated safely in any way.
The only hope for them is to use garbage collection, which is not a required
part of the Ada model. Thus, in the absence of garbage collection, such
allocators are downright dangerous.

I realize that I hold a minority opinion here. But I am strongly against making
it easier to create bad designs in Ada. If it is impossible, then I have some
sympathy, but making it easy is actively harmful.


> Third Hurtle
> ------------
> Burdening upcast conversions in OO.

Without the below example, I would have no idea what you meant by "upcast". I
would have called them "downcast" since they are going toward the root (and the
roots of all [real] trees that I know of are on the bottom). But it would be
better to say "toward the root" and eliminate all doubt.

>     type B is tagged record ...
>     type Any_B is access all B'Class;
>
>     type D is new B with record ...
>     type Any_D is access all D'Class;
>
> Quentin faces this issue very often. Assume we have a container of
> Any_B and in the code we have
>
>     D_Ref : Any_D := new D;
>     ...
>
> when we add D_ref to the container we need to write
>
>     Add (Container, Any_B (D_Ref));
>
> This upcasting conversion is what Bob calls crying wolf and is very annoying
> because Ada teaches us that conversions are potentially dangerous places and
> should be reviewed carefully and in the above example it is simply not true.

I tend to believe that all conversions between access types should have a
conversion. That's because you are moving the object from one "collection" to
another, and that should happen only rarely. Most of the cases where this
happened in the various Claw programs and tools, we handled it using dispatching
rather than a conversion.

Anonymous access types are explicitly intended to avoid conversions, and thus
should be used very sparingly and only as a "bridge" in cases where the named
type that defines a "collection" isn't available (i.e. limited with).

I am strongly against "sprinkling" any types throughout the code, which makes
anonymous access types harmful in most circumstances.

Could you explain why you need two different types to point at objects that
clearly belong to the same collection? That seems like a design problem to me,
but there may be a good reason that I'm missing.

> Way Out?
> -------
> (a) Fix anonymous access. Have Tuck's generalized dynamic
> accessibility
rule
> complemented with the guarantee that "new" (allocators) allocate from
> the
heap
> irrespective of the context and that these allocations can be safely
> deallocated.

This latter would be the worst kind of incompatibility (technically, an "inconsistency"). Programs written on compilers that provide the behavior suggested by the current Implementation Advice would still compile and most likely would pass their tests, but
 they would be leaking memory. Which probably would run out at the worst possible time in a deployed system (for instance, during landing of an aircraft).

Remember, this is an Ada 95 feature and lots of deployed programs probably depend on it in some way. Janus/Ada does issue a warning about leaking on such allocators, but we can get away with that because we never implemented the IA. Compilers that did impl
ement that IA couldn't do so.

We could make such allocators illegal (that would tell users at compile-time to change their code), but I don't think that I can convince the others to make such a change. The best I'm hoping for is a restriction pragma, which admittedly is almost no help 
at all.

We probably can make limited fixes to anonymous access types to fix the limited
with problem and perhaps some others without too much incompatibility, but I'm
pretty sure we can't give you your nirvana.

> (b) Get rid of anonymous access alltogether and give the ability to
> named general access to create primitive ops, create circular data
> types with limited
> with and avoid useless upcasting conversions (perhaps by allowing a
> subtype of a named like
>
>     type B is tagged record ...
>     type Any_B is access all B'Class;
>
>     type D is new B with record ...
>     subtype Any_D is Any_B access D'Class;

This is another option. The latter idea is interesting, although I'd like a
better explanation of the need.

> (c) Change the semantics of
>
>            Object : B'Class
>
>      to be by reference rather than by value (ie Object would be areference to
> a B'Class object and it would be initialized to null if no
> initialization is
> provided etc etc)

I suspect that this too would be way too incompatible. (But it is much more in
line with the way I would like to see the language evolve. The most likely
problem would be with those pesky avionics applications that can't handle
dynamic allocation.)

> (d) Use Array indeces as we do in SPARK.

??? Is this supposed to be a joke? The smiley is missing.

I'd add (e):

(e) Tell people that Ada is just fine as it is and they should use it properly.

<Grin>

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

From: Martin Dowie
Sent: Wednesday, March 18, 2009  3:56 PM

>> Unfortunately, containers cannot always be used, for instance when
>> creating general graph-like data structures, when access time and
>> memory management must be addressed very finely for performance
>> reasons, when developing a safety-critical application where only
>> simple data structures (e.g. a few linked lists) are needed and the
>> developer does not want to certify a whole containers library.
>
> I agree in general, although the last point seems irrelevant to your
> concerns, as such a project would never use tagged types or
> dispatching or dynamic allocation either. So they couldn't run into
> any of the issues you are having.

That's not entirely true... I've been involved in safety-critical apps that use
dynamic allocation - provided the memory allocation is done during an
'initialisation' phase and there is no re-allocation or further claims later. I
can't remember off hand about de-allocation...

And SPARK has allowed tagged types since Issue 2.3 of the Reference
SPARK95 doc.

I can't say whether there are any safety-critical apps that bump into Franco's
problem though!

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

From: Franco Gasperoni
Sent: Wednesday, March 18, 2009  4:34 PM

>> Unfortunately, containers cannot always be used, for instance when
>> creating general graph-like data structures, when access time and
>> memory management must be addressed very finely for performance
>> reasons, when developing a safety-critical application where only
>> simple data structures (e.g. a few linked lists) are needed and the
>> developer does not want to certify a whole containers library.
>
> I agree in general, although the last point seems irrelevant to your
> concerns, as such a project would never use tagged types or
> dispatching or dynamic allocation either. So they couldn't run into
> any of the issues you are having.

They do use tagged types and dispatching in a certified level A application we
have customers that do exactly that and I'd bet you'll be flying on one of those
in the future :)


>> First hurtle:
>> ------------
>>    type Access_B is access all B;
>>    type B is record
>>       ...
>>       XC: Access_C
>>    end record;
>>
>>    type Access_C is access all C;
>>    type C is record
>>       ...
>>       XB : Access_B;
>>    end record;
 >
> attributes, so it doesn't seem very promising as an approach. One
> could imagine a keyword to allow such an export (it would have to be
> syntactic in order to work), and allow the full generality otherwise.
> Still seems limiting, but definitely an alternative.

worth trying - if you can come up with a model where we never need anonymous
access then we clearly have solved the problem one way

>
>> Second Hurtle:
>> -------------
>> Ada *forces* us to mix anonymous and named access types when it comes
>> to OO.
>>
>>     type B is tagged record ...
>>     type Access_B is access all B;
>>     procedure P (XB : Access_B);
>>
>> P is not a primitive operation: we *have* to use anonymous access
>> arghhhhh.
>> But anonymous access types are broken :( :(
>
> Repeat after me: Ada is not a reference language. Ada is not a
> reference language. :-) Ada is an object language (where I'm using
> "object" in it's Ada sense; O-O dogma gives this concept a different
> name which I forget at the moment. Ada logically operates directly on
> objects and does not show the "under-the-hood" references very often.
> It's this nature that allows Ada O-O objects to be stack allocated, directly placed in containers, etc.
>
> Thus, dispatching on access values is fundamentally not Ada. I know
> that a number of us opposing having any solution to this problem in
> Ada 95, but were squashed because of the lack of "in out" parameters
> on functions. Just

certainly it is time to have "in out" in functions regardless of this discussion

> because other languages are designed this way does not mean that Ada
> should be. (The fact that other people smoke dope does not mean that
> we should also be smoking dope!!)
>
> Therefore, procedure P ought to properly be written as:
>
>    procedure P (XB : in out B);
>
> And .all passed when needed. (This has the appropriate side effect of
> making the fact that XB cannot be null obvious, although at least you
> can do that optionally in Ada 2005.)
>
> If you need an access of XB inside of the body of P, you can write
> XB'Unchecked_Access.

Ahah - if you do that than all is well, and may be that is a path to pursue but
then let's not call it 'Unchecked_Access let's just called it 'Pointer  :) It
would be nice if it was safe but at this boint it does not look like we can have
the cake and eat it too, tools like Tuck's can worry about safety or may be that
can be done via pre-conditions (such as never pass a local object).

Seriously if you consider the above good safe Ada style then most of my problems
are solved (provided we can also call it 'XXX where XXX is something short :)

>
>> One could say, why don't you write
>>
>>      procedure P (XB : access B) is
>>         Local : Access_B : Access_B (XB);
>>
>> and use local inside P.
>
> That's almost exactly what you should do:
>
>       procedure P (XB : in out B) is
>          Local : Access_B : Access_B (XB'Unchecked_Access);
>
> You are going to complain about the lose of safety (because of the
> potential for dangling accesses). I agree; I've been searching for a
> way to make this safe automatically for years, but such ways are
> pretty expensive. (Both the runtime accessibility check and the
> parameter passing of tagged objects would require runtime overhead whether or not the feature is used.
>

Another way is via preconditions on P that would tell clients that you can't
pass it local objects


> The real issue here is that we need to be able to manage the lifetime of
> accesses dynamically, and no such way exists automatically.

right....

> The best I can
> do is make B controlled and have its finalization routine clean up any
> lingering messes (such as global lists that it is saved on). This (when done
> right) works perfectly (Claw uses this approach, for instance), but it isn't
> easy and requires overhead on the design of objects.

right controlled types can be fairly heavy artillery, also it would be nice if
controlled was just an interface that could be implemented rather than a root
type we must derive from....

>
> But the only practical alternative to this design is to turn Ada into a
> reference language, not only requiring your original code to work but also
> to require garbage collection (because storage management is impossible if
> you are passing references through) and ban the use of stack allocation and
> container allocation (using references in their places). This seems like a
> massive change to the Ada model, and at odds with the needs of the avionics
> folks among others. I don't see how we can do that.
>

The issue is not to ban it but allow both models to exist and then you chose
one or the other. Also note that we have customers that use OO tagged types try
to use anonymous access (with pain) but don't do dynamic allocation (you can
preallocate everything and never need to deallocate). We don't have an answer
for  these folks.

Also Air traffic management apps (and there are lots in ADa) are not
constrained by no dynamic allocation and they would also like to have the
option to use Ada as a safe reference language.

So Ada should be both a safe object and a reference language, it should make a
choice it should let the developer chose.

>> That is an ackward work-around that
>> does not help us against the call to
>>
>>      P (new B)
>>
>> which depending on the context where P is called will raise
>> an exception when assigning to Local inside P and if no
>> assignment is make does not guarantee that we can free the
>> allocation safely.
>
> Allocators of anonymous access types should never have been allowed in the
> first place. Now that we have them, they can't be fixed. (See below.)
>

They must be fixed or forbidden.

>> The workaround here would be to have
>>
>>     Another_Local : Access_B := new B;
>>     P (Another_Local);
>>
>> admittedly the code starts to be sprinkled with all the
>> spurious variables whose sole purpose is work around Ada's
>> double general access type model :(
>
> If allocations are "sprinkled all over", there is something very wrong with
> the design of your program. Allocations ought to belong to a particular
> "collection" of objects (potentially shared amongst a set of data
> structures), and for that purpose, a named access type makes sense. (I'm
> using "collection" informally here, not in the Ada sense.) Allocations that
> are not clearly part of some collection are impossible to manage and can't
> be deallocated safely in any way. The only hope for them is to use garbage
> collection, which is not a required part of the Ada model. Thus, in the
> absence of garbage collection, such allocators are downright dangerous.

and should forbidden :)

>
> I realize that I hold a minority opinion here. But I am strongly against
> making it easier to create bad designs in Ada. If it is impossible, then I
> have some sympathy, but making it easy is actively harmful.
>

then let's forbid such allocators. better to forbid then create hazards

>
>> Third Hurtle
>> ------------
>> Burdening upcast conversions in OO.
>
> Without the below example, I would have no idea what you meant by "upcast".
> I would have called them "downcast" since they are going toward the root
> (and the roots of all [real] trees that I know of are on the bottom). But it
> would be better to say "toward the root" and eliminate all doubt.
>
>>     type B is tagged record ...
>>     type Any_B is access all B'Class;
>>
>>     type D is new B with record ...
>>     type Any_D is access all D'Class;
>>
>> Quentin faces this issue very often. Assume we have a
>> container of Any_B and in the code we have
>>
>>     D_Ref : Any_D := new D;
>>     ...
>>
>> when we add D_ref to the container we need to write
>>
>>     Add (Container, Any_B (D_Ref));
>>
>> This upcasting conversion is what Bob calls crying wolf and is very
> annoying
>> because Ada teaches us that conversions are potentially dangerous places
> and
>> should be reviewed carefully and in the above example it is simply not
> true.
>
> I tend to believe that all conversions between access types should have a
> conversion. That's because you are moving the object from one "collection"
> to another, and that should happen only rarely. Most of the cases where this
> happened in the various Claw programs and tools, we handled it using
> dispatching rather than a conversion.
>
> Anonymous access types are explicitly intended to avoid conversions, and
> thus should be used very sparingly and only as a "bridge" in cases where the
> named type that defines a "collection" isn't available (i.e. limited with).
>
> I am strongly against "sprinkling" any types throughout the code, which
> makes anonymous access types harmful in most circumstances.
>
> Could you explain why you need two different types to point at objects that
> clearly belong to the same collection? That seems like a design problem to
> me, but there may be a good reason that I'm missing.

Quentin?

>
>> Way Out?
>> -------
>> (a) Fix anonymous access. Have Tuck's generalized dynamic accessibility
> rule
>> complemented with the guarantee that "new" (allocators) allocate from the
> heap
>> irrespective of the context and that these allocations can be
>> safely deallocated.
>
> This latter would be the worst kind of incompatibility (technically, an
> "inconsistency"). Programs written on compilers that provide the behavior
> suggested by the current Implementation Advice would still compile and most
> likely would pass their tests, but they would be leaking memory. Which
> probably would run out at the worst possible time in a deployed system (for
> instance, during landing of an aircraft).
>
> Remember, this is an Ada 95 feature and lots of deployed programs probably
> depend on it in some way. Janus/Ada does issue a warning about leaking on
> such allocators, but we can get away with that because we never implemented
> the IA. Compilers that did implement that IA couldn't do so.
>
> We could make such allocators illegal (that would tell users at compile-time
> to change their code),

Why not, let's make them illegal in Ada 2012, this can only help portabbility
and safety!

> We probably can make limited fixes to anonymous access types to fix the
> limited with problem and perhaps some others without too much
> incompatibility, but I'm pretty sure we can't give you your nirvana.

An object model that works without 'Unchecked_Access is fine.
One were the user has a choice bw object model and reference model is fine
It's the current state of things that I find problematic and you clearly agree
with me. Let's not stick to status quo.

If we all agree that Ada is not a reference language then let's get rid of all
this anonymous access nonsense, and if we agree that Ada should offer the
option to developers to be a reference language then let's fix them.


>
>> (b) Get rid of anonymous access alltogether and give the ability to named
>> general access to create primitive ops, create circular data types with
> limited
>> with and avoid useless upcasting conversions (perhaps by allowing a
> subtype of
>> a named like
>>
>>     type B is tagged record ...
>>     type Any_B is access all B'Class;
>>
>>     type D is new B with record ...
>>     subtype Any_D is Any_B access D'Class;
>
> This is another option. The latter idea is interesting, although I'd like a
> better explanation of the need.

then may be this is something to explore. The idea is to mimick

   type B is range 1 ..1_000;
   subtype D is B range 100 .. 300;

for the same reasons

>
>> (c) Change the semantics of
>>
>>            Object : B'Class
>>
>>      to be by reference rather than by value (ie Object would be a
> reference to
>> a B'Class object and it would be initialized to null if no initialization
> is
>> provided etc etc)
>
> I suspect that this too would be way too incompatible. (But it is much more
> in line with the way I would like to see the language evolve. The most
> likely problem would be with those pesky avionics applications that can't
> handle dynamic allocation.)

we don;t need dynamic applications things can be allocated from preallocated
arrays (and objects never deallocated). Another way is an idea that Bo had
where you would tell the max size that any B'Class object could have and have
the compiler allocate the max size so that Object : B'Class could mute its tag.

This is something we could investigate.

Also Ada is used in many contexts that use dynamic allocation and it is fine to
allow that

>
>> (d) Use Array indeces as we do in SPARK.
>
> ??? Is this supposed to be a joke? The smiley is missing.
>

sort of Using ideces instead of access types gets rid of a heck of a lot of
problems here :)

> I'd add (e):
>
> (e) Tell people that Ada is just fine as it is and they should use it
> properly.

Ada is not fine as is and you and I agree that things should be fixed. you made
a very convincing point in your email.

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

From: Franco Gasperoni
Sent: Wednesday, March 18, 2009  4:49 PM

> Therefore, procedure P ought to properly be written as:
>
>    procedure P (XB : in out B);
>
> And .all passed when needed. (This has the appropriate side effect of
> making the fact that XB cannot be null obvious, although at least you
> can do that optionally in Ada 2005.)
>
> If you need an access of XB inside of the body of P, you can write
> XB'Unchecked_Access.

What about introducing a new pragma to tag the fact that inside P we will use a
reference to XB and so we should pass it only objects whose lifetime is the
sames as that of B, something like

     procedure P (XB : in out B);
     pragma Reference_To (XB); -- just using some random pragma name here

and then inside P we could write

     procedure P (XB : in out B) is
     begin
       ... XB'Access

safely since the compiler could check that all calls to P are made with actuals
whose lifetime is that of type B? We could even allow the romoval of .all so one
could write

    P (new B)

or

    type Any_B is access all B'Class;

    AB : Any_B := new B;

    AB.P; -- dispatching call

that would allow us to never have to use anonymous access as parameters for
primitive operations.

Things are unfortunately a bit more complicated when we want to have a function
returning a reference to an object of type B or B'Class.....

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

From: Randy Brukardt
Sent: Wednesday, March 18, 2009  5:07 PM

> What about introducing a new pragma to tag the fact that inside P we
> will use a reference to XB and so we should pass it only objects whose
> lifetime is the sames as that of B, something like
>
>      procedure P (XB : in out B);
>      pragma Reference_To (XB); -- just using some random pragma name
> here
>
> and then inside P we could write
>
>      procedure P (XB : in out B) is
>      begin
>        ... XB'Access
>
> safely since the compiler could check that all calls to P are made
> with actuals whose lifetime is that of type B?

That's an interesting idea. I wouldn't want to use that in Claw (because we
really wanted to use local objects there), but I can imagine a number of places
that it would help. Perhaps there is some way to include/associate that with my
specified accessibility idea.

It's interesting (but not that surprising) that most of the issues that dog
anonymous access types also appear in "in out" parameters.

> We could even allow the romoval of .all so one could write
>
>     P (new B)
>
> or
>
>     type Any_B is access all B'Class;
>
>     AB : Any_B := new B;
>
>     AB.P; -- dispatching call
>
> that would allow us to never have to use anonymous access as
> parameters for primitive operations.

That seems like going a bit too far for me.

> Things are unfortunately a bit more complicated when we want to have a
> function returning a reference to an object of type B or B'Class.....

Well, if I get my pet idea, "in out" returns (which can only return writable
existing objects), then the same approach could be applied there. And we've then
wiped out the last need for explicit references in an O-O spec.

But I have to admit that's asking a lot (even though it would make the
containers much more usable).

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

From: Randy Brukardt
Sent: Wednesday, March 18, 2009  5:09 PM

...
> And SPARK has allowed tagged types since Issue 2.3 of the Reference
> SPARK95 doc.

The last time I asked about that, SPARK still didn't allow dispatching (or
access-to-subprograms, which are equivalent). O-O without dispatching is like a
three-legged stool with two legs. :-) You don't really need tagged types or
'Class if you can't dispatch. Has that changed?

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

From: Gary Dismukes
Sent: Wednesday, March 18, 2009  5:16 PM

That's still true.  SPARK allows type extension, but no use of class-wide types.

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

From: Bob Duff
Sent: Wednesday, March 18, 2009  6:15 PM

> Without the below example, I would have no idea what you meant by "upcast".
> I would have called them "downcast" since they are going toward the
> root (and the roots of all [real] trees that I know of are on the bottom).

All computer science folks draw trees upside-down, with the root at the top.
"Down" the hierarchy is toward the leaves, "up" is toward the root. This is
standard terminology in OO circles when talking about class/type hierarchies.
Please don't try to make people say "toward the root" instead of "upcast" or
"upward conversion".

Upcasts are safe (T2'Class to T1'Class, where T2 is derived from T1).
Downcasts are unsafe (T1'Class to T2'Class requires a run-time check).

The basic problem that anon access was supposed to solve (and didn't) is to
allow upcasts on pointers to be implicit (access-to-T2'Class implicitly
converted to access-to-T1'Class).  I think this whole discussion has lost sight
of that simple issue, and delved into endless complexity.  Sigh.

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

From: Randy Brukardt
Sent: Wednesday, March 18, 2009  7:14 PM

> All computer science folks draw trees upside-down, with the root at
> the top.
> "Down" the hierarchy is toward the leaves, "up" is toward the root.
> This is standard terminology in OO circles when talking about
> class/type hierarchies.  Please don't try to make people say "toward
> the root" instead of "upcast" or "upward conversion".

I highly doubt that "all" computer science folks do anything in particular at
all. This is just like stacks, sometimes people say they grow up, sometimes they
grow down. Is it too much to ask for some clarity? I really wanted to understand
exactly what is being asked, and not in some secret code.

In any case, this is an Ada subgroup, and the only thing we can truly assume is
that people know Ada terminology. (And even that is a stretch sometimes.) "cast"
has nothing whatsoever to do with Ada, and directions are ambiguous. So, yes,
please, say conversion toward the root, or give an example. (Even better, don't
do any stupid conversions at all, they usually show a design problem. :-)

> Upcasts are safe (T2'Class to T1'Class, where T2 is derived from T1).
> Downcasts are unsafe (T1'Class to T2'Class requires a run-time check).
>
> The basic problem that anon access was supposed to solve (and
> didn't) is to allow upcasts on pointers to be implicit
> (access-to-T2'Class implicitly converted to access-to-T1'Class).  I
> think this whole discussion has lost sight of that simple issue, and
> delved into endless complexity.  Sigh.

This is completely false. This discussion is pretty much the first time I've
heard this described as an issue, and I don't recall *any* discussion that ever
has talked about conversions solely in terms of anonymous access types. (That
is, access conversions are defined in terms of what is legal on direct
conversions.)

Surely that was not the point of anonymous access types in Ada 95. (It didn't
even allow conversions from access T to access T'Class: we added those in the
Corrigendum.) And the only point I ever remember being discussed (other than
some very hazy notion of generality) was about making limited with work better
(as we couldn't figure out how to import an access type in a limited view). For
that case, and ONLY that case, we were trying to avoid conversions.

I really dislike someone who didn't even come to most of the meetings making
silly claims about what problem some Ada 2005 feature was intended to solve. Or
lecturing on "common CS practice". (It took me quite a while to calm down enough
to be able to write a rational response.)

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

From: Bob Duff
Sent: Wednesday, March 18, 2009  7:55 PM

> I really dislike someone who didn't even come to most of the meetings
> making silly claims about what problem some Ada 2005 feature was intended to solve.
> Or lecturing on "common CS practice". (It took me quite a while to
> calm down enough to be able to write a rational response.)

Randy, it's really not my intention to get you all riled up.
I'm truly sorry to give offense!

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

From: Dmitry A. Kazakov
Sent: Thursday, March 19, 2009  3:39 AM

>>     type B is tagged record ...
>>     type Any_B is access all B'Class;
>>
>>     type D is new B with record ...
>>     subtype Any_D is Any_B access D'Class;
>
> This is another option. The latter idea is interesting, although I'd
> like a better explanation of the need.

The issue is about whether a type Y based on some subtype S of the parent type T
is a subtype of X based on T. Graphically:

  T <--------- S
  |              |
  X <-- ? --- Y

In the example S=D'Class, T=B'Class. X, Y are access types to them.

This is the parallel types hierarchies problem. It is not only about access
types. Consider arrays:

   type T is new Integer;
   type X is array (Positive range <>) of T;

   subtype S is T range 1..20;
   type Y is array (Positive range <>) of S;

what is relation between X and Y?

Very often, but not always Y is required to be a subtype of X.

Ada has nothing to handle this.

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

From: Franco Gasperoni
Sent: Thursday, March 19, 2009  9:17 PM

you are absolutely right.

it would be nice if we could write something like

    subtype Y is X array (Positive range <>) of S;

and if we even wanted to constrain the index something like


    subtype Y is X array (10..20) of S;


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

From: Franco Gasperoni
Sent: Thursday, March 19, 2009  9:35 AM

> It's interesting (but not that surprising) that most of the issues
> that dog anonymous access types also appear in "in out" parameters.
>

Let me go ver the things we could do to improve the situation

First: Enhance general named access and ensure we do not need anonymous access.
-------------------------------------------------------------------------------
How:

1/ generalize the notion of subtyping to all data types (see Dmitry email).
    In the aces of named access types this would give

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

       type Any_B is access all B'Class;
       subtype any_D is any_B access all D'Class;

2/ Ensure that we never need anonymous access for dispatching operations
    and that we can safely take the 'access of a parameter. This time I'll
    give you the version witout a pragma but with a new parameter mode:

       procedure P (X : ref B; ....)

    which means that X is a regular "in out" parameter for which you can safely
    take the 'Access of X inside the body of P. This has imposes constraints
    on the caller side (the accessibility level of the actual must be that of
    type B). This allows to safely point to X'Access inside P.


Second: Adjust Anonymous Access
--------------------------------
How:

1/ Add Tuck's dynamic accessibility idea

2/ Forbid anonymous allocators except for co-extensions
    This add some incompatibility with Ada 95/2005 which has a simple
    work around which I find to enhance readability. Specifically,
    given

      procedure P (XA : access B);

    the following would be illegal

      Ptr : access B := new B; --  illegal (Ada 2005 incompatibility)
      P (new B);               --  illegal (Ada   95 incompatibility)

    and one would have to write instead (note the equivalent semantics)

      Obj : aliased B;
      Ptr : access B := Obj'Access;

      declare
         Local_Obj : aliased B;
      begin
         P (Local_Obj'Access);
      end;

    which is in my opinion preferable since it makes the semantics clear
    (I find it very confusing to use "new B" to allocate data other than in
    the heap or a pol specific area).

Note that anonymous allocators in co-extensions pose no problem since they get
allocated in the same place as the englobing object and get automatically
released when the englobing object is freed.

By removing the two anonymous allocators above we are guaranteed that all
allocations are done via a named access type of some sort in some heap/debug
pool and can be freed accordingly.

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

From: Quentin Ochem
Sent: Thursday, March 19, 2009  9:38 AM

> Could you explain why you need two different types to point at
> objects that clearly belong to the same collection? That seems like a design
> problem to me, but there may be a good reason that I'm missing.

My problem is that I have to create a instance of a child of the root type, and
then do some child-specific work on it, such as initialize fields, call new
primitives, etc. E.g., in Franco's example:

   type B is tagged record ...
   procedure P1 (V : B);
   type Any_B is access all B'Class;

   type D is new B with record ...
   procedure P2 (V : D);
   type Any_D is access all D'Class;

   then do, e.g.:

   D_Ref : Any_D := new D;

   D_Ref.P2;

   Container.Add (Any_B (D_Ref));


To me, the type Any_D is wrong - seems like it adds a new type but it's not how
I want to use it - what I really want to say (and have no way currently in Ada)
is to say that Any_D is of type Any_B but points to an object of type B'Class.
Having some way of saying that Any_D is a subtype of Any_B with an additional
constraint would solve the issue. Using e.g. Franco's suggestion:

   type D is new B with record ...
   subtype Any_D is Any_B access D'Class;

or perhaps one step further, at object declaration:

   D_Ref : Any_B access D'Class := new D;

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

From: Franco Gasperoni
Sent: Thursday, March 19, 2009  10:21 AM

I missed some things in my previous email so here is a revised set of ideas


Enhancing general named access and ensure we do not need anonymous access.
--------------------------------------------------------------------------
How:

1/ generalize the notion of subtyping to all data types (see Dmitry email).
    In the aces of named access types this would give

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

       type Any_B is access all B'Class;
       subtype Any_D is any_B access all D'Class;

    Note that this means that when we go from Any_B to Any_D we would have
    a runtime check and would not need a type conversion.

    Alternatively if we wanted to make things simple we could allow implicit
    named access type conversions such as

       type Any_B is access all B'Class;
       type Any_D is access all D'Class;

    D_Ref : Any_D;
    B_Ref : Any_B := Any_D; --  implicit type conversion


2/ Ensure that we never need anonymous access for dispatching operations
    and that we can safely take the 'access of a parameter. This time I'll
    give you the version without a pragma but with a new parameter mode:

       procedure P (X : ref B; ....)

    which means that X is a regular "in out" parameter for which you can safely
    take the 'Access of X inside the body of P. This has imposes constraints
    on the caller side (the accessibility level of the actual must be that of
    type B). This allows to safely point to X by using X'Access inside P.


3/ Allow the use of named general access types when creating circular data
    types in two different packages in the presence of limitred with:

       limited with P2;
       package P1 is
          type Access_Q is access all Q;
          type Q is record
             AR : P2.Access_R;
             ...

       limited with P1;
       package P2 is
          type Access_R is access all R;
          type R is record
             AQ : P1.Access_Q;
             ...

4/ Allow "in out" for functions (anonymous access are used today to work-around
    this restriction which is a shame, we could just allow "in out" for
    functions.

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

From: Franco Gasperoni
Sent: Thursday, March 19, 2009  10:27 AM

Adjusting Anonymous Access
--------------------------------
This is a bit of a radical set of ideas so take it with a grain of salt :)

1/ Make the accessibility level of anonymous "access T" static and be
    that of T (all accessibility checks are now static)

2/ Allow anonymous allocators. They would be allocated from a pool associated
    with T and would have static accessibility (that of T).

3/ Allow "in out" as a parameter mode for anonymous access

4/ Have a T'Free procedure to de-allocate objects allocated with anonymous
    allocators

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

From: Dmitry A. Kazakov
Sent: Thursday, March 19, 2009  2:49 PM

> it would be nice if we could write something like
>
>     subtype Y is X array (Positive range <>) of S;
>
> and if we even wanted to constrain the index something like
>
>     subtype Y is X array (10..20) of S;

The solution should be universal rather than access or array type specific.
It is about propagation of the type constraint.

Let you have some type X defined by an algebraic type operation f

   X = f (T1, T2, ... Tn)

Here f is a operation that takes types as arguments and produces a new type,
such as "is access T", "is array (T1) of T2", "is record T1, T2, ..., Tn", "is
new T" etc.

Then, putting a constraint on any of Ti should produce a corresponding
constrained subtype of X.

One possible way is to allow discriminatns everywhere, and provide mapping of
various existing constraints (like array bounds, numeric ranges, access type
pools, tag of a class-wide type) to the corresponding discriminants. So that you
could declare:

   type X (L, U : Integer) is array (Integer range L..U) of Float;

then

   subtype Y is X (L => 0);  -- Index is positive

Your case with access types:

   type Any_T (Parent : T'Class''Tag) is access all T'Class (Parent);
   subtype Any_S is Any_T (Parent => S''Tag);

Note that "not null", "all", "constant" are all constraints of access types.
There would be no need to wire them into fixed constructs if they considered as
such from the beginning.

In short, it would greatly simplify and unify the language, which obviously will
never happen. (:-()

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

From: Franco Gasperoni
Sent: Friday, March 20, 2009  11:40 AM

> One possible way is to allow discriminatns everywhere,


There is a simpler solution which is

   subtype <identifier> is [null_exclusion] <subtype_mark> <type_definition>

and then it's just a matter of checking that <type_definition> is a "subset" of
the type definition of the <subtype_mark>.

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

From: Dmitry A. Kazakov
Sent: Friday, March 20, 2009  12:30 PM

This is a far more complex solution if workable at all. If I correctly
understood your proposal it would require structural types matching which
firstly not Ada and secondly undecidable.

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

From: Simon Wright
Sent: Friday, March 20, 2009  1:36 AM

...
>  then do, e.g.:
>
>  D_Ref : Any_D := new D;
>
>  D_Ref.P2;
>
>  Container.Add (Any_B (D_Ref));

D_Ref : Any_B := new D;
My_D : D renames D (D_Ref.all);  -- runtime check here would be pointless (in context!)

My_D.P2;

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

From: Quentin Ochem
Sent: Sunday, March 22, 2009  7:13 PM

That's indeed an other option - still, it feels wrong to have two operations to
do (declaration + conversion, or declaration + renaming) for this simple thing.
No other language that I know of requires such machinery. It's particularly
frustrating when using a lot of OOP - and have to write these things all around
the place which really doesn't improve readability or safety.

Anyway - just my 2 cents.

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

Questions? Ask the ACAA Technical Agent