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.Sets;
021 import com.intellij.openapi.util.Condition;
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.Collection;
031 import java.util.Collections;
032 import java.util.Set;
033
034 import static org.jetbrains.jet.lang.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND;
035
036 public class TypeBoundsImpl implements TypeBounds {
037 private final TypeParameterDescriptor typeVariable;
038 private final Variance varianceOfPosition;
039 private final Set<Bound> bounds = Sets.newLinkedHashSet();
040
041 private Collection<JetType> resultValues;
042
043 public TypeBoundsImpl(
044 @NotNull TypeParameterDescriptor typeVariable,
045 @NotNull Variance varianceOfPosition
046 ) {
047 this.typeVariable = typeVariable;
048 this.varianceOfPosition = varianceOfPosition;
049 }
050
051 @NotNull
052 @Override
053 public Variance getVarianceOfPosition() {
054 return varianceOfPosition;
055 }
056
057 public void addBound(@NotNull BoundKind kind, @NotNull JetType type, @NotNull ConstraintPosition position) {
058 resultValues = null;
059 bounds.add(new Bound(type, kind, position));
060 }
061
062 @Override
063 public boolean isEmpty() {
064 return getValues().isEmpty();
065 }
066
067 @NotNull
068 @Override
069 public TypeParameterDescriptor getTypeVariable() {
070 return typeVariable;
071 }
072
073 @Override
074 @NotNull
075 public Collection<Bound> getBounds() {
076 return bounds;
077 }
078
079 @NotNull
080 private static Set<JetType> filterBounds(
081 @NotNull Collection<Bound> bounds,
082 @NotNull BoundKind kind
083 ) {
084 return filterBounds(bounds, kind, null);
085 }
086
087 @NotNull
088 private static Set<JetType> filterBounds(
089 @NotNull Collection<Bound> bounds,
090 @NotNull BoundKind kind,
091 @Nullable Collection<JetType> errorValues
092 ) {
093 Set<JetType> result = Sets.newLinkedHashSet();
094 for (Bound bound : bounds) {
095 if (bound.kind == kind) {
096 if (!ErrorUtils.containsErrorType(bound.type)) {
097 result.add(bound.type);
098 }
099 else if (errorValues != null) {
100 errorValues.add(bound.type);
101 }
102 }
103 }
104 return result;
105 }
106
107 /*package*/ TypeBoundsImpl copy() {
108 TypeBoundsImpl typeBounds = new TypeBoundsImpl(typeVariable, varianceOfPosition);
109 typeBounds.bounds.addAll(bounds);
110 typeBounds.resultValues = resultValues;
111 return typeBounds;
112 }
113
114 @NotNull
115 public TypeBoundsImpl filter(@NotNull final Condition<ConstraintPosition> condition) {
116 TypeBoundsImpl result = new TypeBoundsImpl(typeVariable, varianceOfPosition);
117 result.bounds.addAll(ContainerUtil.filter(bounds, new Condition<Bound>() {
118 @Override
119 public boolean value(Bound bound) {
120 return condition.value(bound.position);
121 }
122 }));
123 return result;
124 }
125
126 @Nullable
127 @Override
128 public JetType getValue() {
129 Collection<JetType> values = getValues();
130 if (values.size() == 1) {
131 return values.iterator().next();
132 }
133 return null;
134 }
135
136 @NotNull
137 @Override
138 public Collection<JetType> getValues() {
139 if (resultValues == null) {
140 resultValues = computeValues();
141 }
142 return resultValues;
143 }
144
145 @NotNull
146 private Collection<JetType> computeValues() {
147 Set<JetType> values = Sets.newLinkedHashSet();
148 if (bounds.isEmpty()) {
149 return Collections.emptyList();
150 }
151 boolean hasStrongBound = ContainerUtil.exists(bounds, new Condition<Bound>() {
152 @Override
153 public boolean value(Bound bound) {
154 return bound.position.isStrong();
155 }
156 });
157 if (!hasStrongBound) {
158 return Collections.emptyList();
159 }
160
161 Set<JetType> exactBounds = filterBounds(bounds, BoundKind.EXACT_BOUND, values);
162 if (exactBounds.size() == 1) {
163 JetType exactBound = exactBounds.iterator().next();
164 if (tryPossibleAnswer(exactBound)) {
165 return Collections.singleton(exactBound);
166 }
167 }
168 values.addAll(exactBounds);
169
170 Pair<Collection<JetType>, Collection<JetType>> pair =
171 TypeUtils.filterNumberTypes(filterBounds(bounds, LOWER_BOUND, values));
172 Collection<JetType> generalLowerBounds = pair.getFirst();
173 Collection<JetType> numberLowerBounds = pair.getSecond();
174
175 JetType superTypeOfLowerBounds = CommonSupertypes.commonSupertypeForNonDenotableTypes(generalLowerBounds);
176 if (tryPossibleAnswer(superTypeOfLowerBounds)) {
177 return Collections.singleton(superTypeOfLowerBounds);
178 }
179 ContainerUtil.addIfNotNull(superTypeOfLowerBounds, values);
180
181 //todo
182 //fun <T> foo(t: T, consumer: Consumer<T>): T
183 //foo(1, c: Consumer<Any>) - infer Int, not Any here
184
185 JetType superTypeOfNumberLowerBounds = TypeUtils.commonSupertypeForNumberTypes(numberLowerBounds);
186 if (tryPossibleAnswer(superTypeOfNumberLowerBounds)) {
187 return Collections.singleton(superTypeOfNumberLowerBounds);
188 }
189 ContainerUtil.addIfNotNull(superTypeOfNumberLowerBounds, values);
190
191 if (superTypeOfLowerBounds != null && superTypeOfNumberLowerBounds != null) {
192 JetType superTypeOfAllLowerBounds = CommonSupertypes.commonSupertypeForNonDenotableTypes(
193 Lists.newArrayList(superTypeOfLowerBounds, superTypeOfNumberLowerBounds));
194 if (tryPossibleAnswer(superTypeOfAllLowerBounds)) {
195 return Collections.singleton(superTypeOfAllLowerBounds);
196 }
197 }
198
199 Set<JetType> upperBounds = filterBounds(bounds, BoundKind.UPPER_BOUND, values);
200 JetType intersectionOfUpperBounds = TypeUtils.intersect(JetTypeChecker.INSTANCE, upperBounds);
201 if (!upperBounds.isEmpty() && intersectionOfUpperBounds != null) {
202 if (tryPossibleAnswer(intersectionOfUpperBounds)) {
203 return Collections.singleton(intersectionOfUpperBounds);
204 }
205 }
206
207 values.addAll(filterBounds(bounds, BoundKind.UPPER_BOUND));
208
209 return values;
210 }
211
212 private boolean tryPossibleAnswer(@Nullable JetType possibleAnswer) {
213 if (possibleAnswer == null) return false;
214 if (!possibleAnswer.getConstructor().isDenotable()) return false;
215
216 for (Bound bound : bounds) {
217 switch (bound.kind) {
218 case LOWER_BOUND:
219 if (!JetTypeChecker.INSTANCE.isSubtypeOf(bound.type, possibleAnswer)) {
220 return false;
221 }
222 break;
223
224 case UPPER_BOUND:
225 if (!JetTypeChecker.INSTANCE.isSubtypeOf(possibleAnswer, bound.type)) {
226 return false;
227 }
228 break;
229
230 case EXACT_BOUND:
231 if (!JetTypeChecker.INSTANCE.equalTypes(bound.type, possibleAnswer)) {
232 return false;
233 }
234 break;
235 }
236 }
237 return true;
238 }
239 }