// 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, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { // 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, ref useSiteDiagnostics); if (better == BetterResult.Left) { candidates[j] = candidates[j].Worse(); } else if (better == BetterResult.Right) { candidates[i] = candidates[i].Worse(); } } } }
public void BinaryOperatorOverloadResolution(BinaryOperatorKind kind, BoundExpression left, BoundExpression right, BinaryOperatorOverloadResolutionResult result, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(left != null); Debug.Assert(right != null); Debug.Assert(result.Results.Count == 0); // SPEC: An operation of the form x&&y or x||y is processed by applying overload resolution // SPEC: as if the operation was written x&y or x|y. // SPEC VIOLATION: For compatibility with Dev11, do not apply this rule to built-in conversions. BinaryOperatorKind underlyingKind = kind & ~BinaryOperatorKind.Logical; // We can do a table lookup for well-known problems in overload resolution. BinaryOperatorEasyOut(underlyingKind, 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. bool hadUserDefinedCandidate = GetUserDefinedOperators(underlyingKind, left, right, result.Results, ref useSiteDiagnostics); // 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. // Note that the native compiler has a bug in its binary operator overload resolution involving // lifted built-in operators. The spec says that we should add the lifted and unlifted operators // to a candidate set, eliminate the inapplicable operators, and then choose the best of what is left. // The lifted operator is defined as, say int? + int? --> int?. That is not what the native compiler // does. The native compiler, rather, effectively says that there are *three* lifted operators: // int? + int? --> int?, int + int? --> int? and int? + int --> int?, and it chooses the best operator // amongst those choices. // // This is a subtle difference; most of the time all it means is that we generate better code because we // skip an unnecessary operand conversion to int? when adding int to int?. But some of the time it // means that a different user-defined conversion is chosen than the one you would expect, if the // operand has a user-defined conversion to both int and int?. // // Roslyn matches the specification and takes the break from the native compiler. if (!hadUserDefinedCandidate) { result.Results.Clear(); GetAllBuiltInOperators(kind, left, right, result.Results, ref useSiteDiagnostics); } // 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, ref useSiteDiagnostics); }
private void BinaryOperatorEasyOut(BinaryOperatorKind kind, BoundExpression left, BoundExpression right, BinaryOperatorOverloadResolutionResult result) { var leftType = left.Type; if ((object)leftType == null) { return; } var rightType = right.Type; if ((object)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.Exists && leftConversion.IsImplicit); Debug.Assert(rightConversion.Exists && rightConversion.IsImplicit); result.Results.Add(BinaryOperatorAnalysisResult.Applicable(signature, leftConversion, rightConversion)); }
private void BinaryOperatorEasyOut(BinaryOperatorKind kind, BoundExpression left, BoundExpression right, BinaryOperatorOverloadResolutionResult result) { var leftType = left.Type; if (leftType is null) { return; } var rightType = right.Type; if (rightType is 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(leftType, signature.LeftType); Conversion rightConversion = Conversions.FastClassifyConversion(rightType, signature.RightType); Debug.Assert(leftConversion.Exists && leftConversion.IsImplicit); Debug.Assert(rightConversion.Exists && rightConversion.IsImplicit); result.Results.Add(BinaryOperatorAnalysisResult.Applicable(signature, leftConversion, rightConversion)); }
private bool VOStructBinaryOperatorComparison(BinaryOperatorKind kind, BoundExpression left, BoundExpression right, BinaryOperatorOverloadResolutionResult result) { if (left.Type == right.Type) { bool isVoStruct = false; if (left.Type.IsPointerType()) { var pt = left.Type as PointerTypeSymbol; isVoStruct = pt.PointedAtType.IsVoStructOrUnion(); } else { isVoStruct = left.Type.IsVoStructOrUnion(); } if (isVoStruct && (kind == BinaryOperatorKind.Equal || kind == BinaryOperatorKind.NotEqual)) { BinaryOperatorSignature sig = new BinaryOperatorSignature(kind, left.Type, right.Type, Compilation.GetSpecialType(SpecialType.System_Boolean)); BinaryOperatorAnalysisResult best = BinaryOperatorAnalysisResult.Applicable(sig, Conversion.Identity, Conversion.Identity); result.Results.Clear(); result.Results.Add(best); return(true); } } return(false); }