!standard 3.10.2(10.2/3) 20-10-15 AI12-0402-1/01 !standard 3.10.2(10.5/5) !class Amendment 20-10-15 !status work item 20-10-15 !status received 20-10-15 !priority Low !difficulty Medium !subject Master of a function call with no special result type !summary TBD. !problem When passing an actual object to an aliased formal, if the subprogram called is a function, we require that the actual not be statically deeper than the master of the function call. On the other hand, if it is a procedure, there is no accessibility requirement for the actual. One strange case is when the result of the function call is returned as the result of the enclosing function, when the result being returned is a simple scalar value, an array, or a record with no access discriminants. In this case, there should not be any accessibility requirement on the actual for the aliased formal. Here is an example of this case: function F1 (X : aliased T1) return T2; function F2 return T2 is Local : aliased T1 := ...; begin return F1 (Local); -- Should be allowed if T2 is "simple" enough. end; Similarly, if the function call is used to define a component of an object created by an allocator, such as: Local : aliased T1 := ... X : Lib_Level_Access := new Lib_Level'(A => F1 (Local)); -- Should be legal if T2 is "simple" enough There seem to be two solutions to this problem: 1) change the definition of the master of the function call when returning a result whose accessibility is not one of the "special" cases, to be the innermost master of the point of the call; 2) remove the static and dynamic accessibility checks of the actual for an aliased formal in such a case. The relevant paragraphs of 3.10.2 that would need modification to change the definition of the master of the call are: 3.10.2(10.2/3): If the result is used (in its entirety) to directly initialize part of an object, the master is that of the object being initialized. In the case where the initialized object is a coextension (see below) that becomes a coextension of another object, the master is that of the eventual object to which the coextension will be transferred. 3.10.2(10.5/5): If the call itself defines the result of a function F, or has an accessibility level that is tied to the result of such a function F, then the master of the call is that of the master of the call invoking F. Or we might modify these paragraph in 6.4.1 about the static and dynamic checks performed at the call site when we have an aliasse formal: 6.4.1(6.4/3): In a function call, the accessibility level of the actual object for each explicitly aliased parameter shall not be statically deeper than the accessibility level of the master of the call (see 3.10.2). 6.4.1(15.2/5): 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 call (see 3.10.2). !proposal We propose to change the rules for determining the master of the function call, as we are already handling various special cases there, and this seems to be just one more special case. The default rule makes the master of the function call be the innermost master invoking the function, and that is the fallback we want. The rules that define the master of the function call already have special cases for the result type being of an anonymous access type. The only other cases we care about are when we might have an accessibility check on return from the call based on the master of the function call. In all other cases it is more flexible to let the master default to the innermost master. The only cases where we have accessibility checks inside a function that depend on the master of the call of the function have to do with functions whose result type is an anonymous access type (relevant rule is 3.10.2(14.5/3)), or whose result subtype allows for objects (or coextensions thereof) that have one or more access discriminants whose value is not constrained by that result subtype (relevant rule is 6.5(21/3)). Note that master of the function call is a dynamic semantics notion, though there is a statically-deeper check between the accessibility level of the master and the actual object passed to an aliased formal (6.4.1(6.4/3)), so we need to keep those static checks in mind. !wording Modify 3.10.2(10.2/3): {If the result is of a class-wide or immutably limited type, or the result subtype has unknown discriminants or an unconstrained access discriminant, or the nominal type of the result has a part that is of a limited private type, then: *}If the result is used (in its entirety) to directly initialize part of an object, the master is that of the object being initialized. In the case where the initialized object is a coextension (see below) that becomes a coextension of another object, the master is that of the eventual object to which the coextension will be transferred. {* If the call itself defines the result of a function F, or has an accessibility level that is tied to the result of such a function F, then the master of the call is that of the master of the call invoking F.} [Aurhor's note: this is moved here from 3.10.2(10.5/5] Delete 3.10.2(10.5/5) [It is moved up to be a bullet above.] !discussion We restrict what were bullet 3.10.2(10.2/3) and 3.10.2(10.5/5) to only apply if the result subtype is something where the function might make use of the master of the call. We could have made the rule depend on characteristics that require breaking privacy since the master of a function call is a dynamic semantics notion, but we have instead used characteristics that are known from a partial view, so we can continue to decide whether to perform static checks on the actual object for an aliased parameter while preserving privacy. We have rearranged the rules defining master of the function call to group the two that do *not* have to do with anonymous access result types. The part about a call defining the result of the enclosing function need not cover the case of anonymous access results, since the updated rule defined in AI12-0390-1 bases everything on type conversion, and anonymous access types are converted on essentially any use, including on returning the result of one function the result of the caller. !examples Looking at our two examples: function F1 (X : aliased T1) return T2; function F2 return T2 is Local : aliased T1 := ...; begin return F1 (Local); -- Is legal so long as T2 is a specific type without an unconstrained -- access discriminant and without limited private or immutably limited -- parts. end; In the case where the result is used to initialize a component of an allocated object: Local : aliased T1 := ... X : Lib_Level_Access := new Lib_Level'(A => F1 (Local)); -- Is legal so long as T2 is a specific type without an unconstrained -- access discriminant and without limited private or immutably limited -- parts. !ASIS No changes needed. !ACATS test ACATS B- and C-Tests are needed to check that the checks and master apply only in the appropriate cases. !appendix Editor's Note, Oct 15, 2020: I'm concerned that this change (as described in version /01) causes issues with build-in-place, adds a maintenance hazard, and adds a runtime incompatibility for potentially any function result with a part of an access type. Moreover, the exact proposal breaks privacy for a Legality Rule, something we never do outside of representation aspects. Discussions are ongoing, but I certainly do not believe this AI is ready for prime time. ****************************************************************