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.util.containers.ContainerUtil;
023 import org.jetbrains.annotations.NotNull;
024 import org.jetbrains.annotations.Nullable;
025 import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
026 import org.jetbrains.jet.lang.resolve.constants.IntegerValueTypeConstructor;
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.LinkedHashSet;
033 import java.util.Set;
034
035 import static org.jetbrains.jet.lang.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND;
036
037 public class TypeBoundsImpl implements TypeBounds {
038 private final TypeParameterDescriptor typeVariable;
039 private final Variance varianceOfPosition;
040 private final Set<Bound> bounds = Sets.newLinkedHashSet();
041
042 private Collection<JetType> resultValues;
043
044 public TypeBoundsImpl(
045 @NotNull TypeParameterDescriptor typeVariable,
046 @NotNull Variance varianceOfPosition
047 ) {
048 this.typeVariable = typeVariable;
049 this.varianceOfPosition = varianceOfPosition;
050 }
051
052 @NotNull
053 @Override
054 public Variance getVarianceOfPosition() {
055 return varianceOfPosition;
056 }
057
058 public void addBound(@NotNull BoundKind kind, @NotNull JetType type, @NotNull ConstraintPosition position) {
059 resultValues = null;
060 bounds.add(new Bound(type, kind, position));
061 }
062
063 @Override
064 public boolean isEmpty() {
065 return getValues().isEmpty();
066 }
067
068 @NotNull
069 @Override
070 public TypeParameterDescriptor getTypeVariable() {
071 return typeVariable;
072 }
073
074 @Override
075 @NotNull
076 public Collection<Bound> getBounds() {
077 return bounds;
078 }
079
080 @NotNull
081 private static Set<JetType> filterBounds(
082 @NotNull Collection<Bound> bounds,
083 @NotNull BoundKind kind
084 ) {
085 return filterBounds(bounds, kind, null);
086 }
087
088 @NotNull
089 private static Set<JetType> filterBounds(
090 @NotNull Collection<Bound> bounds,
091 @NotNull BoundKind kind,
092 @Nullable Collection<JetType> errorValues
093 ) {
094 Set<JetType> result = Sets.newLinkedHashSet();
095 for (Bound bound : bounds) {
096 if (bound.kind == kind) {
097 if (!ErrorUtils.containsErrorType(bound.type)) {
098 result.add(bound.type);
099 }
100 else if (errorValues != null) {
101 errorValues.add(bound.type);
102 }
103 }
104 }
105 return result;
106 }
107
108 /*package*/ TypeBoundsImpl copy() {
109 TypeBoundsImpl typeBounds = new TypeBoundsImpl(typeVariable, varianceOfPosition);
110 typeBounds.bounds.addAll(bounds);
111 typeBounds.resultValues = resultValues;
112 return typeBounds;
113 }
114
115 @NotNull
116 public TypeBoundsImpl filter(@NotNull final Condition<ConstraintPosition> condition) {
117 TypeBoundsImpl result = new TypeBoundsImpl(typeVariable, varianceOfPosition);
118 result.bounds.addAll(ContainerUtil.filter(bounds, new Condition<Bound>() {
119 @Override
120 public boolean value(Bound bound) {
121 return condition.value(bound.position);
122 }
123 }));
124 return result;
125 }
126
127 @Nullable
128 @Override
129 public JetType getValue() {
130 Collection<JetType> values = getValues();
131 if (values.size() == 1) {
132 return values.iterator().next();
133 }
134 return null;
135 }
136
137 @NotNull
138 @Override
139 public Collection<JetType> getValues() {
140 if (resultValues == null) {
141 resultValues = computeValues();
142 }
143 return resultValues;
144 }
145
146 @NotNull
147 private Collection<JetType> computeValues() {
148 Set<JetType> values = Sets.newLinkedHashSet();
149 if (bounds.isEmpty()) {
150 return Collections.emptyList();
151 }
152 boolean hasStrongBound = ContainerUtil.exists(bounds, new Condition<Bound>() {
153 @Override
154 public boolean value(Bound bound) {
155 return bound.position.isStrong();
156 }
157 });
158 if (!hasStrongBound) {
159 return Collections.emptyList();
160 }
161
162 Set<JetType> exactBounds = filterBounds(bounds, BoundKind.EXACT_BOUND, values);
163 if (exactBounds.size() == 1) {
164 JetType exactBound = exactBounds.iterator().next();
165 if (tryPossibleAnswer(exactBound)) {
166 return Collections.singleton(exactBound);
167 }
168 }
169 values.addAll(exactBounds);
170
171 Collection<JetType> numberLowerBounds = new LinkedHashSet<JetType>();
172 Collection<JetType> generalLowerBounds = new LinkedHashSet<JetType>();
173 filterNumberTypes(filterBounds(bounds, LOWER_BOUND, values), numberLowerBounds, generalLowerBounds);
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.DEFAULT, 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 static void filterNumberTypes(
213 @NotNull Collection<JetType> types,
214 @NotNull Collection<JetType> numberTypes,
215 @NotNull Collection<JetType> otherTypes
216 ) {
217 for (JetType type : types) {
218 if (type.getConstructor() instanceof IntegerValueTypeConstructor) {
219 numberTypes.add(type);
220 }
221 else {
222 otherTypes.add(type);
223 }
224 }
225 }
226
227 private boolean tryPossibleAnswer(@Nullable JetType possibleAnswer) {
228 if (possibleAnswer == null) return false;
229 if (!possibleAnswer.getConstructor().isDenotable()) return false;
230
231 for (Bound bound : bounds) {
232 switch (bound.kind) {
233 case LOWER_BOUND:
234 if (!JetTypeChecker.DEFAULT.isSubtypeOf(bound.type, possibleAnswer)) {
235 return false;
236 }
237 break;
238
239 case UPPER_BOUND:
240 if (!JetTypeChecker.DEFAULT.isSubtypeOf(possibleAnswer, bound.type)) {
241 return false;
242 }
243 break;
244
245 case EXACT_BOUND:
246 if (!JetTypeChecker.DEFAULT.equalTypes(bound.type, possibleAnswer)) {
247 return false;
248 }
249 break;
250 }
251 }
252 return true;
253 }
254 }