Version 1.18 of ai05s/ai05-0190-1.txt

Unformatted version of ai05s/ai05-0190-1.txt version 1.18
Other versions for file ai05s/ai05-0190-1.txt

!standard 13.11.3(2)          11-06-03 AI05-0190-1/13
!standard 13.11.3(3)
!standard 13.11.3(4)
!standard 13.11.3(5)
!standard 13.11.3(6)
!standard 13.11.3(8)
!standard 7.6.1(11/2)
!standard H.4(8/1)
!class amendment 09-11-03
!status Amendment 2012 11-06-03
!status ARG Approved (by Letter Ballot) 9-1-1 11-05-16
!status work item 09-11-03
!status received 09-11-03
!priority Low
!difficulty Easy
!subject Global storage pool controls
!summary
Provide a means to force the use of user-defined pools, and a means to specify a particular pool to be used by default.
!problem
Some applications need to keep tight control over heap allocation. For example, it is common for object-oriented applications to have many access-to-class-wide types, but few used for allocation. Another example is an embedded system for which it is inappropriate to rely on the implementation-provided pools.
This can be done by applying "Storage_Size use 0" to all types that should not have allocators, and explicitly specifying a Storage_Pool for the few others. But this is error prone; one might forget the "Storage_Size use 0".
Another problem is that it is technically erroneous to deallocate from the "wrong" pool. But it is implementation defined which pool is used for each access type! Many Ada programmers don't know about this rule, and most Ada implementations use a single global heap by default, so it is common to write code that does "new" for one type, converts to another type, and does Unchecked_Deallocation. This common coding practise could be erroneous on some implementations. It is uncomfortable for erroneousness to be implementation defined in this way.
!proposal
(see summary)
!wording
Replace 7.6.1(11/3) [as modified by AI05-0051-1] with:
The finalization of a master performs finalization of objects created by declarations in the master in the reverse order of their creation. After the finalization of a master is complete, the objects finalized as part of its finalization cease to exist, as do any types and subtypes defined and created within the master.
Each nonderived access type T has an associated "collection", which is the set of objects created by allocators of T, or of types derived from T. Unchecked_Deallocation removes an object from its collection. Finalization of a collection consists of finalization of each object in the collection, in an arbitrary order. The collection of an access type is an object implicitly declared at the following place:
- For a named access type, the first freezing point (see 13.14) of
the type.
- For the type of an access parameter, the call that contains the
allocator.
- For the type of an access result, within the master of the call
(see 3.10.2).
[Note: This is a reference to AI05-0234-1.]
- For any other anonymous access type, the first freezing point of
the innermost enclosing declaration.
AARM Note: The place of the implicit declaration determines when allocated objects are finalized. For multiple collections declared at the same place, we do not define the order of their implicit declarations.
Remove the AARM notes 3.4(38.i), 3.10(26.b), 13.11(43.b) as they talk about the Ada 83 definition of "collection" -- which would be just confusing now.
Renumber section 13.11.3, "Pragma Controlled" to be 13.11.4. [Editor's note: it was removed by AI05-0229-1.] Add a new section:
13.11.3 Default Storage Pools
Syntax
The form of a pragma Default_Storage_Pool is as follows:
pragma Default_Storage_Pool(storage_pool_indicator);
storage_pool_indicator ::= storage_pool_name | null
A pragma Default_Storage_Pool is allowed immediately within the visible part of a package_specification, immediately within a declarative_part, or as a configuration pragma.
Name Resolution Rules
The storage_pool_name is expected to be of type Root_Storage_Pool'Class.
Legality Rules
The storage_pool_name shall denote a variable.
If the pragma is used as a configuration pragma, the storage_pool_indicator shall be null, and it defines the "default pool" to be null within all applicable compilation units (see 10.1.5), except within the immediate scope of another pragma Default_Storage_Pool. Otherwise, Redundant[the pragma occurs immediately within a sequence of declarations, and] it defines the default pool within the immediate scope of the pragma to be either null or the pool denoted by the storage_pool_name, except within the immediate scope of a later pragma Default_Storage_Pool. Redundant[Thus, an inner pragma overrides an outer one.]
A pragma Default_Storage_Pool shall not be used as a configuration pragma that applies to a compilation unit that is within the immediate scope of another such pragma.
AARM Reason: This is to prevent confusion in cases like this:
package Parent is pragma Default_Storage_Pool(...); ... end Parent;
pragma Default_Storage_Pool(...); -- Illegal! package Parent.Child is ... end Parent.Child;
where the Default_Storage_Pool on Parent.Child would not (if it were legal) override the one in Parent.
Static Semantics
The pragma applies to all nonderived access types declared in the places defined above, unless Storage_Pool or Storage_Size is specified for the type:
If the default pool is null, the Storage_Size attribute is defined by the language to be zero. Redundant[Therefore, an allocator for such a type is illegal.]
If the default pool is nonnull, the Storage_Pool attribute is that pool.
Redundant[Otherwise, there is no default pool; the standard storage pool is used for the type as described in 13.11.]
The language-defined aspect Default_Storage_Pool may be used to define the default pool for access types within an instance. The expected type for the Default_Storage_Pool aspect is Root_Storage_Pool'Class. The aspect_definition must be a name that denotes a variable. This aspect overrides any Default_Storage_Pool pragma that might apply to the generic unit.
AARM Ramification: Default_Storage_Pool is the only way to specify the storage pool for an anonymous access type.
Note that coextensions should be allocated in the same pool (or on the stack) as the outer object (see 13.11); the Storage_Pool of the access discriminant (and hence the Default_Storage_Pool) is supposed to be ignored for coextensions. This matches the required finalization point for coextensions.
The default storage pool for an allocator that occurs within an instance of a generic is defined by the Default_Storage_Pool aspect of the instantiation (if specified), or by the Default_Storage_Pool pragma that applied to the generic; the Default_Storage_Pool pragma that applies to the instantiation is irrelevant.
It is possible to specify the Default_Storage_Pool aspect for an instantiation such that allocations will fail. For example, the generic unit might be expecting a pool that supports certain sizes and alignments, and the one on the instance might be more restrictive. It is the programmer's responsibility to get this right.
The semantics of the Default_Storage_Pool aspect are similar to passing a pool object as a generic formal, and putting pragma Default_Storage_Pool at the top of the generic's visible part, specifying that formal.
[end AARM Ramification]
Implementation Permissions
An object created by an allocator that is passed as the actual parameter to an access parameter may be allocated on the stack, and automatically reclaimed, regardless of the default pool.
AARM Discussion: This matches the required finalization point for such an allocated object.
NOTE Default_Storage_Pool may be used with restrictions No_Coextensions and No_Access_Parameter_Allocators (see H.4) to ensure that all allocators use the default pool.
[End new section 13.11.3]
Add new restrictions after H.4(8.1/3), i.e. immediately after No_Anonymous_Allocators:
No_Coextensions There are no coextensions. See 3.10.2.
No_Access_Parameter_Allocators Allocators are not permitted as the actual parameter to an access parameter. See 6.1.
!discussion
Expected usage scenarios are:
- Default_Storage_Pool(null) as a configuration pragma applying to the whole
program.
To use an allocator for an access type, you have to apply a Storage_Pool or Storage_Size pragma to that type, or else put a Default_Storage_Pool pragma such that the type is within the pragma's immediate scope.
- Default_Storage_Pool in the spec of the root package of a program or
subsystem.
This default pool is used within the package and its children, but you can override it with a Storage_Pool or Storage_Size clause, or with another Default_Storage_Pool.
The wording change to 7.6.1(11/3) is necessary as the AI05-0051-1 wording assumed that anonymous access types could not use a user storage pool. Since that's no longer true, the laissez faire finalization cannot work. As a practical matter, it never could have worked (it is all too easy to write code with such dependencies, see the mail of January 27, 2011 in the !appendix for some examples).
In addition, the rules (both the Ada 2005 and the AI05-0051-1 version) does not properly handle allocators of anonymous access parameter types -- these belong to the master enclosing of the call, not the type declaration, and thus should be finalized there as well.
Thus the only part of the AI05-0051-1 changes that we retain is the exception for coextensions (their finalization is defined by 7.6.1(9.1/2), not 7.6.1(11/3)). Otherwise, we define precisely where allocators of each kind of anonymous access type are finalized. We prefer to use the freezing point of the associated entity if in doubt.
!example
with My_Pools; package P is
pragma Default_Storage_Pool(My_Pools.My_Pool);
type Rec is record Link : access Rec; ... end record;
type Rec_Ptr is access all Rec; end P;
The Default_Storage_Pool ensures that type Rec_Ptr and the anonymous type of Link share the same pool.
!corrigendum 7.6.1(11/2)
Replace the paragraph:
The order in which the finalization of a master performs finalization of objects is as follows: Objects created by declarations in the master are finalized in the reverse order of their creation. For objects that were created by allocators for an access type whose ultimate ancestor is declared in the master, this rule is applied as though each such object that still exists had been created in an arbitrary order at the first freezing point (see 13.14) of the ultimate ancestor type; the finalization of these objects is called the finalization of the collection. After the finalization of a master is complete, the objects finalized as part of its finalization cease to exist, as do any types and subtypes defined and created within the master.
by:
The finalization of a master performs finalization of objects created by declarations in the master in the reverse order of their creation. After the finalization of a master is complete, the objects finalized as part of its finalization cease to exist, as do any types and subtypes defined and created within the master.
Each nonderived access type T has an associated collection, which is the set of objects created by allocators of T, or of types derived from T. Unchecked_Deallocation removes an object from its collection. Finalization of a collection consists of finalization of each object in the collection, in an arbitrary order. The collection of an access type is an object implicitly declared at the following place:
!corrigendum 13.11.3(2)
Replace the paragraph:
The form of a pragma Controlled is as follows:
by:
The form of a pragma Default_Storage_Pool is as follows:
!corrigendum 13.11.3(3)
Replace the paragraph:
pragma Controlled(first_subtype_local_name)
by:
pragma Default_Storage_Pool(storage_pool_indicator);
storage_pool_indicator ::= storage_pool_name | null
A pragma Default_Storage_Pool is allowed immediately within the visible part of a package_specification, immediately within a declarative_part, or as a configuration pragma.
Name Resolution Rules
The storage_pool_name is expected to be of type Root_Storage_Pool'Class.
!corrigendum 13.11.3(4)
Replace the paragraph:
The first_subtype_local_name of a pragma Controlled shall denote a non-derived access subtype.
by:
The storage_pool_name shall denote a variable.
If the pragma is used as a configuration pragma, the storage_pool_indicator shall be null, and it defines the default pool to be null within all applicable compilation units (see 10.1.5), except within the immediate scope of another pragma Default_Storage_Pool. Otherwise, the pragma occurs immediately within a sequence of declarations, and it defines the default pool within the immediate scope of the pragma to be either null or the pool denoted by the storage_pool_name, except within the immediate scope of a later pragma Default_Storage_Pool. Thus, an inner pragma overrides an outer one.
A pragma Default_Storage_Pool shall not be used as a configuration pragma that applies to a compilation unit that is within the immediate scope of another such pragma.
!corrigendum 13.11.3(5)
Replace the paragraph:
A pragma Controlled is a representation pragma that specifies the controlled aspect of representation.
by:
The pragma applies to all nonderived access types declared in the places defined above, unless Storage_Pool or Storage_Size is specified for the type:
!corrigendum 13.11.3(6)
Replace the paragraph:
Garbage collection
is a process that automatically reclaims storage, or moves objects to a different address, while the objects still exist.
by:
The language-defined aspect Default_Storage_Pool may be used to define the default pool for access types within an instance. The expected type for the Default_Storage_Pool aspect is Root_Storage_Pool'Class. The aspect_definition must be a name that denotes a variable. This aspect overrides any Default_Storage_Pool pragma that might apply to the generic unit.
!corrigendum 13.11.3(8)
Replace the paragraph:
An implementation need not support garbage collection, in which case, a pragma Controlled has no effect.
by:
An object created by an allocator that is passed as the actual parameter to an access parameter may be allocated on the stack, and automatically reclaimed, regardless of the default pool.
NOTES
10 Default_Storage_Pool may be used with restrictions No_Coextensions and No_Access_Parameter_Allocators (see H.4) to ensure that all allocators use the default pool.
!corrigendum H.4(8/1)
Insert after the paragraph:
No_Local_Allocators
Allocators are prohibited in subprograms, generic subprograms, tasks, and entry bodies.
the new paragraphs:
No_Coextensions
There are no coextensions. See 3.10.2.
No_Access_Parameter_Allocators
Allocators are not permitted as the actual parameter to an access parameter. See 6.1.
!ACATS test
ACATS B and C tests are needed.
!ASIS
This needs an enumeration literal to be added to the type pragma_kinds for the new pragma.
!appendix

From: Bob Duff
Sent: Thursday, November 19, 2009  12:52 PM

New version of AI05-0190-1. [This is version /02 of this AI. - ED]

The previous version used a Restriction to specify "no allocators allowed by
default".  I added pragma Default_Storage_Pool to specify a pool to be used by
default.  Having done that, it seemed better to use a pragma
No_Default_Storage_Pool instead of that Restriction.

I changed the !subject accordingly.

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

From: Bob Duff
Sent: Sunday, November 22, 2009  1:16 PM

I wrote:

> Provide a means to force the use of user-defined pools, and a means to
> specify a particular pool to be used by default.

One interesting question, which comes from a private conversation I had with
Laurent Guerby, is:

What happens if pragma Default_Storage_Spool applies to an instantiation?
Does it apply to access types in the instance?
If so, does that provide a way to control the storage pool used for instances of
Ada.Constainers.Vectors and friends?

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

From: Randy Brukardt
Sent: Tuesday, November 24, 2009  12:28 AM

It's clear that No_Default_Storage_Pool had better apply to instances, so that
any allocators in the generic units are detected. So I suppose from that it
implies that a similar capability exists in the other direction.

But such a capability would be next to useless. There is not (and better not be,
IMHO) any requirements on how the containers packages use the (default) storage
pool (except of course for the bounded forms, where using a pool at all is
disallowed). In particular, a container can request storage from the pool with
any size and any alignment, and as part of any operation. I suppose a program
tied to a particular implementation of the containers might be able to gain some
advantage from a custom pool, but such code is unlikely to be portable.

Note that this is true of any generic that allocates from the default pool.
Unless you are willing to "break privacy" by looking into the body of the
generic, you can't assume anything about how the pool will be called.

But almost all interesting user-defined storage pools make some assumptions
about the alignment and size that will be requested. Such pools would not work
on instances. It would of course be possible to create a monitoring pool that
passes the actual allocation requests to the default pool, but that is not going
to be very helpful in managing storage use.

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

From: Randy Brukardt
Sent: Tuesday, November 24, 2009  12:52 AM

> !wording

Minor issue - you didn't recommend some place to put this new wording. It can't
be floating in space!!

The old positioning of D.7(8) related specifically to it being a restriction, so
that doesn't work. So some other suggestion is needed.

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

From: Bob Duff
Sent: Tuesday, November 24, 2009   7:01 AM

Right.  I intended to suggest someplace in chap 13, but I forgot.
So I'd say it should be 13-dot-<rolldice>.

I'm presuming there was no deliberate intent to max this "optional annex" --
it's just where the Restrictions happen to live.

Actually, I guess it should be 13.11.3, "Default Storage Pools", and bump
"Pragma Controlled" up to 13.11.4.

What about the proposal overall?  Do people like it?
It's radically different from the previous version.

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

From: Tucker Taft
Sent: Tuesday, November 24, 2009   7:36 AM

> But such a capability would be next to useless.

I don't agree this would be next to useless for a container instance.  Clearly
you would have to provide a general purpose storage pool if you are going to
apply it by default to your entire system.  But often all that is wanted is an
ability to replace the system-defined default storage pool with a
project-specific default storage pool.

Unlike your experience, most of the storage pools I create are general purpose
in the sense that they handle any size or alignment, but are more specific in
when/if any automatic reclamation occurs.

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

From: Randy Brukardt
Sent: Tuesday, November 24, 2009  5:45 PM

> I don't agree this would be next to useless for a container instance.
> Clearly you would have to provide a general purpose storage pool if
> you are going to apply it by default to your entire system.  But often
> all that is wanted is an ability to replace the system-defined default
> storage pool with a project-specific default storage pool.

Not sure that makes much sense (unless your vendor's pool is particularly bad,
or if you need additional monitoring capabilities).

> Unlike your experience, most of the storage pools I create are general
> purpose in the sense that they handle any size or alignment, but are
> more specific in when/if any automatic reclamation occurs.

Fine, but those aren't very necessary with the containers, which already are
handling storage management (they're required not to leak, after all). So you
are essentially repeating the existing storage management. Moreover, the
containers (other than the bounded ones) are using controlled objects, and
freeing storage in that case makes your program erroneous (and might very well
cause it to crash hard, depending on how controlled objects are implemented).
The only time it is safe to free storage is if the instance has gone away, and
in that case, the "no leak" rule means that the storage has already been freed.
So while I could see that this capability might be useful with your own
generics, it wouldn't have any real value with the unbounded/indefinite
containers.

One could imagine trying to constrain implementation choices for the unbounded
containers so that pools could be useful (for instance, we'd have to ban reusing
nodes in a different object after they're deleted -- a technique I was planning
to use). But that comes uncomfortably close to requiring the containers to have
a particular body, something that was previously rejected.

Anyway, I still think this capability would necessarily have to be provided in
Bob's proposal (it doesn't make sense otherwise), but I don't think it should be
suggested that it has any particular utility for the language-defined packages.

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

From: Tucker Taft
Sent: Tuesday, November 24, 2009   7:39 AM

I don't understand why No_Default_Storage_Pool is made into its own pragma,
rather than relying on a restriction.  All you said was:

   ... it seemed better to use a pragma
   No_Default_Storage_Pool instead of that Restriction.

Could you provide a bit more rationale?  It smells and feels like a restriction,
so I don't see the advantage of not using the Restrictions pragma.

I also find the wording for No_Default_Storage_Pool a bit confusing, as it says
that an access type that

    ... has neither a specified Storage_Pool nor Storage_Size ...

ends up with Storage_Size 0.  But it isn't clear from the pragma
Default_Storage_Pool that this has the effect of giving an access type a
*specified* Storage_Pool. I think from a language point of view it is still
unspecified, but it picks up the Default_Storage_Pool precisely because it is
unspecified.  Hence I would recommend that you be more explicit and say that any
access type that is "not within the scope of a Default_Storage_Pool nor has a
specified Storage_Pool nor a specified Storage_Size..." ends up with a
Storage_Size of zero.

It is somewhat annoying that Default_Storage_Pool can't be a configuration
pragma, but I understand the problem.  It would be nice if you could just
specify it once and have it apply "everywhere." I suppose if you organize your
library units into a small number of large subsystems, then you would only need
one per top-level library package. For example, in the RTS you would only need
it in the specs for Ada, Interfaces, and System. Of course creating a storage
pool object that can live in a Pure package would be a bit of a challenge!
Especially if it has to depend on System.Storage_Pools... ;-)

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

