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