!standard 12.5.1(5.1/3) 14-05-08 AI12-0036-1/03 !class binding interpretation 12-11-29 !status Corrigendum 1-2012 13-12-06 !status WG9 Approved 14-06-27 !status ARG Approved 9-0-2 13-11-16 !status work item 12-11-29 !status received 12-08-01 !priority Low !difficulty Medium !qualifier Omission !subject The actual for an untagged formal derived type cannot be tagged !summary The actual for an untagged formal derived type cannot be a tagged type. !question Consider: generic type T1 is private; type T2 is new T1; type T3 is new T1; package Pack1 is procedure Proc; end Pack1; package body Pack1 is V1 : T1; V2 : T2; V3 : T3; procedure Proc is begin V2 := T2(V1); -- (A) V3 := T3(V2); -- (B) V1 := T1(V2); -- (C) end Proc; end Pack1; type Root is tagged null record; type Child1 is new Root with record ... end record; type Child2 is new Root with record ... end record; package Inst is new Pack1 (T1 => Root, T2 => Child1, T3 => Child2); Nothing in the Standard makes this illegal. But the conversions (A) and (B) clearly cannot be allowed, as they would leave the extension components uninitialized. Indeed, such conversions would be illegal if type T1 was a tagged private type (and the other types were type extensions). (Conversions like (C) are not a problem for this case.) Some rule has to make this illegal, right? (Yes.) !recommendation (See summary.) !wording Modify 12.5.1(5.1/3): The actual type for a formal derived type shall be a descendant of the ancestor type and every progenitor of the formal type. If the formal type is nonlimited, the actual type shall be nonlimited. {The actual type for a formal derived type shall be tagged if and only if the formal derived type is a private extension.} If the reserved word synchronized appears in the declaration of the formal derived type, the actual type shall be a synchronized tagged type. !discussion An alternative solution would be to adjust the type conversion rules to disallow converting the formal derived type to its ancestor type, if that type could be tagged (that is, is a formal private type or a formal derived type whose ancestor could be tagged). We selected the simpler rule of disallowing the instantiation, as it seems less likely to cause compatibility problems. For instance, Ada doesn't have an "any scalar type" kind of generic formal; formal private types are often used for that purpose. In that case, conversions to the ancestor would be expected and likely, but the generic would not be intended to be instantiated with any composite type (like a tagged type). On the other hand, rejecting the instance is unlikely to be a problem, as most such cases will want to allow extension and thus will be using tagged private types as the ancestor. In addition, untagged formal derived types are rare, so compatibility issues are likely to be much rarer. This solution is still incompatible, but only in cases where the formal derived type does not accurately describe the actual. As noted above, we believe such instances will be rare. Note that the problem does not require the types to be declared in the same generic unit; the problem also appears in cases like: generic type T1 is private; package Pack1 is generic type T2 is new T1; type T3 is new T1; package Inner_Generic is ... end Pack1; or: generic type T1 is private; package Pack1 is ... end Pack1; generic type T2 is new T1; type T3 is new T1; package Pack1.Child is ... end Pack1.Child; The matching rule works in these cases as well. We make the rule symmetric as it is possible to match a formal private extension with a nontagged type: generic type N is tagged private; type NC is new N with private; package GG is ... end GG; package P is type T1 is private; private type T1 is tagged ... end P; with P; package Q is type T2 is new P.T1; end Q; with Q; package body P is package G is new GG (T1, T2); ... end P; Here, T1 is tagged, but T2 is not tagged nor an extension. It's not clear that there is any semantic problem with allowing a case like this, but we're taking the safest option here. !corrigendum 12.5.1(5.1/3) @drepl The actual type for a formal derived type shall be a descendant of the ancestor type and every progenitor of the formal type. If the formal type is nonlimited, the actual type shall be nonlimited. If the reserved word @b appears in the declaration of the formal derived type, the actual type shall be a synchronized tagged type. @dby The actual type for a formal derived type shall be a descendant of the ancestor type and every progenitor of the formal type. If the formal type is nonlimited, the actual type shall be nonlimited. The actual type for a formal derived type shall be tagged if and only if the formal derived type is a private extension. If the reserved word @b appears in the declaration of the formal derived type, the actual type shall be a synchronized tagged type. !ASIS No ASIS impact. !ACATS test An ACATS B-Test should be created to test these rules. !appendix !topic Problem with untagged generic formal derived types !reference 4.6 !from Adam Beneschan 12-08-01 !discussion This example is based on a comp.lang.ada post from Georg Bauhaus. generic type T1 is private; type T2 is new T1; type T3 is new T1; package Pack1 is procedure Proc; end Pack1; package body Pack1 is V1 : T1; V2 : T2; V3 : T3; procedure Proc is begin V2 := T2(V1); -- (A) V3 := T3(V2); -- (B) V1 := T1(V2); -- (C) end Proc; end Pack1; type Root is tagged null record; type Child1 is new Root with record ... end record; type Child2 is new Root with record ... end record; package Inst is new Pack1 (T1 => Root, T2 => Child1, T3 => Child2); I can't find a rule that makes the declaration or body of Pack1 illegal; and the instantiation Inst appears to meet all the requirements. However, the result is that the type conversions in (A) and (B) in the instance body would be illegal conversions outside the instance body; and I think we don't want them to be legal even in an instance body since the result will be that the components specified in the extension parts will not be set to anything. I believe this is a contract model violation. My suggestion: change 4.6(21.1/2) from The target type shall be untagged; to The target type shall be untagged, and shall not be a descendant of a generic formal derived type whose ultimate ancestor is a generic formal type. This would make the type conversions (A) and (B) illegal. (Although paragraph 4.6(21.1) would no longer apply to (C), (C) would still be legal because it's also handled by 4.6(22).) (Note that the generic formals involved don't have to be in the same generic formal part; the problem is the same in cases like generic type T1 is private; package Pack1 is generic type T2 is new T1; type T3 is new T1; package Inner_Generic is ... end Pack1; or generic type T1 is private; package Pack1 is ... end Pack1; generic type T2 is new T1; type T3 is new T1; package Pack1.Child is ... end Pack1.Child; and the proposed solution should also work.) I think adding this rule is very unlikely to break any existing code or cause problems. In fact, I can't even think of a reason why one would need to declare generic formal types as above and instantiate the generic with *untagged* types; thus, usually, the formal types should be declared as tagged private or as private extensions, which would make (A) and (B) illegal under existing rules. I tried to think of other cases, besides type conversions, where instantiating an untagged generic formal derived type with a tagged actual could cause problems; but I couldn't think of any. If there are, an alternative would be to add a rule like The actual for a generic formal untagged derived type may not be a tagged type somewhere in 12.5.1. **************************************************************** From: Tucker Taft Sent: Wednesday, August 1, 2012 2:31 PM > The actual for a generic formal untagged derived type may not be a > tagged type > > somewhere in 12.5.1. This latter change seems like the most straightforward one (after the usual "may not" -> "shall not" fix ;-). ****************************************************************