From: Randy Brukardt
Sent: Tuesday, November 24, 2009  5:50 PM

> I don't understand why No_Default_Storage_Pool is made into its own
> pragma, rather than relying on a restriction.

Bob might have a better reason, but to me it seems that
"No_Default_Storage_Pool" is just another form of specifying the default storage
pool. Perhaps it would be better to just use one pragma:

     pragma Default_Storage_Pool (null);

to mean no default pool, although that would require rather annoying resolution
and legality rules.

In that sense it isn't really a restriction, it is just the absence of a default
storage pool. (Every access type must have a pool before an allocator can be
used - Storage_Size = 0 could be thought of as the same as not having a pool. I
assume Bob worded it to the other way to minimize the wording changes needed.)

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

From: Tucker Taft
Sent: Tuesday, November 24, 2009  7:17 PM

I don't think this accomplishes what Bob (and I want).
You want this to be a configuration pragma, and probably be one of those that
you give all by itself in a file, so it applies to the whole library. That's why
I think it makes the most sense as a restriction.

And yes, this is for cases where you want to monitor and/or control *all*
allocation, because you don't trust or you haven't certified the vendor-provided
default storage pool, or you simply have special requirements relating to
storage management in the given target environment.

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

From: Randy Brukardt
Sent: Wednesday, November 25, 2009  12:38 PM

I was assuming that this would be a configuration pragma. I'm not sure why you
assumed otherwise. Of course, you couldn't give an actual pool in that case
because none would be visible. But there would be no problem with giving "null"
in that usage. So only one pragma is needed, and there is no need to worry about
what happens when they conflict.

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

From: Bob Duff
Sent: Wednesday, February 24, 2010  2:01 PM

New version of AI05-0190-1.

In the phone meeting, we said we didn't like allowing multiple
Default_Storage_Pools immediately within a single sequence of declarations, and
we said they should be at the start.  But that seems overly restrictive, and I
don't think the wording is that hard.  Seems like we want to allow:

    with ...;
    package P is
        My_Pool : ...;
        pragma Default_Storage_Pool(My_Pool);
        type T1 is access...;
        type T2 is access...;
    end P;

And it seems harmless and useful to allow:

    with ...;
    package P is
        My_Pool : ...;
        pragma Default_Storage_Pool(My_Pool);
        type T1 is access...;
        Other_Pool : ...;
        pragma Default_Storage_Pool(Other_Pool);
        type T2 is access...;
    end P;

So I wrote this wording:

   If the pragma occurs immediately within a package_specification or
   declarative_part, it shall precede all declarative_items (other than other
   pragmas).

   Only one such pragma is allowed immediately within a given sequence
   of declarations.

and then deleted it.  I'm including it here for the record.


The new wording and discussion sections are below; the rest of the AI remains
unchanged. [This is version /03 of the AI - Editor.]

P.S. I hate the way configuration pragmas work!

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

From: Randy Brukardt
Sent: Wednesday, February 24, 2010  6:45 PM

I don't see any wording in here defining where these pragmas apply. You seem to
assume that it is scoped like Suppress, but that doesn't happen automatically;
there has to be some words like 11.5(7.1-2/2) somewhere.

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

From: Randy Brukardt
Sent: Wednesday, February 24, 2010  6:52 PM

