-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Support basic end-to-end scenarios for converting string constants to UTF8 byte representation. #58849
Support basic end-to-end scenarios for converting string constants to UTF8 byte representation. #58849
Conversation
…o UTF8 byte representation. Proposal - https://github.com/dotnet/csharplang/blob/main/proposals/utf8-string-literals.md
@dotnet/roslyn-compiler Please review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM aside from some nits and questions
@@ -825,6 +827,17 @@ public bool IsConstantExpression | |||
} | |||
} | |||
|
|||
/// <summary> | |||
/// Returns true if the conversion is an implicit Utf8 strings literal conversion. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like string
was meant to be used here instead of strings
#Closed
src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs
Show resolved
Hide resolved
@@ -1570,7 +1637,22 @@ internal Conversion GetCallerLineNumberConversion(TypeSymbol destination, ref Co | |||
|
|||
TypeSymbol expectedAttributeType = corLibrary.GetSpecialType(SpecialType.System_Int32); | |||
BoundLiteral intMaxValueLiteral = new BoundLiteral(syntaxNode, ConstantValue.Create(int.MaxValue), expectedAttributeType); | |||
return ClassifyStandardImplicitConversion(intMaxValueLiteral, expectedAttributeType, destination, ref useSiteInfo); | |||
|
|||
// Below is a dupliucation of relevant parts of ClassifyStandardImplicitConversion method. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: dupliucation
-> duplication
#Closed
|
||
TypeSymbol destinationOriginalDefinition = destination.OriginalDefinition; | ||
if (constantValue?.IsString == true && // PROTOTYPE(UTF8StringLiterals) : confirm if we actually want it to work with 'null' constant value. | ||
sourceExpression.Type?.SpecialType == SpecialType.System_String && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there known scenarios where constantValue?.IsString == true
but sourceExpression.Type
is not string
? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there known scenarios where constantValue?.IsString == true but sourceExpression.Type is not string?
I am not aware of any specific scenario like that. This doesn't mean they couldn't exist.
@@ -80,9 +80,10 @@ internal ConversionsBase WithNullability(bool includeNullability) | |||
/// Determines if the source expression is convertible to the destination type via | |||
/// any built-in or user-defined implicit conversion. | |||
/// </summary> | |||
public Conversion ClassifyImplicitConversionFromExpression(BoundExpression sourceExpression, TypeSymbol destination, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo) | |||
public Conversion ClassifyImplicitConversionFromExpression(BoundExpression sourceExpression, CSharpCompilation compilation, TypeSymbol destination, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was it considered to make 'compilation' a field on `ConversionsBase` instead of adding a parameter? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was it considered to make 'compilation' a field on `ConversionsBase` instead of adding a parameter?
ConversionBase
intentionally doesn't depend on any binding context. Classification of conversions between types doesn't depend on it. I tried to move all classification of conversion from expression to Conversions
(it has Binder), unfortunately implementation of many methods is structured in such a way that it bundles both classifications (from type and from expression) together and conditionally looks at expression when it is passed in. That effort quickly became rather complicated because one of the goals was to avoid code duplication and minimize code changes. Then I tried the approach where compilation is passed along with expression and that became much easier to implement and I think it is much easier to review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. Thanks for the information.
src/Compilers/CSharp/Test/Semantic/Semantics/Utf8StringsLiteralsTests.cs
Show resolved
Hide resolved
} | ||
"; | ||
|
||
[ConditionalFact(typeof(CoreClrOnly))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't understand why this condition is specified on many of the tests. Is it not possible to execute code that uses the feature on desktop framework? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't understand why this condition is specified on many of the tests. Is it not possible to execute code that uses the feature on desktop framework?
Documentation says "Span" types are not available on desktop.
var comp = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe); | ||
|
||
CompileAndVerify(comp, expectedOutput: @"ReadOnlySpan").VerifyDiagnostics(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there also be a test where a type defines both an implicit conversion from string
and from e.g. ReadOnlySpan<byte>
? Apologies if I missed it. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there also be a test where a type defines both an implicit conversion from
string
and from e.g.ReadOnlySpan<byte>
?
Having an additional test doesn't hurt. However, UserDefinedImplicitConversions_01
demonstrates that a user defined conversion like that cannot be used as an implicit conversion.
} | ||
"; | ||
var comp = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe); | ||
comp.VerifyEmitDiagnostics( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider also leaving a prototype comment here to give a diagnostic which indicates the requirement of a constant string for the conversion to work. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider also leaving a prototype comment here to give a diagnostic which indicates the requirement of a constant string for the conversion to work.
I think the error message here is accurate and sufficiently good.
(destination is ArrayTypeSymbol { IsSZArray: true, ElementType.SpecialType: SpecialType.System_Byte } || // byte[] | ||
((destinationOriginalDefinition.Equals(compilation.GetWellKnownType(WellKnownType.System_Span_T), TypeCompareKind.AllIgnoreOptions) || // Span<T> | ||
destinationOriginalDefinition.Equals(compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T), TypeCompareKind.AllIgnoreOptions)) && // ReadOnlySpan<T> | ||
destinationOriginalDefinition.TypeKind == TypeKind.Struct && destinationOriginalDefinition.IsRefLikeType && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | ||
|
||
[ConditionalFact(typeof(CoreClrOnly))] | ||
public void UserDefinedExplictConversions_01() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public void UserDefinedExplictConversions_01() | |
public void UserDefinedExplicitConversions_01() | |
``` #Resolved |
} | ||
|
||
[ConditionalFact(typeof(CoreClrOnly))] | ||
public void UserDefinedExplictConversions_02() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public void UserDefinedExplictConversions_02() | |
public void UserDefinedExplicitConversions_02() | |
``` #Resolved |
{ | ||
static void Main() | ||
{ | ||
System.Console.WriteLine(Test(""s"")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we prefer
ReadOnlySpan<char>
overbyte[]
rather than being ambiguous?
Conversion to ReadOnlySpan<char>
is not a new conversion.
var comp = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.DebugExe); | ||
|
||
CompileAndVerify(comp, expectedOutput: @"string").VerifyDiagnostics(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider including an example where overload resolution behavior has changed. For instance, will the following report "string" with C#10, and "byte[]" with C#11?
using System;
class Program
{
static void Main()
{
var p = new Program();
Console.WriteLine(p.M(""));
}
public string M(byte[] b) => "byte[]";
}
static class E
{
public static string M(this object o, string s) => "string";
}
``` #Resolved
I will follow up on the feedback marked as pending in another PR. In reply to: 1017843992 |
Proposal - https://github.com/dotnet/csharplang/blob/main/proposals/utf8-string-literals.md
Relates to test plan #58848