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.results;
018
019 import gnu.trove.THashSet;
020 import gnu.trove.TObjectHashingStrategy;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.annotations.Nullable;
023 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
024 import org.jetbrains.kotlin.descriptors.*;
025 import org.jetbrains.kotlin.resolve.OverrideResolver;
026 import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode;
027 import org.jetbrains.kotlin.resolve.calls.model.MutableResolvedCall;
028 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
029 import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall;
030 import org.jetbrains.kotlin.types.*;
031 import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
032
033 import java.util.List;
034 import java.util.Set;
035
036 public class OverloadingConflictResolver {
037
038 private final KotlinBuiltIns builtIns;
039
040 public OverloadingConflictResolver(@NotNull KotlinBuiltIns builtIns) {
041 this.builtIns = builtIns;
042 }
043
044 @Nullable
045 public <D extends CallableDescriptor> MutableResolvedCall<D> findMaximallySpecific(
046 @NotNull Set<MutableResolvedCall<D>> candidates,
047 boolean discriminateGenericDescriptors,
048 @NotNull CheckArgumentTypesMode checkArgumentsMode
049 ) {
050 // Different smartcasts may lead to the same candidate descriptor wrapped into different ResolvedCallImpl objects
051 Set<MutableResolvedCall<D>> maximallySpecific = new THashSet<MutableResolvedCall<D>>(new TObjectHashingStrategy<MutableResolvedCall<D>>() {
052 @Override
053 public boolean equals(MutableResolvedCall<D> o1, MutableResolvedCall<D> o2) {
054 return o1 == null ? o2 == null : o1.getResultingDescriptor().equals(o2.getResultingDescriptor());
055 }
056
057 @Override
058 public int computeHashCode(MutableResolvedCall<D> object) {
059 return object == null ? 0 : object.getResultingDescriptor().hashCode();
060 }
061 });
062 for (MutableResolvedCall<D> candidateCall : candidates) {
063 if (isMaximallySpecific(candidateCall, candidates, discriminateGenericDescriptors, checkArgumentsMode)) {
064 maximallySpecific.add(candidateCall);
065 }
066 }
067 return maximallySpecific.size() == 1 ? maximallySpecific.iterator().next() : null;
068 }
069
070 private <D extends CallableDescriptor> boolean isMaximallySpecific(
071 @NotNull MutableResolvedCall<D> candidateCall,
072 @NotNull Set<MutableResolvedCall<D>> candidates,
073 boolean discriminateGenericDescriptors,
074 @NotNull CheckArgumentTypesMode checkArgumentsMode
075 ) {
076 D me = candidateCall.getResultingDescriptor();
077
078 boolean isInvoke = candidateCall instanceof VariableAsFunctionResolvedCall;
079 VariableDescriptor variable;
080 if (isInvoke) {
081 variable = ((VariableAsFunctionResolvedCall) candidateCall).getVariableCall().getResultingDescriptor();
082 }
083 else {
084 variable = null;
085 }
086
087 for (MutableResolvedCall<D> otherCall : candidates) {
088 D other = otherCall.getResultingDescriptor();
089 if (other == me) continue;
090
091 if (definitelyNotMaximallySpecific(me, other, discriminateGenericDescriptors, checkArgumentsMode)) {
092
093 if (!isInvoke) return false;
094
095 assert otherCall instanceof VariableAsFunctionResolvedCall : "'invoke' candidate goes with usual one: " + candidateCall + otherCall;
096 ResolvedCall<VariableDescriptor> otherVariableCall = ((VariableAsFunctionResolvedCall) otherCall).getVariableCall();
097 if (definitelyNotMaximallySpecific(variable, otherVariableCall.getResultingDescriptor(), discriminateGenericDescriptors, checkArgumentsMode)) {
098 return false;
099 }
100 }
101 }
102 return true;
103 }
104
105 private <D extends CallableDescriptor> boolean definitelyNotMaximallySpecific(
106 D me,
107 D other,
108 boolean discriminateGenericDescriptors,
109 @NotNull CheckArgumentTypesMode checkArgumentsMode
110 ) {
111 return !moreSpecific(me, other, discriminateGenericDescriptors, checkArgumentsMode) ||
112 moreSpecific(other, me, discriminateGenericDescriptors, checkArgumentsMode);
113 }
114
115 /**
116 * Let < mean "more specific"
117 * Subtype < supertype
118 * Double < Float
119 * Int < Long
120 * Int < Short < Byte
121 */
122 private <Descriptor extends CallableDescriptor> boolean moreSpecific(
123 Descriptor f,
124 Descriptor g,
125 boolean discriminateGenericDescriptors,
126 @NotNull CheckArgumentTypesMode checkArgumentsMode
127 ) {
128 boolean resolvingCallableReference = checkArgumentsMode == CheckArgumentTypesMode.CHECK_CALLABLE_TYPE;
129
130 if (f.getContainingDeclaration() instanceof ScriptDescriptor && g.getContainingDeclaration() instanceof ScriptDescriptor) {
131 ScriptDescriptor fs = (ScriptDescriptor) f.getContainingDeclaration();
132 ScriptDescriptor gs = (ScriptDescriptor) g.getContainingDeclaration();
133
134 if (fs.getPriority() != gs.getPriority()) {
135 return fs.getPriority() > gs.getPriority();
136 }
137 }
138
139 boolean isGenericF = isGeneric(f);
140 boolean isGenericG = isGeneric(g);
141 if (discriminateGenericDescriptors) {
142 if (!isGenericF && isGenericG) return true;
143 if (isGenericF && !isGenericG) return false;
144
145 if (isGenericF && isGenericG) {
146 return moreSpecific(BoundsSubstitutor.substituteBounds(f), BoundsSubstitutor.substituteBounds(g), false, checkArgumentsMode);
147 }
148 }
149
150 if (OverrideResolver.overrides(f, g)) return true;
151 if (OverrideResolver.overrides(g, f)) return false;
152
153 ReceiverParameterDescriptor receiverOfF = f.getExtensionReceiverParameter();
154 ReceiverParameterDescriptor receiverOfG = g.getExtensionReceiverParameter();
155 if (receiverOfF != null && receiverOfG != null) {
156 if (!typeMoreSpecific(receiverOfF.getType(), receiverOfG.getType())) return false;
157 }
158
159 List<ValueParameterDescriptor> fParams = f.getValueParameters();
160 List<ValueParameterDescriptor> gParams = g.getValueParameters();
161
162 int fSize = fParams.size();
163 int gSize = gParams.size();
164
165 boolean fIsVararg = isVariableArity(fParams);
166 boolean gIsVararg = isVariableArity(gParams);
167
168 if (resolvingCallableReference && fIsVararg != gIsVararg) return false;
169 if (!fIsVararg && gIsVararg) return true;
170 if (fIsVararg && !gIsVararg) return false;
171
172 if (!fIsVararg && !gIsVararg) {
173 if (resolvingCallableReference && fSize != gSize) return false;
174 if (!resolvingCallableReference && fSize > gSize) return false;
175
176 for (int i = 0; i < fSize; i++) {
177 ValueParameterDescriptor fParam = fParams.get(i);
178 ValueParameterDescriptor gParam = gParams.get(i);
179
180 KotlinType fParamType = fParam.getType();
181 KotlinType gParamType = gParam.getType();
182
183 if (!typeMoreSpecific(fParamType, gParamType)) {
184 return false;
185 }
186 }
187 }
188
189 if (fIsVararg && gIsVararg) {
190 // Check matching parameters
191 int minSize = Math.min(fSize, gSize);
192 for (int i = 0; i < minSize - 1; i++) {
193 ValueParameterDescriptor fParam = fParams.get(i);
194 ValueParameterDescriptor gParam = gParams.get(i);
195
196 KotlinType fParamType = fParam.getType();
197 KotlinType gParamType = gParam.getType();
198
199 if (!typeMoreSpecific(fParamType, gParamType)) {
200 return false;
201 }
202 }
203
204 // Check the non-matching parameters of one function against the vararg parameter of the other function
205 // Example:
206 // f(vararg vf : T)
207 // g(a : A, vararg vg : T)
208 // here we check that typeOf(a) < elementTypeOf(vf) and elementTypeOf(vg) < elementTypeOf(vf)
209 if (fSize < gSize) {
210 ValueParameterDescriptor fParam = fParams.get(fSize - 1);
211 KotlinType fParamType = fParam.getVarargElementType();
212 assert fParamType != null : "fIsVararg guarantees this";
213 for (int i = fSize - 1; i < gSize; i++) {
214 ValueParameterDescriptor gParam = gParams.get(i);
215 if (!typeMoreSpecific(fParamType, getVarargElementTypeOrType(gParam))) {
216 return false;
217 }
218 }
219 }
220 else {
221 ValueParameterDescriptor gParam = gParams.get(gSize - 1);
222 KotlinType gParamType = gParam.getVarargElementType();
223 assert gParamType != null : "gIsVararg guarantees this";
224 for (int i = gSize - 1; i < fSize; i++) {
225 ValueParameterDescriptor fParam = fParams.get(i);
226 if (!typeMoreSpecific(getVarargElementTypeOrType(fParam), gParamType)) {
227 return false;
228 }
229 }
230 }
231 }
232
233 return true;
234 }
235
236 @NotNull
237 private static KotlinType getVarargElementTypeOrType(@NotNull ValueParameterDescriptor parameterDescriptor) {
238 KotlinType varargElementType = parameterDescriptor.getVarargElementType();
239 if (varargElementType != null) {
240 return varargElementType;
241 }
242 return parameterDescriptor.getType();
243 }
244
245 private static boolean isVariableArity(List<ValueParameterDescriptor> fParams) {
246 int fSize = fParams.size();
247 return fSize > 0 && fParams.get(fSize - 1).getVarargElementType() != null;
248 }
249
250 private static boolean isGeneric(CallableDescriptor f) {
251 return !f.getOriginal().getTypeParameters().isEmpty();
252 }
253
254 private boolean typeMoreSpecific(@NotNull KotlinType specific, @NotNull KotlinType general) {
255 boolean isSubtype = KotlinTypeChecker.DEFAULT.isSubtypeOf(specific, general) ||
256 numericTypeMoreSpecific(specific, general);
257
258 if (!isSubtype) return false;
259
260 Specificity.Relation sThanG = TypeCapabilitiesKt.getSpecificityRelationTo(specific, general);
261 Specificity.Relation gThanS = TypeCapabilitiesKt.getSpecificityRelationTo(general, specific);
262 if (sThanG == Specificity.Relation.LESS_SPECIFIC && gThanS != Specificity.Relation.LESS_SPECIFIC) {
263 return false;
264 }
265
266 return true;
267 }
268
269 private boolean numericTypeMoreSpecific(@NotNull KotlinType specific, @NotNull KotlinType general) {
270 KotlinType _double = builtIns.getDoubleType();
271 KotlinType _float = builtIns.getFloatType();
272 KotlinType _long = builtIns.getLongType();
273 KotlinType _int = builtIns.getIntType();
274 KotlinType _byte = builtIns.getByteType();
275 KotlinType _short = builtIns.getShortType();
276
277 if (TypeUtils.equalTypes(specific, _double) && TypeUtils.equalTypes(general, _float)) return true;
278 if (TypeUtils.equalTypes(specific, _int)) {
279 if (TypeUtils.equalTypes(general, _long)) return true;
280 if (TypeUtils.equalTypes(general, _byte)) return true;
281 if (TypeUtils.equalTypes(general, _short)) return true;
282 }
283 if (TypeUtils.equalTypes(specific, _short) && TypeUtils.equalTypes(general, _byte)) return true;
284 return false;
285 }
286
287 }