Sorry. Found it in the Legality Rules. It was formatted as all one long line,
and didn't see that it was out to character 700. It seems weird to have it in
Legality Rules (it's a definition and seems to belong in Static Semantics), but
that isn't a huge issue.

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

From: Bob Duff
Sent: Wednesday, February 24, 2010  7:00 PM

> I don't see any wording in here defining where these pragmas apply.
> You seem to assume that it is scoped like Suppress, but that doesn't
> happen automatically; there has to be some words like 11.5(7.1-2/2) somewhere.

If it's a configuration pragma, the general rules about those work.
If it's in a package spec or decl_part, then it says "within the pragma's
immediate scope", except within an inner one.  Isn't that sufficient?

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

From: Bob Duff
Sent: Wednesday, February 24, 2010  8:08 PM

> Found it in the Legality Rules. It was formatted as all one long line,
> and didn't see that it was out to character 700.

That's odd.  I don't use an editor that does that sort of thing.
Maybe it was garbled on your end?  I don't normally produce texts with
700-character-long lines!

>... It seems weird to have it in
> Legality Rules (it's a definition and seems to belong in Static
>Semantics),  but that isn't a huge issue.

Not sure why I put in in Legality Rules -- probably just a mistake.
It's pretty messy, anyway.  I wish we just had some global way to say such
things...

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

From: Randy Brukardt
Sent: Wednesday, February 24, 2010  8:59 PM

> That's odd.  I don't use an editor that does that sort of thing.
> Maybe it was garbled on your end?  I don't normally produce texts with
> 700-character-long lines!

Outlook 2003 seems to remove single line breaks when cutting-and-pasting. So you
might have sent something friendly, but it might have turned into a mess by the
time I filed it.

> >... It seems weird to have it in
> > Legality Rules (it's a definition and seems to belong in Static
> >Semantics),  but that isn't a huge issue.
>
> Not sure why I put in in Legality Rules -- probably just a mistake.
> It's pretty messy, anyway.  I wish we just had some global way to say
> such things...

Probably because the last Legality Rule depends on it slightly.

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

From: Bob Duff
Sent: Friday, October 22, 2010  11:15 AM

Here's a new version of AI05-0190-1, Global storage pool controls.
It addresses all of the points from the Valencia minutes, except one, which I
will discuss separately.

[This is version /04 of the AI - RLB.]

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

From: Bob Duff
Sent: Friday, October 22, 2010  11:26 AM

The one point I didn't address is this one:

    Tucker makes the claim that access parameters should also be excepted from
    the rules about the default pool. Randy says that the point was to get user
    control over all allocators. Tucker claims that these items are on the
    stack, and should never be allocated anywhere else. Randy says that is not
    what Franco and others wanted; they want all allocators to come from a
    particular pool so that the values can be moved to other types with longer
    lifetimes. Randy thinks that writing stack allocation as an allocator is
    misleading to readers anyway -- if you want stack allocation, declare an
    object! Not surprisingly, Tucker strongly disagrees and no consensus is
    reached.

I have mixed feelings about this.  On the one hand, programmers expect "new" to
do heap allocation (possibly in a user-defined pool).  On the other hand, the
stack allocation idea is kind of cool, efficiency-wise.  On the gripping hand,
not all compilers do it.  GNAT, for example, leaks memory for this:

    procedure Alloc is
       procedure P (X : access String) is
       begin
          null;
       end P;
    begin
       for X in Integer range 100_000 .. 200_000 loop
          P (new String'(1..X => 'x'));
       end loop;
    end Alloc;

(So badly that it took 5 minutes to notice the Control-C when I tried to kill
the program.  Linux has a bad habit of spending a lot of time paging to the disk
instead of paying attention to my keyboard.)

In any case, storage management is generally "Implementation Advice", so we
can't really force compilers to do anything in particular.

I think it would be a Bad Thing if this contentious issue sinks the AI.  The
fact is, passing alligators to access parameters is a rare thing.  I badly want
the ability to specify a default, and I'm willing to go either way on the issue
of access parameters.  I'll just note that if the pragma doesn't apply to access
parameters, then it doesn't accomplish the goal of preventing use of the
system-provided heap.  OTOH, it accomplishes 99% of that goal, which is good
enough for me.

Please discuss.

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

From: Tucker Taft
Sent: Friday, October 22, 2010  11:55 AM

Why not just say that in the presence of pragma Default_Storage_Pool(null),
allocators for access parameters are required to be allocated on the (secondary)
stack (or equivalently, automatically reclaimed at the end of the
statement/declaration containing the call).

It really is pretty trivial to
implement this "optimization" and the intent of this feature was that it would
work this way. This is indicated by AARM 13.1(25.a/2):

   Implementation Note: {AI95-00230-01} For access
   parameters and access discriminants, the "storage pool"
   for an anonymous access type would not normally
   exist as a separate entity. Instead, the designated
   object of the allocator would be allocated, in the
   case of an access parameter, as a local aliased
   variable at the call site, and in the case of an
   access discriminant, contiguous with the object
   containing the discriminant. This is similar to
   the way storage for aggregates is typically managed.

We shouldn't penalize all users because some implementations haven't implemented
the features as it was intended.  If an implementation wants to support this
pragma, then they will have to implement this "optimization" now.

This seems essentially equivalent to supporting:

   Blah & Integer'Image(F(X))

as a parameter.  You aren't supposed to leak storage.

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

From: Bob Duff
Sent: Monday, October 25, 2010  1:20 PM

> Why not just say that in the presence of pragma
> Default_Storage_Pool(null), allocators for access parameters are
> required to be allocated on the (secondary) stack (or equivalently,
> automatically reclaimed at the end of the statement/declaration
> containing the call).

OK.  New version included below.

[This is version /05 - Editor.]

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

From: Randy Brukardt
Sent: Monday, October 25, 2010  2:11 PM

> Why not just say that in the presence of pragma
> Default_Storage_Pool(null), allocators for access parameters are
> required to be allocated on the (secondary) stack (or equivalently,
> automatically reclaimed at the end of the statement/declaration
> containing the call).

This means the pragma is a lie in this case, because we are still allowing an
allocator from *a* default pool. The fact that the pool is somehow "safer" than
the normal default pool is irrelevant.

I realize that you can get the needed effect by using both
No_Anonymous_Allocators (from AI05-0152-1) and Default_Storage_Pool(null), but
you would have to use No_Anonymous_Allocators over the entire program.

> It really is pretty trivial to
> implement this "optimization" and the intent of this feature was that
> it would work this way.

You mean your intent. I doubt that there was ever any concurrence from any
technical group; it's just an AARM note that no one complained about enough to
get it removed. (And I know I have complained about it in the past.)

This is a significant misfeature; the only saving grace is that most compilers
have not implemented it so little code uses it. It simply advertises to do
something that it does not do; most users would be amazed to find out that the
object has such a short lifetime. In the rare cases where you want an allocator
in a call, you're going to want it to live much longer than the call itself
(typically it is creating an object and passing it to a registration routine).

Anonymous access parameters should be even less likely in Ada 2012, now that "in
out" can be used on functions. So readers will be very surprised in the rare
case when they encounter it.

Moreover, it is trivial to write this in the unlikely event that you actually
need it:

       declare
           Temp_Object : aliased Some_Type;
       begin
           A_Call (Temp_Object'Access, ...);
       end;

is far clearer than
       A_Call (new Some_Type, ...);

because the latter implies a different lifetime for the allocator.

...
> We shouldn't penalize all users because some implementations haven't
> implemented the features as it was intended.

We shouldn't penalize all readers of Ada programs because somebody thought of a
neat trick which is completely contrary to the expectations of the readers. I
think anyone that uses this trick in a program deserves a time-out from Ada
programming...

If you say Default_Storage_Pool(null), you don't want any allocators from any
default pool. Period. Making a hole for a rarely-used trick that no one should
be using in the first place is sheer idiocy.

                        Randy.

P.S. Bob says:

>I think it would be a Bad Thing if this contentious issue sinks the AI.

True. But...

>The fact is, passing alligators to access parameters is a rare thing.

It should never happen at all. It's a hacky trick that no sane Ada programmer
ought to use.

>I badly want the ability to specify a default, and I'm willing to go
>either way on the issue of access parameters.

I'm not. The Tucker-friendly version of this proposal is worse than having none
at all. I vote "no" on your current draft. Sorry.

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

From: Bob Duff
Sent: Monday, October 25, 2010  2:53 PM

> It should never happen at all. It's a hacky trick that no sane Ada
> programmer ought to use.

> I'm not. The Tucker-friendly version of this proposal is worse than
> having none at all. I vote "no" on your current draft. Sorry.

The above two paragraphs contradict each other!

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

From: Robert Dewar
Sent: Monday, October 25, 2010  3:06 PM

I am entirely sympathetic to Randy's position here, I find it plain odd for an
access value to deallocate automatically like this. Just not Ada at all, and
VERY unexpected, even worse if it is an optimiation that implementations might
or might not do. And I really don't see where a controlled value would be
finalized in this scenario. Very confusing.

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

From: Tucker Taft
Sent: Monday, October 25, 2010  3:14 PM

I didn't just make this up.  The wording is in the AARM that explains this
intent, and it has been there for over 15 years.  All compilers with AdaMagic
front ends (Aonix, Green Hills, Patriot Missile, ADI SHARC) have provided this
behavior since the beginning.  I can't believe I am the only Ada programmer in
the world who has taken advantage of this functionality.

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

From: Robert Dewar
Sent: Monday, October 25, 2010  3:15 PM

> The above two paragraphs contradict each other!

I don't see the contradiction at all.

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

From: Robert Dewar
Sent: Monday, October 25, 2010  3:18 PM

> I didn't just make this up.  The wording is in the AARM that explains
> this intent, and it has been there for over 15 years.  All compilers
> with AdaMagic front ends (Aonix, Green Hills, Patriot Missile, ADI
> SHARC) have provided this behavior since the beginning.  I can't
> believe I am the only Ada programmer in the world who has taken
> advantage of this functionality.

AARM wording never creates requirements or even implementation advice. GNAT has
never done this "optimization", and I don't particularly like the idea of doing
it. So not only do we have a situation with a feature that is controversial, but
it is also, right now, creating non-portability problems.

It is of course not surprising that AdaMagic does this optimization given the
same author of the AARM and the compiler, but as I say, AARM language never
creates requirements or impl advice, and in this case the optimization does not
seem neutral to me, it seems easy to imagine cases where the limited life time
would cause unexpected results.

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

From: Bob Duff
Sent: Monday, October 25, 2010  3:57 PM

> > The above two paragraphs contradict each other!
>
> I don't see the contradiction at all.

It seems that everyone, including Randy, believes that pragma
Default_Storage_Pool is an important feature. Certainly I do.

But Randy seems to be saying:

  Passing an allocator to an access parameter is
  unimportant, nobody should even use this feature.

  Passing an allocator to an access parameter is
  important -- it has important semantic
  interactions with Default_Storage_Pool.

That's a contradiction.  The semantics of passing an allocator to an access
parameter cannot be both important and unimportant.

If you never use feature X, then you can happily use Default_Storage_Pool
without caring about interactions with feature X -- no matter what X is, nor
what the interactions are.

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

From: Bob Duff
Sent: Monday, October 25, 2010  4:03 PM

> requirements or impl advice, and in this case the optimization does
> not seem neutral to me, it seems easy to imagine cases where the
> limited life time would cause unexpected results.

I'm remaining neutral on this sub-argument between Tucker and Randy.  I see both
sides -- so they're probably both upset with me.  ;-)

But I don't agree with your "easy to imagine..." above.  Try constructing an
example that illustrates the point.  And make sure you compile it, because
otherwise, I won't know if it runs afoul of accessibility rules.  ;-)  Actually,
you have to run it, too, since some of the relevant accessibility rules are
run-time checks!

Note that there is no suggestion to change where anything gets finalized, so
controlled types are a red herring. The existing finalization rules are
compatible with both Tucker's and Randy's view.

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

From: Robert Dewar
Sent: Monday, October 25, 2010  4:04 PM

> But Randy seems to be saying:
>
>    Passing an allocator to an access parameter is
>    unimportant, nobody should even use this feature.
>
>    Passing an allocator to an access parameter is
>    important -- it has important semantic
>    interactions with Default_Storage_Pool.

No, you mispresent him, let me restate, and consider it to be what Robert is
saying:

>>    Passing an allocator to an access parameter is
>>    not a critical feature, and not likely to be
>>    used often.

>>    But if it is used, it should behave in a normal
>>    manner, like any other allocator, and not be subject
>>    to some strange allowed optimization behavior that
>>    may make it have a shorter life time than expeted.

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

From: Bob Duff
Sent: Monday, October 25, 2010  4:12 PM

It's reasonable for Randy (and you) to believe that a non-critical feature
should behave in a certain way.

It's not reasonable to say that the semantics of a non-critical feature are
critical.

The point is: he says he's against having Default_Storage_Pool at all, unless he
gets his way on the semantics of this other, supposedly non-critical, feature.

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

From: Bob Duff
Sent: Monday, October 25, 2010  4:17 PM

>>    But if it is used, it should behave in a normal
>>    manner, like any other allocator, and not be subject
>>    to some strange allowed optimization behavior that
>>    may make it have a shorter life time than expeted.

Anyway, I want to see your example that illustrates how such a "shorter life
time" can be visible.

Also, I think you're misrepresenting Tucker's position a bit:
He's not proposing any new "strange optimization" -- the "optimization" is
already clearly allowed in Ada 95, and it would probably be REQUIRED if we had
some formal way to talk about storage leaks.

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

From: Randy Brukardt
Sent: Monday, October 25, 2010  4:24 PM

>   Passing an allocator to an access parameter is
>   unimportant, nobody should even use this feature.
>
>   Passing an allocator to an access parameter is
>   important -- it has important semantic
>   interactions with Default_Storage_Pool.
>
> That's a contradiction.  The semantics of passing an allocator to an
> access parameter cannot be both important and unimportant.

I understand your point, but you are taking these statements out of context.

We're talking about the behavior of Default_Storage_Pool(null), not this
(mis)feature in general. What Tucker's suggestion does is takes a very simple
rule (no use of default storage pools) and adds a bunch of verbiage to it to
create an exception to the rules. For a case that shouldn't happen in the first
place, so there can be no value to making a hole in the rules. And that hole can
cause trouble in the case that someone accidentally writes such an allocator
(which is *exactly* what this pragma is about detecting - accidental use of
allocators that don't have defined storage pools). So this exception does damage
to the intended purpose of the pragma. Is this going to happen very often? No,
of course not. But then again, neither is any other allocator that would be
detected by this pragma (by definition, a program using this pragma isn't going
to contain such allocators on purpose).

When you tell Robert that it is very difficult to find a legitimate use of an
allocator in this context with a longer lifetime, I surely agree. The problem
really is that people are unlikely to realize all of those things that will fail
and very well may write such an allocator thinking it does what it appears to do
(as opposed to what it *does* do). And that use will compile correctly but not
work. And of course, if that happens in some rarely used code, you may simply
not detect it until the system is fielded. Yuck.

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

From: Robert Dewar
Sent: Monday, October 25, 2010  4:36 PM

> Try constructing an example that illustrates the point.

Suppose the access type is converted to type address using UC, and passed to a C
routine that registers it for some reason or other.

Note that "easy to imagine" /= "expected to occur frequently in practice" !

> Note that there is no suggestion to change where anything gets
> finalized, so controlled types are a red herring.
> The existing finalization rules are compatible with both Tucker's and
> Randy's view.

OK, I guess I don't know enough, because that does not make sense to me, but I
am sure that's my fault! :-) Although I note that our expert on controlled types
is also confused, do perhaps you had better state the rule :-)

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

From: Robert Dewar
Sent: Monday, October 25, 2010  4:42 PM

> It's reasonable for Randy (and you) to believe that a non-critical
> feature should behave in a certain way.
>
> It's not reasonable to say that the semantics of a non-critical
> feature are critical.

Well OK, I guess what's wrong here is making the judgment of what is or what is
not critical. Only the programmer knows what is critical. You can make the
judgment that something will be seldom used, but you can't know if that unusual
usage is or is not critical.

> The point is: he says he's against having Default_Storage_Pool at all,
> unless he gets his way on the semantics of this other, supposedly
> non-critical, feature.

Well that kind of "if I can't have this little feature, I will reject the big
feature, even if the little feature is something we all agree is a corner case"
approach is not easy to deal with. But I don't see changing my opinion on that
basis.

Probably the best compromise is to have permission to allocate on the stack. I
could settle for that I suppose, even though it's a bit odd.

Actually being able to allocate things on the local stack is a generally useful
feature, I would not mind this being more generally available, as it is in
Algol68

    x := heap int;   allocates integer on the heap
    x := loc int;    allocates integer on the stack.

Actually if you try to make a *requirement* to allocate on the stack you can't
(the notion of stack is not part of the formal model of Ada which deals only in
lifetimes). All you can say is that the lifetime is bla, but that does not force
implementations to deallocate. As I noted in an earlier message, even UC makes
an explicit point of not requiring that storage be reused.

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

From: Robert Dewar
Sent: Monday, October 25, 2010  4:50 PM

> Also, I think you're misrepresenting Tucker's position a bit:
> He's not proposing any new "strange optimization" -- the
> "optimization" is already clearly allowed in Ada 95, and it would
> probably be REQUIRED if we had some formal way to talk about storage
> leaks.

So if it is allowed in Ada 95 (a surprise to me but OK), we certainly don't want
to change that. So the most we can do is talk about life times, and have IA that
says that stack allocation should be used.

So I don't see anything for Randy to get too upset about, if anyone is assuming
that the thing does not get freed, as in my example, they are writing
non-portable code, and cannot complain if it breaks.

The RM cannot require this behavior, it can only recommend it.

If I understand Randy correctly, then he is saying that he thinks we should make
an incompatible change in Ada 95, and not allow stack allocation. I really think
that we cannot change the current rule in Ada 95! I was confused into thinking
this was a new permission.

If it is an old permission, I don't see what Tuck is excercised about. There is
no way to make this permission a requirement so what's to worry about?

Randy, there is no way to remove this existing permission without creating a
gratuitous incompatibility.

I am hereby convinced to be completely neutral in this discussion of what seems
to me to be a non-issue!

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

From: Bob Duff
Sent: Monday, October 25, 2010  4:52 PM

> > Try constructing an example that illustrates the point.
>
> Suppose the access type is converted to type address using UC, and
> passed to a C routine that registers it for some reason or other.

OK, well such code would be wrong, since the "optimization"
is allowed, so such code could result in dangling pointers on some
implementations.  But I see the point -- such code might work fine on GNAT, and
if we add the "optimization" it will then fail.

> Note that "easy to imagine" /= "expected to occur frequently in
> practice" !

;-)

Indeed, I think ANY passing of "new T" to an access parameter does not occur
frequently -- much less cases that care about the issue we're discussing.

> > Note that there is no suggestion to change where anything gets
> > finalized, so controlled types are a red herring.
> > The existing finalization rules are compatible with both Tucker's
> > and Randy's view.
>
> OK, I guess I don't know enough, because that does not make sense to
> me, but I am sure that's my fault! :-) Although I note that our expert
> on controlled types is also confused, do perhaps you had better state
> the rule :-)

Well, it's easy to be confused about accessibility rules.
I don't feel like quoting all the rules in their full glory, so I'll just
illustrate with a simple example:

    procedure P(X: access T);

    P(X => new T);
    -- X.all is finalized here, before going to
    -- the next statement.  GNAT does this right,
    -- AFAIK.
    Do_Something_Else;

So if Do_Something_Else manages to get its hands on the value that X had (the
pointer) -- perhaps via your 'Address above, then at best it has a pointer to an
already-finalized object, and at worst it has a dangling pointer.

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

From: Tucker Taft
Sent: Monday, October 25, 2010  4:44 PM

How about this as a compromise?  We disallow allocators for all anonymous access
types when null is specified, but allow implementations that use the stack for
access parameter allocators to continue to do so when a default storage pool is
specified.

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

From: Tucker Taft
Sent: Monday, October 25, 2010  4:51 PM

> Note that there is no suggestion to change where anything gets
> finalized, so controlled types are a red herring.
> The existing finalization rules are compatible with both Tucker's and
> Randy's view.

Good point. I presume even if implementations don't allocate these objects on
the stack, they do properly finalize them immediately after the call. I don't
think that is optional. Because of that it does seem a bit obtuse to insist the
storage hang around.

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

From: Bob Duff
Sent: Monday, October 25, 2010  5:02 PM

> I understand your point, but you are taking these statements out of context.
>
> We're talking about the behavior of Default_Storage_Pool(null), not
> this (mis)feature in general. What Tucker's suggestion does is takes a
> very simple rule (no use of default storage pools) and adds a bunch of
> verbiage to it to create an exception to the rules. For a case that
> shouldn't happen in the first place, so there can be no value to making a hole in the rules.
> And that hole can cause trouble in the case that someone accidentally
> writes such an allocator (which is *exactly* what this pragma is about
> detecting - accidental use of allocators that don't have defined
> storage pools). So this exception does damage to the intended purpose
> of the pragma. Is this going to happen very often? No, of course not.
> But then again, neither is any other allocator that would be detected
> by this pragma (by definition, a program using this pragma isn't going
> to contain such allocators on purpose).
>
> When you tell Robert that it is very difficult to find a legitimate
> use of an allocator in this context with a longer lifetime, I surely
> agree. The problem really is that people are unlikely to realize all
> of those things that will fail and very well may write such an
> allocator thinking it does what it appears to do (as opposed to what
> it *does* do). And that use will compile correctly but not work. And
> of course, if that happens in some rarely used code, you may simply not detect
> it until the system is fielded. Yuck.

Yes, I understand all that.  I even half-agree with it (as I said, I'm trying to
be neutral on this point).

To summarize, you object to a loophole in Default_Storage_Pool(null).
I think that's a fair characterization of your position.
What I don't get is why this loophole is so important that you'd kill the whole
Default_Storage_Pool feature.

I've dealt with the alternative in CodePeer.  There's a documented coding
convention that every access type has to have a Storage_Pool or Storage_Size=0
clause.  But that's a pain in the neck, because without Default_Storage_Pool,
you have to remember to do it. If Default_Storage_Pool can catch 99% of errors
(which are NAMED access types), that's an improvement to Ada.

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

From: Bob Duff
Sent: Monday, October 25, 2010  5:09 PM

> How about this as a compromise?  We disallow allocators for all
> anonymous access types when null is specified, but allow
> implementations that use the stack for access parameter allocators to
> continue to do so when a default storage pool is specified.

Acceptable to me.

It's unfortunate that compiler writers don't agree on what to do about anon
access alligators, but Default_Storage_Pool isn't making that situation any
worse!

> Sent from my iPhone

Which is, no doubt, why your message came across as one giant-long line.  ;-)

I hope you're not texting while driving!

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

From: Bob Duff
Sent: Monday, October 25, 2010  5:17 PM

> So if it is allowed in Ada 95 (a surprise to me but OK), we certainly
> don't want to change that. So the most we can do is talk about life
> times, and have IA that says that stack allocation should be used.

That's exactly what my latest version of the AI says.

And Tucker's recent compromise proposal is even weaker
-- it changes "should" to "may", which is acceptable to me, and I hope is
acceptable to Randy.

> If it is an old permission, I don't see what Tuck is excercised about.

Well, I used to be pretty annoyed at the Alsys implementation because it totally
ignored pragma Pack on records in Ada 83, which was surely intended to do
SOMETHING useful, even though it didn't have formal requirements.  So I see
Tuck's point of view.

> I am hereby convinced to be completely neutral in this discussion of
> what seems to me to be a non-issue!

So I've won over one warring combatant into the "neutral" camp.  ;-)

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

From: Randy Brukardt
Sent: Monday, October 25, 2010  5:15 PM

...
> To summarize, you object to a loophole in Default_Storage_Pool(null).
> I think that's a fair characterization of your position.
> What I don't get is why this loophole is so important that you'd kill
> the whole Default_Storage_Pool feature.

The point of Default_Storage_Pool(null) is to detect the use of allocators that
use default storage pools. There is no technical reason that that cannot be done
100% of the time. Making a hole in it so that the case which is most likely to
be unintentionally misused is allowed makes no sense: it just complicates the
whole feature.

I don't find Default_Storage_Pool important enough to saddle it with exceptions
that detract from its purpose. That is, in this particular case, the feature
isn't important enough that a watered down version is worth having. (That's a
value proposition that differs from proposal to proposal, of course.) Your
mileage obviously varies.

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

From: Randy Brukardt
Sent: Monday, October 25, 2010  5:22 PM

> How about this as a compromise?  We disallow allocators for all
> anonymous access types when null is specified, but allow
> implementations that use the stack for access parameter allocators to
> continue to do so when a default storage pool is specified.

I probably could live with that, because users that really care could use
No_Anonymous_Allocators with that. Unfortunately, that would also throw out the
positive use of anonymous access component allocators (which are only usable
with a specified default pool).

The reason I could live with it is the associated finalization. These allocators
are so screwed up by the current language definition that nothing could make
them work the way everyone (except Tucker) expects.

Note that there is no ACATS test that checks that finalization, so it's dubious
that compilers even get that right. (I know Janus/Ada doesn't, and indeed I have
no idea when or if these ever get finalized.)

I've pretty much concluded that should Janus/Ada simply reject these allocators
always. At least until some customer complains; in that case I'll add a switch
that allows them to work (after lots of work to get the finalization right).
(It's the same strategy that I'm using with coextensions.)

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

From: Randy Brukardt
Sent: Monday, October 25, 2010  5:29 PM

...
> If I understand Randy correctly, then he is saying that he thinks we
> should make an incompatible change in Ada 95, and not allow stack
> allocation. I really think that we cannot change the current rule in
> Ada 95! I was confused into thinking this was a new permission.

No, we're only talking about the impact on pragma Default_Storage_Pool.
Tucker wants to make a special-case exception for these things. I don't believe
that they should even be used in code (because of the bizarre, counter-intuitive
semantics), and object to reducing the value of the pragma that way. That's
especially true for Default_Storage_Pool(null), which is supposed to disallow
all allocators of a default storage pool. Making a hole for this case seriously
damages the value of the pragma.

I'm less worried about the case when the pool is specified, because the
requirement to finalize way too soon means you can't do anything useful with the
object anyway, so allocating it so that the storage sticks around forever isn't
particularly valuable.

In any case, I'm not advocating any inconsistent change here, because it would
be the worst kind of change (a silent change at runtime). We have already added
restriction No_Anonymous_Allocators to allow programmers to ban these things
outright. (That should be the default IMHO, but that's another thing we can't
change.)

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

From: Robert Dewar
Sent: Monday, October 25, 2010  7:52 PM

> How about this as a compromise?  We disallow allocators for all
> anonymous access types when null is specified, but allow
> implementations that use the stack for access parameter allocators to
> continue to do so when a default storage pool is specified. -Tuck

Sounds reasonable

P.S. Tuck, your messages come across as one long line, are you using a Mac
perchance?

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

From: Robert Dewar
Sent: Monday, October 25, 2010  7:59 PM

>> To summarize, you object to a loophole in Default_Storage_Pool(null).
>> I think that's a fair characterization of your position.
>> What I don't get is why this loophole is so important that you'd kill
>> the whole Default_Storage_Pool feature.
>
> The point of Default_Storage_Pool(null) is to detect the use of
> allocators that use default storage pools. There is no technical
> reason that that cannot be done 100% of the time. Making a hole in it
> so that the case which is most likely to be unintentionally misused is
> allowed makes no sense: it just complicates the whole feature.
>
> I don't find Default_Storage_Pool important enough to saddle it with
> exceptions that detract from its purpose. That is, in this particular
> case, the feature isn't important enough that a watered down version
> is worth having. (That's a value proposition that differs from
> proposal to proposal, of course.) Your mileage obviously varies.

I find this hugely excessive rhetoric from Randy. It is the worst case I have
seen recently of best being the enemy of good. To decide that you will kill the
whole feature because of the one exception seems completely incomprehensible,
unless it is just a debating position.

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

