!standard 6.5(21/3) 11-03-16 AI05-0234-1/04 !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); -- 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; 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 describe 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; [Author's note: I believe that a bullet may be needed here for coextensions, but this version doesn't yet address that.] + 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 in terms of masters, lifetimes, and accessibility is transparent to the program. Note that this does not imply that a level has to be passed on calls in cases where the implementation does not build in place, just that the semantics are equivalent.] [AARM] Implementation Note: There are several cases where the implementation has 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 new 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)}. [Author's question: Doesn't the immediately following AARM paragraph 6.4.1(15.a.1/3) need to be changed to reflect this? It seems to be implying that levels don't need to be passed for aliased parameters, but that's no longer true, because we're passing them for the "determined by the point of call" stuff. Perhaps other AARM paragraphs also need changes (maybe in 7.6.1(13.*)?] [Author's question: Does 6.5(8/3) need to be modified somehow to account for "determined by the point of call"? In version 1.6 of AI05-234, Steve Baird made a change to this paragraph to say "ultimate master that elaborated the function body". I don't see why that's necessary, but I may well be missing something!] 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. [Author's question: The part about "of the specific type" is a change proposed by Steve Baird to his original wording "(ignoring the value of bounds and discriminants)", but I'm not sure that this quite works in the class-wide case. Do we need to add more wording to describe getting the type based on the tag in the class-wide case? Note that the reason for this wording is to avoid the implementation having to determine whether such components actually exist in the object, such as when there are empty arrays and inactive variant-dependent components.] 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 : aliaed Integer; begin return X : Untagged := (Fld => (Discrim => Local'Access)) do Foo; end return; -- raises Program_Error after calling Foo. 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 classwide, 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 classwide, 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); -- fails accessibility check? (yes) end Bad_News; Ptr : Ref := Bad_News; begin -- If we reach this point, Ptr.Discrim is a dangling reference ... end Cw_Alloc; 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; -- fails accessibility check? (yes) end Bad_News; Obj : Root'Class := Bad_News; begin -- If we reach this point, Obj.Discrim is a dangling reference ... end Cw_Return; In addition, these checks also need to be generalized 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 : aliaed Integer; begin return X : Untagged := (Fld => (S => (Discrim => Local'Access))) do Foo; end return; -- raises Program_Error after calling Foo. end F; !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 12:49 AM ****************************************************************