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

Support basic end-to-end scenarios for converting string constants to UTF8 byte representation. #58849

Merged

Conversation

AlekseyTs
Copy link
Contributor

@AlekseyTs AlekseyTs commented Jan 13, 2022

@AlekseyTs
Copy link
Contributor Author

@dotnet/roslyn-compiler Please review

@RikkiGibson RikkiGibson self-assigned this Jan 14, 2022
Copy link
Contributor

@RikkiGibson RikkiGibson left a 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.
Copy link
Contributor

@RikkiGibson RikkiGibson Jan 14, 2022

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

@@ -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.
Copy link
Contributor

@RikkiGibson RikkiGibson Jan 14, 2022

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 &&
Copy link
Contributor

@RikkiGibson RikkiGibson Jan 14, 2022

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

Copy link
Contributor Author

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)
Copy link
Contributor

@RikkiGibson RikkiGibson Jan 14, 2022

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

Copy link
Contributor Author

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.

Copy link
Contributor

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.

}
";

[ConditionalFact(typeof(CoreClrOnly))]
Copy link
Contributor

@RikkiGibson RikkiGibson Jan 14, 2022

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

Copy link
Contributor Author

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();
}
Copy link
Contributor

@RikkiGibson RikkiGibson Jan 14, 2022

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

Copy link
Contributor Author

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(
Copy link
Contributor

@RikkiGibson RikkiGibson Jan 14, 2022

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

Copy link
Contributor Author

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 &&
Copy link
Member

@cston cston Jan 14, 2022

Choose a reason for hiding this comment

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

destinationOriginalDefinition.TypeKind == TypeKind.Struct && destinationOriginalDefinition.IsRefLikeType &&

Consider moving these checks ahead of the calls to Equals(compilation.GetWellKnownType(...)) since these seem less expensive. #Resolved

}

[ConditionalFact(typeof(CoreClrOnly))]
public void UserDefinedExplictConversions_01()
Copy link
Member

@cston cston Jan 14, 2022

Choose a reason for hiding this comment

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

Suggested change
public void UserDefinedExplictConversions_01()
public void UserDefinedExplicitConversions_01()
``` #Resolved

}

[ConditionalFact(typeof(CoreClrOnly))]
public void UserDefinedExplictConversions_02()
Copy link
Member

@cston cston Jan 14, 2022

Choose a reason for hiding this comment

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

Suggested change
public void UserDefinedExplictConversions_02()
public void UserDefinedExplicitConversions_02()
``` #Resolved

{
static void Main()
{
System.Console.WriteLine(Test(""s""));
Copy link
Member

@cston cston Jan 14, 2022

Choose a reason for hiding this comment

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

Test(""s"")

Why do we prefer ReadOnlySpan<char> over byte[] rather than being ambiguous? #Resolved

Copy link
Contributor Author

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> over byte[] 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();
}
Copy link
Member

@cston cston Jan 14, 2022

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

@AlekseyTs
Copy link
Contributor Author

AlekseyTs commented Jan 20, 2022

I will follow up on the feedback marked as pending in another PR.


In reply to: 1017843992

@AlekseyTs AlekseyTs merged commit 36def1e into dotnet:features/Utf8StringLiterals Jan 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants