dotnet / dotnet/roslyn

Surprising semantic model and IOp behavior around nullable.

Open
#75,964 6 comments 0 reactions 1 assignee Assigned to @333fred View on GitHub
Area-Compilers Concept-API
Dominant language
C#
Stars
20.7k
Forks
4.3k
PR merge metrics
PR metrics pending

Description

See following test for a template of code demonstrating surprising behavior:

```
[Fact, WorkItem(651624, "https://github.com/dotnet/roslyn/issues/60552")]
public void TestSemanticModelConversionFromNullableToNonNullable()
{
var src =
"""
#nullable enable

using System;

class C
{
public string Main(string? x)
{
return x;
}

public (int a, int b) Main((int, int) x)
{
return x;
}

public dynamic Main(object x)
{
return x;
}
}
""";

var comp = CreateCompilation(src);

var syntaxTree = comp.SyntaxTrees.Single();
var semanticModel = comp.GetSemanticModel(syntaxTree);

var root = syntaxTree.GetRoot();
var returnStatements = root.DescendantNodesAndSelf().OfType().ToArray();

var conversionInfo1 = semanticModel.GetConversion(returnStatements[0].Expression);
var conversionInfo2 = semanticModel.GetConversion(returnStatements[1].Expression);
var conversionInfo3 = semanticModel.GetConversion(returnStatements[2].Expression);

var typeInfo1 = semanticModel.GetTypeInfo(returnStatements[0].Expression);
var typeInfo2 = semanticModel.GetTypeInfo(returnStatements[1].Expression);
var typeInfo3 = semanticModel.GetTypeInfo(returnStatements[2].Expression);
}
```

Here we have 3 methods, which take in a value, and return that same value, all of which go through 'identity' conversions. And, intuitively, at runtime we're not going to actually do any actual conversions here.

However, for both tuples and dynamic, you at least get a TypeInfo telling you accurately what your converted type is. And the converted type is expectedly different from the starting type. In other words, the semantic model lets you know you're goin gfrom `(int,int)` to `(int a, int b)` or from `(object)` to `(dynamic)`.

However, for nullable it does not tell you this. This seems *odd* to me. Effectively, in the semantic model there is no way to say "the starting type is X, but it has changed to Y" despite X and Y being different types.

It's unclear to IDE team why nullable behaves this way. Other identity conversions do not, and the semantic model gives us the info we need to know that the lang is considering converting the types here. This *feels* like a bug. And it's a bug that definitely makes our lives harder as we cannot figure out when these changes are happening, despite them causing the compiler itself ot issue warnings.

This is highly relevant to us in terms of determining which casts we both need to add, or want to remove.

We would ask taht the semantic model indicate this information (and include in the iop tree) As it does for tehse other identity conversions with disparate types.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.