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