From: Robert Dewar
Sent: Monday, October 25, 2010  8:02 PM

> Note that there is no ACATS test that checks that finalization, so
> it's dubious that compilers even get that right. (I know Janus/Ada
> doesn't, and indeed I have no idea when or if these ever get
> finalized.)

Well obviously I can't speak for Janus/Ada, but certainly GNAT gets this right,
and all AdaMagic front ends do, and I would be amazed if Rational did not have
this right.

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

From: Randy Brukardt
Sent: Monday, October 25, 2010  8:30 PM

...
> I find this hugely excessive rhetoric from Randy. It is the worst case
> I have seen recently of best being the enemy of good. To decide that
> you will kill the whole feature because of the one exception seems
> completely incomprehensible, unless it is just a debating position.

Sorry, but I don't find the feature that useful if it has holes allowing various
anonymous access allocators to slip through. *Those* are the cases I'm most
interested in detecting (as there is no way to put a pool on an anonymous
allocator). For named types, searching for all type declarations and making sure
each one has an appropriate pool does the trick; that is neither hard nor
particularly error-prone. (And an ASIS tool can do it automatically if you
want.) But you can't do that for anonymous access types. So you need compiler
support for that case. The exact cases that Tucker wants to allow! That makes
the feature too broken to want in my view.

As I said, your mileage may vary.

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

From: Randy Brukardt
Sent: Monday, October 25, 2010  8:44 PM

In the wording:

> The pragma applies to all nonderived access types declared in the
> places defined here, including within an instance of a generic unit.

As written, this breaks the contract model by requiring legality checks in
generic bodies at the point of an instance. We *never* do that. Either this
needs to be written as a post-compilation check, or there needs to be an
assume-the-worst rule, or the check needs to be done on the body when it is
compiled (and not at the point of the instance).

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

From: Randy Brukardt
Sent: Thursday, January 20, 2011  1:08 AM

I'm putting the rules for AI05-0051-1 into the standard, and in writing the
reason for the change to 7.6.1(11/2), I realized that it reason seems bogus
(now). The discussion for AI05-0051-1 says:

On a separate but related issue, this AI also recommends we relax the rules for
order of finalization of objects (other than coextensions) created by anonymous
allocators, since tying them to a particular freezing point is tricky, and
doesn't seem to be of great value to the user. All that really matters is that
they get finalized in the master determined by their accessibility level, and
that is already specified by 7.6.1(4). For named access types, there is the
concern that their collections be finalized before any corresponding storage
pool object gets finalized, but there is no similar consideration for
non-coextension anonymous allocations, since they always use the "default"
storage pool.

