001 /*
002 * Copyright 2010-2015 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.kotlin.resolve.calls.inference;
018
019 import com.google.common.collect.Lists;
020 import com.google.common.collect.Maps;
021 import kotlin.CollectionsKt;
022 import kotlin.jvm.functions.Function1;
023 import org.jetbrains.annotations.NotNull;
024 import org.jetbrains.annotations.Nullable;
025 import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
026 import org.jetbrains.kotlin.psi.Call;
027 import org.jetbrains.kotlin.types.*;
028 import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
029
030 import java.lang.reflect.InvocationTargetException;
031 import java.lang.reflect.Method;
032 import java.util.*;
033
034 public class ConstraintsUtil {
035 @Nullable
036 public static TypeVariable getFirstConflictingVariable(@NotNull ConstraintSystem constraintSystem) {
037 for (TypeVariable typeVariable : constraintSystem.getTypeVariables()) {
038 TypeBounds constraints = constraintSystem.getTypeBounds(typeVariable);
039 if (constraints.getValues().size() > 1) {
040 return typeVariable;
041 }
042 }
043 return null;
044 }
045
046 @NotNull
047 public static Collection<TypeSubstitutor> getSubstitutorsForConflictingParameters(@NotNull ConstraintSystem constraintSystem) {
048 TypeVariable firstConflictingVariable = getFirstConflictingVariable(constraintSystem);
049 if (firstConflictingVariable == null) return Collections.emptyList();
050 TypeParameterDescriptor firstConflictingParameter = firstConflictingVariable.getOriginalTypeParameter();
051
052 Collection<KotlinType> conflictingTypes = constraintSystem.getTypeBounds(firstConflictingVariable).getValues();
053
054 List<Map<TypeConstructor, TypeProjection>> substitutionContexts = Lists.newArrayList();
055 for (KotlinType type : conflictingTypes) {
056 Map<TypeConstructor, TypeProjection> context = Maps.newLinkedHashMap();
057 context.put(firstConflictingParameter.getTypeConstructor(), new TypeProjectionImpl(type));
058 substitutionContexts.add(context);
059 }
060
061 for (TypeVariable typeVariable : constraintSystem.getTypeVariables()) {
062 if (typeVariable == firstConflictingVariable) continue;
063
064 KotlinType safeType = getSafeValue(constraintSystem, typeVariable);
065 for (Map<TypeConstructor, TypeProjection> context : substitutionContexts) {
066 TypeProjection typeProjection = new TypeProjectionImpl(safeType);
067 context.put(typeVariable.getOriginalTypeParameter().getTypeConstructor(), typeProjection);
068 }
069 }
070 Collection<TypeSubstitutor> typeSubstitutors = new ArrayList<TypeSubstitutor>(substitutionContexts.size());
071 for (Map<TypeConstructor, TypeProjection> context : substitutionContexts) {
072 typeSubstitutors.add(TypeSubstitutor.create(context));
073 }
074 return typeSubstitutors;
075 }
076
077 @NotNull
078 private static KotlinType getSafeValue(@NotNull ConstraintSystem constraintSystem, @NotNull TypeVariable typeVariable) {
079 KotlinType type = constraintSystem.getTypeBounds(typeVariable).getValue();
080 if (type != null) {
081 return type;
082 }
083 //todo may be error type
084 return TypeIntersector.getUpperBoundsAsType(typeVariable.getOriginalTypeParameter());
085 }
086
087 public static boolean checkUpperBoundIsSatisfied(
088 @NotNull ConstraintSystem constraintSystem,
089 @NotNull TypeParameterDescriptor typeParameter,
090 @NotNull Call call,
091 boolean substituteOtherTypeParametersInBound
092 ) {
093 TypeVariable typeVariable = ConstraintSystemUtilsKt.descriptorToVariable(
094 constraintSystem, TypeVariableKt.toHandle(call), typeParameter
095 );
096 KotlinType type = constraintSystem.getTypeBounds(typeVariable).getValue();
097 if (type == null) return true;
098
099 List<TypeParameterDescriptor> typeParametersUsedInSystem = CollectionsKt.map(
100 constraintSystem.getTypeVariables(),
101 new Function1<TypeVariable, TypeParameterDescriptor>() {
102 @Override
103 public TypeParameterDescriptor invoke(TypeVariable variable) {
104 return variable.getOriginalTypeParameter();
105 }
106 }
107 );
108
109 for (KotlinType upperBound : typeParameter.getUpperBounds()) {
110 if (!substituteOtherTypeParametersInBound &&
111 TypeUtils.dependsOnTypeParameters(upperBound, typeParametersUsedInSystem)) {
112 continue;
113 }
114 KotlinType substitutedUpperBound = constraintSystem.getResultingSubstitutor().substitute(upperBound, Variance.INVARIANT);
115
116 assert substitutedUpperBound != null : "We wanted to substitute projections as a result for " + typeParameter;
117 if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(type, substitutedUpperBound)) {
118 return false;
119 }
120 }
121 return true;
122 }
123
124 public static String getDebugMessageForStatus(@NotNull ConstraintSystemStatus status) {
125 StringBuilder sb = new StringBuilder();
126 List<Method> interestingMethods = Lists.newArrayList();
127 for (Method method : status.getClass().getMethods()) {
128 String name = method.getName();
129 boolean isInteresting = name.startsWith("is") || name.startsWith("has") && !name.equals("hashCode");
130 if (method.getParameterTypes().length == 0 && isInteresting) {
131 interestingMethods.add(method);
132 }
133 }
134 Collections.sort(interestingMethods, new Comparator<Method>() {
135 @Override
136 public int compare(@NotNull Method method1, @NotNull Method method2) {
137 return method1.getName().compareTo(method2.getName());
138 }
139 });
140 for (Iterator<Method> iterator = interestingMethods.iterator(); iterator.hasNext(); ) {
141 Method method = iterator.next();
142 try {
143 sb.append("-").append(method.getName()).append(": ").append(method.invoke(status));
144 if (iterator.hasNext()) {
145 sb.append("\n");
146 }
147 }
148 catch (IllegalAccessException e) {
149 sb.append(e.getMessage());
150 }
151 catch (InvocationTargetException e) {
152 sb.append(e.getMessage());
153 }
154 }
155 return sb.toString();
156 }
157 }