!standard 4.3.1(17) 10-08-04 AI05-0220-1/01 !class binding interpretation 10-06-13 !status work item 10-06-13 !status received 10-03-16 !priority Low !difficulty Easy !qualifier Clarification !subject Definition of "needed component" !summary Staticness requirements clarified for record aggregate discriminants. !question Consider: type Enum is (One, Two, Three); type Variant_Record (Disc : Enum) is record case Disc is when One => I : Integer; when Two => F : Float; when Three => null; end case; end record; Variant_Part' (Disc => Function_Call(Param)); 4.3.1(9) says that the components that are "needed" are determined by the "composite value"; that depends on the discriminant values. The discriminant values can be determined at runtime; in this case the needed components are not known until runtime. But "needed" components participate in Legality Rules and clearly need to be known at compile-time. 4.3.1(17) attempts to require a static discriminant value, but it depends on the definition of "needed". Thus, one could argue that the aggregate above is legal if Function_Call returns Three. That is obviously nonsense, but exactly where is the line drawn? If we have: type Variant_Record_2 (Disc : Enum) is record case Disc is when others => null; end case; end record; R : Variant_Record_2 := (Disc => Some_Function_Call); In this case, there are no components in the variant part, so it hard to see how 4.3.1(17) applies. But it seems needlessly complex to require the existence of components to make this check. Should this aggregate be rejected? (Yes.) Similarly: type Enum is (One, Two, Three); type Variant_Record_3 (Disc : Enum; Disc2 : Boolean) is record case Disc is when One => I : Integer; case Disc2 is when True => J : Integer; when False => null; end case; when Two => F : Float; when Three => null; end case; end record; Variant_Record_3' (Disc => Two, Disc2 => Function_Call, F => 2.0); Here, Disc2 is dynamic, but it is known to not control a variant since Disc is statically known to be Two. Should this aggregate be rejected? (No.) !recommendation (See Wording.) !wording Replace 4.3.1(17) If the components of a variant_part are needed, then the value of a discriminant that governs the variant_part shall be given by a static expression. with The value of a discriminant that governs a variant_part P shall be given by a static expression, unless P is nested within a variant V that is not selected by the discriminant value governing the variant_part enclosing V. !discussion The existing wording is quite clear that it applies only when there are actually components. However, as the questioner points out, this makes little sense since it makes the interpretation of the rule dependent on discriminant values that aren't necessarily known when this rule is enforced. Essentially, the rule is circular in that it depends on itself in order to be understandable. Thus we adopt a rule that does not depend on components that are "needed". For this new rule, the variants of the variant_part P mentioned in the wording play no role. In particular, it makes no difference whether the variant_part P contains no component_declararations. This interpretation is technically an incompatibility, as aggregates containing a variant_part with no components controlled by a dynamic discriminant are now rejected, while with the old rule these appear to be legal. This incompatibility would be rare in practice. Moreover, a survey of existing compilers show that all reported (GNAT, Irvine, AdaMagic, Janus/Ada) actually do not consider whether a variant has components in enforcing 4.3.1(17). Thus, the incompatibility does not exist in practice (compilers already reject such programs). Note that wording following "unless" exists to allow the last example of the !question to remain legal. This is necessary to avoid an unnecessary incompatibility: 3 out of 4 existing compilers currently allow this example. There seems to be no important reason to change this result. Finally, the following example was illegal before this change and remains illegal: type T1 (D1 : Boolean) is tagged record case D1 is when False => C : Character; ... when True => S : String(1..10); ... end case; end record; type T2 (D2 : Integer) is new T1 (D1 => Some_Non_Static_Value) with ...; X2 : T2 := (D2 => 123, ....); -- Illegal In this case, the offending non-static discriminant value for D1 does not occur within the aggregate, but the rule still applies and the aggregate is illegal. !corrigendum 4.3.1(17) @drepl If the components of a @fa are needed, then the value of a discriminant that governs the @fa shall be given by a static expression. @dby The value of a discriminant that governs a @fa @i

shall be given by a static expression, unless @i

