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    }