assert mArgExprs.size() == abstractArgTypes.size();
// Check that each expression type can promote to the argument type.
for (int i = 0; i < mArgExprs.size(); i++) {
Type exprType = mArgExprs.get(i).getType(symTab);
mExprTypes.add(exprType);
if (!exprType.promotesTo(abstractArgTypes.get(i))) {
throw new TypeCheckException("Invalid argument to function " + mFunctionName
+ ": argument " + i + " has type " + exprType + "; requires type "
+ abstractArgTypes.get(i));
}
}
mArgTypes = new Type[mArgExprs.size()];
// Now identify all the UniversalType instances in here, and the
// actual constraints on each of these.
Map<UniversalType, List<Type>> unifications = new HashMap<UniversalType, List<Type>>();
for (int i = 0; i < abstractArgTypes.size(); i++) {
Type abstractType = abstractArgTypes.get(i);
Type actualType = mExprTypes.get(i);
UniversalConstraintExtractor constraintExtractor = new UniversalConstraintExtractor();
if (constraintExtractor.extractConstraint(abstractType, actualType)) {
// Found a UniversalType. Make sure it's mapped to a list of actual constraints.
UniversalType univType = constraintExtractor.getUniversalType();
List<Type> actualConstraints = unifications.get(univType);
if (null == actualConstraints) {
actualConstraints = new ArrayList<Type>();
unifications.put(univType, actualConstraints);
}
// Add the actual constraint of the expression being applied as this argument.
actualConstraints.add(constraintExtractor.getConstraintType());
}
}
// Perform unifications on all the UniversalType expressions.
Map<Type, Type> unificationOut = new HashMap<Type, Type>();
for (Map.Entry<UniversalType, List<Type>> unification : unifications.entrySet()) {
UniversalType univType = unification.getKey();
List<Type> actualConstraints = unification.getValue();
Type out = univType.getRuntimeType(actualConstraints);
unificationOut.put(univType, out);
}
// Finally, generate a list of concrete argument types for coercion purposes.
for (int i = 0; i < abstractArgTypes.size(); i++ ) {
Type abstractType = abstractArgTypes.get(i);
mArgTypes[i] = abstractType.replaceUniversal(unificationOut);
assert mArgTypes[i] != null;
}
// Also set mReturnType; if this referenced a UniversalType, use the resolved
// version. Otherwise, use the version from the function directly.
Type fnRetType = mFnSymbol.getReturnType();
try {
mReturnType = fnRetType.replaceUniversal(unificationOut);
} catch (TypeCheckException tce) {
// We can only resolve against our arguments, not our caller's type.
if (fnRetType instanceof ListType) {
// If the unresolved typevar is an argument to a list type, we can
// return this -- it's going to be an empty list, so we can return