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.CallableDescriptor;
024    import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor;
025    import org.jetbrains.jet.lang.descriptors.ScriptDescriptor;
026    import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
027    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
028    import org.jetbrains.jet.lang.resolve.OverridingUtil;
029    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace;
030    import org.jetbrains.jet.lang.types.JetType;
031    import org.jetbrains.jet.lang.types.TypeUtils;
032    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
033    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
034    
035    import java.util.List;
036    import java.util.Set;
037    
038    public 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    }