Exemplo n.º 1
0
        internal Conversion ClassifyImplicitUserDefinedConversionForSwitchGoverningType(TypeSymbol sourceType, out TypeSymbol switchGoverningType, ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            // SPEC:    The governing type of a switch statement is established by the switch expression.
            // SPEC:    1) If the type of the switch expression is sbyte, byte, short, ushort, int, uint,
            // SPEC:       long, ulong, bool, char, string, or an enum-type, or if it is the nullable type
            // SPEC:       corresponding to one of these types, then that is the governing type of the switch statement.
            // SPEC:    2) Otherwise, exactly one user-defined implicit conversion (§6.4) must exist from the
            // SPEC:       type of the switch expression to one of the following possible governing types:
            // SPEC:       sbyte, byte, short, ushort, int, uint, long, ulong, char, string, or, a nullable type
            // SPEC:       corresponding to one of those types

            // NOTE:    We should be called only if (1) is false for source type.
            Debug.Assert((object)sourceType != null);
            Debug.Assert(!sourceType.IsValidSwitchGoverningType());

            UserDefinedConversionResult result = AnalyzeImplicitUserDefinedConversionForSwitchGoverningType(sourceType, ref useSiteDiagnostics);

            if (result.Kind == UserDefinedConversionResultKind.Valid)
            {
                UserDefinedConversionAnalysis analysis = result.Results[result.Best];

                switchGoverningType = analysis.ToType;
                Debug.Assert(switchGoverningType.IsValidSwitchGoverningType(isTargetTypeOfUserDefinedOp: true));
            }
            else
            {
                switchGoverningType = null;
            }

            return(new Conversion(result, isImplicit: true));
        }
Exemplo n.º 2
0
 internal Conversion(UserDefinedConversionResult conversionResult, bool isImplicit)
     : this()
 {
     this.Kind = conversionResult.Kind == UserDefinedConversionResultKind.NoApplicableOperators
         ? ConversionKind.NoConversion
         : isImplicit?ConversionKind.ImplicitUserDefined : ConversionKind.ExplicitUserDefined;
     this.conversionResult = conversionResult;
 }
