Version 1.3 of ais/ai-00162.txt

Unformatted version of ais/ai-00162.txt version 1.3
Other versions for file ais/ai-00162.txt

!standard 03.10.02 (13)          98-10-02 AI95-00162/02
!class binding interpretation 96-09-10
!status work item 98-10-02
!status received 96-09-10
!priority Medium
!difficulty Hard
!subject Anonymous access types and tasks termination/controlled type finalization
!summary 98-10-02
A new master is defined which is associated with the completion of one or more subprogram calls, in a statement or a declaration, that have at least one formal access parameter whose actual parameter is an allocator. This new master is responsible for one or more controlled objects or task objects created by anonymous allocators which are assigned to formal access parameters of one or more subprogram calls in the statement or declaration.
Such a statement or declaration may execute one or more such masters. An
implementation is permitted to determine how many such masters such a statement or declaration will execute.
!question 98-10-02
When an anonymous allocator, which is assigned to a formal access parameter, creates a task or creates a controlled object, who is the master of these things? More specifically, which construct is responsible for waiting for the task to terminate and for finalizing the task or the controlled object?
Here is an example of the problem:
with Ada.Finalization; use Ada.Finalization; package P is task type T; type CT is new Controlled with private; private ... end P;
with P; use P; procedure Test is procedure R (X: access T); procedure S (Y: access CT);
procedure Q (I: Integer) is begin -- (1) ... R (X => new T); -- (2a) ... S (Y => new CT); -- (2b) ... end Q;
procedure R (X: access T) is -- (3a) begin ... end R; procedure S (X: access CT) is -- (3a) begin ... end S;
begin ... Q (I); ... end Test;
Is the master of the anonymous allocator in either statement (2a or 2b) [new T or new CT]: - the innermost enclosing (normal) master (1), - some construct associated with the innermost enclosing statement (2a or 2b)
[as described by 7.6.1(13)] or
- the called subprogram's body (3a or 3b)?
!recommendation 98-10-02
(See summary.)
!wording 98-10-02
There are three wording changes proposed. The first wording change is to the second sentence of 7.6.1(3) to introduce the new master:
"Leaving an execution happens immediately after its completion, except in the case of a master: the execution of a task body, a subprogram_body, an entry_body and certain statements and the elaboration of certain declarations. The execution of a statement is the execution of a master if the statement is a block_statement or an accept statement. If a statement contains one or more anonymous allocators, then the execution of one or more parts of that statement which contain subprogram calls with anonymous allocators is the execution of one or more masters. If a declaration contains one or more anonymous allocators, the elaboration one or more parts of that declaration which contain subprogram calls with anonymous allocators is the execution of one or more masters. An anonymous allocator is an allocator that is the actual parameter in a subprogram call whose associated formal parameter is an access parameter."
The second wording change is to 3.10.2(13) to account for the accessibility level of the allocator:
"The accessibility level of the anonymous access type of an access parameter is the same as that of the view designated by the actual. If the actual is an allocator, this is the accessibility level of the execution of the immediately enclosing statement or the elaboration of the immediately enclosing declaration (which is itself a master -- see 7.6.1)."
The third wording change is to an Implementation Permission paragraph to subsection 7.6.1:
"Implementations have the permission to determine how many actual masters are executed in the execution of a statement or the elaboration of a declaration that causes the execution of multiple subprogram calls with anonymous allocators."
With the new wording for master, there is no need to change 7.6.1(13) to account for anonymous allocators since 7.6.1(4) will handle the task waiting and finalization for the "new" master.
!discussion 98-10-02
As the question states, there are three possibilities for the master of the task or a controlled object created by the anonymous allocator: 1) the innermost enclosing (normal) master 2) some construct associated with the innermost enclosing statement or
declaration
3) the called subprogram's body.
Let's examine each alternative, starting with innermost enclosing (normal) master. While this requires minimal (no) change to the language, it is clumsy in the presence of loops. If the anonymous allocator is inside a loop, one clearly wants to recover the storage associated with the anonymous allocator before going on to the next loop iteration. This would require a block statement inside the loop just for encapsulating the statement with the subprogram call.
The second alternative, some construct associated with the innermost enclosing statement, would introduce one of two options: a) introduce a new master, namely a subprogram call with an anonymous
allocator
b) introduce a new "blocking point" in the execution of a statement
containing a subprogram call with an anonymous allocator
Let's examine the first option, introducing a new master. The following is a proposed wording change to 7.6.1(3) to introduce the new master:
"Leaving an execution happens immediately after its completion, except in the case of a master: the execution of a task body, a subprogram_body, an entry_body and certain statements and the elaboration of certain declarations. The execution of a statement is the execution of a master if the statement is a block_statement or an accept statement. If a statement contains one or more anonymous allocators, then the execution of one or more parts of that statement which contain subprogram calls with anonymous allocators is the execution of one or more masters. If a declaration contains one or more anonymous allocators, the elaboration one or more parts of that declaration which contain subprogram calls with anonymous allocators is the execution of one or more masters. An anonymous allocator is an allocator that is the actual parameter in a subprogram call whose associated formal parameter is an access parameter."
An additional word change is needed in 3.10.2(13) to account for the accessibility level of the allocator:
"The accessibility level of the anonymous access type of an access parameter is the same as that of the view designated by the actual. If the actual is an allocator, this is the accessibility level of the execution of the immediately enclosing statement or the elaboration of the immediately enclosing declaration (which is itself a master -- see 7.6.1)."
If the new wording for master is used, then it appears that there is no need to change 7.6.1(13) to account for anonymous allocators since 7.6.1(4) will handle the "new" master. Some might argue this change as too strong, where pretty much anything is a master, and the concept loses its intuitive appeal. The counter argument is that subprogram calls with anonymous allocators represent a (very) small subset of subprogram calls.
This wording also appears to be unaffected by what statement or declaration the subprogram call with an anonymous allocator might appear. And the only affect it appears to have on the context where it is used is to add more time to the execution of the enclosing statement or the elaboration of the enclosing declaration. In particular, their appearance in a barrier condition are handled by the existing rules on entry calls, namely, such an anonymous allocator can not allocate a task object because that would violate 9.5.1(14) which prevents task activation.
The second option, introducing a new "blocking point", would leave the enclosing block as the master of the task created by the anonymous allocator. Instead this option would add words to 7.6.1(13) which says that any tasks created by an anonymous allocator in a subprogram call are waited for before proceeding to the next statement. Some might argue that this introduces yet another exception to the rules on master-task dependency. But more importantly, requiring that the task be awaited before going on to the next statement without considering the statement itself a master, we will get in trouble with the "terminate" alternative rules, which require that a task depend on some "completed master" for collective termination to kick in.
The third alternative would make the called subprogram's body the master of the task or controlled object created by the anonymous allocator. This is based on the concept the formal parameters of a subprogram should be considered locals of the subprogram, with the actual parameters being the initial expressions for the formals. With this idea, the conceptual "frame" for the called subprogram is created at the start of the evaluation of the actuals, rather than after the evaluation is complete. When the subprogram body returns, it must wait for the task to complete before returning, as usual. This model matches the wording in 3.10.2(13) pretty well. As a favor to implementors, an Implementation Permission would be added where an implementation may postpone waiting for such tasks until other finalization associated with other anonymous objects created by the subprogram call is performed (as defined by 7.6.1(13)). To support this alternative, at least two parts of the ARM that may be affected by wording change: 6.4(10) which describes the overall dynamic semantics of a subprogram call and 6.4.1(8,9) which describe the semantics of parameter association evaluation. 6.4(10) is very clear that the parameter association evaluations are done outside and separately from the execution of the subprogram body. This is inconsistent with the normal relationship of a master and the resources that are dependent on the master. It appears that this paragraph will need major changes. If that is true, what other parts of the ARM would be affected by this change? For example, isn't the dynamic semantics of parameter association evaluation for subprogram calls used as the model for entry calls and protected subprogram calls. Does this mean that the protected subprogram body is blocked waiting for the task to complete? I don't think so. This alternative is beginning to appear very messy.
It appears that the first option of the second alternative is best choice.
Assuming the first option of the second alternative is selected, implementations should have the permission to determine how many masters to execute in the presence of multiple subprogram calls with anonymous allocators, as this example should illustrate:
with P; use P; procedure Test2 is function F (M: access T) return Boolean; function G (N: access T) return Boolean; procedure H (I, J: Boolean); ... begin ... H (F (new CT), G (new CT)); -- (4) ... end Test2;
In the statement (4), the implementation should be permitted to execute one master, associated with the completion of the call to H, or to execute two masters, one associated with the completion of F and the other associated with the completion of G. The following proposed Implementation Permission paragraph should be added to subsection 7.6.1:
"Implementations have the permission to determine how many actual masters are executed in the execution of a statement or the elaboration of a declaration that causes the execution of multiple subprogram calls with anonymous allocators."
!appendix

!section 3.10(2)
!subject Anonymous access types and finalization
!reference RM95 3.10(2)
!reference RM95 3.7(27)
!reference RM95 7.6.1(11)
!from Pascal Leroy 96-08-20
!reference 96-5633.a Pascal Leroy 96-8-21>>
!discussion

The finalization of an object created by an allocator occurs when the master
of the ultimate ancestor of the access type is left, as stated in 7.6.1(11).
 In the case of access discriminants, 3.10(2) tells us that "an
access_definition defines an anonymous access type"; I take this to imply that
an anonymous access type is an "ultimate ancestor" in the sense of 7.6.1(11).
 In addition, 3.7(27) tells us that "an access_definition is elaborated when
the value of a corresponding access discriminant is defined"; I take this to
imply that the master of the anonymous access type is the master that
elaborated the discriminant constraint.

The interaction of these rules has the unfortunate consequence that it makes
it possible to reference an object after it has been finalized, as shown in
the following example:

    procedure P1 is
        type T (D : access Some_Controlled_Type) is ... ;

        procedure P2 is
            type T_Ref is access T;

            X : T_Ref;

            procedure P3 is
            begin
                X := new T (D => new Some_Controlled_Type);
                -- (1)
            end P3;
        begin
            P3;
            -- (2)
        end P2;
    begin
        P2;
    end P1;

