Version 1.5 of ai12s/ai12-0025-1.txt

Unformatted version of ai12s/ai12-0025-1.txt version 1.5
Other versions for file ai12s/ai12-0025-1.txt

!standard 13.10(3)          12-05-16 AI12-0025-1/01
!class Amendment 12-05-16
!status hold 12-06-15
!status work item 12-05-16
!status received 12-02-21
!priority Medium
!difficulty Medium
!subject Allow 'Unchecked_Access on subprograms
!summary
Define an aspect "Allow_Unchecked" which can be applied to an access-to-subprogram type.
!problem
Since Ada 95, the language has allowed 'Unchecked_Access on objects to bypass the accessibility rules, but it has not allowed 'Unchecked_Access on subprograms. There is real code which had a need to declare a named access-to-subprogram type and objects of that type which could hold an access to a subprogram nested at any level. The code uses 'Unrestricted_Access, which is implemented by GNAT and Irvine Compiler, but of course this is not portable to other compilers that don't implement this. Thus a language-defined attribute would be useful.
!proposal
The reason for disallowing 'Unchecked_Access on subprograms, according to AARM 13.10(5.a), is that allowing this would require all access-to-subprogram types, including those which could not legally represent the 'Access of a nested subprogram (i.e. declared at library level), to carry extra overhead in case an 'Unchecked_Access were used.
This now can be solved by defining an aspect to specify that the type needs to include the extra overhead. Conversions of values with the extra overhead to values without it would need to be prohibited.
!wording
[Editor's note: This wording is from the original proposal, it's probably not a complete solution. See the !discussion.]
Insert after 13.10(3):
For a type declaration of an access-to-subprogram type, the following representation aspect may be specified:
Allow_Unchecked
The type of aspect Allow_Unchecked is Boolean.
[AJB: I'm assuming that since this I called this a represenation aspect, an implementation can refuse to support it, if access-to-subprogram types are somehow implemented in a way that would make it difficult to support an object holding an access to a more-nested subprogram.]
The following attribute is defined for a prefix P that denotes a subprogram:
P'Unchecked_Access
All rules and semantics that apply to P'Access (see 3.10.2) apply also to P'Unchecked_Access, except that, for the purpose of accessibility rules and checks, it is as if P were declared immediately within a library package; and except that if P is an explicit_deference of an object of an access-to-subprogram type, the Allow_Unchecked aspect may be True for the object's type. The Allow_Unchecked aspect shall be True for the expected type of the attribute, or else the expected type shall be the anonymous access type of an access parameter.
Delete 13.10(5).
Add to the end of 3.10.2(32):
If P is an explicit_deference of an object of an access-to-subprogram type, the Allow_Unchecked aspect shall not be True for the object's type. [AJB: I would have said "shall be False" but I'm not clear on whether an a Boolean aspect clause that is not specified means that the aspect is False or that it just doesn't exist.]
Delete the last sentence of 3.10.2(37).
Insert after 4.6(24.21):
If the Allow_Unchecked aspect is True for the operand type, it shall also be True for the target type, or else the target type shall be the anonymous access type of an access parameter.
Insert after K.1(5):
Allow_Unchecked
'Unchecked_Access of a subprogram may be used as an expression of the type.
Insert after K.2(257):
P'Unchecked_Access
For a prefix P that denotes a subprogram:
All rules and semantics that apply to P'Access (see 3.10.2) apply also to P'Unchecked_Access, except that, for the purpose of accessibility rules and checks, it is as if P were declared immediately within a library package. See 13.10.
!discussion
Editor's notes:
(1) It would make more sense to use the same conversion rules that apply to anonymous access-to-subprogram for types that have this new aspect, rather than inventing new rules. The existing types use accessibility rules for this purpose, and we probably ought to do the same here.
(2) There needs to be a new rule about erroneousness when calling a non-existent subprogram. (The problem being that the subprogram always exists, but the data that it references may not.) The existing rules (13.10.2(16.3)) only cover objects.
(3) Rules are needed for generic matching (so that 'Unchecked_Access could be used with a formal access-to-subprogram type), and so that the conversion rules are enforced properly.
(4) Taking 'Unchecked_Access in a generic body would need to be illegal in the same cases that 'Access is. Where accessibility is used to enforce those rules now, other rules would need to be added.
!ACATS test
ACATS B and C-tests need to be defined for this new functionality.
!appendix

From: Adam Beneschan
Sent: Tuesday, February 21, 2012  7:32 PM

!topic Allow 'Unchecked_Access on subprograms
!reference 13.10, 3.10.2, 4.6
!from Adam Beneschan 12-02-21
!discussion

Since Ada 95, the language has allowed 'Unchecked_Access on objects to bypass
the accessibility rules, but it has not allowed 'Unchecked_Access on
subprograms.  It would be useful to allow it, however.  I have seen real code
which had a need to declare a named access-to-subprogram type and objects of
that type which could hold an access to a subprogram nested at any level.  The
code uses 'Unrestricted_Access, which is implemented by GNAT and Irvine
Compiler, but of course this is not portable to other compilers that don't
implement this.  Thus a language-defined attribute would be useful.

The reason for disallowing 'Unchecked_Access on subprograms, according to AARM
13.10(5.a), is that allowing this would require all access-to-subprogram types,
including those which could not legally represent the 'Access of a nested
subprogram (i.e. declared at library level), to carry extra overhead in case an
'Unchecked_Access were used.

However, I believe that this can be solved now that the language supports aspect
clauses.  I'm proposing that an Allow_Unchecked aspect be defined for
access-to-subprogram types, and that P'Unchecked_Access be allowed only if the
Allow_Unchecked aspect is True for the expected type.  Thus, the additional
overhead needed to allow P'Unchecked_Access would be present only for those
types that have the aspect specified.  To prevent values of types without this
aspect (other than anonymous access-to-subprogram parameters) from representing
"unsafe" nested accesses, the rules would prevent type conversions from types
with the aspect to types without the aspect, and they would prohibit
P.all'Access where P's type has the Allow_Unchecked aspect.

I've appended my thoughts about how the language would be modified, in the hope
that this helps with some of the workload if the proposal is adopted.  However,
I can't guarantee that these suggestions are correct or complete.

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

Insert after 13.10(3):

For a type declaration of an access-to-subprogram type, the following representation aspect may be specified:

Allow_Unchecked
        The type of aspect Allow_Unchecked is Boolean.

[AJB: I'm assuming that since this I called this a represenation aspect, an
implementation can refuse to support it, if access-to-subprogram types are
somehow implemented in a way that would make it difficult to support an object
holding an access to a more-nested subprogram.]

The following attribute is defined for a prefix P that denotes a
subprogram:

P'Unchecked_Access
        All rules and semantics that apply to P'Access (see 3.10.2)
        apply also to P'Unchecked_Access, except that, for the purpose
        of accessibility rules and checks, it is as if P were declared
        immediately within a library package; and except that if P is
        an explicit_deference of an object of an access-to-subprogram
        type, the Allow_Unchecked aspect may be True for the object's
        type.  The Allow_Unchecked aspect shall be True for the
        expected type of the attribute, or else the expected type
        shall be the anonymous access type of an access parameter.

Delete 13.10(5).

Add to the end of 3.10.2(32):
        If P is an explicit_deference of an object of an
        access-to-subprogram type, the Allow_Unchecked aspect shall
        not be True for the object's type.
        [AJB: I would have said "shall be False" but I'm not clear on
        whether an a Boolean aspect clause that is not specified means
        that the aspect is False or that it just doesn't exist.]

Delete the last sentence of 3.10.2(37).

Insert after 4.6(24.21):

If the Allow_Unchecked aspect is True for the operand type, it shall also be
True for the target type, or else the target type shall be the anonymous access
type of an access parameter.

Insert after K.1(5):

Allow_Unchecked
    'Unchecked_Access of a subprogram may be used as an expression of
    the type.

Insert after K.2(257):

P'Unchecked_Access

        For a prefix P that denotes a subprogram:

        All rules and semantics that apply to P'Access (see 3.10.2)
        apply also to P'Unchecked_Access, except that, for the purpose
        of accessibility rules and checks, it is as if P were declared
        immediately within a library package.  See 13.10.

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

From: Brad Moore
Sent: Wednesday, February 22, 2012  8:37 AM

> Since Ada 95, the language has allowed 'Unchecked_Access on objects to
> bypass the accessibility rules, but it has not allowed
> 'Unchecked_Access on subprograms.  It would be useful to allow it,
> however.  I have seen real code which had a need to declare a named
> access-to-subprogram type and objects of that type which could hold an
> access to a subprogram nested at any level.  The code uses
> 'Unrestricted_Access, which is implemented by GNAT and Irvine
> Compiler, but of course this is not portable to other compilers that
> don't implement this.  Thus a language-defined attribute would be
> useful.

I am also familiar with the code using the access-to-subprogram type use. I had
mentioned the issue to Steve Baird from Adacore, and he suggested that the use
of a tagged type that had a primitive with the desired profile, and then
dispatching on the call might get the desired effect of an access-to-subprogram
type. This would then allow Unchecked_Access on the tagged object, which would
be portable.

I wasn't sure that this suggestion would pan out, because the code needs to
return a result from a call, before returning from the call, and the only way to
do that, that I could see, involved using a parameter to the call that was an
access to an access type.

I did some experimenting last night however, and I believe I found a reasonable
workaround, based on this approach.

It involves instantiating a generic that creates the tagged type.
(An interface actually), then then instantiating a child generic subprogram that
accepts an access to the access-to-class wide type, instead of an access to an
access-to-subbprogram, as was the case before. This seems to work, without any
noticeable loss in performance.

So it seems in this case, at least, the need for 'Unchecked_Access can be
avoided. That's not to say that there might be other possible cases where such a
feature might be needed.

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

From: Jean-Pierre Rosen
Sent: Wednesday, February 22, 2012  9:00 AM

> It involves instantiating a generic that creates the tagged type.
> (An interface actually), then then instantiating a child generic
> subprogram that accepts an access to the access-to-class wide type,
> instead of an access to an access-to-subbprogram

Explain that to a newbie coming from C, and tell him how nice Ada is :-) !

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

From: Bob Duff
Sent: Wednesday, February 22, 2012  9:10 AM

Yeah, I wouldn't explain that to a newbie coming from C.

But if a C programmer complains that it's difficult to deal with pointers to
nested functions in Ada, I'd point out that C doesn't even have nested
functions.  ;-)

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

From: Brad Moore
Sent: Wednesday, February 22, 2012  10:34 AM

Not to mention generics, although one could go to C++ for a similar sort of
capability in templates.

Some of the other language features being used such as portable concurrency, and
portable parallelism, would be difficult for a C programmer to complain about as
well. ;-)

Although it might be tricky to explain to the newbie C programmer the mechanisms
being used in the generic, at least the user of the generic doesn't have to be
aware of the nested subprograms hidden inside the generic, or other complexities
hidden behind the generic abstraction.

Explaining how to use the generic to a newbie C programmer, at least would not
be as difficult a task, I would think.

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

From: Brad Moore
Sent: Wednesday, February 22, 2012  1:14 PM

>Some of the other language features being used such as portable
>concurrency, and portable parallelism, would be difficult for a C
>programmer to complain about as well.

To be fair, I see that the new C11 standard had added support for thread
creation and management, mutexes and condition variables, and atomic objects.

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

From: Gautier de Montmollin
Sent: Sunday, February 26, 2012  10:13 AM

>> It involves instantiating a generic that creates the tagged type.
>> (An interface actually), then then instantiating a child generic
>> subprogram that accepts an access to the access-to-class wide type,
>> instead of an access to an access-to-subbprogram
> Explain that to a newbie coming from C, and tell him how nice Ada is :-) !

Even a long-time Ada user did not understand the whereabouts of this intricated
workaround.

If it is for the sake of saying "see, you /can/ do it in Ada without
Unrestriced_Access", it is close to perversion...

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

From: Brad Moore
Sent: Tuesday, February 28, 2012  12:40 AM

I can see how the brief but rather complex explanation above could be
mystifying.

I think its really not as bad as it sounds though. I will attempt to shed some
light by way of an illustrative example.

And actually, the workaround can be simplified. There is no need to instantiate
a child generic subprogram for instance.

First of all, the changes needed for the workaround need to be separated from
the original problem space.

The problem space can be framed in terms of;

    - *What* was the problem that needed solving?
    - *Why* is the problem worth solving?

The work around can be framed in terms of;
    - *How* was the solution coded to solve the problem.

Once the "What" and "Why" are known, they can be put aside, as it is only the
"How" that matters for comparison between the two approaches.

1. *What* was the problem to solve anyway?

Assume there is a need to call an instance of an Ada generic, and have that
generic instance call a user-defined callback which is passed to the generic.
That callback in turn needs to make a call to a nested subprogram inside the
generic instance body that is nested within the scope of the original call into
the generic. No global variables are to be used, i.e., all declarations are
stack-based.

2. *Why would anyone want to do this?

