3.3.1 Object Declarations

5
Feb

3.3.1 Object Declarations

1 {stand-alone object} {explicit initial value} {initialization expression} An object_declaration declares a stand-alone object with a given nominal subtype and, optionally, an explicit initial value given by an initialization expression. {anonymous array type} {anonymous task type} {anonymous protected type} For an array, task, or protected object, the object_declaration may include the definition of the (anonymous) type of the object.

Syntax

2/2 object_declaration ::= 
    defining_identifier_list : [aliased] [constant] subtype_indication [:= expression];
  |
defining_identifier_list : [aliased] [constant] access_definition [:= expression];
  | defining_identifier_list : [aliased] [constant] array_type_definition [:= expression];
  | single_task_declaration
  | single_protected_declaration

3 defining_identifier_list ::= defining_identifier {, defining_identifier}

Name Resolution Rules

4 {expected type (object_declaration initialization expression) [partial]} For an object_declaration with an expression following the compound delimiter :=, the type expected for the expression is that of the object. {initialization expression} This expression is called the initialization expression. {constructor: See initialization expression}

Legality Rules

5/2 An object_declaration without the reserved word constant declares a variable object. If it has a subtype_indication or an array_type_definition that defines an indefinite subtype, then there shall be an initialization expression.

Static Semantics

6 An object_declaration with the reserved word constant declares a constant object. {full constant declaration} If it has an initialization expression, then it is called a full constant declaration. {deferred constant declaration} Otherwise it is called a deferred constant declaration. The rules for deferred constant declarations are given in clause 7.4. The rules for full constant declarations are given in this subclause.

7 Any declaration that includes a defining_identifier_list with more than one defining_identifier is equivalent to a series of declarations each containing one defining_identifier from the list, with the rest of the text of the declaration copied for each declaration in the series, in the same order as the list. The remainder of this International Standard relies on this equivalence; explanations are given for declarations with a single defining_identifier.

8/2 {nominal subtype} The subtype_indication, access_definition, or full type definition of an object_declaration defines the nominal subtype of the object. The object_declaration declares an object of the type of the nominal subtype. 

8.a/2 Discussion: The phrase “full type definition” here includes the case of an anonymous array, access, task, or protected type. 

8.1/2 {requires late initialization} A component of an object is said to require late initialization if it has an access discriminant value constrained by a per-object expression, or if it has an initialization expression that includes a name denoting the current instance of the type or denoting an access discriminant.

8.b/2 Reason: Such components can depend on the values of other components of the object. We want to initialize them as late and as reproducibly as possible. 

Dynamic Semantics

9/2 {constraint (of an object)} If a composite object declared by an object_declaration has an unconstrained nominal subtype, then if this subtype is indefinite or the object is constant the actual subtype of this object is constrained. The constraint is determined by the bounds or discriminants (if any) of its initial value; {constrained by its initial value} the object is said to be constrained by its initial value. {actual subtype (of an object)} {subtype (of an object): See actual subtype of an object} When not constrained by its initial value, the actual and nominal subtypes of the object are the same. {constrained (object)} {unconstrained (object)} If its actual subtype is constrained, the object is called a constrained object.

10 {implicit initial values (for a subtype)} For an object_declaration without an initialization expression, any initial values for the object or its subcomponents are determined by the implicit initial values defined for its nominal subtype, as follows: 

  • 11 The implicit initial value for an access subtype is the null value of the access type.
  • 12 The implicit initial (and only) value for each discriminant of a constrained discriminated subtype is defined by the subtype.
  • 13 For a (definite) composite subtype, the implicit initial value of each component with a default_expression is obtained by evaluation of this expression and conversion to the component's nominal subtype (which might raise Constraint_Error — see 4.6, “Type Conversions”), unless the component is a discriminant of a constrained subtype (the previous case), or is in an excluded variant (see 3.8.1). {implicit subtype conversion (component defaults) [partial]} For each component that does not have a default_expression, any implicit initial values are those determined by the component's nominal subtype.
  • 14 For a protected or task subtype, there is an implicit component (an entry queue) corresponding to each entry, with its implicit initial value being an empty queue. 
14.a Implementation Note: The implementation may add implicit components for its own use, which might have implicit initial values. For a task subtype, such components might represent the state of the associated thread of control. For a type with dynamic-sized components, such implicit components might be used to hold the offset to some explicit component. 

15 {elaboration (object_declaration) [partial]} The elaboration of an object_declaration proceeds in the following sequence of steps:

16/2   1.  The subtype_indication, access_definition, array_type_definition, single_task_declaration, or single_protected_declaration is first elaborated. This creates the nominal subtype (and the anonymous type in the last four cases).

