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