A functional programming style is desired to provide a generic utility that
performs some complex task, with hooks to allow users to reuse the generic for
different purposes. By disallowing global declarations, this allows the code to
be thread-safe without requiring synchronization or raising concurrency
concerns, since everything about the call is on the local stack. The
user-defined callback may need to call back into the generic to access state
information declared in the scope of the original call.

3. OK, so *How* did this look originally, with the 'Unrestricted_Access
implementation?  (Using Ada 2012 Pre and Post conditions to clarify usage)


generic
    type T is range <>;
    type Call_Into_Type is access procedure (Item : T);
    -- Note: we have to pass in the access to subprogram type
    -- This is not needed in the new implementation.
procedure Execute
   (Item : T;
    --  Value of User Defined type

    Call_Into : not null access Call_Into_Type;
    --  Call_Into is 'set' to an internal nested procedure
    --  before returning from this call, and before Callback
    --  is called. (Which is why this is an access parameter,
    --  instead of an in out parameter.) One cannot count on
    --  an "in out" parameter being set *before* returning from this
    --  call.

    Callback : not null access procedure (Item : T)
    --  Client Callback can call Call_Into)
      with Pre => Call_Into.all = null,
           Post => Call_Into.all = null;
    -- Call_Into is only non-null within the callback

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

with Ada.Text_IO; use Ada.Text_IO;

procedure Execute
    (Item          : T;
     Call_Into : not null access Call_Into_Type;
     Callback : not null access procedure (Item : T)) is

    Call_Count : Natural := 0;
    Total : T := 0;

    procedure Nested_Call (Item : T) is
       --  Note: Nested_Call can view declarations hidden in the generic
    begin
       Call_Count := Call_Count + 1;
       Total := Total + Item;
    end Nested_Call;

