To specify the typechecking of assignment statements we need to define "assignable", which is a relation between types and types, between expressions and variables, and between expressions and types.
A type T is assignable to a type U if:
An expression e is assignable to a variable v if:
The first point can be checked statically; the others generally require runtime checks. Since there is no way to determine statically whether the value of a procedure parameter is local or global, assigning a local procedure is a runtime rather than a static error.
An expression e is assignable to a type T if e is assignable to some variable of type T. (If T is not an open array type, this is the same as saying that e is assignable to any variable of type T.)
An assignment statement has the form:
    v := e
where v is a writable designator and e is an expression
assignable to the variable designated by v.  The statement sets
v to the value of e.  The order of evaluation of
v and e is undefined, but e will be evaluated before
v is updated.
In particular, if v and e are overlapping
subarrays,
the assignment is performed in such a way that no element is used
as a target before it is used as a source.
Examples of assignments:
    VAR
      x: REFANY;
      a: REF INTEGER;
      b: REF BOOLEAN;
    a := b;  (* static error *)
    x := a;  (* no possible error *)
    a := x   (* possible checked runtime error *)
The same comments would apply if x had an ordinal type with non-overlapping subranges a and b, or if x had an object type and a and b had incompatible subtypes. The type ADDRESS is treated differently from other reference types, since a runtime check cannot be performed on the assignment of raw addresses. For example:
    VAR
      x: ADDRESS;
      a: UNTRACED REF INTEGER;
      b: UNTRACED REF BOOLEAN;
    a := b;  (* static error *)
    x := a;  (* no possible error *)
    a := x   (* static error in safe modules *)