private UserDefinedConversionResult AnalyzeExplicitUserDefinedConversions( BoundExpression sourceExpression, TypeSymbol source, TypeSymbol target, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { 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> .GetInstance(); ComputeUserDefinedExplicitConversionTypeSet(source, target, d, ref useSiteDiagnostics); // SPEC: Find the set of applicable user-defined and lifted conversion operators, U... var ubuild = ArrayBuilder <UserDefinedConversionAnalysis> .GetInstance(); ComputeApplicableUserDefinedExplicitConversionSet(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 = MostSpecificSourceTypeForExplicitUserDefinedConversion(u, sourceExpression, source, ref useSiteDiagnostics); 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 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)); }
/// <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 ((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)); }