The discriminant constraint for the allocated object X.all is elaborated by
P3.  The elaboration of this constraint also elaborates the access_definition,
therefore, the master of the anonymous access type is P3.  So when leaving P3,
at the point marked (1), the allocated object X.all.D.all is finalized, as per
7.6.1(11).  But of course, after leaving P3 we can still use the name
X.all.D.all to reference this object (at the point marked (2)), and this is
bad...

It seems to me that we need a rule that says that the master of an anonymous
access type A used in the specification of an access discriminant in type T,
the master of A is the master of T, not the master that caused A to be
elaborated.  Or, alternatively, that when an allocator is used as a
discriminant value for an anonymous access type, the master of the allocated
object is defined to be the master of the discriminated object. (These two
proposals are different, but they both seem to cure the problem, although the
first one is probably easier to implement.)

_____________________________________________________________________
Pascal Leroy                                    +33.1.30.12.09.68
pleroy@rational.com                             +33.1.30.12.09.66 FAX

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

!section 3.10(2)
!subject Anonymous access types and finalization
!reference RM95 3.10(2)
!reference RM95 3.7(27)
!reference RM95 7.6.1(11)
!reference RM95 3.10.2(12)
!reference 96-5633.a Pascal Leroy 96-08-20
!from Tucker Taft 96-08-22
!reference 96-5637.a Tucker Taft 96-8-22>>
!discussion

> The finalization of an object created by an allocator occurs when the master
> of the ultimate ancestor of the access type is left, as stated in 7.6.1(11).
>  In the case of access discriminants, 3.10(2) tells us that "an
> access_definition defines an anonymous access type"; I take this to imply that
> an anonymous access type is an "ultimate ancestor" in the sense of 7.6.1(11).
>  In addition, 3.7(27) tells us that "an access_definition is elaborated when
> the value of a corresponding access discriminant is defined"; I take this to
> imply that the master of the anonymous access type is the master that
> elaborated the discriminant constraint.

Not quite.

The accessibility level determines the lifetime for the purposes of
finalization.  For access discriminants, the accessibility level is
defined by RM95-3.10.2(12):

    The accessibility level of the anonymous access type of an access
    discriminant is the same as that of the containing object or associated
    constrained subtype.

> The interaction of these rules has the unfortunate consequence that it makes
> it possible to reference an object after it has been finalized, as shown in
> the following example:

This is not a problem, given 3.10.2(12).

>     procedure P1 is
>         type T (D : access Some_Controlled_Type) is ... ;
>
>         procedure P2 is
>             type T_Ref is access T;
>
>             X : T_Ref;
>
>             procedure P3 is
>             begin
>                 X := new T (D => new Some_Controlled_Type);
>                 -- (1)
>             end P3;
>         begin
>             P3;
>             -- (2)
>         end P2;
>     begin
>         P2;
>     end P1;
>
> The discriminant constraint for the allocated object X.all is elaborated by
> P3.  The elaboration of this constraint also elaborates the access_definition,
> therefore, the master of the anonymous access type is P3.  So when leaving P3,
> at the point marked (1), the allocated object X.all.D.all is finalized, as per
> 7.6.1(11).  But of course, after leaving P3 we can still use the name
> X.all.D.all to reference this object (at the point marked (2)), and this is
> bad...
>
> It seems to me that we need a rule that says that the master of an anonymous
> access type A used in the specification of an access discriminant in type T,
> the master of A is the master of T, not the master that caused A to be
> elaborated.  Or, alternatively, that when an allocator is used as a
> discriminant value for an anonymous access type, the master of the allocated
> object is defined to be the master of the discriminated object. (These two
> proposals are different, but they both seem to cure the problem, although the
> first one is probably easier to implement.)

As specified by 3.10.2(12), the accessibility level associated with the
nested allocator "new Some_Controlled_Type" is the same as that of
the enclosing object, X.all, which is the same as T_Ref.  The master
is hence P2, and the finalization for the nested allocator should be
performed when exiting P2.

> Pascal Leroy                                    +33.1.30.12.09.68
> pleroy@rational.com                             +33.1.30.12.09.66 FAX

-Tuck

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

