In spite of every effort to define Ada 95 to be upward compatible with Ada 83, compiling Ada 83 source with an Ada 95 compiler may require some source changes. There are a few issues where Ada 95 differs syntactically from Ada 83. And there are issues which arise when any source is migrated from one system to another.
SCORE Ada 95 implementation contains a compiler option, the Ada 83 switch, which reverts the compiler to using Ada 83 syntax. In this mode, it checks for illegal Ada 83 constructs (which should not occur in valid Ada 83 source), and makes an adjustment in the reserved words.
To compile a source file using Ada 83 syntax, use a command of this form:
ada -ada83 [options] source-file
Here are the three major language compatibility issues and how they should be addressed.
1. New Reserved Words The following new reserved words exist in Ada 95:
abstract, aliased, protected, requeue, tagged, until
When compiled with an Ada 95 compiler, any occurrence of these identifiers must be renamed. However, if the source is compiled with the Ada 83 option, the new reserved words lose their special status and will be treated as normal Ada 83 identifiers. When these identifiers are removed from the set of reserved words, nearly all of the new Ada 95 features (e.g., tagged types) are disabled.
2. Character has 256 Positions In Ada 95, the subtype Character has 256 positions; where in Ada 83 it has 128 positions.
This change makes arrays with character index; case statements with a character choice and no others choice; and uses of Character’Last likely to become illegal or incorrect. The solutions may involve defining a subtype of Character with the range from Ada 83, introducing an others choice, or replacing Character’Last with Ascii.Del.
3. Unconstrained Generic Actual Subtypes A new syntax for a generic formal private type is introduced to indicate that the actual subtype is allowed to be indefinite. The syntax in Ada 83 still exists, but requires the actual subtype to be definite.
In Ada 95, the Ada 83 syntax:
generic
type Element_Type is private;
package Stack is ...
requires Element _Type to be matched by an actual subtype such as Integer, a constrained array, or a record type with default discriminants (all definite types). An actual parameter of an indefinite type, such as an unconstrained array, would be illegal.
The Ada 95 syntax which allows either definite or indefinite actual parameters (i.e., the Ada 83 syntax) is shown below:
generic
type Element_Type(<>) is private;
package Stack is ...
The following issues all arise from semantic differences between the two systems (which include implementation-defined characteristics). Listed below are the more common differences. See the DACS to SCORE Migration Guide for complete details.
4. Objects of Mutable Record Types
SCORE avoids implicit heap usage under all circumstances. One effect of this is that certain objects can no longer be declared of mutable discriminant record types because the representation of such objects require too much memory.
Example
type Var_String( Length : Natural := 0 ) is
record
Value : String( 1 .. Length );
end record;
Obj : Var_String;
End example
SCORE will report an error message stating that the object exceeds the capacity for such objects. No single approach will suit all situations but a simple change will often be to introduce a subtype of the discriminant type with constant bounds and use it as the type of the discriminant (this allows the SCORE compiler to reduce its worst-case size calculation and avoid the capacity problem).
Example
type Var_String_Natural is Natural range 0 .. 1000;
type Var_String( Length : Var_String_Natural := 0 ) is
record
Value : String( 1 .. Length );
end record;
Obj : Var_String;
End example
5. Alignment The default alignment of various types have changed from DACS to SCORE. SCORE typically aligns objects of scalar types to their (byte) size. Within records, DACS 80x86 lets all components start at 2 byte boundaries. DACS SPARC native uses the same alignments for the scalar types, but uses the size of arrays and records to determine their alignment so that it may exceed that of any of their components. This difference will only require modifications where the source code implicitly relies on a particular data layout.
DACS 80x86 interprets the value in alignment specifications as a storage unit count. These values probably require modifications to work with SCORE since the storage unit of DACS 80x86 is two bytes while it is one byte in SCORE.
6. Component Clauses DACS 80x86 allows certain record component clauses which SCORE does not allow.
Example
type Arr is array (0..31) of Boolean;
type Rec is
record
Comp : Arr;
end record;
for Rec use
record
Comp at 0 range 0 .. 31;
end record;
End example
The component clause is accepted by DACS 80x86 to pack the component of type Arr in this context though Arr itself is unpacked. SCORE does not allow a record component clause on a component of a structured type to change the layout within the component of the structured type. The source code must be modified so that the component type is packed or otherwise specified to have the size from the component clause (or to introduce a derived type with such packing and use it as the component type and add type conversions as needed).
Example
type Arr is array (0..31) of Boolean;
pragma Pack (Arr);
End example
7. Address Clauses Due to the different definition of the type System.Address in DACS and SCORE, address clauses for objects and subprograms may require modification to change the values to be positive integer values.
SCORE does not allow address clauses on task entries which is the Ada 83 method for defining interrupt handlers. Such interrupt handlers must be rewritten, see the Interrupt Handlers section below for details.
8. Interrupt Handlers In DACS 80x86 interrupt handlers are task entries with address clauses and possibly with pragma Interrupt_Handler; in SCORE interrupt handlers are protected procedures with pragma Attach_Handler or pragma Interrupt_Handler. The two forms cannot be mixed so source code modifications are required to go from one form to the other. The following describes some general approaches.
Example
Value : Value_Type;
task Interrupt_Task is
pragma Interrupt_Handler; – Fast Interrupt Handler
entry Int;
for Int use at 7;
end Interrupt_Task;
task Consumer is
entry Ready;
end Consumer;
task body Interrupt_Task is
begin
accept Int do
Value := Read_Value;
select
Consumer.Ready; – Signal Consumer task
else
null;
end select;
end Int;
end Interrupt_Task;
task body Consumer is
begin
loop
accept Ready;
Process( Value ); -- Process Value when ready
end loop;
end Consumer;
End example
This can be rewritten into a protected object with an interrupt handler. However, since protected objects are not allowed to make blocking calls (e.g., an entry call), the relationship between the two tasks in the example must be reversed and could then look as follows.
Example
Value : Value_Type;
protected Interrupt_Task is
pragma Interrupt_Priority;
entry Ready; -- Queue caller until Value is ready
procedure Int;
pragma Attach_Handler( Int, 7 );
private
Open : Boolean := False;
end Interrupt_Task;
task Consumer is
end Consumer;
protected body Interrupt_Task is
entry Ready when Open is
begin
Open := False;
end Ready;
procedure Int is
begin
Value := Read_Value;
Open := True; -- Signal Value is ready
end Int;
end Interrupt_Task;
task body Consumer is
begin
loop
Interrupt_Task.Ready;
Process( Value ); -- Process Value when ready
end loop;
end Consumer;