CVS difference for ai05s/ai05-0051-1.txt
--- ai05s/ai05-0051-1.txt 2007/12/13 04:39:36 1.4
+++ ai05s/ai05-0051-1.txt 2008/01/29 04:02:04 1.5
@@ -1,4 +1,4 @@
-!standard 6.5(21/2) 07-10-31 AI05-0051-1/03
+!standard 6.5(21/2) 08-01-27 AI05-0051-1/04
!class binding interpretation 07-05-04
!status work item 07-05-04
!status received 07-04-19
@@ -20,34 +20,72 @@
!recommendation
-The accessibility level against which to perform run-time accessibility
-checks at the return statement of a dispatching function is determined by the
-level of the function named at the call-site, even if that is different
-from the accessibility level of the function body reached by the dispatching
-call. If the function has an access result, and the expression of the
-return statement is an (anonymous) allocator, then this accessibility level
-in turn determines the lifetime of the allocated object.
-
-Similar rules would apply for calls through an access-to-subprogram
-value. The caller's view would determine the accessibility level
-to use for run-time accessibility checks.
+We propose that in all cases where accessibility checks are currently
+required in return statements, they be based on an accessibility level
+passed in from the caller, rather than on the accessibility level of the
+master that elaborated the function body. This eliminates the problem
+with dispatching calls, since the passed-in level is unaffected by
+whether the call is a dispatching call to a primitive of a type declared
+at a more nested level than the function named in the call.
+
+Currently, almost all cases where an accessibility check is performed as
+part of a return statement, the caller already needs to pass in
+information because the returned object might need to be created in a
+place or storage pool determined by the context of the call, or might
+need finalization at a point determined by the caller.
+
+The one place where the current rules do not require some caller context
+is where a function has an access result. However, the current rule for
+handling access results causes an anonymous allocator to almost always
+lead to a storage leak. We propose to change the rules for what storage
+pool should be used in such an anonymous allocator, to minimize the
+likelihood of creating a storage leak. As a side-effect, we also solve
+the problem with dispatching calls to functions with access results,
+and make all of these cases more consistent with one another.
+Essentially 3.10.2(14-14.3) will be adjusted so that anonymous
+allocators in return expressions are treated as they would be if they
+were substituted into the call site.
+
+Note that we define the static accessibility level to be associated with a
+passed-in accessibility level to be the same as that of the master that
+elaborated the function body, which preserves the same legality rules
+already present in Ada 2005. Only the dynamic checks are changed to
+overcome the problem with dispatching calls to overridings of nested
+extensions.
!wording
-Add after 3.10.2(10.1):
+Modify 3.10.2(14/2) as follows:
- Within a return statement that applies to a function with an
- access result, the accessibility level of the anonymous result
- access type is determined by the accessibility level of the view
- of the function named at the point of call. For the purposes of
- determining whether this level is statically deeper than another,
- it is presumed to be the same as the level of the function
- containing the return statement. [Redundant: Note that for a
- dispatching call or a call through an access-to-subprogram value,
- the level of the function named at the point of call might be
- different from that of the function containing the return
- statement.]
-
+ The accessibility level of an object created by an allocator is the
+ same as that of the access type, except for an allocator of an
+ anonymous access type {(an "anonymous allocator") in certain
+ contexts} [that defines the value of an access parameter or an
+ access discriminant]. {In particular, for an anonymous allocator
+ that defines the result of a function with an access result, the
+ accessibility level is determined as though the allocator were in
+ place of the call of the function. (If the call is the operand of a
+ type conversion, the level is that of the target access type of the
+ conversion.)} For an {anonymous} allocator defining the value of
+ an access parameter, the accessibility level is that of the
+ innermost master of the call. For one defining an access
+ discriminant, the accessibility level is determined as follows:
+
+Add after 3.10.2(14.4/2):
+
+ * 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, 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 3.10.2(15) as follows:
The accessibility level of a view of an object or subprogram
@@ -58,7 +96,7 @@
level of the view of the function denoted by the dereference is
that of the master containing the access parameter}.
- AARM NOTE:
+ {AARM NOTE:
This is important so that a call through an access-to-subprogram
parameter on a function with an access result, a classwide
result, or a result with access discriminants, has some
@@ -72,35 +110,38 @@
access-to-subprogram parameter is clearly denoting a function
from some scope dynamically enclosing the master containing the
access parameter, it is safe to give it an accessibility level
- of that master.
+ of that master.}
+
+Add after 3.10.2(19/2):
-Modify 6.5(8/2) as follows:
+ * For determining within a return statement applying to a function
+ with an access result, whether a level is statically deeper than
+ the level of the anonymous access type of the access result, 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 the result type of a function is a specific tagged type, the
- tag of the return object is that of the result type. If the result
- type is class-wide, the tag of the return object is that of the
- value of the expression. A check is made that the accessibility
- level of the type identified by the tag of the result is not
- deeper than that of the [master that elaborated the function
- body.] {view of the function named at the point of call.
- [Redundant: Note that for a dispatching call or a call through an
- access-to-subprogram value, this level might be different from
- that of the function containing the return statement.]} If this
- check fails, Program_Error is raised.
+Modify 6.5(8/2) as follows:
+ If the result type of a function is a specific tagged type, the tag
+ of the return object is that of the result type. If the result type
+ is class-wide, the tag of the return object is that of the value of
+ the expression. A check is made that the accessibility level of the
+ type identified by the tag of the result 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.
+
Modify 6.5(21/2) as follows:
If the result subtype of a function has one or more unconstrained
access discriminants, 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.] {view of the function named at the
- point of call. [Redundant: Note that for a dispatching call or a
- call through an access-to-subprogram value, this level might be
- different from that of the function containing the return
- statement.]} If this check fails, Program_Error is raised.
-
+ 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.
Modify 7.6.1(11/2) as follows:
@@ -120,34 +161,53 @@
objects finalized as part of its finalization cease to exist, as
do any types and subtypes defined and created within the master.
+Add the following after 13.11(25.2/2):
+
+ * If the allocator defines the result of a function with an
+ access result, the storage pool is determined as though the
+ allocator were in place of the call of the function. If the call
+ is the operand of a type conversion, the storage pool is that of
+ the target access type of the conversion. If the call is itself
+ defining the result of a function with an access result, this rule
+ is applied recursively.
+
!discussion
-In most cases, the dynamic semantics of access result types follow easily
-from an informal equivalence rule. The declaration
+In many cases, the original formulation the semantics rules
+for access result types followed from an informal equivalence
+rule. The declaration
function Foo ( ...) return access T;
-is pretty much equivalent to
+was much equivalent to:
type T_Ref is access all T;
function Foo (...) return T_Ref;
-, and that's that.
If the function in question overrides an inherited dispatching operation
and if the controlling type of the function is declared in a more nested
-scope than its parent type, then this equivalence breaks down.
-
-If the function returns an allocator, then what is the lifetime and
-accessibility level of the allocated object in the case where the
-function has been invoked via a dispatching call to the operation of
-the parent type?
+scope than its parent type, then this equivalence breaks down because the
+caller and the callee see different access types, at different accessibility
+levels.
+
+Also, if such a function returns an allocator, then what is the lifetime
+and accessibility level of the allocated object, both in the normal
+case, and in the case where the function has been invoked via a
+dispatching call to the operation of the parent type? It is important
+that the object live at least as long as that implied by the
+accessibility level associated with the function result. We would also
+prefer that the storage be allocated from a pool that will minimize
+storage leaks. This AI proposes that we use a simple substitution
+rule to determine both the accessibility level and the best storage
+pool to use.
+
+The accessibility issue for a function with an access result
+is an instance of a more general problem associated with uses of
+the accessibility level of a function body in the case where this level
+does not match the accessibility level of the function named in the
+(dispatching) call.
-This is an instance of a more general problem
-associated with uses of the accessibility level of a function body
-in the case where this level does not match the accessibility level
-of the function named in the (dispatching) call.
-
A function with an access result type may return a reference to an object
whose accessibility level matches that of the function body:
@@ -178,7 +238,7 @@
return (Disc => X'Access);
end F3;
-Each of these three scenarios becomes problematic if the function
+Each of these three scenarios becomes more interesting if the function
overrides an inherited dispatching operation and the controlling
type of the function is declared in a more nested scope than its
parent type.
@@ -204,11 +264,33 @@
begin
<Call X.F1, X.F2, and X.F3>
+ -- Each call returns a reference to an object or a type that
+ -- is more nested than Pkg_1. Hence, it is important that
+ -- the caller and callee agree about what accessibility level
+ -- to presume for the returned object or designated object,
+ -- so we don't end up with a dangling reference.
--
- -- Each call should fail, right?
- -- Otherwise we've got the potential for dangling references.
+ -- For X.F4, we also need to determine the storage pool for the
+ -- anonymous allocator in the return statement. For F2 and
+ -- F3, if the calling context is an initialized allocator, we
+ -- can avoid copying if we have the return object allocated
+ -- directly in the desired storage pool. This is required for
+ -- F3 because it is limited, and is allowed for F2 for efficiency.
--
- -- The situation with X.F4 is even less clear.
+ -- If such an initialized allocator is for an access type
+ -- declared outside Call_Functions, we need to be sure that
+ -- some accessibility check prevents the allocator from succeeding.
+ --
+ -- The result of calls on F1 or F4 might be converted to an access
+ -- type declared outside of Call_Functions. Again, some accessiblity
+ -- check must prevent this.
+ --
+ -- By passing in an accessibility level in all these cases,
+ -- as well as a storage pool or equivalent, the called function
+ -- can do the appropriate accessibility check, and allocate
+ -- from the appropriate storage pool to minimize the chance
+ -- of a storage leak.
+
end Call_Functions;
procedure Nested is
@@ -246,60 +328,9 @@
begin
Nested;
end;
-
-This AI presumes an implementation model where these problematic dispatching
-operations of nested extensions expect an accessibility level to be
-passed in to indicate the accessibility level of the function declaration
-named in the call; that level is then used for anonymous-typed allocators
-at the point of return, and for accessibility checks.
-
-Once we accept that we need to pass in an accessibility level to any
-function that has a class-wide, access-discriminated, or access result
-return type, and is, or might become through renaming, a dispatching
-operation, we conclude that this model might as well be adopted
-universally for functions with such a result type. In particular,
-because with a call through an access-to-subprogram, it is hard to
-know whether you might reach a dispatching function, some sort of
-level would need to passed whenever making a call through an
-access-to-subprogram. And since essentially any function might be
-called through an access-to-subprogram value, all functions with such
-a result type will need to expect a passed-in accessibility level.
-
-Once we accept we are always passing in a level for these kinds of
-functions, we need to decide what are the implications for calls
-through access-to-subprogram values. Since the level of the function
-named by the dereference of an access-to-subprogram value is always at
-least as deep as that of the actual designated function, passing in a
-deeper level will never cause a failure of a run-time accessibility
-check when a corresponding static accessibility check passed. For the
-cases where there is no static accessibility check, such as with an
-access parameter, this may eliminate run-time check failures exactly
-in cases where they are safe, since code calling through a more deeply
-nested access-to-subprogram type is set up to handle more deeply
-nested results.
-
-Here is an example of where these dynamic levels would make
-a difference:
-
-
- procedure Getting_Fancy(
- Y : access T;
- ATF : access function(X : access T) return access T) is
-
- G : access T;
- begin
- G := ATF.all(Y);
- -- passes in "result level" of master Getting_Fancy
- end Getting_Fancy;
- ...
-
- function Identity(X : access T) return access T is
- begin
- return X; -- run-time accessibility check
- -- of level of X against "result level"
- -- passed in to be sure X is no deeper
- end Identity;
+This AI also addresses two somewhat independent issues relating to
+accessibility and anonymous allocators:
For access-to-subprogram parameters, which have by definition an
infinitely deep accessibility level, we specify (in 3.10.2(15)) that
@@ -311,12 +342,6 @@
access-to-subprogram type declared local to the subprogram containing
the access parameter.
-We need to make the dynamic accessibility level of an access result
-type reflect this same set of rules, so we add a paragraph in 3.10.2
-to specify that the accessibility level comes from the caller's view
-of things. This is important because if the return expression is an
-(anonymous) allocator, we need to know its accessibility level.
-
We relax the rules for order of finalization of objects (other than
coextensions) created by anonymous allocators, since tying them to a
particular freezing point is tricky, and doesn't seem to be of great
@@ -327,18 +352,103 @@
storage pool object gets finalized, but there is no similar
consideration for non-coextension anonymous allocations, since they
always use the "default" storage pool.
+
+BACKGROUND ON THIS APPROACH
+
+We had earlier considered a rule where the accessibility
+level of the controlling tag used in the call would
+be used at the call site to determine the actual
+level of the returned object. However, after
+trying to work out the details for that, we gave
+up. Furthermore, it implied a check both in the
+return statement and then another check after return
+on use. That seemed inefficient. Finally, we realized
+that in essentially all of these cases, we already
+need to pass in some kind of caller context because
+the return object might not allow copying or might
+require finalization, and the caller is determining
+where the return object is to be located and/or
+what master it is to be associated with for
+finalization purposes.
+
+Hence the proposed solution is now to change the dynamic
+accessibility-level check at the return statement
+to be based on an accessibility level passed in from
+the caller, rather than basing it on the level of
+the master that elaborated the function body.
+Using the level of the master is too optimistic
+for nested overrides, and is too pessimistic in
+many other cases. We chose to leave the static legality
+checks the same.
+
+After solving this problem for returning class-wide
+objects and objects with access discriminants, we went
+on to propose essentially a similar solution for
+functions with access results. Namely, the "dynamic"
+accessibility level for access results would be
+determined by the caller rather than being determined
+by the master that elaborated the function body.
+This solves the problem with nested overrides,
+makes access results work more consistently with
+access discriminants and functions that return limited
+types, and it minimizes storage leaks. It effectively
+means that whether an initialized allocator is at the
+call site using the result of the function as the initial
+value, or the initialized allocator is at the return
+statement of a function with an access result,
+and the caller merely converts it to the desired
+type, the net effect is the same.
+
+EXAMPLES
+
+Here is an example. In the following, the
+initialization of X and the initialization of Y
+produce essentially the same thing, namely a pointer
+to a heap object allocated from the storage pool
+associated with Lim_T_Ptr:
+
+ function Blurfo return Lim_T is ...
+
+ function Blah return access Lim_T is
+ begin
+ return new Lim_T'(Blurfo);
+ end Blah;
+
+ type Lim_T_Ptr is access Lim_T;
+
+ X : Lim_T_Ptr := new Lim_T'(Blurfo);
+ Y : Lim_T_Ptr := Lim_T_Ptr(Blah);
-Note that in some of this wording, we have stopped using the phrase
-"the master that elaborated the function body" and simply talked about
-the level of the (view of the) function. By definition, the level of
-a declared entity is that of the master that elaborates it, so talking
-about the master that elaborates the function body is a long-winded
-way of saying the level of the function. By simplifying the wording,
-it is easier to talk about the function "named" at the point of the
-call.
+Another useful feature of having the caller specify
+the dynamic accessibility level to check occurs when
+the function returns a reference to a component of
+an object denoted by an access parameter. E.g.:
+ type Rec is record
+ Comp : aliased Comp_Type;
+ end record;
+
+ function Get_Comp(R : access Rec) return access Comp_Type is
+ begin
+ return R.Comp'Access; -- (1)
+ -- Run-time check based on level passed in from caller
+ end Get_Comp;
+
+ procedure Process(G : access Comp_Type) is ...
+
+ procedure Test is
+ Local : aliased Rec;
+ begin
+ Process(Get_Comp(Local'Access));
+ end Test;
+
+The above is perfectly safe and reasonable, but would
+fail if the dynamic check at (1) were against the level of
+the master that elaborated Get_Comp.
+
!ACATS test
+C-Tests need to be constructed to check this semantics.
!appendix
@@ -1621,7 +1731,7 @@
****************************************************************
-From: Tucker Taft
+From: Randy Brukardt
Sent: Wednesday, November 28, 2007 9:40 PM
> > In general, I feel that this proposal adds
@@ -1811,5 +1921,87 @@
I'll wait for your full rebuttal before writing more. (Well, at least I'll
try... ;-)
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Monday, January 28, 2008 9:37 PM
+
+Tucker wrote:
+> Here is another attempt at AI-51 [this is version /04 - ED],
+> addressing Steve's issue with what happens when a function
+> that has an accessibility check upon return
+> based on its level, is reached via a dispatching
+> call through a name that denotes a primitive
+> declared at an outer level. The problem is that
+> the caller and the callee don't agree about
+> the accessibility level.
+
+Your wording has (in several places):
+
+"...is determined as though the allocator were in place of the call of the function..."
+
+"were in place of" doesn't make much sense to me. I know that you mean "...as though
+the allocator were given at the place of the call..." -- so why didn't you say
+that? I think it's a lot clearer.
+
+---
+
+The wording change of 6.5(8/2) doesn't take AI05-0024-1 into account. That's OK,
+other than that it means that a simple level comparision will not do for this
+check. That means that the check will be quite expensive - and that means that
+something that looks cheap (anonymous return types) will be anything but cheap.
+Since the level will be passed in, there won't be any practical way (short of
+inlining) to eliminate this check (unlike the current semantics).
+
+---
+
+You said in your discussion:
+
+> Here is an example. In the following, the
+> initialization of X and the initialization of Y
+> produce essentially the same thing, namely a pointer
+> to a heap object allocated from the storage pool
+> associated with Lim_T_Ptr:
+>
+> function Blurfo return Lim_T is ...
+>
+> function Blah return access Lim_T is
+> begin
+> return new Lim_T'(Blurfo);
+> end Blah;
+>
+> type Lim_T_Ptr is access Lim_T;
+>
+> X : Lim_T_Ptr := new Lim_T'(Blurfo);
+> Y : Lim_T_Ptr := Lim_T_Ptr(Blah);
+
+You didn't mention it, but you have a hack so this works when nested. For instance:
+
+ procedure Something is
+ X : Lim_T_Ptr := new Lim_T'(Blurfo); -- OK
+ Y : Lim_T_Ptr := Lim_T_Ptr(Blah); -- OK, because the level is taken from the
+ -- type conversion, not the call.
+ begin
+ null;
+ end Something;
+
+But it depends on the type conversion being at the point of the call (which is
+why I call it a hack). That isn't necessarily true. Consider:
+
+ procedure Do_Something (A : access Lim_T) is
+ X : Lim_T_Ptr := Lim_T_Ptr(A);
+ begin
+ ...
+ end Do_Something;
+
+ Do_Something (new Lim_T'(Blurfo)); -- OK.
+ Do_Something (Blah); -- OK.
+
+ procedure Nested is
+ begin
+ Do_Something (new Lim_T'(Blurfo)); -- OK.
+ Do_Something (Blah); -- Runtime error. Yuck.
+ end Nested;
*****************************************************************
Questions? Ask the ACAA Technical Agent