!standard 3.10.2(9/2) 10-10-30 AI05-0188-1/09 !standard 3.10.2(32/2) !standard 4.5.7(0) !standard 4.9(12) !standard 4.9(33) !standard 5.4(2) !standard 5.4(4) !standard 5.4(6) !standard 5.4(7) !standard 5.4(8) !standard 5.4(9) !standard 5.4(11) !standard 5.4(12) !standard 6.2(10) !standard 6.5(5.5/2) !class amendment 09-11-03 !status Amendment 2012 10-07-26 !status ARG Approved 10-0-0 10-10-30 !status work item 09-11-03 !status received 09-11-03 !priority Low !difficulty Medium !subject Case expressions !summary Case expressions are added to Ada. !problem Conditional expressions are added by AI05-0147-1. This proposal is to add an analogous construct -- the case expression. We also change the name conditional_expression to if_expression, and use conditional_expression to refer to both if_expression and case_expression. The full coverage rules for case statements and aggregates are a huge benefit to maintenance of Ada programs: If you add an enumeration literal, the compiler tells you about all the case statements and aggregates that need to be modified (assuming you don't defeat the full coverage rules by using "others"). In cases where if_expressions are useful, we don't want to lose the benefits of full coverage rules. A common example is in preconditions. Suppose we have: procedure Add_To_Fruit_Salad( -- In a package spec. Fruit : in out Fruit_Type; Bowl : in out Bowl_Type); procedure Add_To_Fruit_Salad( -- In the body. Fruit : in out Fruit_Type; Bowl : in out Bowl_Type) is begin -- Check if ready to add to fruit salad case Fruit.Kind is when Apple => pragma Assert(Fruit.Is_Crisp); null; when Banana => pragma Assert(Fruit.Is_Peeled); null; when Pineapple => pragma Assert(Fruit.Is_Cored); null; end case; Cut_Up(Fruit); Add_To_Bowl(Fruit, Bowl); end Add_To_Fruit_Salad; We would like to remove those Assert pragmas, and make them into preconditions: procedure Add_To_Fruit_Salad( Fruit : in out Fruit_Type; Bowl : in out Bowl_Type) with Pre => (if Fruit.Kind = Apple then Fruit.Is_Crisp elsif Fruit.Kind = Banana then Fruit.Is_Peeled elsif Fruit.Kind = Pineapple then Fruit.Is_Cored); But then if we add Orange to the Fruit_Kind type, we might be missing a precondition. It would be better to write it like this: procedure Add_To_Fruit_Salad( Fruit : in out Fruit_Type; Bowl : in out Bowl_Type) with Pre => (case Fruit.Kind is when Apple => Fruit.Is_Crisp, when Banana => Fruit.Is_Peeled, when Pineapple => Fruit.Is_Cored); Now if we add Orange, we will get an error, prompting us to add "when Orange => Fruit.Is_Juicy", or "when Orange => True", or whatever is appropriate. Without case expressions, we are left with a nasty choice: put the assertions in the body, where they don't belong, or lose the full coverage rules. !proposal (See wording.) !wording Add immediately after 3.10.2(9/2), as a separate bulleted item: The accessibility level of a conditional_expression is the accessibility level of the evaluated dependent_expression. Add after 3.10.2(32): Legality Rules An expression is said to have *distributed accessibility* if it is - a conditional_expression (see 4.5.7); or - a view conversion, qualified_expression, or parenthesized expression whose operand has distributed accessibility. The statically deeper relationship does not apply to the accessibility level of an expression having distributed accessibility; that is, such an accessibility level is not considered to be statically deeper, nor statically shallower, than any other. Any static accessibility requirement that is imposed on an expression that has distributed accessibility (or on its type) is instead imposed on the *dependent_*expressions of the underlying conditional_expression. This rule is applied recursively if a *dependent_*expression also has distributed accessibility. AARM Discussion: This means that any Legality Rule requiring that the accessibility level of an expression (or that of the type of an expression) shall or shall not be statically deeper than some other level instead applies, in the case where the expression has distributed accessibility, to each *dependent_*expression of the underlying conditional_expression. Replace 4.5.7 as added by AI05-0147-1: A conditional_expression selects for evaluation at most one of the enclosed *dependent_*expressions, depending on a decision among the alternatives. One kind of conditional_expression is the if_expression, which selects for evaluation a *dependent_*expression depending on the value of one or more corresponding conditions. Another kind of conditional_expression is the case_expression, which selects for evaluation one of a number of alternative *dependent_*expressions; the chosen alternative is determined by the value of a *selecting_*expression. Syntax conditional_expression ::= if condition then *dependent*_expression {elsif condition then *dependent*_expression} [else *dependent*_expression] condition ::= *boolean_*expression case_expression ::= (case *selecting_*expression is case_expression_alternative {, case_expression_alternative} ) case_expression_alternative ::= when discrete_choice_list => *dependent*_expression Wherever the Syntax Rules allow an expression, a conditional_expression may be used in place of the expression, so long as it is immediately surrounded by parentheses. AARM Notes as in AI05-0147-1. Name Resolution Rules If a conditional_expression is expected to be of a type T, then each *dependent_*expression of the conditional_expression is expected to be of type T. Similarly, if a conditional_expression is expected to be of some class of types, then each *dependent_*expression of the conditional_expression is subject to the same expectation. If a conditional_expression shall resolve to be of a type T, then each *dependent_*expression shall resolve to be of type T. The possible types of a conditional_expression are further determined as follows: * If the conditional_expression is the operand of a type conversion, the type of the conditional_expression is the target type of the conversion; otherwise * If all of the *dependent_*expressions are of the same type, the type of the conditional_expression is that type; otherwise * If a *dependent_*expression is of an elementary type, the type of the conditional_expression shall be covered by that type; otherwise * If the conditional_expression is expected to be of type T or shall resolve to type T, then the conditional expression is of type T. A condition is expected to be of any boolean type. The expected type for the *selecting_*expression and the discrete_choices are as for case statements (see 5.4). Legality Rules All of the *dependent_*expressions shall be convertible (see 4.6) to the type of the conditional_expression. If the expected type of a conditional_expression is a specific tagged type, all of the *dependent_*expressions of the conditional_expression shall be dynamically tagged, or none shall be dynamically tagged; the conditional_expression is dynamically tagged if all of the *dependent_*expressions are dynamically tagged, is tag-indeterminate if all of the *dependent_*expressions are tag-indeterminate, and is statically tagged otherwise. For an if_expression, if there is no "else" *dependent_*expression, all of the *dependent_*expressions of the if_expression shall be of a boolean type. All Legality Rules that apply to the discrete_choices of a case_statement (see 5.4), apply to the discrete_choices of a case_expression. Dynamic Semantics For the evaluation of an if_expression, the condition specified after if, and any conditions specified after elsif, are evaluated in succession (treating a final else as elsif True then), until one evaluates to True or all conditions are evaluated and yield False. If a condition evaluates to True, the associated *dependent_*expression is evaluated, converted to the type of the conditional_expression, and the resulting value is the value of the if_expression. Otherwise (when there is no else clause), the value of the if_expression is True. AARM Ramification: "Else" is required unless the conditional_expression has a boolean type, so the last sentence can only apply to conditional_expressions with a boolean type. For the evaluation of a case_expression, the *selecting_*expression is first evaluated. If the value of the *selecting_*expression is covered by the discrete_choice_list of some case_expression_alternative, then the *dependent*_expression of the case_expression_alternative is evaluated, converted to the type of the case_expression, and the resulting value is the value of the case_expression. Otherwise (the value is not covered by any discrete_choice_list, perhaps due to being outside the base range), Constraint_Error is raised. ================ In 4.9(12.1/3) as added by AI05-0147-1, add "*selecting_*expression". AI05-0147-1 calls for the replacement of 4.9(33). Add one more bullet: * a *dependent_*expression of a case_expression whose *selecting_*expression is static and not covered by the corresponding discrete_choice_list. The existing bullet is changed to discuss if_expressions rather than conditional_expression. Append to the end of 6.2(10): For a conditional_expression, this object is the one associated with the evaluated dependent_expression. Replace 6.5(5.5/2) and AARM 6.5(5.c/2): [Note: This paragraph was renumbered to 5.6/3 by AI05-0032-1] If the result subtype of the function is limited, then the expression of the return statement (if any) shall be an aggregate, a function call (or equivalent use of an operator), or a qualified_expression or parenthesized expression whose operand is one of these.5.c/2 Discussion: In other words, if limited, the expression must produce a “new” object, rather than being the name of a preexisting object (which would imply copying). with Redundant [If the result subtype of the function is limited, then the expression of the return statement (if any) shall meet the restrictions described in 7.5.] === Replace the first line of 5.4(2) with: case *selecting_*expression is In 5.4(4), 5.4(5), 5.4(6), 5.4(7), 5.4(8), 5.4(9), 5.4(11), 5.4(12), and AARM 5.4(10.b) and 5.4(10.d), replace "expression" with "*selecting_*expression". !discussion The changes to 3.10.2, 6.2, and 6.5 are needed to close minor holes in the original AI05-0147-1; they equally apply to this AI. Defining the accessibility level of a conditional expression to be that of the evaluated expression may preclude making a copy in some cases. Consider type R is record F : aliased Integer; end record; X, Y : R; Type Ref is access constant Integer; Ptr : Ref; procedure Foo (Flag : Boolean) is begin Ptr := R'(if Flag then X else Y).F'Access; end Foo; It would be incorrect to assign into Ptr a reference to a copy that will cease to exist when Foo is exited. This non-copying is required for an immutably limited type and seems desirable for a by-reference type, For a conditional expression of a by-copy type or of a neither-by-copy-nor-by-reference type, the situation seems less clear. However, the given wording is consistent with the treatment of parenthesized expressions, qualified expressions, etc. Note also that the accessibility level of a conditional expression is completely irrelevant in most cases; in those cases, this rule would have no bearing on whether an implementation would be allowed to make a copy. !examples (See !problem.) !ACATS test ACATS B and C tests are needed. !corrigendum 3.10.2(9/2) @dinsa @xbullet, or parenthesized expression, is the same as that of the operand.> @dinst @xbullet is the accessibility level of the evaluated @i@fa.> !corrigendum 3.10.2(32/2) @dinsa @xindent), as determined by the expected type. The accessibility level of P shall not be statically deeper than that of @i. In addition to the places where Legality Rules normally apply (see 12.3), this rule applies also in the private part of an instance of a generic unit. The profile of P shall be subtype-conformant with the designated profile of @i, and shall not be Intrinsic. If the subprogram denoted by P is declared within a generic unit, and the expression P'Access occurs within the body of that generic unit or within the body of a generic unit declared within the declarative region of the generic unit, then the ultimate ancestor of @i shall be either a non-formal type declared within the generic unit or an anonymous access type of an access parameter.> @dinss @s8<@i> An @fa is said to have @i if it is @xbullet (see 4.5.7); or> @xbullet, or parenthesized expression whose operand has distributed accessibility.> The statically deeper relationship does not apply to the accessibility level of an @fa having distributed accessibility; that is, such an accessibility level is not considered to be statically deeper, nor statically shallower, than any other. Any static accessibility requirement that is imposed on an @fa that has distributed accessibility (or on its type) is instead imposed on the @i@fas of the underlying @fa. This rule is applied recursively if a @i@fa also has distributed accessibility. !corrigendum 4.5.7 @dinsc Force a conflict: the real text is in the conflict file. !corrigendum 4.9(12) @dinsa @xbullets are static expressions; @dinst @xbullet all of whose @fas and @i@fas are static expressions; !corrigendum 4.9(33) @drepl A static expression is evaluated at compile time except when it is part of the right operand of a static short-circuit control form whose value is determined by its left operand. This evaluation is performed exactly, without performing Overflow_Checks. For a static expression that is evaluated: @dby An expression is @i if it is part of: Rest of the wording is found in the conflict file. !corrigendum 5.4(2) @drepl @xcode<@fa@ft<@b> @fa@ft<@b>@fa< case_statement_alternative {, case_statement_alternative} >@ft<@b>@fa<;>> @dby @xcode<@fa@ft<@b @i>@fa@ft<@b>@fa< case_statement_alternative {, case_statement_alternative} >@ft<@b>@fa<;>> !corrigendum 5.4(4) @drepl The @fa is expected to be of any discrete type. The expected type for each @fa is the type of the @fa. @dby The @I@fa is expected to be of any discrete type. The expected type for each @fa is the type of the @I@fa. !corrigendum 5.4(6) @drepl The possible values of the @fa shall be covered as follows: @dby The possible values of the @i@fa shall be covered as follows: !corrigendum 5.4(7) @drepl @Xbullet is a @fa (including a @fa or a @fa) having a static and constrained nominal subtype, or is a @fa whose @fa denotes a static and constrained scalar subtype, then each non-@b @fa shall cover only values in that subtype, and each value of that subtype shall be covered by some @fa (either explicitly or by @b).> @dby @Xbullet@fa is a @fa (including a @fa or a @fa) having a static and constrained nominal subtype, or is a @fa whose @fa denotes a static and constrained scalar subtype, then each non-@b @fa shall cover only values in that subtype, and each value of that subtype shall be covered by some @fa (either explicitly or by @b).> !corrigendum 5.4(8) @drepl @xbullet is @i, @i, or a descendant of a formal scalar type, then the @fa shall have an @b @fa.> @dby @xbullet@fa is @i, @i, or a descendant of a formal scalar type, then the @fa shall have an @b @fa.> !corrigendum 5.4(9) @drepl @xbullet shall be covered (either explicitly or by @b).> @dby @xbullet@fa shall be covered (either explicitly or by @b).> !corrigendum 5.4(11) @drepl For the execution of a @fa the @fa is first evaluated. @dby For the execution of a @fa the @i@fa is first evaluated. !corrigendum 5.4(12) @drepl If the value of the @fa is covered by the @fa of some @fa, then the @fa of the @fa<_alternative> is executed. @dby If the value of the @i@fa is covered by the @fa of some @fa, then the @fa of the @fa<_alternative> is executed. !corrigendum 6.2(10) @drepl A parameter of a by-reference type is passed by reference. Each value of a by-reference type has an associated object. For a parenthesized expression, @fa, or @fa, this object is the one associated with the operand. @dby A parameter of a by-reference type is passed by reference. Each value of a by-reference type has an associated object. For a parenthesized expression, @fa, or @fa, this object is the one associated with the operand. For a @fa, this object is the one associated with the evaluated @i@fa. !corrigendum 6.5(5.5/2) @drepl If the result subtype of the function is limited, then the @fa of the return statement (if any) shall be an @fa, a function call (or equivalent use of an operator), or a @fa or parenthesized expression whose operand is one of these. @dby If the result subtype of the function is limited, then the @fa of the return statement (if any) shall meet the restrictions described in 7.5. !appendix From: Bob Duff Sent: Tuesday, November 3, 2009 1:10 PM Here's an AI on case expressions, to complement conditional expressions. [This is version /01 of the AI - ED] **************************************************************** From: Robert Dewar Sent: Tuesday, November 3, 2009 1:19 PM > Case expressions are added to Ada. looks good to me, I am going to go ahead and implement this in GNAT **************************************************************** From: Pascal Leroy Sent: Monday, November 16, 2009 3:53 PM > procedure Add_To_Fruit_Salad( > Fruit : in out Fruit_Type; Bowl : in out Bowl_Type) > with > Pre => > (case Fruit.Kind is > when Apple => Fruit.Is_Crisp, > when Banana => Fruit.Is_Peeled, > when Pineapple => Fruit.Is_Cored); At first I liked the if-expressions: I have often wanted them, and had to simulate them with ugly work-arounds involving Boolean'Pos and the like. Then of course I see the point about having a case-expression where you have the benefits of the coverage rules, and you preserve the symmetry among the choices. I am starting to feel uncomfortable, though, because there is no telling where this will stop. Why not loop-expressions, for instance? procedure P (S : String) with Pre => (for I in S'Range loop if S(I) > 'a' then false); Also, if you have a bunch of subprograms that all have the same pre- or post-conditions, are you going to repeat the same expression over and over again? It seems to me that it's virtually impossible to reuse pre- and post-conditions. That's not good. As Bob likes to point out, the subprogram is the fundamental unit of code reuse, and it's unfortunate to have to give up on it. What I would really like to write is something like: function Fruit_Is_Ready (Fruit : Fruit_Type) return Boolean renames -- Or whatever syntax. (case Fruit.Kind is when Apple => Fruit.Is_Crisp, when Banana => Fruit.Is_Peeled, when Pineapple => Fruit.Is_Cored); procedure Add_To_Fruit_Salad( Fruit : in out Fruit_Type; Bowl : in out Bowl_Type) with Pre => Fruit_Is_Ready(Fruit); Of course, this would probably be more complicated to describe and to implement... **************************************************************** From: Randy Brukardt Sent: Monday, November 16, 2009 4:06 PM ... > I am starting to feel uncomfortable, though, because there is no >telling where this will stop. Why not loop-expressions, for instance? That's AI05-0176-1 (Quantified expressions). Thank Ed for that one. > Also, if you have a bunch of subprograms that all have the same > pre- or post-conditions, are you going to repeat the same expression >over and over again? It seems to me that it's virtually impossible to >reuse pre- and post-conditions. That's not good. As Bob likes to >point out, the subprogram is the fundamental unit of code reuse, and >it's unfortunate to have to give up on it. What I would really like >to write> is something like: > > function Fruit_Is_Ready (Fruit : Fruit_Type) return Boolean renames -- Or whatever syntax. > (case Fruit.Kind is > when Apple => Fruit.Is_Crisp, > when Banana => Fruit.Is_Peeled, > when Pineapple => Fruit.Is_Cored); That's AI05-0177-1, renaming of expressions as functions. Thank me for pushing this idea (although I think it might have been yours originally, many years ago). > Of course, this would probably be more complicated to describe and > to implement... Not really; it's the same as default expressions; the resolution is the same as preconditions. Both of those are already done. Anyway, is there any concern here that is not already on the agenda?? :-) It certainly is true that this creeping featurism is what is worrying me, and Bob, and several others. We'll probably have to do some work to reign that in. But at this stage, it is more important to have all of the ideas on table so we can weight them properly. **************************************************************** From: Robert Dewar Sent: Monday, November 16, 2009 4:09 PM > I am starting to feel uncomfortable, though, because there is no > telling where this will stop. Why not loop-expressions, for instance? > > procedure P (S : String) > with Pre => (for I in S'Range loop if S(I) > 'a' then false); Well indeed, this is the case of quantifiers, which are being actively considered, and indeed go along with the other forms. > function Fruit_Is_Ready (Fruit : Fruit_Type) return Boolean renames -- > Or whatever syntax. > (case Fruit.Kind is > when Apple => Fruit.Is_Crisp, > when Banana => Fruit.Is_Peeled, > when Pineapple => Fruit.Is_Cored); > > procedure Add_To_Fruit_Salad( > Fruit : in out Fruit_Type; Bowl : in out Bowl_Type) > with > Pre => Fruit_Is_Ready(Fruit); > > Of course, this would probably be more complicated to describe and to > implement... I'm a bit confused, I don't quite understand your suggested syntax here, but most certainly functional abstraction is possible in pre and post conditions. **************************************************************** From: Jean-Pierre Rosen Sent: Monday, November 16, 2009 4:51 PM > It certainly is true that this creeping featurism is what is worrying > me, and Bob, and several others. Count me in > We'll probably have to do some work to reign that in. But at this > stage, it is more important to have all of the ideas on table so we > can weight them properly. Hmmm... Time for a "zero based budget" ? **************************************************************** From: Bob Duff Sent: Monday, November 16, 2009 4:57 PM > It certainly is true that this creeping featurism is what is worrying > me, and Bob, and several others. Yeah, but I notice that we don't all agree on which of those creeps should be squelched. E.g. I find case expressions significantly more useful than the +:= thing, and not that much more difficult to implement. I don't even agree with myself from week to week. ;-) I think there's a lot on the table, though not as much as for Ada 2005. **************************************************************** From: Robert Dewar Sent: Monday, November 16, 2009 5:09 PM >> It certainly is true that this creeping featurism is what is worrying >> me, and Bob, and several others. > > Yeah, but I notice that we don't all agree on which of those creeps > should be squelched. E.g. I find case expressions significantly more > useful than the +:= thing, and not that much more difficult to implement. Well not from where I sit. Case expressions is more like a days work, the +:= is more like an hour or two, but still in the very easy category. > I don't even agree with myself from week to week. ;-) > > I think there's a lot on the table, though not as much as for Ada 2005. Well I am the first to get worried about an excessive implementation burden, or gratuitous upward incompatibilities (we still have customers staying away from Ada 2005, perhaps for ever, because of the limited return imcompatibility). But so far the 2012 changes proposed seem very much in reasonable range (I have not seen the real time stuff, which is effectively optional anyway). **************************************************************** From: Pascal Leroy Sent: Tuesday, November 17, 2009 12:56 AM > Anyway, is there any concern here that is not already on the agenda?? :-) Sorry for missing that this stuff was already being worked on. I have a hard time keeping track of all the ideas that are thrown around. And I certainly don't have a "big picture" of where this Amendment is going. **************************************************************** From: Edmond Schonberg Sent: Tuesday, November 17, 2009 12:05 PM > Anyway, is there any concern here that is not already on the agenda?? > :-) > > Sorry for missing that this stuff was already being worked on. I have > a hard time keeping track of all the ideas that are thrown around. > And I certainly don't have a "big picture" of where this Amendment is > going. It is going in the direction of safety, of course! That means that pre/postconditions and type invariants are the most significant additions. To support these there is a new syntax for aspects of entities, and it follows that there is also improved syntax for predicates of various sorts: if-expressions, case expressions, quantified expressions. Then (perhaps) there are new forms of constraints and non-contiguous discrete subtypes. More routine additions include new container types. New syntax for assignment operations is way down the list compared with the above, in my opinion. **************************************************************** From: Bob Duff Sent: Wednesday, February 3, 2010 4:26 PM Here's my homework on AI05-0188-1 Case expressions. Minor changes to !problem, major changes to !wording. [This is version /03 of the AI - Editor.] **************************************************************** From: Tucker Taft Sent: Wednesday, February 3, 2010 4:35 PM Might you consider defining: conditional_expression ::= if_expression | case_expression if_expression ::= IF ... case_expression ::= CASE ... This would seem more consistent, and would eliminate the awkward "conditional_expression or case_expression" appearing all over the place. **************************************************************** From: Bob Duff Sent: Wednesday, February 3, 2010 5:19 PM > Might you consider defining: > > conditional_expression ::= if_expression | case_expression > > if_expression ::= IF ... > > case_expression ::= CASE ... Yes, I might. > This would seem more consistent, and would eliminate the awkward > "conditional_expression or case_expression" > appearing all over the place. If we could get a concensus that both of these AIs are 'in', then we could simplify them by combining them, and write the common wording stuff just once. **************************************************************** From: Bob Duff Sent: Wednesday, February 3, 2010 4:28 PM The proposed syntax for case expressions separates alternatives with commas. Robert thinks perhaps we should leave them out -- not sure. Comments? I am (mildly) in favor of keeping the commas. **************************************************************** From: Robert Dewar Sent: Wednesday, February 3, 2010 4:32 PM I am not sure I like the commas, though that's what I have implemented for now, consider A := (if X = Red then 3 elsif X = Green then 4 elsif X = Blue then 5 else 6); A := (case X is when Red => 3, when Green => 4, when Blue => 5, others => 6); Or should we try to have a more parallel syntax A := (case X is when Red => 3 when Green => 4 when Blue => 5 others => 6); **************************************************************** From: Tucker Taft Sent: Wednesday, February 3, 2010 4:40 PM I prefer the commas. I don't think everyone is going to format their output the way you have here, and it looks better to me to have commas when formatted as follows: (case X is when A => 5, when B => 6, when others => 7) If it were "else when" then I could see dropping the commas, but "when A => 5 when B => 6" is harder to read without the commas in my view. **************************************************************** From: Robert Dewar Sent: Wednesday, February 3, 2010 4:49 PM OK, commas fine by me (and less implementation work for sure at this stage, since already done). **************************************************************** From: Gary Dismukes Sent: Wednesday, February 3, 2010 5:40 PM > Minor changes to !problem, major changes to !wording. Looks good. One spelling nit: > Whereever the Syntax Rules allow an expression, Whereever => Wherever **************************************************************** From: Bob Duff Sent: Wednesday, February 3, 2010 6:38 PM Randy, do you take care of this sort of editorial thing? Anyway, note that this typo was copied from AI-147, so should be fixed in both. Unless we combine them! **************************************************************** From: Randy Brukardt Sent: Wednesday, February 3, 2010 7:56 PM I'll take care of it. **************************************************************** From: Randy Brukardt Sent: Wednesday, February 3, 2010 8:21 PM ... > Add a bullet following 4.3.3(14): > > * For a conditional_expression or case_expression, the applicable index > constraint for each *dependent_*expression is that, if any, defined > for the conditional or case_expression; > > (Minor detail: AI05-0147-1 suggests this should go after (15), but it > seems better after (14).) I put it after (15) because these are semantically parenthesized expressions, and as such, it seemed best to group them with it (and after it). I did that consistently (for instance, in the wording for limited expressions). Not a big deal either way. ... > Modify 7.5(2.1/2): > > In the following contexts, an expression of a limited type is not > permitted unless it is an aggregate, a function_call, [or ]a > parenthesized expression or qualified_expression whose operand is > permitted by this rule{, or a conditional_ or case_expression all of > whose *dependent_*expressions are permitted by this rule}: > > AARM Note: There is nothing corresponding to the implicit "else False" > for conditional_expressions. This AARM note seems to be in the wrong place; I can't imagine what it has to do with expressions of limited types. But it might make sense somewhere in 4.5.8. Tucker later writes: > Might you consider defining: > > conditional_expression ::= if_expression | case_expression > > if_expression ::= IF ... > > case_expression ::= CASE ... > > This would seem more consistent, and would eliminate the awkward "conditional_expression or > case_expression" appearing all over the place. That would be OK, but I would object to combining the sections for the kinds of expressions. We keep the legality and semantic rules for case and if statements separate, and I would think we want to do that here as well. I also have to wonder if this solution works very well for quantified_expressions, which also have to be referred to in many of these places (although the semantics is somewhat different). Would it look weird to combine two of the three new kinds of expressions, and not the third?? **************************************************************** From: Ed Schonberg Sent: Wednesday, February 3, 2010 8:30 PM > I also have to wonder if this solution works very well for > quantified_expressions, which also have to be referred to in many of > these places (although the semantics is somewhat different). Would it > look weird to combine two of the three new kinds of expressions, and > not the third?? The resolution rules for conditional expressions and case expressions are similar, but completely different from those of quantified expressions. The latter are always boolean, and there is nothing in the context that impacts the resolution of the container expression and the predicate, so they have to have their own section. **************************************************************** From: Randy Brukardt Sent: Wednesday, February 3, 2010 8:43 PM One more thing... (unless Steve Jobs has that trademarked :-) > Bob Duff writes: >Add after 4.9(12): > >* A case_expression all of whose conditions and dependent_expressions are > static expressions; There aren't any conditions in a case_expression, so this is clearly wrong. There isn't a name for the expression that controls the choice, so we can't say that here. But I suppose that we don't need to differentiate here: * A case_expression all of whose expressions are static expressions; seems to be good enough. **************************************************************** From: Bob Duff Sent: Wednesday, February 24, 2010 2:41 PM New version of AI05-0188-1, "Case expressions". I'm not sure this is entirely correct, since it's supposed to depend on AI05-0147-1, which Tucker has not yet updated. I am assuming that AI05-0147-1 covers these issues, with wording that still works for this AI (AI05-0188-1). - applicable index constraint - Wording similar to this: Modify 4.4(1) to add conditional_expressions, as follows: In this International Standard, the term "expression" refers to a construct of the syntactic category expression or of any of the following categories: relation, simple_expression, term, factor, primary, conditional_expression. - Wherever the Syntax Rules allow an expression, a case_expression may be used in place of the expression, so long as it is immediately surrounded by parentheses. [AARM: Note that the above is the same rule as for conditional_expressions; see 4.5.7 for further discussion.] - Static expressions (4.9(12, 33)): - 7.5(2.1/2) -- "In the following contexts, an expression of a limited type is not permitted unless...." [Following was version /04 of this AI - Editor.] **************************************************************** From: Steve Baird Sent: Wednesday, February 24, 2010 5:00 PM > If a case_expression is expected to be of a type T, the expected type > for each *dependent_*expression of the case_expression is T. If a > case_expression shall resolve to a type T, each dependent_expression shall resolve to T. > The corresponding wording for if_expressions (formerly conditional_expressions) has a TBH note because it was decided that saying what we really mean here is too much trouble. (Just to recap, I'm referring to the interaction between this wording and, e.g., the rule for qualified expressions (4.7(3)) or for conditions (5.3(4))). Don't we need to do that again here? **************************************************************** From: Bob Duff Sent: Wednesday, February 24, 2010 5:50 PM Yeah, or better yet, make sure the if_expr AI is worded in a way that obviates the need for duplicating the wording for case_exprs. That's Tuck's job. I was told to make the case_expr AI depend on the if_expr AI, but not the other way 'round. But the if_expr AI needs to "secretly" depend on the case_expr AI, in the sense that the wording has to be carefully crafted to cover case_exprs. **************************************************************** From: Randy Brukardt Sent: Wednesday, February 24, 2010 6:02 PM ... >Yeah, or better yet, make sure the if_expr AI is worded in a way that >obviates the need for duplicating the wording for case_exprs. That's Tuck's job. Bob, there is no open homework on AI05-0147-1. It's (supposedly) finished as I posted it (sometime after Feb 4). I believe I extracted the needed wording from Tuck right after the last meeting. So if there is something that needs to be changed about it, please tell us. **************************************************************** From: Randy Brukardt Sent: Wednesday, February 24, 2010 9:27 PM > - Static expressions (4.9(12, 33)): 4.9(33) is about unselected branches in if_expressions. That depends on the definition of "condition" and the execution of if statements. That wording cannot be made to work for case expressions, because the terminology for the two kinds of statements is completely different (you have to use something about "covers"). I left that wording in the draft! We need some wording in here to say that 4.5.7 as added by AI-147-1 is renamed to If expressions, and all uses of conditional_expression in Legality Rules, Static Semantics, and Dynamic Semantics are changed to if_expression. I added that. **************************************************************** From: Steve Baird Sent: Friday, March 26, 2010 4:05 PM If you have a case-expression in a generic package spec which cases on, say, a formal in-out object of type Integer, it must cover all the Integer values. If you then instantiate the generic with an object whose subtype is Natural, I don't think we want to see a legality violation upon rechecking the expanded spec for the instance. I think the currently posted wording for the AI would require rejecting such an example. Or at least it is unclear what this wording The possible values of the expression shall be covered as for case statements (see 5.4). means in this case, because a case statement cannot occur in an expanded instance spec. Do we agree that this needs fixing? **************************************************************** From: Bob Duff Sent: Friday, March 26, 2010 4:20 PM Yes. **************************************************************** From: Randy Brukardt Sent: Friday, March 26, 2010 6:59 PM I agree with Bob. The recheck typically used could mean that it would be impossible to write a legal generic specification for many objects that might be passed to a generic in out parameter. So it seems we need to think about this some more. OTOH, use of generic in out formal parameters is pretty rare. (I don't think I've ever used one outside of test programs.) So it isn't the end of the world if this is broken. Which means that we don't want a page and a half of new rules to deal with this case. **************************************************************** From: Steve Baird Sent: Friday, March 26, 2010 9:08 PM I think it is just a few words to say that this legality rule doesn't apply in an instance spec, along the lines of 8.3(26/2). **************************************************************** From: Robert Dewar Sent: Friday, March 26, 2010 9:18 PM >> I agree with Bob. ... [but] we don't want a page and a half of new >> rules to deal with this case. especially when we know how we want things to work :-) **************************************************************** From: Bob Duff Sent: Saturday, March 27, 2010 7:24 AM > OTOH, use of generic in out formal parameters is pretty rare. They are, but doesn't the same issue arise in other cases, such as formal 'in's, and functions (formal or not) returning a formal subtype? **************************************************************** From: Steve Baird Sent: Saturday, March 27, 2010 7:42 AM ... and formal functions returning non-formal subtypes . **************************************************************** From: Bob Duff Sent: Saturday, March 27, 2010 7:56 AM Oh, yeah, I had forgotten those aren't required to match (yuck). **************************************************************** From: Steve Baird Sent: Monday, August 2, 2010 6:20 PM In discussions with Randy, we have run into what appear to be some loose ends pertaining to conditional expressions. 1) We need wording in 6.2 to extend the definition of "associated object" to handle conditional expressions. This looks easy to fix. 2) 6.5(5.6/3) seems to be a redundant copy of 7.5(2.8/2) which didn't get updated. Rather than update it, it should probably be deleted. Also easy. 3) Interactions between conditional expressions and accessibility levels may need some more thought. Or perhaps these have all been thought about and there are no problems here - given that AIs 147 and 188 do not mention the word "accessibility", this seems unlikely. Consider determining the (static) legality of T'().Aliased_Component'Access Is the static accessibility level of the prefix of the Access attribute well-defined ? It doesn't seem to be. One's first reaction in a case like this should be to ask "how are parenthesized expressions handled?". Unfortunately, it is not obvious how to generalize 3.10.2(9/2) to handle conditional expressions: The accessibility level of a view conversion, qualified_expression, or parenthesized expression, is the same as that of the operand. Perhaps we could follow the rules for an aggregate. It may be that the dynamic accessibility level of a conditional expression is easy to define as that of the selected dependent expression, but that idea needs both wording and review. 6.5 has a legality rule for functions with class-wide results that is defined in terms of the accessibility level of the type of of the (returned) expression. Does this rule do what we want in the case of a conditional expression? **************************************************************** From: Tucker Taft Sent: Monday, August 2, 2010 6:38 PM I would suggest we say that conditional expressions are not aliased, though if they are tagged then a formal parameter that denotes a conditional expression is of course aliased (but very local). **************************************************************** From: Randy Brukardt Sent: Monday, August 2, 2010 7:42 PM That doesn't help. Steve made a similar argument privately, and that would surely work for 'Access. But we also have 4.8(5.1/2) and 6.5(5.6/2) [this is 6.5(5.7/3) in the latest RM draft] that test the accessibility of classwide returns (so that objects of more nested types don't get returned). There are similar rules for access discriminants (I'm not sure this one can happen, but the classwide one surely can). In both of those cases, nothing needs to be aliased to trigger the check. For instance, using 4.8(5.1/2) as the example, what is the static accessibility of: new T'Class'(if C then A else B) if A and B have different tagged types at different levels? (The dynamic accessibility is less of a problem, as we can just use the level of whatever is actually selected.) We could say that there is no static level in this case (but that threatens all of the analysis we've done about dynamic checks not being needed in return cases, since that analysis assumes that static checks are made). Or we could define a rule requiring the static checks for all of the dependent_expressions to pass (but that seems to require changes to 4.8(5.1/2) and 6.5(5.7/3), since I can't imagine any way to say in 3.10.2 that conditional_expressions have *multiple* static accessibility levels!). Or we could require that the static accessibility level of a conditional_expression is calculable by requiring some relationship (but that seems limiting for a case that will be very rare in practice). No good answer seems obvious... **************************************************************** From: Tucker Taft Sent: Monday, August 2, 2010 7:57 PM Perhaps a "simple" equivalence would handle the function return, by saying that a return of a conditional expression is legal only if a return of each of the dependent expressions would be legal. **************************************************************** From: Steve Baird Sent: Tuesday, August 3, 2010 11:13 AM >> I would suggest we say that conditional expressions are not aliased, >> though if they are tagged then a formal parameter that denotes a >> conditional expression is of course aliased (but very local). > > That doesn't help. It also doesn't help for another reason. Sure conditional expressions are not aliased (they don't occur on the list given at the point where the term "aliased" is defined), but they can have aliased subcomponents. Note the example in my original message: > Consider determining the (static) legality of > T'().Aliased_Component'Access **************************************************************** From: Tucker Taft Sent: Tuesday, August 3, 2010 11:34 AM Then I would recommend you treat it like a function call or an aggregate, as you suggested. **************************************************************** From: Steve Baird Sent: Wednesday, August 18, 2010 1:44 PM I think this is right, but I will point out an issue with this approach which seems worth mentioning even if we decide to ignore it. Ideally, I think that it would be desirable to have a conditional expression with a static selector expression if True then else be as semantically similar as possible to a parenthesized expression () . As a design principle, we want to avoid subtle differences between almost-identical things in order to allow program transformations, avoid confusion, etc. [Ok, if you really want to get picky to avoid interactions with freezing rules, assume that exp2 is another copy of exp1 - otherwise exp2 might freeze something] 3.10.2(9/2) states: The accessibility level of a ... parenthesized expression, is the same as that of the operand. To maintain the equivalence that I am asserting is at least somewhat desirable, we would need to special case either 1) a conditional expression with a static selector expression or 2) a conditional expression all of whose dependent expressions have the same static accessibility level or perhaps even a combination of the two (#1 if selector is static, #2 otherwise) in defining the accessibility level of a conditional expression. On the other hand, perhaps this doesn't matter. How often is a user going to want to do something like Flag : Boolean := ... ; -- non-static type R is record F : aliased Integer; end record; R1, R2 : R; type Ref is access constant Integer; Ptr : Ref; begin Ptr := R'(if Flag then R1 else R2).F'Access; declare R3 : R; begin Ptr := R'(if True then R1 else R3).F'Access; ? The proposed simple, clean "treat it like an aggregate" rule would reject both of the two Access attribute uses in this example. Is this ok? **************************************************************** From: Randy Brukardt Sent: Wednesday, August 18, 2010 8:22 PM My initial reaction is that it is not OK. Not so much because of an example like this, but because of the potential accessibility checks in allocators and return statements. I would not want to see return (if Flag then Obj1 else Obj2); to become illegal because the objects have different types (or whatever it is that you are claiming makes the first example illegal; I'm not sure exactly what rule you are proposing - the "same as an aggregate" would make all accessibility checks fail as an aggregate is extremely local and that makes no sense at all in any case where accessibility levels matter). I thought that we were considering a "runtime check only" for these; it's hard to imagine any other rule that will work for these (unless we want to do a static check when all of the operands have the same static level). Any other rule would be the same as making them illegal in all contexts that require an accessibility check, and I don't think that is desirable for any of the reasons that you mentioned at the beginning of your message. **************************************************************** From: Steve Baird Sent: Thursday, August 19, 2010 11:57 AM > My initial reaction is that it is not OK. Not so much because of an > example like this, but because of the potential accessibility checks > in allocators and return statements. I agree that the example I gave is unimportant. That was my point in asking "how often is a user going to want to do ... ?". Your point that it is probably more important to look at scenarios involving return statements and allocators is a good one. To illustrate your point: given a (non-limited) type with an access discriminant, what is the status of function F return Has_Access_Discrim is begin if ... then return Global_Var1; -- OK elsif ... return Global_Var2; -- OK else return (if ... then Global_Var1 else Global_Var2); -- ??? end if; with respect to the static accessibility check of 6.5(5.7/3) ? > I would not want to see > > return (if Flag then Obj1 else Obj2); > > to become illegal because the objects have different types (or > whatever it is that you are claiming makes the first example illegal; > I'm not sure exactly what rule you are proposing - the "same as an > aggregate" would make all accessibility checks fail as an aggregate is > extremely local and that makes no sense at all in any case where accessibility levels matter). I was saying that my example would be illegal because the "same as an aggregate" rule would cause each conditional expressions to have the accessibility levels of its enclosing statement, which would make them too short-lived to be designated by a value of type Ref. The "same as an aggregate" rule makes sense if the implementation model allows making a copy. Making a copy is never required; it should be forbidden at least in the case of a by-reference type (this follows from the definition of "associated object", which we have already noted requires updating). The question is whether making a copy should be forbidden in (some) other cases, as is implicitly done for parenthesized expressions in 3.10.2(9/2): The accessibility level of a ... parenthesized expression, is the same as that of the operand. This rule, in effect, forbids making a copy in at least some cases where the accessibility level matters, such as Global_Ptr := T'((Global_Var)).Aliased_Component'Access Note that we don't want to forbid copying in all cases (e.g., a dependent expression which is a slice of a packed array of Booleans), but this is not a problem because accessibility levels don't matter in these cases (just as for a parenthesized expression whose operand is such a slice). > I thought that we were considering a "runtime check only" for these; > it's hard to imagine any other rule that will work for these (unless > we want to do a static check when all of the operands have the same > static level). Any other rule would be the same as making them illegal > in all contexts that require an accessibility check, and I don't think > that is desirable for any of the reasons that you mentioned at the beginning of your message. I don't understand what you are suggesting. Could you suggest wording, or at least be more specific? I could imagine a legality rule that the static accessibility levels of the dependent expressions must be pairwise comparable (as opposed to equal). The (static) accessibility level of the conditional expression could then be defined to be that of its most short-lived dependent expression. Please remind me of the status of AIs 147 and 188. Do we need a new AI to discuss this stuff? **************************************************************** From: Randy Brukardt Sent: Thursday, August 19, 2010 1:30 PM ... > Note that we don't want to forbid copying in all cases (e.g., a > dependent expression which is a slice of a packed array of Booleans), > but this is not a problem because accessibility levels don't matter in > these cases (just as for a parenthesized expression whose operand is > such a slice). I didn't think we wanted to allow making a copy. It surely would screw up limited types and build-in-place (which we do allow for conditional expressions). > > I thought that we were considering a "runtime check only" for these; > > it's hard to imagine any other rule that will work for these (unless > > we want to do a static check when all of the operands have the same > > static level). Any other rule would be the same as making them > > illegal in all contexts that require an accessibility check, and I > > don't think that is desirable for any of the reasons that you > > mentioned at the beginning of your message. > > I don't understand what you are suggesting. Could you suggest wording, > or at least be more specific? Wording for accessibility rules? Have you lost your mind? ;-) I don't want to spend three days on this, that's your assignment. I was suggesting something along the lines of: If the static accessibility levels of the dependent_expressions are all the same, the static accessibility level of the conditional_expression is that level. Otherwise, the conditional_expression does not have a static level (the dynamic accessibility checks will take care of it). The only problem with this is that it might invalidate the analysis which "proved" no need for runtime overhead for aliased and tagged parameters (we don't want to have to pass dynamic accessibility levels with them; the entire point is to eliminate that runtime hazard from programs). The reason that's a problem is that the analysis depends on the static check rejecting any cases where the dynamic check could fail; if there is a way to "strip" the static check, then the dynamic check has to be made and it requires passing levels with parameters. > I could imagine a legality rule that the static accessibility levels > of the dependent expressions must be pairwise comparable (as opposed > to equal). The (static) accessibility level of the conditional > expression could then be defined to be that of its most short-lived > dependent expression. That's probably a better idea than the above. > Please remind me of the status of AIs 147 and 188. > Do we need a new AI to discuss this stuff? AI-147 was approved, but AI-188 was not. So we could put any new rules in AI-188 - it already makes changes to AI-147 (I'll file this thread onto that AI.) It would be nice to have this worked out before the October meeting, though. **************************************************************** From: Steve Baird Sent: Thursday, August 19, 2010 2:31 PM >> Making a copy ... should be forbidden at least in the case of a >> by-reference type (this follows from the definition of "associated >> object", which we have already noted requires updating). > I didn't think we wanted to allow making a copy. It surely would screw > up limited types and build-in-place (which we do allow for conditional > expressions). A type for which build-in-place is required is always a by-reference type. As noted earlier, we don't allow copying for those. And we pretty much have to allow copying for elementary types. It's those composite but not by-reference types that are the question. I feel that the existing rules for parenthesized expressions should be used as guidelines in resolving this. > Wording for accessibility rules? Have you lost your mind? ;-) I don't > want to spend three days on this, that's your assignment. It was worth a try ... > I was suggesting something along the lines of: > > If the static accessibility levels of the dependent_expressions are > all the same, the static accessibility level of the > conditional_expression is that level. Otherwise, the > conditional_expression does not have a static level (the dynamic accessibility checks will take care of it). "Does not have a static level" would break lots of rules which assume that every object has a well-defined accessibility level. Perhaps you want something along the lines of 3.10.2 (13.1/2)'s The accessibility level of is deeper than that of any master; all such s have this same level. ? > It would be nice to have this worked out before the October meeting, Agreed. **************************************************************** From: Randy Brukardt Sent: Thursday, August 19, 2010 4:04 PM > "Does not have a static level" would break lots of rules which assume > that every object has a well-defined accessibility level. I don't think this is a real problem; access parameters don't have static levels (for one example). Static levels are defined only for a subset of objects. I'd be more worried about losing the assumption that composite parameters (to take one example) always have a static level. >Perhaps you want something along the > lines of 3.10.2 (13.1/2)'s > The accessibility level of is deeper than that of > any master; all such s have this same level. > ? Well, that would always cause the static check to fail unless the objects have the same level; I don't think that is what we want. But we do have to remember to deal with the case where there is no (useful) static level, as in access parameters. BTW, in taking a quick look at 3.10.2, there doesn't seem to be a concept of static levels, just a static comparison ("statically deeper than") which is not defined for all objects. So I would say that the accessibility level of a conditional_expression is that of the selected dependent_expression (that's obviously dynamic). And then a bunch of rules to allow "statically deeper than" to be calculated. **************************************************************** From: Randy Brukardt Sent: Friday, August 20, 2010 12:21 AM ... > I don't think this is a real problem; access parameters don't have > static levels (for one example). Static levels are defined only for a > subset of objects. I'd be more worried about losing the assumption > that composite parameters (to take one example) always have a static > level. AI05-0148 says the same applies to stand-alone objects of anonymous access-to-object types. The following wording is in 3.10.2(19-19.1/3): * The statically deeper relationship does not apply to the accessibility level of the anonymous type of an access parameter specifying an access-to-object type; that is, such an accessibility level is not considered to be statically deeper, nor statically shallower, than any other. * The statically deeper relationship does not apply to the accessibility level of the type of a stand-alone object of an anonymous access-to-object type; that is, such an accessibility level is not considered to be statically deeper, nor statically shallower, than any other. All I was proposing is that the same applies to conditional_expressions if the dependent_expressions don't have the same static level. But I think that would break AI-142-4, requiring lots of distributed overhead for a silly reason. Tucker and I spent a lot of effort proving that: return Some_Param.Component'Access; never needs a dynamic check (and thus there is no overhead to pass a level with the parameter), because any case which causes trouble is illegal (fails the static check). But if writing: return (if True then Some_Param.Component'Access else null); gets rid of the static level and requires a dynamic check on the first dependent_expression, the entire analysis collapses. (It wouldn't surprise me if that happened in other, older cases as well; I just remember the AI-142-4 one because it is recent.) So I think we have to follow your idea of using the statically deepest level of any of the dependent_expressions (at least of those which have a level, anonymous parameters and objects don't, and literals don't, either). That might make some expressions which would work illegal, but the static checks already do that, and at least one of the dependent_expressions would have to be illegal in order for the entire expression to be illegal, which seems like enough. I'll leave wording this as an exercise for the OP (original poster) - Mr. Baird. **************************************************************** From: Steve Baird Sent: Tuesday, October 19, 2010 11:45 AM [He's commenting on version /07, which he had sent the previous day. - Editor.] I think we want to replace both occurrences of "each" in the above wording with "any" and replace the occurrence of "every" in the AARM note with "some". I think all the legality rules are of the form "blah1 shall *not* be statically deeper than blah2", so that we want an "is statically deeper" test to return False in the "mixed" case. For example, I think we don't want to allow type Ref is access constant Integer; type Rec is record F : aliased Integer; end record; Global : Rec; Ptr : Ref; procedure Foo (Flag : Boolean) is Local : Rec; begin Ptr := Rec'(if Flag then Global else Local)'Access; and I think the wording I proposed yesterday would allow this. ****************************************************************