The type computer is the central component when it comes to type inferrence for
expressions
.
Implementations will usually inherit from
XbaseTypeComputer
or
XbaseWithAnnotationsTypeComputer
and
add new expressions by means of overriding
computeTypes(XExpression, ITypeComputationState)
and adding
respective cases.
Implementations are responsible for triggering type inference for all child expressions.
There are three base idioms when implementing type inference for a given expression.
- Expression type is independent from expectations.
-
This is the simplest case. There is no need to take expectations into account.
The computed type is directly propagated to the current
state
.
An example for this are boolean literals.
void computeType(XBooleanLiteral literal, ITypeComputationState state) {
LightweightTypeReference bool = getTypeForName(Boolean.TYPE, state);
state.acceptActualType(bool);
}
The announced type is immediately propagated to all expectations. This is done by the
inference framework.
- Expression type depends on the expectation.
-
The expected type may steer the inferred type of an expression. Therefore the
state may be queried for the currently known expectations and the produced
type may be adjusted accordingly.
String literals are a good example for this mechanism. Literals with exactly one character
may be seen as character literals if the expected type suggests that. A simplified implementation
could look like this.
void computeType(XStringLiteral literal, ITypeComputationState state) {
if (literal.getValue().length() == 1) {
for(ITypeExpectation expectation: state.getExpectations() {
LightweightTypeReference expectedType = expectation.getExpectedType();
if (expectedType.isType(Character.TYPE) || expectedType.isType(Character.class)) {
expectation.acceptActualType(expectedType, ConformanceHint.SUCCESS, ConformanceHint.CHECKED);
} else {
LightweightTypeReference string = getTypeForName(String.class, state);
state.acceptActualType(string);
}
}
} else {
LightweightTypeReference string = getTypeForName(String.class, state);
state.acceptActualType(string);
}
}
- The type of an expression depends on its children.
-
Expression types may be inferred from the types of contained children. Therefore
the given computation state is directly used to compute their types. The framework
will automatically propagate the common type of all children to the parent.
If- or Switch expression fall into this category.
A simplified implementation would again look like this:
void computeTypes(XIfExpression expression, ITypeComputationState state) {
state.withExpectations(getTypeForName(Boolean.TYPE, state).computeTypes(expression.getIfExpression());
state.computeTypes(expression.getThen());
state.computeTypes(expression.getElse());
}
The If expression adjusts the expectation for one of its children and computes its type.
Afterwards, it directly reuses the current expectations to compute the types of its
branch expressions. The common super type of those is assigned to the If expression by
the framework.