!section 3.10(2)
!subject Anonymous access types and finalization
!reference RM95 3.10(2)
!reference RM95 3.7(27)
!reference RM95 7.6.1(11)
!reference RM95 3.10.2(12)
!reference 96-5633.a Pascal Leroy 96-08-20
!from Pascal Leroy 96-08-23
!reference 96-5641.a Pascal Leroy 96-8-23>>
!discussion

> The accessibility level determines the lifetime for the purposes of
> finalization.  For access discriminants, the accessibility level is
> defined by RM95-3.10.2(12):
>
>     The accessibility level of the anonymous access type of an access
>     discriminant is the same as that of the containing object or associated
>     constrained subtype.

I stand corrected.  I had failed to see the significance of accessibility
levels in this case, as explained in 7.6.1(4).  I guess I was reasoning a la
Ada 83 ;-)

Pascal

_____________________________________________________________________
Pascal Leroy                                    +33.1.30.12.09.68
pleroy@rational.com                             +33.1.30.12.09.66 FAX

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

!section 3.10.2(13)
!subject Anonymous allocators and tasks/finalization
!reference RM95 3.10.2(13)
!reference RM95 7.6.1(13)
!reference 96-5633.a Pascal Leroy 96-08-20
!reference 96-5637.a Tucker Taft 96-08-22
!reference 96-5641.a Pascal Leroy 96-08-20
!from Tucker Taft 96-09-10
!reference 96-5698.a Tucker Taft 96-9-10>>
!discussion

In 96-5633.s Pascal Leroy indicated a concern with when the object
created by an "anonymous" allocator (one whose expected type
was the anonymous access type of an access parameter) would
be finalized.  In answer, I pointed to 7.6.1(13) where it identifies
the accessibility level of such allocators as being the same as
that of the execution of the called subprogram.

That seemed to resolve the issue.  However, if you consider
anonymous allocators that create tasks as part of an object
whose size is not known at compile-time, you encounter the
first situation where the master of a task should perhaps be the
execution of a construct "smaller" than a block statement.

It seems that 7.6.1(13) should be augmented to cover the case
of anonymous allocators, which can result in the creation of
"anonymous" objects as part of any subprogram call.  Since these
anonymous objects might be of a limited type (unlike the case
for function returns and array aggregates), we need to consider
task "finalization" as well.  I believe the only consistent
approach is to consider the "master" for such anonymous objects
to be (the execution of) the construct (or construct "part") containing
the subprogram call, and ensure that both task waiting and finalization
occur as part of "leaving" the construct part.  By construct part, I mean
what 7.6.1(13) implicitly identifies as the unit of finalization for temps,
namely, the innermost declarative_item or statement, or in the case of
a compound statement, the part of the compound statement
up to the next statement nested within the compound statement.

