// 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);
        }
Esempio n. 3
0
        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);
 }