Version 1.10 of ai05s/ai05-0234-1.txt
!standard 6.5(21/3) 11-03-17 AI05-0234-1/05
!class binding interpretation 10-11-15
!status work item 10-11-15
!status received 10-10-31
!priority Low
!difficulty Hard
!qualifier Omission
!subject Hole in AI05-0051-1
!summary
Plug access discriminant accessibility checking holes for functions and
allocators.
!question
There remains a problem not covered by AI05-0051-1. Consider the following:
with Text_IO;
procedure Cw_Alloc is
type Root is tagged null record;
type Ref is access Root'Class;
type Extension (Discrim : access Integer)
is new Root with null record;
function Bad_News return Ref is
Local_Int : aliased Integer := 123;
Local_Ext : aliased Extension (Discrim => Local_Int'access);
type Local_Ref is access all Root'Class;
for Local_Ref'Storage_Size use 0;
Local_Ptr : Local_Ref := Local_Ext'access;
begin
return new Root'Class'(Local_Ptr.all);
--
--
end Bad_News;
Ptr : Ref := Bad_News;
procedure Do_Nothing is
begin
null;
end Do_Nothing;
begin
Text_IO.Put_Line
("*** Test failed - an exception should have been raised");
Text_IO.Put_Line
(" Ptr.Discrim.all ="
& Integer'Image (Extension (Ptr.all).Discrim.all));
Do_Nothing;
Text_IO.Put_Line
(" Ptr.Discrim.all ="
& Integer'Image (Extension (Ptr.all).Discrim.all));
end Cw_Alloc;
Another case not addressed is when there is an unconstrained subcomponent
with a defaulted access discriminant:
X : aliased Integer;
type Subcomponent_Type (Discrim : access Integer := X'access)
is limited null record;
type Component_Type is record S : Subcomponent_Type; end record;
type Undiscriminated is record
Fld : Component_Type;
end record;
function F return Undiscriminated is
Local : aliased Integer;
begin
return X : Undiscriminated
:= (Fld => (S => (Discrim => Local'access))) do
Foo;
end return;
--
end F;
Are the access discriminant accessibility checks associated with allocators and
function returns performed even in the case where the existence of those
discriminants is not known statically at the point of the check? (Yes.)
Are access discriminants of subcomponents also checked in the case where the
subtype of the subcomponent is unconstrained? (Yes.)
!wording
Change 3.10.2(10/2):
The accessibility level of an aggregate or the result of a function call
[(or equivalent use of an operator)] that is used (in its entirety) to
directly initialize part of an object is that of the object being initialized.
In other contexts, the accessibility level of an aggregate or the result of a
function call is that of the innermost master that evaluates the aggregate or
function.
revising it to only address the aggregate case, as follows:
The accessibility level of an aggregate that is used (in its entirety) to
directly initialize part of an object is that of the object being initialized.
In other contexts, the accessibility level of an aggregate is that of the
innermost master that evaluates the aggregate.
Add the following after 3.10.2(10/2) (Note: '*' denotes a top-level bullet
and '+' denotes second-level bullets):
* The accessibility level of the result of a function call is "determined by
the point of call" as follows:
+ If the result is used (in its entirety) to directly initialize part of an
object, the accessibility level is that of the object being initialized.
??? In the case where the initialized object is a coextension that becomes
a coextension of another object (see below), the accessibility level is
the level of the eventual object to which the coextension will be
transferred. ???
+ If the result is of an anonymous access type and is the operand of an
explicit conversion, the accessibility level is that of the target type
of the conversion;
+ If the result is of an anonymous access type and defines an access
discriminant, the accessibility level is the same as that for an object
created by an anonymous allocator that defines an access discriminant
(even if the access result is of an access-to-subprogram type).
+ If the call itself defines the result of a function to which one of
the above rules applies, these rules are applied recursively;
+ In other cases, the accessibility level of the result is that of the
innermost master that evaluates the function call.
[AARM] Ramification: The accessibility level of a function result object
in the case where building in place is optional and the implementation elects
not to build in place is the same as if the implementation had decided to
build in place. In other words, the effect of this choice in terms of masters,
lifetimes, and accessibility is transparent to the program.
[AARM] Implementation Note: There are several cases where the implementation
may have to pass in the accessibility level of the result object on a call,
to support later rules where the accessibility level is said to be determined
by the point of call:
* when the function result may have a part with access discriminants;
* when the function result type is an anonymous access type;
* when the function result is built in place;
* when the function has an explicitly aliased parameter.
In particular, this implies passing a level parameter when the result type
is class-wide, since descendants may add access discriminants. For most
implementations this will mean that functions with controlling results
must also have a level parameter.
Add a paragraph after 3.10.2(12.1/2) to cover the case of an access
discriminant whose value is defined by default:
+ If the value of the access discriminant is determined by a default
expression in the discriminant's declaration, the level of the object
or subprogram designated by the associated value (or library level
if null);
[AARM] Discussion: This covers the case of a unconstrained subcomponent
of a limited type with defaulted access discriminants.
Modify 3.10.2(14.5/3) (added as part of AI05-0051-1) as follows, deleting all
but the first sentence (deleted parts are addressed above as new bullets):
* Within a return statement, the accessibility level of the anonymous access
type of an access result is determined by the point of call. [If the call is
the operand of an explicit type conversion, the accessibility level is that
of the target access type of the conversion. If the call is an actual
parameter of another call or the prefix of a name, the accessibility level
is that of the innermost master of the call. If the call defines an access
discriminant, the level is the same as that given above for an object
created by an anonymous allocator that defines an access discriminant (even
if the access result is of an access-to-subprogram type). If the call
itself defines the result of a function with an access result, this rule
is applied recursively.]
Modify 4.8(10.1/3) (as modified by AI05-0051-1) as follows:
For any allocator, if the designated type of the allocator is class-wide,
then a check is made that the master of the type determined by the
subtype_indication, or by the tag of the value of the qualified_expression,
includes the elaboration of the type of the allocator. If {any part of} the
subtype determined by the subtype_indication or qualified_expression
{of the allocator (or by the tag of the value if the type of the qualified
expression is class-wide)} [of the allocator] has one or more access
discriminants, then a check is made that the accessibility level of the
anonymous access type of each access discriminant is not deeper than that
of the type of the allocator. Program_Error is raised if either such check
fails.
Modify 6.4.1(15.1/3) as follows:
* {AI05-0142-4} In a function call, for each explicitly aliased parameter,
a check is made that the accessibility level of the master of the actual
object is not deeper than that [of the master of the function result]
{determined by the point of call (see 3.10.2)}.
Modify 6.5(21/3) (as modified by AI05-0051-1) as follows:
If any part {of the specific type} of the return object {of a function} (or
coextension thereof) [of a function] has one or more access discriminants
whose value is not constrained by the result subtype of the function,
a check is made that the accessibility level of the anonymous access type
of each access discriminant, as determined by the expression or the
return_subtype_indication [of the function] {of the return statement},
is not deeper than the level of the return object as determined by the
point of call (see 3.10.2). If this check fails, Program_Error is raised.
Add the following AARM implementation note after 6.5(21/3):
The reason for saying "any part of the specific type" is to simplify
implementation. In the case of class-wide result objects this allows
the testing of a simple flag in the tagged type descriptor that indicates
whether specific type has any parts with access discriminants. By basing
the test on the object's type rather than the object itself, we avoid
concerns about the whether subcomponents of arrays (which might be empty)
and in variant parts are present.
Add a (massive) AARM note after 6.5(21/3):
For a function with a class-wide result type, the access values that
need to be checked are determined by the tag of the return object.
In order to implement this accessibility check in the case where
the tag of the result is not known statically at the point of the
return statement, an implementation may need to somehow
associate with the tag of a specific tagged
type an indication of whether the type has unconstrained access
discriminants (explicit or inherited) or has any subcomponents
with such discriminants. If an implementation is already maintaining
a statically initialized descriptor of some kind for each specific
tagged type, then an additional Boolean could be added to this
descriptor.
Note that the flag should only be queried in the case where any
access discriminants which the result object might have would
have subtypes with "bad" accessibility levels (as determined by
the rules of 3.10.2 for determining the accessibility level of
the type of an access discriminant in the expression or
return_subtype_indication of a return statement).
Thus, in a case like
type Global is access T'Class;
function F (Ptr : Global) return T'Class is
begin
return Ptr.all;
end F;
there is no need for a run-time accessibility check. The setting
of the bit doesn't matter and there is no need to query it.
On the other hand, given
function F return T'Class is
Local : T'Class := ... ;
begin
return Local;
end F;
In this case, a check would typically be required.
The need for including subcomponents in this check is illustrated by
the following example:
X : aliased Integer;
type Component_Type (Discrim : access Integer := X'access)
is limited null record;
type Undiscriminated is record
Fld : Component_Type;
end record;
function F return Undiscriminated is
Local : aliased Integer;
begin
return X : Undiscriminated := (Fld => (Discrim => Local'access)) do
Foo;
end return;
--
end F;
Ramification:
in the case where the tag of the result is not known statically
at the point of the return statement and the run-time accessibility
check is needed, discriminant values and array bounds
play no role in performing this check. That is, array components are
assumed to have nonzero length and components declared
within variant parts are assumed to be present. Thus, the check
may be implemented simply by testing the aforementioned descriptor
bit and conditionally raising Program_Error.
!discussion
The language already specifies accessibility checks for access discriminants in
the case of allocators (to ensure that discriminant values refer do not refer to
something that is shorter-lived than the access type) and for function results
(to ensure that discriminant values do not refer to something that is
shorter-lived than the eventual destination of the function result, as
determined by the point of call).
In the case where the designated type of an access type is class-wide, say
T'Class, it needs to be made clear that this check is based on the tag of the
allocated object. A check may be required even if, for example, T is
undiscriminated.
In the case where a function result type is class-wide, a similar check is
needed. The problem is illustrated by the following two examples:
procedure Cw_Alloc is
type Root is tagged null record;
type Ref is access Root'Class;
type Extension (Discrim : access Integer)
is new Root with null record;
function Bad_News return Ref is
Local_Int : aliased Integer := 123;
Local_Ext : aliased Extension (Discrim => Local_Int'access);
type Local_Ref is access all Root'Class;
for Local_Ref'Storage_Size use 0;
Local_Ptr : Local_Ref := Local_Ext'access;
begin
return new Root'Class'(Local_Ptr.all);
--
end Bad_News;
Ptr : Ref := Bad_News;
begin
--
...
end Cw_Alloc;
Here is a similar case involving a class-wide function result. This case
is properly handled by the wording changes of AI05-0051, but it serves
as a useful example where the same kind of checking is needed.
procedure Cw_Return is
type Root is tagged null record;
type Extension (Discrim : access Integer)
is new Root with null record;
function Bad_News return Root'Class is
Local_Int : aliased Integer := 123;
Local_Ext : aliased Extension (Discrim => Local_Int'access);
type Local_Ref is access all Root'Class;
for Local_Ref'Storage_Size use 0;
Local_Ptr : Local_Ref := Local_Ext'access;
begin
return Local_Ptr.all;
--
end Bad_News;
Obj : Root'Class := Bad_News;
begin
--
...
end Cw_Return;
In addition, these checks are designed to handle the case of a
subcomponent which has defaulted access discriminant values, as in:
X : aliased Integer;
type Subcomponent_Type (Discrim : access Integer := X'access)
is limited null record;
type Component_Type is record S : Subcomponent_Type; end record;
type Undiscriminated is record
Fld : Component_Type;
end record;
function F return Undiscriminated is
Local : aliased Integer;
begin
return X : Undiscriminated
:= (Fld => (S => (Discrim => Local'access))) do
Foo;
end return;
--
end F;
The main set of wording changes in this AI is formalizing the definition
in 3.10.2 of the term "determined by the point of call", which was already
used in 3.10.2(10.1/2), and several additional uses were added by the changes
of AI05-0051. We also made changes to the check done for allocators to
ensure that the check is done if any part of the object's subtype has
an access discriminant, to check explicitly aliased parameters based on
the level determined at the point of call, and to define the accessibility
level of the access type associated with defaulted access discriminants.
!example
!ACATS Test
Add an ACATS C-Test to check that the exception is raised in examples like the
ones given in this AI.
!ASIS
No impact.
!appendix
From: Steve Baird
Sent: Tuesday, November 2, 2010 8:08 PM
At the recent ARG meeting in Fairfax, we approved AI05-0051 with the
understanding that there remained a known dangling-reference problem involving
allocators which still needs to be addressed.
The following example illustrates the problem:
with Text_IO;
procedure Cw_Alloc is
type Root is tagged null record;
type Ref is access Root'Class;
type Extension (Discrim : access Integer)
is new Root with null record;
function Bad_News return Ref is
Local_Int : aliased Integer := 123;
Local_Ext : aliased Extension (Discrim => Local_Int'Access);
type Local_Ref is access all Root'Class;
for Local_Ref'Storage_Size use 0;
Local_Ptr : Local_Ref := Local_Ext'Access;
begin
return new Root'Class'(Local_Ptr.all);
-- allocator should fail, but RM 4.8(10) doesn't capture this
-- correctly (with or without the changes of AI05-0051)
end Bad_News;
Ptr : Ref := Bad_News;
procedure Do_Nothing is
begin
null;
end Do_Nothing;
begin
Text_IO.Put_Line
("*** Test failed - an exception should have been raised");
Text_IO.Put_Line
(" Ptr.Discrim.all ="
& Integer'Image (Extension (Ptr.all).Discrim.all));
Do_Nothing;
Text_IO.Put_Line
(" Ptr.Discrim.all ="
& Integer'Image (Extension (Ptr.all).Discrim.all));
end Cw_Alloc;
Using the Gnat compiler (version 6.3.2) on x86_64 Linux, the following command
gnatmake cw_alloc.adb -gnat05
produces an executable whose output is
*** Test failed - an exception should have been raised
Ptr.Discrim.all = 123
Ptr.Discrim.all = 0
Note that the call to Do_Nothing caused the value of Ptr.Discrim.all to change
from 123 to 0.
This is not a compiler bug - the Gnat compiler is, as far as I know, correctly
implementing all of the checks that the language definition requires for this
example.
The language definition is missing a check in this case.
****************************************************************
From: Tucker Taft
Sent: Tuesday, November 2, 2010 9:36 PM
Do we know whether the same problem might exist for the return statement? I
think there is some possibility of that.
****************************************************************
From: Steve Baird
Sent: Tuesday, November 2, 2010 9:50 PM
I think you are right; good point. I'll try to flesh this one
out tomorrow, but I see no reason that we won't
run into the same problem.
****************************************************************
From: Steve Baird
Sent: Friday, November 5, 2010 12:33 PM
You are right. Consider the following variation
on the original example:
with Text_IO;
procedure Cw_Return is
type Root is tagged null record;
type Extension (Discrim : access Integer)
is new Root with null record;
function Bad_News return Root'Class is
Local_Int : aliased Integer := 123;
Local_Ext : aliased Extension (Discrim => Local_Int'Access);
type Local_Ref is access all Root'Class;
for Local_Ref'Storage_Size use 0;
Local_Ptr : Local_Ref := Local_Ext'Access;
begin
return Local_Ptr.all;
-- return should fail, but RM 6.5(21) doesn't capture this
-- correctly (with or without the changes of AI05-0051)
end Bad_News;
Obj : Root'Class := Bad_News;
begin
Text_IO.Put_Line
("*** Test failed - an exception should have been raised");
Text_IO.Put_Line
(" Obj.Discrim.all ="
& Integer'Image (Extension (Obj).Discrim.all));
end Cw_Return;
Compiled and executed as described previously, resulting output is
*** Test failed - an exception should have been raised
Obj.Discrim.all = 32767
As before, this is not a compiler bug; this is a language definition
problem.
****************************************************************
From: Steve Baird
Sent: Monday, November 15, 2010 3:39 PM
How do we want to proceed on the "son of AI05-0051" problem?
If we can agree on a general approach, I can try to come up with wording to
throw rocks at on Friday.
To recap, we want to prevent (probably via some kind of runtime check, but that
is to be decided) two bad things:
- a function with a classwide result returning a value which has
an access discriminant which designates an object which is
too short-lived
- an allocator of an access-to-classwide type which allocates a
an object which has an access discriminant which designates an
object which is too short-lived
Randy and I have discussed an approach. It involves a tiny amount of distributed
overhead (an extra bit in the descriptor associated with a tagged type; the
value of the bit is known statically when the type is declared). Tuck may have
also mentioned this approach at the Fairfax meeting when we discussed this
problem for about 20 seconds - I'm not sure.
The bit indicates whether the given type has unconstrained access discriminants
(inherited or not).
To provide motivation, consider a very straightforward (albeit draconian)
solution. We add a runtime accessibility check at the point of the
return/allocator based on the accessibility level of the returned/allocated
"expression" (the term "expression" isn't quite general enough in the case of an
uninitialized allocator, but you get the idea).
This means that the following would raise P_E:
function F return T'Class is
Result : Some_Specific_Undiscriminated_Type := ''' ;
begin
...
return Result;
end F;
That seems like an unacceptable incompatibility.
So we add the rule that the check is only performed if the specific type in
question (which, in genreal, is not known until runtime) has one or more
unconstrained access discriminants.
Most implementations would need something like the aforementioned descriptor bit
in order to implement this.
This check is still conservative in the sense that it fails in come cases which
would be safe to allow, but handling types with no access discriminants is a
substantial improvement.
There are other (statically known) cases where we could safely omit the check.
Notably, we can be a lot more precise in the case of an immutably limited type,
although we still have to worry about, for example,
declare
Local : aliased T;
subtype S is My_Limited_Type (Discrim => Local'Access);
begin
Global_Ptr := new S;
end;
and we have to do the right thing in the usual build-in-place cases (an
aggregate, a function call, an extended-return-statement object).
In some cases, the accessibility level of the object designated by the
discriminant may be known (as opposed to conservatively using the accessibility
level of the discriminated object). For example, consider an aggregate where the
discriminant value is given explicitly as part of the aggregate.
It might seem odd to reject
return Some_Specific_Type
(Discrim => Some_Safe_Global'Access; ,,,,);
on the grounds that the the discriminant *might* refer to a local.
Or what about
return Some_Safe_Global_Constrained_Subtype (Acc_to_Cw.all);
So one question about this approach has to do with which, if any, special cases
we want to identify in order to make the test less conservative.
I think that we don't care about ancestor types (except to the extent that they
contribute inherited discriminants which noone has chosen to constrain),
If a more-distant ancestor type, T1, introduces an access discriminant, and a
less-distant ancestor type, T2, constrains it, then there are two cases:
The acceessibility level of T2 is such that it will or will not fail
the accessibility check on the specific *type* of an allocated or
returned value in the classwide case.
If it is going to fail the check, then we don't need another check.
Raising P_E once is good enough. If it is going to pass the check, then the
discriminant value that was supplied must be ok too, as it must designate
something that already existed when T2 was declared.
Thoughts?
****************************************************************
From: Randy Brukardt
Sent: Monday, November 15, 2010 3:57 PM
This message seems to adequetely sum up what Steve and I talked about, with one
minor (but important) exception:
The proposed check is purely a runtime check (since we cannot know in general
what the specific type returned is), but it would have to include both the
static and dynamic accessibility checks. That is necessary to avoid introducing
distributed overhead for parameters passed by reference. That happens since the
language is very clear that the dynamic accessibility of such a parameter is
that of the actual object, and we definitely don't want to have to pass that
accessibility with all reference parameters. The reason we don't have to do that
now is that the static accessibility check rejects any cases where it could
matter -- they're already illegal. We cannot change that without adding major
runtime overhead (this was the same problem that made handling conditional
expressions such a mess).
This clearly is a wording challenge, but I don't think it can be helped. (And
hardly anyone will ever run into this in practice.)
****************************************************************
From: Tucker Taft
Sent: Monday, November 15, 2010 5:07 PM
I was thinking about this a bit and came to the conclusion that paragraphs
12-12.4 in 3.10.2 might already answer most of your questions. Basically it says
what is the accessibility level of the discriminants, and I believe it handles
some of the special cases you mention.
The most recent wording for 4.8(10.1) for AI05-51/09 says:
If the subtype determined by the subtype_indication or
qualified_expression (or by the tag of the value of the
qualified_expression if the type of the qualified_expression
is class-wide) of the allocator has one or more access
discriminants, then a check is made that the accessibility
level of the anonymous access type of each access
discriminant is not deeper than that of the type of the
allocator. Program_Error is raised if either such check fails.
That says that the tag is checked if class-wide, which implies that the
"has-access-discriminants" bit you mention needs to exist.
The most recent AI05-51 wording for 6.5(21) says:
If any part of the return object (or coextension
thereof) of a function has one or more access
discriminants whose value is not constrained by
the result subtype of the function, a check is
made that the accessibility level of the anonymous
access type of each access discriminant, as
determined by the expression or the return_
subtype_indication of the function, is not deeper
than the level of the return object as determined
by the point of call (see 3.10.2). If this check
fails, Program_Error is raised.
Although it doesn't explicitly say that the tag is checked, it sort of implies
it in saying that the level of the access discriminants is "determined by the
expression," and 3.10.2(12-12.4) indicates what that means. We could be more
explicit about checking the tag, as the wording is in 4.8(10.1).
Hence, I don't see that we need anything more than a "tweak" of the AI05-51
wording.
As far as the "static" check, I'll need Randy to give an example of what he has
in mind there. I don't see how the value of the tag can possibly affect the
static check.
****************************************************************
From: Steve Baird
Sent: Monday, November 15, 2010 5:47 PM
> I was thinking about this a bit and came to the conclusion that
> paragraphs 12-12.4 in 3.10.2 might already answer most of your
> questions.
> Basically it says what is the accessibility level of the
> discriminants, and I believe it handles some of the special cases you
> mention.
I agree. I had forgotten about those rules.
> The most recent wording for 4.8(10.1) for AI05-51/09 says:
>
> If the subtype determined by the subtype_indication or
> qualified_expression (or by the tag of the value of the
> qualified_expression if the type of the qualified_expression
> is class-wide) of the allocator has one or more access
> discriminants, then a check is made that the accessibility
> level of the anonymous access type of each access
> discriminant is not deeper than that of the type of the
> allocator. Program_Error is raised if either such check fails.
>
> That says that the tag is checked if class-wide, which implies that
> the "has-access-discriminants" bit you mention needs to exist.
>
> ...
> Hence, I don't see that we need anything more than a "tweak" of the
> AI05-51 wording.
I believe that we did not approve AI05-51/09 at the last meeting; we backed out
of my proposed changes and approved, in effect, AI05-51/08, You expressed
concern at the time that the wording proposed in version 09 would require
storing an accessibility level for each access discriminant at runtime, so we
decided to close up AI05-0051 and deal with the problems we are discussing today
in a separate AI.
If we decide that it makes sense after all, we can obviously still adopt the
wording of AI05-51/09 or some variation thereon.
I'm just pointing out that the baseline that we are working from today does not
include those version 09 changes (if I am remembering what happened in Fairfax
correctly).
****************************************************************
From: Tucker Taft
Sent: Monday, November 15, 2010 5:51 PM
> I'm just pointing out that the baseline that we are working from today
> does not include those version 09 changes (if I am remembering what
> happened in Fairfax correctly).
Thanks for clarifying that. I guess I need to go back and look at version 08.
But having accepted the fact that we need the "has-access-discriminants" bit,
coupled with a re-reading of 3.10.2(12-12.4) I now think your proposed wording
in version 09 looks pretty good.
****************************************************************
From: Randy Brukardt
Sent: Monday, November 15, 2010 5:57 PM
...
> I believe that we did not approve AI05-51/09 at the last meeting; we
> backed out of my proposed changes and approved, in effect, AI05-51/08,
> You expressed concern at the time that the wording proposed in version
> 09 would require storing an accessibility level for each access
> discriminant at runtime, so we decided to close up
> AI05-0051 and deal with the problems we are discussing today in a
> separate AI.
My notes end with:
We decide to revert to the previous version of this wording, and create a new AI
to address this specific problem. (Especially as AI-142-4 also modified this
paragraph.)
So Steve's memory is correct. Forget the /09 version of the AI.
(Note that /09 wording doesn't make a lot of sense to me, as it seems to mix
statically determined and dynamically known types in the same sentence without
explaining which is which.)
****************************************************************
From: Steve Baird
Sent: Monday, November 15, 2010 4:53 PM
>... the language is very clear that the dynamic accessibility of such
>a parameter is that of the actual object, and we definitely don't want
>to have to pass that accessibility with all reference parameters.
I'm not disagreeing with you because I see the argument: A dynamic accessibility
level is a property of an object (as opposed to a view of an object) and a
by-reference parameter doesn't introduce a new object - just a view of an
existing object.
But how do you reconcile this with 3.10.2(7/2):
A parameter of a master has the same accessibility level
as the master.
?
****************************************************************
From: Randy Brukardt
Sent: Monday, November 15, 2010 5:12 PM
> But how do you reconcile this with 3.10.2(7/2):
> A parameter of a master has the same accessibility level
> as the master.
> ?
I can't, because out of context this doesn't make any sense at all. Masters
don't have parameters; a master is a execution or evaluation of something
(typically a statement), so I'm not quite sure what this is referring to. In
context, it appears to be talking about parameters of declarations, but that
doesn't make much sense either with this particular wording.
I presume this is intended to do two things: define the *static* accessibility
of parameters, and define the *dynamic* accessibility of by-copy parameters, but
it is badly botched. Besides the by-reference case, it also is confused with
access parameters that have some other accessibility defined (either that of the
passed-in entity or "deeper than anything else"). This wording makes no sense
for dynamic accessibility (with which it is associated), period.
I would prefer to invoke the Duff rule on this one (the ARG is not in the
business of answering questions that no one - other than ARG members musing
around - has asked). But perhaps there is really something wrong here (not
surprising, almost everything about accessibility is screwy in one way or
another).
****************************************************************
From: Tucker Taft
Sent: Monday, November 15, 2010 5:37 PM
Dynamic accessibility level *can* vary
depending on the "view." Parameters
in general are treated like local variables, even when of a type that requires
pass by reference. The one exception is explicitly aliased parameters, where
they are guaranteed to live at least as long as the return object.
Similarly, even if an access discriminant is initialized to designate a global
variable, the dynamic accessibility level of a dereference of the access
discriminant is (generally) determined by the dynamic accessibility level of the
enclosing object.
So dynamic accessibility levels do vary
according to the view.
****************************************************************
From: Randy Brukardt
Sent: Monday, November 15, 2010 5:25 PM
...
> As far as the "static" check, I'll need Randy to give an example of
> what he has in mind there.
> I don't see how the value of the tag can possibly affect the static
> check.
Maybe I'm confused, but in the examples that Steve showed, the accessibility
check was against the level of the expression, not the level of the type. That's
a check that you cannot make normally at compile-time, as you would fail an
accessibility check in normal cases.
For example, assuming T is a tagged non-limited type, and TT is an extension of
it at the same level:
function Fooey (P : in TT; ...) return T'Class is
begin
if ... then
return T'Class(P); -- OK in Ada 95 and Ada 2005
else
...
end if;
end Fooey;
This return statement is legal in Ada 2005. There would be an accessibility
check on the tag of TT, but it would staticly be OK.
Now further presume that TT has an access discriminant. If we had directly
returned the object TT, we would have needed an accessibility check on the
object. In that case, the static accessibility check would have failed (we
assume that P is local for this purpose). However, if we didn't have that static
accessibility check, then we would have to use the accessibility of the actual
object, which would have to be passed it (adding overhead). The only reason we
don't need that overhead is because we have the static check that fails.
Now, in the classwide case, we have *only* a runtime check, because in general
we know nothing about the tag until runtime. That means that unless we add some
sort of wording about the static check to the dynamic case, we have a
distributed overhead in that every reference parameter would have to pass a
dynamic accessibility level (in particular, that would be needed for all tagged
parameters).
Note that the check you described would always pass if Fooey was called with a
library-level actual parameter, but should fail if called with a local actual
parameter. That's what we have to avoid.
****************************************************************
From: Tucker Taft
Sent: Monday, November 15, 2010 5:48 PM
> Now further presume that TT has an access discriminant. If we had
> directly returned the object TT, we would have needed an accessibility
> check on the object. In that case, the static accessibility check
> would have failed (we assume that P is local for this purpose).
> However, if we didn't have that static accessibility check, then we
> would have to use the accessibility of the actual object, which would
> have to be passed it (adding overhead). The only reason we don't need
> that overhead is because we have the static check that fails.
The dynamic accessibility level of a parameter, even if passed by reference, is
like that of a local variable. Just think what happens when you take 'Access of
a tagged parameter in Ada 95 and pass it as an access parameter. It gets a
run-time accessibility level that makes it look like a local variable.
The new "aliased" parameters are a bit different, in that they promise to live
at least as long as the return object, so this example should be fine, even in
Ada 2012.
> Now, in the classwide case, we have *only* a runtime check, because in
> general we know nothing about the tag until runtime. That means that
> unless we add some sort of wording about the static check to the
> dynamic case, we have a distributed overhead in that every reference
> parameter would have to pass a dynamic accessibility level (in
> particular, that would be needed for all tagged parameters).
Again, the "dynamic" accessibility level of a parameter is "local" even if the
actual parameter is a global object.
> Note that the check you described would always pass if Fooey was
> called with a library-level actual parameter, but should fail if
> called with a local actual parameter. That's what we have to avoid.
In cases like this, the accessibility level of an access discriminant is the
same as the enclosing object, and that is "local" since it is a parameter. That
means you don't need to pass anything in, and it will fail the run-time check if
the actual object has access discriminants, even if the actual object is a
global object.
I think the net effect is that the "static" check you were mentioning is
*already* a part of the "dynamic" check, since parameters lose their "real"
accessibility level and take on the accessibility of a local variable.
****************************************************************
From: Randy Brukardt
Sent: Monday, November 15, 2010 5:52 PM
> Dynamic accessibility level *can* vary depending on the "view."
> Parameters in general are treated like local variables, even when of a
> type that requires pass by reference.
This is an oxymoron. Views are static properties that do not have any effect on
dynamic properties. It doesn't make any sense.
Beyond that, the one place in the standard that gives some credence to this view
is clearly wrong, in that it is contradicted later in the same clause when
access parameters are discussed.
We never actually decided the question of whether by-reference parameters are
objects in their own right, or whether they are just views of the actuals. It
appears that we will need to do something on this line (either that, or fix up
the wording to make sense).
> The one exception is
> explicitly aliased parameters, where they are guaranteed to live at
> least as long as the return object.
>
> Similarly, even if an access discriminant is initialized to designate
> a global variable, the dynamic accessibility level of a dereference of
> the access discriminant is (generally) determined by the dynamic
> accessibility level of the enclosing object.
The accessibility is that of the access discriminant (which is that of the
enclosing object). So what? I don't see your point here.
> So dynamic accessibility levels do vary according to the view.
Well, that simply doesn't make sense in the Ada model. Of course, nothing else
about accessibility makes sense, either, so perhaps we can get away with it. But
if that is what we want, it needs to be clearly explained in the AARM, and it is
not.
Remember that "view of" can automatically be appended to static semantic rules,
but not to dynamic rules. If you mean that, you had better say so. Yes, I see
these rules are defined in "Static semantics", but that makes no sense for a
dynamic value.
Accessibility is still badly defined. What else is new.
****************************************************************
From: Bob Duff
Sent: Monday, November 15, 2010 6:11 PM
> > But how do you reconcile this with 3.10.2(7/2):
> > A parameter of a master has the same accessibility level
> > as the master.
> > ?
I didn't see the above-quoted message from Steve.
Tucker said in another message that dynamic accessibility is a view property. I
agree -- it has to be that way, because by-ref and by-copy are (sometimes)
impl-def, and we don't want the semantics to differ (other than the essential
difference in aliasing). I realize we're talking about tagged types in this
conversation, but I'm just saying in general, passing something by ref shouldn't
cause differences in accessibility level and so forth.
> I can't, because out of context this doesn't make any sense at all.
> Masters don't have parameters; a master is a execution or evaluation
> of something (typically a statement), so I'm not quite sure what this is
> referring to.
It's talking about a master that is the execution of a procedure body, and the
(formal) parameters of that. These are the plain old run-of-the-mill masters we
got from Ada 83 -- all these "little" masters like statements were added since
then.
****************************************************************
From: Steve Baird
Sent: Monday, November 15, 2010 6:31 PM
Thanks to Tuck and Bob for straightening that one out.
Ignoring new-fangled explicitly aliased parameters, a parameter pretty much
behaves like a local with respect to accessibility.
In most cases, one is more likely to arrive at the right answer if you go back
to the RM and read the relevant wording instead of relying on internalized "I'm
sure I remember it works this way" knowledge. In the case of accessibility
rules, however, every rereading is an opportunity to get confused all over
again.
****************************************************************
From: Randy Brukardt
Sent: Monday, November 15, 2010 6:35 PM
...
> > > But how do you reconcile this with 3.10.2(7/2):
> > > A parameter of a master has the same accessibility level
> > > as the master.
> > > ?
>
> I didn't see the above-quoted message from Steve.
Steve sent it just to me, not sure if that was by accident or on purpose, but
since I wanted to get the Tucker and Bob take on it, I replied to the list of
names.
> Tucker said in another message that dynamic accessibility is a view
> property. I agree -- it has to be that way, because by-ref and
> by-copy are (sometimes) impl-def, and we don't want the semantics to
> differ (other than the essential difference in aliasing). I realize
> we're talking about tagged types in this conversation, but I'm just
> saying in general, passing something by ref shouldn't cause
> differences in accessibility level and so forth.
>
> > I can't, because out of context this doesn't make any sense at all.
> > Masters don't have parameters; a master is a execution or evaluation
> > of something (typically a statement), so I'm not quite sure what
> > this is referring to.
>
> It's talking about a master that is the execution of a procedure body,
> and the (formal) parameters of that. These are the plain old
> run-of-the-mill masters we got from Ada 83
> -- all these "little" masters like statements were added since then.
It would be nice if this wording said that. Especially the word "formal", in
which case it makes at least some sense.
"A {formal} parameter of {the entity that} a master {is executing} has the same
accessibility level as the master."
Or something like that. Plus we need an exception for access parameters (because
this rule surely doesn't not apply to them), and for explicitly aliased
parameters. So
"{Unless otherwise defined, } a {formal} parameter of {the entity that} a master
{is executing} has the same accessibility level as the master."
And we also need an AARM note to explain that.
"AARM Ramification: Note that accessibility can differ depending on the view of
an object (for both static and dynamic accessibility). In addition to the formal
parameter case above, similar effects occur for the entities designated by
access discriminants."
"AARM Reason: We define the (dynamic) accessibility of formal parameters in
order that it not depend on the parameter passing model (by-reference or
by-copy) as that is implementation defined. Otherwise, there would be a
portability issue."
Or something like that (not sure we really want to list out examples, there may
be more). Since we never actually say "dynamic accessibility in the standard, I
put that in parens in the notes.
P.S. Note that my original concern probably still exists for explicitly aliased
parameters used in an allocator, although I doubt anyone will ever write a test
to check that case. ;-)
****************************************************************
From: Bob Duff
Sent: Monday, November 15, 2010 6:43 PM
> Or something like that. Plus we need an exception for access
> parameters (because this rule surely doesn't not apply to them), and
> for explicitly aliased parameters. So
I mostly agree with your message, but I don't see the problem for access
parameters. Are you talking about the parameter itself, or what it designates?
"X: access T", the accessibility level of X is not relevant, since it's not
aliased, so X'[Unchecked_]Access is illegal. The accessibility level of X.all
is dynamic, but X.all is not a parameter so the rule we're talking about doesn't
apply.
The new aliased parameters may well need more thought.
****************************************************************
From: Randy Brukardt
Sent: Tuesday, November 16, 2010 12:23 AM
> > Or something like that. Plus we need an exception for access
> > parameters (because this rule surely doesn't not apply to them), and
> > for explicitly aliased parameters. So
>
> I mostly agree with your message, but I don't see the problem for
> access parameters. Are you talking about the parameter itself, or
> what it designates? "X: access T", the accessibility level of X is
> not relevant, since it's not aliased, so X'[Unchecked_]Access is
> illegal. The accessibility level of X.all is dynamic, but X.all is
> not a parameter so the rule we're talking about doesn't apply.
I suppose you are right.
> The new aliased parameters may well need more thought.
They don't need "more thought", they have their own accessibility rule
(3.10.2(13.3/3)). I wasn't aware of this older blanket rule, or I would have
added wording to repeal it in that case.
They probably need "more thought" for AI05-0234-1, because I'd expect that the
static/dynamic case would come up for allocators (as Tucker points out, they
have to pass a return statement).
BTW, it seems that we really want formal by-reference parameters to be a new
object that happens to share memory with the old object. Besides this
accessibility rule (where claiming that dynamic accessibility is a view property
is unappealing), we also had similar issues with the values of representation
aspects like 'Size and 'Alignment. We surely don't want to have to pass those
along with parameters just so we can report them accurately. (I recall we
decided that wasn't necessary with some phony hand-waving.) I wouldn't be
surprised if this keeps coming up until we finally give in and change the
definition...
****************************************************************
From: Steve Baird
Sent: Tuesday, November 16, 2010 2:21 PM
> Hence, I don't see that we need anything more than a "tweak" of the
> AI05-51 wording.
> ....
> Although it doesn't explicitly say that the tag is checked, it sort of
> implies it in saying that the level of the access discriminants is
> "determined by the expression," and 3.10.2(12-12.4) indicates what
> that means. We could be more explicit about checking the tag, as the
> wording is in 4.8(10.1).
I think that perhaps the version 09 RM wording is fine as is.
When we say
"any part of the return object (or coextension thereof)", I don't see any
need to add
"Oh and, by the way, you might need to look at the object's
tag in order to identify the set of parts we are talking
about here".
, at least in the RM. An AARM note along those lines would be a good idea.
Do you see other changes/tweaks to the version 09 wording that are needed?
I'm trying to determine what, if any, work remains to be done for this AI.
Perhaps just adding an AARM note as mentioned above to the existing version 09
wording?
I am assuming that any changes that may result from Randy's discussion of
whether the definition of the accessibility level of a formal needs revision
will go in another AI.
****************************************************************
From: Tucker Taft
Sent: Tuesday, November 16, 2010 2:44 PM
> When we say
> "any part of the return object (or coextension thereof)", I don't see
> any need to add "Oh and, by the way, you might need to look at the
> object's tag in order to identify the set of parts we are talking
> about here".
> , at least in the RM. An AARM note along those lines would be a good
> idea.
I find it a bit uncomfortable that we mention the tag when talking about the
check on allocators but don't mention it when talking about the check on a
return statement, but I guess I can get over it. I definitely think an AARM note
is in order to emphasize that the need for the check may depend on the value of
the run-time tag when the type is classwide.
> Do you see other changes/tweaks to the version 09 wording that are
> needed?
>
> I'm trying to determine what, if any, work remains to be done for this
> AI. Perhaps just adding an AARM note as mentioned above to the
> existing version 09 wording?
Yes, that seem to be all that is necessary.
****************************************************************
From: Randy Brukardt
Sent: Tuesday, November 16, 2010 3:16 PM
...
> I find it a bit uncomfortable that we mention the tag when talking
> about the check on allocators but don't mention it when talking about
> the check on a return statement, but I guess I can get over it.
> I definitely think an AARM note is in order to emphasize that the need
> for the check may depend on the value of the run-time tag when the
> type is classwide.
I agree with Tucker here, except that I disagree on the "can get over it".
Moreover, I'm concerned that the intent is that the checks depends on some
runtime effect when nothing actually says that.
The /09 wording for return statements appears to be
If the subtype determined by the expression of the simple_return_statement or by
the return_subtype_indication...
This seems to be talking about the nominal subtype of the expression, simply
because there is no such thing as a runtime subtype. That means for a class-wide
type, you are asking whether the class-wide type has access discriminants, which
is not what we want. If you want to talk about the type determined by the tag, I
think you need to say that explicitly:
If the subtype determined by the expression of the simple_return_statement or by
the return_subtype_indication (or the type indicated by the tag if the
determined subtype is class-wide) has one or more access discriminants...
or something like this. If not, Adam will complain. I think this is way too much
for a to-be-honest note.
...
> > I am assuming that any changes that may result from Randy's
> > discussion of whether the definition of the accessibility level of a
> > formal needs revision will go in another AI.
My head hurts just thinking about thinking about it. (That is, this is a
meta-pain.)
****************************************************************
From: Bob Duff
Sent: Tuesday, November 16, 2010 3:22 PM
> I'm trying to determine what, if any, work remains to be done for this
> AI. Perhaps just adding an AARM note as mentioned above to the
> existing version 09 wording?
Plus the intended implementation model (the bit in the type descriptor).
****************************************************************
From: Steve Baird
Sent: Tuesday, November 16, 2010 3:30 PM
> Plus the intended implementation model (the bit in the type
> descriptor).
Good point.
****************************************************************
From: Tucker Taft
Sent: Tuesday, November 16, 2010 3:41 PM
> This seems to be talking about the nominal subtype of the expression,
> simply because there is no such thing as a runtime subtype. That means
> for a class-wide type, you are asking whether the class-wide type has
> access discriminants, which is not what we want. If you want to talk
> about the type determined by the tag, I think you need to say that explicitly:
>
> If the subtype determined by the expression of the
> simple_return_statement or by the return_subtype_indication (or the
> type indicated by the tag if the determined subtype is class-wide) has
> one or more access discriminants...
This isn't the part where the tag comes into play. It is in:
Modify 6.5(21/2) as follows:
If [the result subtype]{any part of the return object (or coextension
thereof)} of a function has one or more [unconstrained] access
discriminants {whose value is not constrained by the result subtype of
the function}, a check is made that the accessibility level of the
anonymous access type of each access discriminant, as determined by
the expression or the return_subtype_indication of the function, is
not deeper than [that of the master that elaborated the function body]
{the level of the return object as determined by the point of call
(see 3.10.2)}. If this check fails, Program_Error is raised.
That mammoth first sentence will need to be broken up a bit if we want to make
it clear that whether the return object or its coextensions have discriminants
may involve checking some run-time tags. Something like the following might
accomplish this:
A check is made whether any part of the return object (or any coextension thereof) of
a function has one or more access discriminants whose value is not
constrained by the result of the function; when the return object or any
coextension is of a class-wide type, this depends on the tag of the
object. If any such access discriminants exist, then a check is made
that the accessibility level of ...
Pretty groddy, but perhaps clearer (and Adam proof?).
****************************************************************
From: Steve Baird
Sent: Wednesday, November 17, 2010 7:04 PM
> A check is made whether any part of the return object (or any
> coextension thereof) of
> a function has one or more access discriminants whose value is not
> constrained by the result of the function; when the return object
> or any
> coextension is of a class-wide type, this depends on the tag of the
> object. If any such access discriminants exist, then a check is made
> that the accessibility level of ...
>
> Pretty groddy, but perhaps clearer (and Adam proof?).
A "check" which does not raise an exception or have any other similar
consequence seems peculiar to me.
Generally, we have wording of the form
A check is made that blah blah.
Mumble_Error is raised if this check fails.
It seems odd to see
A check is made whether this object has any discriminants.
If it does, then a check is made that .... Program_Error
is raised if this second check fails.
So here are my suggested changes, relative to the recently posted version 13 of AI05-0051.
====
Modify 4.8(10.1/2) as follows:
For any allocator, if the designated type of the type of the
allocator is class-wide, then a check is made that the
accessibility level of the type determined by the
subtype_indication, or by the tag of the value of the
qualified_expression, is not deeper than that of the type of the
allocator. If the subtype determined by the subtype_indication or
qualified_ expression (or by the tag of the value of the qualified
expression if the type of the qualified expression is class_wide)
of the allocator has one or more access discriminants, then a
check is made that the accessibility level of the anonymous access
type of each access discriminant is not deeper than that of the
type of the allocator. Program_Error is raised if either such check
fails.
The only change is the parenthesized "(or by the tag ...)" wording.
In 6.5(21/2) (as modified by AI05-0051), we've currently got
If any part of the return object (or coextension thereof) of a
function has one or more access discriminants whose value is not
constrained by the result subtype of the function, a check is made
that the accessibility level of the anonymous access type of each
access discriminant, as determined by the expression or the
return_subtype_indication of the function, is not deeper than the
level of the return object as determined by the point of call (see
3.10.2). If this check fails, Program_Error is raised.
Are we missing an article inside the parens?
Should that be "(or a coextension thereof)" or perhaps "(or any coextension
thereof)"? Flip a coin, but either seems like an improvement. Splitting the
phrase "return object of a function" by adding parens in the middle also seems
confusing when I read it.
In the phrase "the expression or the return_subtype_indication of the function",
should "of the function" be replaced by "of the executed return statement"? Does
it make sense to talk about, for example, *the* return_subtype_indication of a
function?
Or should that whole ", as determined
by ... of the function," clause just be deleted?
Otherwise, how does the current wording apply in a case like
return X : T'Class := ... do ... end return;
, where there is no expression and T'Class does not determine whether ther
return object has access discriminants. So delete the clause.
This all leaves us with
If any part of the return object of a function (or a coextension
thereof) has one or more access discriminants whose value is not
constrained by the result subtype of the function, a check is made
that the accessibility level of the anonymous access type of each
access discriminant is not deeper than the
level of the return object as determined by the point of call (see
3.10.2). If this check fails, Program_Error is raised.
Add an AARM note after 6.5(21/2):
For a function with a classwide result type, the access values that
need to be checked are determined by the tag of the return object.
In order to implement this accessibility check, an implementation may
need to maintain data structures at runtime to keep track of
whether a given specific tagged type (as identified by its tag)
has unconstrained access discriminants (explicit or inherited)
or has any subcomponents with such discriminants.
If an implementation is already maintaining a runtime descriptor
of some kind for each tagged type, then an additional Boolean
could be added to this descriptor. In the cases where this flag
needs to be queried, the accessibility level of the
access dicriminant(s) (if any) is that of the return object.
Do we need an AARM note to make it clear that this wording handles the following
example?
X : aliased Integer;
type Component_Type (Discrim : access Integer := X'Access) is limited
null record;
type Undiscriminated is record F : Component_Type; end record;
function F return Undiscriminated is
Local : aliaed Integer;
begin
return X : Untagged := (F => (Discrim => Local'Access)) do
Foo;
end return;
-- raises P_E after calling Foo.
end F;
I think no note is needed - the rule for this example follows from the "or has
any subcomponents" wording above.
This means that components with defaulted discrims must participate in the
computation of the aforementioned descriptor bit. On the other hand, a component
subject to a propagated discriminant constraint doesn't affect the setting of
the bit.
Note that we don't raise P_E before calling Foo because of
3.10.2(10.1/2):
Within a return statement, the accessibility level of the return
object is that of the execution of the return statement. If the
return statement completes normally by returning from the function,
then prior to leaving the function, the accessibility level of the
return object changes to be a level determined by the point of call,
as does the level of any coextensions (see below) of the return
object.
Do we need to explicitly state that we don't want to distinguish between present
and inactive variant fields. Suppose that type Untagged in the preceding example
had a Boolean discriminant which governed a variant part and the declaration of
the F component was moved into one of the arms of the variant part. I think we
want to look at tags at runtime, but not at discriminant values.
Does this need a TBH note?
****************************************************************
From: Tucker Taft
Sent: Wednesday, November 17, 2010 8:20 PM
> This all leaves us with
>
> If any part of the return object of a function (or a coextension
> thereof) has one or more access discriminants whose value is not
> constrained by the result subtype of the function, a check is made
> that the accessibility level of the anonymous access type of each
> access discriminant is not deeper than the level of the return object
> as determined by the point of call (see 3.10.2). If this check fails,
> Program_Error is raised.
Sounds good to me.
>
> Add an AARM note after 6.5(21/2):
>
> For a function with a classwide result type, the access values that
> need to be checked are determined by the tag of the return object.
> In order to implement this accessibility check, an implementation may
> need to maintain data structures at runtime to keep track of whether a
> given specific tagged type (as identified by its tag) has
> unconstrained access discriminants (explicit or inherited) or has any
> subcomponents with such discriminants.
> If an implementation is already maintaining a runtime descriptor of
> some kind for each tagged type, then an additional Boolean could be
> added to this descriptor. In the cases where this flag needs to be
> queried, the accessibility level of the access dicriminant(s) (if any)
> is that of the return object.
>
> Do we need an AARM note to make it clear that this wording handles the
> following example?
I wouldn't hurt to include this example in the AARM note.
>
> X : aliased Integer;
>
> type Component_Type (Discrim : access Integer := X'Access) is limited
> null record;
>
> type Undiscriminated is record F : Component_Type; end record;
>
> function F return Undiscriminated is
> Local : aliased Integer;
> begin
> return X : Untagged := (F => (Discrim => Local'Access)) do Foo; end
> return;
> -- raises P_E after calling Foo.
> end F;
>
> I think no note is needed - the rule for this example follows from the
> "or has any subcomponents" wording above.
I agree it isn't necessary but it wouldn't hurt.
>
> This means that components with defaulted discrims must participate in
> the computation of the aforementioned descriptor bit.
> On the other hand, a component subject to a propagated discriminant
> constraint doesn't affect the setting of the bit.
>
> Note that we don't raise P_E before calling Foo because of
> 3.10.2(10.1/2):
>
> Within a return statement, the accessibility level of the return
> object is that of the execution of the return statement. If the return
> statement completes normally by returning from the function, then
> prior to leaving the function, the accessibility level of the return
> object changes to be a level determined by the point of call, as does
> the level of any coextensions (see below) of the return object.
>
> Do we need to explicitly state that we don't want to distinguish
> between present and inactive variant fields. Suppose that type
> Untagged in the preceding example had a Boolean discriminant which
> governed a variant part and the declaration of the F component was
> moved into one of the arms of the variant part. I think we want to
> look at tags at runtime, but not at discriminant values.
Agreed.
>
> Does this need a TBH note?
Yes, probably.
****************************************************************
From: Randy Brukardt
Sent: Wednesday, November 17, 2010 9:39 PM
...
> This all leaves us with
>
> If any part of the return object of a function (or a coextension
> thereof) has one or more access discriminants whose value is not
> constrained by the result subtype of the function, a check is made
> that the accessibility level of the anonymous access type of each
> access discriminant is not deeper than the
> level of the return object as determined by the point of call (see
> 3.10.2). If this check fails, Program_Error is raised.
I think this is OK, as it doesn't seem to say that you can figure out whether
the check is needed statically.
> Add an AARM note after 6.5(21/2):
>
> For a function with a classwide result type, the access values that
> need to be checked are determined by the tag of the return object.
> In order to implement this accessibility check, an implementation may
> need to maintain data structures at runtime to keep track of
> whether a given specific tagged type (as identified by its tag)
> has unconstrained access discriminants (explicit or inherited)
> or has any subcomponents with such discriminants.
> If an implementation is already maintaining a runtime descriptor
> of some kind for each tagged type, then an additional Boolean
> could be added to this descriptor. In the cases where this flag
> needs to be queried, the accessibility level of the
> access dicriminant(s) (if any) is that of the return object.
Is it possible to implement a tag without some sort of runtime descriptor? It
would seem that the model of tagged types require such a descriptor somewhere.
Note that the "runtime descriptor" is likely to be built at compile-time, and
there isn't any need for this flag to be any different in that respect. So I
think this note is misleading.
I would have said something like:
For a function with a classwide result type, the access values that
need to be checked are determined by the tag of the return object.
In order to implement this accessibility check, an implementation
probably will need to associate with the tag of a specific tagged
type an indication of whether the type has unconstrained access
discriminants (explicit or inherited) or has any subcomponents
with such discriminants. In the cases where this flag
needs to be queried, the accessibility level of the
access dicriminant(s) (if any) is that of the return object.
Which is simpler and doesn't confuse compile-time determined indications with
the runtime check of the flag.
...
> Do we need to explicitly state that we don't want to distinguish
> between present and inactive variant fields.
> Suppose that type Untagged in the preceding example had a Boolean
> discriminant which governed a variant part and the declaration of the
> F component was moved into one of the arms of the variant part. I
> think we want to look at tags at runtime, but not at discriminant
> values.
>
> Does this need a TBH note?
This is clearly true based on the wording you gave, but it wouldn't hurt to have
a Ramification that makes it clear that we intended that result. It's not a TBH,
simply because the wording says precisely this ("any part" means exactly that,
not any part that happens to be currently selected).
****************************************************************
From: Steve Baird
Sent: Thursday, November 18, 2010 7:19 PM
[Most of this note became version /01 of the AI - Editor.]
...
To be honest:
In the case where the tag of the result is not known statically
at the point of the return statement and the runtime accessibility
check is needed, discriminant values and array bounds
play no role in performing this check. That is, array components are
assumed to have non-zero length and components declared
within variant parts are assumed to be present Thus, the check
may be implemented simply by testing the aforementioned descriptor
bit and conditionally raising Program_Error.
[Randy - if I understand you correctly, you don't think this needs a
TBH label. My concern is in the opposite direction - it seems to
me that this requires explicit RM wording. We haven't talked
at all about the "vanilla" (i.e., specific result type) case, but
what about
Might_Be_Zero : Natural := ...;
type T (D : access Integer := null) is
limited tagged null record;
type Vector is array (1 .. Might_Be_Zero) of T;
function F return Vector is
Local : aliased Integer;
Result : Vector := (others => (D => Local'Access));
begin
return Result;
end F;
Clearly this fails the accessibility check if Might_Be_Zero
is nonzero. What happens if Might_Be_Zero equals zero?
It would be odd to allow conservative "assume all arrays
have non-zero length" behavior in the classwide result type
case described above, but not in this case. It seems that
this needs to be nailed down one way or the other. Bother!
I was hoping we were almost done with this one.]
****************************************************************
From: Randy Brukardt
Sent: Thursday, November 18, 2010 10:15 PM
I don't see the problem. The operative wording is "any part of the return
object"; "any part" is something that has to be determined statically, and thus
the values of bounds and discriminants are irrelevant (they cannot be known in
general).
If there is a problem, it occurs because this is a dynamic semantics rule, and
thus someone could be confused by the fact that "any part" is determined
statically. (It is *always* determined statically, so far as I know, but that
isn't obvious.)
If this was just English wording, I would probably suggest adding "possible":
If any possible part of the return object...
But that isn't formal enough. Probably you are right and normative words need to
be added:
If any part of the return object (ignoring the values of bounds and
discriminants) of a function ...
****************************************************************
From: Steve Baird
Sent: Tuesday, January 25, 2011 5:40 PM
Ok, I'll produce wording.
Here are the two mail messages on the subject that were sent earlier.
As usual, any feedback will be appreciated.
==== Message #1 ====
When an object is built in place, the anonymous object and the object being
initialized which it "mutates" into (as described in 7.6(17.5/3) and following)
may have different accessibility levels.
In some cases this leads to problems when a dynamic accessibility check is
performed against the object's current accessibility level rather than against
the level it is eventually going to have.
AI05-0051 deals with parts of this situation by performing checks against an
accessibility level "as determined by the point of call" but it appears that
some cases involving coextensions are are not handled correctly (and the precise
meaning of this wording is not obvious).
In the case of an aggregate, the situation is even worse.
There are no dynamic accessibility checks associated with the evaluation of an
aggregate, presumably because it was believed that there was no way for the
anonymous object of the aggregate to contain a reference to something more
short-lived than itself.
This is certainly false if mutation occurs, as illustrated by one of the
examples given in this AI.
To address this, I think we need to bite the bullet and define an "ultimate"
(perhaps "post-mutation" or "eventual" would be better terms?) accessibility
level for a type or object.
Roughly speaking, this is the accessibility level that an object will have after
all the mutating has finished.
This is a dynamic notion and is only used in the definition of dynamic
accessibility checks.
More precisely:
The ultimate accessibility level of an anonymous
function_result/aggregate object which is
built in place is the ultimate accessibility level of the
object that the anonymous object is being used to initialize.
Ditto for the ultimate accessibility level of the
named function return object of an extended return statement.
The ultimate accessibility level of the access type
of an allocator which creates a coextension, and of the
coextension objct itself, is the ultimate accessibility level
of the coextension's owner.
The ultimate accessibility level of an anonymous access
type which is a result type for a dispatching
function which is called via a dispatching call
is the ultimate accessibility level of the
result type of the function called by the caller.
[Note:
This case has nothing to do with build in place, but
I think we still need to include this case in order to
get the definition right.]
For all other entities, the ultimate accessibility
level of the entity is the same as its accessibility level.
For example, if an allocator is initialized by calling function Foo which in
turn returns the result of calling function Bar (with b-i-p at every step) then
the ultimate accessibility level of Bar's function result object is that of the
access type.
[Note:
We need to look at the interactions between this
definition and optional (as opposed to required)
build-in-place. I don't know of any problems in
this area, but it is something to keep in mind.]
The checks where we currently (see AI05-0051) use "as determined at the point of
call" wording would be modified to become checks against the ultimate
accessibility level of the object/type in question.
I think his would address the corner case problems with coextensions that we
have discussed earlier in the context of this AI.
In the [rare] case of an aggregate of a discriminated type having one or more
access discriminants for which defaults are provided, a dynamic accessibility
check is performed of the accessibility level of each discriminant value against
the ultimate accessibility level of the anonymous object of the aggregate.
The check can be omitted in the case where the subtype of the object being
initialized is constrained, or when the discriminant value is the result of
evaluating the discriminant's default expression.
[Note:
Typically a dynamic accessibility check has a corresponding static
legality rule to prohibit the cases where it is statically
known that the dynamic check would fail. Static rules corresponding
to the dynamic checks described in this proposal would
need to be considered, but they might not be needed because
typically these rules involve knowledge about both sides of a call.
Perhaps something like an aggregate used to initialize an allocator
with no calls in sight would warrant a check?]
[Note:
We need to confirm that this is straightforward to implement.
I think that the "pass in an accessibility level
parameter" model described in AI05-0051 suffices, but this
needs confirmation.]
==== Message #2 ====
I hate to hair this proposal up with complexity that is specific to
coextensions, but I think the "ultimate accessibility level" definition for a
coextension (and for its access type) needs to take into account transfer of
coextension ownership in the non-bip case (i.e., 3.10.2(14.4/3), as it applies
in the non-bip case).
The definition I outlined earlier already handles this ownership transfer in the
b-i-p case because there really isn't a transfer in that case (the old owner
mutates into the new one).
The idea is that the ult-acc-lev of a coextension is the ult-acc-lev not of its
current owner, but of the object which will end up being its owner.
Would an example illustrating the need for this be useful?
In some sense, this wrinkle strengthens the case for the general "ultimate
accessibility level" approach.
The "ultimate accessibility level" of an object is a complicated idea, but by
factoring out all this complexity into one place (i.e., the definition of the
term), we can plug holes in lots of places in the RM by using this new term in
defining various dynamic accessibility checks.
****************************************************************
From: Steve Baird
Sent: Monday, February 7, 2011 3:18 PM
Here is a first cut at wording for this AI. [This is version /02 of
the AI - Editor.]
Thanks to Gary, Randy, and Bob for reviewing preliminary versions.
Discussion:
The following argument provides motivation for the proposed changes:
1) We need some kind of accessibility checks to deal with the
"access discriminants with defaults" problem illustrated
in the AI (the example involving type Component_Type).
One could view this problem as a consequence of introducing
defaulted access discriminants for limited types. This introduces
the need to either check aggregates (the approach taken in
this version of the AI) or somehow check arbitrarily deeply nested
subcomponents at the point of a function return (the approach
taken by the previous version of this AI).
2) We don't want to repeat checks at the point where an
object's accessibility level changes; we want to perform
the checks against the correct accessibility level in the
first place so that checks are performed while we still have
useful info about both entities whose accessibility levels
are being compared.
The AI lists the various scenarios where the accessibility level
of an object may change. One could imagine an approach where
additional checks are performed at these points. This seems like
a bad idea. The previous version of thie AI took this approach
and involved conservative checks associated with a
"has-subcomponents-with-defaulted-access-discriminants" flag in the
descriptor for a tagged type. The current approach is more precise;
cases which would would have failed overly conservative runtime
accessibility checks with the earlier approach will now succeed.
3) This means that we have to know the "ultimate
accessibility level" of an aggregate result object
at the point where the discriminant value is given.
I think this is implementable using the approach outlined
in AI05-0051, but this needs to be confirmed. In particular,
we need to look at combinations such as a non-build-in-place
function call used to initialize an allocator where the
function result owns a coextension and the result subtype
of the function is class-wide with no discriminants.
4) Having defined this notion for use with aggregates, we might as
well use it elsewhere (see above changes to 4.8 and 6.5)
even though the only holes that this fixes are related to coextensions
(and are therefore less important, at least to some folks).
****************************************************************
From: Steve Baird
Sent: Monday, February 14, 2011 1:17 PM
Question #1 -
Are there interactions between this "ultimate" stuff and aliased paraemters?
Do we also need to add an "ultimate" to the wording for the runtime accessibility check associated with an aliased parameter?
6.4.1(15.1/3):
In a function call, for each explicitly aliased parameter, a check is
made that the accessibility level of the master of the actual object
is not deeper than that of the {ultimate} master of the
function result.
As semantics-preserving polishing, we could then (if it reads better) replace
"that of the ultimate master of the function result"
with
"the ultimate accessibility level of the function result"
The general idea is that if the function result is going to contain a reference
to the parameter, then the parameter must not be shorter-lived than the function
result.
Inside a function, consider, for example, a subaggregate of a b-i-p function
result aggregate of the form
(Access_Discriminant => Aliased_Formal_Parameter'Access)
where the function is declared in a nested scope but the function call is used
to initialize an allocator whose access type is declared in a less-nested scope
(e.g., library level).
Question #2 -
Given the current wording for this AI (i.e., with no mention
of any special rules for aliased parameters), the call to the
function would not fail (at the point of the call), but would
the aggregate's evaluation fail inside the function? This boils
down to a question about the dynamic accessibility level of an
aliased formal parameter.
The already-proposed AI-234 wording for aggregates includes
... a check is made that the accessibility level of the
anonymous access type of each such access discriminant,
as determined by the expression provided
in the aggregate, is not deeper than the ultimate accessibility
level of the anonymous object created for the evaluation
of the aggregate.
What is the (dynamic) accessibility level "determined by the
expression provided" in this case? Does this need some wording?
With the addition of the above-proposed runtime accessibility check, it would be
safe to define the (dynamic) accessibility level of an aliased formal parameter
to be the ultimate accessibility level of the function result (at least in the
case of a function with a non-elementary result type).
Does this seem like the right thing to do?
If it seems like this "ultimate" stuff is getting out of hand, don't shoot the
messenger. I'm just trying to work out the consequences of allowing an object to
change masters.
****************************************************************
From: Tucker Taft
Sent: Monday, February 14, 2011 1:19 PM
This is certainly a painful area.
I would hope we could move most of the "dynamic semantics"
wording of 3.10.2 into AARM notes. For example, I don't think we need to
enumerate in the normative wording all the ways that the master of an object can
change. Also, I would much prefer bullets to a series of unbulleted paragraphs
that are closely parallel with one another. All in all, I would hope we could
reduce the amount of normative wording associated with this issue down to the
bare minimum, with most of the rest in the AARM. Steve, do you think this might
be possible?
****************************************************************
From: Steve Baird
Sent: Monday, February 14, 2011 1:37 PM
> This is certainly a painful area.
That's a polite choice of words.
>
> I would hope we could move most of the "dynamic semantics"
> wording of 3.10.2 into AARM notes.
Certainly a worthy goal.
>For example, I don't think
> we need to enumerate in the normative wording all the ways that the
>master of an object can change.
Sounds right.
> Also, I would much
> prefer bullets to a series of unbulleted paragraphs that are closely
> parallel with one another.
Makes sense.
> All in all, I would
> hope we could reduce the amount of normative wording associated with
> this issue down to the bare minimum, with most of the rest in the
> AARM. Steve, do you think this might be possible?
I'll make a pass through it with this in mind, but I think you may have
identified the low-hanging fruit.
****************************************************************
From: Randy Brukardt
Sent: Monday, February 14, 2011 2:36 PM
> Question #1 -
>
> Are there interactions between this "ultimate" stuff and aliased
> paraemters?
> Do we also need to add an "ultimate" to the wording for the runtime
> accessibility check associated with an aliased parameter?
Tucker and I were assigned to figure out issues associated with aliased
parameters and this AI. I think I speak for both of us when I say that I am glad
that you are taking that over from us. ;-)
The quick answer to your question is yes, but I don't know if it really matters.
(The intent was always that the check is against the caller's version of that
accessibility; isn't that the same as the "ultimate accessibility"??)
...
> Inside a function, consider, for example, a subaggregate of a b-i-p
> function result aggregate of the form
>
> (Access_Discriminant => Aliased_Formal_Parameter'Access)
>
> where the function is declared in a nested scope but the function call
> is used to initialize an allocator whose access type is declared in a
> less-nested scope (e.g., library level).
I'm certain this case is already covered; that was the entire point of the check
(this case). If it *isn't* covered, then nothing is right. The only cases that
might not be covered have to do with the transfer of coextensions in non-b-i-p
calls, but even there I thought that it was already covered.
It still might be better to use the "ultimate" wording simply to reassure
everyone that these are the same, but I don't think it is necessary.
> Question #2 -
> Given the current wording for this AI (i.e., with no mention
> of any special rules for aliased parameters), the call to the
> function would not fail (at the point of the call), but would
> the aggregate's evaluation fail inside the function? This boils
> down to a question about the dynamic accessibility level of an
> aliased formal parameter.
I don't understand this question; everything about accessibility of aliased
parameters is already special. If there is something that isn't, it is wrong.
(Tucker would like to change that for uses not inside of a return statement, but
that is irrelevant to your rules.)
...
> With the addition of the above-proposed runtime accessibility check,
> it would be safe to define the (dynamic) accessibility level of an
> aliased formal parameter to be the ultimate accessibility level of the
> function result (at least in the case of a function with a
> non-elementary result type).
That is what it is currently defined to be (or at least *supposed* to be defined
to be), without the "ultimate" wording. Again, there is no change here that I
can see (modulo wording bugs). Using "ultimate" might simplify the wording, but
it won't change anything.
****************************************************************
From: Steve Baird
Sent: Monday, February 14, 2011 3:54 PM
> (The intent was always that the check is against the caller's
> version of that accessibility; isn't that the same as the "ultimate
> accessibility"??)
Usually they are the same, but in the rare cases where they differ, I think we
want the "ultimate" one.
Certainly they can differ in cases involving coextensions.
In a case like
return Some_Dispatching_Op (...);
where both the function being called and the function being returned from have a
controlling access result type, does the "caller's version of the accessibility"
wording handle the case where *both* of the two calls involved are
trans-accessibility-level (making up terminology on the fly here) dispatching
calls?
[By a trans-accessibility-level call, I mean a call (as described in AI05-0051)
where the body executed by the call is declared in a more nested scope than the
subprogram the caller is (statically) calling.]
> ...
>
>> Question #2 -
>> Given the current wording for this AI (i.e., with no mention
>> of any special rules for aliased parameters), the call to the
>> function would not fail (at the point of the call), but would
>> the aggregate's evaluation fail inside the function? This boils
>> down to a question about the dynamic accessibility level of an
>> aliased formal parameter.
>
> I don't understand this question; everything about accessibility of
> aliased parameters is already special. If there is something that
> isn't, it is wrong. (Tucker would like to change that for uses not
> inside of a return statement, but that is irrelevant to your rules.)
I was only clarifying that by "the current wording for this AI", I meant wording
that is at least 24 hours old, not something brand new that was introduced
earlier in the same e-mail.
I may well be missing something. Where should I be looking for the current
definition of the (dynamic) accessibility level of an aliased formal parameter?
****************************************************************
From: Randy Brukardt
Sent: Monday, February 14, 2011 6:01 PM
> I may well be missing something. Where should I be looking for the
> current definition of the (dynamic) accessibility level of an aliased
> formal parameter?
I believe we are talking about 3.10.2(13.3/3). It even includes the word
"ultimate"!
****************************************************************
From: Randy Brukardt
Sent: Tuesday, February 15, 2011 2:28 AM
BTW, Tucker and I semi-agreed on a fix to the static accessibility rule for
formal explicitly aliased parameters. And we put that fix into AI05-0235-1. If
there is anything that needs to be changed with their dynamic accessibility,
we're leaving it to you to fix (in the paragraph noted above). I am of the
opinion that everything will work fine inside of a function: the expectation is
that the dynamic check always succeeds in cases of comparing return objects to
aliased parameters. (The whole point was to eliminate the possibility of failure
of the dynamic check).
If there is a problem, it's probably with the check of the actual parameter's
accessibility. These are 6.4.1(6.3/3) [static check] and 6.4.1(15.1/3) [dynamic
check]. Enjoy. :-)
****************************************************************
From: Steve Baird
Sent: Wednesday, February 16, 2011 2:51 PM
> Steve Baird:
> * AI05-0234-1 Add missing wording, consider class-wide coextension
> cases. [Need !discussion
Here is a !discussion and !example section for AI05-0234 The Agg_Check example
is new; the other two examples are taken from the previous version of the AI.
[Editor's note: This is version /03 of the AI.]
****************************************************************
From: Gary Dismukes
Sent: Wednesday, March 16, 2011 12:49 AM
Here's a preliminary revision of AI05-234 (the son of AI05-51). [This is
version /04 of the AI - Editor.] This is
an update of the !wording, based on the earlier cvs version 1.4. At the last
ARG meeting it was decided that rather than introducing new terms 'ultimate
master' and 'ultimate accessibility level' as proposed by Steve in version 1.6,
that instead we should define 'determined by the point of call', a phrasing
already used in several places (mostly due to the changes of AI05-51).
So the bulk of the change here is the addition of a set of bulleted paragraphs
in 3.10.2 that attempts to give a consolidated definition of 'determined by
point of call'. Some of these bullets were extracted from the sentences in
3.10.2(14.5/3), which was added by AI05-51. Various other smaller wording
changes are included, a number of which were identified at the last meeting.
I say 'preliminary' above because I'm still trying to sort out some details,
and still hoping to make some more progress on that tomorrow. In a few places
I've inserted some questions for pieces that are missing or may have an issue
that needs addressing. One specific missing piece that I confess is a gap
at this point is the treatment of coextensions. I also haven't updated the
!discussion, and that might need further work. It's also possible that I've
screwed things up here, in which case preliminary may be an understatement. :)
Comments from anyone who has the time outside of working on their own homework
to take a look at this would be greatly appreciated. (Thanks to Steve for
offering some chunks of time to discuss this last week during my struggles
to come to grips with this one after he most generously unloaded it on me.
I'll probably be bending his ear a little further tomorrow...)
BTW, sort of a side question I'd like an answer to that isn't included below
in the AI is about a paragraph in the !wording of AI05-51 that doesn't appear
to have made it into the online RM. The paragraph defines the statically
deeper relationship for the case of an anonymous access result. I'd like
to know if that's an oversight or was it decided at some point that it was
wrong or unneeded (but it looks like it was an oversight).
In AI05-51 (both !wording and !corrigendum) it says:
Add after 3.10.2(19/2):
* For determining whether a level is statically deeper than the level of the
anonymous access type of an access result of a function, when within a
return statement that applies to the function, the level determined by the
point of call is presumed to be the same as that of the level of the master
that elaborated the function body.
If this was supposed to be added, then it would probably make sense to
correct the oversight in AI05-234.
****************************************************************
From: Gary Dismukes
Sent: Wednesday, March 16, 2011 11:05 PM
Here's the revision of AI05-234, with various questions in the preliminary
version that I posted last night removed and/or resolved, plus minor updates
to the !question and !discussion sections.
[This is version /05 of the AI - Editor.]
It would still be nice to get an answer to the side question I included
in the preamble of my previous posting, but not essential to this AI:
> BTW, sort of a side question I'd like an answer to that isn't included below
> in the AI is about a paragraph in the !wording of AI05-51 that doesn't appear
> to have made it into the online RM. The paragraph defines the statically
> deeper relationship for the case of an anonymous access result. I'd like
> to know if that's an oversight or was it decided at some point that it was
> wrong or unneeded (but it looks like it was an oversight).
>
> In AI05-51 (both !wording and !corrigendum) it says:
>
> Add after 3.10.2(19/2):
>
> * For determining whether a level is statically deeper than the level of the
> anonymous access type of an access result of a function, when within a
> return statement that applies to the function, the level determined by the
> point of call is presumed to be the same as that of the level of the master
> that elaborated the function body.
>
> If this was supposed to be added, then it would probably make sense to
> correct the oversight in AI05-234.
****************************************************************
From: Randy Brukardt
Sent: Wednesday, March 16, 2011 11:37 PM
Sorry, I was going to answer this earlier. Tucker noticed this when we were
wording on AI05-0235-1, and the oversight was corrected in that AI. It's in the
working draft of the RM, but of course you don't have that yet.
And it really wasn't an oversight. What happened was that AI05-0142-4 originally
combined some wording about explicitly aliased parameters with this bullet. Then
someone "improved" that wording by eliminating the part about the access result
of a function. Which of course led to it disappearing altogether. Since
AI05-0142-4 included instructions that it's paragraph superseded this one, it
got removed.
In any case, you needn't worry about it.
****************************************************************
Questions? Ask the ACAA Technical Agent