17      2. If the object_declaration includes an initialization expression, the (explicit) initial value is obtained by evaluating the expression and converting it to the nominal subtype (which might raise Constraint_Error — see 4.6). {implicit subtype conversion (initialization expression) [partial]}

18/2  3. The object is created, and, if there is not an initialization expression, the object is initialized by default. {initialized by default} When an object is initialized by default, any per-object constraints (see 3.8) are elaborated and any implicit initial values for the object or for its subcomponents are obtained as determined by the nominal subtype. {initialization (of an object)} {assignment operation (during elaboration of an object_declaration)} Any initial values (whether explicit or implicit) are assigned to the object or to the corresponding subcomponents. As described in 5.2 and 7.6, Initialize and Adjust procedures can be called. {constructor: See initialization} 

18.a Discussion: For a per-object constraint that contains some per-object expressions and some non-per-object expressions, the values used for the constraint consist of the values of the non-per-object expressions evaluated at the point of the type_declaration, and the values of the per-object expressions evaluated at the point of the creation of the object.

18.b The elaboration of per-object constraints was presumably performed as part of the dependent compatibility check in Ada 83. If the object is of a limited type with an access discriminant, the access_definition is elaborated at this time (see 3.7).

18.c Reason: The reason we say that evaluating an explicit initialization expression happens before creating the object is that in some cases it is impossible to know the size of the object being created until its initial value is known, as in “X: String := Func_Call(...);”. The implementation can create the object early in the common case where the size can be known early, since this optimization is semantically neutral. 

19.a Ramification: Since the initial values have already been converted to the appropriate nominal subtype, the only Constraint_Errors that might occur as part of these assignments are for values outside their base range that are used to initialize unconstrained numeric subcomponents. See 3.5. 

20/2 For the third step above evaluations and assignments are performed in an arbitrary order subject to the following restrictions:

  • 20.1/2 Assignment to any part of the object is preceded by the evaluation of the value that is to be assigned. 

20.a.1/2 Reason: Duh. But we ought to say it. Note that, like any rule in the International Standard, it doesn't prevent an “as-if” optimization; as long as the semantics as observed from the program are correct, the compiler can generate any code it wants.

  • 20.2/2 The evaluation of a default_expression that includes the name of a discriminant is preceded by the assignment to that discriminant.

20.a.2/2 Reason: Duh again. But we have to say this, too. It's odd that Ada 95 only required the default expressions to be evaluated before the discriminant is used; it says nothing about discriminant values that come from subtype_indications.

  • 20.3/2 The evaluation of the default_expression for any component that depends on a discriminant is preceded by the assignment to that discriminant.

20.a Reason: For example: 

20.b

type R(D : Integer := F) is
    record
        S : String(1..D) := (others => G);
    end record;

20.c

X : R;

20.d For the elaboration of the declaration of X, it is important that F be evaluated before the aggregate. 

  • 20.4/2 The assignments to any components, including implicit components, not requiring late initialization must precede the initial value evaluations for any components requiring late initialization; if two components both require late initialization, then assignments to parts of the component occurring earlier in the order of the component declarations must precede the initial value evaluations of the component occurring later. 

20.e/2 Reason: Components that require late initialization can refer to the entire object during their initialization. We want them to be initialized as late as possible to reduce the chance that their initialization depends on uninitialized components. For instance: 

20.f/2

