!standard 4.6(50) 16-08-18 AC95-00281/00 !class confirmation 16-08-18 !status received no action 16-08-18 !status received 16-06-28 !subject Downward conversions !summary !appendix From: Jeff Cousins Sent: Tuesday, June 28, 2016 11:27 AM Using CodePeer has (indirectly) revealed that a few of our programmers seem to have circumvented the rule against converting tagged objects away from the root by converting pointers instead. GNAT doesn't give any warning. Is this legal? Shouldn't it at least require an Unchecked_Conversion of some sort? package AFU is type Root_Facade_Type is tagged limited record Error_Code : Integer; end record; end AFU; with AFU; package MCI is type Facade_Handle_Type is limited private; procedure Define_Construct (Fhandle : in out Facade_Handle_Type); private type Facade_Handle_Type is access all AFU.Root_Facade_Type'Class; end MCI; package body MCI is type Construct_Message_Data_T is record Valid_Operation : Boolean; end record; type Constructs_IF_Attr_Type is new AFU.Root_Facade_Type with record Message_In_Progress : Construct_Message_Data_T; end record; type Constructs_IF_Attr_Ptr_Type is access all Constructs_IF_Attr_Type; procedure Define_Construct (Fhandle : in out Facade_Handle_Type) is -- Downward conversion of tagged object not allowed Root_Facade : AFU.Root_Facade_Type := (Error_Code => 99); Constructs_IF_Attr : constant Constructs_IF_Attr_Type := Constructs_IF_Attr_Type (Root_Facade); -- But downward conversion of pointer to tagged object is allowed Constructs_IF_Attr_Ptr : constant Constructs_IF_Attr_Ptr_Type := Constructs_IF_Attr_Ptr_Type (FHandle); Message_Data : Construct_Message_Data_T renames Constructs_IF_Attr_Ptr.Message_In_Progress; begin if Message_Data.Valid_Operation then null; end if; end Define_Construct; end MCI; with MCI; procedure Downward_Conversion_Test is MEZ_Construct_IF_Handle : MCI.Facade_Handle_Type; begin MCI.Define_Construct (Fhandle => MEZ_Construct_IF_Handle); end Downward_Conversion_Test; *************************************************************** From: Tucker Taft Sent: Tuesday, June 28, 2016 12:01 PM So long as the access type is access-to-classwide (which is the case for Facade_Handle_Type), conversion toward the leaves is permitted. See good old RM 4.6, paragraph 24.13 and then 23/2. Conversion toward the leaves is permitted on tagged class-wide types directly as well. *************************************************************** From: Steve Baird Sent: Tuesday, June 28, 2016 1:07 PM Tuck pointed out that we don't allow a downward tagged-to-tagged conversion unless the operand's type is class-wide, and that an analogous rule applies to conversion to a general access-to-tagged type. I'll add that the consistency in the treatment of the two forms of conversion extends to the associated runtime checks. 4.6(50) says: "any checks associated with evaluating a conversion to the target designated subtype are performed" . *************************************************************** From: Randy Brukardt Sent: Tuesday, June 28, 2016 2:18 PM Right. The case in Jeff's example will always raise Constraint_Error. That is, it's not unsafe, it's just not going to work as expected. This sort of class-wide (root) to specific (leaf) conversion is commonly used; I had to use many of them in the Claw Builder. It's the only way to get at specific operations of the object if dispatching isn't appropriate. But it's important to verify that the object is in fact of the right type before doing such a conversion -- I suspect that is the real problem that Codepeer is diagnosing. I.e. type Any_Window_Access is access all Root_Window_Type'Class; type Checkbox is new Root_Window_Type with private; function Is_Checked (C : in Checkbox) return Boolean; Foo : Any_Window_Access := ...; In order to find out if Foo is checked, one ought to use code something like: if Foo.all in Checkbox then if Is_Checked (Checkbox (Foo.all)) then ... -- else not a checkbox end if; And for an access-to-checkbox type: type Checkbox_Access is access all Checkbox; if Foo.all in Checkbox then if Is_Checked (Checkbox_Access (Foo).all) then ... -- else not a checkbox end if; (I personally don't see any reason to use access-to-specific in OOP code - I think it should be discouraged - but of course YMMV.) *************************************************************** From: Jeff Cousins Sent: Tuesday, June 28, 2016 4:54 PM Many thanks for all your replies, I thought some of you clever people would have answers! I'm surprised that our code isn't failing due to a Tag_Check - presumably the Constraint_Error that Randy mentions - as the callers of Define_Construct can't have any knowledge of the derived type declared within the package body. ***************************************************************