Two (rejected0 alternatives are to associate the "anonymous" task with the
innermost enclosing (normal) master of the caller, or with the
execution of the called subprogram.  Neither of these work too well.

    Associating with the enclosing master doesn't work,
    since if the anonymous allocator is inside a loop, one clearly wants
    to recover the storage associated with the anonymous allocator
    before going on to the next loop iteration, even though a loop
    is not a master.

    Associating with the called subprogram
    doesn't work too well, since the call frame for the called
    subprogram doesn't exist at the time when the anonymous task is
    created (and may never exist if one of the later parameter
    evaluations raises an exception).

Also, other temps associated with subprogram call are finalized
immediately after the results of the call are no longer needed, so
it seems that anonymous allocators should operate the same way.

Hence, it seems we need a new notion of "master" to properly
deal with anonymous allocators, or we at least need to *permit*
implementations to wait for tasks created by anonymous allocators
before the enclosing "normal" master completes (I suppose non-real-time
implementations can wait for anything anytime they want, but we care
about real-time-conforming implementations as well).

In any case, 7.6.1(13) should acknowledge this new kind of anonymous
object, namely the anonymous object created by the evaluation
of an "anonymous" allocator.

-Tuck

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

From: Stephen W. Baird
Sent: Thursday, October 30, 2003  11:31 AM

This is a preliminary discussion of AI-162, as per my homework
assignment from the Sydney ARG meeting.

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

Allocators of anonymous access types introduce a fairly large amount of
complexity to the dynamic semantics of Ada.

This complexity is in some sense proportional to the product of two sets:
the set of contexts in which an anonymous access type may occur and a set
of issues which must be resolved for each context.

The set of contexts in which an anonymous access type may occur includes
    - as a discriminant type of a "really limited" type
    - as a formal parameter type
    - as a "garden variety" component type (proposed in AI 230)
    - as a function result type (proposed in AI 325)

The set of issues includes
    - determining the point of finalization for the allocated object
    - determining the master of any task parts of the allocated object
    - determining the storage_pool to be used by the allocator
    - determining the point of reclamation for the allocated object's storage
    - determining the accessibility level of the access value

This leads to the question of how best to express the desired rules.

One could distribute them throughout the manual, adding provisions for
each of the listed contexts to (at least) 3.10.2, 7.6.1, 9.3, and 13.11.
This would require lots of changes (each of several lines) with the
resulting logic scattered nearly to the point of unreadability.

One might try to leverage the work that has already been done for
allocators of named access types by using equivalence rules.
In addition to the general objection "Equivalence rules never work", this
approach has a more specific flaw.
It relies on the assumption that for each allocator of an anonymous access
type, there exists a point where a named access type could have been
declared and which would have resulted in the same program behavior if
that named type had been used as the type of the allocator.
In the case of a function result, this assumption does not hold.

I think the best of the available alternatives would be to add an entirely
new section to the manual, perhaps 4.8.1.
The passages mentioned above (3.10.2, 7.6.1, 9.3, and 13.11) would each
contain a one-line reference to the new section.
This seems like overkill for dealing with what seems like an obscure
corner case, but it is better than the alternatives.

Before I go any further with this, I would like to get the opinion of the
ARG as to whether I am on the right track.

P.S.

An_Only_Tangentially_Related_Rant:
  begin

    I believe the utility of anonymously-typed allocators does not justify
their complexity, that allowing them  was a language design
   mistake, and that AIs 230 and 325 (which, ignoring allocators, seem
reasonable to me) increase the impact of this error.

    I recognize, however, that my opinion on this subject is not widely
shared and that an incompatible change in this area is not a realistic
option.

    I also concede that it would seem odd to continue to allow anonymously
typed allocators in some contexts while disallowing them in
    the new contexts proposed  in AIs 230 and 325.

  end An_Only_Tangentially_Related_Rant;

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

From: Tucker Taft
Sent: Thursday, October 30, 2003  12:00 PM

...
> This leads to the question of how best to express the desired rules.
>
> One could distribute them throughout the manual, adding provisions for
> each of the listed contexts to (at least) 3.10.2, 7.6.1, 9.3, and 13.11.
> This would require lots of changes (each of several lines) with the
> resulting logic scattered nearly to the point of unreadability.

So what else is new?  But seriously, we do this for essentially
every construct.  I don't know why anonymous allocators need
"special" treatment.  Programmers just want them to work.
Implementors can deal with scattered definitions.

> ...
> I think the best of the available alternatives would be to add an entirely
> new section to the manual, perhaps 4.8.1.
> The passages mentioned above (3.10.2, 7.6.1, 9.3, and 13.11) would each
> contain a one-line reference to the new section.
> This seems like overkill for dealing with what seems like an obscure
> corner case, but it is better than the alternatives.

I don't agree.  I don't see sufficient benefit to justify
creating a separate section.

> Before I go any further with this, I would like to get the opinion of the
> ARG as to whether I am on the right track.

I would add/fix the wording in the relevant sections.

With your proposal, you will have to add/change/remove some amount
of wording in each of these sections to point to the new
section, plus write the wording in the new section, in each
case creating sufficient context to link the wording back to
the place where it is relevant.

Sounds like a loser to me.

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

From: Randy Brukardt
Sent: Thursday, October 30, 2003  12:03 PM

Steve wrote:

> An_Only_Tangentially_Related_Rant:
>   begin
>
>     I believe the utility of anonymously-typed allocators does not justify
> their complexity, that allowing them  was a language design
>    mistake, and that AIs 230 and 325 (which, ignoring allocators, seem
> reasonable to me) increase the impact of this error.
>
>     I recognize, however, that my opinion on this subject is not widely
> shared and that an incompatible change in this area is not a realistic
> option.

I agree with you 100% here. I think there would be some value to finding out
how often this 'feature' is used. (I've never used it personally.) So far as
I am aware, the support for this feature primarily comes from one very
influental ARG member. If there really was a lot of interest in this, I
think we would have accepted (with modifications), not killed, Mike Kamrad's
version of this AI. (It, after all, is what is needed to solve this problem.
I very much doubt that there is a substantially simpler solution that solves
the problems.)

>     I also concede that it would seem odd to continue to allow anonymously
>     typed allocators in some contexts while disallowing them in
>     the new contexts proposed  in AIs 230 and 325.

There is another possibility which is not context-dependent and helps quite
a bit. That would be to prohibit anonymous allocators if the designated type
"needs finalization". Since we never figured out what that means, I suspect
that using such a thing would get you into trouble on many compilers.
They'll do *something*, but exactly what isn't clear (and it may not be
useful). I think Janus/Ada would generate an internal error because the
finalization chain wouldn't have been set up (although I haven't tried it).

That would limit the incompatibility, and completely eliminate the task
master and finalization issues. Tucker's answer for the storage
pool/reclamation problem seems OK (other than for AI-325, which I hope dies
an early, well-deserved death).

> I think the best of the available alternatives would be to add an entirely
> new section to the manual, perhaps 4.8.1.
> The passages mentioned above (3.10.2, 7.6.1, 9.3, and 13.11) would each
> contain a one-line reference to the new section.
> This seems like overkill for dealing with what seems like an obscure
> corner case, but it is better than the alternatives.

Without seeing wording, it's very hard to say for sure. But I don't really
see the point; I doubt that you could share much between the rules to handle
the various issues. (Especially if we have the guts to solve the messiest
issues by banning them.)
Without such sharing, there is little benefit to having the rules in the
same place. And I don't think that there is much in common between a "task
master" rule and a "storage pool" rule, for example.

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

From: Tucker Taft
Sent: Thursday, October 30, 2003  12:20 PM

I agree that they might have been a mistake, but they
are definitely used now, and the new Ada 2005 uses are the *simpler*
ones, since they are the cases that *do* correspond to
named access types.  The tough ones are the old cases,
in particular access discriminants of limited types.
Of course, those are exactly the cases where they are
most useful, where you couldn't easily write an equivalent
"normal" allocator.

If you use access discriminants, it is not particularly weird
to want to initialize the access discriminant with an allocator.
They allow you to overcome the limitation that you can't have discriminants
of a record or floating point type.

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

From: Tucker Taft
Sent: Thursday, October 30, 2003  12:30 PM

One other point:

> ... If there really was a lot of interest in this, I
> think we would have accepted (with modifications), not killed, Mike Kamrad's
> version of this AI. (It, after all, is what is needed to solve this problem.
> I very much doubt that there is a substantially simpler solution that solves
> the problems.)

The problem is more one of description, since we don't currently
have individual statements as masters.

Implementors already know how to deal with aggregates and function calls
that require finalization, so fitting task waiting into that
can be relatively straightforward.  Chances are implementors who
handle finalization right also do something reasonable with tasks.

One possibility is to continue to ignore the task-waiting
problem in the RM, and put in some kind of "to be honest".
Or write something simple for the semantics, and allow
more options via an implementation permission.

(Without contract model problems, I don't see how to disallow
creating tasks or controlled objects in anonymous allocators.)

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

From: Stephen W. Baird
Sent: Thursday, October 30, 2003  1:45 PM

Tuck wrote:
> I agree that they might have been a mistake, but they
> are definitely used now, and the new Ada 2005 uses are the *simpler*
> ones, since they are the cases that *do* correspond to
> named access types.  The tough ones are the old cases,
> in particular access discriminants of limited types.
> Of course, those are exactly the cases where they are
> most useful, where you couldn't easily write an equivalent
> "normal" allocator.

I disagree. If an allocator of an anonymous type is used as a
discriminant of an limited type, then that is equivalent to declaring
a named access type at the point of the constraint (not at the
point of the limited type declaration) and using that for the type
of the allocator. The exception to this is if the allocator is
being used as a discriminant value to constrain another allocator,
in which case the named access type would have to be declared at the
point of the type of the second allocator (or, if the type of the second
allocator is also anonymous, at the point where its equivalent named
access type might have been declared - we assume inductively that such
a point exists).

The case where you can't write an equivalent normal allocator is
the anonymous function result type case (AI-325). Here, for the first time
(ignoring shared code generics; I haven't thought about them)
we have an allocator whose accessibility level is not known statically.

In particular, if the caller is more nested than the callee, then it
would be very difficult to use a named access type for the type of
the returned allocator.

If it wasn't for this case, one could use equivalence rules in the RM
and I would at least consider doing so.

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

From: Tucker Taft
Sent: Thursday, October 30, 2003  3:29 PM

I would recommend you not worry about AI-325 when working on AI-162.
AI-325 may sink under its own weight anyway, and the worry about
anonymous allocators in return statements is part of its weight,
not AI-162's, at this point.  No need for you to unburden it.

One other point I might mention.  There seems to be a fair
amount of support for AI-287, "Limited Aggregates Allowed."
If we presume that will happen, then we need to deal with
tasks created by aggregates.  This seems very nearly identical
to the problem associated with tasks created by anonymous
allocators passed as a parameter.

It would seem that 7.6.1(13) could be incorporated into the
definition of master in 7.6.1(3), and then finalization and
task waiting would be handled uniformly for statements or
declarations containing such aggregates or anonymous allocators.

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

From: Randy Brukardt
Sent: Thursday, October 30, 2003  7:00 PM

Tucker said:

> I would recommend you not worry about AI-325 when working on AI-162.
> AI-325 may sink under its own weight anyway, and the worry about
> anonymous allocators in return statements is part of its weight,
> not AI-162's, at this point.  No need for you to unburden it.

Right. And technically, since AI-162 is not an amendment AI, it shouldn't
consider AI-230 or any other amendment either.

> One other point I might mention.  There seems to be a fair
> amount of support for AI-287, "Limited Aggregates Allowed."
> If we presume that will happen, then we need to deal with
> tasks created by aggregates.  This seems very nearly identical
> to the problem associated with tasks created by anonymous
> allocators passed as a parameter.

My initial reaction was no, they're only allowed in contexts where they are
directly initializing an object. But that's not what the AI says. And,
worse, there doesn't seem to be any semantics for passing an aggregate to a
parameter of a by-reference type. So I have no idea what is supposed to
happen here. (This would seem to be the case for Ada 95 as well, for tagged
type aggregates.) The problem is that 6.4.1(10) talks about passing a "view
conversion of the actual parameter". But view conversions are only defined
for "the name of an object" 4.6(5/1). There is no object, only a value, for
an aggregate. So what is going on?

In any case, presuming you are right, any such wording belongs to AI-287 (it
would be unfair to stuff it into some other AI with the claim that we have
to do it anyway, because we clearly don't). Which, unfortunately, means
reopening that AI.

Earlier, Tucker said:

>(Without contract model problems, I don't see how to disallow
>creating tasks or controlled objects in anonymous allocators.)

Well, Janus/Ada currently uses an assume-the-worst model: any program with
such an allocator GP faults (which is certainly the worst that could happen
;-). It clearly would be better to reject any such programs.

Seriously, the language could use an assume-the-worst model, but that seems
a bit strong. (Unless you want to ban such allocators altogether.) It also
clearly could be a mixed legality/runtime check like accessibility. But I
realize that's not particularly "clean".

>The problem is more one of description, since we don't currently
>have individual statements as masters.

Right, but we have to have that (in some form) in order to deal with this
problem.

>Implementors already know how to deal with aggregates and function calls
>that require finalization, so fitting task waiting into that
>can be relatively straightforward.  Chances are implementors who
>handle finalization right also do something reasonable with tasks.

I don't think they are the same problem. Certainly some guy named Tucker
thought that they were separate when it came up discussing AI-280: "Hence, I
think this implies that if you have per-object work to do storage
reclamation, finalizing a master requires three separate sequences:
        1) wait for all subtasks
        2) finalize all controlled/protected objects
        3) reclaim all storage
