dotnet / dotnet/roslyn

Overload resolution incorrectly assumes reference types for unresolved implicit user-defined conversion operator return types

Open
#81,049 1 comment 0 reactions 0 assignees View on GitHub
Area-Compilers Concept-Diagnostic Clarity help wanted
Dominant language
C#
Stars
20.7k
Forks
4.3k
PR merge metrics
PR metrics pending

Description

**Version Used**: Microsoft.CodeAnalysis.CSharp 4.14.0

**Steps to Reproduce**:

This bug requires multiple assemblies to reproduce, so for simplicity I've created a repro in the form of a single C# program that uses Roslyn APIs.

Here's the csproj:

``` xml


Exe
net8.0
enable
enable



```

And here's `Program.cs`:

``` c#
using System.Collections.Immutable;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

static Compilation CreateCompilation(string code, MetadataReference[] references) =>
CSharpCompilation.Create(
assemblyName: Guid.NewGuid().ToString(),
syntaxTrees: [CSharpSyntaxTree.ParseText(code)],
references: [MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location), .. references],
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

static ImmutableArray CreateImage(string code, MetadataReference[] references)
{
var compilation = CreateCompilation(code, references);
using var ms = new MemoryStream();
compilation.Emit(ms);
ms.Seek(0, SeekOrigin.Begin);
return ImmutableArray.Create(ms.ToArray());
}

var myStruct1Image = CreateImage(
"public struct MyStruct1 { }",
[]);

var myStruct2Image = CreateImage(
"""
public struct MyStruct2
{
public static implicit operator MyStruct2(MyStruct1 x) => default;
}
""",
[MetadataReference.CreateFromImage(myStruct1Image)]);

var compilation = CreateCompilation(
"""
public static class TestCode
{
public static void Foo(MyClass x) { }
public static void Foo(MyStruct2 x) { }

public static void Bar() => Foo(null);
}

public class MyClass { }
""",
[
//MetadataReference.CreateFromImage(myStruct1Image),
MetadataReference.CreateFromImage(myStruct2Image)
]);

foreach (var diagnostic in compilation.GetDiagnostics())
{
Console.WriteLine(diagnostic);
}
```

If you run it as-is, you'll see this output:

```
(6,33): error CS0121: The call is ambiguous between the following methods or properties: 'TestCode.Foo(MyClass)' and 'TestCode.Foo(MyStruct2)'
```

This is an unexpected error, because `MyStruct2` is a `struct`, so it shouldn't be a valid candidate for the call to `Foo(null)`.

However, if you uncomment the commented line (`MetadataReference.CreateFromImage(myStruct1Image)`), then you'll see no diagnostics, and compilation succeeds.

This is a quite subtle bug. As far as I can tell, here's what's happening:

* In the `Bar` method, Roslyn compiles the call to `Foo`.
* There are two overloads of `Foo`, so it does overload resolution to find the right one.
* `Foo(MyClass)` is a valid choice.
* `Foo(MyStruct2)` isn't itself a valid choice - because `null` can't be converted to `MyStruct2`.
* However... there's an implicit conversion operator from `MyStruct1` to `MyStruct2`.
* This shouldn't change anything - because `MyStruct1` is also a `struct`, so `null` can't be converted to `MyStruct1`.
* ... and when Roslyn is given the assembly reference for the assembly containing `MyStruct1`, that's indeed what happens - `Foo(MyStruct2)` isn't a valid choice. `Foo(MyClass)` is the only valid choice, and compilation succeeds.
* *but* when Roslyn is not given the assembly reference for the assembly containing `MyStruct1`, it appears to assume that `MyStruct1` is a reference type. Which means `Foo(MyStruct2)` _is_ a valid choice, because `null` can be converted to `MyStruct1`, which can be converted to `MyStruct2`.

That seems wrong to me. Instead of assuming that the return type of the implicit conversion operator is a reference type, I'd have expected Roslyn to instead emit a compiler error saying that it needs the definition of `MyStruct1`, but it can't resolve it.

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.