3.7 Type Checking Expressions, Statements, and Declarations
Type checking an expression or statement determines whether it is type-correct, and if type-correct also determines the type of its result. Type checking a declaration simply checks for type correctness. All constructs are type-checked in a typing context containing the following information:
- a binding for each variable or object name in scope to either:
- the variable's declared type and an indication of whether the variable binding is assignable or constant, or
- to an object with a role annotation and a set of conformed-to types.
- the set of inheritance relations currently in scope;
- a binding for each type name in scope to the corresponding type;
- the set of subtyping relations currently in scope;
- the set of signatures currently in scope (for type checking messages);
- the set of method declarations currently in scope (for type checking resends).
The type checking rules for expressions are as follows:
- A literal constant is always type-correct. The type of the result of a literal constant is the corresponding predefined type.
- A reference name is type-correct iff name is defined in the typing context (i.e., if there exists a declaration of that name earlier in the same scope or in a lexically-enclosing scope) as either a variable or an object. If a variable, then the reference is type-correct, with the type of the result being the associated type of the variable in the typing context. If an object, then the reference is type-correct iff the object is a concrete or dynamic object, with the type of the result being the type of the named object.
- An object constructor expression of the general form
role-annotation object inherits parent1, ..., parentK
subtypes supertype1, ..., supertypeL
isa parent-and-supertype1, ..., parent-and-supertypeM
{ field1@obj1 := expr1, ..., fieldN@objN := exprN }
- is type-correct iff:
- each parenti name is bound to a non-abstract non-
void object in the typing context;
- each supertypei notates a type other than
none in the current typing context;
- each parent-and-supertypej name is bound to a non-abstract non-
void object in the typing context;
- if
@obji is present, then obji names an ancestor of the newly created object (if absent, it is considered to be the same as the newly created object);
- each fieldi names a field Fi specialized on or inherited unambiguously by obji, ignoring any overriding methods, and Fi is not
shared;
- each expri is type-correct, returning an object of static type Ti, and Ti is a subtype of the type of the contents of the field Fi;
- no field Fi is initialized more than once;
- role-annotation is neither
abstract nor template; and
- if role-annotation is
concrete, then there do not exist any fields specialized on or inherited by the newly created object that do not have a default initial value and are not initialized as part of the object creation expression.
- The
representation keyword may be used in place of the object keyword without effect. The type of the result of an object constructor expression is a new anonymous type that is a subtype of each of the supertypei types and each of the types of the parent-and-supertypej objects.
- A closure constructor expression of the general form
&(x1:type1, ..., xN:typeN):typeR { body }
- is type-correct iff:
- the xi, where provided, are distinct;
- each of the typei, if provided, notates a non-
void type in the current typing context;
- typeR, if provided, notates a type in the current typing context;
- body is type-correct, checked in a typing context constructed by extending the current typing context with constant variable bindings for each of the xi to the corresponding type typei; and
- the type of the result of body is a subtype of typeR, if provided; if
:typeR is omitted, then typeR is inferred to be the type of the result of body.
- The type of the result of a closure constructor expression of the above form is
&(type1, ..., typeN):typeR.
- A vector constructor expression of the general form
[expr1,...,exprN] type-correct iff each of the expri is type-correct, with static type Ti which is not void. The type of the result of a vector constructor expression is the predefined parameterized type i_vector instantiated with the least upper bound of the Ti. (See section 4 for information on parameterized types.)
- A message expression of the general form name(expr1
, ..., exprN) is type-correct iff:
- each of the expri is type-correct, with static type Ti which is not
void;[13] and
- the set S = {S1, ..., SM} of applicable signatures is non-empty, where S is the set of signatures in the current typing context of the form Si = signature name
(ti1, ..., tiN):tiR where each Ti is a subtype of ti.
- The type of the result of a message is the greatest lower bound of each of the result types tiR of the applicable signatures. Verifying correctness of the implementation of signatures is described in subsection 3.6.2.
- A resend expression of the general form
resend(..., xi@parenti, ..., exprj, ...)
- is type-correct iff:
- each of the arguments xi or expri is type-correct, with static type Ti which is not
void;
- the resend is nested textually in the body of a method M;
- M takes the same number of arguments, N, as does the resend;
- for each specialized formal parameter formali of M, specialized on objecti, the ith argument to the resend is formali, possibly suffixed with
@parenti, and formali is not shadowed with a local variable of the same name;
- for each unspecialized formal parameter formalj of M, the jth argument to the resend is not be suffixed with
@parentj;
- for each resend argument of the form formali
@parenti, parenti is a proper ancestor of objecti, the specializer of formali, other than void; and
- when method lookup is simulated with a message name the same as M and with N arguments, where argument i is either
any (if formali of M is unspecialized), parenti (if the argument of the resend is directed using the @parenti suffix notation), or objecti, the specializer of formali (otherwise), and where the resending method M is removed from the set of applicable methods in the current typing context, exactly one most-specific target method R is located, and the argument type declarations of this target method Si are supertypes of the corresponding Ti.
- The type of the result of a resend expression is the declared result type of the target method R.
- A parenthetical expression of the form
( body ) is type-correct iff body is type-correct. The type of the result of a parenthetical expression is the type of the result of body.
The following rules define type-correctness of statements:
- An assignment statement of the form name
:= expr is type-correct iff:
- expr is type-correct, with static type Texpr;
- name is bound to an assignable variable of type Tname in the current typing context; and
- Texpr is a subtype of Tname.
- The type of the result of an assignment statement is
void.
- A declaration block is type-correct iff its declarations are type-correct, when checked in a typing context where all names in the declaration block are available throughout the declaration block. The type of the result of a declaration block is
void.
- An expression statement is type-correct iff the expression is type-correct, with static type T. The type of the result of the expression statement is T.
- A non-local return statement, of the form
^ expr, is type-correct iff:
- expr is type-correct, with static type T;
- the non-local return statement is nested textually inside the body of a method M; and
- T is a subtype of the declared result type of M.
- The type of the (local) result of a non-local return is
none.
The body of a method, closure, or parenthetical expression is type-correct iff its statements are type-correct. The type of the result of a body is the type of its last statement, if present, or void, otherwise.
The following rules define type-correctness of declarations:
- A variable declaration of the form let var name
:type := expr, where var is either var or empty, is type-correct iff:
- name is not otherwise defined in the same scope;
- type notates a type in the current typing context; and
- expr is type-correct in a typing context where name and all variables defined later in the same declaration block are unbound, resulting in static type T, and T is a subtype of type.
- The typing context is extended to include a variable binding for name to the type type that is assignable if var is
var and constant otherwise.
- A type declaration of the form
type name subtypes supertype1, ..., supertypeN
- is type-correct iff each of the supertypei notates a type other than
none in the current typing context and no cycles are introduced into the subtyping graph as a result of the declaration. As a result of the declaration, the typing context is extended to include a type binding from name to a new type that is a subtype of each of the supertypei types.
- A representation declaration of the form
role-annotation kind name inherits parent1, ..., parentK
subtypes supertype1, ..., supertypeL
isa parent-and-supertype1, ..., parent-and-supertypeM
{ field1@obj1 := expr1, ..., fieldN@objN := exprN }
- is type-correct under the same conditions as the analogous object constructor expression, with the following changes:
- abstract objects may be named in
inherits and isa clauses;
- the
abstract and template role annotations are allowed; and
- no cycles are allowed to be introduced into the inheritance and subtyping graphs.
- The typing context is extended to include an object binding from name to a new object with role role-annotation that inherits from the parenti objects and the parent-and-supertypej objects. If kind is the
representation keyword, then the new object conforms to the supertypek types. Otherwise, kind is the keyword object, and the typing context is also extended with a type binding from name to a new type that is a subtype of each of the supertypei types, and the new object conforms to the new type.
- A type extension declaration of the form
extend type name subtypes supertype1, ..., supertypeN
- is type-correct iff:
- name is bound in the typing context to a type other than
void, any, none, and dynamic; and
- the same constraints on the
subtypes clause as with the type declaration are satisfied.
- As a result of the declaration, the typing context is extended to reflect that the type name is a subtype of each of the supertypei types.
- A representation extension declaration of the form
extend kind name inherits parent1, ..., parentK
subtypes supertype1, ..., supertypeL
isa parent-and-supertype1, ..., parent-and-supertypeM
{ field1@obj1 := expr1, ..., fieldN@objN := exprN }
- is type-correct iff:
- name is bound in the typing context to an object other than
void and any;
- if kind is
object or omitted, then name also is bound in the typing context to a type other than void, any, none, and dynamic;
- the same constraints on the
inherits, subtypes, isa, and field initialization clauses as with the object representation declaration are satisfied; and
- none of the fieldi
@obji initialize fields already specialized on or inherited by the object before the extension.
- As a result of the declaration, the typing context is extended to reflect that the object name inherits from the parenti objects and the parent-and-supertypej objects. If kind is the
representation keyword, then the typing context is extended to reflect that the object conforms to the supertypek types. Otherwise, kind is the keyword object, and the typing context is extended to reflect that the name type is a subtype of each of the supertypei types and that the name object conforms to the name type.
- A signature declaration of the form
signature name(x1:type1,...,xN:typeN):typeR
- is type-correct iff:
- the xi, when provided, are distinct;
- each of the typei notates a type other than
void in the typing context; and
- typeR notates a type in the typing context.
- The typing context is extended to include the corresponding signature.
- A field signature declaration of the form
var field signature name(x:type):typeR
- is type-correct iff:
- type notates a type other than
void in the typing context; and
- typeR notates a type other than
void in the typing context.
- The typing context is extended to include the signature
signature name(type):typeR
- and, if var is
var, the signature
signature set_name(type,typeR):void
- A method implementation declaration of the general form
method kind name(x1@obj1:type1,...,xN@objN:typeN):typeR { body }
- is type-correct iff:
- the xi, when provided, are distinct;
- each of the typei notates a type other than
void in the typing context;
- if
@obji is present, then obji conforms to typei;
- typeR notates a type in the typing context;
- body is type-correct when checked in a typing context constructed by extending the current typing context with constant variable bindings for each of the xi to the corresponding type typei; and
- the type of the result of body is a subtype of typeR.
- The typing context is extended to include the declared method implementation. If kind is not
implementation, then the typing context is also extended to include the signature
signature name(type1,...,typeN):typeR
- A field implementation declaration of the general form
shared var field kind name(x@obj:type):typeR := expr;
- is type-correct iff:
- type notates a type other than
void in the typing context;
- if
@obj is present, then obj conforms to type;
- typeR notates a type other than
void in the typing context;
- if
:= expr is provided, then expr is type-correct, with static type T, and T is a subtype of typeR; and
- if shared is
shared, then := expr is provided.
- The typing context is extended to include the declared field get accessor method implementation, plus the set accessor method implementation if var is
var, plus the get (and possibly set) signature(s) if kind is not implementation.