is nested within a @fa @i that is not selected by the discriminant value governing the @fa enclosing @i. !ACATS Test Add an additional ACATS B-Test to check that the corner cases clarified by this AI (see the !appendix for part of a possible test). !appendix !topic Definition of "needed component" !reference 4.3.1 !from Adam Beneschan 10-03-16 !discussion While looking over the record aggregate rules when responding to a recent comp.lang.ada post, it occurred to me that the rules regarding components in variant parts might be incorrect. Consider: type Enum is (One, Two, Three); type Variant_Record (Disc : Enum) is record case Disc is when One => I : Integer; when Two => F : Float; when Three => null; end case; end record; For a record_aggregate of type Variant_Record, 4.3.1(9) says that "all copmonents of the value defined by the aggregate are needed". This means that the "needed components" depend on the value of the discriminant (and 9.a in the AARM says so explicitly). Thus, for example, in (Disc => One, I => 5), F is not needed, and for (Disc => Three), neither I nor F is needed. 4.3.1(17) says, "If the components of a variant part are needed, then the value of a discriminant that governs the variant_part shall be given by a static expression". But, by 4.3.1(9), whether "the components of a variant part are needed" depends on the actual value of the discriminant, and thus can't be determined statically. So in this case: Variant_Part' (Disc => Function_Call(Param)); If the function returns Three, then 4.3.1(17) wouldn't apply, since no components of a variant part are needed, and thus the aggregate would be legal. This can't make sense---certainly, the legality of the aggregate can't depend on what the function is going to return at runtime! So I think 4.3.1(17) is worded incorrectly. Maybe it can be solved by defining the concept of a variant_part being "needed"; change 4.3.1(9) to: For the record_component_association_list of a record_aggregate, all components of the composite value defined by the aggregate are needed; for the association list of an extension_aggregate, only those components not determined by the ancestor expression or subtype are needed (see 4.3.2). For the record_component_association_list of a record_aggregate, if the aggregate's type has a variant_part, then that variant_part is needed; for the association list of an extension_aggregate, variant_parts that are part of the aggregate's type, and that are not part of a record type that is an ancestor of the ancestor expression's type or of the ancestor subtype, are needed. Each selector_name in ..." and then change 4.3.1(17) to "If a variant_part is needed, then the value of a discriminant that governs the variant_part shall be given by a static expression". Note: GNAT rejects this, which I think is correct: type Variant_Record_2 (Disc : Enum) is record case Disc is when others => null; end case; end record; R : Variant_Record_2 := (Disc => ); but one could argue from the rules as currently written that this should be legal since there is no possibility that a component in a variant part would be "needed". If the *intent* is for this to be legal, then my proposed change would be an incompatibility. I'm sort of guessing that this isn't a problem. **************************************************************** From: Adam Beneschan Sent: Thursday, March 18, 2010 10:40 PM I realized this morning that this isn't quite correct and would introduce an incompatibility. In this case: type Enum is (One, Two, Three); type Variant_Record (Disc : Enum; Disc2 : Boolean) is record case Disc is when One => I : Integer; case Disc2 is when True => J : Integer; when False => null; end case; when Two => F : Float; when Three => null; end case; end record; the aggregate Variant_Record' (Disc => Two, Disc2 => B, F => 2.0); should be legal even if B is nonstatic; since Disc is statically determined to be Two, the variant part governed by Disc2 isn't used. But the way I worded it, the variant_part governed by Disc2 would still be "needed", because it's there. So another sentence should be added to my proposed wording: For the record_component_association_list of a record_aggregate, if the aggregate's type has a variant_part, then that variant_part is needed; for the association list of an extension_aggregate, variant_parts that are part of the aggregate's type, and that are not part of a record type that is an ancestor of the ancestor expression's type or of the ancestor subtype, are needed. In either case, though, a variant_part that is part of the component_list of an enclosing variant_part is needed only if the enclosing variant_part is needed, and if the discrete_choice_list for the component_list containing the inner variant part covers the value [which must be static] of the discriminant that governs the enclosing variant_part. **************************************************************** From: Steve Baird Sent: Monday, August 2, 2010 6:52 PM Randy and I have been discussing solving the problem identified in this AI by replacing 4.3.1(17) If the components of a variant_part are needed, then the value of a discriminant that governs the variant_part shall be given by a static expression. with something along the lines of If the value of a discriminant that governs a variant_part determines which components are needed, then the value of that discriminant shall be given by a static expression. The variants of the given variant_part play no role in determining the applicability of this rule. AARM note: For example, a variant_part which contains no component_declarations in any of its variants is treated the same (with respect to this rule) as a variant_part which contains component_declarations. The second sentence is intended to clarify the meaning of he word "determines" in the first sentence. We don't want to have to worry about oddball corner cases where one could argue that a nonstatic discriminant might be allowed. On the other hand, it seems important that we do allow a non-static discriminant to govern a nested variant part in the case where the (static) discriminant value governing an enclosing variant_part did not select the enclosing variant of the variant_part in question. The above wording is intended to accomplish this and is hoped to be just a confirmation of existing practice for implementations. This has been confirmed for the GNAT compiler. This AI is not intended to make work for implementers. The goal is to define the "nobody cares" corner cases in a way that is consistent with existing common practice. The test given below can be used to check whether an existing implementation already implements the proposed rule. Randy reports that the Janus compiler rejects the 4 aggregates marked "ERROR", but also rejects the aggregate marked "legal". Results from other compilers could be useful in evaluating this proposal. Comments? -- Steve ===== procedure Corner_Cases (Non_Static : Boolean) is type T1 (D : Boolean) is record case D is when False => null; when True => null; end case; end record; X1 : T1 := (D => Non_Static); -- ERROR type T2 (D : Boolean) is record case D is when Boolean => null; end case; end record; X2 : T2 := (D => Non_Static); -- ERROR type T3 (D : Natural) is record case D is when Natural => null; when others => Unlikely : Float; end case; end record; X3 : T3 := (D => Boolean'Pos (Non_Static)); -- ERROR type T4 (D1, D2 : Boolean) is record case D1 is when False => null; when True => case D2 is when False => null; when True => Int : Integer; end case; end case; end record; X4_A : T4 := (D1 => Non_Static, D2 => False); -- ERROR X4_B : T4 := (D1 => False, D2 => Non_Static); -- legal begin null; end Corner_Cases; **************************************************************** From: Dan Eilers Sent: Monday, August 2, 2010 7:05 PM > Randy reports that the Janus compiler > rejects the 4 aggregates marked "ERROR", but also rejects the > aggregate marked "legal". Results from other compilers could be useful > in evaluating this proposal. Our compiler rejects the 4 aggregates marked "ERROR", and accepts the one marked "legal". **************************************************************** From: Tucker Taft Sent: Monday, August 2, 2010 7:22 PM I don't find the second sentence as being very helpful. Better might be to put it into the AARM note and make the AARM note more explicit. I wonder whether it might be better to make the following incremental wording change: If the components of a variant_part are needed, {even if there are none,} then the value of a discriminant that governs the variant_part shall be given by a static expression. I don't think your rewording really solves the problem anyway, since if all variant alternatives are "null" then you are back to the situation where there are no components, so the value of the discriminant might be thought to be irrelevant depending on the interpretation. I suppose your second sentence was supposed to clarify, but it really didn't help me at all. For the record, the AdaMagic front end complains about the aggregates just as you predicted in your "Corner_Cases" example, so with my vendor hat on, I support the proposed "interpretation" of this rule that you have suggested. **************************************************************** From: Steve Baird Sent: Tuesday, August 3, 2010 12:44 PM > I don't find the second sentence as being very helpful. Better might > be to put it into the AARM note and make the AARM note more explicit. Would it be better if we spell things out more explicitly? The value of a discriminant that governs a variant_part shall be given by a static expression, unless - the variant_part occurs within an enclosing (not necessarily immediately enclosing) variant_part; and - the variant enclosing the enclosed variant_part is not selected by the discriminant value governing the enclosing variant_part. If it seems necessary, we could add another item in the middle of that bulleted list: - the value of the discriminant that governs the enclosing variant_part is given by static expression; and I don't think this is necessary. The problem I see with this approach is illustrated by the following example: type T1 (D1 : Boolean) is record case D1 is when False => null; when True => Int : Integer; end case; end record; type T2 (D2 : Boolean) is new T1 (D1 => Some_Non_Static_Value) with null record; X : T2 := (D2 => False); -- must be illegal It is not clear (at least to me) that the wording I've proposed above covers this case. We want to reject this because the variant_part in T1 (or perhaps the inherited copy thereof in T2, although the RM never speaks of inheriting a variant_part) is governed by Some_Non_Static_Value, an expression which doesn't even occur in the aggregate. The original wording handled this case by making use of the term "needed". I suppose we could adopt a belt-and-suspenders approach, modifying the wording suggested above by replacing The value of a discriminant that governs a variant_part shall be given by a static expression, unless with The value of a discriminant that governs a variant_part whose components are needed shall be given by a static expression, unless and then adding an AARM note to confirm that the phrase "whose components are needed" does indeed apply to a componentless variant_part. This seems clunky, but I don't see an elegant solution. **************************************************************** From: Tucker Taft Sent: Tuesday, August 3, 2010 1:00 PM I guess I still prefer the wording I proposed: If the components of a variant_part are needed, {even if there are none,} then the value of a discriminant that governs the variant_part shall be given by a static expression. **************************************************************** From: Steve Baird Sent: Tuesday, August 3, 2010 1:46 PM It seems to me that any rule that starts with If the components of a variant_part have some property, even if there are none, then ... would, based on a strict reading, apply uniformly to all componentless variant_parts. We want this rule to apply to some componentless variant_parts but not to others, depending on whether the components of the variant_part would have been needed if only the variant_part had components. I don't think the wording you've suggested accomplishes this, at least if it is read strictly. On the other hand, this point is fairly pedantic. I could live with the wording that you suggest if we add AARM text (preferably a "To Be Honest" note) to clarify what is really meant. **************************************************************** From: Tucker Taft Sent: Tuesday, August 3, 2010 2:08 PM Given that this is a corner case, I am reluctant to add a bunch of words to the RM. Adding words to clarify things in the AARM is fine with me. Apparently all of the implementors interpreted this the same way, and as far as I know no "real" user has complained about this, so going overboard in an RM explanation seems a waste of everyone's energy. **************************************************************** From: Tucker Taft Sent: Tuesday, August 3, 2010 2:15 PM How about: If the components of a variant_part are needed, {even if there are none *in the chosen variant*,} then the value of a discriminant that governs the variant_part shall be given by a static expression. This make the extra phrase somewhat more useful even in a non-corner case where some but not all of the variants of the variant part have components. To cover the silly case where none of the variants have components, we can add a "to be honest". **************************************************************** From: Steve Baird Sent: Tuesday, August 3, 2010 2:46 PM My first reaction was to agree with your analysis and view this as an improvement. On the other hand, it seems that we are introducing a circularity in talking about "the chosen variant" in the legality rule which is, in turn, needed to ensure that "the chosen variant" is well defined. At the very least, this is potentially confusing. Given this, I don't think this addition would be an improvement. It's a close call (for me) and, as before, I could live with this as long as the AARM tells the full story. **************************************************************** From: Randy Brukardt Sent: Tuesday, August 3, 2010 4:10 PM ... > It seems to me that any rule that starts with > > If the components of a variant_part have some property, > even if there are none, then ... > > would, based on a strict reading, apply uniformly to all componentless > variant_parts. Isn't that what we want? > We want this rule to apply to some componentless variant_parts but not > to others, depending on whether the components of the variant_part > would have been needed if only the variant_part had components. I can't parse this at all. Could you remind me which of the corner case(s) you are concerned about here? > I don't think the wording you've suggested accomplishes this, at least > if it is read strictly. > > On the other hand, this point is fairly pedantic. It's beyond pedantic, it defies understanding. ;-) > I could live with the wording that you suggest if we add AARM text > (preferably a "To Be Honest" note) to clarify what is really meant. I guess I'd like to know what is really meant before agreeing or disagreeing with this. Steve later says: >> How about: >> >> If the components of a variant_part are needed, {even if >> there are none *in the chosen variant*,} then the value of >> a discriminant that governs the variant_part shall be >> given by a static expression. > ... >On the other hand, it seems that we are introducing a circularity in talking >about "the chosen variant" in the legality rule which is, in turn, needed to >ensure that "the chosen variant" is well defined. At the very least, this is >potentially confusing. Doesn't this logic apply to any wording containing "needed" as well? After all, it is the discriminant values that determine what components are needed (especially in nested variants). Adam's original complaint about this wording was the circularity of "needed" and this rule. The fact that no one actually implements the rule as written was something that Steve and I realized while we were discussing fixes to Adam's original complaint. I agree that everyone seems to have interpreted this the same (with a slight difference in Janus/Ada), but that doesn't address the wording confusion that Adam pointed out. So I think I prefer Steve's earlier proposal, as it doesn't suffer from this circularity. Indeed, I would prefer to simply say: The value of a discriminant that governs a variant_part shall be given by a static expression. since no one has explained to me why there is any value to allowing a non-static discriminant that controls an unselected variant. At a minimum, that seems to be a design problem (using a single discriminant for multiple purposes); if it is simply a "don't care" discriminant (which I would hope), why would using a static discriminant be a hardship?? But I realize that this would introduce an incompatibility (of the best kind - detected at compile-time), so I suppose we need the extra wording: The value of a discriminant that governs a variant_part shall be given by a static expression, unless the variant_part V occurs within an enclosing (not necessarily immediately enclosing) variant_part E and the variant enclosing V is not selected by the discriminant value governing E. (I introduced the names "V" and "E" to clearly mark which variant_part is which in this wording -- there are just too many "enclosing"s in Steve's original wording; once done, the bullets didn't seem necessary. I considered not introducing "E", it doesn't help as much as "V" does, but I went with the shortest wording.) **************************************************************** From: Steve Baird Sent: Tuesday, August 3, 2010 4:27 PM > ... >> It seems to me that any rule that starts with >> >> If the components of a variant_part have some property, >> even if there are none, then ... >> >> would, based on a strict reading, apply uniformly to all >> componentless variant_parts. > > Isn't that what we want? > No. >> We want this rule to apply to some componentless variant_parts but >> not to others, depending on whether the components of the >> variant_part would have been needed if only the variant_part had >> components. > > I can't parse this at all. Could you remind me which of the corner > case(s) you are concerned about here? > type T4 (D1, D2 : Boolean) is record case D1 is when False => null; when True => case D2 is when False => null; when True => Int : Integer; end case; end case; end record; X4_B : T4 := (D1 => False, D2 => Non_Static); -- legal We want the above aggregate to be legal. If the rule applied to every veriant_part, then it would be rejected. > Indeed, I would prefer to simply say: > > The value of a discriminant that governs a variant_part shall be > given by a static expression. That rule would be consistent with what Janus implements and would cause the X4_B aggregate above to be rejected. **************************************************************** From: Steve Baird Sent: Tuesday, August 3, 2010 4:43 PM > Doesn't this logic apply to any wording containing "needed" as well? > After all, it is the discriminant values that determine what > components are needed (especially in nested variants). Adam's original > complaint about this wording was the circularity of "needed" and this rule. Yes, you are right. That circularity seems to have been granted a "grandfather" clause on the grounds that a really correct rule requires too much RM wording. That doesn't necessarily mean that we want to introduce another circularity, but I'd agree that it weakens the argument against adding another one. > But I realize that this would introduce an incompatibility (of the > best kind > - detected at compile-time), so I suppose we need the extra wording: > > The value of a discriminant that governs a variant_part shall be > given by a static expression, unless the variant_part V occurs within an > enclosing (not necessarily immediately enclosing) variant_part E and > the variant enclosing V is not selected by the discriminant value > governing E. If you drop the use of "needed", then how do you handle things like type T1 (D1 : Boolean) is tagged record case D1 is when False => null; when True => Int : Integer; end case; end record; type T2 (D2 : Integer) is new T1 (Some_Non_Static_Value) with null record; X2 : T2 := (D2 => 123); -- must be illegal In this case, the problematic non-static discriminant value does not occur in the aggregate. With the wording you've suggested, it sounds like we iterate over the discriminants in the aggregate, but that's not good enough in the above case. The current wording handles this by use of the word "needed". **************************************************************** From: Randy Brukardt Sent: Tuesday, August 3, 2010 4:59 PM > In this case, the problematic non-static discriminant value does not > occur in the aggregate. With the wording you've suggested, it sounds > like we iterate over the discriminants in the aggregate, but that's > not good enough in the above case. The current wording handles this by > use of the word "needed". I *don't* handle anything; you are the one that suggested this wording (I just cleaned it up to eliminate the feeling that this was "a lot of new wording". But I don't even see the problem that you are complaining about here. You carefully worded this so that it says "the discriminant that governs a variant part" -- it makes no requirement that that discriminant appears in the aggregate! (I recall us discussing this in the past.) I would say that an AARM note to point this out (that the discriminant could be given in a derivation only and not appear in the aggregate) would be sufficient. **************************************************************** From: Steve Baird Sent: Tuesday, August 3, 2010 5:24 PM You're right. This AI is making me punchy. **************************************************************** From: Tucker Taft Sent: Tuesday, August 3, 2010 4:36 PM [Editor's note: This message appears to be a reply to Steve's message of 2:46 PM, but no message was quoted.] OK, how about: If the components of a variant_part are needed {(even if there are no components in some of its variants)}, then the value of a discriminant that governs the variant_part shall be given by a static expression. I really hate to see this definition get a lot longer. By parenthesizing hopefully we reduce even further the need for the casual user to worry about it. **************************************************************** From: Steve Baird Sent: Tuesday, August 3, 2010 5:17 PM > OK, how about: > > If the components of a variant_part are needed {(even if > there are no components in some of its variants)}, > then the value of a discriminant that governs the variant_part > shall be given by a static expression. I can live with any of the recent alternatives you've suggested (with an accompanying AARM explanation). I don't feel strongly about it. > I really hate to see this definition get a lot longer. I think everyone is agreed on that point. **************************************************************** From: Randy Brukardt Sent: Tuesday, August 3, 2010 4:51 PM > OK, how about: > > If the components of a variant_part are needed {(even if > there are no components in some of its variants)}, > then the value of a discriminant that governs the variant_part > shall be given by a static expression. > > I really hate to see this definition get a lot longer. > By parenthesizing hopefully we reduce even further the need for the > casual user to worry about it. I still object to using "needed" in this wording, as it is not well-defined until after this rule is applied. (That is, if the discriminants are non-static, how do you figure out if a component or variant is needed?? We don't want to go there.) I agree about making this definition "a lot longer", but the proposed rewording I suggested is not a lot longer. The value of a discriminant that governs a variant_part shall be given by a static expression, unless the variant_part V occurs within an enclosing (not necessarily immediately enclosing) variant_part E and the variant enclosing V is not selected by the discriminant value governing E. Resorting to word counts, Tucker's wording has 36 words; my rewording (of Steve's older proposal) has 44 words (or 22% larger). 22% is not a lot in RM terms!! **************************************************************** From: Tucker Taft Sent: Tuesday, August 3, 2010 5:12 PM Or shorter: The value of a discriminant that governs a variant_part P shall be given by a static expression, unless P is nested within a variant V that is not selected by the discriminant value governing the variant_part enclosing V. **************************************************************** From: Steve Baird Sent: Tuesday, August 3, 2010 5:20 PM I like it. **************************************************************** From: Randy Brukardt Sent: Tuesday, August 3, 2010 5:32 PM Goody: 38 words, only 5% larger. ;-) P.S. Looks OK to me. **************************************************************** From: Randy Brukardt Sent: Tuesday, August 3, 2010 5:34 PM > You're right. This AI is making me punchy. Well, all you have to do now is write it up, using Tucker's rewrite of my rewrite of your older proposed wording. ****************************************************************