예제 #1
0
        // Return the index of the *unique* item in the array that matches the predicate,
        // or null if there is not one.
        private static BestIndex UniqueIndex <T>(ImmutableArray <T> items, Func <T, bool> predicate)
        {
            if (items.IsEmpty)
            {
                return(BestIndex.None());
            }

            int?result = null;

            for (int i = 0; i < items.Length; ++i)
            {
                if (predicate(items[i]))
                {
                    if (result == null)
                    {
                        result = i;
                    }
                    else
                    {
                        // Not unique.
                        return(BestIndex.IsAmbiguous(result.Value, i));
                    }
                }
            }

            return(result == null?BestIndex.None() : BestIndex.HasBest(result.Value));
        }
예제 #2
0
        /// <summary>
        /// Find the most specific among a set of conversion operators, with the given constraint on the conversion.
        /// </summary>
        private static int?MostSpecificConversionOperator(Func <UserDefinedConversionAnalysis, bool> constraint, ImmutableArray <UserDefinedConversionAnalysis> u)
        {
            // SPEC: If U contains exactly one user-defined conversion operator from SX to TX
            // SPEC: then that is the most-specific conversion operator;
            //
            // SPEC: Otherwise, if U contains exactly one lifted conversion operator that converts from
            // SPEC: SX to TX then this is the most specific operator.
            //
            // SPEC: Otherwise, the conversion is ambiguous and a compile-time error occurs.
            //
            // SPEC ERROR:
            //
            // Clearly the text above cannot be correct because it gives undesirable results.
            // Suppose we have structs E and F with an implicit user defined conversion from
            // F to E. We have an assignment from F to E?. Clearly what should happen is
            // we should convert F to E, then convert E to E?.  But the spec says that this
            // should be an error. Why? Because both F-->E and F?-->E? are added to the candidate
            // set. What is SX? Clearly F, because there is a candidate that takes an F.
            // What is TX? Clearly E? because there is a candidate that returns an E?.
            // And now the overload resolution problem is ambiguous because neither operator
            // takes SX and returns TX.
            //
            // DELIBERATE SPEC VIOLATION:
            //
            // The native compiler takes a rather different approach than the approach described
            // in the specification. Rather than adding both the lifted and unlifted forms of
            // each operator to the candidate set, using those operators to determine the best
            // source and target types, and then choosing the unique operator from that source type
            // to that target type, it instead *transforms in place* the "from" and "to" types
            // of each operator so that their nullability matches those of the source and target
            // types. This can then lead to ambiguities; consider for example a type that
            // has user defined conversions X-->Y and X-->Y?.  If we have a conversion from X to
            // Y?, the spec would say that the operators X-->Y, its lifted form X?-->Y?, and
            // X-->Y? are applicable candidates and that the best of them is X-->Y?.
            //
            // The native compiler arrives at the same conclusion but by different logic; it says
            // that X-->Y has a "half lifted" form X-->Y?, and that it is "worse" than X-->Y?
            // because it is half lifted.

            // Therefore we match this behavior by first checking to see if there is a unique
            // best operator that converts from the source type to the target type with liftings
            // on neither side.

            BestIndex bestUnlifted = UniqueIndex(u,
                                                 conv =>
                                                 constraint(conv) &&
                                                 LiftingCount(conv) == 0);

            if (bestUnlifted.Kind == BestIndexKind.Best)
            {
                return(bestUnlifted.Best);
            }
            else if (bestUnlifted.Kind == BestIndexKind.Ambiguous)
            {
                // If we got an ambiguity, don't continue. We need to bail immediately.

                // UNDONE: We can do better error reporting if we return the ambiguity and
                // use that in the error message.
                return(null);
            }

            // There was no fully-unlifted operator. Check to see if there was any *half-lifted* operator.
            //
            // For example, suppose we had a conversion from X-->Y?, and lifted it to X?-->Y?. (The spec
            // says not to do such a lifting because Y? is not a non-nullable value type, but the native
            // compiler does so and we are being compatible with it.) That would be a half-lifted operator.
            //
            // For example, suppose we had a conversion from X-->Y, and the assignment Y? y = new X(); --
            // this would also be a "half lifted" conversion even though there is no "lifting" going on
            // (in the sense that we are not checking the source to see if it is null.)
            //

            BestIndex bestHalfLifted = UniqueIndex(u,
                                                   conv =>
                                                   constraint(conv) &&
                                                   LiftingCount(conv) == 1);

            if (bestHalfLifted.Kind == BestIndexKind.Best)
            {
                return(bestHalfLifted.Best);
            }
            else if (bestHalfLifted.Kind == BestIndexKind.Ambiguous)
            {
                // UNDONE: We can do better error reporting if we return the ambiguity and
                // use that in the error message.
                return(null);
            }

            // Finally, see if there is a unique best *fully lifted* operator.

            BestIndex bestFullyLifted = UniqueIndex(u,
                                                    conv =>
                                                    constraint(conv) &&
                                                    LiftingCount(conv) == 2);

            if (bestFullyLifted.Kind == BestIndexKind.Best)
            {
                return(bestFullyLifted.Best);
            }
            else if (bestFullyLifted.Kind == BestIndexKind.Ambiguous)
            {
                // UNDONE: We can do better error reporting if we return the ambiguity and
                // use that in the error message.
                return(null);
            }

            return(null);
        }