Version 1.2 of ais/ai-00186.txt

Unformatted version of ais/ai-00186.txt version 1.2
Other versions for file ais/ai-00186.txt

!standard 03.05.04 (14)          99-03-21 AI95-00186/02
!standard 03.04 (9)
!class binding interpretation 99-03-21
!status work item 97-03-19
!status received 97-03-19
!priority Low
!difficulty Medium
!subject Range of root_integer
!summary 99-03-21
An implementation may support integer or modular types which include values outside the range System.Min_Int..System.Max_Int. In some cases evalutation of a non-static root_integer expression outside this range is required not to raise an exception. In most other such cases, the behavior is defined to be implementation dependent.
!question 99-03-21
Consider for example:
generic type T is (<>); package P is ... end P;
package body P is B : Boolean := T'Pos (T'Last) = 3; end P;
Is the evaluation of the expression allowed to raise Constraint_Error if P is instantiated with the largest modular type? (No.)
!recommendation 99-03-21
If an implementation supports a modular type whose range exceeds 0..System.Max_Int, or a nonstandard integer type with values outside System.Min_Int..System.Max_Int, non-static expressions which involve only variables, constants, and attributes of the a type T or its subtypes, or numeric_literals and other values of type root_integer may only raise Constraint_Error if the process of evaluating the expression exceeds the range of type T.
If a non-static expression contains arithmetic operators of type root_real, it is implementation defined whether or not Constraint_Error will be raised if the result is outside the range of root_real, even if the result of the expression is within the range of the target type.
Let T be an integer or modular type which has values outside the range System.Min_Int..System.Max_Int. If a non-static expression of type root_integer has both an attribute, variable or result of type T and an attribute, variable or result of some other type, it is erroneous for the value of the expression to exceed the range of root_integer.
An implementation is allowed to choose a range for root_real that exceeds System.Min_Int..System.Max_Int.
!wording 99-03-21
The second sentence of paragraph 3.4(9) should read:
"For a scalar type {declared by a derived_type_definition}, the base range of the derived type is the same as that of the parent type."
Paragraph 3.5.4(14) should read:
"A type defined by an integer_type_definition is implicitly derived from _root_integer_, an anonymous predefined (specific) integer type, whose base range is {at least} System.Min_Int..System.Max_Int. However, the base range of the new type is not inherited from root_integer, but is instead determined by the range or modulus specified by the integer_type_definition. Integer literals are all of the type _universal_integer_, the universal type (see 3.4.1) for the class rooted at _root_integer_, allowing thier use with the operations of any integer type."
!discussion 99-03-21
The first wording change above is just to eliminate an apparent conflict in the standard. The second sentence of 3.5.4(14) is correct, but since all integer types are derived types, the paragraphs conflict.
The main problem dealt with here is caused by the preference rule (see 8.6(29)) for root_integer. In theory this can cause expressions which appear to involve only operations of of one numeric type to actually be evaluated using the operations of root_integer. Consider for instance the expression T'Val(T'Pos(X) - 3) where X is of type T.
Although all the values in the expression are of either type T or universal_integer, the subtraction operation is that of root_integer. This seems irksome, but unlikely to occur even inside a generic with a formal of a discrete type. However, there are more troubling cases. Consider the Boolean expression S'Modulus > T'Modulus, where S and T are modular subtypes. When is this illegal, guaranteed to work, or can possibly raise Constraint_Error? If the answer depends on the staticness of the subtypes, that way lies madness. (Note that in this particular case, the expression can always be evaluated at compile time even if the subtypes are static or generic formal subtypes.)
Even more troubling is this case:
type Modular is mod System.Max_Binary_Modulus; .. subtype mod is Modular range...; .. Base_Modulus: constant := mod'Modulus;
Does this or should this raise Constraint_Error, or is it implementation dependent? (It is implementation dependent if the subtype is non-static.)
Now let's look at the other side for a minute. There are some truly ugly cases such as X: Integer := Modular'Pos(Y) + Integer'Pos(Z); where Y and Z are varibles of the respective types. Here we have an expression that can only be evaluated at run-time, and possibly no machine arithmetic types that can be used safely, assuming that Integer and Modular are the largest available integer and modular types respectively.
The conclusion is that implementations should always be allowed to "do the right thing" in the reasonable cases, and required not to raise Constraint_Error in expressions without attributes of a single type but no arithmetic operations. But rather than force implementations not to provide otherwise useful types by requiring support of some pathological cases, these case are made erroneous. It would be possible to make these cases a bounded error, but such mixed expressions can (and should) always be rewritten to make explicit which operations should be modular and where overflow is wanted. By making such expressions erroneous only if they exceed the range of root_integer, programmers can still use such expressions if they know the possible ranges of all intermediate results.
Finally this AI allows the range of root_integer to exceed System.Min_Int..System.Max_Int. There is no point in allowing nonstandard integer types if the implementations are then restricted from using them where appropriate. For example, on a machine with a double-width multiplication result and easy addition and subtraction for such values, it would be useful to have that type as a nonstandard integer type, even if some multiplication operators were not provided, and you couldn't use it for indexing. It would be silly to prohibit using that type for evaluating expressions of type root_real. Where possible this is the best solution to the problem.

!section 3.5.4(14)
!subject Range of root_integer
!reference RM95 3.5.4(14)
!from Pascal Leroy 97-03-10
!reference 97-15727.g Pascal Leroy 97-3-10>>

The referenced paragraph defines the base range of root_integer as
System.Min_Int .. System.Max_Int.  This range covers all signed integer types,
but it is possible for modular types to exceed System.Max_Int.  Because
root_integer is the type used at runtime for many computations, the fact that
it doesn't cover all standard integer types has surprising consequences.

Consider for example:

       type T is (<>);
    package P is
    end P;

    package body P is
       B : Boolean := T'Pos (T'Last) = 3;
    end P;

Assume a 32-bit architecture, where Min_Int is -2 ** 31, Max_Int is 2 ** 31 -
1, Max_Binary_Modulus is 2 ** 32, and Max_Nonbinary_Modulus is 2 ** 32 - 1.
 Instantiating P with any signed integer type works fine, but if it is
instantiated with a modular type with 'Modulus greater than 2 ** 31, the
elaboration of B raises Constraint_Error because the result of the attribute
Pos exceeds Max_Int.  This makes it very hard to write a generic that works
for any discrete type.

In fact, it is even very hard to write a generic that works for any modular
type.  Consider a second example:

      type T is mod <>;
   procedure P;

   procedure P is
      if T'Modulus > 3 then
      end if;
   end P;

The conversion of T'Modulus to root_integer raises Constraint_Error.

Pascal Leroy                                    +                             + FAX


Questions? Ask the ACAA Technical Agent