/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* TypeCons.java
* Created: Sep 24, 2003
* By: Bo Ilic
*/
package org.openquark.cal.compiler;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import org.openquark.cal.internal.serialization.ModuleSerializationTags;
import org.openquark.cal.internal.serialization.RecordInputStream;
import org.openquark.cal.internal.serialization.RecordOutputStream;
import org.openquark.cal.module.Cal.Core.CAL_Prelude;
/**
* Models the application of a type constructor (such as Prelude.Boolean or Prelude.Function) to 0 or more type arguments.
*
* The application need not be fully saturated. For example, Prelude.Either could be applied to 0, 1 or 2 arguments (in a
* well-typed type expression).
*
* @author Bo Ilic
*/
public final class TypeConsApp extends TypeExpr {
private static final int serializationSchema = 0;
/** The type constructor at the head or root of this application. cannot be null. */
private TypeConstructor rootTypeCons;
/**
* type arguments applied to the rootTypeCons. For example, if this is [e1, e2, ..., en] then the type represented
* is (((rootTypeCons e1) e2) ... en), or, since type application is left associative, (rootTypeCons e1 e2 ... en).
* Might not be saturated i.e. args.length might be less than the type arity.
* Cannot be null. The elements of the args array also cannot be null.
*/
private TypeExpr[] args;
private static final TypeExpr[] NO_ARGS = new TypeExpr[0];
TypeConsApp(TypeConstructor rootTypeCons, TypeExpr[] args) {
if (rootTypeCons == null) {
throw new NullPointerException("TypeConsApp constructor: the argument 'rootTypeCons' cannot be null.");
}
this.rootTypeCons = rootTypeCons;
if (args == null || args.length == 0) {
this.args = TypeConsApp.NO_ARGS;
} else {
if (TypeExpr.hasNullType(args)) {
throw new NullPointerException();
}
this.args = args;
}
}
/** private constructor used by serialization code. */
private TypeConsApp(TypeConstructor rootTypeCons, int nArgs) {
if (rootTypeCons == null) {
throw new NullPointerException("TypeConsApp constructor: the argument 'rootTypeCons' cannot be null.");
}
this.rootTypeCons = rootTypeCons;
this.args = nArgs != 0 ? new TypeExpr[nArgs] : TypeConsApp.NO_ARGS;
}
/**
* Returns the internal CAL name of the (root) type constuctor. For some of the built-in types that
* have special syntax in the CAL source code, this name is purely internal.
* Note for e.g. "Either Int Boolean" or "Either Char Double" this will return "Either".
* @return the internal CAL name of the type constructor
*/
public QualifiedName getName() {
return rootTypeCons.getName();
}
/**
* @return the root type constructor e.g. for "Either Int Boolean" or "Either Char Double" this
* would be the type constructor for "Either".
*/
public TypeConstructor getRoot() {
return rootTypeCons;
}
/**
* Method getArg.
* @param argN the zero-based index argument number
* @return TypeExpr the type of the argN type argument
*/
public TypeExpr getArg(int argN) {
return args[argN];
}
/**
* @return the number of type parameters for this type constructor application.
* Note that the root type constructor may not be fully saturated
* so that e.g. Prelude.Either will not necessarily have 2 type arguments,
* even in correctly type checking code.
*/
public int getNArgs() {
return args.length;
}
TypeExpr[] getArgs(){
return args;
}
void setArg(int i, TypeExpr typeExpr) {
args[i] = typeExpr;
}
/** {@inheritDoc} */
@Override
TypeExpr prune() {
return this;
}
/** {@inheritDoc} */
@Override
TypeExpr deepPrune() {
// Make sure that the args are deep pruned.
for (int i = 0, nArgs = getNArgs(); i < nArgs; ++i) {
args[i] = args[i].deepPrune();
}
return this;
}
/** {@inheritDoc} */
@Override
TypeExpr normalize() {
// Make sure that the args are normalized.
final int nArgs = getNArgs();
TypeExpr[] normalizedArgs = new TypeExpr[nArgs];
for (int i = 0; i < nArgs; ++i) {
normalizedArgs[i] = args[i].normalize();
}
return new TypeConsApp(rootTypeCons, normalizedArgs);
}
@Override
public boolean sameType(TypeExpr anotherTypeExpr) {
anotherTypeExpr = anotherTypeExpr.prune();
if (anotherTypeExpr instanceof TypeConsApp) {
TypeConsApp anotherTypeConstructorApp = (TypeConsApp)anotherTypeExpr;
if (this.rootTypeCons == anotherTypeConstructorApp.rootTypeCons) {
int thisNArgs = this.getNArgs();
if (thisNArgs == anotherTypeConstructorApp.getNArgs()) {
if (thisNArgs == 0) {
return true;
}
//we've failed with the quick checks. Need a more expensive test.
return this.toString().equals(anotherTypeExpr.toString());
}
//if the 2 type constructors have different numbers of args, they will produce different string representations
return false;
}
//if the 2 type constructors have different roots (which can be compared using referential equality) then they are different.
return false;
}
if (anotherTypeExpr instanceof TypeVar ||
anotherTypeExpr instanceof RecordType) {
return false;
}
if (anotherTypeExpr instanceof TypeApp) {
//For example,
//(TypeConsApp List [Int]) is the same type as (TypeApp (TypeConsApp List []) Int)
//we could determine this a bit more efficiently, but the case of TypeApp is sufficiently rare that it is not
//worth bothering.
return this.toString().equals(anotherTypeExpr.toString());
}
throw new IllegalStateException();
}
/** {@inheritDoc} */
@Override
void getGenericClassConstrainedPolymorphicVars(Set<PolymorphicVar> varSet, NonGenericVars nonGenericVars) {
for (int i = 0, nArgs = getNArgs(); i < nArgs; ++i) {
getArg(i).getGenericClassConstrainedPolymorphicVars(varSet, nonGenericVars);
}
}
/** {@inheritDoc} */
@Override
void getUninstantiatedTypeVars(Set<TypeVar> varSet) {
for (int i = 0, nArgs = getNArgs(); i < nArgs; ++i) {
getArg(i).getUninstantiatedTypeVars(varSet);
}
}
/** {@inheritDoc} */
@Override
void getUninstantiatedRecordVars(Set<RecordVar> varSet) {
for (int i = 0, nArgs = getNArgs(); i < nArgs; ++i) {
getArg(i).getUninstantiatedRecordVars(varSet);
}
}
/** {@inheritDoc} */
@Override
public TypeExpr getCorrespondingTypeExpr(TypeExpr correspondingSuperType, TypeExpr typeToFind) {
if (this == typeToFind) {
return correspondingSuperType;
}
// Search the args
TypeConsApp correspondingTypeConsApp = correspondingSuperType.rootTypeConsApp();
if (correspondingTypeConsApp == null ||
!getRoot().getName().equals(correspondingTypeConsApp.getRoot().getName())) {
return null;
}
for (int i = 0, nArgs = getNArgs(); i < nArgs; ++i) {
TypeExpr correspondingType = getArg(i).getCorrespondingTypeExpr(correspondingTypeConsApp.getArg(i), typeToFind);
if (correspondingType != null) {
return correspondingType;
}
}
return null;
}
/** {@inheritDoc} */
@Override
void toSourceText(StringBuilder sb,
PolymorphicVarContext polymorphicVarContext,
ParenthesizationInfo parenthesizationInfo,
ScopedEntityNamingPolicy namingPolicy) {
String typeConsName = namingPolicy.getName(rootTypeCons);
final int nArgs = getNArgs();
if (nArgs == 0) {
if (getName().equals(CAL_Prelude.TypeConstructors.Unit)) {
sb.append("()");
return;
}
sb.append(typeConsName);
return;
}
if (nArgs == 2 && getName().equals(CAL_Prelude.TypeConstructors.Function)) {
TypeExpr lhs = getArg(0);
TypeExpr rhs = getArg(1);
final boolean reallyParenthesize =
parenthesizationInfo == ParenthesizationInfo.ARG_OF_TEXTUAL_TYPE_CONS_OR_VAR ||
parenthesizationInfo == ParenthesizationInfo.DOMAIN_OF_FUNCTION;
if (reallyParenthesize) {
sb.append('(');
}
lhs.toSourceText(sb, polymorphicVarContext, ParenthesizationInfo.DOMAIN_OF_FUNCTION, namingPolicy);
sb.append(" -> ");
rhs.toSourceText(sb, polymorphicVarContext, ParenthesizationInfo.NONE, namingPolicy);
if (reallyParenthesize) {
sb.append(')');
}
return;
}
if (nArgs == 1 && getName().equals(CAL_Prelude.TypeConstructors.List)) {
sb.append('[');
getArg(0).toSourceText(sb, polymorphicVarContext, ParenthesizationInfo.NONE, namingPolicy);
sb.append(']');
return;
}
// if here: type constructor application
final boolean reallyParenthesize = parenthesizationInfo == ParenthesizationInfo.ARG_OF_TEXTUAL_TYPE_CONS_OR_VAR;
if (reallyParenthesize) {
sb.append('(');
}
sb.append(typeConsName);
for (int i = 0; i < nArgs; ++i) {
sb.append(' ');
getArg(i).toSourceText(sb, polymorphicVarContext, ParenthesizationInfo.ARG_OF_TEXTUAL_TYPE_CONS_OR_VAR, namingPolicy);
}
if (reallyParenthesize) {
sb.append(')');
}
}
/** {@inheritDoc} */
@Override
SourceModel.TypeExprDefn makeDefinitionSourceModel(PolymorphicVarContext polymorphicVarContext, ScopedEntityNamingPolicy namingPolicy) {
final int nArgs = getNArgs();
if (nArgs == 0) {
if (getName().equals(CAL_Prelude.TypeConstructors.Unit)) {
return SourceModel.TypeExprDefn.Unit.make();
}
final ModuleName typeConsModuleName = namingPolicy.getModuleNameForScopedEntity(rootTypeCons);
SourceModel.Name.TypeCons typeConsName =
SourceModel.Name.TypeCons.make(typeConsModuleName, rootTypeCons.getName().getUnqualifiedName());
return SourceModel.TypeExprDefn.TypeCons.make(typeConsName);
}
if (nArgs == 2 && getName().equals(CAL_Prelude.TypeConstructors.Function)) {
return SourceModel.TypeExprDefn.Function.make(
getArg(0).makeDefinitionSourceModel(polymorphicVarContext, namingPolicy),
getArg(1).makeDefinitionSourceModel(polymorphicVarContext, namingPolicy));
}
if (nArgs == 1 && getName().equals(CAL_Prelude.TypeConstructors.List)) {
return SourceModel.TypeExprDefn.List.make(getArg(0).makeDefinitionSourceModel(polymorphicVarContext, namingPolicy));
}
// if here: type constructor application
SourceModel.TypeExprDefn[] typeExprs = new SourceModel.TypeExprDefn[nArgs + 1];
final ModuleName typeConsModuleName = namingPolicy.getModuleNameForScopedEntity(rootTypeCons);
typeExprs[0] = SourceModel.TypeExprDefn.TypeCons.make(typeConsModuleName, rootTypeCons.getName().getUnqualifiedName());
for (int i = 0; i < nArgs; i++) {
typeExprs[i+1] = getArg(i).makeDefinitionSourceModel(polymorphicVarContext, namingPolicy);
}
return SourceModel.TypeExprDefn.Application.make(typeExprs);
}
/** {@inheritDoc} */
@Override
public int getArity() {
if (getName().equals(CAL_Prelude.TypeConstructors.Function)) {
//it is incorrect to assume that Prelude.Function is fully saturated.
if (getNArgs() == 2) {
TypeExpr rhs = getArg(1);
return 1 + rhs.getArity();
} else {
//a non-fully saturated (or oversaturated, which will result in a type error) application of Function
return 0;
}
}
return 0;
}
/**
* @return ForeignTypeInfo information about the Java type corresponding to this foreign type if
* this is a foreign type, or null if not a foreign type.
*/
public ForeignTypeInfo getForeignTypeInfo() {
return getRoot().getForeignTypeInfo();
}
/** {@inheritDoc} */
@Override
int unifyType(TypeExpr anotherTypeExpr, ModuleTypeInfo contextModuleTypeInfo) throws TypeException {
if (anotherTypeExpr instanceof TypeVar) {
return TypeExpr.unifyType(anotherTypeExpr, this, contextModuleTypeInfo);
} else if (anotherTypeExpr instanceof TypeConsApp) {
TypeConsApp typeConsApp = (TypeConsApp) anotherTypeExpr;
if (getName().equals(typeConsApp.getName())) {
//get 1 type closeness point for an exact match of type constructors +
//the type closeness measure contributed by the children.
return TypeConsApp.unifyArgs(args, typeConsApp.args, contextModuleTypeInfo) + 1;
}
throw new TypeException("Type clash: type constructor " + getName() + " does not match " + typeConsApp.getName() + ".");
} else if (anotherTypeExpr instanceof TypeApp) {
TypeApp typeApp = (TypeApp)anotherTypeExpr;
//(TypeCons e1 e2 ... en-1 en) unifies with (a1 a2) by
//1) first check that n >= 1
//2) next unify (TypeCons e1 e2 ... en-1) with a1
//3) finally unify en and a2.
final int nArgs = args.length;
//must have at least a single argument to unify with a type application
if (nArgs == 0) {
throw new TypeException("Type clash: typeConstructor " + getName() + " does not match a type application.");
}
// the type constructor expression, with its final argument dropped.
TypeExpr[] partialArgs = new TypeExpr[nArgs - 1];
System.arraycopy(args, 0, partialArgs, 0, nArgs - 1);
TypeConsApp partialTypeConsApp = new TypeConsApp(rootTypeCons, partialArgs);
int typeCloseness = TypeExpr.unifyType(partialTypeConsApp, typeApp.getOperatorType(), contextModuleTypeInfo);
TypeExpr finalArg = args[nArgs - 1];
return typeCloseness + TypeExpr.unifyType(finalArg, typeApp.getOperandType(), contextModuleTypeInfo);
} else if (anotherTypeExpr instanceof RecordType) {
throw new TypeException("Type clash: type constructor " + getName() + " does not match a record type.");
}
throw new IllegalStateException();
}
/**
* Unifies 2 argument lists. In particular, the 2 argument lists must have the same length, and must
* unify element by element.
* @param args1
* @param args2
* @param contextModuleTypeInfo
* @return int
* @throws TypeException
*/
private static int unifyArgs(TypeExpr[] args1, TypeExpr[] args2, ModuleTypeInfo contextModuleTypeInfo) throws TypeException {
int nArgs = args1.length;
if (nArgs != args2.length) {
throw new TypeException("Type clash- different number of type arguments");
}
int typeCloseness = 0;
for (int i = 0; i < nArgs; ++i) {
typeCloseness += TypeExpr.unifyType(args1[i], args2[i], contextModuleTypeInfo);
}
return typeCloseness;
}
/** {@inheritDoc} */
@Override
void patternMatch(TypeExpr anotherTypeExpr, PatternMatchContext patternMatchContext) throws TypeException {
if (anotherTypeExpr instanceof TypeVar) {
TypeVar anotherTypeVar = (TypeVar) anotherTypeExpr;
//anotherTypeVar must be uninstantiated.
if (anotherTypeVar.getInstance()!= null) {
throw new IllegalArgumentException("TypeConsApp.patternMatch: programming error.");
}
//Can't instantiate a type variable from the declared type expression.
//For example, this error occurs with the following declaration.
//funnyHead :: [a] -> Prelude.Char;
//public funnyHead = List.head;
if (patternMatchContext.isUninstantiableTypeVar(anotherTypeVar)) {
throw new TypeException("Attempt to instantiate a type variable from the declared type.");
}
//can't pattern match a nongeneric type variable to a type constructor involving uninstantiated type variables.
//For example, for the function:
//g x = let y :: [a]; y = x; in y;
//we attempt to instantiate a non-generic type variables (corresponding to x) to [a].
//It is OK though to instantiate to a concrete type e.g.
//g x = let y :: [Int]; y = x; in y;
NonGenericVars nonGenericVars = patternMatchContext.getNonGenericVars();
if (nonGenericVars != null &&
!nonGenericVars.isGenericTypeVar(anotherTypeVar) &&
!this.getUninstantiatedTypeVars().isEmpty()) {
throw new TypeException("Attempt to match a non-generic type variable to a type involving type variables.");
}
// check for infinite types e.g. an attempt to pattern match a to (a->Int).
if (containsUninstantiatedTypeVar(anotherTypeVar)) {
throw new TypeException("Type clash: attempt to create an infinite type.");
}
if (anotherTypeVar.noClassConstraints()) {
anotherTypeVar.setInstance(this);
return;
}
//type variable with constraints needs more complicated handling
TypeConstructor typeCons = this.rootTypeCons;
final int nArgs = this.getNArgs();
List<SortedSet<TypeClass>> argConstraints = new ArrayList<SortedSet<TypeClass>>(nArgs);
for (int i = 0; i < nArgs; ++i) {
argConstraints.add(i, TypeClass.makeNewClassConstraintSet());
}
for (final TypeClass typeClass : anotherTypeVar.getTypeClassConstraintSet()) {
ClassInstance classInstance = patternMatchContext.getModuleTypeInfo().getVisibleClassInstance(typeClass, rootTypeCons);
if (classInstance == null) {
throw new TypeException("Type clash: type " + toString() + " is not a member of type class " + typeClass.getName() + ".");
}
//For example, this could be (Eq a, Eq b) => Tuple2 a b
TypeConsApp classInstanceType = (TypeConsApp)classInstance.getType();
//The type arguments of the type constructor must satisfy additional constraints implied by the class instance.
//Here we calculate what those constraints are.
for (int i = 0; i < nArgs; ++i) {
//we know from how instances are defined that the argument types are all uninstantiated
//type variables (possibly constrained though).
TypeVar classInstanceArgTypeVar = (TypeVar) classInstanceType.getArg(i);
if (!classInstanceArgTypeVar.noClassConstraints()) {
argConstraints.get(i).addAll(classInstanceArgTypeVar.getTypeClassConstraintSet());
}
}
}
//initialize args.
TypeExpr[] args = new TypeExpr[nArgs];
for (int i = 0; i < nArgs; ++i) {
args[i] = TypeVar.makeTypeVar(null, argConstraints.get(i), true);
}
//pattern matching proceeds in 2 steps e.g. For patternMatch (Tuple2 Char Int, Eq a => a)
//step1: instantiate Eq a => a to (Eq b Eq c => Tuple2 b c)
//step2: patternMatch (Tuple2 Char Int, Eq b Eq c => Tuple2 b c)
TypeConsApp constrainedTypeConsApp = new TypeConsApp(typeCons, args);
anotherTypeVar.setInstance(constrainedTypeConsApp);
this.patternMatch(constrainedTypeConsApp, patternMatchContext);
return;
} else if (anotherTypeExpr instanceof TypeConsApp) {
TypeConsApp anotherTypeConsApp = (TypeConsApp) anotherTypeExpr;
if (getName().equals(anotherTypeConsApp.getName())) {
TypeConsApp.patternMatchArgs(args, anotherTypeConsApp.args, patternMatchContext);
return;
}
throw new TypeException("Type clash: The type declaration " + toString() + " does not match " + anotherTypeConsApp + ".");
} else if (anotherTypeExpr instanceof TypeApp) {
TypeApp typeApp = (TypeApp)anotherTypeExpr;
//(TypeCons e1 e2 ... en-1 en) pattern matches with (a1 a2) by
//1) first check that n >= 1
//2) next pattern match (TypeCons e1 e2 ... en-1) with a1
//3) finally pattern match en and a2.
final int nArgs = args.length;
//must have at least a single argument to pattern match with a type application
if (nArgs == 0) {
throw new TypeException("Type clash: typeConstructor " + getName() + " does not match a type application.");
}
// the type constructor expression, with its final argument dropped.
TypeExpr[] partialArgs = new TypeExpr[nArgs - 1];
System.arraycopy(args, 0, partialArgs, 0, nArgs - 1);
TypeConsApp partialTypeConsApp = new TypeConsApp(rootTypeCons, partialArgs);
partialTypeConsApp.patternMatch(typeApp.getOperatorType().prune(), patternMatchContext);
TypeExpr finalArg = args[nArgs - 1].prune();
finalArg.patternMatch(typeApp.getOperandType().prune(), patternMatchContext);
return;
} else if (anotherTypeExpr instanceof RecordType) {
throw new TypeException("Type clash: The type declaration " + toString() + " does not match the record type " + anotherTypeExpr + ".");
}
throw new IllegalStateException();
}
/**
* Pattern matches two lists of arguments.
* @param declaredTypeArgs
* @param inferredTypeArgs
* @param patternMatchContext
* @throws TypeException
*/
private static void patternMatchArgs(TypeExpr[] declaredTypeArgs, TypeExpr[] inferredTypeArgs, PatternMatchContext patternMatchContext) throws TypeException {
int nArgs = declaredTypeArgs.length;
if (nArgs != inferredTypeArgs.length) {
throw new TypeException("The declared type does not match the inferred type.");
}
for (int i = 0; i < nArgs; ++i) {
TypeExpr declaredTypeArg = declaredTypeArgs[i].prune();
TypeExpr inferredTypeArg = inferredTypeArgs[i].prune();
declaredTypeArg.patternMatch(inferredTypeArg, patternMatchContext);
}
}
/** {@inheritDoc} */
@Override
public boolean containsTypeExpr(TypeExpr searchTypeExpr) {
if (this == searchTypeExpr) {
return true;
}
for (int i = 0, nArgs = getNArgs(); i < nArgs; ++i) {
if (getArg(i).containsTypeExpr(searchTypeExpr)) {
return true;
}
}
return false;
}
/** {@inheritDoc} */
@Override
boolean containsRecordVar(RecordVar searchRecordVar) {
for (int i = 0, nArgs = getNArgs(); i < nArgs; ++i) {
if (getArg(i).containsRecordVar(searchRecordVar)) {
return true;
}
}
return false;
}
/** {@inheritDoc} */
@Override
public boolean isPolymorphic() {
for (int i = 0, nArgs = getNArgs(); i < nArgs; ++i) {
if (getArg(i).isPolymorphic()) {
return true;
}
}
return false;
}
/** {@inheritDoc} */
@Override
public boolean usesForeignType() {
if (rootTypeCons.getForeignTypeInfo() != null) {
return true;
}
for (int i = 0, nArgs = getNArgs(); i < nArgs; ++i) {
if (getArg(i).usesForeignType()) {
return true;
}
}
return false;
}
/** {@inheritDoc} */
@Override
final void writeActual (RecordOutputStream s, Map<TypeExpr, Short> visitedTypeExpr, Map<RecordVar, Short> visitedRecordVar) throws IOException {
s.startRecord(ModuleSerializationTags.TYPE_CONSTRUCTOR, serializationSchema);
s.writeQualifiedName(rootTypeCons.getName());
s.writeShortCompressed(args.length);
for (int i = 0; i < args.length; ++i) {
args[i].write(s, visitedTypeExpr, visitedRecordVar);
}
s.endRecord();
}
/**
* Read an instance of TypeConsApp from the RecordInputStream.
* Read position will be after the record header.
* @param s
* @param schema
* @param mti
* @param visitedTypeExpr
* @param visitedRecordVar
* @param msgLogger the logger to which to log deserialization messages.
* @return an instance of TypeConsApp.
* @throws IOException
*/
final static TypeExpr load (RecordInputStream s, int schema, ModuleTypeInfo mti, Map<Short, TypeExpr> visitedTypeExpr, Map<Short, RecordVar> visitedRecordVar, CompilerMessageLogger msgLogger) throws IOException {
DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "TypeConsApp", msgLogger);
QualifiedName rootTypeConsName = s.readQualifiedName();
TypeConstructor rootTypeCons = mti.getReachableTypeConstructor(rootTypeConsName);
// This may be a type constructor in a module that is not directly imported.
if (rootTypeCons == null) {
throw new IOException ("Unable to resolve TypeConstructor " + rootTypeConsName + " while loading TypeConsApp.");
}
int nArgs = s.readShortCompressed();
// We now have enough information to create the TypeConsApp instance and
// add it to the visitedTypeExpr map. We must do this before loading the
// argument types as they may refer to this TypeConsApp instance.
TypeConsApp typeConsApp = new TypeConsApp(rootTypeCons, nArgs);
visitedTypeExpr.put(new Short((short)visitedTypeExpr.size()), typeConsApp);
// Now load the argument types.
for (int i = 0; i < nArgs; ++i) {
TypeExpr te = TypeExpr.load(s, mti, visitedTypeExpr, visitedRecordVar, msgLogger);
typeConsApp.setArg(i, te);
}
s.skipRestOfRecord();
return typeConsApp;
}
}