Unfortunately, whoever wrote this (sounds like Tucker to me) never read
AI05-0190-1. (In his defense, it probably hadn't been proposed yet.) It would
appear that the last sentence is completely false:

    procedure Mess_It_Up is
        Super : My_Storage_Pool;
        pragma Default_Storage_Pool(Super);

        Obj : access Some_Controlled_Type := new Some_Controlled_Type'(...);
    begin
        ...

The rewrite of 7.6.1(11/2) would allow the access object Obj to be finalized at
any point during the finalization of the master Mess_It_Up. However, if that
happened *after* the finalization for Super, and that finalization freed the
memory of Super (which is likely), the finalization would be erroneous. That
makes no sense, especially as the current language would have no such problem.

There seem to be three fixes (in order of my preference):

(1) Simply drop the change to 7.6.1(11/2) from AI05-0051-1. The justification
    for the change was weak to begin with, and with the use of pragma
    Default_Storage_Pool, it is downright wrong.

(2) Make it illegal for pragma Default_Storage_Pool to refer to an object in the
    same scope as the pragma. That is, for this case to be legal, the storage
    pool would have to be global. Then the order of finalization is irrelevant.
    However, that seems to disallow some important usage cases. Most
    importantly, it would prevent using the pragma to force the entire program
    to use a special storage pool (since a global Default_Storage_Pool pragma
    would necessarily be disallowed).

(3) Make pragma Default_Storage_Pool not apply to anonymous access types. But
    that was one of the primary purposes of the pragma (to give a way to specify
    a storage pool for an anonymous access type). It would make just as much
    sense to forget the pragma altogether (which is solution 3B).

So I'm in favor of (1). The justification for the change is pretty weak, and
mainly seems to be an attempt to prevent ACATS test authors from dreaming up
tests of the finalization order of allocated anonymous access types. It's hard
to imagine a well-designed program that would care; but if one exists, this
laisse faire finalization probably wouldn't work right anyway.

Thoughts??

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

From: Randy Brukardt
Sent: Thursday, January 20, 2011  1:13 AM

I should also have mentioned that I had to document this as an inconsistency
with Ada 2005 (since the behavior of programs could change from Ada 2005 to Ada
2012, with the caveat that a program that could tell would be pretty fragile
anyway, so there shouldn't be many of them). It was hard for me to justify that
inconsistency, since there doesn't seem to be an important technical reason for
the change -- its just easier for implementers.

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

From: Gary Dismukes
Sent: Thursday, January 20, 2011  1:26 PM

> ...
>
> There seem to be three fixes (in order of my preference):
>
> (1) Simply drop the change to 7.6.1(11/2) from AI05-0051-1. The
> justification for the change was weak to begin with, and with the use
> of pragma Default_Storage_Pool, it is downright wrong.

This "fix" seems reasonable.  My only worry is the assertion in the !discussion
that "tying them to a particular freezing point is tricky".  Do we know what
some of the tricky cases are and how hard they'll be to implement properly? Of
course, if it's too tricky, then implementations will just do whatever they feel
like doing (and in practice it may not matter much, as long as it's not
tested:-).

> (2) Make it illegal for pragma Default_Storage_Pool to refer to an
> object in the same scope as the pragma. That is, for this case to be
> legal, the storage pool would have to be global. Then the order of
> finalization is irrelevant. However, that seems to disallow some
> important usage cases. Most importantly, it would prevent using the
> pragma to force the entire program to use a special storage pool
> (since a global Default_Storage_Pool pragma would necessarily be disallowed).

That sounds like a no-go.

> (3) Make pragma Default_Storage_Pool not apply to anonymous access types.
> But that was one of the primary purposes of the pragma (to give a way
> to specify a storage pool for an anonymous access type). It would make
> just as much sense to forget the pragma altogether (which is solution 3B).

Also not appealing.

> So I'm in favor of (1). The justification for the change is pretty
> weak, and mainly seems to be an attempt to prevent ACATS test authors
> from dreaming up tests of the finalization order of allocated
> anonymous access types. It's hard to imagine a well-designed program
> that would care; but if one exists, this laisse faire finalization probably
> wouldn't work right anyway.
>
> Thoughts??

As another possibility, why couldn't we allow the ordering of finalization to be
relaxed, but subject to not occurring later than the finalization of the
associated storage pool?  Or would that be too hard to word properly for some
reason?

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

From: Randy Brukardt
Sent: Thursday, January 20, 2011  4:18 PM

...
> > There seem to be three fixes (in order of my preference):
> >
> > (1) Simply drop the change to 7.6.1(11/2) from AI05-0051-1. The
> > justification for the change was weak to begin with, and with the
> > use of pragma Default_Storage_Pool, it is downright wrong.
>
> This "fix" seems reasonable.  My only worry is the assertion in the
> !discussion that "tying them to a particular freezing point is
> tricky".  Do we know what some of the tricky cases are and how hard
> they'll be to implement properly?
> Of course, if it's too tricky, then implementations will just do
> whatever they feel like doing (and in practice it may not matter much,
> as long as it's not tested:-).

I think the "tricky" part is that the collection might be logically inside of
some other entity (such as a subprogram). I know this happened to me in one of
the new ACATS programs: an anonymous access type tried to generate a task master
object that ended up inside of the subprogram that was being declared.

But this can happen for named access types as well (because the freezing point
can be inside of some other construct, such as the specification for a
subprogram body), so I don't see the big deal about anonymous access types
*only*. The compiler needs to be able to move this stuff somewhere else in
either case. Maybe it's a little more common for anonymous access, but that only
helps if you are going to intentionally leave a bug in your compiler and hope
that no one hits it. Doesn't seem a like a compelling reason to me.

As far as it being "tricky", that's because it depends on freezing (always a bad
idea, because it is hard to reason about). But that's true for all access types,
so it hard to imagine how it helps to have different rules for named and
anonymous access types.

...
> > So I'm in favor of (1). The justification for the change is pretty
> > weak, and mainly seems to be an attempt to prevent ACATS test
> > authors from dreaming up tests of the finalization order of
> > allocated anonymous access types. It's hard to imagine a
> > well-designed program that would care; but if one exists, this
> > laisse faire finalization probably wouldn't work right anyway.
> >
> > Thoughts??
>
> As another possibility, why couldn't we allow the ordering of
> finalization to be relaxed, but subject to not occurring later than
> the finalization of the associated storage pool?
> Or would that be too hard to word properly for some reason?

I suppose we could do that, but one then has to wonder why it applies only to
*anonymous* access types. Whether the order matters or not would seem to apply
equally to named and anonymous access types. If it isn't important for anonymous
types, it shouldn't be important for named types, either.

Indeed, I would prefer that the finalization occurred at the point of the
declaration of the access type [*any* access type] unless that would occur after
the finalization of the storage pool. (That would be a lot easier to reason
about than either some unspecified place or at some freezing point which is
itself hard to determine.) The problem is that it is inconsistent, and we don't
have any serious problem that would justify introducing an inconsistency. So I
think we should simply forget this change.

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

From: Steve Baird
Sent: Thursday, January 20, 2011  5:44 PM

> There seem to be three fixes (in order of my preference):
 >
 > (1) Simply drop the change to 7.6.1(11/2) from AI05-0051-1.

Don't we still need to say that the rule given in 7.6 for finalization of
objects created by anonymous-access-typed allocators doesn't apply to
coextensions (which have their own rule about when they get finalized)?

If we simply drop the change as you suggest, it seems that we end up with two
(inconsistent) rules about when coextensions are finalized.

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

From: Randy Brukardt
Sent: Thursday, January 20, 2011  5:50 PM

I suppose you are right, but if that was the reason for the change, it ought to
have been mentioned somewhere. And just adding "that are not coextensions of
some other object" to the access type wording would do the trick, without any
inconsistency.

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

From: Steve Baird
Sent: Thursday, January 20, 2011  5:57 PM

> I suppose you are right, but if that was the reason for the change, it
> ought to have been mentioned somewhere.

Agreed. Fixing coextension finalization may have been an unintended (but still
desirable) consequence of this change.

> And just adding "that are not coextensions of some other object" to
> the access type wording would do the trick, without any inconsistency.

Agreed.

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

From: Tucker Taft
Sent: Thursday, January 20, 2011  7:49 AM

> As another possibility, why couldn't we allow the ordering of
> finalization to be relaxed, but subject to not occurring later than
> the finalization of the associated storage pool?  Or would that be too
> hard to word properly for some reason?

This seems preferable to me.

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

From: Randy Brukardt
Sent: Monday, January 24, 2011  9:58 PM

>                           Implementation Permissions
>
> An object created by an anonymous allocator may be allocated on the
> stack, and automatically reclaimed, regardless of the default pool.

I find this wording misleading, as it seems to imply that it is OK to allow such
allocators even when the default pool is null. That was not the intent; there is
some support for that in that Storage_Size is zero for such a type. But since
you can't name the type, how can you tell?

Either we should have an AARM "To Be Honest" note ("This does not apply whrn the
default pool is null; the allocator is statically illegal and where the object
would have been created is irrelevant.") or we should add something to the
wording:

... regardless of the default pool (other than when the default pool is null).

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

From: Bob Duff
Sent: Tuesday, January 25, 2011  9:13 AM

> >> The pragma applies to all nonderived access types declared in the
> >> places defined here, including within an instance of a generic unit.
>
> >As written, this breaks the contract model by requiring legality
> >checks in generic bodies at the point of an instance. We *never* do
> >that. Either this needs to be written as a post-compilation check, or
> >there needs to be an assume-the-worst rule, or the check needs to be
> >done on the body when it is compiled (and not at the point of the instance).
>
> This problem has not been addressed in this AI. (Nor have we discussed
> it.)

Which legality check are you talking about?  Do you mean the one that says "new
T" is illegal if (roughly speaking) Storage_Size is statically zero?  If so, it
seems like that should be a run-time check in instances -- raise Storage_Error
if the size is zero. We normally let the implementation round up Storage_Size
(e.g. to a page boundary), but we should probably explicitly say that zero stays
zero, and "new" MUST raise Storage_Error (in cases where it's not illegal).

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

From: Randy Brukardt
Sent: Tuesday, January 25, 2011  1:54 PM

I thought that the quoted wording was from a Legality Rule, but I see it is not
anymore. I also thought this controlled other legality rules, but I can't find
any. So I was mistaken. But...

The original Storage_Size = 0 wording worked for generic bodies; it is simply
not checked there. See 4.8(5.3/2). However, we *deleted* that rule in
AI05-0157-1 since it could not possibly ever happen (see AARM 4.8(5.e/3)), and
it eliminated checks on cases where we statically know the right answer.

That happened because of issues with the Unchecked_Deallocation rule that we
were adding. So we will have to go back and redo that AI completely. That is not
an AI that I want to have to reopen, but it looks like we have no choice.

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

From: Bob Duff
Sent: Tuesday, January 25, 2011  3:51 PM

> The original Storage_Size = 0 wording worked for generic bodies; it is
> simply not checked there. See 4.8(5.3/2). However, we *deleted* that
> rule in AI05-0157-1 since it could not possibly ever happen (see AARM
> 4.8(5.e/3)), and it eliminated checks on cases where we statically know the
> right answer.
>
> That happened because of issues with the Unchecked_Deallocation rule
> that we were adding. So we will have to go back and redo that AI
> completely. That is not an AI that I want to have to reopen, but it
> looks like we have no choice.

I don't understand the problem.  The reasoning in 4.8(5.e/3) seems still valid
in the presence of Default_Storage_Pool.  Perhaps you can illustrate the problem
with example code?

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

From: Randy Brukardt
Sent: Tuesday, January 25, 2011  4:04 PM

The problem is that now we can set the storage size of an access type declared
in a generic package "after the fact". For instance:

generic
package Foo is
    type Acc is access Integer;
    procedure Something (P : out Acc);
end Foo;

package body Foo is
    procedure Something (P : out Acc) is
    begin
        P := new Integer'(10);
    end Something;
end Foo;

with Foo;
procedure Test is
    pragma Default_Storage_Pool (null);
    package Bar is new Foo; -- !!
    B : Bar.Acc;
begin
    Bar.Something (B);
end Test;

The wording says that the pragma applies to the instance; that means that
Bar.Acc has a Storage_Size defined to be zero. The legality rule says that the
allocator is illegal if Storage_Size is zero (no exceptions), so we appear to
have a contract problem.

In this case, we could depend on the notion that legality rules aren't checked
in instance bodies, but that seems to be weird at best. (And it's inconsistent
with the "Redundant" text in the wording.) But even if we do that, it would seem
that we would want to recheck in the private part (why would we want it to be
unchecked there?), so we still need rewording.

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

From: Bob Duff
Sent: Tuesday, January 25, 2011  5:16 PM

>...because explicit use of pools
> does not necessarily mean heap allocation (it is quite sensible to
>have  pools totally allocated on the stack -- I have such pools).

Yeah, me too.  I'd call that a heap within a stack.

> So I tend to either suggest (1) [which is the easiest] or dropping
> Default_Storage_Pool(null) in favor of a restriction.

I can live with a restriction.  But maybe you should clarify: I think
"Default_Storage_Pool(null)" would mean exactly the same thing as "pragma
Restrictions(No_Standard_Storage_Pool)".  Do you see some difference?

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

From: Jean-Pierre Rosen
Sent: Wednesday, January 26, 2011  3:10 AM

> (3) Default_Storage_Pool never applies to instances.
> No way to control the pool on a per-instance basis.
> The "new" in your example uses whatever default pool applies to the
> generic body.
>
I had two contradictory reactions to this one:

1) the one who writes the software component is responsible for managing its
   memory, and the user should not interfere with (or even know) what's in the
   body => does not apply to instances

2) The user may have a special purpose (certified) pool, or wants to make sure
   that the generic has no hidden allocator (without having to look inside the
   body) => does apply to instances

A compromise might be that the pragma does not apply to instances (i.e.
default storage pool determined at the place of the generic), but that an
explicit default could be an aspect of the instantiation:
   package Inst is new Gen with Default_Storage_Pool => ...;

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

From: Randy Brukardt
Sent: Wednesday, January 26, 2011  6:42 PM

I've had this concern too, although it isn't specific to generics. It seems to
apply to any package that you might right. I have to wonder what might happen if
some user-defined pool got used for Claw, for one example. I can easily imagine
it causing bogus errors that would appear to be problems in Claw -- that could
be a real pain for support reasons.

Similar concerns apply to the containers. If someone applied a pool that only
supported certain sizes, the unbounded containers probably will fail. And
probably not in a way that has been tested. More vendor support ends up being
needed.

This concern again makes me wonder about the wisdom of even having this pragma.

(And if I sound confused WRT this pragma, it is because I am. That does not seem
to be a good sign to me.)

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

From: Randy Brukardt
Sent: Wednesday, January 26, 2011  6:25 PM

> I can live with a restriction.  But maybe you should clarify:
> I think "Default_Storage_Pool(null)" would mean exactly the same thing
> as "pragma
> Restrictions(No_Standard_Storage_Pool)".  Do you see some difference?

Well, there are two obvious differences. The enforcement of restrictions inside
of generics is already defined, so we don't have to do anything new there. (That
makes it a post-compilation check, as I recall.)

The other was that I was thinking of it only applying to named access types. But
the name I suggested is wrong for that; with that name, it should apply to all
access types. Not sure about that.

****************************************************************
==== Different thread starts here ====

From: Bob Duff
Sent: Tuesday, January 25, 2011  9:10 AM

> >                           Implementation Permissions
> >
> > An object created by an anonymous allocator may be allocated on the
> > stack, and automatically reclaimed, regardless of the default pool.
>
> I find this wording misleading, as it seems to imply that it is OK to
> allow such allocators even when the default pool is null.

I don't think it's misleading.  It does indeed "imply ... null".

> ...That was not the intent;

I thought it WAS the intent.  The minutes say:

    Tucker explains the latest compromise. The ?however? would be
    deleted from the null case, and an Implementation Permission would
    be added that the stack can be used rather than the specified pool
    for anonymous allocators.

    If you say no non-specified pool, that's really what you want. OTOH,
    forcing the use of a pool that requires a storage leak makes no
    sense, thus we make an exception when a pool is specified. If you
    don't want to use the stack, you can use the No_Anonymous_Allocators
    restriction.

I took this to mean that stack allocation is always OK.
And if you don't want that, you say No_Anonymous_Allocators.
But I can't imagine WHY you wouldn't want stack allocation
-- it's really HEAP allocation that causes trouble in some programs.

Tucker?  Others?

> there is some support for that in that Storage_Size is zero for such a type.
> But since you can't name the type, how can you tell?
>
> Either we should have an AARM "To Be Honest" note ("This does not
> apply whrn the default pool is null; the allocator is statically
> illegal and where the object would have been created is irrelevant.")
> or we should add something to the wording:
>
> ... regardless of the default pool (other than when the default pool
> is null).

Yes, if I misunderstood the intent, then that last "(other than..."
should be added.

P.S. I find this sub-issue to be of the utmost unimportance, compared to the
general usefulness of the whole storage-pool-controls feature. I don't care too
much how it's resolved, so long as it doesn't sink the whole feature.

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

From: Randy Brukardt
Sent: Tuesday, January 25, 2011  2:07 PM

> I took this to mean that stack allocation is always OK.
> And if you don't want that, you say No_Anonymous_Allocators.
> But I can't imagine WHY you wouldn't want stack allocation
> -- it's really HEAP allocation that causes trouble in some programs.

Obviously my note taking was a bit vague here. The critical sentence is "If you
say no non-specified pool, that's really what you want." That means you don't
want any form of "new" from any pool other than the one you specify; presumably
you are going to convert the types to some other access type and might call
Unchecked_Deallocation on them; having the pools be different is a significant
problem in that case.

Indeed, I still don't buy this compromise. The above argument works just as well
when the pool is specified to a particular value. In that case, stack allocation
will cause either runtime errors or erroneous behavior. And
No_Anonymous_Allocators is a nasty club to fix that; it eliminates the parameter
cases that cause trouble but it also eliminates the component and object cases
that cannot cause any problems (presuming the pool is as specified).

In my view, the entire point of the pragma is to ensure that every "new" in your
program comes from a single pool that you define. The fact that doesn't actually
work is a significant detriment.

An alternative would be to have restrictions that are fine-grained enough to
block the bad stuff while not disallowing stuff that isn't a problem.
Specifically:

       pragma Restrictions (No_Coextensions);
       pragma Restrictions (No_Access_Parameter_Allocators);

I've never seen any sane reason for writing a parameter allocator; an aliased
local object works just as well and isn't misleading to the reader. (And if this
happens a lot, the use of an access parameter instead of "in out" in the first
place is questionable.) These are already the default in Janus/Ada [although I
admit part of the reason is that neither work correctly anyway; it's better to
reject a construct than to implement it wrong -- but the reason that they don't
work is that no one other than Tucker cares about them].

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

From: Tucker Taft
Sent: Tuesday, January 25, 2011  2:47 PM

...
> Indeed, I still don't buy this compromise. The above argument works
> just as well when the pool is specified to a particular value. In that
> case, stack allocation will cause either runtime errors or erroneous
> behavior. And No_Anonymous_Allocators is a nasty club to fix that; it
> eliminates the parameter cases that cause trouble but it also
> eliminates the component and object cases that cannot cause any
> problems (presuming the pool is as specified).

This is questionable in my view, because even if you specify that the anonymous
allocator use a particular storage pool, the object created will still be
finalized when the subprogram call is completed.

...
> I've never seen any sane reason for writing a parameter allocator; an
> aliased local object works just as well and isn't misleading to the reader.

I don't see this as a valid argument.  This has been in the language for 15
years, and some people use it as it was described and intended to be used.  It
works like a stack-based object, and is finalized upon return.

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

From: Bob Duff
Sent: Tuesday, January 25, 2011  3:22 PM

> > I took this to mean that stack allocation is always OK.
> > And if you don't want that, you say No_Anonymous_Allocators.
> > But I can't imagine WHY you wouldn't want stack allocation
> > -- it's really HEAP allocation that causes trouble in some programs.
>
> Obviously my note taking was a bit vague here. The critical sentence
> is "If you say no non-specified pool, that's really what you want."

I thought that was somebody (probably you) expressing that opinion, and then the
"OTOH..." was somebody else (probably Tucker) expressing the opposite opinion,
and the "thus we..." indicates that we're going with the second opinion.

>... That means you
> don't want any form of "new" from any pool other than the one you
>specify;  presumably you are going to convert the types to some other
>access type and  might call Unchecked_Deallocation on them; having the
>pools be different is  a significant problem in that case.
>
> Indeed, I still don't buy this compromise. The above argument works
> just as well when the pool is specified to a particular value. In that
> case, stack allocation will cause either runtime errors or erroneous
> behavior. And No_Anonymous_Allocators is a nasty club to fix that; it
> eliminates the parameter cases that cause trouble but it also
> eliminates the component and object cases that cannot cause any
> problems (presuming the pool is as specified).
>
> In my view, the entire point of the pragma is to ensure that every
> "new" in your program comes from a single pool that you define. The
> fact that doesn't actually work is a significant detriment.

Tucker's view is almost identical.  I think it's fair to characterize his view
as "the point of the pragma is to ensure that every heap allocation in your
program comes from a single pool that you define."

You seem to believe that coextensions and access parameter allocators are evil
and/or useless, so shouldn't be used.  If you believe that, then it makes no
sense to care deeply about the way pragma Default_Storage_Pool interacts with
these features.

> An alternative would be to have restrictions that are fine-grained
> enough to block the bad stuff while not disallowing stuff that isn't a problem.
> Specifically:
>
>        pragma Restrictions (No_Coextensions);
>        pragma Restrictions (No_Access_Parameter_Allocators);

If that makes you happy, it's fine with me.  I have no problem adding all manner
of restrictions, and letter each project choose what restrictions to impose on
itself.

Anyway, we've got to choose how to deal with this controversy, and it's really
not all that important which way we choose. And concensus doesn't require
unanimity.  It's perfectly acceptable for you (or Tuck) to the lone "No" vote.
You win some, you lose some.

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

From: Randy Brukardt
Sent: Tuesday, January 25, 2011  3:30 PM

...
> > Indeed, I still don't buy this compromise. The above argument works
> > just as well when the pool is specified to a particular value. In
> > that case, stack allocation will cause either runtime errors or
> > erroneous behavior. And No_Anonymous_Allocators is a nasty club to
> > fix that; it eliminates the parameter cases that cause trouble but
> > it also eliminates the component and object cases that cannot cause
> > any problems (presuming the pool is as specified).
>
> This is questionable in my view, because even if you specify that the
> anonymous allocator use a particular storage pool, the object created
> will still be finalized when the subprogram call is completed.

Right, but that's the crux of the problem: "new" for access parameters simply
does work the way most people want and need it to work. It *appears* to do the
heap-based allocation that you need, but actually it does something completely
different -- and something that is completely redundant with things you can do
in other ways.

But I agree that it would be dangerously inconsistent to actually fix this to do
what it needs to do. But new pragmas cannot compound the damage.

That's why I suggested another solution. The problem with requiring
"No_Anonymous_Allocators" is that it throws out the very thing that you cannot
do any other way: giving a storage pool to anonymous allocators. And if you want
to give a storage pool to anonymous allocators, you want to give it to all of
them, not just the ones that are convenient for the implementer.

...
> > An alternative would be to have restrictions that are fine-grained
> > enough to block the bad stuff while not disallowing stuff that isn't
> > a problem.
> > Specifically:
> >
> >         pragma Restrictions (No_Coextensions);
> >         pragma Restrictions (No_Access_Parameter_Allocators);
> >
> > I've never seen any sane reason for writing a parameter allocator;
> > an aliased local object works just as well and isn't misleading to
> > the reader.
>
> I don't see this as a valid argument.  This has been in the language
> for 15 years, and some people use it as it was described and intended
> to be used.  It works like a stack-based object, and is finalized upon
> return.

So what? It's lying to the reader: "new" means heap-based allocation. We have
lots of other ways to get stack-based allocation, but no other way to get
heap-based allocation. So by stealing "new" this way, Ada 95 prevented anyone
from getting the heap-based allocation in the unlikely event that they need it.
And it prevents us from ever giving this better semantics.

I realize all that we can do these days is make the problematic cases illegal.
Thus I've suggested additional restrictions in order to limit the size of the
club.

The original reason for proposing pragma Default_Storage_Pool was to provide a
way to specify storage pools for anonymous access types. Any other benefits are
a happy accident. If it does not work to specify storage pools for anonymous
access types, then it dubious that we need it at all. Given all of the problems
that have entailed from that primary purpose (this argument, the AI-51
conflicts, the AI-157 conflicts), perhaps we need to abandon that primary
purpose.

That means that either this pragma should only apply to *named* access types, or
we should dump the pragma entirely and replace it by a Restriction -
No_Standard_Storage_Pools. (No [named?] access types can be declared without a
storage pool clause.)

In any case, I would like explore alternatives that actually meet the intent of
enhancing anonymous access allocators (if that is possible), or that simplify
this proposal so it doesn't promise something it can't deliver.

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

From: Bob Duff
Sent: Tuesday, January 25, 2011  3:49 PM

> The original reason for proposing pragma Default_Storage_Pool was to
> provide a way to specify storage pools for anonymous access types.

That's not what I remember, but anyway, I'm certain that's not the reason I want
this feature.  My reasons are accurately described in the !problem section of
the AI, and they have nothing to do with anon acc types.

>...Any other
> benefits are a happy accident. If it does not work to specify storage
>pools  for anonymous access types, then it dubious that we need it at all.

I strongly disagree with that assessment.

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

From: Bob Duff
Sent: Tuesday, January 25, 2011  4:12 PM

> That's not what I remember, but anyway, I'm certain that's not the
> reason I want this feature.  My reasons are accurately described in
> the !problem section of the AI, and they have nothing to do with anon
> acc types.

Your memory is faulty. All of the access type changes that were proposed
originated in the bunch of "how to make anonymous access types more useful"
discussions. One of the "requirements" that came from that was a way to specify
storage pools for anonymous access types.

I understand your position, but it seems to me that a Restriction
No_Standard_Storage_Pool would provide the benefit you are looking for in the
!problem statement, and without any implication that it has any effect on
anonymous access types.

I find the third paragraph somewhat bogus: if you deallocate from a type with a
different pool, you should expect it to fail. If you need to do this, you need
to specify a pool on each of the access types involved, and because it is so
critical to the algorithm, it ought to be done explicitly. It makes more sense
if you include anonymous accesses, but essentially the proposal you give is to
ignore them for this purpose (it's still implementation-defined if the pool is
used, and in practice it never will be used). So how does that help??

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

From: Bob Duff
Sent: Tuesday, January 25, 2011  4:27 PM

> The problem is that now we can set the storage size of an access type
> declared in a generic package "after the fact". For instance:

Thanks for the example.

Before I concoct wording, let's agree on what we want.
Possibilities:

(1) Your example is legal, and the "new" raises Storage_Error.
Doesn't seem very useful.

(2) "Default_Storage_Pool(My_Pool);" applies to instances, but
"Default_Storage_Pool(null);" does not.  This seems to accomplish the goal of
applying to instances -- you can tell Containers.Vectors which pool to use, for
example. Telling Vectors that it can't use ANY pool doesn't seem useful; that
would be the job of some No_Heap_Allocation restriction.

(3) Default_Storage_Pool never applies to instances.
No way to control the pool on a per-instance basis.
The "new" in your example uses whatever default pool applies to the generic
body.

I can live with any of these.  Opinions?

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

From: Randy Brukardt
Sent: Tuesday, January 25, 2011  4:46 PM

> Before I concoct wording, let's agree on what we want.
> Possibilities:
>
> (1) Your example is legal, and the "new" raises Storage_Error.
> Doesn't seem very useful.

I think this is actually what the current wording does. It's not useful, but it
is at least well-defined.

> (2) "Default_Storage_Pool(My_Pool);" applies to instances, but
> "Default_Storage_Pool(null);" does not.  This seems to accomplish the
> goal of applying to instances -- you can tell Containers.Vectors which
> pool to use, for example.
> Telling Vectors that it can't use ANY pool doesn't seem useful; that
> would be the job of some No_Heap_Allocation restriction.

I suppose; but then I start to wonder what the point of
Default_Storage_Pool(null) is. You can make the same argument as above about any
package that you didn't write (those in Claw, for example). Whether or not it is
generic isn't that relevant. Moreover, this seems uncomfortable because we would
have a pragma that operates differently depending on its parameters.

The idea of preferring a restriction is vaguely like the
No_Standard_Storage_Pools restriction I was suggesting elsewhere. I think that
would be a better way to enforce that, because explicit use of pools does not
necessarily mean heap allocation (it is quite sensible to have pools totally
allocated on the stack -- I have such pools). And it is easy to find anything
explicit that you don't want, it is the implicit stuff that is hard to manage.

> (3) Default_Storage_Pool never applies to instances.
> No way to control the pool on a per-instance basis.
> The "new" in your example uses whatever default pool applies to the
> generic body.

That seems confusing to me.

So I tend to either suggest (1) [which is the easiest] or dropping
Default_Storage_Pool(null) in favor of a restriction.

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

From: Bob Duff
Sent: Tuesday, January 25, 2011  5:00 PM

> > That's not what I remember, but anyway, I'm certain that's not the
> > reason I want this feature.  My reasons are accurately described in
> > the !problem section of the AI, and they have nothing to do with
> > anon acc types.
>
> Your memory is faulty.

Quite possible.  But I'm also quite certain about my current opinion.

>...All of the access type changes that were proposed  originated in the
>bunch of "how to make anonymous access types more useful"
> discussions.

I have no interest in that futile exercise.
I can certainly believe that this AI was born from that.

>...One of the "requirements" that came from that was a way to  specify
>storage pools for anonymous access types.

True.

> I understand your position, but it seems to me that a Restriction
> No_Standard_Storage_Pool would provide the benefit you are looking for
> in the !problem statement, and without any implication that it has any
> effect on anonymous access types.

A restriction No_Standard_Storage_Pool would solve part of the problem.
It's equivalent to the currently-proposed Default_Storage_Pool(null).
I don't see any reason why one or the other has any particular implication about
anon access.

Default_Storage_Pool(My_Pool) is more of a convenience, avoiding writing
"Storage_Size = 0" and "Storage_Pool = My_Pool" all over the place.

> I find the third paragraph somewhat bogus: if you deallocate from a
> type with a different pool, you should expect it to fail. If you need
> to do this, you need to specify a pool on each of the access types
> involved, and because it is so critical to the algorithm, it ought to
> be done explicitly. It makes more sense if you include anonymous
> accesses, but essentially the proposal you give is to ignore them for
> this purpose (it's still implementation-defined if the pool is used,
> and in practice it never will be used). So how does that help??

The third paragraph is about this:

    type T1 is tagged...;
    type A1 is access all T1'Class;
    type T2 is new T1 with...;
    type A2 is access all T2'Class;

    X : A2 := new T2...;
    Y : A1 := A1(X);
    Free(Y); -- Erroneous?!

People do this sort of thing all the time (with the code above distributed
across many files), and I find it appalling that it works just fine in GNAT (and
others) but it might be erroneous, depending on the whim of the compiler writer.

No user-defined pools in sight.  And no anon access in sight.

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

From: Tucker Taft
Sent: Wednesday, January 26, 2011  8:03 AM

> Your memory is faulty. All of the access type changes that were
> proposed originated in the bunch of "how to make anonymous access types more useful"
> discussions. One of the "requirements" that came from that was a way
> to specify storage pools for anonymous access types.

I guess my memory is faulty as well, then.  I find this lumping of the "Ada 95"
anonymous access types (access discriminants and access parameters) with the
"Ada 2005" anonymous access types (anonymous access types for local objects,
record components, and return values) to be a mistake.  The "Ada 95" anon access
types have been around a long time, and the allocators for them had no inherent
storage leak problems, as they were defined so that their storage came from
either the stack (access parameters), or the same storage pool as that of the
enclosing object (access discriminants).

Apparently not every implementor read the Ada 95 semantics, or chose to ignore
them for whatever reason, but the rules for finalization of such objects (which
were reiterated in Ada 2005 and Ada 2012), and the run-time accessibility
checking for access parameters, makes it clear that allocators for access
discriminants (now called co-extensions) and for access parameters are very
different from those for the "Ada 2005" anon access types, and do not need to be
"fixed" to avoid storage leaks or to make sure that the default (heap) storage
pool doesn't get used inadvertently.

The "how to make anon access types more useful" goal was, as far as my memory
goes, all related to the various problems that showed up with the "Ada 2005"
anon access types.  The "Ada 95" ones worked fine, and their allocators didn't
have any storage leaks. I don't think anyone is helped by lumping them with the
much thornier problems associated with the Ada 2005 anon access types.

I believe the global storage pool control should only affect those "Ada 2005"
anon access type allocators that don't have a well-defined storage pool already.
My understanding of the "compromise" was that it was always OK to use the stack
for access-parameter allocators, or access-discriminant allocators when the base
object was itself on the stack.  The only effect was that when the global
storage pool was set to "null" then all allocators, including these for Ada 95
access types, must be coming from individually user-specified storage pools,
which being impossible for an access parameter allocator, effectively disallows
them.  Presumably the access discriminant allocator (aka coextension) is still
allowed so long as it is piggy-backed into an individually user-specified
storage pool.

From my perspective this is a "compromise" since I think it is dumb to ever
disallow access parameter allocators given that they always (at least
semantically) live on the stack. But I was willing to simply disallow them in
these cases.

I really don't believe access-parameter allocators should *ever* be allocated
from a storage pool, as that requires a completely different run-time model for
these beasts from what is used in all other cases, and still doesn't change when
they need to be finalized (on subprogram return), nor does it change their
run-time accessibility level (which corresponds to the point of call).

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

From: Randy Brukardt
Sent: Wednesday, January 26, 2011  6:22 PM

> I guess my memory is faulty as well, then.

Yes, it is.

> I find this
> lumping of the "Ada 95" anonymous access types (access discriminants
> and access parameters) with the "Ada 2005"
> anonymous access types (anonymous access types for local objects,
> record components, and return values) to be a mistake.  The "Ada 95"
> anon access types have been around a long time, and the allocators for
> them had no inherent storage leak problems, as they were defined so
> that their storage came from either the stack (access parameters), or
> the same storage pool as that of the enclosing object (access
> discriminants).

That's absolutely a load of rubbish. The "Ada 95 anonymous access types" are the
crux of the problem. You've apparently forgotten or have blocked out the entire
discussion about anonymous access types.

Here's a message from Franco Gasperoni expanding on his paper that originally
started the access type discussion (this is filed in AI05-0138-1 in case you
want to verify it):


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).

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

