Skip to content

Commit

Permalink
Change natural type of UTF-8 string literals to ReadOnlySpan<byte>
Browse files Browse the repository at this point in the history
  • Loading branch information
AlekseyTs authored May 27, 2022
1 parent 9c4920a commit 54586cd
Show file tree
Hide file tree
Showing 35 changed files with 998 additions and 720 deletions.
53 changes: 0 additions & 53 deletions docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,59 +97,6 @@ A possible workaround is to switch to using `>>>` operator:
static C1 Test1(C1 x, int y) => x >>> y;
```

## UTF8 String Literal conversion

***Introduced in .NET SDK 6.0.400, Visual Studio 2022 version 17.3.***
The language added conversions between `string` constants and `byte` sequences
where the text is converted into the equivalent UTF8 byte representation.
Specifically the compiler allowed an implicit conversions from **`string` constants**
to `byte[]`, `Span<byte>`, and `ReadOnlySpan<byte>` types.

The conversions can lead to an overload resolution failure due to an ambiguity for a code
that compiled successfully before. For example:
``` C#
Test("s"); // error CS0121: The call is ambiguous between the following methods or properties: 'C.Test(ReadOnlySpan<char>)' and 'C.Test(byte[])'
static string Test(ReadOnlySpan<char> a) => "ReadOnlySpan";
static string Test(byte[] a) => "array";
```

A possible workaround is to apply an explicit cast to the constant string argument.

The conversions can lead to an invocation of a different member. For example:
``` C#
Test("s", (int)1); // Used to call `Test(ReadOnlySpan<char> a, long x)`, but calls `Test(byte[] a, int x)` now
static string Test(ReadOnlySpan<char> a, long x) => "ReadOnlySpan";
static string Test(byte[] a, int x) => "array";
```

A possible workaround is to apply an explicit cast to the constant string argument.

The conversions can lead to an invocation of an instance member where an extension method used to be invoked.
For example:
``` C#
class Program
{
static void Main()
{
var p = new Program();
p.M(""); // Used to call E.M, but calls Program.M now
}

public string M(byte[] b) => "byte[]";
}

