/*******************************************************************************
*
* Copyright (C) 2008 Fujitsu Services Ltd.
*
* Author: Nick Battle
*
* This file is part of VDMJ.
*
* VDMJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VDMJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VDMJ. If not, see <http://www.gnu.org/licenses/>.
*
******************************************************************************/
package org.overture.typechecker;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import org.overture.ast.assistant.pattern.PTypeList;
import org.overture.ast.definitions.ATypeDefinition;
import org.overture.ast.definitions.PDefinition;
import org.overture.ast.factory.AstFactory;
import org.overture.ast.lex.LexNameList;
import org.overture.ast.types.ABracketType;
import org.overture.ast.types.AClassType;
import org.overture.ast.types.AFunctionType;
import org.overture.ast.types.AInMapMapType;
import org.overture.ast.types.ANamedInvariantType;
import org.overture.ast.types.AOperationType;
import org.overture.ast.types.AOptionalType;
import org.overture.ast.types.AParameterType;
import org.overture.ast.types.AProductType;
import org.overture.ast.types.ARecordInvariantType;
import org.overture.ast.types.ASeq1SeqType;
import org.overture.ast.types.ASet1SetType;
import org.overture.ast.types.SSetType;
import org.overture.ast.types.AUndefinedType;
import org.overture.ast.types.AUnionType;
import org.overture.ast.types.AUnknownType;
import org.overture.ast.types.AUnresolvedType;
import org.overture.ast.types.AVoidReturnType;
import org.overture.ast.types.AVoidType;
import org.overture.ast.types.PType;
import org.overture.ast.types.SMapType;
import org.overture.ast.types.SNumericBasicType;
import org.overture.ast.types.SSeqType;
import org.overture.typechecker.assistant.ITypeCheckerAssistantFactory;
/**
* A class for static type checking comparisons.
*/
public class TypeComparator
{
/**
* A vector of type pairs that have already been compared. This is to allow recursive type definitions to be
* compared without infinite regress.
*/
private Vector<TypePair> done = new Vector<TypePair>(256);
private final ITypeCheckerAssistantFactory assistantFactory;
public TypeComparator(ITypeCheckerAssistantFactory assistantFactory)
{
this.assistantFactory = assistantFactory;
}
/**
* A result value for comparison of types. The "Maybe" value is needed so that the fact that a type's subtypes are
* being actively compared in a recursive call can be recorded. For example, if a MySetType contains references to
* MySetType in its element's type, the comparison of those types will see the "Maybe" result of the original call
* and not recurse. That will not fail the lower level comparison, but the overall "yes" will not be recorded until
* the recursive calls return (assuming there are no "no" votes of course).
*/
private static enum Result
{
Yes, No, Maybe
}
private static class TypePair
{
public PType a;
public PType b;
public Result result;
public TypePair(PType a, PType b)
{
this.a = a;
this.b = b;
this.result = Result.Maybe;
}
@Override
public boolean equals(Object other)
{
if (other instanceof TypePair)
{
TypePair to = (TypePair) other;
return a.equals(to.a) && b.equals(to.b);
}
return false;
}
@Override
public int hashCode()
{
return a.hashCode() + b.hashCode();
}
}
/**
* Test whether the two types are compatible. This means that, at runtime, it is possible that the two types are the
* same, or sufficiently similar that the "from" value can be assigned to the "to" value.
*
* @param to
* @param from
* @return True if types "a" and "b" are compatible.
*/
public synchronized boolean compatible(PType to, PType from)
{
done.clear();
return searchCompatible(to, from, false) == Result.Yes;
}
public synchronized boolean compatible(PType to, PType from,
boolean paramOnly)
{
done.clear();
return searchCompatible(to, from, paramOnly) == Result.Yes;
}
/**
* Compare two type lists for placewise compatibility. , assistantFactory @param to
*
* @param to
* @param from
* @return True if all types compatible.
*/
public synchronized boolean compatible(List<PType> to, List<PType> from)
{
done.clear();
return allCompatible(to, from, false) == Result.Yes;
}
/**
* Compare two type lists for placewise compatibility. This is used to check ordered lists of types such as those in
* a ProductType or parameters to a function or operation.
*
* @param to
* @param from
* @return Yes or No.
*/
private Result allCompatible(List<PType> to, List<PType> from,
boolean paramOnly)
{
if (to.size() != from.size())
{
return Result.No;
} else
{
for (int i = 0; i < to.size(); i++)
{
if (searchCompatible(to.get(i), from.get(i), paramOnly) == Result.No)
{
return Result.No;
}
}
}
return Result.Yes;
}
/**
* Search the {@link #done} vector for an existing comparison of two types before either returning the previous
* result, or making a new comparison and adding that result to the vector.
*
* @param to
* @param from
* @return Yes or No.
*/
private Result searchCompatible(PType to, PType from, boolean paramOnly)
{
TypePair pair = new TypePair(to, from);
int i = done.indexOf(pair);
if (i >= 0)
{
return done.get(i).result; // May be "Maybe".
} else
{
done.add(pair);
}
// The pair.result is "Maybe" until this call returns.
pair.result = test(to, from, paramOnly);
return pair.result;
}
/**
* The main implementation of the compatibility checker. If "a" and "b" are the same object the result is "yes"; if
* either is an {@link UnknownType}, we are dealing with earlier parser errors and so "yes" is returned to avoid too
* many errors; if either is a {@link ParameterType} the result is also "yes" on the grounds that the type cannot be
* tested at compile time. If either type is a {@link BracketType} or a {@link NamedType} the types are reduced to
* their underlying type before proceeding; if either is an {@link OptionalType} and the other is optional also, the
* result is "yes", otherwise the underlying type of the optional type is taken before proceeding; the last two
* steps are repeated until the types will not reduce further. To compare the reduced types, if "a" is a union type,
* then all the component types of "a" are compared to "b" (or b's components, if it too is a union type) until a
* match is found; otherwise basic type comparisons are made, involving any subtypes - for example, if they are both
* sets, then the result depends on whether their "set of" subtypes are compatible, by a recursive call. Similarly
* with maps and sequences, function/operation parameter types, and record field types. Lastly, a simple
* {@link org.overture.vdmj.types.Type#equals} operation is performed on two basic types to decide the result.
*
* @param to
* @param from
* @param paramOnly
* @return Yes or No.
*/
private Result test(PType to, PType from, boolean paramOnly)
{
if (to instanceof AUnresolvedType)
{
throw new TypeCheckException("Unknown type: " + to, to.getLocation(), to);
}
if (from instanceof AUnresolvedType)
{
throw new TypeCheckException("Unknown type: " + from, from.getLocation(), from);
}
if (to == from) // (assistantFactory.createPTypeAssistant().equals(to, from))
{
return Result.Yes; // Same object!
}
if (to instanceof AUnknownType || from instanceof AUnknownType)
{
return Result.Yes; // Hmmm... too many errors otherwise
}
if (to instanceof AUndefinedType || from instanceof AUndefinedType)
{
return Result.Yes; // Not defined "yet"...?
}
if (from instanceof AParameterType)
{
return Result.Yes; // Runtime checked... Note "to" checked below
}
// Obtain the fundamental type of BracketTypes, NamedTypes and
// OptionalTypes.
boolean resolved = false;
while (!resolved)
{
if (to instanceof ABracketType)
{
to = ((ABracketType) to).getType();
continue;
}
if (from instanceof ABracketType)
{
from = ((ABracketType) from).getType();
continue;
}
if (to instanceof ANamedInvariantType)
{
to = ((ANamedInvariantType) to).getType();
continue;
}
if (from instanceof ANamedInvariantType)
{
from = ((ANamedInvariantType) from).getType();
continue;
}
if (to instanceof AOptionalType)
{
if (from instanceof AOptionalType)
{
return Result.Yes;
}
to = ((AOptionalType) to).getType();
continue;
}
if (from instanceof AOptionalType)
{
// Can't assign nil to a non-optional type? This should maybe
// generate a warning here?
if (to instanceof AOptionalType)
{
return Result.Yes;
}
from = ((AOptionalType) from).getType();
continue;
}
resolved = true;
}
// OK... so we have fully resolved the basic types...
if (to instanceof AUnionType)
{
AUnionType ua = (AUnionType) to;
for (PType ta : ua.getTypes())
{
if (searchCompatible(ta, from, paramOnly) == Result.Yes)
{
return Result.Yes;
}
}
} else
{
if (from instanceof AUnionType)
{
AUnionType ub = (AUnionType) from;
for (PType tb : ub.getTypes())
{
if (searchCompatible(to, tb, paramOnly) == Result.Yes)
{
return Result.Yes;
}
}
} else if (to instanceof SNumericBasicType)
{
return from instanceof SNumericBasicType ? Result.Yes
: Result.No;
} else if (to instanceof AProductType)
{
if (!(from instanceof AProductType))
{
return Result.No;
}
List<PType> ta = ((AProductType) to).getTypes();
List<PType> tb = ((AProductType) from).getTypes();
return allCompatible(ta, tb, paramOnly);
} else if (to instanceof SMapType)
{
if (!(from instanceof SMapType))
{
return Result.No;
}
SMapType ma = (SMapType) to;
SMapType mb = (SMapType) from;
return ma.getEmpty()
|| mb.getEmpty()
|| searchCompatible(ma.getFrom(), mb.getFrom(), paramOnly) == Result.Yes
&& searchCompatible(ma.getTo(), mb.getTo(), paramOnly) == Result.Yes ? Result.Yes
: Result.No;
} else if (to instanceof SSetType) // Includes set1
{
if (!(from instanceof SSetType))
{
return Result.No;
}
SSetType sa = (SSetType) to;
SSetType sb = (SSetType) from;
if (to instanceof ASet1SetType && sb.getEmpty())
{
return Result.No;
}
return sa.getEmpty()
|| sb.getEmpty()
|| searchCompatible(sa.getSetof(), sb.getSetof(), paramOnly) == Result.Yes ? Result.Yes
: Result.No;
} else if (to instanceof SSeqType) // Includes seq1
{
if (!(from instanceof SSeqType))
{
return Result.No;
}
SSeqType sa = (SSeqType) to;
SSeqType sb = (SSeqType) from;
if (to instanceof ASeq1SeqType && sb.getEmpty())
{
return Result.No;
}
return sa.getEmpty()
|| sb.getEmpty()
|| searchCompatible(sa.getSeqof(), sb.getSeqof(), paramOnly) == Result.Yes ? Result.Yes
: Result.No;
} else if (to instanceof AFunctionType)
{
if (!(from instanceof AFunctionType))
{
return Result.No;
}
AFunctionType fa = (AFunctionType) to;
AFunctionType fb = (AFunctionType) from;
return allCompatible(fa.getParameters(), fb.getParameters(), paramOnly) == Result.Yes
&& (paramOnly || searchCompatible(fa.getResult(), fb.getResult(), paramOnly) == Result.Yes) ? Result.Yes
: Result.No;
} else if (to instanceof AOperationType)
{
if (!(from instanceof AOperationType))
{
return Result.No;
}
AOperationType fa = (AOperationType) to;
AOperationType fb = (AOperationType) from;
return allCompatible(fa.getParameters(), fb.getParameters(), paramOnly) == Result.Yes
&& (paramOnly || searchCompatible(fa.getResult(), fb.getResult(), paramOnly) == Result.Yes) ? Result.Yes
: Result.No;
} else if (to instanceof ARecordInvariantType)
{
if (!(from instanceof ARecordInvariantType))
{
return Result.No;
}
ARecordInvariantType rf = (ARecordInvariantType) from;
ARecordInvariantType rt = (ARecordInvariantType) to;
return assistantFactory.createPTypeAssistant().equals(rf, rt) ? Result.Yes
: Result.No;
} else if (to instanceof AClassType)
{
if (!(from instanceof AClassType))
{
return Result.No;
}
AClassType cfrom = (AClassType) from;
AClassType cto = (AClassType) to;
// VDMTools doesn't seem to worry about sub/super type
// assignments. This was "cfrom.equals(cto)".
if (assistantFactory.createPTypeAssistant().hasSupertype(cfrom, cto)
|| assistantFactory.createPTypeAssistant().hasSupertype(cto, cfrom))
{
return Result.Yes;
}
} else if (from instanceof AVoidReturnType)
{
if (to instanceof AVoidType || to instanceof AVoidReturnType)
{
return Result.Yes;
} else
{
return Result.No;
}
} else if (to instanceof AVoidReturnType)
{
if (from instanceof AVoidType
|| from instanceof AVoidReturnType)
{
return Result.Yes;
} else
{
return Result.No;
}
}
else if (to instanceof AParameterType)
{
// If the from type includes the "to" parameter anywhere, then the types must be identical,
// otherwise they match. We can only test for that easily with toString() :-(
// See overture bug #562.
String fstr = from.toString();
String tstr = to.toString();
if (fstr.indexOf(tstr) >= 0)
{
return to.equals(from) ? Result.Yes : Result.No;
}
else
{
return Result.Yes;
}
}
else
{
return assistantFactory.createPTypeAssistant().equals(to, from) ? Result.Yes
: Result.No;
}
}
return Result.No;
}
/**
* Test whether one type is a subtype of another.
*
* @param sub
* @param sup
* @return True if sub is a subtype of sup.
*/
public synchronized boolean isSubType(PType sub, PType sup)
{
return isSubType(sub, sup, false);
}
public synchronized boolean isSubType(PType sub, PType sup,
boolean invignore)
{
done.clear();
return searchSubType(sub, sup, invignore) == Result.Yes;
}
/**
* Compare two type lists for placewise subtype compatibility. This is used to check ordered lists of types such as
* those in a ProductType or parameters to a function or operation.
*
* @param sub
* @param sup
* @return Yes or No.
*/
private Result allSubTypes(List<PType> sub, List<PType> sup,
boolean invignore)
{
if (sub.size() != sup.size())
{
return Result.No;
} else
{
for (int i = 0; i < sub.size(); i++)
{
if (searchSubType(sub.get(i), sup.get(i), invignore) == Result.No)
{
return Result.No;
}
}
}
return Result.Yes;
}
/**
* Search the {@link #done} vector for an existing subtype comparison of two types before either returning the
* previous result, or making a new comparison and adding that result to the vector.
*
* @param sub
* @param sup
* @param invignore
* @return Yes or No, if sub is a subtype of sup.
*/
private Result searchSubType(PType sub, PType sup, boolean invignore)
{
TypePair pair = new TypePair(sub, sup);
int i = done.indexOf(pair);
if (i >= 0)
{
return done.get(i).result; // May be "Maybe".
} else
{
done.add(pair);
}
// The pair.result is "Maybe" until this call returns.
pair.result = subtest(sub, sup, invignore);
return pair.result;
}
/**
* The main implementation of the subtype checker. If "a" and "b" are the same object the result is "yes"; if either
* is an {@link UnknownType}, we are dealing with earlier parser errors and so "yes" is returned to avoid too many
* errors; if either is a {@link ParameterType} the result is also "yes" on the grounds that the type cannot be
* tested at compile time. If either type is a {@link BracketType} or a {@link NamedType} the types are reduced to
* their underlying type before proceeding; if either is an {@link OptionalType} and the other is optional also, the
* result is "yes", otherwise the underlying type of the optional type is taken before proceeding; the last two
* steps are repeated until the types will not reduce further. To compare the reduced types, if "a" is a union type,
* then all the component types of "a" are compared to "b" (or b's components, if it too is a union type); otherwise
* basic type comparisons are made, involving any subtypes - for example, if they are both sets, then the result
* depends on whether their "set of" subtypes are subtypes, by a recursive call. Similarly with maps and sequences,
* function/operation parameter types, and record field types. Lastly, a simple
* {@link org.overture.vdmj.types.Type#equals} operation is performed on two basic types to decide the result.
*
* @param sub
* @param sup
* @param invignore
* @return Yes or No.
*/
private Result subtest(PType sub, PType sup, boolean invignore)
{
if (sub instanceof AUnresolvedType)
{
throw new TypeCheckException("Unknown type: " + sub, sub.getLocation(), sub);
}
if (sup instanceof AUnresolvedType)
{
throw new TypeCheckException("Unknown type: " + sup, sup.getLocation(), sup);
}
if (sub instanceof AUnknownType || sup instanceof AUnknownType)
{
return Result.Yes; // Hmmm... too many errors otherwise
}
if (sub instanceof AParameterType || sup instanceof AParameterType)
{
return Result.Yes; // Runtime checked...
}
if (sub instanceof AUndefinedType || sup instanceof AUndefinedType)
{
return Result.Yes; // Usually uninitialized variables etc.
}
// Obtain the fundamental type of BracketTypes, NamedTypes and
// OptionalTypes.
boolean resolved = false;
while (!resolved)
{
if (sub instanceof ABracketType)
{
sub = ((ABracketType) sub).getType();
continue;
}
if (sup instanceof ABracketType)
{
sup = ((ABracketType) sup).getType();
continue;
}
if (sub instanceof ANamedInvariantType)
{
ANamedInvariantType nt = (ANamedInvariantType) sub;
if (nt.getInvDef() == null || invignore)
{
sub = nt.getType();
continue;
}
}
if (sup instanceof ANamedInvariantType)
{
ANamedInvariantType nt = (ANamedInvariantType) sup;
if (nt.getInvDef() == null || invignore)
{
sup = nt.getType();
continue;
}
}
if (sub instanceof AOptionalType && sup instanceof AOptionalType)
{
sub = ((AOptionalType) sub).getType();
sup = ((AOptionalType) sup).getType();
continue;
}
resolved = true;
}
if (sub instanceof AUnknownType || sup instanceof AUnknownType)
{
return Result.Yes; // Hmmm... too many errors otherwise
}
// TODO: When nodes are cloned this 'if(sub == sup)' will not work,
// but this might not be the best way to solve it
// if(sub == sup)
if (sub.equals(sup))
{
return Result.Yes;
}
// OK... so we have fully resolved the basic types...
if (sub instanceof AUnionType)
{
AUnionType subu = (AUnionType) sub;
for (PType suba : subu.getTypes())
{
if (searchSubType(suba, sup, invignore) == Result.No)
{
return Result.No;
}
}
return Result.Yes; // Must be all of them
} else
{
if (sup instanceof AUnionType)
{
AUnionType supu = (AUnionType) sup;
for (PType supt : supu.getTypes())
{
if (searchSubType(sub, supt, invignore) == Result.Yes)
{
return Result.Yes; // Can be any of them
}
}
return Result.No;
} else if (sub instanceof ANamedInvariantType)
{
ANamedInvariantType subn = (ANamedInvariantType) sub;
return searchSubType(subn.getType(), sup, invignore);
} else if (sup instanceof AOptionalType)
{
// Supertype includes a nil value, and the subtype is not
// optional (stripped above), so we test the optional's type.
AOptionalType op = (AOptionalType) sup;
return searchSubType(sub, op.getType(), invignore);
} else if (sub instanceof SNumericBasicType)
{
if (sup instanceof SNumericBasicType)
{
SNumericBasicType subn = (SNumericBasicType) sub;
SNumericBasicType supn = (SNumericBasicType) sup;
return assistantFactory.createSNumericBasicTypeAssistant().getWeight(subn) <= assistantFactory.createSNumericBasicTypeAssistant().getWeight(supn) ? Result.Yes
: Result.No;
}
} else if (sub instanceof AProductType)
{
if (!(sup instanceof AProductType))
{
return Result.No;
}
List<PType> subl = ((AProductType) sub).getTypes();
List<PType> supl = ((AProductType) sup).getTypes();
return allSubTypes(subl, supl, invignore);
} else if (sub instanceof SMapType)
{
if (!(sup instanceof SMapType))
{
return Result.No;
}
SMapType subm = (SMapType) sub;
SMapType supm = (SMapType) sup;
if (subm.getEmpty() || supm.getEmpty())
{
return Result.Yes;
}
if (searchSubType(subm.getFrom(), supm.getFrom(), invignore) == Result.Yes
&& searchSubType(subm.getTo(), supm.getTo(), invignore) == Result.Yes)
{
if (!(sub instanceof AInMapMapType)
&& sup instanceof AInMapMapType)
{
return Result.No;
}
return Result.Yes;
} else
{
return Result.No;
}
} else if (sub instanceof SSetType)
{
if (!(sup instanceof SSetType))
{
return Result.No;
}
SSetType subs = (SSetType) sub;
SSetType sups = (SSetType) sup;
if ((subs.getEmpty() && !(sup instanceof ASet1SetType)) || sups.getEmpty())
{
return Result.Yes;
}
if (searchSubType(subs.getSetof(), sups.getSetof(), invignore) == Result.Yes)
{
if (!(sub instanceof ASet1SetType) && (sup instanceof ASet1SetType))
{
return Result.No;
}
return Result.Yes;
}
else
{
return Result.No;
}
} else if (sub instanceof SSeqType) // Includes seq1
{
if (!(sup instanceof SSeqType))
{
return Result.No;
}
SSeqType subs = (SSeqType) sub;
SSeqType sups = (SSeqType) sup;
if ((subs.getEmpty() && !(sup instanceof ASeq1SeqType)) || sups.getEmpty())
{
return Result.Yes;
}
if (searchSubType(subs.getSeqof(), sups.getSeqof(), invignore) == Result.Yes)
{
if (!(sub instanceof ASeq1SeqType) && sup instanceof ASeq1SeqType)
{
return Result.No;
}
return Result.Yes;
} else
{
return Result.No;
}
} else if (sub instanceof AFunctionType)
{
if (!(sup instanceof AFunctionType))
{
return Result.No;
}
AFunctionType subf = (AFunctionType) sub;
AFunctionType supf = (AFunctionType) sup;
return allSubTypes(subf.getParameters(), supf.getParameters(), invignore) == Result.Yes
&& searchSubType(subf.getResult(), supf.getResult(), invignore) == Result.Yes ? Result.Yes
: Result.No;
} else if (sub instanceof AOperationType)
{
if (!(sup instanceof AOperationType))
{
return Result.No;
}
AOperationType subo = (AOperationType) sub;
AOperationType supo = (AOperationType) sup;
return allSubTypes(subo.getParameters(), supo.getParameters(), invignore) == Result.Yes
&& searchSubType(subo.getResult(), supo.getResult(), invignore) == Result.Yes ? Result.Yes
: Result.No;
} else if (sub instanceof ARecordInvariantType)
{
if (!(sup instanceof ARecordInvariantType))
{
return Result.No;
}
ARecordInvariantType subr = (ARecordInvariantType) sub;
ARecordInvariantType supr = (ARecordInvariantType) sup;
return assistantFactory.createPTypeAssistant().equals(subr, supr) ? Result.Yes
: Result.No;
} else if (sub instanceof AClassType)
{
if (!(sup instanceof AClassType))
{
return Result.No;
}
AClassType supc = (AClassType) sup;
AClassType subc = (AClassType) sub;
if (assistantFactory.createPTypeAssistant().hasSupertype(subc, supc))
{
return Result.Yes;
}
} else
{
return assistantFactory.createPTypeAssistant().equals(sub, sup) ? Result.Yes
: Result.No;
}
}
return Result.No;
}
/**
* Check that the compose types that are referred to in a type have a matching definition in the environment. The
* method returns a list of types that do not exist if the newTypes parameter is passed.
*
* @param type
* @param env
* @param newTypes
* @return
*/
public PTypeList checkComposeTypes(PType type, Environment env,
boolean newTypes)
{
PTypeList undefined = new PTypeList();
for (PType compose : env.af.createPTypeAssistant().getComposeTypes(type))
{
ARecordInvariantType composeType = (ARecordInvariantType) compose;
PDefinition existing = env.findType(composeType.getName(), null);
if (existing != null)
{
// If the type is already defined, check that it has the same shape and
// does not have an invariant (which cannot match a compose definition).
boolean matches = false;
if (existing instanceof ATypeDefinition)
{
ATypeDefinition edef = (ATypeDefinition) existing;
PType etype = existing.getType();
if (edef.getInvExpression() == null
&& etype instanceof ARecordInvariantType)
{
ARecordInvariantType retype = (ARecordInvariantType) etype;
if (retype.getFields().equals(composeType.getFields()))
{
matches = true;
}
}
}
if (!matches)
{
TypeChecker.report(3325, "Mismatched compose definitions for "
+ composeType.getName(), composeType.getLocation());
TypeChecker.detail2(composeType.getName().getName(), composeType.getLocation(), existing.getName().getName(), existing.getLocation());
}
} else
{
if (newTypes)
{
undefined.add(composeType);
} else
{
TypeChecker.report(3113, "Unknown type name '"
+ composeType.getName() + "'", composeType.getLocation());
}
}
}
// Lastly, check that the compose types extracted are compatible
LexNameList done = new LexNameList();
for (PType c1 : undefined)
{
for (PType c2 : undefined)
{
if (c1 != c2)
{
ARecordInvariantType r1 = (ARecordInvariantType) c1;
ARecordInvariantType r2 = (ARecordInvariantType) c2;
if (r1.getName().equals(r2.getName())
&& !done.contains(r1.getName())
&& !r1.getFields().equals(r2.getFields()))
{
TypeChecker.report(3325, "Mismatched compose definitions for "
+ r1.getName(), r1.getLocation());
TypeChecker.detail2(r1.getName().getName(), r1.getLocation(), r2.getName().getName(), r2.getLocation());
done.add(r1.getName());
}
}
}
}
return undefined;
}
/**
* Calculate the intersection of two types.
*
* @param a
* @param b
* @return
*/
public PType intersect(PType a, PType b)
{
Set<PType> tsa = new HashSet<PType>();
Set<PType> tsb = new HashSet<PType>();
// Obtain the fundamental type of BracketTypes, NamedTypes and OptionalTypes.
boolean resolved = false;
while (!resolved)
{
if (a instanceof ABracketType)
{
a = ((ABracketType) a).getType();
continue;
}
if (b instanceof ABracketType)
{
b = ((ABracketType) b).getType();
continue;
}
if (a instanceof ANamedInvariantType)
{
ANamedInvariantType nt = (ANamedInvariantType) a;
if (nt.getInvDef() == null)
{
a = nt.getType();
continue;
}
}
if (b instanceof ANamedInvariantType)
{
ANamedInvariantType nt = (ANamedInvariantType) b;
if (nt.getInvDef() == null)
{
b = nt.getType();
continue;
}
}
if (a instanceof AOptionalType && b instanceof AOptionalType)
{
a = ((AOptionalType) a).getType();
b = ((AOptionalType) b).getType();
continue;
}
resolved = true;
}
if (a instanceof AUnionType)
{
AUnionType uta = (AUnionType) a;
tsa.addAll(uta.getTypes());
} else
{
tsa.add(a);
}
if (b instanceof AUnionType)
{
AUnionType utb = (AUnionType) b;
tsb.addAll(utb.getTypes());
} else
{
tsb.add(b);
}
// Keep largest types which are compatible (eg. nat and int choses int)
Set<PType> result = new HashSet<PType>();
for (PType atype : tsa)
{
for (PType btype : tsb)
{
if (isSubType(atype, btype))
{
result.add(btype);
} else if (isSubType(btype, atype))
{
result.add(atype);
}
}
}
if (result.isEmpty())
{
return null;
} else
{
List<PType> list = new Vector<PType>();
list.addAll(result);
return AstFactory.newAUnionType(a.getLocation(), list);
}
}
/**
* Return the narrowest of two types/type lists.
*/
public synchronized List<PType> narrowest(List<PType> t1, List<PType> t2)
{
return allSubTypes(t1, t2, false) == Result.Yes ? t1 : t2;
}
public synchronized PType narrowest(PType t1, PType t2)
{
return isSubType(t1, t2) ? t1 : t2;
}
}