!standard 4.6 (8-23) 99-05-14 AI95-00219/01 !class binding interpretation 99-05-14 !status work item 99-05-14 !status received 99-05-14 !priority Medium !difficulty Easy !subject Conversions between related types. !summary It is always legal to convert between two untagged types if they have a common ancestor. It always legal to convert between two tagged types if they have a common ancestor, and obey the additional restrictions of 4.6(22-23). !question Is it always legal to convert between two untagged types if they have a common ancestor? (Yes.) Is it always legal to convert between two tagged types if they have a common ancestor, and obey the additional restrictions of 4.6(22-23)? (Yes.) There appear to be cases where the RM says no. For example: package Type_Conversion is type Root is private; private type Root is access all Integer; end Type_Conversion; package Type_Conversion.Son is type Son_Type is new Root; end Type_Conversion.Son; with Type_Conversion.Son; use Type_Conversion.Son; package Type_Conversion.Daughter is type Daughter_Type is new Root; Son_Var: Son_Type; Daughter_Var: Daughter_Type; A: Daughter_Type := Daughter_Type(Son_Var); -- Legal. B: Son_Type := Son_Type(Daughter_Var); private -- Here, Daughter_Type becomes a general access type. C: Daughter_Type := Daughter_Type(Son_Var); -- Illegal?! D: Son_Type := Son_Type(Daughter_Var); end Type_Conversion.Daughter; In the declaration of C, the target type is a general access type (by 7.3.1) so 4.6(13) applies. The source type is not an access-to-object type (when viewed from that place), so it's illegal. 4.6(21) does not apply, because the target type *is* included in one of the "above four cases". Was this intended? (No.) Note that the same wording problem arises for the special cases mentioned in 4.6(8,9,18) -- numerics, arrays, and, access-to-subprogram. !recommendation Conversions between types with a common ancestor are always legal unless forbidden by 4.6(22-23). !wording Move 4.6(21-23) earlier, before all the special cases, and make it clear that the special cases only apply when the "common ancestor" case doesn't. (** I'd prefer REAL wording, but I'm not write it for Bob as I did with the rest of the AI. - Editor **) !discussion The RM wording causes several surprising results. Consider the declaration of A in the example above. It is clearly legal. But moving it into the private part makes it illegal. Normally moving something into a private part or body *increases* the things you're allowed to do. Another surprise occurs if we remove the "all" from the full declaration of Root. In this case the declaration of C is clearly legal, because then 4.6(21) applies. It would be very strange if adding "all" makes some conversions illegal. Most importantly, the RM as written is incompatible with Ada 83, which allowed conversions between any two type related by derivation. Certainly no such incompatibility was intended; no such incompatibility is listed under the "Incompatibilities with Ada 83" heading in the AARM. We therefore conclude that the behavior defined by the RM was not intended by the language designers. !appendix !topic Type conversions of derived types !reference RM-4.6(8-23), 7.3.1 !from Bob Duff !keywords type conversion, derived type !discussion Question: Is it always legal to convert between two untagged types if they have a common ancestor? Is it always legal to convert between two tagged types if they have a common ancestor, and obey the additional restrictions of 4.6(22-23)? The answers should be "Yes", but a literal reading of the RM implies "No" in some cases. Example: package Type_Conversion is type Root is private; private type Root is access all Integer; end Type_Conversion; package Type_Conversion.Son is type Son_Type is new Root; end Type_Conversion.Son; with Type_Conversion.Son; use Type_Conversion.Son; package Type_Conversion.Daughter is type Daughter_Type is new Root; Son_Var: Son_Type; Daughter_Var: Daughter_Type; A: Daughter_Type := Daughter_Type(Son_Var); -- Legal. B: Son_Type := Son_Type(Daughter_Var); private -- Here, Daughter_Type becomes a general access type. C: Daughter_Type := Daughter_Type(Son_Var); -- Illegal?! D: Son_Type := Son_Type(Daughter_Var); end Type_Conversion.Daughter; In the declaration of C, the target type is a general access type (by 7.3.1) so 4.6(13) applies. The source type is not an access-to-object type (when viewed from that place), so it's illegal. 4.6(21) does not apply, because the target type *is* included in one of the "above four cases". That's clearly not the intent of the language designers. Reasons: First of all, I think it would be incompatible with Ada 83 (although I'm not sure -- I don't have my Ada 83 RM at hand, and I've largely forgotten that language ;-)). Certainly no such incompatibility was intended; no such incompatibility is listed under the "Incompatibilities with Ada 83" heading in the AARM. Second, consider the declaration of A. It is clearly legal. Why should moving it into the private part make it illegal? Normally moving into a private part or body *increases* the things you're allowed to do. Third, if we remove "all" from the full declaration of Root, then the declaration of C is clearly legal, because then 4.6(21) applies; it would be very strange for the "all" to have this effect. Fourth, the intent is that 4.6(21-23) should always be applicable, and that's the only sensible rule; this problem is merely an accident of wording. Note that the same wording problem arises for the special cases mentioned in 4.6(8,9,18) -- numerics, arrays, and, access-to-subprogram. I hesitate to suggest wording changes, but here's a timid attempt: Move 4.6(21-23) earlier, before all the special cases, and make it clear that the special cases only apply when the "common ancestor" case doesn't. Current behavior of some compilers: As of yesterday, the Averstar front end finds it illegal, but I've changed it to make it legal, in anticipation of the expected ARG ruling. The Rational compiler finds it legal. The GNAT compiler (version 3.11p) doesn't complain about the declaration of C, but complains on the declaration of D that Son_Type is not visible. If I remove D, GNAT is happy, so that's apparently a bug not directly related to this issue. Note: I tripped over this problem in real code I was modifying (in our run-time system); I changed an "access" to "access all", and some type conversions suddenly became illegal (according to our compiler). The comments in the compiler sent me to 4.6(13), and I was surprised to find that it was doing exactly what the RM literally says. - Bob *************************************************************