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.collections.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 }