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
017package org.jetbrains.jet.lang.resolve.calls.inference;
018
019import com.google.common.base.Predicate;
020import com.google.common.collect.Collections2;
021import com.google.common.collect.Lists;
022import com.google.common.collect.Maps;
023import com.google.common.collect.Sets;
024import org.jetbrains.annotations.NotNull;
025import org.jetbrains.annotations.Nullable;
026import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
027import org.jetbrains.jet.lang.types.*;
028import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
029
030import java.util.*;
031
032public 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}