dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
18.96k stars 4.03k forks source link

string.Concat behavior difference in recent preview #72883

Closed Tragetaschen closed 6 months ago

Tragetaschen commented 6 months ago

Version Used: 16.9.5 vs 16.10.0 Preview 2.0

Steps to Reproduce:

public string M1(bool flag)
{
    return string.Concat(
        // 17.9.5     CS1729 'string' does not contain a constructor that takes 0 arguments
        // 17.10.0    CS1503 Argument 1: cannot convert from 'target-typed conditional expression' to 'System.Collections.Generic.IEnumerable<string?>'
        flag ? "Hello" : []
    );
}
public string M2(string s, bool flag)
{
    return string.Concat(
        s.AsSpan(), // or just `s`
        // 17.9.5     CS1729 'string' does not contain a constructor that takes 0 arguments
        // 17.10.0    compiles
        flag ? "Hello" : []
    );
}

This is probably due to #71793 I wrote a variant of the the second case in the VS preview and was wondering why it failed in CI.

Expected Behavior / Actual Behavior: I have no actual issue with the new behavior, but I'm wondering if it is intentional.

jaredpar commented 6 months ago

@cston believe this is in effect of the collection expression changes. Can you confirm?

cston commented 6 months ago

@Tragetaschen, thanks for reporting this.

This is an expected change from tightening conversion requirements for collection expressions in 17.10 - see LDM-2024-01-10, https://github.com/dotnet/roslyn/pull/71592:

For a struct or class type that implements System.Collections.IEnumerable and that does not have a create method conversions section should require presence of at least the following APIs:

  • An accessible constructor that is applicable with no arguments.
  • ...

The updated conversion requirements mean that in 17.10, string is no longer considered a valid target type for a collection expression.

Since there is no conversion from [] to string, the conditional expression flag ? "Hello" : [] is untyped, and is therefore target typed in the calls to string.Concat().

For M1, we cannot determine an applicable overload for string.Concat() with a single untyped argument. For error reporting, the compiler chooses the first overload string.Concat(IEnumerable<string>), and reports a conversion error from the conditional expression to IEnumerable<string>.

For M2, the first argument to string.Concat() has type ReadOnlySpan<char> and the second argument is untyped, so the best overload is Concat(ReadOnlySpan<char>, ReadOnlySpan<char>). As a result, the conditional expression and the [] collection expression are implicitly converted to ReadOnlySpan<char>.

Tragetaschen commented 6 months ago

Thanks!