Version 1.1 of ai22s/ai22-0017-1.txt

Unformatted version of ai22s/ai22-0017-1.txt version 1.1
Other versions for file ai22s/ai22-0017-1.txt

!standard 13.11(21)          21-11-12 AI22-0017-1/00
!standard 13.11.4(21/3)
!standard 13.11.4(31/3)
!class binding interpretation 21-11-12
!status work item 21-11-12
!status received 21-04-28
!priority Low
!difficulty Hard
!qualifier Clarification
!subject Accessibility level of an object in a declare expression
!summary
** TBD
!question
The accessibility level of an object declared within a declare expression is well defined by RM 3.10.2(7), but this definition has some perhaps-unexpected consequences.
Given
procedure Proc (B : Boolean) is .... ;
begin Proc (Boolean'(declare X : constant Some_Controlled_Type := ...; begin True));
is it really intended that X should be finalized after, not before, the call to Proc?
Does it change anything if we introduce a quantified expression so that the declaration of X is elaborated more than once, as in
procedure Proc (B : Boolean) is .... ;
begin Proc ((for all I in 1 .. 100 => Boolean'(declare X : constant Some_Controlled_Type := ...; begin True)));
, so that multiple objects require finalization?
There is an AARM note after the rule prohibiting use of 'Access and similar things within a declare expression:
Reason: We do not want to define accessibility rules for declare_items, as nested declare_expressions cause complexities or usage warts. We want to keep this feature simple to use, understand, and implement. Thus, we do not allow any of the features that would require accessibility rules.
It appears that this goal was not completely achieved.
Usually, evaluation of a declare expression
(decl1; decl2; begin expr)
can be thought of being like evaluation of a call to
function Some_Function
decl1; decl2;
begin return expr; end;
(at least if the type of the declare expression is not limited).
But this equivalence breaks down when we start talking about accessibility and finalization because the execution of a function body is a master and the execution of a declare expression typically is not (although it could be a master in the same scenarios where any expression can be a master - see RM 7.6.1(3)).
Consider another example:
procedure Access_Type_Conversion_Test is pragma Assertion_Policy (Check);
type Rec; type Drec (Discrim : access Rec) is null record; type Rec is limited record Self : Drec (Rec'access); end record;
type Ref is access all Rec; Ptr : Ref;
function Rec_Init return Rec is begin return Result : Rec; end Rec_Init;
function Update_Ptr (X : access Rec) return Boolean is begin Ptr := Ref (X); return False; end Update_Ptr;
function Subp_Version return Boolean is Local : Rec; begin return Update_Ptr (Local.Self.Discrim); end Subp_Version;
-- Flag1 : constant Boolean := Subp_Version;
Flag2 : constant Boolean := (declare Local : constant Rec := Rec_Init; begin Update_Ptr (Local.Self.Discrim)); begin pragma Assert (Ptr /= null); end Access_Conversion_Test;
The commented-out declaration of Flag1 would, if uncommented, fail an accessibility check on the type conversion inside the function Update_Ptr. This is consistent with the general idea of preventing references to an object declared inside a subprogram from "leaking out" and persisting after the subprogram's execution has completed.
The same is not true if we use a declare expression instead of a function (as is done in the declaration of Flag2). Because a declare expression is not a master, the accessibility check in Update_Ptr succeeds in this case and Ptr is allowed to designate the object Local declared within the declare expression. The consequences of this for implementations get even messier when a declare expression is involved repeatedly as part of another expression which involves multiple evaluations of a subexpression (e.g., a quantified expression or an array aggregate - see the quantified expression example above).
Is it intended that this should be allowed? Is some action needed here?
!recommendation
** TBD.
!wording
** TBD.
!discussion
** TBD.
!ACATS test
** TBD.
!appendix

From: Steve Baird [privately]
Sent: Tuesday, August 31, 2021  7:46 PM

Do you remember what we were trying to prevent with the rule that an
Access attribute reference cannot occur in a declare expression?

Whatever it is we were trying to prevent, I'm wondering whether we left the
door open for something similar by allowing a call where the
formal parameter is explicitly aliased and the actual parameter object was
created as "part of " (speaking loosely) evaluating a declare expression.

I don't see any problems with the following example, but it does let me
get the same effect as if I had used Rr.Ff.F'Access to constrain Dr (except
that doing it that way would be illegal) and that makes me wonder if there
is a problem lurking somewhere nearby.

with Text_Io; use Text_Io;
procedure Declare_Expr_Finalization is
   pragma Assertion_Policy (Check);

   type Rec is record F : aliased Integer; end record;
   type Rec_Rec is record FF : aliased Rec; end record;

   D_Value : Integer := 0;

   package Pkg is
      type Drec (D : access constant Integer) is new Ada.Finalization.Controlled
        with null record;
      overriding procedure Finalize (X : in out Drec);
   end Pkg;
   package body Pkg is
      procedure Finalize (X : in out Drec) is
      begin
         D_Value := X.D.all;
         Put_Line ("In Finalize");
      end Finalize;
   end Pkg;
   
   function Make (R : aliased Rec) return Pkg.Drec is
       (Ada.Finalization.Controlled with D => R.F'Access);

   X : Integer :=
     (declare
         Rr : constant Rec_Rec := (Ff => (F => 123));
         Dr : Pkg.Drec renames Make (Rr.Ff);
      begin
         Dr.D.all);
begin
   Put_Line ("Hello");
   pragma Assert (X = 123);
   pragma Assert (D_Value = 123);
end Declare_Expr_Finalization;

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

From: Randy Brukardt [privately]
Sent: Friday, September 3, 2021  11:40 PM

AARM says (immediately after the Legality Rule):
 
Reason: We do not want to define accessibility rules for declare_items, as
nested declare_expressions cause complexities or usage warts. We want to keep
this feature simple to use, understand, and implement. Thus, we do not allow 
any of the features that would require accessibility rules. 
 
I'm sure that Baird character had constructed some examples that made our
heads explode.
 
It would appear that the case of a declare expression returning a function 
whose accessibility level is *significant* also needs to be banned, because
otherwise one needs to be able to define the accessibility of the return
object -- and we just said we didn't want to do that. So probably we would
need to ban directly returning a function call with aliased parameters. (Humm,
not sure the model here, we are assuming no B-I-P is required but that seems
weird for functions like this.)
 
As far as it not working perfectly, that I don't doubt -- I think you already 
proved that the intent to delay finalization until the entire expression is
finished doesn't work very well if a loop (such as a quantified expression)
contains a declare expression. (I also think that the answer is "tough"; if it
hurts, introduce a master [presumably with a subprogram].)

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

Questions? Ask the ACAA Technical Agent