Trying to intermingle any of these three will violate the RM in my view."
(message of Dec. 11, 2001) I don't see a practical RM wording that doesn't
intermingle them.

Similarly, finalization of anonymous access allocators is a completely
separate problem from finalization of aggregates. The issue is that access
types have their own, separate finalization list [as an access type is a
master]. That list has to be initialized somewhere, and that requires
special processing. Similarly for access type task masters. You'd have to do
an extra scan of every statement's expression tree to see if any
finalization chains or masters were needed.

But I think you would prefer that this access "type" does not have the
semantics of an access type at all. (That is, there is no separate
finalization list, pool, and task master). That seems like a disaster; we'd
have to look at every rule that involves tasks or finalization or pools and
insure that they have the right wording to support such a "split".

Implementation-wise, it would be worse: such an item would have to be
represented differently than an access type in the compiler, and we'd have
to check and in many cases do something different everywhere that an access
type is used. A quick check of Janus/Ada's source shows 69 uses of "Is_Ptr",
but the middle pass doesn't even use a special function: it assumes that all
Ptr_Class types (that's a representation) are implemented the same way. I
found over 550 uses of "Ptr_Class". (These counts probably are high, because
of hits of entities that contain those phrases.) To find and change all of
the placed needed to separately support such allocators would be daunting.

In any event, such a model wouldn't completely work for Janus/Ada. For
historical reasons, task masters are separate objects from the finalization
chain (which implements masters in the language sense); they're linked onto
the list at an appropriate place. So, while we might be able to get rid of
the finalization chain for anonymous access types (although that seems
sensible only for anonymous access used as a parameter), getting rid of the
master isn't possible. We'd have to generate a statement level master for
the access type, and that would be a new (and potentially expensive)
construct.

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

From: Tucker Taft
Sent: Thursday, October 30, 2003  8:26 PM

> ...
> > One other point I might mention.  There seems to be a fair
> > amount of support for AI-287, "Limited Aggregates Allowed."
> > If we presume that will happen, then we need to deal with
> > tasks created by aggregates.  This seems very nearly identical
> > to the problem associated with tasks created by anonymous
> > allocators passed as a parameter.
>
> My initial reaction was no, they're only allowed in contexts where they are
> directly initializing an object. But that's not what the AI says. And,
> worse, there doesn't seem to be any semantics for passing an aggregate to a
> parameter of a by-reference type.

What is the confusion here?  An aggregate is a constant object (3.3(21)).
It seems clear what it means to pass it by reference.

> ... So I have no idea what is supposed to
> happen here. (This would seem to be the case for Ada 95 as well, for tagged
> type aggregates.) The problem is that 6.4.1(10) talks about passing a "view
> conversion of the actual parameter". But view conversions are only defined
> for "the name of an object" 4.6(5/1). There is no object, only a value, for
> an aggregate. So what is going on?

Aggregates are objects.  Also, values of a by-reference type have
associated objects (see 6.2(10)).

> In any case, presuming you are right, any such wording belongs to AI-287 (it
> would be unfair to stuff it into some other AI with the claim that we have
> to do it anyway, because we clearly don't). Which, unfortunately, means
> reopening that AI.

I'm not sure we need to do that.  I am suggesting that via AI-162 we
create a general solution, not one tailored to anonymous allocators.
Once we establish a master around a statement or declaration that
can handle temporary tasks, finalizable objects, or short-lived
anonymous access types, it should be relatively easy to deal
with aggregates and anonymous allocators.

> >The problem is more one of description, since we don't currently
> >have individual statements as masters.
>
> Right, but we have to have that (in some form) in order to deal with this
> problem.

And aggregates containing tasks.

> >Implementors already know how to deal with aggregates and function calls
> >that require finalization, so fitting task waiting into that
> >can be relatively straightforward.  Chances are implementors who
> >handle finalization right also do something reasonable with tasks.
>
> I don't think they are the same problem. Certainly some guy named Tucker
> thought that they were separate when it came up discussing AI-280: "Hence, I
> think this implies that if you have per-object work to do storage
> reclamation, finalizing a master requires three separate sequences:
>         1) wait for all subtasks
>         2) finalize all controlled/protected objects
>         3) reclaim all storage
> Trying to intermingle any of these three will violate the RM in my view."
> (message of Dec. 11, 2001) I don't see a practical RM wording that doesn't
> intermingle them.

