Previous Table of Contents Next


Note that T <: U and U <: T does not imply that T and U are the same because the subtype relation is unaffected by parameter names, default values, and packing.

For example, consider the following:

   TYPE
     T = [0..255];
     U = BITS 8 FOR [0..255];
     AT = ARRAY OF T;
     AU = ARRAY OF U;

The types T and U are subtypes of one another but are not the same. The types AT and AU are unrelated by the subtype relation.

11.4.11. Predeclared Opaque Types

The language predeclares the two types:

   TEXT    <: REFANY
   MUTEX    <: ROOT

which represent text strings and mutual exclusion semaphores, respectively. These are opaque types. Their properties are specified in the required interfaces Text and Thread.

11.5. Statements

Look into any carpenter’s tool-bag and see how many different hammers, chisels, planes, and screwdrivers he keeps there—not for ostentation or luxury, but for different sorts of jobs.

Robert Graves and Alan Hodge

Executing a statement produces a computation that can halt (normal outcome), raise an exception, cause a checked runtime error, or loop forever. If the outcome is an exception, it can optionally be paired with an argument.

We define the semantics of EXIT and RETURN with exceptions called the exit-exception and the return-exception. The exit-exception takes no argument; the return-exception takes an argument of arbitrary type. Programs cannot name these exceptions explicitly.

Implementations should speed up normal outcomes at the expense of exceptions (except for the return-exception and exit-exception). Expending a thousand instructions per exception raised to save one instruction per procedure call would be reasonable.

If an expression is evaluated as part of the execution of a statement, and the evaluation raises an exception, then the exception becomes the outcome of the statement.

The empty statement is a no-op. In this chapter, empty statements are written (*skip*).

11.5.1. Assignment

To specify the type-checking 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

  T <: U.
  U <: T and T is an array or a reference type other than ADDRESS. (This restriction is lifted in unsafe modules.)
  T and U are ordinal types with at least one member in common.

An expression e is assignable to a variable v if

  The type of e is assignable to the type of v.
  The value of e is a member of the type of v and is not a local procedure, and if it is an array, then it has the same shape as v.

The first point can be checked statically; the others generally require runtime checks. Because 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 because a runtime check cannot be performed on the assignment of raw addresses:

   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 *)

11.5.2. Procedure Calls

A procedure call has the form

   P(Bindings)

where P is a procedure-valued expression and Bindings is a list of keyword or positional bindings. A keyword binding has the form name := actual, where actual is an expression and name is an identifier. A positional binding has the form actual, where actual is an expression. When keyword and positional bindings are mixed in a call, the positional bindings must precede the keyword bindings. If the list of bindings is empty, the parentheses are still required.

The list of bindings is rewritten to fit the signature of P’s type as follows: First, each positional binding actual is converted and added to the list of keyword bindings by supplying the name of the ith formal parameter, where actual is the ith binding in Bindings. Second, for each parameter that has a default and is not bound after the first step, the binding name := default is added to the list of bindings, where name is the name of the parameter and default is its default value. The rewritten list of bindings must bind only formal parameters and must bind each formal parameter exactly once. For example, suppose that the type of P is

   PROCEDURE(ch: CHAR; n: INTEGER := 0)

Then the following calls are all equivalent:

   P(‘a’, 0)
   P(‘a’)
   P(ch := ‘a’)
   P(n := 0, ch := ‘a’)
   P(‘a’, n := 0)

The call P() is illegal because it doesn’t bind ch. The call P(n := 0, ‘a’) is illegal because it has a keyword parameter before a positional parameter.


Previous Table of Contents Next