[Randy again:]

The above is an explict complaint about the semantics of allocators of access
parameters (as defined in Ada 95). I don't see anything here that suggests that
there is no problem with Ada 95 access parameter allocators; it's exactly the
reverse. His original paper also asked for Unchecked_Deallocation of anonymous
access, and ability to specify storage pools, neither of which would work
with the above example and the current semantics. Indeed, he explicitly asks
to change the semantics of allocators of access parameters!

The discussion after that considered a number of fixes, including ways to
specify the scope of anonymous access types, before finally settling on
No_Anonymous_Allocators as a sort of fix.

My feeling with Default_Storage_Pool was that it too was intended to provide a
part of the solution to his original concerns. If it is not, then it should do
nothing for *any* anonymous access types. Having it do some and not all is just
too confusing to understand. Making which ones are handled as
*implementation-defined* is even more confusing and means that programs using
that pragma aren't portable in some circumstances.

Moreover, in the face of a pragma Default_Storage_Pool, having an allocator
(*any* allocator) that does not respect the pragma (in the absense of a specific
setting to the contrary) is actively harmful -- you have something that *looks*
like a heap allocation but in fact is not.

The real problem here is that anonymous access types have the wrong lifetime for
many uses; if you want to keep the object for a long time, you really need to
specify that when the access type is declared. Otherwise, only anonymous access
components are likely to do the right thing (and that's only because they are
likely to be declared at library-level). For instance, imagine the following
code:

     declare
         A : access T := new T'(...);
     begin
         if <condition> then
             P (A);
         else
             ...
     end;

and imagine that P is defined as:

     type Acc_T is access all T;
     Global_Chain : Acc_T;

     procedure P (PM : access T) is
     begin
         PM.Next := Global_Chain;
         Global_Chain := PM;
     end P;

This sort of code is quite likely when Ada 2005 limited withs appear. The
problem of course is that the accessibility check will fail when you try to put
the object on the global chain. (I had lots of trouble with this in Claw; it
caused me to abandon access parameters and functions altogether. If I had been
cleverer, I could have used .all'Unchecked_Access to strip off the dynamic
accessibility check, but that is still barf-inducing.)

Anyway, we decided not to try to fix this problem, which is fine, but the user
hazard of getting the wrong lifetime for allocators still exists. I don't care
so much when the user hasn't said anything about storage pools, because they are
getting the standard allocator and that does whatever and they have no right to
complain. But when they specify something, it seems wrong for it to be ignored.

I do agree that using their specification is *also* bad for access parameters
considering the very short lifetime of the objects (and I agree that we can't
fix that, despite the fact that it is garbage). So I conclude that the only
reasonable thing to do it is ban such allocators in the presense of a
Default_Storage_Pool pragma.

> Apparently not every implementor read the Ada 95 semantics, or chose
> to ignore them for whatever reason, but the rules for finalization of
> such objects (which were reiterated in Ada 2005 and Ada 2012), and the
> run-time accessibility checking for access parameters, makes it clear
> that allocators for access discriminants (now called
> co-extensions) and for access parameters are very different from those
> for the "Ada 2005" anon access types, and do not need to be "fixed" to
> avoid storage leaks or to make sure that the default (heap) storage
> pool doesn't get used inadvertently.

"Robert's rule of sensibility" was applied here: the standard cannot possibly
mean something that is clearly confusing to the reader and lacks any significant
value.

> The "how to make anon access types more useful" goal was, as far as my
> memory goes, all related to the various problems that showed up with
> the "Ada 2005" anon access types.  The "Ada 95" ones worked fine, and
> their allocators didn't have any storage leaks.
> I don't think anyone is helped by lumping them with the much thornier
> problems associated with the Ada 2005 anon access types.

From rereading part of these discussions, I see that this was always a blind
spot of yours. The questioner gives examples of problems with anonymous access
parameters, and you answered saying the we need to fix anonymous access objects
(not even used in the examples). When I see "new", I see storage pool
allocation, not stack allocation; anything else is lying to me and misleading.
If I want a short lifetime, I'll declare an object, not use "new"! "New" only
for global lifetimes, and I expect to have to do the cleanup myself (if they
aren't going to live the life of the program).

...
> I really don't believe access-parameter allocators should
> *ever* be allocated...

At this point, you are absolutely right. :-)

> ... from a storage pool, as that requires a completely different
> run-time model for these beasts from what is used in all other cases,
> and still doesn't change when they need to be finalized (on subprogram
> return), nor does it change their run-time accessibility level (which
> corresponds to the point of call).

I reluctantly agree with this, because all of these things are wrong and do not
do what the reader expects (and usually wants). Because it is too late to fix
this, the only solution is to not allow them at all where they would be
confusing. And that surely includes whenever Default_Storage_Pool is given
(otherwise the pragma is lying, or the model is overly confusing).

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

From: Randy Brukardt
Sent: Wednesday, January 26, 2011  6:36 PM

> A restriction No_Standard_Storage_Pool would solve part of the problem.
> It's equivalent to the currently-proposed Default_Storage_Pool(null).
> I don't see any reason why one or the other has any particular
> implication about anon access.

I really was thinking of No_Standard_Storage_Pool_for_Named_Access_Types.
See my other message.

> Default_Storage_Pool(My_Pool) is more of a convenience, avoiding
> writing "Storage_Size = 0" and "Storage_Pool = My_Pool" all over the
> place.

Right. And it causes trouble, so one solution is to drop it. Two others are
(1) Keep it, but eliminate all mention of anonymous access types from the pragma
    (so it really is "Default_Storage_Pool_for_Named_Access_Types", although I
    don't think I'd suggest this name!).
(2) Ban anonymous allocators that don't use this pool whenever the pragma
    applies. (They become illegal.)

> > I find the third paragraph somewhat bogus: if you deallocate from a
> > type with a different pool, you should expect it to fail. If you
> > need to do this, you need to specify a pool on each of the access
> > types involved, and because it is so critical to the algorithm, it
> > ought to be done explicitly. It makes more sense if you include
> > anonymous accesses, but essentially the proposal you give is to
> > ignore them for this purpose (it's still implementation-defined if
> > the pool is used, and in practice it never will be used). So how does that help??
>
> The third paragraph is about this:
>
>     type T1 is tagged...;
>     type A1 is access all T1'Class;
>     type T2 is new T1 with...;
>     type A2 is access all T2'Class;
>
>     X : A2 := new T2...;
>     Y : A1 := A1(X);
>     Free(Y); -- Erroneous?!
>
> People do this sort of thing all the time (with the code above
> distributed across many files), and I find it appalling that it works
> just fine in GNAT (and others) but it might be erroneous, depending on
> the whim of the compiler writer.
>
> No user-defined pools in sight.  And no anon access in sight.

Right, but this example is what I consider "slightly bogus". I can't imagine why
anyone would expect the above to work! Ada never, ever has supported this sort
of code. If people are writing this on purpose, they get what they deserve. (I
added code to Janus/Ada to detect this in easy cases after you showed this
example; that at least prevents real erroneousness in such cases.)

The reason I said "slightly bogus" is that I can imagine this happening by
accident, and the fact that it is not [always] detected is annoying. But that
almost certainly involves anonymous access types (even more likely in Ada 2012,
as we now have more implicit conversions). No one can accidentally write named
type conversions, and there is no reason to have multiple identical named access
types anyway.

So I don't find this example convincing. The notion of easily applying a debug
pool to an entire program seems more compelling to me (and not that much).

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

From: Randy Brukardt
Sent: Thursday, January 27, 2011  10:09 PM

I was given a homework task of proposing a revised rule for 7.6.1(11/3).

As a reminder, here is the rule as currently worded (last revised by
AI05-0051-1):

"The order in which the finalization of a master performs finalization of
objects is as follows: Objects created by declarations in the master are
finalized in the reverse order of their creation. For objects that were created
by allocators for a named access type whose ultimate ancestor is declared in the
master, this rule is applied as though each such object that still exists had
been created in an arbitrary order at the first freezing point (see 13.14) of
the ultimate ancestor type; the finalization of these objects is called the
finalization of the collection. Objects created by allocators for an anonymous
access type that are not coextensions of some other object, are finalized in an
arbitrary order during the finalization of their associated master. After the
finalization of a master is complete, the objects finalized as part of its
finalization cease to exist, as do any types and subtypes defined and created
within the master."

The change applied here was to allow objects allocated from anonymous access
types to finalize at any time so long as it is done as part of the finalization
of the correct master.

The problem with this is that AI05-0190-1 allows the specification of a storage
pool for an anonymous access type (indirectly), so it is possible with the above
wording for the objects allocated from the storage pool to be finalized *after*
the storage pool is finalized. For example:

   type Cont is new Ada.Finalization.Controlled with record
       Is_Groddy : Boolean := True;
       ...
   end record;
   overriding procedure Finalize (Obj : in out Cont) is
   begin
       Obj.Is_Groddy := False;
   end Finalize;
   function Is_Done (Obj : in Cont) return Boolean is
   begin
       return Obj.Is_Groddy;
   end Is_Done;

   declare
      My_Pool : Some_Pool_Type; -- (1)
      pragma Default_Storage_Pool (My_Pool);
      type Rec is record -- (2)
          Ptr : access Cont := new Cont;
      end record;
      Obj : Rec; -- (3)
   begin ...

The allocator that is executed as part of the default initialization of Obj will
allocate from My_Pool. The object (according to the above wording) can be
finalized at any point during the finalization of this declarative part,
including before Rec is finalized or after My_Pool is finalized.

It should be clear that this wording is way too broad. This wording would make
it impossible for there to be any interactions between the finalization of the
access type and the finalization of other objects in the same master (at least
in portable code, of course such code might work for a given implementation).
One such interaction is shown above: the storage pool of the access type.

But that is only the tip of the iceberg. Consider a modification of the above
example where Rec is a controlled type itself, and Ptr is an allocated component
that is considered logically part of the record:

   declare
      My_Pool : Some_Pool_Type; -- (1)
      pragma Default_Storage_Pool (My_Pool);
      type Rec is new Ada.Finalization.Controlled record -- (2)
          Ptr : not null access Cont := new Cont;
          ...
      end record;
      overriding procedure Finalize (Obj : in out Rec) is
      begin
           if Is_Done(Obj.Ptr) then
               ...
           end if;
      end Finalize;
      Obj : Rec; -- (3)
   begin ...

The proposed wording would allow the allocated object (logically part of
Rec) to be finalized before the object (but would not require it). This would
mean that Is_Done would return False instead of True and whatever was supposed
to happen during the finalization of Obj would not happen.

[Claw has some objects that are structured like this in order to share data
between 'clones' of objects; the equivalent of Is_Done is used to determine
whether to do a complete finalization of this object as it is the only existing
'clone'. If this happened, some objects would never get finalized.]

This example shows that the finalization can happen too early as well as too
late (I know I said something else during our phone call today, but I hadn't
thought of this example before then).

The storage pool example shows one way that finalization could be too late, but
that can happen other ways as well. For instance, consider another example drawn
from Claw (in Claw, these are at library-level, of course):

     declare
         My_Lock : Lock_Type;
         type Root is new Ada.Finalization.Controlled with ...
         overriding procedure Finalize (Obj : in out Root) is
         begin
             My_Lock.Seize;
             ... -- Do stuff here.
             My_Lock.Release;
         end Finalize;
         Obj : access Root := new Root'(...);
     begin

Here, the allocator can be finalized at any point, including before Obj or after
My_Lock. Of course, if it happens after the finalization of My_Lock, the
finalization will raise Program_Error (because calling a finalized PO raises
that exception). That's not going to allow a smooth clean-up.

Note that the wording above allows finalization at any time during the
finalization of the appropriate master. That means for library-level
declarations like the above, they don't even have to come during the
finalization of the same package. Obj could be declared in some user package
that the author of the locking code is not even aware of, and yet finalize after
the lock has gone away. This seems like madness to me (and it is likely to cause
madness in the tech-support person that has to figure it out).

Note that the critical point here is that the order needs to be well-defined
(and not implementation-defined!), and relate fairly closely to the inverse
order of elaboration. Flexibility seems harmful.

I am supposed to make a suggestion as to how to fix the wording. So far as I can
tell, the easiest fix is to revert to the previous wording modulo the
coextension fix (the changes are marked compared to the Ada 2005 7.6.1(11/2)):

"The order in which the finalization of a master performs finalization of
objects is as follows: Objects created by declarations in the master are
finalized in the reverse order of their creation. For objects that were created
by allocators for an access type whose ultimate ancestor is declared in the
master{ and that are not coextensions of some other object}, this rule is
applied as though each such object that still exists had been created in an
arbitrary order at the first freezing point (see 13.14) of the ultimate ancestor
type; the finalization of these objects is called the finalization of the
collection. After the finalization of a master is complete, the objects
finalized as part of its finalization cease to exist, as do any types and
subtypes defined and created within the master."

