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 }