Пример #1
0
        private BetterResult BetterOperator(UnaryOperatorSignature op1, UnaryOperatorSignature op2, BoundExpression operand, ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            // First we see if the conversion from the operand to one operand type is better than
            // the conversion to the other.

            BetterResult better = BetterConversionFromExpression(operand, op1.OperandType, op2.OperandType, ref useSiteDiagnostics);

            if (better == BetterResult.Left || better == BetterResult.Right)
            {
                return(better);
            }

            // There was no better member on the basis of conversions. Go to the tiebreaking round.

            // SPEC: In case the parameter type sequences P1, P2 and Q1, Q2 are equivalent -- that is, every Pi
            // SPEC: has an identity conversion to the corresponding Qi -- the following tie-breaking rules
            // SPEC: are applied:

            if (Conversions.HasIdentityConversion(op1.OperandType, op2.OperandType))
            {
                // SPEC: If Mp has more specific parameter types than Mq then Mp is better than Mq.

                // Under what circumstances can two unary operators with identical signatures be "more specific"
                // than another? With a binary operator you could have C<T>.op+(C<T>, T) and C<T>.op+(C<T>, int).
                // When doing overload resolution on C<int> + int, the latter is more specific. But with a unary
                // operator, the sole operand *must* be the containing type or its nullable type. Therefore
                // if there is an identity conversion, then the parameters really were identical. We therefore
                // skip checking for specificity.

                // SPEC: If one member is a non-lifted operator and the other is a lifted operator,
                // SPEC: the non-lifted one is better.

                bool lifted1 = op1.Kind.IsLifted();
                bool lifted2 = op2.Kind.IsLifted();

                if (lifted1 && !lifted2)
                {
                    return(BetterResult.Right);
                }
                else if (!lifted1 && lifted2)
                {
                    return(BetterResult.Left);
                }
            }

            // Always prefer operators with val parameters over operators with in parameters:
            if (op1.RefKind == RefKind.None && op2.RefKind == RefKind.In)
            {
                return(BetterResult.Left);
            }
            else if (op2.RefKind == RefKind.None && op1.RefKind == RefKind.In)
            {
                return(BetterResult.Right);
            }

            return(BetterResult.Neither);
        }
Пример #2
0
        // This method takes an array of items and a predicate which filters out the valid items.
        // From the valid items we find the index of the *unique best item* in the array.
        // In order for a valid item x to be considered best, x must be better than every other
        // item. The "better" relation must be consistent; that is:
        //
        // better(x,y) == Left     requires that    better(y,x) == Right
        // better(x,y) == Right    requires that    better(y,x) == Left
        // better(x,y) == Neither  requires that    better(y,x) == Neither
        //
        // It is possible for the array to contain the same item twice; if it does then
        // the duplicate is ignored. That is, having the "best" item twice does not preclude
        // it from being the best.

        // UNDONE: Update this to give a BestIndex result that indicates ambiguity.
        private static int?UniqueBestValidIndex <T>(ImmutableArray <T> items, Func <T, bool> valid, Func <T, T, BetterResult> better)
        {
            if (items.IsEmpty)
            {
                return(null);
            }

            int?candidateIndex = null;
            T   candidateItem  = default(T);

            for (int currentIndex = 0; currentIndex < items.Length; ++currentIndex)
            {
                T currentItem = items[currentIndex];
                if (!valid(currentItem))
                {
                    continue;
                }

                if (candidateIndex == null)
                {
                    candidateIndex = currentIndex;
                    candidateItem  = currentItem;
                    continue;
                }

                BetterResult result = better(candidateItem, currentItem);

                if (result == BetterResult.Equal)
                {
                    // The list had the same item twice. Just ignore it.
                    continue;
                }
                else if (result == BetterResult.Neither)
                {
                    // Neither the current item nor the candidate item are better,
                    // and therefore neither of them can be the best. We no longer
                    // have a candidate for best item.
                    candidateIndex = null;
                    candidateItem  = default(T);
                }
                else if (result == BetterResult.Right)
                {
                    // The candidate is worse than the current item, so replace it
                    // with the current item.
                    candidateIndex = currentIndex;
                    candidateItem  = currentItem;
                }
                // Otherwise, the candidate is better than the current item, so
                // it continues to be the candidate.
            }

            if (candidateIndex == null)
            {
                return(null);
            }

            // We had a candidate that was better than everything that came *after* it.
            // Now verify that it was better than everything that came before it.

            for (int currentIndex = 0; currentIndex < candidateIndex.Value; ++currentIndex)
            {
                T currentItem = items[currentIndex];
                if (!valid(currentItem))
                {
                    continue;
                }

                BetterResult result = better(candidateItem, currentItem);
                if (result != BetterResult.Left && result != BetterResult.Equal)
                {
                    // The candidate was not better than everything that came before it. There is
                    // no best item.
                    return(null);
                }
            }

            // The candidate was better than everything that came before it.

            return(candidateIndex);
        }