The supposed problem with this wording is that it is hard to determine the
freezing point of an anonymous access type. I'm not actually sure why that's any
harder than determining the freezing point of a *named* access type; freezing
points are not good places to do things in general. But we're obviously not
going to change that.

We could use the point of declaration of the anonymous access type instead
(unlike a named access type, the storage pool cannot be declared or specified
after the type declaration, which is why named access types have to use the
freezing point). I'm unconvinced that really helps anything.

On an mostly unrelated point, I don't think this version (or the Ada 2005
wording) works right for access parameters, as the declaration or freezing point
is irrelevant (the master is that of the *call*). So we probably need some
special wording for that case.

If we adopted all of these suggestions together, we'd end up with something
like:

"The order in which the finalization of a master performs finalization of
objects is as follows: Objects created by declarations in the master are
finalized in the reverse order of their creation. For objects that were created
by allocators for a named access type whose ultimate ancestor is declared in the
master, this rule is applied as though each such object that still exists had
been created in an arbitrary order at the first freezing point (see 13.14) of
the ultimate ancestor type; the finalization of these objects is called the
finalization of the collection. Objects created by allocators for an anonymous
access type that are coextensions of some other object are finalized with that
other object. Objects created by allocators for an anonymous access parameter
type are finalized though each such object that still exists had been created in
an arbitrary order at the evaluation of the containing subprogram call. Objects
created by other allocators for an anonymous access type are finalized though
each such object that still exists had been created in an arbitrary order at the
point of declaration of an anonymous access type. After the finalization of a
master is complete, the objects finalized as part of its finalization cease to
exist, as do any types and subtypes defined and created within the master."

There's probably some way to simplify this (the part about the "objects being
created in an arbitrary order at" some point keeps getting repeated over and
other). I'm not going to try right now, as I think we need more discussion to
decide what we want.

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

From: Tucker Taft
Sent: Thursday, January 27, 2011  10:41 PM

Thanks for working on this.  You are probably right that we just need to define
the freezing point for anonymous access types that are not for access discrims
or access params.  Below you are effectively making it at the point of
declaration, but I suspect we may want to make it later.  For anon access
components, I suspect we want to make it at the point that the immediately
enclosing type is frozen.  For anon access stand-alone objects, I suspect we
want to make it at the point the stand-alone object is frozen.

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

From: Jean-Pierre Rosen
Sent: Wednesday, February  9, 2011  3:35 AM

> The semantics of the Default_Storage_Pool aspect are similar to
> passing a pool object as a generic formal, and putting pragma
> Default_Storage_Pool at the top of the generic's visible part, specifying that
> formal.

I appreciate the word "similar" to avoid the dreadful "equivalent", but I'm
wondering about the intended effect in this case:

with Some_Pool;
procedure Formal_Access is

   Pool_Obj1: Some_Pool.Pool;
   Pool_Obj2: Some_Pool.Pool;
   pragma Default_Storage_Pool (Pool_Obj1);

   type Acc is access Integer;
   Local_Acc : Acc := new Integer;

   generic
      V : in Acc := new Integer;
   procedure Gen;
   procedure Gen is begin null; end Gen;

   procedure Inst1 is new Gen                 -- (1)
      with Default_Storage_Pool => Pool_Obj2;
   procedure Inst2 is new Gen (new Integer)   -- (2)
      with Default_Storage_Pool => Pool_Obj2;
   procedure Inst2 is new Gen (Local_Acc)     -- (3)
      with Default_Storage_Pool => Pool_Obj2;

begin
   null;
end Formal_Access;

Since the visible part of a generic includes the formal part, the "similarity"
implies that in cases (1) and (2) (default or explicit actual), the object is
allocated from Pool_Obj2, while obviously (3) is allocated form Pool_Obj1. Is
this intended?

With my naive user hat on, I would expect (1) to be allocated from
Pool_Obj2 (it's the generic that is doing the allocation), and (2) to be
allocated from Pool_Obj1 (I am doing the allocation). Now, don't tell me that
naive users will have lots of surprises as far as pools and accessibility are
concerned...

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

From: Bob Duff
Sent: Wednesday, February  9, 2011  7:14 AM

> I appreciate the word "similar" to avoid the dreadful "equivalent",
> but I'm wondering about the intended effect in this case:

I can get away with "similar" only in the AARM.  ;-)

...
>    procedure Inst1 is new Gen                 -- (1)
>       with Default_Storage_Pool => Pool_Obj2;
>    procedure Inst2 is new Gen (new Integer)   -- (2)
>       with Default_Storage_Pool => Pool_Obj2;
>    procedure Inst2 is new Gen (Local_Acc)     -- (3)
>       with Default_Storage_Pool => Pool_Obj2;

The default storage pool (whether defined by the pragma or by the aspect) is
always applying to access types.  Not to individual "new"s. The pragma is really
just a shorthand for putting "for T'Storage_Pool use..." all over the place.
The aspect is more magical, since it works inside instances, but it still
applies to types.

So all allocators in the above example use Pool_Obj1.
There are no access types declared inside Gen, so the aspect clause isn't doing
anything.

I think it has to be this way, because otherwise type conversions amongst access
types could cause yet more chaos.

>...Now, don't tell me
> that naive users will have lots of surprises as far as pools and
>accessibility are concerned...

Well, it doesn't bother me that using user-defined pools is complicated. I just
wish the DEFAULT were simple, like it is in every other language I can think of.

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

From: Randy Brukardt
Sent: Thursday, February 10, 2011  12:40 AM

> I think it has to be this way, because otherwise type conversions
> amongst access types could cause yet more chaos.

Forget type conversions! If it worked on "new", you would have individual items
for a single access type allocated from different pools. For a pool-specific
type, that would be madness (there would be no way to tell what pool to
deallocate from). For a general access type, it still would be madness, but the
sort of madness that is somehow expected for general access types. :-) But you
still couldn't figure out deallocations.

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

From: Randy Brukardt
Sent: Thursday, February 10, 2011  1:46 AM

I've made an attempt at this rewording, but I'm not terribly happy with the
results.

A reminder (from the !discussion of the AI):

The wording change to 7.6.1(11/3) is necessary as the AI05-0051-1 wording
assumed that anonymous access types could not use a user storage pool. Since
that's no longer true, the laissez faire finalization cannot work. As a
practical matter, it never could have worked (it is all to easy to write code
with such dependencies, see the mail of January 27, 2011 in the !appendix for
some examples).

In addition, the rules (both the Ada 2005 and the AI05-0051-1 version) does not
properly handle allocators of anonymous access parameter types -- these belong
to the master enclosing of the call, not the type declaration, and thus should
be finalized there as well.

Thus the only part of the AI05-0051-1 changes that we retain is the exception
for coextensions (their finalization is defined by 7.6.1(9.1/2), not
7.6.1(11/3)). Otherwise, we define precisely where allocators of each kind of
anonymous access type are finalized. We prefer to use the freezing point of the
associated entity if in doubt.

=====

Following is the wording I came up with:

Replace 7.6.1(11/3) [as modified by AI05-0051-1] with:

The *collection master* of an allocator of an access type is:
  * For an allocator of a named access type, the master in which the
    ultimate ancestor of the access type is declared;
  * For an allocator of an anonymous access parameter type, the master
    of the call that contains the allocator;
  * For an allocator of other kinds of anonymous access types, the
    master in which the associated entity is declared.
    AARM Discussion: The associated entity will be a record type
    (discriminant or component) or object declaration.

The *collection point* of an allocator of an access type is:
  * For an allocator of a named access type, the freezing point of the
    ultimate ancestor of the access type is declared;
  * For an allocator of an anonymous access parameter type, the
    call that contains the allocator;
  * For an allocator of an anonymous access type declared as part of
    a stand-alone object declaration, the freezing point of the object;
  * For an allocator of an anonymous access type that declares a component,
    the freezing point of the composite type that declares the component.

The order in which the finalization of a master performs finalization of objects
is as follows: Objects created by declarations in the master are finalized in
the reverse order of their creation. For objects that were created by allocators
whose collection master is this master and that are not coextensions of some
other object, this rule is applied as though each such object that still exists
had been created in an arbitrary order at the collection point of the allocator;
the finalization of these objects is called the *finalization of the
collection*. After the finalization of a master is complete, the objects
finalized as part of its finalization cease to exist, as do any types and
subtypes defined and created within the master.

AARM Ramification: If multiple anonymous access types have the same collection
point, the associated objects can be finalized in any order at that point.

====

This is not some of my best work!

I had to define two terms in order to avoid repeating the middle wording over
and over and over. Moreover, we want to allow all of the "finalizations of the
collections" of {anonymous only??) access types frozen at the same point to be
finalized at the same time; we can't enforce any order (if multiple entities are
frozen at the same point, there is no order to refer to), so we ought not make
any requirements at all.

Part of the trouble is the need to define what master these allocators belong
to, and also the need to define *where* in that master they are finalized.
Perhaps there is some way to combine these, but it escapes me at the moment.

One possible simplification would be to split out the anonymous access parameter
cases, since those are the most unusual of the bunch. But that doesn't seem to
help much.

I also point out that this wording allows objects of multiple named access types
frozen at the same point to be interleaved. (That is, finalization is not of all
of the objects of one access type, then all of the objects of another access
type -- although it is typically implemented that way). I would argue that the
Ada 2005 wording already allows this behavior (it talks about objects being
finalized in an arbitrary order, without any restrictions beyond the freezing
point), but it is not as clear to me that that is intended.

Ideas welcome.

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

From: Steve Baird
Sent: Thursday, February 10, 2011  1:56 PM

> Replace 7.6.1(11/3) [as modified by AI05-0051-1] with:
>
> The *collection master* of an allocator of an access type is:
>   * For an allocator of a named access type, the master in which the
>     ultimate ancestor of the access type is declared;
>   * For an allocator of an anonymous access parameter type, the master
>     of the call that contains the allocator;
>   * For an allocator of other kinds of anonymous access types, the
>     master in which the associated entity is declared.
>     AARM Discussion: The associated entity will be a record type
>     (discriminant or component) or object declaration.
>
... or record subtype, as in

     subtype S is Some_Type_With_An_Access_Discriminant
       (Discrim => new Designated_Type);

or an anonymous object (as for an aggregate or function result).

What about all the anonymous function result type interactions with nested-scope
extension types first discussed in AI05-0051?

Perhaps you only want to define "collection master" and "collection point" for
non-coextension allocators. The finalization point rules for coextensions don't
need further specification in this AI and this might allow the definitions to be
simpler (otherwise, it is not obvious (to me) that there are no interactions
with the "ultimate master" stuff in AI05-0234).

> The *collection point* of an allocator of an access type is:
>   * For an allocator of a named access type, the freezing point of the
>     ultimate ancestor of the access type is declared;

Typo (I think): delete the two words "is declared" above?

>   * For an allocator of an anonymous access parameter type, the
>     call that contains the allocator;

Is this a change? Compare the "innermost master that evaluates the aggregate or
function_call" wording of 3.10.2(10/2). Consider a procedure call passes in two
function calls, each of which passes in an access-parameter allocator:

     P (F (new Designated), F (new Designated));

Do the two calls to F define two different collection points?
I would have thought that the call to P would be the one and only collection
point in this example.

>   * For an allocator of an anonymous access type declared as part of
>     a stand-alone object declaration, the freezing point of the
> object;

>   * For an allocator of an anonymous access type that declares a component,
>     the freezing point of the composite type that declares the component.
>

Do you need to say "that declares a non-discriminant component" above?

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

From: Randy Brukardt
Sent: Thursday, February 10, 2011  7:24 PM

> > Replace 7.6.1(11/3) [as modified by AI05-0051-1] with:
> >
> > The *collection master* of an allocator of an access type is:
> >   * For an allocator of a named access type, the master in which the
> >     ultimate ancestor of the access type is declared;
> >   * For an allocator of an anonymous access parameter type, the master
> >     of the call that contains the allocator;
> >   * For an allocator of other kinds of anonymous access types, the
> >     master in which the associated entity is declared.
> >     AARM Discussion: The associated entity will be a record type
> >     (discriminant or component) or object declaration.
> >
> ... or record subtype, as in
>
>      subtype S is Some_Type_With_An_Access_Discriminant
>        (Discrim => new Designated_Type);

No, we're talking about the entity that declares the anonymous access type, not
the allocator. That probably needs clarification.

> What about all the anonymous function result type interactions with
> nested-scope extension types first discussed in AI05-0051?

You'll have to tell me. I have no clue.

> Perhaps you only want to define "collection master" and "collection
> point" for non-coextension allocators. The finalization point rules
> for coextensions don't need further specification in this AI and this
> might allow the definitions to be simpler (otherwise, it is not
> obvious (to me) that there are no interactions with the "ultimate
> master" stuff in AI05-0234).

Neither of these definitions are used for any coextension cases, and I made no
effort to care about coextensions cases beyond ensuring that there is some
definition in that case. The wording that uses these definitions explicitly
excludes all coextensions because 7.6.1(9.1/2) already covers that, and we don't
want any hint of anything else conflicting with that.

> > The *collection point* of an allocator of an access type is:
> >   * For an allocator of a named access type, the freezing
> point of the
> >     ultimate ancestor of the access type is declared;
>
> Typo (I think): delete the two words "is declared" above?

Right.

> >   * For an allocator of an anonymous access parameter type, the
> >     call that contains the allocator;
>
> Is this a change? Compare the "innermost master that evaluates the
> aggregate or function_call" wording of 3.10.2(10/2). Consider a
> procedure call passes in two function calls, each of which passes in
> an access-parameter allocator:
>
>      P (F (new Designated), F (new Designated));
>
> Do the two calls to F define two different collection points?
> I would have thought that the call to P would be the one and only
> collection point in this example.

They have the same master, and different collection points. This isn't a change,
because this was defined completely wrong in the past (as the freezing point of
the access type). 3.10.2 has nothing to do with it -- other than that this
wording should be consistent. So I assume that compilers have done "something"
here, but whatever it is has no support from the language wording.

The only effect of having different collection points is to require an ordering.
I don't see any way to avoid that, because the ordering within masters is
(usually) important. For instance, if these allocators were used such that they
were not directly in a master (consider aliased parameters, for instance), the
order of finalization is very important.

There might be some way to get the effect you want, but it would be very
complex. (And we'd have to abandon this attempt at wording altogether; it cannot
be done this way.)

> >   * For an allocator of an anonymous access type declared as part of
> >     a stand-alone object declaration, the freezing point of the
> > object;
>
> >   * For an allocator of an anonymous access type that
> declares a component,
> >     the freezing point of the composite type that declares
> the component.
> >
>
> Do you need to say "that declares a non-discriminant component" above?

Same as above: coextensions are irrelevant here, as this is never used for them.
But I wanted it defined in some way for them, so we don't get into arguments
about whether the order that rules are applied in are significant. I toyed with
having a separate bullet for coextensions but that seemed like overkill when it
will never be used.

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

From: Randy Brukardt
Sent: Thursday, February 10, 2011  1:10 AM

> > >   * For an allocator of other kinds of anonymous access types, the
> > >     master in which the associated entity is declared.
> > >     AARM Discussion: The associated entity will be a record type
> > >     (discriminant or component) or object declaration.
...
> No, we're talking about the entity that declares the anonymous access
> type, not the allocator. That probably needs clarification.

Actually, the wording is just wrong. I changed it to:

  * For an allocator of other kinds of anonymous access types, the
    master in which the entity associated with the anonymous access type
    is declared.

which is what Tucker suggested that we use.

> > What about all the anonymous function result type interactions with
> > nested-scope extension types first discussed in AI05-0051?

I suspect that I need another bullet for that case. Since we don't care about
coextensions here, we just have to deal with anonymous access function results.
This bullet would go before the one noted above:

   * For an allocator of an anonymous access result type Redundant[used in a
     return statement], the master of the return object;
     AARM Discussion: The master of the return object changes during the
     execution of a return statement; this refers to whatever master the object
     has at the present time. Note that we don't need a similar rule for access
     discriminants because these rules do not have to handle coextensions.

> The only effect of having different collection points is to require an
> ordering. I don't see any way to avoid that, because the ordering within
> masters is (usually) important. For instance, if these allocators were used
> such that they were not directly in a master (consider aliased parameters, for
> instance), the order of finalization is very important.

This example isn't complete enough. Imagine a function call used in a renames,
with an explicitly aliased parameter that is an aggregate with components that
are anonymous allocators. In this case, the master of the aggregate (and thus
the allocators) is that of the renames (which is fairly long-lived). The
ordering within the "call tree" probably isn't significant (although the
existing wording seems to make it so), but we don't want to allow this
finalization to "float away" from the renames; it has to be done in order after
the following declaration and before any preceding declaration.

This is the ordering of finalization *within* a master; as I showed in previous
examples, this is very important to have defined because otherwise a lot of
legitimate finalization (especially at library level) has no hope of working
consistently.

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

