//////////////////////////////////////////////////////////////////////////////// // 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); switch (WhichMethodIsBetter(candidate, contender, pTypeThrough, args)) { case BetterType.Left: ambiguous = false; // (meaning m1 is better...) break; case BetterType.Right: ambiguous = false; candidate = contender; break; default: // 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; } break; } } if (!ambiguous) { // 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); } switch (WhichMethodIsBetter(contender, candidate, pTypeThrough, args)) { case BetterType.Right: // meaning m2 is better continue; case BetterType.Same: case BetterType.Neither: ambig1 = candidate; ambig2 = contender; break; } break; } } // an ambiguous 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); }
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); }
//////////////////////////////////////////////////////////////////////////////// // 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. protected 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; }
protected 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.size != 0) { if (mpwi2.TypeArgs.size == 0) { return BetterType.Right; } } else if (mpwi2.TypeArgs.size != 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; }
//////////////////////////////////////////////////////////////////////////////// // 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 protected 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; CType type1 = pTypeThrough != null ? pTypeThrough : mpwi1.GetType(); CType type2 = pTypeThrough != null ? pTypeThrough : mpwi2.GetType(); MethodOrPropertySymbol methProp1 = GroupToArgsBinder.FindMostDerivedMethod(GetSymbolLoader(), mpwi1.MethProp(), type1); MethodOrPropertySymbol methProp2 = GroupToArgsBinder.FindMostDerivedMethod(GetSymbolLoader(), mpwi2.MethProp(), type2); List<Name> names1 = methProp1.ParameterNames; List<Name> names2 = methProp2.ParameterNames; for (int i = 0; i < args.carg; i++) { EXPR arg = args.fHasExprs ? args.prgexpr[i] : null; CType argType = args.types.Item(i); CType p1 = pta1.Item(i); CType p2 = pta2.Item(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 if (arg.RuntimeObjectActualType != null) { argType = arg.RuntimeObjectActualType; } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // END RUNTIME BINDER ONLY CHANGE // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BetterType betterConversion = WhichConversionIsBetter(arg, argType, p1, p2); if (betterMethod == BetterType.Right && betterConversion == BetterType.Left) { betterMethod = BetterType.Neither; break; } else if (betterMethod == BetterType.Left && betterConversion == BetterType.Right) { betterMethod = BetterType.Neither; break; } else if (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.size != pta2.size && betterMethod == BetterType.Neither) { if (node1.fExpanded && !node2.fExpanded) { return BetterType.Right; } else if (node2.fExpanded && !node1.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.size == args.carg) { return BetterType.Left; } else if (pta2.size == args.carg) { return BetterType.Right; } return BetterType.Neither; } return betterMethod; }