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
017 package org.jetbrains.jet.lang.resolve.calls.inference;
018
019 import com.google.common.collect.Lists;
020 import com.google.common.collect.Maps;
021 import com.google.common.collect.Sets;
022 import com.intellij.openapi.util.Pair;
023 import com.intellij.util.containers.ContainerUtil;
024 import org.jetbrains.annotations.NotNull;
025 import org.jetbrains.annotations.Nullable;
026 import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
027 import org.jetbrains.jet.lang.types.*;
028 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
029
030 import java.util.*;
031
032 public 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 return values;
039 }
040 TypeConstraints typeConstraintsWithoutErrorTypes = filterNotContainingErrorType(typeConstraints, values);
041 Collection<JetType> exactBounds = typeConstraintsWithoutErrorTypes.getExactBounds();
042 if (exactBounds.size() == 1) {
043 JetType exactBound = exactBounds.iterator().next();
044 if (trySuggestion(exactBound, typeConstraints)) {
045 return Collections.singleton(exactBound);
046 }
047 }
048 values.addAll(exactBounds);
049
050 Pair<Collection<JetType>, Collection<JetType>> pair =
051 TypeUtils.filterNumberTypes(typeConstraintsWithoutErrorTypes.getLowerBounds());
052 Collection<JetType> generalLowerBounds = pair.getFirst();
053 Collection<JetType> numberLowerBounds = pair.getSecond();
054
055 JetType superTypeOfLowerBounds = commonSupertype(generalLowerBounds);
056 if (trySuggestion(superTypeOfLowerBounds, typeConstraints)) {
057 return Collections.singleton(superTypeOfLowerBounds);
058 }
059 ContainerUtil.addIfNotNull(superTypeOfLowerBounds, values);
060
061 Collection<JetType> upperBounds = typeConstraintsWithoutErrorTypes.getUpperBounds();
062 for (JetType upperBound : upperBounds) {
063 if (trySuggestion(upperBound, typeConstraints)) {
064 return Collections.singleton(upperBound);
065 }
066 }
067 //todo
068 //fun <T> foo(t: T, consumer: Consumer<T>): T
069 //foo(1, c: Consumer<Any>) - infer Int, not Any here
070
071 values.addAll(typeConstraintsWithoutErrorTypes.getUpperBounds());
072
073 JetType superTypeOfNumberLowerBounds = commonSupertypeForNumberTypes(numberLowerBounds);
074 if (trySuggestion(superTypeOfNumberLowerBounds, typeConstraints)) {
075 return Collections.singleton(superTypeOfNumberLowerBounds);
076 }
077 ContainerUtil.addIfNotNull(superTypeOfNumberLowerBounds, values);
078
079 if (superTypeOfLowerBounds != null && superTypeOfNumberLowerBounds != null) {
080 JetType superTypeOfAllLowerBounds = commonSupertype(Lists.newArrayList(superTypeOfLowerBounds, superTypeOfNumberLowerBounds));
081 if (trySuggestion(superTypeOfAllLowerBounds, typeConstraints)) {
082 return Collections.singleton(superTypeOfAllLowerBounds);
083 }
084 }
085 return values;
086 }
087
088 @Nullable
089 private static JetType commonSupertype(@NotNull Collection<JetType> lowerBounds) {
090 if (lowerBounds.isEmpty()) return null;
091 if (lowerBounds.size() == 1) {
092 JetType type = lowerBounds.iterator().next();
093 if (type.getConstructor() instanceof IntersectionTypeConstructor) {
094 return commonSupertype(type.getConstructor().getSupertypes());
095 }
096 }
097 return CommonSupertypes.commonSupertype(lowerBounds);
098 }
099
100 @Nullable
101 private static JetType commonSupertypeForNumberTypes(@NotNull Collection<JetType> numberLowerBounds) {
102 if (numberLowerBounds.isEmpty()) return null;
103 return TypeUtils.commonSupertypeForNumberTypes(numberLowerBounds);
104 }
105
106 private static boolean trySuggestion(
107 @Nullable JetType suggestion,
108 @NotNull TypeConstraints typeConstraints
109 ) {
110 if (suggestion == null) return false;
111 if (!suggestion.getConstructor().isDenotable()) return false;
112 if (typeConstraints.getExactBounds().size() > 1) return false;
113
114 for (JetType exactBound : typeConstraints.getExactBounds()) {
115 if (!JetTypeChecker.INSTANCE.equalTypes(exactBound, suggestion)) {
116 return false;
117 }
118 }
119 for (JetType lowerBound : typeConstraints.getLowerBounds()) {
120 if (!JetTypeChecker.INSTANCE.isSubtypeOf(lowerBound, suggestion)) {
121 return false;
122 }
123 }
124 for (JetType upperBound : typeConstraints.getUpperBounds()) {
125 if (!JetTypeChecker.INSTANCE.isSubtypeOf(suggestion, upperBound)) {
126 return false;
127 }
128 }
129 return true;
130 }
131
132 @NotNull
133 private static TypeConstraints filterNotContainingErrorType(
134 @NotNull TypeConstraints typeConstraints,
135 @NotNull Collection<JetType> values
136 ) {
137 TypeConstraintsImpl typeConstraintsWithoutErrorType = new TypeConstraintsImpl(typeConstraints.getVarianceOfPosition());
138 Collection<Pair<TypeConstraintsImpl.BoundKind, JetType>> allBounds = ((TypeConstraintsImpl) typeConstraints).getAllBounds();
139 for (Pair<TypeConstraintsImpl.BoundKind, JetType> pair : allBounds) {
140 TypeConstraintsImpl.BoundKind boundKind = pair.getFirst();
141 JetType type = pair.getSecond();
142 if (ErrorUtils.containsErrorType(type)) {
143 values.add(type);
144 }
145 else if (type != null) {
146 typeConstraintsWithoutErrorType.addBound(boundKind, type);
147 }
148 }
149 return typeConstraintsWithoutErrorType;
150 }
151
152 @Nullable
153 public static JetType getValue(@Nullable TypeConstraints typeConstraints) {
154 //todo all checks
155 //todo variance dependance
156 if (typeConstraints == null) {
157 //todo assert typeConstraints != null;
158 return null;
159 }
160 Set<JetType> values = getValues(typeConstraints);
161 if (values.size() == 1) {
162 return values.iterator().next();
163 }
164 return null;
165 }
166
167
168
169 @Nullable
170 public static TypeParameterDescriptor getFirstConflictingParameter(@NotNull ConstraintSystem constraintSystem) {
171 for (TypeParameterDescriptor typeParameter : constraintSystem.getTypeVariables()) {
172 TypeConstraints constraints = constraintSystem.getTypeConstraints(typeParameter);
173 if (getValues(constraints).size() > 1) {
174 return typeParameter;
175 }
176 }
177 return null;
178 }
179
180 @NotNull
181 public static Collection<TypeSubstitutor> getSubstitutorsForConflictingParameters(@NotNull ConstraintSystem constraintSystem) {
182 TypeParameterDescriptor firstConflictingParameter = getFirstConflictingParameter(constraintSystem);
183 if (firstConflictingParameter == null) return Collections.emptyList();
184
185 Collection<JetType> conflictingTypes = getValues(constraintSystem.getTypeConstraints(firstConflictingParameter));
186
187 ArrayList<Map<TypeConstructor, TypeProjection>> substitutionContexts = Lists.newArrayList();
188 for (JetType type : conflictingTypes) {
189 Map<TypeConstructor, TypeProjection> context = Maps.newLinkedHashMap();
190 context.put(firstConflictingParameter.getTypeConstructor(), new TypeProjection(type));
191 substitutionContexts.add(context);
192 }
193
194 for (TypeParameterDescriptor typeParameter : constraintSystem.getTypeVariables()) {
195 if (typeParameter == firstConflictingParameter) continue;
196
197 JetType safeType = getSafeValue(constraintSystem, typeParameter);
198 for (Map<TypeConstructor, TypeProjection> context : substitutionContexts) {
199 TypeProjection typeProjection = new TypeProjection(safeType);
200 context.put(typeParameter.getTypeConstructor(), typeProjection);
201 }
202 }
203 Collection<TypeSubstitutor> typeSubstitutors = Lists.newArrayList();
204 for (Map<TypeConstructor, TypeProjection> context : substitutionContexts) {
205 typeSubstitutors.add(TypeSubstitutor.create(context));
206 }
207 return typeSubstitutors;
208 }
209
210 @NotNull
211 public static JetType getSafeValue(@NotNull ConstraintSystem constraintSystem, @NotNull TypeParameterDescriptor typeParameter) {
212 TypeConstraints constraints = constraintSystem.getTypeConstraints(typeParameter);
213 JetType type = getValue(constraints);
214 if (type != null) {
215 return type;
216 }
217 //todo may be error type
218 return typeParameter.getUpperBoundsAsType();
219 }
220
221 public static boolean checkUpperBoundIsSatisfied(
222 @NotNull ConstraintSystem constraintSystem,
223 @NotNull TypeParameterDescriptor typeParameter,
224 boolean substituteOtherTypeParametersInBound
225 ) {
226 TypeConstraints typeConstraints = constraintSystem.getTypeConstraints(typeParameter);
227 assert typeConstraints != null;
228 JetType type = getValue(typeConstraints);
229 if (type == null) return true;
230 for (JetType upperBound : typeParameter.getUpperBounds()) {
231 if (!substituteOtherTypeParametersInBound && TypeUtils.dependsOnTypeParameters(upperBound, constraintSystem.getTypeVariables())) {
232 continue;
233 }
234 JetType substitutedUpperBound = constraintSystem.getResultingSubstitutor().substitute(upperBound, Variance.INVARIANT);
235
236 assert substitutedUpperBound != null : "We wanted to substitute projections as a result for " + typeParameter;
237 if (!JetTypeChecker.INSTANCE.isSubtypeOf(type, substitutedUpperBound)) {
238 return false;
239 }
240 }
241 return true;
242 }
243
244 public static boolean checkBoundsAreSatisfied(
245 @NotNull ConstraintSystem constraintSystem,
246 boolean substituteOtherTypeParametersInBounds
247 ) {
248 for (TypeParameterDescriptor typeVariable : constraintSystem.getTypeVariables()) {
249 if (!checkUpperBoundIsSatisfied(constraintSystem, typeVariable, substituteOtherTypeParametersInBounds)) {
250 return false;
251 }
252 }
253 return true;
254 }
255 }