!standard 3.10.2(12/2) 11-11-12 AI05-0281-1/01 !standard 3.10.2(12.1/2) !standard 3.10.2(12.5/3) !class ramification 11-11-12 !status ARG Approved 5-0-4 11-11-12 !status work item 11-11-08 !status received 11-09-26 !priority Low !difficulty Hard !qualifier Omission !subject Accessibility of access discriminants of a subtype !summary The subtype_indication mentioned in 3.10.2(12.1/2) is not necessarily declared at the place of the check (which is an allocator or return statement); it could be declared in a separate declaration elsewhere. !question Consider: procedure Condit_Expr_Accessibility is Flag : Boolean := True; Test_Failed : exception; type Drec (Int_Ref : access Integer) is null record; type Global_Ref is access Drec; Global_Ptr : Global_Ref; Global_Var : aliased Integer; procedure Nested is type Local_Ref is access Drec; Local_Ptr : Local_Ref; Local_Var : aliased Integer; function Checker return Drec is -- Flag is always True at this point, but the -- compiler doesn't know that. subtype S is Drec (Int_Ref => (if Flag then Local_Var'Access else Global_Var'Access)); begin Flag := False; return X : S; end Checker; begin Local_Ptr := new Drec'(Checker); -- -- for the above call, it is ok for -- Checker to return a result which --- references Local_Var Flag := True; begin Global_Ptr := new Drec'(Checker); -- -- for the above Call, returning -- a result which references Local_Var -- should fail an accessibility check raise Test_Failed; exception when Program_Error => null; end; end Nested; begin Nested; end Condit_Expr_Accessibility; In order to make the return accessibility check, we need to know the accessibility of S.Int_Ref. 3.10.2(12.1/2) does not apply (because this is not in an allocator or return statement), so we use 3.10.2(12.5/3). But that says that it is the "accessibility of the enclosing object" -- and there is no enclosing object. Is this right? (No.) !response The subtype_indication mentioned in 3.10.2(12.1/2) is not necessarily declared in the allocator or return statement; the *check* has to occur in one of those contexts, but not the declaration of the subtype. So 3.10.2(12.1/2) does apply, and there is no problem. !wording Add an AARM note after 3.10.2(12.d/2) to clarify this: The subtype_indication mentioned in this bullet is not necessarily the one given in the allocator or return statement that is determining the accessibility level; the constrained subtype might have been defined in an earlier declaration (as a named subtype). !discussion It would be nice to make this clearer in the normative wording, but this wording is clear as mud already, so messing with it further is unlikely to help. Thus we just add an AARM note. !ACATS Test An ACATS C-Test should be created (but this is likely to be low priority). !ASIS No ASIS impact. !appendix From: Steve Baird Sent: Monday, September 26, 2011 5:24 PM The following issue came up while reviewing interactions between conditional expressions and accessibility, but it seems that conditional expressions are not an essential part of the problem. Consider: procedure Condit_Expr_Accessibility is Flag : Boolean := True; Test_Failed : exception; type Drec (Int_Ref : access Integer) is null record; type Global_Ref is access Drec; Global_Ptr : Global_Ref; Global_Var : aliased Integer; procedure Nested is type Local_Ref is access Drec; Local_Ptr : Local_Ref; Local_Var : aliased Integer; function Checker return Drec is -- Flag is always True at this point, but the -- compiler doesn't know that. subtype S is Drec (Int_Ref => (if Flag then Local_Var'Access else Global_Var'Access)); begin Flag := False; return X : S; end Checker; begin Local_Ptr := new Drec'(Checker); -- -- for the above call, it is ok for -- Checker to return a result which --- references Local_Var Flag := True; begin Global_Ptr := new Drec'(Checker); -- -- for the above Call, returning -- a result which references Local_Var -- should fail an accessibility check raise Test_Failed; exception when Program_Error => null; end; end Nested; begin Nested; end Condit_Expr_Accessibility; On returning from Checker, we need to perform a check that the discriminant of the result designates something which is sufficiently long-lived: RM 6.5: 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 return statement, is not deeper than the level of the master of the call (see 3.10.2) So in this case, what is the "accessibility level of the anonymous access type of each discriminant". What is the accessibility level of, speaking loosely, the type of S.Int_Ref? I don't believe that 3.10.2 handles this case correctly, Strictly speaking, I think it falls into the "when others" handler: The accessibility level of the anonymous access type of an access discriminant in any other context is that of the enclosing object. This is obviously absurd (there is no enclosing object - S is a subtype). One solution would be to use the rule that is currently used only in the special case where the subtype_indication in question is the subtype_indication for an extended return statement or for an allocator: If the value of the access discriminant is determined by a discriminant_association in a subtype_indication, the accessibility level of the object or subprogram designated by the associated value (or library level if the value is null) It seems clear that this rule was very explicitly intended only for the special cases of allocators and return statements. Was this an oversight? **************************************************************** !response The rules clearly were intended to apply only to "original" declarations, and not the declaration of a completion. No user or implementer is going to misinterpret these rules, given that a literal interpretation would prevent the declaration of any concrete tagged type with primitive operations (those operations could never be completed). Moreover, one could argue that subprogram declarations in a body are not primitive subprograms - whether or not completions of a primitive subprogram also is a primitive subprogram when a stand-alone subprogram would not be primitive is not clearly addressed by 3.2.3(2-7) -- a literal reading would say that they are not. But that would just make the issue even more confusing. With the unlikelyhood of misinterpretation, we simply add a To Be Honest note to the AARM to make it crystal clear that completions are not considered. Note the introduction of expression functions by AI05-0177-1 makes this a more significant issue. We would not want the following to be illegal: package Pak2 is type T is tagged private; function F (Obj : T) return Boolean; private type T is tagged record Flag : Boolean; end record; Object : T; -- Freezes T. function F (Obj : T) return Boolean is (Obj.Flag); end Pak2; !wording Add a new AARM note after 3.9.2(13) and after 13.14(16): AARM To Be Honest: This rule only applies to "original" declarations and not to the completion of a primitive subprogram, even though a completion is technically an explicit declaration, and it may declare a primitive subprogram. !ACATS Test An ACATS test could be constructed for the expression function case (the example of the question is too much of a pathology to matter). !ASIS This has no impact on ASIS. !appendix !topic Subprogram body declarations and 3.9.2(13) !reference RM 3.9.2(13), 13.14(16), 6.3(5) !from Adam Beneschan 10-03-25 !discussion This is a wording nitpick that I'm not sure really needs to be fixed: package Pak1 is type T1 is tagged record ... end record; procedure Op (X : in out T1); end Pak1; with Pak1; procedure Proc2 is type T2 is new Pak1.T1 with record ... end record; overriding procedure Op (X : in out T2); Var : T2; overriding procedure Op (X : in out T2) is -- (A) begin ... end Op; ... Shouldn't cause any problems, right? Op (on T2) is declared before Var, which is before T2 is frozen. However, the way things are worded: 3.9.2(13): "The explicit declaration of a primitive subprogram of a tagged type shall occur before the type is frozen." 6.3(5): "A subprogram_body is considered a declaration. It can either complete a previous declaration, or itself be the initial declaration of the subprogram." one could think that since line (A) is a declaration, and it's occurring after the type is frozen, that 3.9.2(13) makes it illegal. Perhaps the intent is so obvious that no fix is needed; but if it's considered incorrect, then changing 3.9.2(13) and 13.14(16) to something like The explicit declaration (that is not the completion of a previous declaration) of a primitive subprogram of a tagged type shall occur before the type is frozen. would work. ****************************************************************