!standard 4.9(14) 09-01-24 AC95-00169/01 !class confirmation 09-01-24 !status received no action 09-01-24 !status received 08-12-15 !subject Static-bound array record components treated differently than simple variable !summary !appendix From: Jeffrey R. Carter Date: Monday, December 15, 2008 11:42 AM Consider the following code: package Index_Test_Use is type Index is range 1 .. 4; type Valid_List is array (Index) of Boolean; type Info is record Valid : Valid_List := (others => False); end record; procedure P; end Index_Test_Use; package body Index_Test_Use is procedure P is Object : Valid_List := (others => False); Result : Info; begin -- P Variable : for I in Object'Range loop case I is when 1 => null; when 2 => null; when 3 => null; when 4 => null; end case; end loop Variable; Component : for I in Result.Valid'Range loop case I is -- Line 20 when 1 => null; when 2 => null; when 3 => null; when 4 => null; end case; end loop Component; end P; end Index_Test_Use; This results in the compiler messages (GNAT Pro 6.1.2): index_test_use.adb:20:10: missing case values: -128 .. 0 index_test_use.adb:20:10: missing case values: 5 .. 127 The vendor writes: "IN fact this is the correct behavior : the attributes First and Last on Result.Valid are not static, and the case statement uses the base type to establish the range of the variable. The attributes are not static because the prefix, even though it is of an array type with static bounds, does not denote a statically constrained array object, as specified in 4.9 (14) because it is a selected component." This is unintuitive and violates the principle of least surprise; I have used Ada since 1984 and still thought this was a compiler error. The only difference between the 2 case statements is that in one the object used for the subtype of the loop variable is a simple variable, and in the other it is a record component. I suspect most users think they would be treated the same. I request that this difference in the handling of static-bound array objects be eliminated in the next revision of the language. **************************************************************** From: Adam Beneschan Date: Monday, December 15, 2008 1:40 PM Offhand, I'd guess that the most serious problem is that a selected component could be in a variant part. Another possible problem: in order to get the language rules right, you'd have to write them in a way so that the prefix of the selected component would have to be known to exist. Suppose your example had been written: type Info_Acc is access record; ... Result : Info_P; ... for I in Result.Valid'Range loop ... case I is ... Now it wouldn't do to consider the range to be "static", since Result may be null. Sure, the bounds are known at compile time *if* the construct doesn't raise an exception. And for the purposes of the CASE statement, that ought to be enough. But calling the bounds static would, I'd guess, lead to other problems elsewhere in the language; and inventing a new concept of an expression that is "static unless an exception is raised" might lead to too much additional verbiage and confusion. Maybe there's a simple way to make things work. But if not, then this may be a case where the Principle Of Least Surprise must be superseded by the Principle Of Not Tripling The Size Of The RM Just To Add A Bunch Of Special Cases To Make Everything Work That Looks Like It Oughta Work. (Note that I say *may* be a case. It also may be a case where it's important enough to get this one right to make the additional verbiage worthwhile. It really isn't my place to make a judgment either way.) **************************************************************** From: Randy Brukardt Date: Monday, December 15, 2008 2:00 PM > Now it wouldn't do to consider the range to be "static", since Result > may be null. Sure, the bounds are known at compile time *if* the > construct doesn't raise an exception. And for the purposes of the > CASE statement, that ought to be enough. But calling the bounds > static would, I'd guess, lead to other problems elsewhere in the > language; and inventing a new concept of an expression that is "static > unless an exception is raised" might lead to too much additional > verbiage and confusion. This issue is a classic slippery-slope case. As you note, there are many cases where it would not make sense for the prefix to be considered static, even though it would work for the case statement example. It comes down to a question of where to draw the line; whereever that is will not be satisfactory in some cases. For instance, say that we allowed this for all record components (probably not a good idea as Adam points out, but bear with me). But then most array prefixes would still not be static: Obj : array (1..10) of Valid_List; Arr_Component : for I in Obj(1)'Range loop case I is ... That surely also violates the principle of least surprise. OTOH, if Arr_Component : for I in Obj(Some_Function)'Range loop case I is ... is considered static, that too would violate the principle of least surprise. The point is, whereever you draw the line between static and not static, there will be some cases that are surprising. That seems unavoidable. As such, it seems unlikely that changing the rule would lead to any real improvement (short of introducing a whole new kind of staticness, which surely seems like overkill to me). It should be noted that there is an easy workaround to this issue in many cases (including this one): don't use an object as the prefix! That's not required here, as the subtype has a name. Subtype_Loop : for I in Valid_List'Range loop case I is ... That's not quite as convinient, but there isn't any question about the staticness. **************************************************************** From: Bob Duff Date: Monday, December 15, 2008 2:06 PM I agree with Jeff Carter that the current rules are surprising. However, I am inclined to say, "not sufficiently broken" in this case. Not because there's anything wrong with Jeff's idea, but because there are many more important things for ARG to worry about. **************************************************************** From: Dan Eilers Date: Monday, December 15, 2008 6:23 PM > That surely also violates the principle of least surprise. OTOH, if > > Arr_Component : for I in Obj(Some_Function)'Range loop > case I is ... > > is considered static, that too would violate the principle of least > surprise. I believe ASIS has an operation "Get_Array_Element_Type", and if Ada had a similar attribute, then you could say: for I in Obj'Array_Element_Type'Range loop case I is ... and probably expect the range to be static. > It should be noted that there is an easy workaround to this issue in > many cases (including this one): don't use an object as the prefix! > That's not required here, as the subtype has a name. > > Subtype_Loop : for I in Valid_List'Range loop > case I is ... Similarly, ASIS has an operation "Corresponding_Type_Declaration", and if Ada had a similar attribute, then you could say: Subtype_Loop : for I in Object'Corresponding_Type'Range loop case I is ... and probably expect the range to be static, even if "Object" was a non-static name. ****************************************************************