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.Function;
020 import com.google.common.base.Functions;
021 import com.google.common.collect.Lists;
022 import com.google.common.collect.Maps;
023 import com.google.common.collect.Sets;
024 import com.intellij.openapi.util.Condition;
025 import com.intellij.openapi.util.Conditions;
026 import com.intellij.util.containers.ContainerUtil;
027 import org.jetbrains.annotations.NotNull;
028 import org.jetbrains.annotations.Nullable;
029 import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
030 import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
031 import org.jetbrains.jet.lang.types.*;
032 import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure;
033 import org.jetbrains.jet.lang.types.checker.TypingConstraints;
034 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
035
036 import java.util.Collection;
037 import java.util.List;
038 import java.util.Map;
039 import java.util.Set;
040
041 import static org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.EQUAL;
042 import static org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.SUB_TYPE;
043 import static org.jetbrains.jet.lang.resolve.calls.inference.TypeBounds.BoundKind.*;
044 import static org.jetbrains.jet.lang.resolve.calls.inference.TypeBounds.Bound;
045 import static org.jetbrains.jet.lang.types.TypeUtils.CANT_INFER_TYPE_PARAMETER;
046 import static org.jetbrains.jet.lang.types.TypeUtils.DONT_CARE;
047
048 public class ConstraintSystemImpl implements ConstraintSystem {
049
050 public enum ConstraintKind {
051 SUB_TYPE, EQUAL
052 }
053
054 private final Map<TypeParameterDescriptor, TypeBoundsImpl> typeParameterBounds = Maps.newLinkedHashMap();
055 private final Set<ConstraintPosition> errorConstraintPositions = Sets.newHashSet();
056 private boolean hasErrorInConstrainingTypes;
057
058 private final ConstraintSystemStatus constraintSystemStatus = new ConstraintSystemStatus() {
059 // for debug ConstraintsUtil.getDebugMessageForStatus might be used
060
061 @Override
062 public boolean isSuccessful() {
063 return !hasContradiction() && !hasUnknownParameters();
064 }
065
066 @Override
067 public boolean hasContradiction() {
068 return hasTypeConstructorMismatch() || hasConflictingConstraints();
069 }
070
071 @Override
072 public boolean hasViolatedUpperBound() {
073 if (isSuccessful()) return false;
074 return getSystemWithoutWeakConstraints().getStatus().isSuccessful();
075 }
076
077 @Override
078 public boolean hasConflictingConstraints() {
079 for (TypeBoundsImpl typeBounds : typeParameterBounds.values()) {
080 if (typeBounds.getValues().size() > 1) return true;
081 }
082 return false;
083 }
084
085 @Override
086 public boolean hasUnknownParameters() {
087 for (TypeBoundsImpl typeBounds : typeParameterBounds.values()) {
088 if (typeBounds.isEmpty()) {
089 return true;
090 }
091 }
092 return false;
093 }
094
095 @Override
096 public boolean hasTypeConstructorMismatch() {
097 return !errorConstraintPositions.isEmpty();
098 }
099
100 @Override
101 public boolean hasTypeConstructorMismatchAt(@NotNull ConstraintPosition constraintPosition) {
102 return errorConstraintPositions.contains(constraintPosition);
103 }
104
105 @Override
106 public boolean hasOnlyErrorsFromPosition(ConstraintPosition constraintPosition) {
107 if (isSuccessful()) return false;
108 ConstraintSystem systemWithoutConstraintsFromPosition = filterConstraintsOut(constraintPosition);
109 if (systemWithoutConstraintsFromPosition.getStatus().isSuccessful()) {
110 return true;
111 }
112 if (errorConstraintPositions.size() == 1 && errorConstraintPositions.contains(constraintPosition)) {
113 // e.g. if systemWithoutConstraintsFromPosition has unknown type parameters, it's not successful
114 return true;
115 }
116 return false;
117 }
118
119 @Override
120 public boolean hasErrorInConstrainingTypes() {
121 return hasErrorInConstrainingTypes;
122 }
123 };
124
125 @NotNull
126 private static Map<TypeParameterDescriptor, TypeProjection> getParameterToInferredValueMap(
127 @NotNull Map<TypeParameterDescriptor, TypeBoundsImpl> typeParameterBounds,
128 @Nullable TypeProjection defaultTypeProjection
129 ) {
130 Map<TypeParameterDescriptor, TypeProjection> substitutionContext = Maps.newHashMap();
131 for (Map.Entry<TypeParameterDescriptor, TypeBoundsImpl> entry : typeParameterBounds.entrySet()) {
132 TypeParameterDescriptor typeParameter = entry.getKey();
133 TypeBounds typeBounds = entry.getValue();
134
135 TypeProjection typeProjection;
136 JetType value = typeBounds.getValue();
137 if (value != null && !TypeUtils.equalsOrContainsAsArgument(value, TypeUtils.DONT_CARE)) {
138 typeProjection = new TypeProjectionImpl(value);
139 }
140 else {
141 typeProjection = defaultTypeProjection;
142 }
143 substitutionContext.put(typeParameter, typeProjection);
144 }
145 return substitutionContext;
146 }
147
148 private TypeSubstitutor createTypeSubstitutorWithDefaultForUnknownTypeParameter(@NotNull JetType defaultType) {
149 return TypeUtils.makeSubstitutorForTypeParametersMap(
150 getParameterToInferredValueMap(typeParameterBounds, new TypeProjectionImpl(defaultType)));
151 }
152
153 @NotNull
154 @Override
155 public ConstraintSystemStatus getStatus() {
156 return constraintSystemStatus;
157 }
158
159 @Override
160 public void registerTypeVariables(@NotNull Map<TypeParameterDescriptor, Variance> typeVariables) {
161 for (Map.Entry<TypeParameterDescriptor, Variance> entry : typeVariables.entrySet()) {
162 TypeParameterDescriptor typeVariable = entry.getKey();
163 Variance positionVariance = entry.getValue();
164 typeParameterBounds.put(typeVariable, new TypeBoundsImpl(typeVariable, positionVariance));
165 }
166 TypeSubstitutor constantSubstitutor = TypeUtils.makeConstantSubstitutor(typeParameterBounds.keySet(), DONT_CARE);
167 for (Map.Entry<TypeParameterDescriptor, TypeBoundsImpl> entry : typeParameterBounds.entrySet()) {
168 TypeParameterDescriptor typeVariable = entry.getKey();
169 TypeBoundsImpl typeBounds = entry.getValue();
170
171 for (JetType declaredUpperBound : typeVariable.getUpperBounds()) {
172 if (KotlinBuiltIns.getInstance().getNullableAnyType().equals(declaredUpperBound)) continue; //todo remove this line (?)
173 JetType substitutedBound = constantSubstitutor.substitute(declaredUpperBound, Variance.INVARIANT);
174 if (substitutedBound != null) {
175 typeBounds.addBound(UPPER_BOUND, substitutedBound, ConstraintPosition.getTypeBoundPosition(typeVariable.getIndex()));
176 }
177 }
178 }
179 }
180
181 @Override
182 @NotNull
183 public ConstraintSystem copy() {
184 return createNewConstraintSystemFromThis(Functions.<TypeParameterDescriptor>identity(),
185 new Function<TypeBoundsImpl, TypeBoundsImpl>() {
186 @Override
187 public TypeBoundsImpl apply(TypeBoundsImpl typeBounds) {
188 return typeBounds.copy();
189 }
190 },
191 Conditions.<ConstraintPosition>alwaysTrue());
192 }
193
194 @NotNull
195 public ConstraintSystem substituteTypeVariables(@NotNull Function<TypeParameterDescriptor, TypeParameterDescriptor> typeVariablesMap) {
196 return createNewConstraintSystemFromThis(typeVariablesMap,
197 // type bounds are proper types and don't contain other variables
198 Functions.<TypeBoundsImpl>identity(),
199 Conditions.<ConstraintPosition>alwaysTrue());
200 }
201
202 @NotNull
203 public ConstraintSystem filterConstraintsOut(@NotNull ConstraintPosition... excludePositions) {
204 final Set<ConstraintPosition> positions = Sets.newHashSet(excludePositions);
205 return filterConstraints(new Condition<ConstraintPosition>() {
206 @Override
207 public boolean value(ConstraintPosition constraintPosition) {
208 return !positions.contains(constraintPosition);
209 }
210 });
211 }
212
213 @NotNull
214 public ConstraintSystem filterConstraints(@NotNull final Condition<ConstraintPosition> condition) {
215 return createNewConstraintSystemFromThis(Functions.<TypeParameterDescriptor>identity(),
216 new Function<TypeBoundsImpl, TypeBoundsImpl>() {
217 @Override
218 public TypeBoundsImpl apply(TypeBoundsImpl typeBounds) {
219 return typeBounds.filter(condition);
220 }
221 },
222 condition);
223 }
224
225 @NotNull
226 public ConstraintSystem getSystemWithoutWeakConstraints() {
227 return filterConstraints(new Condition<ConstraintPosition>() {
228 @Override
229 public boolean value(ConstraintPosition constraintPosition) {
230 // 'isStrong' for compound means 'has some strong constraints'
231 // but for testing absence of weak constraints we need 'has only strong constraints' here
232 if (constraintPosition instanceof ConstraintPosition.CompoundConstraintPosition) {
233 ConstraintPosition.CompoundConstraintPosition position =
234 (ConstraintPosition.CompoundConstraintPosition) constraintPosition;
235 return position.consistsOfOnlyStrongConstraints();
236 }
237 return constraintPosition.isStrong();
238 }
239 });
240 }
241
242 @NotNull
243 private ConstraintSystem createNewConstraintSystemFromThis(
244 @NotNull Function<TypeParameterDescriptor, TypeParameterDescriptor> substituteTypeVariable,
245 @NotNull Function<TypeBoundsImpl, TypeBoundsImpl> replaceTypeBounds,
246 @NotNull Condition<ConstraintPosition> filterConstraintPosition
247 ) {
248 ConstraintSystemImpl newSystem = new ConstraintSystemImpl();
249 for (Map.Entry<TypeParameterDescriptor, TypeBoundsImpl> entry : typeParameterBounds.entrySet()) {
250 TypeParameterDescriptor typeParameter = entry.getKey();
251 TypeBoundsImpl typeBounds = entry.getValue();
252
253 TypeParameterDescriptor newTypeParameter = substituteTypeVariable.apply(typeParameter);
254 assert newTypeParameter != null;
255 newSystem.typeParameterBounds.put(newTypeParameter, replaceTypeBounds.apply(typeBounds));
256 }
257 newSystem.errorConstraintPositions.addAll(ContainerUtil.filter(errorConstraintPositions, filterConstraintPosition));
258 //todo if 'filterConstraintPosition' is not trivial, it's incorrect to just copy 'hasErrorInConstrainingTypes'
259 newSystem.hasErrorInConstrainingTypes = hasErrorInConstrainingTypes;
260 return newSystem;
261 }
262
263 @Override
264 public void addSupertypeConstraint(
265 @Nullable JetType constrainingType,
266 @NotNull JetType subjectType,
267 @NotNull ConstraintPosition constraintPosition
268 ) {
269 if (constrainingType != null && TypeUtils.noExpectedType(constrainingType)) return;
270
271 addConstraint(SUB_TYPE, subjectType, constrainingType, constraintPosition);
272 }
273
274 @Override
275 public void addSubtypeConstraint(
276 @Nullable JetType constrainingType,
277 @NotNull JetType subjectType,
278 @NotNull ConstraintPosition constraintPosition
279 ) {
280 addConstraint(SUB_TYPE, constrainingType, subjectType, constraintPosition);
281 }
282
283 private void addConstraint(
284 @NotNull ConstraintKind constraintKind,
285 @Nullable JetType subType,
286 @Nullable JetType superType,
287 @NotNull final ConstraintPosition constraintPosition
288 ) {
289 TypeCheckingProcedure typeCheckingProcedure = new TypeCheckingProcedure(new TypingConstraints() {
290 @Override
291 public boolean assertEqualTypes(
292 @NotNull JetType a, @NotNull JetType b, @NotNull TypeCheckingProcedure typeCheckingProcedure
293 ) {
294 doAddConstraint(EQUAL, a, b, constraintPosition, typeCheckingProcedure);
295 return true;
296
297 }
298
299 @Override
300 public boolean assertEqualTypeConstructors(
301 @NotNull TypeConstructor a, @NotNull TypeConstructor b
302 ) {
303 throw new IllegalStateException("'assertEqualTypeConstructors' shouldn't be invoked inside 'isSubtypeOf'");
304 }
305
306 @Override
307 public boolean assertSubtype(
308 @NotNull JetType subtype, @NotNull JetType supertype, @NotNull TypeCheckingProcedure typeCheckingProcedure
309 ) {
310 doAddConstraint(SUB_TYPE, subtype, supertype, constraintPosition, typeCheckingProcedure);
311 return true;
312 }
313
314 @Override
315 public boolean noCorrespondingSupertype(
316 @NotNull JetType subtype, @NotNull JetType supertype
317 ) {
318 errorConstraintPositions.add(constraintPosition);
319 return true;
320 }
321 });
322 doAddConstraint(constraintKind, subType, superType, constraintPosition, typeCheckingProcedure);
323 }
324
325 private boolean isErrorOrSpecialType(@Nullable JetType type) {
326 if (type == DONT_CARE || type == CANT_INFER_TYPE_PARAMETER) {
327 return true;
328 }
329
330 if (type == null || (type.isError() && type != TypeUtils.PLACEHOLDER_FUNCTION_TYPE)) {
331 hasErrorInConstrainingTypes = true;
332 return true;
333 }
334 return false;
335 }
336
337 private void doAddConstraint(
338 @NotNull ConstraintKind constraintKind,
339 @Nullable JetType subType,
340 @Nullable JetType superType,
341 @NotNull ConstraintPosition constraintPosition,
342 @NotNull TypeCheckingProcedure typeCheckingProcedure
343 ) {
344
345 if (isErrorOrSpecialType(subType) || isErrorOrSpecialType(superType)) return;
346 assert subType != null && superType != null;
347
348 assert superType != TypeUtils.PLACEHOLDER_FUNCTION_TYPE : "The type for " + constraintPosition + " shouldn't be a placeholder for function type";
349
350 KotlinBuiltIns kotlinBuiltIns = KotlinBuiltIns.getInstance();
351 if (subType == TypeUtils.PLACEHOLDER_FUNCTION_TYPE) {
352 if (!kotlinBuiltIns.isFunctionOrExtensionFunctionType(superType)) {
353 if (isMyTypeVariable(superType)) {
354 // a constraint binds type parameter and any function type, so there is no new info and no error
355 return;
356 }
357 errorConstraintPositions.add(constraintPosition);
358 }
359 return;
360 }
361
362 // todo temporary hack
363 // function literal without declaring receiver type { x -> ... }
364 // can be considered as extension function if one is expected
365 // (special type constructor for function/ extension function should be introduced like PLACEHOLDER_FUNCTION_TYPE)
366 if (constraintKind == SUB_TYPE && kotlinBuiltIns.isFunctionType(subType) && kotlinBuiltIns.isExtensionFunctionType(superType)) {
367 subType = createCorrespondingExtensionFunctionType(subType, DONT_CARE);
368 }
369
370 // can be equal for the recursive invocations:
371 // fun <T> foo(i: Int) : T { ... return foo(i); } => T <: T
372 if (subType.equals(superType)) return;
373
374 assert !isMyTypeVariable(subType) || !isMyTypeVariable(superType) :
375 "The constraint shouldn't contain different type variables on both sides: " + subType + " <: " + superType;
376
377
378 if (isMyTypeVariable(subType)) {
379 generateTypeParameterConstraint(subType, superType, constraintKind == SUB_TYPE ? UPPER_BOUND : EXACT_BOUND, constraintPosition);
380 return;
381 }
382 if (isMyTypeVariable(superType)) {
383 generateTypeParameterConstraint(superType, subType, constraintKind == SUB_TYPE ? LOWER_BOUND : EXACT_BOUND, constraintPosition);
384 return;
385 }
386 // if superType is nullable and subType is not nullable, unsafe call error will be generated later,
387 // but constraint system should be solved anyway
388 typeCheckingProcedure.isSubtypeOf(TypeUtils.makeNotNullable(subType), TypeUtils.makeNotNullable(superType));
389 }
390
391 private void generateTypeParameterConstraint(
392 @NotNull JetType parameterType,
393 @NotNull JetType constrainingType,
394 @NotNull TypeBoundsImpl.BoundKind boundKind,
395 @NotNull ConstraintPosition constraintPosition
396 ) {
397 TypeBoundsImpl typeBounds = getTypeBounds(parameterType);
398 assert typeBounds != null : "constraint should be generated only for type variables";
399
400 if (!parameterType.isNullable() || !constrainingType.isNullable()) {
401 typeBounds.addBound(boundKind, constrainingType, constraintPosition);
402 return;
403 }
404 // For parameter type T:
405 // constraint T? = Int? should transform to T >: Int and T <: Int?
406 // constraint T? >: Int? should transform to T >: Int
407 JetType notNullConstrainingType = TypeUtils.makeNotNullable(constrainingType);
408 if (boundKind == EXACT_BOUND || boundKind == LOWER_BOUND) {
409 typeBounds.addBound(LOWER_BOUND, notNullConstrainingType, constraintPosition);
410 }
411 // constraint T? <: Int? should transform to T <: Int?
412 if (boundKind == EXACT_BOUND || boundKind == UPPER_BOUND) {
413 typeBounds.addBound(UPPER_BOUND, constrainingType, constraintPosition);
414 }
415 }
416
417 public void processDeclaredBoundConstraints() {
418 for (Map.Entry<TypeParameterDescriptor, TypeBoundsImpl> entry : typeParameterBounds.entrySet()) {
419 TypeParameterDescriptor typeParameterDescriptor = entry.getKey();
420 TypeBoundsImpl typeBounds = entry.getValue();
421 for (JetType declaredUpperBound : typeParameterDescriptor.getUpperBounds()) {
422 //todo order matters here
423 Collection<Bound> bounds = Lists.newArrayList(typeBounds.getBounds());
424 for (Bound bound : bounds) {
425 if (bound.kind == LOWER_BOUND || bound.kind == EXACT_BOUND) {
426 ConstraintPosition position = ConstraintPosition.getCompoundConstraintPosition(
427 ConstraintPosition.getTypeBoundPosition(typeParameterDescriptor.getIndex()), bound.position);
428 addSubtypeConstraint(bound.type, declaredUpperBound, position);
429 }
430 }
431 ClassifierDescriptor declarationDescriptor = declaredUpperBound.getConstructor().getDeclarationDescriptor();
432 if (declarationDescriptor instanceof TypeParameterDescriptor && typeParameterBounds.containsKey(declarationDescriptor)) {
433 TypeBoundsImpl typeBoundsForUpperBound = typeParameterBounds.get(declarationDescriptor);
434 for (Bound bound : typeBoundsForUpperBound.getBounds()) {
435 if (bound.kind == UPPER_BOUND || bound.kind == EXACT_BOUND) {
436 ConstraintPosition position = ConstraintPosition.getCompoundConstraintPosition(
437 ConstraintPosition.getTypeBoundPosition(typeParameterDescriptor.getIndex()), bound.position);
438 typeBounds.addBound(UPPER_BOUND, bound.type, position);
439 }
440 }
441 }
442 }
443 }
444 }
445
446 @NotNull
447 @Override
448 public Set<TypeParameterDescriptor> getTypeVariables() {
449 return typeParameterBounds.keySet();
450 }
451
452 @Override
453 @NotNull
454 public TypeBounds getTypeBounds(@NotNull TypeParameterDescriptor typeVariable) {
455 TypeBoundsImpl typeBounds = typeParameterBounds.get(typeVariable);
456 assert typeBounds != null : "TypeParameterDescriptor is not a type variable for constraint system: " + typeVariable;
457 return typeBounds;
458 }
459
460 @Nullable
461 private TypeBoundsImpl getTypeBounds(@NotNull JetType type) {
462 ClassifierDescriptor parameterDescriptor = type.getConstructor().getDeclarationDescriptor();
463 if (parameterDescriptor instanceof TypeParameterDescriptor) {
464 return typeParameterBounds.get(parameterDescriptor);
465 }
466 return null;
467 }
468
469 private boolean isMyTypeVariable(@NotNull JetType type) {
470 ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor();
471 return descriptor instanceof TypeParameterDescriptor && typeParameterBounds.get(descriptor) != null;
472 }
473
474 @NotNull
475 @Override
476 public TypeSubstitutor getResultingSubstitutor() {
477 return createTypeSubstitutorWithDefaultForUnknownTypeParameter(TypeUtils.CANT_INFER_TYPE_PARAMETER);
478 }
479
480 @NotNull
481 @Override
482 public TypeSubstitutor getCurrentSubstitutor() {
483 return createTypeSubstitutorWithDefaultForUnknownTypeParameter(TypeUtils.DONT_CARE);
484 }
485
486 @NotNull
487 public static JetType createCorrespondingExtensionFunctionType(@NotNull JetType functionType, @NotNull JetType receiverType) {
488 assert KotlinBuiltIns.getInstance().isFunctionType(functionType);
489
490 List<TypeProjection> typeArguments = functionType.getArguments();
491 assert !typeArguments.isEmpty();
492
493 List<JetType> arguments = Lists.newArrayList();
494 // excluding the last type argument of the function type, which is the return type
495 int index = 0;
496 int lastIndex = typeArguments.size() - 1;
497 for (TypeProjection typeArgument : typeArguments) {
498 if (index < lastIndex) {
499 arguments.add(typeArgument.getType());
500 }
501 index++;
502 }
503 JetType returnType = typeArguments.get(lastIndex).getType();
504 return KotlinBuiltIns.getInstance().getFunctionType(functionType.getAnnotations(), receiverType, arguments, returnType);
505 }
506 }