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.