!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.
!appendix 97-03-19
!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>>
!discussion
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:
generic
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:
generic
type T is mod <>;
procedure P;
procedure P is
begin
if T'Modulus > 3 then
...
end if;
end P;
The conversion of T'Modulus to root_integer raises Constraint_Error.
_____________________________________________________________________
Pascal Leroy +33.1.30.12.09.68
pleroy@rational.com +33.1.30.12.09.66 FAX
****************************************************************