Version 1.3 of ai12s/ai12-0372-1.txt

Unformatted version of ai12s/ai12-0372-1.txt version 1.3
Other versions for file ai12s/ai12-0372-1.txt

!standard 3.10.2(19.3/4)          20-06-10 AI12-0372-1/03
!standard 6.4.1(6.4/3)
!class binding interpretation 20-03-09
!status work item 20-03-09
!status received 20-03-09
!priority Low
!difficulty Easy
!qualifier Omission
!subject Static accessibility of "master of the call"
!summary
Within a function body, the accessibility level of entities declared in the function are statically deeper than the master of the call.
!question
Consider:
procedure Test1 is type Discriminated (D : access Integer) is null record;
function Make (Param : aliased in out Integer) return Discriminated is begin return (D => Param'access); end;
function Func return Discriminated is Y : aliased Integer := 0; begin return Make (Y); -- (1) end;
Result : Discriminated := Func; begin null; end Test;
The call at (1) invokes 6.4.1(6.4/3). We would want this to be illegal, as the master of the call of Func is used as the master of the call of Make, and Y is clearly more nested than any possible call of Func.
But how do we determine that? 3.10.2(19.3/4) only applies to anonynous access types in a return statement for an access result. But there's no access result here. No other "statically deeper" rule comes close. Is there a rule missing here? (Yes.)
!recommendation
(See Summary.)
!wording
Modify 3.10.2(10.5/5):
If the call itself defines the result of a function {F} [to which one of the above rules applies], or has an accessibility level that is tied to the result of such a function {F}, [these rules are applied recursively] {then the master of the call is that of the master of the call invoking F}.
Delete 3.10.2(19.3/4):
For determining whether a level is statically deeper than the level of the anonymous access type of an access result of a function or generic function F, when within a return statement that applies to F or the return expression of expression function F, the level of the master of the call is presumed to be the same as that of the level of the master that elaborated the body of F.
and replace it with:
When within a function body or the return expression of an expression function, the accessibility level of the master representing an execution of the function is statically deeper than that of the master of the function call invoking that execution[Redundant:, independent of how the master of the function call is determined (see above)].
!discussion
When combined with other rules, such as 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).
and given the proposed revised rule given above for 3.10.2(10.5/5), we will correctly conclude that the call on Make(Y) in the return statement in the example given in the !problem will be disallowed, because Y is statically deeper than the level of the master of the call.
The new rule also covers the case from the deleted paragraph, because it will also make the level of anything declared within a function F statically deeper than the master that elaborated the body of F, which is the primary intent of this prior rule.
Here is a somewhat more complex example:
procedure Test2 is type Discriminated (D : access Integer) is null record;
function Make (Param : aliased in out Integer) return Discriminated is begin return (D => Param'access); end;
function Func return Discriminated is Y : aliased Integer := 0; C : Discriminated := Make (Y); -- (1) begin if True then return Make (Y); -- (2) else return C; -- (3) end if; end;
Result : Discriminated := Func; begin null; end Test2;
6.4.1(6.4/3) applies to each of the calls of Make. For each of these cases, we have to check whether Y is statically deeper than the master of the call of Make.
For the call at (1), 3.10.2(10.2/3) says that the "master of the call" is that of the object C. We then check the level of Y against the level of C, which is the same so this call is legal.
For the call at (2), however, 3.10.2(10.5/5) says that the "master of the call" is that of the enclosing function Func. By the new rule, we know that the level of Y is statically deeper than the master of the call of Func, making the call at (2) illegal.
For the return of C at (3), "master of the call" is not involved with the static check 6.5(5.9/5); 3.10.2(12.5/3) says that the level of C.D is that of C, and C is clearly statically deeper than the master that elaborated the body of F, so 6.5(5.9/5) clearly fails.
With this revised "statically deeper" rule, 6.5(5.9/5) could instead say "master of the call", the answer would be the same. But it's not necessary to change the rule, so we didn't do so. (That makes the discriminant case more different from the other cases - in particular the anonymous access case - but that seems harmless.)
---
It can be necessarily to apply 3.10.2(10.1-6/3) more than once before it is possible to apply the new rule to determine the result. Consider:
procedure Test3 is type Discriminated (D : access Integer) is tagged null record;
Glob : aliased Integer := 1;
function Make (Param : aliased in out Integer) return Discriminated is begin return (D => Param'access); end Make;
function Copy (Obj : aliased in Discriminated; Selector : in Natural) return Discriminated is begin if Selector = 0 then return Make (Glob); -- (1) else return Obj; -- (2) end if; end Copy;
function Func return Discriminated is Y : aliased Integer := 0; begin return Copy (Make (Y), 0); -- (3) end Func;
Result : Discriminated := Func; begin null; end Test3;
(1) is OK as Glob is library-level, which surely is not deeper than anything. (2) is OK directly by 3.10.2(21.1/5).
For (3), 6.4.1(6.4/3) again applies to the call of Make (as well as the call of Copy). We again have to check whether Y is statically deeper than the master of the call of Make.
Copy's parameter has the same accessibility as is the master of the call of Copy (by 3.10.2(7/5)). Therefore the accessibility of the parameter of Copy is tied to the master of the call of Copy. We know by applying the second part of 3.10.2(10.5/5) that the master of the call of Make is therefore the master of the call of Copy. By applying the first part of 3.10.2(10.5/5), the master of the call of Copy is that of Func. Now we can apply 3.10.2(10/5) again then know by the new rule that that the level of Y is statically deeper than the master of the call of Func, making the call at (3) illegal.
!ASIS
No ASIS effect.
!ACATS test
An ACATS B-Test should be constructed to verify that the examples are appropriately illegal.
!appendix

From: Steve Baird [part of private mail]

procedure Test1 is
   type Discriminated (D : access Integer) is null record;

   function Make (Param : aliased in out Integer)
      return Discriminated is
   begin
      return (D => Param'Access);
   end;
 
   function Func return Discriminated is
      Y : aliased Integer := 0;
   begin
      return Make (Y);       --  Legal?
   end;
 
   Result : Discriminated := Func;
begin
   null;
end;
 
 
===

We've got the 6.4.1 rule
 
   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).
 
and it is clear what the accessibility of Y is. The question is 
whether the level of Y is statically deeper than the accessibility 
level of the master of the call. Using common sense, the answer is 
obviously yes.
But we are looking for RM wording.
 
In 3.10.2 we've got this rule
 
   For determining whether a level is statically deeper than the level
   of the anonymous access type of an access result of a function or
   generic function F, when within a return statement that applies to F
   or the return expression of expression function F the function, the
   level of the master of the call is presumed to be the same as that of
   the level of the master that elaborated the function body of F.
 
which would give us just what we want if it applied in this case, 
but it doesn't. Note that there are no anonymous access types in this example.
I think this may be root of the problem - perhaps we want to amend 
this rule so that it applies in this case. I think we want a general 
rule that implies, one way or another, that the level of the locals 
declared immediately within a function is statically deeper than 
that of the master of the call to that function (although that might 
not be the right wording to express this idea).

****************************************************************

From: Justin Squirek
Sent: Thursday, June 11, 2020  12:33 AM

I was going through AI12-0372 and found the example "Test3" under the 
discussion section has some errors:

procedure Test3 is
   type Discriminated (D : access Integer) is null record;

   Glob : aliased Integer := 1;

   function Make (Param : aliased in out Integer)
      return Discriminated is
   begin
      return (D => Param'Access);
   end Make;

   function Copy (Obj : aliased in out Discriminated; 
                  Selector : in Natural) is  --  No return here
   begin
      if Selector = 0 then
          return Make (Glob); 
      else
          return Obj; 
      end if;
   end Copy;          
 
   function Func return Discriminated is
      Y : aliased Integer := 0;
   begin
      return Copy (Make (Y)); --  Missing Selector, and a function call is used
                              --  as an actual for an aliased in out formal
   end Func;
 
   Result : Discriminated := Func;
begin
   null;
end Test3;

****************************************************************

From: Randy Brukardt
Sent: Thursday, June 11, 2020  12:33 AM

The parameter to Copy needs to be mode "in", and type Discriminated needs to
be tagged. Then with the other missing text this should work and illustrate 
the issue. Sigh.


procedure Test3 is
   type Discriminated (D : access Integer) is tagged null record;

   Glob : aliased Integer := 1;

   function Make (Param : aliased in out Integer)
      return Discriminated is
   begin
      return (D => Param'Access);
   end Make;

   function Copy (Obj : aliased in Discriminated; 
                  Selector : in Natural) return Discriminated is
   begin
      if Selector = 0 then
          return Make (Glob); 
      else
          return Obj; 
      end if;
   end Copy;          
 
   function Func return Discriminated is
      Y : aliased Integer := 0;
   begin
       return Copy (Make (Y), 0);  
   end Func;
 
   Result : Discriminated := Func;
begin
   null;
end Test3;



****************************************************************

Questions? Ask the ACAA Technical Agent