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