Version 1.1 of ai05s/ai05-0149-1.txt

Unformatted version of ai05s/ai05-0149-1.txt version 1.1
Other versions for file ai05s/ai05-0149-1.txt

!standard 4.5.2(3/2)          09-04-29 AI05-0149-1/02
!standard 4.5.2(30.2/2)
!class Amendment 09-04-12
!status work item 09-04-12
!status received 09-04-12
!priority Medium
!difficulty Medium
!subject Access type membership
!summary
(See proposal.)
!problem
There is currently no way to check whether a conversion from an anonymous access type to a named access type will pass its run-time accessibility check. Normally a membership test can be used prior to a conversion to check if the conversion will succeed, but the current rules for access-type membership tests don't allow for this.
!proposal
When the tested type of a membership test is a general access type, allow the expression to be of an anonymous access type.
!wording
Modify 4.5.2(3/2) as follows:
The tested type of a membership test is the type of the range or the type determined by the subtype_mark. If the tested type is tagged, then the simple_expression shall resolve to be of a type that is convertible (see 4.6) to the tested type; [if untagged] {if the tested type is a general access-to-object type, then the simple_expession shall resolve to be of the same type, or universal_access, or an anonymous access-to-object type whose designated type is the same as that of the tested type, or if tagged, convertible to the designated type of the tested type; otherwise}, the expected type for the simple_expression is the tested type.
Add after 4.5.2(30.2/2):
* if the tested type is a general access-to-object type, the type of the simple_expression is convertible to the tested type and its accessibility level is no deeper than that of the tested type; further, if the designated type is tagged and the simple_expression is non-null, the tag of the object designated by the value of the simple_expression is covered by the designated type of the tested type.
!discussion
The basic goal is that membership will return false if an attempt to convert the simple_expression into the tested type will be illegal or fail a run-time check.
We started from the approach used for tagged types, by allowing the simple_expression to be of any type that is convertible to the tested type. We then relaxed that to simplify name resolution, since we don't normally worry about things like static designated subtype matching in name resolution. We morphed the requirement for convertibility into a criteria for membership returning True.
We considered making a membership test illegal rather than returning False if the corresponding conversion would be illegal, but that didn't seem as friendly in places like generics, and there are currently no legality rules associated with membership tests.
We have limited it to access-to-object types, as there seems little need for the corresponding capability for access-to-subprogram types.
There are some places where run-time accessibility checks are performed other than conversions to a named type, and we aren't helping for those. But this seems like a simple improvement that handles many of the interesting cases of run-time accessibility checks.
!example
!ACATS test
ACATS C-Tests are needed to check that this membership is allowed and gets appropriate results.
!appendix

From: Franco Gasperoni
Date: Friday, November 14, 2008  1:54 PM

Right now a developer has no simple way to defend its code against accessibility
level problems, in particular should the user disable all checks.

To that extent we could define a new attribute 'Level, where for any entity E
for which an accessibility level is defined:

    E'Level returns a Natural which represents the accessibility
            level of E (better a private type with
            <, >, <=, >=, =, and a constant for the library_level
            and something to print it as an integer for teaching purposes)

