public BetterType CompareTypes(TypeArray ta1, TypeArray ta2) { if (ta1 == ta2) { return(BetterType.Same); } if (ta1.Size != ta2.Size) { // The one with more parameters is more specific. return(ta1.Size > ta2.Size ? BetterType.Left : BetterType.Right); } BetterType nTot = BetterType.Neither; for (int i = 0; i < ta1.Size; i++) { CType type1 = ta1.Item(i); CType type2 = ta2.Item(i); BetterType nParam = BetterType.Neither; LAgain: if (type1.GetTypeKind() != type2.GetTypeKind()) { if (type1.IsTypeParameterType()) { nParam = BetterType.Right; } else if (type2.IsTypeParameterType()) { nParam = BetterType.Left; } } else { switch (type1.GetTypeKind()) { default: Debug.Assert(false, "Bad kind in CompareTypes"); break; case TypeKind.TK_TypeParameterType: case TypeKind.TK_ErrorType: break; case TypeKind.TK_PointerType: case TypeKind.TK_ParameterModifierType: case TypeKind.TK_ArrayType: case TypeKind.TK_NullableType: type1 = type1.GetBaseOrParameterOrElementType(); type2 = type2.GetBaseOrParameterOrElementType(); goto LAgain; case TypeKind.TK_AggregateType: nParam = CompareTypes(type1.AsAggregateType().GetTypeArgsAll(), type2.AsAggregateType().GetTypeArgsAll()); break; } } if (nParam == BetterType.Right || nParam == BetterType.Left) { if (nTot == BetterType.Same || nTot == BetterType.Neither) { nTot = nParam; } else if (nParam != nTot) { return(BetterType.Neither); } } } return(nTot); }
private BetterType WhichMethodIsBetterTieBreaker( CandidateFunctionMember node1, CandidateFunctionMember node2, CType pTypeThrough, ArgInfos args) { MethPropWithInst mpwi1 = node1.mpwi; MethPropWithInst mpwi2 = node2.mpwi; // Same signatures. If they have different lifting numbers, the smaller number wins. // Otherwise, if one is generic and the other isn't then the non-generic wins. // Otherwise, if one is expanded and the other isn't then the non-expanded wins. // Otherwise, if one has fewer modopts than the other then it wins. if (node1.ctypeLift != node2.ctypeLift) { return(node1.ctypeLift < node2.ctypeLift ? BetterType.Left : BetterType.Right); } // Non-generic wins. if (mpwi1.TypeArgs.Count != 0) { if (mpwi2.TypeArgs.Count == 0) { return(BetterType.Right); } } else if (mpwi2.TypeArgs.Count != 0) { return(BetterType.Left); } // Non-expanded wins if (node1.fExpanded) { if (!node2.fExpanded) { return(BetterType.Right); } } else if (node2.fExpanded) { return(BetterType.Left); } // See if one's parameter types (un-instantiated) are more specific. BetterType nT = GetGlobalSymbols().CompareTypes( RearrangeNamedArguments(mpwi1.MethProp().Params, mpwi1, pTypeThrough, args), RearrangeNamedArguments(mpwi2.MethProp().Params, mpwi2, pTypeThrough, args)); if (nT == BetterType.Left || nT == BetterType.Right) { return(nT); } // Fewer modopts wins. if (mpwi1.MethProp().modOptCount != mpwi2.MethProp().modOptCount) { return(mpwi1.MethProp().modOptCount < mpwi2.MethProp().modOptCount ? BetterType.Left : BetterType.Right); } // Bona-fide tie. return(BetterType.Neither); }
private static BetterType CompareTypes(TypeArray ta1, TypeArray ta2) { if (ta1 == ta2) { return(BetterType.Same); } if (ta1.Count != ta2.Count) { // The one with more parameters is more specific. return(ta1.Count > ta2.Count ? BetterType.Left : BetterType.Right); } BetterType nTot = BetterType.Neither; for (int i = 0; i < ta1.Count; i++) { CType type1 = ta1[i]; CType type2 = ta2[i]; BetterType nParam = BetterType.Neither; LAgain: if (type1.TypeKind != type2.TypeKind) { if (type1 is TypeParameterType) { nParam = BetterType.Right; } else if (type2 is TypeParameterType) { nParam = BetterType.Left; } } else { switch (type1.TypeKind) { default: Debug.Fail("Bad kind in CompareTypes"); break; case TypeKind.TK_TypeParameterType: break; case TypeKind.TK_PointerType: case TypeKind.TK_ParameterModifierType: case TypeKind.TK_ArrayType: case TypeKind.TK_NullableType: type1 = type1.BaseOrParameterOrElementType; type2 = type2.BaseOrParameterOrElementType; goto LAgain; case TypeKind.TK_AggregateType: nParam = CompareTypes(((AggregateType)type1).TypeArgsAll, ((AggregateType)type2).TypeArgsAll); break; } } if (nParam == BetterType.Right || nParam == BetterType.Left) { if (nTot == BetterType.Same || nTot == BetterType.Neither) { nTot = nParam; } else if (nParam != nTot) { return(BetterType.Neither); } } } return(nTot); }
//////////////////////////////////////////////////////////////////////////////// // Determine which method is better for the purposes of overload resolution. // Better means: as least as good in all params, and better in at least one param. // Better w/r to a param means is an ordering, from best down: // 1) same type as argument // 2) implicit conversion from argument to formal type // Because of user defined conversion opers this relation is not transitive. // // If there is a tie because of identical signatures, the tie may be broken by the // following rules: // 1) If one is generic and the other isn't, the non-generic wins. // 2) Otherwise if one is expanded (params) and the other isn't, the non-expanded wins. // 3) Otherwise if one has more specific parameter types (at the declaration) it wins: // This occurs if at least on parameter type is more specific and no parameter type is // less specific. //* A type parameter is less specific than a non-type parameter. //* A constructed type is more specific than another constructed type if at least // one type argument is more specific and no type argument is less specific than // the corresponding type args in the other. // 4) Otherwise if one has more modopts than the other does, the smaller number of modopts wins. // // Returns Left if m1 is better, Right if m2 is better, or Neither/Same private BetterType WhichMethodIsBetter( CandidateFunctionMember node1, CandidateFunctionMember node2, CType pTypeThrough, ArgInfos args) { MethPropWithInst mpwi1 = node1.mpwi; MethPropWithInst mpwi2 = node2.mpwi; // Substitutions should have already been done on these! TypeArray pta1 = RearrangeNamedArguments(node1.@params, mpwi1, pTypeThrough, args); TypeArray pta2 = RearrangeNamedArguments(node2.@params, mpwi2, pTypeThrough, args); // If the parameter types for both candidate methods are identical, // use the tie breaking rules. if (pta1 == pta2) { return(WhichMethodIsBetterTieBreaker(node1, node2, pTypeThrough, args)); } // Otherwise, do a parameter-by-parameter comparison: // // Given an argument list A with a set of argument expressions {E1, ... En} and // two applicable function members Mp and Mq with parameter types {P1,... Pn} and // {Q1, ... Qn}, Mp is defined to be a better function member than Mq if: //* for each argument the implicit conversion from Ex to Qx is not better than // the implicit conversion from Ex to Px. //* for at least one argument, the conversion from Ex to Px is better than the // conversion from Ex to Qx. BetterType betterMethod = BetterType.Neither; int carg = args.carg; for (int i = 0; i < carg; i++) { Expr arg = args.fHasExprs ? args.prgexpr[i] : null; CType p1 = pta1[i]; CType p2 = pta2[i]; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // RUNTIME BINDER ONLY CHANGE // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // // We need to consider conversions from the actual runtime type // since we could have private interfaces that we are converting CType argType = arg?.RuntimeObjectActualType ?? args.types[i]; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // END RUNTIME BINDER ONLY CHANGE // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BetterType betterConversion = WhichConversionIsBetter(argType, p1, p2); if (betterMethod == BetterType.Right) { if (betterConversion == BetterType.Left) { betterMethod = BetterType.Neither; break; } } else if (betterMethod == BetterType.Left) { if (betterConversion == BetterType.Right) { betterMethod = BetterType.Neither; break; } } else { Debug.Assert(betterMethod == BetterType.Neither); if (betterConversion == BetterType.Right || betterConversion == BetterType.Left) { betterMethod = betterConversion; } } } // We may have different sizes if we had optional parameters. If thats the case, // the one with fewer parameters wins (ie less optional parameters) unless it is // expanded. If so, the one with more parameters wins (ie option beats expanded). if (pta1.Count != pta2.Count && betterMethod == BetterType.Neither) { if (node1.fExpanded) { if (!node2.fExpanded) { return(BetterType.Right); } } else if (node2.fExpanded) { return(BetterType.Left); } // Here, if both methods needed to use optionals to fill in the signatures, // then we are ambiguous. Otherwise, take the one that didn't need any // optionals. if (pta1.Count == carg) { return(BetterType.Left); } if (pta2.Count == carg) { return(BetterType.Right); } return(BetterType.Neither); } return(betterMethod); }
//////////////////////////////////////////////////////////////////////////////// // Determine best method for overload resolution. Returns null if no best // method, in which case two tying methods are returned for error reporting. private CandidateFunctionMember FindBestMethod( List <CandidateFunctionMember> list, CType pTypeThrough, ArgInfos args, out CandidateFunctionMember methAmbig1, out CandidateFunctionMember methAmbig2) { Debug.Assert(list.Any()); Debug.Assert(list.First().mpwi != null); Debug.Assert(list.Count > 0); // select the best method: /* * Effectively, we pick the best item from a set using a non-transitive ranking function * So, pick the first item (candidate) and compare against next (contender), if there is * no next, goto phase 2 * If first is better, move to next contender, if none proceed to phase 2 * If second is better, make the contender the candidate and make the item following * contender into the new contender, if there is none, goto phase 2 * If neither, make contender+1 into candidate and contender+2 into contender, if possible, * otherwise, if contender was last, return null, otherwise if new candidate is last, * goto phase 2 * Phase 2: compare all items before candidate to candidate * If candidate always better, return it, otherwise return null * */ // Record two method that are ambiguous for error reporting. CandidateFunctionMember ambig1 = null; CandidateFunctionMember ambig2 = null; bool ambiguous = false; CandidateFunctionMember candidate = list[0]; for (int i = 1; i < list.Count; i++) { CandidateFunctionMember contender = list[i]; Debug.Assert(candidate != contender); BetterType result = WhichMethodIsBetter(candidate, contender, pTypeThrough, args); if (result == BetterType.Left) { ambiguous = false; continue; // (meaning m1 is better...) } else if (result == BetterType.Right) { ambiguous = false; candidate = contender; } else { // in case of tie we don't want to bother with the contender who tied... ambig1 = candidate; ambig2 = contender; i++; if (i < list.Count) { contender = list[i]; candidate = contender; } else { ambiguous = true; } } } if (ambiguous) { goto AMBIG; } // Now, compare the candidate with items previous to it... foreach (CandidateFunctionMember contender in list) { if (contender == candidate) { // We hit our winner, so its good enough... methAmbig1 = null; methAmbig2 = null; return(candidate); } BetterType result = WhichMethodIsBetter(contender, candidate, pTypeThrough, args); if (result == BetterType.Right) { // meaning m2 is better continue; } else if (result == BetterType.Same || result == BetterType.Neither) { ambig1 = candidate; ambig2 = contender; } break; } AMBIG: // an ambig call. Return two of the ambiguous set. if (ambig1 != null && ambig2 != null) { methAmbig1 = ambig1; methAmbig2 = ambig2; } else { // For some reason, we have an ambiguity but never had a tie. // This can easily happen in a circular graph of candidate methods. methAmbig1 = list.First(); methAmbig2 = list.Skip(1).First(); } return(null); }