Exemplo n.º 3
0
        private UserDefinedConversionResult AnalyzeExplicitUserDefinedConversions(
            BoundExpression sourceExpression,
            TypeSymbol source,
            TypeSymbol target,
            bool isChecked,
            ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
        {
            Debug.Assert(sourceExpression is null || Compilation is not null);
            Debug.Assert(sourceExpression != null || (object)source != null);
            Debug.Assert((object)target != null);

            // SPEC: A user-defined explicit conversion from type S to type T is processed
            // SPEC: as follows:

            // SPEC: Find the set of types D from which user-defined conversion operators
            // SPEC: will be considered...
            var d = ArrayBuilder <(NamedTypeSymbol ParticipatingType, TypeParameterSymbol ConstrainedToTypeOpt)> .GetInstance();

            ComputeUserDefinedExplicitConversionTypeSet(source, target, d, ref useSiteInfo);

            // SPEC: Find the set of applicable user-defined and lifted conversion operators, U...
            var ubuild = ArrayBuilder <UserDefinedConversionAnalysis> .GetInstance();

            ComputeApplicableUserDefinedExplicitConversionSet(sourceExpression, source, target, isChecked: isChecked, d, ubuild, ref useSiteInfo);
            d.Free();
            ImmutableArray <UserDefinedConversionAnalysis> u = ubuild.ToImmutableAndFree();

            // SPEC: If U is empty, the conversion is undefined and a compile-time error occurs.
            if (u.Length == 0)
            {
                return(UserDefinedConversionResult.NoApplicableOperators(u));
            }

            // SPEC: Find the most specific source type SX of the operators in U...
            TypeSymbol sx = MostSpecificSourceTypeForExplicitUserDefinedConversion(u, sourceExpression, source, ref useSiteInfo);

            if ((object)sx == null)
            {
                return(UserDefinedConversionResult.NoBestSourceType(u));
            }

            // SPEC: Find the most specific target type TX of the operators in U...
            TypeSymbol tx = MostSpecificTargetTypeForExplicitUserDefinedConversion(u, target, ref useSiteInfo);

            if ((object)tx == null)
            {
                return(UserDefinedConversionResult.NoBestTargetType(u));
            }

            int?best = MostSpecificConversionOperator(sx, tx, u);

            if (best == null)
            {
                return(UserDefinedConversionResult.Ambiguous(u));
            }

            return(UserDefinedConversionResult.Valid(u, best.Value));
        }
Exemplo n.º 4
0
        internal Conversion(UserDefinedConversionResult conversionResult, bool isImplicit)
        {
            _kind = conversionResult.Kind == UserDefinedConversionResultKind.NoApplicableOperators
                ? ConversionKind.NoConversion
                : isImplicit ? ConversionKind.ImplicitUserDefined : ConversionKind.ExplicitUserDefined;

            _uncommonData = new UncommonData(
                isExtensionMethod: false,
                isArrayIndex: false,
                conversionResult: conversionResult,
                conversionMethod: null,
                nestedConversions: default);
Exemplo n.º 5
0
        private Conversion(ConversionKind kind, bool isExtensionMethod, bool isArrayIndex, UserDefinedConversionResult conversionResult, MethodSymbol methodGroupConversionMethod)
        {
            this.Kind                        = kind;
            this.conversionResult            = conversionResult;
            this.methodGroupConversionMethod = methodGroupConversionMethod;

            this.flags = isExtensionMethod ? IsExtensionMethodMask : (byte)0;
            if (isArrayIndex)
            {
                flags |= IsArrayIndexMask;
            }
        }
Exemplo n.º 6
0
            public UncommonData(
                bool isExtensionMethod,
                bool isArrayIndex,
                UserDefinedConversionResult conversionResult,
                MethodSymbol conversionMethod,
                ImmutableArray <Conversion> nestedConversions)
            {
                _conversionMethod     = conversionMethod;
                _conversionResult     = conversionResult;
                _nestedConversionsOpt = nestedConversions;

                _flags = isExtensionMethod ? IsExtensionMethodMask : (byte)0;
                if (isArrayIndex)
                {
                    _flags |= IsArrayIndexMask;
                }
            }
Exemplo n.º 7
0
 ConversionKind ClassifyVoNullLiteralConversion(BoundExpression source, TypeSymbol destination, out Conversion conv)
 {
     if (_binder.Compilation.Options.HasRuntime && destination is NamedTypeSymbol)
     {
         var usualType = _binder.Compilation.UsualType();
         var nts       = destination as NamedTypeSymbol;
         if (nts.ConstructedFrom == usualType)
         {
             var op = usualType.GetOperators("op_Implicit")
                      .WhereAsArray(o => o.ParameterCount == 1 && o.ParameterTypes[0].IsObjectType() && o.ReturnType == usualType)
                      .AsSingleton() as MethodSymbol;
             if (op != null)
             {
                 var sourceType = _binder.Compilation.GetSpecialType(SpecialType.System_Object);
                 UserDefinedConversionAnalysis uca = UserDefinedConversionAnalysis.Normal(op, Conversion.ImplicitReference, Conversion.Identity, sourceType, destination);
                 UserDefinedConversionResult   cr  = UserDefinedConversionResult.Valid(new[] { uca }.AsImmutable(), 0);
                 conv = new Conversion(cr, isImplicit: true);
                 return(ConversionKind.ImplicitUserDefined);
             }
         }
     }
     conv = Conversion.NoConversion;
     return(ConversionKind.NoConversion);
 }
Exemplo n.º 8
0
        /// <remarks>
        /// NOTE: Keep this method in sync with AnalyzeImplicitUserDefinedConversion.
        /// </remarks>
        protected UserDefinedConversionResult AnalyzeImplicitUserDefinedConversionForV6SwitchGoverningType(TypeSymbol source, ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            // SPEC:    The governing type of a switch statement is established by the switch expression.
            // SPEC:    1) If the type of the switch expression is sbyte, byte, short, ushort, int, uint,
            // SPEC:       long, ulong, bool, char, string, or an enum-type, or if it is the nullable type
            // SPEC:       corresponding to one of these types, then that is the governing type of the switch statement.
            // SPEC:    2) Otherwise, exactly one user-defined implicit conversion (§6.4) must exist from the
            // SPEC:       type of the switch expression to one of the following possible governing types:
            // SPEC:       sbyte, byte, short, ushort, int, uint, long, ulong, char, string, or, a nullable type
            // SPEC:       corresponding to one of those types

            // NOTE:    This method implements part (2) above, it should be called only if (1) is false for source type.
            Debug.Assert((object)source != null);
            Debug.Assert(!source.IsValidV6SwitchGoverningType());

            // NOTE: For (2) we use an approach similar to native compiler's approach, but call into the common code for analyzing user defined implicit conversions.
            // NOTE:    (a) Compute the set of types D from which user-defined conversion operators should be considered by considering only the source type.
            // NOTE:    (b) Instead of computing applicable user defined implicit conversions U from the source type to a specific target type,
            // NOTE:        we compute these from the source type to ANY target type.
            // NOTE:    (c) From the conversions in U, select the most specific of them that targets a valid switch governing type

            // SPEC VIOLATION: Because we use the same strategy for computing the most specific conversion, as the Dev10 compiler did (in fact
            // SPEC VIOLATION: we share the code), we inherit any spec deviances in that analysis. Specifically, the analysis only considers
            // SPEC VIOLATION: which conversion has the least amount of lifting, where a conversion may be considered to be in unlifted form,
            // SPEC VIOLATION: half-lifted form (only the argument type or return type is lifted) or fully lifted form. The most specific computation
            // SPEC VIOLATION: looks for a unique conversion that is least lifted. The spec, on the other hand, requires that the conversion
            // SPEC VIOLATION: be *unique*, not merely most use the least amount of lifting among the applicable conversions.

            // SPEC VIOLATION: This introduces a SPEC VIOLATION for the following tests in the native compiler:

            // NOTE:    // See test SwitchTests.CS0166_AggregateTypeWithMultipleImplicitConversions_07
            // NOTE:    struct Conv
            // NOTE:    {
            // NOTE:        public static implicit operator int (Conv C) { return 1; }
            // NOTE:        public static implicit operator int (Conv? C2) { return 0; }
            // NOTE:        public static int Main()
            // NOTE:        {
            // NOTE:            Conv? D = new Conv();
            // NOTE:            switch(D)
            // NOTE:            {   ...

            // SPEC VIOLATION: Native compiler allows the above code to compile
            // SPEC VIOLATION: even though there are two user-defined implicit conversions:
            // SPEC VIOLATION: 1) To int type (applicable in normal form): public static implicit operator int (Conv? C2)
            // SPEC VIOLATION: 2) To int? type (applicable in lifted form): public static implicit operator int (Conv C)

            // NOTE:    // See also test SwitchTests.TODO
            // NOTE:    struct Conv
            // NOTE:    {
            // NOTE:        public static implicit operator int? (Conv C) { return 1; }
            // NOTE:        public static implicit operator string (Conv? C2) { return 0; }
            // NOTE:        public static int Main()
            // NOTE:        {
            // NOTE:            Conv? D = new Conv();
            // NOTE:            switch(D)
            // NOTE:            {   ...

            // SPEC VIOLATION: Native compiler allows the above code to compile too
            // SPEC VIOLATION: even though there are two user-defined implicit conversions:
            // SPEC VIOLATION: 1) To string type (applicable in normal form): public static implicit operator string (Conv? C2)
            // SPEC VIOLATION: 2) To int? type (applicable in half-lifted form): public static implicit operator int? (Conv C)

            // SPEC VIOLATION: This occurs because the native compiler compares the applicable conversions to find one with the least amount
            // SPEC VIOLATION: of lifting, ignoring whether the return types are the same or not.
            // SPEC VIOLATION: We do the same to maintain compatibility with the native compiler.

            // (a) Compute the set of types D from which user-defined conversion operators should be considered by considering only the source type.
            var d = ArrayBuilder <NamedTypeSymbol> .GetInstance();

            ComputeUserDefinedImplicitConversionTypeSet(source, t: null, d: d, useSiteDiagnostics: ref useSiteDiagnostics);

            // (b) Instead of computing applicable user defined implicit conversions U from the source type to a specific target type,
            //     we compute these from the source type to ANY target type. We will filter out those that are valid switch governing
            //     types later.
            var ubuild = ArrayBuilder <UserDefinedConversionAnalysis> .GetInstance();

            ComputeApplicableUserDefinedImplicitConversionSet(null, source, target: null, d: d, u: ubuild, useSiteDiagnostics: ref useSiteDiagnostics, allowAnyTarget: true);
            d.Free();
            ImmutableArray <UserDefinedConversionAnalysis> u = ubuild.ToImmutableAndFree();

            // (c) Find that conversion with the least amount of lifting
            int?best = MostSpecificConversionOperator(conv => conv.ToType.IsValidV6SwitchGoverningType(isTargetTypeOfUserDefinedOp: true), u);

            if (best != null)
            {
                return(UserDefinedConversionResult.Valid(u, best.Value));
            }

            return(UserDefinedConversionResult.NoApplicableOperators(u));
        }
Exemplo n.º 9
0
        /// <remarks>
        /// NOTE: Keep this method in sync with <see cref="AnalyzeImplicitUserDefinedConversionForV6SwitchGoverningType"/>.
        /// </remarks>
        private UserDefinedConversionResult AnalyzeImplicitUserDefinedConversions(
            BoundExpression sourceExpression,
            TypeSymbol source,
            TypeSymbol target,
            ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            Debug.Assert(sourceExpression != null || (object)source != null);
            Debug.Assert((object)target != null);

            // User-defined conversions that involve generics can be quite strange. There
            // are two basic problems: first, that generic user-defined conversions can be
            // "shadowed" by built-in conversions, and second, that generic user-defined
            // conversions can make conversions that would never have been legal user-defined
            // conversions if declared non-generically. I call this latter kind of conversion
            // a "suspicious" conversion.
            //
            // The shadowed conversions are easily dealt with:
            //
            // SPEC: If a predefined implicit conversion exists from a type S to type T,
            // SPEC: all user-defined conversions, implicit or explicit, are ignored.
            // SPEC: If a predefined explicit conversion exists from a type S to type T,
            // SPEC: any user-defined explicit conversion from S to T are ignored.
            //
            // The rule above can come into play in cases like:
            //
            // sealed class C<T> { public static implicit operator T(C<T> c) { ... } }
            // C<object> c = whatever;
            // object o = c;
            //
            // The built-in implicit conversion from C<object> to object must shadow
            // the user-defined implicit conversion.
            //
            // The caller of this method checks for user-defined conversions *after*
            // predefined implicit conversions, so we already know that if we got here,
            // there was no predefined implicit conversion.
            //
            // Note that a user-defined *implicit* conversion may win over a built-in
            // *explicit* conversion by the rule given above. That is, if we created
            // an implicit conversion from T to C<T>, then the user-defined implicit
            // conversion from object to C<object> could be valid, even though that
            // would be "replacing" a built-in explicit conversion with a user-defined
            // implicit conversion. This is one of the "suspicious" conversions,
            // as it would not be legal to declare a user-defined conversion from
            // object in a non-generic type.
            //
            // The way the native compiler handles suspicious conversions involving
            // interfaces is neither sensible nor in line with the rules in the
            // specification. It is not clear at this time whether we should be exactly
            // matching the native compiler, the specification, or neither, in Roslyn.

            // Spec (6.4.4 User-defined implicit conversions)
            //   A user-defined implicit conversion from an expression E to type T is processed as follows:

            // SPEC: Find the set of types D from which user-defined conversion operators...
            var d = ArrayBuilder <NamedTypeSymbol> .GetInstance();

            ComputeUserDefinedImplicitConversionTypeSet(source, target, d, ref useSiteDiagnostics);

            // SPEC: Find the set of applicable user-defined and lifted conversion operators, U...
            var ubuild = ArrayBuilder <UserDefinedConversionAnalysis> .GetInstance();

            ComputeApplicableUserDefinedImplicitConversionSet(sourceExpression, source, target, d, ubuild, ref useSiteDiagnostics);
            d.Free();
            ImmutableArray <UserDefinedConversionAnalysis> u = ubuild.ToImmutableAndFree();

            // SPEC: If U is empty, the conversion is undefined and a compile-time error occurs.
            if (u.Length == 0)
            {
                return(UserDefinedConversionResult.NoApplicableOperators(u));
            }

            // SPEC: Find the most specific source type SX of the operators in U...
            TypeSymbol sx = MostSpecificSourceTypeForImplicitUserDefinedConversion(u, source, ref useSiteDiagnostics);

#if XSHARP
            if ((object)sx == null)
            {
                // When converting to USUAL and no valid operator is found then choose the operator with an Object Parameter
                // Except when the source is a pointer type.
                if (this is Conversions)
                {
                    Conversions conv       = this as Conversions;
                    bool        usePointer = source.IsPointerType();
                    if (conv.Compilation.Options.HasRuntime && target == conv.Compilation.UsualType())
                    {
                        for (int i = 0; i < u.Length; i++)
                        {
                            var x = u[i];
                            if (usePointer && x.ToType == target && x.FromType.IsVoidPointer())
                            {
                                return(UserDefinedConversionResult.Valid(u, i));
                            }
                            if (!usePointer && x.ToType == target && x.FromType == conv.Compilation.GetSpecialType(SpecialType.System_Object))
                            {
                                return(UserDefinedConversionResult.Valid(u, i));
                            }
                        }
                    }
                }
            }
#endif
            if ((object)sx == null)
            {
                return(UserDefinedConversionResult.NoBestSourceType(u));
            }

            // SPEC: Find the most specific target type TX of the operators in U...
            TypeSymbol tx = MostSpecificTargetTypeForImplicitUserDefinedConversion(u, target, ref useSiteDiagnostics);
            if ((object)tx == null)
            {
                return(UserDefinedConversionResult.NoBestTargetType(u));
            }

            int?best = MostSpecificConversionOperator(sx, tx, u);
            if (best == null)
            {
                return(UserDefinedConversionResult.Ambiguous(u));
            }

            return(UserDefinedConversionResult.Valid(u, best.Value));
        }
Exemplo n.º 10
0
        private static UserDefinedConversionResult MostSpecificConversionOperatorForSwitchGoverningType(TypeSymbol sx, ImmutableArray <UserDefinedConversionAnalysis> u)
        {
            // This method finds the most specific user-defined implicit conversion operator from the best source type SX to a valid switch governing type.
            // It implements steps (e) and (f) for AnalyzeImplicitUserDefinedConversionForSwitchGoverningType, see comments in that method for details.

            // (e) Instead of finding the most specific target type TX of the operators in U, we consider all unique target types TX for all operators in U.
            //     Let this set of unique target types be called Y.
            var y = new HashSet <TypeSymbol>();
            UserDefinedConversionResult?exactConversionResult = null;

            // (f) Check if there is exactly one target type TX in Y such that it satisfied both conditions below:
            //     (i)  TX is a valid switch governing type as per condition (2) of the SPEC for establishing the switch governing type and
            //     (ii) There is a valid most specific user defined implicit conversion operator from SX to TX.

            foreach (UserDefinedConversionAnalysis analysis in u)
            {
                TypeSymbol tx = analysis.ToType;

                if (y.Add(tx) && tx.IsValidSwitchGoverningType(isTargetTypeOfUserDefinedOp: true))
                {
                    if (!exactConversionResult.HasValue)
                    {
                        // NOTE:    As mentioned in the comments at the start of this function, native compiler doesn't call into
                        // NOTE:    the code for analyzing user defined implicit conversion, i.e. MostSpecificConversionOperator,
                        // NOTE:    but does the analysis of applicable lifted/normal forms itself.
                        // NOTE:    This introduces a SPEC VIOLATION for the following test in the native compiler:

                        // NOTE:    // See test SwitchTests.CS0166_AggregateTypeWithMultipleImplicitConversions_07
                        // NOTE:    struct Conv
                        // NOTE:    {
                        // NOTE:        public static implicit operator int (Conv C) { return 1; }
                        // NOTE:        public static implicit operator int (Conv? C2) { return 0; }
                        // NOTE:        public static int Main()
                        // NOTE:        {
                        // NOTE:            Conv? D = new Conv();
                        // NOTE:            switch(D)
                        // NOTE:            {   ...

                        // SPEC VIOLATION: Native compiler allows the above code to compile
                        // SPEC VIOLATION: even though there are two user-defined implicit conversions:
                        // SPEC VIOLATION: 1) To int type (applicable in normal form): public static implicit operator int (Conv? C2)
                        // SPEC VIOLATION: 2) To int? type (applicable in lifted form): public static implicit operator int (Conv C)

                        // SPEC VIOLATION: We maintain compability with the native compiler.

                        int?best = MostSpecificConversionOperator(sx, tx, u);
                        if (best != null)
                        {
                            exactConversionResult = UserDefinedConversionResult.Valid(u, best.Value);
                            continue;
                        }
                    }

                    return(UserDefinedConversionResult.Ambiguous(u));
                }
            }

            // If there exists such unique TX in Y, then that operator is the resultant user defined conversion and TX is the resultant switch governing type.
            // Otherwise we either have ambiguity or no applicable operators.

            return(exactConversionResult.HasValue ?
                   exactConversionResult.Value :
                   UserDefinedConversionResult.NoApplicableOperators(u));
        }
Exemplo n.º 11
0
        /// <remarks>
        /// NOTE: Keep this method in sync with AnalyzeImplicitUserDefinedConversion.
        /// </remarks>
        protected UserDefinedConversionResult AnalyzeImplicitUserDefinedConversionForSwitchGoverningType(TypeSymbol source, ref HashSet <DiagnosticInfo> useSiteDiagnostics)
        {
            // SPEC:    The governing type of a switch statement is established by the switch expression.
            // SPEC:    1) If the type of the switch expression is sbyte, byte, short, ushort, int, uint,
            // SPEC:       long, ulong, bool, char, string, or an enum-type, or if it is the nullable type
            // SPEC:       corresponding to one of these types, then that is the governing type of the switch statement.
            // SPEC:    2) Otherwise, exactly one user-defined implicit conversion (§6.4) must exist from the
            // SPEC:       type of the switch expression to one of the following possible governing types:
            // SPEC:       sbyte, byte, short, ushort, int, uint, long, ulong, char, string, or, a nullable type
            // SPEC:       corresponding to one of those types

            // NOTE:    This method implements part (2) above, it should be called only if (1) is false for source type.
            Debug.Assert((object)source != null);
            Debug.Assert(!source.IsValidSwitchGoverningType());

            // NOTE:    There are multiple possible approaches for implementing (2):
            // NOTE:    1)  AnalyzeImplicitUserDefinedConversion from source type to each of the possible governing types
            // NOTE:        mentioned in (2): Though this approach exactly matches the specification, it is highly inefficient.
            // NOTE:        We need to consider 20 possible target types (ten primitive non-nullable types
            // NOTE:        and ten primitive nullable types) to determine if there is exactly one valid user defined conversion to
            // NOTE:        any one of these types, requiring 20 calls to AnalyzeImplicitUserDefinedConversion.
            // NOTE:    2)  Native compiler's approach: Native compiler implements this by walking through all the implicit user defined operators
            // NOTE:        from the source type to a valid switch governing type, as per (2), and determining if there is a unique best.
            // NOTE:        This part is that piece of code doesn't call into the code for analyzing user defined implicit conversion, but does the
            // NOTE:        analysis of applicable lifted/normal forms itself. This makes it very difficult to maintain and is bug prone.
            // NOTE:        See the SPEC VIOLATION comment later in this method for one of the cases where it gets the analysis wrong and violates the
            // NOTE:        language specification.
            // NOTE:    3)  Use an approach similar to native compiler's approach, but call into the common code for analyzing user defined implicit conversion.


            // NOTE:    We choose approach (3) and implement it as a slight variation of AnalyzeImplicitUserDefinedConversion as follows:

            // NOTE:    (a) Compute the set of types D from which user-defined conversion operators should be considered by considering only the source type.
            // NOTE:    (b) Instead of computing applicable user defined implicit conversions U from the source type to a specific target type,
            // NOTE:        we compute these from the source type to ANY target type.
            // NOTE:    (c) If U is empty, the conversion is undefined and a compile-time error occurs.
            // NOTE:    (d) Find the most specific source type SX of the operators in U...
            // NOTE:    (e) Instead of finding the most specific target type TX of the operators in U, we consider all unique target types TX for all operators in U.
            // NOTE:        Let this set of unique target types be called Y.
            // NOTE:    (f) Check if there is exactly one target type TX in Y such that it satisfied both conditions below:
            // NOTE:        (i)  TX is a valid switch governing type as per condition (2) of the SPEC for establishing the switch governing type and
            // NOTE:        (ii) There is a valid most specific user defined implicit conversion operator from SX to TX.
            // NOTE:        If there exists such unique TX in Y, then that operator is the resultant user defined conversion and TX is the resultant switch governing type.
            // NOTE:        Otherwise we either have ambiguity or no applicable operators.

            // (a) Compute the set of types D from which user-defined conversion operators should be considered by considering only the source type.
            var d = ArrayBuilder <NamedTypeSymbol> .GetInstance();

            ComputeUserDefinedImplicitConversionTypeSet(source, t: null, d: d, useSiteDiagnostics: ref useSiteDiagnostics);

            // (b) Instead of computing applicable user defined implicit conversions U from the source type to a specific target type,
            //     we compute these from the source type to ANY target type.
            var ubuild = ArrayBuilder <UserDefinedConversionAnalysis> .GetInstance();

            ComputeApplicableUserDefinedImplicitConversionSet(null, source, target: null, d: d, u: ubuild, useSiteDiagnostics: ref useSiteDiagnostics, allowAnyTarget: true);
            d.Free();
            ImmutableArray <UserDefinedConversionAnalysis> u = ubuild.ToImmutableAndFree();

            // (c) If U is empty, the conversion is undefined and a compile-time error occurs.
            if (u.Length == 0)
            {
                return(UserDefinedConversionResult.NoApplicableOperators(u));
            }

            // (d) Find the most specific source type SX of the operators in U...
            TypeSymbol sx = MostSpecificSourceTypeForImplicitUserDefinedConversion(u, source, ref useSiteDiagnostics);

            if ((object)sx == null)
            {
                return(UserDefinedConversionResult.NoBestSourceType(u));
            }

            return(MostSpecificConversionOperatorForSwitchGoverningType(sx, u));
        }