!standard 4.8(5.2/2) 08-01-18 AI05-0075-1/02 !standard 4.8(10.1/2) !standard 6.5(5.3/2) !standard 6.5(5.6/2) !standard 6.5(21/2) !class binding interpretation 07-10-30 !status work item 07-10-30 !status received 07-10-30 !priority Medium !difficulty Hard !qualifier Omission !subject More access discriminant checks needed !summary New checks to avoid dangling discriminants are performed. !question In the case where the result returned by a function has one or more access discriminants, should the runtime check of 6.5(21/2) be performed even if the function result type is class-wide and does not have the same discriminants as the function result? (Yes.) Should similar checks be performed for class-wide allocators? (Yes.) Should compile-time checks analogous to the existing checks of 4.8(5.2/2) and 6.5(5.6/2) also be performed? (Yes.) !recommendation Additional static and runtime checks are performed (see wording). !wording Replace 4.8(5.2/2) If the designated subtype of the type of the allocator has one or more unconstrained access discriminants, then the accessibility level of the anonymous access type of each access discriminant, as determined by the subtype_indication or qualified_expression of the allocator, shall not be statically deeper than that of the type of the allocator (see 3.10.2). with If the subtype determined by the subtype_indication or qualified_expression of the allocator has one or more access discriminants, then the accessibility level of the anonymous access type of each access discriminant shall not be statically deeper than that of the type of the allocator (see 3.10.2). Append after 4.8(10.1/2) If the designated subtype of the type of the allocator is class-wide, then a check is made that either the type of the allocated object (that is, the type determined either by the subtype_indication or by the tag of the value of the qualified_expression) lacks access discriminants or 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 the check fails. In 6.5(5.6/2) replace - If the result subtype of the function is class-wide, the accessibility level of the type of the expression of the return statement shall not be statically deeper than that of the master that elaborated the function body. If the result subtype has one or more unconstrained access discriminants, the accessibility level of the anonymous access type of each access discriminant, as determined by the expression of the simple_return_statement or the return_subtype_indication, shall not be statically deeper than that of the master that elaborated the function body. with two separate bulleted items - If the result subtype of the function is class-wide, the accessibility level of the type of the expression of the return statement (if any) shall not be statically deeper than that of the master that elaborated the function body. - If the subtype determined by the expression of the simple_return_statement or by the return_subtype_indication has one or more access discriminants, the accessibility level of the anonymous access type of each access discriminant shall not be statically deeper than that of the master that elaborated the function body. Append after 6.5(21/2): If the result type of the function is class-wide, then a check is made that either the (specific) type of the function result lacks access discriminants or that the accessibility level of the anonymous access type of each access discriminant is not deeper than that of the master that elaborated the function body. If the check fails, Program_Error is raised. !discussion A wording change in 6.5(21/2) seems to be needed; in the case of a class-wide result type, the specified check may need to be performed even if the result type of the function has no access discriminants. Consider the following example: declare type Root is tagged null record; type Ext (D : access Integer) is new Root with null record; function F return Root'Class is Local_Var : aliased Integer; function Local_Func return Ext is begin return (D => Local_Var'Access); end Local_Func; begin return Local_Func; end F; procedure Do_Something (X : access Integer) is ... end; begin Do_Something (Ext (F).D); end; If this test executes without raising any exception, then a dangling reference will be passed to Do_Something. It seems that the check described in 6.5(21/2) needs to be performed as part of returning from F, but the result subtype of F lacks unconstrained access discriminants. This will introduce distributed overhead in the sense that a function which returns a classwide result may have to check for the possibility that its result has a "bad" access discriminant values even if there are no access discriminants anywhere in the program. For some implementations, this might involve querying a "has anonymous access discriminants" flag in the descriptor associated with a specific tagged type. There is an analogous problem for allocators. Consider the following variation on the preceding example: declare type Root is tagged null record; type Ext (D : access Integer) is new Root with null record; type Ref is access Root'Class; function F return Ref is Local_Var : aliased Integer; function Local_Func return Ext is begin return (D => Local_Var'access); end Local_Func; begin return new Ext'(Local_Func); end F; procedure Do_Something (X : access Integer) is ... end; begin Do_Something (Ext (F.all).D); end; At a minimum, additional runtime checks are needed to cover these cases (see 4.8(10.1/2), 6.5(21/2)). Just adding runtime checks would be sloppy. Additional static checks are also needed (see 4.8(5.2/2) and 6.5(5.6/2)). So that's what's been done. The preceding two examples illustrate the need for runtime checks in the cases of function results and allocators. The following variations on those examples illustrate the need for static checking for function results and for allocators: declare type Root is tagged null record; type Ext (D : access Integer) is new Root with null record; function F return Root'Class is Local_Var : aliased Integer; Result : Ext (Local_Var'Access); begin return Result; -- should be rejected end F; procedure Do_Something (X : access Integer) is ... end; begin Do_Something (Ext (F).D); end; declare type Root is tagged null record; type Ext (D : access Integer) is new Root with null record; type Ref is access Root'Class; function F return Ref is Local_Var : aliased Integer; subtype Designated is Ext (D => Local_Var'Access); begin return new Designated; -- should be rejected end F; procedure Do_Something (X : access Integer) is ... end; begin Do_Something (Ext (F.all).D); end; Note that AI05-0032, not this AI, contains the wording changes needed to ensure the static rejection of cases like declare type Root is tagged null record; function F return Root'Class is type Local_Extension is new Root with null record; begin return X : Local_Extension; -- should be rejected end F; begin null; end; Does the proposed wording correctly handle the case where no static check is possible because no information is known about the discriminant values? Consider the first two examples (which are intended to illustrate the need for runtime checks; they are not intended to be rejected at compile-time). Does the proposed wording for static checks correctly let these examples through without any definitional problems? In particular, is the test that "the accessibility level of the anonymous access type of each access discriminant shall not be statically deeper than that of the master that elaborated the function body" well-defined in these cases? When we refer to the subtype "determined by the expression of the simple_return_statement", is this well-defined? Suppose, for example, that the expression is an aggregate. Do we need to generalize the definition of "nominal subtype" so that it is defined for all expressions, not just names? --!corrigendum 7.6.1(17.1/1) !ACATS Test ACATS B-Tests and C-Tests need to check the new rules. !appendix [The original question can be found in AI05-0051-1, from which this AI was split. - ED.] **************************************************************** From: Tucker Taft Sent: Tuesday, October 30, 2007 9:02 PM You use the phrase "This condition shall also hold ...". That's a new one, as far as I can remember, for the RM. I would suggest spelling it out, perhaps introduced by: Similarly, if the designated subtype of the type of the allocator is class-wide ... then the accessibility level ... shall not be statically deeper ... Alternatively, in this case, couldn't we combine the rules into: If the type determined by the subtype_indication or the qualified_expression of the allocator has one or more access discriminants, then the accessibility level of the anonymous access type of each access discriminant, as determined by the subtype_indication or qualified_expression of the allocator, shall not be statically deeper than that of the type of the allocator (see 3.10.2). **************************************************************** Summary of a private thread about this AI: The AI has: A couple of other changes that really should have gone into AI05-0032 are also included to handle static rejection of cases like function Foo return T'Class is type Local_Extension is new T with null record; begin return X : Local_Extension; end Foo; --- But this case is statically illegal, because "Local_Extension" does not have the same type as "T'Class" -- and 6.5(5.2/2) requires that for extended return statements (for functions with ordinary subtype returns -- there are different rules for access results). --- But Steve is talking about AI05-0032, which allows the return_subtype_indication to determine any type that is *covered* by the result type of the function. --- Why? That AI has never been approved. (And thus isn't in *my* AARM.) If it needs additional rules in order to work, then those rules need to be in that AI, not in some unrelated AI that happens to modify that paragraph. That's especially true as AI-32 is an Amendment AI, and may not be implemented for years if we don't vote to change the status. --- Good point, Randy. AI05-0032 should be amended instead of adding it to this AI. ****************************************************************