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.base.Predicate;
020    import com.google.common.collect.Collections2;
021    import com.google.common.collect.Lists;
022    import com.google.common.collect.Maps;
023    import com.google.common.collect.Sets;
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.*;
031    
032    public class ConstraintsUtil {
033    
034        @NotNull
035        public static Set<JetType> getValues(@Nullable TypeConstraints typeConstraints) {
036            Set<JetType> values = Sets.newLinkedHashSet();
037            if (typeConstraints != null && !typeConstraints.isEmpty()) {
038                if (typeConstraints.getExactBounds().size() == 1) {
039                    if (verifyOneExactBound(typeConstraints)) {
040                        JetType exactBound = typeConstraints.getExactBounds().iterator().next();
041                        return Collections.singleton(exactBound);
042                    }
043                }
044                values.addAll(typeConstraints.getExactBounds());
045                Collection<JetType> lowerBounds = filterNotContainingErrorType(typeConstraints.getLowerBounds());
046                if (!lowerBounds.isEmpty()) {
047                    JetType superTypeOfLowerBounds = CommonSupertypes.commonSupertype(lowerBounds);
048                    for (JetType value : values) {
049                        if (!JetTypeChecker.INSTANCE.isSubtypeOf(superTypeOfLowerBounds, value)) {
050                            values.add(superTypeOfLowerBounds);
051                            break;
052                        }
053                    }
054                    if (values.isEmpty()) {
055                        values.add(superTypeOfLowerBounds);
056                    }
057                }
058                Collection<JetType> upperBounds = filterNotContainingErrorType(typeConstraints.getUpperBounds());
059                if (!upperBounds.isEmpty()) {
060                    //todo subTypeOfUpperBounds
061                    JetType subTypeOfUpperBounds = upperBounds.iterator().next(); //todo
062                    for (JetType value : values) {
063                        if (!JetTypeChecker.INSTANCE.isSubtypeOf(value, subTypeOfUpperBounds)) {
064                            values.add(subTypeOfUpperBounds);
065                            break;
066                        }
067                    }
068                    if (values.isEmpty()) {
069                        values.add(subTypeOfUpperBounds);
070                    }
071                }
072            }
073            return values;
074        }
075    
076        private static boolean verifyOneExactBound(@NotNull TypeConstraints typeConstraints) {
077            JetType exactBound = typeConstraints.getExactBounds().iterator().next();
078            for (JetType lowerBound : typeConstraints.getLowerBounds()) {
079                if (!JetTypeChecker.INSTANCE.isSubtypeOf(lowerBound, exactBound)) {
080                    return false;
081                }
082            }
083            for (JetType upperBound : typeConstraints.getUpperBounds()) {
084                if (!JetTypeChecker.INSTANCE.isSubtypeOf(exactBound, upperBound)) {
085                    return false;
086                }
087            }
088            return true;
089        }
090    
091        @NotNull
092        private static Collection<JetType> filterNotContainingErrorType(@NotNull Collection<JetType> types) {
093            return Collections2.filter(types, new Predicate<JetType>() {
094                @Override
095                public boolean apply(@Nullable JetType type) {
096                    if (ErrorUtils.containsErrorType(type)) return false;
097                    return true;
098                }
099            });
100        }
101    
102        @Nullable
103        public static JetType getValue(@Nullable TypeConstraints typeConstraints) {
104            //todo all checks
105            //todo variance dependance
106            if (typeConstraints == null) {
107                //todo assert typeConstraints != null;
108                return null;
109            }
110            Set<JetType> values = getValues(typeConstraints);
111            if (values.size() == 1) {
112                return values.iterator().next();
113            }
114            return null;
115        }
116    
117    
118    
119        @Nullable
120        public static TypeParameterDescriptor getFirstConflictingParameter(@NotNull ConstraintSystem constraintSystem) {
121            for (TypeParameterDescriptor typeParameter : constraintSystem.getTypeVariables()) {
122                TypeConstraints constraints = constraintSystem.getTypeConstraints(typeParameter);
123                if (getValues(constraints).size() > 1) {
124                    return typeParameter;
125                }
126            }
127            return null;
128        }
129    
130        @NotNull
131        public static Collection<TypeSubstitutor> getSubstitutorsForConflictingParameters(@NotNull ConstraintSystem constraintSystem) {
132            TypeParameterDescriptor firstConflictingParameter = getFirstConflictingParameter(constraintSystem);
133            if (firstConflictingParameter == null) return Collections.emptyList();
134    
135            Collection<JetType> conflictingTypes = getValues(constraintSystem.getTypeConstraints(firstConflictingParameter));
136    
137            ArrayList<Map<TypeConstructor, TypeProjection>> substitutionContexts = Lists.newArrayList();
138            for (JetType type : conflictingTypes) {
139                Map<TypeConstructor, TypeProjection> context = Maps.newLinkedHashMap();
140                context.put(firstConflictingParameter.getTypeConstructor(), new TypeProjection(type));
141                substitutionContexts.add(context);
142            }
143    
144            for (TypeParameterDescriptor typeParameter : constraintSystem.getTypeVariables()) {
145                if (typeParameter == firstConflictingParameter) continue;
146    
147                JetType safeType = getSafeValue(constraintSystem, typeParameter);
148                for (Map<TypeConstructor, TypeProjection> context : substitutionContexts) {
149                    TypeProjection typeProjection = new TypeProjection(safeType);
150                    context.put(typeParameter.getTypeConstructor(), typeProjection);
151                }
152            }
153            Collection<TypeSubstitutor> typeSubstitutors = Lists.newArrayList();
154            for (Map<TypeConstructor, TypeProjection> context : substitutionContexts) {
155                typeSubstitutors.add(TypeSubstitutor.create(context));
156            }
157            return typeSubstitutors;
158        }
159    
160        @NotNull
161        public static JetType getSafeValue(@NotNull ConstraintSystem constraintSystem, @NotNull TypeParameterDescriptor typeParameter) {
162            TypeConstraints constraints = constraintSystem.getTypeConstraints(typeParameter);
163            JetType type = getValue(constraints);
164            if (type != null) {
165                return type;
166            }
167            //todo may be error type
168            return typeParameter.getUpperBoundsAsType();
169        }
170    
171        public static boolean checkUpperBoundIsSatisfied(
172                @NotNull ConstraintSystem constraintSystem,
173                @NotNull TypeParameterDescriptor typeParameter,
174                boolean substituteOtherTypeParametersInBound
175        ) {
176            TypeConstraints typeConstraints = constraintSystem.getTypeConstraints(typeParameter);
177            assert typeConstraints != null;
178            JetType type = getValue(typeConstraints);
179            if (type == null) return true;
180            for (JetType upperBound : typeParameter.getUpperBounds()) {
181                if (!substituteOtherTypeParametersInBound && TypeUtils.dependsOnTypeParameters(upperBound, constraintSystem.getTypeVariables())) {
182                    continue;
183                }
184                JetType substitutedUpperBound = constraintSystem.getResultingSubstitutor().substitute(upperBound, Variance.INVARIANT);
185    
186                assert substitutedUpperBound != null : "We wanted to substitute projections as a result for " + typeParameter;
187                if (!JetTypeChecker.INSTANCE.isSubtypeOf(type, substitutedUpperBound)) {
188                    return false;
189                }
190            }
191            return true;
192        }
193    
194        public static boolean checkBoundsAreSatisfied(
195                @NotNull ConstraintSystem constraintSystem,
196                boolean substituteOtherTypeParametersInBounds
197        ) {
198            for (TypeParameterDescriptor typeVariable : constraintSystem.getTypeVariables()) {
199                if (!checkUpperBoundIsSatisfied(constraintSystem, typeVariable, substituteOtherTypeParametersInBounds)) {
200                    return false;
201                }
202            }
203            return true;
204        }
205    }