[comp.lang.ada] Default initial values

NCOHEN@IBM.COM ("Norman H. Cohen") (03/29/91)

This message addresses two related topics:
   (1) default initialization and safety
   (2) providing default initialization for all Ada types

(1) Default Initialization and Safety

Someone pointed out that compiler-defined default initialization can
mask errors that occur when a programmer forgets to put an application-
sensible value into a variable before using the variable.  If there
is no compiler-defined default initialization, the arbitrary initial
contents of the variable MAY cause an exception to be raised, thus
revealing the programming error.

Of course you have to be lucky.  The arbitrary initial contents of the
variable may also be a perfectly legitimate value every time the
program is tested (but not, of course, the first time the deployed
system is demonstrated to the Secretary of Defense).

This suggests that an appropriate compiler-defined default initial value
would be a value GUARANTEED to raise an exception.  This is, in essence,
a run-time counterpart to Dijkstra's suggestion.  Dijkstra's suggestion
strikes me as overly restrictive in cases like the following:

  procedure P(X: in T1);
     Y : T2 := ARBITRARY_AND_MEANINGLESS_VALUE_DIJKSTRA_MADE_ME_SUPPLY;
  begin
     if SOME_FUNCTION(X) then
        Y := ... ;
     else
        Y := ...;
     end if;
     -- Y now has a sensible value appropriate for the current value of X
     ...
     [use Y]
   end P;

The PL/C compiler (a compiler developed at Cornell for an instructional
subset of PL/I) did something like this to detect any attempt at
run-time to use the value of an uninitialized variable.  32-bit integers
were restricted to the range -(2**31 - 1) .. 2**31 - 1 and the one
remaining twos-complement value, -2**31, was reserved for use as the
default initial value.  The checking code consisted entirely of
negating a value (and discarding the result) before using it.  An
attempt to compute the negation of -2**31 (which does not have a 32-bit
twos-complement representation), would generate a hardware trap to the
error routine.  An attempt at run-time to use a programmer-uninitialized
variable was ALWAYS caught.

(2) Providing Default Initialization for All Ada Types

The provision of default initialization only for record types in Ada 83
was not accidental.  Record types really are different.  When you
specify default initial values for the components of a record type, you
do not specify expressions of the record type BEING declared, but values
of COMPONENT types declared earlier.  This is important because of the
Ada 83 model that the operations on a type are declared (some implicitly,
some explicitly) sometime AFTER the type declaration.  (The implicitly
declared operations are declared IMMEDIATELY after the type declaration;
see RM 3.3.3(1,2), though 3.5.1(3) seems to contradict 3.3.3(2) in the
case of enumeration literals.)  One can't write a default initial value
expression for a type when none of the type's operations are available.
Even a numeric literal really corresponds to an implicit conversion from
a universal type to the integer type required by the context.

One fix that has been proposed for Ada 9X is to state that the
operations implicitly declared for a type are declared immediately
after the type DEFINITION, but before any default initial value, and
are visible immediately after the type definition.  For example:

   type MY_BASE is range -2**31 .. 2**31-1
      -- The operations implicitly declared here include:
      --    o implicit conversion from universal_integer to MY_BASE
      --    o function "**"
      --         (LEFT: MY_BASE; RIGHT: INTEGER) return MY_BASE;
      --    o function "-" (RIGHT: MY_BASE) return MY_BASE;
      := -2**31;

However, this is misleading when implicitly declared operations used in
the default-value expression are hidden later by explicit declarations.
(For example, if the declaration of MY_BASE were to be followed by
explicit declarations of "+", "-", etc. overriding the predefined
operators with operators for cyclic arithmetic--so that, for instance,
MY_BASE'LAST + 1 would yield the value MY_BASE'FIRST--the unary
"-" in the default-value expression would still have to be the predefined
version.)

In short, the seemingly simple proposal to allow default initial values
for all types in Ada 9X (except perhaps limited types) has a number of
technical complications.   It is unclear whether the benefits of the
proposed feature are worth the resulting complication of the language
definition.  This is not to say that the proposal will definitely be
rejected, but rather to explain that there is another side to the story .

Another complication is the possibility of declaring a subtype that
does not include the default value:

   subtype MY_INTEGER is MY_BASE range MY_BASE'FIRST+1 .. MY_BASE'LAST;

For such a subtype, elaboration of an object declaration without an
explicit initial value would presumably raise CONSTRAINT_ERROR.
(Run-time enforcement of Dijkstra's rule!)  It is to fix this problem
(which doesn't really strike me as a problem, by the way) that default
values for subtypes have been proposed.  However, associating initial
values with subtypes would invalidate a fundamental principle in Ada 83,
structural equivalence of subtypes.  (An occurrence of a type mark
denoting a subtype is, in essence, equivalent to the occurrence of the
subtype indication in the declaration of that subtype, with a few
syntactic adjustments.)