type T (D : Natural) is
  limited record
    C1 : T1 (T'Access);
    C2 : Natural := F (D);
    C3 : String (1 .. D) := (others => ' ');
  end record;

20.g/2 Component C1 requires late initialization. The initialization could depend on the values of any component of T, including D, C2, or C3. Therefore, we want it to be initialized last. Note that C2 and C3 do not require late initialization; they only have to be initialized after D.

20.h/2 It is possible for there to be more than one component that requires late initialization. In this case, the language can't prevent problems, because all of the components can't be the last one initialized. In this case, we specify the order of initialization for components requiring late initialization; by doing so, programmers can arrange their code to avoid accessing uninitialized components, and such arrangements are portable. Note that if the program accesses an uninitialized component, 13.9.1 defines the execution to be erroneous. 

21 [There is no implicit initial value defined for a scalar subtype.] {uninitialized variables [partial]} In the absence of an explicit initialization, a newly created scalar object might have a value that does not belong to its subtype (see 13.9.1 and H.1).

21.a To be honest: It could even be represented by a bit pattern that doesn't actually represent any value of the type at all, such as an invalid internal code for an enumeration type, or a NaN for a floating point type. It is a generally a bounded error to reference scalar objects with such “invalid representations”, as explained in 13.9.1, “Data Validity”. 

21.b Ramification: There is no requirement that two objects of the same scalar subtype have the same implicit initial “value” (or representation). It might even be the case that two elaborations of the same object_declaration produce two different initial values. However, any particular uninitialized object is default-initialized to a single value (or invalid representation). Thus, multiple reads of such an uninitialized object will produce the same value each time (if the implementation chooses not to detect the error). 

NOTES

22 (7)  Implicit initial values are not defined for an indefinite subtype, because if an object's nominal subtype is indefinite, an explicit initial value is required.

23 (8)  {stand-alone constant} {stand-alone variable} As indicated above, a stand-alone object is an object declared by an object_declaration. Similar definitions apply to “stand-alone constant” and “stand-alone variable.” A subcomponent of an object is not a stand-alone object, nor is an object that is created by an allocator. An object declared by a loop_parameter_specification, parameter_specification, entry_index_specification, choice_parameter_specification, or a formal_object_declaration is not called a stand-alone object.

24 (9)  The type of a stand-alone object cannot be abstract (see 3.9.3). 

Examples

25 Example of a multiple object declaration: 

26

--  the multiple object declaration 

27/2

John, Paul : not null Person_Name := new Person(Sex => M);  --  see 3.10.1

28

--  is equivalent to the two single object declarations in the order given

29/2

John : not null Person_Name := new Person(Sex => M);
Paul : not null Person_Name := new Person(Sex => M);

30 Examples of variable declarations: 

31/2

Count, Sum  : Integer;
Size        : Integer range 0 .. 10_000 := 0;
Sorted      : Boolean := False;
Color_Table : array(1 .. Max) of Color;
Option      : Bit_Vector(1 .. 10) := (others => True);
Hello       : aliased String := "Hi, world.";
θ, φ        : Float range -π .. +π;

32 Examples of constant declarations: 

33/2

Limit     : constant Integer := 10_000;
Low_Limit : constant Integer := Limit/10;
Tolerance : constant Real := Dispersion(1.15);
Hello_Msg : constant access String := Hello'Access; -- see 3.10.2

Extensions to Ada 83

33.a {extensions to Ada 83} The syntax rule for object_declaration is modified to allow the aliased reserved word.

33.b A variable declared by an object_declaration can be constrained by its initial value; that is, a variable of a nominally unconstrained array subtype, or discriminated type without defaults, can be declared so long as it has an explicit initial value. In Ada 83, this was permitted for constants, and for variables created by allocators, but not for variables declared by object_declarations. This is particularly important for tagged class-wide types, since there is no way to constrain them explicitly, and so an initial value is the only way to provide a constraint. It is also important for generic formal private types with unknown discriminants.

33.c We now allow an unconstrained_array_definition in an object_declaration. This allows an object of an anonymous array type to have its bounds determined by its initial value. This is for uniformity: If one can write “X: constant array(Integer range 1..10) of Integer := ...;” then it makes sense to also allow “X: constant array(Integer range <>) of Integer := ...;”. (Note that if anonymous array types are ever sensible, a common situation is for a table implemented as an array. Tables are often constant, and for constants, there's usually no point in forcing the user to count the number of elements in the value.) 

Wording Changes from Ada 83

33.d We have moved the syntax for object_declarations into this subclause.

33.e Deferred constants no longer have a separate syntax rule, but rather are incorporated in object_declaration as constants declared without an initialization expression

Inconsistencies With Ada 95

33.f/2 {inconsistencies with Ada 95} Unconstrained aliased objects of types with discriminants with defaults are no longer constrained by their initial values. This means that a program that raised Constraint_Error from an attempt to change the discriminants will no longer do so. The change only affects programs that depended on the raising of Constraint_Error in this case, so the inconsistency is unlikely to occur outside of the ACATS. This change may however cause compilers to implement these objects differently, possibly taking additional memory or time. This is unlikely to be worse than the differences caused by any major compiler upgrade. 

Extensions to Ada 95

33.g/2 {extensions to Ada 95} A constant may have a limited type; the initialization expression has to be built-in-place (see 7.5).

33.h/2 {extensions to Ada 95} A stand-alone object may have an anonymous access type.

Wording Changes from Ada 95

33.i/2 Corrigendum: Corrected wording to say that per-object constraints are elaborated (not evaluated).

33.j/2 The rules for evaluating default initialization have been tightened. In particular, components whose default initialization can refer to the rest of the object are required to be initialized last.

33.k/2 Added examples of various new constructs.