begin

    --  Allow client to call nested procedure
    --  Note: Unrestricted_Access is not portable
    Call_Into.all := Nested_Call'Unrestricted_Access;

    --  Note: It is OK to call the Nested_Call from within the callback,
    --  since local variables exist until after returning from the
    --  callback, up until returning from the Execute call below.
    Callback (Item);

    Put_Line ("Old Total=" & T'Image (Total) &
              ", called" & Natural'Image (Call_Count) & " times");

    Call_Into.all := null;
    --  Ensure Client can't call Nested_Call once we've returned from the
    --  generic
end Execute;

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

with Execute;

procedure Old_Client (Item : Natural) is

    type Call_Into_Type is access procedure (Number : Natural);
    --  Needed to instantiate generic

    Call_Into : aliased Call_Into_Type := null;
    --  Will be non-null within scope of callback P below

    procedure My_Execute is new
      Execute (T => Natural,
               Call_Into_Type => Call_Into_Type);

    procedure P (Number : Natural) with Pre => Call_Into /= null;
    --  Note Ada 2012 Precondition

    procedure P (Number : Natural) is
    begin
       Call_Into (Number);
       --  Call nested subprogram in the generic

       --  Do some recursion, just to make it more interesting
       if Number > 0 then
          P (Number - 1);
       end if;
    end P;

begin
    My_Execute
      (Item  => Item,
       Call_Into => Call_Into'Access,
       Callback => P'Access);
    --  Execute sets Call_Into above, then calls callback,
    --  which in turn calls back "into" the generic
    --  calling a nested procedure
end Old_Client;

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

4. And *How* does the new implementation look using the
'Unchecked_Access instead of 'Unrestricted_Access?

generic
    type T is range <>;
    --  Note: No need to pass in access to subprogram type,
    --  Instead, generic package provides access to object type
    --  below
package Pak is

    --  Interface to call into a generic
    type Dispatcher_Type is interface;

    procedure Call_Into
      (Dispatcher : Dispatcher_Type;
       Item : T) is abstract;

    --  Need class-wide access type for Dispatcher, so we
    --  can dispatch calls to nested derived type declared
    --  inside the generic.
    type Dispatcher_Access is access all Dispatcher_Type'Class;

    procedure Execute
      (Item : T;
       Dispatcher : not null access Dispatcher_Access;
    --  Dispatcher is 'set' to an internal object of a derived type
    --  before returning from this call, and before Callback
    --  is called. (Which is why this is an access parameter,
    --  instead of an in out parameter.) One cannot count on
    --  an "in out" parameter being set *before* returning from this
    --  call.

       Callback : not null access procedure (Item : T))
    --  Client Callback can dispatch calls to  Dispatcher.Call_Into
      with Pre => Dispatcher.all = null,
           Post => Dispatcher.all /= null;

end Pak;

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

with Ada.Text_IO; use Ada.Text_IO;

package body Pak is

    procedure Execute
      (Item          : T;
       Dispatcher    : not null access Dispatcher_Access;
       Callback : not null access procedure (Item : T))
    is

       Call_Count : Natural := 0;
       Total : T := 0;

       type Internal_Dispatcher_Type is
         new Dispatcher_Type with null record;

       overriding procedure Call_Into
         (Dispatcher : Internal_Dispatcher_Type;
          Item       : T) is
       --  Note: This nested call can view declarations hidden
       --  in the generic
       begin
          Call_Count := Call_Count + 1; --  Do Stuff
          Total := Total + Item;
       end Call_Into;

       --  Local object to use for dispatching to nested procedure
       Internal_Dispatcher : aliased Internal_Dispatcher_Type;

    begin

       --  Allow client to dispatch to nested procedure
       --  Note: Unchecked_Access is portable
       Dispatcher.all := Internal_Dispatcher'Unchecked_Access;

       --  Note: It is OK to dispatch calls to the nested call, Call_Into
       --  from within the callback, since local variables exist until
       --  after returning from the callback, up until returning from the
       --  Execute call below.
       Callback (Item);

       Put_Line ("New Total=" & T'Image (Total) &
                 ", called" & Natural'Image (Call_Count) & " times");

       Dispatcher.all := null;
       --  Ensure Client can't call nested procedure once we've
       --  returned from this call

    end Execute;
end Pak;

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

with Pak;

procedure New_Client (Item : Natural) is
    package My_Pak is new Pak (T => Natural);
    use type My_Pak.Dispatcher_Access;

    Dispatcher : aliased My_Pak.Dispatcher_Access := null;

    procedure P (Number : Natural) with Pre => Dispatcher /= null;

    procedure P (Number : Natural) is
    begin
       --  Call into nested procedure in generic
       Dispatcher.Call_Into (Number);

       --  Do some recursion, just to make it more interesting
       if Number  > 0 then
          P (Number - 1);
       end if;
    end P;

begin
    My_Pak.Execute
      (Item  => Item,
       Dispatcher => Dispatcher'Access,
       Callback => P'Access);
    --  Execute calls callback, which in turn calls back "into" the
    --  generic Calling a nested procedure
end New_Client;

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

-- Test driver for both old and new approaches

with Old_Client;
with New_Client;

procedure Test_Call_Into is
begin
    Old_Client (10);
    New_Client (10);
end Test_Call_Into;

Outputs =>

    Old Total= 55, called 11 times
    New Total= 55, called 11 times

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

5. OK, so to summarize, what where the differences between
the two approaches?

   - Old approach uses Generic Procedure, instead of Generic Package
   - Generic Procedure needs a formal parameter to define the access to
     subprogram type, whereas Generic package defines an access to
     interface object that can be used to dispatch to a derived type.
   - Old approach passes access to subprogram access into the generic,
     new approach passes access to interface access into the generic
   - Old approach uses 'Unrestricted_Access to set the nested
     procedure to the client visible access-to-subprogram object,
     whereas new approach uses 'Unchecked_Access to set the nested
     derived object with the desired nested primitive subprogram to the
     client visible access-to-classwide type object.
   - Old approach is non-portable, new approach is portable

When you compare the two approaches, they really aren't that different.
I don't find the second approach any less appealing than the first
approach, from an elegance standpoint.


I hope this helps clarify.

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

From: Randy Brukardt
Sent: Monday, March  5, 2012  5:57 PM

...
> However, I believe that this can be solved now that the language
> supports aspect clauses.  I'm proposing that an Allow_Unchecked aspect
> be defined for access-to-subprogram types, and that P'Unchecked_Access
> be allowed only if the Allow_Unchecked aspect is True for the expected
> type.  Thus, the additional overhead needed to allow
> P'Unchecked_Access would be present only for those types that have the
> aspect specified.  To prevent values of types without this aspect
> (other than anonymous access-to-subprogram parameters) from
> representing "unsafe" nested accesses, the rules would prevent type
> conversions from types with the aspect to types without the aspect,
> and they would prohibit P.all'Access where P's type has the
> Allow_Unchecked aspect.

This would be "necessary", but insufficient. Essentially, such access types
would have to have the same semantics as anonymous access-to-subprogram. These
"named anonymous access types" also could not be compatible with foreign
languages (such as C) and there also would need to be some sort of rules for
generic matching.

In addition, if we're continuing to support generic sharing in the language (*),
we'd need some rules to prevent taking  'Unchecked_Access in a generic body in
the same way we already have rules preventing 'Access. (The problems with
finding the data for a generic instance don't have anything to do with "static
links".)

Anyway, I don't think we would want to invent a totally new semantics when we
already have essentially the correct semantics for anonymous
access-to-subprogram. Your proposal would be better if you unified the existing
anonymous access-to-subprogram with your new aspect.

(*) I could imagine that Ada 2020 would totally abandon the idea that any
generic sharing can take place. That would take the form of relaxing the
contract model enough to detect errors in generic bodies (rather than raising
Program_Error) and probably would eliminate some other anomalies as well. But I
would hope that we'd do that based on its own merits rather than any association
with something as "old-school" as access-to-subprogram. [After all, I don't
think packages should contain any visible access types in their specifications,
and that surely includes access-to-subprogram types. So I find the idea of
expanding the use of such types unappealing.]

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

From: Adam Beneschan
Sent: Wednesday, March 21, 2012  8:42 PM

> ...
> > However, I believe that this can be solved now that the language
> > supports aspect clauses.  I'm proposing that an Allow_Unchecked
> > aspect be defined for access-to-subprogram types, and that
> > P'Unchecked_Access be allowed only if the Allow_Unchecked aspect is
> > True for the expected type.  Thus, the additional overhead needed to
> > allow P'Unchecked_Access would be present only for those types that
> > have the aspect specified.  To prevent values of types without this
> > aspect (other than anonymous access-to-subprogram parameters) from
> > representing "unsafe" nested accesses, the rules would prevent type
> > conversions from types with the aspect to types without the aspect,
> > and they would prohibit P.all'Access where P's type has the
> > Allow_Unchecked aspect.
>
> This would be "necessary", but insufficient. Essentially, such access
> types would have to have the same semantics as anonymous access-to-subprogram.

I'm not sure what you're saying.  Do you mean that Allow_Unchecked access types
would have the same semantics as anonymous access-to-subprogram types and are
therefore unnecessary, or that there are some rules or semantics involving
anonymous access-to-subprogram types that would also have to be added for access
types with Allow_Unchecked aspects?  If it's the latter, I don't see what those
rules are; could you elaborate?

> These "named anonymous access types" also could not be compatible with
> foreign languages (such as C)

I don't see why not.  It seems like it should be possible to say something like

   type Gtk_Something_Handler is access procedure (...parameters...)
     with Allow_Unchecked, Convention => C;

and then if an implementation couldn't allow 'Unchecked_Access on arbitrary
subprograms because it couldn't implement the access type as a simple address,
it would reject the type declaration.  I think the advantage is that this could
be done within the language; right now, implementations have had to define their
own language-defined attribute (GNAT's and ICC's 'Unrestricted_Access) which
leads to non-portability.


> and there also would need to be some sort of rules for generic
> matching.

I didn't think of that, but it seems possible.  I'd have to think a little more
about what those rules would be.


> In addition, if we're continuing to support generic sharing in the
> language (*), we'd need some rules to prevent taking
> 'Unchecked_Access in a generic body in the same way we already have
> rules preventing 'Access. (The problems with finding the data for a
> generic instance don't have anything to do with "static links".)

Yes, that's a complication.

> Anyway, I don't think we would want to invent a totally new semantics
> when we already have essentially the correct semantics for anonymous
> access-to-subprogram. Your proposal would be better if you unified the
> existing anonymous access-to-subprogram with your new aspect.

The sort of problem I'm trying to solve (that doesn't involve foreign-language
interfaces) is a case where a subprogram or task S contains a nested subprogram
NS, and S somehow has to hand NS's access off to some other program module, and
will get it back later from that module.  The other module treats the record
containing the subprogram access as an opaque box that it isn't allowed to open.

For example, consider a case where there are multiple client task types that can
call a routine to submit a "job" to a scheduler.  The job record contains data
that the scheduler doesn't know anything about.  But the scheduler will then
decide, somehow, when it's time to perform each job, and at that point the
scheduler will call back the appropriate task and hand the job record back to
the task.  A reasonable (I think) way to design this is to define a synchronized
interface that the client task types will be derived from:

    type Job_Type is tagged null record;

    type Client_Type is synchronized interface;
    procedure Run_Job (C : Client_Type; Job : Job_Type'Class) is abstract;
    procedure No_More_Jobs (C : Client_Type) is abstract;

    type Client_Acc is access all Client_Type'Class;

A Submit procedure is called by each task to submit jobs for
scheduling:

    procedure Submit (C   : Client_Acc;
                      Job : Job_Type'Class);

This would, presumably, save C and Job in some kind of data structure.
(In real life, there may be other parameters having to do with the job's
priority, perhaps.)  Later, when the scheduler decides it's time to run the job,
it calls

    C.Run_Job (Job);

The client task might look something like

    type My_Job_1 is new Job_Type with record
        N : Integer;
    end record;

    task type Task1 is new Client_Type with
        entry Who_Am_I (Client : Client_Acc);
        entry Run_Job (Job : Job_Type'Class);
        entry No_More_Jobs;
    end Task1;

    task body Task1 is
        procedure Perform_Job (Job : My_Job_1) is
        begin
            ... do the work
        end Perform_Job;

        Done : Boolean;
        Me   : Client_Acc;
    begin
        accept Who_Am_I (Client : Client_Acc) do
            Me := Client;
        end Who_Am_I;
        Submit (Me, My_Job_1' (N => 111));
        Submit (Me, My_Job_1' (N => 222));
        Submit (Me, My_Job_1' (N => 333));

        Done := False;
        while not Done loop
            select
                accept Run_Job (Job : Job_Type'Class) do
                    Perform_Job (My_Job_1 (Job));
                end Run_Job;
            or
                accept No_More_Jobs do
                    Done := True;
                end No_More_Jobs;
            end select;
        end loop;
    end Task1;

For each actual Task1 object T, the master that creates T would have to call
T.Who_Am_I (T'Access) so that T would know what access value to pass to Submit.
(Alternatively, the Submit calls could look like

    Submit (Task1'Unchecked_Access, My_Job_1' (N => 111));

When I wrote this, all the task objects were at library level, and I was trying
to use as little 'Unchecked as possible.)

I tried this and it worked fine, although I was a little annoyed that
My_Job_1 had to be declared at library level, not inside Task1.  (This seems
like a flaw, but I'm not sure what the answer is.)

This method breaks down, though, when the job record contains an
access-to-subprogram that could hold the 'Access of a nested subprogram:

    type Job_Procedure is access procedure;
    type My_Job_2 is new Job_Type with record
        Proc : Job_Procedure;
    end record;

    task body Task1 is
        procedure Do_Job_1 is  -- let's say this access other
                               -- variables inside Task1 and thus
                               -- must be nested
        begin
            ... do the work
        end Do_Job_1;

        procedure Do_Job_2 is ...
        procedure Do_Job_3 is ...

        Done : Boolean;
        Me   : Client_Acc;
    begin
        accept Who_Am_I (Client : Client_Acc) do
            Me := Client;
        end Who_Am_I;
        Submit (Me, My_Job_2' (Proc => Do_Job_1'access));
        Submit (Me, My_Job_2' (Proc => Do_Job_2'access));
        Submit (Me, My_Job_2' (Proc => Do_Job_3'access));

        Done := False;
        while not Done loop
            select
                accept Run_Job (Job : Job_Type'Class) do
                    My_Job_2 (Job).Proc.all;
                end Run_Job;
            or
                accept No_More_Jobs do
                    Done := True;
                end No_More_Jobs;
            end select;
        end loop;
    end Task1;

This is the kind of case where it would be nice to declare Job_Procedure with
Allow_Unchecked, and then 'Unchecked_Access could be used in the Submit calls.

After I submitted my earlier proposal, though, I figured out that there's
another way to solve this particular problem:

    type Job_Procedure_Holder is tagged null record;
    type My_Job_2 is new Job_Type with record
        Proc_Holder : access Job_Procedure_Holder'Class;
    end record;

and then in Task1:

    type Job_Procedure is access procedure;
    type My_Holder is new Job_Procedure_Holder with record
        Op : Job_Procedure;
    end record;

    Job1_Holder : aliased My_Holder := (Op => Do_Job_1'Access);
    Job2_Holder : aliased My_Holder := (Op => Do_Job_2'Access);
    Job3_Holder : aliased My_Holder := (Op => Do_Job_3'Access);
        -- these are OK since Job_Procedure is now defined inside
        -- Task1

    Submit (Me, My_Job_2' (Proc_Holder => Job1_Holder'Unchecked_Access));
    Submit (Me, My_Job_2' (Proc_Holder => Job2_Holder'Unchecked_Access));
    Submit (Me, My_Job_2' (Proc_Holder => Job3_Holder'Unchecked_Access));

so that an 'Unchecked_Access on an object can take the place of the disallowed
'Unchecked_Access on a subprogram.

This might be somewhat similar to Brad's solution (except with the procedure
access explicit instead of in a dispatch vector).  I'm not sure; I'd have to
study it more.

Anyway, since there's a workaround for this particular problem that isn't too
horrible, perhaps that reduces the worth of my proposal. But I hope this
explains the sort of problem I was trying to solve. Plus, it still seems less
than ideal that in an interfacing-to-C case that programs having to rely on an
implementation-defined attribute, and that there's no general language-defined
answer.

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

From: Randy Brukardt
Sent: Wednesday, March 21, 2012  10:09 PM

> > This would be "necessary", but insufficient. Essentially, such
> > access types would have to have the same semantics as anonymous
access-to-subprogram.
>
> I'm not sure what you're saying.  Do you mean that Allow_Unchecked
> access types would have the same semantics as anonymous
> access-to-subprogram types and are therefore unnecessary,

No (see below).

 or that there are some rules or semantics
> involving anonymous access-to-subprogram types that would also have to
> be added for access types with Allow_Unchecked aspects?  If it's the
> latter, I don't see what those rules are; could you elaborate?

Pretty much all of them; in particular, we don't allow conversions between
anonymous access-to-subprogram and regular access-to-subprogram. The
representation of the types would be the same, the requirements to successfully
create an 'Unchecked_Access would be the same as 'Access for anonymous
access-to-subprogram, etc.

We noticed this problem back in the Ada 2005 days, but didn't pursue it mainly
because simply the thought of a named anonymous access type caused mental
breakdowns. :-) We'd need a better name for the concept (maybe "generalized
access-to-subprogram", not sure) and then both anonymous access-to-subprogram
and this new kind of type would have that category. Note that we'd need
accessibility rules for 'Access (not everyone would want to use [or need]
'Unchecked_Access). Best to use all of the rules already existing rather than
invent a new bunch.

> > These "named anonymous access types" also could not be compatible
> > with foreign languages (such as C)
>
> I don't see why not.  It seems like it should be possible to say
> something like
>
>    type Gtk_Something_Handler is access procedure (...parameters...)
>      with Allow_Unchecked, Convention => C;
>
> and then if an implementation couldn't allow 'Unchecked_Access on
> arbitrary subprograms because it couldn't implement the access type as
> a simple address, it would reject the type declaration.  I think the
> advantage is that this could be done within the language; right now,
> implementations have had to define their own language-defined
> attribute (GNAT's and ICC's 'Unrestricted_Access) which leads to
> non-portability.

But all implementations would have to reject this; the entire point is that the
pointer includes a static link or display such that a nested routine can be
successfully called (presuming the scope in question hasn't been exited). That
could not be compatible with C (which has no need for such links/display).

(Well, you could use self-modifying code to do this, but anyone writing that
these days ought to be lynched. The Ada Standard surely should not be
encouraging anyone to do that.)

Anyone claiming to do this within the language (or outside) with a "bare
address" is simply lying. (Or I completely do not understand your proposal...)

> > and there also would need to be some sort of rules for generic
> > matching.
>
> I didn't think of that, but it seems possible.  I'd have to think a
> little more about what those rules would be.

Assume-the-best in the spec, assume-the-worst in the body is usually the first
cut. But that would prevent using 'Unchecked_Access in generics, which I doubt
you want. Thus, you'd need to allow your aspect on formal access types and then
some sort of matching rule is needed. Definitely a complication.

> > In addition, if we're continuing to support generic sharing in the
> > language (*), we'd need some rules to prevent taking
> > 'Unchecked_Access in a generic body in the same way we already have
> > rules preventing 'Access. (The problems with finding the data for a
> > generic instance don't have anything to do with "static links".)
>
> Yes, that's a complication.
>
> > Anyway, I don't think we would want to invent a totally new
> > semantics when we already have essentially the correct semantics for
> > anonymous access-to-subprogram. Your proposal would be better if you
> > unified the existing anonymous access-to-subprogram with your new aspect.
>
> The sort of problem I'm trying to solve (that doesn't involve
> foreign-language interfaces) is a case where a subprogram or task S
> contains a nested subprogram NS, and S somehow has to hand NS's access
> off to some other program module, and will get it back later from that
> module.  The other module treats the record containing the subprogram
> access as an opaque box that it isn't allowed to open.

I don't see how this differs substantially from the
anonymous-access-to-subprogram that exists. The key is that you need to include
extra information in the pointer in order to allow calling a nested subprogram
(a static link, part of a display, etc.) That's the same thing you need to do
for anonymous access-to-subprogram. You can't convert these to "regular"
access-to-subprogram because removing that information is not at all safe (in
Janus/Ada, no calls other than to a subprogram declared at library-level would
work in that case), and you can't convert back because typically you don't have
the same information at the point of conversion as you did when the
'Unchecked_Access was taken (so you can't construct the correct extra
information).

We handled that with "infinite" accessibility for such types. Probably it would
be better to use explicit rules, but in any case, you can't allow any such
conversions. I don't think this would be a problem (why would you want to mix
such types?).

I looked at your example but didn't see anything in it which changed my basic
point, and it was too long to understand very well.

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

From: Bob Duff
Sent: Friday, March 23, 2012  1:36 PM

> >    type Gtk_Something_Handler is access procedure (...parameters...)
> >      with Allow_Unchecked, Convention => C;
> >
> > and then if an implementation couldn't allow 'Unchecked_Access on
> > arbitrary subprograms because it couldn't implement the access type
> > as a simple address, it would reject the type declaration.  I think
> > the advantage is that this could be done within the language; right
> > now, implementations have had to define their own language-defined
> > attribute (GNAT's and ICC's 'Unrestricted_Access) which leads to
> > non-portability.
>
> But all implementations would have to reject this; ...

GNAT would not have to reject this.  It is already supported via
P'Unrestricted_Access.  The mechanism is to build a trampoline on the stack,
which is some code that knows where the static link is, and passes it along to
P.  P'Unrestricted_Access returns the address of the trampoline.

I was unaware that ICC supported Unrestricted_Access.  Does it use trampolines?

We got rid of almost all usage of trampolines in GNAT a while back, but this
case (passing a nested procedure to C) still uses trampolines.

> (Well, you could use self-modifying code to do this, but anyone
> writing that these days ought to be lynched. The Ada Standard surely
> should not be encouraging anyone to thinkdo that.)

It's not exactly self-modifying code -- it's creating NEW code on the stack.
But yeah, it requires telling the OS that that part of the stack is executable,
if it's enforcing no-execute permissions, and it's definitely a questionable
practise.

The general thing with interface to foreign languages is that implementations
are allowed to support whatever they like -- there's no "encouragement".  That
is, if you can figure out what it means to export (say) a generic package to C
code, then you're free to implement that.

> Anyone claiming to do this within the language (or outside) with a
> "bare address" is simply lying.

The address of a trampoline is just a single (bare?) address pointing to some
code.  Honest.  ;-)

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

From: Adam Beneschan
Sent: Friday, March 23, 2012  1:56 PM

> I was unaware that ICC supported Unrestricted_Access.  Does it use
> trampolines?

No.  We do allow 'Unrestricted_Access on nested subprograms when the target
access type is not a single address.  We've also added some features so that a
nested subprogram's 'Unrestricted_Access could be passed as a single address, if
the subprogram is declared with the C convention, which means it doesn't require
a static link (and if the nested subprogram accesses objects that would require
a static link, the compiler rejects it).  We've also recently added ways that
local variables that a nested subprogram could access can be moved into a global
data area if the programmer can ensure that the owning subprogram won't be
called reentrantly.

But I think either trampolines or some other workaround like we've done is
necessary to get GtkAda to compile without a major rewrite.

By the way, when we do reject 'Unrestricted_Access (because the access type is a
single address and it won't work), we reject the actual 'Unrestricted_Access (or
cause a Program_Error raise in a generic body); we don't reject the declaration
of the access type.  This is somewhat contrary to what I wrote in the quoted
part above.

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

From: Randy Brukardt
Sent: Friday, March 23, 2012  2:12 PM

...
> > (Well, you could use self-modifying code to do this, but anyone
> > writing that these days ought to be lynched. The Ada Standard surely
> > should not be encouraging anyone to thinkdo that.)
>
> It's not exactly self-modifying code -- it's creating NEW code on the
> stack.  But yeah, it requires telling the OS that that part of the
> stack is executable, if it's enforcing no-execute permissions, and
> it's definitely a questionable practise.

(Sorry, I was using "self-modifying code" to mean any code created and
immediately executed by a program. I don't think it matters *where* that code
lives.)

It's more than questionable. I'm amazed that your safety-critical customers let
you get away with such practices: such code is virtually impossible to verify,
extremely difficult to debug, and dangerous (in that it opens additional avenues
for attack).

I would not want there to be anything in the Ada standard which even remotely
suggested that doing this is a good idea. Suggesting that compilers ought to be
able to provide an interface to C for a "generalized access-to-subprogram type"
is certainly within that category.

If an implementer wants to depart from good sense (for whatever reason),
obviously the Ada Standard isn't going to have any impact on that. But let's
restrict the discussion here to things that at least can be implemented without
resorting to questionable practices.

[Aside: I think that GNAT's support for such things causes real damage for the
safety of Ada code, as it encourages customers to ask/demand other vendors to
support similar features. Just because you can implement something does not mean
that you should. I realize that there is not much that can be done about this
now (GNAT is not suddenly going to eliminate this from their compiler), but it
is a significant frustration for me.]

> The general thing with interface to foreign languages is that
> implementations are allowed to support whatever they like
> -- there's no "encouragement".  That is, if you can figure out what it
> means to export (say) a generic package to C code, then you're free to
> implement that.
>
> > Anyone claiming to do this within the language (or outside) with a
> > "bare address" is simply lying.
>
> The address of a trampoline is just a single (bare?) address pointing
> to some code.  Honest.  ;-)

That's not really a "bare address"; it's the address of a thunk (in the original
sense of the term); the original routine is nowhere to be found. If the callee
is expecting the actual code to be at that location, they're going to be out of
luck.

Anyway, it's obvious that you can implement anything with an interpreter of some
sort. So what? We're talking about compilers and code that can be verified for
Ada; it's unclear that there is any value to any other scheme for a language
like Ada (if you want a real dynamic language, something like Python is a better
bet). At worst, I'm guilty of too narrow of a view.

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

From: Tucker Taft
Sent: Friday, March 23, 2012  2:34 PM

I am against standardizing subp'Unchecked_Access.
I believe that inside of Ada you can relatively easily support what you need
using 'Unchecked_Access producing an access-to-class-wide value.

When interfacing to C I would say you should be declaring your exported
C-convention routine at the library level.  That doesn't seem to be a big
imposition, if it isn't making up-level references anyway.  Probably combining
these would also work, where you write a callback that has convention C, and
then in the callback it calls through an access-to-class-wide value that has
been created using 'Unchecked_Access.

I may not understand all of the subtleties, but I agree with Randy that there is
not sufficient benefit to justify standardizing this sort of thing.

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

From: Bob Duff
Sent: Friday, March 23, 2012  2:47 PM

> It's more than questionable. I'm amazed that your safety-critical
> customers let you get away with such practices: such code is virtually
> impossible to verify, extremely difficult to debug, and dangerous (in
> that it opens additional avenues for attack).

Anybody who doesn't want to use trampolines can use the relevant Restriction or
command-line switch, and get a warning or error (their choice) if they
accidentally use one of the constructs that causes trampolines to be generated.

I think they're more of an issue in a security-critical context, rather than a
safety-critical one.  For example, in a stand-alone medical device, not
connected to the internet, there's no problem using trampolines, other than the
fact that they're inefficient on modern hardware (because you have to flush the
instruction cache, or similar).

Anyway, as I said, it's easy to avoid trampolines in GNAT; there are only a few
obscure cases where they are still used. (They used to be used all over the
place, and that was a real pain.)

> I would not want there to be anything in the Ada standard which even
> remotely suggested that doing this is a good idea. Suggesting that
> compilers ought to be able to provide an interface to C for a
> "generalized access-to-subprogram type" is certainly within that category.

I am not suggesting that the RM should "suggest that compilers...".
Just that compilers should be allowed to do "...".
Annex B just wouldn't make these newly-suggested things "C compatible" or "C
eligible" or whatever (I forget the terminology).

> [Aside: I think that GNAT's support for such things causes real damage
> for the safety of Ada code, as it encourages customers to ask/demand
> other vendors to support similar features. Just because you can
> implement something does not mean that you should. I realize that
> there is not much that can be done about this now (GNAT is not
> suddenly going to eliminate this from their compiler), but it is a
> significant frustration for me.]

GNAT inherited trampolines from the gcc C compiler.
The gcc dialect of C supports nested functions!

> > The address of a trampoline is just a single (bare?) address
> > pointing to some code.  Honest.  ;-)
>
> That's not really a "bare address";

OK, I don't really know what "bare" means in this context.
An address is always the address of _something_.

>... it's the address of a thunk (in the  original sense of the term);
>the original routine is nowhere to be found.

Heh?  The original routine is still there.  That is, if you say
P'Unrestricted_Access (and pass that to C code), the usual code for P exists,
and the trampoline simply loads the static link into a register, and jumps to P.
Note that trampolines are not used if P is at library level.

>... If
> the callee is expecting the actual code to be at that location,
>they're  going to be out of luck.

The caller is expecting that if they do a "call" to that location, they'll end
up in P, with a valid static link.  Nobody cares what actual code is at the
addressed location.  On some machines, for example, there's an extra indirection
for calls in general.

> Anyway, it's obvious that you can implement anything with an
> interpreter of some sort. So what? We're talking about compilers and
> code that can be verified for Ada; it's unclear that there is any
> value to any other scheme for a language like Ada (if you want a real
> dynamic language, something like Python is a better bet). At worst, I'm guilty
>  of too narrow of a view.

I don't think interpreters are relevant.  We're talking about interfacing to C,
under the assumption that the C implementation isn't under control of the Ada
implementer, and typical C implementations are not interpreters.  (In the case
of GNAT, the C implementation IS under our control, but we don't wish to modify
the way it uses trampolines!)

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

From: Adam Beneschan
Sent: Friday, March 23, 2012  2:47 PM

> > It's not exactly self-modifying code -- it's creating NEW code on
> > the stack.  But yeah, it requires telling the OS that that part of
> > the stack is executable, if it's enforcing no-execute permissions,
> > and it's definitely a questionable practise.
>
> (Sorry, I was using "self-modifying code" to mean any code created and
> immediately executed by a program. I don't think it matters *where*
> that code lives.)
>
> It's more than questionable. I'm amazed that your safety-critical
> customers let you get away with such practices: such code is virtually
> impossible to verify, extremely difficult to debug, and dangerous (in
> that it opens additional avenues for attack).

Off the top of my head, I'd say that *any* program that includes C code already
has those properties.  Not that I'm a language bigot, or anything...  :) But if
the only use of trampolines would be to facilitate certain types of interfaces
to C code, as appears to be the case in later versions of GNAT, then it's not
clear to me that they'd make anything worse from a safety standpoint.

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

From: Bob Duff
Sent: Friday, March 23, 2012  2:49 PM

> I am against standardizing subp'Unchecked_Access.

Me, too.

That is, please don't take my discussion of an obscure point (how/if to
implement subp'Unchecked_Access in the interface-to-C case) as advocating for
the feature in general.

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

From: Randy Brukardt
Sent: Friday, March 23, 2012  3:13 PM

I'm a bit surprised to read this. I think I have better reasons to hate this
concept than most others, but I have to say that I'm ambivalent.

Specifically, I know that it is next-to-impossible to do anything with 'Access
on objects; you almost always have to use 'Unchecked_Access. The situation is
somewhat better for subprograms, but the same sorts of problems come to bear
there. There are workarounds (as there was to not having 'Access in Ada 83), but
they're complicated and don't necessarily make the program any safer or easier
to read.

In addition, Ada already has this feature in anonymous access-to-subprograms.
But even when we invented that, there were some of us ("us" here means the ARG;
I don't remember which group I was in) that felt that we needed a named version.
We couldn't find the right terminology and the idea was dropped (not without a
lot of discussion). But that need hasn't gone away; some of us would like to ban
all anonymous types and this is one feature that cannot be implemented any other
way. That would be good to fix.

And note that 'Unchecked_Access is not always necessary with such a type; some
users might be able to use 'Access just fine with this capability.

On the other hand, a named anonymous access-to-subprogram type (I've been
calling it a "generalized" type to avoid the insanity of "named anonymous")
would need the same restrictions as anonymous ones do: no conversions (other
than to other generalized access-to-subprograms - maybe), don't match generic
formal access-to-subprogram, unlikely to support as foreign interface, and
probably others. It's not clear if these restrictions would allow the sorts of
uses that users are expecting. If not, the concept would essentially be
unimplementable (and it would be a lot easier to decide my position!)

In any case, not the most important thing to deal with in Ada 2020, but it seems
worthy of discussion.

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

From: Bob Duff
Sent: Friday, March 23, 2012  4:12 PM

> I'm a bit surprised to read this. I think I have better reasons to
> hate this concept than most others, but I have to say that I'm ambivalent.

I don't hate this feature.  I actually think it's pretty reasonable.
But I think we need to raise the bar for Ada 2020.  Ada 2012 already has a lot
of baggage, and we should be rather reluctant to add new stuff.

In this case, the existing anonymous access-to-subp parameters cover almost all
of the cases where you want 'Access of a nested subprogram.  For the other
cases, an access-to-class-wide is usually a sufficient workaround.

> Specifically, I know that it is next-to-impossible to do anything with
> 'Access on objects; you almost always have to use 'Unchecked_Access.

I think "almost always" is an exaggeration.  For example, in the GNAT sources
(compiler and runtimes), there are 2311 occurrences of 'Access, and 236
occurrences of 'Unchecked_Access.  In PolyORB, the numbers are 1280 and 90.

But you're basically right -- the reasons why you often need 'Unchecked_Access
on objects can apply to subprograms, too.

>...some of us would like to ban all anonymous types ...

I have no objection to the concept of anonymous types (or other anonymous
things, for that matter -- e.g. anonymous procedures (lambdas)).

But I don't like the fact that anonymous types have all sorts of magical
properties.

And I don't like the fact that Ada is inconsistent (you can have an anonymous
array type, but not an anonymous record type).

We're not going to fix those things, of course!

> In any case, not the most important thing to deal with in Ada 2020,
> but it seems worthy of discussion.

Yes, it's worthy of discussion.

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

From: Randy Brukardt
Sent: Friday, March 23, 2012  3:21 PM

> >... If
> > the callee is expecting the actual code to be at that location,
> >they're  going to be out of luck.
>
> The caller is expecting that if they do a "call" to that location,
> they'll end up in P, with a valid static link.
> Nobody cares what actual code is at the addressed location.
> On some machines, for example, there's an extra indirection for calls
> in general.

"Nobody cares" is way too strong. I know a lot of development tools (profilers,
debuggers, etc.) that assume the code is at the address (there isn't much else
that they can do, after all), and they aren't going to work if that's not true.
Probably my attitudes are colored too much by these admittedly unusual
circumstances, but I think that techniques that don't work with such tools are
best avoided. [Truth-in-advertising disclosure: Janus/Ada fails this test, too,
as all interface calls go through a translation wrapper, which converts the
parameter representation and order appropriately. These were intended to be
inlined, but since we never implemented automatic inlining, they tend to cause
(external) tools to fail.]

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

From: Bob Duff
Sent: Friday, March 23, 2012  4:18 PM

> > The caller is expecting that if they do a "call" to that location,
> > they'll end up in P, with a valid static link.
> > Nobody cares what actual code is at the addressed location.
> > On some machines, for example, there's an extra indirection for
> > calls in general.
>
> "Nobody cares" is way too strong.

OK, change that to "programmers don't care".

>...I know a lot of development tools
> (profilers, debuggers, etc.) that assume the code is at the address
>(there  isn't much else that they can do, after all), and they aren't
>going to work  if that's not true.

Sure, profilers and debuggers need to understand the code generated by the
compiler.  But it's wrong to think that such tools must assume "the code is at
the address".  AdaCore supports such tools, and they work just fine with
trampolines, and with targets where the standard ABI involves an extra
indirection for calls in general.

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

From: Randy Brukardt
Sent: Friday, March 23, 2012  4:57 PM

> > > The caller is expecting that if they do a "call" to that location,
> > > they'll end up in P, with a valid static link.
> > > Nobody cares what actual code is at the addressed location.
> > > On some machines, for example, there's an extra indirection for
> > > calls in general.
> >
> > "Nobody cares" is way too strong.
>
> OK, change that to "programmers don't care".

Programmers care if tools work.

> >...I know a lot of development tools
> > (profilers, debuggers, etc.) that assume the code is at the address
> >(there  isn't much else that they can do, after all), and
> they aren't
> >going to work  if that's not true.
>
> Sure, profilers and debuggers need to understand the code generated by
> the compiler. But it's wrong to think that such tools must assume "the
> code is at the address".  AdaCore supports such tools, and they work
> just fine with trampolines, and with targets where the standard ABI
> involves an extra indirection for calls in general.

A profiler (in particular) needs to know where the *real* subprogram is, or it
will give you timings only for the trampoline or wrapper. The tool will "work",
but the results it gives will be useless.

In general, these tools are a "black-box" to us smaller vendors. We can only use
what they already know how to understand (and typically, far less than that).
Moreover, we're not in a position to test many of these tools ourselves; users
are just going to expect them to work.

The list of Ada-aware tools is very short, and the vast majority of those think
Ada is the same as GNAT. The only tools we can really assume to work are those
we write ourselves, but of course that's not really practical.

Some tools claim to allow configuration or other such ways to support "other"
mechanisms. But my experience is that such mechanisms almost never work unless
some other compiler (typically the C compiler) already uses it. (Indeed, I don't
recall any case where such a mechanism *did* work on Windows OR Unix.) So,
unless you're important enough to forge some sort of business relationship with
the other tools vendor, you're going to have to ignore everything fancy -- or
build your own tools.

The net effect is that a compiler (at least from a smaller vendor) really does
have to match the *exact* expected calling convention on a target, or forget
using any of the tools. And that includes the meaning of pointers to subprograms
and the like. Moreover, you almost always have to find out what's *really*
expected from reverse engineering. (The documentation, if any, never reflects
the real reality.) And that probably goes as far as code placement.

Of course, if you actually follow those things, you'll end up eliminating
virtually any reason for anyone to use your compiler. So, personally, I've given
up on third-party anything. It's just too hard to make them work usefully; you
can build your own tool with the same amount of effort (and you won't have to
worry about the next release breaking everything you've done).

I think we've gotten way off topic here, so I'll try to give this a rest.

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

From: Brad Moore
Sent: Friday, October 25, 2013  10:04 AM

In further support of this AI, I have come across a stronger case.

A useful construction is to be able to pass an array of access to nested
parameterless procedures into another subprogram. Such a construction can be
used to execute subprograms recursively in parallel for example.

Using GNAT, there are three (possibly more, but if so, non-obvious) ways this
can be done.

1) Pass an array of Unchecked_Access to class wide tagged types objects
2) Pass an array of System.Address of access to subprogams, and use
   Unchecked_Conversion to convert the addresses back into access to subprograms
   so that they can be called.
3) Pass an array of Unrestricted_Access to subprograms.

Option 1 is portable, but it has a lot of overhead and is very slow, and
   considerably more awkward to write. (about 50-60 times slower than option 3
   in my testing) Tagged type objects need to be derived and declared. Tagged
   types have dispatching tables, add an additional parameter for primitives,
   have a tag, etc.

Option 2 is less portable, quite a bit faster, and quite a bit less awkward and
   less elegant to write. (about 5 times faster than option 1 in my testing)
   Calls to Unchecked_Conversion add overhead, and dealing with addresses is
   less safe than dealing with access to subprogram types.

Option 3 is non-portable, as it involves using Unrestricted_Access which is GNAT
   specific (about 50-60 times faster than option 1, and the least awkward to
   write

The argument I present is that if we allowed Unchecked_Access on subprograms, it
would allow a programmer to express a portable solution in a much less awkward
manner, that could execute potentially 50-60 times faster than the current
portable solution.

My test program follows if that helps illustrate.

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

with System;

package Block_Pkg is

   --  Portable but very slow solution:
   --  Execute an array of tagged type subprograms

   type Tagged_Subprogram is interface;
   procedure Process (Item : Tagged_Subprogram) is abstract;

   type Tagged_Subprogram_List is array (Positive range <>) of
     not null access Tagged_Subprogram'Class;

   procedure Execute_Tagged_Subprogram_List (List : Tagged_Subprogram_List);

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

   --  Less portable but medium performance solution: (about 5 times faster)
   --  Execute an array of address to access to subprogram

   type Subprogram_Address_List is array (Positive range <>) of System.Address;

   procedure Execute_Subprogram_Address_List (List : Subprogram_Address_List);

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

   --  Non portable but fast performance solution: (about 50-60 times faster)
   --  Execute an array of access to subprogram

   type Subprogram_List is array (Positive range <>) of
     not null access procedure;

   procedure Execute_Subprogram_List (List : Subprogram_List);

end Block_Pkg;

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

with Ada.Unchecked_Conversion;

package body Block_Pkg is

   procedure Execute_Tagged_Subprogram_List (List : Tagged_Subprogram_List) is
   begin
      for I of List loop
         I.Process;
      end loop;
   end Execute_Tagged_Subprogram_List;

   procedure Execute_Subprogram_Address_List
     (List : Subprogram_Address_List)
   is
      type Access_To_Subprogram is not null access procedure;

      function Convert is new Ada.Unchecked_Conversion
        (Source => System.Address,
         Target => Access_To_Subprogram);
   begin
      for Execute of List loop
         Convert (Execute).all;
      end loop;
   end Execute_Subprogram_Address_List;

   procedure Execute_Subprogram_List (List : Subprogram_List) is
   begin
      for Execute of List loop
         Execute.all;
      end loop;
   end Execute_Subprogram_List;

end Block_Pkg;

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

with Block_Pkg;
with Ada.Command_Line;
with Ada.Real_Time;
with Ada.Text_IO;
with Ada.Float_Text_IO; use Ada; use Ada.Text_IO;

procedure Test_Access_To_Subprogram is
   N : constant Natural := Natural'Value (Ada.Command_Line.Argument (1));
   Start_Time : Real_Time.Time;
   Elapsed    : Duration;
   use type Real_Time.Time;

begin

   Portable_But_Very_Slow_And_Awkward : declare

      procedure Tagged_Subprograms (N : Natural) is

         type Cat_Block is new Block_Pkg.Tagged_Subprogram with null record;
         overriding procedure Process (Item : Cat_Block) is
            pragma Unreferenced (Item);
         begin
            Tagged_Subprograms (N - 1);
         end Process;

         type Dog_Block is new Block_Pkg.Tagged_Subprogram with null record;
         overriding procedure Process (Item : Dog_Block) is
            pragma Unreferenced (Item);
         begin
            Tagged_Subprograms (N - 1);
         end Process;

         type Horse_Block is new Block_Pkg.Tagged_Subprogram with null record;
         overriding procedure Process (Item : Horse_Block) is
            pragma Unreferenced (Item);
         begin
            Tagged_Subprograms (N - 1);
         end Process;

         type Cow_Block is new Block_Pkg.Tagged_Subprogram with null record;
         overriding procedure Process (Item : Cow_Block) is
            pragma Unreferenced (Item);
         begin
            Tagged_Subprograms (N - 1);
         end Process;

         Cat : aliased Cat_Block;
         Dog : aliased Dog_Block;
         Horse : aliased Horse_Block;
         Cow : aliased Cow_Block;

      begin -- Tagged_Subprograms

         if N = 0 then
            return;
         else
            Block_Pkg.Execute_Tagged_Subprogram_List
              ((1 => Cat'Unchecked_Access,
                2 => Dog'Unchecked_Access,
                3 => Horse'Unchecked_Access,
                4 => Cow'Unchecked_Access));
         end if;

      end Tagged_Subprograms;

   begin -- Portable_But_Very_Slow_And_Awkward

      Put ("Tagged subprograms: Elapsed=");

      Start_Time := Real_Time.Clock;

      Tagged_Subprograms (N);

      Elapsed := Real_Time.To_Duration (Real_Time.Clock - Start_Time);

      Float_Text_IO.Put (Item => Float (Elapsed),
                         Fore => 2,
                         Aft  => 2,
                         Exp  => 0);

      Put_Line (" seconds");
   end Portable_But_Very_Slow_And_Awkward;

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

   Less_Portable_But_Medium_Speed : declare

      procedure Subprogram_Addresses (N : Natural) is

         procedure Cat is
         begin
            Subprogram_Addresses (N - 1);
         end Cat;

         procedure Cow is
         begin
            Subprogram_Addresses (N - 1);
         end Cow;

         procedure Dog is
         begin
            Subprogram_Addresses (N - 1);
         end Dog;

         procedure Horse is
         begin
            Subprogram_Addresses (N - 1);
         end Horse;

      begin -- Subprogram_Addresses

         if N = 0 then
            return;
         else

            Block_Pkg.Execute_Subprogram_Address_List
              ((1 => Cat'Address,
                2 => Dog'Address,
                3 => Horse'Address,
                4 => Cow'Address));
         end if;

      end Subprogram_Addresses;

   begin  -- Less_Portable_But_Medium_Speed

      Put ("System.Address conversions: Elapsed=");

      Start_Time := Real_Time.Clock;

      Subprogram_Addresses (N);

      Elapsed := Real_Time.To_Duration (Real_Time.Clock - Start_Time);

      Float_Text_IO.Put (Item => Float (Elapsed),
                         Fore => 2,
                         Aft  => 2,
                         Exp  => 0);

      Put_Line (" seconds");
   end Less_Portable_But_Medium_Speed;

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

   Non_Portable_But_Fast : declare
      procedure Subprograms (N : Natural) is

         procedure Cat is
         begin
            Subprograms (N - 1);
         end Cat;

         procedure Cow is
         begin
            Subprograms (N - 1);
         end Cow;

         procedure Dog is
         begin
            Subprograms (N - 1);
         end Dog;

         procedure Horse is
         begin
            Subprograms (N - 1);
         end Horse;

      begin -- Subprograms

         if N = 0 then
            return;
         else

            Block_Pkg.Execute_Subprogram_List
              ((1 => Cat'Unrestricted_Access,
                2 => Dog'Unrestricted_Access,
                3 => Horse'Unrestricted_Access,
                4 => Cow'Unrestricted_Access));
         end if;

      end Subprograms;

   begin -- Non_Portable_But_Fast

      Put ("Unrestricted_Access: Elapsed=");

      Start_Time := Real_Time.Clock;

      Subprograms (N);

      Elapsed := Real_Time.To_Duration (Real_Time.Clock - Start_Time);

      Float_Text_IO.Put (Item => Float (Elapsed),
                         Fore => 2,
                         Aft  => 2,
                         Exp  => 0);

      Put_Line (" seconds");
   end Non_Portable_But_Fast;

end Test_Access_To_Subprogram;

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

if I execute the above program with a command line parameter of 13, I get the
following output on my machine.

Tagged subprograms: Elapsed=73.63 seconds System.Address conversions:
Elapsed=13.47 seconds Unrestricted_Access: Elapsed= 1.45 seconds

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

From: Tucker Taft
Sent: Friday, October 25, 2013  11:15 AM

> ...
> The argument I present is that if we allowed Unchecked_Access on
> subprograms, it would allow a programmer to express a portable
> solution in a much less awkward manner, that could execute potentially 50-60 times faster than the current portable solution.

It is not easy to implement Unchecked_Access on subprograms in many run-time
models.  It doesn't become any easier to do so if we add it to the Ada standard.
The main reason we didn't add it during the Ada 9X process was because of this
difficulty, and I don't think anything has really changed here.  In particular,
the various Ada compilers based on the AdaMagic front end (Green Hills, Atego,
Analog Devices SHARK, Raytheon Patriot) would have real trouble implementing
this in a well-defined manner.

> My test program follows if that helps illustrate.

Have you tried this on non-GNAT-based Ada compilers?  Did it work as expected?

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

From: Robert Dewar
Sent: Friday, October 25, 2013  1:19 PM

>> ...
>> The argument I present is that if we allowed Unchecked_Access on
>> subprograms, it would allow a programmer to express a portable
>> solution in a much less awkward manner, that could execute potentially
>> 50-60 times faster than the current portable solution.
>
> It is not easy to implement Unchecked_Access on subprograms in many
> run-time models.  It doesn't become any easier to do so if we add it
> to the Ada standard.  The main reason we didn't add it during the Ada
> 9X process was because of this difficulty, and I don't think anything
> has really changed here.  In particular, the various Ada compilers
> based on the AdaMagic front end (Green Hills, Atego, Analog Devices SHARK,
> Raytheon Patriot) would have real trouble implementing this in a well-defined
> manner.

Well at least *allow* Ada 2012 compilers to do this, it is annoying to have
progress held up by compilers that realistically are never likely to implement
full Ada 2012 anyway, and I suspect that if they tried to do so, handling this
would be by no means the biggest barrier???

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

From: Bob Duff
Sent: Monday, October 28, 2013  2:38 PM

> Well at least *allow* Ada 2012 compilers to do this, ...

I don't see the point, given that we already have 'Unrestricted_Access.

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

From: Tucker Taft
Sent: Friday, October 25, 2013  2:08 PM

> Well at least *allow* Ada 2012 compilers to do this, it is annoying to
> have progress held up by compilers that realistically are never likely
> to implement full Ada 2012 anyway, and I suspect that if they tried to
> do so, handling this would be by no means the biggest barrier???

Ada 2012 compilers are free to support the GNAT subp'Unrestricted_Access
attribute. Declaring that all Ada 2012 compilers *must* support
subp'Unchecked_Access only raises the barrier to full support of Ada 2012.

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

From: Robert Dewar
Sent: Friday, October 25, 2013  2:33 PM

OK, and that is a concern, but how realistic is it that this is in reality a
significant additional barrier to full support of Ada 2012. In other words, do
you really think that adding this requirement would make the difference?

I just remember in the past the mistakes we have made in accomodating old
compilers that in fact never made the transition, e.g. all the considerations of
supporting display use in the old ALsys compiler, which never in fact made it to
Ada 95 after all.

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

From: Tucker Taft
Sent: Saturday, October 26, 2013  10:39 AM

Unchecked_Access on a subprogram is not easy to define.
AdaMagic consistently uses static links for non-library-level subprograms, and
does not use static links for library-level subprograms. If you have a
library-level access-to-subprogram type, it is represented by a single code
address.  The only way I know how to convert a single code address into a
static-link/address pair is to use a "trampoline," which implies writable code
space, something that is bad news from a security point of view.

If there were a very good, solid definition of exactly what Unchecked_Access on
a subprogram meant, that would be interesting to read and consider.  But just
saying it can be useful is not enough!

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

From: Steve Baird
Sent: Monday, October 28, 2013  2:20 PM

> AdaMagic consistently uses static links for non-library-level
> subprograms, and does not use static links for library-level subprograms.
> If you have a library-level access-to-subprogram type, it is represented by a
> single code address.

I believe the Rational/Atego compiler does something similar.

IMO, requiring support for Unchecked_Access for subprograms would effectively
force implementations to abandon this sort of optimization, at least in the
vanilla case. One could imagine some way of specifying that this optimization is
still ok in some cases but that wouldn't just fall out.

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

From: Bob Duff
Sent: Monday, October 28, 2013  2:38 PM

It's not just an optimization; it's important for interoperability with C and
other languages.

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

From: Randy Brukardt
Sent: Monday, October 28, 2013  3:30 PM

...
> If you have a library-level access-to-subprogram type, it is
> represented by a single code address.  The only way I know how to
> convert a single code address into a static-link/address pair is to
> use a "trampoline," which implies writable code space, something that
> is bad news from a security point of view.

Right. For what it's worth (probably not much), Janus/Ada uses displays rather
than static links. Anonymous-access-to-subprogram is a (very) fat pointer
including a display copy and some generic descriptor pointers, and calls to
those are quite expensive because all of those things have to be set up and torn
down. (We use a similar mechanism for calls through generic formal subprograms.)
None of that would happen with 'Unchecked_Access (as named access-to-subprogram
is a bare pointer, important for C interfacing at least), and the results would
be hard to predict.

> If there were a very good, solid definition of exactly what
> Unchecked_Access on a subprogram meant, that would be interesting to
> read and consider.  But just saying it can be useful is not enough!

I think such a definition is relatively easy: a call through a value created
from 'Unchecked_Access is erroneous if the called body references any up-level
data (more formally, if the called body accesses anything other than parameters,
local objects, and objects with library-level accessibility). Because of the
varying implementation models, no up-level access can be guarenteed. (You might
not have the right static link or display in the subprogram body for such
access.)

The problem with that, of course, is that it means that you can't do anything
with such a subprogram access that you can't do with 'Access on a library-level
subprogram (presuming you want to avoid erroneous execution, which one needs to
do if they want portable code). So what gain there would be from such a
definition is unclear to me, and obviously there is a cost to implementing such
a definition.

To do better would be much harder (even if one wanted to assume a static link
model) - unless there is no concern about requiring Ada code to be insecure (by
essentially requiring trampolines). I don't think we should even consider
requiring trampolines or similar constructs (I never even considered such an
implementation in Janus/Ada, for any purpose, it always seemed out of bounds to
me, even in the 1980s).

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

From: Robert Dewar
Sent: Monday, October 28, 2013  7:38 PM

>> IMO, requiring support for Unchecked_Access for subprograms would
>> effectively force implementations to abandon this sort of
>> optimization, at least in the vanilla case. One could imagine some
>> way of specifying that this optimization is still ok in some cases
>> but that wouldn't just fall out.
>
> It's not just an optimization; it's important for interoperability
> with C and other languages.

Well we end up interoperable with C, so I don't see this!

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

From: Bob Duff
Sent: Tuesday, October 29, 2013  5:43 AM

> Well we end up interoperable with C, so I don't see this!

Because GNAT represents an access to library level procedure as the address of
the code, just like the other compilers being discussed.  (Nested procedures are
more complicated, and the margin of this email is too small...)

Anyway, GNAT manages to do the "optimization" Steve is talking about while still
supporting 'Unrestricted_Access on nested procedures.  So it's possible.  But
it's probably not easy for other implementations.

Anyway, I am opposed to adding 'Unchecked_Access on subprograms. The usefulness
is marginal, and the implementation burden large (for all bug GNAT).  (I'm
pretty much opposed to adding ANY big features to Ada until other
implementations have a chance to catch up.)

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

From: Bob Duff
Sent: Tuesday, October 29, 2013  6:10 AM

> Unchecked_Access on a subprogram is not easy to define.

Heh?  To me, it seems trivial to define.  Hard to implement in some existing
implementations, perhaps, but not hard to define what it means.

You seem to be mixing up semantics (above) with implementation issues (below).

> AdaMagic consistently uses static links for non-library-level
> subprograms, and does not use static links for library-level subprograms.
> If you have a library-level access-to-subprogram type, it is
> represented by a single code address.  The only way I know how to
> convert a single code address into a static-link/address pair is to use a
> "trampoline," ...

See the GNAT implementation.  (I'm not prepared to describe it here from memory
-- I've no doubt forgotten important details!)

>... which implies writable code space,  something that is bad news from
>a security point of view.

Right, and is grossly inefficient.

> If there were a very good, solid definition of exactly what
> Unchecked_Access on a subprogram meant, that would be interesting to
> read and consider.  But just saying it can be useful is not enough!

I think the RM already describes what an acc-to-subp means.  We just need to say
that it is erroneous to evaluate an object whose value designates a subprogram
that no longer exists.  P'Unchecked_Access means exactly the same as P'Access at
run time -- the only difference is a legality rule on P'Access that makes it
safer.

I'm still opposed to adding this feature -- even though its semantics are
crystal clear to me!

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

From: Tucker Taft
Sent: Tuesday, October 29, 2013  10:00 AM

>> Unchecked_Access on a subprogram is not easy to define.
>
> Heh?  To me, it seems trivial to define.  Hard to implement in some
> existing implementations, perhaps, but not hard to define what it
> means.
>
> You seem to be mixing up semantics (above) with implementation issues
> (below).

One issue is whether 'Unchecked_Access implies that the designated subprogram is
presumed to *not* make up-level references into some or all of the enclosing
scopes.  Another part of the problem is that the meaning of the phrase you use
below, "the designated subprogram no longer exists."  Clearly we aren't worried
about the subprogram's code going away, we are worried about some or all of the
enclosing scopes of the subprogram going away. Another issue is whether you can
use 'Unchecked_Access to bypass a caller-side elaboration check.

In any case, the challenge of going from a single code address to a pair
<code-addr,static-link> is non-trivial.

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

From: Bob Duff
Sent: Tuesday, October 29, 2013  11:38 AM

I still think you're thinking about this backwards.  You seem to be thinking, "I
am familiar with an implementation that uses technique X. Now what would happen
if we use X unmodified to implement this new feature (in particular, a call via
a global access type to a nested procedure)?"

It won't work, that's what.

> One issue is whether 'Unchecked_Access implies that the designated
> subprogram is presumed to *not* make up-level references into some or
> all of the enclosing scopes.

In my view the answer is obvious:  Ada does not forbid up-level references, so
of course they are allowed.

I can't imagine anyone seriously suggesting otherwise -- it would defeat the
main purpose of nested procedures, and would render the suggested
'Unchecked_Access feature entirely useless.

>...Another part of the problem is that the  meaning of the phrase you
>use below, "the designated subprogram no  longer exists."

In Ada, declared entities begin to exist when the elaboration of some
declaration creates them ("procedure P(...);" in this case).  And they cease to
exist when the containing procedure (or whatever) is left.

>...Clearly we aren't worried about the subprogram's code  going away,
>we are worried about some or all of the enclosing scopes  of the
>subprogram going away.

In implementation terms, that's true.

In semantic terms, so long as a procedure exists, everything it might up-level
reference also exists.  And don't forget that a single procedure decl can create
many procedures, sometimes existing simultaneously.

>...Another issue is whether you can use  'Unchecked_Access to bypass a
>caller-side elaboration check.

I don't see why you would want to bypass elab checks, and I don't think anybody
suggested that.  Am I missing something -- would there be some benefit to that?

> In any case, the challenge of going from a single code address to a
> pair <code-addr,static-link> is non-trivial.

Right.  I think that's the argument you should be making.  In particular, low
benefit to users, high implementation cost, implies cost/benefit ratio is too
high.  I think your discussions about semantics obscure this simple point (e.g.
talking about some oddball semantics where up-level references don't work).

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

From: Bob Duff
Sent: Tuesday, October 29, 2013  11:45 AM

> In my view the answer is obvious:  Ada does not forbid up-level
> references, so of course they are allowed.

Just to emphasize:

I think Randy also made this point:  If up-level references don't work, then
'Unchecked_Access is 100% useless.

Also note that Brad is the one requesting the feature, and his recent example
(see Test_Access_To_Subprogram) uses up-level references.  (If it didn't, he
wouldn't need/want 'Unchecked_Access!)

So I suggest that we assume that if 'Unchecked_Access is allowed, then OF COURSE
up-level references still work as they do in Ada today.  And then attack the
idea based on the difficulty of implementing that properly.

Otherwise, we have confusion (e.g. Brad didn't ask for erroneousness of uplevel
refs!).

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

From: Randy Brukardt
Sent: Tuesday, October 29, 2013  2:20 PM

...
> Just to emphasize:
>
> I think Randy also made this point:  If up-level references don't
> work, then 'Unchecked_Access is 100% useless.

Right, I made that point. I also made the point that implementing up-level addresses for 'Unchecked_Access is impractical (it requires trampolines or making access incompatible with C).

> Also note that Brad is the one requesting the feature, and his recent
> example (see Test_Access_To_Subprogram) uses up-level references.  (If
> it didn't, he wouldn't need/want
> 'Unchecked_Access!)
>
> So I suggest that we assume that if 'Unchecked_Access is allowed, then
> OF COURSE up-level references still work as they do in Ada today.  And
> then attack the idea based on the difficulty of implementing that
> properly.
>
> Otherwise, we have confusion (e.g. Brad didn't ask for erroneousness
> of uplevel refs!).

I agree. The requested feature is impossible(*) to implement without making
uplevel addresses erroneous or making some sort of on-the-fly subprogram
creation or using only fat pointers. Thus it does not belong in Ada.

Note that we (RRS) encountered a similar problem with 'Access of subprograms in
the bodies of shared generics. Such subprograms have to have an extra parameter
that represents the data for the particular instance involved. For a bare
access-to-subprogram type, there is nowhere to get that data from, nor does the
profile match properly without it. The solution to this problem: demonstrate to
the ARG that allowing such 'Access prevents generic code sharing of any kind
(except in trivial instances where the generic unit has no instance data) and
get such 'Accesses declared illegal.

'Unchecked_Access would reintroduce this problem, and on top of that would cause
a similar problem for all subprograms (now you need to get the display or static
link data from somewhere, but the only places that that data could be stored at
require the display or static link to be set properly - global data would not be
task-safe).

Someone requesting this feature (i.e. Brad) would have to show that there is a
correct implementation that would actually work and not require writable code
space or breaking task-safety. (My understanding is that 'Unrestricted_Access in
GNAT uses a form of trampoline, which we are not going to consider. If that's
false, it's imperative that someone explain the details of the implementation,
because I know of no way to implement this and I'm convinced that it is
impossible without violating task-safety or having writable code.)

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

From: Bob Duff
Sent: Tuesday, October 29, 2013  5:08 PM

> I agree. The requested feature is impossible(*) to implement without
> making uplevel addresses erroneous or making some sort of on-the-fly
> subprogram creation or using only fat pointers.

Not impossible.  Just hard.

>...Thus it does not belong in Ada.

I agree it does not belong in Ada at this time.  I might change my tune if there
were several Ada 2012 compilers, with teams of maintainers looking for more work
to do.  ;-)

> Someone requesting this feature (i.e. Brad) would have to show that
> there is a correct implementation that would actually work and not
> require writable code space or breaking task-safety. (My understanding
> is that 'Unrestricted_Access in GNAT uses a form of trampoline, which
> we are not going to consider. If that's false, it's imperative that
> someone explain the details of the implementation, because I know of
> no way to implement this and I'm convinced that it is impossible
> without violating task-safety or having writable code.)

GNAT no longer uses trampolines, except in rare cases that are not relevant to
this discussion.  I was involved in removing trampolines, but unfortunately I
don't remember all the details. And I'm not willing to dig up the info.

The basic idea is: If P is top level, then P'Access is represented by the
address of P's code.  This address is always even, because of alignment.  If P
is nested, then P'Unrestricted_Access is represented as the address of a
descriptor, plus 1 (so it's odd, again because of alignment).  The descriptor
contains the static link and code address. (I suppose it could work with
displays just as well.)

An indirect call does "if the address is even, call that, else subtract 1, load
static link and then do the call."  The "even" case is the fast path, and it
involves a correctly-predicted jump, which is free or nearly free (compared to
an indirect call!) on most machines.

Or something like that.  I'm leaving out all sorts of complexities and details,
but those aren't important here.  Maybe it was minus 1 instead of plus 1, etc
etc.

Access value is one word.  Compatible with C representation.
Task/thread safe.  No trampolines or other writeable-code.

One important point: this implementation is target dependent, so complexities
are multiplied by the number of supported target machine architectures.

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

From: Robert Dewar
Sent: Tuesday, October 29, 2013  5:19 PM

> One important point: this implementation is target dependent, so
> complexities are multiplied by the number of supported target machine
> architectures.

That's FAR too pessimistic, on the contrary the even/odd approach is pretty much
target independent, it applies fine to all targets we support (which is a LOT!)

Of course on some machines like Alpha, the ABI doesn't use code addresses
anyway, procedure descriptors with data are always used, so there is no
trampoline issue there in any case.

But yes, the even/odd idea has worked out just fine to get rid of trampolines in
GNAT.

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

From: Robert Dewar
Sent: Tuesday, October 29, 2013  5:34 PM

By the way, this even/odd trick is a really nice example of how you have to
rethink things for modern architectures.

Old thinking "oh my gosh, an extra test and jump for every indirect call, that's
a disastrous performance hit".

New thinking "nearly always we can predict the jump so the test and jump is
free, and indeed we can't measure any performance hit at all, compared with the
use of trampolines" (where you rarely take the trampline hit, and nearly always
just do a direct call).

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

From: Randy Brukardt
Sent: Tuesday, October 29, 2013  7:02 PM

I grant that the branch would be nearly free, but compared to single indirect
call, it would seem (on Intel, at least) that you would either end up with a
extra memory read (we typically make the call getting the address directly from
memory, it is never in a register) or have to free a register (possibly causing
an extra write to spill a temporary).

Also, the additional code size could cause additional paging if indirect calls
are common. (I wouldn't expect that in well-written Ada code, but there is
C-in-Ada out there that does that.)

So it doesn't sound completely free in practice, although it probably is in
benchmarks (which aren't likely to have registers in use across calls).

In any case, I agree with your basic point that (carefully designed) branching
is close to free on modern architectures, so the main reason to worry about it
is simply code size and the associated cache and page pressure. Not at all the
same as in 1980's and early 1990's processors.

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

From: Robert Dewar
Sent: Tuesday, October 29, 2013  7:09 PM

> Also, the additional code size could cause additional paging if
> indirect calls are common. (I wouldn't expect that in well-written Ada
> code, but there is C-in-Ada out there that does that.)

We were unable to detect any impact even in weird test programs designed to
over-excercise this feature.

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

From: Randy Brukardt
Sent: Tuesday, October 29, 2013  5:39 PM

...
> Access value is one word.  Compatible with C representation.
> Task/thread safe.  No trampolines or other writeable-code.

Humm. This is compatible with C *representation*, but it's not likely to work
with C *code*, in that the C compiler isn't likely to be generating code to use
the "odd" representation. So if an access created with 'Unrestricted_Access is
passed to C, it's going to fail in some weird way (usually that should be a
memory protection fault). So I wouldn't call this representation truly
"compatible with C". (I suppose GCC might actually be prepared for this, but
most of us don't have the luxury of sharing anything with the C compiler.)

This also requires aligning subprogram start addresses. That's something we do
only in time optimization mode (for space optimization, the padding required can
be significant, so we leave it out). I recall reading once that Windows NT had
to eliminate subprogram alignment because the extra alignment increased paging
(causing a slow-down) more than it increased speed. (That's probably not as much
of an issue anymore, but it certainly used to be one.)

The latter issue is certainly minor, but the former seems important to me.
It doesn't matter to keep a C-compatible representation if the C compiler can't
reliably call the value in that representation. YMMV.

In any case, thanks for the explanation.

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

From: Robert Dewar
Sent: Tuesday, October 29, 2013  5:52 PM

> Humm. This is compatible with C *representation*, but it's not likely
> to work with C *code*, in that the C compiler isn't likely to be
> generating code to use the "odd" representation. So if an access
> created with 'Unrestricted_Access is passed to C, it's going to fail
> in some weird way (usually that should be a memory protection fault).
> So I wouldn't call this representation truly "compatible with C". (I
> suppose GCC might actually be prepared for this, but most of us don't
> have the luxury of sharing anything with the C compiler.)

Well in fact gcc *is* prepared for that. In the event your C compiler is simply
totally unable to accomodate any scheme, then of course you can't have
X'Unchecked_Access (Unrestricted_Access in GNAT) generate C-convention pointers.
Note that this does not by any means say that the feature is useless, even if it
can generate only Ada-compatible subprogram pointers.

> This also requires aligning subprogram start addresses. That's
> something we do only in time optimization mode (for space
> optimization, the padding required can be significant, so we leave it
> out). I recall reading once that Windows NT had to eliminate
> subprogram alignment because the extra alignment increased paging
> (causing a slow-down) more than it increased speed. (That's probably
> not as much of an issue anymore, but it certainly used to be one.)

Oh my gosh, unaligned access to subprograms, and unaligned labels is an
efficiency disaster on all modern architectures, so I think your recalled
reading is bogus.

That's been true for a long time. The early versions of Norton's system test had
a loop to check performance. Norton (who didn't know much early on) didn't know
about alignment, the second version was accidentally unaligned and suddently
have HORRIBLE results on some machines.

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

From: Bob Duff
Sent: Tuesday, October 29, 2013  6:23 PM

> Humm. This is compatible with C *representation*, but it's not likely
> to work with C *code*, in that the C compiler isn't likely to be
> generating code to use the "odd" representation. So if an access
> created with 'Unrestricted_Access is passed to C, it's going to fail
> in some weird way (usually that should be a memory protection fault).
> So I wouldn't call this representation truly "compatible with C". (I
> suppose GCC might actually be prepared for this, but most of us don't
> have the luxury of sharing anything with the C compiler.)

C compilers don't support nested procedures, so don't know about static links /
displays.  There's no way you can expect a C compiler to do an indirect call to
a nested Ada procedure.  So what?  C can do indirect calls to top-level Ada
procedures, which is all anybody would expect.  After all, you can't expect to
call (e.g.) Ada protected procedures from C, either.

Now, the gcc "C compiler" is not (just) a C compiler.  It supports an extended
dialect of C that has nested functions, and those interoperate with Ada just
fine.  That's one of the details I left out, and I'll continue to do so, since
it's not relevant to the Ada standard. (You're right that the gcc C compiler
knows nothing of the "odd" representation, but you shouldn't care about that
detail as far as ARG is concerned.)

> In any case, thanks for the explanation.

You're welcome.

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

From: Steve Baird
Sent: Tuesday, October 29, 2013  6:29 PM

> ...
>> The basic idea is: If P is top level, then P'Access is represented by
>> the address of P's code.  This address is always even, because of
>> alignment.  If P is nested, then P'Unrestricted_Access is represented
>> as the address of a descriptor, plus 1 (so it's odd, again because of
>> alignment).

> In any case, thanks for the explanation.

My thanks too.

On 10/28/2013 12:19 PM, Steve Baird wrote:
> IMO, requiring support for Unchecked_Access for subprograms would
> effectively force implementations to abandon this sort of
> optimization,

I stand corrected.

About which phrase, the internet sez:
   It was first recorded in John Dryden's The Maiden Queen (1668): "I
   stand corrected, and myself reprove."

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

From: Randy Brukardt
Sent: Tuesday, October 29, 2013  6:38 PM

> > Humm. This is compatible with C *representation*, but it's not
> > likely to work with C *code*, in that the C compiler isn't likely to
> > be generating code to use the "odd" representation. So if an access
> > created with 'Unrestricted_Access is passed to C, it's going to fail
> > in some weird way (usually that should be a memory protection fault).
> > So I wouldn't call this representation truly "compatible with C". (I
> > suppose GCC might actually be prepared for this, but most of us
> > don't have the luxury of sharing anything with the C compiler.)
>
> Well in fact gcc *is* prepared for that. In the event your C compiler
> is simply totally unable to accomodate any scheme,

Like say Visual C... :-)

> then of course you can't have X'Unchecked_Access (Unrestricted_Access
> in GNAT) generate C-convention pointers.
> Note that this does not by any means say that the feature is useless,
> even if it can generate only Ada-compatible subprogram pointers.

But it also means that the feature can't be implemented within the restrictions
intended (unless you can change the C compiler). I don't see how we could put
the feature into Ada and then say it is OK for it not work with C interfacing. I
can't think of any other feature with a C counterpart for which we allow that.

> > This also requires aligning subprogram start addresses. That's
> > something we do only in time optimization mode (for space
> > optimization, the padding required can be significant, so we leave
> > it out). I recall reading once that Windows NT had to eliminate
> > subprogram alignment because the extra alignment increased paging
> > (causing a slow-down) more than it increased speed. (That's probably
> > not as much of an issue anymore, but it certainly used to be one.)
>
> Oh my gosh, unaligned access to subprograms, and unaligned labels is
> an efficiency disaster on all modern architectures, so I think your
> recalled reading is bogus.

At least on the Intel machines, "efficiency disaster" is an overstatement.
I've tested the runtime difference for aligned and unaligned subprograms in
Janus/Ada, and the difference is barely noticeable at all in the programs I've
tried. I'm sure that it might matter more for some program for which that is the
predominant issue, but by no means is it a "disaster".

There seems to be much more effect between the various Intel processors in the
effects of other optimizations (like unrolling block compares) than there is
from alignment. (And yes, I checked that the code was in fact aligned, because I
was very surprised at the lack of effect.) [Most of this testing was done in
2009, so it's fairly recent.]

There might be an Intel processor where alignment is critically important, but
none of the ones I have have shown that. (I haven't tried the experiment on my
new Linux box because I don't have Janus/Ada for Linux and I don't know how to
make the appropriate experiments with GNAT.)

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

From: Robert Dewar
Sent: Tuesday, October 29, 2013  6:46 PM

  > There seems to be much more effect between the various Intel processors in
> the effects of other optimizations (like unrolling block compares)
> than there is from alignment. (And yes, I checked that the code was in
> fact aligned, because I was very surprised at the lack of effect.)
> [Most of this testing was done in 2009, so it's fairly recent.]

2009 is NOT recent when it comes to x86 technology, you are talking several
generations back!

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

From: Randy Brukardt
Sent: Tuesday, October 29, 2013  7:14 PM

True, but you were talking about a version of Norton a lot older than that; from
that I took it that you meant that alignment was important going way, way back.
(It's not just in the most recent generation.) Plus you claimed that my
recollection of an article that claimed that alignment had to be turned off for
Windows NT (which would have been in the mid-1990s) was bogus. So I would expect
that your statement applied to all processors of recent vintage, not just the
most recent generation. As such, 2009 results are quite relevant.

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

From: Randy Brukardt
Sent: Tuesday, October 29, 2013  6:49 PM

> C compilers don't support nested procedures, so don't know about
> static links / displays.  There's no way you can expect a C compiler
> to do an indirect call to a nested Ada procedure.  So what?  C can do
> indirect calls to top-level Ada procedures, which is all anybody would
> expect.

Why? The C code does not know or care that the routine is nested, just like the
*Ada* code doesn't know nor care that when you make an indirect call. It's the
routine itself that has to worry about nesting (managing a display or static
link).

If you're arguing that C somehow needs accessibility checks when Ada is not
supposed to, that seems like madness to me.

I remain convinced that C ought to be able to call any subprogram access value
that can be put into a C convention library-level access-to-subprogram. To say
that 'Unchecked_Access is always erroneous in that case makes no sense to me at
all, because what is "Unchecked" about that? And there is no justification
whatsoever for compile-time rejection of the 'Unchecked_Access.

To me, this feature remains impossible to implement, because there is no scheme
that will work in all of the ways it needs to work. As such the feature does not
belong in Ada -- ever.

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

From: Robert Dewar
Sent: Tuesday, October 29, 2013  6:53 PM

A curiously reactionary attitude if you ask me!

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

From: Randy Brukardt
Sent: Tuesday, October 29, 2013  7:20 PM

"reactionary"? Not sure what you mean by that.

Read "ever" in my statement above as meaning "so long as interfacing to C is
important" and "so long as writable code is considered a security hazard". I
expect those to be the case so long as I'm working in Ada (as opposed to being
retired).

If there is anything here that's "reactionary", it's worrying about
access-to-subprograms. I'd much rather people avoided access-to-subprogram
altogether and used more modern techniques.

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

From: Robert Dewar
Sent: Tuesday, October 29, 2013  7:46 PM

SOmetimes access to subprograms are the correct approach, just as gotos are
sometimes the best approach. Absolutism and purity can be a menace in
programming.

What is reactionary to me is letting the design of Ada be too controlled by
shortcomings in specific C compilers :-). As, an example of this happening, IMO
we made a bad mistake not allowing more general use of funargs in Ada 95, based
on worrying about specific existing Ada technologies that never were very
significant in the long run.

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

From: Randy Brukardt
Sent: Wednesday, October 30, 2013  9:29 PM

> > If there is anything here that's "reactionary", it's worrying about
> > access-to-subprograms. I'd much rather people avoided
> > access-to-subprogram altogether and used more modern techniques.
>
> SOmetimes access to subprograms are the correct approach, just as
> gotos are sometimes the best approach. Absolutism and purity can be a
> menace in programming.

True enough; I sometime use access-to-subprograms in interfacing because they're
the best match to the underlying API. But the question here isn't whether Ada
should allow access-to-subprograms (nobody is suggesting removing them!), but
rather whether it is a good use of scarce resources to extend an aging
technology. I would say the same thing if someone was proposing to extend gotos.

> What is reactionary to me is letting the design of Ada be too
> controlled by shortcomings in specific C compilers :-). As, an example
> of this happening, IMO we made a bad mistake not allowing more general
> use of funargs in Ada 95, based on worrying about specific existing
> Ada technologies that never were very significant in the long run.

As I mentioned above, one of the most important uses of access-to-subprogram
(IMHO) is in interfacing to C. And we're aiming a lot of our new Ada work at
safety-critical users. I don't think those users want to see new ways to have
erroneous execution added to Ada. And that's going to be necessary for pretty
much all Ada vendors; we're not really talking about specific C compilers; the
shortcoming is in the C language (the only reason that GCC doesn't have the
problem is that it extended the C language).

I'm especially leery of adding a feature to Ada that cannot be implemented
without new erroneous cases (or other bad properties) for the majority of Ada
implementations. We have enough instances of users wanting some nifty GNAT
gadget without adding ones to the language that cannot be implemented perfectly.

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

From: Randy Brukardt
Sent: Wednesday, October 30, 2013  11:46 AM

>> If there is anything here that's "reactionary", it's worrying about
>> access-to-subprograms. I'd much rather people avoided
>> access-to-subprogram altogether and used more modern techniques.

Ada being a "safe language", the "right" answer to Brad's needs would be
subprogram types as parameter types. The big advantages are:
- no safety issues: the activation block of the enclosing routine is sure to
  exist
- no implementation issues: if one doesn't like fat routine references, one can
  implement it with the help of 2 actual parameters (much like dope for arrays
  is handled as a second parameter without asking C-oriented backends to do
  something un-Cish).

(Water under the bridge, I guess.)

As to Brad's problem: If there are no up-level addresses in the routine, there
is no need to make it local to another routine. Use packages and bodies to hide
the subprogram from visibility. If there are up-level addresses in there, surely
"Unchecked" in the sense of "It will not work because no static link is passed"
is an unbelievably unacceptable implementation semantics. So, whatever it is, it
needs to include the static link or equivalent.

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

From: Randy Brukardt
Sent: Wednesday, October 30, 2013  9:20 PM

> Ada being a "safe language", the "right" answer to Brad's needs would
> be subprogram types as parameter types. The big advantages are:
> - no safety issues: the activation block of the enclosing routine is
> sure to exist
> - no implementation issues: if one doesn't like fat routine
> references, one can implement it with the help of 2 actual parameters
> (much like dope for arrays is handled as a second parameter without
> asking C-oriented backends to do something un-Cish).
>
> (Water under the bridge, I guess.)

Humm, this sounds like anonymous access-to-subprogram parameters, which of
course are in both Ada 2005 and Ada 2012. There should be no need for
'Unchecked_Access when using those, as there is no accessibility check on
'Access (so they'd be equivalent). I was assuming that Brad's problem couldn't
be handled with anonymous access-to-subprogram parameters, because if it could,
he surely would be using them.

Having a general "subprogram type" that could be used as objects and the like
would solve the problem, but of course that would reintroduce accessibility
issues (it's unclear that that would be substantially different than
access-to-subprogram).

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

From: Randy Brukardt
Sent: Wednesday, October 30, 2013  9:34 PM

...
> >...Thus it does not belong in Ada.
>
> I agree it does not belong in Ada at this time.  I might change my
> tune if there were several Ada 2012 compilers, with teams of
> maintainers looking for more work to do.  ;-)

Well, we're not talking about adding *anything* to Ada "at this time". We're
talking about the next version of Ada, which I doubt that we will even start
thinking about seriously for another 3-4 years. (If we really want Ada 2020,
we'll have to start in late 2016. But even that might be too soon.)

Discussions like this one primarily serve to keep us sane and interested,
because no one would pay any attention if the only discussion was on correcting
the accessibility rules to treat generic functions and functions the same. :-)
[I'm working on an AI about this wording issue, and it's hard to imagine anyone
will come to a meeting just to discuss that. ;-)]

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

From: Brad Moore
Sent: Thursday, October 31, 2013  8:33 AM

> Humm, this sounds like anonymous access-to-subprogram parameters,
> which of course are in both Ada 2005 and Ada 2012. There should be no
> need for 'Unchecked_Access when using those, as there is no
> accessibility check on 'Access (so they'd be equivalent). I was
> assuming that Brad's problem couldn't be handled with anonymous
> access-to-subprogram parameters, because if it could, he surely would be using them.

Actually, I hadn't considered this, as I wouldn't have expected that passing in
an in-mode array of anonymous access-to-subprogram parameter would be different
than passing in a bunch of anonymous access-to-subprogram parameters.

Thanks for the suggestion.

I tried this, though and it works, and it runs the fastest of all. The downside
is you need to know statically how many parameters you need to pass.

Most cases I am interested in can be handled by passing in two anonymous
access-to-subprograms. I could create multiple versions of functions for cases
of 3, 4, 5, etc subprograms, which would be OK. Ideally you'd like to pass in
any number of them though.

Also, I have some more results.

On GNAT Linux (an AMD quadcore box), the program executes with the following
times when passed a parameter of 13 at the command line.

Tagged subprograms: Elapsed=72.60 seconds System.Address conversions:
Elapsed=14.70 seconds Unrestricted_Access: Elapsed=1.45 seconds Access to fixed
list: Elapsed=0.90 seconds.

On GNAT Windows (an Intel dualcore notebook with an Atom processor), I observed
the following times, when passing a value of 11 at the command line. (I had to
use a smaller number, because the system is slower over all)

Tagged subprograms: Elapsed=19.49 seconds System.Address conversions:
Elapsed=138.91 seconds Unrestricted_Access: Elapsed= 0.35 seconds Access to
fixed list : Elapsed= 0.28 seconds

Note that System.Address conversions is considerably worse on Windows for some
reason, and is about 500 times slower than the best time.

Tucker was asking about trying this on another compiler.
I tried this on the ICC compiler, and found that the System.Address approach
fails, which I presume is because they use fat pointers with static links,
similar to most other implementations based what I've heard in this email
thread.

I suspect GNAT may be the only compiler where the System.Address conversions
works, which means that that approach is effectively no more portable than using
'Unrestricted_Access, so if one has to choose between these two choices on GNAT,
'Unrestricted_Access is the better choice.

ICC's version of 'Unrestricted_Access apparently does not support nested
subprograms that access objects from an enclosing scope, and would not compile
that part of the test code.

The times with ICC were interesting in that for tagged access to subprogram,
they were considerably faster than GNAT, however not as fast for the case using
'Access to a list of anonymous access-to-subprogram. Still, the 'Access to a
list of anonymous access-to-subprogram was by far a better time than access to
tagged subprogram.

So to summarize, given what Ada has today, I would probably make do with a fixed
list of anonymous access-to-subprogram parameters, which gives me the best
performance, and is portable.

This lessens the need for adding some sort of support for 'Unchecked_Access to
nested subprograms.

It still could be useful for the case of providing an interface that can handle
any number of anonymous access-to-subprogram parameters though. It might be
worth considering if a list of anonymous access-to- subprogram parameters can be
passed using 'Access, should it be possible to pass an in mode array of
anonymous access-to-subprogram parameters using 'Access as well?

Also, thanks all this thread has been an interesting read. I particularly
enjoyed Bob Duff's explanation of the odd address technique.

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

From: Tucker Taft
Sent: Thursday, October 31, 2013  4:34 PM

Thanks for the interesting set of data.
Glad to hear that you were able to solve your problem using an existing portable
feature!

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

From: Randy Brukardt
Sent: Thursday, October 31, 2013  6:01 PM

...
> > Humm, this sounds like anonymous access-to-subprogram parameters,
> > which of course are in both Ada 2005 and Ada 2012. There should be
> > no need for 'Unchecked_Access when using those, as there is no
> > accessibility check on 'Access (so they'd be equivalent). I was
> > assuming that Brad's problem couldn't be handled with anonymous
> > access-to-subprogram parameters, because if it could, he surely
> > would be using them.
>
> Actually, I hadn't considered this, as I wouldn't have expected that
> passing in an in-mode array of anonymous access-to-subprogram
> parameter would be different than passing in a bunch of anonymous
> access-to-subprogram parameters.

I agree that's weird, but it's also the way things are. Welcome to the wacky
world of Ada's anonymous access types. :-)

> Thanks for the suggestion.
>
> I tried this, though and it works, and it runs the fastest of all. The
> downside is you need to know statically how many parameters you need
> to pass.
>
> Most cases I am interested in can be handled by passing in two
> anonymous access-to-subprograms. I could create multiple versions of
> functions for cases of 3, 4, 5, etc subprograms, which would be OK.
> Ideally you'd like to pass in any number of them though.

Well, you could default parameters that you don't need to null. The body would
have to test how many there are, but it would be a bit easier to read than
having a bunch of overloaded routines. You'd still have a maximum number of
procedures that way, though.

    procedure Execute_Subprogram_List (Proc1 : access procedure;
                                       Proc2 : access procedure := null;
                                       Proc3 : access procedure := null;
                                       ...
                                       Procn : access procedure := null);

You'd have to pre-test each one for null before trying to call it, but that's
unlikely to add any significant overhead (partly because the language probably
already is doing that somewhere, and partly because the overhead would be
swamped by the cost of the indirect call).

I've used techniques like this one occasionally (not for access-to-subprogram,
though); it actually simplifies the call a tad because you don't have to pass an
aggregate (thus no need for qualification or parens), and you don't have to live
with a fixed size list (only the maximum is fixed).

...
> It still could be useful for the case of providing an interface that
> can handle any number of anonymous access-to-subprogram parameters
> though.
> It might be worth considering if a list of anonymous
> access-to- subprogram parameters can be passed using 'Access, should
> it be possible to pass an in mode array of anonymous
> access-to-subprogram parameters using 'Access as well?

The problem with the array is that it is a type that could be used in other ways
as well. (Say, to declare an object.) In other cases, the nice property that the
accessibility is always OK so long as the parameter exists isn't true, and we
have all of the previous issues again. OTOH, if we allowed anonymous arrays in
subprogram specifications (we don't currently), then it would make sense that an
anonymous array of anonymous access-to-subprogram used as an in parameter would
have the same special semantics. But that's pretty weird - but it might make a
sensible alternative to 'Unchecked_Access.

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

From: Erhard Ploedereder
Sent: Friday, November  1, 2013  2:29 PM

...
> Actually, I hadn't considered this, as I wouldn't have expected that 
> passing in an in-mode array of anonymous access-to-subprogram 
> parameter would be different than passing in a bunch of anonymous 
> access-to-subprogram parameters.

Big difference, really. As soon as subprogram values or references are
assignable, all hell breaks loose wrt safety of up-level addressing local
variables. Only parameter passing is safe in this regard.

(I trust that there isn't some funny trick around to assign values of
anonymous subprogram access type, or else Randy's remark does not hold that
they are equivalent to subprogram parameters.)

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

From: Randy Brukardt
Sent: Friday, November  1, 2013  2:44 PM

Anonymous access-to-subprogram parameters have infinitely deep accessibility,
so the accessibility check fails on any attempt to assign them to some other
kind of access-to-subprogram type (including anonymous access-to-subprogram
components and objects).

Since we don't have 'Unchecked_Access for subprograms ("There's a hole in the
bucket, a hole." :-) the only ways to assign such values involve Chapter
13 tricks (unchecked conversion, overlays, etc.), and no one ought to expect 
hose to work portably. So these are safe and we don't have to make an
accessibility check on the 'Access.

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

Questions? Ask the ACAA Technical Agent