Type analysis tasks


This module implements a set of concepts that are basic for
the type analysis task in many statically typed languages:
Definition and use of typed objects, notation of user defined
types, and definition and use of type identifiers.
The use of this module is demonstrated in
the following section, which intorduces a running example.
A complete executable specification for an example
is available in $/Type/Examples/Type.fw .
This module is instantiated by
$/Type/Typing.gnrc +referto=|KEY| :inst
The referto parameter modifies the names
of Key attributes, and hence, has to be the same as the
referto parameter used for the module instance that supplies
the key attributes.
The module uses the following basic concepts for implementation
of type analysis tasks:
Types are represented by DefTableKeys .
Such a key is created for each program construct which
denotes a particular type.
The roles of this module associate the property IsType
(set to 1) to such type keys in order to distinguish them
from keys for other objects.
Further properties may be associated to characterize the particular type.
NoKey represents the unknown Type.
The property TypeOf is associated to typed objects
and states the type of the object.
Identifiers may be defined to denote types. They may be
introduced by type definitions.
In case of a type definition which simply renames the type given
by a type identifier, it is assumed that both type identifiers
refer to the same type.
This module uses an instance of the Defer module
(see Deferred Property Association) to relate keys that represent the same type.
A type identifier key refers indirectly via the Defer relation
to a type key, where the type properties are associated to.
Let k be a type identifier; then a call TransDefer (k)
accesses the type key at the end of the Defer chain via
arbitrary many steps.
This module provides the following computational roles:
RootType :
is inherited by the grammar root by default.
TypedDefinition :
a definition that defines one or several typed objects.
TypedDefId :
a defining occurrence of an identifier for a typed object.
TypedUseId :
an applied occurrence of an identifier for a typed object.
ChkTypedUseId :
checks that the key represents a typed object.
TypeDefDefId :
a defining occurrence of a type identifer.
ChkTypeDefDefId :
checks that a type identifer is defined acyclic.
TypeDefUseId :
an applied occurrence of a type identifer.
ChkTypeDefUseId :
checks that the key represents a type.
TypeDenotation :
a type denotation.
At least the roles TypedDefinition ,
TypedDefId , TypedUseId ,
and TypeDefUseId
are used. Those are sufficient for languages that have only
predefined types. User defined types require additionally the use of
TypeDenotation .
For definitions of names that denote types
TypeDefDefId and ChkTypeDefDefId are necessary.
The roles are described as follows:
TypedDefinition
is a context where one or several typed objects are defined
in its subtree, using the role TypedDefId .
They all have the same type which is to be computed as
the attribute THIS.Type .
TypedDefId
is a defining occurrence of an identifier for a typed object.
The TypeOf property is associated to THIS.KEYKey .
It is obtained from the attribute INH.Type .
A default computation for INH.Type is provided
that takes the type from INCLUDING TypedDefinition.Type .
Further properties may be associated to the object along
with its type. Then the postcondition of their computation should
be INH.GotProp which is empty by default. It guarantees that
the property values can be accessed where the typed object is used.
An attribute SYNT.TypeIsSet represents the state
where the TypeOf property of this TypedDefId has been
set. The default computations of TypedDefId and TypedUseId
are set up such that the TypeOf properties of all
TypedDefId are set before any TypeOf property of
a TypedUseId is accessed. In cases where a different dependency
pattern is needed, it can be obtained by overriding the
computations of SYNT.TypeIsSet in the two contexts.
An example for a different dependency pattern is a left-to-right
chain which may allow to set the type in a defining occurrence using
applied occurrences that have been set before.
TypedUseId
is an applied occurrence of an identifier for a typed object.
The attribute SYNT.Type is the type associated to the object.
The Defer chain is already walked down to the type key.
See the explanation of the attribute SYNT.TypeIsSet
for the role TypedDefId .
ChkTypedUseId
checks that the key represents a typed object, and issues an error
message otherwise.
Types are denoted either by a used occurrence of a type identifier,
role TypeDefUseId , or by some composite TypeDenotation .
Either role provides a Type attribute representing the denoted
type.
TypeDefUseId
is an applied occurrence of a type identifier.
The Type attribute is its key which is related to the
type key by the Defer relation.
ChkTypeDefUseId
is usually inherited along with TypeDefUseId .
It issues an error message if the identifier does not
denote a type.
TypeDenotation
characterizes a context that denotes a user defined type,
e.g. an array type.
The attribute SYNT.Type is a new, unique type key.
If the type rules of the language state that a thus denoted
type is equivalent to any other type, suitable functions
that check for type equivalence have to be used.
Any property that further describes the type has
to be associated to SYNT.Type by computations that
establish the postcondition SYNT.GotType .
(Its default is the empty postcondition.)
It guarantees that the properties
can be accessed when an object having that type is used
(role TypedUseId ).
TypeDefDefId
is a defining occurrences of a type identifier.
Its key is related by the Defer relation to the
attribute THIS.Type . Its computation has to be provided.
ChkTypeDefDefId
checks that its attribute THIS.Type refers to a type,
and that the type does not refer to itself.
It is usually inherited along with TypeDefDefId .
RootType
is inherited by the grammar root by default.
It ensures that all TypeOf and Defer properties
are established when a Type attribute
is computed. (The same holds for properties that are defined
along with types.)
That condition is provided by the
condition attribute SYNT.GotType .
Computations that access type properties, e.g.
those which decompose composite types,
have to be specified to depend on INCLUDING RootType.GotType .
The use of the basic type module is demonstrated by introducing type analysis
to our running example.
A complete executable specification of our running example for the type
analysis task
is available in $/Type/Examples/Type.fw .
For the moment we assume the there are only three predefined
types int , float , and bool . They are represented
by the keys intType , realType , and boolType
as introduced in (see Predefined Identifiers of Name analysis according to scope rules).
We add a .pdl specification which states that those keys are types
and which defines the types of the constants true and false .
A voidType is introduced here although there is no predefined
identifier to denote it. It enables us to express that in certain
contexts type coercion to voidType is to be applied.
intType -> IsType = {1};
realType -> IsType = {1};
boolType -> IsType = {1};
voidType -> IsType = {1};
trueKey -> TypeOf = {boolType};
falseKey-> TypeOf = {boolType};
intKey -> Defer = {intType};
realKey -> Defer = {realType};
boolKey -> Defer = {boolType};
The last group relates the keys of the predefined type identifiers
to the type keys.
It is not recommended to use intKey etc. themselves
as type keys. In that case it would not be possible to have
different predefined type identifiers that could denote the same types.
The following association of module roles to grammar symbols
in our .lido specification is obvious:
SYMBOL DefIdent INHERITS TypedDefId END;
SYMBOL UseIdent INHERITS TypedUseId, ChkTypedUseId END;
SYMBOL TypeUseIdent INHERITS TypeDefUseId, ChkTypeDefUseId END;
Our language construct ObjDecl defines typed objects.
It specifies that the Type attribute is taken
from the TypeDenoter . Until now, a TypeDenoter
can have only one form: TypeUseIdent .
Its key is taken to represent the denoted type:
SYMBOL ObjDecl INHERITS TypedDefinition END;
RULE: ObjDecl ::= TypeDenoter DefIdent COMPUTE
ObjDecl.Type = TypeDenoter.Type;
END;
RULE: TypeDenoter ::= TypeUseIdent COMPUTE
TypeDenoter.Type = TypeUseIdent.Type;
END;
ATTR Type: DefTableKey;
The last line allows us to use Type attributes without
specifying their type repeatedly.
The types of literal expressions are then specified by
RULE: Expression ::= IntNumber COMPUTE
Expression.Type = intType;
END;
RULE: Expression ::= RealNumber COMPUTE
Expression.Type = realType;
END;
Computations like the following propagate type information
upwards through adjacent contexts:
RULE: Expression ::= Variable COMPUTE
Expression.Type = Variable.Type;
END;
RULE: Variable ::= UseIdent COMPUTE
Variable.Type = UseIdent.Type;
END;
Occurrences of Expression in certain contexts require
that their type is compatible to a type imposed by the context.
Hence, we introduce an inherited attribute ReqType and a check
for compatibility.
The two rules demonstrate how such type requirements are imposed:
SYMBOL Expression: ReqType: DefTableKey;
SYMBOL Expression COMPUTE
IF (NOT (Compatible (THIS.ReqType, THIS.Type)),
message (FATAL, "expression does not have the required type",
0, COORDREF));
END;
RULE: Statement ::= Expression ';' COMPUTE
Expression.ReqType = voidKey;
END;
RULE: Statement ::= Variable '=' Expression ';' COMPUTE
Expression.ReqType = Variable.Type;
END;
The function Compatible needs to be
implemented according to the type rules of the particular language.
(An example implementation is shown in $/Type/Examples/Type.fw ).
We now introduce type definitions to our language.
The concrete production is
Declaration: 'type' TypeDenoter TypeDefIdent ';'.
TypeDefIdent: Ident.
From the view of language design such type definitions only make sense
if there are other than the predefined types that can be given a name.
In (see Properties of Types) it is demonstrated how to introduce
some common types.
The TypeDefIdent has the role of a defining type identifier
occurrence that renames the type of the TypeDenoter :
SYMBOL TypeDefIdent INHERITS
IdDefScope, IdentOcc,
TypeDefDefId, ChkTypeDefDefId
END;
RULE: Declaration ::= 'type' TypeDenoter TypeDefIdent ';' COMPUTE
TypeDefIdent.Type = TypeDenoter.Type;
END;


|