All very true, but they tend to happen at the same point.
For example, in our compiler, we have a single "master" record
which can have a list of tasks, and a list of cleanup actions.
We first do all the finalization actions, and then we wait for
all the tasks.  If we are also going to reclaim storage at this
point, we do that after the finalization and task waiting.

> Similarly, finalization of anonymous access allocators is a completely
> separate problem from finalization of aggregates. The issue is that access
> types have their own, separate finalization list [as an access type is a
> master].

An access type is not itself a master.  An access type is elaborated
by some master, and that determines the master of the tasks
created by allocators for the access type (9.3(2)).

> ... That list has to be initialized somewhere, and that requires
> special processing. Similarly for access type task masters. You'd have to do
> an extra scan of every statement's expression tree to see if any
> finalization chains or masters were needed.
>
> But I think you would prefer that this access "type" does not have the
> semantics of an access type at all. (That is, there is no separate
> finalization list, pool, and task master). ...

No, I am not saying that.  All I am recommending is that they
all share a single master from a languge description point of view,
which seems appropriate, given the fact that an access type is
not itself a master.  I don't want to get into the business of
telling you how to implement this at a detailed level.

I am simply arguing that many implementors (not all) use
much of the same mechanism for finalization and task waiting,
and we should take advantage of that by having aggregates
and these kinds of short-lived anonymous access types share
the same master.  Obviously you don't have to take advantage
of that.  But we certainly shouldn't require that the finalization
and the task waiting happen at different points.

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