From: Bob Duff
Sent: Saturday, February 19, 2011  9:27 AM

Wording for AI05-0190-1, as requested during the ARG meeting yesterday.
The intent is to clarify finalization points of objects created by allocators of
anonymous access types.  The bulk of the AI remains unchanged.

I re-reify the concept of "collection" from Ada 83.
The collection is an object, so it's finalization point follows from where this
object is implicitly declared.

We no longer need to define the phrase "finalization of the collection", because
it follows from the normal rules of English that it means, well, finalization of
the collection.

Replace 7.6.1(11/3) [as modified by AI05-0051-1] with:

The finalization of a master performs finalization of objects created by
declarations in the master in the reverse order of their creation.
After the finalization of a master is complete, the objects finalized as part of
its finalization cease to exist, as do any types and subtypes defined and created
within the master.

Each nonderived access type has an associated "collection", which is the set of objects
created by allocators of the type, or of types derived from the type.
Unchecked_Deallocation removes the object from its collection.
(A coextension of an object X is an element of the same collection as X, if any.)
Finalization of a collection consists of finalization of each object in the collection,
in an arbitrary order. The collection of an access type is an object implicitly declared
at the following place:

    - For a named access type, the first freezing point (see 13.14) of
      the type.

    - For the type of an access parameter, the call that contains the
      allocator.

    - For any other anonymous access type, the first freezing point of
      the innermost enclosing declaration.

AARM Note: The place of the implicit declaration determines when allocated objects are
finalized. For multiple collections declared at the same place, we do not define the
order of their implicit declarations.

[I think Tucker would like to loosen the above, to allow later finalization (which means
an earlier implicit declaration of the collection). The above is the latest place where
the collection can be implicitly declared. We could give permission to declare it earlier,
but no earlier than the storage pool of the access type. I think Randy's issue was that
the finalization of a heap object could happen after the pool, which is what this wording
is trying to prevent.]

The following AARM annotations need to be removed.
In retrospect, these were pretty unhelpful anyway.

3.3.2:

38.i      As explained in Section 13, the concept of "storage pool" replaces
          the Ada 83 concept of "collection." These concepts are similar, but
          not the same.

3.10:

26.b      We use the term "storage pool" to talk about the data area from
          which allocation takes place. The term "collection" is no longer
          used. ("Collection" and "storage pool" are not the same thing
          because multiple unrelated access types can share the same storage
          pool; see 13.11 for more discussion.)

13.11:

43.b      Ada 83 had a concept called a "collection," which is similar to what
          we call a storage pool. All access types in the same derivation
          class shared the same collection. In Ada 95, all access types in the
          same derivation class share the same storage pool, but other
          (unrelated) access types can also share the same storage pool,
          either by default, or as specified by the user. A collection was an
          amorphous collection of objects; a storage pool is a more concrete
          concept - hence the different name.

[End list of removed AARM annotations.]

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

From: Bob Duff
Sent: Wednesday, March 16, 2011  4:44 PM

New version of AI05-0190-1, Global storage pool controls. [This is version /10
of the AI - Editor.]

Thanks you, Steve, for your help!

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

From: John Barnes
Sent: Thursday, April 07, 2011 5:26 AM

!problem

line 4  is is  => it is

!wording

Under static semantics it is a bit odd to read

if a storage pool is a pool ...

I know it means as opposed to null but it looks odd.

When is a door not a door. When it is a jar.


OK generally at a quick read.

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

From: Bob Duff
Sent: Thursday, April 07, 2011 6:10 PM

New version of AI05-0190-1, Global storage pool controls,
based on the April 7, 2011 phone meeting. [This is version /11 of the AI - Editor.]

The main change was to change:

    - For the type of an access result, at the place where the accessibility
      level is determined by the point of call (see 3.10.2).

to use the new "master of call" term newly defined in
AI05-0234-1, Hole in AI05-0051-1:

    - For the type of an access result, within the master of the call
      (see 3.10.2).

Also fixed John's editorial comment: "pool is a pool" --> "pool is nonnull".

This completes my homework from the April 7 meeting.  I did it without
seeing the minutes, which is slightly dangerous, because Randy's notes
are usually better than mine.  When the minutes come out, I'll double
check that I didn't miss something.

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

From: Tucker Taft
Sent: Monday, May 2, 2011  9:44 PM

[Appropriate part of a large message - Editor.]

AI05-0190-1/11  Global storage pool controls
    [Uses new term from AI05-0243-1 to fix finalization issues; pool pragmas.]
    Approve __X____ Disapprove ______ Abstain _______
   Comments:
     Should we move pragma Controlled to the obsolescent Annex?

     Normally, we try to make the redundant brackets enclose a phrase
     which could be omitted.  In this, we probably need to enclose the
     word ", and" as well:  "... Otherwise, Redundant[the pragma occurs
       immediately within a sequence of declarations], and it defines
       the ..."

     This doesn't make sense to me:
       A pragma Default_Storage_Pool shall not be used as a configuration pragma
       within the immediate scope of another such pragma.
     Configuration pragmas appear outside of any scope, at the beginning
     of a "compilation."  Perhaps we mean to say that a Default_Storage_Pool
     configuration pragma is not permitted if it applies to a compilation
     unit which is within the immediate scope of another Default_Storage_Pool
     pragma.

     This is confusing to me:
       Redundant[Otherwise, there is no default pool; the Storage_Pool attribute
       is implementation defined.]
     Can we just leave out this case completely?  It seems to be implying that
     this is a special case, when in fact this is the normal case.  If we feel we
     must say something, I would prefer we just send them off to 13.11 rather
     than saying that it is "implementation defined."  In the Intro to 13.11,
     the RM says: "By default, the implementation chooses a standard storage pool
     for each access-to-object type."

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

From: Randy Brukardt
Sent: Monday, May 2, 2011  10:32 PM

[Appropriate part of a larger message - Editor.]

> AI05-0190-1/11  Global storage pool controls
>     [Uses new term from AI05-0243-1 to fix finalization issues; pool pragmas.]
>     Approve __X____ Disapprove ______ Abstain _______
>    Comments:
>      Should we move pragma Controlled to the obsolescent Annex?

AI05-0229-1 does that.

...
>      This doesn't make sense to me:
>        A pragma Default_Storage_Pool shall not be used as a configuration pragma
>        within the immediate scope of another such pragma.
>      Configuration pragmas appear outside of any scope, at the beginning
>      of a "compilation."  Perhaps we mean to say that a Default_Storage_Pool
>      configuration pragma is not permitted if it applies to a compilation
>      unit which is within the immediate scope of another Default_Storage_Pool
>      pragma.

I have no idea. Bob??

>      This is confusing to me:
>        Redundant[Otherwise, there is no default pool; the Storage_Pool attribute
>        is implementation defined.]
>      Can we just leave out this case completely?  It seems to be implying that
>      this is a special case, when in fact this is the normal case.  If we feel we
>      must say something, I would prefer we just send them off to 13.11 rather
>      than saying that it is "implementation defined."  In the Intro to 13.11,
>      the RM says: "By default, the implementation chooses a standard storage pool
>      for each access-to-object type."

Probably a reference to 13.11 would be best.

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

From: Bob Duff
Sent: Tuesday, May 3, 2011  2:31 PM

> I have no idea. Bob??

Tucker's rewording is fine with me.  Except he misuses "which".  ;-)
So:

    A pragma Default_Storage_Pool shall not be used as a configuration pragma
    that applies to a compilation unit that is within the immediate scope of
    another such pragma.

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

From: Erhard Ploedereder
Sent: Sunday, May 15, 2011 11:32 AM

[Appropriate part of a larger message - Editor.]

AI05-0190-1/11  Global storage pool controls
   [Uses new term from AI05-0243-1 to fix finalization issues; pool pragmas.]
   Approve ______ Disapprove __X____ Abstain _______

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

comment: It is regrettable in this day and age of multicore that we stuck to
strict sequentiality in the first sentence of 7.6.1(11), since we were already
changing the words. I approve of the finalization wording only because the old
wording was much worse and because it is late in the game.

I seriously disagree with the writeup of the Default_Storage_Pool pragma (but
not with the general intent of such a pragma.):

> The default storage pool for an allocator that occurs within an
> instance of a generic is defined by the Default_Storage_Pool pragma
> that applied to the generic, or by the Default_Storage_Pool aspect of
> the instantiation;

add: {and the user shall toss a coin to guess which it is; he may also query the
oracle of Delphi or call the language designers at 0-800-... (49c/minute)} i.e.,
I strongly disagree if this is really the intended un-semantics.

> the Default_Storage_Pool pragma that applies to the instantiation is
> irrelevant.

surprising: what happened to the notion that aspect pragmas do specify aspects?


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

From: Brad Moore
Sent: Sunday, May 15, 2011  2:52 PM

[Appropriate part of a larger message - Editor.]

> AI05-0190-1/11  Global storage pool controls
>     [Uses new term from AI05-0234-1 to fix finalization issues; pool
> pragmas.]
>     Approve ______ Disapprove ______ Abstain ___X____

I am on the borderline of disapproving this AI because I feel the AI as
currently worded is significantly broken. There needs to be a way for a
storage pool implementer to suppress a Default_Storage_Pool pragma used
as a configuration pragma. I feel that the AI is an important one, though,
and can probably be fixed with a binding interpretation post Ada 2012, but
that might involve a new pragma, aspect, or syntax being defined to suppress
the Default_Storage_Pool pragma, or somehow refer to the storage pool that
you get if the Default_Storage_Pool pragma is not specified. e.g. pragma
Suppress_Default_Storage_Pool_Overriding.

Editorial Comment:
"appplying" in the problem section

Editorial Comment:
7.6.1(11/3)   ", the objects finalized as part of its finalization{, }
cease to exist, as do any types and subtypes"
I was having difficulty parsing this sentence. I was reading as "part of the
finalization of the master ceases to exist", not as "the objects finalized
during the finalization of the master cease to exist". Perhaps this even a
better alternative than simply inserting a comma.

Editorial Comment:
Discussion: "(it is all to easy" => "(it is all too easy"

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

From: Gary Dismukes
Sent: Monday, May 16, 2011  4:39 PM

[Appropriate part of a larger message - Editor.]

> AI05-0190-1/11  Global storage pool controls
>    [Uses new term from AI05-0243-1 to fix finalization issues; pool pragmas.]
>    Approve __X___ Disapprove ______ Abstain _______
>

[Comment: For the access result case the !wording says:

  - For the type of an access result, within the master of the call
  (see 3.10.2).

The phrase "within the master of the call" doesn't seem precise enough to me.
Exactly where within the master is the object declared?  Presumably it had
better at least be before the call.  Ideally it should be at a point immediately
preceding the call, but wording for this seems tricky, since the call we're
talking about depends on the specific master that ends up applying by the rules
of 3.10.2.  This doesn't concern me enough to vote against, but I wanted to
mention it.  End of Comment]

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

From: Randy Brukardt
Sent: Wednesday, May 18, 2011  10:11 PM

...
> Editorial Comment:
> 7.6.1(11/3)   ", the objects finalized as part of its
> finalization{, }
> cease to exist, as do any types and subtypes"
> I was having difficulty parsing this sentence. I was reading as "part
> of the finalization of the master ceases to exist", not as "the
> objects finalized during the finalization of the master cease to
> exist". Perhaps this even a better alternative than simply inserting a
> comma.

The sentence in question was added in Ada 2005, and is unchanged in this AI.
(That's not clear from the AI text, so Bob should avoid complaining about people
asking to fix existing Ada 2005 text. :-)

The full sentence is:

"After the finalization of a master is complete, the objects finalized as part
of its finalization cease to *exist*, as do any types and subtypes defined and
created within the master."

Adding a comma doesn't help anything; if anything it makes it worse by
separating "cease to exist" from "objects finalized". The "obvious" fix is to
get rid of "its":

"After the finalization of a master is complete, the objects finalized as part
of the finalization of the master cease to *exist*, as do any types and subtypes
defined and created within the master."

That to me is just adding words rather than much clarity; it seems pretty clear
what "its finalization" refers to.

Your suggestion of replacing "as part of" with "during" might be OK (and it
probably reads better), but it seems to me that it might be construed to include
any other objects that happened to be finalized at the same time (by a different
task, perhaps), and that would be wrong. "As part of" is clearer in that
respect.

So in the absence of a clear improvement, I'm not going to make any change here.
But perhaps someone else has a better idea.

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

From: Randy Brukardt
Sent: Wednesday, May 18, 2011  10:57 PM

> The phrase "within the master of the call" doesn't seem precise enough
> to me.  Exactly where within the master is the object declared?
> Presumably it had better at least be before the call.  Ideally it
> should be at a point immediately preceding the call, but wording for
> this seems tricky, since the call we're talking about depends on the
> specific master that ends up applying by the rules of 3.10.2.  This
> doesn't concern me enough to vote against, but I wanted to mention it.
> End of Comment]

This had bothered me in the past, but in the absence of a better idea, I didn't
do anything about it. As Gary notes, saying more would quickly get confusing,
because of the difficulty of identifying the master in question.

I've rationalized that the master of a call is usually a very small area, so it
is hard to come up with a case where it could matter. Beyond that, it doesn't
really matter if it is before or after the call, since its only effect is
determine when the collection is finalized, and the call itself does not
participate in finalization.

I suppose this laxness could be a problem if the "master of the call" is that of
an access type (as in an initialized allocator); in a long-lived master, the
place within the master could in fact matter -- especially if it is far away
from the call's position in the master. Still, it's hard to imagine an
implementation doing something completely nonsensical here.

Of course, if someone has wonderful wording with the appropriate properties,
I'll be happy to use it.

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

From: Randy Brukardt
Sent: Wednesday, May 18, 2011  11:32 PM

> AI05-0190-1/11  Global storage pool controls
>    [Uses new term from AI05-0243-1 to fix finalization issues; pool pragmas.]
>    Approve ______ Disapprove __X____ Abstain _______
>
> ================
>
> comment: It is regrettable in this day and age of multicore that we
> stuck to strict sequentiality in the first sentence of 7.6.1(11),
> since we were already changing the words. I approve of the
> finalization wording only because the old wording was much worse and
> because it is late in the game.

In the abstract, I would agree. But as a practical matter, parallel finalization
would be a significant compatibility problem. Most existing Finalize routines do
not protect themselves against parallel execution, and thus many would fail if
used that way.

Admittedly, the problem can happen by declaring objects of a controlled type in
different tasks; but it is not unusual for types to be documented as "not task
safe". For such types, there is no parallelism currently required and adding
some would be pretty nasty.

I vaguely recall this topic being discussed during the Ada 95 definition, and
the decision was to not require types to do finalization locked that did not
need to be "task safe". One could make a fairly strong argument today that that
decision was wrong, but it seems too late to change it.

(Note that one of the things I wanted to try to get from "superpure"
categorization, which later turned into global in/out annotations, was the
permission to evaluate subprograms in parallel if they are declared to have no
side-effects and their parameters don't conflict. Since the annotations didn't
survive, neither did the permission.)

> I seriously disagree with the writeup of the Default_Storage_Pool
> pragma (but not with the general intent of such a pragma.):
>
> > The default storage pool for an allocator that occurs within an
> > instance of a generic is defined by the Default_Storage_Pool pragma
> > that applied to the generic, or by the Default_Storage_Pool aspect
> > of the instantiation;
>
> add: {and the user shall toss a coin to guess which it is; he may also
> query the oracle of Delphi or call the language designers at 0-800-...
> (49c/minute)} i.e., I strongly disagree if this is really the intended
> un-semantics.

Humm, a "1-900" line for Ada advice. Maybe that can be my next career! ;-)

The static semantics rules say (as part of the definition of the aspect):

This aspect overrides any Default_Storage_Pool pragma that might apply to the
generic unit.

so the semantics are well-defined. The wording you are reading is in the lengthy
AARM note that follows the normative definition (it has 5 paragraphs). I presume
Bob thought that repeating the formal definition in detail was unnecessary.

I switched the words around to make it a bit clearer:

The default storage pool for an allocator that occurs within an instance of a
generic is defined by the Default_Storage_Pool aspect of the instantiation (if
specified), or by the Default_Storage_Pool pragma that applied to the generic;
the Default_Storage_Pool pragma that applies to the instantiation is irrelevant.

> > the Default_Storage_Pool pragma that applies to the instantiation is
> > irrelevant.
>
> surprising: what happened to the notion that aspect pragmas do specify
> aspects?

The Default_Storage_Pool pragma is a configuration pragma, not an aspect pragma.
(It applies to the entire partition, not to any single entity.) The aspect is
something different (it just applies to generic instantiations).

This is the same reason that Discard_Names is not an aspect pragma, even though
it is possible to imagine a Discard_Names aspect, and the pragma has the right
form for an aspect pragma.

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


Questions? Ask the ACAA Technical Agent