Version 1.1 of 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; --
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 := (D => E1, F1 => 123); --
Acc.all := 456; --
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