static class E
{
public static string M(this object o, string s) => "string";
}
```

Possible workarounds are:
1. Apply an explicit cast to the constant string argument.
2. Call the extension method by using static method invocation syntax.

## Foreach enumerator as a ref struct

***Introduced in .NET SDK 6.0.300, Visual Studio 2022 version 17.2.*** A `foreach` using a ref struct enumerator type reports an error if the language version is set to 7.3 or earlier.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ protected override void InitializeWorker(AnalysisContext context)
var expressionType = context.Compilation.GetTypeByMetadataName(typeof(System.Linq.Expressions.Expression<>).FullName!);
context.RegisterOperationAction(c => AnalyzeOperation(c, expressionType), OperationKind.ArrayCreation);
// Temporarily disabling, https://github.com/dotnet/roslyn/issues/61517 tracks the follow up work
// context.RegisterOperationAction(c => AnalyzeOperation(c, expressionType), OperationKind.ArrayCreation);
});

private void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol? expressionType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public void M()
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestSimpleByteArray()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -211,7 +211,7 @@ public void M()
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestConstant()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -241,7 +241,7 @@ public void M()
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestImplicitArray()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -269,7 +269,7 @@ public void M()
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestExplicitCast()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -297,7 +297,7 @@ public void M()
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestHexLiteral()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -325,7 +325,7 @@ public void M()
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestBinaryExpression()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -353,7 +353,7 @@ public void M()
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestEmptyArray()
{
await new VerifyCS.Test
Expand All @@ -380,7 +380,7 @@ public void M()
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestTrivia1()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -408,7 +408,7 @@ public void M()
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestTrivia2()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -436,7 +436,7 @@ public void M(byte[] b)
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestMultiple()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -468,7 +468,7 @@ public void M()
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestEscapeChars()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -496,7 +496,7 @@ public void M()
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestEmoji()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -581,7 +581,7 @@ public void M()
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestUnicodeReplacementChar()
{
// The unicode replacement character is what is returned when, for example, an unpaired
Expand Down Expand Up @@ -612,7 +612,7 @@ public void M()
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestCollectionInitializer()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -716,7 +716,7 @@ public void Dispose(int a = 1, bool b = true, params byte[] others) { }
}.RunAsync();
}

[Theory, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Theory(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
// Various cases copied from https://github.com/dotnet/runtime/blob/main/src/libraries/Common/tests/Tests/System/Net/aspnetcore/Http3/QPackDecoderTest.cs
[InlineData(new byte[] { 0x37, 0x02, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65 }, "7translate")]
[InlineData(new byte[] { 0x3f, 0x01 }, "?")]
Expand Down Expand Up @@ -782,7 +782,7 @@ public class C
Assert.NotEqual(bytes, newBytes);
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestParamArray1()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -812,7 +812,7 @@ public void M(params byte[] b)
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestParamArray2()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -842,7 +842,7 @@ public void M(int i, params byte[] b)
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestParamArray3()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -872,7 +872,7 @@ public void M(params byte[] b)
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestParamArray4()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -902,7 +902,7 @@ public void M(params byte[] b)
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestParamArray5()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -932,7 +932,7 @@ public void M(params byte[] b)
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestParamArray6()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -981,7 +981,7 @@ public void M(int x, params byte[] b)
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestParamArray8()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -1011,7 +1011,7 @@ public void M(int x, params byte[] b)
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestParamArray9()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -1041,7 +1041,7 @@ public void M(int x, params byte[] b)
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestParamArray10()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -1071,7 +1071,7 @@ public void M(int x, params byte[] b)
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestParamArray11()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -1101,7 +1101,7 @@ public void M(int x, int y, int z, params byte[] b)
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestParamArray12()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -1131,7 +1131,7 @@ public C(params byte[] b)
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestParamArray13()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -1171,7 +1171,7 @@ public void M()
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestParamArray14()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -1225,7 +1225,7 @@ public sealed class IsExternalInit
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestParamArray15()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -1313,7 +1313,7 @@ public B(params byte[] bytes)
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestParamArray16()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -1343,7 +1343,7 @@ public void M(int[] i, byte[] b)
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestParamArray17()
{
await new VerifyCS.Test
Expand Down Expand Up @@ -1373,7 +1373,7 @@ public void M(int[] i, params byte[] b)
}.RunAsync();
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/61517"), Trait(Traits.Feature, Traits.Features.CodeActionsUseUTF8StringLiteral)]
public async Task TestMultidimensionalArray()
{
await new VerifyCS.Test
Expand Down
2 changes: 2 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2613,6 +2613,7 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin
case BoundKind.DefaultExpression:
case BoundKind.Parameter:
case BoundKind.ThisReference:
case BoundKind.UTF8String:
// always returnable
return Binder.ExternalScope;

Expand Down Expand Up @@ -3018,6 +3019,7 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint
case BoundKind.DefaultExpression:
case BoundKind.Parameter:
case BoundKind.ThisReference:
case BoundKind.UTF8String:
// always returnable
return true;

Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5925,7 +5925,7 @@ private BoundUTF8String BindUTF8StringLiteral(LiteralExpressionSyntax node, Bind
CheckFeatureAvailability(node, MessageID.IDS_FeatureUTF8StringLiterals, diagnostics);

var value = (string)node.Token.Value;
var type = ArrayTypeSymbol.CreateSZArray(Compilation.Assembly, TypeWithAnnotations.Create(GetSpecialType(SpecialType.System_Byte, diagnostics, node)));
var type = GetWellKnownType(WellKnownType.System_ReadOnlySpan_T, diagnostics, node).Construct(GetSpecialType(SpecialType.System_Byte, diagnostics, node));

return new BoundUTF8String(node, value, type);
}
Expand Down
3 changes: 0 additions & 3 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -7061,9 +7061,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="IDS_FeatureUTF8StringLiterals" xml:space="preserve">
<value>UTF-8 string literals</value>
</data>
<data name="ERR_ExpressionTreeContainsUTF8StringLiterals" xml:space="preserve">
<value>An expression tree may not contain UTF-8 string conversion or literal.</value>
</data>
<data name="IDS_FeatureUnsignedRightShift" xml:space="preserve">
<value>unsigned right shift</value>
</data>
Expand Down
Loading

0 comments on commit 54586cd

Please sign in to comment.