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