Next: , Previous: Overloading, Up: Top


6 Changing the Type of a Value

Most language definitions allow a user to supply operands of one type to an operator requiring operands of another under certain circumstances. For example, it would be reasonable for the extended version of Mystery (see Overloaded Indications and Procedures) to allow an addition with one integer and one real operand. In that case, analysis computation would assume a coercion operator to convert the value of the integer operand to a real value. The rAddOpr would then be applied to the result of the coercion and the original real operand.

The Expression module uses applicable coercions automatically, provided that one or more coercion operators have been defined. Like normal operators, the language-defined coercions can be specified in the file whose name is the referto parameter of the PreDefOp module (see Language-Defined Operators).

PreDefCvt(`opr',`sig'), specifies a coercion:

`opr'
The known definition table key representing the name of the coercion operator.
`sig'
The signature of the specified coercion operator (which must have exactly one operand).

PreDefCvt and PreDefOpr (see Relating operators to indications) invocations with the same `opr' values are not allowed.

A coercion from integer to real for the extended Mystery can be specified by:

     PreDefCvt(iTor, (intType):realType);

The known key iTor is defined by the module. No further specification is necessary.

A coercion defines an edge of the coercion graph, a directed graph whose nodes correspond to types used in the program. If the coercion graph contains cycles, then it is difficult to describe the semantics of an expression. For example, suppose that we defined a coercion from real values to integer values in addition to iTor, creating a cycle in the coercion graph. Given that cycle, what is the effect of the following expression?

     1.2 + 3

The extended Mystery has only two operators for which PlusInd is the indication: iAddOp and rAddOp. Should we coerce 1.2 to an integer value and use iAddOp, or coerce 3 to a real value and use rAddOp?

Of course we could define the language in such a way that this decision is unambiguous, and guarantee the appropriate coercions by defining them with appropriate costs (see Overload resolution), but the definition would be more complex because of the cycle. Thus we recommend that the coercion graph not contain cycles.

A cast context is an expression context in which a specific type is required. For example, consider the extended Mystery program:

     VAR i: INTEGER; VAR a: REAL;
     BEGIN i := 1; i := 2.3; a := 4; a := 5.6 END;

In each assignment, the type of the left-hand side determines the type that must be yielded by the right-hand side (see Values).

CastContext(`type',`rator',`expr') is used to provide the necessary type computations: If a result of type `type' cannot be obtained directly from the tree rooted in `expr', a cast operator can be selected from those overloading the indication `rator'. Argument `type' is a definition table key, `rator' is a grammar symbol playing the OperatorSymbol role, and `expr' is a grammar symbol playing the ExpressionSymbol role.

Here is the definition of assignment for extended Mystery:

     RULE: Stmt ::= Expr ':=' Expr
     COMPUTE
       CastContext(Expr[1].Type,,Expr[2]);
       Indication(castInd);
       ChkRator;
     END;

Consider how the type analysis would proceed for each of the assignments in our extended Mystery program:

i := 1
The right-hand side delivers the desired type, so no conversion is required.
i := 2.3
The right-hand side delivers a real value, which cannot be coerced to the desired integer type. Thus an operator identified by the castInd indication must be available to provide the conversion.
a := 4
The right-hand side delivers the desired type, due to a coercion operator. Thus no additional conversion is required.
a := 5.6
The right-hand side delivers the desired type, so no conversion is required.

The cast operator might be defined by:

     PreDefOpr(castInd, rToi, (realType):intType)

Many languages also provide explicit cast contexts, which break the normal type relationship between two adjacent expression nodes (see Values). Here is an example, using C syntax:

     RULE: Expr ::= '(' Type ')' Expr
     COMPUTE
       PrimaryContext(Expr[1],Type.Type);
       CastContext(Type.Type,,Expr[2]);
       Indication(castInd);
       ChkRator;
     END;

The type of Expr[1] is known to be of the type Type, and therefore Expr[1] acts as a leaf of the expression tree containing it. Expr[2], on the other hand, acts as the root of an expression tree: the type of value it should produce is known.