The spatial arrangement of declarative items in a declarative part is (almost) free. This means that constant and variable object declarations, and type and subtype declarations can be mixed in groupings that best reflect the logical needs of a program.
An Ada program can be read linearly, line by line in a sequential manner, very much in the same way as we read good English prose. This means that our understanding of a text progresses line by line (declaration by declaration) and that we can reuse in later lines the knowledge acquired in earlier lines, as is shown in the following example where each declaration is made in terms of the one on the previous line:
declare LENGTH : constant INTEGER := 100; SQUARE : constant INTEGER := LENGTH*LENGTH; subtype AREA is INTEGER range 0 .. SQUARE; SURFACE, EXTENT : AREA; begin ... end |
Several rules of the language were designed to serve this purpose of linear reading. For example, the scope rules allow declarations to appear in the order used above. But they would not allow the initialization of SQUARE to refer to LENGTH if the declaration of LENGTH were given after that of SQUARE: the Ada rules forbid forward references: we can refer only to what we have already read. Other specific rules that support this linear reading will be seen later, when we discuss subprograms and access types (Chapters 8 and 6).
Reading is linear and so also is elaboration of declarations. During program execution, declarations are elaborated (that is, they achieve their effect), one by one, in the linear order in which they appear.
For example, the constant LENGTH and the variable SURFACE do not exist before the execution of the above block statement. This execution will start by the elaboration of the four declarations that form the declarative part: one after the other. Thus after elaboration of the declaration of LENGTH, this constant will exist; but the variable SURFACE will exist only after the elaboration of its declaration - the last one. Finally, after executing the statements enclosed by begin and end, all the entities created by the elaboration of the declarative part will disappear (or at least become unreachable).
The above illustrates the logical model of execution of an Ada program. In this model, elaboration is a process that takes place dynamically, at run time. This does not, however, prevent a compiler from using a more static approach - for example, for storage allocation - as long as it can guarantee that this implementation technique will yield an effect equivalent to that of the logical model.
The only limitation imposed by the language on the order of declarative items is that bodies must appear after simpler declarative items such as object declarations. This rule was devised to avoid the poor degree of readability that would result from mixing large and small textual items. In Algol-like languages that allowed this mixing, the occurrence of an isolated variable between the bodies of two large subprograms was a well-known source of error. Although good programming practice would avoid such isolated variable declarations, they could still be generated by faulty uses of a text-editor and cause subtle errors when hiding outer declarations:
declare X : INTEGER := 0; begin ... declare ... -- several procedures procedure P is -- very long ... end P; X : INTEGER; -- a mistake: not legal Ada procedure Q is -- also long ... end Q; ... -- more procedures begin ... X := 2; -- should modify X ... end; if X = 0 then -- but apparently did not! PUT("SOMETHING STRANGE is HAPPENING"); end if; ... end; |
With the Ada rules, the declaration of a variable between two procedure bodies is not allowed and therefore this error will be detected and signaled by the compiler. Note that these rules do not forbid a local declaration of X hiding the outer one - there may be good reasons for such a declaration - but the hiding declaration will have to occur before the bodies and therefore in a portion of text that is easier to inspect than the potentially much longer text of the sequence of bodies.