!standard 4.5.7 (0) 10-02-03 AI05-0188-1/03 !class amendment 09-11-03 !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 contruct -- the 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 conditional_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 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).) Modify 4.4(1) to add conditional and case 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, case_expression. Add a new clause: 4.5.8 Case expressions A case_expression selects for evaluation one of a number of alternative expressions; the chosen alternative is defined by the value of an expression. Syntax case_expression ::= (case 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 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.] Name Resolution Rules The expected type for the expression and the discrete_choices are as for case statements (see 5.4). 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. Legality Rules The expressions and discrete_ranges given as discrete_choices of a case_expression shall be static. Redundant[A discrete_choice others, if present, shall appear alone and in the last discrete_choice_list.] The possible values of the expression shall be covered as for case statements (see 5.4). As for case statements, two distinct discrete_choices of a case_expression shall not cover the same value. If the expected type of a case_expression is any type in a class of types (instead of a particular type), all *dependent_*expressions of the case_expression shall have the same type. If the expected type of a case_expression is a specific tagged type, all of the *dependent_*expressions of the case_expression shall be dynamically tagged, or none shall be dynamically tagged; the case_expression is dynamically tagged if all of the *dependent_*expressions are dynamically tagged, is tag-indeterminate if all of the *dependent_*expressions are tag-indeterminant, and is statically tagged otherwise. AARM Note: There is nothing corresponding to the implicit "else False" for conditional_expressions. Dynamic Semantics For the execution of a case expression, the expression specified after case is first evaluated. If the value of the expression is covered by the discrete_choice_list of some case_expression_alternative, then the *dependent*_expression of the _alternative is evaluated, and this 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. ================ Add after 4.9(12): * A case_expression all of whose conditions and dependent_expressions are static expressions; AI05-0147-1 calls for the replacement of 4.9(33). Add one more bullet: * a *dependent_*expression of a case_expression whose expression is static and not covered by the corresponding discrete_choice_list. 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}: !discussion !examples !ACATS test ACATS B and C tests are needed. !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: 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?? ****************************************************************