/** * Implement the Dart function subtype rule. Unlike the classic arrow rule (return type is * covariant, and parameter types are contravariant), in Dart they must just be assignable. */ private boolean isSubtypeOfFunction(FunctionType t, FunctionType s) { if (s.getKind() == TypeKind.DYNAMIC || t.getKind() == TypeKind.DYNAMIC) { return true; } // Classic: return type is covariant; Dart: assignable. if (!isAssignable(t.getReturnType(), s.getReturnType())) { // A function that returns a value can be used as a function where you ignore the value. if (!s.getReturnType().equals(typeProvider.getVoidType())) { return false; } } Type tRest = t.getRest(); Type sRest = s.getRest(); if ((tRest == null) != (sRest == null)) { return false; } if (tRest != null) { // Classic: parameter types are contravariant; Dart: assignable. if (!isAssignable(sRest, tRest)) { return false; } } { Map<String, Type> sOpti = s.getOptionalParameterTypes(); Map<String, Type> tOpti = t.getOptionalParameterTypes(); if (tOpti.size() < sOpti.size()) { return false; } Iterator<Entry<String, Type>> tList = tOpti.entrySet().iterator(); Iterator<Entry<String, Type>> sList = sOpti.entrySet().iterator(); while (sList.hasNext()) { if (!tList.hasNext()) { return false; } Entry<String, Type> sEntry = sList.next(); Entry<String, Type> tEntry = tList.next(); if (!isAssignable(tEntry.getValue(), sEntry.getValue())) { return false; } } } Map<String, Type> tNamed = t.getNamedParameterTypes(); Map<String, Type> sNamed = s.getNamedParameterTypes(); if (tNamed.isEmpty() && !sNamed.isEmpty()) { return false; } // T's named parameters must be in the same order and assignable to S's but // maybe a superset. if (!sNamed.isEmpty()) { LinkedHashMap<String, Type> tMap = (LinkedHashMap<String, Type>) (tNamed); LinkedHashMap<String, Type> sMap = (LinkedHashMap<String, Type>) (sNamed); if (!tMap.keySet().containsAll(sMap.keySet())) { return false; } for (Entry<String, Type> entry : sMap.entrySet()) { String name = entry.getKey(); Type sType = sMap.get(name); Type tType = tMap.get(name); if (!isAssignable(tType, sType)) { return false; } } // Iterator<Entry<String, Type>> tList = tMap.entrySet().iterator(); // Iterator<Entry<String, Type>> sList = sMap.entrySet().iterator(); // // t named parameters must start with the named parameters of s // while (sList.hasNext()) { // if (!tList.hasNext()) { // return false; // } // Entry<String, Type> sEntry = sList.next(); // Entry<String, Type> tEntry = tList.next(); // if (!sEntry.getKey().equals(tEntry.getKey())) { // return false; // } // // Classic: parameter types are contravariant; Dart: assignable. // if (!isAssignable(tEntry.getValue(), sEntry.getValue())) { // return false; // } // } } // Classic: parameter types are contravariant; Dart: assignable. return areAssignable(s.getParameterTypes().iterator(), t.getParameterTypes().iterator()); }