[Ada Information Clearinghouse]

Ada '83 Quality and Style:

Guidelines for Professional Programmers

Copyright 1989, 1991,1992 Software Productivity Consortium, Inc., Herndon, Virginia.

CHAPTER 5: Programming Practices

5.9 Erroneous Execution

An Ada program is erroneous when it violates or extends the rules of the language governing program behavior. Neither compilers nor run-time environments are able to detect erroneous behavior in all circumstances and contexts. As stated in Section 1.6 of Department of Defense (1983), "The effects of erroneous execution are unpredictable." If the compiler does detect an instance of an erroneous program, its options are to indicate a compile time error, to insert the code to raise Program_Error, possibly to write a message to that effect, or to do nothing at all.

Erroneousness is not a concept unique to Ada. The guidelines below describe or explain the specific instances of erroneousness defined in the Ada Language Reference Manual. Although Incorrect Order Dependencies is not, strictly speaking, a case of erroneous execution, the rationale for avoiding such dependencies is the same. Consequently, the guideline is included in this section.

Language Ref Manual references: 1.6 Classification of Errors

In this section...
5.9.1 Unchecked Conversion
5.9.2 Unchecked Deallocation
5.9.3 Dependence on Parameter Passing Mechanism
5.9.4 Multiple Address Clauses
5.9.5 Suppression of Exception Check
5.9.6 Initialization
5.9.7 Direct_IO and Sequential_IO
5.9.8 Incorrect Order Dependencies
Summary of Guidelines from this section


5.9.1 Unchecked Conversion

guideline

example

The following example may run without exception, depending on the implementation:
------------------------------------------------------------------------ 
with Unchecked_Conversion; 
with Text_IO;

procedure Test is

   type Color is (Red, Yellow, Blue);
   
   function Integer_To_Color is 
      new Unchecked_Conversion (Source => Integer, 
                                Target => Color);
                                
   A_Color : Color; 
   List    : array (Color) of Boolean;
   
   Data : Boolean;
   