From: Randy Brukardt
Sent: Thursday, October 30, 2003  9:00 PM

Tucker replied to me:

> > But I think you would prefer that this access "type" does not have the
> > semantics of an access type at all. (That is, there is no separate
> > finalization list, pool, and task master). ...
>
> No, I am not saying that.  All I am recommending is that they
> all share a single master from a languge description point of view,
> which seems appropriate, given the fact that an access type is
> not itself a master.  I don't want to get into the business of
> telling you how to implement this at a detailed level.

Feel free. Your model may make more sense. :-)

> I am simply arguing that many implementors (not all) use
> much of the same mechanism for finalization and task waiting,
> and we should take advantage of that by having aggregates
> and these kinds of short-lived anonymous access types share
> the same master.  Obviously you don't have to take advantage
> of that.  But we certainly shouldn't require that the finalization
> and the task waiting happen at different points.

That I certainly can agree with. It would be harder still if the task master
(for waiting) was inconsistently positioned. (We do use the finalization
mechanism for determining the actual point of triggering of waiting, because
that automatically handles exceptions and aborts. But it is a separate
object placed on the finalization chain at the right point. Storage
management for user-accessible objects (not temporaries) will be done the
same way when I get around to changing it.)

My problem is figuring out how to declare a temporary task master object
(which contains the waiting links) at some point where such things are not
expected. Declaring task masters is messy, because we don't want to generate
one unless we're sure we need it (if one is generated, the tasking runtime
is required in the program, which is not true otherwise. And they're
relatively expensive to initialize, as doing so requires a task supervisor
call). Indeed, this is one of the very few places where we diverge from a
pure-one-pass design, and it's because we do that that it becomes messy. But
this is probably not a language issue.

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


Questions? Ask the ACAA Technical Agent