/** * Copyright (c) 2009-2011, The HATS Consortium. All rights reserved. * This file is licensed under the terms of the Modified BSD License. */ package abs.frontend.typechecker; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.HashSet; import abs.frontend.ast.*; public class DataTypeType extends Type { private final DataTypeDecl decl; private final List<Type> typeArgs = new ArrayList<Type>(); @Override public Type copy() { return new DataTypeType(decl,typeArgs); } @Override public Type fullCopy() { List<Type> typeArgCopy = new ArrayList<Type>(); for (Type t : typeArgs) { typeArgCopy.add(t.fullCopy()); } Type copy = new DataTypeType(decl,typeArgCopy); copy.metaData.putAll(metaData); return copy; } public DataTypeType(DataTypeDecl decl) { this(decl, new Type[0]); } public DataTypeType(DataTypeDecl decl, ParametricDataTypeUse typeUses) { this(decl, typeUses.getTypes().toArray(new Type[0])); } public DataTypeType(DataTypeDecl decl, Type... typeArgs) { this.decl = decl; for (Type t : typeArgs) { if (t == null) throw new IllegalArgumentException("Type argument was null"); this.typeArgs.add(t); } } public DataTypeType(DataTypeDecl decl, List<Type> typeArgs) { this.decl = decl; this.typeArgs.addAll(typeArgs); } public List<Type> getTypeArgs() { return Collections.unmodifiableList(typeArgs); } public int numTypeArgs() { return typeArgs.size(); } public Type getTypeArg(int i) { return typeArgs.get(i); } public DataTypeType withTypeArgs(Type... typeArgs) { DataTypeType copy = (DataTypeType) copy(); copy.typeArgs.addAll(Arrays.asList(typeArgs)); if (copy.typeArgs.contains(null)) throw new IllegalArgumentException("One type argument was null"); return copy; } public boolean hasTypeArgs() { return !typeArgs.isEmpty(); } @Override public boolean hasReferences() { Set<DataTypeDecl> checkedDecls = new HashSet<DataTypeDecl>(); return hasReferences(checkedDecls); } /** * Helper method to handle recursive data types. * This will also handle mutually recursive data types, * which would be difficult to handle in aspects (at least declarative ones). */ private boolean hasReferences(Set<DataTypeDecl> checkedDecls) { if (isFutureType()) { return true; } if (checkedDecls.contains(decl)) { return false; } checkedDecls.add(decl); // Check if any type arguments may contain references // Expect that they will be used in constructors for (Type t : typeArgs) { if (t.isReferenceType()) { return true; } else if (t.isDataType() && ((DataTypeType) t).hasReferences(checkedDecls)) { return true; } } // Check if constructors use reference types // Type parameters are ignored here for (DataConstructor c : decl.getDataConstructors()) { for (ConstructorArg arg : c.getConstructorArgs()) { Type t = arg.getType(); if (t.isReferenceType()) { return true; } else if (t.isDataType() && ((DataTypeType) t).hasReferences(checkedDecls)) { return true; } } } return false; } @Override public boolean isDataType() { return true; } @Override public boolean equals(Object o) { if (!(o instanceof DataTypeType)) return false; DataTypeType t = (DataTypeType) o; if (!t.decl.equals(this.decl)) return false; if (numTypeArgs() != t.numTypeArgs()) return false; for (int i = 0; i < numTypeArgs(); i++) { if (!getTypeArg(i).equals(t.getTypeArg(i))) return false; } return true; } @Override public boolean isAssignableTo(Type t) { return this.isAssignableTo(t, true); } @Override public boolean isAssignableTo(Type t, boolean considerSubtyping) { if (super.isAssignableTo(t)) return true; if (!(t instanceof DataTypeType)) return false; DataTypeType dt = (DataTypeType) t; if (!dt.decl.equals(this.decl)) { // Int can be assigned to Rat if (this.isNumericType() && dt.isRatType()) return true; return false; } if (numTypeArgs() != dt.numTypeArgs()) return false; for (int i = 0; i < numTypeArgs(); i++) { if (!getTypeArg(i).isAssignableTo(dt.getTypeArg(i), true)) return false; } return true; } @Override public int hashCode() { return decl.hashCode(); } public DataTypeDecl getDecl() { return decl; } @Override public boolean isAnnotationType() { for (Annotation a :decl.getAnnotations()) { try { Type t = a.getType(); if (t.isDataType()) { DataTypeType dt = (DataTypeType) t; if (dt.getDecl().getName().equals("Annotation")) { if (a.getValue() instanceof DataConstructorExp) { DataConstructorExp dexp = (DataConstructorExp) a.getValue(); if (dexp.getDecl().getName().equals("TypeAnnotation")) return true; } } } } catch (TypeCheckerException e) { // ignore illegally typed annotations (for now at least) continue; } } return false; } public boolean isFutureType() { return decl.getName().equals("Fut"); } public boolean isBoolType() { return decl.getName().equals("Bool"); } public boolean isUnitType() { return decl.getName().equals("Unit"); } public boolean isIntType() { return decl.getName().equals("Int"); } public boolean isRatType() { return decl.getName().equals("Rat"); } public boolean isStringType() { return decl.getName().equals("String"); } public boolean isExceptionType() { return decl.getName().equals("Exception"); } public String toString() { StringBuffer buf = new StringBuffer(super.toString()); if (hasTypeArgs()) { buf.append('<'); boolean first = true; for (Type t : typeArgs) { if (!first) buf.append(','); if (t != null) buf.append(t.toString()); else buf.append("<unknown>"); first = false; } buf.append('>'); } return buf.toString(); } @Override public String getModuleName() { return decl.getModuleDecl().getName(); } @Override public String getSimpleName() { return decl.getName(); } public Type substituteTypeParams(Type t) { if (!hasTypeArgs()) return t; if (!(t.isDataType() || t.isTypeParameter())) return t; Map<String, Type> substitution = getSubstitutionMap(); if (t.isDataType()) { DataTypeType dt = (DataTypeType) t; List<Type> substitutedArgs = new ArrayList<Type>(); for (Type arg : dt.getTypeArgs()) { if (!arg.isTypeParameter()) { substitutedArgs.add(arg); } else { TypeParameter tp = (TypeParameter) arg; Type st = substitution.get(tp.getDecl().getName()); assert st != null : "We're pretty sure getSubstitution() took care of it: "+tp.getDecl().getName(); substitutedArgs.add(st); } } return new DataTypeType(dt.getDecl(), substitutedArgs.toArray(new Type[0])); } else { TypeParameter tp = (TypeParameter) t; return substitution.get(tp.getDecl().getName()); } } private Map<String, Type> getSubstitutionMap() { Map<String, Type> substitution = new HashMap<String, Type>(); ParametricDataTypeDecl pd = (ParametricDataTypeDecl) decl; for (int i = 0; i < numTypeArgs(); i++) { substitution.put(pd.getTypeParameter(i).getName(), getTypeArg(i)); } return substitution; } @Override public Type applyBinding(Map<TypeParameter, Type> binding) { if (hasTypeArgs()) { List<Type> argTypes = TypeCheckerHelper.applyBindings(binding,getTypeArgs()); return new DataTypeType(getDecl(), argTypes); } else return super.applyBinding(binding); } @Override public DataTypeUse toUse() { if (hasTypeArgs()) { abs.frontend.ast.List<TypeUse> ls = new abs.frontend.ast.List<TypeUse>(); for (Type arg : getTypeArgs()) { ls.add(arg.toUse()); } return new ParametricDataTypeUse(getQualifiedName(), new abs.frontend.ast.List<Annotation>(), ls); } else { return new DataTypeUse(getQualifiedName(), new abs.frontend.ast.List<Annotation>()); } } }