Пример #3
0
        private bool VOBetterFunctionMember <TMember>(
            MemberResolutionResult <TMember> m1,
            MemberResolutionResult <TMember> m2,
            ArrayBuilder <BoundExpression> arguments,
            out BetterResult result,
            out HashSet <DiagnosticInfo> useSiteDiagnostics
            )
            where TMember : Symbol
        {
            result = BetterResult.Neither;
            bool Ambiguous = false;

            // Prefer the member not declared in VulcanRT, if applicable
            useSiteDiagnostics = null;
            if (Compilation.Options.HasRuntime)
            {
                var asm1 = m1.Member.ContainingAssembly;
                var asm2 = m2.Member.ContainingAssembly;
                if (asm1 != asm2)
                {
                    // prefer XSharpCore over XSharpVO, so typed versions get preference over untyped versions
                    //if (asm1.IsXSharpCore() && asm2.IsXSharpVO())
                    //{
                    //    result = BetterResult.Left;
                    //    return true;
                    //}
                    // prefer non runtime over runtime to allow overriding built-in functions
                    if (asm1.IsRT() != asm2.IsRT())
                    {
                        if (asm1.IsRT())
                        {
                            result = BetterResult.Right;
                            return(true);
                        }
                        else if (asm2.IsRT())
                        {
                            result = BetterResult.Left;
                            return(true);
                        }
                    }
                    // prefer functions/method in the current assembly over external methods
                    if (asm1.IsFromCompilation(Compilation))
                    {
                        result = BetterResult.Left;
                        return(true);
                    }
                    if (asm2.IsFromCompilation(Compilation))
                    {
                        result = BetterResult.Right;
                        return(true);
                    }
                }
                if (m1.Member.HasClipperCallingConvention() != m2.Member.HasClipperCallingConvention())
                {
                    if (m1.Member.HasClipperCallingConvention())
                    {
                        result = BetterResult.Right;
                    }
                    else
                    {
                        result = BetterResult.Left;
                    }
                    return(true);
                }
                if (m1.Member.GetParameterCount() == m2.Member.GetParameterCount())
                {
                    // In case of 2 methods with the same # of parameters
                    // we have different / extended rules compared to C#
                    var parsLeft   = m1.Member.GetParameters();
                    var parsRight  = m2.Member.GetParameters();
                    var usualType  = Compilation.UsualType();
                    var objectType = Compilation.GetSpecialType(SpecialType.System_Object);
                    var len        = parsLeft.Length;
                    if (arguments.Count < len)
                    {
                        len = arguments.Count;
                    }

                    bool equalLeft  = true;
                    bool equalRight = true;
                    // check if all left types are equal
                    if (parsLeft.Length == arguments.Count)
                    {
                        for (int i = 0; i < len; i++)
                        {
                            var parLeft = parsLeft[i];
                            var arg     = arguments[i];
                            if (parLeft.Type != arg.Type)
                            {
                                equalLeft = false;
                                break;
                            }
                        }
                    }
                    // check if all right types are equal
                    if (parsRight.Length == arguments.Count)
                    {
                        for (int i = 0; i < len; i++)
                        {
                            var parRight = parsRight[i];
                            var arg      = arguments[i];
                            if (parRight.Type != arg.Type)
                            {
                                equalRight = false;
                                break;
                            }
                        }
                    }
                    // Only exit here when one of the two is better than the other
                    if (equalLeft && !equalRight)
                    {
                        result = BetterResult.Left;
                        return(true);
                    }
                    if (equalRight && !equalLeft)
                    {
                        result = BetterResult.Right;
                        return(true);
                    }
                    for (int i = 0; i < len; i++)
                    {
                        var  parLeft       = parsLeft[i];
                        var  parRight      = parsRight[i];
                        var  refLeft       = parLeft.RefKind;
                        var  refRight      = parRight.RefKind;
                        var  arg           = arguments[i];
                        bool argCanBeByRef = arg.Kind == BoundKind.AddressOfOperator;
                        var  argType       = arg.Type;
                        if (argCanBeByRef)
                        {
                            var bao = arg as BoundAddressOfOperator;
                            argType = bao.Operand.Type;
                        }

                        if (parLeft.Type != parRight.Type || refLeft != refRight)
                        {
                            // Prefer the method with a more specific parameter which is not an array type over USUAL
                            if (parLeft.Type == usualType && argType != usualType && !parRight.Type.IsArray())
                            {
                                result = BetterResult.Right;
                                return(true);
                            }
                            if (parRight.Type == usualType && argType != usualType && !parLeft.Type.IsArray())
                            {
                                result = BetterResult.Left;
                                return(true);
                            }
                            // Prefer the method with Object type over the one with Object[] type
                            if (parLeft.Type == objectType && parRight.Type.IsArray() && ((ArrayTypeSymbol)parRight.Type).ElementType == objectType)
                            {
                                result = BetterResult.Left;
                                return(true);
                            }
                            if (parRight.Type == objectType && parLeft.Type.IsArray() && ((ArrayTypeSymbol)parLeft.Type).ElementType == objectType)
                            {
                                result = BetterResult.Right;
                                return(true);
                            }
                            // Now check for REF parameters and possible REF arguments
                            if (argCanBeByRef)
                            {
                                var op     = arg as BoundAddressOfOperator;
                                var opType = op?.Operand?.Type;
                                if (refLeft == RefKind.Ref && opType == parLeft.Type)
                                {
                                    result = BetterResult.Left;
                                    return(true);
                                }
                                if (refRight == RefKind.Ref && opType == parRight.Type)
                                {
                                    result = BetterResult.Right;
                                    return(true);
                                }
                                if (refLeft != refRight)
                                {
                                    if (refLeft == RefKind.Ref)
                                    {
                                        result = BetterResult.Left;
                                        return(true);
                                    }
                                    if (refRight == RefKind.Ref)
                                    {
                                        result = BetterResult.Right;
                                        return(true);
                                    }
                                }
                            }
                            if (refLeft != refRight)
                            {
                                if (parLeft.Type == argType && refLeft != RefKind.None && argCanBeByRef)
                                {
                                    result = BetterResult.Left;
                                    return(true);
                                }
                                if (parRight.Type == argType && refRight != RefKind.None && argCanBeByRef)
                                {
                                    result = BetterResult.Right;
                                    return(true);
                                }
                                if (parLeft.Type == argType && refLeft == RefKind.None && !argCanBeByRef)
                                {
                                    result = BetterResult.Left;
                                    return(true);
                                }
                                if (parRight.Type == argType && refRight == RefKind.None && !argCanBeByRef)
                                {
                                    result = BetterResult.Right;
                                    return(true);
                                }
                            }
                            // now fall back to original type (and not addressof type)
                            argType = arg.Type;
                            // Handle passing Enum values to methods that have a non enum parameter
                            if (argType?.TypeKind == TypeKind.Enum)
                            {
                                // First check if they have the enum type itself
                                if (argType == parLeft.Type)
                                {
                                    result = BetterResult.Left;
                                    return(true);
                                }
                                if (argType == parRight.Type)
                                {
                                    result = BetterResult.Right;
                                    return(true);
                                }
                                // Then check the underlying type
                                argType = argType.EnumUnderlyingType();
                                if (argType == parLeft.Type)
                                {
                                    result = BetterResult.Left;
                                    return(true);
                                }
                                if (argType == parRight.Type)
                                {
                                    result = BetterResult.Right;
                                    return(true);
                                }
                            }
                            if (argType == parLeft.Type)
                            {
                                result = BetterResult.Left;
                                return(true);
                            }
                            if (argType == parRight.Type)
                            {
                                result = BetterResult.Right;
                                return(true);
                            }
                            // VoFloat prefers overload with double over all other conversions
                            if (argType == Compilation.FloatType())
                            {
                                var doubleType = Compilation.GetSpecialType(SpecialType.System_Double);
                                if (parLeft.Type == doubleType)
                                {
                                    result = BetterResult.Left;
                                    return(true);
                                }
                                if (parRight.Type == doubleType)
                                {
                                    result = BetterResult.Right;
                                    return(true);
                                }
                            }
                            // handle case where argument is usual and the method is not usual
                            // prefer method with "native VO" parameter type
                            if (argType == Compilation.UsualType())
                            {
                                // no need to check if parleft or parright are usual that was checked above
                                if (parLeft.Type != parRight.Type)
                                {
                                    // is there an VO style conversion possible ?
                                    var leftConvert  = parLeft.Type.IsValidVOUsualType(Compilation);
                                    var rightConvert = parRight.Type.IsValidVOUsualType(Compilation);
                                    if (leftConvert != rightConvert)
                                    {
                                        // One is a valid conversion, the other is not.
                                        if (leftConvert)
                                        {
                                            result = BetterResult.Left;
                                        }
                                        else
                                        {
                                            result = BetterResult.Right;
                                        }
                                        return(true);
                                    }
                                }
                            }
                        }
                    }
                }
                // when both methods are in a functions class from different assemblies
                // pick the first one in the references list
                //
                if (asm1 != asm2 &&
                    string.Equals(m1.Member.ContainingType.Name, XSharpSpecialNames.FunctionsClass, XSharpString.Comparison) &&
                    string.Equals(m2.Member.ContainingType.Name, XSharpSpecialNames.FunctionsClass, XSharpString.Comparison))
                {
                    foreach (var reference in Compilation.ReferencedAssemblyNames)
                    {
                        if (reference.Name == asm1.Name)
                        {
                            result    = BetterResult.Left;
                            Ambiguous = true;
                        }
                        if (reference.Name == asm2.Name)
                        {
                            result    = BetterResult.Right;
                            Ambiguous = true;
                        }
                        if (Ambiguous)
                        {
                            TMember r1, r2;
                            if (result == BetterResult.Left)
                            {
                                r1 = m1.Member;
                                r2 = m2.Member;
                            }
                            else
                            {
                                r1 = m2.Member;
                                r2 = m1.Member;
                            }

                            var info = new CSDiagnosticInfo(ErrorCode.WRN_VulcanAmbiguous,
                                                            new object[] {
                                r1.Name,
                                r1.Kind.ToString(),
                                new FormattedSymbol(r1, SymbolDisplayFormat.CSharpErrorMessageFormat),
                                r2.Kind.ToString(),
                                new FormattedSymbol(r2, SymbolDisplayFormat.CSharpErrorMessageFormat)
                            });
                            useSiteDiagnostics = new HashSet <DiagnosticInfo>();
                            useSiteDiagnostics.Add(info);
                            return(true);
                        }
                    }
                }
            }


            // generate warning that function takes precedence over static method
            var func1 = m1.Member.ContainingType.Name.EndsWith("Functions");
            var func2 = m2.Member.ContainingType.Name.EndsWith("Functions");

            if (func1 && !func2)
            {
                result = BetterResult.Left;
                var info = new CSDiagnosticInfo(ErrorCode.WRN_FunctionsTakePrecedenceOverMethods,
                                                new object[] {
                    m1.Member.Name,
                    new FormattedSymbol(m1.Member, SymbolDisplayFormat.CSharpErrorMessageFormat),
                    new FormattedSymbol(m2.Member, SymbolDisplayFormat.CSharpErrorMessageFormat),
                    m1.Member.Kind.ToString()
                });
                useSiteDiagnostics = new HashSet <DiagnosticInfo>();
                useSiteDiagnostics.Add(info);
            }
            if (func2 && !func1)
            {
                result = BetterResult.Right;
                var info = new CSDiagnosticInfo(ErrorCode.WRN_FunctionsTakePrecedenceOverMethods,
                                                new object[] {
                    m2.Member.Name,
                    new FormattedSymbol(m2.Member, SymbolDisplayFormat.CSharpErrorMessageFormat),
                    new FormattedSymbol(m1.Member, SymbolDisplayFormat.CSharpErrorMessageFormat),
                    m2.Member.Kind.ToString()
                });
                useSiteDiagnostics = new HashSet <DiagnosticInfo>();
                useSiteDiagnostics.Add(info);
            }
            return(false);
        }