100 Uninitialised Locations

A location is either initialised or uninitialised. An initialised location contains a value or extension satisfying its type and cardinality constraint. An uninitialised location has no current value or extension. Uninitialised is a state of a location, not a value in the carrier of its declared type.


var String message;

This declaration creates an uninitialised location. The declaration is legal: a location need not be initialised merely because it is declared, and it may remain uninitialised until it goes out of scope if it is never read. An implementation may warn that the variable is unused, but unused and uninitialised variables are not by themselves illegal.

Initialisation and cardinality

Initialisation is independent of cardinality. An empty extension is an ordinary initialised state when the location's cardinality permits it:


var String names*;       // uninitialised
var String others* = {}; // initialised with the empty extension

Assignment to an uninitialised location initialises it:


var String message;
message := "hello";
print message;           // legal

The assigned value or extension must satisfy the same type and cardinality rules as an assignment to an already-initialised location.

Reads

A program that reads an uninitialised location is illegal. Every control-flow path that can reach a read must have initialised the location or relevant sublocation. The compiler must reject the program unless definite- initialisation analysis establishes this property.


var String s;
var String t = s + "a";  // static error: s may be uninitialised

A read is any operation that requires or exposes the current contents of a location. Reads include:

  • evaluating the location in an expression;
  • copying, moving, returning, or capturing its contents;
  • passing it as an ordinary argument;
  • allowing a composite containing an uninitialised sublocation to escape.

For both an exclusive-reference argument &s and an explicit move argument ^s, the call-site expression must denote a location. Using an expression as a location designator does not by itself read that location. Nevertheless, both forms require the denoted location to be initialised. An exclusive-reference call cannot be used to initialise an uninitialised caller location, and an explicit move cannot move a value from one. Initialise the location by assignment before making the call.

The denoted location may be a whole variable or a sublocation within it. For example, &p.x grants exclusive access to the sublocation p.x. That sublocation must be initialised even if other sublocations of p are not. The initialisation requirement is applied to the particular location denoted by the argument expression.

A plain assignment does not read its target and may therefore initialise it. A compound update does read its target before writing the result and requires the target to be initialised:


var Integer count;
count := 1;       // legal: initialises count

var Integer total;
total += 1;       // static error: reads total before writing it

Definite initialisation and control flow

At a control-flow join, a location is definitely initialised only if it is initialised on every reachable incoming path. It is a static error to read a location when any reachable incoming path may leave it uninitialised:


var String message;

if condition {
    message := "yes";
}

print message;  // static error: message may be uninitialised

The analysis accounts for branches, loops, early returns, exception paths, and other control transfers. A path ending in unreachable; has no normally completing successor and therefore does not contribute a state to a later join; see Unreachable.

Moving from an initialised owned location leaves that location uninitialised. Its binding remains in scope and a later plain assignment may initialise it again. Once a call argument ^s moves the value out of the caller-owned location denoted by s, that location remains uninitialised on both normal and exceptional exits from the call. See Parameter Access and Ownership.

Partially initialised composites

A value, entity, quotient value, or structural value may be partially initialised while it is under construction. Its attributes and other constituent locations have their own initialisation states:


value Point
{
    Float32 x;
    Float32 y;
}

func Point makePoint()
{
    var Point p;
    p.x := 1;

    var Float32 x = p.x; // legal: p.x is initialised
    var Float32 y = p.y; // static error: p.y is uninitialised
    return p;            // static error: p is only partially initialised
}

Reading an initialised sublocation of a partially initialised composite is legal. Reading an uninitialised sublocation is illegal. Reading, copying, moving, returning, passing, capturing, or otherwise allowing the composite as a whole to escape requires every constituent location to be initialised.

The same principle applies to arrays, lists, maps, and other composite structures: initialisation is tracked at the locations that constitute the value. A value inserted into a collection or otherwise made externally observable must be fully initialised. An optional or many-valued constituent location is still required to be initialised before escape, although its initialised extension may be empty when its cardinality permits.

For nominal values, all required constituent locations must be initialised before the value is read or transferred as a whole.

No required runtime representation

These are language-legality and static-analysis rules. The language does not require implementations to represent the initialisation state with a runtime tag or to insert runtime checks. A conforming compiler rejects a program when it cannot establish definite initialisation at every read.