begin  -- Test

   A_Color := Integer_To_Color(15); 
   Data    := List(A_Color); 
   Text_IO.Put_Line(Color'Image(A_Color));
   
end Test; 
------------------------------------------------------------------------

rationale

An unchecked conversion is a bit-for-bit copy without regard to the meanings attached to those bits and bit positions by either the source or the destination type. The source bit pattern can easily be meaningless in the context of the destination type. Unchecked conversions can create values that violate type constraints on subsequent operations. Unchecked conversion of objects mismatched in size has implementation-dependent results.

Language Ref Manual references: 4.6 Type Conversions, 7.3 Package Bodies, 13.10.2 Unchecked Type Conversions


5.9.2 Unchecked Deallocation

guideline

rationale

Most of the reasons for using Unchecked_Deallocation with caution have been given in Guideline 5.4.3. When this feature is used, there is no checking that there is only one access path to the storage being deallocated. Thus, any other access paths are not made null. Depending on such a check is erroneous.

Language Ref Manual references: 3.8 Access Types, 13.10.1 Unchecked Storage Deallocation


5.9.3 Dependence on Parameter Passing Mechanism

guideline

example

The output of this program depends on the particular parameter passing mechanism that was used:
------------------------------------------------------------------------ 
with Text_IO;

procedure Outer is

   type Coordinates is 
      record 
         X : Integer := 0; 
         Y : Integer := 0; 
      end record;
      
   Outer_Point : Coordinates;
   
   package Integer_IO is 
      new Text_IO.Integer_IO (Num => Integer);
      
   --------------------------------------------------------------------- 
   procedure Inner (Inner_Point : in out Coordinates) is 
   begin 
      Inner_Point.X := 5;
      
      -- The following line causes the output of the program to 
      -- depend on the parameter passing mechanism. 
      Integer_IO.Put(Outer_Point.X); 
   end Inner; 
   ---------------------------------------------------------------------
   
begin  -- Outer 
   Integer_IO.Put(Outer_Point.X); 
   Inner         (Outer_Point); 
   Integer_IO.Put(Outer_Point.X); 
end Outer; 
------------------------------------------------------------------------
If the parameter passing mechanism is by copy, the results on the standard
output file are:

0 0 5

If the parameter passing mechanism is by reference, the results are:

0 5 5

rationale

The language definition specifies that a parameter whose type is an array, record, or task type can be passed by copy or reference. It is erroneous to assume that either mechanism is used in a particular case.

exceptions

Frequently, when interfacing Ada to foreign code, dependence on parameter passing mechanisms used by a particular implementation is unavoidable. In this case, isolate the calls to the foreign code in an interface package that exports operations that do not depend on the parameter-passing mechanism.

Language Ref Manual references: 3.6 Array Types, 3.7 Record Types, 6.2 Formal Parameter Modes, 6.4 Subprogram Calls, 13.9 Interface to Other Languages


5.9.4 Multiple Address Clauses

guideline

example

Single_Address : constant ...

Interrupt_Vector_Table : Hardware_Array; 
for Interrupt_Vector_Table use at Single_Address;

rationale

The result of specifying a single address for multiple objects or program units is undefined, as is specifying multiple addresses for a single object or program unit. Specifying multiple address clauses for an interrupt entry is also undefined. It does not necessarily overlay objects or program units, or associate a single entry with more than one interrupt.

Language Ref Manual references: 9.5 Entries, Entry Calls, and Accept Statements, 13.5 Address Clauses, 13.5.1 Interrupts


5.9.5 Suppression of Exception Check

guideline

rationale

If you disable exception checks and program execution results in a condition in which an exception would otherwise occur, the program execution is erroneous. The results are unpredictable. Further, you must still be prepared to deal with the suppressed exceptions if they are raised in and propagated from the bodies of subprograms, tasks, and packages you call.

By minimizing the code which has exception checking removed, you increase the reliability of the program. There is a rule of thumb which suggests that 20 percent of the code is responsible for 80 percent of the CPU time. So once you have identified the code that actually needs exception checking removed, it is wise to isolate it in a block (with appropriate comments) and leave the surrounding code with exception checking in effect.

Language Ref Manual references: 11.7 Suppressing Checks, B Predefined Language Pragmas


5.9.6 Initialization

guideline

example

------------------------------------------------------------------------ 
package Robot_Controller is

   ... 
   function Sense return Position; 
   ...
   
end Robot_Controller;

------------------------------------------------------------------------ 
package body Robot_Controller is

   ... 
   Goal : Position := Sense;       -- This raises Program_Error 
   ...
   
   --------------------------------------------------------------------- 
   function Sense return Position is 
   begin 
      ... 
   end Sense; 
   ---------------------------------------------------------------------
   
begin  -- Robot_Controller 
   Goal := Sense;                  -- The function has been elaborated.
   
   ... 
end Robot_Controller; 
------------------------------------------------------------------------

rationale

Ada does not define an initial default value for objects of any type other than access types. Using the value of an object before it has been assigned a value causes unpredictable behavior, possibly raising an exception. Objects can be initialized implicitly by declaration or explicitly by assignment statements. Initialization at the point of declaration is safest as well as easiest for maintainers. You can also specify default values for components of records as part of the type declarations for those records.

Ensuring initialization does not imply initialization at the declaration. In the example above, Goal must be initialized via a function call. This cannot occur at the declaration, because the function Sense has not yet been elaborated, but can occur later as part of the sequence of statements of the body of the enclosing package.

An unelaborated function called within a declaration (initialization) raises the exception, Program_Error, that must be handled outside of the unit containing the declarations. This is true for any exception the function raises even if it has been elaborated.

If an exception is raised by a function call in a declaration, it is not handled in that immediate scope. It is raised to the enclosing scope. This can be controlled by nesting blocks.

note

Sometimes, elaboration order can be dictated with pragma Elaborate. Pragma
Elaborate only applies to library units.

Language Ref Manual references: 3.2.1 Object Declarations, 3.9 Declarative Parts, 6.5 Function Subprograms


5.9.7 Direct_IO and Sequential_IO

guideline

rationale

As with Unchecked_Conversion, there is no check on the value obtained from the read operations found in Direct_IO and Sequential_IO. See Guideline 5.9.1 for an example.

note

It is sometimes difficult to force an optimizing compiler to perform the necessary checks on a value that the compiler believes is in range. Most compiler vendors allow the option of suppressing optimization which can be helpful.

Language Ref Manual references: 14.2.2 Sequential Input-Output, 14.2.4 Direct Input-Output


5.9.8 Incorrect Order Dependencies

guideline

rationale

As stated in the Ada Language Reference Manual, an incorrect order dependency may arise whenever ". . . different parts of a given construct are to be executed in some order that is not defined by the language. . . . The construct is incorrect if execution of these parts in a different order would have a different effect." (Department of Defense 1983, section 1.6).

While an incorrect order dependency may not adversely affect the program on a certain implementation, the code might not execute correctly when it is ported. Avoid incorrect order dependencies, but also recognize that even an unintentional error of this kind could prohibit portability.

Language Ref Manual references: 6.4.1 Parameter Associations


Back to document index