Version 1.1 of acs/ac-00169.txt

Unformatted version of acs/ac-00169.txt version 1.1
Other versions for file acs/ac-00169.txt

!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.

****************************************************************


Questions? Ask the ACAA Technical Agent