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    }