/* * 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. */ /* * TypeVar.java * Created: Sep 24, 2003 * By: Bo Ilic */ package org.openquark.cal.compiler; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; 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; /** * A type variable. It is uninstantiated when the instance field is null and instantiated * otherwise. An instantiated type variable behaves like its instantiation. * @author Bo Ilic */ public final class TypeVar extends TypeExpr implements PolymorphicVar { private static final int serializationSchema = 0; /** * If null, then this TypeVar is not instantiated. If non-null, then this TypeVar has been instantiated * and can be thought of as being equivalent to the TypeExpr that it has been instantiated to. */ private TypeExpr instance; /** * The preferred textual name of this TypeVar. null if there is no preferredName. * When a type variable is introduced via a textual declaration, such as with a type declaration, a class instance * definition, or a data declaration, then we can preserve the user supplied name, and make use of it whenever * possible in displaying String representations of types involving this TypeVar. */ private String preferredName; /** * (TypeClass). an uninstantiated type variable can be constrained to be instantiated to a type that is a member of * all the type classes listed in the typeClassConstraintSet. Note that if a class B inherits from A * and B is in the typeClassConstraintSet, then A will not be- the constraint is implicit. * If typeClassConstraintSet is the empty set, then the type variable can be instantiated to any typeExpr. */ private SortedSet<TypeClass> typeClassConstraintSet; /** * Make an unconstrained (i.e. no type context in its type) and uninstantiated type variable */ TypeVar() { this((String)null); } /** * Make an unconstrained (i.e. no type context in its type) and uninstantiated type variable * @param preferredName preferred textual name of this TypeVar. null if there is no preferredName. */ TypeVar(String preferredName) { this(preferredName, TypeClass.NO_CLASS_CONSTRAINTS); } /** * Keep this constructor private! * <p> * Note that the typeClassConstraintSet is assumed to: * <ol> * <li> not to contain redundant superclass constraints (see TypeClass.removeSuperclassConstraints) * <li> be unmodifiable * <li> initially created with TypeClass.makeNewClassConstraintSet() so that it has the right comparator. * </ol> * <p> * @param preferredName preferred textual name of this TypeVar. null if there is no preferredName. * @param typeClassConstraintSet (TypeClass SortedSet) constraints set should initially be * created with TypeClass.makeNewClassConstraintSet(). */ private TypeVar(String preferredName, SortedSet<TypeClass> typeClassConstraintSet) { if (typeClassConstraintSet == null) { throw new NullPointerException(); } if (preferredName != null && !LanguageInfo.isValidTypeVariableName(preferredName)) { throw new IllegalArgumentException("invalid preferred name " + preferredName); } this.preferredName = preferredName; this.typeClassConstraintSet = typeClassConstraintSet; } /** * General factory method for creating a new TypeVar. * Note that redundant superclass constraints in typeClassConstraintSet will be removed by this * method so this does not need to be done by the caller. * * @param preferredName preferred textual name of this TypeVar. null if there is no preferredName. * @param typeClassConstraintSet (TypeClass SortedSet) constraints set should initially be created with TypeClass.makeNewClassConstraintSet(). * @param validateTypeClassConstraintSet remove redundant superclass constaints, and make the set unmodifiable. This * is not necessary if reusing another TypeVar's or RecordVar's typeClassConstraintSet. */ static TypeVar makeTypeVar(String preferredName, SortedSet<TypeClass> typeClassConstraintSet, boolean validateTypeClassConstraintSet) { if (typeClassConstraintSet.isEmpty()) { return new TypeVar(preferredName); } if (validateTypeClassConstraintSet) { TypeClass.removeSuperclassConstraints(typeClassConstraintSet); return new TypeVar(preferredName, Collections.unmodifiableSortedSet(typeClassConstraintSet)); } else { return new TypeVar(preferredName, typeClassConstraintSet); } } /** * @return an efficient copy of this uninstantiated type variable. */ TypeVar copyUninstantiatedTypeVar() { if (instance != null) { throw new IllegalStateException("this TypeVar is not uninstantiated"); } if (noClassConstraints()) { return new TypeVar(preferredName); } return new TypeVar(preferredName, typeClassConstraintSet); } /** * @return the type expression that this type variable has been instantiated to, or null if this is an uninstantiated type variable. */ TypeExpr getInstance() { return instance; } void setInstance(TypeExpr instance) { if (this.instance != null) { throw new IllegalStateException("TypeExpr.setInstance: Programming Error- can't set the instance of an instantiated type variable."); } this.instance = instance; } /**{@inheritDoc} */ public String getPreferredName() { return preferredName; } /** * A helper for selecting a preferred name from 2 possibly different preferred names. * * @param preferredName1 * @param preferredName2 * @return a new preferredName. Will be one of preferredName1, preferredName2. */ private static String selectPreferredName(String preferredName1, String preferredName2) { if (preferredName1 == null) { return preferredName2; } if (preferredName2 == null) { return preferredName1; } //otherwise, arbitrarily select the first of the 2 preferred names. There is no particular reason to favour //one preferred name over another, but picking *a* preferred name, instead of just returning null, will likely //give a more readable type string. return preferredName1; } /** * @return boolean checks whether this type var has an empty type class constraint set. */ boolean noClassConstraints(){ return typeClassConstraintSet.isEmpty(); } /** * Represents the type class constraints on this type variable. This affects the allowable instantiations * of the type variable. * @return SortedSet of TypeClass ordered alphabetically by fully qualified name. * Will always return a non-null value. Note that this set is unmodifiable. */ SortedSet<TypeClass> getTypeClassConstraintSet() { return typeClassConstraintSet; } /** {@inheritDoc} */ @Override TypeExpr prune() { if (instance != null) { return instance.prune(); } return this; } /** {@inheritDoc} */ @Override TypeExpr deepPrune() { if (instance != null) { return instance.deepPrune(); } return this; } /** {@inheritDoc} */ @Override TypeExpr normalize() { if (instance != null) { return instance.normalize(); } return this; } @Override public boolean sameType(TypeExpr anotherTypeExpr) { if (instance != null) { return instance.sameType(anotherTypeExpr); } anotherTypeExpr = anotherTypeExpr.prune(); if (anotherTypeExpr instanceof TypeVar) { TypeVar anotherTypeVar = (TypeVar)anotherTypeExpr; int nConstraints = this.typeClassConstraintSet.size(); if (nConstraints == anotherTypeVar.typeClassConstraintSet.size()) { if (nConstraints == 0) { return true; } return this.toString().equals(anotherTypeVar.toString()); } return false; } if (anotherTypeExpr instanceof TypeConsApp || anotherTypeExpr instanceof RecordType || anotherTypeExpr instanceof TypeApp) { return false; } throw new IllegalStateException(); } /** {@inheritDoc} */ @Override void getGenericClassConstrainedPolymorphicVars(Set<PolymorphicVar> varSet, NonGenericVars nonGenericVars) { if (instance != null) { instance.getGenericClassConstrainedPolymorphicVars(varSet, nonGenericVars); return; } if (!noClassConstraints() && (nonGenericVars == null || nonGenericVars.isGenericTypeVar(this))) { varSet.add(this); } } /** {@inheritDoc} */ @Override void getUninstantiatedTypeVars(Set<TypeVar> varSet) { if (instance != null) { instance.getUninstantiatedTypeVars(varSet); return; } varSet.add(this); } /** {@inheritDoc} */ @Override void getUninstantiatedRecordVars(Set<RecordVar> varSet) { if (instance != null) { instance.getUninstantiatedRecordVars(varSet); return; } } /** {@inheritDoc} */ @Override public TypeExpr getCorrespondingTypeExpr(TypeExpr correspondingSuperType, TypeExpr typeToFind){ if (this == typeToFind) { return correspondingSuperType; } if (instance == null) { return null; } return instance.getCorrespondingTypeExpr(correspondingSuperType, typeToFind); } /** {@inheritDoc} */ @Override void toSourceText(StringBuilder sb, PolymorphicVarContext polymorphicVarContext, ParenthesizationInfo parenthesizationInfo, ScopedEntityNamingPolicy namingPolicy) { if (instance != null) { instance.toSourceText(sb, polymorphicVarContext, parenthesizationInfo, namingPolicy); return; } polymorphicVarContext.addPolymorphicVar(this); String typeVarName = polymorphicVarContext.getPolymorphicVarName(this); if (DEBUG_INFO) { sb.append(typeVarName).append(toAddressString()); return; } sb.append(typeVarName); return; } /** {@inheritDoc} */ @Override SourceModel.TypeExprDefn makeDefinitionSourceModel(PolymorphicVarContext polymorphicVarContext, ScopedEntityNamingPolicy namingPolicy) { if (instance != null) { return instance.makeDefinitionSourceModel(polymorphicVarContext, namingPolicy); } polymorphicVarContext.addPolymorphicVar(this); return SourceModel.TypeExprDefn.TypeVar.make(SourceModel.Name.TypeVar.make(polymorphicVarContext.getPolymorphicVarName(this))); } /** {@inheritDoc} */ @Override public int getArity() { if (instance != null) { return instance.getArity(); } return 0; } /** {@inheritDoc} */ @Override int unifyType(TypeExpr anotherTypeExpr, ModuleTypeInfo contextModuleTypeInfo) throws TypeException { //this TypeVar must be uninstantiated. if (instance != null) { throw new IllegalStateException("TypeVar.unifyType: programming error- instantiated type variable encountered."); } // check for infinite types e.g. an attempt to unify a to (a->Int). if (anotherTypeExpr.containsUninstantiatedTypeVar(this)) { if (this != anotherTypeExpr) { throw new TypeException("Type clash: attempt to create an infinite type."); } // a type variable unifies with itself. //get 1 type closeness point for the same type variable in the 2 type expressions return 1; } if (anotherTypeExpr instanceof TypeVar) { TypeVar anotherTypeVar = (TypeVar) anotherTypeExpr; //anotherTypeVar must be uninstantiated. if (anotherTypeVar.instance != null) { throw new IllegalArgumentException("TypeVar.unifyType: programming error- instantiated type variable encountered."); } //if anotherTypeVar is less specialized than this TypeVar (i.e. this TypeVar has all the //class constraints of anotherTypeVar) then specialize anotherTypeVar to this TypeVar. // if (isClassConstraintSpecialization(anotherTypeVar)) { // // anotherTypeVar.setInstance(this); // //get 0 type closeness points when a specialization of an unconstrained type variable is required. // //get 1 type closeness point when a constrained type var matches another constrained type var. // return noClassConstraints() || anotherTypeVar.noClassConstraints() ? 0 : 1; // } // // if (anotherTypeVar.isClassConstraintSpecialization(this)) { // // setInstance(anotherTypeVar); // //get 0 type closeness points when a specialization of an unconstrained type variable is required. // //get 1 type closeness point when a constrained type var matches another constrained type var. // return noClassConstraints() || anotherTypeVar.noClassConstraints() ? 0 : 1; // } //For example, Unify (Enum a => a, Num a => a) --> (Enum a, Num a) => a TypeVar spanningTypeVar = TypeVar.spanningTypeVar(this, anotherTypeVar); setInstance(spanningTypeVar); anotherTypeVar.setInstance(spanningTypeVar); //get 1 type closeness point if there are dependencies between the constraints. e.g. //Unify ((Enum a, Ord a) => a, (Outputable a, Num a) => a) --> (Enum a, Outputable a, Num a) => a. //the unification doesn't have 2 + 2 = 4 constraints, but rather only 3, because of dependencies. if (spanningTypeVar.typeClassConstraintSet.size() < this.typeClassConstraintSet.size() + anotherTypeVar.typeClassConstraintSet.size()) { return 1; } return 0; } else if (anotherTypeExpr instanceof TypeConsApp) { TypeConsApp typeConsApp = (TypeConsApp) anotherTypeExpr; if (noClassConstraints()) { setInstance(typeConsApp); //get 0 type closeness points when a specialization of an unconstrained type variable is required. return 0; } return unifyConstrainedTypeVarWithTypeCons(typeConsApp, contextModuleTypeInfo); } else if (anotherTypeExpr instanceof TypeApp) { final TypeApp typeApp = (TypeApp)anotherTypeExpr; if (noClassConstraints()) { setInstance(typeApp); //get 0 type closeness points when a specialization of an unconstrained type variable is required. return 0; } //want to handle cases such as: //unify (Eq a => a, (TypeApp (TypeApp (TypeConsApp Either) b) c)) int nArgs = 1; TypeExpr operatorType = typeApp.getOperatorType().prune(); while (operatorType instanceof TypeApp) { ++nArgs; operatorType = ((TypeApp)operatorType).getOperatorType().prune(); } if (!(operatorType instanceof TypeConsApp)) { //for example, //unify (Eq a => a, TypeApp b c) //will result in a type clash. throw new TypeException("Type clash: a constrained type variable cannot unify with a type application not rooted in a type constructor."); } TypeConsApp rootTypeConsApp = (TypeConsApp)operatorType; nArgs += rootTypeConsApp.getNArgs(); TypeExpr[] args = new TypeExpr[nArgs]; //fill in the arguments from the partially saturated TypeConsApp. System.arraycopy(rootTypeConsApp.getArgs(), 0, args, 0, rootTypeConsApp.getNArgs()); //fill in the arguments from the chain of TypeApp nodes. int currentArgIndex = nArgs - 1; operatorType = typeApp; while (operatorType instanceof TypeApp) { TypeApp typeAppOperatorType = (TypeApp)operatorType; args[currentArgIndex] = typeAppOperatorType.getOperandType(); operatorType = typeAppOperatorType.getOperatorType().prune(); --currentArgIndex; } TypeConsApp typeConsApp = new TypeConsApp(rootTypeConsApp.getRoot(), args); return unifyConstrainedTypeVarWithTypeCons(typeConsApp, contextModuleTypeInfo); } else if (anotherTypeExpr instanceof RecordType) { RecordType recordType = (RecordType) anotherTypeExpr; if (!noClassConstraints()) { return unifyConstrainedTypeVarWithRecordType(recordType, contextModuleTypeInfo); } setInstance(recordType); //get 0 type closeness points when a specialization of an unconstrained type variable is required. return 0; } throw new IllegalArgumentException("programming error"); } /** * A helper function to unify a type constructor when this TypeVar is a constrained uninstantiated type variable. * For example, Unify (Eq a => a, (b, c)) ----> (Eq a, Eq b) => (a, b) * @param typeConsApp * @param contextModuleTypeInfo * @return int type closeness */ private int unifyConstrainedTypeVarWithTypeCons(TypeConsApp typeConsApp, ModuleTypeInfo contextModuleTypeInfo) throws TypeException { if (instance != null || noClassConstraints()) { //this TypeVar must be uninstantiated and constrained. throw new IllegalStateException(); } TypeConstructor typeCons = typeConsApp.getRoot(); final int nArgs = typeConsApp.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 : typeClassConstraintSet) { ClassInstance classInstance = contextModuleTypeInfo.getVisibleClassInstance(typeClass, typeCons); if (classInstance == null) { throw new TypeException("Type clash: type " + typeConsApp.toString() + " is not a member of type class " + typeClass.getName() + "."); } //For example, this could be (Eq a, Eq b) => LegacyTuple.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); } //unification proceeds in 2 steps e.g. For unify (Eq a => a, Tuple2 Char Int) //step1: instantiate Eq a => a to (Eq b Eq c => Tuple2 b c) //step2: unify (Eq b Eq c => Tuple2 b c, Tuple2 Char Int) TypeConsApp constrainedTypeConsApp = new TypeConsApp(typeCons, args); setInstance(constrainedTypeConsApp); // We do not award any type closeness points for the fact that the type classes matched, because the // instantiated variable is guaranteed to have at least one feature that will be awarded // a point. return TypeExpr.unifyType(constrainedTypeConsApp, typeConsApp, contextModuleTypeInfo); } /** * A helper function to unify a record type when this TypeVar is a constrained uninstantiated type variable. * For example, Unify (Eq a => a, {r | field1 :: [b]} ----> (Eq s, Eq c) => {s | field1 :: [c]} * @param recordType * @param contextModuleTypeInfo * @return int type closeness */ private int unifyConstrainedTypeVarWithRecordType(RecordType recordType, ModuleTypeInfo contextModuleTypeInfo) throws TypeException { if (instance != null || noClassConstraints()) { //this TypeVar must be uninstantiated and constrained. throw new IllegalStateException(); } //unification proceeds in 3 steps. //For example, when unifying: //(Eq r, Outputable r) => r //and //{s | field1 :: [a], field2 :: Char} //first check that there is a record instance for each of the class constraints on r i.e. //that there are instance Eq {r} and Outputable {r}. // //Then instantiate r to: //(Eq t, Outputable t, t\field1, t\field2, Eq u, Outputable u, Eq v, Outputable v) => {t | field1 :: u, field2 :: v} // //then unify //(Eq t, Outputable t, Eq u, Outputable u, Eq v, Outputable v) => {t | field1 :: u, field2 :: v} //and //{s | field1 :: [a], field2 :: Char} //which reduces this case to the unification of 2 record types. //For example, unifying (Eq a, Chartable a} => a //with {r | field1 :: b} //would result in //(Eq r, ChartItem r, Eq c, ChartItem c) => {r | field1 :: c} //the interesting thing here is that because the instance declaration is //instance ChartItem r => Chartable {r} where ... //the constraints on the records and fields are different from the constraints on the type variable a. SortedSet<TypeClass> recordTypeClassConstraintSet = TypeClass.makeNewClassConstraintSet(); //check that there is a record instance for each of the class constraints on this type variable for (final TypeClass typeClass : typeClassConstraintSet) { ClassInstance classInstance = contextModuleTypeInfo.getVisibleClassInstance(new ClassInstanceIdentifier.UniversalRecordInstance(typeClass.getName())); if (classInstance == null) { throw new TypeException("Type clash: the record type " + recordType + " is not member of the type class " + typeClass.getName() + "."); } //For example, this could be (Eq r) => {r}, in the case of the Eq {r} instance //or it could be (ChartItem r) => {r} in the case of the Chartable {r} instance RecordType classInstanceType = (RecordType)classInstance.getType(); recordTypeClassConstraintSet.addAll(classInstanceType.getPrunedRecordVar().getTypeClassConstraintSet()); } //remove redundant constraints and make unmodifiable. This lets us bypass checks on recordTypeClassConstraint set below //(the false argument in making the record var and extension fields vars). This is an efficiency optimization. TypeClass.removeSuperclassConstraints(recordTypeClassConstraintSet); recordTypeClassConstraintSet = Collections.unmodifiableSortedSet(recordTypeClassConstraintSet); //create an intermediate record type whose recordVar and extension fields map all have //the type class constraint set equal to recordTypeClassConstraintSet Map<FieldName, TypeExpr> constrainedExtensionFieldsMap = new HashMap<FieldName, TypeExpr>(); Set<FieldName> recordTypeHasFieldsSet = recordType.getHasFieldsMap().keySet(); for (final FieldName fieldName : recordTypeHasFieldsSet) { constrainedExtensionFieldsMap.put(fieldName, TypeVar.makeTypeVar(null, recordTypeClassConstraintSet, false)); } RecordVar constrainedRecordVar; RecordVar recordTypeRecordVar = recordType.getPrunedRecordVar(); if (recordTypeRecordVar.isNoFields()) { constrainedRecordVar = RecordVar.NO_FIELDS; } else { constrainedRecordVar = RecordVar.makeRecordVar(null, recordTypeHasFieldsSet, recordTypeClassConstraintSet, false); } RecordType constrainedRecordType = new RecordType(constrainedRecordVar, constrainedExtensionFieldsMap); setInstance(constrainedRecordType); int typeCloseness = TypeExpr.unifyType(constrainedRecordType, recordType, contextModuleTypeInfo); // Fields added to the intermediate type get points for matching types, but not for matching names, // because the field names weren't present in the original type. return typeCloseness - constrainedRecordType.getNHasFields(); } /** {@inheritDoc} */ @Override void patternMatch(TypeExpr anotherTypeExpr, PatternMatchContext patternMatchContext) throws TypeException { //this TypeVar must be uninstantiated. if (instance != null) { throw new IllegalStateException("TypeVar.patternMatch: programming error."); } if (anotherTypeExpr instanceof TypeVar) { //a TypeVar matches with itself if (this == anotherTypeExpr) { return; } TypeVar anotherTypeVar = (TypeVar) anotherTypeExpr; //anotherTypeVar must be uninstantiated. if (anotherTypeVar.instance != null) { throw new IllegalArgumentException("TypeVar.patternMatch: programming error."); } //Can't instantiate a type variable from the declared type expression. //For example, this error occurs with the following declaration. //funnyId :: a -> b; //public funnyId = Prelude.id; if (patternMatchContext.isUninstantiableTypeVar(anotherTypeVar)) { //todoBI can give a better error message indicating the name of the type variable\ //with respect to the declared type name. This can be done for all cases (TypeConsApp //RecordType etc, but the todo is here only). throw new TypeException("Attempt to instantiate a type variable from the declared type."); } //can't pattern match a nongeneric type variable to a generic type variable. //For example, for the function: //g y = let f x = x + y; in f y; //we can't add a local type declaration f :: Num a => a -> a; //because that would imply that a is a generic polymorphic type variable, //and f could be applied polymorphically in the "in" part. In fact, //f's actual type is constrained by each application in the "in" part. NonGenericVars nonGenericVars = patternMatchContext.getNonGenericVars(); if (nonGenericVars != null && !nonGenericVars.isGenericTypeVar(anotherTypeVar)) { throw new TypeException("Attempt to match a non-generic type variable to a generic type variable."); } // check for infinite types e.g. an attempt to pattern match a to (a->Int). if (containsUninstantiatedTypeVar(anotherTypeVar)) { // Note that we already know that anotherTypeExpr != this. throw new TypeException("Type clash: attempt to create an infinite type."); } //if anotherTypeVar is less specialized than this TypeVar i.e. this TypeVar has all the //class constraints of anotherTypeVar then specialize anotherTypeVar to this TypeVar. if (isClassConstraintSpecialization(anotherTypeVar)) { anotherTypeVar.setInstance(this); return; } } throw new TypeException("Type clash: The type declaration " + toString() + " does not match " + anotherTypeExpr.toString() + "."); } /** * Creates a new TypeVar whose typeClassConstraintSet contains all the constraints specified by typeVar1 and typeVar2. * @param typeVar1 * @param typeVar2 * @return TypeVar the new TypeVar */ private static TypeVar spanningTypeVar(TypeVar typeVar1, TypeVar typeVar2) { if (typeVar1.instance != null || typeVar2.instance != null) { throw new IllegalArgumentException("typeVar1 and typeVar2 must be uninstantiated"); } final String newPreferredName = TypeVar.selectPreferredName(typeVar1.preferredName, typeVar2.preferredName); if (typeVar1.noClassConstraints() && typeVar2.noClassConstraints()) { return new TypeVar(newPreferredName); } SortedSet<TypeClass> spanningConstraints = TypeClass.makeNewClassConstraintSet(); spanningConstraints.addAll(typeVar1.typeClassConstraintSet); spanningConstraints.addAll(typeVar2.typeClassConstraintSet); return TypeVar.makeTypeVar( newPreferredName, spanningConstraints, true); } /** * Returns true if the class type constraints of this TypeVar are at least as stringent as those of anotherTypeVar. * Creation date: (3/22/01 4:02:47 PM) * @param anotherTypeVar * @return boolean */ private boolean isClassConstraintSpecialization(TypeVar anotherTypeVar) { if (instance != null || anotherTypeVar.instance != null) { throw new IllegalStateException("TypeVar.isClassConstraintSpecialization: Programming error. Must compare uninstantiated type variables."); } if (anotherTypeVar.noClassConstraints()) { return true; } if (noClassConstraints()) { return false; } //todoBI this can be cached for efficiency. Set<TypeClass> flattenedTypeClassConstraintSet = flattenTypeClassConstraintSet(); return flattenedTypeClassConstraintSet.containsAll(anotherTypeVar.typeClassConstraintSet); } /** * Creation date: (3/28/01 3:25:22 PM) * @return Set the set of all type class constraints on this TypeVar i.e. including implicit * constraints from non-parent ancestor classes. */ Set<TypeClass> flattenTypeClassConstraintSet() { Set<TypeClass> flattenedConstraints = new HashSet<TypeClass>(typeClassConstraintSet); for (final TypeClass typeClass : typeClassConstraintSet) { flattenedConstraints.addAll(typeClass.calculateAncestorClassList()); } return flattenedConstraints; } /** {@inheritDoc} */ @Override public boolean containsTypeExpr(TypeExpr searchTypeExpr) { if (this == searchTypeExpr) { return true; } if (instance != null) { return instance.containsTypeExpr(searchTypeExpr); } return false; } /** {@inheritDoc} */ @Override boolean containsRecordVar(RecordVar searchRecordVar) { if (instance != null) { return instance.containsRecordVar(searchRecordVar); } return false; } /** {@inheritDoc} */ @Override public boolean isPolymorphic() { return instance == null || instance.isPolymorphic(); } /** {@inheritDoc} */ @Override public boolean usesForeignType() { if (instance == null) { return false; } return instance.usesForeignType(); } /** {@inheritDoc} */ @Override final void writeActual (RecordOutputStream s, Map<TypeExpr, Short> visitedTypeExpr, Map<RecordVar, Short> visitedRecordVar) throws IOException { if (instance == null) { s.startRecord(ModuleSerializationTags.TYPE_VAR, serializationSchema); } else { s.startRecord(ModuleSerializationTags.TYPE_VAR_WITH_INSTANCE, serializationSchema); } if (instance != null) { instance.write(s, visitedTypeExpr, visitedRecordVar); } s.writeShortCompressed(typeClassConstraintSet.size()); for (final TypeClass typeClass : typeClassConstraintSet) { s.writeQualifiedName(typeClass.getName()); } s.writeUTF(preferredName); s.endRecord(); } /** * Read an instance of TypeVar 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 TypeVar. * @throws IOException */ final static TypeExpr load (RecordInputStream s, short tag, int schema, ModuleTypeInfo mti, Map<Short, TypeExpr> visitedTypeExpr, Map<Short, RecordVar> visitedRecordVar, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "TypeVar", msgLogger); TypeVar tv = new TypeVar(); visitedTypeExpr.put(new Short((short)visitedTypeExpr.size()), tv); TypeExpr instance = null; if (tag == ModuleSerializationTags.TYPE_VAR_WITH_INSTANCE) { instance = TypeExpr.load(s, mti, visitedTypeExpr, visitedRecordVar, msgLogger); } SortedSet<TypeClass> typeClassConstraintSet = null; int nTypeClasses = s.readShortCompressed(); if (nTypeClasses > 0) { typeClassConstraintSet = TypeClass.makeNewClassConstraintSet(); for (int i = 0; i < nTypeClasses; ++i) { QualifiedName qn = s.readQualifiedName(); // This could be a type from a non-direct dependee module. TypeClass tc = mti.getReachableTypeClass(qn); if (tc == null) { throw new IOException ("Unable to resolve TypeClass " + qn + " while loading TypeVar."); } typeClassConstraintSet.add(tc); } } else { typeClassConstraintSet = TypeClass.NO_CLASS_CONSTRAINTS; } tv.typeClassConstraintSet = typeClassConstraintSet; if (instance != null) { tv.setInstance(instance); } tv.preferredName = s.readUTF(); s.skipRestOfRecord(); return tv; } }