As an example and using Natural for 'Level consider the following:

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

       procedure P (X : not null access Integer);
    end Pack;

    package body Pack is
       procedure P (X : not null access Integer) is
          --  X.all'Level = accessibility level of the object
                            pointed to by the actual

          Y : access Integer := G;
          --  Y'Level = 1
          --  Y.all'Level = 0
       begin
          null;
       end P;
    end Pack;

    with Pack; use Pack;
    procedure Main is
       W : aliased Integer;
       --  W'Level = 1
    begin
       P (G);
       --  Inside P, X.all'Level = 0

       P (W'Access);
       --  Inside P, X.all'Level = 1
    end Main;

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

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

    procedure Insert (X, Y : not null access Cell'Class) is;
    begin
       Y.Next := X.Next;
       Y.Next := Y;  --  Accessibility check we cannot defend against
    end;

If we use 'Level we can defend against the accessibility problem as follows

    procedure Insert (X, Y : not null access Cell'Class) is;
    begin
       if Y.all'Level <= X.Next'Level then
          Y.Next := X.Next;
          Y.Next := Y;
       else
          ...
       end if;
    end;

Note that if instead of "not null" the Y access parameter can be null then we
have to write

    procedure Insert (X : not null access Cell'Class;
                      Y :          access Cell'Class) is;
    begin
       if Y /= null and then Y.all'Level <= X.Next'Level then
          Y.Next := X.Next;
          Y.Next := Y;
       else
          ...
       end if;
    end;

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

From: Randy Brukardt
Date: Friday, November 14, 2008  3:28 PM

> Right now a developer has no simple way to defend its code against
> accessibility level problems, in particular should the user disable
> all checks.

I would be in favor of allowing more control of accessibility.

But I don't see any reason to worry about cases where the user has disabled all
checks (that is, pragma Suppress). By doing so, they are telling the compiler:
"I've thoroughly tested this program, and I'm certain that no checks are needed.
Make the program as fast as possible, but I understand that if I'm wrong that no
checks are needed, the program will fall over dead."

> To that extent we could define a new attribute 'Level, where
> for any entity E for which an accessibility level is defined:
>
>     E'Level returns a Natural which represents the accessibility
>             level of E (better a private type with
>             <, >, <=, >=, =, and a constant for the library_level
>             and something to print it as an integer for teaching purposes)

You seem to be confusing static and dynamic accessibility. This attribute would
presumably be testing the static accessibility (since you seem to expect that it
can be represented as an integer), which is not what the dynamic checks test.
Moreover, there is no way to easily describe dynamic accessibility, since
"incomparable" is a possible result. (It comes up with tasks and class-wide
types; if we generalized accessibility at all, it most likely would come up with
all access types as well.)

Steve points out that there ought to be a membership for accessibility, which
seems like a better idea (an operation to compare to accessibility of two
values, not a way to make that into an object). That probably would mitigate
your concern.

My thought on accessibility is that Tucker wants to extend dynamic accessibility
checks into more places (anonymous access returns; stand-alone objects). But
this is something that could be useful in all contexts. And it has a runtime
overhead, so declaring that you don't want it also would seem useful in all
contexts. (Specifically, it would be nice to be able to declare static
accessibility for an access parameter, if it is intended only to take existing
library level objects or some such. Dynamic accessibility always implies the
possibility of passing in something that causes something to break further down
the line, and that makes routines more fragile than they have to be.)

This comes back to Bob's idea that we don't really want features where you have
to do things a certain way (in this case, use anonymous access types) in order
to get some other useful but unrelated feature (dynamic accessibility checks).

So I wonder if it would make sense to allow an access type (*any* access type,
named or anonymous) to declare that it wants to use dynamic accessibility (or
static accessibility!) -- then the programmer can decide between the static
checks that never seem to actually work and dynamic checks that work but are
expensive.

One could go even further and allow an access type to have a static
accessibility that is deeper than where it is declared. (That would seem to have
value only for anonymous access types, so I don't think it is worth it.)

I haven't thought about the details of this idea too much, since I wanted to
find out if I was the only one thinking this way. So perhaps there is a giant
hole in it somewhere.

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

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

> Steve points out that there ought to be a membership for
> accessibility, which seems like a better idea (an operation to compare
> to accessibility of two values, not a way to make that into an
> object). That probably would mitigate your concern.

Absolutely, that would solve it

> My thought on accessibility is that Tucker wants to extend dynamic
> accessibility checks into more places (anonymous access returns;
> stand-alone objects). But this is something that could be useful in all contexts.

Tuck is right on track there and I agree with your statement.

> And it
> has a runtime overhead, so declaring that you don't want it also would
> seem useful in all contexts. (Specifically, it would be nice to be
> able to declare static accessibility for an access parameter, if it is
> intended only to take existing library level objects or some such.
> Dynamic accessibility always implies the possibility of passing in
> something that causes something to break further down the line, and
> that makes routines more fragile than they have to be.)

Yes

> This comes back to Bob's idea that we don't really want features where
> you have to do things a certain way (in this case, use anonymous
> access types) in order to get some other useful but unrelated feature
> (dynamic accessibility checks).
>
> So I wonder if it would make sense to allow an access type (*any*
> access type, named or anonymous) to declare that it wants to use
> dynamic accessibility (or static accessibility!) -- then the
> programmer can decide between the static checks that never seem to
> actually work and dynamic checks that work but are expensive.

That is a very interesting thought indeed. In particular if we could "request"
and be guaranteed that all anonymous access to a given designated type T have
(say) static library-level accessibility then local anonymous allocators would
have to be allocated on the heap and would allow us to present anonymous access
as the natural match to the * T of C/C++ (except that it is safer :)

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

From: Tucker Taft
Sent: Wednesday, March 11, 2009  8:31 AM

Having an explicit level makes me nervous, especially since I presume it is
a static nesting level, whereas accessibility is actually officially defined
based on a dynamic nesting level, and in some cases, there is no simple static
nesting level that can be used instead.

What I would prefer would be to have a membership test properly report
whether a conversion of an anonymous access value to a named access type
would raise Program_Error *or* Constraint_Error.

E.g.:

       type Acc_T is access all T;

       X : Acc_T;

       procedure P(A : access T) is
       begin
          if A in Acc_T then
              X := Acc_T(A);  -- Won't raise P_E or C_E
          else
              -- Trouble, Acc_T(A) would raise P_E or C_E
              Put_Line("Can't convert to Acc_T");
          end if;
       end P;

    That is, a use of "A in Acc_T" would return False iff "Acc_T(A)"
    would raise Program_Error or Constraint_Error

Right now, such a membership test is not legal, since there are no implicit
conversions from anonymous access types to named access types, so we could add
this capability without incurring any upward incompatibility.

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

From: Gary Dismukes
Sent: Wednesday, March 11, 2009  11:58 AM

> What I would prefer would be to have a membership test properly report 
> whether a conversion of an anonymous access value to a named access 
> type would raise Program_Error
> *or* Constraint_Error.

Yes, I like that much better than tests using levels.  Making levels explicit
would be sure to run into tricky issues in some cases.

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

From: Bob Duff
Sent: Wednesday, March 11, 2009  12:51 PM

> What I would prefer would be to have a membership test properly report 
> whether a conversion of an anonymous access value to a named access 
> type would raise Program_Error
> *or* Constraint_Error.

Yes, I agree, that makes a lot of sense.

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

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

> Having an explicit level makes me nervous, especially since I presume 
> it is a static nesting level, whereas accessibility is actually 
> officially defined based on a dynamic nesting level, and in some 
> cases, there is no simple static nesting level that can be used 
> instead.

I pointed that out immediately after Franco posted the original message back
in November.
 
> What I would prefer would be to have a membership test properly report 
> whether a conversion of an anonymous access value to a named access 
> type would raise Program_Error
> *or* Constraint_Error.

And Steve Baird proposed something like this (in another thread) also back
in November. (That's how he got himself assigned to this accursed
subcommittee...)

> E.g.:
> 
>        type Acc_T is access all T;
> 
>        X : Acc_T;
> 
>        procedure P(A : access T) is
>        begin
>           if A in Acc_T then
>               X := Acc_T(A);  -- Won't raise P_E or C_E
>           else
>               -- Trouble, Acc_T(A) would raise P_E or C_E
>               Put_Line("Can't convert to Acc_T");
>           end if;
>        end P;
> 
>     That is, a use of "A in Acc_T" would return False iff "Acc_T(A)"
>     would raise Program_Error or Constraint_Error
> 
> Right now, such a membership test is not legal, since there are no 
> implicit conversions from anonymous access types to named access 
> types, so we could add this capability without incurring any upward 
> incompatibility.

...but thanks for working out the details. The main reason that I didn't make a
separate AI for this idea originally was that I couldn't figure out how it would
work.

OTOH, using a straight membership means that there is no such check possible for
named access types nor for anything else that has a dynamic accessibility check
(tagged returns, for instance). The problem seems more general than just anonymous
access parameters.

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

From: Tucker Taft
Sent: Sunday, April 12, 2009  8:46 PM

Here is my write-up of membership tests for accessibility.
[This is version /01 of the AI. - ED]
I basically piggy-backed on the change we made for tagged type, by requiring
that the simple_expression of a membership test resolve to a type that is
convertible to the tested type.  Seems like a pretty simple and natural fix.

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

From: Bob Duff
Sent: Monday, April 13, 2009  8:27 AM

> !proposal
> 
> When the tested type of a membership test is a general access type, 
> allow the expression to be of an anonymous access type.

That's not quite right -- the new wording allows additional tests for named types
as well.  Right?  Shouldn't it be "to be of any type convertible to the tested type,
including an anonymous access type"?

Other than that, I agree with the proposal.

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

From: Tucker Taft
Sent: Monday, April 13, 2009  8:37 AM

Good point.  Once I started the wording, it seemed simplest to allow any type
convertible to the tested type.  Requiring it to be anonymous would have required
more wording, to no obvious advantage.

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

From: Randy Brukardt
Sent: Thursday, April 30, 2009  1:05 AM

[A comment on version /02 of the AI - ED]

Why did you limit it to anonymous access type conversions? It's possibility to
convert between two (different) named general access types, and your previous
definition allowed it to be used in that case as well. That seemed like a good
thing to me (we want to avoid forcing people to use anonymous access types simply
because they get this feature). I realize it isn't that important, but it could be
useful in generic bodies, and it seems odd to explain this as wanting to be able
to test whether a conversion will succeed, and then not actually do that.

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


Questions? Ask the ACAA Technical Agent