This project is read-only.

Lifted user-defined conversion not reflected in SemanticModel


When an expression is implicitly converted to a nullable and converted via a lifted user-defined conversion operator, the user-defined conversion is not represented in the Conversion value returned by the SemanticModel. However, getting the TypeInfo.ConvertedType correctly reflects both conversions being applied:
const string sourceText =
@"class X
    static void Main()
        int value = 0;
        Y? converted1 = value;
        var converted2 = value == 0 ? default(Y?) : value;

struct Y
    public static implicit operator Y(int value)
        return new Y();

var tree = CSharpSyntaxTree.ParseText(sourceText);
var compilation = CSharpCompilation.Create("Test.exe",
    syntaxTrees: new[] { tree },
    references: new[] { new MetadataFileReference(typeof(object).Assembly.Location) },
    options: new CSharpCompilationOptions(OutputKind.ConsoleApplication));

var model = compilation.GetSemanticModel(tree);
var valueNode1 = (IdentifierNameSyntax)tree.GetCompilationUnitRoot().FindNode(
    new TextSpan(73, 5));
var conversion1 = model.GetConversion(valueNode1);
var convertedType1 = model.GetTypeInfo(valueNode1).ConvertedType;

// conversion1.IsUserDefined == false, but convertedType1 is Y?, 
// which means the user-defined conversion was used

var valueNode2 = (IdentifierNameSyntax)tree.GetCompilationUnitRoot().FindNode(
    new TextSpan(127, 5));
var conversion2 = model.GetConversion(valueNode2);
var convertedType2 = model.GetTypeInfo(valueNode2).ConvertedType;

// conversion2.IsUserDefined == false, but convertedType2 is Y?, 
// which means the user-defined conversion was used


mdour wrote Jul 13, 2014 at 6:07 AM

Also, if you add on the following code at the end of the listing above:
var conversion3 = model.ClassifyConversion(valueNode1, convertedType1);
var conversion4 = model.ClassifyConversion(valueNode2, convertedType2);
Both conversion3 and conversion4 report that the conversion is an implicit user-defined conversion and don't reflect that fact that an implicit nullable conversion also took place.