Version 1.1 of ai12s/ai12-0057-1.txt

Unformatted version of ai12s/ai12-0057-1.txt version 1.1
Other versions for file ai12s/ai12-0057-1.txt

!standard 13.10(3)          13-01-22 AI12-0057-1/01
!class Amendment 13-01-22
!status work item 13-01-22
!status received 12-07-03
!priority Medium
!difficulty Easy
!subject Unchecked_Access for discriminant-dependent subcomponents
!summary
Unchecked_Access can be used on discriminant-dependent subcomponents; the programmer is guarenteeing that the discriminant will not change while the access value is in use.
!problem
The rules prevent 'Access from being used on components that depend on discriminants, when the discriminants could be changed:
type Enum is (E1, E2);
type T1 (D : Enum := E1) is record case D is when E1 => F1 : aliased Integer; when E2 => F2 : Long_Float; end case; end record;
V1 : T1;
type Int_Acc is access all Integer; Acc : Int_Acc;
Acc := V1.F1'access; -- ILLEGAL
This is illegal because if V1 were reassigned so that its discriminant were changed to E2, Acc would now be pointing at a portion of a float rather than an integer.
It seems sensible to allow
Acc := V1.F1'Unchecked_Access;
if the programmer knows, from the logic of the program, that V1's discriminant won't change before the access value is used.
However, the rules don't currently allow this. 13.10(3) says that the only 'Access rule that doesn't apply to 'Unchecked_Access is the rule about accessibility levels.
!proposal
(See wording.)
!wording
Modify 13.10(3):
All rules and semantics that apply to X'Access (see 3.10.2) apply also to X'Unchecked_Access, except that, for the purposes of accessibility rules and checks, it is as if X were declared immediately within a library package{, and for the purposes of subcomponent checks, it is as if the object of any discriminants that X depends on is known to be constrained}.
!discussion
This is exactly analogous to the use of 'Unchecked_Access on local variables in a subprogram; the language can't guarantee that the access value won't be used after the local variable goes away, but the programmer is certain that it won't. Here, we're allowing the programmer to make the same promise about discriminant-dependent subcomponents.
One could ask why these two particular legality rules are excepted from checks by 'Unchecked_Access, while other requirements like aliasedness, being non constant for access-to-variable types, and even basic subtype and type matching are still checked. That's because these other checks are not about detecting dangling pointers, but rather are about the basic model of Ada programming (not being able to break the type model without unchecked programming; not having aliases to non-aliased objects; and not changing constants). However, the discriminant-dependent subcomponent check is exists solely to prevent accesses to non-existent components from being used; it has no other semantic purpose (just like accessibility checks).
Note that the chosen wording requires that the subcomponent exist when X'Unchecked_Access is evaluated; variant and index checks are still performed in X.
If the component X ceases to exist because of a change to a discriminant, use of existing access values to X is erroneous. That's true even if the discriminant is changed back to its original value, as this change creates a new (and logically different) component object from the original. For instance, execution of the following (using the example of the question as a basis) is erroneous:
Acc := V1.F1'Unchecked_Access; V1 := (D => E2, F2 => 123.45); -- V1.F1 gone ...; -- no uses of Acc here V1 := (D => E1, F1 => 123); -- V1.F1 returns? Acc.all := 456; -- Erroneous
Code like this will probably work properly on most implementations, but we want it to be technically erroneous as we do allow compilers to use indirect components that are reallocated when discriminants change. In such a case, the component of the third value of V1 might be in a different location than that of the original version.
[Editor's note: This could happen in Janus/Ada if the aliased object is a component of a component that is a discriminant-dependent array. That is, this is not purely a thought experiment.]
[Editor's note 2: 13.11.2(16/3) just talks about access to a non-existent object. This clearly covers the case of changing the discriminant once; it's not as crystal-clear when it is changed back. Possibly we should add an AARM note following 13.11.2(16/3) to make it clear that a component that "appears" because of a discriminant change is a different object than any component that happened to exist for that discriminant value in the past. If we wanted to say this normatively, we should say somewhere in 5.2 that any components that are not in the target value after assignment (because of discriminant changes) cease to exist. "cease to exist" is defined in 7.6.1(11/3) for leaving of scopes after finalization, and in 13.11.2 for Unchecked_Deallocation, but nothing is said about objects (components) that disappear during assignment. I think this is "obvious" (and indeed, we thought that about leaving of scopes, but then we decided to formally define it in Ada 2005 because not everyone thought it was obvious); there is some risk of introducing bugs if we clarify this incorrectly which is why I didn't propose such a clarification.]
!example
(See problem.)
!ACATS test
ACATS C-Test(s) should be created to test that 'Unchecked_Access can be used on discriminant dependent components.
!appendix

!topic Allow 'Unchecked_Access on discriminant-dependent subcomponents
!reference 13.10(3)
!from Adam Beneschan 12-07-03
!discussion

The rules prevent 'Access from being used on components that depend on
discriminants, when the discriminants could be changed:

    type Enum is (E1, E2);
    
    type T1 (D : Enum := E1) is 
        record
            case D is
                when E1 =>
                    F1 : aliased Integer;
                when E2  =>
                    F2 : Long_Float;
            end case;
        end record;

    V1 : T1;
    
    type Int_Acc is access all Integer;
    Acc : Int_Acc;
    
    Acc := V1.F1'Access;   -- ILLEGAL

This is illegal because if V1 were reassigned so that its discriminant
were changed to E2, Acc would now be pointing at a portion of a float
rather than an integer.

It seems sensible to me to allow 

    Acc := V1.F1'Unchecked_Access;

if the programmer knows, from the logic of the program, that V1's
discriminant won't change before the access value is used.  I think
this is exactly analogous to the use of 'Unchecked_Access on local
variables in a subprogram; the language can't guarantee that the
access value won't be used after the local variable goes away, but the
programmer is certain that it won't.

However, the rules don't currently allow this.  13.10(3) says that the
only 'Access rule that doesn't apply to 'Unchecked_Access is the rule
about accessibility levels.  I'd like to propose to change this to
something like:

    All rules and semantics that apply to X'Access (see 3.10.2) apply
    also to X'Unchecked_Access, except that, for the purposes of
    accessibility rules and checks, it is as if X were declared
    immediately within a library package, and except that the
    restrictions on X being a subcomponent that depends on
    discriminants (in 3.10.2(26)) do not apply.

[This does come from actual code, by the way.  It involves a design
from a long time ago that I think ought to be redone not to use a
variant record, but that involves a much larger amount of work and
isn't an option.]

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

From: Tucker Taft
Sent: Tuesday, July  3, 2012  11:46 AM

> It seems sensible to me to allow
>
>      Acc := V1.F1'Unchecked_Access;
> ...

This seems like a reasonable proposal to me.

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

From: Steve Baird
Sent: Friday, July  6, 2012  1:23 PM

I'm unsure.

Arguments in favor:
    No clean workaround if we don't allow it.

    It has the same flavor as the existing use
    of Unchecked_Access - to generate an access value
    when the compiler can't be sure this won't
    lead to dangling reference problems but the user
    is willing to promise that it won't.

    No worse than suppressing constraint checks for
    a variant record.

Opposed:
    Unchecked_Access has a clean semantic model; it only relaxes
    accessibility checking. It doesn't relax
    any of the numerous other semantic checks that have to
    pass for a use of X'Access to be legal (aliasedness,
    constancy for access-to-variable types, even basic subtype
    and type matching). So this is the first step down a slippery slope

    We'd need a precise definition of what constitutes misuse here.
    For example, would this be erroneous?

[context taken from original example]:
>     type Enum is (E1, E2);
>     
>     type T1 (D : Enum := E1) is 
>         record
>             case D is
>                 when E1 =>
>                     F1 : aliased Integer;
>                 when E2  =>
>                     F2 : Long_Float;
>             end case;
>         end record;
> 
>     V1 : T1;
>     
>     type Int_Acc is access all Integer;
>     Acc : Int_Acc;
>     

      Acc := V1.F1'Unchecked_Access;
      V1 := (D => E2, F2 => 123.45); -- V1.F1 gone
      ...; -- no uses of Acc her,
      V1 := (D => E1, F1 => 123); -- V1.F1 returns?
      Acc.all := 456; -- erroneous?

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

Questions? Ask the ACAA Technical Agent