public void BinaryOperatorOverloadResolution(BinaryOperatorKind kind, BoundExpression left, BoundExpression right, BinaryOperatorOverloadResolutionResult result) { Debug.Assert(left != null); Debug.Assert(right != null); Debug.Assert(result.Results.Count == 0); // We can do a table lookup for well-known problems in overload resolution. BinaryOperatorEasyOut(kind, left, right, result); if (result.Results.Count > 0) { return; } // The following is a slight rewording of the specification to emphasize that not all // operands of a binary operation need to have a type. // SPEC: An operation of the form x op y, where op is an overloadable binary operator is processed as follows: // SPEC: The set of candidate user-defined operators provided by the types (if any) of x and y for the // SPEC operation operator op(x, y) is determined. GetUserDefinedOperators(kind, left, right, result.Results); // SPEC: If the set of candidate user-defined operators is not empty, then this becomes the set of candidate // SPEC: operators for the operation. Otherwise, the predefined binary operator op implementations, including // SPEC: their lifted forms, become the set of candidate operators for the operation. if (!result.AnyValid()) { GetAllBuiltInOperators(kind, left, right, result.Results); } // SPEC: The overload resolution rules of 7.5.3 are applied to the set of candidate operators to select the best // SPEC: operator with respect to the argument list (x, y), and this operator becomes the result of the overload // SPEC: resolution process. If overload resolution fails to select a single best operator, a binding-time // SPEC: error occurs. BinaryOperatorOverloadResolution(left, right, result); }
private void BinaryOperatorEasyOut(BinaryOperatorKind kind, BoundExpression left, BoundExpression right, BinaryOperatorOverloadResolutionResult result) { var leftType = left.Type; if (leftType == null) { return; } var rightType = right.Type; if (rightType == null) { return; } if (PossiblyUnusualConstantOperation(left, right)) { return; } var easyOut = BinopEasyOut.OpKind(kind, leftType, rightType); if (easyOut == BinaryOperatorKind.Error) { return; } BinaryOperatorSignature signature = this.Compilation.builtInOperators.GetSignature(easyOut); Conversion? leftConversion = Conversions.FastClassifyConversion(left.Type, signature.LeftType); Conversion? rightConversion = Conversions.FastClassifyConversion(right.Type, signature.RightType); Debug.Assert(leftConversion.HasValue && leftConversion.Value.IsImplicit); Debug.Assert(rightConversion.HasValue && rightConversion.Value.IsImplicit); result.Results.Add(BinaryOperatorAnalysisResult.Applicable(signature, leftConversion.Value, rightConversion.Value)); }
// Takes a list of candidates and mutates the list to throw out the ones that are worse than // another applicable candidate. private void BinaryOperatorOverloadResolution( BoundExpression left, BoundExpression right, BinaryOperatorOverloadResolutionResult result ) { // SPEC: Given the set of applicable candidate function members, the best function member in that set is located. // SPEC: If the set contains only one function member, then that function member is the best function member. if (result.GetValidCount() == 1) { return; } // SPEC: Otherwise, the best function member is the one function member that is better than all other function // SPEC: members with respect to the given argument list, provided that each function member is compared to all // SPEC: other function members using the rules in 7.5.3.2. If there is not exactly one function member that is // SPEC: better than all other function members, then the function member invocation is ambiguous and a binding-time // SPEC: error occurs. // UNDONE: This is a naive quadratic algorithm; there is a linear algorithm that works. Consider using it. var candidates = result.Results; for (int i = 0; i < candidates.Count; ++i) { if (candidates[i].Kind != OperatorAnalysisResultKind.Applicable) { continue; } // Is this applicable operator better than every other applicable method? for (int j = 0; j < candidates.Count; ++j) { if (i == j) { continue; } if (candidates[j].Kind == OperatorAnalysisResultKind.Inapplicable) { continue; } var better = BetterOperator(candidates[i].Signature, candidates[j].Signature, left, right); if (better == BetterResult.Left) { candidates[j] = candidates[j].Worse(); } else if (better == BetterResult.Right) { candidates[i] = candidates[i].Worse(); } } } }