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