001/* 002 * Copyright 2010-2013 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.jetbrains.jet.lang.resolve.calls.inference; 018 019import com.google.common.base.Predicate; 020import com.google.common.collect.Collections2; 021import com.google.common.collect.Lists; 022import com.google.common.collect.Maps; 023import com.google.common.collect.Sets; 024import org.jetbrains.annotations.NotNull; 025import org.jetbrains.annotations.Nullable; 026import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; 027import org.jetbrains.jet.lang.types.*; 028import org.jetbrains.jet.lang.types.checker.JetTypeChecker; 029 030import java.util.*; 031 032public class ConstraintsUtil { 033 034 @NotNull 035 public static Set<JetType> getValues(@Nullable TypeConstraints typeConstraints) { 036 Set<JetType> values = Sets.newLinkedHashSet(); 037 if (typeConstraints != null && !typeConstraints.isEmpty()) { 038 if (typeConstraints.getExactBounds().size() == 1) { 039 if (verifyOneExactBound(typeConstraints)) { 040 JetType exactBound = typeConstraints.getExactBounds().iterator().next(); 041 return Collections.singleton(exactBound); 042 } 043 } 044 values.addAll(typeConstraints.getExactBounds()); 045 Collection<JetType> lowerBounds = filterNotContainingErrorType(typeConstraints.getLowerBounds()); 046 if (!lowerBounds.isEmpty()) { 047 JetType superTypeOfLowerBounds = CommonSupertypes.commonSupertype(lowerBounds); 048 for (JetType value : values) { 049 if (!JetTypeChecker.INSTANCE.isSubtypeOf(superTypeOfLowerBounds, value)) { 050 values.add(superTypeOfLowerBounds); 051 break; 052 } 053 } 054 if (values.isEmpty()) { 055 values.add(superTypeOfLowerBounds); 056 } 057 } 058 Collection<JetType> upperBounds = filterNotContainingErrorType(typeConstraints.getUpperBounds()); 059 if (!upperBounds.isEmpty()) { 060 //todo subTypeOfUpperBounds 061 JetType subTypeOfUpperBounds = upperBounds.iterator().next(); //todo 062 for (JetType value : values) { 063 if (!JetTypeChecker.INSTANCE.isSubtypeOf(value, subTypeOfUpperBounds)) { 064 values.add(subTypeOfUpperBounds); 065 break; 066 } 067 } 068 if (values.isEmpty()) { 069 values.add(subTypeOfUpperBounds); 070 } 071 } 072 } 073 return values; 074 } 075 076 private static boolean verifyOneExactBound(@NotNull TypeConstraints typeConstraints) { 077 JetType exactBound = typeConstraints.getExactBounds().iterator().next(); 078 for (JetType lowerBound : typeConstraints.getLowerBounds()) { 079 if (!JetTypeChecker.INSTANCE.isSubtypeOf(lowerBound, exactBound)) { 080 return false; 081 } 082 } 083 for (JetType upperBound : typeConstraints.getUpperBounds()) { 084 if (!JetTypeChecker.INSTANCE.isSubtypeOf(exactBound, upperBound)) { 085 return false; 086 } 087 } 088 return true; 089 } 090 091 @NotNull 092 private static Collection<JetType> filterNotContainingErrorType(@NotNull Collection<JetType> types) { 093 return Collections2.filter(types, new Predicate<JetType>() { 094 @Override 095 public boolean apply(@Nullable JetType type) { 096 if (ErrorUtils.containsErrorType(type)) return false; 097 return true; 098 } 099 }); 100 } 101 102 @Nullable 103 public static JetType getValue(@Nullable TypeConstraints typeConstraints) { 104 //todo all checks 105 //todo variance dependance 106 if (typeConstraints == null) { 107 //todo assert typeConstraints != null; 108 return null; 109 } 110 Set<JetType> values = getValues(typeConstraints); 111 if (values.size() == 1) { 112 return values.iterator().next(); 113 } 114 return null; 115 } 116 117 118 119 @Nullable 120 public static TypeParameterDescriptor getFirstConflictingParameter(@NotNull ConstraintSystem constraintSystem) { 121 for (TypeParameterDescriptor typeParameter : constraintSystem.getTypeVariables()) { 122 TypeConstraints constraints = constraintSystem.getTypeConstraints(typeParameter); 123 if (getValues(constraints).size() > 1) { 124 return typeParameter; 125 } 126 } 127 return null; 128 } 129 130 @NotNull 131 public static Collection<TypeSubstitutor> getSubstitutorsForConflictingParameters(@NotNull ConstraintSystem constraintSystem) { 132 TypeParameterDescriptor firstConflictingParameter = getFirstConflictingParameter(constraintSystem); 133 if (firstConflictingParameter == null) return Collections.emptyList(); 134 135 Collection<JetType> conflictingTypes = getValues(constraintSystem.getTypeConstraints(firstConflictingParameter)); 136 137 ArrayList<Map<TypeConstructor, TypeProjection>> substitutionContexts = Lists.newArrayList(); 138 for (JetType type : conflictingTypes) { 139 Map<TypeConstructor, TypeProjection> context = Maps.newLinkedHashMap(); 140 context.put(firstConflictingParameter.getTypeConstructor(), new TypeProjection(type)); 141 substitutionContexts.add(context); 142 } 143 144 for (TypeParameterDescriptor typeParameter : constraintSystem.getTypeVariables()) { 145 if (typeParameter == firstConflictingParameter) continue; 146 147 JetType safeType = getSafeValue(constraintSystem, typeParameter); 148 for (Map<TypeConstructor, TypeProjection> context : substitutionContexts) { 149 TypeProjection typeProjection = new TypeProjection(safeType); 150 context.put(typeParameter.getTypeConstructor(), typeProjection); 151 } 152 } 153 Collection<TypeSubstitutor> typeSubstitutors = Lists.newArrayList(); 154 for (Map<TypeConstructor, TypeProjection> context : substitutionContexts) { 155 typeSubstitutors.add(TypeSubstitutor.create(context)); 156 } 157 return typeSubstitutors; 158 } 159 160 @NotNull 161 public static JetType getSafeValue(@NotNull ConstraintSystem constraintSystem, @NotNull TypeParameterDescriptor typeParameter) { 162 TypeConstraints constraints = constraintSystem.getTypeConstraints(typeParameter); 163 JetType type = getValue(constraints); 164 if (type != null) { 165 return type; 166 } 167 //todo may be error type 168 return typeParameter.getUpperBoundsAsType(); 169 } 170 171 public static boolean checkUpperBoundIsSatisfied( 172 @NotNull ConstraintSystem constraintSystem, 173 @NotNull TypeParameterDescriptor typeParameter, 174 boolean substituteOtherTypeParametersInBound 175 ) { 176 TypeConstraints typeConstraints = constraintSystem.getTypeConstraints(typeParameter); 177 assert typeConstraints != null; 178 JetType type = getValue(typeConstraints); 179 if (type == null) return true; 180 for (JetType upperBound : typeParameter.getUpperBounds()) { 181 if (!substituteOtherTypeParametersInBound && TypeUtils.dependsOnTypeParameters(upperBound, constraintSystem.getTypeVariables())) { 182 continue; 183 } 184 JetType substitutedUpperBound = constraintSystem.getResultingSubstitutor().substitute(upperBound, Variance.INVARIANT); 185 186 assert substitutedUpperBound != null : "We wanted to substitute projections as a result for " + typeParameter; 187 if (!JetTypeChecker.INSTANCE.isSubtypeOf(type, substitutedUpperBound)) { 188 return false; 189 } 190 } 191 return true; 192 } 193 194 public static boolean checkBoundsAreSatisfied( 195 @NotNull ConstraintSystem constraintSystem, 196 boolean substituteOtherTypeParametersInBounds 197 ) { 198 for (TypeParameterDescriptor typeVariable : constraintSystem.getTypeVariables()) { 199 if (!checkUpperBoundIsSatisfied(constraintSystem, typeVariable, substituteOtherTypeParametersInBounds)) { 200 return false; 201 } 202 } 203 return true; 204 } 205}