Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add span type inference #74646

Conversation

jjonescz
Copy link
Member

@jjonescz jjonescz commented Aug 5, 2024

Test plan: #73445
Corresponding speclet update: dotnet/csharplang#8333

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels Aug 5, 2024
// var d1 = a.M;
Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "a.M").WithLocation(4, 10),
Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M").WithArguments("M", "System.Func<int, int>").WithLocation(4, 12),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that type inference successfully infers the delegate type to be Func<int, int> but then binding fails (because the span conversion doesn't exist for extension method receiver). I'm not sure if this is a problem, I think this could happen for some existing scenarios as well.

@jjonescz jjonescz force-pushed the FirstClassSpan-14-TypeInference branch from f408180 to 733cb63 Compare August 5, 2024 14:47
@jjonescz
Copy link
Member Author

jjonescz commented Aug 8, 2024

@cston @333fred for reviews, thanks

Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly LGTM. Couple of minor comments.


private static TypeWithAnnotations GetSpanElementType(TypeSymbol type)
{
return ((NamedTypeSymbol)type).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0];
Copy link
Member

@333fred 333fred Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider assert that type is Span or ROS. #Resolved

var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13);
CompileAndVerify(comp, expectedOutput: "1").VerifyDiagnostics();

var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "3" : "2";
Copy link
Member

@333fred 333fred Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exception is being thrown on CoreCLR? I assume something about variance? Consider leaving a comment. #Resolved

Copy link
Member

@cston cston Aug 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider reporting the exception type rather than "3", in this test and the next test.

using System;
class C
{
void M(ReadOnlySpan<string> a, object b) => M1(a, b);
Copy link
Member

@333fred 333fred Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's test a scenario like this with a non-variant non-array conversion, like ROS<int> and object/vice versa; I expect the former to fail, and the latter to succeed. #Resolved


comp = CreateCompilationWithSpanAndMemoryExtensions(source);
CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics();
}
Copy link
Member

@cston cston Aug 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

Are we testing similar cases to these new tests, but with IEnumerable<T> and ReadOnlySpan<T>? #Closed

// (4,43): error CS0411: The type arguments for method 'C.M1<T>(Span<T[]>, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
// void M(Span<string[]> a, object b) => M1(a, b);
Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1<T>(System.Span<T[]>, T)").WithLocation(4, 43));
}
Copy link
Member

@cston cston Aug 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

Consider testing with void M2(Span<object[]> a, string b) => M1(a, b) as well. #Closed

""";
var comp = CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics();
AssertEx.Equal("C.M1<System.Object>", DisplayInvokedMethodTypeArguments(comp));
}
Copy link
Member

@cston cston Aug 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

Please test:

void M2(int[] a, object b) => M1(a, b);
void M3(object[] a, int b) => M1(a, b);
``` #Closed

// (4,36): error CS0411: The type arguments for method 'C.M1<T>(Span<T>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
// void M(ReadOnlySpan<int> a) => M1(a);
Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1<T>(System.Span<T>)").WithLocation(4, 36));
}
Copy link
Member

@cston cston Aug 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

Are we testing inferring ReadOnlySpan<T> from call with Span<U>?

void M(Span<string> a, object b) => M1(a, b);
void M1<T>(ReadOnlySpan<T> a, T b) { }
``` #Resolved

@@ -600,7 +600,7 @@ static bool CanConvertToSubpattern(IdentifierNameSyntax name, SemanticModel mode

// Process all nodes to refactor in reverse to ensure nested nodes
// are processed before the outer nodes to refactor.
foreach (var originalNode in nodes.Reverse())
foreach (var originalNode in Enumerable.Reverse(nodes))
Copy link
Member

@cston cston Aug 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enumerable.Reverse(nodes)

Why was this change needed? Are we now binding nodes.Reverse() to MemoryExtensions.Reverse<T>(Span<T>)? And if so, is that an issue? (It looks like the array that is created from var nodes = ... ToArray(); should not include array variance.) #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we now binding nodes.Reverse() to MemoryExtensions.Reverse<T>(Span<T>)?

Yes.

And if so, is that an issue?

Yes, because MemoryExtensions.Reverse returns void.

@@ -105,14 +105,10 @@ private static string Concat(string container, string name)
}
}
""";
internal const string s_collectionExtensionsWithSpan = s_collectionExtensions +
internal const string s_collectionExtensionsWithReadOnlySpan = s_collectionExtensions +
Copy link
Member

@cston cston Aug 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s_collectionExtensionsWithReadOnlySpan

Alternatively, could we leave the name unchanged, and simply remove the Report<T>(this in Span<T>) overload? Would that result in many additional test changes? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not many additional changes, I can do that.

// (4,36): error CS0411: The type arguments for method 'C.M1<T>(Span<T>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
// void M(ReadOnlySpan<int> a) => M1(a);
Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("C.M1<T>(System.Span<T>)").WithLocation(4, 36));
}
Copy link
Member

@cston cston Aug 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

Consider testing variance when the array and span are the type arguments. For instance, with the following, for the calls with I<object[]> and IOut<object[]> I'd expect type inference to succeed but the subsequent argument conversion to fail, and for the call with IIn<object[]>, I'd expect type inference to fail.

interface I<T> where T : allows ref struct { }
class A
{
    static void M(I<object[]> a) => M1(a);
    static void M1<T>(I<ReadOnlySpan<T>> x) { }
}
interface IOut<out T> where T : allows ref struct { }
class B
{
    static void M(IOut<object[]> a) => M1(a);
    static void M1<T>(IOut<ReadOnlySpan<T>> x) { }
}
interface IIn<in T> where T : allows ref struct { }
class C
{
    static void M(IIn<object[]> a) => M1(a);
    static void M1<T>(IIn<ReadOnlySpan<T>> x) { }
}
``` #Closed

@jjonescz jjonescz merged commit dd8f133 into dotnet:features/FirstClassSpan Aug 13, 2024
28 checks passed
@jjonescz jjonescz deleted the FirstClassSpan-14-TypeInference branch August 13, 2024 07:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers New Feature - First-class Span Types untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants